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.
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.
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.
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
Noneinstead of proceeding, and publishing is off for the thread’s entire current activation. Noartifacts_publisheditem is posted at all, for any turn, until the thread is next re-activated with a correctedsource. Anoutbox_invalidwarning 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, theartifacts_publisheditem for that turn is posted, withaliases: [],skipped: [], andmetadata.errorset to the resolution failure.
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’suser_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.
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
- Job Session Object — the object these items belong to.
- Handoff
- Interactive
post_session_thread_itemlist_session_thread_items

