@brutalist/mcp 1.13.0 → 1.14.1
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 +57 -31
- package/dist/brutalist-server.js +9 -9
- package/dist/brutalist-server.js.map +1 -1
- package/dist/cli-adapters/agy-adapter.d.ts +30 -0
- package/dist/cli-adapters/agy-adapter.d.ts.map +1 -0
- package/dist/cli-adapters/agy-adapter.js +280 -0
- package/dist/cli-adapters/agy-adapter.js.map +1 -0
- package/dist/cli-adapters/index.d.ts +8 -12
- package/dist/cli-adapters/index.d.ts.map +1 -1
- package/dist/cli-adapters/index.js +2 -2
- package/dist/cli-adapters/index.js.map +1 -1
- package/dist/cli-agents.d.ts +6 -29
- package/dist/cli-agents.d.ts.map +1 -1
- package/dist/cli-agents.js +30 -129
- package/dist/cli-agents.js.map +1 -1
- package/dist/debate/debate-orchestrator.d.ts +2 -4
- package/dist/debate/debate-orchestrator.d.ts.map +1 -1
- package/dist/debate/debate-orchestrator.js.map +1 -1
- package/dist/domains/argument-space.d.ts +8 -8
- package/dist/domains/argument-space.js +3 -3
- package/dist/domains/argument-space.js.map +1 -1
- package/dist/domains/critic-persona.d.ts +2 -1
- package/dist/domains/critic-persona.d.ts.map +1 -1
- package/dist/domains/critic-persona.js.map +1 -1
- package/dist/handlers/tool-handler.js.map +1 -1
- package/dist/mcp-registry.d.ts +0 -6
- package/dist/mcp-registry.d.ts.map +1 -1
- package/dist/mcp-registry.js +0 -35
- package/dist/mcp-registry.js.map +1 -1
- package/dist/metrics/registry.d.ts +2 -2
- package/dist/metrics/registry.js +2 -2
- package/dist/metrics/types.d.ts +1 -1
- package/dist/metrics/types.js +1 -1
- package/dist/model-resolver.d.ts +1 -13
- package/dist/model-resolver.d.ts.map +1 -1
- package/dist/model-resolver.js +12 -7
- package/dist/model-resolver.js.map +1 -1
- package/dist/streaming/output-parser.d.ts +1 -1
- package/dist/streaming/output-parser.d.ts.map +1 -1
- package/dist/streaming/output-parser.js +0 -17
- package/dist/streaming/output-parser.js.map +1 -1
- package/dist/streaming/streaming-orchestrator.js +1 -1
- package/dist/streaming/streaming-orchestrator.js.map +1 -1
- package/dist/types/brutalist.d.ts +3 -3
- package/dist/types/brutalist.d.ts.map +1 -1
- package/dist/types/tool-config.d.ts +4 -4
- package/dist/types/tool-config.d.ts.map +1 -1
- package/dist/types/tool-config.js +7 -6
- package/dist/types/tool-config.js.map +1 -1
- package/package.json +4 -3
- package/dist/cli-adapters/gemini-adapter.d.ts +0 -84
- package/dist/cli-adapters/gemini-adapter.d.ts.map +0 -1
- package/dist/cli-adapters/gemini-adapter.js +0 -305
- package/dist/cli-adapters/gemini-adapter.js.map +0 -1
|
@@ -8,17 +8,16 @@ import type { CLIAgentOptions } from '../cli-agents.js';
|
|
|
8
8
|
import type { ModelResolver } from '../model-resolver.js';
|
|
9
9
|
import type { StructuredLogger } from '../logger.js';
|
|
10
10
|
export { parseNDJSON } from './shared.js';
|
|
11
|
-
export type CLIName = 'claude' | 'codex' | '
|
|
11
|
+
export type CLIName = 'claude' | 'codex' | 'agy';
|
|
12
12
|
export interface MCPSupportConfig {
|
|
13
13
|
/** How this CLI receives MCP server configuration */
|
|
14
|
-
configMethod: 'flag-file' | 'config-override'
|
|
14
|
+
configMethod: 'flag-file' | 'config-override';
|
|
15
15
|
configFlag?: string;
|
|
16
16
|
strictFlag?: string;
|
|
17
17
|
configOverrideKey?: string;
|
|
18
|
-
whitelistFlag?: string;
|
|
19
18
|
/** Hard write-prevention mechanism native to this CLI */
|
|
20
19
|
writeProtection: {
|
|
21
|
-
method: 'disallowed-tools' | 'sandbox'
|
|
20
|
+
method: 'disallowed-tools' | 'sandbox';
|
|
22
21
|
flag: string;
|
|
23
22
|
value: string;
|
|
24
23
|
};
|
|
@@ -44,9 +43,8 @@ export interface CLIBuilderConfig {
|
|
|
44
43
|
* class (see Phase 1 fix in cli-agents.ts).
|
|
45
44
|
*
|
|
46
45
|
* Each provider adapter populates this from its own protocol-level
|
|
47
|
-
* signals (Claude: `result.subtype` / `is_error`; Codex: error events
|
|
48
|
-
*
|
|
49
|
-
* assistant prose to classify refusals.
|
|
46
|
+
* signals (Claude: `result.subtype` / `is_error`; Codex: error events).
|
|
47
|
+
* The orchestrator never inspects assistant prose to classify refusals.
|
|
50
48
|
*/
|
|
51
49
|
export type DecodeRefusalReason = 'quota' | 'auth' | 'policy';
|
|
52
50
|
export type DecodeErrorReason = 'malformed' | 'empty' | 'unknown';
|
|
@@ -84,16 +82,14 @@ export interface CLIProvider {
|
|
|
84
82
|
* Each adapter inspects ITS OWN protocol-level signals to classify the
|
|
85
83
|
* run — refusal markers must come from the CLI's structured error
|
|
86
84
|
* channel (stream-json `result` events for Claude, error items for
|
|
87
|
-
* Codex
|
|
88
|
-
* assistant text the CLI returned.
|
|
85
|
+
* Codex) and never from the assistant text the CLI returned.
|
|
89
86
|
*
|
|
90
87
|
* The orchestrator consumes `DecodeResult.kind` directly; no caller
|
|
91
88
|
* grep the prose for "rate limit"-style strings (Phase 1 hot-fix
|
|
92
89
|
* scoped that pattern set to stderr; Phase 2 removes it entirely).
|
|
93
90
|
*
|
|
94
|
-
* stderr is passed in alongside stdout because
|
|
95
|
-
*
|
|
96
|
-
* on stderr, not in the JSON event stream.
|
|
91
|
+
* stderr is passed in alongside stdout because Codex error envelopes
|
|
92
|
+
* surface refusal state on stderr, not in the JSON event stream.
|
|
97
93
|
*/
|
|
98
94
|
decode(stdout: string, stderr: string, args: string[], log?: StructuredLogger): DecodeResult;
|
|
99
95
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli-adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI1C,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli-adapters/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,11 +3,11 @@ export { parseNDJSON } from './shared.js';
|
|
|
3
3
|
// ── Provider Registry ──────────────────────────────────────────────────────
|
|
4
4
|
import { ClaudeAdapter } from './claude-adapter.js';
|
|
5
5
|
import { CodexAdapter } from './codex-adapter.js';
|
|
6
|
-
import {
|
|
6
|
+
import { AgyAdapter } from './agy-adapter.js';
|
|
7
7
|
const providers = {
|
|
8
8
|
claude: new ClaudeAdapter(),
|
|
9
9
|
codex: new CodexAdapter(),
|
|
10
|
-
|
|
10
|
+
agy: new AgyAdapter(),
|
|
11
11
|
};
|
|
12
12
|
/**
|
|
13
13
|
* Get a provider adapter by name.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli-adapters/index.ts"],"names":[],"mappings":"AAUA,6BAA6B;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli-adapters/index.ts"],"names":[],"mappings":"AAUA,6BAA6B;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAoI1C,8EAA8E;AAE9E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,SAAS,GAAiC;IAC9C,MAAM,EAAE,IAAI,aAAa,EAAE;IAC3B,KAAK,EAAE,IAAI,YAAY,EAAE;IACzB,GAAG,EAAE,IAAI,UAAU,EAAE;CACtB,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,IAAa;IACvC,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAc,CAAC;AAC7C,CAAC"}
|
package/dist/cli-agents.d.ts
CHANGED
|
@@ -7,12 +7,12 @@ export declare const CLAUDE_ALIASES: readonly ["opus", "sonnet", "haiku"];
|
|
|
7
7
|
export interface CLIAgentOptions {
|
|
8
8
|
workingDirectory?: string;
|
|
9
9
|
timeout?: number;
|
|
10
|
-
clis?: ('claude' | 'codex' | '
|
|
10
|
+
clis?: ('claude' | 'codex' | 'agy')[];
|
|
11
11
|
analysisType?: BrutalistPromptType;
|
|
12
12
|
models?: {
|
|
13
13
|
claude?: string;
|
|
14
14
|
codex?: string;
|
|
15
|
-
|
|
15
|
+
agy?: string;
|
|
16
16
|
};
|
|
17
17
|
onStreamingEvent?: (event: StreamingEvent) => void;
|
|
18
18
|
progressToken?: string | number;
|
|
@@ -45,14 +45,14 @@ export interface CLIAgentOrchestratorDeps {
|
|
|
45
45
|
}
|
|
46
46
|
export interface StreamingEvent {
|
|
47
47
|
type: 'agent_start' | 'agent_progress' | 'agent_complete' | 'agent_error';
|
|
48
|
-
agent: 'claude' | 'codex' | '
|
|
48
|
+
agent: 'claude' | 'codex' | 'agy' | 'system';
|
|
49
49
|
content?: string;
|
|
50
50
|
timestamp: number;
|
|
51
51
|
sessionId?: string;
|
|
52
52
|
metadata?: Record<string, any>;
|
|
53
53
|
}
|
|
54
54
|
export interface CLIContext {
|
|
55
|
-
availableCLIs: ('claude' | 'codex' | '
|
|
55
|
+
availableCLIs: ('claude' | 'codex' | 'agy')[];
|
|
56
56
|
}
|
|
57
57
|
export declare class CLIAgentOrchestrator {
|
|
58
58
|
private defaultTimeout;
|
|
@@ -98,37 +98,14 @@ export declare class CLIAgentOrchestrator {
|
|
|
98
98
|
private parseNDJSON;
|
|
99
99
|
private decodeClaudeStreamJson;
|
|
100
100
|
private extractCodexAgentMessage;
|
|
101
|
-
private extractGeminiResponse;
|
|
102
101
|
private emitThrottledStreamingEvent;
|
|
103
102
|
private buildCLICommand;
|
|
104
103
|
detectCLIContext(): Promise<CLIContext>;
|
|
105
|
-
selectSingleCLI(preferredCLI?: 'claude' | 'codex' | '
|
|
104
|
+
selectSingleCLI(preferredCLI?: 'claude' | 'codex' | 'agy', analysisType?: BrutalistPromptType): 'claude' | 'codex' | 'agy';
|
|
106
105
|
private _executeCLI;
|
|
107
106
|
executeClaudeCode(userPrompt: string, systemPromptSpec: string, options?: CLIAgentOptions): Promise<CLIAgentResponse>;
|
|
108
107
|
executeCodex(userPrompt: string, systemPromptSpec: string, options?: CLIAgentOptions): Promise<CLIAgentResponse>;
|
|
109
|
-
|
|
110
|
-
executeSingleCLI(cli: 'claude' | 'codex' | 'gemini', userPrompt: string, systemPromptSpec: string, options?: CLIAgentOptions): Promise<CLIAgentResponse>;
|
|
111
|
-
/**
|
|
112
|
-
* Gemini frontier rotation - iterate through GEMINI_FRONTIER_CHAIN on
|
|
113
|
-
* rotatable failures (capacity saturation OR tier access denial).
|
|
114
|
-
*
|
|
115
|
-
* Only active when neither caller nor operator has chosen a model. Each
|
|
116
|
-
* attempt injects the model via options.models.gemini. Per-attempt
|
|
117
|
-
* failures are classified by isGeminiRotatableError(): capacity errors
|
|
118
|
-
* (quota/429) AND access errors (ModelNotFoundError / permission denied)
|
|
119
|
-
* both trigger rotation. On unrelated failures (auth, prompt rejection,
|
|
120
|
-
* subprocess crashes) rotation stops immediately — a different model
|
|
121
|
-
* will not fix those. On chain exhaustion, the last failing response
|
|
122
|
-
* is returned.
|
|
123
|
-
*
|
|
124
|
-
* In practice the typical non-preview user trajectory is:
|
|
125
|
-
* gemini-3.1-pro-preview -> access denied (rotate)
|
|
126
|
-
* gemini-3-pro-preview -> access denied (rotate)
|
|
127
|
-
* gemini-3-flash-preview -> success (3-series flash, pro-grade
|
|
128
|
-
* reasoning, universally available as
|
|
129
|
-
* of the model launch)
|
|
130
|
-
*/
|
|
131
|
-
private _executeGeminiWithRotation;
|
|
108
|
+
executeSingleCLI(cli: 'claude' | 'codex' | 'agy', userPrompt: string, systemPromptSpec: string, options?: CLIAgentOptions): Promise<CLIAgentResponse>;
|
|
132
109
|
private waitForAvailableSlot;
|
|
133
110
|
executeCLIAgents(cliAgents: string[], systemPrompt: string, userPrompt: string, options?: CLIAgentOptions): Promise<CLIAgentResponse[]>;
|
|
134
111
|
executeCLIAgent(agent: string, systemPrompt: string, userPrompt: string, options?: CLIAgentOptions): Promise<CLIAgentResponse>;
|
package/dist/cli-agents.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-agents.d.ts","sourceRoot":"","sources":["../src/cli-agents.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAKpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli-agents.d.ts","sourceRoot":"","sources":["../src/cli-agents.ts"],"names":[],"mappings":"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"}
|
package/dist/cli-agents.js
CHANGED
|
@@ -5,35 +5,8 @@ import { logger } from './logger.js';
|
|
|
5
5
|
import { ModelResolver } from './model-resolver.js';
|
|
6
6
|
import { cleanupTempConfig } from './mcp-registry.js';
|
|
7
7
|
import { getProvider, parseNDJSON } from './cli-adapters/index.js';
|
|
8
|
-
import {
|
|
8
|
+
import { AGY_BINARY } from './cli-adapters/agy-adapter.js';
|
|
9
9
|
import { safeMetric } from './metrics/index.js';
|
|
10
|
-
/**
|
|
11
|
-
* Detect errors where rotating to the next Gemini frontier tier is likely
|
|
12
|
-
* to succeed. Covers two failure families:
|
|
13
|
-
*
|
|
14
|
-
* 1. Capacity saturation on the current tier
|
|
15
|
-
* (429 / "No capacity available" / quota / rate-limit).
|
|
16
|
-
*
|
|
17
|
-
* 2. Access denial on the current tier — the model exists but the
|
|
18
|
-
* user's account lacks preview-tier access. Appears as
|
|
19
|
-
* ModelNotFoundError / "Requested entity was not found" / 403 /
|
|
20
|
-
* "permission denied". In production the frontier chain is
|
|
21
|
-
* probe-tested (not user-typos), so these errors mean "this tier
|
|
22
|
-
* is unavailable to THIS caller" — which is exactly when rotation
|
|
23
|
-
* to the next tier should fire. Dropping from a pro preview down
|
|
24
|
-
* to `gemini-3-flash-preview` (the chain floor) trades pro-tier
|
|
25
|
-
* reasoning for flash-tier latency/cost while still keeping
|
|
26
|
-
* Pro-grade quality per Google's 3-Flash positioning.
|
|
27
|
-
*
|
|
28
|
-
* Does NOT match: auth failures (missing/invalid API key), prompt-safety
|
|
29
|
-
* rejections, or subprocess crashes — these will not differ between
|
|
30
|
-
* frontier tiers.
|
|
31
|
-
*/
|
|
32
|
-
function isGeminiRotatableError(error) {
|
|
33
|
-
if (!error)
|
|
34
|
-
return false;
|
|
35
|
-
return /no capacity available|\b429\b|overloaded|rateLimitExceeded|rate limit|quota|too many requests|ModelNotFoundError|Requested entity was not found|\b403\b|permission denied|access denied/i.test(error);
|
|
36
|
-
}
|
|
37
10
|
function sanitizeModelNameForMessage(model) {
|
|
38
11
|
if (!model)
|
|
39
12
|
return 'requested model';
|
|
@@ -276,7 +249,7 @@ async function spawnAsync(command, args, options = {}) {
|
|
|
276
249
|
}
|
|
277
250
|
// Use secure environment
|
|
278
251
|
const secureEnv = options.env || createSecureEnvironment();
|
|
279
|
-
// On Windows, npm-installed CLIs (
|
|
252
|
+
// On Windows, npm-installed CLIs (codex) are .cmd batch shims that
|
|
280
253
|
// require shell:true for spawn() to execute them. Native .exe CLIs (claude)
|
|
281
254
|
// work either way. On Unix, shell remains false to prevent injection.
|
|
282
255
|
//
|
|
@@ -463,7 +436,7 @@ async function spawnAsync(command, args, options = {}) {
|
|
|
463
436
|
if (command === 'claude') {
|
|
464
437
|
child.stdin?.end();
|
|
465
438
|
}
|
|
466
|
-
//
|
|
439
|
+
// Codex works fine with stdin left open
|
|
467
440
|
}
|
|
468
441
|
});
|
|
469
442
|
}
|
|
@@ -563,10 +536,6 @@ export class CLIAgentOrchestrator {
|
|
|
563
536
|
const provider = getProvider('codex');
|
|
564
537
|
return provider.decodeOutput(jsonOutput, ['--json']);
|
|
565
538
|
}
|
|
566
|
-
extractGeminiResponse(jsonOutput) {
|
|
567
|
-
const provider = getProvider('gemini');
|
|
568
|
-
return provider.decodeOutput(jsonOutput, ['--output-format', 'json']);
|
|
569
|
-
}
|
|
570
539
|
emitThrottledStreamingEvent(agent, type, content, onStreamingEvent, options) {
|
|
571
540
|
if (!onStreamingEvent)
|
|
572
541
|
return;
|
|
@@ -632,17 +601,21 @@ export class CLIAgentOrchestrator {
|
|
|
632
601
|
return this.cliContext;
|
|
633
602
|
}
|
|
634
603
|
const availableCLIs = [];
|
|
635
|
-
//
|
|
604
|
+
// Detection probes. For agy, AGY_BINARY (resolved in the agy adapter
|
|
605
|
+
// at module load) prefers ~/.local/bin/agy over PATH to avoid the
|
|
606
|
+
// macOS Antigravity-desktop-IDE wrapper that otherwise shadows the
|
|
607
|
+
// CLI agent. See the adapter's resolveAgyBin() for the full rationale.
|
|
636
608
|
const cliChecks = [
|
|
637
609
|
{ name: 'claude', command: 'claude --version' },
|
|
638
610
|
{ name: 'codex', command: 'codex --version' },
|
|
639
|
-
{ name: '
|
|
611
|
+
{ name: 'agy', command: `${AGY_BINARY} --version` }
|
|
640
612
|
];
|
|
641
613
|
// NOTE: These `--version` probes are NOT spawn attempts — they must not
|
|
642
614
|
// increment `cliSpawnTotal`. Only _executeCLI counts spawns.
|
|
643
615
|
const results = await Promise.allSettled(cliChecks.map(async (check) => {
|
|
616
|
+
const probeCmd = check.name === 'agy' ? AGY_BINARY : check.name;
|
|
644
617
|
try {
|
|
645
|
-
await spawnAsync(
|
|
618
|
+
await spawnAsync(probeCmd, ['--version'], { timeout: CLI_CHECK_TIMEOUT });
|
|
646
619
|
this.emitLog().debug(`CLI available: ${check.name}`);
|
|
647
620
|
return check.name;
|
|
648
621
|
}
|
|
@@ -666,18 +639,22 @@ export class CLIAgentOrchestrator {
|
|
|
666
639
|
this.emitLog().info(`✅ Using preferred CLI: ${preferredCLI}`);
|
|
667
640
|
return preferredCLI;
|
|
668
641
|
}
|
|
669
|
-
// 2. Smart selection based on analysis type
|
|
642
|
+
// 2. Smart selection based on analysis type. Agy is always LAST in
|
|
643
|
+
// priority order: it's 2-4× slower per call than claude/codex
|
|
644
|
+
// (30-60s vs 5-25s) and Flash-pinned, so it's only auto-selected
|
|
645
|
+
// when the others are unavailable. Callers who explicitly pass
|
|
646
|
+
// `preferredCLI: 'agy'` get it regardless (handled at step 1).
|
|
670
647
|
const selectionRules = {
|
|
671
|
-
'code': ['claude', 'codex', '
|
|
672
|
-
'architecture': ['
|
|
673
|
-
'research': ['claude', '
|
|
674
|
-
'security': ['codex', 'claude', '
|
|
675
|
-
'data': ['
|
|
676
|
-
'product': ['claude', '
|
|
677
|
-
'infrastructure': ['
|
|
678
|
-
'idea': ['claude', '
|
|
679
|
-
'debate': ['claude', '
|
|
680
|
-
'default': ['claude', '
|
|
648
|
+
'code': ['claude', 'codex', 'agy'],
|
|
649
|
+
'architecture': ['claude', 'codex', 'agy'],
|
|
650
|
+
'research': ['claude', 'codex', 'agy'],
|
|
651
|
+
'security': ['codex', 'claude', 'agy'],
|
|
652
|
+
'data': ['claude', 'codex', 'agy'],
|
|
653
|
+
'product': ['claude', 'codex', 'agy'],
|
|
654
|
+
'infrastructure': ['codex', 'claude', 'agy'],
|
|
655
|
+
'idea': ['claude', 'codex', 'agy'],
|
|
656
|
+
'debate': ['claude', 'codex', 'agy'],
|
|
657
|
+
'default': ['claude', 'codex', 'agy']
|
|
681
658
|
};
|
|
682
659
|
const priority = selectionRules[analysisType || 'default'] || selectionRules.default;
|
|
683
660
|
// 3. Select by priority from available CLIs
|
|
@@ -704,7 +681,7 @@ export class CLIAgentOrchestrator {
|
|
|
704
681
|
// resolving a model, which is the right semantics for the response.
|
|
705
682
|
let built;
|
|
706
683
|
// Provider label for the spawn counter. Derived from cliName so the
|
|
707
|
-
// label set stays in sync with the 'claude' | 'codex' | '
|
|
684
|
+
// label set stays in sync with the 'claude' | 'codex' | 'agy' union
|
|
708
685
|
// instead of reading adapter.name.
|
|
709
686
|
const provider = cliName;
|
|
710
687
|
// Gate for the catch-branch counter emission. Per compose.py:174,
|
|
@@ -801,8 +778,8 @@ export class CLIAgentOrchestrator {
|
|
|
801
778
|
// instead of grepping the returned string for refusal markers.
|
|
802
779
|
// Each adapter classifies refusal from its own protocol-level
|
|
803
780
|
// signals (Claude `result.subtype`, Codex error events / stderr
|
|
804
|
-
// markers
|
|
805
|
-
//
|
|
781
|
+
// markers). The orchestrator does not inspect assistant prose to
|
|
782
|
+
// classify outcomes.
|
|
806
783
|
let finalOutput = stdout;
|
|
807
784
|
const providerAdapter = getProvider(cliName);
|
|
808
785
|
const decodeLog = this.log?.forOperation(`${cliName}_spawn`);
|
|
@@ -1016,33 +993,12 @@ export class CLIAgentOrchestrator {
|
|
|
1016
993
|
async executeCodex(userPrompt, systemPromptSpec, options = {}) {
|
|
1017
994
|
return this._executeCLI('codex', userPrompt, systemPromptSpec, options, (user, sys, opts) => this.buildCLICommand('codex', user, sys, opts));
|
|
1018
995
|
}
|
|
1019
|
-
async executeGemini(userPrompt, systemPromptSpec, options = {}) {
|
|
1020
|
-
return this._executeCLI('gemini', userPrompt, systemPromptSpec, options, (user, sys, opts) => this.buildCLICommand('gemini', user, sys, opts));
|
|
1021
|
-
}
|
|
1022
996
|
async executeSingleCLI(cli, userPrompt, systemPromptSpec, options = {}) {
|
|
1023
997
|
// Wait for available slot to prevent resource exhaustion
|
|
1024
998
|
await this.waitForAvailableSlot();
|
|
1025
999
|
this.runningCLIs++;
|
|
1026
1000
|
this.emitLog().info(`\u{1F3AF} Executing ${cli} (${this.runningCLIs}/${this.MAX_CONCURRENT_CLIS} slots used)`);
|
|
1027
1001
|
try {
|
|
1028
|
-
// Gemini frontier rotation: when using the default frontier chain
|
|
1029
|
-
// (no caller-specified model, no env-var override), rotate through
|
|
1030
|
-
// GEMINI_FRONTIER_CHAIN on saturation OR access-denied failures.
|
|
1031
|
-
// The chain (see src/cli-adapters/gemini-adapter.ts) is two pro
|
|
1032
|
-
// previews followed by `gemini-3-flash-preview` as the floor. The
|
|
1033
|
-
// 2.5-pro fallback was removed because 3-flash ships with
|
|
1034
|
-
// pro-grade reasoning and is universally available; falling to it
|
|
1035
|
-
// beats dropping a generation back. Access-denied rotation is the
|
|
1036
|
-
// typical user path: pro preview tiers aren't granted to every
|
|
1037
|
-
// account, so the chain falls through to 3-flash. Rotation is
|
|
1038
|
-
// disabled when the caller or operator has explicitly chosen a
|
|
1039
|
-
// model (BRUTALIST_GEMINI_MODEL=... or models.gemini=...).
|
|
1040
|
-
const geminiRotationActive = cli === 'gemini'
|
|
1041
|
-
&& !options.models?.gemini
|
|
1042
|
-
&& !process.env.BRUTALIST_GEMINI_MODEL;
|
|
1043
|
-
if (geminiRotationActive) {
|
|
1044
|
-
return await this._executeGeminiWithRotation(userPrompt, systemPromptSpec, options);
|
|
1045
|
-
}
|
|
1046
1002
|
// Dispatch to adapter via buildCLICommand (which delegates to provider)
|
|
1047
1003
|
const response = await this._executeCLI(cli, userPrompt, systemPromptSpec, options, (user, sys, opts) => this.buildCLICommand(cli, user, sys, opts));
|
|
1048
1004
|
const requestedCodexModel = options.models?.codex;
|
|
@@ -1073,60 +1029,6 @@ export class CLIAgentOrchestrator {
|
|
|
1073
1029
|
this.emitLog().info(`\u2705 Released CLI slot (${this.runningCLIs}/${this.MAX_CONCURRENT_CLIS} slots used)`);
|
|
1074
1030
|
}
|
|
1075
1031
|
}
|
|
1076
|
-
/**
|
|
1077
|
-
* Gemini frontier rotation - iterate through GEMINI_FRONTIER_CHAIN on
|
|
1078
|
-
* rotatable failures (capacity saturation OR tier access denial).
|
|
1079
|
-
*
|
|
1080
|
-
* Only active when neither caller nor operator has chosen a model. Each
|
|
1081
|
-
* attempt injects the model via options.models.gemini. Per-attempt
|
|
1082
|
-
* failures are classified by isGeminiRotatableError(): capacity errors
|
|
1083
|
-
* (quota/429) AND access errors (ModelNotFoundError / permission denied)
|
|
1084
|
-
* both trigger rotation. On unrelated failures (auth, prompt rejection,
|
|
1085
|
-
* subprocess crashes) rotation stops immediately — a different model
|
|
1086
|
-
* will not fix those. On chain exhaustion, the last failing response
|
|
1087
|
-
* is returned.
|
|
1088
|
-
*
|
|
1089
|
-
* In practice the typical non-preview user trajectory is:
|
|
1090
|
-
* gemini-3.1-pro-preview -> access denied (rotate)
|
|
1091
|
-
* gemini-3-pro-preview -> access denied (rotate)
|
|
1092
|
-
* gemini-3-flash-preview -> success (3-series flash, pro-grade
|
|
1093
|
-
* reasoning, universally available as
|
|
1094
|
-
* of the model launch)
|
|
1095
|
-
*/
|
|
1096
|
-
async _executeGeminiWithRotation(userPrompt, systemPromptSpec, options) {
|
|
1097
|
-
const chain = GEMINI_FRONTIER_CHAIN;
|
|
1098
|
-
let lastResponse = null;
|
|
1099
|
-
for (let i = 0; i < chain.length; i++) {
|
|
1100
|
-
const model = chain[i];
|
|
1101
|
-
const attemptOptions = {
|
|
1102
|
-
...options,
|
|
1103
|
-
models: { ...(options.models || {}), gemini: model },
|
|
1104
|
-
};
|
|
1105
|
-
if (i > 0) {
|
|
1106
|
-
this.emitLog().info(`Gemini rotation: attempting tier ${i + 1}/${chain.length} (${model})`);
|
|
1107
|
-
}
|
|
1108
|
-
const response = await this._executeCLI('gemini', userPrompt, systemPromptSpec, attemptOptions, (user, sys, opts) => this.buildCLICommand('gemini', user, sys, opts));
|
|
1109
|
-
if (response.success) {
|
|
1110
|
-
if (i > 0) {
|
|
1111
|
-
this.emitLog().warn(`Gemini served by ${model} after ${i} rotation${i === 1 ? '' : 's'} (tier ${i + 1}/${chain.length})`);
|
|
1112
|
-
}
|
|
1113
|
-
else {
|
|
1114
|
-
this.emitLog().debug(`Gemini served by frontier ${model}`);
|
|
1115
|
-
}
|
|
1116
|
-
return response;
|
|
1117
|
-
}
|
|
1118
|
-
if (!isGeminiRotatableError(response.error)) {
|
|
1119
|
-
this.emitLog().debug(`Gemini ${model} failed with non-rotatable error; aborting rotation`, {
|
|
1120
|
-
errorPreview: response.error?.slice(0, 120),
|
|
1121
|
-
});
|
|
1122
|
-
return response;
|
|
1123
|
-
}
|
|
1124
|
-
this.emitLog().warn(`Gemini ${model} unavailable (capacity or access); rotating to next frontier tier`);
|
|
1125
|
-
lastResponse = response;
|
|
1126
|
-
}
|
|
1127
|
-
this.emitLog().error(`Gemini frontier chain exhausted (${chain.length} tiers); no tier available to this account`);
|
|
1128
|
-
return lastResponse;
|
|
1129
|
-
}
|
|
1130
1032
|
async waitForAvailableSlot() {
|
|
1131
1033
|
let waitTime = 100; // Start with 100ms wait time
|
|
1132
1034
|
while (this.runningCLIs >= this.MAX_CONCURRENT_CLIS) {
|
|
@@ -1136,8 +1038,7 @@ export class CLIAgentOrchestrator {
|
|
|
1136
1038
|
}
|
|
1137
1039
|
}
|
|
1138
1040
|
async executeCLIAgents(cliAgents, systemPrompt, userPrompt, options = {}) {
|
|
1139
|
-
|
|
1140
|
-
const validAgents = cliAgents.filter(agent => ['claude', 'codex', 'gemini'].includes(agent));
|
|
1041
|
+
const validAgents = cliAgents.filter(agent => ['claude', 'codex', 'agy'].includes(agent));
|
|
1141
1042
|
if (validAgents.length === 0) {
|
|
1142
1043
|
return [];
|
|
1143
1044
|
}
|
|
@@ -1165,7 +1066,7 @@ export class CLIAgentOrchestrator {
|
|
|
1165
1066
|
.map(result => result.value);
|
|
1166
1067
|
}
|
|
1167
1068
|
async executeCLIAgent(agent, systemPrompt, userPrompt, options = {}) {
|
|
1168
|
-
if (!['claude', 'codex', '
|
|
1069
|
+
if (!['claude', 'codex', 'agy'].includes(agent)) {
|
|
1169
1070
|
throw new Error(`Unsupported CLI agent: ${agent}`);
|
|
1170
1071
|
}
|
|
1171
1072
|
return await this.executeSingleCLI(agent, userPrompt, systemPrompt, options);
|