@polderlabs/bizar 10.23.19 → 10.23.21
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 +49 -2
- package/cli/provision.mjs +3 -1
- package/config/claude/agents/_shared/CLAUDE_TOOLS.md +1 -4
- package/config/claude/agents/office-manager.md +18 -17
- package/config/claude/hooks/agent-model-guard.mjs +7 -4
- package/config/claude/hooks/sessionstart-prime.mjs +1 -1
- package/config/claude/hooks/team-lifecycle.mjs +5 -1
- package/config/claude/hooks/worker-suggest.mjs +3 -3
- package/config/workflows/bizar-debug.js +1 -1
- package/config/workflows/bizar-implement.js +1 -1
- package/config/workflows/bizar-research.js +1 -1
- package/config/workflows/lib/dispatch.js +7 -6
- package/config/workflows/ultracode-research.js +1 -1
- package/config/workflows/ultracode-review.js +1 -1
- package/config/workflows/ultracode.js +1 -1
- package/package.json +1 -1
- package/packages/sdk/dist/version.d.ts +1 -1
- package/packages/sdk/dist/version.js +1 -1
- package/packages/sdk/package.json +1 -1
package/cli/commands/models.mjs
CHANGED
|
@@ -995,6 +995,9 @@ export function applyModelOverrides({ settingsJsonPath, pickedIds, liveIds = [],
|
|
|
995
995
|
const generatedAgents = syncGeneratedModelAgents(synced, {
|
|
996
996
|
agentsDir: settingsJsonPath === undefined ? undefined : join(dirname(path), 'agents', 'bizar-models'),
|
|
997
997
|
});
|
|
998
|
+
const stableRoleAgents = syncStableRoleModelAgents(synced, {
|
|
999
|
+
agentsDir: settingsJsonPath === undefined ? undefined : join(dirname(path), 'agents'),
|
|
1000
|
+
});
|
|
998
1001
|
if (requiresGatewayModelDiscovery(synced)) {
|
|
999
1002
|
settings.env = {
|
|
1000
1003
|
...(settings.env || {}),
|
|
@@ -1026,6 +1029,7 @@ export function applyModelOverrides({ settingsJsonPath, pickedIds, liveIds = [],
|
|
|
1026
1029
|
skippedDisabled,
|
|
1027
1030
|
nativeAgentAliases,
|
|
1028
1031
|
generatedAgents,
|
|
1032
|
+
stableRoleAgents,
|
|
1029
1033
|
settingsPath: path,
|
|
1030
1034
|
};
|
|
1031
1035
|
}
|
|
@@ -1102,6 +1106,38 @@ export function syncGeneratedModelAgents(modelIds, opts = {}) {
|
|
|
1102
1106
|
return { agentsDir, names };
|
|
1103
1107
|
}
|
|
1104
1108
|
|
|
1109
|
+
/**
|
|
1110
|
+
* Bind the ordinary, recognizable Bizar role definitions to the default
|
|
1111
|
+
* operator-selected model. Claude Code watches definition edits, so future
|
|
1112
|
+
* `subagent_type: greg` calls use the chosen full ID without a restart or an
|
|
1113
|
+
* alias-only native `model` parameter. Generated per-model definitions remain
|
|
1114
|
+
* available for explicitly requested advanced routing.
|
|
1115
|
+
*/
|
|
1116
|
+
export function syncStableRoleModelAgents(modelIds, opts = {}) {
|
|
1117
|
+
const agentsDir = opts.agentsDir || join(resolveClaudeConfigDir(), 'agents');
|
|
1118
|
+
const defaultModel = (Array.isArray(modelIds) ? modelIds : []).find((id) => typeof id === 'string' && id.trim())?.trim() || null;
|
|
1119
|
+
const names = [];
|
|
1120
|
+
for (const role of BIZAR_AGENT_ROLES) {
|
|
1121
|
+
const path = join(agentsDir, `${role}.md`);
|
|
1122
|
+
if (!existsSync(path)) continue;
|
|
1123
|
+
const source = readFileSync(path, 'utf8');
|
|
1124
|
+
if (!source.startsWith('---\n')) continue;
|
|
1125
|
+
const end = source.indexOf('\n---', 4);
|
|
1126
|
+
if (end < 0) continue;
|
|
1127
|
+
const frontmatter = source.slice(4, end);
|
|
1128
|
+
const body = source.slice(end + 4);
|
|
1129
|
+
const nextFrontmatter = defaultModel
|
|
1130
|
+
? (/^model:\s*.*$/m.test(frontmatter)
|
|
1131
|
+
? frontmatter.replace(/^model:\s*.*$/m, `model: ${defaultModel}`)
|
|
1132
|
+
: `${frontmatter}\nmodel: ${defaultModel}`)
|
|
1133
|
+
: frontmatter.replace(/^model:\s*.*\n?/m, '');
|
|
1134
|
+
const next = `---\n${nextFrontmatter}\n---${body}`;
|
|
1135
|
+
if (next !== source) writeFileSync(path, next, { mode: 0o600 });
|
|
1136
|
+
names.push(role);
|
|
1137
|
+
}
|
|
1138
|
+
return { agentsDir, names, defaultModel };
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1105
1141
|
const NATIVE_AGENT_OVERRIDE_KEYS = Object.freeze({
|
|
1106
1142
|
sonnet: 'claude-sonnet-5',
|
|
1107
1143
|
opus: 'claude-opus-5',
|
|
@@ -2364,9 +2400,20 @@ export async function run(name, args, isHelpRequest, deps = {}) {
|
|
|
2364
2400
|
const router = loadRouter(routerPath);
|
|
2365
2401
|
const models = configuredEnabledModels(router);
|
|
2366
2402
|
const agentTypes = Object.fromEntries(models.map((id) => [id, Object.fromEntries(BIZAR_AGENT_ROLES.map((role) => [role, modelAgentName(id, role)]))]));
|
|
2367
|
-
const
|
|
2403
|
+
const stableRoleTypes = Object.fromEntries(BIZAR_AGENT_ROLES.map((role) => [role, role]));
|
|
2404
|
+
const payload = {
|
|
2405
|
+
routerPath,
|
|
2406
|
+
models,
|
|
2407
|
+
defaultModel: models[0] || null,
|
|
2408
|
+
stableRoleTypes,
|
|
2409
|
+
agentTypes,
|
|
2410
|
+
agentsDir: join(resolveClaudeConfigDir(), 'agents', 'bizar-models'),
|
|
2411
|
+
};
|
|
2368
2412
|
if (wantJson) process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
|
|
2369
|
-
else
|
|
2413
|
+
else {
|
|
2414
|
+
process.stdout.write(`default\t${JSON.stringify(stableRoleTypes)}\n`);
|
|
2415
|
+
for (const id of models) process.stdout.write(`${id}\t${JSON.stringify(agentTypes[id])}\n`);
|
|
2416
|
+
}
|
|
2370
2417
|
return true;
|
|
2371
2418
|
}
|
|
2372
2419
|
|
package/cli/provision.mjs
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
configuredEnabledModels,
|
|
38
38
|
requiresGatewayModelDiscovery,
|
|
39
39
|
syncGeneratedModelAgents,
|
|
40
|
+
syncStableRoleModelAgents,
|
|
40
41
|
} from './commands/models.mjs';
|
|
41
42
|
import { validateNativeWorkflowDirectory } from '../config/workflows/lib/native-contract.mjs';
|
|
42
43
|
|
|
@@ -514,7 +515,8 @@ export function syncConfiguredModelAgents({ dryRun = false } = {}) {
|
|
|
514
515
|
const models = configuredEnabledModels(router);
|
|
515
516
|
if (dryRun) return { ok: true, message: `[dry-run] would sync ${models.length} generated model agent(s)`, models };
|
|
516
517
|
const generated = syncGeneratedModelAgents(models, { agentsDir: join(resolveClaudeDir(), 'agents', 'bizar-models') });
|
|
517
|
-
|
|
518
|
+
const stable = syncStableRoleModelAgents(models, { agentsDir: join(resolveClaudeDir(), 'agents') });
|
|
519
|
+
return { ok: true, message: `${stable.names.length} stable role model(s) and ${generated.names.length} generated model agent(s) synced`, ...generated, stableRoleAgents: stable };
|
|
518
520
|
}
|
|
519
521
|
|
|
520
522
|
export async function syncSkillFiles({ dryRun = false, force = false } = {}) {
|
|
@@ -368,10 +368,7 @@ Background (async, returns immediately):
|
|
|
368
368
|
|
|
369
369
|
### Coordinating multiple background agents
|
|
370
370
|
|
|
371
|
-
When fanning out several research or exploration tasks, issue multiple `Agent` calls in the **same message** with `run_in_background: true`. Each runs in parallel. Collect
|
|
372
|
-
|
|
373
|
-
- Waiting for the user to ask, then re-dispatching sync `Agent` calls that summarize the background work.
|
|
374
|
-
- Reading the background agent's final summary when it completes.
|
|
371
|
+
When fanning out several research or exploration tasks, issue multiple `Agent` calls in the **same message** with `run_in_background: true`. Each runs in parallel. Collect the original final summary/result when the task completes. Do not re-dispatch a completed background agent to summarize its work: that creates a new, context-poor task and its acknowledgement is not a replacement for the original deliverable.
|
|
375
372
|
|
|
376
373
|
---
|
|
377
374
|
|
|
@@ -44,8 +44,8 @@ That definition's frontmatter owns the full-ID selection for ordinary agents,
|
|
|
44
44
|
workflows, and teams. Include the raw ID in `additionalContext.bizarConfiguredModel`
|
|
45
45
|
for audit telemetry. Never use `inherit` or an unconfigured provider default.
|
|
46
46
|
`sonnet`, `opus`, `haiku`, and `fable` are compatibility aliases only. If no
|
|
47
|
-
|
|
48
|
-
`bizar models`; never omit the
|
|
47
|
+
stable Bizar role definition exists, stop and ask the operator to run
|
|
48
|
+
`bizar models`; never omit the stable role type or cycle providers.
|
|
49
49
|
|
|
50
50
|
Invoke the selected workflow by `name` first. If Claude reports that the Bizar
|
|
51
51
|
name is unavailable, resolve the active Claude config directory and retry once
|
|
@@ -59,21 +59,20 @@ implementation around a broken workflow installation.
|
|
|
59
59
|
## Models
|
|
60
60
|
|
|
61
61
|
For every dispatch, select the cheapest sufficient enabled configured model
|
|
62
|
-
from the global Bizar router.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
Bizar role, pass its role-specific generated name (for example `greg-*` or
|
|
68
|
-
`todd-*`) as `subagent_type`, and omit `model`; the generated global
|
|
62
|
+
from the global Bizar router. The default selected model is written into each
|
|
63
|
+
stable global Bizar role definition by `bizar models`; `disabledProviders`
|
|
64
|
+
excludes invalid selections. Native aliases are compatibility transport labels
|
|
65
|
+
bound by `bizar models`, not Anthropic selections. Pass the stable role name
|
|
66
|
+
(for example `greg` or `todd`) as `subagent_type` and omit native `model`; the
|
|
69
67
|
definition's `model:` frontmatter selects the full gateway ID. This is the
|
|
70
|
-
default for individual subagents, workflows, and agent-team teammates.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
default for individual subagents, workflows, and agent-team teammates. Use
|
|
69
|
+
`bizar models --agent-types --json` only for an explicit advanced per-model
|
|
70
|
+
choice. For teams, do not name a competing model in the spawn prompt. Use
|
|
71
|
+
`bizar worker` only when a separately launched process worktree is useful. If
|
|
72
|
+
no enabled configured candidate or stable definition exists, stop with the
|
|
74
73
|
configuration error and run `bizar models`. Never let Claude choose an
|
|
75
|
-
unconfigured default,
|
|
76
|
-
|
|
74
|
+
unconfigured default, use an unmapped alias, or retry by cycling models,
|
|
75
|
+
providers, or tiers.
|
|
77
76
|
|
|
78
77
|
## Worktree Discipline and integration
|
|
79
78
|
|
|
@@ -94,8 +93,10 @@ Worktree branches use `wt/<agent_type>-<short-task-id>`.
|
|
|
94
93
|
- Inspect an idle task after its second idle notification. Stop and reassign a
|
|
95
94
|
task that has no progress/evidence; do not model-cycle it.
|
|
96
95
|
- A `TaskCompleted`, `SubagentStop`, or `<task-notification>` is terminal. Read
|
|
97
|
-
its `<result
|
|
98
|
-
the active objective. Never route a completion notification as
|
|
96
|
+
its original `<result>` once, mark the worker done/failed, merge queued work,
|
|
97
|
+
and continue the active objective. Never route a completion notification as
|
|
98
|
+
a new prompt, send it back to the worker, or replace its deliverable with a
|
|
99
|
+
later acknowledgement/status reply.
|
|
99
100
|
|
|
100
101
|
## Learning and completion
|
|
101
102
|
|
|
@@ -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 { isGeneratedModelAgentName } from '../../../cli/commands/models.mjs';
|
|
52
|
+
import { BIZAR_AGENT_ROLES, isGeneratedModelAgentName } from '../../../cli/commands/models.mjs';
|
|
53
53
|
|
|
54
54
|
function deny(reason) {
|
|
55
55
|
return {
|
|
@@ -223,11 +223,14 @@ export async function guardAgentModel(input, options = {}) {
|
|
|
223
223
|
: '';
|
|
224
224
|
|
|
225
225
|
// The native Agent tool rejects arbitrary gateway IDs in its `model` enum.
|
|
226
|
-
//
|
|
227
|
-
//
|
|
226
|
+
// Stable Bizar definitions own their selected full ID in frontmatter, so an
|
|
227
|
+
// omitted native model is correct and must not be coupled to the parent
|
|
228
|
+
// session model. Generated per-model definitions remain valid too.
|
|
228
229
|
if (!requested || requested === 'inherit') {
|
|
229
230
|
const generated = isGeneratedModelAgentName(toolInput.subagent_type, [...userPicks]);
|
|
231
|
+
const stable = BIZAR_AGENT_ROLES.includes(String(toolInput.subagent_type || '').trim());
|
|
230
232
|
if (generated && !requested) return {};
|
|
233
|
+
if (stable && !requested && userPicks.size > 0) return {};
|
|
231
234
|
const configured = configuredContext;
|
|
232
235
|
const parent = readConfiguredParentModel(options);
|
|
233
236
|
if (!configured || configured !== parent || !userPicks.has(configured)) {
|
|
@@ -252,7 +255,7 @@ export async function guardAgentModel(input, options = {}) {
|
|
|
252
255
|
// a gateway accepts arbitrary IDs for the main conversation. Full IDs belong
|
|
253
256
|
// in Bizar's generated subagent frontmatter, not in this enum-shaped field.
|
|
254
257
|
if (userPicks.has(requested)) {
|
|
255
|
-
return deny('Bizar Agent dispatch blocked: native Agent model accepts aliases only. Use the role
|
|
258
|
+
return deny('Bizar Agent dispatch blocked: native Agent model accepts aliases only. Use the stable Bizar role as subagent_type (for example `greg` or `todd`) and omit model; run `bizar models` if the role definition is missing.');
|
|
256
259
|
}
|
|
257
260
|
|
|
258
261
|
// F-185 contract: when the orchestrator passes both `routingDecisionId`
|
|
@@ -188,7 +188,7 @@ function startupBriefing(cwd, featureBrief, recentCommits, projectLine, progress
|
|
|
188
188
|
if (progressLast) lines.push(`- Progress: ${progressLast}.`);
|
|
189
189
|
lines.push('- You are @mike. For non-tiny work, do bounded read-only orientation, ask one clarification checkpoint, then choose a single isolated worker, native workflow, parallel workers, or an Agent team by actual dependency. Use worktrees for editors and explicit Bizar models for every Agent.');
|
|
190
190
|
lines.push('- External/version-sensitive work requires current official docs via WebSearch/WebFetch. Use relevant installed skills; apply i-have-adhd to user output. WIP=1.');
|
|
191
|
-
lines.push('- TaskCompleted/SubagentStop/<task-notification> is terminal: consume <result
|
|
191
|
+
lines.push('- TaskCompleted/SubagentStop/<task-notification> is terminal: consume its original <result> once, mark done/failed, merge queued work, and continue the objective. Never turn a terminal notification into a worker follow-up or replace that result with a later status reply.');
|
|
192
192
|
// Default-first-stop hint when nothing is active yet.
|
|
193
193
|
if (featureBrief && featureBrief.active.length === 0) {
|
|
194
194
|
lines.push(
|
|
@@ -56,7 +56,11 @@ export function handleTeamLifecycle(input, options = {}) {
|
|
|
56
56
|
task.updatedAt = now;
|
|
57
57
|
if (event === 'TaskCompleted' || event === 'SubagentStop') {
|
|
58
58
|
task.state = /fail|error/i.test(record.status) ? 'failed' : 'completed';
|
|
59
|
-
|
|
59
|
+
// Claude Code already delivers the terminal result to the parent. Do
|
|
60
|
+
// not add another instruction here: lifecycle hook output can be
|
|
61
|
+
// surfaced in an agent context and turn a completed task into a bogus
|
|
62
|
+
// follow-up conversation ("Ready", "No action needed", etc.).
|
|
63
|
+
// This hook owns durable liveness telemetry only.
|
|
60
64
|
} else if (event === 'TeammateIdle') {
|
|
61
65
|
task.idleCount = Number(task.idleCount || 0) + 1;
|
|
62
66
|
if (task.idleCount >= 2) context = `Agent task ${taskId} has been idle ${task.idleCount} times. Inspect its evidence now; stop and reassign if it made no progress. Do not model-cycle.`;
|
|
@@ -60,8 +60,8 @@ 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,
|
|
64
|
-
'- Consume terminal agent
|
|
63
|
+
'- For every normal Agent, workflow worker, or agent-team teammate, pass its stable Bizar role name (`greg`, `todd`, `linda`, and so on) as `subagent_type` and OMIT the native `model` field. `bizar models` keeps that global role definition frontmatter bound to the default enabled operator-selected gateway ID. Never put a raw gateway ID or a Claude family alias in the native model field; aliases are compatibility-only. `bizar models --agent-types --json` is advanced explicit per-model routing only. For teams, do not name a competing model in the spawn prompt. If the stable role 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
|
+
'- Consume each terminal agent result exactly once from its original `<result>`/final summary, merge queued worktrees with bizar worktree-merge, and run integration checks in the primary session. Never send a terminal notification, idle ping, or completed task back to that agent as a follow-up and never re-dispatch a completed background agent merely to summarize its result. 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.',
|
|
67
67
|
].join('\n');
|
|
@@ -83,7 +83,7 @@ process.stdin.on('end', async () => {
|
|
|
83
83
|
process.stdout.write(JSON.stringify({
|
|
84
84
|
hookSpecificOutput: {
|
|
85
85
|
hookEventName: 'UserPromptSubmit',
|
|
86
|
-
additionalContext: 'Bizar terminal task update: consume
|
|
86
|
+
additionalContext: 'Bizar terminal task update: consume this original structured <result> exactly once. Mark the agent terminal and merge any queued worktree. Do not send this notification back to the agent, do not request a follow-up acknowledgement, and do not replace its result with a later idle/status reply.',
|
|
87
87
|
},
|
|
88
88
|
}) + '\n');
|
|
89
89
|
return;
|
|
@@ -26,7 +26,7 @@ const routeModel = (risk) => {
|
|
|
26
26
|
const selected = candidate.trim()
|
|
27
27
|
return selected
|
|
28
28
|
}
|
|
29
|
-
const routeAgentType = (
|
|
29
|
+
const routeAgentType = (_risk, role = 'todd') => ({ 'research-analyst': 'greg', planner: 'paul', architect: 'paul', implementer: 'todd', 'qa-reviewer': 'linda', reviewer: 'linda', adversarial: 'linda', security: 'linda', qa: 'linda' }[role] || role)
|
|
30
30
|
if (!routeModel('medium') || !routeModel('high') || !routeAgentType('medium') || !routeAgentType('high')) {
|
|
31
31
|
return {
|
|
32
32
|
status: 'blocked',
|
|
@@ -25,7 +25,7 @@ const routeModel = (risk) => {
|
|
|
25
25
|
const selected = candidate.trim()
|
|
26
26
|
return selected
|
|
27
27
|
}
|
|
28
|
-
const routeAgentType = (
|
|
28
|
+
const routeAgentType = (_risk, role = 'todd') => ({ 'research-analyst': 'greg', planner: 'paul', architect: 'paul', implementer: 'todd', 'qa-reviewer': 'linda', reviewer: 'linda', adversarial: 'linda', security: 'linda', qa: 'linda' }[role] || role)
|
|
29
29
|
if (!routeModel('medium') || !routeModel('high') || !routeAgentType('medium') || !routeAgentType('high')) {
|
|
30
30
|
return {
|
|
31
31
|
status: 'blocked',
|
|
@@ -26,7 +26,7 @@ const routeModel = (risk) => {
|
|
|
26
26
|
const selected = candidate.trim()
|
|
27
27
|
return selected
|
|
28
28
|
}
|
|
29
|
-
const routeAgentType = (
|
|
29
|
+
const routeAgentType = (_risk, role = 'todd') => ({ 'research-analyst': 'greg', planner: 'paul', architect: 'paul', implementer: 'todd', 'qa-reviewer': 'linda', reviewer: 'linda', adversarial: 'linda', security: 'linda', qa: 'linda' }[role] || role)
|
|
30
30
|
if (!routeModel('medium') || !routeModel('high') || !routeAgentType('medium') || !routeAgentType('high')) {
|
|
31
31
|
return {
|
|
32
32
|
status: 'blocked',
|
|
@@ -33,15 +33,14 @@ 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
36
|
const ROLE_TO_BIZAR_AGENT = Object.freeze({
|
|
38
|
-
'research-analyst': 'greg', planner: 'paul', implementer: 'todd',
|
|
39
|
-
'qa-reviewer': 'linda', reviewer: 'linda', 'debug-specialist': 'carl',
|
|
37
|
+
'research-analyst': 'greg', planner: 'paul', architect: 'paul', implementer: 'todd',
|
|
38
|
+
'qa-reviewer': 'linda', reviewer: 'linda', adversarial: 'linda', security: 'linda', qa: 'linda', 'debug-specialist': 'carl',
|
|
40
39
|
'principal-engineer': 'karen', 'ui-designer': 'ria', 'it-lead': 'steve',
|
|
41
40
|
'knowledge-manager': 'oscar', 'support-tech': 'kevin', 'exec-assistant': 'pam',
|
|
42
41
|
'office-coordinator': 'brenda', 'office-greeter': 'janet', 'brand-designer': 'brad',
|
|
43
42
|
});
|
|
44
|
-
function
|
|
43
|
+
function stableAgentName(role = 'todd') { return ROLE_TO_BIZAR_AGENT[role] || (Object.values(ROLE_TO_BIZAR_AGENT).includes(role) ? role : 'todd'); }
|
|
45
44
|
|
|
46
45
|
const { O_APPEND, O_CREAT, O_WRONLY } = fsConstants;
|
|
47
46
|
|
|
@@ -806,12 +805,14 @@ export function classifyDispatchOutcome(result, error, startMs) {
|
|
|
806
805
|
}
|
|
807
806
|
|
|
808
807
|
/**
|
|
809
|
-
* Build an Agent payload that selects a
|
|
808
|
+
* Build an Agent payload that selects a stable Bizar role definition. Its
|
|
809
|
+
* frontmatter is refreshed by `bizar models` with the default selected full
|
|
810
|
+
* gateway ID, avoiding the alias-only native `model` transport.
|
|
810
811
|
*/
|
|
811
812
|
export function augmentPayload(opts, decision, agentName, context = {}) {
|
|
812
813
|
return {
|
|
813
814
|
...opts,
|
|
814
|
-
subagent_type:
|
|
815
|
+
subagent_type: stableAgentName(opts.role),
|
|
815
816
|
additionalContext: {
|
|
816
817
|
...(opts.additionalContext && typeof opts.additionalContext === 'object' ? opts.additionalContext : {}),
|
|
817
818
|
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 = (
|
|
27
|
+
const routeAgentType = (_risk, role = 'todd') => ({ 'research-analyst': 'greg', planner: 'paul', architect: 'paul', implementer: 'todd', 'qa-reviewer': 'linda', reviewer: 'linda', adversarial: 'linda', security: 'linda', qa: 'linda' }[role] || role)
|
|
28
28
|
if (!routeModel('medium') || !routeModel('high') || !routeAgentType('medium') || !routeAgentType('high')) {
|
|
29
29
|
return {
|
|
30
30
|
status: 'blocked',
|
|
@@ -23,7 +23,7 @@ const routeModel = (risk) => {
|
|
|
23
23
|
const selected = candidate.trim()
|
|
24
24
|
return selected
|
|
25
25
|
}
|
|
26
|
-
const routeAgentType = (
|
|
26
|
+
const routeAgentType = (_risk, role = 'todd') => ({ 'research-analyst': 'greg', planner: 'paul', architect: 'paul', implementer: 'todd', 'qa-reviewer': 'linda', reviewer: 'linda', adversarial: 'linda', security: 'linda', qa: 'linda' }[role] || role)
|
|
27
27
|
if (!routeModel('medium') || !routeModel('high') || !routeAgentType('medium') || !routeAgentType('high')) {
|
|
28
28
|
return {
|
|
29
29
|
status: 'blocked',
|
|
@@ -25,7 +25,7 @@ const routeModel = (risk) => {
|
|
|
25
25
|
const selected = candidate.trim()
|
|
26
26
|
return selected
|
|
27
27
|
}
|
|
28
|
-
const routeAgentType = (
|
|
28
|
+
const routeAgentType = (_risk, role = 'todd') => ({ 'research-analyst': 'greg', planner: 'paul', architect: 'paul', implementer: 'todd', 'qa-reviewer': 'linda', reviewer: 'linda', adversarial: 'linda', security: 'linda', qa: 'linda' }[role] || role)
|
|
29
29
|
if (!routeModel('medium') || !routeModel('high') || !routeAgentType('medium') || !routeAgentType('high')) {
|
|
30
30
|
return {
|
|
31
31
|
status: 'blocked',
|
package/package.json
CHANGED