Commit Graph
7 Commits
Author SHA1 Message Date
Marc-André Bombeck 1acd41eb58 fix(ai): keep tools on the forced final round and narrow reasoning-dialect learning
The Coach loop's final round sent toolChoice "none" without the tool
definitions. With tool_use/tool_result blocks in the history Anthropic
refuses such a request (400), and OpenAI Chat Completions refuses
tool_choice without tools, so every budget, time, cap and no-progress stop
ended in an error on those wires. The final round now carries the tools
with toolChoice "none", which every wire accepts, thinking included.

Codex kept reasoning items as provider state even when the request asked
for no encrypted reasoning; replaying such an item under store:false is a
400. Items are kept only when the request included
reasoning.encrypted_content and the item carries it.

The reasoning dialect cache learned from any 4xx that mentioned
reasoning or thinking (auth, rate limits, model names such as
deepseek-reasoner) and from replay errors, and kept the downgrade for the
life of the process. Learning now requires an HTTP 400 that refuses a
sent parameter, ignores replay errors and the model name, and a learned
downgrade expires after an hour.
2026-10-06 21:50:19 +02:00
Marc-André Bombeck c7d659eb11 fix(coach): ask the capability before deferring a memory refresh, and pin test fixtures to the new quiet time 2026-10-06 21:02:41 +02:00
Marc-André Bombeck ae5cee331e feat(coach): resolve the turn's thinking depth from the person and the operator
The chat route hands the turn the level resolveCoachTurnReasoningLevel
returns: the stored Coach preference, absent read as medium, after the
operator's switch and cap. The turn still applies the operator-paid
ceiling once its chain is known. The controls loader no longer throws
when the request cache cannot serve it; it fails closed to off. The
admin guard pins the route to the resolver.
2026-10-06 19:57:04 +02:00
Marc-André Bombeck 3f7a16316f chore(ai): drop an unused reasoning default 2026-10-06 19:54:42 +02:00
Marc-André Bombeck 74e68745c1 feat(ai): resolve the reasoning level against the operator's controls
Add resolveReasoning, the one decision for how hard a call may think:
the operator's switch, then the provider's ability, then the requested
level (the person's Coach preference, or a background job's own level),
then the operator's cap, then a medium ceiling when the operator pays.
The answer names the layer that decided it.

loadReasoningControls reads the two app_settings columns, memoised per
request, read afresh in background runs, and fails closed to "off".
loadCoachReasoningState resolves the published Coach state from the
memoised provider presence; resolveJobReasoning resolves one background
call and never fails it.

A guard proves the switch and the cap over the whole input space and
requires every call that carries reasoning to take it from the
resolver.
2026-10-06 19:44:51 +02:00
Marc-André Bombeck 8d23c79ef5 feat(ai): add the reasoning support matrix and a per-endpoint dialect cache
reasoningSupport() reads what each provider can do with reasoning from the provider type and model name: effort, summaries, live summaries, state that must travel between tool rounds, and whether off is a real off. Unknown names are treated optimistically.

The dialect cache records what an endpoint refused (keyed by provider, endpoint and model) so a client retries with the lesser request once and never pays the refused round-trip again. Downgrades are marked on the wide event with meta keys only.
2026-10-06 19:26:28 +02:00
Marc-André Bombeck 210becb28b feat(coach): lay down the reasoning, trail and memory contract
Add the shared contract the reasoning release builds on, with no change
to how a turn runs today.

- Schema: operator reasoning controls on app_settings (0376), the
  encrypted trail text on coach_messages (0377), and source,
  last_used_at and source_message_id on coach_facts (0378).
- Provider contract: CompletionParams.reasoning and onReasoning,
  CompletionResult.reasoning and providerState, AiMessage.providerState.
- Reasoning levels, thinking budgets and the background-job table in
  src/lib/ai/reasoning/levels.ts; coachPrefsJson.reasoning, absent
  meaning medium, salvaged field by field like every other pref.
- Stream: activity, memoryNote and planProposal frames, done.stop and
  done.withheldResults, result.interim, a compare chart kind, eight
  table refs, three more clarification kinds with an assumed choice,
  the change_assumption chip, and assumptions, activity, stop,
  memoryNote and planProposal on the provenance. Zod mirror, type
  parity and the native-client key rule cover every new frame.
- Chat request: memoryDecision and planDecision, parsed and not yet
  acted on.
- Stub contracts for the memory functions and the activity recorder,
  inert until their implementations land.
- Backup, restore and rotation carry the new columns; a fact's message
  reference is resolved against the restored messages like its
  conversation reference.
- Message keys for the trail, memory, plans, assumptions, replies,
  interim tables, comparison charts and reasoning settings in all seven
  locales, each reachable from a catalog and checked both ways.
2026-10-06 18:39:50 +02:00