triflux 10.61.2 → 10.63.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/.claude/rules/tfx-routing.md +14 -9
- package/README.ko.md +11 -9
- package/README.md +11 -8
- package/adapters/codex/skills/tfx-live/SKILL.md +30 -5
- package/bin/tfx-live.mjs +591 -83
- package/bin/triflux.mjs +9 -2
- package/hub/team/claude-transcript.mjs +27 -0
- package/hub/team/retry-state-machine.mjs +16 -24
- package/hub/team/session-context.mjs +13 -2
- package/package.json +1 -1
- package/scripts/lib/agent-route-policy.mjs +40 -40
- package/scripts/lib/codex-profile-config.mjs +44 -33
- package/scripts/lib/gemini-profiles.mjs +112 -126
- package/scripts/lib/model-family-resolver.mjs +335 -0
- package/scripts/setup.mjs +21 -47
- package/scripts/tfx-route.sh +29 -98
- package/skills/tfx-auto/SKILL.md +1 -1
- package/skills/tfx-lead/SKILL.md +5 -5
- package/skills/tfx-live/SKILL.md +30 -5
- package/skills/tfx-setup/SKILL.md +1 -1
package/bin/triflux.mjs
CHANGED
|
@@ -67,6 +67,7 @@ import {
|
|
|
67
67
|
saveRegistry,
|
|
68
68
|
syncRegistryTargets,
|
|
69
69
|
} from "../scripts/lib/mcp-guard-engine.mjs";
|
|
70
|
+
import { resolveModelFamilies } from "../scripts/lib/model-family-resolver.mjs";
|
|
70
71
|
import {
|
|
71
72
|
formatPsmuxInstallGuidance,
|
|
72
73
|
formatPsmuxUpdateGuidance,
|
|
@@ -988,11 +989,17 @@ function previewCodexProfiles() {
|
|
|
988
989
|
? readFileSync(CODEX_CONFIG_PATH, "utf8")
|
|
989
990
|
: "";
|
|
990
991
|
const profiles = [];
|
|
992
|
+
const resolved = resolveModelFamilies("codex", {
|
|
993
|
+
codexHome: CODEX_DIR,
|
|
994
|
+
allowCommand: false,
|
|
995
|
+
persist: false,
|
|
996
|
+
}).profiles;
|
|
991
997
|
|
|
992
998
|
// Codex 0.134+: 프로필은 별도 파일 ~/.codex/<name>.config.toml 로 관리한다.
|
|
993
999
|
for (const profile of REQUIRED_CODEX_PROFILES) {
|
|
994
1000
|
const profilePath = join(CODEX_DIR, `${profile.name}.config.toml`);
|
|
995
|
-
const
|
|
1001
|
+
const { model, effort } = resolved[profile.name];
|
|
1002
|
+
const desiredContent = `model = "${model}"\nmodel_reasoning_effort = "${effort}"\n`;
|
|
996
1003
|
const existingContent = existsSync(profilePath)
|
|
997
1004
|
? readFileSync(profilePath, "utf8")
|
|
998
1005
|
: null;
|
|
@@ -1483,7 +1490,7 @@ async function cmdSetup(options = {}) {
|
|
|
1483
1490
|
"gemini-profiles",
|
|
1484
1491
|
`Antigravity/Gemini profiles 설정 실패: ${renderErrorMessage(gemini.message)}`,
|
|
1485
1492
|
);
|
|
1486
|
-
if (gemini.
|
|
1493
|
+
if (gemini.changed) report.alreadyInGoalState = false;
|
|
1487
1494
|
|
|
1488
1495
|
const settingsPath = join(CLAUDE_DIR, "settings.json");
|
|
1489
1496
|
const hudPath = join(CLAUDE_DIR, "hud", "hud-qos-status.mjs");
|
|
@@ -58,6 +58,28 @@ function beginTurn(state) {
|
|
|
58
58
|
function acceptEntry(state, entry, marker) {
|
|
59
59
|
if (entry?.isSidechain) return;
|
|
60
60
|
const message = entry?.message;
|
|
61
|
+
if (
|
|
62
|
+
entry?.type === "attachment" &&
|
|
63
|
+
entry.attachment?.type === "goal_status"
|
|
64
|
+
) {
|
|
65
|
+
const goal = entry.attachment;
|
|
66
|
+
const cleared = goal.met === true && goal.sentinel === true;
|
|
67
|
+
state.goal = {
|
|
68
|
+
state: cleared ? "none" : goal.met ? "complete" : "active",
|
|
69
|
+
objective: cleared ? null : goal.condition,
|
|
70
|
+
judgedBy: "evaluator",
|
|
71
|
+
...(goal.reason !== undefined ? { reason: goal.reason } : {}),
|
|
72
|
+
...(goal.tokens !== undefined ? { tokensUsed: goal.tokens } : {}),
|
|
73
|
+
updatedAt: entry.timestamp ?? null,
|
|
74
|
+
};
|
|
75
|
+
if (goal.sentinel === true) {
|
|
76
|
+
state.goalSentinel = {
|
|
77
|
+
...goal,
|
|
78
|
+
updatedAt: entry.timestamp,
|
|
79
|
+
count: (state.goalSentinel?.count ?? 0) + 1,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
61
83
|
if (entry?.type === "system" && entry.subtype === "compact_boundary") {
|
|
62
84
|
state.compactCount += 1;
|
|
63
85
|
state.compact = {
|
|
@@ -211,6 +233,8 @@ export async function readClaudeTranscript(transcriptPath, { requestId } = {}) {
|
|
|
211
233
|
measuredAt: null,
|
|
212
234
|
compactCount: 0,
|
|
213
235
|
compact: null,
|
|
236
|
+
goal: { state: "none", objective: null, judgedBy: "evaluator" },
|
|
237
|
+
goalSentinel: null,
|
|
214
238
|
};
|
|
215
239
|
}
|
|
216
240
|
try {
|
|
@@ -248,6 +272,7 @@ export async function readClaudeTranscript(transcriptPath, { requestId } = {}) {
|
|
|
248
272
|
model: state.model,
|
|
249
273
|
measuredAt: state.measuredAt,
|
|
250
274
|
},
|
|
275
|
+
enqueued: state.queuedContent !== null,
|
|
251
276
|
userSeen: state.userSeen,
|
|
252
277
|
response: state.response,
|
|
253
278
|
error: state.error,
|
|
@@ -255,5 +280,7 @@ export async function readClaudeTranscript(transcriptPath, { requestId } = {}) {
|
|
|
255
280
|
sectionClosed: state.sectionClosed,
|
|
256
281
|
compactCount: state.compactCount,
|
|
257
282
|
compact: state.compact,
|
|
283
|
+
goal: state.goal,
|
|
284
|
+
goalSentinel: state.goalSentinel,
|
|
258
285
|
};
|
|
259
286
|
}
|
|
@@ -23,10 +23,8 @@ import {
|
|
|
23
23
|
} from "node:fs";
|
|
24
24
|
import { dirname, join } from "node:path";
|
|
25
25
|
|
|
26
|
-
import {
|
|
27
|
-
|
|
28
|
-
normalizeCodexProfileName,
|
|
29
|
-
} from "../../scripts/lib/codex-profile-config.mjs";
|
|
26
|
+
import { normalizeCodexProfileName } from "../../scripts/lib/codex-profile-config.mjs";
|
|
27
|
+
import { CODEX_FAMILY_PROFILES } from "../../scripts/lib/model-family-resolver.mjs";
|
|
30
28
|
|
|
31
29
|
export const STATES = Object.freeze({
|
|
32
30
|
PLANNING: "PLANNING",
|
|
@@ -47,13 +45,12 @@ const ESCALATION_CHAIN_CONFIG_PATH = ".triflux/config/escalation-chain.json";
|
|
|
47
45
|
const DEFAULT_CLAUDE_MODEL = "fable";
|
|
48
46
|
|
|
49
47
|
// Escalation chain (2026-09-22 정책):
|
|
50
|
-
// 1. codex
|
|
48
|
+
// 1. codex astra_max: 최난도 단일 작업용 Codex escalation 단계
|
|
51
49
|
// 2. claude fable alias: Claude CLI가 최신 Fable tier로 해석하는 최종 수단
|
|
52
50
|
const DEFAULT_ESCALATION_CHAIN = Object.freeze([
|
|
53
51
|
Object.freeze({
|
|
54
52
|
cli: "codex",
|
|
55
|
-
|
|
56
|
-
profile: "gpt6_astra_max",
|
|
53
|
+
profile: "astra_max",
|
|
57
54
|
}),
|
|
58
55
|
Object.freeze({ cli: "claude", model: DEFAULT_CLAUDE_MODEL }),
|
|
59
56
|
]);
|
|
@@ -62,8 +59,8 @@ const STUCK_THRESHOLD = 3;
|
|
|
62
59
|
|
|
63
60
|
function normalizeEscalationProfile(profile) {
|
|
64
61
|
const normalized = normalizeCodexProfileName(profile);
|
|
65
|
-
return normalized === "ultra" || normalized === "
|
|
66
|
-
? "
|
|
62
|
+
return normalized === "ultra" || normalized === "astra_ultra"
|
|
63
|
+
? "astra_max"
|
|
67
64
|
: normalized;
|
|
68
65
|
}
|
|
69
66
|
|
|
@@ -307,26 +304,21 @@ function normalizeEscalationChainEntry(entry, index, source) {
|
|
|
307
304
|
`${source}: chain[${index}].cli must be a non-empty string`,
|
|
308
305
|
);
|
|
309
306
|
}
|
|
307
|
+
const profile = typeof entry.profile === "string" ? entry.profile.trim() : "";
|
|
308
|
+
const normalizedProfile = normalizeEscalationProfile(profile);
|
|
309
|
+
if (
|
|
310
|
+
entry.cli === "codex" &&
|
|
311
|
+
Object.hasOwn(CODEX_FAMILY_PROFILES, normalizedProfile)
|
|
312
|
+
) {
|
|
313
|
+
return { cli: entry.cli, profile: normalizedProfile };
|
|
314
|
+
}
|
|
310
315
|
if (typeof entry.model !== "string" || entry.model.trim() === "") {
|
|
311
316
|
throw new Error(
|
|
312
317
|
`${source}: chain[${index}].model must be a non-empty string`,
|
|
313
318
|
);
|
|
314
319
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
cli: entry.cli,
|
|
318
|
-
model: entry.model,
|
|
319
|
-
};
|
|
320
|
-
if (typeof entry.profile === "string" && entry.profile.trim() !== "") {
|
|
321
|
-
const profile = entry.profile.trim();
|
|
322
|
-
normalized.profile =
|
|
323
|
-
entry.cli === "codex" ? normalizeEscalationProfile(profile) : profile;
|
|
324
|
-
// A remapped legacy profile must not keep reporting its old model.
|
|
325
|
-
if (normalized.profile !== profile) {
|
|
326
|
-
normalized.model =
|
|
327
|
-
canonicalCodexProfileModel(normalized.profile) ?? normalized.model;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
320
|
+
const normalized = { cli: entry.cli, model: entry.model };
|
|
321
|
+
if (profile) normalized.profile = profile;
|
|
330
322
|
return normalized;
|
|
331
323
|
}
|
|
332
324
|
|
|
@@ -10,7 +10,7 @@ const MODEL_CONTEXT = [
|
|
|
10
10
|
{
|
|
11
11
|
cli: "claude",
|
|
12
12
|
model:
|
|
13
|
-
/^(?:claude-)?(?:fable
|
|
13
|
+
/^(?:claude-)?(?:fable|opus|sonnet|haiku)-(?:[5-9]|[1-9]\d+)(?:[.-]\d+)*(?:$|[-[])/i,
|
|
14
14
|
tokens: 1_000_000,
|
|
15
15
|
source:
|
|
16
16
|
"https://platform.claude.com/docs/en/build-with-claude/context-windows",
|
|
@@ -137,12 +137,19 @@ function acceptCodexLine(state, line) {
|
|
|
137
137
|
} catch {
|
|
138
138
|
return false;
|
|
139
139
|
}
|
|
140
|
+
if (entry?.type === "compacted") {
|
|
141
|
+
state.compactCount += 1;
|
|
142
|
+
state.compact = { preTokens: state.tokens, postTokens: null };
|
|
143
|
+
}
|
|
140
144
|
if (entry?.type === "turn_context")
|
|
141
145
|
state.model = entry.payload?.model || state.model;
|
|
142
146
|
if (entry?.type === "event_msg" && entry.payload?.type === "token_count") {
|
|
143
147
|
const info = entry.payload.info;
|
|
144
|
-
if (Number.isFinite(info?.last_token_usage?.total_tokens))
|
|
148
|
+
if (Number.isFinite(info?.last_token_usage?.total_tokens)) {
|
|
145
149
|
state.tokens = info.last_token_usage.total_tokens;
|
|
150
|
+
if (state.compact && state.compact.postTokens === null)
|
|
151
|
+
state.compact.postTokens = state.tokens;
|
|
152
|
+
}
|
|
146
153
|
if (Number.isFinite(info?.model_context_window))
|
|
147
154
|
state.executionContextLimitTokens = info.model_context_window;
|
|
148
155
|
}
|
|
@@ -172,6 +179,8 @@ export async function readCodexContext(rolloutPath, threadId) {
|
|
|
172
179
|
decoder: new StringDecoder("utf8"),
|
|
173
180
|
model: null,
|
|
174
181
|
tokens: null,
|
|
182
|
+
compactCount: 0,
|
|
183
|
+
compact: null,
|
|
175
184
|
executionContextLimitTokens: null,
|
|
176
185
|
};
|
|
177
186
|
}
|
|
@@ -199,6 +208,8 @@ export async function readCodexContext(rolloutPath, threadId) {
|
|
|
199
208
|
return {
|
|
200
209
|
...modelContext("codex", state.model, state.tokens),
|
|
201
210
|
model: state.model,
|
|
211
|
+
compactCount: state.compactCount,
|
|
212
|
+
compact: state.compact,
|
|
202
213
|
executionContextLimitTokens: state.executionContextLimitTokens,
|
|
203
214
|
};
|
|
204
215
|
}
|
package/package.json
CHANGED
|
@@ -10,127 +10,127 @@ import { resolveGeminiProfileForPurpose } from "./gemini-profiles.mjs";
|
|
|
10
10
|
|
|
11
11
|
const policy = {
|
|
12
12
|
executor: {
|
|
13
|
-
profile: "
|
|
13
|
+
profile: "sol_high",
|
|
14
14
|
timeoutSec: 1080,
|
|
15
15
|
mcpHint: "implement",
|
|
16
16
|
subcommand: "exec",
|
|
17
17
|
},
|
|
18
18
|
codex: {
|
|
19
|
-
profile: "
|
|
19
|
+
profile: "sol_high",
|
|
20
20
|
timeoutSec: 1080,
|
|
21
21
|
mcpHint: "implement",
|
|
22
22
|
subcommand: "exec",
|
|
23
23
|
},
|
|
24
24
|
"build-fixer": {
|
|
25
|
-
profile: "
|
|
25
|
+
profile: "luna_high",
|
|
26
26
|
timeoutSec: 540,
|
|
27
27
|
mcpHint: "implement",
|
|
28
28
|
subcommand: "exec",
|
|
29
29
|
},
|
|
30
30
|
cleanup: {
|
|
31
|
-
profile: "
|
|
31
|
+
profile: "sol_med",
|
|
32
32
|
timeoutSec: 540,
|
|
33
33
|
mcpHint: "implement",
|
|
34
34
|
subcommand: "exec",
|
|
35
35
|
},
|
|
36
36
|
deslop: {
|
|
37
|
-
profile: "
|
|
37
|
+
profile: "sol_med",
|
|
38
38
|
timeoutSec: 540,
|
|
39
39
|
mcpHint: "implement",
|
|
40
40
|
subcommand: "exec",
|
|
41
41
|
},
|
|
42
42
|
debugger: {
|
|
43
|
-
profile: "
|
|
43
|
+
profile: "astra_xhigh",
|
|
44
44
|
timeoutSec: 900,
|
|
45
45
|
mcpHint: "implement",
|
|
46
46
|
subcommand: "exec",
|
|
47
47
|
},
|
|
48
48
|
"deep-executor": {
|
|
49
|
-
profile: "
|
|
49
|
+
profile: "astra_xhigh",
|
|
50
50
|
timeoutSec: 3600,
|
|
51
51
|
mcpHint: "implement",
|
|
52
52
|
subcommand: "exec",
|
|
53
53
|
},
|
|
54
54
|
architect: {
|
|
55
|
-
profile: "
|
|
55
|
+
profile: "astra_xhigh",
|
|
56
56
|
timeoutSec: 3600,
|
|
57
57
|
mcpHint: "analyze",
|
|
58
58
|
subcommand: "exec",
|
|
59
59
|
},
|
|
60
60
|
critic: {
|
|
61
|
-
profile: "
|
|
61
|
+
profile: "astra_xhigh",
|
|
62
62
|
timeoutSec: 3600,
|
|
63
63
|
mcpHint: "analyze",
|
|
64
64
|
subcommand: "exec",
|
|
65
65
|
},
|
|
66
66
|
planner: {
|
|
67
|
-
profile: "
|
|
67
|
+
profile: "astra_xhigh",
|
|
68
68
|
timeoutSec: 3600,
|
|
69
69
|
mcpHint: "analyze",
|
|
70
70
|
subcommand: "exec",
|
|
71
71
|
},
|
|
72
72
|
analyst: {
|
|
73
|
-
profile: "
|
|
73
|
+
profile: "astra_xhigh",
|
|
74
74
|
timeoutSec: 3600,
|
|
75
75
|
mcpHint: "analyze",
|
|
76
76
|
subcommand: "exec",
|
|
77
77
|
},
|
|
78
78
|
"code-reviewer": {
|
|
79
|
-
profile: "
|
|
79
|
+
profile: "sol_high",
|
|
80
80
|
timeoutSec: 1800,
|
|
81
81
|
mcpHint: "review",
|
|
82
82
|
subcommand: "review",
|
|
83
83
|
},
|
|
84
84
|
"quality-reviewer": {
|
|
85
|
-
profile: "
|
|
85
|
+
profile: "sol_high",
|
|
86
86
|
timeoutSec: 1800,
|
|
87
87
|
mcpHint: "review",
|
|
88
88
|
subcommand: "review",
|
|
89
89
|
},
|
|
90
90
|
"security-reviewer": {
|
|
91
|
-
profile: "
|
|
91
|
+
profile: "astra_xhigh",
|
|
92
92
|
timeoutSec: 1800,
|
|
93
93
|
mcpHint: "review",
|
|
94
94
|
subcommand: "review",
|
|
95
95
|
},
|
|
96
96
|
scientist: {
|
|
97
|
-
profile: "
|
|
97
|
+
profile: "sol_high",
|
|
98
98
|
timeoutSec: 1440,
|
|
99
99
|
mcpHint: "analyze",
|
|
100
100
|
subcommand: "exec",
|
|
101
101
|
},
|
|
102
102
|
"document-specialist": {
|
|
103
|
-
profile: "
|
|
103
|
+
profile: "sol_high",
|
|
104
104
|
timeoutSec: 1440,
|
|
105
105
|
mcpHint: "analyze",
|
|
106
106
|
subcommand: "exec",
|
|
107
107
|
},
|
|
108
108
|
"scientist-deep": {
|
|
109
|
-
profile: "
|
|
109
|
+
profile: "astra_xhigh",
|
|
110
110
|
timeoutSec: 3600,
|
|
111
111
|
mcpHint: "analyze",
|
|
112
112
|
subcommand: "exec",
|
|
113
113
|
},
|
|
114
114
|
verifier: {
|
|
115
|
-
profile: "
|
|
115
|
+
profile: "sol_high",
|
|
116
116
|
timeoutSec: 1200,
|
|
117
117
|
mcpHint: "review",
|
|
118
118
|
subcommand: "review",
|
|
119
119
|
},
|
|
120
120
|
"test-engineer": {
|
|
121
|
-
profile: "
|
|
121
|
+
profile: "sol_high",
|
|
122
122
|
timeoutSec: 1200,
|
|
123
123
|
mcpHint: "implement",
|
|
124
124
|
subcommand: "exec",
|
|
125
125
|
},
|
|
126
126
|
"qa-tester": {
|
|
127
|
-
profile: "
|
|
127
|
+
profile: "sol_high",
|
|
128
128
|
timeoutSec: 1200,
|
|
129
129
|
mcpHint: "review",
|
|
130
130
|
subcommand: "review",
|
|
131
131
|
},
|
|
132
132
|
spark: {
|
|
133
|
-
profile: "
|
|
133
|
+
profile: "luna_low",
|
|
134
134
|
timeoutSec: 180,
|
|
135
135
|
mcpHint: "implement",
|
|
136
136
|
subcommand: "exec",
|
|
@@ -138,13 +138,13 @@ const policy = {
|
|
|
138
138
|
// These entries are only for explicit direct-Codex overrides. agent-map.json
|
|
139
139
|
// remains the provider-selection authority for their normal execution.
|
|
140
140
|
designer: {
|
|
141
|
-
profile: "
|
|
141
|
+
profile: "astra_xhigh",
|
|
142
142
|
timeoutSec: 900,
|
|
143
143
|
mcpHint: "implement",
|
|
144
144
|
subcommand: "exec",
|
|
145
145
|
},
|
|
146
146
|
writer: {
|
|
147
|
-
profile: "
|
|
147
|
+
profile: "luna_high",
|
|
148
148
|
timeoutSec: 900,
|
|
149
149
|
mcpHint: "implement",
|
|
150
150
|
subcommand: "exec",
|
|
@@ -167,13 +167,13 @@ const DEFAULT_CODEX_AGENT = "executor";
|
|
|
167
167
|
|
|
168
168
|
const CANONICAL_CODEX_PROFILE_OVERRIDES = Object.freeze(
|
|
169
169
|
new Set([
|
|
170
|
-
"
|
|
171
|
-
"
|
|
172
|
-
"
|
|
173
|
-
"
|
|
174
|
-
"
|
|
175
|
-
"
|
|
176
|
-
"
|
|
170
|
+
"luna_low",
|
|
171
|
+
"luna_high",
|
|
172
|
+
"sol_med",
|
|
173
|
+
"sol_high",
|
|
174
|
+
"astra_xhigh",
|
|
175
|
+
"astra_max",
|
|
176
|
+
"astra_ultra",
|
|
177
177
|
]),
|
|
178
178
|
);
|
|
179
179
|
const ULTRA_ELIGIBLE_CODEX_AGENTS = Object.freeze(
|
|
@@ -183,8 +183,8 @@ const ULTRA_ELIGIBLE_CODEX_AGENTS = Object.freeze(
|
|
|
183
183
|
const NESTED_GLOBAL_PROFILE_OVERRIDES = new Set([
|
|
184
184
|
"max",
|
|
185
185
|
"ultra",
|
|
186
|
-
"
|
|
187
|
-
"
|
|
186
|
+
"astra_max",
|
|
187
|
+
"astra_ultra",
|
|
188
188
|
]);
|
|
189
189
|
|
|
190
190
|
export function resolveCodexAgentPolicy(agent) {
|
|
@@ -219,9 +219,9 @@ export function resolveCodexAgentProfile(
|
|
|
219
219
|
|
|
220
220
|
const canonical =
|
|
221
221
|
requested === "max"
|
|
222
|
-
? "
|
|
222
|
+
? "astra_max"
|
|
223
223
|
: requested === "ultra"
|
|
224
|
-
? "
|
|
224
|
+
? "astra_ultra"
|
|
225
225
|
: requested;
|
|
226
226
|
if (!CANONICAL_CODEX_PROFILE_OVERRIDES.has(canonical)) {
|
|
227
227
|
if (allowCustom) return requested;
|
|
@@ -229,9 +229,9 @@ export function resolveCodexAgentProfile(
|
|
|
229
229
|
`[agent-route-policy] unsupported profile override: ${requested}`,
|
|
230
230
|
);
|
|
231
231
|
}
|
|
232
|
-
if (canonical !== "
|
|
232
|
+
if (canonical !== "astra_ultra") return canonical;
|
|
233
233
|
return nested || !ULTRA_ELIGIBLE_CODEX_AGENTS.has(agent)
|
|
234
|
-
? "
|
|
234
|
+
? "astra_max"
|
|
235
235
|
: canonical;
|
|
236
236
|
}
|
|
237
237
|
|
|
@@ -270,7 +270,7 @@ export function resolveNestedCodexAgentProfile(
|
|
|
270
270
|
});
|
|
271
271
|
const { effort } = resolveCodexProfileConfigValues(candidate, { codexHome });
|
|
272
272
|
if (!effort) return fallback;
|
|
273
|
-
return effort.toLowerCase() === "ultra" ? "
|
|
273
|
+
return effort.toLowerCase() === "ultra" ? "astra_max" : candidate;
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
const EXPECTED_DURATION_BY_AGENT = Object.freeze({
|
|
@@ -408,13 +408,13 @@ async function runCli() {
|
|
|
408
408
|
const module = await import(pathToFileURL(geminiProfilesModule).href);
|
|
409
409
|
resolver = (agent) => {
|
|
410
410
|
try {
|
|
411
|
-
return module.resolveGeminiProfileForPurpose(agent) || "
|
|
411
|
+
return module.resolveGeminiProfileForPurpose(agent) || "flash";
|
|
412
412
|
} catch {
|
|
413
|
-
return "
|
|
413
|
+
return "flash";
|
|
414
414
|
}
|
|
415
415
|
};
|
|
416
416
|
} catch {
|
|
417
|
-
resolver = () => "
|
|
417
|
+
resolver = () => "flash";
|
|
418
418
|
}
|
|
419
419
|
}
|
|
420
420
|
process.stdout.write(
|
|
@@ -9,6 +9,12 @@ import {
|
|
|
9
9
|
import { homedir } from "node:os";
|
|
10
10
|
import { dirname, join } from "node:path";
|
|
11
11
|
import { writeRotatedBackup } from "./backup-rotation.mjs";
|
|
12
|
+
import {
|
|
13
|
+
CODEX_FAMILY_PROFILES,
|
|
14
|
+
modelFamilyPaths,
|
|
15
|
+
resolveModelFamilies,
|
|
16
|
+
writeModelFile,
|
|
17
|
+
} from "./model-family-resolver.mjs";
|
|
12
18
|
|
|
13
19
|
const EFFORT_BY_SUFFIX = {
|
|
14
20
|
ultra: "ultra",
|
|
@@ -20,41 +26,38 @@ const EFFORT_BY_SUFFIX = {
|
|
|
20
26
|
low: "low",
|
|
21
27
|
};
|
|
22
28
|
|
|
23
|
-
const CANONICAL_PROFILE_VALUES = Object.freeze({
|
|
24
|
-
gpt6_luna_low: { model: "gpt-6-luna", effort: "low" },
|
|
25
|
-
gpt6_luna_high: { model: "gpt-6-luna", effort: "high" },
|
|
26
|
-
gpt61_sol_med: { model: "gpt-6.1-sol", effort: "medium" },
|
|
27
|
-
gpt61_sol_high: { model: "gpt-6.1-sol", effort: "high" },
|
|
28
|
-
gpt6_astra_xhigh: { model: "gpt-6-astra", effort: "xhigh" },
|
|
29
|
-
gpt6_astra_max: { model: "gpt-6-astra", effort: "max" },
|
|
30
|
-
gpt6_astra_ultra: { model: "gpt-6-astra", effort: "ultra" },
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
// GPT-5.6 lane names map to the GPT-6 lane with the same effort. GPT-6 has no
|
|
34
|
-
// Terra tier, so Terra lanes move to Sol. GPT-6 Sol lane names map to GPT-6.1
|
|
35
|
-
// Sol with the same effort.
|
|
36
29
|
const LEGACY_CANONICAL_PROFILE_ALIASES = Object.freeze({
|
|
37
|
-
gpt56_sol_xhigh: "
|
|
38
|
-
gpt56_sol_max: "
|
|
39
|
-
gpt56_sol_ultra: "
|
|
40
|
-
gpt56_terra_high: "
|
|
41
|
-
gpt56_terra_med: "
|
|
42
|
-
gpt56_luna_low: "gpt6_luna_low",
|
|
43
|
-
gpt6_sol_high: "gpt61_sol_high",
|
|
44
|
-
gpt6_sol_med: "gpt61_sol_med",
|
|
30
|
+
gpt56_sol_xhigh: "astra_xhigh",
|
|
31
|
+
gpt56_sol_max: "astra_max",
|
|
32
|
+
gpt56_sol_ultra: "astra_ultra",
|
|
33
|
+
gpt56_terra_high: "sol_high",
|
|
34
|
+
gpt56_terra_med: "sol_med",
|
|
45
35
|
});
|
|
46
36
|
|
|
47
37
|
export function normalizeCodexProfileName(profileName) {
|
|
48
38
|
const normalized = String(profileName ?? "").trim();
|
|
49
|
-
|
|
39
|
+
const legacy = LEGACY_CANONICAL_PROFILE_ALIASES[normalized];
|
|
40
|
+
if (legacy) return legacy;
|
|
41
|
+
const familyName = normalized.replace(/^gpt\d+_(sol|astra|luna)_/, "$1_");
|
|
42
|
+
return Object.hasOwn(CODEX_FAMILY_PROFILES, familyName)
|
|
43
|
+
? familyName
|
|
44
|
+
: normalized;
|
|
50
45
|
}
|
|
51
46
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
47
|
+
export function ensureManagedCodexProfiles(opts = {}) {
|
|
48
|
+
const { codexHome } = modelFamilyPaths(opts);
|
|
49
|
+
const { profiles } = resolveModelFamilies("codex", opts);
|
|
50
|
+
let changed = 0;
|
|
51
|
+
for (const [name, { model, effort }] of Object.entries(profiles)) {
|
|
52
|
+
if (
|
|
53
|
+
writeModelFile(
|
|
54
|
+
join(codexHome, `${name}.config.toml`),
|
|
55
|
+
`model = "${model}"\nmodel_reasoning_effort = "${effort}"\n`,
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
changed++;
|
|
59
|
+
}
|
|
60
|
+
return { changed, profiles };
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
function readProfileScalar(raw, key) {
|
|
@@ -70,7 +73,7 @@ function readProfileScalar(raw, key) {
|
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
/**
|
|
73
|
-
* Resolve a Codex effort profile name (e.g. "
|
|
76
|
+
* Resolve a Codex effort profile name (e.g. "sol_high") to the `-c key=value`
|
|
74
77
|
* config overrides that `codex exec` accepts, so the headless CLI lanes can
|
|
75
78
|
* select a profile WITHOUT `codex exec --profile <name>`. codex 0.134+ rejects
|
|
76
79
|
* `--profile X` whenever config.toml still contains an inline `[profiles.X]`
|
|
@@ -118,12 +121,20 @@ export function codexProfileConfigOverrides(profileName, opts = {}) {
|
|
|
118
121
|
opts,
|
|
119
122
|
);
|
|
120
123
|
if (opts.enforceCanonicalProfile === true) {
|
|
121
|
-
|
|
122
|
-
|
|
124
|
+
if (Object.hasOwn(CODEX_FAMILY_PROFILES, normalizedProfileName)) {
|
|
125
|
+
({ model, effort } = resolveModelFamilies("codex", {
|
|
126
|
+
...opts,
|
|
127
|
+
allowCommand: false,
|
|
128
|
+
persist: false,
|
|
129
|
+
}).profiles[normalizedProfileName]);
|
|
130
|
+
}
|
|
123
131
|
}
|
|
124
132
|
if (opts.disallowUltra === true && effort?.toLowerCase() === "ultra") {
|
|
125
|
-
model = "
|
|
126
|
-
|
|
133
|
+
({ model, effort } = resolveModelFamilies("codex", {
|
|
134
|
+
...opts,
|
|
135
|
+
allowCommand: false,
|
|
136
|
+
persist: false,
|
|
137
|
+
}).profiles.astra_max);
|
|
127
138
|
}
|
|
128
139
|
const overrides = [];
|
|
129
140
|
if (model) overrides.push(`model="${model}"`);
|