triflux 10.46.0 → 10.48.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.md +68 -106
- package/bin/tfx-live.mjs +279 -19
- package/config/routing-policy.json +1 -1
- package/hooks/codex-session-hook.mjs +7 -0
- package/hooks/session-start-fast.mjs +6 -0
- package/hub/dynamic-routing-engine.mjs +1 -1
- package/hub/lib/codex-session-registry.mjs +306 -0
- package/hub/lib/cto-env.mjs +2 -3
- package/hub/server.mjs +2 -51
- package/hub/team/recovery-store.mjs +1 -1
- package/hub/team/synapse-registry.mjs +14 -11
- package/hub/team/worktree-lifecycle.mjs +8 -48
- package/hub/tray-lifecycle.mjs +1 -1
- package/hub/workers/codex-mcp.mjs +1 -1
- package/hud/context-monitor.mjs +1 -1
- package/hud/providers/codex.mjs +91 -8
- package/hud/providers/cto.mjs +3 -0
- package/package.json +1 -1
- package/scripts/__tests__/tfx-route-bash-node-parity.test.mjs +1 -1
- package/scripts/__tests__/tfx-route-node-entry.test.mjs +4 -4
- package/scripts/lib/agent-route-policy.mjs +13 -13
- package/scripts/lib/codex-profile-config.mjs +10 -7
- package/scripts/setup.mjs +12 -5
- package/scripts/tfx-route.sh +23 -21
- package/skills/tfx-auto/SKILL.md +1 -1
- package/skills/tfx-live/SKILL.md +28 -0
- package/skills/tfx-plan/SKILL.md +2 -2
- package/skills/tfx-profile/SKILL.md +5 -5
- package/skills/tfx-setup/SKILL.md +1 -1
- package/tui/codex-profile.mjs +1 -0
- package/tui/setup.mjs +2 -2
- package/hub/team/git-preflight.mjs +0 -286
package/hud/providers/codex.mjs
CHANGED
|
@@ -38,6 +38,8 @@ export function classifyBucket(bucket) {
|
|
|
38
38
|
|
|
39
39
|
// primary/secondary를 window_minutes 기준으로 정규화
|
|
40
40
|
// HUD 규약: primary=5h, secondary=1w
|
|
41
|
+
// window_minutes 가 비어 있으면 길이를 모르므로 보고된 칸에 둔다(app-server 스키마에서
|
|
42
|
+
// windowDurationMins 는 선택 필드). 길이를 알지만 5h/1w 가 아닌 버킷은 버린다.
|
|
41
43
|
export function normalizeBuckets(rl) {
|
|
42
44
|
let primary = null;
|
|
43
45
|
let secondary = null;
|
|
@@ -46,6 +48,12 @@ export function normalizeBuckets(rl) {
|
|
|
46
48
|
if (kind === "five_hour") primary = bucket;
|
|
47
49
|
else if (kind === "weekly") secondary = bucket;
|
|
48
50
|
}
|
|
51
|
+
if (!primary && rl.primary && rl.primary.window_minutes == null) {
|
|
52
|
+
primary = rl.primary;
|
|
53
|
+
}
|
|
54
|
+
if (!secondary && rl.secondary && rl.secondary.window_minutes == null) {
|
|
55
|
+
secondary = rl.secondary;
|
|
56
|
+
}
|
|
49
57
|
return { primary, secondary };
|
|
50
58
|
}
|
|
51
59
|
|
|
@@ -104,7 +112,35 @@ function isNewerSnapshot(candidate, current) {
|
|
|
104
112
|
);
|
|
105
113
|
}
|
|
106
114
|
|
|
107
|
-
function
|
|
115
|
+
function positiveWindowMinutes(bucket) {
|
|
116
|
+
const minutes = Number(bucket?.window_minutes);
|
|
117
|
+
return minutes > 0 ? minutes : null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 계정 하나에는 같은 종류의 창이 한 번에 하나만 열린다. 브로커 계정이 없으면
|
|
121
|
+
// ~/.codex/sessions 와 HUD 라벨이 모두 ~/.codex/auth.json 계정 하나의 것이다. 그래서
|
|
122
|
+
// 세션 로그에 더 늦게 리셋되는 창이 나타났고 옛 창이 그 창이 열리기 전에만 관측됐다면,
|
|
123
|
+
// 옛 창은 조기 리셋(2026-09-23 실측)이나 계정 전환으로 끝난 창이다. 프로브로만 본 창은
|
|
124
|
+
// 다른 계정일 수 있어 근거로 쓰지 않는다. 새 창이 열린 뒤에도 관측된 창은 함께 도는
|
|
125
|
+
// 계정이므로 max-used 후보로 남긴다.
|
|
126
|
+
function isReplacedWindowGroup(group, other) {
|
|
127
|
+
if (other === group || !other.seenInSessions) return false;
|
|
128
|
+
if (other.minResetAt <= group.maxResetAt || !other.windowMinutes) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
const lastSeenMs = Date.parse(group.latest.timestamp);
|
|
132
|
+
if (!Number.isFinite(lastSeenMs)) return false;
|
|
133
|
+
// resets_at 은 실측으로 아래로 약 30초, 위로 1초 흔들리므로 가장 늦은 값으로 개시 시각을 잡는다.
|
|
134
|
+
const otherOpenedAtMs = (other.maxResetAt - other.windowMinutes * 60) * 1000;
|
|
135
|
+
return lastSeenMs <= otherOpenedAtMs;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function selectCodexWindowSnapshot(
|
|
139
|
+
snapshots,
|
|
140
|
+
nowSec,
|
|
141
|
+
windowKey,
|
|
142
|
+
replaceEndedWindows,
|
|
143
|
+
) {
|
|
108
144
|
const groups = [];
|
|
109
145
|
const ungrouped = [];
|
|
110
146
|
|
|
@@ -126,19 +162,29 @@ function selectCodexWindowSnapshot(snapshots, nowSec, windowKey) {
|
|
|
126
162
|
minResetAt: resetAt,
|
|
127
163
|
maxResetAt: resetAt,
|
|
128
164
|
latest: snapshot,
|
|
165
|
+
seenInSessions: !snapshot.probe,
|
|
166
|
+
windowMinutes: positiveWindowMinutes(snapshot[windowKey]),
|
|
129
167
|
};
|
|
130
168
|
groups.push(group);
|
|
131
169
|
continue;
|
|
132
170
|
}
|
|
133
171
|
|
|
172
|
+
if (!snapshot.probe) group.seenInSessions = true;
|
|
173
|
+
group.windowMinutes ??= positiveWindowMinutes(snapshot[windowKey]);
|
|
134
174
|
group.minResetAt = Math.min(group.minResetAt, resetAt);
|
|
135
175
|
group.maxResetAt = Math.max(group.maxResetAt, resetAt);
|
|
136
176
|
if (isNewerSnapshot(snapshot, group.latest)) group.latest = snapshot;
|
|
137
177
|
}
|
|
138
178
|
|
|
139
|
-
const
|
|
179
|
+
const openGroups = groups.filter(
|
|
140
180
|
(group) => Number(group.latest[windowKey]?.resets_at) > nowSec,
|
|
141
181
|
);
|
|
182
|
+
const activeGroups = replaceEndedWindows
|
|
183
|
+
? openGroups.filter(
|
|
184
|
+
(group) =>
|
|
185
|
+
!openGroups.some((other) => isReplacedWindowGroup(group, other)),
|
|
186
|
+
)
|
|
187
|
+
: openGroups;
|
|
142
188
|
let selected = null;
|
|
143
189
|
if (activeGroups.length > 0) {
|
|
144
190
|
selected = activeGroups
|
|
@@ -168,16 +214,18 @@ function selectCodexWindowSnapshot(snapshots, nowSec, windowKey) {
|
|
|
168
214
|
return { snapshot: selected, activeGroupCount: activeGroups.length };
|
|
169
215
|
}
|
|
170
216
|
|
|
171
|
-
function selectCodexSnapshot(snapshots, nowSec) {
|
|
217
|
+
function selectCodexSnapshot(snapshots, nowSec, replaceEndedWindows) {
|
|
172
218
|
const primarySelection = selectCodexWindowSnapshot(
|
|
173
219
|
snapshots,
|
|
174
220
|
nowSec,
|
|
175
221
|
"primary",
|
|
222
|
+
replaceEndedWindows,
|
|
176
223
|
);
|
|
177
224
|
const secondarySelection = selectCodexWindowSnapshot(
|
|
178
225
|
snapshots,
|
|
179
226
|
nowSec,
|
|
180
227
|
"secondary",
|
|
228
|
+
replaceEndedWindows,
|
|
181
229
|
);
|
|
182
230
|
const primarySnapshot = primarySelection.snapshot;
|
|
183
231
|
if (!primarySnapshot) return null;
|
|
@@ -198,7 +246,7 @@ function selectCodexSnapshot(snapshots, nowSec) {
|
|
|
198
246
|
return selected;
|
|
199
247
|
}
|
|
200
248
|
|
|
201
|
-
function mergeRateLimitSnapshots(snapshots, nowSec) {
|
|
249
|
+
function mergeRateLimitSnapshots(snapshots, nowSec, replaceEndedWindows) {
|
|
202
250
|
const merged = {};
|
|
203
251
|
const codexSnapshots = [];
|
|
204
252
|
for (const snapshot of snapshots) {
|
|
@@ -211,7 +259,11 @@ function mergeRateLimitSnapshots(snapshots, nowSec) {
|
|
|
211
259
|
}
|
|
212
260
|
}
|
|
213
261
|
|
|
214
|
-
const codex = selectCodexSnapshot(
|
|
262
|
+
const codex = selectCodexSnapshot(
|
|
263
|
+
codexSnapshots,
|
|
264
|
+
nowSec,
|
|
265
|
+
replaceEndedWindows,
|
|
266
|
+
);
|
|
215
267
|
if (codex) merged.codex = codex;
|
|
216
268
|
return merged;
|
|
217
269
|
}
|
|
@@ -227,9 +279,16 @@ export function getCodexRateLimits({
|
|
|
227
279
|
now = new Date(),
|
|
228
280
|
maxLinesPerFile = 800,
|
|
229
281
|
extraSnapshots = [],
|
|
282
|
+
// 교체된 창 판정(isReplacedWindowGroup). 브로커 Codex 계정이 있으면 끈다.
|
|
283
|
+
replaceEndedWindows = true,
|
|
230
284
|
} = {}) {
|
|
231
285
|
let syntheticBucket = null; // 최근 token_count에서 합성 (행 활성화 + 토큰 데이터용)
|
|
232
|
-
|
|
286
|
+
// 프로브도 세션 로그와 같은 window_minutes 기준으로 5h/1w 슬롯을 정한다.
|
|
287
|
+
// app-server 는 5h 창이 없으면 주간 창을 primary 로 보낸다(2026-09-23 실측).
|
|
288
|
+
const recentSnapshots = extraSnapshots.map((snapshot) => ({
|
|
289
|
+
...snapshot,
|
|
290
|
+
...normalizeBuckets(snapshot),
|
|
291
|
+
}));
|
|
233
292
|
const nowSec = Math.floor(now.getTime() / 1000);
|
|
234
293
|
|
|
235
294
|
// 7일간 스캔: 실제 rate_limits 우선, 합성 버킷은 폴백
|
|
@@ -313,7 +372,11 @@ export function getCodexRateLimits({
|
|
|
313
372
|
}
|
|
314
373
|
|
|
315
374
|
const snapshots = dayOffset <= 1 ? recentSnapshots : daySnapshots;
|
|
316
|
-
const mergedBuckets = mergeRateLimitSnapshots(
|
|
375
|
+
const mergedBuckets = mergeRateLimitSnapshots(
|
|
376
|
+
snapshots,
|
|
377
|
+
nowSec,
|
|
378
|
+
replaceEndedWindows,
|
|
379
|
+
);
|
|
317
380
|
if (Object.keys(mergedBuckets).length > 0) {
|
|
318
381
|
if (syntheticBucket) {
|
|
319
382
|
const main =
|
|
@@ -369,10 +432,30 @@ export async function collectProbeSnapshots(now = new Date()) {
|
|
|
369
432
|
);
|
|
370
433
|
}
|
|
371
434
|
|
|
435
|
+
// 브로커 계정은 자기 CODEX_HOME 에서 돌기 때문에, 옛 창이 전환 전 계정의 창이어도 그
|
|
436
|
+
// 계정이 아직 쓰이고 있을 수 있다. accounts.json 의 codex 항목과 프로브용 인증 캐시로 판정한다.
|
|
437
|
+
export function hasBrokerCodexAccounts({
|
|
438
|
+
brokerDir = CODEX_BROKER_AUTH_CACHE_DIR,
|
|
439
|
+
} = {}) {
|
|
440
|
+
const accounts = readJson(join(brokerDir, "accounts.json"), null);
|
|
441
|
+
if (Array.isArray(accounts?.codex) && accounts.codex.length > 0) return true;
|
|
442
|
+
try {
|
|
443
|
+
return readdirSync(brokerDir).some((file) =>
|
|
444
|
+
/^codex-auth-.+\.json$/.test(file),
|
|
445
|
+
);
|
|
446
|
+
} catch {
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
372
451
|
export async function refreshCodexRateLimitsCache() {
|
|
373
452
|
const now = new Date();
|
|
374
453
|
const extraSnapshots = await collectProbeSnapshots(now);
|
|
375
|
-
const buckets = getCodexRateLimits({
|
|
454
|
+
const buckets = getCodexRateLimits({
|
|
455
|
+
now,
|
|
456
|
+
extraSnapshots,
|
|
457
|
+
replaceEndedWindows: !hasBrokerCodexAccounts(),
|
|
458
|
+
});
|
|
376
459
|
// buckets가 null이어도 캐시 갱신 (stale 데이터 제거)
|
|
377
460
|
writeJsonSafe(CODEX_QUOTA_CACHE_PATH, { timestamp: Date.now(), buckets });
|
|
378
461
|
return buckets;
|
package/hud/providers/cto.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFileSync, statSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { resolveRoleControlSnapshot } from "../../hub/lib/cto-env.mjs";
|
|
3
4
|
import {
|
|
4
5
|
CTO_LAKE_CURRENT_RELATIVE_PATH,
|
|
5
6
|
CTO_STATUS_LINE_MAX_CHARS,
|
|
@@ -53,6 +54,8 @@ function extractStatusLine(lines) {
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
export function readCtoStatus(options = {}) {
|
|
57
|
+
if (!resolveRoleControlSnapshot(options.env).auto_collect_enabled)
|
|
58
|
+
return null;
|
|
56
59
|
try {
|
|
57
60
|
const currentPath = resolveCurrentPath(options);
|
|
58
61
|
const stat = statSync(currentPath);
|
package/package.json
CHANGED
|
@@ -87,7 +87,7 @@ describe("tfx-route bash/node parity — Phase 0 회귀가드", () => {
|
|
|
87
87
|
assert.equal(plan.agent, "executor");
|
|
88
88
|
assert.equal(plan.cliType, "codex");
|
|
89
89
|
assert.equal(plan.adapter, "codex");
|
|
90
|
-
assert.equal(plan.effort, "
|
|
90
|
+
assert.equal(plan.effort, "gpt61_sol_high");
|
|
91
91
|
assert.equal(plan.runMode, "fg");
|
|
92
92
|
});
|
|
93
93
|
|
|
@@ -234,14 +234,14 @@ describe("tfx-route.mjs — buildRoutePlan (agent-map.json 전수)", () => {
|
|
|
234
234
|
});
|
|
235
235
|
|
|
236
236
|
describe("cli-codex.mjs — 핵심 매핑", () => {
|
|
237
|
-
test("executor →
|
|
237
|
+
test("executor → gpt61_sol_high / 1080s / fg / mcp=implement", () => {
|
|
238
238
|
const p = codexAdapter.plan({
|
|
239
239
|
agent: "executor",
|
|
240
240
|
prompt: "x",
|
|
241
241
|
mcpProfile: "auto",
|
|
242
242
|
});
|
|
243
|
-
assert.equal(p.effort, "
|
|
244
|
-
assert.equal(p.profile, "
|
|
243
|
+
assert.equal(p.effort, "gpt61_sol_high");
|
|
244
|
+
assert.equal(p.profile, "gpt61_sol_high");
|
|
245
245
|
assert.equal(p.timeoutMs, 1_080_000);
|
|
246
246
|
assert.equal(p.runMode, "fg");
|
|
247
247
|
assert.equal(p.opusOversight, false);
|
|
@@ -334,7 +334,7 @@ describe("cli-codex.mjs — 핵심 매핑", () => {
|
|
|
334
334
|
prompt: "x",
|
|
335
335
|
mcpProfile: "auto",
|
|
336
336
|
});
|
|
337
|
-
assert.equal(p.effort, "
|
|
337
|
+
assert.equal(p.effort, "gpt61_sol_high");
|
|
338
338
|
assert.equal(p.timeoutMs, 1_080_000);
|
|
339
339
|
});
|
|
340
340
|
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
|
|
8
8
|
const policy = {
|
|
9
9
|
executor: {
|
|
10
|
-
profile: "
|
|
10
|
+
profile: "gpt61_sol_high",
|
|
11
11
|
timeoutSec: 1080,
|
|
12
12
|
runMode: "fg",
|
|
13
13
|
opusOversight: false,
|
|
@@ -15,7 +15,7 @@ const policy = {
|
|
|
15
15
|
subcommand: "exec",
|
|
16
16
|
},
|
|
17
17
|
codex: {
|
|
18
|
-
profile: "
|
|
18
|
+
profile: "gpt61_sol_high",
|
|
19
19
|
timeoutSec: 1080,
|
|
20
20
|
runMode: "fg",
|
|
21
21
|
opusOversight: false,
|
|
@@ -31,7 +31,7 @@ const policy = {
|
|
|
31
31
|
subcommand: "exec",
|
|
32
32
|
},
|
|
33
33
|
cleanup: {
|
|
34
|
-
profile: "
|
|
34
|
+
profile: "gpt61_sol_med",
|
|
35
35
|
timeoutSec: 540,
|
|
36
36
|
runMode: "fg",
|
|
37
37
|
opusOversight: false,
|
|
@@ -39,7 +39,7 @@ const policy = {
|
|
|
39
39
|
subcommand: "exec",
|
|
40
40
|
},
|
|
41
41
|
deslop: {
|
|
42
|
-
profile: "
|
|
42
|
+
profile: "gpt61_sol_med",
|
|
43
43
|
timeoutSec: 540,
|
|
44
44
|
runMode: "fg",
|
|
45
45
|
opusOversight: false,
|
|
@@ -95,7 +95,7 @@ const policy = {
|
|
|
95
95
|
subcommand: "exec",
|
|
96
96
|
},
|
|
97
97
|
"code-reviewer": {
|
|
98
|
-
profile: "
|
|
98
|
+
profile: "gpt61_sol_high",
|
|
99
99
|
timeoutSec: 1800,
|
|
100
100
|
runMode: "bg",
|
|
101
101
|
opusOversight: false,
|
|
@@ -103,7 +103,7 @@ const policy = {
|
|
|
103
103
|
subcommand: "review",
|
|
104
104
|
},
|
|
105
105
|
"quality-reviewer": {
|
|
106
|
-
profile: "
|
|
106
|
+
profile: "gpt61_sol_high",
|
|
107
107
|
timeoutSec: 1800,
|
|
108
108
|
runMode: "bg",
|
|
109
109
|
opusOversight: false,
|
|
@@ -119,7 +119,7 @@ const policy = {
|
|
|
119
119
|
subcommand: "review",
|
|
120
120
|
},
|
|
121
121
|
scientist: {
|
|
122
|
-
profile: "
|
|
122
|
+
profile: "gpt61_sol_high",
|
|
123
123
|
timeoutSec: 1440,
|
|
124
124
|
runMode: "bg",
|
|
125
125
|
opusOversight: false,
|
|
@@ -127,7 +127,7 @@ const policy = {
|
|
|
127
127
|
subcommand: "exec",
|
|
128
128
|
},
|
|
129
129
|
"document-specialist": {
|
|
130
|
-
profile: "
|
|
130
|
+
profile: "gpt61_sol_high",
|
|
131
131
|
timeoutSec: 1440,
|
|
132
132
|
runMode: "bg",
|
|
133
133
|
opusOversight: false,
|
|
@@ -143,7 +143,7 @@ const policy = {
|
|
|
143
143
|
subcommand: "exec",
|
|
144
144
|
},
|
|
145
145
|
verifier: {
|
|
146
|
-
profile: "
|
|
146
|
+
profile: "gpt61_sol_high",
|
|
147
147
|
timeoutSec: 1200,
|
|
148
148
|
runMode: "fg",
|
|
149
149
|
opusOversight: false,
|
|
@@ -151,7 +151,7 @@ const policy = {
|
|
|
151
151
|
subcommand: "review",
|
|
152
152
|
},
|
|
153
153
|
"test-engineer": {
|
|
154
|
-
profile: "
|
|
154
|
+
profile: "gpt61_sol_high",
|
|
155
155
|
timeoutSec: 1200,
|
|
156
156
|
runMode: "bg",
|
|
157
157
|
opusOversight: false,
|
|
@@ -159,7 +159,7 @@ const policy = {
|
|
|
159
159
|
subcommand: "exec",
|
|
160
160
|
},
|
|
161
161
|
"qa-tester": {
|
|
162
|
-
profile: "
|
|
162
|
+
profile: "gpt61_sol_high",
|
|
163
163
|
timeoutSec: 1200,
|
|
164
164
|
runMode: "bg",
|
|
165
165
|
opusOversight: false,
|
|
@@ -212,8 +212,8 @@ export const CANONICAL_CODEX_PROFILE_OVERRIDES = Object.freeze(
|
|
|
212
212
|
new Set([
|
|
213
213
|
"gpt6_luna_low",
|
|
214
214
|
"gpt6_luna_high",
|
|
215
|
-
"
|
|
216
|
-
"
|
|
215
|
+
"gpt61_sol_med",
|
|
216
|
+
"gpt61_sol_high",
|
|
217
217
|
"gpt6_astra_xhigh",
|
|
218
218
|
"gpt6_astra_max",
|
|
219
219
|
"gpt6_astra_ultra",
|
|
@@ -21,22 +21,25 @@ const EFFORT_BY_SUFFIX = {
|
|
|
21
21
|
const CANONICAL_PROFILE_VALUES = Object.freeze({
|
|
22
22
|
gpt6_luna_low: { model: "gpt-6-luna", effort: "low" },
|
|
23
23
|
gpt6_luna_high: { model: "gpt-6-luna", effort: "high" },
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
gpt61_sol_med: { model: "gpt-6.1-sol", effort: "medium" },
|
|
25
|
+
gpt61_sol_high: { model: "gpt-6.1-sol", effort: "high" },
|
|
26
26
|
gpt6_astra_xhigh: { model: "gpt-6-astra", effort: "xhigh" },
|
|
27
27
|
gpt6_astra_max: { model: "gpt-6-astra", effort: "max" },
|
|
28
28
|
gpt6_astra_ultra: { model: "gpt-6-astra", effort: "ultra" },
|
|
29
29
|
});
|
|
30
30
|
|
|
31
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.
|
|
32
|
+
// Terra tier, so Terra lanes move to Sol. GPT-6 Sol lane names map to GPT-6.1
|
|
33
|
+
// Sol with the same effort.
|
|
33
34
|
const LEGACY_CANONICAL_PROFILE_ALIASES = Object.freeze({
|
|
34
35
|
gpt56_sol_xhigh: "gpt6_astra_xhigh",
|
|
35
36
|
gpt56_sol_max: "gpt6_astra_max",
|
|
36
37
|
gpt56_sol_ultra: "gpt6_astra_ultra",
|
|
37
|
-
gpt56_terra_high: "
|
|
38
|
-
gpt56_terra_med: "
|
|
38
|
+
gpt56_terra_high: "gpt61_sol_high",
|
|
39
|
+
gpt56_terra_med: "gpt61_sol_med",
|
|
39
40
|
gpt56_luna_low: "gpt6_luna_low",
|
|
41
|
+
gpt6_sol_high: "gpt61_sol_high",
|
|
42
|
+
gpt6_sol_med: "gpt61_sol_med",
|
|
40
43
|
});
|
|
41
44
|
|
|
42
45
|
export function normalizeCodexProfileName(profileName) {
|
|
@@ -65,7 +68,7 @@ function readProfileScalar(raw, key) {
|
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
/**
|
|
68
|
-
* Resolve a Codex effort profile name (e.g. "
|
|
71
|
+
* Resolve a Codex effort profile name (e.g. "gpt61_sol_high") to the `-c key=value`
|
|
69
72
|
* config overrides that `codex exec` accepts, so the headless CLI lanes can
|
|
70
73
|
* select a profile WITHOUT `codex exec --profile <name>`. codex 0.134+ rejects
|
|
71
74
|
* `--profile X` whenever config.toml still contains an inline `[profiles.X]`
|
|
@@ -83,7 +86,7 @@ function readProfileScalar(raw, key) {
|
|
|
83
86
|
*
|
|
84
87
|
* @param {string} profileName
|
|
85
88
|
* @param {{ codexHome?: string }} [opts]
|
|
86
|
-
* @returns {string[]} e.g. ['model="gpt-6-sol"', 'model_reasoning_effort="high"']
|
|
89
|
+
* @returns {string[]} e.g. ['model="gpt-6.1-sol"', 'model_reasoning_effort="high"']
|
|
87
90
|
*/
|
|
88
91
|
export function resolveCodexProfileConfigValues(profileName, opts = {}) {
|
|
89
92
|
if (typeof profileName !== "string" || !profileName) {
|
package/scripts/setup.mjs
CHANGED
|
@@ -437,14 +437,14 @@ const REQUIRED_CODEX_PROFILES = [
|
|
|
437
437
|
name: "gpt6_astra_xhigh",
|
|
438
438
|
lines: ['model = "gpt-6-astra"', 'model_reasoning_effort = "xhigh"'],
|
|
439
439
|
},
|
|
440
|
-
// GPT-6 Sol: implementation, review, and verification role defaults.
|
|
440
|
+
// GPT-6.1 Sol: implementation, review, and verification role defaults.
|
|
441
441
|
{
|
|
442
|
-
name: "
|
|
443
|
-
lines: ['model = "gpt-6-sol"', 'model_reasoning_effort = "high"'],
|
|
442
|
+
name: "gpt61_sol_high",
|
|
443
|
+
lines: ['model = "gpt-6.1-sol"', 'model_reasoning_effort = "high"'],
|
|
444
444
|
},
|
|
445
445
|
{
|
|
446
|
-
name: "
|
|
447
|
-
lines: ['model = "gpt-6-sol"', 'model_reasoning_effort = "medium"'],
|
|
446
|
+
name: "gpt61_sol_med",
|
|
447
|
+
lines: ['model = "gpt-6.1-sol"', 'model_reasoning_effort = "medium"'],
|
|
448
448
|
},
|
|
449
449
|
// GPT-6 Luna: high for focused lanes, low for latency-first lanes.
|
|
450
450
|
{
|
|
@@ -636,6 +636,13 @@ const SYNC_MAP = [
|
|
|
636
636
|
// 설치본이 남지 않게 한다. hooks/ 는 플러그인 루트에서 직접 import 되는 경로라
|
|
637
637
|
// 이 복사 목록의 대상이 아니다.
|
|
638
638
|
...scanLibFiles(PLUGIN_ROOT, CLAUDE_DIR),
|
|
639
|
+
// hud/providers/cto.mjs 가 ../../hub/lib/cto-env.mjs 를 정적 import 한다(ADR-0018).
|
|
640
|
+
// 의존성 없는 env 판독 모듈이라 이 파일 하나만 hud 보다 먼저 복사한다.
|
|
641
|
+
{
|
|
642
|
+
src: join(PLUGIN_ROOT, "hub", "lib", "cto-env.mjs"),
|
|
643
|
+
dst: join(CLAUDE_DIR, "hub", "lib", "cto-env.mjs"),
|
|
644
|
+
label: "hub/lib/cto-env.mjs",
|
|
645
|
+
},
|
|
639
646
|
...scanHudFiles(PLUGIN_ROOT, CLAUDE_DIR),
|
|
640
647
|
{
|
|
641
648
|
src: join(PLUGIN_ROOT, "scripts", "notion-read.mjs"),
|
package/scripts/tfx-route.sh
CHANGED
|
@@ -1424,7 +1424,7 @@ route_agent() {
|
|
|
1424
1424
|
exit 1
|
|
1425
1425
|
}
|
|
1426
1426
|
IFS=$'\t' read -r policy_profile policy_timeout policy_mode policy_oversight policy_mcp policy_subcommand <<< "$policy_row"
|
|
1427
|
-
case "$policy_profile" in gpt6_astra_xhigh|
|
|
1427
|
+
case "$policy_profile" in gpt6_astra_xhigh|gpt61_sol_high|gpt61_sol_med|gpt6_luna_high|gpt6_luna_low) ;; *) echo "ERROR: invalid Codex profile policy" >&2; exit 1 ;; esac
|
|
1428
1428
|
[[ "$policy_timeout" =~ ^[1-9][0-9]*$ ]] || { echo "ERROR: invalid Codex timeout policy" >&2; exit 1; }
|
|
1429
1429
|
case "$policy_mode" in fg|bg) ;; *) echo "ERROR: invalid Codex run mode policy" >&2; exit 1 ;; esac
|
|
1430
1430
|
case "$policy_oversight" in true|false) ;; *) echo "ERROR: invalid Codex oversight policy" >&2; exit 1 ;; esac
|
|
@@ -1495,9 +1495,11 @@ normalize_codex_profile_name() {
|
|
|
1495
1495
|
gpt56_sol_xhigh) echo "gpt6_astra_xhigh" ;;
|
|
1496
1496
|
gpt56_sol_max) echo "gpt6_astra_max" ;;
|
|
1497
1497
|
gpt56_sol_ultra) echo "gpt6_astra_ultra" ;;
|
|
1498
|
-
gpt56_terra_high) echo "
|
|
1499
|
-
gpt56_terra_med) echo "
|
|
1498
|
+
gpt56_terra_high) echo "gpt61_sol_high" ;;
|
|
1499
|
+
gpt56_terra_med) echo "gpt61_sol_med" ;;
|
|
1500
1500
|
gpt56_luna_low) echo "gpt6_luna_low" ;;
|
|
1501
|
+
gpt6_sol_high) echo "gpt61_sol_high" ;;
|
|
1502
|
+
gpt6_sol_med) echo "gpt61_sol_med" ;;
|
|
1501
1503
|
*) echo "$1" ;;
|
|
1502
1504
|
esac
|
|
1503
1505
|
}
|
|
@@ -1588,9 +1590,9 @@ case "$TFX_CODEX_TRANSPORT" in
|
|
|
1588
1590
|
;;
|
|
1589
1591
|
esac
|
|
1590
1592
|
case "$TFX_CODEX_PROFILE" in
|
|
1591
|
-
auto|max|ultra|gpt6_luna_low|gpt6_luna_high|
|
|
1593
|
+
auto|max|ultra|gpt6_luna_low|gpt6_luna_high|gpt61_sol_med|gpt61_sol_high|gpt6_astra_xhigh|gpt6_astra_max|gpt6_astra_ultra) ;;
|
|
1592
1594
|
*)
|
|
1593
|
-
echo "ERROR: TFX_CODEX_PROFILE 값은 auto, max, ultra 또는 canonical gpt6_* profile이어야 합니다. (현재: $TFX_CODEX_PROFILE)" >&2
|
|
1595
|
+
echo "ERROR: TFX_CODEX_PROFILE 값은 auto, max, ultra 또는 canonical gpt6_*/gpt61_* profile이어야 합니다. (현재: $TFX_CODEX_PROFILE)" >&2
|
|
1594
1596
|
exit 1
|
|
1595
1597
|
;;
|
|
1596
1598
|
esac
|
|
@@ -1645,7 +1647,7 @@ apply_cli_mode() {
|
|
|
1645
1647
|
writer)
|
|
1646
1648
|
CLI_ARGS="exec --profile gpt6_luna_high ${codex_base}"; CLI_EFFORT="gpt6_luna_high"; DEFAULT_TIMEOUT=900 ;;
|
|
1647
1649
|
*)
|
|
1648
|
-
CLI_ARGS="exec --profile
|
|
1650
|
+
CLI_ARGS="exec --profile gpt61_sol_high ${codex_base}"; CLI_EFFORT="gpt61_sol_high"; DEFAULT_TIMEOUT=1080 ;;
|
|
1649
1651
|
esac
|
|
1650
1652
|
echo "[tfx-route] TFX_CLI_MODE=codex: $AGENT_TYPE → codex($CLI_EFFORT)로 리매핑" >&2
|
|
1651
1653
|
fi ;;
|
|
@@ -1792,9 +1794,9 @@ apply_plan_guard() {
|
|
|
1792
1794
|
replacement=""
|
|
1793
1795
|
case "$CLI_EFFORT" in
|
|
1794
1796
|
gpt55_low|spark53_low|codex53_low|gpt54_low|mini54_low) replacement="gpt6_luna_low" ;;
|
|
1795
|
-
gpt55_med|spark53_med|codex53_med|mini54_med) replacement="
|
|
1797
|
+
gpt55_med|spark53_med|codex53_med|mini54_med) replacement="gpt61_sol_med" ;;
|
|
1796
1798
|
gpt55_xhigh|codex53_xhigh|gpt54_xhigh) replacement="gpt6_astra_xhigh" ;;
|
|
1797
|
-
gpt55_high|spark53_*|codex53_*|gpt54_*|mini54_*) replacement="
|
|
1799
|
+
gpt55_high|spark53_*|codex53_*|gpt54_*|mini54_*) replacement="gpt61_sol_high" ;;
|
|
1798
1800
|
esac
|
|
1799
1801
|
fi
|
|
1800
1802
|
[[ -z "$replacement" ]] && return
|
|
@@ -1832,22 +1834,22 @@ apply_no_claude_native_mode() {
|
|
|
1832
1834
|
OPUS_OVERSIGHT="false"
|
|
1833
1835
|
;;
|
|
1834
1836
|
verifier)
|
|
1835
|
-
CLI_ARGS="exec --profile
|
|
1836
|
-
CLI_EFFORT="
|
|
1837
|
+
CLI_ARGS="exec --profile gpt61_sol_high ${codex_base} review"
|
|
1838
|
+
CLI_EFFORT="gpt61_sol_high"
|
|
1837
1839
|
DEFAULT_TIMEOUT=1200
|
|
1838
1840
|
RUN_MODE="fg"
|
|
1839
1841
|
OPUS_OVERSIGHT="false"
|
|
1840
1842
|
;;
|
|
1841
1843
|
test-engineer)
|
|
1842
|
-
CLI_ARGS="exec --profile
|
|
1843
|
-
CLI_EFFORT="
|
|
1844
|
+
CLI_ARGS="exec --profile gpt61_sol_high ${codex_base}"
|
|
1845
|
+
CLI_EFFORT="gpt61_sol_high"
|
|
1844
1846
|
DEFAULT_TIMEOUT=1200
|
|
1845
1847
|
RUN_MODE="bg"
|
|
1846
1848
|
OPUS_OVERSIGHT="false"
|
|
1847
1849
|
;;
|
|
1848
1850
|
qa-tester)
|
|
1849
|
-
CLI_ARGS="exec --profile
|
|
1850
|
-
CLI_EFFORT="
|
|
1851
|
+
CLI_ARGS="exec --profile gpt61_sol_high ${codex_base} review"
|
|
1852
|
+
CLI_EFFORT="gpt61_sol_high"
|
|
1851
1853
|
DEFAULT_TIMEOUT=1200
|
|
1852
1854
|
RUN_MODE="bg"
|
|
1853
1855
|
OPUS_OVERSIGHT="false"
|
|
@@ -1879,7 +1881,7 @@ resolve_safe_retry_codex_profile() {
|
|
|
1879
1881
|
echo "gpt6_astra_max"
|
|
1880
1882
|
return
|
|
1881
1883
|
;;
|
|
1882
|
-
gpt6_luna_low|gpt6_luna_high|
|
|
1884
|
+
gpt6_luna_low|gpt6_luna_high|gpt61_sol_med|gpt61_sol_high|gpt6_astra_xhigh|gpt6_astra_max)
|
|
1883
1885
|
echo "$profile"
|
|
1884
1886
|
return
|
|
1885
1887
|
;;
|
|
@@ -1889,7 +1891,7 @@ resolve_safe_retry_codex_profile() {
|
|
|
1889
1891
|
codex_home="${CODEX_HOME:-${TFX_CODEX_HOME:-${HOME:-}/.codex}}"
|
|
1890
1892
|
profile_file="${codex_home}/${profile}.config.toml"
|
|
1891
1893
|
if [[ ! -f "$profile_file" ]]; then
|
|
1892
|
-
echo "
|
|
1894
|
+
echo "gpt61_sol_high"
|
|
1893
1895
|
return
|
|
1894
1896
|
fi
|
|
1895
1897
|
effort=$(awk -F= '
|
|
@@ -1903,7 +1905,7 @@ resolve_safe_retry_codex_profile() {
|
|
|
1903
1905
|
' "$profile_file" 2>/dev/null || true)
|
|
1904
1906
|
case "$effort" in
|
|
1905
1907
|
ultra) echo "gpt6_astra_max" ;;
|
|
1906
|
-
"") echo "
|
|
1908
|
+
"") echo "gpt61_sol_high" ;;
|
|
1907
1909
|
*) echo "$profile" ;;
|
|
1908
1910
|
esac
|
|
1909
1911
|
}
|
|
@@ -2116,8 +2118,8 @@ apply_dynamic_routing_override() {
|
|
|
2116
2118
|
local codex_base
|
|
2117
2119
|
codex_base="$(build_codex_base)"
|
|
2118
2120
|
CLI_CMD="codex"
|
|
2119
|
-
CLI_ARGS="exec --profile
|
|
2120
|
-
CLI_EFFORT="
|
|
2121
|
+
CLI_ARGS="exec --profile gpt61_sol_high ${codex_base}"
|
|
2122
|
+
CLI_EFFORT="gpt61_sol_high"; DEFAULT_TIMEOUT=1080; RUN_MODE="fg"; OPUS_OVERSIGHT="false"
|
|
2121
2123
|
;;
|
|
2122
2124
|
antigravity)
|
|
2123
2125
|
CLI_CMD="agy"
|
|
@@ -3079,7 +3081,7 @@ run_codex_exec() {
|
|
|
3079
3081
|
if [[ "$exit_code_local" -ne 0 ]] && grep -qE "is not supported when using Codex with a ChatGPT account" "$STDERR_LOG" 2>/dev/null; then
|
|
3080
3082
|
local fallback_profile=""
|
|
3081
3083
|
case "$CLI_EFFORT" in
|
|
3082
|
-
gpt6_astra_ultra|gpt6_astra_max|gpt6_astra_xhigh|
|
|
3084
|
+
gpt6_astra_ultra|gpt6_astra_max|gpt6_astra_xhigh|gpt61_sol_high|gpt61_sol_med|gpt6_luna_high) fallback_profile="gpt6_luna_low" ;;
|
|
3083
3085
|
esac
|
|
3084
3086
|
if [[ -n "$fallback_profile" ]]; then
|
|
3085
3087
|
echo "[tfx-route] tier fallback: $CLI_EFFORT not supported on ChatGPT account → retry with $fallback_profile" >&2
|
|
@@ -3539,7 +3541,7 @@ EOF
|
|
|
3539
3541
|
FULL_PROMPT="$PROMPT"
|
|
3540
3542
|
fi
|
|
3541
3543
|
# CTO north-star: codex lane(line ~2670)과 동일하게 brief 를 prompt 앞에 주입.
|
|
3542
|
-
# prepend_codex_north_star 는 CLI-agnostic (TFX_CTO_NORTH_STAR opt-
|
|
3544
|
+
# prepend_codex_north_star 는 CLI-agnostic (TFX_CTO_NORTH_STAR opt-in +
|
|
3543
3545
|
# .triflux/lake/current.md 만 참조) 하므로 agy 워커에서도 그대로 재사용한다.
|
|
3544
3546
|
# sentinel 은 command-substitution 이 trailing newline 을 삼키는 것을 막는다.
|
|
3545
3547
|
local _agy_north_star_file="${WORKDIR:-$PWD}/.triflux/lake/current.md"
|
package/skills/tfx-auto/SKILL.md
CHANGED
|
@@ -1009,7 +1009,7 @@ Agent(subagent_type="oh-my-claudecode:{agent}", model="{model}", prompt="{prompt
|
|
|
1009
1009
|
|
|
1010
1010
|
| 입력 | CLI | MCP |
|
|
1011
1011
|
|------|-----|-----|
|
|
1012
|
-
| codex / executor | Codex (`
|
|
1012
|
+
| codex / executor | Codex (`gpt61_sol_high`; 복잡 구현은 deep-executor/xhigh) | implement |
|
|
1013
1013
|
| debugger / deep-executor | Codex (`gpt6_astra_xhigh`) | implement |
|
|
1014
1014
|
| build-fixer | Codex (`gpt6_luna_high`) | implement |
|
|
1015
1015
|
| spark | Codex (`gpt6_luna_low`) | implement |
|
package/skills/tfx-live/SKILL.md
CHANGED
|
@@ -79,6 +79,34 @@ the npm `node .../codex` shim, `codex.js`, and native `codex` executables.
|
|
|
79
79
|
on that host; unavailable `ps` produces a peer `preflightWarning` and allows
|
|
80
80
|
attach to continue.
|
|
81
81
|
|
|
82
|
+
### Claude discovery and Codex registry
|
|
83
|
+
|
|
84
|
+
`tfx-live list-sessions --cli claude [--cwd DIR]` lists local tmux sessions from
|
|
85
|
+
`~/.claude/sessions/*.json` (`TFX_CLAUDE_SESSIONS_DIR` overrides the directory).
|
|
86
|
+
It returns `session`, `target`, `paneId`, pane `cwd`, `pid`, `sessionId`, `short`,
|
|
87
|
+
`name`, `title`, `nameSource`, and `status` only for live PIDs matching the pane process
|
|
88
|
+
or a descendant within four levels. Claude discovery rejects `--remote` and
|
|
89
|
+
`--transport uds`; `--transport auto` uses local tmux, and `--cwd` matches the
|
|
90
|
+
pane cwd exactly.
|
|
91
|
+
|
|
92
|
+
`title` equals `name` unless `nameSource` is `derived` or the name is empty.
|
|
93
|
+
In those cases it uses the latest non-empty, trimmed `custom-title` or `ai-title`
|
|
94
|
+
from the last 262144 bytes of `~/.claude/projects/*/<sessionId>.jsonl`
|
|
95
|
+
(`TFX_CLAUDE_PROJECTS_DIR` overrides the projects directory). `customTitle`
|
|
96
|
+
takes precedence over `aiTitle` on the same line; no matching title yields null.
|
|
97
|
+
|
|
98
|
+
Codex SessionStart and UserPromptSubmit hooks atomically write `<pid>.json` to
|
|
99
|
+
`${XDG_STATE_HOME:-$HOME/.local/state}/triflux/codex-sessions`
|
|
100
|
+
(`TFX_CODEX_SESSION_REGISTRY_DIR` overrides it). Version 1 contains
|
|
101
|
+
`{ version: 1, writer: "triflux", pid, sessionId, cwd, tmux, tmuxPane, source,
|
|
102
|
+
startedAt, updatedAt }`: `tmux` is `<session>:@<window_id>.%<pane_id>` or null,
|
|
103
|
+
`tmuxPane` is `%<pane_id>` or null, `source` defaults to null, and timestamps
|
|
104
|
+
are milliseconds. `startedAt` stays unchanged for the same PID and thread.
|
|
105
|
+
Local Codex discovery adds `threadId` and `name` to matching entries in `panes`;
|
|
106
|
+
names come from `$CODEX_HOME/session_index.jsonl` (default `~/.codex`), and are
|
|
107
|
+
null when absent. Dead PID records are pruned after writes.
|
|
108
|
+
Records are written only when the Codex process is inside the hook's tmux pane, so TUIs attached to a shared app-server daemon (`daemon_auto_start`) are not recorded.
|
|
109
|
+
|
|
82
110
|
`ask` and tmux `interrupt` accept session names, `name:window`, and
|
|
83
111
|
`name:window.pane`. `start` and `stop` require a session name only; `stop`
|
|
84
112
|
rejects pane targets because it kills the entire session. `--remote HOST`
|
package/skills/tfx-plan/SKILL.md
CHANGED
|
@@ -10,7 +10,7 @@ argument-hint: "<구현할 기능> [--quick]"
|
|
|
10
10
|
> **ARGUMENTS 처리**: `--quick` → Quick 모드. 그 외 → Deep 모드 (기본).
|
|
11
11
|
|
|
12
12
|
> AI makes completeness near-free. 기본은 Opus 4.6(Planner) + Codex(Architect) + Claude Opus(Critic) 합의.
|
|
13
|
-
> Antigravity는 `agy --print`
|
|
13
|
+
> Antigravity는 `agy --print` 경로가 단순 프롬프트에서만 검증됐으므로, schema-driven critic에는 쓰지 않고 `--quick` freeform 보조로만 사용한다.
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
@@ -49,7 +49,7 @@ Tier 3:
|
|
|
49
49
|
3. Claude → `Agent(run_in_background=true)`
|
|
50
50
|
4. Bash + Agent 동시 호출
|
|
51
51
|
5. deprecated Gemini CLI 또는 legacy Gemini assign 금지. Antigravity는 `antigravity:`/`agy` 이름만 사용한다.
|
|
52
|
-
6. `agy --print`는
|
|
52
|
+
6. `agy --print`는 프롬프트를 옵션 값으로 받는다(`--print '<prompt>'`). 표준 입력 프롬프트는 agy 1.1.27부터 동작하지 않는다. strict JSON critic 검증에 실패하면 Claude critic으로 대체한다.
|
|
53
53
|
|
|
54
54
|
### 모델 역할
|
|
55
55
|
|