Output

Output storage maintains agent memory across runs. Configure filtering to keep only relevant lines and tail to bound context size.

Basic Storage

Store the output of any state:

output:
  store: true

Output saved to .raili/<workflow>/outputs/<stateId>.md with run separators.

Additionally, Raili also writes a per-state “latest” file that contains only the most recent run’s filtered output. The latest file is created alongside the history file and uses the same base name with a .latest.md suffix (for example code.latest.md for code.md). On each run when output.store: true is configured, Raili appends the full run to the history file and overwrites the .latest.md file with the filtered content from the current run. If the configured filtering (markers/tail) yields an empty result, the .latest.md file is not created or is left unchanged.

Note: For group (sub-workflow) states the on-disk filename uses only the sub-state name (the final segment of the virtual state id). For example a sub-state with virtual id groupx.produce is stored as produce.md (no parent prefix). The latest file follows the same naming convention (e.g. produce.latest.md).

Marker-based Extraction

Use optional marker and marker_end strings to capture the portion of output to persist. Searches are case-insensitive but slicing preserves the original casing and spacing.

Rules:

YAML example (both markers):

output:
  store: true
  marker: "//SUMMARY//"
  marker_end: "//SUMMARY_END//"

This is useful for agent outputs that include clear delimiters surrounding the structured content to persist (for example a //SUMMARY// block). If you previously relied on an implicit "OUTPUT:" default, note that there is no longer a default marker — provide marker explicitly when you want marker-based extraction.

Tail (Keep Last N Lines)

Keep only the last N lines:

output:
  store: true
  tail: 100

Combined Filtering

Marker extraction (using marker and/or marker_end) and tail work together.

output:
  store: true
  marker: "SUMMARY:"
  marker_end: "SUMMARY_END:"
  tail: 200

Process: first extract according to the configured markers (see rules in “Marker-based Extraction”) → then apply tail to keep only the last N lines of the extracted content.

Agent Memory Strategy

Full history is always stored on disk for audit trail. Use use_latest to control how many of the most recent stored runs are injected into the agent prompt.

Learnings (opt-in): States may declare a teach: mapping to push lessons to agents from outputs or variables. By default learnings are stored globally at .raili/learnings/<agentId>.md so agents can accumulate knowledge across workflows. If a workflow-local file exists at .raili/<workflow>/learnings/<agentId>.md, both global and local learnings are merged when injected into the agent prompt; workflow-local lessons take precedence on conflicts and duplicates are removed. Merged learnings are injected into the agent prompt under ## Learnings from previous runs before execution. Use scope: workflow on a teach source to keep a lesson local to the current workflow; omitting scope (or using scope: global) writes to the global store. See documentation/states.md for teach examples and usage.

Validation note: Raili now validates teach: mappings at startup — every agent ID referenced in a state’s teach: block must exist in agent-registry.json. If a teach: entry references an unknown agent the loader will fail fast with a clear error message describing the offending state and agent ID. The runner also enforces this check before processing teach: during execution, and the raili teach CLI command performs the same validation and will error if the specified agent is not registered.

Note: when injecting learnings into agent prompts, source tags (e.g., [var:...], [manual], [output:state]) are removed; only the lesson bodies are included as bullet-prefixed items to reduce token usage and improve readability.

Interaction with approvals

When an approval is answered with a typed reason (FAILED), or when an approval resolver returns a structured result containing a reason, Raili mirrors that non-empty reason into context.vars using the key <STATE>_<OUTCOME> (uppercased). The Runner processes a state’s teach: mappings after approvals are handled and approval-exposed variables are available, so teach: can reference approval-produced variables (for example ${REVIEW_FAILED}) declared on the same state. This ensures learnings can be created directly from user-provided or resolver-produced approval reasons in the originating state.

Additionally, feedback resolvers that return structured objects with a metadata field have that metadata persisted under context.feedbacks (keyed by state id) and recorded in the state’s meta.feedback.metadata entry inside context.stateHistory. The feedback value itself is still mirrored into context.vars under the declared expose_var so notify commands and scripts can read it via $RAILI_VAR_<UPPERCASE>. Feedback metadata is intentionally not exported as environment variables; it is stored for audit and tooling purposes.

Common Patterns

Test state — capture failures

test:
  type: script
  script: npm-test
  output:
    store: true
    marker: "FAILURE_SUMMARY:"
    tail: 200

Build state — bounded logs

build:
  type: command
  command: npm run build
  output:
    store: true
    tail: 100

Agent state — full memory

code:
  type: agent
  agent: coder
  output:
    store: true

Analysis state — filtered output

analyze:
  type: agent
  agent: analyzer
  output:
    store: true
    marker: "SUMMARY:"
    tail: 150

Resetting Memory

Clear a state’s output when starting a new cycle:

start_cycle:
  type: engine
  reset_outputs:
    - code          # clear code's memory
    - test          # clear test's memory

When outputs are cleared via reset_outputs (or programmatically via clearAgentOutputs) Raili deletes both the history file (<state>.md) and the companion latest file (<state>.latest.md). This ensures no stale .latest.md remains to be injected into agent prompts or used by scripts.

This is useful when looping back to retry after failures.

Full History

The complete history file contains all runs with separators:

--- Run 2026-03-13T08:15:00Z ---
[output from first run]

--- Run 2026-03-13T08:20:30Z ---
[output from second run]

--- Run 2026-03-13T08:25:15Z ---
[output from third run]

On disk for audit trail. By default Raili injects all stored outputs into the agent prompt; use use_latest to limit how many recent runs are injected to bound context.

Run log JSONL

For longitudinal metrics, Raili also writes a compact JSONL run log to .raili/<workflow>/run-log.jsonl. Each line is a single JSON object summarizing the run (runId, declared input vars, state counts, loops, approvalFailures, terminalState, duration). This file is append-only and safe to read concurrently. Only input variables declared in the workflow with log: true are included in the vars object — all other inputs are excluded by default.

The canonical success indicator field in each run object is success (boolean). Older legacy successful field is no longer supported.

Token accounting (per-state)

In addition to run-level metrics, Raili records per-state token consumption when an agent emits a Copilot token-reporting line (for example: ↑ 256.9k (223.0k cached) • ↓ 9.7k). When detected, the runner attaches a meta.tokens object to the producing state’s history entry in .raili/<workflow>/context.json so tooling and UIs can attribute consumption to the exact state and run.

The recorded TokenUsage object contains numeric fields input, output, and optional cached, plus the original display strings input_display, output_display, and cached_display. When present, an AI Credits footer (for example AI Credits 0.72 (1h30m45s)) is parsed and merged into the same object as ai_display (original footer string), ai_credits (numeric credit amount), and ai_time (duration converted to total seconds). Numeric token fields are absolute integers (suffixes such as k/M and commas are parsed). Example entry:

{
  state: "analyze",
  enteredAt: "2026-03-16T12:00:00Z",
  meta: {
    tokens: {
      "input": 256900,
      "cached": 223000,
      "output": 9700,
      "input_display": "256.9k",
      "cached_display": "223.0k",
      "output_display": "9.7k",
      "ai_display": "AI Credits 0.72 (1h30m45s)",
      "ai_credits": 0.72,
      "ai_time": 5445
    }
  }
}

Token parsing is intentionally tolerant (accepts commas and `k`/`M` suffixes). See `documentation/states.md` for a brief note about agent token emission and where the data is persisted.