@polderlabs/bizar 10.25.2 → 10.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +34 -41
  2. package/cli/bin.mjs +0 -43
  3. package/cli/commands/hook.mjs +0 -7
  4. package/cli/commands/setup-provider.mjs +4 -2
  5. package/cli/commands/tools.mjs +19 -6
  6. package/cli/commands/upgrade-defaults.mjs +6 -33
  7. package/cli/commands/util.mjs +1 -1
  8. package/cli/commands/validate.mjs +26 -20
  9. package/cli/commands/worker.mjs +30 -21
  10. package/cli/commands/workflow.mjs +14 -100
  11. package/cli/config-paths.mjs +0 -12
  12. package/cli/core/workflow-state.mjs +2 -5
  13. package/cli/doctor.mjs +33 -23
  14. package/cli/install/interactive-setup.mjs +5 -4
  15. package/cli/provision.mjs +46 -186
  16. package/config/agents/model-assignment.mjs +45 -97
  17. package/config/claude/CLAUDE.md +213 -0
  18. package/config/claude/agents/_shared/SKILLS.md +4 -2
  19. package/config/claude/agents/office-manager.md +28 -27
  20. package/config/claude/agents/planner.md +18 -15
  21. package/config/claude/commands/setup-provider.md +8 -2
  22. package/config/claude/hooks/README.md +14 -2
  23. package/config/claude/hooks/worker-suggest.mjs +1 -1
  24. package/config/claude/hooks/workflow-route-guard.mjs +4 -3
  25. package/config/claude/settings.json +12 -1
  26. package/config/skills/bizar/README.md +9 -0
  27. package/config/skills/bizar/SKILL.md +48 -0
  28. package/config/workflows/bizar-debug.js +17 -25
  29. package/config/workflows/bizar-implement.js +15 -24
  30. package/config/workflows/bizar-research.js +14 -24
  31. package/config/workflows/lib/dispatch.js +219 -998
  32. package/config/workflows/ultracode-research.js +14 -23
  33. package/config/workflows/ultracode-review.js +14 -23
  34. package/config/workflows/ultracode.js +15 -24
  35. package/package.json +1 -1
  36. package/packages/sdk/dist/autonomy/outcome-record.d.ts +1 -1
  37. package/packages/sdk/dist/autonomy/outcome-record.js +1 -1
  38. package/packages/sdk/dist/bench/auto-reduction.d.ts +3 -3
  39. package/packages/sdk/dist/bench/auto-reduction.js +3 -3
  40. package/packages/sdk/dist/mcp/server.js +0 -17
  41. package/packages/sdk/dist/release/known-good-releases.d.ts +7 -0
  42. package/packages/sdk/dist/release/known-good-releases.js +43 -1
  43. package/packages/sdk/package.json +1 -1
  44. package/cli/commands/model.mjs +0 -189
  45. package/cli/commands/models.mjs +0 -2808
  46. package/cli/commands/tier.mjs +0 -159
  47. package/config/claude/hooks/agent-model-guard.mjs +0 -304
  48. package/config/claude/hooks/sessionstart-model-sync.mjs +0 -312
  49. package/config/claude/hooks/thinking-route.mjs +0 -311
  50. package/packages/sdk/dist/router/agent-model-registry.d.ts +0 -323
  51. package/packages/sdk/dist/router/agent-model-registry.js +0 -492
  52. package/packages/sdk/dist/router/codemod-intent.d.ts +0 -68
  53. package/packages/sdk/dist/router/codemod-intent.js +0 -112
  54. package/packages/sdk/dist/router/dispatch-evidence.d.ts +0 -176
  55. package/packages/sdk/dist/router/dispatch-evidence.js +0 -505
  56. package/packages/sdk/dist/router/failover-mirror.mjs +0 -315
  57. package/packages/sdk/dist/router/failover.d.ts +0 -188
  58. package/packages/sdk/dist/router/failover.js +0 -255
  59. package/packages/sdk/dist/router/index.d.ts +0 -176
  60. package/packages/sdk/dist/router/index.js +0 -238
  61. package/packages/sdk/dist/router/model-profile.d.ts +0 -229
  62. package/packages/sdk/dist/router/model-profile.js +0 -199
  63. package/packages/sdk/dist/router/model-router.d.ts +0 -113
  64. package/packages/sdk/dist/router/model-router.js +0 -268
  65. package/packages/sdk/dist/router/outcome-learner.d.ts +0 -180
  66. package/packages/sdk/dist/router/outcome-learner.js +0 -506
  67. package/packages/sdk/dist/router/q-learning-router.d.ts +0 -83
  68. package/packages/sdk/dist/router/q-learning-router.js +0 -277
  69. package/packages/sdk/dist/router/select-dispatch-model.d.ts +0 -227
  70. package/packages/sdk/dist/router/select-dispatch-model.js +0 -532
package/README.md CHANGED
@@ -35,21 +35,22 @@ the project you happen to be working on.
35
35
 
36
36
  | You want | Bizar provides |
37
37
  | --- | --- |
38
- | A clean way to begin | A guided installer and `bizar models` picker |
39
- | Your own gateway models | Global selection, full-ID subagent definitions, and native alias mapping |
38
+ | A clean way to begin | A guided installer and a static four-alias dispatch surface |
39
+ | Your own gateway models | Four native aliases (`haiku`/`sonnet`/`opus`/`fable`) with OmniRoute handling ordered failover between configured full IDs |
40
40
  | Useful parallel work | Isolated worktrees, scoped tasks, and specialist roles |
41
41
  | Fewer surprises | Explicit safety checks for releases, publication, deployment, pushes, and destructive operations |
42
42
  | Confidence at the end | Tests, architecture checks, E2E checks, and evidence-aware handoff |
43
43
 
44
44
  ## Start here
45
45
 
46
- Install Bizar globally, install its Claude Code integration, then choose the
47
- models you want Bizar to use.
46
+ Install Bizar globally and its Claude Code integration. Bizar dispatches
47
+ through four static native aliases — `haiku`, `sonnet`, `opus`, `fable` —
48
+ and OmniRoute handles ordered failover between configured full IDs for the
49
+ chosen alias, so there is no model picker step.
48
50
 
49
51
  ```sh
50
52
  npm install -g @polderlabs/bizar
51
53
  bizar install
52
- bizar models
53
54
  ```
54
55
 
55
56
  Restart Claude Code after installation. The installer adds Bizar's agents, skills, commands, hooks, settings, and the
@@ -59,19 +60,18 @@ for Bizar and OpenKan, not for the Claude Code CLI.
59
60
  It preserves your configured gateway endpoint and credential values during a
60
61
  clean reinstall.
61
62
 
62
- On a new interactive install, Bizar also asks for an optional default model,
63
- whether Claude Code agent teams should be enabled, the OpenKan install
64
- directory, and whether the current project should receive a `.ok/` workspace.
65
- OpenKan is installed from npm as `@polderlabs/openkan@latest`; its package-owned
66
- agent and skill are installed into the same Claude configuration. Use
67
- `bizar install --yes` for CI or a prompt-free refresh.
63
+ On a new interactive install, Bizar also asks whether Claude Code agent teams
64
+ should be enabled, the OpenKan install directory, and whether the current
65
+ project should receive a `.ok/` workspace. OpenKan is installed from npm as
66
+ `@polderlabs/openkan@latest`; its package-owned agent and skill are
67
+ installed into the same Claude configuration. Use `bizar install --yes` for
68
+ CI or a prompt-free refresh.
68
69
 
69
70
  For a completely fresh Bizar-managed Claude setup while retaining endpoint and
70
71
  authentication settings:
71
72
 
72
73
  ```sh
73
74
  bizar install --force
74
- bizar models
75
75
  ```
76
76
 
77
77
  Then open any repository in Claude Code and describe the outcome you want.
@@ -107,40 +107,36 @@ foregrounded.
107
107
 
108
108
  ## Your models, everywhere Bizar dispatches
109
109
 
110
- `bizar models` is the single operator-facing place to select models. It
111
- discovers candidates from your configured gateway and writes your selections to
112
- the global model router:
110
+ Bizar dispatches through four static native aliases `haiku`, `sonnet`,
111
+ `opus`, `fable`. Claude Code's native per-call `model` field accepts one of
112
+ these aliases. OmniRoute handles ordered failover between the configured full
113
+ gateway IDs for the chosen alias, so the operator never picks a picker-style
114
+ gateway ID per agent at this layer. There is no `bizar models` picker, no
115
+ `model-router.json`, no `userSelected` block, and no Agent-model-guard hook.
113
116
 
114
- ```text
115
- ~/.claude/model-router.json
116
- ```
117
+ The four aliases are the entire dispatch surface:
117
118
 
118
- The router is never stored in a project directory. Bizar uses the selected
119
- models for direct subagents, workflows, and agent-team teammates.
119
+ - `haiku` trivial / cheap micro-edits
120
+ - `sonnet` ordinary implementation, research, planning lanes
121
+ - `opus` — hard / architectural / adversarial / debug / high-risk review lanes
122
+ - `fable` — explicit Anthropic OpenAI-compat surfaces
123
+
124
+ Workflow scripts (`config/workflows/*.js`) inline a tiny `dispatchAgent`
125
+ wrapper that picks the alias from a static policy; agent definitions stay
126
+ model-agnostic so the harness, not the agent, owns alias selection.
120
127
 
121
128
  ```mermaid
122
129
  flowchart TD
123
- Picker["bizar models"] --> Router["Global model router\n~/.claude/model-router.json"]
124
- Router --> Definitions["Global Bizar agent definitions\nfull model ID in frontmatter"]
125
- Router --> Aliases["sonnet · opus · haiku · fable\ncompatibility aliases"]
126
- Definitions --> Agent["Subagents"]
127
- Definitions --> Workflow["Workflow workers"]
128
- Definitions --> Team["Agent-team teammates"]
130
+ Policy["Static alias policy\n(office-manager.md + workflow routeModel)"] --> Aliases["haiku · sonnet · opus · fable"]
131
+ Aliases --> OmniRoute["OmniRoute ordered failover\nper configured full ID"]
132
+ OmniRoute --> Agent["Subagents"]
133
+ OmniRoute --> Workflow["Workflow workers"]
134
+ OmniRoute --> Team["Agent-team teammates"]
129
135
  ```
130
136
 
131
- Claude Code's native per-call model field has a small alias vocabulary. Bizar
132
- avoids making that vocabulary a limitation: it projects each selected gateway
133
- model into a global subagent definition whose frontmatter contains the full
134
- model ID. The agent, workflow, and team routes use that definition. The four
135
- native aliases are compatibility shortcuts only; they do not enable an
136
- unselected provider or reduce your selected-model pool to four choices.
137
-
138
137
  Useful inspection commands:
139
138
 
140
139
  ```sh
141
- bizar models --list
142
- bizar models --agent-types --json
143
- bizar models explain todd
144
140
  bizar doctor
145
141
  ```
146
142
 
@@ -153,9 +149,7 @@ also ships 80 skills for planning, debugging, verification, review,
153
149
  worktrees, and implementation practice.
154
150
 
155
151
  The coordinator selects specialists when their expertise reduces a concrete
156
- risk. It does not create parallel workers merely to look busy. You can inspect
157
- the installed specialist definition names through `bizar models --agent-types
158
- --json` and use a relevant Bizar specialist directly when needed.
152
+ risk. It does not create parallel workers merely to look busy.
159
153
 
160
154
  ```text
161
155
  Core coordination Specialist coverage
@@ -195,7 +189,7 @@ point where an external or difficult-to-reverse decision belongs to you.
195
189
  ├── rules/ focused guidance for common development work
196
190
  ├── workflows/ native workflow definitions
197
191
  ├── settings.json Bizar-managed Claude Code integration
198
- └── model-router.json operator-selected model state
192
+ └── agents/ Bizar agent definitions (alias-agnostic)
199
193
 
200
194
  ~/.config/bizar/
201
195
  ├── installed.json install record
@@ -217,7 +211,6 @@ plane, background daemon, or general-purpose note vault.
217
211
  | `bizar install` | Install or refresh Bizar, OpenKan, and the global Claude configuration |
218
212
  | `bizar openkan install` | Refresh the managed `@polderlabs/openkan` npm runtime and its agent/skill |
219
213
  | `bizar openkan init` | Initialise `.ok/` planning state in the current project |
220
- | `bizar models` | Discover and select the models Bizar may dispatch |
221
214
  | `bizar doctor` | Diagnose the global installation and provider connectivity |
222
215
  | `bizar validate` | Run an install-focused health check |
223
216
  | `ok task` | Inspect or coordinate scoped worktree tasks (OpenKan-native) |
package/cli/bin.mjs CHANGED
@@ -108,7 +108,6 @@ function showHelp() {
108
108
  artifact Toggle global browser completion artifacts
109
109
  learn Manage bounded global preferences and project lessons
110
110
  tools Inventory installed agents, skills, hooks, commands, and MCP tools
111
- tier Explain the explicit configured model tier for a task
112
111
  upgrade-defaults Reapply safe Bizar-owned Claude defaults
113
112
  backup Create / list / verify / delete backups of BizarHarness state
114
113
  restore Restore BizarHarness from a backup
@@ -136,12 +135,10 @@ function showHelp() {
136
135
  hook <name> Run a portable Claude Code hook
137
136
  worktree-merge <branch> Merge a feature branch with archive tag (no work lost)
138
137
  worker <subcommand> Run an exact-model Claude process worker in a worktree
139
- models Configure the global model picker and Models.dev metadata
140
138
  evidence <subcommand> Inspect model-routing evidence
141
139
  improve <subcommand> Propose and verify bounded self-edits
142
140
  sandbox <subcommand> Manage local execution sandboxes
143
141
  explain-run <id> Explain a recorded run
144
- model Deprecated alias for models
145
142
 
146
143
  Examples:
147
144
  bizar install
@@ -452,22 +449,6 @@ async function main() {
452
449
  break;
453
450
  }
454
451
 
455
- case 'models': {
456
- const mod = await importCommand('models');
457
- if (!mod) {
458
- console.error(chalk.red(` ✗ Could not load models command module`));
459
- process.exit(EXIT_ERROR);
460
- return;
461
- }
462
- dbg('loaded command module:', 'models');
463
- const found = await mod.run(cmd, cmdArgs, isHelpRequest);
464
- if (found === false) {
465
- console.error(chalk.red(` ✗ Usage: bizar models [--list|--set|--clear|--json] — run 'bizar models --help'`));
466
- process.exit(EXIT_USAGE);
467
- }
468
- break;
469
- }
470
-
471
452
  case 'artifact': {
472
453
  const mod = await importCommand('artifact');
473
454
  if (!mod) { process.exit(EXIT_ERROR); return; }
@@ -483,7 +464,6 @@ async function main() {
483
464
  }
484
465
 
485
466
  case 'tools':
486
- case 'tier':
487
467
  case 'upgrade-defaults': {
488
468
  const mod = await importCommand(cmd);
489
469
  if (!mod) { process.exit(EXIT_ERROR); return; }
@@ -615,29 +595,6 @@ async function main() {
615
595
  break;
616
596
  }
617
597
 
618
- case 'model': {
619
- // Deprecated alias. Routes to the original `model.mjs` so the
620
- // legacy JSON shape (`{ providers: { ... }, total: N }`) and table
621
- // output keep working for existing scripts and tests. New code
622
- // should use `bizar models` (see `case 'models'`).
623
- const mod = await importCommand('model');
624
- if (!mod) {
625
- console.error(chalk.red(` ✗ Could not load model command module`));
626
- process.exit(EXIT_ERROR);
627
- return;
628
- }
629
- dbg('loaded command module (deprecated alias):', 'model');
630
- if (!isHelpRequest) {
631
- console.error(chalk.yellow(' ! `bizar model` is deprecated; use `bizar models` for the new picker surface.'));
632
- }
633
- const found = await mod.run(cmd, cmdArgs, isHelpRequest);
634
- if (found === false) {
635
- console.error(chalk.red(` ✗ Usage: bizar model <subcommand> — run 'bizar model --help'`));
636
- process.exit(EXIT_USAGE);
637
- }
638
- break;
639
- }
640
-
641
598
  case 'workflow': {
642
599
  const mod = await importCommand('workflow');
643
600
  if (!mod) {
@@ -18,14 +18,12 @@ const HOOK_ROOT = resolve(PACKAGE_ROOT, 'config', 'claude', 'hooks');
18
18
  const PRETOOL_SAFETY_LEAVES = new Set([
19
19
  'pretooluse-editwrite', 'path-ownership-guard',
20
20
  'pretooluse-bash', 'git-workflow-guard',
21
- 'agent-model-guard',
22
21
  'workflow-route-guard',
23
22
  ]);
24
23
 
25
24
  export const HOOK_PROGRAMS = Object.freeze({
26
25
  'advisor-context': 'advisor-context.mjs',
27
26
  'agent-grounding': 'agent-grounding.mjs',
28
- 'agent-model-guard': 'agent-model-guard.mjs',
29
27
  'content-style-guard': 'content-style-guard.mjs',
30
28
  'completion-artifact': 'completion-artifact.mjs',
31
29
  'control-inbox': 'control-inbox.mjs',
@@ -40,11 +38,9 @@ export const HOOK_PROGRAMS = Object.freeze({
40
38
  'pretooluse-bash': 'pretooluse-bash.mjs',
41
39
  'pretooluse-editwrite': 'pretooluse-editwrite.mjs',
42
40
  'sessionend-recall': 'sessionend-recall.mjs',
43
- 'sessionstart-model-sync': 'sessionstart-model-sync.mjs',
44
41
  'sessionstart-prime': 'sessionstart-prime.mjs',
45
42
  'simplify-guard': 'simplify-guard.mjs',
46
43
  telemetry: 'telemetry.mjs',
47
- 'thinking-route': 'thinking-route.mjs',
48
44
  'team-lifecycle': 'team-lifecycle.mjs',
49
45
  'verify-deliverables': 'verify-deliverables.mjs',
50
46
  'worker-suggest': 'worker-suggest.mjs',
@@ -59,12 +55,10 @@ export const EVENT_CHAINS = Object.freeze({
59
55
  'keyword-router',
60
56
  'workflow-route-guard',
61
57
  'worker-suggest',
62
- 'thinking-route',
63
58
  'telemetry',
64
59
  ]),
65
60
  'session-start': Object.freeze([
66
61
  'control-inbox',
67
- 'sessionstart-model-sync',
68
62
  'sessionstart-prime',
69
63
  'persistent-mode',
70
64
  'telemetry',
@@ -238,7 +232,6 @@ export function selectEventChain(eventKey, input = '') {
238
232
  if (toolName === 'Bash') {
239
233
  return ['workflow-route-guard', 'pretooluse-bash', 'git-workflow-guard'];
240
234
  }
241
- if (toolName === 'Agent') return ['workflow-route-guard', 'agent-model-guard'];
242
235
  return [];
243
236
  }
244
237
 
@@ -43,7 +43,9 @@ export function updateProviderSettings(current, options) {
43
43
  const next = { ...current, env: { ...(current.env || {}) } };
44
44
  if (options.gateway !== undefined) {
45
45
  next.env.ANTHROPIC_BASE_URL = options.gateway;
46
- next.env.BIZAR_MODEL_ROUTER_URL = options.gateway;
46
+ // Legacy compatibility: drop any pre-existing router-URL mirror so the
47
+ // install surface converges on a single provider URL.
48
+ delete next.env.BIZAR_MODEL_ROUTER_URL;
47
49
  }
48
50
  if (options.key !== undefined) next.env.ANTHROPIC_AUTH_TOKEN = options.key;
49
51
  if (options.model !== undefined) next.env.ANTHROPIC_MODEL = options.model;
@@ -87,7 +89,7 @@ export async function runSetupProvider(args = []) {
87
89
 
88
90
  if (options.list) {
89
91
  console.log(` Settings: ${path}`);
90
- console.log(` Gateway: ${current.env?.ANTHROPIC_BASE_URL || current.env?.BIZAR_MODEL_ROUTER_URL || '(Anthropic default)'}`);
92
+ console.log(` Gateway: ${current.env?.ANTHROPIC_BASE_URL || '(Anthropic default)'}`);
91
93
  console.log(` Model: ${current.env?.ANTHROPIC_MODEL || '(Claude Code default)'}`);
92
94
  console.log(` API key: ${redact(current.env?.ANTHROPIC_AUTH_TOKEN || current.env?.ANTHROPIC_API_KEY)}`);
93
95
  return { ok: true, path, settings: current };
@@ -15,7 +15,7 @@ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
15
15
  import { join, resolve } from 'node:path';
16
16
  import { fileURLToPath } from 'node:url';
17
17
 
18
- import { resolveClaudeConfigDir, resolveGlobalModelRouter } from '../config-paths.mjs';
18
+ import { resolveClaudeConfigDir } from '../config-paths.mjs';
19
19
  const __filename = fileURLToPath(import.meta.url);
20
20
  const __dirname = resolve(__filename, '..', '..');
21
21
  const CLAUDE_DIR = resolveClaudeConfigDir();
@@ -80,8 +80,6 @@ export async function runTools(cmdArgs) {
80
80
 
81
81
  const settingsPath = join(CLAUDE_DIR, 'settings.json');
82
82
  const hasSettings = existsSync(settingsPath);
83
- const modelRouterPath = resolveGlobalModelRouter();
84
- const hasModelRouter = existsSync(modelRouterPath);
85
83
 
86
84
  const summary = {
87
85
  userLevel: CLAUDE_DIR,
@@ -92,7 +90,6 @@ export async function runTools(cmdArgs) {
92
90
  wiredCommands,
93
91
  mcpTools,
94
92
  settings: { path: settingsPath, present: hasSettings },
95
- modelRouter: { path: modelRouterPath, present: hasModelRouter },
96
93
  };
97
94
 
98
95
  if (asJson) {
@@ -118,8 +115,24 @@ export async function runTools(cmdArgs) {
118
115
  section(`wired bizar commands (${wiredCommands.length})`, wiredCommands.map((c) => 'bizar ' + c));
119
116
  console.log(chalk.bold('\n settings'));
120
117
  console.log(` ${hasSettings ? '✓' : '✗'} ${settingsPath}`);
121
- console.log(chalk.bold(' model-router'));
122
- console.log(` ${hasModelRouter ? '✓' : '✗'} ${modelRouterPath}`);
118
+ if (hasSettings) {
119
+ try {
120
+ const parsed = JSON.parse(readFileSync(settingsPath, 'utf8'));
121
+ const env = (parsed && parsed.env) || {};
122
+ const aliases = {
123
+ sonnet: env.ANTHROPIC_DEFAULT_SONNET_MODEL,
124
+ haiku: env.ANTHROPIC_DEFAULT_HAIKU_MODEL,
125
+ opus: env.ANTHROPIC_DEFAULT_OPUS_MODEL,
126
+ fable: env.ANTHROPIC_DEFAULT_FABLE_MODEL,
127
+ };
128
+ console.log(chalk.bold(' alias map'));
129
+ for (const [key, value] of Object.entries(aliases)) {
130
+ console.log(` ${key.padEnd(8)} → ${value || '(unset)'}`);
131
+ }
132
+ const model = parsed?.model;
133
+ if (model) console.log(chalk.dim(` default model: ${model}`));
134
+ } catch { /* ignore malformed settings */ }
135
+ }
123
136
  }
124
137
 
125
138
  export async function run(name, args, isHelpRequest) {
@@ -35,42 +35,18 @@
35
35
  import chalk from 'chalk';
36
36
  import { existsSync, readFileSync, writeFileSync, copyFileSync } from 'node:fs';
37
37
  import { join } from 'node:path';
38
- import { resolveClaudeConfigDir, resolveGlobalModelRouter } from '../config-paths.mjs';
39
- import { configuredFallbackModels } from './models.mjs';
38
+ import { resolveClaudeConfigDir } from '../config-paths.mjs';
40
39
 
41
40
  const CLAUDE_DIR = resolveClaudeConfigDir();
42
41
  const SETTINGS_PATH = join(CLAUDE_DIR, 'settings.json');
43
- // 10.22.0 / Phase 4 spirit-of-constraint: the install model id comes
44
- // from the operator's `userSelected.models[0]`, not a hardcoded literal.
45
- // The Bizar global router is the sole model-policy source.
46
- const BIZAR_ROUTER_PATH = resolveGlobalModelRouter();
47
-
48
- function readConfiguredModels() {
49
- for (const path of [BIZAR_ROUTER_PATH]) {
50
- if (!existsSync(path)) continue;
51
- try {
52
- const parsed = JSON.parse(readFileSync(path, 'utf8'));
53
- const list = Array.isArray(parsed?.userSelected?.models)
54
- ? parsed.userSelected.models.filter((id) => typeof id === 'string' && id.trim())
55
- : [];
56
- if (list.length > 0) return list;
57
- const fallback = configuredFallbackModels(parsed);
58
- if (fallback.length > 0) return fallback;
59
- // An explicit empty selection is an intentional global policy.
60
- if (path === BIZAR_ROUTER_PATH && parsed && typeof parsed === 'object'
61
- && Array.isArray(parsed.userSelected?.models)) {
62
- return [];
63
- }
64
- } catch { /* keep falling through */ }
65
- }
66
- return [];
67
- }
68
42
 
69
43
  const STATIC_FAVORED = {
70
44
  permissions: { defaultMode: 'bypassPermissions' },
71
45
  worktree: { bgIsolation: 'worktree' },
72
- // `model` is intentionally absent — `buildFavored()` derives it from
73
- // userSelected.models[0] (or omits it when empty).
46
+ // `model` is intentionally absent — settings.json ships the alias
47
+ // binding (model + modelOverrides + the four ANTHROPIC_DEFAULT_*_MODEL
48
+ // env vars) as the canonical source. `upgrade-defaults` only re-applies
49
+ // the safe Bizar-owned UX defaults, not model policy.
74
50
  alwaysThinkingEnabled: true,
75
51
  effortLevel: 'high',
76
52
  skipDangerousModePermissionPrompt: true,
@@ -79,10 +55,7 @@ const STATIC_FAVORED = {
79
55
  };
80
56
 
81
57
  function buildFavored() {
82
- const out = { ...STATIC_FAVORED };
83
- const picks = readConfiguredModels();
84
- if (picks[0]) out.model = picks[0];
85
- return out;
58
+ return { ...STATIC_FAVORED };
86
59
  }
87
60
 
88
61
  export async function run(name, args, isHelpRequest) {
@@ -82,7 +82,7 @@ export function showDoctorHelp() {
82
82
  • hook wiring
83
83
  • installed agents and skills
84
84
  • required local tools
85
- model-router reachability
85
+ alias map (ANTHROPIC_DEFAULT_*_MODEL)
86
86
 
87
87
  Prints ✓/✗ for each check and a final summary. Exits non-zero
88
88
  if any check fails. Use \`bizar doctor\` after installation or
@@ -26,8 +26,7 @@ import chalk from 'chalk';
26
26
  import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
27
27
  import { join } from 'node:path';
28
28
  import { spawnSync } from 'node:child_process';
29
- import { resolveBizarHome, resolveClaudeConfigDir, resolveGlobalModelRouter } from '../config-paths.mjs';
30
- import { configuredEnabledModels, listModels, resolveEndpoint } from './models.mjs';
29
+ import { resolveBizarHome, resolveClaudeConfigDir } from '../config-paths.mjs';
31
30
 
32
31
  function claudeDir() { return resolveClaudeConfigDir(); }
33
32
  function bizarHome() { return resolveBizarHome(); }
@@ -54,9 +53,13 @@ export const REQUIRED_COMMANDS = [
54
53
  ];
55
54
 
56
55
  // v6.3.0 — Claude Code hook adapter scripts (executable, .mjs extension).
56
+ // OmniRoute alias-routing overhaul (10.25.x): `agent-model-guard.mjs` and
57
+ // `sessionstart-model-sync.mjs` are removed from active registration AND
58
+ // from required-hook validation. Settings.json's model + modelOverrides
59
+ // is the sole alias binding; native Claude Code validates the alias/model
60
+ // setting on its own.
57
61
  export const REQUIRED_HOOKS = [
58
62
  'agent-grounding.mjs',
59
- 'agent-model-guard.mjs',
60
63
  'advisor-context.mjs',
61
64
  'bizar-hook-wrapper.sh',
62
65
  'completion-artifact.mjs',
@@ -73,12 +76,10 @@ export const REQUIRED_HOOKS = [
73
76
  'pretooluse-bash.mjs',
74
77
  'pretooluse-editwrite.mjs',
75
78
  'sessionend-recall.mjs',
76
- 'sessionstart-model-sync.mjs',
77
79
  'sessionstart-prime.mjs',
78
80
  'simplify-guard.mjs',
79
81
  'team-lifecycle.mjs',
80
82
  'telemetry.mjs',
81
- 'thinking-route.mjs',
82
83
  'verify-deliverables.mjs',
83
84
  'worker-suggest.mjs',
84
85
  'workflow-route-guard.mjs',
@@ -350,22 +351,27 @@ const CHECKS = {
350
351
  },
351
352
 
352
353
  'provider-reachable': async () => {
353
- const { endpoint, authToken } = resolveEndpoint();
354
- if (!endpoint) {
355
- const path = resolveGlobalModelRouter();
356
- const router = readJsonSafe(path);
357
- const fallback = router ? configuredEnabledModels(router) : [];
358
- if (fallback.length === 0) {
359
- throw new Error(`no gateway URL or enabled configured model in ${path}; implicit defaults are prohibited`);
360
- }
361
- return `no gateway URL; explicit configured fallback is ${fallback[0]}`;
362
- }
363
- try {
364
- const models = await listModels({ endpoint, authToken, timeoutMs: 4000 });
365
- return `provider reachable at ${endpoint} (${models.length} models)`;
366
- } catch (err) {
367
- throw new Error(`provider at ${endpoint} unreachable: ${err.message ?? err}`);
354
+ // The picker/router is gone. Provider reachability is the operator's
355
+ // concern — Claude Code (or any HTTP client) probes its own gateway.
356
+ // We only confirm an operator-configured gateway URL is present and
357
+ // that the four alias env vars map to the four OmniRoute combos.
358
+ const path = settingsJsonPath();
359
+ const settings = readJsonSafe(path);
360
+ const env = (settings && typeof settings === 'object' && settings.env) || {};
361
+ const aliasMap = {
362
+ sonnet: env.ANTHROPIC_DEFAULT_SONNET_MODEL,
363
+ haiku: env.ANTHROPIC_DEFAULT_HAIKU_MODEL,
364
+ opus: env.ANTHROPIC_DEFAULT_OPUS_MODEL,
365
+ fable: env.ANTHROPIC_DEFAULT_FABLE_MODEL,
366
+ };
367
+ const missing = Object.entries(aliasMap).filter(([, v]) => typeof v !== 'string' || !v.trim()).map(([k]) => k);
368
+ if (missing.length > 0) {
369
+ throw new Error(`alias env map missing entries in ${path}: ${missing.join(', ')}`);
368
370
  }
371
+ const endpoint = env.ANTHROPIC_BASE_URL;
372
+ return endpoint
373
+ ? `provider gateway configured at ${endpoint}; aliases: sonnet→${aliasMap.sonnet}, haiku→${aliasMap.haiku}, opus→${aliasMap.opus}, fable→${aliasMap.fable}`
374
+ : `no gateway URL configured; default provider is Anthropic; aliases: sonnet→${aliasMap.sonnet}, haiku→${aliasMap.haiku}, opus→${aliasMap.opus}, fable→${aliasMap.fable}`;
369
375
  },
370
376
 
371
377
  'claude-md-mirrored': async () => {
@@ -1,16 +1,21 @@
1
1
  /**
2
- * Exact-model process workers.
2
+ * Process workers spawned with a native Claude alias.
3
3
  *
4
4
  * This command is the separately launched alternative: it creates an isolated
5
- * worktree and starts a top-level Claude process with the literal gateway ID
6
- * selected through `bizar models`. No global settings are rewritten per
7
- * worker, so parallel workers cannot race each other's model selection.
5
+ * worktree and starts a top-level Claude process with a native alias
6
+ * (default `sonnet`). No global settings are rewritten per worker, so
7
+ * parallel workers cannot race each other's model selection. The alias is
8
+ * passed through to `claude --model <alias>`; native Claude Code is the
9
+ * sole authority on alias-to-combo resolution.
8
10
  */
9
11
  import { spawn, spawnSync } from 'node:child_process';
10
12
  import { createWriteStream, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
11
13
  import { basename, dirname, join } from 'node:path';
12
14
  import { randomUUID } from 'node:crypto';
13
- import { resolveBizarHome, resolveClaudeConfigDir, resolveGlobalModelRouter } from '../config-paths.mjs';
15
+ import { resolveBizarHome, resolveClaudeConfigDir } from '../config-paths.mjs';
16
+
17
+ const NATIVE_ALIASES = Object.freeze(new Set(['sonnet', 'haiku', 'opus', 'fable']));
18
+ const DEFAULT_ALIAS = 'sonnet';
14
19
 
15
20
  function runGit(args, cwd) {
16
21
  const result = spawnSync('git', args, { cwd, encoding: 'utf8' });
@@ -18,14 +23,18 @@ function runGit(args, cwd) {
18
23
  return result.stdout.trim();
19
24
  }
20
25
 
21
- function readRouter() {
22
- const path = resolveGlobalModelRouter();
23
- try { return JSON.parse(readFileSync(path, 'utf8')); } catch { throw new Error('Global Bizar model router is missing or invalid; run `bizar models`.'); }
26
+ function normalizeAlias(value) {
27
+ const alias = typeof value === 'string' && value.trim() ? value.trim().toLowerCase() : DEFAULT_ALIAS;
28
+ if (!NATIVE_ALIASES.has(alias)) {
29
+ throw new Error(`worker start requires a native alias (sonnet | haiku | opus | fable); got "${alias}"`);
30
+ }
31
+ return alias;
24
32
  }
25
33
 
26
- export function enabledModels(router) {
27
- const disabled = new Set((router.disabledProviders || []).filter((id) => typeof id === 'string').map((id) => id.trim().toLowerCase()));
28
- return (router.userSelected?.models || []).filter((id) => typeof id === 'string' && id.trim() && ![...disabled].some((prefix) => id.toLowerCase().startsWith(prefix)));
34
+ export function enabledModels() {
35
+ // Retained for backwards compatibility with `cli/commands/worker.test.mjs`.
36
+ // The picker is gone; native aliases are the only legal `model` values.
37
+ return [...NATIVE_ALIASES];
29
38
  }
30
39
 
31
40
  function workerRoot() { return join(resolveBizarHome(), 'workers'); }
@@ -36,23 +45,21 @@ function writeState(state) {
36
45
  }
37
46
 
38
47
  export function buildWorkerPlan({ repoRoot = process.cwd(), model, task, agent = 'todd' } = {}) {
39
- if (!model || typeof model !== 'string') throw new Error('worker start requires --model <selected gateway model id>.');
40
48
  if (!task || typeof task !== 'string') throw new Error('worker start requires --task <bounded task>.');
41
- const router = readRouter();
42
- if (!enabledModels(router).includes(model)) throw new Error(`Model ${model} is not an enabled global bizar models selection.`);
49
+ const normalized = normalizeAlias(model);
43
50
  const root = runGit(['rev-parse', '--show-toplevel'], repoRoot);
44
51
  const id = `worker-${randomUUID().slice(0, 8)}`;
45
52
  const branch = `wt/${id}`;
46
53
  const worktree = join(dirname(root), `${basename(root)}-${id}`);
47
54
  const logPath = join(workerRoot(), `${id}.log`);
48
- return { id, root, branch, worktree, model, task: task.trim(), agent, logPath };
55
+ return { id, root, branch, worktree, model: normalized, task: task.trim(), agent, logPath };
49
56
  }
50
57
 
51
58
  export function workerClaudeArgs(plan) {
52
59
  return [
53
60
  '--print', '--name', `bizar-${plan.id}`, '--model', plan.model,
54
61
  '--permission-mode', 'acceptEdits', '--agent', plan.agent,
55
- `${plan.task}\n\nYou are an exact-model Bizar process worker in ${plan.worktree}. Work only in this worktree. Do not spawn subagents. Implement and test the bounded task, commit one logical change locally, never push, then report the commit and verification.`,
62
+ `${plan.task}\n\nYou are an isolated Bizar process worker in ${plan.worktree}, running with the native "${plan.model}" alias. Work only in this worktree. Do not spawn subagents. Implement and test the bounded task, commit one logical change locally, never push, then report the commit and verification.`,
56
63
  ];
57
64
  }
58
65
 
@@ -75,12 +82,14 @@ function listWorkers() {
75
82
 
76
83
  function usage() {
77
84
  console.log(`
78
- bizar worker start --model <selected-id> --task <task> [--agent todd] [--background]
85
+ bizar worker start --model <sonnet|haiku|opus|fable> --task <task> [--agent todd] [--background]
79
86
  bizar worker list [--json]
80
87
 
81
- Starts an isolated top-level Claude Code process in a wt/ worktree with the
82
- exact selected gateway model. Use this when a separately launched worktree
83
- process is useful. Merge completed branches with bizar worktree-merge <branch>.
88
+ Starts an isolated top-level Claude Code process in a wt/ worktree with a
89
+ native alias. Default alias is "sonnet". Native Claude Code maps the
90
+ alias to an OmniRoute combo via the four ANTHROPIC_DEFAULT_*_MODEL
91
+ env vars in config/claude/settings.json. Merge completed branches with
92
+ bizar worktree-merge <branch>.
84
93
  `);
85
94
  }
86
95
 
@@ -108,7 +117,7 @@ export async function run(name, args, isHelpRequest) {
108
117
  });
109
118
  child.once('error', (error) => {
110
119
  writeState({ ...state, status: 'failed', exitedAt: new Date().toISOString(), error: error.message });
111
- console.error(`Unable to start exact-model worker: ${error.message}`);
120
+ console.error(`Unable to start isolated worker: ${error.message}`);
112
121
  });
113
122
  state.pid = child.pid || null;
114
123
  writeState(state);