@polderlabs/bizar 10.23.17 → 10.23.19
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.
- package/cli/commands/models.mjs +22 -7
- package/config/claude/agents/office-manager.md +3 -2
- package/config/claude/hooks/agent-model-guard.mjs +3 -3
- package/config/claude/hooks/worker-suggest.mjs +1 -1
- package/config/workflows/bizar-debug.js +2 -2
- package/config/workflows/bizar-implement.js +2 -2
- package/config/workflows/bizar-research.js +2 -2
- package/config/workflows/lib/dispatch.js +10 -3
- package/config/workflows/ultracode-research.js +2 -2
- package/config/workflows/ultracode-review.js +2 -2
- package/config/workflows/ultracode.js +2 -2
- package/package.json +1 -1
- package/packages/sdk/dist/router/agent-model-registry.js +1 -1
- package/packages/sdk/dist/router/failover-mirror.mjs +2 -2
package/cli/commands/models.mjs
CHANGED
|
@@ -675,7 +675,7 @@ export function defaultTierHint(modelId) {
|
|
|
675
675
|
// Premium first — strongest model family.
|
|
676
676
|
if (/(qwen3\.8|gpt-5|opus|o3-pro|o4-mini|sonnet-4)/.test(id)) return 'premium';
|
|
677
677
|
// High — newer mid-tier (haiku-4 is stronger than haiku-3-5).
|
|
678
|
-
if (/(haiku-4|sonnet-3-7|mini-high|m3-high|grok-3)/.test(id)) return 'high';
|
|
678
|
+
if (/(haiku-4|sonnet-3-7|mini-high|m3-high|grok-3|glm[-/]?5\.3[-/]?flash)/.test(id)) return 'high';
|
|
679
679
|
// Default — mainline sonnet / gpt-4 / m3.
|
|
680
680
|
if (/(sonnet|gpt-4|m3(-|$)|(^|[^a-z])default($|[^a-z]))/.test(id)) return 'default';
|
|
681
681
|
// Budget — small / fast variants. Match after the default family so that
|
|
@@ -1072,8 +1072,19 @@ export const NATIVE_AGENT_ALIASES = Object.freeze(['sonnet', 'opus', 'haiku', 'f
|
|
|
1072
1072
|
|
|
1073
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
1074
|
|
|
1075
|
-
|
|
1076
|
-
|
|
1075
|
+
// These are the stable Bizar role frontmatter names. Model definitions are
|
|
1076
|
+
// projected per role so Claude Code's task UI retains a meaningful Bizar agent
|
|
1077
|
+
// identity instead of displaying an opaque model-only worker.
|
|
1078
|
+
export const BIZAR_AGENT_ROLES = Object.freeze(['mike', 'paul', 'karen', 'linda', 'ria', 'greg', 'steve', 'oscar', 'todd', 'susan', 'pam', 'brenda', 'janet', 'kevin', 'brad', 'carl']);
|
|
1079
|
+
|
|
1080
|
+
export function modelAgentName(modelId, role = 'worker') {
|
|
1081
|
+
const safeRole = BIZAR_AGENT_ROLES.includes(role) ? role : 'worker';
|
|
1082
|
+
return `${safeRole}-bizar-${[...String(modelId || '').toLowerCase()].map((ch) => MODEL_AGENT_WORDS[ch] || 'unknown').join('-')}`;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
export function isGeneratedModelAgentName(name, modelIds) {
|
|
1086
|
+
return typeof name === 'string' && (Array.isArray(modelIds) ? modelIds : []).some((id) =>
|
|
1087
|
+
BIZAR_AGENT_ROLES.some((role) => name === modelAgentName(id, role)));
|
|
1077
1088
|
}
|
|
1078
1089
|
|
|
1079
1090
|
export function syncGeneratedModelAgents(modelIds, opts = {}) {
|
|
@@ -1082,8 +1093,12 @@ export function syncGeneratedModelAgents(modelIds, opts = {}) {
|
|
|
1082
1093
|
if (existsSync(agentsDir)) rmSync(agentsDir, { recursive: true, force: true });
|
|
1083
1094
|
if (!ids.length) return { agentsDir, names: [] };
|
|
1084
1095
|
mkdirSync(agentsDir, { recursive: true, mode: 0o700 });
|
|
1085
|
-
const names =
|
|
1086
|
-
ids.forEach((id
|
|
1096
|
+
const names = [];
|
|
1097
|
+
ids.forEach((id) => BIZAR_AGENT_ROLES.forEach((role) => {
|
|
1098
|
+
const name = modelAgentName(id, role);
|
|
1099
|
+
names.push(name);
|
|
1100
|
+
writeFileSync(join(agentsDir, `${name}.md`), `---\nname: ${name}\ndescription: Bizar ${role} role on a configured gateway model.\nmodel: ${id}\n---\n\nYou are the Bizar ${role} role. Follow the assigned task, preserve your role boundary, and report concise evidence to the coordinator.\n`, { mode: 0o600 });
|
|
1101
|
+
}));
|
|
1087
1102
|
return { agentsDir, names };
|
|
1088
1103
|
}
|
|
1089
1104
|
|
|
@@ -2348,10 +2363,10 @@ export async function run(name, args, isHelpRequest, deps = {}) {
|
|
|
2348
2363
|
if (wantAgentTypes) {
|
|
2349
2364
|
const router = loadRouter(routerPath);
|
|
2350
2365
|
const models = configuredEnabledModels(router);
|
|
2351
|
-
const agentTypes = Object.fromEntries(models.map((id) => [id, modelAgentName(id)]));
|
|
2366
|
+
const agentTypes = Object.fromEntries(models.map((id) => [id, Object.fromEntries(BIZAR_AGENT_ROLES.map((role) => [role, modelAgentName(id, role)]))]));
|
|
2352
2367
|
const payload = { routerPath, models, agentTypes, agentsDir: join(resolveClaudeConfigDir(), 'agents', 'bizar-models') };
|
|
2353
2368
|
if (wantJson) process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
|
|
2354
|
-
else for (const id of models) process.stdout.write(`${id}\t${agentTypes[id]}\n`);
|
|
2369
|
+
else for (const id of models) process.stdout.write(`${id}\t${JSON.stringify(agentTypes[id])}\n`);
|
|
2355
2370
|
return true;
|
|
2356
2371
|
}
|
|
2357
2372
|
|
|
@@ -63,8 +63,9 @@ from the global Bizar router. User-selected models take precedence over tier
|
|
|
63
63
|
candidates; `disabledProviders` excludes both. Native aliases are compatibility
|
|
64
64
|
transport labels bound by `bizar models`, not Anthropic selections. Native
|
|
65
65
|
Agent/Task calls accept only those aliases, so do not pass a full gateway ID in
|
|
66
|
-
`model`. Run `bizar models --agent-types --json`, look up the selected ID
|
|
67
|
-
its generated name
|
|
66
|
+
`model`. Run `bizar models --agent-types --json`, look up the selected ID and
|
|
67
|
+
Bizar role, pass its role-specific generated name (for example `greg-*` or
|
|
68
|
+
`todd-*`) as `subagent_type`, and omit `model`; the generated global
|
|
68
69
|
definition's `model:` frontmatter selects the full gateway ID. This is the
|
|
69
70
|
default for individual subagents, workflows, and agent-team teammates. For
|
|
70
71
|
teams, do not name a competing model in the spawn prompt. Use `bizar worker`
|
|
@@ -49,7 +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 {
|
|
52
|
+
import { isGeneratedModelAgentName } from '../../../cli/commands/models.mjs';
|
|
53
53
|
|
|
54
54
|
function deny(reason) {
|
|
55
55
|
return {
|
|
@@ -226,7 +226,7 @@ export async function guardAgentModel(input, options = {}) {
|
|
|
226
226
|
// Inheritance is safe only when the auditable Bizar selection equals the
|
|
227
227
|
// actual global Claude parent model and is a selected, enabled user pick.
|
|
228
228
|
if (!requested || requested === 'inherit') {
|
|
229
|
-
const generated =
|
|
229
|
+
const generated = isGeneratedModelAgentName(toolInput.subagent_type, [...userPicks]);
|
|
230
230
|
if (generated && !requested) return {};
|
|
231
231
|
const configured = configuredContext;
|
|
232
232
|
const parent = readConfiguredParentModel(options);
|
|
@@ -252,7 +252,7 @@ export async function guardAgentModel(input, options = {}) {
|
|
|
252
252
|
// a gateway accepts arbitrary IDs for the main conversation. Full IDs belong
|
|
253
253
|
// in Bizar's generated subagent frontmatter, not in this enum-shaped field.
|
|
254
254
|
if (userPicks.has(requested)) {
|
|
255
|
-
return deny(
|
|
255
|
+
return deny('Bizar Agent dispatch blocked: native Agent model accepts aliases only. Use the role-specific generated subagent_type from `bizar models --agent-types --json` and omit model; re-run `bizar models` if it is missing.');
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
// F-185 contract: when the orchestrator passes both `routingDecisionId`
|
|
@@ -60,7 +60,7 @@ const ROUTE_POLICY = [
|
|
|
60
60
|
'Adaptive Bizar routing policy:',
|
|
61
61
|
'- If this is the primary session, you ARE @mike. First do only bounded read-only orientation. Then ask one concise clarification question describing the inferred outcome, the material choice/risk, and your proposed coordination mode. Wait for the answer before edits, branches, tests, or editor dispatch. If the user explicitly waives questions, continue autonomously.',
|
|
62
62
|
'- After clarification, choose the lightest coordination mode: a direct tiny edit, one isolated Agent for a bounded change, a native Bizar Workflow for repeatable phased work, parallel Agents for disjoint scopes, or an Agent team only when 3+ sustained roles need cross-talk. Do not force a workflow or team when it adds no value.',
|
|
63
|
-
'- For every Agent, workflow worker, or agent-team teammate, choose one exact ID from the enabled `bizar models` user selection, then obtain its generated definition name from `bizar models --agent-types --json
|
|
63
|
+
'- For every Agent, workflow worker, or agent-team teammate, choose one exact ID from the enabled `bizar models` user selection, then obtain its role-specific generated definition name from `bizar models --agent-types --json` (for example greg or todd). Pass that name as `subagent_type` and OMIT the native `model` field. The definition frontmatter owns the exact gateway ID. Never put a raw gateway ID or a Claude family alias in the native model field; aliases are compatibility-only. For teams, do not name a competing model in the spawn prompt. If the map or definition is missing, run `bizar models`; do not retry by cycling providers or tiers. Every editing worker uses call-level `isolation: "worktree"`. For genuinely disjoint writable scopes, dispatch concurrently; otherwise use one owner.',
|
|
64
64
|
'- Consume terminal agent results, merge queued worktrees with bizar worktree-merge, and run integration checks in the primary session. A subagent may not recursively dispatch itself.',
|
|
65
65
|
'- Do NOT execute any tool you do not have. If a tool you need is missing from your tools list, dispatch to a subagent that has it — do not pretend you have it.',
|
|
66
66
|
'- If you are already running as a Bizar custom agent, follow your assigned role and do not recursively dispatch yourself.',
|
|
@@ -26,7 +26,7 @@ const routeModel = (risk) => {
|
|
|
26
26
|
const selected = candidate.trim()
|
|
27
27
|
return selected
|
|
28
28
|
}
|
|
29
|
-
const routeAgentType = (risk) => WORKFLOW_ROUTING.agentTypes?.[routeModel(risk)] || ''
|
|
29
|
+
const routeAgentType = (risk, role = 'todd') => WORKFLOW_ROUTING.agentTypes?.[routeModel(risk)]?.[({ 'research-analyst': 'greg', planner: 'paul', implementer: 'todd', 'qa-reviewer': 'linda', reviewer: 'linda' }[role] || role)] || ''
|
|
30
30
|
if (!routeModel('medium') || !routeModel('high') || !routeAgentType('medium') || !routeAgentType('high')) {
|
|
31
31
|
return {
|
|
32
32
|
status: 'blocked',
|
|
@@ -38,7 +38,7 @@ const dispatchAgent = (agentFn, agentName, prompt, opts = {}) => {
|
|
|
38
38
|
const sequence = ++WORKFLOW_DISPATCH_SEQUENCE
|
|
39
39
|
const prefix = `[Bizar dispatch ${sequence}: ${agentName}; role=${opts.role || 'worker'}; phase=${opts.phase || 'work'}; label=${opts.label || agentName}]`
|
|
40
40
|
const agentOptions = {
|
|
41
|
-
subagent_type: routeAgentType(opts.risk || 'medium'),
|
|
41
|
+
subagent_type: routeAgentType(opts.risk || 'medium', opts.role),
|
|
42
42
|
effort: opts.risk === 'high' ? 'high' : 'medium',
|
|
43
43
|
}
|
|
44
44
|
if (opts.schema) agentOptions.schema = opts.schema
|
|
@@ -25,7 +25,7 @@ const routeModel = (risk) => {
|
|
|
25
25
|
const selected = candidate.trim()
|
|
26
26
|
return selected
|
|
27
27
|
}
|
|
28
|
-
const routeAgentType = (risk) => WORKFLOW_ROUTING.agentTypes?.[routeModel(risk)] || ''
|
|
28
|
+
const routeAgentType = (risk, role = 'todd') => WORKFLOW_ROUTING.agentTypes?.[routeModel(risk)]?.[({ 'research-analyst': 'greg', planner: 'paul', implementer: 'todd', 'qa-reviewer': 'linda', reviewer: 'linda' }[role] || role)] || ''
|
|
29
29
|
if (!routeModel('medium') || !routeModel('high') || !routeAgentType('medium') || !routeAgentType('high')) {
|
|
30
30
|
return {
|
|
31
31
|
status: 'blocked',
|
|
@@ -37,7 +37,7 @@ const dispatchAgent = (agentFn, agentName, prompt, opts = {}) => {
|
|
|
37
37
|
const sequence = ++WORKFLOW_DISPATCH_SEQUENCE
|
|
38
38
|
const prefix = `[Bizar dispatch ${sequence}: ${agentName}; role=${opts.role || 'worker'}; phase=${opts.phase || 'work'}; label=${opts.label || agentName}]`
|
|
39
39
|
const agentOptions = {
|
|
40
|
-
subagent_type: routeAgentType(opts.risk || 'medium'),
|
|
40
|
+
subagent_type: routeAgentType(opts.risk || 'medium', opts.role),
|
|
41
41
|
effort: opts.risk === 'high' ? 'high' : 'medium',
|
|
42
42
|
}
|
|
43
43
|
if (opts.schema) agentOptions.schema = opts.schema
|
|
@@ -26,7 +26,7 @@ const routeModel = (risk) => {
|
|
|
26
26
|
const selected = candidate.trim()
|
|
27
27
|
return selected
|
|
28
28
|
}
|
|
29
|
-
const routeAgentType = (risk) => WORKFLOW_ROUTING.agentTypes?.[routeModel(risk)] || ''
|
|
29
|
+
const routeAgentType = (risk, role = 'todd') => WORKFLOW_ROUTING.agentTypes?.[routeModel(risk)]?.[({ 'research-analyst': 'greg', planner: 'paul', implementer: 'todd', 'qa-reviewer': 'linda', reviewer: 'linda' }[role] || role)] || ''
|
|
30
30
|
if (!routeModel('medium') || !routeModel('high') || !routeAgentType('medium') || !routeAgentType('high')) {
|
|
31
31
|
return {
|
|
32
32
|
status: 'blocked',
|
|
@@ -38,7 +38,7 @@ const dispatchAgent = (agentFn, agentName, prompt, opts = {}) => {
|
|
|
38
38
|
const sequence = ++WORKFLOW_DISPATCH_SEQUENCE
|
|
39
39
|
const prefix = `[Bizar dispatch ${sequence}: ${agentName}; role=${opts.role || 'worker'}; phase=${opts.phase || 'work'}; label=${opts.label || agentName}]`
|
|
40
40
|
const agentOptions = {
|
|
41
|
-
subagent_type: routeAgentType(opts.risk || 'medium'),
|
|
41
|
+
subagent_type: routeAgentType(opts.risk || 'medium', opts.role),
|
|
42
42
|
effort: opts.risk === 'high' ? 'high' : 'medium',
|
|
43
43
|
}
|
|
44
44
|
if (opts.schema) agentOptions.schema = opts.schema
|
|
@@ -34,7 +34,14 @@ import { homedir } from 'node:os';
|
|
|
34
34
|
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
35
35
|
|
|
36
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
|
-
|
|
37
|
+
const ROLE_TO_BIZAR_AGENT = Object.freeze({
|
|
38
|
+
'research-analyst': 'greg', planner: 'paul', implementer: 'todd',
|
|
39
|
+
'qa-reviewer': 'linda', reviewer: 'linda', 'debug-specialist': 'carl',
|
|
40
|
+
'principal-engineer': 'karen', 'ui-designer': 'ria', 'it-lead': 'steve',
|
|
41
|
+
'knowledge-manager': 'oscar', 'support-tech': 'kevin', 'exec-assistant': 'pam',
|
|
42
|
+
'office-coordinator': 'brenda', 'office-greeter': 'janet', 'brand-designer': 'brad',
|
|
43
|
+
});
|
|
44
|
+
function modelAgentName(modelId, role = 'todd') { return `${ROLE_TO_BIZAR_AGENT[role] || (Object.values(ROLE_TO_BIZAR_AGENT).includes(role) ? role : 'todd')}-bizar-${[...String(modelId || '').toLowerCase()].map((ch) => MODEL_AGENT_WORDS[ch] || 'unknown').join('-')}`; }
|
|
38
45
|
|
|
39
46
|
const { O_APPEND, O_CREAT, O_WRONLY } = fsConstants;
|
|
40
47
|
|
|
@@ -88,7 +95,7 @@ function defaultTierHintForId(modelId) {
|
|
|
88
95
|
const id = String(modelId || '').toLowerCase();
|
|
89
96
|
if (!id) return 'default';
|
|
90
97
|
if (/(qwen3\.8|gpt-5|opus|o3-pro|o4-mini|sonnet-4)/.test(id)) return 'premium';
|
|
91
|
-
if (/(haiku-4|sonnet-3-7|mini-high|m3-high|grok-3)/.test(id)) return 'high';
|
|
98
|
+
if (/(haiku-4|sonnet-3-7|mini-high|m3-high|grok-3|glm[-/]?5\.3[-/]?flash)/.test(id)) return 'high';
|
|
92
99
|
if (/(sonnet|gpt-4|m3(-|$)|(^|[^a-z])default($|[^a-z]))/.test(id)) return 'default';
|
|
93
100
|
if (/(nano|mini[-/]|flash|lite|tiny|haiku($|[-_]\d))/.test(id)) return 'budget';
|
|
94
101
|
return 'mid';
|
|
@@ -804,7 +811,7 @@ export function classifyDispatchOutcome(result, error, startMs) {
|
|
|
804
811
|
export function augmentPayload(opts, decision, agentName, context = {}) {
|
|
805
812
|
return {
|
|
806
813
|
...opts,
|
|
807
|
-
subagent_type: modelAgentName(decision.modelId),
|
|
814
|
+
subagent_type: modelAgentName(decision.modelId, opts.role),
|
|
808
815
|
additionalContext: {
|
|
809
816
|
...(opts.additionalContext && typeof opts.additionalContext === 'object' ? opts.additionalContext : {}),
|
|
810
817
|
bizarConfiguredModel: decision.modelId ?? null,
|
|
@@ -24,7 +24,7 @@ const routeModel = (risk) => {
|
|
|
24
24
|
const selected = candidate.trim()
|
|
25
25
|
return selected
|
|
26
26
|
}
|
|
27
|
-
const routeAgentType = (risk) => WORKFLOW_ROUTING.agentTypes?.[routeModel(risk)] || ''
|
|
27
|
+
const routeAgentType = (risk, role = 'todd') => WORKFLOW_ROUTING.agentTypes?.[routeModel(risk)]?.[({ 'research-analyst': 'greg', planner: 'paul', implementer: 'todd', 'qa-reviewer': 'linda', reviewer: 'linda' }[role] || role)] || ''
|
|
28
28
|
if (!routeModel('medium') || !routeModel('high') || !routeAgentType('medium') || !routeAgentType('high')) {
|
|
29
29
|
return {
|
|
30
30
|
status: 'blocked',
|
|
@@ -36,7 +36,7 @@ const dispatchAgent = (agentFn, agentName, prompt, opts = {}) => {
|
|
|
36
36
|
const sequence = ++WORKFLOW_DISPATCH_SEQUENCE
|
|
37
37
|
const prefix = `[Bizar dispatch ${sequence}: ${agentName}; role=${opts.role || 'worker'}; phase=${opts.phase || 'work'}; label=${opts.label || agentName}]`
|
|
38
38
|
const agentOptions = {
|
|
39
|
-
subagent_type: routeAgentType(opts.risk || 'medium'),
|
|
39
|
+
subagent_type: routeAgentType(opts.risk || 'medium', opts.role),
|
|
40
40
|
effort: opts.risk === 'high' ? 'high' : 'medium',
|
|
41
41
|
}
|
|
42
42
|
if (opts.schema) agentOptions.schema = opts.schema
|
|
@@ -23,7 +23,7 @@ const routeModel = (risk) => {
|
|
|
23
23
|
const selected = candidate.trim()
|
|
24
24
|
return selected
|
|
25
25
|
}
|
|
26
|
-
const routeAgentType = (risk) => WORKFLOW_ROUTING.agentTypes?.[routeModel(risk)] || ''
|
|
26
|
+
const routeAgentType = (risk, role = 'todd') => WORKFLOW_ROUTING.agentTypes?.[routeModel(risk)]?.[({ 'research-analyst': 'greg', planner: 'paul', implementer: 'todd', 'qa-reviewer': 'linda', reviewer: 'linda' }[role] || role)] || ''
|
|
27
27
|
if (!routeModel('medium') || !routeModel('high') || !routeAgentType('medium') || !routeAgentType('high')) {
|
|
28
28
|
return {
|
|
29
29
|
status: 'blocked',
|
|
@@ -35,7 +35,7 @@ const dispatchAgent = (agentFn, agentName, prompt, opts = {}) => {
|
|
|
35
35
|
const sequence = ++WORKFLOW_DISPATCH_SEQUENCE
|
|
36
36
|
const prefix = `[Bizar dispatch ${sequence}: ${agentName}; role=${opts.role || 'worker'}; phase=${opts.phase || 'work'}; label=${opts.label || agentName}]`
|
|
37
37
|
const agentOptions = {
|
|
38
|
-
subagent_type: routeAgentType(opts.risk || 'medium'),
|
|
38
|
+
subagent_type: routeAgentType(opts.risk || 'medium', opts.role),
|
|
39
39
|
effort: opts.risk === 'high' ? 'high' : 'medium',
|
|
40
40
|
}
|
|
41
41
|
if (opts.schema) agentOptions.schema = opts.schema
|
|
@@ -25,7 +25,7 @@ const routeModel = (risk) => {
|
|
|
25
25
|
const selected = candidate.trim()
|
|
26
26
|
return selected
|
|
27
27
|
}
|
|
28
|
-
const routeAgentType = (risk) => WORKFLOW_ROUTING.agentTypes?.[routeModel(risk)] || ''
|
|
28
|
+
const routeAgentType = (risk, role = 'todd') => WORKFLOW_ROUTING.agentTypes?.[routeModel(risk)]?.[({ 'research-analyst': 'greg', planner: 'paul', implementer: 'todd', 'qa-reviewer': 'linda', reviewer: 'linda' }[role] || role)] || ''
|
|
29
29
|
if (!routeModel('medium') || !routeModel('high') || !routeAgentType('medium') || !routeAgentType('high')) {
|
|
30
30
|
return {
|
|
31
31
|
status: 'blocked',
|
|
@@ -37,7 +37,7 @@ const dispatchAgent = (agentFn, agentName, prompt, opts = {}) => {
|
|
|
37
37
|
const sequence = ++WORKFLOW_DISPATCH_SEQUENCE
|
|
38
38
|
const prefix = `[Bizar dispatch ${sequence}: ${agentName}; role=${opts.role || 'worker'}; phase=${opts.phase || 'work'}; label=${opts.label || agentName}]`
|
|
39
39
|
const agentOptions = {
|
|
40
|
-
subagent_type: routeAgentType(opts.risk || 'medium'),
|
|
40
|
+
subagent_type: routeAgentType(opts.risk || 'medium', opts.role),
|
|
41
41
|
effort: opts.risk === 'high' ? 'high' : 'medium',
|
|
42
42
|
}
|
|
43
43
|
if (opts.schema) agentOptions.schema = opts.schema
|
package/package.json
CHANGED
|
@@ -213,7 +213,7 @@ export function defaultTierHintForId(modelId) {
|
|
|
213
213
|
return "default";
|
|
214
214
|
if (/(qwen3\.8|gpt-5|opus|o3-pro|o4-mini|sonnet-4)/.test(id))
|
|
215
215
|
return "premium";
|
|
216
|
-
if (/(haiku-4|sonnet-3-7|mini-high|m3-high|grok-3)/.test(id))
|
|
216
|
+
if (/(haiku-4|sonnet-3-7|mini-high|m3-high|grok-3|glm[-/]?5\.3[-/]?flash)/.test(id))
|
|
217
217
|
return "high";
|
|
218
218
|
if (/(sonnet|gpt-4|m3(-|$)|(^|[^a-z])default($|[^a-z]))/.test(id))
|
|
219
219
|
return "default";
|
|
@@ -31,7 +31,7 @@ export function defaultTierHintForId(modelId) {
|
|
|
31
31
|
const id = String(modelId || "").toLowerCase();
|
|
32
32
|
if (!id) return "default";
|
|
33
33
|
if (/(qwen3\.8|gpt-5|opus|o3-pro|o4-mini|sonnet-4)/.test(id)) return "premium";
|
|
34
|
-
if (/(haiku-4|sonnet-3-7|mini-high|m3-high|grok-3)/.test(id)) return "high";
|
|
34
|
+
if (/(haiku-4|sonnet-3-7|mini-high|m3-high|grok-3|glm[-/]?5\.3[-/]?flash)/.test(id)) return "high";
|
|
35
35
|
if (/(sonnet|gpt-4|m3(-|$)|(^|[^a-z])default($|[^a-z]))/.test(id)) return "default";
|
|
36
36
|
if (/(nano|mini[-/]|flash|lite|tiny|haiku($|[-_]\d))/.test(id)) return "budget";
|
|
37
37
|
return "mid";
|
|
@@ -312,4 +312,4 @@ export function classifyError(message) {
|
|
|
312
312
|
if (/(invalid.*model|unknown.*model|model not found|no such model|not a valid model)/.test(m)) return "invalid-model";
|
|
313
313
|
if (/(quality|incomplete|truncated|garbage|low quality|incoherent)/.test(m)) return "model-quality";
|
|
314
314
|
return "invalid-model";
|
|
315
|
-
}
|
|
315
|
+
}
|