Crux
GuidesAdvanced

Headless Calls

Use adapter codecs, prepare/step/finish handles, or transport callbacks when you want to own the provider wire call.

Sometimes you want Crux's prompt, tool, and safety pipeline but need to make the provider HTTP call yourself: a proxy that signs requests, a recorder that captures traffic, a queue that schedules calls, or a test harness that replays fixtures. Crux supports this without forking the pipeline. At the lightest level, every first-party adapter exports codecs that translate between resolved prompts and provider wire formats:

const params = toParams(resolvedPrompt, {
  model: "gpt-4o",
  settings: { temperature: 0.2 },
});

const response = await client.chat.completions.create(params);
const facts = fromResponse(response);

From there, Crux adapters have four execution rungs. They all share the same prompt resolution, tool policy, validation, safety, memory, and observability pipeline; they only differ in who makes the provider call.

RungYou ownCrux ownsUse it when
CodecsControl flow and provider wire callsTranslation onlyYou already have a custom loop and only need request/response conversion
HandleControl flow and provider wire callsTool protocol, approvals, validation directives, typed tool context, memory, cost, and observabilityYou need a sans-I/O state machine for tests, queues, or nonstandard HTTP clients
transportThe wire callbackThe full loop, including routing and timeoutsYou want Crux to orchestrate while a proxy, recorder, or custom client performs each request
ManagedNothingEverythingYou want the normal generate() or stream() path

Codecs

Every first-party generation adapter exports toParams() and fromResponse(), shown above. They are translation helpers only.

Use codecs when you own the loop. They do not execute tools, enforce approvals, validate toolsContext, retry structured output, write memory, or emit generation spans.

Handles

prepare() returns provider params for the next step without making a network call. Feed the provider response back into step(); Crux advances the same protocol engine used by managed generate().

const call = await adapter.prepare(editDraft, {
  model: "claude-sonnet-4-5-20250929",
  input: { instruction: "Fix the intro" },
});

const first = await client.messages.create(call.params);
const outcome = await call.step(first);

if (outcome.done) {
  console.log(outcome.result.text);
} else {
  const nextResponse = await client.messages.create(outcome.next.params);
  const result = await outcome.next.finish(nextResponse);
  console.log(result.text);
}

Handles are useful for fixture-driven tests and runtimes where the provider wire call must be scheduled or proxied outside Crux. finish(response) is step(response) plus an assertion that the call is complete; it throws CruxIncompleteCallError if the model requested another step.

The AI SDK adapter handle exposes one pending generateText() or generateObject() gateway call. SDK-thrown structured-output validation errors are covered by managed generate() and transport, not by the handle, because the public handle accepts completed SDK results rather than SDK exceptions.

Transport

transport is the BYO-wire mode for managed execution. Crux still drives the loop; your callback only performs the provider call for each step.

const result = await adapter.generate(editDraft, {
  model: "gpt-4o",
  input: { instruction: "Fix the intro" },
  transport: (params, info) => {
    audit.write({ stepIndex: info.stepIndex, modelId: info.modelId, params });
    return proxy.callProvider(params, { signal: info.signal });
  },
});

Because Crux initiates each step, transport preserves fallback/cascade routing, structured timeout budgets, tool execution, approvals, validation retry, memory capture, and observability.

stream() with transport is intentionally unsupported in this contract revision. Adapters reject it with CruxTransportStreamUnsupportedError instead of ignoring the callback.

Capability Matrix

FeatureCodecsHandletransportManaged
Prompts/context, skills (static), retrieval, flows, evals, compaction
Memory write / cost / execution observabilitymanual digest✅ auto✅ auto✅ auto
Tool loop, middleware, typed context, maxSteps/stopWhen❌ (user's loop)
Approval suspension/resume (managed)protocol helpers only
Validation retry✅ (directive; AI SDK structured validation SDK-throw path is managed+transport only)
Per-step/tool observability spans
fallback / cascade / enforced timeout budgets

The first-party equivalence tests compare managed generate(), hand-driven handles, and transport for each adapter. The AI SDK validation-retry exception above is covered by managed and transport equivalence because those paths own the SDK call and can observe SDK-thrown validation failures.

On this page