triflux 4.2.3 → 4.2.5
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/bin/triflux.mjs +3 -15
- package/hub/server.mjs +3 -2
- package/hub/team/native.mjs +58 -47
- package/hub/workers/delegator-mcp.mjs +17 -5
- package/package.json +62 -62
- package/scripts/lib/mcp-filter.mjs +34 -8
- package/scripts/tfx-route.sh +14 -8
- package/scripts/token-snapshot.mjs +561 -561
- package/skills/tfx-multi/SKILL.md +38 -7
- package/scripts/psmux-steering-prototype.sh +0 -368
package/bin/triflux.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
// triflux CLI — setup, doctor, version
|
|
3
3
|
import { copyFileSync, existsSync, readFileSync, writeFileSync, mkdirSync, chmodSync, readdirSync, unlinkSync, rmSync, statSync, openSync, closeSync } from "fs";
|
|
4
4
|
import { join, dirname } from "path";
|
|
@@ -1045,13 +1045,7 @@ async function cmdDoctor(options = {}) {
|
|
|
1045
1045
|
shells: codexCli.shells,
|
|
1046
1046
|
...(codexCli.fix ? { fix: codexCli.fix } : {}),
|
|
1047
1047
|
});
|
|
1048
|
-
|
|
1049
|
-
if (process.env.OPENAI_API_KEY) {
|
|
1050
|
-
ok("OPENAI_API_KEY 설정됨");
|
|
1051
|
-
} else {
|
|
1052
|
-
warn(`OPENAI_API_KEY 미설정 ${GRAY}(Pro 구독이면 불필요)${RESET}`);
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1048
|
+
// API 키 검사 제거 — bash exec 기반이므로 API 키 불필요
|
|
1055
1049
|
|
|
1056
1050
|
// 4. Codex Profiles
|
|
1057
1051
|
section("Codex Profiles");
|
|
@@ -1090,13 +1084,7 @@ async function cmdDoctor(options = {}) {
|
|
|
1090
1084
|
shells: geminiCli.shells,
|
|
1091
1085
|
...(geminiCli.fix ? { fix: geminiCli.fix } : {}),
|
|
1092
1086
|
});
|
|
1093
|
-
|
|
1094
|
-
if (process.env.GEMINI_API_KEY) {
|
|
1095
|
-
ok("GEMINI_API_KEY 설정됨");
|
|
1096
|
-
} else {
|
|
1097
|
-
warn(`GEMINI_API_KEY 미설정 ${GRAY}(gemini auth login)${RESET}`);
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1087
|
+
// API 키 검사 제거 — bash exec 기반이므로 API 키 불필요
|
|
1100
1088
|
|
|
1101
1089
|
// 6. Claude Code
|
|
1102
1090
|
section(`Claude Code ${AMBER}●${RESET}`);
|
package/hub/server.mjs
CHANGED
|
@@ -718,7 +718,7 @@ export async function startHub({ port = 27888, dbPath, host = '127.0.0.1', sessi
|
|
|
718
718
|
await pipe.start();
|
|
719
719
|
await assignCallbacks.start();
|
|
720
720
|
|
|
721
|
-
return new Promise((
|
|
721
|
+
return new Promise((resolveHub, reject) => {
|
|
722
722
|
httpServer.listen(port, host, () => {
|
|
723
723
|
const info = {
|
|
724
724
|
port,
|
|
@@ -763,10 +763,11 @@ export async function startHub({ port = 27888, dbPath, host = '127.0.0.1', sessi
|
|
|
763
763
|
store.close();
|
|
764
764
|
try { unlinkSync(PID_FILE); } catch {}
|
|
765
765
|
try { unlinkSync(TOKEN_FILE); } catch {}
|
|
766
|
+
httpServer.closeAllConnections();
|
|
766
767
|
await new Promise((resolveClose) => httpServer.close(resolveClose));
|
|
767
768
|
};
|
|
768
769
|
|
|
769
|
-
|
|
770
|
+
resolveHub({
|
|
770
771
|
...info,
|
|
771
772
|
httpServer,
|
|
772
773
|
store,
|
package/hub/team/native.mjs
CHANGED
|
@@ -10,12 +10,12 @@ import * as fs from "node:fs/promises";
|
|
|
10
10
|
import os from "node:os";
|
|
11
11
|
import path from "node:path";
|
|
12
12
|
|
|
13
|
-
const ROUTE_SCRIPT = "~/.claude/scripts/tfx-route.sh";
|
|
14
|
-
export const SLIM_WRAPPER_SUBAGENT_TYPE = "slim-wrapper";
|
|
15
|
-
const ROUTE_LOG_RE = /\[tfx-route\]/i;
|
|
16
|
-
const ROUTE_COMMAND_RE = /(?:^|[\s"'`])(?:bash\s+)?(?:[^"'`\s]*\/)?tfx-route\.sh\b/i;
|
|
17
|
-
const ROUTE_PROMPT_RE = /tfx-route\.sh/i;
|
|
18
|
-
const DIRECT_TOOL_BYPASS_RE = /\b(?:Read|Edit|Write)\s*\(/;
|
|
13
|
+
const ROUTE_SCRIPT = "~/.claude/scripts/tfx-route.sh";
|
|
14
|
+
export const SLIM_WRAPPER_SUBAGENT_TYPE = "slim-wrapper";
|
|
15
|
+
const ROUTE_LOG_RE = /\[tfx-route\]/i;
|
|
16
|
+
const ROUTE_COMMAND_RE = /(?:^|[\s"'`])(?:bash\s+)?(?:[^"'`\s]*\/)?tfx-route\.sh\b/i;
|
|
17
|
+
const ROUTE_PROMPT_RE = /tfx-route\.sh/i;
|
|
18
|
+
const DIRECT_TOOL_BYPASS_RE = /\b(?:Read|Edit|Write)\s*\(/;
|
|
19
19
|
|
|
20
20
|
function inferWorkerIndex(agentName = "") {
|
|
21
21
|
const match = /(\d+)(?!.*\d)/.exec(agentName);
|
|
@@ -60,46 +60,46 @@ export function buildSlimWrapperAgent(cli, opts = {}) {
|
|
|
60
60
|
* @param {string} [input.promptText]
|
|
61
61
|
* @param {string} [input.stdoutText]
|
|
62
62
|
* @param {string} [input.stderrText]
|
|
63
|
-
* @returns {{
|
|
64
|
-
* expectedRouteInvocation: boolean,
|
|
65
|
-
* promptMentionsRoute: boolean,
|
|
66
|
-
* sawRouteCommand: boolean,
|
|
67
|
-
* sawRouteLog: boolean,
|
|
68
|
-
* sawDirectToolBypass: boolean,
|
|
69
|
-
* usedRoute: boolean,
|
|
70
|
-
* abnormal: boolean,
|
|
71
|
-
* reason: string|null,
|
|
72
|
-
* }}
|
|
73
|
-
*/
|
|
74
|
-
export function verifySlimWrapperRouteExecution(input = {}) {
|
|
63
|
+
* @returns {{
|
|
64
|
+
* expectedRouteInvocation: boolean,
|
|
65
|
+
* promptMentionsRoute: boolean,
|
|
66
|
+
* sawRouteCommand: boolean,
|
|
67
|
+
* sawRouteLog: boolean,
|
|
68
|
+
* sawDirectToolBypass: boolean,
|
|
69
|
+
* usedRoute: boolean,
|
|
70
|
+
* abnormal: boolean,
|
|
71
|
+
* reason: string|null,
|
|
72
|
+
* }}
|
|
73
|
+
*/
|
|
74
|
+
export function verifySlimWrapperRouteExecution(input = {}) {
|
|
75
75
|
const promptText = String(input.promptText || "");
|
|
76
76
|
const stdoutText = String(input.stdoutText || "");
|
|
77
77
|
const stderrText = String(input.stderrText || "");
|
|
78
|
-
const combinedLogs = `${stdoutText}\n${stderrText}`;
|
|
79
|
-
const promptMentionsRoute = ROUTE_PROMPT_RE.test(promptText);
|
|
80
|
-
const sawRouteCommand = ROUTE_COMMAND_RE.test(combinedLogs);
|
|
81
|
-
const sawRouteLog = ROUTE_LOG_RE.test(combinedLogs);
|
|
82
|
-
const sawDirectToolBypass = DIRECT_TOOL_BYPASS_RE.test(stdoutText);
|
|
83
|
-
const usedRoute = sawRouteCommand || sawRouteLog;
|
|
84
|
-
const expectedRouteInvocation = promptMentionsRoute;
|
|
85
|
-
const abnormal = expectedRouteInvocation && (sawDirectToolBypass || !usedRoute);
|
|
86
|
-
const reason = !abnormal
|
|
87
|
-
? null
|
|
88
|
-
: sawDirectToolBypass
|
|
89
|
-
? "direct_tool_bypass_detected"
|
|
90
|
-
: "missing_tfx_route_evidence";
|
|
91
|
-
|
|
92
|
-
return {
|
|
93
|
-
expectedRouteInvocation,
|
|
94
|
-
promptMentionsRoute,
|
|
95
|
-
sawRouteCommand,
|
|
96
|
-
sawRouteLog,
|
|
97
|
-
sawDirectToolBypass,
|
|
98
|
-
usedRoute,
|
|
99
|
-
abnormal,
|
|
100
|
-
reason,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
78
|
+
const combinedLogs = `${stdoutText}\n${stderrText}`;
|
|
79
|
+
const promptMentionsRoute = ROUTE_PROMPT_RE.test(promptText);
|
|
80
|
+
const sawRouteCommand = ROUTE_COMMAND_RE.test(combinedLogs);
|
|
81
|
+
const sawRouteLog = ROUTE_LOG_RE.test(combinedLogs);
|
|
82
|
+
const sawDirectToolBypass = DIRECT_TOOL_BYPASS_RE.test(stdoutText);
|
|
83
|
+
const usedRoute = sawRouteCommand || sawRouteLog;
|
|
84
|
+
const expectedRouteInvocation = promptMentionsRoute;
|
|
85
|
+
const abnormal = expectedRouteInvocation && (sawDirectToolBypass || !usedRoute);
|
|
86
|
+
const reason = !abnormal
|
|
87
|
+
? null
|
|
88
|
+
: sawDirectToolBypass
|
|
89
|
+
? "direct_tool_bypass_detected"
|
|
90
|
+
: "missing_tfx_route_evidence";
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
expectedRouteInvocation,
|
|
94
|
+
promptMentionsRoute,
|
|
95
|
+
sawRouteCommand,
|
|
96
|
+
sawRouteLog,
|
|
97
|
+
sawDirectToolBypass,
|
|
98
|
+
usedRoute,
|
|
99
|
+
abnormal,
|
|
100
|
+
reason,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
105
|
* role/mcp_profile별 tfx-route.sh 기본 timeout (초)
|
|
@@ -108,10 +108,21 @@ export function verifySlimWrapperRouteExecution(input = {}) {
|
|
|
108
108
|
* @param {string} mcpProfile — MCP 프로필
|
|
109
109
|
* @returns {number} timeout(초)
|
|
110
110
|
*/
|
|
111
|
-
function getRouteTimeout(role,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
function getRouteTimeout(role, _mcpProfile) {
|
|
112
|
+
// tfx-route.sh route_agent()의 DEFAULT_TIMEOUT 기반, 최소 1080초(18분) 보장.
|
|
113
|
+
// Bash timeout = 이 값 + 60초 여유. 짧은 역할도 네트워크/스케줄 지연 대비.
|
|
114
|
+
const TIMEOUTS = {
|
|
115
|
+
'build-fixer': 1080, debugger: 1080, executor: 1080,
|
|
116
|
+
'deep-executor': 3600, architect: 3600, planner: 3600,
|
|
117
|
+
critic: 3600, analyst: 3600, scientist: 1800,
|
|
118
|
+
'scientist-deep': 3600, 'document-specialist': 1800,
|
|
119
|
+
'code-reviewer': 1800, 'security-reviewer': 1800,
|
|
120
|
+
'quality-reviewer': 1800, verifier: 1800,
|
|
121
|
+
designer: 1080, writer: 1080,
|
|
122
|
+
explore: 1080, 'test-engineer': 1080, 'qa-tester': 1080,
|
|
123
|
+
spark: 600,
|
|
124
|
+
};
|
|
125
|
+
return TIMEOUTS[role] || 1080;
|
|
115
126
|
}
|
|
116
127
|
|
|
117
128
|
/**
|
|
@@ -6,7 +6,7 @@ import { randomUUID } from 'node:crypto';
|
|
|
6
6
|
import { existsSync, readFileSync } from 'node:fs';
|
|
7
7
|
import { dirname, isAbsolute, resolve } from 'node:path';
|
|
8
8
|
import process from 'node:process';
|
|
9
|
-
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
10
10
|
|
|
11
11
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
12
12
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
@@ -14,15 +14,25 @@ import * as z from 'zod';
|
|
|
14
14
|
|
|
15
15
|
import { CodexMcpWorker } from './codex-mcp.mjs';
|
|
16
16
|
import { GeminiWorker } from './gemini-worker.mjs';
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
|
|
20
|
+
// mcp-filter.mjs 동적 해석 — 프로젝트(hub/workers/)와 배포(scripts/hub/workers/) 양쪽 대응
|
|
21
|
+
const MCP_FILTER_CANDIDATES = [
|
|
22
|
+
resolve(SCRIPT_DIR, '../../scripts/lib/mcp-filter.mjs'), // 프로젝트 원본
|
|
23
|
+
resolve(SCRIPT_DIR, '../../lib/mcp-filter.mjs'), // 배포 (~/.claude/scripts/)
|
|
24
|
+
];
|
|
25
|
+
const mcpFilterPath = MCP_FILTER_CANDIDATES.find((p) => existsSync(p));
|
|
26
|
+
if (!mcpFilterPath) {
|
|
27
|
+
throw new Error(`mcp-filter.mjs not found. candidates: ${MCP_FILTER_CANDIDATES.join(', ')}`);
|
|
28
|
+
}
|
|
29
|
+
const {
|
|
18
30
|
buildPromptHint,
|
|
19
31
|
getCodexMcpConfig,
|
|
20
32
|
getGeminiAllowedServers,
|
|
21
33
|
resolveMcpProfile,
|
|
22
34
|
SUPPORTED_MCP_PROFILES,
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
35
|
+
} = await import(pathToFileURL(mcpFilterPath).href);
|
|
26
36
|
const SERVER_INFO = { name: 'triflux-delegator', version: '1.0.0' };
|
|
27
37
|
const DEFAULT_CONTEXT_BYTES = 32 * 1024;
|
|
28
38
|
const DEFAULT_ROUTE_TIMEOUT_SEC = 120;
|
|
@@ -70,6 +80,8 @@ const CODEX_PROFILE_BY_AGENT = Object.freeze({
|
|
|
70
80
|
'scientist-deep': 'thorough',
|
|
71
81
|
'document-specialist': 'high',
|
|
72
82
|
verifier: 'thorough',
|
|
83
|
+
designer: 'high', // Gemini primary, codex fallback용
|
|
84
|
+
writer: 'high', // Gemini primary, codex fallback용
|
|
73
85
|
spark: 'spark_fast',
|
|
74
86
|
});
|
|
75
87
|
|
package/package.json
CHANGED
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "triflux",
|
|
3
|
-
"version": "4.2.
|
|
4
|
-
"description": "CLI-first multi-model orchestrator for Claude Code — route tasks to Codex, Gemini, and Claude",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"triflux": "bin/triflux.mjs",
|
|
8
|
-
"tfx": "bin/triflux.mjs",
|
|
9
|
-
"tfl": "bin/triflux.mjs",
|
|
10
|
-
"tfx-setup": "bin/tfx-setup.mjs",
|
|
11
|
-
"tfx-doctor": "bin/tfx-doctor.mjs"
|
|
12
|
-
},
|
|
13
|
-
"files": [
|
|
14
|
-
"bin",
|
|
15
|
-
"hub",
|
|
16
|
-
"skills",
|
|
17
|
-
"!**/failure-reports",
|
|
18
|
-
"scripts",
|
|
19
|
-
"hooks",
|
|
20
|
-
"hud",
|
|
21
|
-
".claude-plugin",
|
|
22
|
-
".mcp.json",
|
|
23
|
-
"README.md",
|
|
24
|
-
"README.ko.md",
|
|
25
|
-
"LICENSE"
|
|
26
|
-
],
|
|
27
|
-
"scripts": {
|
|
28
|
-
"setup": "node scripts/setup.mjs",
|
|
29
|
-
"preinstall": "node scripts/preinstall.mjs",
|
|
30
|
-
"postinstall": "node scripts/setup.mjs",
|
|
31
|
-
"test": "node --test --test-force-exit --test-concurrency=1 \"tests/**/*.test.mjs\" \"scripts/__tests__/**/*.test.mjs\"",
|
|
32
|
-
"test:unit": "node --test --test-force-exit --test-concurrency=1 tests/unit/**/*.test.mjs",
|
|
33
|
-
"test:integration": "node --test --test-force-exit --test-concurrency=1 tests/integration/**/*.test.mjs",
|
|
34
|
-
"test:route-smoke": "node --test scripts/test-tfx-route-no-claude-native.mjs"
|
|
35
|
-
},
|
|
36
|
-
"engines": {
|
|
37
|
-
"node": ">=18.0.0"
|
|
38
|
-
},
|
|
39
|
-
"repository": {
|
|
40
|
-
"type": "git",
|
|
41
|
-
"url": "git+https://github.com/tellang/triflux.git"
|
|
42
|
-
},
|
|
43
|
-
"homepage": "https://github.com/tellang/triflux#readme",
|
|
44
|
-
"author": "tellang",
|
|
45
|
-
"license": "MIT",
|
|
46
|
-
"dependencies": {
|
|
47
|
-
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
48
|
-
"better-sqlite3": "^12.6.2",
|
|
49
|
-
"systray2": "^2.1.4"
|
|
50
|
-
},
|
|
51
|
-
"keywords": [
|
|
52
|
-
"claude-code",
|
|
53
|
-
"plugin",
|
|
54
|
-
"codex",
|
|
55
|
-
"gemini",
|
|
56
|
-
"cli-routing",
|
|
57
|
-
"orchestration",
|
|
58
|
-
"multi-model",
|
|
59
|
-
"triflux",
|
|
60
|
-
"tfx"
|
|
61
|
-
]
|
|
62
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "triflux",
|
|
3
|
+
"version": "4.2.5",
|
|
4
|
+
"description": "CLI-first multi-model orchestrator for Claude Code — route tasks to Codex, Gemini, and Claude",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"triflux": "bin/triflux.mjs",
|
|
8
|
+
"tfx": "bin/triflux.mjs",
|
|
9
|
+
"tfl": "bin/triflux.mjs",
|
|
10
|
+
"tfx-setup": "bin/tfx-setup.mjs",
|
|
11
|
+
"tfx-doctor": "bin/tfx-doctor.mjs"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"bin",
|
|
15
|
+
"hub",
|
|
16
|
+
"skills",
|
|
17
|
+
"!**/failure-reports",
|
|
18
|
+
"scripts",
|
|
19
|
+
"hooks",
|
|
20
|
+
"hud",
|
|
21
|
+
".claude-plugin",
|
|
22
|
+
".mcp.json",
|
|
23
|
+
"README.md",
|
|
24
|
+
"README.ko.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"setup": "node scripts/setup.mjs",
|
|
29
|
+
"preinstall": "node scripts/preinstall.mjs",
|
|
30
|
+
"postinstall": "node scripts/setup.mjs",
|
|
31
|
+
"test": "node --test --test-force-exit --test-concurrency=1 \"tests/**/*.test.mjs\" \"scripts/__tests__/**/*.test.mjs\"",
|
|
32
|
+
"test:unit": "node --test --test-force-exit --test-concurrency=1 tests/unit/**/*.test.mjs",
|
|
33
|
+
"test:integration": "node --test --test-force-exit --test-concurrency=1 tests/integration/**/*.test.mjs",
|
|
34
|
+
"test:route-smoke": "node --test scripts/test-tfx-route-no-claude-native.mjs"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/tellang/triflux.git"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/tellang/triflux#readme",
|
|
44
|
+
"author": "tellang",
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
48
|
+
"better-sqlite3": "^12.6.2",
|
|
49
|
+
"systray2": "^2.1.4"
|
|
50
|
+
},
|
|
51
|
+
"keywords": [
|
|
52
|
+
"claude-code",
|
|
53
|
+
"plugin",
|
|
54
|
+
"codex",
|
|
55
|
+
"gemini",
|
|
56
|
+
"cli-routing",
|
|
57
|
+
"orchestration",
|
|
58
|
+
"multi-model",
|
|
59
|
+
"triflux",
|
|
60
|
+
"tfx"
|
|
61
|
+
]
|
|
62
|
+
}
|
|
@@ -71,8 +71,21 @@ const PROFILE_DEFINITIONS = Object.freeze({
|
|
|
71
71
|
]),
|
|
72
72
|
}),
|
|
73
73
|
}),
|
|
74
|
+
analyze: Object.freeze({
|
|
75
|
+
description: '분석/설계 워커용. 추론 + 검색 MCP 허용',
|
|
76
|
+
allowedServers: Object.freeze(['context7', 'brave-search', 'tavily', 'exa', 'sequential-thinking']),
|
|
77
|
+
alwaysOnServers: Object.freeze(['context7', 'sequential-thinking']),
|
|
78
|
+
maxSearchServers: 2,
|
|
79
|
+
allowedToolsByServer: Object.freeze({
|
|
80
|
+
context7: Object.freeze(['resolve-library-id', 'query-docs']),
|
|
81
|
+
'brave-search': Object.freeze(['brave_web_search', 'brave_news_search']),
|
|
82
|
+
exa: Object.freeze(['web_search_exa', 'get_code_context_exa']),
|
|
83
|
+
tavily: Object.freeze(['tavily_search', 'tavily_extract']),
|
|
84
|
+
'sequential-thinking': Object.freeze(['sequentialthinking']),
|
|
85
|
+
}),
|
|
86
|
+
}),
|
|
74
87
|
explore: Object.freeze({
|
|
75
|
-
description: '
|
|
88
|
+
description: '탐색/리서치 워커용. 읽기/검색 중심 MCP만 허용',
|
|
76
89
|
allowedServers: Object.freeze(['context7', 'brave-search', 'tavily', 'exa']),
|
|
77
90
|
alwaysOnServers: Object.freeze(['context7']),
|
|
78
91
|
maxSearchServers: 2,
|
|
@@ -116,7 +129,7 @@ const PROFILE_DEFINITIONS = Object.freeze({
|
|
|
116
129
|
|
|
117
130
|
export const LEGACY_PROFILE_ALIASES = Object.freeze({
|
|
118
131
|
implement: 'executor',
|
|
119
|
-
analyze: '
|
|
132
|
+
analyze: 'analyze',
|
|
120
133
|
review: 'reviewer',
|
|
121
134
|
docs: 'writer',
|
|
122
135
|
minimal: 'default',
|
|
@@ -147,15 +160,17 @@ function resolveAutoProfile(agentType = '') {
|
|
|
147
160
|
case 'build-fixer':
|
|
148
161
|
case 'debugger':
|
|
149
162
|
case 'deep-executor':
|
|
163
|
+
return 'executor';
|
|
150
164
|
case 'test-engineer':
|
|
151
165
|
case 'qa-tester':
|
|
152
|
-
return '
|
|
166
|
+
return 'none';
|
|
153
167
|
case 'designer':
|
|
154
168
|
return 'designer';
|
|
155
169
|
case 'architect':
|
|
156
170
|
case 'planner':
|
|
157
171
|
case 'critic':
|
|
158
172
|
case 'analyst':
|
|
173
|
+
return 'analyze';
|
|
159
174
|
case 'scientist':
|
|
160
175
|
case 'scientist-deep':
|
|
161
176
|
case 'document-specialist':
|
|
@@ -474,17 +489,28 @@ export function getGeminiAllowedServers(options = {}) {
|
|
|
474
489
|
export function getCodexMcpConfig(options = {}) {
|
|
475
490
|
const allowedServers = new Set(resolveAllowedServers(options));
|
|
476
491
|
const resolvedProfile = resolveMcpProfile(options.agentType, options.requestedProfile);
|
|
492
|
+
// Codex에 실제 등록된 서버만 대상으로 config override 생성.
|
|
493
|
+
// 미등록 서버에 enabled=false를 보내면 "invalid transport" 에러 발생.
|
|
494
|
+
const registeredServers = parseAvailableServers(options.availableServers);
|
|
495
|
+
// Codex 0.115+: 미등록 서버에 config override를 보내면 "invalid transport" 에러.
|
|
496
|
+
// 등록 서버 정보가 없으면 override를 생성하지 않는다 (안전 기본값).
|
|
497
|
+
if (registeredServers.length === 0) {
|
|
498
|
+
return { mcp_servers: {} };
|
|
499
|
+
}
|
|
500
|
+
const targetServers = registeredServers;
|
|
501
|
+
|
|
477
502
|
if (resolvedProfile === 'none') {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
};
|
|
503
|
+
// Codex 0.115+: transport 없는 서버에 enabled=false를 보내면 "invalid transport" 에러.
|
|
504
|
+
// 비허용 서버는 override에서 제외하고, 허용 서버만 명시적으로 설정한다.
|
|
505
|
+
return { mcp_servers: {} };
|
|
481
506
|
}
|
|
482
507
|
|
|
483
508
|
const config = { mcp_servers: {} };
|
|
484
509
|
const allowedToolsByServer = getProfileDefinition(resolvedProfile).allowedToolsByServer;
|
|
485
|
-
for (const server of
|
|
510
|
+
for (const server of targetServers) {
|
|
511
|
+
// Codex 0.115+: transport 없는 서버에 enabled=false를 보내면 "invalid transport" 에러.
|
|
512
|
+
// 비허용 서버는 override에서 제외한다 (Codex 기본 설정이 유지됨).
|
|
486
513
|
if (!allowedServers.has(server)) {
|
|
487
|
-
config.mcp_servers[server] = { enabled: false };
|
|
488
514
|
continue;
|
|
489
515
|
}
|
|
490
516
|
|
package/scripts/tfx-route.sh
CHANGED
|
@@ -558,20 +558,23 @@ route_agent() {
|
|
|
558
558
|
CLI_ARGS="-m gemini-3-flash-preview -y --prompt"
|
|
559
559
|
CLI_EFFORT="flash"; DEFAULT_TIMEOUT=900; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
|
|
560
560
|
|
|
561
|
-
# ───
|
|
561
|
+
# ─── 탐색/검증/테스트 (Codex 우선, claude-native fallback은 apply_cli_mode auto에서) ───
|
|
562
562
|
explore)
|
|
563
|
-
CLI_TYPE="
|
|
564
|
-
|
|
563
|
+
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
564
|
+
CLI_ARGS="exec --profile fast ${codex_base}"
|
|
565
|
+
CLI_EFFORT="fast"; DEFAULT_TIMEOUT=600; RUN_MODE="fg"; OPUS_OVERSIGHT="false" ;;
|
|
565
566
|
verifier)
|
|
566
567
|
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
567
568
|
CLI_ARGS="exec --profile thorough ${codex_base} review"
|
|
568
569
|
CLI_EFFORT="thorough"; DEFAULT_TIMEOUT=1200; RUN_MODE="fg"; OPUS_OVERSIGHT="false" ;;
|
|
569
570
|
test-engineer)
|
|
570
|
-
CLI_TYPE="
|
|
571
|
-
|
|
571
|
+
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
572
|
+
CLI_ARGS="exec ${codex_base}"
|
|
573
|
+
CLI_EFFORT="high"; DEFAULT_TIMEOUT=1200; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
|
|
572
574
|
qa-tester)
|
|
573
|
-
CLI_TYPE="
|
|
574
|
-
|
|
575
|
+
CLI_TYPE="codex"; CLI_CMD="codex"
|
|
576
|
+
CLI_ARGS="exec --profile thorough ${codex_base} review"
|
|
577
|
+
CLI_EFFORT="thorough"; DEFAULT_TIMEOUT=1200; RUN_MODE="bg"; OPUS_OVERSIGHT="false" ;;
|
|
575
578
|
|
|
576
579
|
# ─── 경량 ───
|
|
577
580
|
spark)
|
|
@@ -849,7 +852,10 @@ resolve_mcp_policy() {
|
|
|
849
852
|
fi
|
|
850
853
|
|
|
851
854
|
available_servers=$(get_cached_servers "$CLI_TYPE")
|
|
852
|
-
|
|
855
|
+
# Codex 0.115+: 미등록 서버에 config override(enabled=true/false 모두)를 보내면
|
|
856
|
+
# "invalid transport" 에러 발생. 캐시 비어있으면 빈 문자열로 유지하여
|
|
857
|
+
# mcp-filter가 override를 생성하지 않도록 한다.
|
|
858
|
+
[[ -z "$available_servers" ]] && available_servers=""
|
|
853
859
|
|
|
854
860
|
local -a cmd=(
|
|
855
861
|
"$NODE_BIN" "$filter_script" shell
|