@polderlabs/bizar 10.23.15 → 10.23.16

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.
@@ -18,7 +18,7 @@
18
18
  * reject user-selected IDs.
19
19
  */
20
20
  import chalk from 'chalk';
21
- import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
21
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
22
22
  import { dirname, join } from 'node:path';
23
23
  import readline from 'node:readline';
24
24
 
@@ -992,6 +992,9 @@ export function applyModelOverrides({ settingsJsonPath, pickedIds, liveIds = [],
992
992
  // print-mode `[claude-code:unrecognized_model]` diagnostics for Agent SDK calls.
993
993
  settings.modelOverrides = buildClaudeModelOverrides(synced);
994
994
  const nativeAgentAliases = applyNativeAgentAliasTargets(settings, synced);
995
+ const generatedAgents = syncGeneratedModelAgents(synced, {
996
+ agentsDir: settingsJsonPath === undefined ? undefined : join(dirname(path), 'agents', 'bizar-models'),
997
+ });
995
998
  if (requiresGatewayModelDiscovery(synced)) {
996
999
  settings.env = {
997
1000
  ...(settings.env || {}),
@@ -1022,6 +1025,7 @@ export function applyModelOverrides({ settingsJsonPath, pickedIds, liveIds = [],
1022
1025
  skippedStale: skipped,
1023
1026
  skippedDisabled,
1024
1027
  nativeAgentAliases,
1028
+ generatedAgents,
1025
1029
  settingsPath: path,
1026
1030
  };
1027
1031
  }
@@ -1066,6 +1070,23 @@ export const CLAUDE_MODEL_OVERRIDE_KEYS = Object.freeze([
1066
1070
  // gateway IDs below; they never select an Anthropic provider by themselves.
1067
1071
  export const NATIVE_AGENT_ALIASES = Object.freeze(['sonnet', 'opus', 'haiku', 'fable']);
1068
1072
 
1073
+ const MODEL_AGENT_WORDS = Object.freeze({ a:'alpha', b:'bravo', c:'charlie', d:'delta', e:'echo', f:'foxtrot', g:'golf', h:'hotel', i:'india', j:'juliet', k:'kilo', l:'lima', m:'mike', n:'november', o:'oscar', p:'papa', q:'quebec', r:'romeo', s:'sierra', t:'tango', u:'uniform', v:'victor', w:'whiskey', x:'xray', y:'yankee', z:'zulu', 0:'zero', 1:'one', 2:'two', 3:'three', 4:'four', 5:'five', 6:'six', 7:'seven', 8:'eight', 9:'nine', '/':'slash', '.':'dot', '-':'dash', '_':'under' });
1074
+
1075
+ export function modelAgentName(modelId) {
1076
+ return `bizar-model-${[...String(modelId || '').toLowerCase()].map((ch) => MODEL_AGENT_WORDS[ch] || 'unknown').join('-')}`;
1077
+ }
1078
+
1079
+ export function syncGeneratedModelAgents(modelIds, opts = {}) {
1080
+ const agentsDir = opts.agentsDir || join(resolveClaudeConfigDir(), 'agents', 'bizar-models');
1081
+ const ids = [...new Set((Array.isArray(modelIds) ? modelIds : []).filter((id) => typeof id === 'string' && id.trim()).map((id) => id.trim()))];
1082
+ if (existsSync(agentsDir)) rmSync(agentsDir, { recursive: true, force: true });
1083
+ if (!ids.length) return { agentsDir, names: [] };
1084
+ mkdirSync(agentsDir, { recursive: true, mode: 0o700 });
1085
+ const names = ids.map((id) => modelAgentName(id));
1086
+ ids.forEach((id, index) => writeFileSync(join(agentsDir, `${names[index]}.md`), `---\nname: ${names[index]}\ndescription: Bizar configured gateway model worker.\nmodel: ${id}\n---\n\nFollow the assigned Bizar role and task. Report concise evidence to the coordinator.\n`, { mode: 0o600 }));
1087
+ return { agentsDir, names };
1088
+ }
1089
+
1069
1090
  const NATIVE_AGENT_OVERRIDE_KEYS = Object.freeze({
1070
1091
  sonnet: 'claude-sonnet-5',
1071
1092
  opus: 'claude-opus-5',
@@ -38,16 +38,14 @@ Before every Workflow, Agent, or Agent-team call, read the global Bizar model
38
38
  router and construct a small `args.routing` object whose `default`, `medium`,
39
39
  and `high` values are explicit enabled configured gateway IDs. Include the
40
40
  user's task in the same args object under the workflow's documented task field.
41
- Pass the chosen full gateway ID directly in every native Agent `model` field;
42
- current Claude Code supports full model IDs there. Include that same ID in
43
- `additionalContext.bizarConfiguredModel` for audit telemetry. Never use
44
- `inherit` or an unconfigured provider default. `bizar models` additionally
45
- maintains `sonnet`, `opus`, `haiku`, and `fable` aliases as compatibility
46
- shortcuts, but aliases must not limit dispatch to four selected models. The
47
- exact-model `bizar worker` command remains available where a separate top-level
48
- Claude process is useful, not as a fallback for normal native dispatch. If no
49
- configured model exists, stop and ask the operator to run `bizar models`; never
50
- omit model selection or cycle providers.
41
+ Use the generated `bizar-models` user agent matching the chosen full gateway
42
+ ID as `subagent_type`, and omit the native Agent `model` parameter entirely.
43
+ That definition's frontmatter owns the full-ID selection for ordinary agents,
44
+ workflows, and teams. Include the raw ID in `additionalContext.bizarConfiguredModel`
45
+ for audit telemetry. Never use `inherit` or an unconfigured provider default.
46
+ `sonnet`, `opus`, `haiku`, and `fable` are compatibility aliases only. If no
47
+ generated configured definition exists, stop and ask the operator to run
48
+ `bizar models`; never omit the generated agent type or cycle providers.
51
49
 
52
50
  Invoke the selected workflow by `name` first. If Claude reports that the Bizar
53
51
  name is unavailable, resolve the active Claude config directory and retry once
@@ -49,6 +49,7 @@ import { pathToFileURL } from 'node:url';
49
49
 
50
50
  import { loadModelRouter } from '../../../config/agents/model-assignment.mjs';
51
51
  import { resolveClaudeConfigDir } from '../../../cli/config-paths.mjs';
52
+ import { modelAgentName } from '../../../cli/commands/models.mjs';
52
53
 
53
54
  function deny(reason) {
54
55
  return {
@@ -194,7 +195,7 @@ function readTransportTarget(alias, options = {}) {
194
195
 
195
196
  export async function guardAgentModel(input, options = {}) {
196
197
  if (!input || typeof input !== 'object') return {};
197
- if (input.hook_event_name !== 'PreToolUse' || input.tool_name !== 'Agent') return {};
198
+ if (input.hook_event_name !== 'PreToolUse' || !['Agent', 'Task'].includes(input.tool_name)) return {};
198
199
  const toolInput = input.tool_input && typeof input.tool_input === 'object' ? input.tool_input : {};
199
200
  const requested = typeof toolInput.model === 'string' ? toolInput.model.trim() : '';
200
201
  const failoverBlock = readFailoverBlock(toolInput);
@@ -225,6 +226,8 @@ export async function guardAgentModel(input, options = {}) {
225
226
  // Inheritance is safe only when the auditable Bizar selection equals the
226
227
  // actual global Claude parent model and is a selected, enabled user pick.
227
228
  if (!requested || requested === 'inherit') {
229
+ const generated = typeof toolInput.subagent_type === 'string' && [...userPicks].some((id) => toolInput.subagent_type === modelAgentName(id));
230
+ if (generated && !requested) return {};
228
231
  const configured = configuredContext;
229
232
  const parent = readConfiguredParentModel(options);
230
233
  if (!configured || configured !== parent || !userPicks.has(configured)) {
@@ -26,10 +26,11 @@ const routeModel = (risk) => {
26
26
  const selected = candidate.trim()
27
27
  return selected
28
28
  }
29
- if (!routeModel('medium') || !routeModel('high')) {
29
+ const routeAgentType = (risk) => WORKFLOW_ROUTING.agentTypes?.[routeModel(risk)] || ''
30
+ if (!routeModel('medium') || !routeModel('high') || !routeAgentType('medium') || !routeAgentType('high')) {
30
31
  return {
31
32
  status: 'blocked',
32
- reason: 'No explicit enabled Bizar model was supplied for workflow routing. Read the global model router and retry with args.routing; provider defaults are prohibited.',
33
+ reason: 'No generated Bizar model-agent mapping was supplied for workflow routing. Run bizar models and retry; provider defaults are prohibited.',
33
34
  }
34
35
  }
35
36
  let WORKFLOW_DISPATCH_SEQUENCE = 0
@@ -37,7 +38,7 @@ const dispatchAgent = (agentFn, agentName, prompt, opts = {}) => {
37
38
  const sequence = ++WORKFLOW_DISPATCH_SEQUENCE
38
39
  const prefix = `[Bizar dispatch ${sequence}: ${agentName}; role=${opts.role || 'worker'}; phase=${opts.phase || 'work'}; label=${opts.label || agentName}]`
39
40
  const agentOptions = {
40
- model: routeModel(opts.risk || 'medium'),
41
+ subagent_type: routeAgentType(opts.risk || 'medium'),
41
42
  effort: opts.risk === 'high' ? 'high' : 'medium',
42
43
  }
43
44
  if (opts.schema) agentOptions.schema = opts.schema
@@ -25,10 +25,11 @@ const routeModel = (risk) => {
25
25
  const selected = candidate.trim()
26
26
  return selected
27
27
  }
28
- if (!routeModel('medium') || !routeModel('high')) {
28
+ const routeAgentType = (risk) => WORKFLOW_ROUTING.agentTypes?.[routeModel(risk)] || ''
29
+ if (!routeModel('medium') || !routeModel('high') || !routeAgentType('medium') || !routeAgentType('high')) {
29
30
  return {
30
31
  status: 'blocked',
31
- reason: 'No explicit enabled Bizar model was supplied for workflow routing. Read the global model router and retry with args.routing; provider defaults are prohibited.',
32
+ reason: 'No generated Bizar model-agent mapping was supplied for workflow routing. Run bizar models and retry; provider defaults are prohibited.',
32
33
  }
33
34
  }
34
35
  let WORKFLOW_DISPATCH_SEQUENCE = 0
@@ -36,7 +37,7 @@ const dispatchAgent = (agentFn, agentName, prompt, opts = {}) => {
36
37
  const sequence = ++WORKFLOW_DISPATCH_SEQUENCE
37
38
  const prefix = `[Bizar dispatch ${sequence}: ${agentName}; role=${opts.role || 'worker'}; phase=${opts.phase || 'work'}; label=${opts.label || agentName}]`
38
39
  const agentOptions = {
39
- model: routeModel(opts.risk || 'medium'),
40
+ subagent_type: routeAgentType(opts.risk || 'medium'),
40
41
  effort: opts.risk === 'high' ? 'high' : 'medium',
41
42
  }
42
43
  if (opts.schema) agentOptions.schema = opts.schema
@@ -26,10 +26,11 @@ const routeModel = (risk) => {
26
26
  const selected = candidate.trim()
27
27
  return selected
28
28
  }
29
- if (!routeModel('medium') || !routeModel('high')) {
29
+ const routeAgentType = (risk) => WORKFLOW_ROUTING.agentTypes?.[routeModel(risk)] || ''
30
+ if (!routeModel('medium') || !routeModel('high') || !routeAgentType('medium') || !routeAgentType('high')) {
30
31
  return {
31
32
  status: 'blocked',
32
- reason: 'No explicit enabled Bizar model was supplied for workflow routing. Read the global model router and retry with args.routing; provider defaults are prohibited.',
33
+ reason: 'No generated Bizar model-agent mapping was supplied for workflow routing. Run bizar models and retry; provider defaults are prohibited.',
33
34
  }
34
35
  }
35
36
  let WORKFLOW_DISPATCH_SEQUENCE = 0
@@ -37,7 +38,7 @@ const dispatchAgent = (agentFn, agentName, prompt, opts = {}) => {
37
38
  const sequence = ++WORKFLOW_DISPATCH_SEQUENCE
38
39
  const prefix = `[Bizar dispatch ${sequence}: ${agentName}; role=${opts.role || 'worker'}; phase=${opts.phase || 'work'}; label=${opts.label || agentName}]`
39
40
  const agentOptions = {
40
- model: routeModel(opts.risk || 'medium'),
41
+ subagent_type: routeAgentType(opts.risk || 'medium'),
41
42
  effort: opts.risk === 'high' ? 'high' : 'medium',
42
43
  }
43
44
  if (opts.schema) agentOptions.schema = opts.schema
@@ -33,6 +33,9 @@ import { existsSync, readFileSync, appendFileSync, mkdirSync, writeFileSync, ren
33
33
  import { homedir } from 'node:os';
34
34
  import { dirname, isAbsolute, join, resolve } from 'node:path';
35
35
 
36
+ const MODEL_AGENT_WORDS = { a:'alpha', b:'bravo', c:'charlie', d:'delta', e:'echo', f:'foxtrot', g:'golf', h:'hotel', i:'india', j:'juliet', k:'kilo', l:'lima', m:'mike', n:'november', o:'oscar', p:'papa', q:'quebec', r:'romeo', s:'sierra', t:'tango', u:'uniform', v:'victor', w:'whiskey', x:'xray', y:'yankee', z:'zulu', 0:'zero', 1:'one', 2:'two', 3:'three', 4:'four', 5:'five', 6:'six', 7:'seven', 8:'eight', 9:'nine', '/':'slash', '.':'dot', '-':'dash', '_':'under' };
37
+ function modelAgentName(modelId) { return `bizar-model-${[...String(modelId || '').toLowerCase()].map((ch) => MODEL_AGENT_WORDS[ch] || 'unknown').join('-')}`; }
38
+
36
39
  const { O_APPEND, O_CREAT, O_WRONLY } = fsConstants;
37
40
 
38
41
  /* ────────────────────────────────────────────────────────────────────────── */
@@ -796,15 +799,12 @@ export function classifyDispatchOutcome(result, error, startMs) {
796
799
  }
797
800
 
798
801
  /**
799
- * Build the augmented native-Agent payload. Claude Code supports an explicit
800
- * full model ID in an Agent invocation; use the selected gateway ID directly
801
- * so every configured selection is usable by native agents and teams. The
802
- * transport aliases remain a compatibility option for callers that need them.
802
+ * Build an Agent payload that selects a generated full-ID model definition.
803
803
  */
804
804
  export function augmentPayload(opts, decision, agentName, context = {}) {
805
805
  return {
806
806
  ...opts,
807
- model: decision.modelId,
807
+ subagent_type: modelAgentName(decision.modelId),
808
808
  additionalContext: {
809
809
  ...(opts.additionalContext && typeof opts.additionalContext === 'object' ? opts.additionalContext : {}),
810
810
  bizarConfiguredModel: decision.modelId ?? null,
@@ -24,10 +24,11 @@ const routeModel = (risk) => {
24
24
  const selected = candidate.trim()
25
25
  return selected
26
26
  }
27
- if (!routeModel('medium') || !routeModel('high')) {
27
+ const routeAgentType = (risk) => WORKFLOW_ROUTING.agentTypes?.[routeModel(risk)] || ''
28
+ if (!routeModel('medium') || !routeModel('high') || !routeAgentType('medium') || !routeAgentType('high')) {
28
29
  return {
29
30
  status: 'blocked',
30
- reason: 'No explicit enabled Bizar model was supplied for workflow routing. Read the global model router and retry with args.routing; provider defaults are prohibited.',
31
+ reason: 'No generated Bizar model-agent mapping was supplied for workflow routing. Run bizar models and retry; provider defaults are prohibited.',
31
32
  }
32
33
  }
33
34
  let WORKFLOW_DISPATCH_SEQUENCE = 0
@@ -35,7 +36,7 @@ const dispatchAgent = (agentFn, agentName, prompt, opts = {}) => {
35
36
  const sequence = ++WORKFLOW_DISPATCH_SEQUENCE
36
37
  const prefix = `[Bizar dispatch ${sequence}: ${agentName}; role=${opts.role || 'worker'}; phase=${opts.phase || 'work'}; label=${opts.label || agentName}]`
37
38
  const agentOptions = {
38
- model: routeModel(opts.risk || 'medium'),
39
+ subagent_type: routeAgentType(opts.risk || 'medium'),
39
40
  effort: opts.risk === 'high' ? 'high' : 'medium',
40
41
  }
41
42
  if (opts.schema) agentOptions.schema = opts.schema
@@ -23,10 +23,11 @@ const routeModel = (risk) => {
23
23
  const selected = candidate.trim()
24
24
  return selected
25
25
  }
26
- if (!routeModel('medium') || !routeModel('high')) {
26
+ const routeAgentType = (risk) => WORKFLOW_ROUTING.agentTypes?.[routeModel(risk)] || ''
27
+ if (!routeModel('medium') || !routeModel('high') || !routeAgentType('medium') || !routeAgentType('high')) {
27
28
  return {
28
29
  status: 'blocked',
29
- reason: 'No explicit enabled Bizar model was supplied for workflow routing. Read the global model router and retry with args.routing; provider defaults are prohibited.',
30
+ reason: 'No generated Bizar model-agent mapping was supplied for workflow routing. Run bizar models and retry; provider defaults are prohibited.',
30
31
  }
31
32
  }
32
33
  let WORKFLOW_DISPATCH_SEQUENCE = 0
@@ -34,7 +35,7 @@ const dispatchAgent = (agentFn, agentName, prompt, opts = {}) => {
34
35
  const sequence = ++WORKFLOW_DISPATCH_SEQUENCE
35
36
  const prefix = `[Bizar dispatch ${sequence}: ${agentName}; role=${opts.role || 'worker'}; phase=${opts.phase || 'work'}; label=${opts.label || agentName}]`
36
37
  const agentOptions = {
37
- model: routeModel(opts.risk || 'medium'),
38
+ subagent_type: routeAgentType(opts.risk || 'medium'),
38
39
  effort: opts.risk === 'high' ? 'high' : 'medium',
39
40
  }
40
41
  if (opts.schema) agentOptions.schema = opts.schema
@@ -25,10 +25,11 @@ const routeModel = (risk) => {
25
25
  const selected = candidate.trim()
26
26
  return selected
27
27
  }
28
- if (!routeModel('medium') || !routeModel('high')) {
28
+ const routeAgentType = (risk) => WORKFLOW_ROUTING.agentTypes?.[routeModel(risk)] || ''
29
+ if (!routeModel('medium') || !routeModel('high') || !routeAgentType('medium') || !routeAgentType('high')) {
29
30
  return {
30
31
  status: 'blocked',
31
- reason: 'No explicit enabled Bizar model was supplied for workflow routing. Read the global model router and retry with args.routing; provider defaults are prohibited.',
32
+ reason: 'No generated Bizar model-agent mapping was supplied for workflow routing. Run bizar models and retry; provider defaults are prohibited.',
32
33
  }
33
34
  }
34
35
  let WORKFLOW_DISPATCH_SEQUENCE = 0
@@ -36,7 +37,7 @@ const dispatchAgent = (agentFn, agentName, prompt, opts = {}) => {
36
37
  const sequence = ++WORKFLOW_DISPATCH_SEQUENCE
37
38
  const prefix = `[Bizar dispatch ${sequence}: ${agentName}; role=${opts.role || 'worker'}; phase=${opts.phase || 'work'}; label=${opts.label || agentName}]`
38
39
  const agentOptions = {
39
- model: routeModel(opts.risk || 'medium'),
40
+ subagent_type: routeAgentType(opts.risk || 'medium'),
40
41
  effort: opts.risk === 'high' ? 'high' : 'medium',
41
42
  }
42
43
  if (opts.schema) agentOptions.schema = opts.schema
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polderlabs/bizar",
3
- "version": "10.23.15",
3
+ "version": "10.23.16",
4
4
  "description": "Autonomous, human-in-the-loop multi-agent harness for Claude Code with guarded workflows, typed SDK primitives, and MCP tools.",
5
5
  "type": "module",
6
6
  "bin": {