bullswarm 0.23.0 → 0.23.2
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/README.md +11 -0
- package/connectors/command-code.json +5 -3
- package/data/openrouter-benchmarks.json +32 -4
- package/package.json +1 -1
- package/src/workflow/action-validator.js +15 -0
- package/src/workflow/v2-dispatch.js +2 -2
- package/src/workflow/v2-planner.js +3 -0
- package/src/workflow/v2-state.js +4 -0
package/README.md
CHANGED
|
@@ -256,6 +256,17 @@ updates the requirement ledger from schema-valid evidence. Only real
|
|
|
256
256
|
consolidated gaps re-enter the planner. There are no formal reviewer or repair
|
|
257
257
|
roles and no automatic semantic repair/reverify loop.
|
|
258
258
|
|
|
259
|
+
Lane and effort are separate decisions for every proposed action. `analyze` is
|
|
260
|
+
read-only investigation, judgment, or evidence; `build` is contextual product,
|
|
261
|
+
test, or documentation mutation; `chore` is deterministic mechanical mutation.
|
|
262
|
+
The kernel rejects evidence outside `analyze`, file ownership inside `analyze`,
|
|
263
|
+
and any `chore` above low effort. Low is for fixed-procedure checks and edits,
|
|
264
|
+
medium is the default for ordinary bounded work, and high is reserved for
|
|
265
|
+
architecture, ambiguous tradeoffs, cross-cutting integration, or genuinely
|
|
266
|
+
adversarial acceptance judgment. Merely being an analysis/evidence action or
|
|
267
|
+
part of a difficult goal never promotes an action to high. The selected effort
|
|
268
|
+
then resolves through the High/Medium/Low routes configured by `bullswarm setup`.
|
|
269
|
+
|
|
259
270
|
The planner does not author phases or declare success/failure. The kernel
|
|
260
271
|
derives stable presentation stages for the TUI and computes the final V2
|
|
261
272
|
result. Old autonomous run directories are not migrated or resumed;
|
|
@@ -46,13 +46,16 @@
|
|
|
46
46
|
},
|
|
47
47
|
"costRank": 1,
|
|
48
48
|
"lanes": [
|
|
49
|
+
"analyze",
|
|
49
50
|
"build",
|
|
50
51
|
"chore"
|
|
51
52
|
],
|
|
52
|
-
"capabilities": ["code-reading", "file-editing"],
|
|
53
|
-
"modelDiscovery": { "cmd": ["command-code", "--list-models"], "parse": "columns", "ignorePattern": "^(Available|Open Source
|
|
53
|
+
"capabilities": ["code-reading", "file-editing", "strong-analysis", "workflow-planning"],
|
|
54
|
+
"modelDiscovery": { "cmd": ["command-code", "--list-models"], "parse": "columns", "ignorePattern": "^(Available|Open Source$|Anthropic$|OpenAI$|Google$|Sakana$|Meta$|xAI$|Pass|cmd|Docs)", "timeoutMs": 20000, "maxModels": 150 },
|
|
54
55
|
"modelSelection": { "flag": "--model", "mode": "replace-or-append" },
|
|
55
56
|
"modelProfiles": [
|
|
57
|
+
{ "match": "^meta/muse-spark-1\\.3-contributor$", "tier": "high", "qualityRank": 5, "pricing": { "inputUsdPerMillion": 0.1, "cacheReadUsdPerMillion": 0.002, "outputUsdPerMillion": 0.2 }, "pricingSource": "https://commandcode.ai/docs/resources/pricing-limits", "pricingUpdatedAt": "2026-09-03" },
|
|
58
|
+
{ "match": "^meta/muse-spark-", "tier": "high", "qualityRank": 5, "pricing": { "inputUsdPerMillion": 1.25, "cacheReadUsdPerMillion": 0.15, "outputUsdPerMillion": 4.25 }, "pricingSource": "https://commandcode.ai/docs/resources/pricing-limits", "pricingUpdatedAt": "2026-09-03" },
|
|
56
59
|
{ "match": "^gpt-5\\.6-sol$", "tier": "high", "qualityRank": 6, "pricing": { "inputUsdPerMillion": 4, "cacheReadUsdPerMillion": 0.4, "outputUsdPerMillion": 20 }, "pricingSource": "https://help.openai.com/en/articles/20001415-chatgpt-rate-card-enterprise-token-based-pricing", "pricingUpdatedAt": "2026-08-27" },
|
|
57
60
|
{ "match": "^gpt-5\\.6-terra$", "tier": "medium", "qualityRank": 4, "pricing": { "inputUsdPerMillion": 2, "cacheReadUsdPerMillion": 0.2, "outputUsdPerMillion": 12 }, "pricingSource": "https://developers.openai.com/api/docs/models/gpt-5.6-terra", "pricingUpdatedAt": "2026-08-27" },
|
|
58
61
|
{ "match": "^gpt-5\\.6-luna$", "tier": "low", "qualityRank": 3, "pricing": { "inputUsdPerMillion": 0.2, "cacheReadUsdPerMillion": 0.02, "outputUsdPerMillion": 1.2 }, "pricingSource": "https://developers.openai.com/api/docs/models", "pricingUpdatedAt": "2026-08-27" },
|
|
@@ -65,7 +68,6 @@
|
|
|
65
68
|
{ "match": "(?:fable|opus|sol|grok-4\\.6|minimax-m3)", "tier": "high", "qualityRank": 5 }
|
|
66
69
|
],
|
|
67
70
|
"subscription": { "plan": null, "monthlyPriceUsd": null, "includedValueUsd": null, "quotaWindow": "monthly" },
|
|
68
|
-
"$comment-lanes": "cannot serve analyze (upstream 403 on the frontier model)",
|
|
69
71
|
"flags": {
|
|
70
72
|
"stealth": false
|
|
71
73
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "bullswarm.openrouter.benchmarks.v1",
|
|
3
|
-
"capturedAt": "2026-09-
|
|
3
|
+
"capturedAt": "2026-09-02T07:52:24.004Z",
|
|
4
4
|
"upstream": {
|
|
5
5
|
"benchmarks": "https://openrouter.ai/api/v1/benchmarks",
|
|
6
6
|
"models": "https://openrouter.ai/api/v1/models",
|
|
@@ -170,6 +170,20 @@
|
|
|
170
170
|
},
|
|
171
171
|
"pricingSource": "https://openrouter.ai/api/v1/models"
|
|
172
172
|
},
|
|
173
|
+
"~z-ai/glm-flash-latest": {
|
|
174
|
+
"id": "~z-ai/glm-flash-latest",
|
|
175
|
+
"name": "Z.ai: GLM Flash Latest",
|
|
176
|
+
"created": 1787817633,
|
|
177
|
+
"indices": {},
|
|
178
|
+
"ranks": {},
|
|
179
|
+
"pricing": {
|
|
180
|
+
"inputUsdPerMillion": 0.075,
|
|
181
|
+
"cacheReadUsdPerMillion": 0.015,
|
|
182
|
+
"cacheWriteUsdPerMillion": null,
|
|
183
|
+
"outputUsdPerMillion": 0.25
|
|
184
|
+
},
|
|
185
|
+
"pricingSource": "https://openrouter.ai/api/v1/models"
|
|
186
|
+
},
|
|
173
187
|
"~z-ai/glm-latest": {
|
|
174
188
|
"id": "~z-ai/glm-latest",
|
|
175
189
|
"name": "Z.ai: GLM Latest",
|
|
@@ -744,6 +758,20 @@
|
|
|
744
758
|
},
|
|
745
759
|
"pricingSource": "https://openrouter.ai/api/v1/models"
|
|
746
760
|
},
|
|
761
|
+
"anthropic/claude-fable-5.1:batch": {
|
|
762
|
+
"id": "anthropic/claude-fable-5.1:batch",
|
|
763
|
+
"name": "Anthropic: Claude Fable 5.1 (batch)",
|
|
764
|
+
"created": 1788285838,
|
|
765
|
+
"indices": {},
|
|
766
|
+
"ranks": {},
|
|
767
|
+
"pricing": {
|
|
768
|
+
"inputUsdPerMillion": 5,
|
|
769
|
+
"cacheReadUsdPerMillion": 0.125,
|
|
770
|
+
"cacheWriteUsdPerMillion": 6.25,
|
|
771
|
+
"outputUsdPerMillion": 25
|
|
772
|
+
},
|
|
773
|
+
"pricingSource": "https://openrouter.ai/api/v1/models"
|
|
774
|
+
},
|
|
747
775
|
"anthropic/claude-haiku-4.5": {
|
|
748
776
|
"id": "anthropic/claude-haiku-4.5",
|
|
749
777
|
"name": "Anthropic: Claude Haiku 4.5",
|
|
@@ -1603,10 +1631,10 @@
|
|
|
1603
1631
|
"indices": {},
|
|
1604
1632
|
"ranks": {},
|
|
1605
1633
|
"pricing": {
|
|
1606
|
-
"inputUsdPerMillion": 0.
|
|
1607
|
-
"cacheReadUsdPerMillion": 0.
|
|
1634
|
+
"inputUsdPerMillion": 0.44,
|
|
1635
|
+
"cacheReadUsdPerMillion": 0.014,
|
|
1608
1636
|
"cacheWriteUsdPerMillion": null,
|
|
1609
|
-
"outputUsdPerMillion":
|
|
1637
|
+
"outputUsdPerMillion": 1.32
|
|
1610
1638
|
},
|
|
1611
1639
|
"pricingSource": "https://openrouter.ai/api/v1/models"
|
|
1612
1640
|
},
|
package/package.json
CHANGED
|
@@ -5,6 +5,11 @@ export const ACTION_PROGRAM_SCHEMA_VERSION = 'bullswarm.workflow.program.v2';
|
|
|
5
5
|
const ID_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
6
6
|
const LANES = new Set(['analyze', 'build', 'chore']);
|
|
7
7
|
const EFFORTS = new Set(['high', 'medium', 'low']);
|
|
8
|
+
export const DEFAULT_EFFORT_BY_LANE = Object.freeze({
|
|
9
|
+
analyze: 'medium',
|
|
10
|
+
build: 'medium',
|
|
11
|
+
chore: 'low',
|
|
12
|
+
});
|
|
8
13
|
const PROGRAM_FIELDS = new Set(['schemaVersion', 'actions']);
|
|
9
14
|
const ACTION_FIELDS = new Set([
|
|
10
15
|
'id', 'purpose', 'dependsOn', 'affects', 'ownedFiles', 'prompt',
|
|
@@ -241,6 +246,7 @@ function maxParallelism(actions, byId) {
|
|
|
241
246
|
*/
|
|
242
247
|
export function validateActionProgram(program, runtime = {}) {
|
|
243
248
|
const issues = [];
|
|
249
|
+
const enforceRoutingPolicy = runtime.enforceRoutingPolicy !== false;
|
|
244
250
|
if (!isObject(program)) throw new ActionValidationError(['program must be an object']);
|
|
245
251
|
for (const key of Object.keys(program)) if (!PROGRAM_FIELDS.has(key)) issues.push(`program.${key} is not allowed`);
|
|
246
252
|
if (program.schemaVersion !== ACTION_PROGRAM_SCHEMA_VERSION) {
|
|
@@ -284,6 +290,15 @@ export function validateActionProgram(program, runtime = {}) {
|
|
|
284
290
|
const ownedFiles = normalizeOwnedFiles(action.ownedFiles, `${at}.ownedFiles`, issues);
|
|
285
291
|
if (!LANES.has(action.lane)) issues.push(`${at}.lane must be analyze|build|chore`);
|
|
286
292
|
if (!EFFORTS.has(action.effort)) issues.push(`${at}.effort must be high|medium|low`);
|
|
293
|
+
if (enforceRoutingPolicy && evidenceFor.length && action.lane !== 'analyze') {
|
|
294
|
+
issues.push(`${at} evidence actions must use lane analyze`);
|
|
295
|
+
}
|
|
296
|
+
if (enforceRoutingPolicy && ownedFiles.length && action.lane === 'analyze') {
|
|
297
|
+
issues.push(`${at} analyze actions must not own workspace files; use build or chore for mutations`);
|
|
298
|
+
}
|
|
299
|
+
if (enforceRoutingPolicy && action.lane === 'chore' && action.effort !== 'low') {
|
|
300
|
+
issues.push(`${at} chore actions are deterministic mechanical work and must use low effort`);
|
|
301
|
+
}
|
|
287
302
|
if (evidenceFor.length && (affects.length || ownedFiles.length)) {
|
|
288
303
|
issues.push(`${at} evidence actions must have empty affects and ownedFiles`);
|
|
289
304
|
}
|
|
@@ -4,8 +4,8 @@ import { pickPool, isQuarantined } from '../lib/route.js';
|
|
|
4
4
|
import { assertDepthAllowed, childDepthEnv, loadState, quarantinePool, saveState } from '../lib/state.js';
|
|
5
5
|
import { disabledModelsForPool, resolveDispatchModel, selectedModelsForTier } from '../lib/strategy.js';
|
|
6
6
|
import { watchOnce } from '../lib/watch.js';
|
|
7
|
+
import { DEFAULT_EFFORT_BY_LANE } from './action-validator.js';
|
|
7
8
|
|
|
8
|
-
const EFFORT_BY_LANE = Object.freeze({ analyze: 'high', build: 'medium', chore: 'low' });
|
|
9
9
|
const MECHANICAL_KINDS = new Set(['auth', 'provider', 'process', 'interrupted', 'schema']);
|
|
10
10
|
|
|
11
11
|
function clone(value) {
|
|
@@ -154,7 +154,7 @@ export async function dispatchV2Action({
|
|
|
154
154
|
const saveCoreState = dependencies.saveState ?? saveState;
|
|
155
155
|
const now = dependencies.now ?? Date.now;
|
|
156
156
|
const uuid = dependencies.uuid ?? randomUUID;
|
|
157
|
-
const effort = action.effort ??
|
|
157
|
+
const effort = action.effort ?? DEFAULT_EFFORT_BY_LANE[action.lane] ?? 'medium';
|
|
158
158
|
let candidates = preparePools(pools, action, effort, {
|
|
159
159
|
avoidPools, preferredModel, strictPool, now: now(),
|
|
160
160
|
});
|
|
@@ -170,6 +170,9 @@ export function buildV2PlannerPrompt(context) {
|
|
|
170
170
|
'The numeric values in context.targets are advisory planning targets, never execution ceilings. Prefer to stay within them by consolidating optional work, but exceed them whenever the smallest essential program needs more actions, agent dispatches, or gap rounds. Reaching or crossing a target is not a reason to return exhausted.',
|
|
171
171
|
'context.execution.concurrency limits only how many dependency-ready actions run at once. It does not limit the total number of independent actions in the program; the scheduler will batch wider programs safely.',
|
|
172
172
|
'Use only generic actions. A work action declares affects and any exact ownedFiles. affects means the action directly owns and delivers a bounded acceptance slice of that requirement; merely editing a supporting test or sharing a file does not make an action affect every requirement associated with that file. An evidence action declares evidenceFor, has empty affects/ownedFiles, and independently inspects the work it judges.',
|
|
173
|
+
'Choose lane from the action itself, not from the overall goal: analyze is read-only investigation, judgment, or evidence; build changes behavior, documentation, or tests and requires contextual implementation; chore is only deterministic mechanical mutation with no design choice. Evidence actions must use analyze. Analyze actions cannot own files. Chore actions must use low effort.',
|
|
174
|
+
'Choose effort independently from lane, using the cheapest tier sufficient for this one action. Low is for fixed-procedure checks or mechanical edits whose success is objectively decidable. Medium is the default for normal bounded analysis or implementation with local decisions. High is exceptional: use it only when architecture, ambiguous tradeoffs, cross-cutting integration, or adversarial acceptance judgment materially determines correctness. If uncertain, choose medium.',
|
|
175
|
+
'Do not choose high merely because an action uses analyze, supplies evidence, affects an important requirement, mentions many files, or belongs to a difficult overall goal. Do not choose low merely because an action is short. Examples: exact file comparison or formatting update = low; ordinary scoped feature plus focused test = medium; choosing an architecture across subsystems = high; running deterministic acceptance commands = low; interpreting ambiguous cross-cutting acceptance evidence = high.',
|
|
173
176
|
'Dependencies represent required data or exact-file ordering only. Do not serialize unrelated work. Do not add reviewer, verify, repair, phase, completion, pool, model, timeout, or retry fields.',
|
|
174
177
|
'Every mandatory unresolved requirement needs an evidence action. Parallel actions must be both file-disjoint and acceptance-independent. Isolated parallel siblings cannot see each other\'s unintegrated changes. If one action writes tests for behavior introduced by another action, combine code and tests under one owner or make the test action depend on and consume an artifact from the implementation action; never run new behavioral tests against the unchanged baseline in parallel. Prompts must be self-contained and include exact scope plus acceptance evidence.',
|
|
175
178
|
'For mutating behavioral work, keep implementation and its focused regression test under one coherent owner. The action must prove the regression on the untouched baseline, then exercise the real production entry point or state transition after the change; disconnected helpers, no-op assertions, and test-only behavior do not satisfy acceptance.',
|
package/src/workflow/v2-state.js
CHANGED
|
@@ -380,6 +380,10 @@ function validateProgram(program, state) {
|
|
|
380
380
|
maxParallel: state.config.settings.concurrency ?? state.config.settings.maxParallel ?? 100,
|
|
381
381
|
enforceMaxActions: false,
|
|
382
382
|
enforceMaxParallel: false,
|
|
383
|
+
// Durable runs predate the current routing policy. Replay their exact
|
|
384
|
+
// accepted program; strict lane/effort policy applies to new planner
|
|
385
|
+
// candidates before dispatch, not historical display or resume.
|
|
386
|
+
enforceRoutingPolicy: false,
|
|
383
387
|
},
|
|
384
388
|
);
|
|
385
389
|
for (const action of actions) {
|