JournalDeveloper Tools

Field guide / 7

Stately Agent renamed reasoning to keep workflows portable

A one-word collision in Stately Agent's AI SDK v7 migration shows where provider-specific reasoning controls belong, and where they do not.

Sep 6, 20267By ISH Team
Stately Agent renamed reasoning to keep workflows portable
Advertisement

Stately Agent renamed reasoning to keep workflows portable

Stately Agent and Vercel's AI SDK both wanted the word reasoning, but they meant different things by it. The collision surfaced when Stately Agent 2.0.0-alpha.21 moved its AI SDK peer dependency to version 7 and renamed a boolean from reasoning to includeReasoning.

Stately's old field requested a reasoning field inside a structured-output envelope. AI SDK v7 uses reasoning for reasoning effort. Keeping both meanings under one property name would have caused a type conflict and broken the ability to pass a Stately request directly to AI SDK's generateText or streamText.

That small rename offers a practical rule for multi-provider systems: portable workflows should describe the result they need, while the host owns the controls for producing it.

One word was doing two jobs

Stately Agent can ask a model for structured output. Set includeReasoning: true, and the output envelope gains an optional reasoning string before the final result. This is an output-shape decision. It says what the workflow expects back.

AI SDK v7's reasoning option changes how a model runs. One provider may accept an effort enum, another a thinking-token budget, and a third may expose no equivalent control. That is execution policy.

This distinction follows the AI SDK's own settings model, which exposes shared controls while warning when a provider does not support one. The v7 planning issue also identifies top-level reasoning APIs as part of the major release. A provider-neutral wrapper therefore has to handle both the common surface and the places where implementations diverge.

The migration changes this:

export const triageTicket = createTextLogic({
  model: "careful",
  reasoning: true,
  prompt: ({ input }) => input.ticket,
});

to this:

export const triageTicket = createTextLogic({
  model: "careful",
  includeReasoning: true,
  prompt: ({ input }) => input.ticket,
});

includeReasoning names the outcome without borrowing a control word from the model layer.

A direct executor is worth protecting

The Stately release note identifies a less obvious constraint: AgentTextRequest should remain spread-compatible with AI SDK call options. Developers can hand raw generateText and streamText functions to a workflow as executors, with no adapter between them.

A boolean named reasoning no longer satisfies the SDK's option type. Renaming it keeps that direct path open.

Adapters are useful, but they collect responsibilities. Streaming chunks, tool selection, abort signals, usage data, and new upstream options all need translation. Every translation can lose information. Direct assignability gives advanced users a smaller integration surface, and it gives maintainers a compatibility property they can enforce in TypeScript.

The same principle appeared in our examination of served-model logging in benchmark systems: an abstraction is easier to trust when the underlying execution facts remain visible.

The workflow chooses a role; the host chooses the dialect

Stately's model map can attach settings to a named model reference:

const models = defineModels({
  quick: openai("gpt-5.4-mini"),
  deep: {
    model: openai("gpt-5.4"),
    settings: { reasoning: "xhigh" },
  },
});

The workflow asks for quick or deep. It does not store xhigh as though every provider understands that value. A different host can map deep to a model controlled by a thinking-token budget, or to one without a reasoning switch. The workflow file stays untouched.

This is a clean ownership boundary. API keys, endpoints, retries, and provider-specific settings already belong to the host. Reasoning effort belongs there too. A deployment using a compatible routing layer such as api.ish.chat can follow the same pattern: keep the workflow's intent stable, then resolve the model and its controls at deployment time.

createAiSdkExecutors also gained global or request-derived settings. They resolve from least to most specific: host defaults, model-reference settings, then request settings. Stately prevents those layers from setting model, prompt fields, tools, or toolChoice. A generic settings object cannot quietly replace the substance of a request.

Stately's model and provider guide shows why that separation pays off: the same executor boundary can accept AI SDK language models, OpenAI-compatible endpoints, or hand-written integrations. The workflow does not need to know which path the host chose.

A successful call can still break telemetry

Two usage changes in the migration deserve explicit tests. AI SDK v7 moved reasoningTokens under usage.outputTokenDetails and cached input under usage.inputTokenDetails.cacheReadTokens. Stately copies those values into the flat AgentUsage fields it aggregates while preserving the SDK's raw usage object.

Miss that mapping and requests still complete, but dashboards record zero reasoning or cache tokens. Cost reports then look cleaner than reality. Our analysis of cache-normalized agent costs shows why cached and uncached input should remain distinguishable.

AI SDK v7 also rejects role: "system" inside message arrays unless the caller opts in. Stately's adapter enables it because its messages are machine-authored server content, preserving the existing systemMessage() behavior. Type-checking alone will not prove either telemetry or message handling is correct.

A migration checklist for portable agent code

When an upstream AI SDK takes ownership of a common field name, inspect the whole seam:

  1. Separate output requests from execution policy. includeReasoning asks for a field; reasoning effort configures a model.
  2. Remove provider vocabulary from stored workflows. Resolve it through host-owned model aliases instead.
  3. Add a CI type test that assigns your request object to the upstream SDK's call type. It should fail when the contracts drift.
  4. Run usage fixtures containing both cached input and reasoning tokens through the adapter.
  5. Keep raw provider usage beside normalized totals. New fields should remain inspectable before your aggregate schema catches up.
  6. After a major SDK upgrade, exercise system messages, streaming, tools, and abort behavior in integration tests.

Stately Agent 2.0 is still alpha, and its documentation advises pinning an exact version. Its boundary is useful beyond this package, though. Provider portability does not require every model to expose the same controls. It requires a durable workflow vocabulary and a narrow place where the differences are translated.

#Stately Agent#AI SDK#agent workflows#API design#provider portability
Advertisement

Keep reading

Related stories

Browse the archive