{"_id":"@aabadin/opencode-model-forecast","_rev":"3-84c86239df67cd3df292fb59a4563f82","name":"@aabadin/opencode-model-forecast","dist-tags":{"latest":"0.4.1"},"versions":{"0.3.0":{"name":"@aabadin/opencode-model-forecast","version":"0.3.0","keywords":["opencode","opencode-plugin","plugin","model-forecast","model-selection","rate-limit","fallback","ai","llm","sdd"],"author":{"name":"aabadin"},"license":"MIT","_id":"@aabadin/opencode-model-forecast@0.3.0","maintainers":[{"name":"aabadin","email":"aabadin@gmail.com"}],"homepage":"https://github.com/adrianabadin/subagentsplugin#readme","bugs":{"url":"https://github.com/adrianabadin/subagentsplugin/issues"},"bin":{"model-forecast":"dist/cli.js"},"dist":{"shasum":"9e7340cc9627865d8c78d3df94b32668012cbd60","tarball":"https://registry.npmjs.org/@aabadin/opencode-model-forecast/-/opencode-model-forecast-0.3.0.tgz","fileCount":25,"integrity":"sha512-d9AoYTz3l/Ebr+3BCnc1D8JCp4TIQxpVrip/nDJj53jOManvKixlqvoS0bO6q7RRbDBgQV+5epoPZk+dr1fz3Q==","signatures":[{"sig":"MEQCIEGWah/ZzHOmo5DgBVPWPyDLuttgQGwdQd19JWxhVi2hAiBguf4UT4QZ5uSD4i2tBVd6IIGL+0OyeVx9PHbl0Ra+uQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":422930},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=24 <25","opencode":">=1.17.11"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./api":{"types":"./dist/api.d.ts","import":"./dist/api.js"}},"private":false,"scripts":{"test":"vitest run","build":"tsup","prepack":"npm run build","pack:dry":"npm pack --dry-run","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"aabadin","email":"aabadin@gmail.com"},"repository":{"url":"git+https://github.com/adrianabadin/subagentsplugin.git","type":"git"},"_npmVersion":"11.12.1","description":"Model + effort forecast plugin for OpenCode with supervised multi-provider fallback recovery.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"solid-js":"^1.9.12"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"8.5.1","vitest":"4.1.6","typescript":"6.0.3","@types/node":"25.7.0","@vitest/coverage-v8":"4.1.6"},"peerDependencies":{"@opencode-ai/plugin":">=1.17.11"},"_npmOperationalInternal":{"tmp":"tmp/opencode-model-forecast_0.3.0_1784008110878_0.6492794669001567","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@aabadin/opencode-model-forecast","version":"0.4.0","keywords":["opencode","opencode-plugin","plugin","model-forecast","model-selection","rate-limit","fallback","ai","llm","sdd"],"author":{"name":"aabadin"},"license":"MIT","_id":"@aabadin/opencode-model-forecast@0.4.0","maintainers":[{"name":"aabadin","email":"aabadin@gmail.com"}],"homepage":"https://github.com/adrianabadin/subagentsplugin#readme","bugs":{"url":"https://github.com/adrianabadin/subagentsplugin/issues"},"bin":{"model-forecast":"dist/cli.js"},"dist":{"shasum":"899357fe697996da984a8b95055945741a3b4d03","tarball":"https://registry.npmjs.org/@aabadin/opencode-model-forecast/-/opencode-model-forecast-0.4.0.tgz","fileCount":25,"integrity":"sha512-DP4iMEp+8V7grenMamOZzIetcv67byocONuGBIV6bFBh6y2OX7k8DRhPgnKiBfXJlXPeqIrJuhc730O6kguPsg==","signatures":[{"sig":"MEYCIQDn70bje/CCYu4Agzo9sfFKBGzflMk+gi9MObuIc2PBKgIhANaHt4NNbnc+JnrDxsn3nPOPF6h56K6DaMS7oDEsPdaA","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":441440},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=24 <25","opencode":">=1.17.11"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./api":{"types":"./dist/api.d.ts","import":"./dist/api.js"}},"private":false,"scripts":{"test":"vitest run","build":"tsup","prepack":"npm run build","pack:dry":"npm pack --dry-run","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"aabadin","email":"aabadin@gmail.com"},"repository":{"url":"git+https://github.com/adrianabadin/subagentsplugin.git","type":"git"},"_npmVersion":"11.12.1","description":"Model + effort forecast plugin for OpenCode with supervised multi-provider fallback recovery.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"solid-js":"^1.9.12"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"8.5.1","vitest":"4.1.6","typescript":"6.0.3","@types/node":"25.7.0","@vitest/coverage-v8":"4.1.6"},"peerDependencies":{"@opencode-ai/plugin":">=1.17.11"},"_npmOperationalInternal":{"tmp":"tmp/opencode-model-forecast_0.4.0_1784168944859_0.17374356687487946","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"@aabadin/opencode-model-forecast","version":"0.4.1","description":"Model + effort forecast plugin for OpenCode with supervised multi-provider fallback recovery.","type":"module","private":false,"main":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./api":{"types":"./dist/api.d.ts","import":"./dist/api.js"},"./tui":{"types":"./dist/tui.d.ts","import":"./dist/tui.js"}},"bin":{"model-forecast":"dist/cli.js"},"scripts":{"build":"tsup","typecheck":"tsc --noEmit","prepack":"npm run build","prepublishOnly":"npm run build && npm test","test":"vitest run","test:coverage":"vitest run --coverage","test:watch":"vitest","pack:dry":"npm pack --dry-run"},"keywords":["opencode","opencode-plugin","plugin","model-forecast","model-selection","rate-limit","fallback","ai","llm","sdd"],"author":{"name":"aabadin"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/adrianabadin/subagentsplugin.git"},"bugs":{"url":"https://github.com/adrianabadin/subagentsplugin/issues"},"homepage":"https://github.com/adrianabadin/subagentsplugin#readme","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"devDependencies":{"@types/node":"25.7.0","@vitest/coverage-v8":"4.1.6","tsup":"8.5.1","typescript":"6.0.3","vitest":"4.1.6"},"peerDependencies":{"@opencode-ai/plugin":">=1.17.11"},"engines":{"node":">=24 <25","opencode":">=1.17.11"},"dependencies":{"solid-js":"^1.9.12"},"types":"./dist/index.d.ts","_id":"@aabadin/opencode-model-forecast@0.4.1","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-0kcupLEBQn2fCy8O9xFTtqIMSW8HEcYf4OniTDEZiQbCMHEeAvVc/mdpyDKn4kWx6RaM0dyNiZHJqEYLy3k9ZA==","shasum":"c0908fc81589f481e22e0ec03042c0334b5a18f3","tarball":"https://registry.npmjs.org/@aabadin/opencode-model-forecast/-/opencode-model-forecast-0.4.1.tgz","fileCount":25,"unpackedSize":452357,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCE+FrBjuCl7pbwnLsNAcazeAs5dwVbi7cF9BtWBOVZDQIgdF5UH71MqMyeFoneq95fCdA2aF2sY/0bshKmjpM/HGU="}]},"_npmUser":{"name":"aabadin","email":"aabadin@gmail.com"},"directories":{},"maintainers":[{"name":"aabadin","email":"aabadin@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/opencode-model-forecast_0.4.1_1784340799526_0.29127880126939143"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-14T05:48:30.697Z","modified":"2026-07-18T02:13:19.828Z","0.3.0":"2026-07-14T05:48:31.034Z","0.4.0":"2026-07-16T02:29:05.016Z","0.4.1":"2026-07-18T02:13:19.714Z"},"bugs":{"url":"https://github.com/adrianabadin/subagentsplugin/issues"},"author":{"name":"aabadin"},"license":"MIT","homepage":"https://github.com/adrianabadin/subagentsplugin#readme","keywords":["opencode","opencode-plugin","plugin","model-forecast","model-selection","rate-limit","fallback","ai","llm","sdd"],"repository":{"type":"git","url":"git+https://github.com/adrianabadin/subagentsplugin.git"},"description":"Model + effort forecast plugin for OpenCode with supervised multi-provider fallback recovery.","maintainers":[{"name":"aabadin","email":"aabadin@gmail.com"}],"readme":"# @aabadin/opencode-model-forecast\r\n\r\nModel + effort forecast plugin for [OpenCode](https://opencode.ai): caches provider/model/benchmark data at startup, recommends a model per SDD phase, and (in auto mode) supervises failed subagents with bounded multi-provider fallback recovery. Rate-limit quarantines use a reset signal when available and otherwise last 10 minutes.\r\n\r\n## Features\r\n\r\n- **Forecast engine** — pure `forecast(input)` returns `{ model, effort, reasoning, fallback }`. Verbose mode additively exposes evidence, confidence, and alternatives.\r\n- **Generated profiles** — at session start (auto mode), the plugin generates one hidden `__mf_<base>__<model>_<hash>` agent per base SDD phase × connected model, in-memory via the OpenCode `config` hook. Prompts and permissions are preserved; only `model` changes.\r\n- **Selection policy** — opt-in auto mode rewrites `task.subagent_type` to the cheapest viable rung that meets the confidence threshold. Threshold + ladder are configurable.\r\n- **Supervised fallback recovery** — authoritative rate limits, model configuration errors, empty output, and provider failures are recovered through at most three total attempts. The coordinator owns task/session lifecycle, cancels hung attempts, and returns either fallback output or an explicit exhausted result.\r\n- **CLI** — `model-forecast --phase <phase> [--verbose] [--diff-lines N] [--risk-domain ...]` for offline forecasting.\r\n- **Loader-clean** — plugin root is a single default export; OpenCode accepts the package directly.\r\n\r\n## Install\r\n\r\n### Global (recommended for personal use)\r\n\r\n```bash\r\nnpm install -g @aabadin/opencode-model-forecast\r\n```\r\n\r\nThen add to your OpenCode config (`~/.config/opencode/opencode.json` or any project-level `.opencode/opencode.json`):\r\n\r\n```json\r\n{\r\n  \"$schema\": \"https://opencode.ai/config.json\",\r\n  \"plugin\": [\"@aabadin/opencode-model-forecast\"]\r\n}\r\n```\r\n\r\nOpenCode auto-discovers the package from the global `node_modules`.\r\n\r\n### Per-repo (recommended for teams / CI)\r\n\r\n```bash\r\nnpm install @aabadin/opencode-model-forecast\r\n```\r\n\r\nThen add the same line to the repo's `.opencode/opencode.json`:\r\n\r\n```json\r\n{\r\n  \"$schema\": \"https://opencode.ai/config.json\",\r\n  \"plugin\": [\"@aabadin/opencode-model-forecast\"]\r\n}\r\n```\r\n\r\nOpenCode scans the project's `node_modules` and loads the plugin from there.\r\n\r\n### With options\r\n\r\n```json\r\n{\r\n  \"$schema\": \"https://opencode.ai/config.json\",\r\n  \"plugin\": [\r\n    [\"@aabadin/opencode-model-forecast\", {\r\n      \"mode\": \"auto\",\r\n      \"allowlist\": [\"sdd-design\", \"sdd-apply\"],\r\n      \"quarantine\": {\r\n        \"enabled\": true,\r\n        \"ttlMs\": 3600000\r\n      }\r\n    }]\r\n  ]\r\n}\r\n```\r\n\r\n## Plugin options\r\n\r\n| Option | Type | Default | Description |\r\n|--------|------|---------|-------------|\r\n| `mode` | `\"advisory\" \\| \"auto\"` | `\"advisory\"` | `advisory` returns `{}` (no hooks). `auto` registers `config` + `tool.execute.before` + `tool.execute.after`. |\r\n| `confidenceThreshold` | number | `0.6` | Minimum confidence for a switch decision. Below the threshold, the plugin keeps the configured default and emits an audit skip. |\r\n| `allowlist` | string[] | `[]` | Only subagent types starting with one of these prefixes get rewritten. Empty = no allowlist. |\r\n| `denylist` | string[] | `[]` | Models in this list are never switched to, even if the selection picks them. |\r\n| `generatedProfiles.phasePrefixes` | string[] | `[\"sdd-\"]` | Phase prefixes used to generate per-phase profile aliases. |\r\n| `generatedProfiles.enabled` | boolean | `true` | Master switch for generated-profile routing. |\r\n| `quarantine.enabled` | boolean | `true` (when `mode === \"auto\"`) | Enable the 429 fallback quarantine. Set `false` to skip the after hook entirely. |\r\n| `quarantine.ttlMs` | number | `600000` (10 min) | Default time-to-live for a quarantined model without a reset signal. Valid reset-derived values are clamped to 1 second through 24 hours. |\r\n| `recovery.enabled` | boolean | `true` (when `mode === \"auto\"`) | Enable supervised recovery, watchdogs, and parent recovery without disabling model selection. |\r\n| `recovery.timeouts.INACTIVITY_TIMEOUT_MS` | number | `180000` | Override the inactivity watchdog for reasoning models that do not emit OpenCode activity heartbeats. |\r\n\r\nReasoning models may spend several minutes before emitting visible output. The\r\nrecovery watchdog treats every child-session event, including repeated busy\r\nstatus events, as activity. If a provider emits no heartbeats during reasoning,\r\nincrease `recovery.timeouts.INACTIVITY_TIMEOUT_MS` instead of disabling\r\nrecovery entirely.\r\n\r\n## CLI\r\n\r\nAfter install, the `model-forecast` binary is on your PATH:\r\n\r\n```bash\r\nmodel-forecast --phase sdd-design --verbose\r\nmodel-forecast --phase sdd-apply --diff-lines 250 --risk-domain update\r\nmodel-forecast --phase sdd-tasks --context-breadth package --modality code\r\n```\r\n\r\nRun `model-forecast --help` for the full flag list.\r\n\r\n### `quarantine` subcommand\r\n\r\nManually block a model or a whole provider group — either permanently or\r\nfor a TTL you specify in hours. Persists to\r\n`~/.cache/opencode-model-forecast/quarantine.json` so the block survives\r\nplugin restarts. Rate-limit auto-quarantines (60-min TTL) stay in memory\r\nonly and are NEVER persisted, so the manual and automatic layers don't\r\ninterfere.\r\n\r\n```bash\r\n# Block a single model permanently\r\nmodel-forecast quarantine add openai/gpt-5.5 --permanent --reason \"billing issue\"\r\n\r\n# Block a model for 24 hours\r\nmodel-forecast quarantine add openai/gpt-5.5 --ttl-hours 24\r\n\r\n# Block an entire provider group (expands via the benchmark registry)\r\nmodel-forecast quarantine add opencode-go/* --permanent\r\n\r\n# List current quarantines\r\nmodel-forecast quarantine list\r\n\r\n# Release (group-expanded — `opencode-go/*` clears every alias)\r\nmodel-forecast quarantine release openai/gpt-5.5\r\n```\r\n\r\nMutations take effect on the next plugin load (the CLI lives in a\r\ndifferent process from the plugin). For **immediate in-session effect**\r\nopen the TUI (`/forecast-config` → Quarantine), which mutates the live\r\nin-process `QuarantineStore` via a `globalThis`-backed cross-bundle\r\nsingleton.\r\n\r\nAn AI-agent skill (`mf-quarantine`) also ships inside the package at\r\n`skills/mf-quarantine/SKILL.md`. Copy it into\r\n`~/.config/opencode/skills/mf-quarantine/SKILL.md` to invoke it from\r\nnatural-language prompts.\r\n\r\n## Plugin behavior contract\r\n\r\n- **Default (`mode: \"advisory\"`)** — the plugin returns `{}`. OpenCode uses its native model resolution. The CLI and skill still work for offline forecasting.\r\n- **Auto (`mode: \"auto\"`)** — the plugin registers three hooks:\r\n  - `config` — generates per-phase profiles from connected models.\r\n  - `tool.execute.before` — when the orchestrator launches a task for an allowed phase, rewrites `subagent_type` to a generated profile if the selection confidence exceeds the threshold.\r\n  - `tool.execute.after` — when the task output matches a rate-limit pattern, quarantines the model and emits an audit + stderr warning with the next viable model.\r\n\r\nThe failing task still returns its error to the orchestrator. The next task of the same phase auto-picks the next rung of the cost ladder.\r\n\r\n## Rollback\r\n\r\nTo disable everything without uninstalling:\r\n\r\n```json\r\n{\r\n  \"plugin\": [\r\n    [\"@aabadin/opencode-model-forecast\", {\r\n      \"mode\": \"advisory\",\r\n      \"quarantine\": { \"enabled\": false }\r\n    }]\r\n  ]\r\n}\r\n```\r\n\r\n## Develop\r\n\r\n```bash\r\ngit clone https://github.com/aabadin/opencode-model-forecast.git\r\ncd opencode-model-forecast\r\nnpm install\r\nnpm test\r\nnpm run typecheck\r\nnpm run build\r\n```\r\n\r\nRequires Node `>=24 <25` and OpenCode `>=1.17.11`.\r\n\r\n## Architecture\r\n\r\n- `src/index.ts` — clean plugin entry (default export only).\r\n- `src/plugin.ts` — plugin implementation; `modelForecastPlugin(input, options)` returns hooks or `{}`.\r\n- `src/api.ts` — public API barrel (`forecast`, `refreshCache`, scoring, evidence, types).\r\n- `src/quarantine.ts` — `QuarantineStore` (TTL-keyed, idempotent add, injectable clock).\r\n- `src/hooks.ts` — coordinator-backed task and after hooks.\r\n- `src/attempt-coordinator.ts` — single owner of recovery task, session, tombstone, and timer lifecycle.\r\n- `src/profiles.ts` — generated-profile catalog + `createGeneratedProfileResolver` (with optional quarantine filter).\r\n- `src/select.ts`, `src/scoring.ts`, `src/evidence.ts` — selection engine.\r\n- `src/policy.ts` — default cost ladder (`minimax → google-antigravity → openai → glm-5.2 → anthropic`).\r\n- `src/audit.ts` — non-throwing audit sink.\r\n- `dist/` — ESM build. The package publishes only the default plugin root export and the `./api` programmatic surface.\r\n\r\n## License\r\n\r\nMIT — see [LICENSE](./LICENSE).\r\n\r\n## Links\r\n\r\n- Repository: <https://github.com/aabadin/opencode-model-forecast>\r\n- Issues: <https://github.com/aabadin/opencode-model-forecast/issues>\r\n- Related: [OpenCode plugin docs](https://opencode.ai/docs/plugins/)\r\n","readmeFilename":"README.md"}