fix: give sub-agents the provider their pinned model needs
Sub-agent sessions are created with `noExtensions: true`, which also
removes every provider an extension registered through
`pi.registerProvider` from the sub-session's own ModelRuntime. Since
bff187d pinned sub-agents to the invoking session's model, every check
depends on a provider registration it deliberately does not load, so
scans died in about 20ms with "No API key found for neuralwatt" on a
perfectly healthy login — the key was in auth.json, the provider simply
did not exist in that session's runtime.
Thread the invoking session's registry down to the runner and
re-register just the pinned model's provider there, through an inline
extension factory: inline factories load regardless of `noExtensions`,
so the sub-agent authenticates exactly as the host does while every
other extension stays out of its scope. Registrations are narrowed once
at the extension entry point (`asProviderRegistry`) and relayed
untouched — a native provider keeps its own stream and auth wiring.
The generated omp port keeps its isolation: it disables extension
discovery and has no extension registrations to forward, so it carries
the same shapes with a stub for the helper.
Tests: new tests/provider-forwarding.test.ts pins the narrowing and the
relay (native wins over config-only, nothing forwarded for a built-in);
it fails on the previous tree, where the helper does not exist. Full
suite 162 pass with one pre-existing failure (MISC-46, worktree-only).
bun run typecheck clean; port-to-omp regenerates and typechecks.
MISC-45
Co-Authored-By: nw-flash-flex <noreply@neuralwatt.com>
This commit is contained in:
@@ -36,6 +36,10 @@ const OMP = join(HOME, ".omp", "agent", "extensions");
|
||||
const SKIP = new Set([
|
||||
"port-to-omp.mjs",
|
||||
"release-tag.sh",
|
||||
// Pins pi-only behaviour: provider forwarding into an in-memory session. The
|
||||
// port disables extension discovery, so it has no such behaviour to assert
|
||||
// (see the agent-runner rules below).
|
||||
"provider-forwarding.test.ts",
|
||||
".gitea",
|
||||
".github",
|
||||
"node_modules",
|
||||
@@ -246,9 +250,28 @@ const FILE_RULES = {
|
||||
},
|
||||
],
|
||||
"src/agent-runner.ts": [
|
||||
{
|
||||
// The port has no extension provider registrations to read: its sessions
|
||||
// are created with extension discovery disabled and its ModelRegistry
|
||||
// has no getRegistered*Provider lookups. Keep only the shape the plumbing
|
||||
// passes around, so the ported files stay type-correct.
|
||||
re: /\/\*\* The invoking session's provider registry[\s\S]*?\n\}\n/,
|
||||
to:
|
||||
"/**\n * Provider registrations as the omp port passes them around. The port does not\n * forward any: omp sessions disable extension discovery, and its ModelRegistry\n * keeps no extension provider registrations to forward.\n */\nexport interface ProviderRegistry {\n\tgetRegisteredNativeProvider(providerId: string): object | undefined;\n\tgetRegisteredProviderConfig(providerId: string): object | undefined;\n}\n",
|
||||
label: "provider registry typing",
|
||||
},
|
||||
{
|
||||
// The inline provider extension has no purpose in the port — extension
|
||||
// discovery is disabled when the session is created — so the port carries
|
||||
// only the stub the plumbing references.
|
||||
re: /\n\/\*\*\n \* Build the inline extension that re-registers a provider inside a sub-session\.[\s\S]*?\n\t\};\n\}\n/,
|
||||
to:
|
||||
"\n/**\n * The omp port forwards no provider registrations (see `ProviderRegistry`), so\n * sub-agent sessions get no inline extension.\n */\nexport function providerForwardingExtension(): undefined {\n\treturn undefined;\n}\n",
|
||||
label: "provider forwarding stub",
|
||||
},
|
||||
{
|
||||
from:
|
||||
"\t// Lazily import the SDK so the rest of the module graph (and tests using the\n\t// fake runner) never resolve the heavy pi-coding-agent package.\n\tconst {\n\t\tcreateAgentSession,\n\t\tDefaultResourceLoader,\n\t\tgetAgentDir,\n\t\tSessionManager,\n\t} = await import(\"@oh-my-pi/pi-coding-agent\");\n\tconst loader = new DefaultResourceLoader({\n\t\tcwd: opts.cwd,\n\t\tagentDir: getAgentDir(),\n\t\tsystemPromptOverride: () => agent.systemPrompt,\n\t\t// Keep the sub-agent isolated: no nested extensions/skills/themes/etc.\n\t\tnoExtensions: true,\n\t\tnoSkills: true,\n\t\tnoThemes: true,\n\t\tnoPromptTemplates: true,\n\t});\n\tawait loader.reload();",
|
||||
"\t// Lazily import the SDK so the rest of the module graph (and tests using the\n\t// fake runner) never resolve the heavy pi-coding-agent package.\n\tconst {\n\t\tcreateAgentSession,\n\t\tDefaultResourceLoader,\n\t\tgetAgentDir,\n\t\tSessionManager,\n\t} = await import(\"@oh-my-pi/pi-coding-agent\");\n\t// Re-register the pinned model's provider inside the sub-session: inline\n\t// extension factories load even under `noExtensions`, so this is the one\n\t// registration a pinned model needs in order to authenticate.\n\tconst providerExtension = providerForwardingExtension(\n\t\topts.modelRegistry,\n\t\topts.model?.provider,\n\t);\n\tconst loader = new DefaultResourceLoader({\n\t\tcwd: opts.cwd,\n\t\tagentDir: getAgentDir(),\n\t\tsystemPromptOverride: () => agent.systemPrompt,\n\t\t// Keep the sub-agent isolated: no nested extensions/skills/themes/etc.\n\t\tnoExtensions: true,\n\t\tnoSkills: true,\n\t\tnoThemes: true,\n\t\tnoPromptTemplates: true,\n\t\textensionFactories: providerExtension ? [providerExtension] : [],\n\t});\n\tawait loader.reload();",
|
||||
to:
|
||||
"\t// Lazily import the SDK so the rest of the module graph (and tests using the\n\t// fake runner) never resolve the heavy pi-coding-agent package. The specifier\n\t// is static by intent (dynamic-import exception: module is intentionally\n\t// excluded from the import-time graph to keep fake-runner tests SDK-free).\n\tconst { createAgentSession, AgentRegistry, SessionManager } = await import(\n\t\t\"@oh-my-pi/pi-coding-agent\"\n\t);",
|
||||
label: "SDK import + loader removal",
|
||||
|
||||
Reference in New Issue
Block a user