{"_id":"@anierbeck/pi-model-dynamic-router","_rev":"21-cf37123d5419e3abf66df91448554524","name":"@anierbeck/pi-model-dynamic-router","dist-tags":{"latest":"1.6.1"},"versions":{"1.1.0":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.1.0","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.1.0","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"commands":["router"],"extension":"./dist/index.js"},"bin":{"pi-model-router":"dist/index.js"},"dist":{"shasum":"9fac37bf0de45373ebbc1c2b7b1851a16939df7e","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.1.0.tgz","fileCount":9,"integrity":"sha512-0iig0ExPP9HaCBk5rK+6OYM7i8pphYuFNDzC7hSfP6FN5nAx+LtmhmZ40XDN6o4oP6+3w4T6KOI78XmpTpaOXw==","signatures":[{"sig":"MEUCIGPPduCFNJNHtuz/bPkXsmFhBflsamVTDBEhA/L79RTSAiEAuI798x2l/suAYZ8Oiu5Cqd4FC6HTGgjE5da8QobpBWk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":438692},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"0b8fe782302f6c7f46346fa39b7abfc586769184","scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@mariozechner/pi-ai --external:@mariozechner/pi-coding-agent --external:@mariozechner/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"anierbeck","email":"bcanhome@googlemail.com"},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.12.1","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"26.0.0","dependencies":{},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@mariozechner/pi-ai":"*","@mariozechner/pi-tui":"*","@mariozechner/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.1.0_1782557038608_0.45614311822403497","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.1.1","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.1.1","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"commands":["router"],"extension":"./dist/index.js"},"bin":{"pi-model-router":"dist/index.js"},"dist":{"shasum":"534f29cb87cefe720c837938373d19875818f34d","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.1.1.tgz","fileCount":9,"integrity":"sha512-88+V3V+m/zeb2MCIsxOP2oPvQbHs6BlhuCAL5rTPP/eQ5/SYJEXqchHsZNLcSlCY4Kxwl/EZovtCC0Xd5AJLIw==","signatures":[{"sig":"MEQCIGIHMaLCnDRfFFy+ZtEifC06N5Nsf+XbdD+MiAq8e2kkAiBmfilsmWrL+Noskcs4mMm3b5Ik8mcvbtSyz0iialFTCA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":438692},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"05dc4cb4febd01098829f2e331ad3590ee09cdde","scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@mariozechner/pi-ai --external:@mariozechner/pi-coding-agent --external:@mariozechner/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.13.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.17.0","dependencies":{},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@mariozechner/pi-ai":"*","@mariozechner/pi-tui":"*","@mariozechner/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.1.1_1782557718506_0.9861922111923529","host":"s3://npm-registry-packages-npm-production"}},"1.1.2":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.1.2","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.1.2","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"commands":["router"],"extension":"./dist/index.js"},"bin":{"pi-model-router":"dist/index.js"},"dist":{"shasum":"1eab0af06f07092c078f31e7843afca41de81827","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.1.2.tgz","fileCount":9,"integrity":"sha512-zj7f8FimNAYwqMv4jvq9lKJcDN9/7YslMiiXwQZWlQPiqobGX5jmRlAIy1mUgCGU3K6eNpf03Gx23lF2y7XVKg==","signatures":[{"sig":"MEYCIQCiVT0cuwlvRyRJLiqHFQZB8ygGwAwtq5NW4GcIH+Lx9AIhALB1e00D5tbYjjEY5nER0t+rY8rDdp5gGNve4s+cY6oZ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.1.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":438719},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"0c70142bc4015707525e455cc1780007ab87c06d","scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@mariozechner/pi-ai --external:@mariozechner/pi-coding-agent --external:@mariozechner/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.13.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.17.0","dependencies":{},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@mariozechner/pi-ai":"*","@mariozechner/pi-tui":"*","@mariozechner/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.1.2_1782585111315_0.08705273097404609","host":"s3://npm-registry-packages-npm-production"}},"1.1.3":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.1.3","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.1.3","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"commands":["router"],"extension":"./dist/index.js"},"bin":{"pi-model-router":"dist/index.js"},"dist":{"shasum":"7dce8e320b71aab9193a2fc684430a4075262fe5","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.1.3.tgz","fileCount":9,"integrity":"sha512-3KH5qBzm6/WAAVxmtKn9E+Hf/wCCg2Boj8qDGVWZslNP17nyjradPth6sQX4sXaYL8UUhzRFLWyG/DxomtQDpA==","signatures":[{"sig":"MEQCIGVCkSmbEFz59qtyrggsICyeHhxdgHN2TyJRNyC2m9OvAiAXfcSaAVXUmQ7yYyVKRVK87YN1bEyyYoL3OGFj4+68ew==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.1.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":438844},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"add2ba4d149b0f6abd10db51f28795eeafc2376f","scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:@earendil-works/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.13.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.17.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","@earendil-works/pi-ai":"^0.74.2","@earendil-works/pi-tui":"^0.74.2","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@earendil-works/pi-coding-agent":"^0.74.2","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.1.3_1782647262926_0.418742727544019","host":"s3://npm-registry-packages-npm-production"}},"1.1.4":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.1.4","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.1.4","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"extensions":["./dist/index.js"]},"bin":{"pi-model-router":"dist/index.js"},"dist":{"shasum":"3db9588ed19366bc625116546001e7edb891b945","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.1.4.tgz","fileCount":9,"integrity":"sha512-jIQnbixTGMKcWHASdjo3Vyz/4WHRtCB6ANVrgG0qcSsB1kDuDCvkv61OudkLBsdYDDcxPaRKirBSkdm0riirIw==","signatures":[{"sig":"MEQCIBSUla/IG9/SxWv85akF8eGNXv6lJfbc1E4w5fiM6ediAiB8JPDtt52q/hwdb+rf/FSpI/SCuzKMtBLEk7RVZkWFhw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.1.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":438818},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"e63badd9357609cdd1939ee2a3d501dcfec6fb06","scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:@earendil-works/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.13.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.17.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","@earendil-works/pi-ai":"^0.74.2","@earendil-works/pi-tui":"^0.74.2","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@earendil-works/pi-coding-agent":"^0.74.2","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.1.4_1782648484341_0.9904568477830691","host":"s3://npm-registry-packages-npm-production"}},"1.1.5":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.1.5","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.1.5","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"extensions":["./dist/index.js"]},"bin":{"pi-model-router":"dist/index.js"},"dist":{"shasum":"e7e0a5eed1d92ef1de7aed4f6fe89acf828720d3","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.1.5.tgz","fileCount":9,"integrity":"sha512-HpUuFoxRyH8+WxzfY7HWu4isGNESG7NOPLcJh40KEHoNlE2Dnm6ip2HLvFxgqxq/Wxljt/ulVWO9NiuRlWtr7A==","signatures":[{"sig":"MEYCIQCWpSr3dvPYWtBCyui+wDfoXIg46l9phIQiz4opevMmzgIhAKaV3Bj+e1kX0vlOV6iAvlIFYZ0/5mdsX2UXKdODWEcZ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.1.5","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":439088},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"0e75b0722170c27061b961188473bc9e3a9d5e0f","scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:@earendil-works/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.13.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.17.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","@earendil-works/pi-ai":"^0.74.2","@earendil-works/pi-tui":"^0.74.2","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@earendil-works/pi-coding-agent":"^0.74.2","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.1.5_1782648878043_0.47742712967180667","host":"s3://npm-registry-packages-npm-production"}},"1.1.6":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.1.6","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.1.6","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"extensions":["./dist/index.js"]},"bin":{"pi-model-router":"dist/index.js"},"dist":{"shasum":"8c5318ab138cc09c6b077a8cf9243df1e6bbcaa1","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.1.6.tgz","fileCount":9,"integrity":"sha512-FxdPc0kMCrQSQIMXKUnvzwtq2k6WJ0MeVputHZaQ9cPOTNK7XgpAsmcjWYC4SFg3JLON5Sn1mS9N+ePUuOVTOw==","signatures":[{"sig":"MEYCIQCt8k9d857o9/cJDcmyVYDCO8YojuhHt8CV2iggZYgzFwIhAN45goLUVMMsZE4fl7IsX8zAJIlT0M9mDx/F0lcd5hpt","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.1.6","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":438899},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"e281536c0f866dec32f92ab50df2d074db70f12e","scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:@earendil-works/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.13.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.17.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","@earendil-works/pi-ai":"^0.74.2","@earendil-works/pi-tui":"^0.74.2","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@earendil-works/pi-coding-agent":"^0.74.2","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.1.6_1782650763955_0.8436357834756727","host":"s3://npm-registry-packages-npm-production"}},"1.1.7":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.1.7","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.1.7","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"extensions":["./dist/index.js"]},"dist":{"shasum":"88e53fd72bd7ad116698ced5c83b5d641549b67d","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.1.7.tgz","fileCount":9,"integrity":"sha512-+tQP1wTFPA17L0eic2tlOTtAADwi8lW/qT2griV6+k0xZ3ely8jDKbt+1f5qM65Xyrodr254QozFK8JaTMA0Qw==","signatures":[{"sig":"MEUCIANi/OcgwJC8MxIzZd3B3zigN0SY4L/NXfIpLpnP+A9dAiEAqI2S4Uv51JOnnfTvDBmIt85CNsOhmbtRxOIe2eWPiww=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.1.7","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":460407},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"ea492a6ef9004caf63e88efb265d2916ff47b663","scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:@earendil-works/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"if [ -z \"$(git tag --points-at HEAD 2>/dev/null)\" ]; then echo 'No git tag found at current commit, skipping publish'; exit 1; fi","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.16.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.18.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","@earendil-works/pi-ai":"^0.74.2","@earendil-works/pi-tui":"^0.74.2","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@earendil-works/pi-coding-agent":"^0.74.2","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.1.7_1785003074620_0.7306858965941232","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.2.0","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.2.0","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"extensions":["./dist/index.js"]},"dist":{"shasum":"5fdc760b119e8ce612ef190165f3baab530fcab5","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.2.0.tgz","fileCount":9,"integrity":"sha512-O4PlaP7VerLT3sygU3mF/AE3YtCSmFPY/KakbPbYFGuwyd068iLH/NvLs0uFdFNyxYceOJLFVWjkL+TTbdvaEw==","signatures":[{"sig":"MEQCIEQJ1X6AMvvYqATQbUfFRJMWe/XozK3IUC96VvVogi32AiA7wF3PBr51aAg9V7ZEoLQUvkLWko8HhvCnWnlry8jaOg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":491343},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"4952556b95acc5eb2b75e6465786ab41125a4f7c","scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:@earendil-works/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"if [ -z \"$(git tag --points-at HEAD 2>/dev/null)\" ]; then echo 'No git tag found at current commit, skipping publish'; exit 1; fi","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.16.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.18.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","@earendil-works/pi-ai":"^0.82.1","@earendil-works/pi-tui":"^0.82.1","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@earendil-works/pi-coding-agent":"^0.82.1","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.2.0_1785391255145_0.8617871747888712","host":"s3://npm-registry-packages-npm-production"}},"1.2.1":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.2.1","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.2.1","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"extensions":["./dist/index.js"]},"dist":{"shasum":"90c8e9b090e79dfdafa038edde0d24ffc97c79a8","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.2.1.tgz","fileCount":9,"integrity":"sha512-Kha1m/Z7kT3GGNblFZ9tgFPU0flEfST1UM5waH03SagupYNfz6xVcA6KEM8IwYA3eSvC9HRaBdLt1Y7invXB1g==","signatures":[{"sig":"MEUCIFVBtba9Z0t7mLE42szpSyMAPPZjnnrRUZDUUne4z+yyAiEA7nk72+M+uCgey3qqvebwVdj2Iar2U+7pUWjToLLA0tE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":491705},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"7dcc55d285c1d061c387f1d97a687c18fe6faece","scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:@earendil-works/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"if [ -z \"$(git tag --points-at HEAD 2>/dev/null)\" ]; then echo 'No git tag found at current commit, skipping publish'; exit 1; fi","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.16.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.18.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","@earendil-works/pi-ai":"^0.83.0","@earendil-works/pi-tui":"^0.83.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@earendil-works/pi-coding-agent":"^0.83.0","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.2.1_1785397546553_0.7381627801325781","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.3.0","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.3.0","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"extensions":["./dist/index.js"]},"dist":{"shasum":"469ca27ca59f84c4a3f3053acf5c7086ee0064e0","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.3.0.tgz","fileCount":9,"integrity":"sha512-k1tXDMHtlKkBAfui4fVn9UvXpCX7LoVbRmuz+pwoRRMPd1cQtR7khUjjeKF1l/GiFb4/D6hhhrWn0eAZu/VgJg==","signatures":[{"sig":"MEYCIQDFTwpVTzoOBXSxHQVI/MKxD3txnhBDHTmYlRok9h8UFgIhAJyJBkP2FM8tiLV0Nt3jZugI0KVd8gUfWa5ys3HIHgxA","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":512150},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"00d46ab16bd99f3e8480659ce0dea60e18f4cd88","scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:@earendil-works/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"if [ -z \"$(git tag --points-at HEAD 2>/dev/null)\" ]; then echo 'No git tag found at current commit, skipping publish'; exit 1; fi","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.17.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","@earendil-works/pi-ai":"^0.83.0","@earendil-works/pi-tui":"^0.83.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@earendil-works/pi-coding-agent":"^0.83.0","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.3.0_1786723962673_0.6398575492911149","host":"s3://npm-registry-packages-npm-production"}},"1.3.1":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.3.1","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.3.1","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"extensions":["./dist/index.js"]},"dist":{"shasum":"479958343fdab677a9b96b709ce1d300bb324bd2","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.3.1.tgz","fileCount":9,"integrity":"sha512-KT1HsD/8fgDPlVLowgDaOBikDHK1C1TZnAAaPrCjFQWIHasx98i3SuZ728thVeCLR4AVJ9Ks/vRm8j6e38UANQ==","signatures":[{"sig":"MEUCIF91r4JIngSsT17qLBWINlTWbp72dfD3TjGriIWuLG3zAiEAk8AjvnHOZbS/W8QAjYkbUCzn+O48NEJNYj8kynrn0Ps=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.3.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":513965},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"0a999304bb394e642ab35da007aafc084a4c8b58","scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:@earendil-works/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"if [ -z \"$(git tag --points-at HEAD 2>/dev/null)\" ]; then echo 'No git tag found at current commit, skipping publish'; exit 1; fi","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.17.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","@earendil-works/pi-ai":"^0.83.0","@earendil-works/pi-tui":"^0.83.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@earendil-works/pi-coding-agent":"^0.83.0","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.3.1_1786727032273_0.4658875686631554","host":"s3://npm-registry-packages-npm-production"}},"1.4.0":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.4.0","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.4.0","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"extensions":["./dist/index.js"]},"dist":{"shasum":"6c54377fb9fa67e895180c92138a605fea8892dd","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.4.0.tgz","fileCount":9,"integrity":"sha512-Z1xs4ONOohxd63V2UF5XQPQgO34OGrwRdu8cy2aBiewFSE5arOEtOIK98NJ9/qq14k/anxd4Z6++cHc2k4SBmg==","signatures":[{"sig":"MEYCIQDiQE/ideVVH/w8W3tKXg7ARuq47pSED8O19TfgQruVRwIhAOcvL8t5Z3q5uumLqcSZYUd/L4/2RrUydYJk5ugWOWFG","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":540953},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"d99f466c2797189b257e5d29ed8749b96ba97f20","scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:@earendil-works/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"if [ -z \"$(git tag --points-at HEAD 2>/dev/null)\" ]; then echo 'No git tag found at current commit, skipping publish'; exit 1; fi","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.17.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","@earendil-works/pi-ai":"^0.83.0","@earendil-works/pi-tui":"^0.83.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@earendil-works/pi-coding-agent":"^0.83.0","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.4.0_1786956400133_0.043168872614292475","host":"s3://npm-registry-packages-npm-production"}},"1.4.2":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.4.2","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.4.2","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"extensions":["./dist/index.js"]},"dist":{"shasum":"74a9b1c3f0461b07c36c1984dfe2c668cc5f39cb","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.4.2.tgz","fileCount":9,"integrity":"sha512-pGgci4qj0HFEj0gNLdoAGWamkt6nVzaKBkLQeGBxgbdCz1uiILwMrJivmpX3uk6Bgwki7kSQfdG3V9B9tm9hxw==","signatures":[{"sig":"MEQCIAN2kuaIEbVmfevY/gmLqRoTr2mUFHrSpvHFgBFoSLrmAiBsND5oe5aO9zVnk+IBvPKwwm/xVIYYiJsr15tzXJA4fQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.4.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":562001},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"367378419bcfef57be3f1b700088565aa5ea6f99","scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:@earendil-works/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"if [ -z \"$(git tag --points-at HEAD 2>/dev/null)\" ]; then echo 'No git tag found at current commit, skipping publish'; exit 1; fi","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.17.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","@earendil-works/pi-ai":"^0.83.0","@earendil-works/pi-tui":"^0.83.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@earendil-works/pi-coding-agent":"^0.83.0","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.4.2_1787900283221_0.8215083147632596","host":"s3://npm-registry-packages-npm-production"}},"1.5.0":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.5.0","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.5.0","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"extensions":["./dist/index.js"]},"dist":{"shasum":"9cea9245e6da53d2fd8ecf6dea55b9f3d5da897f","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.5.0.tgz","fileCount":9,"integrity":"sha512-aMsRVs2DA/8OuVFN2nDSFBdRD+4HDUF7/n6b44zyPY97m0jhKTdeMaJySicEUxo1zYB5rkZ77CbY36otWaIV1Q==","signatures":[{"sig":"MEUCIEwQxLUsDIl9M5Y06GaBLhFQuLPxnYI+rITmXP18dXbvAiEA3aKLzMYd8UEUHTjpE+VNcHJ14PDF/+CswQ+5LoPSp7o=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":566547},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"628ce98776ae690997dae69a11dd0d81f2ef3cd8","scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:@earendil-works/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"if [ -z \"$(git tag --points-at HEAD 2>/dev/null)\" ]; then echo 'No git tag found at current commit, skipping publish'; exit 1; fi","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.17.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","@earendil-works/pi-ai":"^0.83.0","@earendil-works/pi-tui":"^0.83.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@earendil-works/pi-coding-agent":"^0.83.0","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.5.0_1787913920635_0.5749838059417809","host":"s3://npm-registry-packages-npm-production"}},"1.5.1":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.5.1","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.5.1","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"extensions":["./dist/index.js"]},"dist":{"shasum":"57714426d9f9767e9e51a0992e8d01262a828e07","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.5.1.tgz","fileCount":9,"integrity":"sha512-YPBvROwn3Vy5VM75af3xSJT9jqJkWQn652ouIgKG9E/mPResKOqs4XFUlH0+ec6ymDHKpOg3UlZG6uslQHQpJQ==","signatures":[{"sig":"MEUCIQDz4hSsa0dMx6oq8eBjC8/Eh/e7O1vnONoI2SkfWCATSQIgAKyWy/TT8vFTSRc9qGtGhmDoP4D8Iu+IZsNaUQDW7eY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.5.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":585371},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"f90f4e67b2c331a3d88fb2abd949dfa0264af30c","private":false,"scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:@earendil-works/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"if [ -z \"$(git tag --points-at HEAD 2>/dev/null)\" ]; then echo 'No git tag found at current commit, skipping publish'; exit 1; fi","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.17.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","@earendil-works/pi-ai":"^0.83.0","@earendil-works/pi-tui":"^0.83.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@earendil-works/pi-coding-agent":"^0.83.0","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.5.1_1788452632909_0.5884619920497223","host":"s3://npm-registry-packages-npm-production"}},"1.5.2":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.5.2","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.5.2","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"extensions":["./dist/index.js"]},"dist":{"shasum":"f70548debdc10f26acca3b0989a09ac0395084be","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.5.2.tgz","fileCount":9,"integrity":"sha512-+5EzC8OYpBBvmPage/TQ9j70uYTrn8Sqjd+MoWPxJz5T2ON6SACS56ZLtRlRowkncHjFceDVe32nDWGNTP8BbA==","signatures":[{"sig":"MEYCIQDtmRWowz3Cv7wkw+SaH2yqWAcpQXQouFHUlf31wEyzkgIhAII1Q8XcOHvwVYRU26zCQarJf2hc+Se9uuNrHJXBbYwp","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.5.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":586307},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"bac8f499c33b1146bde69e4b24a673dc0128ec1e","private":false,"scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:@earendil-works/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"if [ -z \"$(git tag --points-at HEAD 2>/dev/null)\" ]; then echo 'No git tag found at current commit, skipping publish'; exit 1; fi","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.19.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.20.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","@earendil-works/pi-ai":"^0.83.0","@earendil-works/pi-tui":"^0.83.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@earendil-works/pi-coding-agent":"^0.83.0","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.5.2_1789056910917_0.8372904755657695","host":"s3://npm-registry-packages-npm-production"}},"1.5.3":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.5.3","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.5.3","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"extensions":["./dist/index.js"]},"dist":{"shasum":"d1905f4c941c8e944cbea76c062295ae76dd18d4","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.5.3.tgz","fileCount":9,"integrity":"sha512-v8TV50f2GGFSufcBjsY0VcZSdyoRD/tAvuJVeOg+RXtKTJcAE0DefppSH9Ast23l0p/vCpfO9ZfY2ldmQ/kYeg==","signatures":[{"sig":"MEQCIEBk8VlvJyHiRHVZo0EGwug6UV2VhC2GWdVXODIgmykWAiBV9LggI0Bc5PkCCylAVQxoSz6RgG/TMVEU+QD6FY1eiQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.5.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":586542},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"d54f9cadcdee4ead1d187f33aa3dae04357ca5d3","private":false,"scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:@earendil-works/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"if [ -z \"$(git tag --points-at HEAD 2>/dev/null)\" ]; then echo 'No git tag found at current commit, skipping publish'; exit 1; fi","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.19.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.20.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","@earendil-works/pi-ai":"^0.83.0","@earendil-works/pi-tui":"^0.83.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@earendil-works/pi-coding-agent":"^0.83.0","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.5.3_1789059823997_0.04155424595809354","host":"s3://npm-registry-packages-npm-production"}},"1.5.4":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.5.4","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.5.4","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"extensions":["./dist/index.js"]},"dist":{"shasum":"d34091bd46a93711af834b7a33f4082dcce80fd5","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.5.4.tgz","fileCount":9,"integrity":"sha512-JxwXD7S/WW0UzCxTNOoBpu/D4EKhJ/3d7bPmG4YNaHhmjF0lH7hxcv8AjcxL9oLWMevtAQv3LSeRQSvD06/vdA==","signatures":[{"sig":"MEUCIB4wpR9aZYLS+c4N/dWC05CDLF3LN7jdnqnbuc1kVeWJAiEA8DfvgnIOw+5k0/9VQLdBpsJzAf/10NIbdToui+3bEqI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIGfOQrXhy48wh8kR8rJiJZtDogJy1Y3xBeglQz2oRwIUAiEAh+aTijGj3cq3/BFLn4pfJXyf6X3O+csXItAz+f+W7yc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.5.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":589084},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"483ad25e967ccd5b6a9c7bb7795ea999c0ec22c8","private":false,"scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:@earendil-works/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"if [ -z \"$(git tag --points-at HEAD 2>/dev/null)\" ]; then echo 'No git tag found at current commit, skipping publish'; exit 1; fi","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.19.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.20.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","@earendil-works/pi-ai":"^0.83.0","@earendil-works/pi-tui":"^0.83.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@earendil-works/pi-coding-agent":"^0.83.0","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.5.4_1789756204795_0.04787097843368637","host":"s3://npm-registry-packages-npm-production"}},"1.6.0":{"name":"@anierbeck/pi-model-dynamic-router","version":"1.6.0","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"author":{"name":"ANierbeck"},"license":"MIT","_id":"@anierbeck/pi-model-dynamic-router@1.6.0","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"pi":{"extensions":["./dist/index.js"]},"dist":{"shasum":"f460db4c19880fce43cf8be0110cd48cd65681e5","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.6.0.tgz","fileCount":9,"integrity":"sha512-g61YNowxSmba4AP3qC0GlWRs36fXQ7NAjAahWof6YiKyVxoFGlfby2YJi6nEFW+JZn5jCpg3gqOiyn3gWday3g==","signatures":[{"sig":"MEQCIDKa00VKtAuiHxtOPLso1QdpZV2LkZYx5VC1q7gUHYhOAiBkhljFwXPzY1JQYb/9t8H1VxQHd1QFP+3PZ+aDwXP7mg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIAFRVKH1sN0Eb7gAYfIT9ROlkcRSAx8GHPmHFpKVn+dxAiAkhkh6uHjYtK5YplWm/q2OTvCTZqOLXfRbWjMST0if4w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.6.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":718111},"main":"dist/index.js","type":"module","engines":{"node":">=20.11.0"},"gitHead":"0f8e51e7a6e6e41a106e1cde452b439758b5f76d","private":false,"scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:@earendil-works/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","audit:kpi":"node scripts/router-kpi-audit.ts","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"if [ -z \"$(git tag --points-at HEAD 2>/dev/null)\" ]; then echo 'No git tag found at current commit, skipping publish'; exit 1; fi","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"9962de05-f5ab-4685-9637-89c5889deab9"}},"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.19.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"_nodeVersion":"24.21.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.0.0","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","@earendil-works/pi-ai":"^0.83.0","@earendil-works/pi-tui":"^0.83.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@earendil-works/pi-coding-agent":"^0.83.0","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"tmp":"tmp/pi-model-dynamic-router_1.6.0_1791059368006_0.3415674305237977","host":"s3://npm-registry-packages-npm-production"}},"1.6.1":{"pi":{"extensions":["./dist/index.js"]},"_id":"@anierbeck/pi-model-dynamic-router@1.6.1","bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"dist":{"shasum":"090bec67101f026e40667a571b4a97603fd864d1","tarball":"https://registry.npmjs.org/@anierbeck/pi-model-dynamic-router/-/pi-model-dynamic-router-1.6.1.tgz","fileCount":9,"integrity":"sha512-vfWofC+hUAOxYUUs/WpKNMYIpef2eiFHBsxHKrHCakzrkSF1jXMNpgs8APe/XI4Bt3+qdYOk9AaBhERxqDD1oA==","signatures":[{"sig":"MEYCIQDTwyerjHa46RkuUDwPE15bPLXpGcnjv2aZ42eha+GhJwIhAMDB3T+tSK1J9VxGUkDEleKR0mGBgckG50mivAm/Zy6o","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFSIo145I63o6IP5VDNe3oLGsF9AYN5n7yjDKdhHJrRfAiEAtfenkfB4Rfi0obibKSQ9H7OYQxrYZ3WXVYN1kgcF+qA="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@anierbeck%2fpi-model-dynamic-router@1.6.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":712647},"main":"dist/index.js","name":"@anierbeck/pi-model-dynamic-router","type":"module","author":{"name":"ANierbeck"},"engines":{"node":">=20.11.0"},"gitHead":"35724d8e057aaf0890cc7c2be9d3a82e5c353c12","license":"MIT","private":false,"scripts":{"test":"vitest run","build":"tsc && esbuild index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --external:@earendil-works/pi-ai --external:@earendil-works/pi-coding-agent --external:@earendil-works/pi-tui --external:@sinclair/typebox && cp router-defaults.yaml router-config.json model-map.yaml dist/","prepack":"npm run build","prepare":"git rev-parse --is-inside-work-tree >/dev/null 2>&1 && git config core.hooksPath .githooks || true","audit:kpi":"node scripts/router-kpi-audit.ts","test:watch":"vitest","secret-scan":"node scripts/secret-scan.ts","test:coverage":"vitest run --coverage","prepublishOnly":"if [ -z \"$(git tag --points-at HEAD 2>/dev/null)\" ]; then echo 'No git tag found at current commit, skipping publish'; exit 1; fi","test:integration":"TEST_INTEGRATION=true vitest run test/classifier.test.ts"},"version":"1.6.1","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"9962de05-f5ab-4685-9637-89c5889deab9"}},"homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"_npmVersion":"11.19.0","description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","directories":{},"maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"_nodeVersion":"24.21.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.9.1","execa":"^9.6.1","eslint":"^10.5.0","vitest":"^3.2.6","esbuild":"^0.28.1","prettier":"^3.8.4","@eslint/js":"^10.0.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^25.9.2","@vitest/coverage-v8":"^3.2.6","@earendil-works/pi-ai":"^0.83.0","@stryker-mutator/core":"^10.0.0","@earendil-works/pi-tui":"^0.83.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.6","@typescript-eslint/parser":"^8.61.0","@stryker-mutator/vitest-runner":"^10.0.0","@earendil-works/pi-coding-agent":"^0.83.0","@typescript-eslint/eslint-plugin":"^8.61.0"},"peerDependencies":{"@sinclair/typebox":"*","@earendil-works/pi-ai":"*","@earendil-works/pi-tui":"*","@earendil-works/pi-coding-agent":"*"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pi-model-dynamic-router_1.6.1_1791130420661_0.42816634654986996"}}},"time":{"created":"2026-06-27T10:43:58.434Z","modified":"2026-10-04T16:13:41.057Z","1.1.0":"2026-06-27T10:43:58.798Z","1.1.1":"2026-06-27T10:55:18.650Z","1.1.2":"2026-06-27T18:31:51.460Z","1.1.3":"2026-06-28T11:47:43.071Z","1.1.4":"2026-06-28T12:08:04.496Z","1.1.5":"2026-06-28T12:14:38.199Z","1.1.6":"2026-06-28T12:46:04.134Z","1.1.7":"2026-07-25T18:11:14.801Z","1.2.0":"2026-07-30T06:00:55.294Z","1.2.1":"2026-07-30T07:45:46.696Z","1.3.0":"2026-08-14T16:12:42.898Z","1.3.1":"2026-08-14T17:03:52.426Z","1.4.0":"2026-08-17T08:46:40.265Z","1.4.2":"2026-08-28T06:58:03.355Z","1.5.0":"2026-08-28T10:45:20.786Z","1.5.1":"2026-09-03T16:23:53.057Z","1.5.2":"2026-09-10T16:15:11.130Z","1.5.3":"2026-09-10T17:03:44.255Z","1.5.4":"2026-09-18T18:30:04.905Z","1.6.0":"2026-10-03T20:29:28.079Z","1.6.1":"2026-10-04T16:13:40.763Z"},"bugs":{"url":"https://github.com/ANierbeck/pi-model-dynamic-router/issues"},"author":{"name":"ANierbeck"},"license":"MIT","homepage":"https://github.com/ANierbeck/pi-model-dynamic-router#readme","keywords":["pi","pi-extension","pi-package","model-router","llm","ai"],"repository":{"url":"git+https://github.com/ANierbeck/pi-model-dynamic-router.git","type":"git"},"description":"Pi extension that routes model groups to concrete models. Balances intelligence, cost, and availability.","maintainers":[{"name":"anierbeck","email":"bcanhome@googlemail.com"}],"readme":"# pi-model-dynamic-router\n\n> Pi extension that routes model group names to concrete provider/model pairs. Auto-discovers models and pricing. Balances intelligence (GDPval), cost, and availability.\n\n> **Fork of [`a-canary/pi-model-router`](https://github.com/a-canary/pi-model-router)** — adds content-based dynamic routing (prompt classification → model group) on top of the upstream's price/quality/availability routing.\n\n## What You Get\n\n- **Content-aware routing** — every prompt is classified (trivial one-liner up to\n  architecture review) and routed to a matching model tier: free models for daily\n  coding, top-tier models reserved for design and planning work.\n- **Automatic discovery & pricing** — the router scans local daemons (Ollama,\n  LM Studio) and cloud catalogs, and scrapes Artificial Analysis benchmark\n  scores (GDPval plus per-task capability columns: AA-Briefcase Elo for\n  planning work, SciCode/Terminal-Bench for coding).\n- **Cost & quota balancing** — flat-fee subscription models carry the daily\n  load so expensive top-tier models stay available; quality-equivalence\n  windows pick the cheapest model that is just as good.\n- **Failover everywhere** — model backoff, provider cooldowns, cascading\n  fallback groups, and mid-stream retries are all transparent: the session\n  just continues on the next best model.\n\n## Quick Start\n\n```bash\npi install npm:@anierbeck/pi-model-dynamic-router\n# Development checkout instead:\nln -s ~/pi-model-dynamic-router ~/.pi/agent/extensions/pi-model-dynamic-router\n```\n\n1. **Add a provider key** — keys live with Pi, not with the router\n   (see [Adding a Provider](#adding-a-provider)).\n2. Run `/reload` in pi — the router discovers models, prices, and quality\n   scores automatically.\n3. Switch your session to a group: `scout`, `operational`, `tactical`,\n   `strategic`, `planning`, or `dynamic` (content-classified per prompt).\n\nThe shipped defaults need no configuration. To personalize, see\n[Personalized configuration](#personalized-configuration) below — or jump\nstraight to [How It Works](#how-it-works).\n\n## Personalized configuration\n\nUsers can override the embedded defaults without editing extension files:\n\n- **Global**: `~/.pi/agent/router-config.user.json`\n- **Project-local**: `<project>/.pi/router-config.json`\n\nSupports `exclude` rules (no paid OpenRouter models, no Fable, etc.).\nSee [`docs/config-override.md`](docs/config-override.md) for details.\n\n## How It Works\n\n### Dynamic Routing\n\nThe **dynamic routing** feature automatically classifies user prompts and selects the optimal model group based on the task type. It uses a classifier chain (cloud-first with `classifier_cloud_fallback: true`, Ollama **mistral-nemo:latest** primary / **gemma2:2b** fallback as the local last resort) and routes by the `CATEGORY_TO_GROUP` table in `src/content-classifier.ts`: `trivial`/`exploration` → `scout`, `simple`/`standard` → `operational`, `code_simple` → `simple`, `code_complex`/`fallback` → `tactical`, and `design`/`planning` → `planning` (top tier only).\n\n#### Categories for Classification\n\nThe system classifies prompts into the following categories (see `CATEGORY_TO_GROUP` in `src/content-classifier.ts` for the authoritative mapping):\n\n- `trivial`: Greetings, one-liners, questions about the router itself\n- `simple`: Simple conversational requests\n- `standard`: Everyday tasks with no special shape\n- `code_simple`: Simple code changes (1-10 lines, syntax fixes, typos)\n- `code_complex`: Complex code changes (refactoring, debugging, >50 lines)\n- `design`: Architecture, system design, API design\n- `planning`: Project planning, roadmaps, task breakdown\n- `exploration`: Research, unclear requirements, brainstorming\n- `fallback`: Unclear or multiple categories apply\n\n#### Mapping of Categories to Model Groups\n\nEach category maps to a specific model group (`CATEGORY_TO_GROUP`, `src/content-classifier.ts`). Two tier-routing mechanisms (ADR-0023) keep the tiers meaningful despite GDPval compression at the top of the score range: a per-group **`max_gdpval`** cap (the shipped `tactical` is capped at 1700, so the flat-fee Mistral tank — glm-5-3 at 1644 — carries the daily `code_complex` load, while Claude's top models stay in `strategic`), and a **`best_quality_window`** (default 5%): inside a `best`-method group, candidates within the window of the best score are treated as equally good — cheapest first, cost ties to the lower score (in `strategic`, claude-sonnet-5-5 beats claude-opus-5-5 at equal subscription cost). On Mistral-quota days, `tactical` escalates through its `fallback_groups` into `strategic`, so Claude takes over automatically. Since the AA multi-benchmark round, a group can additionally rank its pool task-type-aware via **`score_by`** (ADR-0023 round 2): the same Artificial Analysis scrape that yields GDPval also carries the per-benchmark columns, so `planning` ranks by AA-Briefcase Elo and `tactical` by the SciCode/Terminal-Bench coding blend — floors and caps stay on GDPval, and a missing column falls back to GDPval ordering.\n\n| Category | Model Group | Use Case |\n|----------|-------------|----------|\n| `trivial` | scout | Greetings, one-liners — any free model |\n| `simple` | operational | Simple conversational requests |\n| `standard` | operational | Everyday tasks (GDPval ≥ 300) |\n| `code_simple` | simple | Simple coding tasks (GDPval ≥ 300, free models only) |\n| `code_complex` | tactical | Complex coding tasks (GDPval ≥ 600, capped at 1700 — the free-tank tier) |\n| `design` | planning | High-stakes design decisions — top tier only (GDPval ≥ 1700) |\n| `planning` | planning | Project planning and architecture — top tier only (GDPval ≥ 1700) |\n| `exploration` | scout | Research and exploration — any model, cheap |\n| `fallback` | tactical | Uncertain classification — a decent model, not a free one |\n\n#### Dynamic Group\n\nThe **`dynamic`** group is a special group that classifies each prompt in real-time (cloud chain first, Ollama as last resort: **mistral-nemo:latest** primary, **gemma2:2b** fallback) and automatically routes to the most appropriate model group via the `CATEGORY_TO_GROUP` table — `scout`, `operational`, `simple`, or `tactical` (`strategic` is not a classification target; `planning` is — design/planning prompts route to the top-tier-only planning group, never to the free-tank tactical tier). This enables **context-aware model selection** without manual intervention.\n\n**Requirements for Dynamic Routing:**\n\nTo use the **`dynamic`** group, you need:\n- **Ollama** installed and running locally (`ollama serve`)\n- **mistral-nemo:latest** pulled for best classification quality (`ollama pull mistral-nemo:latest`)\n- **gemma2:2b** pulled as fallback (`ollama pull gemma2:2b`) — used automatically if mistral-nemo:latest fails\n- Ollama accessible from your system (default: `http://localhost:11434`)\n\nCloud-first (2026-09-27): with `classifier_cloud_fallback: true` (set on the\nshipped `dynamic` group) the classifier tries a chain of free cloud models\nFIRST — pinned `classifier_cloud_model` → scan-time probe-verified list →\ntiered discovery → configured free models — and treats Ollama as the last\nresort (see [Data handling & privacy](#data-handling--privacy)). If every\ncloud candidate fails and Ollama is unavailable, the classifier falls back\nto static keyword-based classification (only if `allowStaticFallback` is\nenabled) — otherwise the category `fallback` is returned.\n\n---\n\n### Cascading Fallback & Intelligent Routing\n\nThe router implements a **multi-layer fallback system** that automatically recovers from model failures, rate limits, and unavailability.\n\n#### How it works\n\nWhen a model fails (API error, rate limit, empty response, or usage limit exceeded), the router:\n\n1. **Tries the next model in the same group**\n2. **If all models in the group fail → cascades to fallback groups** in this order:\n   ```\n   strategic → tactical → operational → scout → fallback\n   ```\n3. **Continues until a working model is found**\n\n**Example:** You select `dynamic` group, but the first model hits a rate limit → router automatically tries the next `dynamic` model → if all fail, tries `strategic` → then `tactical` → etc.\n\n#### Configuration\n\nEach group can define its fallback chain in `router-config.json`:\n\n```json\n{\n  \"strategic\": {\n    \"description\": \"Best models by GDPval\",\n    \"method\": \"best\",\n    \"models\": [\"anthropic/claude-3-sonnet\", \"mistral/mistral-medium-3.5\"],\n    \"fallback_groups\": [\"tactical\", \"operational\", \"scout\", \"fallback\"]\n  }\n}\n```\n\n**Note:** The `dynamic` group automatically inherits the full cascade chain.\n\n---\n\n### Group-Based Cost/Quality Routing\n\nThe router encodes cost-quality tradeoffs directly in **model groups** rather than a\nseparate tier overlay. Each group's `min_gdpval` and `max_cost` settings act as the\ncost tier: `trivial`/`simple` use `max_cost: 0` (free models only), `scout`/`fallback`\nuse `min_gdpval: 0` (anything), `tactical`/`strategic` raise the GDPval floor to\n600/700. A classified prompt maps to a group via `CATEGORY_TO_GROUP`\n(content-classifier.ts), and the group's own filters do the rest.\n\nA group may also set `min_context_length` to require a minimum model context\nwindow (in tokens). Models whose scanned context window is unknown or below\nthe threshold are dropped — strict, like `min_gdpval`. This lets a\nuse-case-specific group (e.g. `bulk_reader`) guarantee its cheap models can\nactually hold the large inputs the use case demands, instead of falling back\nto the smallest free model that would truncate.\n\n> **Historical note:** an earlier separate \"Cost Tier System\" (free/budget/premium\n> buckets, `src/cost-tiers.ts`) existed as a second filter layer on top of groups,\n> but was removed — it was redundant with the group thresholds and conflicted\n> with local models and the fallback cascade (see git history, commit that\n> removed it: \"remove cost-tier overlay from dynamic routing\").\n\n---\n\n### Model Momentum\n\nAfter **context compaction** (when >30% of tokens are removed or >5 messages/500 tokens are dropped), the router **reuses the previous model** for the next turn.\n\n#### Why?\n\n- **Consistency:** Maintains the same model's \"thinking style\" after major context changes\n- **Efficiency:** Avoids unnecessary model switching\n- **Stability:** Reduces variation in responses during long conversations\n\n#### Detection\n\nCompaction is automatically detected when:\n- Token count drops by >30% compared to previous turn\n- More than 5 messages are removed\n- More than 500 tokens are removed\n\n**Note:** Model momentum only forces reuse during compaction. For similar tasks, it provides a **hint** to the classifier.\n\n---\n\n### Status Line Integration\n\nThe router now **synchronizes with Pi's status line** to display the **actually active model** (not failed candidates).\n\n#### Behavior\n\n- Status line updates **as soon as a model's stream is established** (before the first token)\n- Only shows models that **successfully started streaming**\n- Failed candidates (API errors, rate limits) **never appear** in the status line\n- After successful completion, the model remains displayed until the next turn\n\n#### Example\n\n```\n# Before (incorrect):\nscout/dynamic→claude-bridge/claude-fable-5  # ← Failed, but shown!\n\n# After (correct):\nscout/dynamic→mistral/mistral-medium-3.5   # ← Actually active model\n```\n\nThis provides **accurate feedback** about which model is currently generating responses.\n\n---\n\n### Auto-Discovery\n\nOn startup, the router automatically:\n\n1. **Resolves nothing credential-related itself (ADR-0022).** The router never reads or writes Pi's credential store — Pi resolves API keys (auth.json incl. `!` secret-manager commands, models.json, env, CLI OAuth) via `modelRegistry.getApiKeyForProvider` whenever a router-internal path needs one (free-model registration, free-cloud fallback). If a key lives in a `pass` store or a shell command, reference it from Pi's own auth.json and Pi executes it.\n2. **Scans local models** (Ollama / LM Studio) and OpenRouter's public pricing catalog; cloud model inventory comes from Pi's catalog (ADR-0021 — the router registers no scan-discovered cloud models)\n3. **Scrapes GDPval scores** from [Artificial Analysis](https://artificialanalysis.ai/evaluations/gdpval-aa) with hardcoded fallbacks — a plain, unauthenticated GET of a public leaderboard page; no local data is sent\n4. **Caches pricing** per provider/model from OpenRouter's public pricing endpoint\n\nAll scanning is async and non-blocking.\n\n### Data handling & privacy\n\n- **The router never reads or writes Pi's credential store (ADR-0022).** No API key, key reference, or auth-file pointer is stored in `router-config.json` or resolved by the router — Pi owns credential resolution end-to-end. (Legacy `keys` arrays in older router-config files are ignored, never read.)\n- **Prompt content stays local by default.** The dynamic-group content classifier runs against a local Ollama model. If both local classifier models are unavailable, it falls back to static keyword matching (only if `allowStaticFallback` is enabled) rather than sending anything externally.\n- **Optional cloud classifier fallback (`classifier_cloud_fallback`, off by default).** If explicitly enabled in `router-config.json`, and only as a last resort when local classification fails, the raw prompt is sent to a free cloud model from your own configured `free_models` for classification purposes. This is opt-in and separate from using that same provider as a normal answering fallback, because classification and answering have different data-exposure implications for the same free-model config. Enable only if you're comfortable with that provider seeing prompt content for classification, not just for answering your requests.\n- **GDPval scraping and pricing/model scans are outbound-only, read-only HTTP GETs** to public model/leaderboard endpoints; no prompt content, API keys, or other local data is included in those requests.\n\n---\n\n### Group Selection\n\nEach group auto-discovers available models, filters by quality, and selects\nby billing preference. The shipped groups (see `router-config.json`):\n\n| Group | Method | Quality gate | Ranks within the pool by | Use for |\n|-------|--------|--------------|--------------------------|---------|\n| **strategic** | `best` | GDPval ≥ 700 | GDPval (quality window) | Critical decisions |\n| **planning** | `best` | GDPval ≥ 1700 | AA-Briefcase Elo (`score_by`) | Design & architecture — top tier only |\n| **tactical** | `best` | 600 ≤ GDPval ≤ 1700 | SciCode/Terminal-Bench blend (`score_by`) | Daily coding — the free-tank tier |\n| **operational** | `tiered` | GDPval ≥ 300 | billing preference | Everyday tasks |\n| **scout** | `tiered` | GDPval ≥ 0 | billing preference | Exploration, cheap work |\n| **fallback** | `tiered` | GDPval ≥ 0 | billing preference | Last resort |\n| **dynamic** | `dynamic` | — | — | Auto-classifies each prompt and routes to the best group |\n\nNo curated model lists. Groups draw from all discovered models automatically.\nQuality gates (`min_gdpval`/`max_gdpval`) always use GDPval — a `score_by`\ncolumn only orders the models **within** an admitted pool.\n\n#### GDPval\n\nGDPval is a composite quality score from [Artificial Analysis](https://artificialanalysis.ai/evaluations/gdpval-aa) that combines intelligence, throughput, and cost-efficiency into a single number. Higher = better overall value. The router scrapes scores **once** on first run and caches them; subsequent startups use the cache. Use `/router scan` to force a refresh. Hardcoded fallbacks from `gdpval_builtin` in the config are always loaded as a baseline.\n\n#### Price Routing — how `tiered` works\n\n1. **Filter** — discard any model below the group's GDPval percentile threshold.\n2. **Sort** — rank survivors by billing tier first, then by effective cost within each tier:\n   - Tier 0: free models\n   - Tier 1: subscription (lowest rate-limit pressure first, then cost)\n   - Tier 2: local (Ollama / LM Studio)\n   - Tier 3: pay-per-token (ascending effective cost)\n3. **Select** — pick the top-ranked model (cheapest within the preferred billing tier that clears the quality floor).\n\nThis means `operational` picks the cheapest model that clears its GDPval floor, while `strategic` ranks by `best` (highest score, quality window applied) regardless of cost.\n\n#### costMux\n\nAfter 4 consecutive HTTP 429s from a provider, the router applies a permanent **cost multiplier penalty** (`costMux`) to all its models. This pushes the provider to the back of the sorted list without blocking it entirely — useful when a provider is temporarily overloaded but still reachable. The penalty persists for the session and is reset on `/router reload`.\n\n---\n\n### Rate Limits & Failover\n\nOn HTTP 429 the router works through two escalating responses:\n\n1. **Model backoff** — the model enters exponential backoff (1 min → 2 → 4 → ... → 90 min cap) and the group falls over to its next-ranked candidate for the current request. (ADR-0022 removed multi-key rotation — with keys owned and resolved by Pi there is exactly one key per provider.)\n2. **costMux penalty** — after 4 consecutive 429s, the provider receives a permanent cost multiplier for the session (see [costMux](#costmux) above), demoting all its models in future selections.\n\nBoth mechanisms are transparent to the user — the session continues with the next available model.\n\n#### Rate Limit & Subscription Handling\n\nThe router automatically handles **rate limits, usage limits, and subscription errors** from all providers, including third-party extensions like **claude-bridge**.\n\n##### Supported Error Patterns\n\n| Error Type | Detection | Behavior |\n|------------|-----------|----------|\n| **Rate Limit (429)** | HTTP 429 response | Soft failure → try next model |\n| **Usage Limit Exceeded** | \"out of usage credits\", \"rate limit hit\" | Soft failure → try next model |\n| **API Provider Not Found** | \"No API provider registered\" | Soft failure → try next model |\n| **Empty Response** | No tokens within timeout | Soft failure → try next model |\n| **Hard API Error** | Connection refused, timeout | Soft failure → try next model |\n\n##### Example: Claude Subscription Limits\n\nIf you hit your Claude subscription limit:\n\n```\nWarning: [rate-limit] Claude unknown rate limit hit — resets unknown\nYou're out of usage credits. Run /usage-credits to keep using Fable 5\n```\n\n**The router will:**\n1. Detect the \"out of usage credits\" message\n2. Treat it as a **soft failure** (not a hard error)\n3. **Automatically try the next model** in the group\n4. If all models in the group fail → cascade to fallback groups\n\n##### Important Notes\n\n- **Claude-bridge:** Different subscription tiers have different model access:\n  - **Pro:** Claude 3.5 Sonnet, Haiku\n  - **Max:** All models including Fable 5, Opus 5\n- **The router cannot know your subscription tier** — it tries models and falls back on errors\n- **This is intentional:** It allows graceful degradation when limits are hit\n\n##### Best Practices\n\n1. **Order models by preference** in your groups (most preferred first)\n2. **Include fallback models** from different providers\n3. **Use cascading fallback groups** for maximum reliability\n4. **Check `/usage-credits`** if you consistently hit limits\n\n**Example configuration for reliability:**\n\n```json\n{\n  \"strategic\": {\n    \"models\": [\n      \"claude-bridge/claude-opus-5\",    // First choice (Max only)\n      \"claude-bridge/claude-sonnet-5\",  // Fallback (Pro/Max)\n      \"anthropic/claude-3-5-sonnet\",    // Cloud fallback\n      \"mistral/mistral-medium-3.5\"     // Final fallback\n    ],\n    \"fallback_groups\": [\"tactical\", \"operational\", \"scout\", \"fallback\"]\n  }\n}\n```\n\nThis ensures **automatic recovery** when subscription limits are hit.\n\n---\n\n### Stream Retry\n\nWhen a streaming response fails mid-stream (empty body, connection drop, timeout), the group automatically retries with the next ranked candidate without requiring the user to resend the prompt. Soft failures are distinguished from hard errors: a 4xx response is not retried, but an interrupted stream or empty response is.\n\n---\n\n### Delegating subtasks to cheap groups (Pi subagents)\n\nThe router only resolves a group name to the best available model *for a single completion request* — it has no concept of \"split this task into subtasks and run the cheap ones on a cheap model.\" That kind of decomposition belongs one layer up, in [Pi's subagent system](https://github.com/earendil-works/pi-subagents), which can already address any configured group directly by model ref: every group is registered as its own provider, so `<group>/<group>` (e.g. `trivial/trivial`, `scout/scout`, `strategic/strategic`) is a valid `model` value for `subagent(...)` calls, exactly like any other provider/model pair Pi knows about.\n\nThis lets you fan out I/O-heavy work (reading/summarizing several files) to a cheap group in parallel, then run one expensive-group call over the collected results — without any router code changes:\n\n```js\nsubagent({\n  workflowScript: `\n    const files = [\"src/a.ts\", \"src/b.ts\", \"src/c.ts\"];\n    const summaries = await runs.all(files.map((f) => ({\n      key: f,\n      agent: \"scout\",\n      model: \"trivial/trivial\",\n      task: \"Summarize the public API of \" + f,\n    })));\n    const combined = summaries.map((s) => s.output).join(\"\\\\n\\\\n\");\n    return runs.run(\"synthesize\", {\n      agent: \"worker\",\n      model: \"strategic/strategic\",\n      task: \"Given these file summaries, propose a refactor:\\\\n\\\\n\" + combined,\n    });\n  `,\n});\n```\n\nThe `bulk_reader` group adds a `min_context_length` floor so the cheap model\nit resolves to is guaranteed to hold several files at once — exactly the\nproperty a trivial/scout group does *not* guarantee:\n\n```js\nsubagent({\n  workflowScript: `\n    const files = [\"src/a.ts\", \"src/b.ts\", \"src/c.ts\", \"src/d.ts\"];\n    const summaries = await runs.all(files.map((f) => ({\n      key: f,\n      agent: \"scout\",\n      model: \"bulk_reader/bulk_reader\",\n      task: \"Read \" + f + \" and return a bullet list of its public API.\",\n    })));\n    return runs.run(\"synthesize\", {\n      agent: \"worker\",\n      model: \"strategic/strategic\",\n      task: \"Given these file summaries, propose a refactor:\\\\n\\\\n\" +\n        summaries.map((s) => s.output).join(\"\\\\n\\\\n\"),\n    });\n  `,\n});\n```\n\n`code_writer/code_writer` is the symmetric counterpart: a cheap group with\nenough context for a spec + one reference file, used to generate boilerplate\nthe expensive model never has to read back as output tokens.\n\nSee [docs/adr/0007-task-decomposition-and-delegation.md](docs/adr/0007-task-decomposition-and-delegation.md) for why this lives in the subagent layer rather than in the router.\n\n## Configuration\n\n### Main Configuration File\n\n`router-config.json`:\n\n```jsonc\n{\n  \"providers\": {\n    \"openrouter\": {\n      \"billing\": \"pay_per_token\",\n      \"free_models\": [\n        \"openrouter/qwen/qwen3-4b:free\",\n        \"openrouter/openai/gpt-4o-mini:free\"\n      ]\n    }\n  },\n  \"model_groups\": {\n    \"strategic\": { \"method\": \"best\" },\n    \"tactical\": { \"method\": \"tiered\", \"min_gdpval_pct\": 75 },\n    \"scout\": { \"method\": \"tiered\", \"min_gdpval_pct\": 25 }\n  },\n  \"model_metrics\": {\n    \"claude-bridge/claude-sonnet-5\": { \"cost_per_m\": 0.0000015 }\n  },\n  \"gdpval_builtin\": {\n    \"mistral-medium-3-5\": 933,\n    \"claude-sonnet-5\": 1603,\n    \"qwen3-8-27b\": 580\n  }\n}\n```\n\n#### Billing Preference (per-group tier override)\n\nBy default, `method: \"tiered\"` sorts by billing tier first: **free → subscription → local → payg**. This means already-paid subscription models (e.g. Mistral) always rank ahead of local compute (Ollama), even in scout where local models conceptually belong on top.\n\n`billing_preference` re-ranks a group by billing tier after its `method` has ordered the candidates. It does not change which models passed the filters — only their order. Five values:\n\n| Value | Ordering | Use for |\n|-------|----------|---------|\n| `\"default\"` (or omitted) | free → subscription → local → payg | Groups where an already-paid subscription model is the cheaper choice in time/quota terms. |\n| `\"cloud_first\"` | cloud (free → subscription) ahead of local | Groups that should prefer cloud models — the local daemon is a fallback, not the default (scout / bulk_reader / code_writer). |\n| `\"local_first\"` | free → local → subscription → payg | Groups where local models should rank ahead of subscription, but genuinely-free remote models still win. |\n| `\"local_before_payg\"` | free → subscription → local → payg | Cheap groups (trivial / simple): free and subscription first, local ahead of pay-as-you-go only. |\n| `\"strict_local\"` | local → free → subscription → payg | Groups where the local daemon should answer **first**, ahead of even the $0 remote models. Not used by the shipped config (a guard test forbids it there). |\n\n`payg` is always last. This is opt-in per group — other groups keep the default ordering. The shipped config pins: `trivial`/`simple` → `local_before_payg`, `scout`/`bulk_reader`/`code_writer` → `cloud_first`.\n\n> **A subscription model's $0 cost is not free.** Flat-rate plans like pi-claude hide a hard time/token limit, so a trivial prompt routed there is the single most expensive thing the router can do. Prefer a local model or a genuine `:free` model for cheap work.\n\n#### Agent-capability filter (`non_agent_model_prefixes`)\n\nModels whose ref starts with one of these prefixes are excluded from all\nrouting groups (they remain selectable as plain chat models). GDPval scores\ncapability, not agent-reliability — raw chat/completion/audio families\n(e.g. `voxtral-`, `ministral-`) must never win a routing slot over an\nagent-capable model, whatever their benchmark score. The shipped default:\n\n```json\n\"non_agent_model_prefixes\": [\n  \"mistral-small-\",\n  \"magistral-small-\",\n  \"ministral-\",\n  \"voxtral-\",\n  \"codestral-\"\n]\n```\n\nReplace the array in any config layer to change the filter.\n\n```json\n\"scout\": {\n  \"method\": \"tiered\",\n  \"billing_preference\": \"cloud_first\",\n  \"min_gdpval\": 0\n},\n\"trivial\": {\n  \"method\": \"tiered\",\n  \"billing_preference\": \"local_before_payg\",\n  \"min_gdpval\": 0\n}\n```\n\n#### Read Delegation (bulk reads)\n\nDelegation has two halves. A large `read`/`bash` result is **replaced** by a summary produced by a cheap `bulk_reader` group, and a full-file `read` is **blocked before it runs** and redirected to a targeted `offset`/`limit` read. Both are off by default.\n\n```json\n\"delegation\": {\n  \"enabled\": true,\n  \"group\": \"bulk_reader\",\n  \"tools\": [\"read\", \"bash\"],\n  \"min_chars\": 3500,\n  \"max_raw_chars\": 60000,\n  \"block_lines\": 350,\n  \"expensive_groups\": [\"strategic\", \"tactical\"],\n  \"expensive_providers\": []\n}\n```\n\n| Key | Default | Meaning |\n|-----|---------|---------|\n| `enabled` | `false` | Master switch. Anything other than `true` is off (fail-open). |\n| `group` | `\"bulk_reader\"` | Model group that produces the summary. Must be registered as a group so the router intercepts it. |\n| `tools` | `[\"read\", \"bash\"]` | Result-bearing tools to delegate. Trusted **as a whole**: a non-array, or any non-string/empty entry, discards the entire list and uses the default. |\n| `min_chars` | `3500` | Minimum result size before delegating. Below this, delegation latency exceeds the savings. |\n| `max_raw_chars` | `60000` | Cap on result text sent to the sub-call. |\n| `block_lines` | `350` | Pre-call block for full-file reads. `0` **disables** pre-call blocking explicitly; negative or non-numeric falls back to the default. |\n| `expensive_groups` | `[\"strategic\", \"tactical\"]` | Groups whose models may not do full-file reads. Matched against the **active** config's materialized model lists. |\n| `expensive_providers` | `[]` | Provider **prefixes** whose models count as expensive regardless of group — e.g. `[\"pi-claude\"]` matches `pi-claude/claude-sonnet-5`. |\n\nTargeted reads (any `read` with `offset` or `limit`) and piped/grep'd `bash` commands are **always** delegated around — they are precise extracts the orchestrator needs verbatim, and the pre-call block never fires for them.\n\nGroup and provider lists are trusted as a whole, like `tools`: a partially-valid list would silently block or delegate the wrong models, so an invalid list falls back to the default and an empty list (`[]`) genuinely disables the check. A config whose `model_groups` have no materialized `models` arrays (static-only) matches nothing on `expensive_groups` — the size threshold still protects on its own.\n\nThe expensive-model pre-call block judges the caller by the **driving** model of the turn — the first model that streamed — not by whatever model streamed most recently. A nested `bulk_reader` sub-call mid-turn does not un-block the expensive model's subsequent reads.\n\n#### Provider Configuration\n\n**Provider registration is conservative (never overwrites):** the router only registers a provider with Pi when Pi does **not** know it yet — i.e. when `modelRegistry.find(provider, modelId)` returns nothing for every model of that provider. This protects `models.json` entries (with `compat` flags), extension-provided providers, and Pi-native providers from being clobbered. If Pi already knows the provider from any source, the router does not touch the registration.\n\n**Real per-model capabilities (not hardcoded):** when the router does register a provider, it uses the **real** capabilities the scan captured from the provider's `/v1/models` (Mistral `capabilities.vision/reasoning`/`max_context_length`, OpenRouter `architecture.input_modalities`/`context_length`) — never a hardcoded `reasoning: true / input: ['text','image']` blanket. Unknown fields fall back to conservative defaults (`vision: false` unless confirmed, `reasoning: false` unless confirmed), so a model is never falsely advertised as vision-capable (which caused 422 errors for GLM-5-2).\n\n**Ollama is setup-independent:** the router scrapes Ollama's `/api/show` per model to get the real context length (`model_info.*.context_length`) and capabilities, and registers Ollama with `providerOptions.num_ctx` set to that real value — so prompts >32K don't get truncated. This works for **every** user, with or without any specific Ollama extension. The router only registers Ollama when Pi doesn't know it; if another extension or `models.json` already registered Ollama, the router doesn't overwrite.\n\n**Per-provider model filter (optional, generic):** a `PROVIDER_MAP` entry may set `modelFilter: \"<regex>\"` to constrain which scanned model ids are kept. Generic and user-configurable — not a hardcoded special case. Useful when a key sees a broad catalog (e.g. a provider key that returns all of a vendor's models when the provider is meant for a subset).\n\n#### New Configuration Options\n\n| Option | Purpose | Example |\n|--------|---------|---------|\n| **`fallback_groups`** | Define cascade chain for fallback | `[\"tactical\", \"operational\", \"scout\"]` |\n| **`cost_per_m`** | Cost per million tokens (for estimates) | `0.0000015` |\n| **`model_metrics`** | Per-model cost overrides | `{ \"claude-bridge/claude-sonnet-5\": { \"cost_per_m\": 0.0000015 } }` |\n| **`gdpval_builtin`** | GDPval overrides for new models (keyed by **slug**) | `{ \"mistral-medium-3-5\": 933, \"qwen3-8-27b\": 580 }` |\n| **`billing_preference`** | Per-group tier ordering override (`\"local_first\"` / `\"strict_local\"` rank local models ahead of subscription) | `\"local_first\"` |\n| **`modelFilter`** (PROVIDER_MAP) | Regex to constrain scanned model ids per provider | `\"^(zai-)?glm\"` |\n\nGroups need no `models` arrays — everything is auto-discovered **plus** any explicitly listed models.\n\n### Adding a Provider\n\nThe router has **no credential storage of its own** (ADR-0022): API keys live\nexclusively with Pi, and Pi resolves them whenever a request needs one. Adding\na provider is therefore a single step — give the key to Pi:\n\n1. **Store the key where Pi looks for it** — any one of:\n   - `pi auth <provider>` (Pi's built-in auth)\n   - an environment variable (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, …)\n   - an entry in Pi's `auth.json` — including a `!`-prefixed secret-manager\n     command (e.g. `!pass show api/openrouter`); Pi executes it\n   - CLI OAuth for CLI-auth providers (`qwen auth login`, `gemini auth login`)\n2. **Restart pi** — the router picks up everything Pi knows, scans local\n   daemons and cloud catalogs, and the provider's models start competing in\n   your groups automatically.\n\nFor a non-standard base URL or model list, configure the provider in Pi's\n`models.json` — the router never overwrites an existing Pi registration\n(see [Provider Configuration](#provider-configuration) below).\n\nThere is a guided walkthrough in the shipped skill: `/skill:router-login`\n(same steps, with connectivity checks and troubleshooting).\n\n### Supported Providers\n\n**26 known providers** (the router's `PROVIDER_MAP`, below) — plus any\nextension-registered provider (e.g. claude-bridge), which the router\ndiscovers automatically.\n\n| Provider | Type | Registration | Notes |\n|----------|------|--------------|-------|\n| **anthropic** | Built-in | Pi | Token-based (via claude-bridge extension for subscription) |\n| **openai** | Built-in | Pi | Standard OpenAI |\n| **google** | Built-in | Pi | Google AI |\n| **mistral** | Built-in | Pi | Mistral Cloud |\n| **openrouter** | Router | Router | **Free tier models available** |\n| **ollama** | Extension | Extension | Local models |\n| **lm-studio** | Extension | Extension | Local models |\n| **claude-bridge** | Extension | Extension | **Claude subscription via local proxy** |\n| **qwen-cli** | Extension | Extension | Qwen CLI |\n| **gemini-cli** | Extension | Extension | Google Gemini CLI |\n| **antigravity** | Extension | Extension | - |\n| **chutes** | Router | Router | Free tier models available |\n| **mistral-zai** | Router | Router | Mistral via Z.AI |\n| **groq** | Router | Router | Fast inference, free tier |\n| **cerebras** | Router | Router | Fast inference |\n| **xai** | Router | Router | xAI (Grok) |\n| **zai** | Router | Router | Z.AI |\n| **huggingface** | Router | Router | - |\n| **kimi-coding** | Router | Router | - |\n| **minimax** | Router | Router | - |\n| **minimax-cn** | Router | Router | - |\n| **opencode** | Router | Router | - |\n| **opencode-go** | Router | Router | - |\n| **vercel-ai-gateway** | Router | Router | - |\n| **azure-openai** | Router | Router | - |\n| **deepseek** | Router | Router | - |\n| **github-copilot** | Router | Router | Subscription |\n\n**Claude-bridge Support:**\n- **Important:** Claude-bridge is a **separate Pi extension** that must be installed to use Claude models with a subscription.\n- **How it works:** The extension registers `claude-bridge/*` models with Pi. The router **discovers and uses** them automatically.\n- **Model availability** depends on your Claude subscription plan (Pro, Max, etc.).\n- **No double registration:** The router **does not** register claude-bridge providers itself — it only uses models already registered by the extension.\n\n## Commands\n\n| Command | Description |\n|---------|-------------|\n| `/router` | Overview: providers, groups, selections, rate limits |\n| `/router <group>` | Detailed view of a group with ranked candidates |\n| `/router scan` | Re-scan models and GDPval scores |\n| `/router cost` | Audit-depth cost report: per-model, per-window usage from the router's own token accounting |\n| `/router errors [n]` | Last n session errors (default 15, max 50) with status-line correlation |\n| `/router cooldowns [clear]` | Active rate-limit cooldowns (ref, remaining, hits); `clear` also resets model-health streaks |\n| `/router blocklist` | Models blocked after a permanent provider failure (reason, since, re-probe time) |\n| `/router blocklist clear [ref]` | Unblock one model, or all (e.g. after fixing an API key) |\n| `/router reload` | Hot-reload config and cache |\n\n### Logging\n\nThe router logs to `~/.pi/logs/router.log` (mirrored to `<project>/.pi/logs/router.log`).\nEach file rotates at 20 MB into `router.log.1` … `router.log.4`; older data is dropped.\n\nLog levels, from quiet to verbose: `\"error\"` (hard failures only) → `\"warn\"`\n(adds operational problems: rate limits, failed models, fallbacks, wedges) →\n`\"info\"` (adds routine narration and the per-prompt routing trace) → `\"debug\"`\n(adds `[diag]` lines). Set `\"log_level\"` in `~/.pi/agent/router-config.user.json`,\nor via `ROUTER_LOG_LEVEL` (overrides the config). The shipped default is `\"warn\"`\n(release builds must not be verbose); local dev typically sets `\"info\"` or\n`\"debug\"`. The recurring \"tryStream skipped\" line is written once per model\nuntil its reason changes.\n\n### KPI audit\n\n`npm run audit:kpi -- [--since 7d|24h|<ISO date>] [--log <path>] [--json]` summarizes\n`~/.pi/logs/router.log`: delegation savings and failures, blocked full-file reads,\nfailed hops by reason and model, total failovers, learned blocklist entries,\nwatchdog wedge events and classifier health.\n\n## Tools\n\n| Tool | Purpose |\n|------|---------|\n| `set_model_from_group` | Switch session to best model from a group |\n| `resolve_model_group` | Preview what a group would resolve to |\n| `update_model_metrics` | Manual metric override |\n| `bulk_read` | Answer a question about files via a cheap reader model, without loading their content into the session context |\n\nThe `dynamic` group has no dedicated tools — classification runs automatically\ninside the group's resolve path (`src/content-classifier.ts`).\n\n## Footer\n\n```\nstrategic/anthropic/claude-opus-4-6 | int:1450 tps:80 | 12k/8k $1.43 62% | ⏱14m | ⌂ proj | ⎇ main | ⛔2 | ⚠1 err\n```\n\nThe `⚠N err` part counts the session's recorded stream failures — the same\nentries `/router errors` lists in full.\n\n\n## Internals\n\nHow the code is organized — relevant if you work on the router itself,\nnot needed to use it.\n\nThe router uses a **modular architecture** with the following components:\n\n| Module | Purpose | Key Features |\n|--------|---------|--------------|\n| **providers.ts** | Provider definitions and mappings | 26 supported providers, authentication patterns |\n| **types.ts** | Type definitions | Config, Cache, Metrics, RateLimit, Group, Provider types |\n| **utils.ts** | Utility functions | String manipulation, reference parsing |\n| **rate-limit.ts** | Rate limit management | Backoff cooldowns, cost multiplier |\n| **discovery.ts** | Discovery management | Free-model inventory (ADR-0022: key discovery removed — Pi owns credential resolution) |\n| **metrics.ts** | Metrics management | GDPval, throughput, latency tracking |\n| **cache.ts** | Cache management | Persistent caching, versioning |\n| **routing.ts** | Routing logic | Model selection, filtering, sorting |\n| **stream-orchestrator.ts** | Stream orchestration | `groupStream`/`driveStream` extraction from index.ts, `buildOrchestratorContext` factory with live getters for router/rateLimitManager/cacheManager |\n| **detection.ts** | Error event detection | Rate-limit/abort/overflow text patterns, `isRateLimitLikeReason()`, `isAbortLikeText()`, `parseResetAtMs()` |\n| **content-classifier.ts** | Content classification | mistral-nemo:latest primary, gemma2:2b fallback, cloud fallback via pi's `modelRegistry.completeSimple()` (see ADR 0004) |\n| **escalation.ts** | Session escalation | Loop detection, level tracking, session-safe reset |\n| **model-matcher.ts** | LLM-assisted model matching | Batched matching, plausibility guard, hallucination rejection |\n| **local-llm.ts** | Provider-agnostic LLM caller | Ollama OR LM Studio, OpenRouter free cloud fallback |\n| **exclude.ts** | Personalized exclude rules | Provider/pattern/paid-model filtering for all groups |\n| **config-loader.ts** | Layered configuration | Deep-merge defaults → global → project-local overrides |\n\n**index.ts wiring (2026-10 refactor):** index.ts is now a thin extension entry point\n(~640 lines, down from ~3750): it owns the shared mutable state (the one `cache`\nobject, `cfg`, managers), `load()`/`loadCache()`, and\n`buildOrchestratorContext()`. All behavior lives in `createX(deps)` factory\nmodules that receive **live getters** (plus setters for write access) so\nreload-time swaps are always seen — never stale closure captures:\n\n| Factory module | Owns |\n|----------------|------|\n| **context-utils.ts** | context estimation, timeouts, compaction detection |\n| **limit-glue.ts** | metrics/rate-limit/cost glue functions |\n| **model-resolve-glue.ts** | `resolve`, `detectGroup`, `fmtModel`, `getTopModels` |\n| **scan-runner.ts** | `scan()` incl. GDPval scrape + LLM matching |\n| **dynamic-config-runner.ts** | `generateDynamicConfigNow` |\n| **free-model-registration.ts** | `registerFreeModelOnDemand` |\n| **stream-proxy.ts** | `groupStream`, `tryStream`, `consumeWithDetection`, local-stream limiter |\n| **group-registration.ts** | `registerGroupProviders` (Ü1 guard), `registerGroupModels` (merge-not-replace) |\n| **event-handlers.ts** | the core `pi.on(...)` handlers |\n| **tools.ts** | the four `pi.registerTool` registrations |\n| **commands.ts** | the `/router` command |\n\nThe final `session_shutdown` handler and process-exit/signal cleanup stay at the\nbottom of index.ts (handler order is load-bearing). The router never calls\n`pi.setModel()` except in the `set_model_from_group` tool.\n\nThis modular design enables better maintainability, testing, and extensibility.\n\n\n### GDPval model matching pipeline\n\nWhen a model needs a GDPval score, the router resolves it in three tiers:\n\n1. **model-map.yaml** (authoritative) — explicit model-id → slug mapping\n2. **Token-set fallback** (deterministic) — fuzzy token matching\n3. **LLM-assisted matching** (semantic) — a local LLM matches model ids to\n   GDPval slugs, with cross-family and size-tier guards\n\nSee [`docs/architecture.md`](docs/architecture.md) for details.\n\n## Development\n\n```bash\nnpm install          # also wires the pre-push secret scan (core.hooksPath)\nnpm test             # vitest\nnpx tsc --noEmit     # type check\nnpm run secret-scan -- --range origin/main..HEAD   # manual range scan\n```\n\n`main` is protected: changes land through pull requests with the `test`\nand `secret-scan` checks green. The pre-push hook scans every pushed commit\nfor credentials (gitleaks, `brew install gitleaks`) and for private\nreferences (`scripts/forbidden-patterns.ts`), because in a public\nrepository a push is already a publication. See `AGENTS.md` §8.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}