Prepared: 2026-07-16
Baseline: Piclaw 6f340fd65 on main
Current Earendil: 0.80.7
Target Earendil: 0.80.9
Scope: @earendil-works/pi-agent-core, @earendil-works/pi-ai, @earendil-works/pi-coding-agent, and all Piclaw-private provider/model extensions that register providers, inspect models, or issue auxiliary model requests
Upgrade Piclaw to Earendil 0.80.9 as one atomic compatibility PR, organized into separately testable commits. This is not a dependency-only update: Earendil 0.80.8 replaced the SDK model/auth architecture with async ModelRuntime, and 0.80.9 adds Kimi K3 native deferred-tool loading plus smaller provider and session fixes.
The migration should make ModelRuntime Piclaw's process-wide model/auth service while retaining ModelRegistry only as the synchronous extension-compatibility facade. Piclaw must initialize the runtime cache-first with model network access disabled, start the web service without waiting for remote catalogs, and perform bounded background refreshes afterward.
The highest-value outcomes are:
- Canonical request auth, headers, provider environment, OAuth refresh, and model discovery.
- Dynamic provider catalogs without a process restart.
- Removal of direct dependencies on the now-private
AuthStorageand OAuth registry APIs. - Upgrade and consolidation of Piclaw's private provider/model extension layer, including Azure OpenAI/Foundry, GitHub Copilot, model-control, compaction, and semantic summarizers.
- Simplification of Piclaw's private GitHub Copilot model discovery patch.
- Immediate reliability fixes for long OpenAI Codex session IDs.
- Native deferred-tool loading for Kimi K3, matching Piclaw's existing
list_tools/activate_toolsdesign.
Estimated implementation: 6–8 engineering days plus package publication and a 24-hour microVM soak.
Risk: Medium–high, concentrated in authentication, custom provider composition/streaming, model selection, dynamic providers, auxiliary model requests, and cross-repository add-on compatibility.
Recommended delivery: A coordinated piclaw-addons compatibility PR and core Piclaw upgrade PR, with add-on packages published before the upgraded core is rolled out; no live deployment until both CI surfaces and microVM validation pass.
package.json currently pins:
{
"@earendil-works/pi-agent-core": "0.80.7",
"@earendil-works/pi-ai": "0.80.7",
"@earendil-works/pi-coding-agent": "0.80.7"
}Target all three packages at exactly 0.80.9 to prevent cross-package API skew.
An isolated worktree was upgraded to 0.80.9 and typechecked without modifying the live checkout.
Observed results:
- 11 TypeScript errors before tests.
- Focused tests failed at module load because removed exports were imported.
- 24 runtime source files reference affected model/auth APIs.
- 29 test files reference affected model/auth APIs.
- Upstream changed 193 files across
pi-ai,pi-agent-core, andpi-coding-agentbetween0.80.7and0.80.9.
Primary breakpoints:
AuthStorageis no longer exported.ModelRegistry.create()andModelRegistry.inMemory()are gone.- SDK/session services accept
modelRuntimeinstead ofauthStorage+modelRegistry. AgentSession.modelRegistrybecameAgentSession.modelRuntime.- Canonical request auth moved to
ModelRuntime.getAuth(). ModelRegistry.refresh()became asynchronous.getOAuthProvider()is no longer exported from@earendil-works/pi-ai/oauth.- Piclaw's login and provider-usage code reads raw credential structures directly.
The upgrade must include Piclaw-private extensions, not merely the central agent pool. The model-sensitive private surface is:
| Class | Files | Required migration |
|---|---|---|
| Azure provider registration and streaming | runtime/extensions/integrations/azure-openai.ts, runtime/src/extensions/azure-openai-api.ts, runtime/src/runtime/provider-bootstrap.ts |
Revalidate registerProvider composition, transient token refresh/re-registration, custom API names, custom streamSimple, headers, retries, model caps, and live model replacement |
| Azure extension variants | runtime/extensions/experimental/azure-openai.harness.ts, runtime/extensions/integrations/azure-openai-session/index.ts, runtime/extensions/integrations/azure-openai-images.ts |
Keep the harness behavior aligned with the primary integration; verify session hooks and image commands against refreshed models |
| Dynamic GitHub Copilot catalog | runtime/src/extensions/github-copilot-dynamic-models.ts |
Replace private OAuth lookup and boot registry patch with inherited auth plus refreshModels(context), or remove the overlay if upstream now covers the account catalog |
| Model control tools | runtime/src/extensions/model-control.ts |
Await async refresh, then enumerate/switch using a coherent post-refresh snapshot |
| Auxiliary model consumers | runtime/src/extensions/context-prune/summarizer.ts, runtime/extensions/integrations/context-mode.ts, runtime/src/extensions/smart-compaction/model-request.ts, runtime/src/extensions/smart-compaction/stream-complete.ts |
Route auth and completion through the shared ModelRuntime or an injected runtime-owned executor; preserve headers, env, base URL, retries, abort, and usage |
| Provider payload/response compatibility | runtime/src/extensions/provider-request-sanitizer.ts, runtime/src/extensions/provider-response-diagnostics.ts, runtime/src/extensions/llm-context-normalizer.ts, runtime/src/extensions/smart-compaction/remote-compaction.ts |
Re-run hook-order and payload-shape tests under the new Models dispatch path; do not infer capabilities from provider/model names |
| Model-aware prompt/profile behavior | runtime/src/extensions/local-lite-prompt-profile.ts, smart-compaction context/orchestrator files |
Verify behavior against refreshed canonical model objects and explicit model metadata |
| Extension registry and injection seam | runtime/src/extensions/index.ts, runtime/src/agent-pool/session.ts, runtime/src/extensions/context-mode-api.ts |
Inject runtime-owned model execution helpers into built-ins where possible; retain the extension compatibility facade only where external extension loading requires it |
There are 64 files under runtime/src/extensions and runtime/extensions importing Earendil packages; all must compile, while the table above identifies the subset requiring behavioral migration rather than type-only validation.
The separate /workspace/piclaw-addons repository also contains runtime extensions that depend on affected model/provider APIs. They require their own PR, package version bumps, catalog regeneration, publication, and installation validation.
| Add-on | Current sensitive behavior | Required migration |
|---|---|---|
@rcarmo/piclaw-addon-cheapskate |
registers and re-registers a custom rotating provider | update the API identifier/config to the 0.80.9 provider contract; prove merge/re-registration semantics, auth resolution, backend rotation, and in-flight safety |
@rcarmo/piclaw-addon-codex-conversion |
overrides openai-codex with a custom streamSimple |
verify partial built-in provider override preserves upstream OAuth, base URL, catalog filtering, model headers, and new Codex session-ID behavior; test images/web-search and WebSocket cleanup |
@rcarmo/piclaw-addon-delegate |
captures runtime catalog through synchronous modelRegistry.refresh() and getAvailable() |
await refresh or use a coherent cached snapshot; preserve fallback to CLI model discovery and abort behavior |
@rcarmo/piclaw-addon-skill-model-effort |
resolves models through the extension compatibility facade | await refresh before applying skill model metadata when freshness matters; preserve unique model matching and thinking-level mapping |
@rcarmo/piclaw-addon-smart-compaction |
calls getApiKeyAndHeaders() and completeSimple() directly |
migrate to the runtime-owned execution pattern available to external extensions, or add a small compatibility adapter that preserves full auth/header/env/base-URL behavior and is tested against 0.80.9 |
@rcarmo/piclaw-addon-autoresearch |
test fixture stubs registerProvider() |
update fixtures/types so standalone compatibility tests represent the new extension API |
Repository-wide prerequisites:
- update
piclaw-addonsdevelopment Earendil packages from0.80.3to0.80.9 - keep Pi core packages as peer dependencies in published add-ons; do not bundle them
- run standalone-import tests and
bunx tsc --noEmit - bump every functionally changed add-on version
- run
bun run sync:catalogandbun run check:catalog - preserve public GitHub Pages tarball installation URLs
The add-ons checkout already contains unrelated local modifications. Implementation must use a new worktree and must not absorb or overwrite those changes.
The existing unrelated dirty file must remain untouched:
runtime/extensions/viewers/editor/vendor/codemirror.meta.json
Use a dedicated worktree and verify the main checkout remains unchanged.
Piclaw should own one process-wide ModelRuntime and pass it to every main session, side session, compaction request, provider bootstrap, and control surface.
runtime startup
|
+-- ModelRuntime.create({ allowModelNetwork: false })
| |
| +-- auth.json credentials
| +-- models.json configuration
| +-- models-store.json cached catalogs
|
+-- AgentPool
| +-- main sessions
| +-- side sessions
| +-- compaction/summarization requests
| +-- model picker/control handlers
|
+-- background catalog refresh after listeners are ready
Use new ModelRegistry(modelRuntime) only where a synchronous extension-facing facade is required. New Piclaw runtime code should use ModelRuntime directly.
Allowed compatibility uses:
- externally loaded extensions that receive only
ExtensionContext.modelRegistry - extension tests explicitly exercising the compatibility facade
- temporary migration adapters with a documented deletion condition
Piclaw-owned built-in extensions should prefer an injected runtime-owned executor over assembling auth and calling completeSimple() directly. The executor should expose only the operation required by the extension (for example completeSimple(model, context, options)) and internally delegate to the shared ModelRuntime. This keeps provider auth, model headers, base URL, environment, OAuth refresh, and request transforms coherent without exposing the full process-wide runtime to every extension.
Disallowed new uses:
- process-wide model ownership through
ModelRegistry - direct request-auth assembly through
getApiKeyAndHeaders() - direct credential mutation through
AuthStorage - model catalog reads immediately after an unawaited
refresh()
Do not permit remote catalog refresh to block web startup. Upstream ModelRuntime.create() can wait up to 15 seconds when model networking is enabled.
Required policy:
- Create the runtime with
allowModelNetwork: false. - Load built-ins,
models.json, credentials, and cachedmodels-store.jsonduring startup. - Start web/Pushover channels.
- Schedule one bounded background refresh with network access enabled.
- Coalesce concurrent refreshes.
- Serve the last good snapshot while refresh is running or fails.
- Log provider-specific refresh errors without making the instance unhealthy.
- Allow an explicit forced refresh for diagnostics/administration.
Suggested defaults:
- background refresh timeout: 10–15 seconds total
- per-provider extension timeout: preserve the existing 3.5-second Copilot timeout unless evidence supports changing it
- refresh cadence: rely on upstream throttling where available; add a Piclaw coalescing guard so repeated model-picker requests do not start duplicate work
- startup: no network call on the critical path before the web listener is accepting requests
Estimate: 0.25 day
Risk: Low
-
Prune stale worktrees and create a dedicated branch/worktree, for example:
cd /workspace/piclaw git worktree prune git worktree add /workspace/piclaw-earendil-0809 -b upgrade/earendil-0.80.9 main -
Record baseline versions, current model configuration, and test results.
-
Preserve the unrelated CodeMirror metadata modification in the main checkout.
-
Add or retain test fixtures for:
- file-backed credentials
- in-memory credentials
- custom
models.json - cached dynamic catalogs
- Codex and Copilot OAuth-shaped credentials
-
Run baseline focused tests before changing dependencies.
- Dedicated worktree exists.
- Baseline test results are recorded in the PR description or an implementation note.
- Main checkout only contains the pre-existing protected modification.
Estimate: 0.75–1 day
Risk: High
Primary:
package.jsonbun.lockruntime/src/runtime.tsruntime/src/runtime/composition.tsruntime/src/runtime/bootstrap.tsruntime/src/agent-pool.tsruntime/src/agent-pool/contracts.tsruntime/src/agent-pool/service-factory.ts
Supporting tests:
- runtime composition/bootstrap tests
runtime/test/agent-pool/agent-pool.test.tsruntime/test/agent-pool/service-factory.test.ts
-
Update all Earendil packages together:
bun update \ @earendil-works/pi-agent-core@0.80.9 \ @earendil-works/pi-ai@0.80.9 \ @earendil-works/pi-coding-agent@0.80.9
-
Introduce async construction for the process-wide model runtime. Preferred shape:
createRuntimeCoreServices()becomes async, or- add an async factory that creates
ModelRuntimeand then constructsAgentPool.
Avoid a promise-valued runtime field or hidden lazy initialization inside request handlers.
-
Construct the runtime with canonical Piclaw paths and cache-first startup:
const modelRuntime = await ModelRuntime.create({ authPath: join(getAgentDir(), "auth.json"), modelsPath: join(getAgentDir(), "models.json"), modelsStorePath: join(getAgentDir(), "models-store.json"), allowModelNetwork: false, });
-
Inject
ModelRuntimeintoAgentPool. -
If extension compatibility is needed, construct one facade:
const modelRegistry = new ModelRegistry(modelRuntime);
-
Add an explicit background refresh coordinator with:
- a single in-flight promise
- timeout/abort support
- last-good snapshot behavior
- structured logs per provider
-
Start background refresh only after Piclaw's listener/channel startup succeeds.
Prefer a required modelRuntime dependency in the production AgentPool constructor or an async AgentPool.create() factory. Keep test-only convenience construction explicit; do not make production requests wait for first-use initialization.
- startup does not perform remote model fetches before the web listener starts
- multiple refresh requests coalesce
- refresh failure leaves cached models available
- offline startup works with and without
models-store.json - startup diagnostics expose malformed
models.jsonwithout crashing unrelated channels
- Dependencies resolve at
0.80.9. - Process-wide runtime is created exactly once.
- No production path constructs
AuthStorageor callsModelRegistry.create(). - Startup is cache-first and bounded.
Estimate: 1–1.5 days
Risk: High
runtime/src/agent-pool/session.tsruntime/src/agent-pool/session-manager.tsruntime/src/agent-pool/runtime-facade.tsruntime/src/agent-pool/side-prompt-runner.tsruntime/src/agent-pool/cache-stats.tsruntime/src/session-rotation.tsruntime/src/utils/model-auth.tsruntime/src/utils/model-utils.tsruntime/src/extensions/context-prune/summarizer.tsruntime/src/extensions/smart-compaction/model-request.tsruntime/src/agent-control/handlers/model.tsruntime/src/agent-control/handlers/info.tsruntime/src/extensions/model-control.ts
-
Replace session-service fields:
authStorage + modelRegistry -> modelRuntime -
Pass
modelRuntimetocreateAgentSessionFromServices()and every replacement runtime created for:- main sessions
- side sessions
- session rotation
- fork/resume/new-session flows
-
Replace
session.modelRegistryreads withsession.modelRuntime. -
Change request auth helpers to call:
const resolved = await modelRuntime.getAuth(model);
Forward:
resolved.auth.apiKeyresolved.auth.headers, excluding null/deleted entries where required by the downstream typeresolved.auth.baseUrlthrough the runtime/provider path rather than manually modifying modelsresolved.env
-
Prefer
modelRuntime.streamSimple()for Piclaw-owned auxiliary model calls where practical. This delegates auth/header/base-URL assembly to the canonical runtime and reduces duplicated logic. -
Preserve explicit retry/timeout settings used by compaction and side prompts.
-
Update model enumeration:
- synchronous display:
getAvailableSnapshot() - freshness-required path:
await getAvailable()orawait refresh()followed by a read - all models:
getModels() - exact lookup:
getModel(provider, id)
- synchronous display:
-
Update cache-stat model lookup to accept a minimal model lookup interface or
ModelRuntime, not the extension facade. -
Make all refresh-dependent control handlers async and await refresh before immediate reads.
-
Fix the legacy session-affinity compatibility hook:
- do not apply immediately after starting an async refresh
- apply after the refresh resolves
- preserve JSONC files without destructive rewriting
- document removal after the compatibility window ends
-
Remove obsolete automatic-recovery signatures for missing
getApiKeyAndHeaders()once all canonical paths are migrated, while retaining meaningful auth-failure patterns.
- main session creation and resume
- side-prompt auth forwarding
- compaction stream auth forwarding
- custom model headers and provider-scoped environment
- session rotation preserves provider/model/thinking selection
- model picker does not hydrate a cold session
- model refresh is awaited when a fresh result is required
- current-model restore uses the refreshed canonical model object
- legacy session-affinity compatibility applies after async reload
- No Piclaw request path manually reimplements canonical runtime auth assembly.
- Main, side, compaction, and summarization calls use the same model runtime.
- No stale synchronous read follows an unawaited
ModelRegistry.refresh().
Estimate: 1–1.5 days
Risk: High
runtime/src/agent-control/handlers/login.tsruntime/src/agent-control/provider-defs.tsruntime/src/agent-control/agent-control-handlers.tsruntime/src/agent-pool/provider-usage.ts- related agent-control/provider-usage tests
- Replace Piclaw's private
AuthStorageLikecontrol path withModelRuntimemethods:getProviders()getProvider()getProviderAuthStatus()checkAuth()login()logout()listCredentials()
- Build
/loginprovider options from provider-owned auth capabilities rather thangetOAuthProviders(). - Adapt the web card flow to upstream's
AuthInteractioncontract:prompt()maps text, secret, select, and manual-code requests to supported card stepsnotify()maps info links, auth URLs, device codes, and progress to card content- cancellation uses
AbortSignal
- For API-key login, call
modelRuntime.login(providerId, "api_key", interaction)instead of writingauth.jsondirectly. - For OAuth login, call
modelRuntime.login(providerId, "oauth", interaction)and preserve the existing non-blocking web flow where needed. Ensure there is one owner for the background login promise and a clear retry/check state. - Use
modelRuntime.logout(providerId)for deletion and refresh. - After editing custom-provider configuration in
models.json, callawait modelRuntime.reloadConfig(); do not mutate registry internals. - Preserve backup behavior for user-edited config files where Piclaw still writes them.
- Refactor provider quota lookup:
- call
modelRuntime.getAuth(providerId)first so OAuth refresh remains canonical - use the resolved API key for ZAI and other normal request auth
- use exported
readStoredCredential(providerId, authPath)only for supplemental fields unavailable in request auth, such as Codex account ID or Copilot's original GitHub token - never instantiate or expose the removed
AuthStorage
- call
- Keep quota failure non-fatal and preserve stale cached values.
- Do not log credentials or resolved request headers.
- Provider lists/status must use non-secret metadata.
- Preserve
auth.jsonpermissions and format. - Do not copy secrets into new Piclaw state files.
- Avoid direct credential-file writes except through canonical upstream login/logout.
- provider discovery and display names
- API-key login, status, and logout
- device-code OAuth notifications
- OAuth completion and cancellation
- expired credential refresh without duplicate refreshes
- custom provider login and model availability
- provider quota with refreshed Codex credential
- provider quota with Copilot supplemental refresh token
- auth failure leaves the service responsive and returns actionable UI text
- No source import of
AuthStorage. - No source import of
getOAuthProvider(). - Login/logout uses provider-owned runtime APIs.
- Quota/status code does not own credential refresh.
Estimate: 0.75–1 day
Risk: Medium
runtime/src/extensions/github-copilot-dynamic-models.tsruntime/src/runtime/provider-bootstrap.tsruntime/src/runtime/bootstrap.tsruntime/test/extensions/github-copilot-dynamic-models.test.ts- provider-bootstrap tests
Earendil 0.80.9 already:
- owns GitHub Copilot OAuth and refresh
- derives credential-specific Copilot endpoints
- fetches account-enabled model IDs during credential refresh
- filters its substantial built-in catalog by account availability
- preserves Copilot-specific stream implementations and headers
Piclaw should retain only the incremental capability it still needs: importing valid live model IDs absent from upstream's generated static catalog.
- Remove the
getOAuthProvider()import and any extension-owned OAuth registration. - Register a provider overlay that inherits upstream auth and streams and supplies only
refreshModels(context). - In
refreshModels(context):- restore Piclaw's cached extension catalog from
context.storefirst - if
context.allowNetworkis false, return the cached/known merged catalog - require an OAuth credential with a usable Copilot access token for network discovery
- derive the credential-specific endpoint safely, including enterprise support
- fetch
/modelswith the shared abort signal and bounded timeout - filter embeddings, trajectory helpers, disabled policies, and non-chat entries
- merge live entries with upstream's known models
- preserve upstream metadata when a known model exists
- infer API/limits/vision/reasoning conservatively for unknown models
- persist the validated merged catalog with
context.store.write()
- restore Piclaw's cached extension catalog from
- Do not re-register OAuth, base auth, or custom stream handlers.
- Remove the current boot-time function that directly manipulates the global registry.
- Trigger canonical runtime refresh after channels are ready. Allow the session/model picker to use the cached snapshot immediately.
- Keep a feature flag to disable Piclaw's incremental Copilot overlay independently of upstream's built-in provider.
Before preserving the private overlay, compare the live account catalog with upstream 0.80.9's generated catalog. If all selectable account models are already represented, remove the Piclaw overlay entirely and rely on upstream filtering. This is the preferred outcome.
- built-in Copilot OAuth remains inherited
- no private OAuth export is required
- offline cache restoration
- unknown valid chat model import
- embedding/trajectory/disabled model rejection
- known model metadata preservation
- enterprise endpoint derivation
- network timeout leaves cached models intact
- concurrent refreshes do not duplicate requests
- No boot-time registry patch.
- No extension-owned Copilot OAuth implementation.
- Account-specific catalog behavior is preserved or proven redundant.
Estimate: 1–1.5 days
Risk: High
This work package covers all Piclaw-owned provider registrations and model-consuming extensions. It is required for the upgrade, not a deferred cleanup.
runtime/extensions/integrations/azure-openai.tsruntime/extensions/experimental/azure-openai.harness.tsruntime/src/extensions/azure-openai-api.tsruntime/src/runtime/provider-bootstrap.tsruntime/extensions/integrations/azure-openai-session/index.tsruntime/extensions/integrations/azure-openai-images.ts- all
runtime/test/extensions/azure-openai-*.test.tsfiles runtime/test/runtime/provider-bootstrap.test.ts- rename/update
runtime/test/agent-pool/earendil-0807-provider-regressions.test.ts
- Compile provider configs against the
0.80.9ProviderConfigInputandProviderModelConfigcontracts. Replaceanyat the registration boundary withParameters<ExtensionAPI["registerProvider"]>[1]or the exported provider config type where practical. - Retain private API names only where required to prevent global handler collisions:
- Azure Responses custom API
- Foundry completion adapter
- secondary Azure endpoint API
- Verify custom
streamSimpledispatch underModelRuntimecomposition:- extension stream is selected for its declared API
- built-in provider streams remain available for inherited APIs
- re-registration does not discard unspecified provider fields
- Keep token acquisition inside the Azure integration, but stop embedding each transient bearer token as long-lived model ownership if a runtime-safe resolver can be used. Preferred order:
- use an extension/provider auth resolver supported by the public contract, if available
- otherwise preserve bounded re-registration on token refresh and prove that
ModelRuntime.registerProvider()atomically updates request auth without dropping models or in-flight sessions
- Do not write Azure access tokens to
auth.jsonormodels-store.json. - Preserve provider/model-specific headers and ensure canonical runtime request assembly does not duplicate
Authorizationorapi-keyheaders. - Revalidate custom base-URL normalization, deployment routing, Responses-only guardrails, Foundry routing, request token estimates, retry-after handling, session affinity, image output, and tool-call limits.
- Move model-cap discovery into provider
refreshModels(context)where that improves cache/offline behavior without coupling credential refresh to model refresh. If token rotation and capability discovery must remain separate, document and test the two lifecycles explicitly. - Make bootstrap non-blocking for the web listener. Azure provider registration may use cached/static model definitions immediately, then refresh token/caps in the background.
- Make the process-level provider bootstrap the sole owner of Azure token/model registration and its refresh timer. The session extension (
azure-openai-session) should retain only session-scoped context repair and image/Flux commands. Do not load the fullazure-openai.tsdefault extension per session merely to register providers. - Ensure process shutdown stops the sole Azure bootstrap timer; session replacement must not stop or recreate process-level provider registration.
- Keep the experimental harness behavior aligned with the production integration or remove duplicated code if the harness is no longer needed. Do not allow the two implementations to diverge in auth, model metadata, or API routing.
- static API key and managed identity registration
- token refresh re-registration preserves all provider models
- no duplicate auth headers
- multiple Azure endpoints remain isolated
- Responses-only models never fall through to completions
- Foundry models use the intended completion adapter
- model-cap refresh updates context/output limits atomically
- network/capability failure retains the last good catalog
- exactly one Azure token-refresh timer exists per process
- process shutdown clears the provider timer
- session replacement rebinds context/image hooks without re-registering providers or restarting the process timer
- payload sanitizer and response diagnostics still observe Azure requests/responses
runtime/src/extensions/model-control.tsruntime/test/extensions/extensions-model-control.test.ts- web model-state and compose refresh tests
- Change
list_modelsandswitch_modelto awaitctx.modelRegistry.refresh()before reading the synchronous compatibility snapshot. - Prefer one local
const registry = ctx.modelRegistryand one coherent post-refresh read; do not mix snapshots across awaits. - Preserve enabled-model scoping and pagination.
- Surface refresh errors while continuing to show cached models.
- Verify
pi.setModel()receives the refreshed canonical model object. - Keep model/thinking status persistence synchronized after refresh and switching.
runtime/src/extensions/context-prune/summarizer.tsruntime/extensions/integrations/context-mode.tsruntime/src/extensions/context-mode-api.tsruntime/src/extensions/smart-compaction/model-request.tsruntime/src/extensions/smart-compaction/stream-complete.tsruntime/src/extensions/smart-compaction/model-execution.tsruntime/src/extensions/smart-compaction/orchestrator.tsruntime/src/extensions/smart-compaction/remote-compaction.ts- related context-mode, context-prune, smart-compaction, stream-complete, and remote-compaction tests
- Inject a runtime-owned simple-completion executor into Piclaw built-in extensions. The executor must delegate to the shared
ModelRuntimeand preserve:- resolved API key
- model and configured headers
- provider-scoped environment
- credential-specific base URL
- OAuth refresh
- abort signal
- retry/timeouts and usage
- Replace direct
ctx.modelRegistry.getApiKeyAndHeaders()pluscompleteSimple()sequences in Piclaw-owned extensions. - Where an externally loaded extension cannot receive the executor, keep a compatibility helper around
ctx.modelRegistry, but await async refresh where required and add a deletion note. - Preserve fail-open behavior for semantic tool-output summaries: failure must fall back to preview/storage behavior and must not fail the parent tool call.
- Preserve fail-closed behavior where compaction correctness requires a valid model/auth pair.
- Keep the model and auth resolution atomic so token budgets and capability decisions are made for the same model used for the request.
- Re-run remote-compaction capability tests and retain the rule that support is explicit provider metadata/configuration—not inferred from model names or
api: "openai-responses". - Verify provider hooks (
before_provider_headers, request sanitizer, response diagnostics) still run for runtime-owned auxiliary requests where intended. IfModelRuntime.streamSimple()bypasses AgentSession extension hooks, preserve the required transforms explicitly through the injected executor and document which hooks do or do not apply.
runtime/src/extensions/provider-request-sanitizer.tsruntime/src/extensions/provider-response-diagnostics.tsruntime/src/extensions/llm-context-normalizer.tsruntime/src/extensions/local-lite-prompt-profile.ts- related tests
- Revalidate hook order against
ModelRuntimefinal request assembly:- auth/model headers assembled
before_provider_headerstransform applied- request payload sanitizer applied
- provider response diagnostics observe status/headers
- Ensure header deletion/null semantics survive conversion between upstream
ProviderHeadersand Piclaw records. - Verify local-lite model detection still uses explicit provider/base-URL configuration and does not become a generic model-name heuristic.
- Verify refreshed model objects do not lose custom compatibility metadata used by prompt profiles or compaction.
- Every Piclaw-private provider/model extension compiles against
0.80.9. - Azure OpenAI, Foundry, secondary Azure, and experimental harness tests pass.
- Dynamic provider re-registration is atomic and does not lose catalog/auth/stream configuration.
- Azure provider registration/token refresh has one process-level owner; session extensions do not create competing timers.
- Model-control awaits refresh and continues to serve cached data on refresh failure.
- Context-prune, context-mode, and smart-compaction requests use the shared runtime-owned execution seam.
- Provider sanitizer/diagnostic hooks have explicit tested behavior for both normal AgentSession calls and auxiliary calls.
- No private extension imports removed Earendil auth/OAuth APIs.
Estimate: 1–1.5 days
Risk: High
Repository: /workspace/piclaw-addons
- Create a dedicated add-ons worktree from a clean branch; preserve unrelated dirty files in the existing checkout.
- Update root development dependencies for all Earendil packages to
0.80.9. - Upgrade
cheapskate:- use an API name accepted by the
0.80.9provider contract (openai-completionsunless a custom API implementation is deliberately registered) - type its provider/model configs
- verify provider re-registration merges only defined fields and updates routing atomically
- test rate-limit/context-limit rotation while a session remains active
- use an API name accepted by the
- Upgrade
codex-conversion:- keep its override as a partial overlay on the built-in
openai-codexprovider - do not replace or reimplement upstream OAuth/catalog behavior
- verify custom
streamSimplereceives runtime-resolved auth, headers, environment, and credential-specific base URL - retain upstream long-session-ID clamp and deferred-tool behavior where applicable
- test WebSocket cleanup, image-generation activity, and surfaced web-search activity
- keep its override as a partial overlay on the built-in
- Upgrade
delegate:- make runtime catalog capture async when forcing refresh
- use
await ctx.modelRegistry.refresh()followed by one snapshot read, or explicitly choosegetAvailable()cached behavior - preserve CLI fallback and metadata merge
- Upgrade
skill-model-effort:- refresh once before resolving a requested skill model when the command/read action applies overrides
- preserve unambiguous matching and restore behavior
- test model disappearance or refresh failure using the last good snapshot
- Upgrade standalone
smart-compaction:- remove assumptions that auth is only API key + headers
- preserve provider env and credential-specific base URL
- prefer an external-extension-safe model execution API; if upstream exposes no such context method, use the
ModelRegistrycompatibility facade behind one adapter and test the completeResolvedRequestAuthforwarding contract - retain explicit remote-compaction capability rules
- Update
autoresearchprovider fixtures. - Bump changed package versions, regenerate catalog metadata, and pack each changed add-on.
- Run clean standalone imports against Piclaw with Earendil
0.80.9. - Open and merge the add-ons PR separately, then allow CI to publish public tarballs.
- Verify published tarball URLs return the new versions before core rollout.
cd /workspace/piclaw-addons
bun install
bunx tsc --noEmit
bun test \
addons/cheapskate/index.test.ts \
addons/codex-conversion/index.test.ts \
addons/delegate/index.test.ts \
addons/skill-model-effort/index.test.ts \
addons/smart-compaction/index.test.ts
bun run sync:catalog
bun run check:catalogAlso run repository standalone-import tests and pack inspection for each changed package. Use the add-on microVM E2E suite for installed-package behavior.
- All affected add-ons compile and pass tests against Earendil
0.80.9. - Changed packages have version bumps and synchronized catalog entries.
- Public tarballs are published and downloadable without authentication.
- A clean Piclaw
0.80.9test instance imports every installed affected add-on without startup errors. - Provider overrides preserve inherited auth/catalog behavior.
- No changed package bundles Pi core dependencies.
Estimate: 0.25–0.5 day
Risk: Low
No dedicated Piclaw feature implementation should be required. Existing additive activation through activate_tools already provides the signal Earendil uses for deferred tool loading.
-
Add or adapt a provider-fixture test for a model with:
compat: { deferredToolsMode: "kimi" }
-
Verify the sequence:
- discovery tools active initially
- target tool registered but inactive
activate_toolsadds the target without removing active tools- the following request includes the newly activated definition at the tool-result position
- the tool is callable in that same agent run
-
Confirm fallback behavior remains correct for non-Kimi models.
-
Audit lazily activated Piclaw tools with
promptSnippetorpromptGuidelines. These change the system prompt and can invalidate the provider cache prefix even when the schema is deferred. -
Do not remove useful prompt metadata globally. Record high-volume candidates for a later optimization PR, based on prompt-cache measurements.
- Existing same-turn activation tests pass under
0.80.9. - Kimi serialization is covered by at least one focused compatibility test.
- No provider-name or model-name heuristics are added in Piclaw.
Use two coordinated PRs because published add-ons are an independent compatibility surface and some installed packages must be available before the upgraded core is rolled out.
Recommended commits:
chore(deps): test add-ons against Earendil 0.80.9fix(providers): update private provider extensions for ModelRuntimefix(models): await refreshed catalogs in model-aware add-onstest(addons): cover Earendil 0.80.9 compatibilitychore(catalog): publish upgraded add-on packages
Merge and publish PR A first. Do not install into the local live instance yet.
Use one atomic core PR because intermediate dependency/runtime states do not compile independently. Recommended commits:
chore(deps): update Earendil packages to 0.80.9- dependency files
- async runtime foundation
refactor(models): adopt ModelRuntime across sessions and requests- main/side/compaction core
refactor(auth): use provider-owned login and credential APIs- login/logout/status/quota
refactor(providers): migrate private provider and catalog extensions- Azure OpenAI/Foundry with one process-level registration owner
- Copilot overlay
- provider bootstrap and refresh
refactor(extensions): route model helpers through shared runtime- model-control
- context-mode/context-prune
- smart-compaction and diagnostics
test(models): cover Earendil 0.80.9 compatibility- focused regressions and fixtures
PR B may be developed in parallel but must use the published PR A package set for its final microVM gate.
Both PR descriptions must include:
- upstream release links
- the breaking API summary
- startup-network policy
- before/after model/auth architecture
- exact local test results
- microVM deployment and UX evidence
- rollback instructions
Do not merge without user approval.
Run after each meaningful work package:
bun run typecheck
bun run lint
bun run build:webFinal local gate:
make ci-fastHosted CI remains authoritative after pushing the PR.
At minimum, run affected tests covering:
runtime/test/agent-pool/agent-pool.test.tsruntime/test/agent-pool/service-factory.test.tsruntime/test/agent-pool/session-manager.test.tsruntime/test/agent-pool/session-auto-compaction-control.test.tsruntime/test/agent-pool/session-persistence-sanitizer.test.tsruntime/test/session-rotation.test.ts
- replace or rename
runtime/test/agent-pool/earendil-0807-ambient-auth.test.tsfor0.80.9 runtime/test/utils/model-auth.test.tsruntime/test/agent-pool/compaction-stream-auth.test.tsruntime/test/agent-pool/side-prompt-runner.test.tsruntime/test/agent-control/provider-defs.test.tsruntime/test/agent-control/agent-control-handlers.test.tsruntime/test/agent-pool/provider-usage.test.ts
Core repository:
runtime/test/agent-pool/tool-activation-live-update.test.tsruntime/test/extensions/github-copilot-dynamic-models.test.tsruntime/test/extensions/extensions-model-control.test.tsruntime/test/extensions/smart-compaction.test.tsruntime/test/extensions/context-prune.test.tsruntime/test/extensions/context-mode.test.tsruntime/test/extensions/provider-request-sanitizer.test.tsruntime/test/extensions/provider-response-diagnostics.test.tsruntime/test/runtime/provider-bootstrap.test.tsruntime/test/extensions/azure-openai-api.test.tsruntime/test/extensions/azure-openai-bootstrap.test.tsruntime/test/extensions/azure-openai-harness-bootstrap.test.tsruntime/test/extensions/azure-openai-routing.test.tsruntime/test/extensions/azure-openai-streaming.test.tsruntime/test/extensions/azure-openai-retry-after.test.tsruntime/test/extensions/azure-openai-session.test.tsruntime/test/extensions/azure-openai-image-output.test.tsruntime/test/extensions/azure-openai-tool-call-limit.test.ts- renamed/updated
runtime/test/agent-pool/earendil-0807-provider-regressions.test.ts
Add-ons repository:
addons/cheapskate/index.test.tsaddons/codex-conversion/index.test.tsaddons/delegate/index.test.tsaddons/skill-model-effort/index.test.tsaddons/smart-compaction/index.test.tsaddons/autoresearch/supervisor.test.ts- repository standalone-import tests
- catalog sync/check and packed-tarball import checks
runtime/test/channels/web/web-channel.test.ts- model-picker endpoint tests
- login card/action tests
- agent status/model-state tests
Add tests that prove:
- Cache-first runtime construction performs no model network request.
- Background refresh does not block channel startup.
- Concurrent refreshes coalesce.
- A failed refresh preserves the last good catalog.
models.jsonreload is awaited before a fresh synchronous facade read.- Model headers, provider environment, and custom base URL survive request assembly.
- OAuth refresh is serialized across concurrent main/side requests.
- Main, side, compaction, and summarizer calls share the same runtime.
- Login/logout uses provider-owned methods.
- Copilot dynamic refresh inherits upstream OAuth and stream behavior.
- Azure token refresh/re-registration preserves provider models, custom streams, headers, and endpoint isolation.
- Azure/Foundry model-cap refresh preserves the last good catalog on failure.
- Model-control awaits refresh and serves cached models with a concise refresh error.
- Context-mode, context-prune, and smart-compaction auxiliary calls preserve headers, env, credential-specific base URL, abort, and usage through the shared runtime executor.
- Provider sanitizer and response diagnostics have explicit tested behavior under the new dispatch path.
- Cheapskate backend rotation retains its custom provider and active session.
- Codex-conversion preserves inherited OAuth/catalog behavior while overriding only streaming.
- Delegate and skill-model-effort use coherent post-refresh catalog snapshots.
- Standalone smart-compaction forwards complete request auth or uses the supported runtime execution seam.
- Kimi additive tool activation remains callable in the same run.
- Sessions created before their first assistant response receive the improved clone/fork diagnostic where applicable.
Deploy the PR branch to the dedicated test microVM; do not restart the local instance.
Required scenarios:
- Cold start with network available.
- Cold start with provider network blocked or DNS failure.
- Existing
auth.json,models.json, and nomodels-store.json. - Existing cached
models-store.jsonwhile offline. - Open model picker immediately after startup; it must render promptly from the snapshot.
- Observe background refresh and subsequent updated model list.
- Switch among at least:
- current default
openai-codex/gpt-5.4 - GitHub Copilot model
- Azure OpenAI or Foundry model using test-safe configuration
- custom
models.jsonmodel, if configured in the fixture
- current default
- Send a normal prompt and one tool-using prompt through each configured private provider.
- Run a side prompt/delegate path.
- Trigger context-mode semantic summarization and context-prune summarization.
- Trigger manual compaction in a test chat and exercise any enabled remote-compaction path only with an explicitly supported provider fixture.
- Verify Azure token/model-cap refresh does not interrupt an active session or erase models.
- Install and exercise upgraded
cheapskate,codex-conversion,delegate,skill-model-effort, and standalonesmart-compactionpackages. - Verify add-on provider overrides do not remove upstream OAuth/catalog capabilities.
- Rotate/fork/resume a persisted session.
- Exercise login/logout with test-safe provider fixtures; do not expose real credentials in artifacts.
- Verify no secrets appear in logs or web responses.
Collect:
- service startup duration
- time until web listener is ready
- model-picker response time before and after refresh
- structured refresh logs
- Playwright report/screenshots where useful
Recommended performance gates:
- no regression greater than 10% in median listener-ready time
- model-picker cached response remains under 500 ms on the microVM
- no request waits for a remote catalog unless it explicitly asks for a forced fresh result
Run the microVM for at least 24 hours with:
- periodic model-picker polling
- representative scheduled tasks
- at least two providers
- repeated main and side prompts
- one forced provider failure interval
Watch for:
- repeated refresh loops
- OAuth refresh storms
- unhandled promise rejections
- catalog loss after transient failures
- session creation latency
- memory growth from retained runtime/catalog objects
- Complete add-ons typecheck, focused tests, standalone imports, pack checks, and catalog validation.
- Push the
piclaw-addonsbranch and open PR A. - Hosted add-ons CI must pass.
- Merge only after explicit user approval.
- Verify new public tarballs and catalog entries are available.
- Complete core local static/focused gates using the upgraded add-on package set.
- Push the Piclaw branch and open PR B.
- Hosted core CI must pass.
- Deploy core PR B plus the published add-ons from PR A to the microVM.
- Run UX and operational matrix.
- Complete 24-hour soak.
- Merge only after explicit user approval.
- Confirm
mainCI is green. - Remove both feature worktrees and prune registrations.
-
Check active sessions with
session_status. -
If another session is active, do not restart; report and wait.
-
Back up:
/workspace/.pi/agent/auth.json/workspace/.pi/agent/models.json/workspace/.pi/agent/settings.json- existing
models-store.json, if present
-
Install with:
cd /workspace/piclaw && make local-install
-
Call
exit_processas the final tool action sosystemctl --userrestarts Piclaw. -
Verify health, model list, default model, prompt, tool call, login status, and logs.
Do not perform this stage without explicit permission.
Follow Piclaw's normal prerelease UX tag and final release gates. Do not push a final release tag without the required passing UX prerelease run.
Rollback must be code-first and non-destructive.
Rollback if any of these occur:
- users cannot authenticate with the current default provider
- persisted sessions cannot restore their model
- side prompts or compaction lose provider auth/headers
- startup is blocked by remote catalog networking
- model catalogs disappear after transient refresh errors
- OAuth refresh loops or corrupts credentials
- secrets are exposed in logs/responses
- Stop rollout. Revert the core PR and, if necessary, the affected add-on package versions through new PRs.
- Reinstall the previous known-good Piclaw build and pin/reinstall the prior public add-on tarballs from the previous catalog versions.
- Restart only through the approved service-manager flow.
- Restore backed-up configuration only if evidence shows it changed incorrectly.
- Leave
models-store.jsonin place unless it is proven to cause a problem;0.80.7does not depend on it. - Preserve session JSONL and the messages database.
- Attach failure logs with secrets redacted.
auth.jsonremains the canonical credential file in both versions.models.jsonremains canonical custom-provider configuration.models-store.jsonis additive cache state.- Installed add-on versions/catalog URLs are an independent rollback dimension; record the exact pre-rollout package versions.
- No destructive data migration is required or permitted.
- Do not rewrite user JSONC solely to migrate compatibility fields.
| Risk | Severity | Mitigation |
|---|---|---|
| OAuth/API-key regression | High | Provider-owned APIs; focused Codex/Copilot tests; microVM login validation |
| Slow server startup | High | allowModelNetwork: false; refresh after listeners start |
| Empty/stale model picker | Medium | cached snapshots; coalesced background refresh; explicit freshness path |
| Lost account-specific Copilot models | Medium | compare live/static catalog; preserve only necessary refreshModels overlay |
| Azure token refresh drops provider configuration | High | typed provider configs; atomic re-registration tests; retain last good catalog |
| Azure auth/header duplication | High | one auth owner per request; explicit Authorization/api-key header assertions |
| Private auxiliary model calls bypass request transforms | High | injected runtime-owned executor; document/test hook behavior |
| Installed private add-ons fail after core upgrade | High | separate compatibility PR, version bumps, published tarballs, clean import/E2E gate |
| Provider override erases inherited auth/catalog | High | partial-overlay tests for Codex conversion and dynamic providers |
| Duplicate OAuth refreshes | High | one shared runtime and upstream serialized credential modification |
| Quota display regression | Medium | runtime refresh first; one-off stored credential read only for supplemental metadata |
| Custom provider/header regression | High | request-auth integration tests including headers, env, and base URL |
| Compaction/side prompt auth divergence | High | same ModelRuntime for all auxiliary requests |
| Prefix-cache regression from lazy tools | Medium | additive activation; measure prompt metadata candidates before changing them |
| Refresh storm from web polling | Medium | one in-flight refresh coordinator and provider throttling |
| User config damage | High | no destructive rewrite; backups before rollout; JSONC compatibility retained |
| Scope creep into xAI/Kimi UI work | Low | validate inherited capabilities only; defer unrelated product changes |
The upgrade is complete only when all criteria below are satisfied.
- All three Earendil packages are pinned to
0.80.9. - Piclaw owns one process-wide
ModelRuntime. -
ModelRegistryexists only at documented extension-compatibility boundaries. - No runtime source imports
AuthStorage. - No runtime source imports
getOAuthProvider(). - Main, side, compaction, summarization, and control paths use the shared runtime.
- No refresh-dependent synchronous read follows an unawaited refresh.
- Web startup does not wait for provider catalog network calls.
- Cached models are available offline.
- Background refresh is bounded, coalesced, and failure-tolerant.
- Model-picker requests remain responsive during refresh.
- Existing Codex and Copilot credentials remain usable without manual migration.
- API-key and OAuth login/logout use provider-owned runtime APIs.
- Concurrent OAuth use does not duplicate refreshes.
- Quota/status functionality works without owning credential refresh.
- No secrets appear in logs, status payloads, or test artifacts.
- Custom
models.jsonproviders retain headers, environment, auth, and endpoints. - Copilot account-specific model behavior is preserved or the private overlay is proven unnecessary and removed.
- Azure OpenAI, Foundry, secondary Azure, and experimental harness configurations compile and pass focused tests.
- Azure token/model-cap refresh preserves last-good models and does not duplicate auth headers.
- Exactly one process-level Azure provider registration/refresh owner exists; session replacement does not duplicate timers or providers.
- Model-control tools await refresh and fall back to cached snapshots on failure.
- Context-mode, context-prune, and smart-compaction use the shared runtime-owned model execution seam.
- Provider request sanitizer and response diagnostics have explicit tested behavior for normal and auxiliary model calls.
- Upgraded
cheapskate,codex-conversion,delegate,skill-model-effort, and standalonesmart-compactionpackages pass clean import and focused tests. - Add-on provider overrides preserve inherited upstream OAuth, catalog, headers, and endpoints.
- Existing same-turn
activate_toolsbehavior passes. - Kimi deferred-tool serialization has focused coverage.
- No provider capability is inferred from model-name heuristics in Piclaw.
-
bun run typecheckpasses. -
bun run lintpasses. -
bun run build:webpasses. - Focused affected tests pass.
-
make ci-fastpasses. - Add-ons PR CI, standalone imports, package packing, and catalog validation pass.
- Updated add-on tarballs are publicly downloadable and installed in the microVM fixture.
- Core PR CI passes.
- MicroVM UX/operational matrix passes with the upgraded private add-ons.
- 24-hour microVM soak shows no refresh/auth/session regressions.
- Rollback has been rehearsed or verified from artifacts/config compatibility.
- Live install/restart is performed only after explicit permission and active-session check.
Keep these out of the initial compatibility PR unless required to pass the acceptance criteria:
- A user-facing “refresh model catalogs” control.
- New xAI/Grok subscription UI beyond inherited provider-owned login support.
- Kimi-specific provider onboarding.
- Broad removal of
promptSnippet/promptGuidelinesfrom lazily activated tools. - Removal of legacy session-affinity compatibility before its support window ends.
- General model-control redesign unrelated to the upstream API migration.
- Provider quota API expansion.
The private Azure, Copilot, model-control, context-mode/context-prune, smart-compaction, and provider-diagnostics migrations are explicitly not deferred; they are part of WP4/WP5 and must pass before the core PR merges. The affected piclaw-addons packages in WP6 are also mandatory and must be published before rollout.
Create separate issues after the migration if measurements or user demand justify them.
Start two clean worktrees:
- Core Piclaw WP0/WP1: establish the shared cache-first
ModelRuntime, compile the runtime foundation, and add startup/refresh tests before migrating login or dynamic providers. - Add-ons WP6 preparation: update development dependencies to
0.80.9, run a compile-only compatibility pass, and turn each failure into the focused package changes listed above.
Develop both tracks in parallel, but publish/verify the add-ons PR before the final core microVM gate and live rollout. This exposes architectural and cross-package incompatibilities early without modifying either dirty primary checkout.