docs(config): describe the model list refresh exception

The Pi extension provider baseline section stated that every later
provider registration is a no-op, naming session_start as an example.
That is now the exact case that is allowed, so the shipped configuration
reference contradicted the behavior. docs/config.md is in the package
files allowlist, so this text reaches users.

Describe what stays frozen, the two conditions under which a known
provider's model list refresh is applied, and why function-valued fields
are always rejected. Record the accepted trade-offs: catalogs are shared
daemon-wide state with last-registration-wins, and a model entry's own
baseUrl/headers take precedence over the provider-level values. Split
the log behavior out and note that ignored mutations are de-duplicated
per provider while applied refreshes are always logged.

Also correct the reload-behavior bullet: a restart is needed to add or
remove a provider or change its connection settings, but not for a known
provider refreshing only its model list.
This commit is contained in:
Federico Jaramillo Martinez
2026-07-26 14:14:30 +02:00
parent 531ccf7b2a
commit 36d67262ec
2 changed files with 72 additions and 14 deletions
+52 -11
View File
@@ -177,7 +177,7 @@
<li><code>pathAccess</code>: applies on the next request; existing file views may need a browser refresh.</li>
<li><code>uploads.defaultFolder</code>: applies to newly opened Files upload dialogs and new direct drag/drop batches after config/workspace refresh.</li>
<li><code>plugins</code>: reload the browser tab after changing PI WEB plugin enablement.</li>
<li>Pi package install/remove/update: not a PI WEB config key; after a mutation, type <code>/reload</code> in each idle PI WEB session on the target machine to refresh ordinary Pi resources such as extensions, skills, prompt templates, themes, and context/system prompt files. Reload the browser page separately for PI WEB browser plugin changes. If a global Pi extension adds, removes, or changes a provider, manually restart <code>pi-web-sessiond.service</code>; <code>/reload</code> cannot change the startup provider baseline. See <a href="#pi-extension-provider-baseline">Pi extension provider baseline</a>.</li>
<li>Pi package install/remove/update: not a PI WEB config key; after a mutation, type <code>/reload</code> in each idle PI WEB session on the target machine to refresh ordinary Pi resources such as extensions, skills, prompt templates, themes, and context/system prompt files. Reload the browser page separately for PI WEB browser plugin changes. If a global Pi extension adds or removes a provider, or changes a provider's connection settings, manually restart <code>pi-web-sessiond.service</code>; <code>/reload</code> cannot change the startup provider baseline. A known provider refreshing only its own model list is applied without a restart. See <a href="#pi-extension-provider-baseline">Pi extension provider baseline</a>.</li>
<li><code>shortcuts</code>: saved settings apply in the browser after config refresh/save.</li>
</ul>
</section>
@@ -628,18 +628,59 @@
providers from the active agent directory's <code>models.json</code>.
</p>
<p>
After startup capture, every later Pi extension provider registration, native registration, and
unregistration is a no-op, regardless of source or provider ID. This includes global extensions replayed
while sessions load, project extensions attempting to add or replace a provider, same-ID replacement or
unregistration, lifecycle callbacks such as <code>session_start</code>, and <code>/reload</code>. The
captured provider stays unchanged while non-provider Pi extension features continue to load and reload
normally.
After startup capture, a provider's connection settings are fixed for the daemon lifetime. Later
attempts to add a provider, replace an existing provider's configuration, register a native provider, or
unregister a provider are no-ops, regardless of source or provider ID. This includes project extensions
attempting to add or replace a provider, lifecycle callbacks such as <code>session_start</code>, and
<code>/reload</code>. Non-provider Pi extension features continue to load and reload normally.
</p>
<h3>Model list refresh for a known provider</h3>
<p>
One narrow update is applied after startup: a provider captured in the baseline may refresh
<strong>its own model list</strong>. Extensions that fetch an updated catalog typically re-send their
complete provider configuration, so PI WEB compares the incoming registration against the recorded
baseline and applies it only when both hold:
</p>
<ul>
<li>the provider ID is already in the startup baseline, and</li>
<li>
every field except the model list is unchanged — <code>name</code>, <code>baseUrl</code>,
<code>apiKey</code>, <code>api</code>, <code>streamSimple</code>, <code>headers</code>,
<code>authHeader</code>, <code>oauth</code>, and <code>refreshModels</code>.
</li>
</ul>
<p>
Anything else stays a no-op, including a provider that was not in the baseline and a known provider
whose credentials, base URL, or API surface differ from startup. Function-valued fields cannot be
compared by value, so a registration that supplies a new <code>streamSimple</code>,
<code>refreshModels</code>, or <code>oauth</code> implementation is treated as a change and ignored.
</p>
<p>
Ignored mutations are written to the session-daemon log once per operation and provider ID. The log entry
contains no provider configuration or credentials, and PI WEB does not show a session warning or
notification. This prevents accidental provider, configuration, or credential contamination between
projects; it is not a security boundary because Pi extensions remain trusted daemon code.
An applied refresh becomes the new comparison point, so a provider can refresh repeatedly. Re-sending an
unchanged model list is a replay rather than an update and is ignored. Refreshed models are visible to
sessions immediately; no restart and no network request is involved, because the extension has already
produced the catalog.
</p>
<p>
Model lists are shared daemon-wide state. If extensions in two workspaces register different model lists
for the same provider ID, the last registration wins. A model entry may also carry its own
<code>baseUrl</code> and <code>headers</code>, which take precedence over the provider-level values for
that model, so an accepted refresh can change where requests for those models are sent. Both are
accepted trade-offs: a catalog is treated as a property of the provider rather than of the project, and
Pi extensions are trusted daemon code.
</p>
<h3>Provider decisions in the daemon log</h3>
<p>
Ignored mutations are written to the session-daemon log once per operation and provider ID, so a
replaying extension cannot flood the log. Applied model list refreshes are logged every time, with the
resulting model count, because each one changes shared runtime state. Neither entry contains provider
configuration or credentials, and PI WEB does not show a session warning or notification.
</p>
<p>
This prevents accidental provider, configuration, or credential contamination between projects; it is
not a security boundary because Pi extensions remain trusted daemon code.
</p>
<p>
Configure providers before the daemon starts: use the active agent directory's