crouter
SDK

Migration

Replace the removed generate and local APIs with the Crouter client.

Migration from generate() and local()

Phase 1.

This is a hard cut. generate(), local(), and the Environment interface are deleted in the same release that adds Crouter. There is no coexistence period, no deprecated re-export, and no compatibility shim. Both packages are pre-1.0 and the callers are countable.

Pin your old version or migrate; there is no third option.

Before and after

// before
import { generate, local } from '@north-light/crouter-sdk';
import { z } from 'zod';

const schema = z.object({ name: z.string(), version: z.string() });

const result = await generate({
  prompt: 'Read package.json and report its name and version.',
  schema,
  env: local(),
  cwd: '/path/to/repo',
  profile: 'my-app',
  kind: 'general',
  model: 'anthropic/strong',
  deadline: '10m',
  signal,
});

if (result.kind === 'result') console.log(result.value.name);
else if (result.kind === 'declined') console.warn(result.reason);
else console.error(result.reason, result.detail);
// after
import Crouter from '@north-light/crouter-sdk';
import { z } from 'zod';

const client = new Crouter();

const run = await client.nodes.parse(
  {
    prompt: 'Read package.json and report its name and version.',
    output_schema: z.object({ name: z.string(), version: z.string() }),
    cwd: '/path/to/repo',
    profile: 'my-app',
    kind: 'general',
    model: 'anthropic/strong',
    deadline: '10m',
  },
  { signal },
);

if (run.kind === 'result') console.log(run.output_parsed.name);
else if (run.reason === 'declined') console.warn(run.declined?.reason);
else console.error(run.reason, run.detail);

What moved where

| Before | After | |---|---| | generate({ … }) | client.nodes.parse({ … }) | | local() / env: local() | new Crouter() — the client is the connection | | local({ autostart: false }) | new Crouter({ autostart: false }) | | env.daemon() + new CrtrClient(…) | new Crouter(env.connection()) — see Docker environment | | client.ensureProfile('x') | client.profiles.ensure('x') | | schema | output_schema | | signal in the params object | signal in the second argument, the per-request options | | onEvent | client.nodes.stream()Streaming | | the Environment interface | deleted; nothing implements it |

The result union changed

generate() returned a three-way union of its own invention. parse() returns the settled NodeOutcome the daemon already defines — the same union the wire has, not a translation of it.

| generate() | parse() | |---|---| | { kind: 'result', value, nodeId, reportPath } | { kind: 'result', output_parsed, structured_result, final_report_path, … } | | { kind: 'declined', reason, nodeId } | { kind: 'failure', reason: 'declined', declined: { reason, code, retryable } \| null, … } | | { kind: 'failure', reason, detail, nodeId } | { kind: 'failure', reason, detail, … } |

A decline is now a failure with reason: 'declined', so narrow on run.reason === 'declined' rather than run.kind === 'declined'. It carries more than the old shape did: the agent's own code and a retryable flag.

value became output_parsed, and reportPath became final_report_path.

Every NodeOutcomeDTO carries the wire field node_id.

signal actually cancels now

generate() raced an abort against a promise it could not cancel. parse() passes the signal to fetch, so aborting stops the in-flight request and raises APIUserAbortError.

Aborting still stops your client waiting, not the run. Call client.nodes.cancel(id) to stop the agent, or give it a deadline so the daemon stops it for you.

Progress reporting

onEvent emitted three notifications: node created, report pushed, settled. Two of its three are available in phase 1 without it:

  • The node is created when client.nodes.create() returns — you hold the node.
  • Reports are available from client.nodes.reports.list(id).
  • Settlement is the return of waitForOutcome.

Streaming is strictly more capable than onEvent was: the three events it emitted are three of the events the stream carries, alongside assistant text deltas and tool calls.

CrtrClient is no longer yours to construct

The SDK does not re-export CrtrClient — the raw client is an implementation detail of Crouter. If you were constructing one to reach a route the SDK does not wrap, use client.request() instead; see Resource map.