crouter
SDKRecipes

Typed extraction

When an application needs one bounded structured answer with no follow-up, read this because nodes.parse returns a typed outcome union instead of hiding agent failures.

Typed extraction

Use this recipe to extract a small fact set from files in one working directory. Run it with npx tsx examples/guides/typed-extraction.ts /path/to/repo; it prints a package name and version or exits with an explicit agent outcome.

import Crouter from '@north-light/crouter-sdk';
import { resolve } from 'node:path';
import { z } from 'zod';

const client = new Crouter();
const cwd = resolve(process.argv[2] ?? process.cwd());

const extraction = await client.nodes.parse({
  prompt: 'Read package.json in this directory. Return its name and version.',
  cwd,
  root: true,
  root_lifecycle: 'terminal',
  deadline: '5m',
  output_schema: z.object({
    name: z.string(),
    version: z.string(),
  }),
});

if (extraction.kind === 'result') {
  console.log(`package ${extraction.output_parsed.name}@${extraction.output_parsed.version}`);
} else if (extraction.reason === 'declined') {
  console.error(`agent declined: ${extraction.declined?.reason ?? 'no reason supplied'}`);
  process.exitCode = 2;
} else {
  console.error(`agent failed: ${extraction.reason}`, extraction.detail);
  process.exitCode = 1;
}

nodes.parse() creates a terminal root, waits for it, and returns the structured result typed from the Zod schema. The schema belongs at the boundary where your application consumes the answer, not in a second parser after the run.

Observed against the local daemon in this checkout:

package @north-light/crouter@0.3.332

Handle every outcome arm. A result contains output_parsed; a decline says the agent could not honestly meet the schema; another failure carries its reason and detail. Transport and daemon failures still throw, so let those reach your normal application error handling.

This is the right shape for bounded work: one prompt, one answer, and no future message. If a user or external event must return to the same agent later, use a resident node instead. See nodes and the canvas for why an SDK node has durable identity, and lifecycle and wakes for the terminal-versus-resident decision.