@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
@@ -10,8 +10,6 @@ import {
10
10
  resumeWorkflow,
11
11
  startWorkflow,
12
12
  } from '../core/workflow-state.mjs';
13
- import { loadModelRouter } from '../../config/agents/model-assignment.mjs';
14
- import { resolveGlobalModelRouter } from '../config-paths.mjs';
15
13
 
16
14
  const DEFAULT_PROBE_TIMEOUT_MS = 3_000;
17
15
 
@@ -21,96 +19,15 @@ function normalizeEndpoint(value) {
21
19
  return normalized || null;
22
20
  }
23
21
 
24
- function requireEffectiveInferenceEndpoint(registry, env = process.env) {
25
- const configuredEndpoint = normalizeEndpoint(registry?.gateway?.endpoint);
22
+ function requireEffectiveInferenceEndpoint(env = process.env) {
26
23
  const inferenceEndpoint = normalizeEndpoint(env.ANTHROPIC_BASE_URL);
27
24
  if (!inferenceEndpoint) {
28
25
  throw new WorkflowStateError(
29
26
  'INFERENCE_ENDPOINT_REQUIRED',
30
- `workflow start requires ANTHROPIC_BASE_URL=${registry?.gateway?.endpoint || '<configured gateway>'}`,
27
+ 'workflow start requires ANTHROPIC_BASE_URL to point at the inference gateway',
31
28
  );
32
29
  }
33
- if (inferenceEndpoint !== configuredEndpoint) {
34
- throw new WorkflowStateError(
35
- 'INFERENCE_ENDPOINT_MISMATCH',
36
- `ANTHROPIC_BASE_URL must match the configured workflow gateway ${registry.gateway.endpoint}`,
37
- );
38
- }
39
- const routerEndpointValue = env.BIZAR_MODEL_ROUTER_URL;
40
- if (typeof routerEndpointValue === 'string' && routerEndpointValue !== '') {
41
- const routerEndpoint = normalizeEndpoint(routerEndpointValue);
42
- if (!routerEndpoint || routerEndpoint !== configuredEndpoint) {
43
- throw new WorkflowStateError(
44
- 'INFERENCE_ENDPOINT_MISMATCH',
45
- `BIZAR_MODEL_ROUTER_URL must not contradict the configured workflow gateway ${registry.gateway.endpoint}`,
46
- );
47
- }
48
- }
49
- }
50
-
51
- function probeUrl(registry) {
52
- const endpoint = registry?.gateway?.endpoint;
53
- const probe = registry?.gateway?.availabilityProbe;
54
- if (typeof endpoint !== 'string' || !endpoint || typeof probe !== 'string' || !probe) {
55
- throw new WorkflowStateError(
56
- 'MODEL_REGISTRY_INVALID',
57
- 'model registry requires gateway.endpoint and gateway.availabilityProbe',
58
- );
59
- }
60
- return `${endpoint.replace(/\/+$/, '')}/${probe.replace(/^\/+/, '')}?limit=1000`;
61
- }
62
-
63
- export async function probeAvailableModels({
64
- registry,
65
- fetchImpl = globalThis.fetch,
66
- authToken = process.env.ANTHROPIC_AUTH_TOKEN || process.env.BIZAR_MODEL_ROUTER_TOKEN,
67
- timeoutMs = DEFAULT_PROBE_TIMEOUT_MS,
68
- } = {}) {
69
- if (typeof fetchImpl !== 'function') {
70
- throw new WorkflowStateError('GATEWAY_UNAVAILABLE', 'this Node runtime has no fetch implementation');
71
- }
72
- const controller = new AbortController();
73
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
74
- try {
75
- const headers = { Accept: 'application/json' };
76
- if (authToken) headers.Authorization = `Bearer ${authToken}`;
77
- const response = await fetchImpl(probeUrl(registry), {
78
- method: 'GET',
79
- headers,
80
- signal: controller.signal,
81
- });
82
- if (!response.ok) {
83
- throw new WorkflowStateError(
84
- 'GATEWAY_UNAVAILABLE',
85
- `model availability probe returned HTTP ${response.status}`,
86
- );
87
- }
88
- let payload;
89
- try {
90
- payload = await response.json();
91
- } catch {
92
- throw new WorkflowStateError('GATEWAY_RESPONSE_INVALID', 'model availability probe returned invalid JSON');
93
- }
94
- if (!payload || !Array.isArray(payload.data)) {
95
- throw new WorkflowStateError('GATEWAY_RESPONSE_INVALID', 'model availability response must contain a data array');
96
- }
97
- const ids = payload.data.map((entry) => entry?.id);
98
- if (ids.some((id) => typeof id !== 'string' || !id || id.trim() !== id)) {
99
- throw new WorkflowStateError('GATEWAY_RESPONSE_INVALID', 'model availability response contains an invalid exact model id');
100
- }
101
- if (new Set(ids).size !== ids.length) {
102
- throw new WorkflowStateError('GATEWAY_RESPONSE_INVALID', 'model availability response contains duplicate model ids');
103
- }
104
- return ids;
105
- } catch (error) {
106
- if (error instanceof WorkflowStateError) throw error;
107
- if (error?.name === 'AbortError') {
108
- throw new WorkflowStateError('GATEWAY_TIMEOUT', `model availability probe timed out after ${timeoutMs}ms`);
109
- }
110
- throw new WorkflowStateError('GATEWAY_UNAVAILABLE', `model availability probe failed: ${error?.message || String(error)}`);
111
- } finally {
112
- clearTimeout(timeout);
113
- }
30
+ return inferenceEndpoint;
114
31
  }
115
32
 
116
33
  function parseFlags(args) {
@@ -118,7 +35,7 @@ function parseFlags(args) {
118
35
  const values = new Set([
119
36
  '--profile', '--workflow', '--mode', '--project', '--session', '--session-id',
120
37
  '--run', '--run-id', '--revision', '--expected-revision', '--stage', '--expected-stage',
121
- '--reason', '--goal', '--evidence', '--router',
38
+ '--reason', '--goal', '--evidence',
122
39
  ]);
123
40
  // Boolean flags take no value. Long flags use the kebab->camel key;
124
41
  // short flags carry an explicit key mapping.
@@ -162,7 +79,6 @@ function showHelp() {
162
79
  Common flags:
163
80
  --project <path> Project root (defaults to the current directory)
164
81
  --session <id> Claude session id (defaults to CLAUDE_SESSION_ID)
165
- --router <path> Strict model router registry (defaults to the shipped registry)
166
82
  --mode <name> Routing hint for start (ralplan routes to the 8-step consensus protocol)
167
83
  --deliberate Force the pre-mortem round on a ralplan-shaped workflow
168
84
  --advisory Run the protocol without enforced gates (observability only)
@@ -286,24 +202,22 @@ export async function run(name, args, isHelpRequest) {
286
202
  '--mode cannot be combined with --profile or --workflow; --mode owns the routing hint',
287
203
  );
288
204
  }
289
- const registry = loadModelRouter(
290
- flags.router || process.env.BIZAR_MODEL_ROUTER_PATH || resolveGlobalModelRouter(),
291
- );
292
- let availableModelIds;
205
+ // The picker/router is gone; workflows no longer probe a model list
206
+ // or load a model-router registry. Native Claude Code resolves the
207
+ // alias via the four ANTHROPIC_DEFAULT_*_MODEL env vars shipped in
208
+ // config/claude/settings.json. We still require ANTHROPIC_BASE_URL
209
+ // so an operator without a gateway cannot silently inherit a
210
+ // provider default.
293
211
  try {
294
- requireEffectiveInferenceEndpoint(registry);
295
- availableModelIds = await probeAvailableModels({ registry });
296
- } catch {
297
- // Discovery is optional; the resolver still supplies an explicit
298
- // enabled configured-tier model. Never inherit a provider default.
299
- availableModelIds = undefined;
212
+ requireEffectiveInferenceEndpoint();
213
+ } catch (error) {
214
+ if (!(error instanceof WorkflowStateError)) throw error;
215
+ throw error;
300
216
  }
301
217
  state = startWorkflow({
302
218
  ...ctx,
303
219
  profile: routing.profile,
304
220
  goal: flags.goal,
305
- registry,
306
- availableModelIds,
307
221
  });
308
222
  if (routing.routing.mode !== null && !flags.json) {
309
223
  // Surface the routing hint in human mode. In JSON mode the
@@ -14,18 +14,6 @@ export function resolveBizarHome({ env = process.env, cwd = process.cwd() } = {}
14
14
  return join(home, '.config', 'bizar');
15
15
  }
16
16
 
17
- export function resolveGlobalModelRouter(options = {}) {
18
- const { env = process.env, cwd = process.cwd() } = options;
19
- const override = typeof env.BIZAR_MODEL_ROUTER_CONFIG === 'string'
20
- ? env.BIZAR_MODEL_ROUTER_CONFIG.trim()
21
- : '';
22
- if (override) return isAbsolute(override) ? override : resolve(cwd, override);
23
- // Claude Code reads hooks, agents, skills, and model-router from ~/.claude/.
24
- // Use ~/.claude/model-router.json as the single canonical location so the
25
- // hook and the CLI always agree on the same file.
26
- return join(resolveClaudeConfigDir({ env, cwd }), 'model-router.json');
27
- }
28
-
29
17
  export function resolveGlobalLearningDir(options = {}) {
30
18
  return join(resolveBizarHome(options), 'learning');
31
19
  }
@@ -346,6 +346,7 @@ export function validateWorkflowState(state, context) {
346
346
  assert(typeof agent === 'string' && agent.length > 0, 'INVALID_STATE', 'routing decision agent is invalid');
347
347
  assert(decision && typeof decision === 'object', 'INVALID_STATE', `routing decision for ${agent} is invalid`);
348
348
  assert(typeof decision.tier === 'string' && decision.tier.length > 0, 'INVALID_STATE', `routing tier for ${agent} is invalid`);
349
+ assert(['haiku', 'sonnet', 'opus', 'fable'].includes(decision.alias), 'INVALID_STATE', `alias for ${agent} is not a static native alias`);
349
350
  assert(decision.model === null || (typeof decision.model === 'string' && decision.model.length > 0), 'INVALID_STATE', `routing model for ${agent} is invalid`);
350
351
  assert(typeof decision.inheritSession === 'boolean', 'INVALID_STATE', `routing inheritance for ${agent} is invalid`);
351
352
  }
@@ -400,9 +401,7 @@ export function startWorkflow({
400
401
  sessionId,
401
402
  profile = 'default',
402
403
  goal,
403
- availableModelIds,
404
- registry,
405
- requiredAgents,
404
+ requiredAgents = [],
406
405
  now = new Date(),
407
406
  }) {
408
407
  const paths = resolveWorkflowPaths({ projectRoot, sessionId });
@@ -415,8 +414,6 @@ export function startWorkflow({
415
414
  assignmentSnapshot = createRunAssignmentSnapshot({
416
415
  runId,
417
416
  agentNames: requiredAgents,
418
- availableModelIds,
419
- registry,
420
417
  createdAt: now.toISOString(),
421
418
  });
422
419
  } catch (error) {
package/cli/doctor.mjs CHANGED
@@ -20,7 +20,10 @@
20
20
  * hook-files-installed: all shipped hook entrypoints are executable
21
21
  * tools-on-path: at least one of semble/skills/claude
22
22
  * bizar-home: BIZAR_HOME exists
23
- * provider-reachable: provider gateway responds
23
+ * alias-map: ANTHROPIC_DEFAULT_*_MODEL entries are present
24
+ * and equal default/common/hard/fable
25
+ * legacy-router: non-destructive warning if ~/.claude/model-router.json
26
+ * still exists on disk (no Bizar runtime reads it)
24
27
  *
25
28
  * Usage:
26
29
  * import { runDoctor } from "./doctor.mjs";
@@ -34,14 +37,12 @@ import { join } from 'node:path';
34
37
  import {
35
38
  resolveBizarHome,
36
39
  resolveClaudeConfigDir,
37
- resolveGlobalModelRouter,
38
40
  } from './config-paths.mjs';
39
41
  import {
40
42
  REQUIRED_AGENTS,
41
43
  REQUIRED_COMMANDS,
42
44
  REQUIRED_HOOKS,
43
45
  } from './commands/validate.mjs';
44
- import { configuredEnabledModels, listModels, resolveEndpoint } from './commands/models.mjs';
45
46
  import { validateNativeWorkflowDirectory } from '../config/workflows/lib/native-contract.mjs';
46
47
  import { resolveOpenKanHome, verifyOpenKanRuntime } from './openkan.mjs';
47
48
 
@@ -195,28 +196,36 @@ async function checkOpenKanRuntime() {
195
196
  return `OpenKan ${home} is runnable (${result.launcher})`;
196
197
  }
197
198
 
198
- async function checkProviderReachable() {
199
- const { endpoint, authToken } = resolveEndpoint();
200
- if (!endpoint) {
201
- const routerPath = resolveGlobalModelRouter();
202
- let router;
203
- try {
204
- router = JSON.parse(readFileSync(routerPath, 'utf8'));
205
- } catch {
206
- throw new Error(`no provider URL and no readable global model router at ${routerPath}`);
207
- }
208
- const configured = configuredEnabledModels(router);
209
- if (configured.length === 0) {
210
- throw new Error('no enabled configured model; implicit provider defaults are prohibited');
199
+ async function checkAliasMap() {
200
+ const settingsPath = join(resolveClaudeConfigDir(), 'settings.json');
201
+ const settings = readFileSync(settingsPath, 'utf8');
202
+ const parsed = JSON.parse(settings);
203
+ const env = (parsed && typeof parsed === 'object' && parsed.env) || {};
204
+ const expected = {
205
+ ANTHROPIC_DEFAULT_SONNET_MODEL: 'default',
206
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: 'common',
207
+ ANTHROPIC_DEFAULT_OPUS_MODEL: 'hard',
208
+ ANTHROPIC_DEFAULT_FABLE_MODEL: 'fable',
209
+ };
210
+ const mismatches = [];
211
+ for (const [key, expectedValue] of Object.entries(expected)) {
212
+ const actual = env[key];
213
+ if (typeof actual !== 'string' || !actual.trim()) {
214
+ mismatches.push(`${key}=<unset>`);
215
+ } else if (actual !== expectedValue) {
216
+ mismatches.push(`${key}=${actual} (expected ${expectedValue})`);
211
217
  }
212
- return `no gateway URL; explicit configured fallback is ${configured[0]}`;
213
218
  }
214
- try {
215
- const models = await listModels({ endpoint, authToken, timeoutMs: 3000 });
216
- return `provider at ${endpoint} ok (${models.length} models)`;
217
- } catch (err) {
218
- throw new Error(`provider at ${endpoint} unreachable: ${err.message ?? err}`);
219
+ if (mismatches.length > 0) {
220
+ throw new Error(`alias map drift in ${settingsPath}: ${mismatches.join(', ')}`);
219
221
  }
222
+ return `alias map ok (sonnet→default, haiku→common, opus→hard, fable→fable)`;
223
+ }
224
+
225
+ async function checkLegacyRouterPresent() {
226
+ const routerPath = join(resolveClaudeConfigDir(), 'model-router.json');
227
+ if (!existsSync(routerPath)) return 'no legacy model-router.json present';
228
+ return `legacy model-router.json present at ${routerPath} — safe to delete (no Bizar runtime reads it)`;
220
229
  }
221
230
 
222
231
  // ── runner ──────────────────────────────────────────────────────────────────
@@ -235,7 +244,8 @@ const CHECKS = [
235
244
  { name: 'tools-on-path', run: checkToolsAvailable },
236
245
  { name: 'bizar-home', run: checkBizarHome },
237
246
  { name: 'openkan-runtime', run: checkOpenKanRuntime },
238
- { name: 'provider-reachable', run: checkProviderReachable },
247
+ { name: 'alias-map', run: checkAliasMap },
248
+ { name: 'legacy-router', run: checkLegacyRouterPresent },
239
249
  ];
240
250
 
241
251
  export async function runDoctor(opts = {}) {
@@ -18,9 +18,11 @@ export function readProviderSettings(path = providerSettingsPath()) {
18
18
 
19
19
  export function detectProviderConfiguration({ env = process.env, settings = {} } = {}) {
20
20
  const settingsEnv = settings?.env && typeof settings.env === 'object' ? settings.env : {};
21
- const url = env.BIZAR_MODEL_ROUTER_URL?.trim()
22
- || env.ANTHROPIC_BASE_URL?.trim()
23
- || settingsEnv.BIZAR_MODEL_ROUTER_URL?.trim()
21
+ // The picker surface is gone; we no longer consult a separate
22
+ // BIZAR_MODEL_ROUTER_URL. The install only configures
23
+ // ANTHROPIC_BASE_URL + ANTHROPIC_AUTH_TOKEN; the four aliases
24
+ // (sonnet/haiku/opus/fable) are bound by the shipped settings template.
25
+ const url = env.ANTHROPIC_BASE_URL?.trim()
24
26
  || settingsEnv.ANTHROPIC_BASE_URL?.trim()
25
27
  || '';
26
28
  const key = env.ANTHROPIC_AUTH_TOKEN?.trim()
@@ -173,7 +175,6 @@ export async function runInteractiveSetup({
173
175
  }
174
176
 
175
177
  env.ANTHROPIC_BASE_URL = url;
176
- env.BIZAR_MODEL_ROUTER_URL = url;
177
178
  env.ANTHROPIC_AUTH_TOKEN = key;
178
179
  writeLine(output, ' ✓ Provider configuration ready; the key will be stored in global Claude settings.');
179
180