You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Loop hooks — prepareStep + activeTools (on CommonCallOptions; work in both generateText and streamChat whenever tools is present): prepareStep(ctx) runs before every model step, after automatic compaction, and may return { messages, activeTools, toolChoice, model } — messages becomes the base history for this and all following steps (doubling as a user-controlled compaction/rewrite hook, including system-prompt edits via the system-role message), while activeTools/toolChoice/model apply to that step only. A thrown prepareStep fails the call like any caller code — it is never swallowed. Static activeTools restricts which tools are sent every step (unknown names warn and are dropped; matching nothing fails open to the full list); prepareStep's activeTools overrides it. prepareStep is a plain call option on the free functions (no agent class to instantiate) and composes with the automatic compaction below — compaction runs first, prepareStep sees the result.
Budget stop conditions — totalTokensExceed / costExceeds (StopCondition factories, exported from root + /edge alongside stepCountIs / hasToolCall, now also exported): stop the loop once cumulative REAL usage or cost — all steps and sub-agents included — crosses a bound, OR-ed into stopWhen like any condition and evaluated at the step boundary (never mid-tool-batch). A budget stop never changes finishReason (the locked union is untouched); instead it sets providerMetadata.deuz.stoppedBy: 'totalTokensExceed' | 'costExceeds' on the result / finish part (GenerateTextResult.providerMetadata is a new additive field). costExceeds needs deps.priceProvider — without one it warns once and never fires. Token- and cost-budget bounds are first-class StopCondition factories here (alongside stepCountIs/hasToolCall), and stoppedBy reports which bound ended the loop.
Automatic layered compaction (compaction?: 'auto' | CompactionPolicy on CommonCallOptions; opt-in, off by default, active only inside the agentic loop): three cheapest-first layers — prune old tool results into [pruned N chars] stubs, prune old reasoning parts, summarize the oldest unprotected slice into one message — run before a step once estimated context fill crosses a threshold (default 92%), always leaving every system message, the first user message, the last message, and the last keepRecentSteps assistant turns untouched. History stays immutable and prefix-stable for prompt-cache hits; a failed summarize logs a warning and skips the layer instead of ending the call; token counts are a calibrated heuristic, not a real tokenizer. Streaming emits a new compactionStreamPart/UI part per layer that ran; buffered calls log it. Anthropic's native providerOptions.anthropic.context_management still works verbatim alongside this. This is automatic, layered, cache-aware context management from one opt-in flag — the alternative is to estimate tokens and prune inside a per-step hook yourself.
Sub-agents — agentTool (exported from root + /edge; AgentToolDef exported type): wraps a { model, tools, system, maxSteps, maxDepth, ... } definition into a Tool that runs a nested agentic loop and returns its final text — no new runtime. When the parent streams, the sub-agent's entire canonical stream forwards live as agentPath-tagged sub-agent parts, rather than surfacing only the final text. The parent's server-mode approveToolCall is inherited to every nesting depth as first-class behavior, so a sub-agent's own tool calls stay gated with no extra wiring. Usage folds into the parent total and is tagged with meta.agentPath; maxDepth (default 2) guards against runaway nesting; the parent signal propagates down. Client-mode approval inside a sub-agent isn't supported yet (needs durable suspend/resume — lands in 1.5); a gated sub-agent call with no inherited approver returns a clear is_error instead.