Provider.getModels() is sync-only (last-known list; must not throw) with
an optional refreshModels() where dynamic providers fetch. The
sync-or-async union invited latent sync assumptions that would detonate
on the first dynamic provider; async-only reads would force sync
consumer surfaces (extension find/getAll) through Promises. Sync reads
plus an explicit refresh verb keeps the contract single and the
staleness visible.
Models.getModels()/getModel() are sync best-effort reads;
Models.refresh(provider?) rejects with ModelsError(model_source) for a
single provider and is concurrent best-effort across all providers.
createProvider() takes a models array plus an optional refreshModels
fetcher (stored on success, in-flight calls deduped, list unchanged on
rejection). forceRefresh options are gone.
Also finishes the in-progress AuthStorage fallbackResolver removal
(drops the now-unused includeFallback option from getApiKey).
The root barrel is now core-only and side-effect free: types,
createModels/createProvider, auth substrate, lazyStream/lazyApi, faux,
utils. Generated catalogs, api-registry, env-api-keys, images, global
stream functions, and per-API lazy wrappers leave the root.
New @earendil-works/pi-ai/compat preserves the old surface verbatim as
a strict superset of the root: api-dispatch stream/complete with env
key injection, the builtin registration side effect (skip-if-present so
it cannot clobber earlier overrides), deprecated getModel/getModels/
getProviders aliases of the new getBuiltin* reads in providers/all,
lazy api wrappers + setBedrockProviderModule, and image generation.
Compat dies with the coding-agent ModelManager migration.
Packaging: exports map gains ./compat, ./providers/*, ./api/*;
sideEffects array lists only the effectful modules.
Old-global imports across agent/coding-agent/examples and pi-ai tests
switch to /compat (path-only; compat is a superset). The coding-agent
extension loader resolves the pi-ai ROOT specifier to compat, so
existing user extensions using the old global API keep working at
runtime until compat is removed. vitest configs alias /compat to src;
browser smoke imports old globals from /compat.
Behind PI_EXPERIMENTAL=1, show a first-time setup dialog on interactive startup when the default agent directory is used and settings.json does not exist. The dialog preselects the detected terminal appearance with an explicit dark/light choice (live preview) and asks for opt-in analytics data sharing. Submitting writes settings.json, which serves as the completion marker; opting in stores a generated trackingId. Escape at any point skips out of setup.
The HTTP timeout setting (httpIdleTimeoutMs) was only used as a fallback for the
openai-codex-responses API. For other providers like openai-completions
(llama.cpp), the SDK default timeout (10 min) was used instead, ignoring the
user's disabled timeout setting.
Now httpIdleTimeoutMs applies universally as the default SDK request timeout
for all providers that support timeoutMs. Setting HTTP timeout = false (0)
correctly disables SDK timeouts across the board by sending max int32
(effectively infinite) instead of 0, since SDKs treat timeout=0 as immediate
timeout.
closes#5294
The HTTP timeout setting (httpIdleTimeoutMs) was only used as a fallback for the
openai-codex-responses API. For other providers like openai-completions
(llama.cpp), the SDK default timeout (10 min) was used instead, ignoring the
user's disabled timeout setting.
Now httpIdleTimeoutMs applies universally as the default SDK request timeout
for all providers that support timeoutMs. Setting HTTP timeout = false (0)
correctly disables SDK timeouts across the board.
closes#5294
Wrap the path shown in read/write/edit/ls tool titles in an OSC 8 file:// hyperlink when the terminal advertises hyperlink support.
The link target is always the absolute path, while the visible text stays relative/home-shortened and line annotations (e.g. :120-140) are kept out of the target.
When an extension queues a follow-up during `agent_end` it
gets stuck on the follow-up queue until after the next user
message.
Add a hasQueuedMessages() check to _handlePostAgentRun
so the existing while/continue loop drains them.
- One-line fix in _handlePostAgentRun
- Integration test in agent-session-queue
- git-merge-and-resolve example extension with tests
Add streamingBehavior to InputEvent so extensions can distinguish
idle prompts from mid-stream steers and queued follow-ups.
- Add streamingBehavior field to InputEvent type
- Thread it through ExtensionRunner.emitInput() and AgentSession.prompt()
- Add streaming-aware input gate example with tests
- Document in extensions.md