Skip to main content

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.

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 each session_agent_harness section in config.yaml, the worker:
  1. Claims a per-session worker object — the affinity lock and activity log; see The worker session object.
  2. 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.
  3. 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.
  4. Supports interactive prompts — when the agent pauses for input, posts a pending_prompt item and resolves the user’s plain-text reply back to the agent. See Interactive prompts.
  5. 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. At login, the worker validates the API key against GET /v1/users/me and records the returned user_id in ~/.blobhub-worker/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

A session_agent_harness section targets exactly one BlobHub session via (org_id, blob_id, revision_id, session_id). One worker process can run any number of sections — each runs independently. Two sections targeting the same session in the same config are refused with DUPLICATE_SECTION_TARGET. Configuration reference (including a full working example): Configuration.

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 (different user_id) refuses to attach with SESSION_OWNED_BY_DIFFERENT_USER.
  • metadata.instance.* — the currently running instance (id, version, started_at, last_seen_at, status). Informational only.
The worker also posts items into this thread as it operates — 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 + an agent block, then sets instance.state = "pending" to hand it off:
workspace:
  work_folder: /absolute/path/to/repo   # required
agent:
  type: claude_code                     # required (or codex)
instance:
  state: pending                        # the control channel
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 is active, 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 content and structured detail in metadata. 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.
Full item shapes: Thread items. Interactive prompts: Interactive prompts.

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