SDK tutorial · 6 of 10

Own output and activity

Conversation semantics should remain in Heddle while your host decides where streamed text, status, trace evidence, and final results appear.

Customization depth

Level 3 · Host-owned presentation

Hosting depth

One process owns the turn

Documentation status

Runnable reference

Assumptions

  • The product owns an output sink or local UI but does not yet need remote replay.
  • One process stays alive for the entire turn.
  • The UI can consume semantic activities without parsing console text.
Heddle owns
  • Emit assistant-stream, lifecycle, tool, approval, progress, and compaction activities.
  • Provide a generic text host with compact and verbose rendering modes.
  • Return structured artifacts, tool results, failures, and trace location after settlement.
Your product owns
  • Own the output destination, UI state, tool presentation, telemetry, notifications, and result application.
  • Decide which activity is user-visible and which evidence remains operational detail.
  • Add the hosted run layer before supporting request detachment or client reconnection.

Start with the generic text host

createConversationTextHost handles assistant stream deltas, compact activity lines, trace labels, compaction status, and the final turn summary:

TypeScript
import { createConversationTextHost } from '@roackb2/heddle'

const textHost = createConversationTextHost({
  activity: 'status',
  trace: 'off',
  compaction: 'status',
  output: (text) => process.stdout.write('[agent] ' + text),
})

const result = await engine.turns.submit({
  sessionId,
  prompt,
  host: textHost.host,
})

textHost.renderTurnResult(result)

Each output category supports the relevant combination of:

  • off — suppress the category;
  • status — compact human-readable status;
  • verbose — serialized payload details where supported.

Use a custom writer for a terminal view, local application panel, log collector, or webhook buffer. Do not parse the resulting text back into product state.

Consume semantic activity directly

When the product has its own presentation model, provide host callbacks:

TypeScript
import {
  HeddleEventType,
  type ConversationEngineHost,
} from '@roackb2/heddle'

const host: ConversationEngineHost = {
  events: {
    onActivity(activity) {
      if (activity.type === HeddleEventType.assistantStream) {
        renderAssistantDelta(activity.text)
        return
      }

      renderAgentActivity(activity)
    },
  },
  compaction: {
    onStatus(status) {
      renderCompactionStatus(status)
    },
  },
  trace: {
    onEvent(event) {
      recordOperationalEvidence(event)
    },
  },
}

Import HeddleEventType instead of duplicating string literals. Treat semantic activity as a discriminated contract, and keep raw trace events as lower-level evidence rather than the primary UI model.

Finish with the structured result

After the turn settles, use result.summary, result.failure, result.artifacts, and result.toolResults to render durable product state. The text host is intentionally generic; product-specific success messages should be layered on structured fields.

Lifecycle limit

This pattern assumes the caller and process remain connected to the turn. A webhook destination does not by itself create replay, cancellation, or reconnection semantics. When request and subscription lifetimes differ, add a host-long-lived ConversationRunService and expose it through the product's transport.

Canonical sources