> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blobhub.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Job Session Object

> The per-thread session object the worker drives: metadata, ownership, settings

A **Job Session Object** is the unit of work a `session_agent_harness` section drives — a `thread`-typed
session object whose `metadata` describes a workspace, an agent, and a control-channel state. The user prepares
it; the worker watches the control channel, takes over when it goes `pending`, and drives one agent in the
workspace. This page covers the static shape and ownership of that object; the lifecycle dynamics live in
[Handoff](/worker/session-agent-harness/thread-handoff).

## Wire metadata shape

Inside the thread envelope (`value.thread.metadata`), the worker reads and writes these blocks:

```yaml theme={null}
workspace:
  work_folder: /absolute/path/to/repo          # required
agent:
  type: claude_code | codex                     # required
  executable: claude                            # optional
  model: claude-sonnet-4-6                       # optional
  effort: low | medium | high | <passthrough>   # optional
  permissions: autonomous | approval            # optional (default: approval)
instance:
  state: pending | active | completed | failed  # the control channel
```

The envelope is created and updated via
[`upload_session_object`](/blob-types/workflow/operations/upload-session-object) on the thread's alias.

## Required vs optional

* `workspace.work_folder` — **required**. An absolute path to the repository on the worker host.
* `agent.type` — **required**. One of `claude_code` or `codex`.
* Everything else under `agent.*` — **optional**. Omitted fields fall through the settings tiers below.

## Field ownership and the control channel

The protocol relies on a strict ownership split; each side never modifies fields the other owns.

| Field            | Writer                          |
| ---------------- | ------------------------------- |
| `workspace.*`    | user only                       |
| `agent.*`        | user only                       |
| `instance.state` | user **and** worker (see below) |

`instance.state` is the **control channel**. The user drives the thread with it:

* set `pending` to hand the thread to the worker,
* set `completed` to stop the worker cleanly,
* reset `failed → pending` to retry a failed thread.

The worker writes `instance.state` only to the values `active` and `failed`. It **never** writes `completed`
(that is user-set), and it never writes a resume pointer or an error block to the wire. Failure detail lives
locally and surfaces server-side as a `thread_failed` item in the worker activity thread — not on this
envelope.

## Agent settings resolution

Each agent setting resolves through three tiers, with the most specific winning **per field**:

1. the thread's own `agent.*` on this envelope,
2. the worker's `agents.<type>.*` in `config.yaml`,
3. the codebase default.

Only `permissions` has a non-empty codebase default (`approval`); `executable` falls back to `claude` for
`claude_code` and `codex` for `codex`. `permissions` controls the approval gate: `autonomous` runs without a
gate, while `approval` routes sensitive actions to an interactive prompt that a human answers from the web UI,
denying the action if no human is reachable. See [Configuration](/worker/configuration) for where the worker
config tier is set.

## Wire ↔ local mapping

The worker mirrors the envelope into a local `thread.yaml` on the worker host, but with a **different** field
layout. The two must not be conflated:

| Wire (envelope)  | Local (`thread.yaml`)  |
| ---------------- | ---------------------- |
| `agent.type`     | `workspace.agent_type` |
| `instance.state` | `agent.state`          |

The local `thread.yaml` keeps the agent type under `workspace.agent_type` and the state under `agent.state`,
and it additionally holds two **local-only** fields that never appear on the wire:

* `agent.agent_session_id` — the SDK resume pointer used to reattach the agent after a restart.
* `agent.error` — the `{code, message}` detail of a local failure.

Because `agent_session_id` and the error block are local-only, a reader inspecting the wire envelope will
never see them there; thread failures surface to other clients as a `thread_failed` activity item, and the
local detail is what the worker uses to recover. See
[Filesystem Layout](/worker/session-agent-harness/filesystem) for the full local layout and
[Worker Session Object](/worker/session-agent-harness/worker-object) for the activity thread.

## See also

* [Handoff](/worker/session-agent-harness/thread-handoff)
* [Thread Items](/worker/session-agent-harness/thread-items)
* [Filesystem Layout](/worker/session-agent-harness/filesystem)
* [Worker Session Object](/worker/session-agent-harness/worker-object)
* [`upload_session_object`](/blob-types/workflow/operations/upload-session-object)
