The merge of main kept two copies of testProviderCredentials in the
generated models-api.ts; regeneration is authoritative.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cumulative implement + simplify_fable diff recovered from the run's meta
branch (fabro/meta/01KY7YH7RYCJ1BDVTTP96ZA4HV, stage 006 diff.patch).
The run validated this tree clean: cargo nextest (7,007 passed), clippy,
fmt, TS client regen + typecheck, web tests (679 passed), docs check.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The crate reorganization renamed lib/crates/ to lib/apps|components|foundation/.
Git followed all modified files across the rename; the only conflict was the
newly added codec/cache.rs, now placed at lib/components/fabro-llm/src/codec/cache.rs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reuses run_multi_turn_cache_test — the same live cache verification the
anthropic, openai, and gemini routes already have. OpenRouter was the
one caching route with no live caller, which is exactly where the
missing-breakpoints bug hid: unit and wire tests prove we now send
cache_control, but only a live call proves OpenRouter forwards it to
Anthropic and cache reads actually appear.
Runs with: set -a && source .env && set +a && \
cargo nextest run -p fabro-llm --profile e2e --run-ignored only \
-E 'test(openrouter_claude_multi_turn_cache)'
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Anthropic prompt caching is opt-in per request: without explicit
ephemeral cache_control breakpoints in the body, no cache writes or
reads ever happen. The OpenAI-compatible codec never emitted them, so
every run on openrouter Claude models billed the full conversation at
the uncached input rate on every turn (0 cache tokens on the billing
page, confirmed by OpenRouter's activity portal).
- Add a `cache_control_breakpoints` model feature declaring that a
route only caches when the request marks the cacheable prefix; set it
on the builtin OpenRouter Claude rows. Catalog build rejects the flag
without `prompt_cache`.
- Teach the Chat Completions wire shape a parts-form content variant so
a message can carry the annotation; unmarked messages keep the
plain-string form for compatibility with strict servers.
- Mark the last system message (covers tools + system upstream) and the
second-to-last user turn, counting tool results as user turns —
mirroring the anthropic codec's placement so agent loops get
incremental cache hits.
- Extract the shared placement/opt-out policy into codec::cache and
refactor the anthropic codec onto it; anthropic wire snapshots are
unchanged.
- Honor `provider_options.<name>.auto_cache = false` as an opt-out and
consume the control key instead of merging it into the body.
- Mirror the new feature through settings (fabro-config), the OpenAPI
schema, and the generated TypeScript client.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Document the contract of read_last_file_routing_json (terminal JSON
extraction only; routing validation happens downstream), extract a
shared sandbox_with_file test helper, and drop the misleading
"standalone" wording from the fallback docs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Return 400 (not 500) for WorkflowError::ModelReference from run
creation, matching ModelSelection: an ambiguous model/provider token
is user input, not a server fault.
- Gate fabro-workflow's test_support module behind
cfg(any(test, feature = "test-support")) so the feature actually
controls exposure, per the repo's test-support boundary guidance.
Add the self dev-dependency so tests/it keeps compiling, and gate
the pipeline helpers that only test_support consumed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tag every LLM request in a run with an x-session-id header carrying the
run ID, so gateways that understand session tracing (e.g. OpenRouter
broadcast) can group a run's requests into one session.
Adds ExtraHeadersCredentialSource to fabro-auth: a CredentialSource
decorator that appends fixed headers to every resolved credential,
leaving operator-configured extra_headers untouched. The run pipeline
wraps its vault/env source with it, so agent stages, prompt stages,
hooks, and PR-content generation all pick up the header through the
existing extra_headers plumbing with no fabro-llm changes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>