Skip to main content
These are the items a Job Session Object carries: every agent step the worker observes becomes one thread item, posted via post_session_thread_item. The content array carries a brief human-readable rendering; metadata carries the structured detail.

General shape

content[].type and metadata.type are in different scopes — content-item shape vs. event shape — and don’t collide. Consumers (renderers, exporters, the future native widget) dispatch on metadata.type. The presence of metadata.type is the distinguishing mark vs. plain user-posted items, which have no metadata.type set.

Item types (per-thread)

text

The agent’s user-facing text output.
A text item carries no metadata beyond type — the full output is content[0].text. If an item exceeds the budget, the generic truncation path excerpts content[0].text and sets metadata.truncated (see the truncation rules below); there is no separate full-text copy for text items.

tool_call

The agent invoked a tool. Posted before the result so live tailing shows the call landing.

tool_result

The tool’s result for the matching invocation_id.
A tool_result carries no tool name — renderers fold a tool_call / tool_result pair by invocation_id and take the name from the tool_call. The brief is → <first output line> (N lines).

thinking

Internal reasoning blocks (Claude Code). The brief content.text shows the first ~120 characters; the full reasoning is in metadata.text.

status

Progress / status pings (e.g. “Compacting context”). Carries a machine-readable token.

turn_end

Marker that the agent has finished its current turn. Useful for renderers to draw a turn separator and for monitoring.
input_tokens is the total input the model processed this turn (cached + uncached); input_tokens_cached is the portion served from the prompt cache. Both adapters (Claude Code and Codex) report the same breakdown.

pending_prompt and pending_prompt_resolved

Used for the interactive-prompt round-trip. See Interactive prompts.

artifacts_published

Posted once per turn on a thread whose envelope carries an artifacts block (see Job Session Object → Artifacts) — even when nothing was published, so an empty outbox is visible rather than indistinguishable from the feature being off. Posted after the outbox is walked and every publishable file in it is uploaded, immediately before that turn’s turn_end item — so anything reacting to turn_end always sees a complete artifact set.
Both lists are capped at 100 entries because a thread item is capped at 350 KB and a large outbox would otherwise overflow it; counts always reports the true totals regardless of the cap. skipped[].reason is one of binary, too_large, invalid_alias, unreadable, or upload_failed. A source that resolves outside work_folder is checked twice, at two different times, because the outbox is a directory inside the agent’s own writable workspace and the worker cannot assume it stays put for the length of a turn:
  • Caught at activation, before the first turn runs — the worker sets its in-memory artifacts spec to None instead of proceeding, and publishing is off for the thread’s entire current activation. No artifacts_published item is posted at all, for any turn, until the thread is next re-activated with a corrected source. An outbox_invalid warning lands in the worker’s log instead.
  • Caught at publish time — the activation-time check passed against the filesystem as it stood then, but the outbox path is re-resolved live on every publish, and the agent had the whole turn to rewrite it (for example, replacing the outbox directory with a symlink pointing outside work_folder). When that re-resolution lands outside the workspace, the artifacts_published item for that turn is posted, with aliases: [], skipped: [], and metadata.error set to the resolution failure.
Neither check is redundant with the other: the second one exists precisely because the first can’t see what happens during the turn it’s meant to guard, and a use-time re-check is the correct design for a directory an untrusted agent can rewrite. This item is an audit record, not the discovery mechanism. Find published artifacts by listing the session with prefix=<artifacts.prefix>, exactly as any other client would — never by reading this item.

policy_applied

Posted once before each turn on a guarded thread, naming the rule set in force. Doubles as an enforcement acknowledgement: its presence is what tells a reader this turn actually ran gated, rather than against a worker version too old to know about policy at all.
rules lists every rule in evaluation order — the worker’s own first, then the thread’s own — each tagged origin: worker | thread.

policy_denied

Posted once for every tool call a guarded policy denied.
detail is the denied call’s command or path, whichever the matched rule’s effect uses; when neither is populated — a codex file-change call, for instance, which carries no path at all (see Job Session Object → Policy and guarded permissions) — it falls back to the tool or call name instead.

Self-filter

Every item the worker posts carries the worker’s user_id (because BlobHub stamps the calling user on every post). The worker filters out items where user_id == self.user_id when consuming a thread — they are echoes of its own emissions and must not be re-fed to the agent. This has a documented consequence: a human posting from the same user account is also filtered. See Reference.

350 KB truncation

The worker enforces a 350 KB budget (350_000 bytes) on each serialized thread item and truncates large payloads before posting.
After truncation, a posted item looks like:
The local_log field is relative to the profile’s own jobs/ directory. The full untruncated payload is always recoverable from the worker’s local disk.

See also