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

# Output policy

> Control SubAgent narration, tool activity, decision results, provider reasoning, and metadata disclosure.

Output policy changes which events and metadata Harnest publishes. It does not
change prompts or model responses.

## Choose SubAgent narration

| Value   | Behavior                                                        |
| ------- | --------------------------------------------------------------- |
| `False` | Hide provisional SubAgent narration; the default                |
| `True`  | Publish that narration to neutral transports and the playground |

```python theme={null}
from harnest import lifecycle
from harnest.output import AgentMetadataMode, OutputPolicy


@lifecycle.output_policy
def output_policy():
    return OutputPolicy(subagent_messages=True)
```

## Choose provider reasoning

Provider-exposed reasoning is private by default. Opt in only when every caller
may receive prompt-derived details and provisional internal narration:

```python theme={null}
@lifecycle.output_policy
def output_policy():
    return OutputPolicy(thinking=True)
```

This controls readable ADK thought parts and LangGraph reasoning content. It
does not enable model reasoning or expose provider signatures.

## Choose tool activity

Tool execution remains enabled even when its public progress is hidden. Use a
suppressed tool-activity policy when callers should receive only agent
lifecycle updates and the canonical answer or structured result:

```python theme={null}
@lifecycle.output_policy
def output_policy():
    return OutputPolicy(tool_activity=False)
```

The default `True` value publishes neutral `tool_call` and `tool_result`
events. `False` removes both event types from JSON,
SSE, WebSocket, local, and playground output. A2A already keeps tool arguments
and results private.

## Choose decision results

Results from `context.decisions.evaluate(...)` are private by default. Keep
`OutputPolicy(decision_results=False)` to hide Jev or another decision provider's
judgments while still using them for routing, tools, and LLM input.

To expose them separately from the final answer:

```python lifecycle/output.py theme={null}
from harnest import lifecycle
from harnest.output import OutputPolicy


@lifecycle.output_policy
def output_policy():
    return OutputPolicy(decision_results=True)
```

Each completed evaluation adds a `decision_result` item to public `output`.
Its `value` contains decision/provider versions, typed answers, the policy
outcome, duration, and a safe error category when evaluation falls back.
Request state, question instructions, and raw provider payloads are excluded.
Private graph-to-model inputs are not presented as user turns when restoring
conversation history in ADK or LangGraph.
SSE and WebSocket use `response.decision_result`; streaming A2A tasks use
`metadata.harnest.type: decision_result`. Playground displays an expandable
Decision panel. Streamed results appear when the backend next yields an event.

This controls decision events, not arbitrary data copied into a final response
or tool result. Keep decision fields out of your final output schema if they
should remain internal. It does not redact LLM text, remove internal graph
state/checkpoints, or retroactively delete previously disclosed results.
Opted-in decision events are retained in durable completion output when
checkpoint storage is configured. Standalone `Decisions.evaluate(...)` calls
outside the context facade do not emit public events.

See the [Jev example](/docs/harnest/build/models-and-libraries/typed-decisions/jev)
for a decision followed by an LLM reply.

## Choose agent metadata

Harnest emits one centralized `agent_metadata` event for metadata reported by
ADK or LangGraph. The default `AgentMetadataMode.NORMALIZED` mode exposes the
portable fields that are present: model, provider, finish reason, and exact
input, output, and total token counts. Counts are never estimated.

Use `AgentMetadataMode.SUPPRESS` when callers must not receive any
model/provider metadata:

```python theme={null}
@lifecycle.output_policy
def output_policy():
    return OutputPolicy(agent_metadata=AgentMetadataMode.SUPPRESS)
```

Suppression removes per-call `agent_metadata` events and the aggregate `usage`
derived from them. Suppressed metadata is not added to durable completion
snapshots.

Use raw mode when an authorized caller needs the native framework payload:

```python theme={null}
from harnest import lifecycle
from harnest.output import AgentMetadataMode, OutputPolicy


@lifecycle.output_policy
def output_policy():
    return OutputPolicy(agent_metadata=AgentMetadataMode.RAW)
```

Raw mode keeps the normalized fields and adds `raw`. ADK uses its native
`LlmResponse` metadata field names. LangGraph namespaces `stream_metadata`,
`response_metadata`, `usage_metadata`, and `additional_kwargs` so fields do not
collide. The primary message content and Harnest-owned event state are not
copied. Native metadata can still contain provider-defined content-like values,
including reasoning annotations in `additional_kwargs`.

<Warning>
  Native metadata can contain provider identifiers, diagnostic details, and
  other sensitive or high-cardinality values. Raw values cross every enabled
  neutral transport and may be persisted by a remote A2A task store. Enable raw
  mode only for callers authorized to receive that provider payload. Harnest
  never copies raw metadata into its playground trace history.
</Warning>

When Harnest owns checkpoint storage, it durably retains the normalized
completion snapshot so another replica can return the same per-call metadata,
aggregate usage, caller metadata, and structured result. Raw metadata remains
ephemeral by default, even when it is exposed live. Persist it only with the
separate opt-in:

```python theme={null}
@lifecycle.output_policy
def output_policy():
    return OutputPolicy(
        agent_metadata=AgentMetadataMode.RAW,
        persist_raw_agent_metadata=True,
    )
```

Durable completion snapshots are JSON-validated and limited to 4 MiB. They use
the configured checkpoint backend's retention behavior. PostgreSQL retains the
snapshot with the run; Redis applies the configured checkpoint TTL.

<Warning>
  Persisted raw metadata remains available for the lifetime of the durable run.
  Enable `persist_raw_agent_metadata` only when storage, retention, and caller
  authorization are appropriate for the native provider payload.
</Warning>

## What remains visible

| Event                             |             Default | Suppression control                         |
| --------------------------------- | ------------------: | ------------------------------------------- |
| Root-agent messages               |             Visible | Always visible                              |
| Tool calls and results            |             Visible | `tool_activity=False`                       |
| Final answer or structured result |             Visible | Always visible                              |
| Intermediate SubAgent narration   |              Hidden | `subagent_messages=False`                   |
| Decision results                  |              Hidden | `decision_results=False`                    |
| Provider-exposed reasoning        |              Hidden | `thinking=False`                            |
| Model/provider metadata and usage | Visible, normalized | `agent_metadata=AgentMetadataMode.SUPPRESS` |

<Warning>
  Intermediate narration may be provisional and later revised. Use
  `True` only when your product deliberately presents progress.
</Warning>

All arguments are keyword-only. Binary controls require `True` or `False`, and
`agent_metadata` requires `AgentMetadataMode`; string policy values are not
accepted. When moving an older project to this contract, run
`harnest upgrade AGENT_DIR --apply` to rewrite released string and positional
forms.

## Scope

| Property      | Rule                                              |
| ------------- | ------------------------------------------------- |
| Factory       | Synchronous and zero-argument                     |
| Allowed       | Zero or one per root agent                        |
| Frameworks    | Managed and advanced roots through neutral routes |
| Native routes | Framework-owned                                   |
| Tests         | Does not change authored fixtures                 |
