create-byan-agent 2.45.0 → 2.47.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.
@@ -0,0 +1,149 @@
1
+ // F1 — the dispatch BRAIN. Single source of truth for the intelligent dispatch:
2
+ // given a task's NATURE and COMPLEXITY, decide which RUNTIME runs it (Codex or
3
+ // Claude), which MODEL, and (Codex only) which reasoning EFFORT.
4
+ //
5
+ // It composes with, does not duplicate, native-tiers.js: the Claude-side model
6
+ // tier is delegated to that module's TIER_MODEL vocabulary (which, by design,
7
+ // only ever yields haiku / sonnet / null — so Fable can never leak in from the
8
+ // Claude side either). This module adds the two NEW axes the intelligent dispatch
9
+ // needs — runtime selection and Codex reasoning effort — on top of it.
10
+ //
11
+ // Routing table is the cross-checked result (5 independent sources, see
12
+ // docs / CHANGELOG): Codex wins autonomous execution, shell/CI/DevOps, deploy,
13
+ // browser/computer use ; Claude wins architecture, refactor-at-repo-scale,
14
+ // quality, planning, and ALL verification. Two hard red lines are enforced here,
15
+ // not left to the caller:
16
+ // 1. Fable is NEVER emitted (long-term product decision).
17
+ // 2. Verification is NEVER routed to Codex (a runtime must not grade its own
18
+ // work ; the reviewer stays on Claude).
19
+ //
20
+ // Pure: no I/O, no clock, deterministic. The Codex transport (F2) and the
21
+ // orchestrating loop (F4) consume this; they never re-decide routing.
22
+
23
+ import { TIER_MODEL } from './native-tiers.js';
24
+
25
+ export const RUNTIMES = Object.freeze({ CODEX: 'codex', CLAUDE: 'claude' });
26
+
27
+ // Codex runs on the ChatGPT-subscription entitled model. Kept as a named constant
28
+ // so a plan change is a one-line edit, not a scatter of string literals.
29
+ export const CODEX_MODEL = 'gpt-5.4';
30
+
31
+ export const EFFORTS = Object.freeze({ LOW: 'low', MEDIUM: 'medium', HIGH: 'high' });
32
+
33
+ // Models we refuse to emit, ever. Fable is excluded by explicit long-term
34
+ // decision; the guard makes that refusal mechanical rather than a convention.
35
+ export const FORBIDDEN_MODELS = Object.freeze(['fable', 'claude-fable-5']);
36
+
37
+ // Task natures that route to Codex. Everything NOT here (and not a verification
38
+ // nature) falls through to Claude — Claude is the safe default, so an unknown or
39
+ // ambiguous nature keeps judgment on Claude rather than gambling it on Codex.
40
+ const CODEX_NATURES = Object.freeze([
41
+ 'execution', 'exec', 'shell', 'terminal', 'command',
42
+ 'deploy', 'deployment', 'devops', 'ci', 'cd', 'pipeline',
43
+ 'scripting', 'script', 'automation', 'browser', 'computer-use', 'e2e-run',
44
+ ]);
45
+
46
+ // Natures that MUST stay on Claude even if a future edit mislabels them — the
47
+ // verification red line. Checked before the Codex table so it can never be
48
+ // overridden by a nature that also looks like execution (e.g. "run-and-verify").
49
+ const VERIFICATION_NATURES = Object.freeze([
50
+ 'verification', 'verify', 'validate', 'review', 'audit', 'check', 'qa',
51
+ ]);
52
+
53
+ function normalize(value) {
54
+ return String(value == null ? '' : value).trim().toLowerCase();
55
+ }
56
+
57
+ function matchesAny(text, list) {
58
+ return list.some((kw) => text.includes(kw));
59
+ }
60
+
61
+ // assertNoFable(model) — mechanical enforcement of red line #1. Throws rather
62
+ // than silently substituting, so a Fable request is a loud failure at the source.
63
+ export function assertNoFable(model) {
64
+ const m = normalize(model);
65
+ if (FORBIDDEN_MODELS.some((f) => m.includes(f))) {
66
+ throw new Error(`dispatch-router: forbidden model "${model}" (Fable is excluded by long-term policy)`);
67
+ }
68
+ return model;
69
+ }
70
+
71
+ // isVerification(nature) — red line #2 helper. A verification nature never leaves
72
+ // Claude.
73
+ export function isVerification(nature) {
74
+ return matchesAny(normalize(nature), VERIFICATION_NATURES);
75
+ }
76
+
77
+ // routeRuntime(nature) -> 'codex' | 'claude'. Verification wins first (stays
78
+ // Claude), then the Codex table, else Claude (safe default).
79
+ export function routeRuntime(nature) {
80
+ const n = normalize(nature);
81
+ if (isVerification(n)) return RUNTIMES.CLAUDE;
82
+ if (matchesAny(n, CODEX_NATURES)) return RUNTIMES.CODEX;
83
+ return RUNTIMES.CLAUDE;
84
+ }
85
+
86
+ // Map a complexity input to a coarse bucket. Accepts a 0-100 number (the
87
+ // complexity-scorer scale) OR a label (trivial/low/medium/high/hard). Anything
88
+ // unrecognised is treated as medium — a safe middle that neither over-spends nor
89
+ // under-powers.
90
+ export function complexityBucket(complexity) {
91
+ if (typeof complexity === 'number' && Number.isFinite(complexity)) {
92
+ if (complexity < 34) return EFFORTS.LOW;
93
+ if (complexity < 67) return EFFORTS.MEDIUM;
94
+ return EFFORTS.HIGH;
95
+ }
96
+ const c = normalize(complexity);
97
+ if (['trivial', 'low', 'simple', 'easy'].includes(c)) return EFFORTS.LOW;
98
+ if (['high', 'hard', 'complex', 'frontier'].includes(c)) return EFFORTS.HIGH;
99
+ return EFFORTS.MEDIUM;
100
+ }
101
+
102
+ // effortForComplexity(complexity) -> a Codex reasoning-effort value. Codex is the
103
+ // only side with a real effort knob (`codex exec -c model_reasoning_effort=...`);
104
+ // this maps complexity onto it 1:1 with the bucket.
105
+ export function effortForComplexity(complexity) {
106
+ return complexityBucket(complexity);
107
+ }
108
+
109
+ // claudeModelForComplexity(complexity) -> 'haiku' | 'sonnet' | null(omit=inherit).
110
+ // Delegates to native-tiers' TIER_MODEL so the Claude vocabulary stays in one
111
+ // place: low -> cheap(haiku), medium -> balanced(sonnet), high -> deep(null =
112
+ // inherit the session model, i.e. Opus on an Opus session). Fable is structurally
113
+ // impossible here (TIER_MODEL has no Fable entry), but we still assert to make the
114
+ // guarantee explicit and catch a future TIER_MODEL drift.
115
+ export function claudeModelForComplexity(complexity) {
116
+ const bucket = complexityBucket(complexity);
117
+ const model = bucket === EFFORTS.LOW
118
+ ? TIER_MODEL.cheap
119
+ : bucket === EFFORTS.MEDIUM
120
+ ? TIER_MODEL.balanced
121
+ : TIER_MODEL.deep; // null = inherit (never pinned to Fable)
122
+ return model == null ? null : assertNoFable(model);
123
+ }
124
+
125
+ // dispatch({ nature, complexity }) -> the full routing decision:
126
+ // { runtime, model, effort, reasoning }
127
+ // - Codex: model = CODEX_MODEL, effort = complexity bucket (the real knob).
128
+ // - Claude: model = haiku/sonnet/null(inherit), effort = null (Claude has no
129
+ // effort knob — its "effort" IS the model tier).
130
+ // Both red lines are enforced here regardless of caller input.
131
+ export function dispatch({ nature, complexity } = {}) {
132
+ const runtime = routeRuntime(nature);
133
+ if (runtime === RUNTIMES.CODEX) {
134
+ return {
135
+ runtime,
136
+ model: assertNoFable(CODEX_MODEL),
137
+ effort: effortForComplexity(complexity),
138
+ reasoning: `nature "${normalize(nature)}" routes to Codex ; effort scaled to complexity`,
139
+ };
140
+ }
141
+ return {
142
+ runtime,
143
+ model: claudeModelForComplexity(complexity), // null = inherit session model
144
+ effort: null, // Claude effort = model tier, no separate knob
145
+ reasoning: isVerification(nature)
146
+ ? 'verification stays on Claude (red line: a runtime never grades its own work)'
147
+ : `nature "${normalize(nature)}" stays on Claude (default-safe) ; model scaled to complexity`,
148
+ };
149
+ }
@@ -94,7 +94,7 @@
94
94
  "name": "byan-byan",
95
95
  "module": "core",
96
96
  "tier": "connector-bound",
97
- "sourceHash": "6798beb68df9831e0b9bf3ca17100174316c51b2671d96b2ef5bff6def4eabc7"
97
+ "sourceHash": "523ca56a08c8b85edba423e1bfdd0d9c777442bcb266b5a74e65c14814da128d"
98
98
  },
99
99
  "byan-byan-v2": {
100
100
  "name": "byan-byan-v2",
@@ -0,0 +1,84 @@
1
+ # Intelligent dispatch — Codex/Claude routing + architect-dev loop
2
+
3
+ Route each task to the runtime that is genuinely better for it (Codex or Claude),
4
+ on the right model and effort for its complexity, and let an architect (Claude)
5
+ and a dev (Codex or Claude) exchange turn by turn until the work converges.
6
+
7
+ This is "option B": a deterministic orchestration loop (the durable, testable
8
+ backbone), with the Codex transport behind a swappable adapter so the tighter
9
+ `codex mcp-server` coupling can slot in later without touching the core.
10
+
11
+ ## The routing table (cross-checked, 5 independent sources)
12
+
13
+ | Task nature | Runtime | Why |
14
+ |-------------|---------|-----|
15
+ | execution / shell / terminal / deploy / devops / CI / scripting / browser / automation | Codex | Codex leads autonomous execution, terminal-task benchmarks, browser/computer use |
16
+ | architecture / design / planning / refactor / quality / analysis / exploration / anything unknown | Claude | Claude leads planning, repo-scale refactor, code quality ; unknown stays on Claude (safe default) |
17
+ | verification / review / validate / audit / qa | Claude (hard rule) | A runtime must not grade its own work |
18
+
19
+ The raw "who is better overall" score (SWE-bench Verified) is a near tie whose
20
+ leader flips by leaderboard, so it is left out of the routing signal — only the
21
+ task-type signal is used, which is stable across sources.
22
+
23
+ ## Model and effort by complexity
24
+
25
+ - **Claude side**: complexity picks the model tier via `native-tiers` —
26
+ low -> haiku, medium -> sonnet, high -> inherit the session model (Opus). There
27
+ is no separate effort knob on Claude: the model tier IS the effort. Fable is
28
+ excluded (the router does not emit it).
29
+ - **Codex side**: fixed model (`gpt-5.4`, the entitled subscription model) with a
30
+ real reasoning-effort knob — low / medium / high — scaled to complexity via
31
+ `codex exec -c model_reasoning_effort=...`.
32
+
33
+ ## The three red lines (enforced in code, not left to the caller)
34
+
35
+ 1. **No Fable.** `assertNoFable` throws rather than emit a Fable model.
36
+ 2. **Verification stays on Claude.** The router forces a verification nature to
37
+ Claude ; the orchestrator re-asserts it and throws if it ever regresses.
38
+ 3. **Codex does not write.** Codex runs read-only and returns a unified diff ;
39
+ Claude applies it with `git apply`. This works under a locked-down sandbox
40
+ (e.g. Landlock) and keeps the apply + later verification on Claude.
41
+
42
+ ## How the agents "talk" (the honest live-chat answer)
43
+
44
+ There is no simultaneous free-form chat between two models — that does not exist
45
+ natively. What delivers the same outcome is a turn-by-turn loop through a shared
46
+ board, like a chat server holding a conversation:
47
+
48
+ 1. The architect (Claude) posts the design to the board.
49
+ 2. The dev (Codex or Claude) reads the board, does the work, posts a result — or
50
+ a question.
51
+ 3. If there is an open question, the architect answers ; the loop continues.
52
+ 4. Convergence when the dev delivers a result with no open question (or a round
53
+ cap is hit). If the dev runtime fails (Codex unavailable/errored), the loop
54
+ reports `dev-failed` so the caller falls back to Claude.
55
+
56
+ ## Files
57
+
58
+ | File | Role |
59
+ |------|------|
60
+ | `lib/dispatch-router.js` | F1 — pure routing brain (nature+complexity -> runtime+model+effort) |
61
+ | `lib/codex-bridge.js` | F2 — Codex transport: `codex exec` -> diff, `git apply`, adapter seam (exec shipped, mcp a V2 slot) |
62
+ | `lib/dispatch-blackboard.js` | F3 — the turn-by-turn shared board (build/render pure, JSONL I/O isolated) |
63
+ | `lib/dispatch-orchestrator.js` | F4 — the loop core (routes, runs architect<->dev, converges) ; executors injected |
64
+ | `.claude/workflows/intelligent-dispatch.js` | Native launch facade for one routed task (design -> implement -> verify) |
65
+
66
+ All four libs are pure/injectable and unit-tested under `test/dispatch-*.test.js`
67
+ and `test/codex-bridge.test.js`.
68
+
69
+ ## Launching it
70
+
71
+ The orchestrator runs as full Node on the main thread (or an MCP worker), because
72
+ a native workflow script has no imports and no filesystem — it cannot use these
73
+ libs directly. So the main-thread FD / hermes skill decides routing with F1,
74
+ provides the executors (a Claude subagent for Claude turns, the Codex bridge for
75
+ Codex turns), and drives `orchestrate({ tasks, executors })`. The native workflow
76
+ script `intelligent-dispatch.js` is the thin facade for the single-task case: its
77
+ dev leaf holds the Bash tool and is what actually invokes `codex exec`.
78
+
79
+ ## V2 slot — tighter coupling
80
+
81
+ `getAdapter('codex-mcp')` is a declared but unavailable adapter. When
82
+ `codex mcp-server` is wired, Codex becomes a tool Claude can call mid-reasoning
83
+ (and Codex can call BYAN tools back) — the closest thing to live. It drops in as a
84
+ new transport behind the same F2 seam, with no change to F1 or the loop.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-byan-agent",
3
- "version": "2.45.0",
3
+ "version": "2.47.0",
4
4
  "description": "BYAN v2.8 - Intelligent AI agent creator with ELO trust system + scientific fact-check + Hermes universal dispatcher + native Claude Code integration (hooks, skills, MCP server). Multi-platform (Claude Code, Codex). Merise Agile + TDD + 71 Mantras. ~54% LLM cost savings.",
5
5
  "main": "src/index.js",
6
6
  "bin": {