An agent’s provider is read-only once set: its sessions, linked accounts
and native memory all belong to that harness, so switching it would
strand them. Run the same setup on another harness by forking the agent.
The lock moved here from the agent’s bundle
(SPEC_AGENT_BUNDLE_FORMAT_V0_3_2026_10_05.md §3.1). An empty stored
provider can still be set once.
Infer a provider slug from a model name prefix.
Only maps prefixes that correspond to a registered provider slug.
Callers must still validate the result via providers::get_provider.
Returns (agent_row_id, newly_inserted). In the consolidated model the
definition row IS the agent, so the “stub instance” that makes a defined
agent visible in My Agents is that same row carrying an instance_name;
instance_create folds into it. newly_inserted = false when the row
already carries a name (the stub already existed); callers use this to
avoid broadcasting agents:changed on no-op calls. The returned id is
the row’s id, which is what instance_create reports back.
Core logic for the agent.define command, shared by the WebSocket RPC
handler and the HTTP service dispatch (("agent", "define") in service.rs).
Persist system_prompt and env content blobs for a freshly created or
updated agent definition. Errors are logged but not propagated — the
definition row is already committed and the caller has already published
agents:changed, so a content-write failure must not abort the response.
Rejects a non-empty model_vendor_base_url unless provider_id’s
ProviderConfig declares support for redirection via
base_url_env_var. Pure — no I/O — so it’s directly unit-testable
without the async Store/Broker harness agent_define_core needs.
An empty base_url is always fine — that’s “use the harness’s default
vendor endpoint,” never rejected regardless of provider.