Summary
Audit of as type casts in packages/agents/src identified casts that are necessary but worth tracking for future cleanup when TypeScript or dependencies provide better alternatives.
Remaining casts to track
1. Zod v4 private API access — output.ts:59
const def = (schema as unknown as Record<string, unknown>)["_zod"] as
| { def: { element?: ZodType } }
| undefined;
Accesses Zod's private _zod.def.element to extract the element schema from z.array(). Zod v4 does not expose a public API for this. The guard fails safely (throws a descriptive error instructing users to use Output.array() explicitly), but the cast reaches into internals that may change in a Zod major version.
Action: Replace with a public Zod API if one becomes available. Tracked with a TODO comment in the code.
2. onFinish union cast — flow-agent.ts (generate + stream)
const configOnFinish = config.onFinish as
| ((event: { input: TInput; result: FlowAgentGenerateResult<TOutput>; duration: number }) => void | Promise<void>)
| undefined;
FlowAgentConfig is a discriminated union (WithOutput | WithoutOutput) where onFinish has different result types (FlowAgentGenerateResult<TOutput> vs FlowAgentGenerateResult<string>). TypeScript can't call a union of contravariant functions, and discriminant narrowing doesn't help because the result variable's type doesn't change with the narrowing. The cast is correct because the implementation signature uses TOutput = any.
Action: Could be eliminated by adding a shared onFinish to FlowAgentConfigBase with a base event type that both variants extend. Low priority — the cast is well-documented and safe.
3. PromiseLike vs Promise in stream results
BaseStreamResult fields (output, usage, finishReason) are typed as PromiseLike<T> (from AI SDK), which doesn't have .catch(). All suppression sites use .then(undefined, noop) with oxlint-disable comments. This is correct but noisy.
Action: Consider whether BaseStreamResult fields should be Promise<T> instead of PromiseLike<T>, or add a utility like suppressRejection(promiseLike).
Already fixed in this audit
- Removed unnecessary
as Record<string, Tool> / as SubAgents casts on empty object defaults (agent.ts)
- Removed unnecessary
as Logger narrowing replaced with log! (agent.ts)
- Removed vestigial
as unknown as StreamResult & { ok: true } and as unknown as BaseGenerateResult & { ok: true } double-casts in factory.ts — TS narrowing works correctly after !result.ok guard
- Removed
OnFinishHook type alias that was only used for the cast (cast still needed but inlined with explanation)
- Fixed
.catch() on PromiseLike in test files
- Added explanatory comment for Zod internals access
Summary
Audit of
astype casts inpackages/agents/srcidentified casts that are necessary but worth tracking for future cleanup when TypeScript or dependencies provide better alternatives.Remaining casts to track
1. Zod v4 private API access —
output.ts:59Accesses Zod's private
_zod.def.elementto extract the element schema fromz.array(). Zod v4 does not expose a public API for this. The guard fails safely (throws a descriptive error instructing users to useOutput.array()explicitly), but the cast reaches into internals that may change in a Zod major version.Action: Replace with a public Zod API if one becomes available. Tracked with a
TODOcomment in the code.2.
onFinishunion cast —flow-agent.ts(generate + stream)FlowAgentConfigis a discriminated union (WithOutput | WithoutOutput) whereonFinishhas different result types (FlowAgentGenerateResult<TOutput>vsFlowAgentGenerateResult<string>). TypeScript can't call a union of contravariant functions, and discriminant narrowing doesn't help because theresultvariable's type doesn't change with the narrowing. The cast is correct because the implementation signature usesTOutput = any.Action: Could be eliminated by adding a shared
onFinishtoFlowAgentConfigBasewith a base event type that both variants extend. Low priority — the cast is well-documented and safe.3.
PromiseLikevsPromisein stream resultsBaseStreamResultfields (output,usage,finishReason) are typed asPromiseLike<T>(from AI SDK), which doesn't have.catch(). All suppression sites use.then(undefined, noop)withoxlint-disablecomments. This is correct but noisy.Action: Consider whether
BaseStreamResultfields should bePromise<T>instead ofPromiseLike<T>, or add a utility likesuppressRejection(promiseLike).Already fixed in this audit
as Record<string, Tool>/as SubAgentscasts on empty object defaults (agent.ts)as Loggernarrowing replaced withlog!(agent.ts)as unknown as StreamResult & { ok: true }andas unknown as BaseGenerateResult & { ok: true }double-casts infactory.ts— TS narrowing works correctly after!result.okguardOnFinishHooktype alias that was only used for the cast (cast still needed but inlined with explanation).catch()onPromiseLikein test files