Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.trodo.ai/docs/llms.txt

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

Inside a wrapAgent callback, every withSpan anywhere in the call tree attaches to the active run via AsyncLocalStorage (Node) or contextvars (Python). You do not pass runId down through function arguments. Split your agent across as many files as you like.
// agent.js — entry point
import trodo from 'trodo-node';
import { triage } from './triage.js';
import { research } from './research.js';
import { write } from './write.js';

trodo.init({ siteId: process.env.TRODO_SITE_ID });

export async function support(question) {
  const { result } = await trodo.wrapAgent('support-agent', async (run) => {
    run.setInput({ question });

    const intent = await triage(question);       // no runId argument
    const facts  = await research(intent);        // no runId argument
    const answer = await write(question, facts);  // no runId argument

    run.setOutput({ answer });
    return answer;
  });
  return result;
}
// triage.js
import trodo from 'trodo-node';

export async function triage(question) {
  return trodo.withSpan({ kind: 'agent', name: 'triage' }, async (span) => {
    span.setInput({ question });
    // ...LLM call auto-captured as a child span
    return 'refund';
  });
}
// research.js, write.js — same pattern: import trodo, call withSpan, done.

Why this works

wrapAgent opens an AsyncLocalStorage (Node) / contextvars.Context (Python) that carries the active runId. Every withSpan reads that context and attaches to the same run. The context propagates across await boundaries, across imports, across helper functions — anywhere the async chain reaches.

When the context breaks

SituationWhat happensFix
worker_threads / ProcessPoolExecutor / new processContext doesn’t cross the boundaryCapture runId, pass it in, use joinRun
setImmediate / bare .then() that escapes the awaited treeSpan is droppedKeep work inside the awaited chain
HTTP to another serviceContext doesn’t cross the wireUse propagationHeaders() + middleware

See also