@triflux/remote 10.44.0 → 10.45.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.
- package/hub/pipe.mjs +10 -15
- package/hub/schema.sql +1 -0
- package/hub/server.mjs +172 -93
- package/hub/store.mjs +127 -61
- package/hub/team/backend.mjs +4 -2
- package/hub/team/claude-daemon-control.mjs +2 -0
- package/hub/team/cli/commands/start/start-headless.mjs +2 -0
- package/hub/team/cli/commands/start/start-mux.mjs +24 -3
- package/hub/team/cli/services/runtime-mode.mjs +23 -9
- package/hub/team/conductor.mjs +80 -24
- package/hub/team/cto-auto-collect.mjs +2 -6
- package/hub/team/cto-pull.mjs +4 -0
- package/hub/team/dashboard-open.mjs +1 -0
- package/hub/team/execution-mode.mjs +38 -2
- package/hub/team/headless.mjs +493 -82
- package/hub/team/interactive-tui-transport.mjs +15 -1
- package/hub/team/launcher-template.mjs +17 -1
- package/hub/team/orchestrator.mjs +36 -9
- package/hub/team/pane.mjs +176 -14
- package/hub/team/psmux.mjs +79 -70
- package/hub/team/retry-state-machine.mjs +39 -16
- package/hub/team/session.mjs +57 -0
- package/hub/team/synapse-http.mjs +149 -4
- package/hub/team/terminal-opener.mjs +68 -13
- package/hub/team/tui-viewer.mjs +65 -14
- package/hub/tools.mjs +10 -1
- package/hub/workers/codex-mcp.mjs +8 -3
- package/hub/workers/delegator-mcp.mjs +1 -1
- package/package.json +1 -1
- package/scripts/lib/agent-route-policy.mjs +84 -24
- package/scripts/lib/cli-agy.mjs +24 -4
- package/scripts/lib/cli-codex.mjs +2 -0
- package/scripts/lib/codex-profile-config.mjs +26 -8
- package/scripts/lib/codex-recovery.sh +19 -0
- package/scripts/lib/gemini-profiles.mjs +113 -29
- package/scripts/lib/keyword-rules.mjs +12 -0
- package/scripts/lib/logger.mjs +60 -43
- package/scripts/lib/machine-profile.mjs +293 -0
- package/scripts/lib/mcp-health.mjs +44 -18
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// Canonical Codex role policy. Concrete model IDs stay in profile config files.
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
normalizeCodexProfileName,
|
|
5
|
+
resolveCodexProfileConfigValues,
|
|
6
|
+
} from "./codex-profile-config.mjs";
|
|
4
7
|
|
|
5
8
|
const policy = {
|
|
6
9
|
executor: {
|
|
@@ -44,7 +47,7 @@ const policy = {
|
|
|
44
47
|
subcommand: "exec",
|
|
45
48
|
},
|
|
46
49
|
debugger: {
|
|
47
|
-
profile: "
|
|
50
|
+
profile: "gpt6_astra_xhigh",
|
|
48
51
|
timeoutSec: 900,
|
|
49
52
|
runMode: "bg",
|
|
50
53
|
opusOversight: false,
|
|
@@ -52,7 +55,7 @@ const policy = {
|
|
|
52
55
|
subcommand: "exec",
|
|
53
56
|
},
|
|
54
57
|
"deep-executor": {
|
|
55
|
-
profile: "
|
|
58
|
+
profile: "gpt6_astra_xhigh",
|
|
56
59
|
timeoutSec: 3600,
|
|
57
60
|
runMode: "bg",
|
|
58
61
|
opusOversight: true,
|
|
@@ -60,7 +63,7 @@ const policy = {
|
|
|
60
63
|
subcommand: "exec",
|
|
61
64
|
},
|
|
62
65
|
architect: {
|
|
63
|
-
profile: "
|
|
66
|
+
profile: "gpt6_astra_xhigh",
|
|
64
67
|
timeoutSec: 3600,
|
|
65
68
|
runMode: "bg",
|
|
66
69
|
opusOversight: true,
|
|
@@ -68,7 +71,7 @@ const policy = {
|
|
|
68
71
|
subcommand: "exec",
|
|
69
72
|
},
|
|
70
73
|
critic: {
|
|
71
|
-
profile: "
|
|
74
|
+
profile: "gpt6_astra_xhigh",
|
|
72
75
|
timeoutSec: 3600,
|
|
73
76
|
runMode: "bg",
|
|
74
77
|
opusOversight: true,
|
|
@@ -76,7 +79,7 @@ const policy = {
|
|
|
76
79
|
subcommand: "exec",
|
|
77
80
|
},
|
|
78
81
|
planner: {
|
|
79
|
-
profile: "
|
|
82
|
+
profile: "gpt6_astra_xhigh",
|
|
80
83
|
timeoutSec: 3600,
|
|
81
84
|
runMode: "fg",
|
|
82
85
|
opusOversight: true,
|
|
@@ -84,7 +87,7 @@ const policy = {
|
|
|
84
87
|
subcommand: "exec",
|
|
85
88
|
},
|
|
86
89
|
analyst: {
|
|
87
|
-
profile: "
|
|
90
|
+
profile: "gpt6_astra_xhigh",
|
|
88
91
|
timeoutSec: 3600,
|
|
89
92
|
runMode: "fg",
|
|
90
93
|
opusOversight: true,
|
|
@@ -108,7 +111,7 @@ const policy = {
|
|
|
108
111
|
subcommand: "review",
|
|
109
112
|
},
|
|
110
113
|
"security-reviewer": {
|
|
111
|
-
profile: "
|
|
114
|
+
profile: "gpt6_astra_xhigh",
|
|
112
115
|
timeoutSec: 1800,
|
|
113
116
|
runMode: "bg",
|
|
114
117
|
opusOversight: true,
|
|
@@ -132,7 +135,7 @@ const policy = {
|
|
|
132
135
|
subcommand: "exec",
|
|
133
136
|
},
|
|
134
137
|
"scientist-deep": {
|
|
135
|
-
profile: "
|
|
138
|
+
profile: "gpt6_astra_xhigh",
|
|
136
139
|
timeoutSec: 3600,
|
|
137
140
|
runMode: "bg",
|
|
138
141
|
opusOversight: false,
|
|
@@ -174,7 +177,7 @@ const policy = {
|
|
|
174
177
|
// These entries are only for explicit direct-Codex overrides. agent-map.json
|
|
175
178
|
// remains the provider-selection authority for their normal execution.
|
|
176
179
|
designer: {
|
|
177
|
-
profile: "
|
|
180
|
+
profile: "gpt6_astra_xhigh",
|
|
178
181
|
timeoutSec: 900,
|
|
179
182
|
runMode: "bg",
|
|
180
183
|
opusOversight: false,
|
|
@@ -207,15 +210,23 @@ export const DEFAULT_CODEX_AGENT = "executor";
|
|
|
207
210
|
|
|
208
211
|
export const CANONICAL_CODEX_PROFILE_OVERRIDES = Object.freeze(
|
|
209
212
|
new Set([
|
|
210
|
-
"gpt56_luna_low",
|
|
211
|
-
"
|
|
213
|
+
"gpt56_luna_low",
|
|
214
|
+
"gpt56_terra_med",
|
|
215
|
+
"gpt56_terra_high",
|
|
216
|
+
"gpt6_astra_xhigh",
|
|
217
|
+
"gpt6_astra_max",
|
|
218
|
+
"gpt6_astra_ultra",
|
|
212
219
|
]),
|
|
213
220
|
);
|
|
214
221
|
export const ULTRA_ELIGIBLE_CODEX_AGENTS = Object.freeze(
|
|
215
222
|
new Set(["deep-executor", "scientist-deep"]),
|
|
216
223
|
);
|
|
224
|
+
|
|
217
225
|
const NESTED_GLOBAL_PROFILE_OVERRIDES = new Set([
|
|
218
|
-
"max",
|
|
226
|
+
"max",
|
|
227
|
+
"ultra",
|
|
228
|
+
"gpt6_astra_max",
|
|
229
|
+
"gpt6_astra_ultra",
|
|
219
230
|
]);
|
|
220
231
|
|
|
221
232
|
export function resolveCodexAgentPolicy(agent) {
|
|
@@ -224,35 +235,84 @@ export function resolveCodexAgentPolicy(agent) {
|
|
|
224
235
|
);
|
|
225
236
|
}
|
|
226
237
|
|
|
238
|
+
/**
|
|
239
|
+
* Resolves a requested Codex profile against the canonical role policy.
|
|
240
|
+
*
|
|
241
|
+
* `retryProfile` intentionally wins over `profileOverride`: retry snapshots
|
|
242
|
+
* represent an already-selected escalation step and must not be replaced by a
|
|
243
|
+
* later process-level `TFX_CODEX_PROFILE`. Ultra is reserved for an eligible
|
|
244
|
+
* top-level role; every nested/sandboxed or ineligible request is max.
|
|
245
|
+
*/
|
|
227
246
|
export function resolveCodexAgentProfile(
|
|
228
247
|
agent,
|
|
229
|
-
{
|
|
248
|
+
{
|
|
249
|
+
profileOverride = "auto",
|
|
250
|
+
retryProfile = null,
|
|
251
|
+
nested = false,
|
|
252
|
+
allowCustom = false,
|
|
253
|
+
} = {},
|
|
230
254
|
) {
|
|
231
255
|
const defaultProfile = resolveCodexAgentPolicy(agent).profile;
|
|
232
256
|
const retryRequested = String(retryProfile ?? "").trim();
|
|
233
|
-
const requested =
|
|
257
|
+
const requested = normalizeCodexProfileName(
|
|
258
|
+
retryRequested || String(profileOverride || "auto").trim(),
|
|
259
|
+
);
|
|
234
260
|
if (!requested || requested === "auto") return defaultProfile;
|
|
235
|
-
|
|
261
|
+
|
|
262
|
+
const canonical =
|
|
263
|
+
requested === "max"
|
|
264
|
+
? "gpt6_astra_max"
|
|
265
|
+
: requested === "ultra"
|
|
266
|
+
? "gpt6_astra_ultra"
|
|
267
|
+
: requested;
|
|
236
268
|
if (!CANONICAL_CODEX_PROFILE_OVERRIDES.has(canonical)) {
|
|
237
269
|
if (allowCustom) return requested;
|
|
238
|
-
throw new Error(
|
|
270
|
+
throw new Error(
|
|
271
|
+
`[agent-route-policy] unsupported profile override: ${requested}`,
|
|
272
|
+
);
|
|
239
273
|
}
|
|
240
|
-
if (canonical !== "
|
|
241
|
-
return nested || !ULTRA_ELIGIBLE_CODEX_AGENTS.has(agent)
|
|
274
|
+
if (canonical !== "gpt6_astra_ultra") return canonical;
|
|
275
|
+
return nested || !ULTRA_ELIGIBLE_CODEX_AGENTS.has(agent)
|
|
276
|
+
? "gpt6_astra_max"
|
|
277
|
+
: canonical;
|
|
242
278
|
}
|
|
243
279
|
|
|
280
|
+
/**
|
|
281
|
+
* Nested headless, team, worker, and delegator launches always select an
|
|
282
|
+
* explicit role profile rather than inheriting the global Codex config. A
|
|
283
|
+
* custom profile that resolves to ultra is also downgraded to max.
|
|
284
|
+
*/
|
|
244
285
|
export function resolveNestedCodexAgentProfile(
|
|
245
286
|
agent,
|
|
246
|
-
{
|
|
287
|
+
{
|
|
288
|
+
profileOverride = "auto",
|
|
289
|
+
globalProfile = "auto",
|
|
290
|
+
retryProfile = null,
|
|
291
|
+
codexHome,
|
|
292
|
+
} = {},
|
|
247
293
|
) {
|
|
248
294
|
const explicitProfile = String(profileOverride || "auto").trim();
|
|
249
|
-
const globalOverride =
|
|
250
|
-
|
|
295
|
+
const globalOverride = normalizeCodexProfileName(
|
|
296
|
+
String(globalProfile || "auto").trim(),
|
|
297
|
+
);
|
|
298
|
+
// Headless lanes must select their role policy explicitly. Only the max and
|
|
299
|
+
// ultra exception lanes may cross the process environment boundary.
|
|
300
|
+
const effectiveOverride =
|
|
301
|
+
explicitProfile !== "auto"
|
|
302
|
+
? explicitProfile
|
|
303
|
+
: NESTED_GLOBAL_PROFILE_OVERRIDES.has(globalOverride)
|
|
304
|
+
? globalOverride
|
|
305
|
+
: "auto";
|
|
251
306
|
const fallback = resolveCodexAgentProfile(agent);
|
|
252
|
-
const candidate = resolveCodexAgentProfile(agent, {
|
|
307
|
+
const candidate = resolveCodexAgentProfile(agent, {
|
|
308
|
+
profileOverride: effectiveOverride,
|
|
309
|
+
retryProfile,
|
|
310
|
+
nested: true,
|
|
311
|
+
allowCustom: true,
|
|
312
|
+
});
|
|
253
313
|
const { effort } = resolveCodexProfileConfigValues(candidate, { codexHome });
|
|
254
314
|
if (!effort) return fallback;
|
|
255
|
-
return effort.toLowerCase() === "ultra" ? "
|
|
315
|
+
return effort.toLowerCase() === "ultra" ? "gpt6_astra_max" : candidate;
|
|
256
316
|
}
|
|
257
317
|
|
|
258
318
|
export function describeCodexAgentPolicy() {
|
package/scripts/lib/cli-agy.mjs
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
// cli-agy.mjs — Antigravity (agy) CLI adapter (Phase 0 PoC).
|
|
2
2
|
//
|
|
3
|
-
// 책임: agy --print 는
|
|
4
|
-
//
|
|
3
|
+
// 책임: agy 1.1.27 부터 --print 는 값(프롬프트)이 필수이고 stdin 프롬프트는 거부되므로
|
|
4
|
+
// plan() 은 프롬프트를 args 의 --print 값으로 넣고 stdinMode="argv" 를 돌려준다.
|
|
5
|
+
// --print 뒤에 다른 플래그가 오면 그 플래그를 프롬프트로 삼키므로 항상 마지막에 둔다.
|
|
5
6
|
//
|
|
6
7
|
// 출처: scripts/tfx-route.sh route_agent() L1052-L1059, run_codex_exec() L2036-L2046.
|
|
7
8
|
// .claude/rules/tfx-update-logic.md Antigravity CLI 1.0.0 정밀 sanity matrix.
|
|
8
9
|
// timeoutSec은 lane 예상 소요 시간(advisory)이다. hard ceiling 집행은 Bash route가 소유한다.
|
|
9
10
|
|
|
11
|
+
import {
|
|
12
|
+
resolveGeminiModel,
|
|
13
|
+
resolveGeminiProfileForPurpose,
|
|
14
|
+
} from "./gemini-profiles.mjs";
|
|
15
|
+
|
|
10
16
|
export const id = "agy";
|
|
11
17
|
export const cliType = "antigravity";
|
|
12
18
|
export const command = "agy";
|
|
@@ -38,6 +44,7 @@ export function plan({
|
|
|
38
44
|
mcpProfile = "auto",
|
|
39
45
|
timeoutSec,
|
|
40
46
|
contextFile,
|
|
47
|
+
geminiProfilesPath,
|
|
41
48
|
} = {}) {
|
|
42
49
|
if (!agent) {
|
|
43
50
|
throw new Error("[cli-agy] agent required");
|
|
@@ -49,8 +56,21 @@ export function plan({
|
|
|
49
56
|
command,
|
|
50
57
|
profile: cfg.profile,
|
|
51
58
|
effort: cfg.profile,
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
// effort 는 역할별 SSOT(gemini-profiles.mjs): designer → High, writer → Medium ...
|
|
60
|
+
geminiProfile: resolveGeminiProfileForPurpose(agent),
|
|
61
|
+
model: resolveGeminiModel(resolveGeminiProfileForPurpose(agent), {
|
|
62
|
+
profilesPath: geminiProfilesPath,
|
|
63
|
+
}),
|
|
64
|
+
args: [
|
|
65
|
+
"--dangerously-skip-permissions",
|
|
66
|
+
"--model",
|
|
67
|
+
resolveGeminiModel(resolveGeminiProfileForPurpose(agent), {
|
|
68
|
+
profilesPath: geminiProfilesPath,
|
|
69
|
+
}),
|
|
70
|
+
"--print",
|
|
71
|
+
prompt,
|
|
72
|
+
],
|
|
73
|
+
stdinMode: "argv",
|
|
54
74
|
timeoutMs: effectiveTimeoutSec * 1000,
|
|
55
75
|
runMode: cfg.runMode,
|
|
56
76
|
opusOversight: cfg.opusOversight,
|
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
resolveNestedCodexAgentProfile,
|
|
19
19
|
} from "./agent-route-policy.mjs";
|
|
20
20
|
|
|
21
|
+
// Compatibility re-exports for launchers which historically imported these
|
|
22
|
+
// helpers from this adapter. The policy module owns the semantics.
|
|
21
23
|
export { resolveCodexAgentProfile, resolveNestedCodexAgentProfile };
|
|
22
24
|
|
|
23
25
|
export function plan({
|
|
@@ -22,11 +22,22 @@ const CANONICAL_PROFILE_VALUES = Object.freeze({
|
|
|
22
22
|
gpt56_luna_low: { model: "gpt-5.6-luna", effort: "low" },
|
|
23
23
|
gpt56_terra_med: { model: "gpt-5.6-terra", effort: "medium" },
|
|
24
24
|
gpt56_terra_high: { model: "gpt-5.6-terra", effort: "high" },
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
gpt6_astra_xhigh: { model: "gpt-6-astra", effort: "xhigh" },
|
|
26
|
+
gpt6_astra_max: { model: "gpt-6-astra", effort: "max" },
|
|
27
|
+
gpt6_astra_ultra: { model: "gpt-6-astra", effort: "ultra" },
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
const LEGACY_CANONICAL_PROFILE_ALIASES = Object.freeze({
|
|
31
|
+
gpt56_sol_xhigh: "gpt6_astra_xhigh",
|
|
32
|
+
gpt56_sol_max: "gpt6_astra_max",
|
|
33
|
+
gpt56_sol_ultra: "gpt6_astra_ultra",
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export function normalizeCodexProfileName(profileName) {
|
|
37
|
+
const normalized = String(profileName ?? "").trim();
|
|
38
|
+
return LEGACY_CANONICAL_PROFILE_ALIASES[normalized] || normalized;
|
|
39
|
+
}
|
|
40
|
+
|
|
30
41
|
function readProfileScalar(raw, key) {
|
|
31
42
|
const match = String(raw).match(
|
|
32
43
|
new RegExp(
|
|
@@ -64,32 +75,39 @@ export function resolveCodexProfileConfigValues(profileName, opts = {}) {
|
|
|
64
75
|
if (typeof profileName !== "string" || !profileName) {
|
|
65
76
|
return { model: null, effort: null };
|
|
66
77
|
}
|
|
78
|
+
const normalizedProfileName = normalizeCodexProfileName(profileName);
|
|
67
79
|
const codexHome =
|
|
68
80
|
opts.codexHome || process.env.CODEX_HOME || join(homedir(), ".codex");
|
|
69
81
|
let model = null;
|
|
70
82
|
let effort = null;
|
|
71
83
|
try {
|
|
72
84
|
const raw = readFileSync(
|
|
73
|
-
join(codexHome, `${
|
|
85
|
+
join(codexHome, `${normalizedProfileName}.config.toml`),
|
|
74
86
|
"utf8",
|
|
75
87
|
);
|
|
76
88
|
model = readProfileScalar(raw, "model");
|
|
77
89
|
effort = readProfileScalar(raw, "model_reasoning_effort");
|
|
78
90
|
} catch {
|
|
79
|
-
const suffix = /_(ultra|max|xhigh|high|med|medium|low)$/.exec(
|
|
91
|
+
const suffix = /_(ultra|max|xhigh|high|med|medium|low)$/.exec(
|
|
92
|
+
normalizedProfileName,
|
|
93
|
+
);
|
|
80
94
|
if (suffix) effort = EFFORT_BY_SUFFIX[suffix[1]] || null;
|
|
81
95
|
}
|
|
82
96
|
return { model, effort };
|
|
83
97
|
}
|
|
84
98
|
|
|
85
99
|
export function codexProfileConfigOverrides(profileName, opts = {}) {
|
|
86
|
-
|
|
100
|
+
const normalizedProfileName = normalizeCodexProfileName(profileName);
|
|
101
|
+
let { model, effort } = resolveCodexProfileConfigValues(
|
|
102
|
+
normalizedProfileName,
|
|
103
|
+
opts,
|
|
104
|
+
);
|
|
87
105
|
if (opts.enforceCanonicalProfile === true) {
|
|
88
|
-
const canonical = CANONICAL_PROFILE_VALUES[
|
|
106
|
+
const canonical = CANONICAL_PROFILE_VALUES[normalizedProfileName];
|
|
89
107
|
if (canonical) ({ model, effort } = canonical);
|
|
90
108
|
}
|
|
91
109
|
if (opts.disallowUltra === true && effort?.toLowerCase() === "ultra") {
|
|
92
|
-
model = "gpt-
|
|
110
|
+
model = "gpt-6-astra";
|
|
93
111
|
effort = "max";
|
|
94
112
|
}
|
|
95
113
|
const overrides = [];
|
|
@@ -8,9 +8,16 @@
|
|
|
8
8
|
# 4차 FALLBACK_FAILED → 모두 실패 시 marker + stderr_log 경로 출력
|
|
9
9
|
|
|
10
10
|
recover_codex_stdout() {
|
|
11
|
+
# 복구 진입 여부 플래그(전역, local 아님) — 아우터 no-op 가드가 읽는다.
|
|
12
|
+
# 0 = 진짜 stdout 존재(복구 미진입), 1 = stdout 이 애초에 비어 복구 진입함.
|
|
13
|
+
CODEX_STDOUT_WAS_RECOVERED=0
|
|
11
14
|
if [[ -s "$STDOUT_LOG" || ! -s "$STDERR_LOG" ]]; then
|
|
12
15
|
return 0
|
|
13
16
|
fi
|
|
17
|
+
# 여기 도달 = stdout 이 애초에 비어있었다. 아래에서 STDOUT_LOG 에 채워지는 내용은
|
|
18
|
+
# 전부 stderr 에서 복구된 것이지 워커의 진짜 산출물이 아니다. 이 플래그로 아우터
|
|
19
|
+
# 가드가 "복구된 stderr 노이즈"를 성공 출력으로 오인하지 않게 한다(#result-verification).
|
|
20
|
+
CODEX_STDOUT_WAS_RECOVERED=1
|
|
14
21
|
|
|
15
22
|
# 1차: codex marker
|
|
16
23
|
sed 's/\r$//' "$STDERR_LOG" \
|
|
@@ -48,3 +55,15 @@ recover_codex_stdout() {
|
|
|
48
55
|
echo "[tfx-route] 경고: codex stdout 비어있음, stderr 복구도 실패. 위 stderr_log 경로에서 raw codex 출력 확인 가능." >&2
|
|
49
56
|
fi
|
|
50
57
|
}
|
|
58
|
+
|
|
59
|
+
# ── MCP transport 채널 사망 판정 ──
|
|
60
|
+
# Codex MCP transport 채널이 실행 중 죽으면 codex exec 가 exit 0 + 빈 stdout 으로
|
|
61
|
+
# 끝나는 변형이 있다(래퍼 부재로 인한 pre-flight CODEX_MCP_TRANSPORT_EXIT_CODE=70
|
|
62
|
+
# 과 구분됨 — 이건 런타임 채널 사망). stderr 의 하드 크래시 서명으로 이 사망을
|
|
63
|
+
# 검출한다. 아우터 가드가 exit 0 이어도 결과를 실패로 승격하는 근거로 쓴다.
|
|
64
|
+
# 인자: [stderr_log] (미지정 시 $STDERR_LOG). 반환: 0=크래시 서명 있음, 1=없음.
|
|
65
|
+
codex_stdout_transport_crashed() {
|
|
66
|
+
local stderr_log="${1:-${STDERR_LOG:-}}"
|
|
67
|
+
[[ -n "$stderr_log" && -f "$stderr_log" ]] || return 1
|
|
68
|
+
grep -qE 'Transport channel closed|rmcp::transport worker quit with fatal' "$stderr_log" 2>/dev/null
|
|
69
|
+
}
|
|
@@ -8,48 +8,124 @@ import {
|
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
|
|
11
|
-
// 모델명 SSOT. 값은
|
|
11
|
+
// 모델명 SSOT. 값은 `agy models` 가 출력하는 display name 형식이다
|
|
12
12
|
// (`--model` 인자가 이 문자열을 받는다). 옛 `gemini-*-preview` ID 형식이 아님.
|
|
13
|
+
// 카탈로그 확인: agy 1.1.27 `agy models` (2026-09-07). 운영 정책은 3.8 Flash 한 세대만
|
|
14
|
+
// 쓰고 effort(Low/Medium/High)로 차등한다. Pro 와 이전 세대 Flash 는 기본 프로필에서 뺐다.
|
|
13
15
|
const DEFAULT_GEMINI_PROFILES = {
|
|
14
|
-
model: "Gemini 3.
|
|
16
|
+
model: "Gemini 3.8 Flash (Medium)",
|
|
15
17
|
profiles: {
|
|
16
|
-
|
|
17
|
-
model: "Gemini 3.
|
|
18
|
-
hint: "3.
|
|
18
|
+
flash38_low: {
|
|
19
|
+
model: "Gemini 3.8 Flash (Low)",
|
|
20
|
+
hint: "3.8 Flash (Low) — 경량·저비용",
|
|
19
21
|
},
|
|
20
|
-
|
|
21
|
-
model: "Gemini 3.
|
|
22
|
-
hint: "3.
|
|
22
|
+
flash38: {
|
|
23
|
+
model: "Gemini 3.8 Flash (Medium)",
|
|
24
|
+
hint: "3.8 Flash (Medium) — 기본, 비용·속도 균형",
|
|
23
25
|
},
|
|
24
|
-
|
|
25
|
-
model: "Gemini 3.
|
|
26
|
-
hint: "3.
|
|
27
|
-
},
|
|
28
|
-
pro31_low: {
|
|
29
|
-
model: "Gemini 3.1 Pro (Low)",
|
|
30
|
-
hint: "3.1 Pro (Low) — 플래그십 경량",
|
|
31
|
-
},
|
|
32
|
-
pro31: {
|
|
33
|
-
model: "Gemini 3.1 Pro (High)",
|
|
34
|
-
hint: "3.1 Pro (High) — 플래그십 최고 (긴 컨텍스트/복잡 추론)",
|
|
35
|
-
},
|
|
36
|
-
flash3: {
|
|
37
|
-
model: "Gemini 3 Flash",
|
|
38
|
-
hint: "3.0 Flash — 레거시 호환",
|
|
26
|
+
flash38_high: {
|
|
27
|
+
model: "Gemini 3.8 Flash (High)",
|
|
28
|
+
hint: "3.8 Flash (High) — 코드/추론 강화",
|
|
39
29
|
},
|
|
40
30
|
},
|
|
41
31
|
};
|
|
42
32
|
|
|
43
33
|
// 1회 마이그레이션 테이블: 옛 ID 형식 모델값 → agy display name.
|
|
44
|
-
// null =
|
|
34
|
+
// null = 카탈로그에서 빠진 모델(2.5 계열, 3 Flash) → 해당 프로필 제거 대상.
|
|
45
35
|
const LEGACY_MODEL_MIGRATION = {
|
|
46
36
|
"gemini-3.1-pro-preview": "Gemini 3.1 Pro (High)",
|
|
47
|
-
"gemini-3-flash-preview":
|
|
37
|
+
"gemini-3-flash-preview": null,
|
|
38
|
+
"Gemini 3 Flash": null,
|
|
48
39
|
"gemini-2.5-pro": null,
|
|
49
40
|
"gemini-2.5-flash": null,
|
|
50
41
|
"gemini-2.5-flash-lite": null,
|
|
51
42
|
};
|
|
52
|
-
|
|
43
|
+
// 과거 setup 이 자동 생성했던 프로필 이름. 정책상 더 쓰지 않으므로 제거한다.
|
|
44
|
+
// 사용자가 직접 이름 붙인 프로필은 건드리지 않는다.
|
|
45
|
+
const LEGACY_PROFILE_NAMES = [
|
|
46
|
+
"pro25",
|
|
47
|
+
"flash25",
|
|
48
|
+
"lite25",
|
|
49
|
+
"flash3",
|
|
50
|
+
"pro31",
|
|
51
|
+
"pro31_low",
|
|
52
|
+
"flash35",
|
|
53
|
+
"flash35_high",
|
|
54
|
+
"flash35_low",
|
|
55
|
+
];
|
|
56
|
+
// 과거 setup 이 자동 생성한 기본 model 값. 사용자가 고른 값이 아니므로 새 기본으로 올린다.
|
|
57
|
+
const LEGACY_DEFAULT_MODELS = new Set(["Gemini 3.5 Flash (Medium)"]);
|
|
58
|
+
|
|
59
|
+
// ── 용도별 effort (SSOT) ──
|
|
60
|
+
// 한 세대(3.8 Flash)만 쓰고 역할에 따라 effort 를 나눈다. 키는 tfx 에이전트/역할 이름
|
|
61
|
+
// (agent-map.json 의 에이전트, tfx multi --assign 의 role). bash(tfx-route.sh)와
|
|
62
|
+
// node(execution-mode, gemini-adapter, cli-agy)가 모두 이 표를 읽는다.
|
|
63
|
+
// High : 판단이 결과를 좌우하는 역할 (검토, 설계, 디버깅, 분석)
|
|
64
|
+
// Medium : 생성/작성/실행 (기본값)
|
|
65
|
+
// Low : 분류, 요약, 추출, 번역처럼 짧고 기계적인 작업
|
|
66
|
+
const GEMINI_PROFILE_BY_PURPOSE = Object.freeze({
|
|
67
|
+
designer: "flash38_high",
|
|
68
|
+
reviewer: "flash38_high",
|
|
69
|
+
"code-reviewer": "flash38_high",
|
|
70
|
+
"security-reviewer": "flash38_high",
|
|
71
|
+
"quality-reviewer": "flash38_high",
|
|
72
|
+
critic: "flash38_high",
|
|
73
|
+
architect: "flash38_high",
|
|
74
|
+
verifier: "flash38_high",
|
|
75
|
+
analyst: "flash38_high",
|
|
76
|
+
debugger: "flash38_high",
|
|
77
|
+
planner: "flash38_high",
|
|
78
|
+
scientist: "flash38_high",
|
|
79
|
+
"test-engineer": "flash38_high",
|
|
80
|
+
writer: "flash38",
|
|
81
|
+
executor: "flash38",
|
|
82
|
+
worker: "flash38",
|
|
83
|
+
"document-specialist": "flash38",
|
|
84
|
+
researcher: "flash38",
|
|
85
|
+
antigravity: "flash38",
|
|
86
|
+
agy: "flash38",
|
|
87
|
+
gemini: "flash38",
|
|
88
|
+
summarizer: "flash38_low",
|
|
89
|
+
classifier: "flash38_low",
|
|
90
|
+
triage: "flash38_low",
|
|
91
|
+
translator: "flash38_low",
|
|
92
|
+
formatter: "flash38_low",
|
|
93
|
+
explore: "flash38_low",
|
|
94
|
+
});
|
|
95
|
+
const GEMINI_DEFAULT_PURPOSE_PROFILE = "flash38";
|
|
96
|
+
|
|
97
|
+
function resolveGeminiProfileForPurpose(name) {
|
|
98
|
+
const key = String(name ?? "")
|
|
99
|
+
.trim()
|
|
100
|
+
.toLowerCase();
|
|
101
|
+
return GEMINI_PROFILE_BY_PURPOSE[key] || GEMINI_DEFAULT_PURPOSE_PROFILE;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 프로필 이름 → agy display name. 사용자 파일(~/.gemini/triflux-profiles.json)이
|
|
105
|
+
// 우선하고, 없으면 DEFAULT_GEMINI_PROFILES 로 떨어진다. 이미 모델명이면 그대로 돌려준다.
|
|
106
|
+
function resolveGeminiModel(profileOrModel, opts = {}) {
|
|
107
|
+
const raw = String(profileOrModel ?? "").trim();
|
|
108
|
+
if (!raw) return DEFAULT_GEMINI_PROFILES.model;
|
|
109
|
+
if (/^gemini[\s-]/i.test(raw)) return raw;
|
|
110
|
+
const profilesPath =
|
|
111
|
+
opts.profilesPath ||
|
|
112
|
+
process.env.GEMINI_PROFILES_PATH ||
|
|
113
|
+
join(homedir(), ".gemini", "triflux-profiles.json");
|
|
114
|
+
let cfg = null;
|
|
115
|
+
try {
|
|
116
|
+
cfg = JSON.parse(readFileSync(profilesPath, "utf8"));
|
|
117
|
+
} catch {
|
|
118
|
+
cfg = null;
|
|
119
|
+
}
|
|
120
|
+
const entry = cfg?.profiles?.[raw];
|
|
121
|
+
const fromFile = typeof entry === "string" ? entry : entry?.model;
|
|
122
|
+
if (typeof fromFile === "string" && fromFile.trim()) return fromFile.trim();
|
|
123
|
+
const fromDefault = DEFAULT_GEMINI_PROFILES.profiles[raw];
|
|
124
|
+
if (fromDefault) return fromDefault.model;
|
|
125
|
+
if (typeof cfg?.model === "string" && cfg.model.trim())
|
|
126
|
+
return cfg.model.trim();
|
|
127
|
+
return DEFAULT_GEMINI_PROFILES.model;
|
|
128
|
+
}
|
|
53
129
|
|
|
54
130
|
const DEFAULT_PROFILE_COUNT = Object.keys(
|
|
55
131
|
DEFAULT_GEMINI_PROFILES.profiles,
|
|
@@ -132,9 +208,10 @@ function ensureGeminiProfiles({
|
|
|
132
208
|
}
|
|
133
209
|
if (
|
|
134
210
|
typeof cfg.model === "string" &&
|
|
135
|
-
Object.hasOwn(LEGACY_MODEL_MIGRATION, cfg.model)
|
|
211
|
+
(Object.hasOwn(LEGACY_MODEL_MIGRATION, cfg.model) ||
|
|
212
|
+
LEGACY_DEFAULT_MODELS.has(cfg.model))
|
|
136
213
|
) {
|
|
137
|
-
// 옛 ID
|
|
214
|
+
// 옛 ID 형식/이전 세대 자동 생성 기본값은 새 기본(DEFAULT)으로 정규화한다.
|
|
138
215
|
cfg.model = DEFAULT_GEMINI_PROFILES.model;
|
|
139
216
|
migrated = true;
|
|
140
217
|
}
|
|
@@ -180,4 +257,11 @@ function ensureGeminiProfiles({
|
|
|
180
257
|
}
|
|
181
258
|
}
|
|
182
259
|
|
|
183
|
-
export {
|
|
260
|
+
export {
|
|
261
|
+
DEFAULT_GEMINI_PROFILES,
|
|
262
|
+
ensureGeminiProfiles,
|
|
263
|
+
GEMINI_DEFAULT_PURPOSE_PROFILE,
|
|
264
|
+
GEMINI_PROFILE_BY_PURPOSE,
|
|
265
|
+
resolveGeminiModel,
|
|
266
|
+
resolveGeminiProfileForPurpose,
|
|
267
|
+
};
|
|
@@ -69,6 +69,16 @@ function normalizeRule(rule) {
|
|
|
69
69
|
.map((s) => s.trim())
|
|
70
70
|
: [];
|
|
71
71
|
|
|
72
|
+
// platform: process.platform 화이트리스트 (빈 배열 = 전 플랫폼). Windows 전용
|
|
73
|
+
// 스킬(tfx-wt 등)이 macOS/Linux 프롬프트에 오탐하는 것을 막는다. 배열이 아닌
|
|
74
|
+
// 값은 규칙 자체를 무효로 본다.
|
|
75
|
+
if (rule.platform != null && !Array.isArray(rule.platform)) return null;
|
|
76
|
+
const platform = Array.isArray(rule.platform)
|
|
77
|
+
? rule.platform
|
|
78
|
+
.filter((s) => typeof s === "string" && s.trim())
|
|
79
|
+
.map((s) => s.trim())
|
|
80
|
+
: [];
|
|
81
|
+
|
|
72
82
|
return {
|
|
73
83
|
id: rule.id.trim(),
|
|
74
84
|
patterns,
|
|
@@ -81,6 +91,7 @@ function normalizeRule(rule) {
|
|
|
81
91
|
state,
|
|
82
92
|
mcp_route: mcpRoute,
|
|
83
93
|
repo_scope: repoScope,
|
|
94
|
+
platform,
|
|
84
95
|
explicit: rule.explicit === true,
|
|
85
96
|
};
|
|
86
97
|
}
|
|
@@ -160,6 +171,7 @@ export function matchRules(compiledRules, cleanText) {
|
|
|
160
171
|
state: rule.state || null,
|
|
161
172
|
mcp_route: rule.mcp_route || null,
|
|
162
173
|
repo_scope: rule.repo_scope || [],
|
|
174
|
+
platform: rule.platform || [],
|
|
163
175
|
explicit: rule.explicit === true,
|
|
164
176
|
});
|
|
165
177
|
}
|