Relaxes the provider policy from 'global config only' to 'global sources':
providers registered by agent-dir (global) extensions are learned once at
daemon startup and allowed on the shared runtime; project-extension
registrations are still rejected with a session warning. Global extensions
load identically for every session, so their providers are daemon-consistent
and cannot leak project state (#76).
- Shim now allows allowlisted ids through and also covers Pi 0.81's native
provider path (registerNativeProvider), closing a bypass.
- Startup learning step loads only global extensions against a scratch cwd
and diffs the runtime's registered provider ids.
- Bumps @earendil-works/* dev/peer ranges to >=0.81.1 <0.82; adapts to the
Agent.streamFn -> streamFunction rename.
- Docs, changeset, unit and acceptance tests updated (global-extension allow
path, late re-registration a la pi-tensorx, native provider rule).
Unit tests for the policy shim (swallowed registrations, no-op
unregister, untouched global providers, rejection wording) and
acceptance tests wired as sessiond wires production: load-time
rejections surface as session warnings while extension tools and
commands keep working, late registrations are broadcast to active
sessions' notification inboxes, colliding provider ids across
workspaces cannot affect each other, and a project-level models.json
does not alter the shared runtime.
PI WEB only supports globally configured providers (Pi built-ins,
agent-dir models.json, environment credentials). A daemon-wide shim on
the shared ModelRuntime swallows extension registerProvider calls and
makes unregisterProvider a no-op, so one workspace's extensions can no
longer corrupt the provider set of concurrent sessions (issue #76).
Rejections during a services load surface as session warnings through
the existing diagnostics pipeline; late registrations from session
event handlers broadcast a notification to active sessions. Everything
else extensions register keeps working.
Requires manual restart of pi-web-sessiond.service (daemon wiring changed).
Replace AuthStorage.inMemory / ModelRegistry.create|inMemory across all test
and support code with the pi-ai InMemoryCredentialStore + async
ModelRuntime.create({ credentials }). Add shared test-runtime seams
(createTestModelRuntime, testModelRuntime, seedCredential) in testSupport.ts
and thread modelRuntime into fakeRuntime and every PiSessionService
construction (now a required dependency). Rework the anthropic subscription
warning tests onto a temp auth.json seam read via readStoredCredential, and
the auth-loss warning test onto a live credential store + runtime refresh.
Make getLoginProviderOptions synchronous and fix associated await/lint sites.
npm run verify green (typecheck + lint + knip + 1390 tests).
Pass modelRuntime to createAgentSessionServices instead of authStorage +
modelRegistry; carry ModelRuntime on PiAgentSession; make modelRuntime a
required PiSessionService dependency (sessiond already injects auth.runtime).
Switch anthropicSubscriptionWarning to readStoredCredential, and rederive
model reads (availableModels/setModel/syncCurrentModelAuthWarning) via the
runtime (getAvailableSnapshot/getModel/hasConfiguredAuth). sessiond.ts and
piSessionService.ts now typecheck; only slice-5 test/support files remain.
Reimplement OAuthLoginFlowService against the pi-ai AuthInteraction
({ signal?, prompt(AuthPrompt), notify(AuthEvent) }) contract instead of
the removed OAuthLoginCallbacks shape, and drive login via
runtime.login(providerId, "oauth", interaction). start() now takes a
ModelRuntime instead of authStorage, resolving the authService.ts line-83
error. AuthPrompt text/secret/manual_code/select map onto the existing
web-UI prompt/select flow state; auth_url/device_code map onto the auth
field; info/progress append to progress. Per-prompt AuthPrompt.signal now
cancels just that pending request without ending the flow.
Slice 3 of the issue-62 authStorage migration relay.
Rederive login/logout provider options from runtime.getProviders() +
listCredentials() + getProviderAuthStatus() instead of the removed
authStorage.getOAuthProviders()/list()/get() + getAll()/
getProviderDisplayName() surface (Pi 0.80.8+).
- Replace the AuthProviderModelRegistry structural interface with a
runtime-shaped AuthProviderRuntime (getProviders/listCredentials/
getProviderAuthStatus); a real ModelRuntime satisfies it.
- Make getLoginProviderOptions/getLogoutProviderOptions async to match
the await call sites already in authService.ts.
- OAuth-capable providers = auth.oauth present; api-key providers =
auth.apiKey present, preserving OAUTH_ONLY_PROVIDERS /
isApiKeyLoginProvider logic. Display names from Provider.name.
- Update the test double to the new runtime shape.
Slice 2 of the authStorage migration relay. tsc: 31 -> 28 errors
(remaining are cross-slice: slices 3/4/5).
Move AuthService off the removed AuthStorage / ModelRegistry.create surface
onto the async ModelRuntime API:
- AuthService.create({ agentDir | runtime }) async factory wrapping
ModelRuntime.create({ authPath, modelsPath }); createModelRuntimeForAgentDir
replaces createModelRegistryForAgentDir.
- saveApiKey -> runtime.login(providerId, "api_key", nonInteractive) so the
key is persisted through the runtime credential store.
- logoutProvider -> runtime.logout; refreshAuthState -> await runtime.refresh().
- startOAuthLogin now passes the runtime into OAuthLoginFlowService.start.
- authProviders/requireOAuthLoginProvider became async around getLogin/Logout
provider options.
- sessiond.ts: async createRuntime, AuthService.create, pass modelRuntime to
PiSessionService; sessionDaemonStartup awaits createRuntime.
Cross-slice: authProviderOptions (2), oauthLoginFlowService (3), and
piSessionService (4) still expose the old ModelRegistry shape, so the tree does
not fully typecheck yet. Session-daemon path changed -> manual sessiond restart
needed once the migration lands.
Show a pinned banner at the top of the session view with resource and
runtime diagnostics (skills, prompts, themes, extension load errors) plus
the Anthropic subscription-auth billing notice, recomputed live from the
current runtime so they stay accurate across browser reloads.
Warnings carry an optional dismiss capability; the Anthropic notice is
dismissable and durably suppressed through pi's own anthropicExtraUsage
warning setting. Also fixes the testing-guide skill frontmatter so it
loads.
Seed the in-flight partial assistant message (text, thinking, and
in-progress tool calls) when opening or reconnecting to a session that is
mid-stream, then continue streaming live deltas on top of it. Replaces the
blocking "Catching up..." placeholder and the end-of-turn transcript reload.
Server stamps every per-session UI event with a monotonic seq at the
SessionEventHub publish choke point and exposes
GET /sessions/:sessionId/stream-snapshot returning { seq, partial }. The
client fetches the snapshot on join, seeds the normalized partial into the
in-memory transcript (never the history cache), and applies buffered/live
events using the seq watermark for exactly-once delivery.
The snapshot is a progressive enhancement: a 404 from an older remote
pi-web or a not-yet-restarted session daemon falls back to an empty seed
(seq 0, drops nothing), so sessions still open and stream normally. The
stream-snapshot route is registered in the federation allowlist for
remote-machine proxying.