Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Effectful-Tech/clanka/llms.txt

Use this file to discover all available pages before exploring further.

The OutputFormatter module turns the raw event stream produced by a Clanka agent into coloured, human-readable text. It ships one formatter — pretty — and a Muxer service for interleaving output from multiple concurrent agents.

Type

import type * as Stream from "effect/Stream"
import type { AgentFinished, Output } from "clanka/AgentOutput"

export type OutputFormatter = <E, R>(
  stream: Stream.Stream<Output, AgentFinished | E, R>,
) => Stream.Stream<string, Exclude<E, AgentFinished>, R>
An OutputFormatter is a Stream pipeline operator. It consumes a Stream<Output, AgentFinished | E, R> and returns a Stream<string, Exclude<E, AgentFinished>, R>. The AgentFinished error channel is consumed internally and rendered as a completion message.

pretty(options?)

The built-in formatter. Call it once to produce an OutputFormatter you can pipe your agent stream through.
import { OutputFormatter } from "clanka"

export const pretty = (options?: {
  readonly outputTruncation?: number | boolean | undefined
}): OutputFormatter
options.outputTruncation
number | boolean
Controls how many lines of script output are shown before a truncation notice is appended.
  • Omitted or false — no truncation (default).
  • true — truncate to 20 lines (the built-in default when enabled).
  • number — truncate to exactly that many lines.
When truncation fires, the formatter appends \n... (truncated, total N lines) to the output.
return
OutputFormatter
A stream operator Stream<Output, AgentFinished | E, R> → Stream<string, Exclude<E, AgentFinished>, R>.

Usage

import * as Stream from "effect/Stream"
import * as Effect from "effect/Effect"
import { OutputFormatter } from "clanka"
import { NodeRuntime } from "@effect/platform-node"
import * as Stdio from "effect/Stdio"

// pipe the agent stream through pretty() and write to stdout
const program = Effect.gen(function* () {
  const stdio = yield* Stdio.Stdio
  yield* agentStream.pipe(
    OutputFormatter.pretty({ outputTruncation: 50 }),
    Stream.run(stdio.stdout()),
  )
})

Effect.runPromise(program)
Pass the result of pretty() to layerMuxer when you need to merge output from several agents into one ordered stream.

Output event rendering

pretty maps each Output event tag to a styled string. The table below documents every tag and the text it produces.
TagRendered as
AgentStartBold green heading with agent ID, model/provider, and the prompt text.
ReasoningStartBold yellow “Thinking:” heading followed by a space.
ReasoningDeltaThe raw reasoning delta text (streamed inline).
ReasoningEndTwo newlines to close the reasoning block.
ScriptStartBold blue “Executing script” heading.
ScriptDeltaDimmed script source delta (streamed inline).
ScriptEndTwo newlines to close the script block.
ScriptOutputBold blue “Script output” heading followed by the (possibly truncated) output text in dim style.
ErrorRetryRed “Error: … Retrying…” message with a full Cause pretty-print below.
UsageCyan token-count summary: context / input / output.
SubagentStartBold magenta heading with subagent ID, model/provider, and dim prompt.
SubagentCompleteBold magenta heading with subagent ID and its completion summary.
SubagentPartMagenta “Subagent #N:” prefix prepended to the inner part’s formatted output.
AgentFinished (error channel)Bold green “Task complete:” heading followed by the summary.

Muxer service

Muxer multiplexes output from several concurrent agent streams into a single Stream<string>, serialising multi-line blocks so that reasoning and script sections from different agents are never interleaved mid-block.
import { OutputFormatter } from "clanka"
import * as Context from "effect/Context"
import * as Layer from "effect/Layer"
import type * as Stream from "effect/Stream"
import type * as Effect from "effect/Effect"

// Muxer is exported from clanka as OutputFormatter.Muxer
export class Muxer extends Context.Service<Muxer, {
  add<E, R>(
    agent: Stream.Stream<OutputFormatter.Output, OutputFormatter.AgentFinished | E, R>,
  ): Effect.Effect<void, never, R>
  readonly output: Stream.Stream<string>
}>()("clanka/OutputFormatter/Muxer") {}

// layerMuxer builds the Muxer layer from any OutputFormatter
export const layerMuxer: (formatter: OutputFormatter.OutputFormatter) => Layer.Layer<Muxer>
layerMuxer
(formatter: OutputFormatter) => Layer<Muxer>
Builds the Muxer layer from any OutputFormatter. Typically called with pretty(options).

Example

import * as Effect from "effect"
import * as Stream from "effect/Stream"
import * as Stdio from "effect/Stdio"
import { OutputFormatter } from "clanka"

const MuxerLive = OutputFormatter.layerMuxer(
  OutputFormatter.pretty({ outputTruncation: true })
)

const program = Effect.gen(function* () {
  const muxer = yield* OutputFormatter.Muxer
  const stdio = yield* Stdio.Stdio
  yield* muxer.add(agentStreamA)
  yield* muxer.add(agentStreamB)
  yield* muxer.output.pipe(Stream.run(stdio.stdout()))
}).pipe(Effect.provide(MuxerLive))
Muxer is a scoped resource. Agent streams are forked into the enclosing scope via Effect.forkIn, so they are automatically cleaned up when the scope closes.

Build docs developers (and LLMs) love