session_agent_harness is one of the job types the worker supports. A section
with this job_type attaches the worker to one BlobHub session, watches that session’s thread
session objects, and — when the user marks a thread as pending — spawns a coding agent
(Claude Code or Codex) inside the configured workspace and bridges items between the agent and
the thread.
The job type uses no new BlobHub backend endpoints — it speaks the existing session-object and
thread-item commands as the user whose API key the worker carries.
What a section does
For eachsession_agent_harness section in config.yaml, the worker:
- Claims a per-session
workerobject — the affinity lock and activity log; see Theworkersession object. - Discovers thread session objects in the session and runs the
handoff state machine for each — when a
user marks a thread
instance.state = "pending", the worker validates the workspace and spawns the configured coding agent. - Bridges items:
- Outbound — for each agent emission (text, tool call, tool result, thinking, status, turn
end, pending prompt), posts a thread item with brief text plus structured
metadata. See Thread items. - Inbound — coalesces other users’ thread items into the next agent turn.
- Outbound — for each agent emission (text, tool call, tool result, thinking, status, turn
end, pending prompt), posts a thread item with brief text plus structured
- Supports interactive prompts — when the agent pauses for input, posts a
pending_promptitem and resolves the user’s plain-text reply back to the agent. See Interactive prompts. - Resumes cleanly after restart — replays missed items, re-attaches the agent session, cancels stale prompts. See Recovery.
Identity
The worker acts on behalf of a single BlobHub user. Atlogin, the worker
validates the API key against GET /v1/users/me and records the returned user_id in
the profile’s own identity.yaml. Every API call the worker subsequently makes is attributed to
that user.
Recommendation: run the worker under a dedicated service-account user with its own API key.
If a human is logged in as the same user and posts to the same thread via the web UI, the worker
classifies those posts as its own and ignores them — see
Reference.
The worker also carries an instance_id, generated fresh on every start. The instance_id is
informational (logs, dashboard) and is not an affinity key.
Sections
Asession_agent_harness section targets exactly one BlobHub session via
(org_id, blob_id, revision_id, session_id) — session_id may be a uuid or a session alias, resolved once
at attach — and, optionally, one slice of that session’s object namespace (session_prefix). One worker
process can run any number of sections — each runs independently. Two sections targeting the same
(session, prefix) are refused with DUPLICATE_SECTION_TARGET.
Configuration reference (including a full working example):
Configuration. Namespace scoping and how the worker discovers work under a
prefix: Concepts.
The worker session object
When a section attaches to a session, the worker creates (or claims) a session object whose alias
is worker. Its value is a thread-typed session object whose metadata carries:
metadata.user.user_id— the affinity key. A different worker (differentuser_id) refuses to attach withSESSION_OWNED_BY_DIFFERENT_USER.metadata.instance.*— the currently running instance (id, version, started_at, last_seen_at, status). Informational only.
attached, thread_active,
thread_failed, detached, etc. They form a visible worker activity log in the session.
There is no blobhub-worker detach command in v1. To free a session, delete the worker session
object via the API: delete_session_object(session_id, alias="worker"). The running worker
observes the deletion and stops that section cleanly.
Full shape and rules: The worker session object.
Threads and the agent state machine
Inside a session, the user creates thread session objects (one per task the agent should drive). The user populates each thread’s metadata with a workspace + anagent block, then sets
instance.state = "pending" to hand it off:
artifacts block (publish a local output folder into the session namespace
each turn) and a policy block (the rule set a guarded thread is gated against). Both are covered in
full on Job Session Object.
The worker watches session events, picks up pending threads, validates the workspace, spawns
the coding agent inside work_folder, and drives instance.state through the canonical state
machine:
The worker writes only instance.state, and only the values active and failed. The user
owns workspace.* and agent.* and sets instance.state (pending to hand off, completed to
stop cleanly, reset failed → pending to retry). The agent resume pointer (agent_session_id) and
any error detail are local to the worker — they never appear on the wire envelope; a failure
surfaces server-side as a thread_failed item in the worker activity log.
Full object shape and field ownership:
Job Session Object. State-machine dynamics:
Handoff.
Items in / items out
While a thread isactive, two flows run in parallel:
- Inbound — the worker watches session events for
session_thread_item_posted{alias}. New user items are dropped through a self-filter (the worker’s own posts are ignored), coalesced into the next agent turn, and handed to the agent’s prompt. - Outbound — each agent emission (text segment, tool call, tool result, thinking, status,
turn-end, pending prompt) is translated into a thread item with brief text in
contentand structured detail inmetadata. The full untruncated payload is always written to a local log for recovery if the item is truncated to fit the 350 KB per-item budget.
Process model
session_agent_harness sections run in the same single worker process, on a single asyncio
event loop. The dashboard (Textual) is opt-in via --tui. By default the worker runs headless
with structured JSON logs to stderr, suitable for systemd or a container.
Single-instance enforcement is generic — see Filesystem layout.
See also
- Job Session Object — object shape, ownership, settings tiers.
- Configuration — full working example with one section.
- Filesystem Layout — per-section/per-thread on-disk state.
- Handoff — the lifecycle state-machine dynamics.
- Recovery — what survives restarts and what doesn’t.
- Reference — error codes and limitations.
- Thread session object reference

