@triflux/core 10.44.0 → 10.46.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/hud/renderers.mjs CHANGED
@@ -22,11 +22,9 @@ import {
22
22
  import {
23
23
  ACCOUNT_LABEL_WIDTH,
24
24
  FIVE_HOUR_MS,
25
- LEGACY_SV_ACCUMULATOR,
26
25
  ONE_DAY_MS,
27
26
  PROVIDER_PREFIX_WIDTH,
28
27
  SEVEN_DAY_MS,
29
- SV_ACCUMULATOR_PATH,
30
28
  TEAM_STATE_PATH,
31
29
  } from "./constants.mjs";
32
30
  import { buildContextUsageView } from "./context-monitor.mjs";
@@ -40,13 +38,11 @@ import {
40
38
  formatResetRemaining,
41
39
  formatResetRemainingDayHour,
42
40
  formatSavings,
43
- formatSvPct,
44
41
  formatTimeCell,
45
42
  formatTimeCellDH,
46
43
  formatTokenCount,
47
44
  padAnsiRight,
48
45
  readJson,
49
- readJsonMigrate,
50
46
  stripAnsi,
51
47
  truncateAnsi,
52
48
  } from "./utils.mjs";
@@ -88,25 +84,6 @@ export function readLatestBenchmarkDiff() {
88
84
  }
89
85
  }
90
86
 
91
- // 토큰 절약액 누적치 읽기 (tfx-auto token tracker)
92
- export function readTokenSavings() {
93
- const savingsPath = join(
94
- homedir(),
95
- ".omc",
96
- "state",
97
- "tfx-auto-tokens",
98
- "savings-total.json",
99
- );
100
- const data = readJson(savingsPath, null);
101
- if (!data || data.totalSaved === 0) return null;
102
- return data;
103
- }
104
-
105
- // sv-accumulator.json에서 누적 토큰/비용 읽기
106
- export function readSvAccumulator() {
107
- return readJsonMigrate(SV_ACCUMULATOR_PATH, LEGACY_SV_ACCUMULATOR, null);
108
- }
109
-
110
87
  /**
111
88
  * 파이프라인 벤치마크 diff 결과를 HUD 요약 문자열로 포맷
112
89
  */
@@ -279,9 +256,12 @@ export function getMicroLine(
279
256
  codexBuckets,
280
257
  geminiSession,
281
258
  geminiBucket,
282
- combinedSvPct,
283
259
  geminiMarker = "g",
260
+ options = {},
284
261
  ) {
262
+ // showCodex / showGemini 는 machine profile 의 TFX_DISABLE_* 게이트다. 기본값이
263
+ // true 라 8번째 인자를 주지 않는 기존 호출은 그대로 동작한다.
264
+ const { showCodex = true, showGemini = true } = options;
285
265
  const ctxView = contextView || buildContextUsageView({}, null);
286
266
  // Claude 5h/1w
287
267
  const cF =
@@ -325,34 +305,27 @@ export function getMicroLine(
325
305
  gVal = dim("--");
326
306
  }
327
307
 
328
- // sv
329
- const sv = formatSvPct(combinedSvPct).trim();
330
-
331
308
  const cols = getTerminalColumns() || 120;
332
- const line =
333
- `${bold(claudeOrange("c"))}${dim(":")}${cVal} ` +
334
- `${bold(codexWhite("x"))}${dim(":")}${xVal} ` +
335
- `${bold(geminiBlue(geminiMarker))}${dim(":")}${gVal} ` +
336
- `${dim("sv:")}${sv} ` +
337
- `${dim("CTX:")}${colorByPercent(ctxView.percent, ctxView.display)}`;
338
- return truncateAnsi(line, cols);
309
+ // 세그먼트를 모아 join 한다. 차단된 프로바이더를 뺄 때 공백이 겹치지 않는다.
310
+ const segments = [`${bold(claudeOrange("c"))}${dim(":")}${cVal}`];
311
+ if (showCodex) {
312
+ segments.push(`${bold(codexWhite("x"))}${dim(":")}${xVal}`);
313
+ }
314
+ if (showGemini) {
315
+ segments.push(`${bold(geminiBlue(geminiMarker))}${dim(":")}${gVal}`);
316
+ }
317
+ segments.push(
318
+ `${dim("CTX:")}${colorByPercent(ctxView.percent, ctxView.display)}`,
319
+ );
320
+ return truncateAnsi(segments.join(" "), cols);
339
321
  }
340
322
 
341
323
  // ============================================================================
342
324
  // Claude 행 렌더러
343
325
  // ============================================================================
344
- export function getClaudeRows(
345
- currentTier,
346
- contextView,
347
- claudeUsage,
348
- combinedSvPct,
349
- ) {
326
+ export function getClaudeRows(currentTier, contextView, claudeUsage) {
350
327
  const ctxView = contextView || buildContextUsageView({}, null);
351
328
  const prefix = `${bold(claudeOrange("c"))}:`;
352
- // 절약 퍼센트
353
- const svStr = formatSvPct(combinedSvPct);
354
- const svSuffix = `${dim("sv:")}${svStr}`;
355
-
356
329
  // API 실측 데이터
357
330
  const fiveHourPercent = claudeUsage?.fiveHourPercent ?? null;
358
331
  const weeklyPercent = claudeUsage?.weeklyPercent ?? null;
@@ -418,7 +391,7 @@ export function getClaudeRows(
418
391
  const warning = ctxView.warningTag
419
392
  ? ` ${dim("|")} ${yellow(ctxView.warningTag)}`
420
393
  : "";
421
- const contextSection = `${svSuffix} ${dim("|")} ${dim("CTX:")}${colorByPercent(ctxView.percent, ctxView.display)}${warning}`;
394
+ const contextSection = `${dim("CTX:")}${colorByPercent(ctxView.percent, ctxView.display)}${warning}`;
422
395
  return [{ prefix, left: quotaSection, right: contextSection }];
423
396
  }
424
397
 
@@ -428,7 +401,7 @@ export function getClaudeRows(
428
401
  const warning = ctxView.warningTag
429
402
  ? ` ${dim("|")} ${yellow(ctxView.warningTag)}`
430
403
  : "";
431
- const contextSection = `${svSuffix} ${dim("|")} ${dim("CTX:")}${colorByPercent(ctxView.percent, ctxView.display)}${warning}`;
404
+ const contextSection = `${dim("CTX:")}${colorByPercent(ctxView.percent, ctxView.display)}${warning}`;
432
405
  return [{ prefix, left: quotaSection, right: contextSection }];
433
406
  }
434
407
 
@@ -513,7 +486,6 @@ export function getProviderRow(
513
486
  accountsState,
514
487
  realQuota,
515
488
  codexEmail,
516
- savingsMultiplier,
517
489
  modelLabel,
518
490
  ) {
519
491
  const accountLabel = fitText(
@@ -521,10 +493,6 @@ export function getProviderRow(
521
493
  ACCOUNT_LABEL_WIDTH,
522
494
  );
523
495
 
524
- // 절약 퍼센트 섹션
525
- const svPct =
526
- savingsMultiplier != null ? Math.round(savingsMultiplier * 100) : null;
527
- const svStr = formatSvPct(svPct);
528
496
  const _modelLabelStr = modelLabel ? ` ${markerColor(modelLabel)}` : "";
529
497
 
530
498
  // 프로바이더별 색상 프로필
@@ -708,10 +676,7 @@ export function getProviderRow(
708
676
  quotaSection = `${dim("5h:")}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))} ${dim("1w:")}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCellDH("--d--h"))}`;
709
677
  }
710
678
  const prefix = `${bold(markerColor(`${marker}`))}:`;
711
- const compactRight = [
712
- svStr ? `${dim("sv:")}${svStr}` : "",
713
- accountLabel ? markerColor(accountLabel) : "",
714
- ]
679
+ const compactRight = [accountLabel ? markerColor(accountLabel) : ""]
715
680
  .filter(Boolean)
716
681
  .join(" ");
717
682
  return { prefix, left: quotaSection, right: compactRight };
@@ -800,11 +765,8 @@ export function getProviderRow(
800
765
 
801
766
  const prefix = `${bold(markerColor(`${marker}`))}:`;
802
767
  const accountSection = `${markerColor(accountLabel)}`;
803
- const svSection = svStr ? `${dim("sv:")}${svStr}` : "";
804
768
  const modelLabelSection = modelLabel ? markerColor(modelLabel) : "";
805
- const rightParts = [svSection, accountSection, modelLabelSection].filter(
806
- Boolean,
807
- );
769
+ const rightParts = [accountSection, modelLabelSection].filter(Boolean);
808
770
  return {
809
771
  prefix,
810
772
  left: quotaSection,
package/hud/utils.mjs CHANGED
@@ -5,11 +5,7 @@
5
5
  import { createHash } from "node:crypto";
6
6
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
7
7
  import { dirname } from "node:path";
8
- import {
9
- PERCENT_CELL_WIDTH,
10
- SV_CELL_WIDTH,
11
- TIME_CELL_INNER_WIDTH,
12
- } from "./constants.mjs";
8
+ import { PERCENT_CELL_WIDTH, TIME_CELL_INNER_WIDTH } from "./constants.mjs";
13
9
 
14
10
  export async function readStdinJson() {
15
11
  if (process.stdin.isTTY) return {};
@@ -338,16 +334,6 @@ export function createHttpsPost(https, timeoutMs) {
338
334
  };
339
335
  }
340
336
 
341
- // sv 퍼센트 포맷 (1000+ → k 표기, 5자 고정폭)
342
- export function formatSvPct(value) {
343
- if (value == null) return "--%".padStart(SV_CELL_WIDTH);
344
- if (value >= 10000)
345
- return `${Math.round(value / 1000)}k%`.padStart(SV_CELL_WIDTH);
346
- if (value >= 1000)
347
- return `${(value / 1000).toFixed(1)}k%`.padStart(SV_CELL_WIDTH);
348
- return `${value}%`.padStart(SV_CELL_WIDTH);
349
- }
350
-
351
337
  export function formatSavings(dollars) {
352
338
  if (dollars >= 100) return `$${Math.round(dollars)}`;
353
339
  if (dollars >= 10) return `$${dollars.toFixed(1)}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@triflux/core",
3
- "version": "10.44.0",
3
+ "version": "10.46.0",
4
4
  "description": "triflux core — CLI routing, pipeline, adapters. Zero native dependencies.",
5
5
  "type": "module",
6
6
  "main": "hub/index.mjs",
@@ -1,10 +1,13 @@
1
1
  // Canonical Codex role policy. Concrete model IDs stay in profile config files.
2
2
 
3
- import { resolveCodexProfileConfigValues } from "./codex-profile-config.mjs";
3
+ import {
4
+ normalizeCodexProfileName,
5
+ resolveCodexProfileConfigValues,
6
+ } from "./codex-profile-config.mjs";
4
7
 
5
8
  const policy = {
6
9
  executor: {
7
- profile: "gpt56_terra_high",
10
+ profile: "gpt6_sol_high",
8
11
  timeoutSec: 1080,
9
12
  runMode: "fg",
10
13
  opusOversight: false,
@@ -12,7 +15,7 @@ const policy = {
12
15
  subcommand: "exec",
13
16
  },
14
17
  codex: {
15
- profile: "gpt56_terra_high",
18
+ profile: "gpt6_sol_high",
16
19
  timeoutSec: 1080,
17
20
  runMode: "fg",
18
21
  opusOversight: false,
@@ -20,7 +23,7 @@ const policy = {
20
23
  subcommand: "exec",
21
24
  },
22
25
  "build-fixer": {
23
- profile: "gpt56_luna_low",
26
+ profile: "gpt6_luna_high",
24
27
  timeoutSec: 540,
25
28
  runMode: "fg",
26
29
  opusOversight: false,
@@ -28,7 +31,7 @@ const policy = {
28
31
  subcommand: "exec",
29
32
  },
30
33
  cleanup: {
31
- profile: "gpt56_terra_med",
34
+ profile: "gpt6_sol_med",
32
35
  timeoutSec: 540,
33
36
  runMode: "fg",
34
37
  opusOversight: false,
@@ -36,7 +39,7 @@ const policy = {
36
39
  subcommand: "exec",
37
40
  },
38
41
  deslop: {
39
- profile: "gpt56_terra_med",
42
+ profile: "gpt6_sol_med",
40
43
  timeoutSec: 540,
41
44
  runMode: "fg",
42
45
  opusOversight: false,
@@ -44,7 +47,7 @@ const policy = {
44
47
  subcommand: "exec",
45
48
  },
46
49
  debugger: {
47
- profile: "gpt56_sol_xhigh",
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: "gpt56_sol_xhigh",
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: "gpt56_sol_xhigh",
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: "gpt56_sol_xhigh",
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: "gpt56_sol_xhigh",
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: "gpt56_sol_xhigh",
90
+ profile: "gpt6_astra_xhigh",
88
91
  timeoutSec: 3600,
89
92
  runMode: "fg",
90
93
  opusOversight: true,
@@ -92,7 +95,7 @@ const policy = {
92
95
  subcommand: "exec",
93
96
  },
94
97
  "code-reviewer": {
95
- profile: "gpt56_terra_high",
98
+ profile: "gpt6_sol_high",
96
99
  timeoutSec: 1800,
97
100
  runMode: "bg",
98
101
  opusOversight: false,
@@ -100,7 +103,7 @@ const policy = {
100
103
  subcommand: "review",
101
104
  },
102
105
  "quality-reviewer": {
103
- profile: "gpt56_terra_high",
106
+ profile: "gpt6_sol_high",
104
107
  timeoutSec: 1800,
105
108
  runMode: "bg",
106
109
  opusOversight: false,
@@ -108,7 +111,7 @@ const policy = {
108
111
  subcommand: "review",
109
112
  },
110
113
  "security-reviewer": {
111
- profile: "gpt56_sol_xhigh",
114
+ profile: "gpt6_astra_xhigh",
112
115
  timeoutSec: 1800,
113
116
  runMode: "bg",
114
117
  opusOversight: true,
@@ -116,7 +119,7 @@ const policy = {
116
119
  subcommand: "review",
117
120
  },
118
121
  scientist: {
119
- profile: "gpt56_terra_high",
122
+ profile: "gpt6_sol_high",
120
123
  timeoutSec: 1440,
121
124
  runMode: "bg",
122
125
  opusOversight: false,
@@ -124,7 +127,7 @@ const policy = {
124
127
  subcommand: "exec",
125
128
  },
126
129
  "document-specialist": {
127
- profile: "gpt56_terra_high",
130
+ profile: "gpt6_sol_high",
128
131
  timeoutSec: 1440,
129
132
  runMode: "bg",
130
133
  opusOversight: false,
@@ -132,7 +135,7 @@ const policy = {
132
135
  subcommand: "exec",
133
136
  },
134
137
  "scientist-deep": {
135
- profile: "gpt56_sol_xhigh",
138
+ profile: "gpt6_astra_xhigh",
136
139
  timeoutSec: 3600,
137
140
  runMode: "bg",
138
141
  opusOversight: false,
@@ -140,7 +143,7 @@ const policy = {
140
143
  subcommand: "exec",
141
144
  },
142
145
  verifier: {
143
- profile: "gpt56_terra_high",
146
+ profile: "gpt6_sol_high",
144
147
  timeoutSec: 1200,
145
148
  runMode: "fg",
146
149
  opusOversight: false,
@@ -148,7 +151,7 @@ const policy = {
148
151
  subcommand: "review",
149
152
  },
150
153
  "test-engineer": {
151
- profile: "gpt56_terra_high",
154
+ profile: "gpt6_sol_high",
152
155
  timeoutSec: 1200,
153
156
  runMode: "bg",
154
157
  opusOversight: false,
@@ -156,7 +159,7 @@ const policy = {
156
159
  subcommand: "exec",
157
160
  },
158
161
  "qa-tester": {
159
- profile: "gpt56_terra_high",
162
+ profile: "gpt6_sol_high",
160
163
  timeoutSec: 1200,
161
164
  runMode: "bg",
162
165
  opusOversight: false,
@@ -164,7 +167,7 @@ const policy = {
164
167
  subcommand: "review",
165
168
  },
166
169
  spark: {
167
- profile: "gpt56_luna_low",
170
+ profile: "gpt6_luna_low",
168
171
  timeoutSec: 180,
169
172
  runMode: "fg",
170
173
  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: "gpt56_sol_xhigh",
180
+ profile: "gpt6_astra_xhigh",
178
181
  timeoutSec: 900,
179
182
  runMode: "bg",
180
183
  opusOversight: false,
@@ -182,7 +185,7 @@ const policy = {
182
185
  subcommand: "exec",
183
186
  },
184
187
  writer: {
185
- profile: "gpt56_luna_low",
188
+ profile: "gpt6_luna_high",
186
189
  timeoutSec: 900,
187
190
  runMode: "bg",
188
191
  opusOversight: false,
@@ -207,12 +210,13 @@ 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
- "gpt56_terra_med",
212
- "gpt56_terra_high",
213
- "gpt56_sol_xhigh",
214
- "gpt56_sol_max",
215
- "gpt56_sol_ultra",
213
+ "gpt6_luna_low",
214
+ "gpt6_luna_high",
215
+ "gpt6_sol_med",
216
+ "gpt6_sol_high",
217
+ "gpt6_astra_xhigh",
218
+ "gpt6_astra_max",
219
+ "gpt6_astra_ultra",
216
220
  ]),
217
221
  );
218
222
  export const ULTRA_ELIGIBLE_CODEX_AGENTS = Object.freeze(
@@ -222,8 +226,8 @@ export const ULTRA_ELIGIBLE_CODEX_AGENTS = Object.freeze(
222
226
  const NESTED_GLOBAL_PROFILE_OVERRIDES = new Set([
223
227
  "max",
224
228
  "ultra",
225
- "gpt56_sol_max",
226
- "gpt56_sol_ultra",
229
+ "gpt6_astra_max",
230
+ "gpt6_astra_ultra",
227
231
  ]);
228
232
 
229
233
  export function resolveCodexAgentPolicy(agent) {
@@ -251,14 +255,16 @@ export function resolveCodexAgentProfile(
251
255
  ) {
252
256
  const defaultProfile = resolveCodexAgentPolicy(agent).profile;
253
257
  const retryRequested = String(retryProfile ?? "").trim();
254
- const requested = retryRequested || String(profileOverride || "auto").trim();
258
+ const requested = normalizeCodexProfileName(
259
+ retryRequested || String(profileOverride || "auto").trim(),
260
+ );
255
261
  if (!requested || requested === "auto") return defaultProfile;
256
262
 
257
263
  const canonical =
258
264
  requested === "max"
259
- ? "gpt56_sol_max"
265
+ ? "gpt6_astra_max"
260
266
  : requested === "ultra"
261
- ? "gpt56_sol_ultra"
267
+ ? "gpt6_astra_ultra"
262
268
  : requested;
263
269
  if (!CANONICAL_CODEX_PROFILE_OVERRIDES.has(canonical)) {
264
270
  if (allowCustom) return requested;
@@ -266,9 +272,9 @@ export function resolveCodexAgentProfile(
266
272
  `[agent-route-policy] unsupported profile override: ${requested}`,
267
273
  );
268
274
  }
269
- if (canonical !== "gpt56_sol_ultra") return canonical;
275
+ if (canonical !== "gpt6_astra_ultra") return canonical;
270
276
  return nested || !ULTRA_ELIGIBLE_CODEX_AGENTS.has(agent)
271
- ? "gpt56_sol_max"
277
+ ? "gpt6_astra_max"
272
278
  : canonical;
273
279
  }
274
280
 
@@ -287,7 +293,9 @@ export function resolveNestedCodexAgentProfile(
287
293
  } = {},
288
294
  ) {
289
295
  const explicitProfile = String(profileOverride || "auto").trim();
290
- const globalOverride = String(globalProfile || "auto").trim();
296
+ const globalOverride = normalizeCodexProfileName(
297
+ String(globalProfile || "auto").trim(),
298
+ );
291
299
  // Headless lanes must select their role policy explicitly. Only the max and
292
300
  // ultra exception lanes may cross the process environment boundary.
293
301
  const effectiveOverride =
@@ -305,7 +313,7 @@ export function resolveNestedCodexAgentProfile(
305
313
  });
306
314
  const { effort } = resolveCodexProfileConfigValues(candidate, { codexHome });
307
315
  if (!effort) return fallback;
308
- return effort.toLowerCase() === "ultra" ? "gpt56_sol_max" : candidate;
316
+ return effort.toLowerCase() === "ultra" ? "gpt6_astra_max" : candidate;
309
317
  }
310
318
 
311
319
  export function describeCodexAgentPolicy() {
@@ -1,12 +1,18 @@
1
1
  // cli-agy.mjs — Antigravity (agy) CLI adapter (Phase 0 PoC).
2
2
  //
3
- // 책임: agy --print 는 stdin-only 이므로 plan() 에서 stdinMode="pipe" 시그널을 반환한다.
4
- // positional prompt 패턴은 환영 메시지 폴백을 유발하므로 사용 금지.
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
- args: ["--print", "--dangerously-skip-permissions"],
53
- stdinMode: "pipe",
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,
@@ -19,14 +19,39 @@ const EFFORT_BY_SUFFIX = {
19
19
  };
20
20
 
21
21
  const CANONICAL_PROFILE_VALUES = Object.freeze({
22
- gpt56_luna_low: { model: "gpt-5.6-luna", effort: "low" },
23
- gpt56_terra_med: { model: "gpt-5.6-terra", effort: "medium" },
24
- gpt56_terra_high: { model: "gpt-5.6-terra", effort: "high" },
25
- gpt56_sol_xhigh: { model: "gpt-5.6-sol", effort: "xhigh" },
26
- gpt56_sol_max: { model: "gpt-5.6-sol", effort: "max" },
27
- gpt56_sol_ultra: { model: "gpt-5.6-sol", effort: "ultra" },
22
+ gpt6_luna_low: { model: "gpt-6-luna", effort: "low" },
23
+ gpt6_luna_high: { model: "gpt-6-luna", effort: "high" },
24
+ gpt6_sol_med: { model: "gpt-6-sol", effort: "medium" },
25
+ gpt6_sol_high: { model: "gpt-6-sol", effort: "high" },
26
+ gpt6_astra_xhigh: { model: "gpt-6-astra", effort: "xhigh" },
27
+ gpt6_astra_max: { model: "gpt-6-astra", effort: "max" },
28
+ gpt6_astra_ultra: { model: "gpt-6-astra", effort: "ultra" },
28
29
  });
29
30
 
31
+ // GPT-5.6 lane names map to the GPT-6 lane with the same effort. GPT-6 has no
32
+ // Terra tier, so Terra lanes move to Sol.
33
+ const LEGACY_CANONICAL_PROFILE_ALIASES = Object.freeze({
34
+ gpt56_sol_xhigh: "gpt6_astra_xhigh",
35
+ gpt56_sol_max: "gpt6_astra_max",
36
+ gpt56_sol_ultra: "gpt6_astra_ultra",
37
+ gpt56_terra_high: "gpt6_sol_high",
38
+ gpt56_terra_med: "gpt6_sol_med",
39
+ gpt56_luna_low: "gpt6_luna_low",
40
+ });
41
+
42
+ export function normalizeCodexProfileName(profileName) {
43
+ const normalized = String(profileName ?? "").trim();
44
+ return LEGACY_CANONICAL_PROFILE_ALIASES[normalized] || normalized;
45
+ }
46
+
47
+ /** Model ID of a canonical profile (after legacy normalization), or null. */
48
+ export function canonicalCodexProfileModel(profileName) {
49
+ return (
50
+ CANONICAL_PROFILE_VALUES[normalizeCodexProfileName(profileName)]?.model ??
51
+ null
52
+ );
53
+ }
54
+
30
55
  function readProfileScalar(raw, key) {
31
56
  const match = String(raw).match(
32
57
  new RegExp(
@@ -40,7 +65,7 @@ function readProfileScalar(raw, key) {
40
65
  }
41
66
 
42
67
  /**
43
- * Resolve a Codex effort profile name (e.g. "gpt56_terra_high") to the `-c key=value`
68
+ * Resolve a Codex effort profile name (e.g. "gpt6_sol_high") to the `-c key=value`
44
69
  * config overrides that `codex exec` accepts, so the headless CLI lanes can
45
70
  * select a profile WITHOUT `codex exec --profile <name>`. codex 0.134+ rejects
46
71
  * `--profile X` whenever config.toml still contains an inline `[profiles.X]`
@@ -58,38 +83,45 @@ function readProfileScalar(raw, key) {
58
83
  *
59
84
  * @param {string} profileName
60
85
  * @param {{ codexHome?: string }} [opts]
61
- * @returns {string[]} e.g. ['model="gpt-5.6-terra"', 'model_reasoning_effort="high"']
86
+ * @returns {string[]} e.g. ['model="gpt-6-sol"', 'model_reasoning_effort="high"']
62
87
  */
63
88
  export function resolveCodexProfileConfigValues(profileName, opts = {}) {
64
89
  if (typeof profileName !== "string" || !profileName) {
65
90
  return { model: null, effort: null };
66
91
  }
92
+ const normalizedProfileName = normalizeCodexProfileName(profileName);
67
93
  const codexHome =
68
94
  opts.codexHome || process.env.CODEX_HOME || join(homedir(), ".codex");
69
95
  let model = null;
70
96
  let effort = null;
71
97
  try {
72
98
  const raw = readFileSync(
73
- join(codexHome, `${profileName}.config.toml`),
99
+ join(codexHome, `${normalizedProfileName}.config.toml`),
74
100
  "utf8",
75
101
  );
76
102
  model = readProfileScalar(raw, "model");
77
103
  effort = readProfileScalar(raw, "model_reasoning_effort");
78
104
  } catch {
79
- const suffix = /_(ultra|max|xhigh|high|med|medium|low)$/.exec(profileName);
105
+ const suffix = /_(ultra|max|xhigh|high|med|medium|low)$/.exec(
106
+ normalizedProfileName,
107
+ );
80
108
  if (suffix) effort = EFFORT_BY_SUFFIX[suffix[1]] || null;
81
109
  }
82
110
  return { model, effort };
83
111
  }
84
112
 
85
113
  export function codexProfileConfigOverrides(profileName, opts = {}) {
86
- let { model, effort } = resolveCodexProfileConfigValues(profileName, opts);
114
+ const normalizedProfileName = normalizeCodexProfileName(profileName);
115
+ let { model, effort } = resolveCodexProfileConfigValues(
116
+ normalizedProfileName,
117
+ opts,
118
+ );
87
119
  if (opts.enforceCanonicalProfile === true) {
88
- const canonical = CANONICAL_PROFILE_VALUES[profileName];
120
+ const canonical = CANONICAL_PROFILE_VALUES[normalizedProfileName];
89
121
  if (canonical) ({ model, effort } = canonical);
90
122
  }
91
123
  if (opts.disallowUltra === true && effort?.toLowerCase() === "ultra") {
92
- model = "gpt-5.6-sol";
124
+ model = "gpt-6-astra";
93
125
  effort = "max";
94
126
  }
95
127
  const overrides = [];