- Return String from the sanitize helpers instead of Cow: every call
site feeds the result into json!, which allocates anyway, so the
borrowed fast path only cost extra branches and Cow-variant tests.
- Route all toolUse/toolResult construction through private
tool_use_block/tool_result_block constructors that own the sanitize
calls, so the toolUse/toolResult pairing invariant is enforced by
construction rather than by call-site discipline.
- Drop a test assertion the type system already guarantees (encoding
takes &Request, so it cannot mutate the input) and assert wiring
tests against the sanitize helpers instead of re-pinning the exact
replacement literals in a second file.
No wire-format changes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The steering dock on run pages now starts collapsed, staying out of
the way until the operator opens it. A run that is interrupted and
waiting for steering still forces the dock open.
While collapsed, the whole dock header is a click target that expands
it. Clicks on buttons in the bar (Interrupt, the chevron) keep their
own behavior, and the chevron remains the keyboard/assistive-tech
toggle. The interview dock shares the shell, so it gets the same
click-to-open behavior.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A fork carries a checkpoint from its source run, but its first
run.created event contains only a sandbox plan. Resume previously tried
to reconnect that planned sandbox and failed because no instance
exists. Now a fork resume with a Planned sandbox record builds a fresh
sandbox instead; later fork resumes still reconnect the ready instance,
and a same-run resume with an uninitialized sandbox still fails the
precondition check.
Also consolidates the test module's three near-identical InitOptions
literals into a shared test_init_options helper.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot review flagged two backward-compatibility breaks with events
persisted by pre-model-keyed releases; both are stored data that can
never be rewritten, so accept the old shapes on read:
- FailoverProps: original_provider/original_model/attempt are Option
again with serde defaults. New events always set them; failover events
recorded before model-keyed fallbacks lack them. Restores the
historical-event test.
- RunModelSettings: temporary custom deserializer accepts the legacy
flat-array fallbacks shape inside stored run.created events, keying
the chain under the requested model name when one is set. Remove once
pre-0.311 run logs are out of the support window.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot review flagged that a failover event's from route can be a
candidate that failed during activation and never served traffic. That
is intentional — events chain (one event's to is the next one's from)
so the stream records every candidate tried, with the error explaining
why each was abandoned. Document it at the emit site.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Consolidation pass over the fallback feature, no intended behavior
changes beyond noted validation and event-shape cleanups:
- Unify the two parallel notice types: FallbackPlanNotice is gone;
ModelFallbackNotice now owns the runtime NoNearbyReasoningLevel case
and the shared ChainEmpty wording. Notices emit through a new
Emitter::notice_scoped with their own level, and each distinct notice
is emitted once per run instead of on every LLM call.
- Move canonical_model_id onto Catalog so chain keys are written and
read through one function; reject provider-qualified fallback keys,
which could never match at dispatch and were silently dead config.
- Type FallbackTarget as ProviderId/ModelId, removing repeated
ProviderId::new re-wrapping at every use site.
- Derive FallbackPlan's current route from a position index instead of
storing current/requested_controls copies; advance() no longer has
unreachable None branches.
- Bundle the agent invocation's live state (session, bridge, lease,
forwarder, accounting) into LiveAgentInvocation; failover_agent_session
drops from 21 parameters to 7 and the six copies of the
abort/discard/classify teardown collapse into two methods.
- Share one route_request builder between one_shot and its failover
loop; complete_one_shot_request takes the request by value instead of
deep-cloning the message payload per call.
- Event::Failover carries FailoverProps directly; the props' original
route and attempt fields are now required, and reasoning efforts are
typed ReasoningEffort instead of strings.
- Reuse RunModelSettings/RunModelControls in fabro-api via
with_replacement, add the missing controls property to the OpenAPI
schema, regenerate the TS client, and add the type-identity/JSON
parity test.
- Smaller cleanups: ReasoningEffort::closest_supported uses enum
discriminants; ModelFallbackPolicy gains len(); resolve_model_fallbacks
takes a provider slice; duplicate-target filtering lives only in the
resolver.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>