#7767 makes _agent_status_callback declare 'nonlocal _last_runtime_model_identity'
(reset on a fallback warning). #7760's test compiled the extracted callback alone
at module level, where a nonlocal has no binding -> SyntaxError, failing all six
relay tests. Wrap the callback in a scope that binds every nonlocal it declares.
Verified: 16/16 with and without the Agent on py3.11; still 16/16 against master's
callback (no nonlocal); reverting reproduces the 6 failures.
Editing a cron job with the model picker at "Default" sent provider: null
unconditionally, wiping provider-only pins (provider set, no model — the
pattern for self-hosted LLM endpoints). The combined picker cannot express
that state, so such jobs always rendered as "Default" and were silently
re-resolved to the bare global default on any save, breaking dispatch.
#4030 deliberately made clearing the picker mean "no overrides"; that
contract is preserved for explicit clears and model+provider jobs, while
provider-only pins survive saves where the picker was never touched.
- _cronProviderForClear(prevDetail, pickerTouched) decides the provider
submitted on clear: touched picker -> null; untouched + no model on the
job -> prev.provider.
- _cronModelPickerTouched is set by a change listener added when the picker
populates, and reset at openCronEdit/openCronCreate/duplicateCurrentCron.
- Tests: delegation + flag lifecycle locked in, and the helper driven
through node for provider-only / model-pinned / no-override / no-snapshot
inputs (6 passed).
Refs: #4030
The replay-authority predicate accepted any non-empty assistant message,
but a new turn copies the full transcript into INFLIGHT.messages. In an
established conversation that content is the previous turn's reply, so
`canSeed` came back true with both stale cursor halves preserved and the
server kept skipping the range that rebuilds the body.
Message-based evidence now counts only from a current `_live` assistant
positioned after the latest user boundary; a historical assistant row
fails closed to floor 0 and an empty event id.
Refs #7640 (blocker on #7651)
The #7535 ownership guard skipped the global model.base_url backfill for
two shapes that master handled:
- A `model` section with `base_url` but no `provider` declares no owner,
so `_providers_match_for_context("", <session provider>)` was false and
the endpoint was dropped, silently falling back to 256,000 instead of
querying the configured /v1/models (the profile-setup path writes this
shape).
- `opencode_go` and `opencode-go` are two spellings of the same built-in
provider, but `_canonical_context_provider` does not fold underscores,
so the owner session lost its URL.
Allow the backfill when no owner is declared, and compare both sides
through api.config._canonicalise_provider_id, which folds `_` to `-` while
keeping distinct `custom:*` slugs distinct. The #7535 case (a custom:ark
owner vs an opencode-go session) still resolves to an empty base_url.