Commit Graph
3 Commits
Author SHA1 Message Date
amosblomqvist da0cd558d0 practice slice 3B — the practice agents: partitioner, generator, grader
src/agent/subagents/practice.ts: createPracticeAgents(deps) with the
ExtractionModels mirror (per-stage engine overrides + engineFor +
providerKeyMissing + refuseIfStagesUnready, all three roles strong-tier).
Generator doctrine = the owner's problem-engineering framework verbatim
(minus Sources) + Part III wiring section per the green-lit amendments
(FULL SET input with minimal-use doctrine; degradation never in
meta-commentary). Partitioner: true-partition doctrine, validate via
chainCoverage + exactly-once + unknown-ids, error-back retries through
runSubmitAgent's new validate hook (ToolInputError → error tool result).
Generator: echo law (chains set === target). Grader: verdict union +
nonempty feedback (schema-enforced). renderChains/renderPropositions/
renderParams/renderPreviousProblems. BACKGROUND_MODEL_ROLES extended with
the practice trio (settings override symmetry). runSubmitAgent gains the
validate seam (null = accept). 16-test faux-engine suite incl. retry
paths, refusal gates, neverSubmitted.
2026-09-29 18:14:30 +03:00
amosblomqvist 66090ed769 Pre-send key guards: no orphan error-shell chats on first send
The owner's asymmetry find (interlude over the zero-keys wave): the
models-unconfigured case guards BEFORE send — no request, no chat
created, setup opens — but the key cases only reacted to the 503 after
POST /api/chats had already created the record, leaving orphan error-shell
chats in the sidebar. The guards now twin the models mechanism on BOTH
creation paths (home start(), /chat/new send()):

- Zero keys (useZeroKeys reuse): no provider key present -> skip creation
  and send entirely, open SetupDialog (stage 1 first-key screen).
- Strong-provider key missing (owner-approved extension): keys exist
  generally but the strong slot's pinned provider has none — the original
  deleted-key scenario. New pure strongKeyless(catalog) in setup-cta.tsx:
  slots.strong resolved pin + providers[] (present-key only, the catalog's
  own federation surface), false when unconfigured (the models guard owns
  that case) or catalog not loaded (the server 503 gate remains the belt).
  useStrongKeyless rides useModelCatalog, re-reading on the 'keys' AND
  'models' signals (a deleted key or a re-pinned slot flips it live).

Both guards run after the configured check, same
skip-everything-and-open-setup treatment; 'force' (setup's onDone replay)
bypasses both — completing setup implies a keyed provider. /chat/[id]
sessions keep the 503 revert-draft interception unchanged: those chats
legitimately exist.

Pins: strongKeyless fires on a keyless strong pin (keys elsewhere or
none), never fires with the pin's key present (incl. the keyPresent=false
belt), false on unset pin / unloaded catalog. Normal-path pins ride the
existing startDraftSession contract suite. Live-verified on :3001 under
LH_IGNORE_ENV_KEYS=1 with a file-key anthropic + openrouter-pinned strong:
both guards on both pages (setup opens stage-1 / stage-2 respectively,
draft preserved, 154 chats unchanged, zero sidebar entries), normal path
with a keyed strong provider still creates and lands the prompt redirect.
929/929, tsc/lint/build green.
2026-09-25 00:46:10 +03:00
amosblomqvist 1f777f770c Zero-keys wave: provider-key gate renders honestly everywhere
Over Platform's 66dbea9 key-presence seam, the five owner-locked tasks:

1. Zero-keys pill — components/setup-cta.tsx: pure hasProviderKey
   (classifyKeys reuse — tool keys like Deepgram never count), useZeroKeys
   (GET /api/keys, re-read on the new lh:settings-saved 'keys' signal),
   ZeroKeysPill (testid setup-cta-keys) taking PRECEDENCE over the models
   CTA on chat/home/new-session. SettingsSection gains 'keys'; keys-section
   dispatches after save AND delete.

2. Setup stage 1 is self-contained: provider picker (MenuShell +
   ProviderMenuList) + key paste + Save via PUT /api/keys/:id — the dialog
   STAYS MOUNTED; its own save dispatches the keys signal, the fetch effect
   re-runs on it, and the stage flips to the pickers on its own.
   onOpenKeys + setup-open-keys die. SetupKeysBody exported for SSR pins.

3. parseProviderKeyRefusal (chat.tsx, envelope-verbatim twin of
   parseModelsRefusal) joins the revert-draft-and-open-setup interception;
   activity-dock reads 'needs a provider key'; dispatch-card shows
   'Add a key' — both deep-link setup, which lands on stage 1 by itself.

4. Dictation trio: the token route's only 503 (missing Deepgram key)
   becomes the client-authored DICTATION_KEYLESS short line; the errored
   idle click clears the error and deep-links Settings -> Keys ->
   DEEPGRAM_API_KEY via the new open(section, row) seam (dialog landOnRow
   -> SettingsDialogBody initialRow -> KeysSection openRow follow-effect).

Live-verified on :3001 under LH_IGNORE_ENV_KEYS=1 (the fresh-user switch):
pill on all three surfaces + precedence over the CTA, stage-1 save ->
auto-advance with dialog mounted, real 503 send refusal reverting the
draft and opening setup, dictation 503 -> short line -> Settings lands on
the unfolded Deepgram row with the error cleared, keys-section save and
delete both flipping the pill live, and a fabricated refused research job
rendering the 'Add a key' dispatch card. 926/926, tsc/lint/build green.
2026-09-24 23:20:59 +03:00