SDK tutorial · 7 of 10

Consume turn results

Render and automate from structured result fields. Do not parse assistant prose or raw provider errors to decide retry, billing, or credential behavior.

Customization depth

Level 3 · Structured result handling

Hosting depth

Any conversation-engine host

Documentation status

Supported SDK boundary

Assumptions

  • The product needs deterministic behavior after a turn settles.
  • Provider diagnostics must not cross the public API boundary unchanged.
  • Product-specific response schemas will be projected from Heddle's host-facing result.
Heddle owns
  • Return the persisted summary, safe failure category, updated session, trace path, session artifacts, and current-turn tool results.
  • Normalize model failures without credentials or raw provider messages.
  • Distinguish quota exhaustion from retryable rate limiting.
Your product owns
  • Map Heddle outcomes and failure codes into product API errors, UI state, metrics, and retry policy.
  • Project only intended public fields across a transport boundary.
  • Keep trace files and internal tool payloads behind product authorization.

Read the host-facing result

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

console.log(result.outcome)
console.log(result.summary)
console.log(result.failure)
console.log(result.session.id)
console.log(result.traceFile)
console.log(result.artifacts.map((artifact) => artifact.id))
console.log(result.toolResults.map((entry) => entry.call.tool))

The fields have distinct responsibilities:

FieldUse it for
outcomeWhy runtime execution stopped.
summaryThe persisted assistant summary for the turn.
failureStable, safe model-failure classification.
sessionThe updated persisted conversation record.
traceFileOptional local path to lower-level evidence.
artifactsArtifacts currently associated with the session.
toolResultsCompleted calls from this turn, including input, result, duration, step, and timestamp.

Branch on failure codes, never prose

TypeScript
switch (result.failure?.code) {
  case 'authentication':
    throw new ProductModelCredentialError()
  case 'quota':
    throw new ProductModelQuotaError()
  case 'rate_limit':
    scheduleBoundedRetry()
    break
}

Current model failure codes are:

  • authentication — rejected or missing usable credentials;
  • permission — credential exists but cannot perform the request;
  • quota — billing/quota capacity is exhausted and is non-retryable without an external change;
  • rate_limit — transient throttling that may be retried with product policy;
  • request — invalid or unsupported provider request;
  • transport — network or provider transport failure;
  • empty_response — the model returned no usable response;
  • unknown — no safer stable classification was available.

failure never contains credentials or raw provider messages. Do not inspect summary for error strings; it is user-facing assistant content and can change independently.

Project a public result

When the turn crosses an API boundary, create an explicit product schema:

TypeScript
const publicResult = {
  outcome: result.outcome,
  message: result.summary,
  failureCode: result.failure?.code,
  artifacts: result.artifacts.map(({ id, kind, title }) => ({
    id,
    kind,
    title,
  })),
}

Do not send traceFile, unrestricted tool inputs/results, storage paths, or provider diagnostics to an untrusted client by default.

Use traces only for deeper evidence

Most product behavior should use turn-result fields and semantic activity. Reach for the raw trace when an authorized operator needs audit evidence, debugging, or custom analysis.

Canonical sources