@triflux/remote 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.
Files changed (40) hide show
  1. package/hub/pipe.mjs +10 -15
  2. package/hub/schema.sql +1 -0
  3. package/hub/server.mjs +172 -93
  4. package/hub/store.mjs +127 -61
  5. package/hub/team/backend.mjs +4 -2
  6. package/hub/team/claude-daemon-control.mjs +2 -0
  7. package/hub/team/cli/commands/start/start-headless.mjs +2 -0
  8. package/hub/team/cli/commands/start/start-mux.mjs +24 -3
  9. package/hub/team/cli/services/runtime-mode.mjs +23 -9
  10. package/hub/team/conductor.mjs +80 -24
  11. package/hub/team/cto-auto-collect.mjs +2 -6
  12. package/hub/team/cto-pull.mjs +4 -0
  13. package/hub/team/dashboard-open.mjs +1 -0
  14. package/hub/team/execution-mode.mjs +38 -2
  15. package/hub/team/headless.mjs +493 -82
  16. package/hub/team/interactive-tui-transport.mjs +15 -1
  17. package/hub/team/launcher-template.mjs +17 -1
  18. package/hub/team/orchestrator.mjs +36 -9
  19. package/hub/team/pane.mjs +176 -14
  20. package/hub/team/psmux.mjs +79 -70
  21. package/hub/team/retry-state-machine.mjs +47 -16
  22. package/hub/team/session.mjs +57 -0
  23. package/hub/team/synapse-http.mjs +149 -4
  24. package/hub/team/terminal-opener.mjs +68 -13
  25. package/hub/team/tui-viewer.mjs +65 -14
  26. package/hub/team/uds-orchestrator.mjs +384 -83
  27. package/hub/tools.mjs +10 -1
  28. package/hub/workers/codex-mcp.mjs +9 -4
  29. package/hub/workers/delegator-mcp.mjs +1 -1
  30. package/package.json +1 -1
  31. package/scripts/lib/agent-route-policy.mjs +99 -38
  32. package/scripts/lib/cli-agy.mjs +24 -4
  33. package/scripts/lib/cli-codex.mjs +2 -0
  34. package/scripts/lib/codex-profile-config.mjs +45 -13
  35. package/scripts/lib/codex-recovery.sh +19 -0
  36. package/scripts/lib/gemini-profiles.mjs +113 -29
  37. package/scripts/lib/keyword-rules.mjs +12 -0
  38. package/scripts/lib/logger.mjs +60 -43
  39. package/scripts/lib/machine-profile.mjs +293 -0
  40. package/scripts/lib/mcp-health.mjs +44 -18
@@ -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,15 +210,24 @@ 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", "gpt56_terra_med", "gpt56_terra_high", "gpt56_sol_xhigh",
211
- "gpt56_sol_max", "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",
212
220
  ]),
213
221
  );
214
222
  export const ULTRA_ELIGIBLE_CODEX_AGENTS = Object.freeze(
215
223
  new Set(["deep-executor", "scientist-deep"]),
216
224
  );
225
+
217
226
  const NESTED_GLOBAL_PROFILE_OVERRIDES = new Set([
218
- "max", "ultra", "gpt56_sol_max", "gpt56_sol_ultra",
227
+ "max",
228
+ "ultra",
229
+ "gpt6_astra_max",
230
+ "gpt6_astra_ultra",
219
231
  ]);
220
232
 
221
233
  export function resolveCodexAgentPolicy(agent) {
@@ -224,35 +236,84 @@ export function resolveCodexAgentPolicy(agent) {
224
236
  );
225
237
  }
226
238
 
239
+ /**
240
+ * Resolves a requested Codex profile against the canonical role policy.
241
+ *
242
+ * `retryProfile` intentionally wins over `profileOverride`: retry snapshots
243
+ * represent an already-selected escalation step and must not be replaced by a
244
+ * later process-level `TFX_CODEX_PROFILE`. Ultra is reserved for an eligible
245
+ * top-level role; every nested/sandboxed or ineligible request is max.
246
+ */
227
247
  export function resolveCodexAgentProfile(
228
248
  agent,
229
- { profileOverride = "auto", retryProfile = null, nested = false, allowCustom = false } = {},
249
+ {
250
+ profileOverride = "auto",
251
+ retryProfile = null,
252
+ nested = false,
253
+ allowCustom = false,
254
+ } = {},
230
255
  ) {
231
256
  const defaultProfile = resolveCodexAgentPolicy(agent).profile;
232
257
  const retryRequested = String(retryProfile ?? "").trim();
233
- const requested = retryRequested || String(profileOverride || "auto").trim();
258
+ const requested = normalizeCodexProfileName(
259
+ retryRequested || String(profileOverride || "auto").trim(),
260
+ );
234
261
  if (!requested || requested === "auto") return defaultProfile;
235
- const canonical = requested === "max" ? "gpt56_sol_max" : requested === "ultra" ? "gpt56_sol_ultra" : requested;
262
+
263
+ const canonical =
264
+ requested === "max"
265
+ ? "gpt6_astra_max"
266
+ : requested === "ultra"
267
+ ? "gpt6_astra_ultra"
268
+ : requested;
236
269
  if (!CANONICAL_CODEX_PROFILE_OVERRIDES.has(canonical)) {
237
270
  if (allowCustom) return requested;
238
- throw new Error(`[agent-route-policy] unsupported profile override: ${requested}`);
271
+ throw new Error(
272
+ `[agent-route-policy] unsupported profile override: ${requested}`,
273
+ );
239
274
  }
240
- if (canonical !== "gpt56_sol_ultra") return canonical;
241
- return nested || !ULTRA_ELIGIBLE_CODEX_AGENTS.has(agent) ? "gpt56_sol_max" : canonical;
275
+ if (canonical !== "gpt6_astra_ultra") return canonical;
276
+ return nested || !ULTRA_ELIGIBLE_CODEX_AGENTS.has(agent)
277
+ ? "gpt6_astra_max"
278
+ : canonical;
242
279
  }
243
280
 
281
+ /**
282
+ * Nested headless, team, worker, and delegator launches always select an
283
+ * explicit role profile rather than inheriting the global Codex config. A
284
+ * custom profile that resolves to ultra is also downgraded to max.
285
+ */
244
286
  export function resolveNestedCodexAgentProfile(
245
287
  agent,
246
- { profileOverride = "auto", globalProfile = "auto", retryProfile = null, codexHome } = {},
288
+ {
289
+ profileOverride = "auto",
290
+ globalProfile = "auto",
291
+ retryProfile = null,
292
+ codexHome,
293
+ } = {},
247
294
  ) {
248
295
  const explicitProfile = String(profileOverride || "auto").trim();
249
- const globalOverride = String(globalProfile || "auto").trim();
250
- const effectiveOverride = explicitProfile !== "auto" ? explicitProfile : NESTED_GLOBAL_PROFILE_OVERRIDES.has(globalOverride) ? globalOverride : "auto";
296
+ const globalOverride = normalizeCodexProfileName(
297
+ String(globalProfile || "auto").trim(),
298
+ );
299
+ // Headless lanes must select their role policy explicitly. Only the max and
300
+ // ultra exception lanes may cross the process environment boundary.
301
+ const effectiveOverride =
302
+ explicitProfile !== "auto"
303
+ ? explicitProfile
304
+ : NESTED_GLOBAL_PROFILE_OVERRIDES.has(globalOverride)
305
+ ? globalOverride
306
+ : "auto";
251
307
  const fallback = resolveCodexAgentProfile(agent);
252
- const candidate = resolveCodexAgentProfile(agent, { profileOverride: effectiveOverride, retryProfile, nested: true, allowCustom: true });
308
+ const candidate = resolveCodexAgentProfile(agent, {
309
+ profileOverride: effectiveOverride,
310
+ retryProfile,
311
+ nested: true,
312
+ allowCustom: true,
313
+ });
253
314
  const { effort } = resolveCodexProfileConfigValues(candidate, { codexHome });
254
315
  if (!effort) return fallback;
255
- return effort.toLowerCase() === "ultra" ? "gpt56_sol_max" : candidate;
316
+ return effort.toLowerCase() === "ultra" ? "gpt6_astra_max" : candidate;
256
317
  }
257
318
 
258
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,
@@ -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({
@@ -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 = [];
@@ -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
+ }