Summary
Dispatching an agent whose model: field names a provider registered by another extension (via pi.registerProvider) fails in the child session, while the same model works when it is the main session's model.
Two behaviors are involved, both specific to child sessions:
- The runtime used to resolve the child's model is created in this extension's init and does not include providers that other extensions register afterwards.
- Child sessions are built with
noExtensions: true, so request-transform hooks (before_provider_request) installed by other extensions never run inside them.
Observed behavior
Dispatching such an agent through the subagent tool returns:
backend create failed: agent model '<provider>/<id>' not found in runtime (provider '<provider>', id '<id>').
Available: <providers from the built-in catalog and models.json>, … — pick one of these for the model param
The Available: list contains models from the built-in catalog and models.json, and does not contain any provider registered by another extension.
When the model id requires a request-time rewrite by an extension (for example an alias that maps to a different upstream id), the second behavior shows up as an upstream error instead:
model call ended with stopReason 'error' (provider/auth failure or rate limit) for model '<provider>/<id>'
with the upstream responding 400 for the alias id, i.e. the rewrite hook did not run.
Environment
- armory-fleet 1.4.0
- pi 0.85.1
- Node v24.16.0
- Linux 6.8.12-1021-tegra
Points in the code
src/index.ts:207 — const modelRuntime = await ModelRuntime.create(); runs during extension init.
src/index.ts:153 — model = modelRuntime.getModel(provider, id) resolves the child's model against that instance.
src/engine/child-loader.ts:83 — noExtensions: true for the child resource loader.
pi.registerProvider(...) in another extension registers into the host runtime; ModelRuntime.create() builds a separate instance from the built-in catalog + models.json + the models store. ExtensionContext.modelRegistry.getRegisteredProviderIds() / getRegisteredNativeProvider() / getRegisteredProviderConfig() do expose what the host has, and registering those onto the child-session runtime reproduces the missing models.
Separately, passing -e/--extension <path> on the pi CLI does not reach child loaders: that path is read from each loader's own additionalExtensionPaths (pi resource-loader.js:277), not from process argv.
Minimal reproduction
- An extension that registers a provider:
// ~/.pi/agent/extensions/demo-provider.ts
export default function (pi: any) {
pi.registerProvider("demo", {
api: "anthropic-messages",
baseUrl: "https://example.invalid",
models: [{ id: "demo-model", name: "Demo", contextWindow: 100000, maxTokens: 8000 }],
});
}
- An agent that uses it:
<!-- ~/.pi/agent/agents/probe-demo.md -->
---
name: probe-demo
description: probe
model: demo/demo-model
tools: [read]
---
Reply OK.
- Dispatch it, e.g. through the
subagent tool with agent: "probe-demo".
Result: not found in runtime (provider 'demo', id 'demo-model'), while pi -p --model demo/demo-model in the main session resolves and runs.
Notes
- The same dispatch through the native
Agent tool works, since that path runs with extensions loaded.
- Passing the extension explicitly (
-e) does not change the child-session result.
Summary
Dispatching an agent whose
model:field names a provider registered by another extension (viapi.registerProvider) fails in the child session, while the same model works when it is the main session's model.Two behaviors are involved, both specific to child sessions:
noExtensions: true, so request-transform hooks (before_provider_request) installed by other extensions never run inside them.Observed behavior
Dispatching such an agent through the
subagenttool returns:The
Available:list contains models from the built-in catalog andmodels.json, and does not contain any provider registered by another extension.When the model id requires a request-time rewrite by an extension (for example an alias that maps to a different upstream id), the second behavior shows up as an upstream error instead:
with the upstream responding
400for the alias id, i.e. the rewrite hook did not run.Environment
Points in the code
src/index.ts:207—const modelRuntime = await ModelRuntime.create();runs during extension init.src/index.ts:153—model = modelRuntime.getModel(provider, id)resolves the child's model against that instance.src/engine/child-loader.ts:83—noExtensions: truefor the child resource loader.pi.registerProvider(...)in another extension registers into the host runtime;ModelRuntime.create()builds a separate instance from the built-in catalog +models.json+ the models store.ExtensionContext.modelRegistry.getRegisteredProviderIds()/getRegisteredNativeProvider()/getRegisteredProviderConfig()do expose what the host has, and registering those onto the child-session runtime reproduces the missing models.Separately, passing
-e/--extension <path>on the pi CLI does not reach child loaders: that path is read from each loader's ownadditionalExtensionPaths(piresource-loader.js:277), not from process argv.Minimal reproduction
subagenttool withagent: "probe-demo".Result:
not found in runtime (provider 'demo', id 'demo-model'), whilepi -p --model demo/demo-modelin the main session resolves and runs.Notes
Agenttool works, since that path runs with extensions loaded.-e) does not change the child-session result.