@triflux/core 10.51.0 → 10.52.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 (55) hide show
  1. package/hub/bridge.mjs +65 -1172
  2. package/hub/index.mjs +2 -18
  3. package/hub/lib/process-utils.mjs +1 -33
  4. package/hub/team/claude-daemon-control.mjs +1 -2
  5. package/hub/team/claude-session-projection.mjs +5 -73
  6. package/hub/team/nativeProxy.mjs +759 -0
  7. package/hub/team/session-context.mjs +1 -1
  8. package/hud/constants.mjs +2 -0
  9. package/hud/hud-qos-status.mjs +21 -5
  10. package/hud/providers/gemini.mjs +20 -0
  11. package/hud/renderers.mjs +79 -46
  12. package/hud/utils.mjs +9 -5
  13. package/package.json +2 -4
  14. package/scripts/lib/agent-route-policy.mjs +0 -67
  15. package/scripts/lib/cli-claude.mjs +0 -12
  16. package/scripts/lib/env-probe.mjs +0 -70
  17. package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
  18. package/scripts/lib/mcp-guard-engine.mjs +40 -98
  19. package/scripts/lib/session-presence.mjs +1 -99
  20. package/hub/adaptive-diagnostic.mjs +0 -350
  21. package/hub/adaptive-inject.mjs +0 -224
  22. package/hub/adaptive-memory.mjs +0 -277
  23. package/hub/adaptive.mjs +0 -172
  24. package/hub/assign-callbacks.mjs +0 -170
  25. package/hub/delegator/contracts.mjs +0 -38
  26. package/hub/delegator/index.mjs +0 -14
  27. package/hub/delegator/schema/delegator-tools.schema.json +0 -250
  28. package/hub/delegator/service.mjs +0 -336
  29. package/hub/delegator/tool-definitions.mjs +0 -35
  30. package/hub/hitl.mjs +0 -202
  31. package/hub/lib/known-errors.json +0 -72
  32. package/hub/lib/memory-store.mjs +0 -1001
  33. package/hub/lib/phase-manager.mjs +0 -493
  34. package/hub/lib/state-snapshot.mjs +0 -293
  35. package/hub/lib/trace-recorder.mjs +0 -153
  36. package/hub/lib/uuidv7.mjs +0 -45
  37. package/hub/memory-doctor.mjs +0 -640
  38. package/hub/middleware/quota-middleware.mjs +0 -70
  39. package/hub/middleware/request-logger.mjs +0 -191
  40. package/hub/paths.mjs +0 -24
  41. package/hub/pipeline/index.mjs +0 -163
  42. package/hub/pipeline/state.mjs +0 -191
  43. package/hub/pipeline/transitions.mjs +0 -131
  44. package/hub/reflexion.mjs +0 -290
  45. package/hub/role-contract.mjs +0 -312
  46. package/hub/router.mjs +0 -2026
  47. package/hub/session-fingerprint.mjs +0 -418
  48. package/hub/state.mjs +0 -300
  49. package/hub/team/synapse-http.mjs +0 -311
  50. package/hub/team-bridge.mjs +0 -27
  51. package/scripts/lib/claudemd-scanner.mjs +0 -26
  52. package/scripts/lib/context.mjs +0 -51
  53. package/scripts/lib/handoff.mjs +0 -187
  54. package/scripts/lib/logger.mjs +0 -122
  55. package/scripts/lib/stealth-fetch.mjs +0 -176
@@ -96,7 +96,7 @@ export function contextGuard(
96
96
  : {};
97
97
  }
98
98
 
99
- async function findCodexRollout(threadId) {
99
+ export async function findCodexRollout(threadId) {
100
100
  if (!/^[a-zA-Z0-9-]+$/u.test(threadId || "")) return null;
101
101
  const root = path.join(
102
102
  process.env.CODEX_HOME || path.join(homedir(), ".codex"),
package/hud/constants.mjs CHANGED
@@ -171,6 +171,8 @@ export const ANTIGRAVITY_REFRESH_LOCK_PATH = join(
171
171
  ".antigravity-refresh-lock",
172
172
  );
173
173
  export const ANTIGRAVITY_QUOTA_STALE_MS = 5 * 60 * 1000;
174
+ // 조회가 잠시 실패해도 이 시간 안에 성공한 적이 있으면 로그인 상태로 본다.
175
+ export const ANTIGRAVITY_SIGNED_IN_GRACE_MS = 60 * 60 * 1000;
174
176
  export const ANTIGRAVITY_REFRESH_FLAG = "--refresh-antigravity-quota";
175
177
  export const ACCOUNT_LABEL_WIDTH = 10;
176
178
  export const PROVIDER_PREFIX_WIDTH = 2;
@@ -20,6 +20,7 @@ import {
20
20
  CLAUDE_BAND_MARKER_DIR,
21
21
  CLAUDE_BAND_MARKER_TTL_MS,
22
22
  CLAUDE_REFRESH_FLAG,
23
+ CODEX_AUTH_PATH,
23
24
  CODEX_REFRESH_FLAG,
24
25
  } from "./constants.mjs";
25
26
  import { buildContextUsageView } from "./context-monitor.mjs";
@@ -37,6 +38,7 @@ import {
37
38
  } from "./providers/codex.mjs";
38
39
  import {
39
40
  getAntigravityAccountLabel,
41
+ getAntigravityAuthKind,
40
42
  readAntigravityQuotaSnapshot,
41
43
  refreshAntigravityQuotaCache,
42
44
  scheduleAntigravityQuotaRefresh,
@@ -48,7 +50,12 @@ import {
48
50
  renderAlignedRows,
49
51
  } from "./renderers.mjs";
50
52
  import { selectTier } from "./terminal.mjs";
51
- import { readJson, readStdinJson } from "./utils.mjs";
53
+ import {
54
+ formatTimeCell,
55
+ formatTimeCellDH,
56
+ readJson,
57
+ readStdinJson,
58
+ } from "./utils.mjs";
52
59
 
53
60
  async function main() {
54
61
  if (process.argv.includes(CLAUDE_REFRESH_FLAG)) {
@@ -95,12 +102,15 @@ async function main() {
95
102
  ? { ...claudeUsageSnapshot.data, stale: claudeUsageSnapshot.isStale }
96
103
  : null;
97
104
  const codexBuckets = codexSnapshot.buckets;
105
+ const antigravityQuota = antigravityAllowed
106
+ ? { ...antigravitySnapshot?.data, auth: getAntigravityAuthKind() }
107
+ : null;
98
108
  const currentTier = selectTier();
99
109
  if (currentTier === "nano") {
100
110
  const microLine = getMicroLine(contextView, claudeUsage, codexBuckets, {
101
111
  showCodex,
102
112
  showAntigravity: antigravityAllowed,
103
- antigravityQuota: antigravitySnapshot?.data,
113
+ antigravityQuota,
104
114
  });
105
115
  process.stdout.write(`\x1b[0m${microLine}\n`);
106
116
  return;
@@ -134,14 +144,20 @@ async function main() {
134
144
  geminiBlue,
135
145
  accountsConfig,
136
146
  accountsState,
137
- antigravitySnapshot?.data,
147
+ antigravityQuota,
138
148
  getAntigravityAccountLabel(),
139
149
  ),
140
150
  );
141
151
  }
142
152
 
143
153
  const outputLines = renderAlignedRows(rows);
144
- if (!codexBuckets && outputLines[codexRowIndex] != null) {
154
+ // 회색은 로그인 안 된 경우에만 쓴다. 로그인 상태의 조회 공백은 --% 로만 보인다.
155
+ if (
156
+ outputLines[codexRowIndex] != null &&
157
+ !codexBuckets &&
158
+ !existsSync(CODEX_AUTH_PATH) &&
159
+ !hasBrokerCodexAccounts()
160
+ ) {
145
161
  outputLines[codexRowIndex] = `${DIM}${outputLines[codexRowIndex]}${RESET}`;
146
162
  }
147
163
  // 알림 배너와 TUI 스타일이 HUD 내용에 겹치지 않도록 한다.
@@ -152,7 +168,7 @@ async function main() {
152
168
 
153
169
  main().catch(() => {
154
170
  process.stdout.write(
155
- `\x1b[0m${bold(claudeOrange("c"))}: ${dim("5h:--% (n/a) 1w:--% (n/a) | ctx:--%")}\n`,
171
+ `\x1b[0m${bold(claudeOrange("c"))}: ${dim(`5h:--% ${formatTimeCell("")} 1w:--% ${formatTimeCellDH("")} | CTX:--%`)}\n`,
156
172
  );
157
173
  });
158
174
 
@@ -7,6 +7,7 @@ import {
7
7
  ANTIGRAVITY_REFRESH_FLAG,
8
8
  ANTIGRAVITY_REFRESH_LOCK_PATH,
9
9
  ANTIGRAVITY_SETTINGS_PATH,
10
+ ANTIGRAVITY_SIGNED_IN_GRACE_MS,
10
11
  SPAWN_LOCK_TTL_MS,
11
12
  } from "../constants.mjs";
12
13
  import { decodeJwtEmail, readJson, writeJsonSafe } from "../utils.mjs";
@@ -41,6 +42,18 @@ function isGcpProjectAuth() {
41
42
  return Boolean(readJson(ANTIGRAVITY_SETTINGS_PATH, null)?.gcp?.project);
42
43
  }
43
44
 
45
+ // Keychain 인증은 파일을 남기지 않으므로 최근 쿼터 조회 성공도 로그인 근거로 본다.
46
+ export function getAntigravityAuthKind() {
47
+ if (isGcpProjectAuth()) return "project";
48
+ if (getAntigravityAccountLabel()) return "account";
49
+ const cache = readJson(ANTIGRAVITY_QUOTA_CACHE_PATH, null);
50
+ const signedInAge = Date.now() - Number(cache?.signedInAt);
51
+ return cache?.accountLabel == null &&
52
+ signedInAge < ANTIGRAVITY_SIGNED_IN_GRACE_MS
53
+ ? "account"
54
+ : null;
55
+ }
56
+
44
57
  export function readAntigravityQuotaSnapshot() {
45
58
  // 프로젝트 과금의 기본 응답을 개인 플랜의 0% 사용률로 표시하지 않는다.
46
59
  if (isGcpProjectAuth()) return { data: null, shouldRefresh: false };
@@ -103,10 +116,17 @@ export function refreshAntigravityQuotaCache(execFileSyncFn = execFileSync) {
103
116
  // 실패도 캐시해 인증 오류나 CLI 부재 시 매 렌더마다 실행하지 않는다.
104
117
  }
105
118
  if (accountLabel === getAntigravityAccountLabel()) {
119
+ const previous = readJson(ANTIGRAVITY_QUOTA_CACHE_PATH, null);
120
+ const signedInAt = buckets
121
+ ? Date.now()
122
+ : previous?.accountLabel === accountLabel
123
+ ? previous?.signedInAt
124
+ : undefined;
106
125
  writeJsonSafe(ANTIGRAVITY_QUOTA_CACHE_PATH, {
107
126
  timestamp: Date.now(),
108
127
  accountLabel,
109
128
  buckets,
129
+ signedInAt,
110
130
  });
111
131
  }
112
132
  return buckets;
package/hud/renderers.mjs CHANGED
@@ -22,6 +22,7 @@ import {
22
22
  PERCENT_CELL_WIDTH,
23
23
  PROVIDER_PREFIX_WIDTH,
24
24
  SEVEN_DAY_MS,
25
+ TIME_CELL_INNER_WIDTH,
25
26
  } from "./constants.mjs";
26
27
  import { buildContextUsageView } from "./context-monitor.mjs";
27
28
  import { getTerminalColumns, tierBar, tierDimBar } from "./terminal.mjs";
@@ -124,7 +125,9 @@ export function getMicroLine(
124
125
  segments.push(`${bold(codexWhite("x"))}${dim(":")}${xVal}`);
125
126
  }
126
127
  if (showAntigravity) {
127
- const marker = options.antigravityQuota ? bold(geminiBlue("a")) : dim("a");
128
+ const marker = options.antigravityQuota?.auth
129
+ ? bold(geminiBlue("a"))
130
+ : dim("a");
128
131
  segments.push(
129
132
  `${marker}${dim(":")}${antigravityPercentText(options.antigravityQuota)}${options.antigravityQuota?.stale ? dim("*") : ""}`,
130
133
  );
@@ -134,19 +137,22 @@ export function getMicroLine(
134
137
  }
135
138
 
136
139
  function antigravityPercentText(quota) {
140
+ if (quota?.auth === "project") {
141
+ return geminiBlue("GCP".padStart(PERCENT_CELL_WIDTH));
142
+ }
137
143
  return quota?.usedPercent != null
138
144
  ? colorByProvider(
139
145
  quota.usedPercent,
140
146
  formatPercentCell(quota.usedPercent),
141
147
  geminiBlue,
142
148
  )
143
- : dim("--".padStart(PERCENT_CELL_WIDTH));
149
+ : dim(formatPlaceholderPercentCell());
144
150
  }
145
151
 
146
152
  // context 는 토큰 수 대신 사용률만 보여 준다.
147
153
  function contextPercentText(ctxView) {
148
- const text = ctxView.display === "--" ? "--" : `${ctxView.percent}%`;
149
- return colorByPercent(ctxView.percent, text);
154
+ if (ctxView.display === "--") return dim("--%");
155
+ return colorByPercent(ctxView.percent, `${ctxView.percent}%`);
150
156
  }
151
157
 
152
158
  // ============================================================================
@@ -160,10 +166,10 @@ export function getClaudeRows(currentTier, contextView, claudeUsage) {
160
166
  const weeklyPercent = claudeUsage?.weeklyPercent ?? null;
161
167
  const fiveHourReset = claudeUsage?.fiveHourResetsAt
162
168
  ? formatResetRemaining(claudeUsage.fiveHourResetsAt, FIVE_HOUR_MS)
163
- : "n/a";
169
+ : "";
164
170
  const weeklyReset = claudeUsage?.weeklyResetsAt
165
171
  ? formatResetRemainingDayHour(claudeUsage.weeklyResetsAt, SEVEN_DAY_MS)
166
- : "n/a";
172
+ : "";
167
173
 
168
174
  const hasData = claudeUsage != null;
169
175
 
@@ -198,11 +204,11 @@ export function getClaudeRows(currentTier, contextView, claudeUsage) {
198
204
  const fShort =
199
205
  hasData && fiveHourPercent != null
200
206
  ? colorByProvider(fiveHourPercent, `${fiveHourPercent}%`, claudeOrange)
201
- : dim("--");
207
+ : dim("--%");
202
208
  const wShort =
203
209
  hasData && weeklyPercent != null
204
210
  ? colorByProvider(weeklyPercent, `${weeklyPercent}%`, claudeOrange)
205
- : dim("--");
211
+ : dim("--%");
206
212
  const quotaSection = `${fShort}${dim("/")}${wShort}`;
207
213
  return [{ prefix, left: quotaSection, right: "" }];
208
214
  }
@@ -274,34 +280,13 @@ export function getProviderRow(
274
280
 
275
281
  const prefix = `${bold(markerColor(marker))}:`;
276
282
  if (provider === "antigravity") {
277
- const usedPercent = realQuota?.usedPercent;
278
- const active = usedPercent != null;
279
- const quotaPercent = antigravityPercentText(realQuota);
280
- const right = `${active ? markerColor(accountLabel) : dim(accountLabel)}${realQuota?.stale ? dim(" [stale]") : ""}`;
281
- if (currentTier === "nano" || currentTier === "micro") {
282
- return {
283
- prefix: active ? prefix : dim(`${marker}:`),
284
- left: quotaPercent,
285
- right,
286
- };
287
- }
288
- const bar = active
289
- ? tierBar(currentTier, usedPercent, GEMINI_BLUE)
290
- : currentTier === "full"
291
- ? " ".repeat(GAUGE_WIDTH + 1)
292
- : "";
293
- const reset =
294
- (Date.parse(realQuota?.resetTime) - Date.now() >= ONE_DAY_MS
295
- ? formatResetRemainingDayHour(realQuota.resetTime)
296
- : formatResetRemaining(realQuota?.resetTime)) || "n/a";
297
- const showTime = currentTier === "full" || currentTier === "compact";
298
- // /usage의 모델별 값에는 5h/1w 식별자가 없으므로 창을 추측하지 않는다.
299
- const slot = `${dim("--:")}${bar}${quotaPercent}${showTime ? ` ${dim(formatTimeCell(reset))}` : ""}`;
300
- return {
301
- prefix: active ? prefix : dim(`${marker}:`),
302
- left: `${slot} ${" ".repeat(stripAnsi(slot).length)}`,
303
- right,
304
- };
283
+ return getAntigravityRow(
284
+ currentTier,
285
+ marker,
286
+ markerColor,
287
+ accountLabel,
288
+ realQuota,
289
+ );
305
290
  }
306
291
  const provAnsi = CODEX_WHITE;
307
292
  const provFn = codexWhite;
@@ -337,7 +322,7 @@ export function getProviderRow(
337
322
  };
338
323
  }
339
324
  }
340
- return { prefix: minPrefix, left: dim("--/--"), right: "" };
325
+ return { prefix: minPrefix, left: dim("--%/--%"), right: "" };
341
326
  }
342
327
 
343
328
  if (currentTier === "minimal") {
@@ -397,16 +382,16 @@ export function getProviderRow(
397
382
  weekP != null
398
383
  ? colorByProvider(weekP, formatPercentCell(weekP), provFn)
399
384
  : dim(formatPlaceholderPercentCell());
400
- const fiveReset =
401
- formatResetRemaining(main.primary?.resets_at) || "n/a";
402
- const weekReset =
403
- formatResetRemainingDayHour(main.secondary?.resets_at) || "n/a";
385
+ const fiveReset = formatResetRemaining(main.primary?.resets_at);
386
+ const weekReset = formatResetRemainingDayHour(
387
+ main.secondary?.resets_at,
388
+ );
404
389
  quotaSection = `${dim("5h:")}${fCell} ${dim(formatTimeCell(fiveReset))} ${dim("1w:")}${wCell} ${dim(formatTimeCellDH(weekReset))}`;
405
390
  if (main.mixedWindows) quotaSection += dim("*");
406
391
  }
407
392
  }
408
393
  if (!quotaSection) {
409
- quotaSection = `${dim("5h:")}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))} ${dim("1w:")}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCellDH("--d--h"))}`;
394
+ quotaSection = `${dim("5h:")}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell(""))} ${dim("1w:")}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCellDH(""))}`;
410
395
  }
411
396
  const compactRight = [accountLabel ? markerColor(accountLabel) : ""]
412
397
  .filter(Boolean)
@@ -429,9 +414,8 @@ export function getProviderRow(
429
414
  main.secondary?.used_percent != null
430
415
  ? clampPercent(main.secondary.used_percent)
431
416
  : null;
432
- const fiveReset = formatResetRemaining(main.primary?.resets_at) || "n/a";
433
- const weekReset =
434
- formatResetRemainingDayHour(main.secondary?.resets_at) || "n/a";
417
+ const fiveReset = formatResetRemaining(main.primary?.resets_at);
418
+ const weekReset = formatResetRemainingDayHour(main.secondary?.resets_at);
435
419
  const fCell =
436
420
  fiveP != null
437
421
  ? colorByProvider(fiveP, formatPercentCell(fiveP), provFn)
@@ -459,7 +443,7 @@ export function getProviderRow(
459
443
 
460
444
  // 폴백
461
445
  if (!quotaSection) {
462
- quotaSection = `${dim("5h:")}${tierDimBar(currentTier)}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))} ${dim("1w:")}${tierDimBar(currentTier)}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCellDH("--d--h"))}`;
446
+ quotaSection = `${dim("5h:")}${tierDimBar(currentTier)}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell(""))} ${dim("1w:")}${tierDimBar(currentTier)}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCellDH(""))}`;
463
447
  }
464
448
 
465
449
  const accountSection = `${markerColor(accountLabel)}`;
@@ -469,3 +453,52 @@ export function getProviderRow(
469
453
  right: accountSection,
470
454
  };
471
455
  }
456
+
457
+ // 회색은 로그인 안 된 경우에만 쓴다. GCP 프로젝트 과금은 개인 쿼터가 없을 뿐 정상 상태다.
458
+ function getAntigravityRow(
459
+ currentTier,
460
+ marker,
461
+ markerColor,
462
+ accountLabel,
463
+ quota,
464
+ ) {
465
+ const usedPercent = quota?.usedPercent;
466
+ const hasValue = usedPercent != null;
467
+ const loggedIn = quota?.auth != null;
468
+ const project = quota?.auth === "project";
469
+ const tint = loggedIn ? markerColor : dim;
470
+ const prefix = loggedIn ? `${bold(markerColor(marker))}:` : dim(`${marker}:`);
471
+ const stale = quota?.stale ? dim(" [stale]") : "";
472
+ const right = `${tint(accountLabel)}${stale}`;
473
+ const percent = antigravityPercentText(quota);
474
+ if (currentTier === "nano" || currentTier === "micro") {
475
+ return { prefix, left: percent, right };
476
+ }
477
+ const isFull = currentTier === "full";
478
+ const showTime = isFull || currentTier === "compact";
479
+ const reset =
480
+ Date.parse(quota?.resetTime) - Date.now() >= ONE_DAY_MS
481
+ ? formatResetRemainingDayHour(quota.resetTime)
482
+ : formatResetRemaining(quota?.resetTime);
483
+ const timeCell = showTime ? ` ${dim(formatTimeCell(reset))}` : "";
484
+ // /usage의 모델별 값에는 5h/1w 식별자가 없으므로 창을 추측하지 않는다.
485
+ // 두 창(5h, 1w) 폭에 맞춰 c, x 행과 구분자 열을 맞춘다.
486
+ const windowWidth =
487
+ "5h:".length +
488
+ (isFull ? GAUGE_WIDTH + 1 : 0) +
489
+ PERCENT_CELL_WIDTH +
490
+ (showTime ? TIME_CELL_INNER_WIDTH + 3 : 0);
491
+ let slot;
492
+ if (project) {
493
+ // 쿼터 대신 과금 방식을 막대 자리(막대가 없는 표시에서는 퍼센트 자리)에 보인다.
494
+ const mark = isFull ? `${markerColor("GCP".padEnd(GAUGE_WIDTH))} ` : "";
495
+ const cell = isFull ? dim(formatPlaceholderPercentCell()) : percent;
496
+ slot = `${dim("--:")}${mark}${cell}${timeCell}`;
497
+ } else {
498
+ const bar = hasValue
499
+ ? tierBar(currentTier, usedPercent, GEMINI_BLUE)
500
+ : tierDimBar(currentTier);
501
+ slot = `${dim("--:")}${bar}${percent}${timeCell}`;
502
+ }
503
+ return { prefix, left: padAnsiRight(slot, windowWidth * 2 + 1), right };
504
+ }
package/hud/utils.mjs CHANGED
@@ -145,7 +145,7 @@ export function formatPlaceholderPercentCell() {
145
145
  }
146
146
 
147
147
  export function normalizeTimeToken(value) {
148
- const text = String(value || "n/a");
148
+ const text = String(value || "");
149
149
  const hourMinute = text.match(/^(\d+)h(\d+)m$/);
150
150
  if (hourMinute) {
151
151
  return `${Number(hourMinute[1])}h${String(Number(hourMinute[2])).padStart(2, "0")}m`;
@@ -157,16 +157,20 @@ export function normalizeTimeToken(value) {
157
157
  return text;
158
158
  }
159
159
 
160
+ // 값이 없는 시간 칸은 단위 모양을 남겨 정상 값과 같은 폭으로 그린다.
161
+ function emptyTimeCell(unit) {
162
+ return unit === "dh" ? "(--d--h)" : "(--h--m)";
163
+ }
164
+
160
165
  export function formatTimeCell(value) {
161
166
  const text = normalizeTimeToken(value);
162
- // 시간값(숫자 포함)은 0패딩, 비시간값(n/a 등)은 공백패딩
163
- const padChar = /\d/.test(text) ? "0" : " ";
164
- return `(${text.padStart(TIME_CELL_INNER_WIDTH, padChar)})`;
167
+ if (!/\d/.test(text)) return emptyTimeCell("hm");
168
+ return `(${text.padStart(TIME_CELL_INNER_WIDTH, "0")})`;
165
169
  }
166
170
 
167
- // n/a도 정상 주간 시간과 같은 너비를 유지한다.
168
171
  export function formatTimeCellDH(value) {
169
172
  const text = normalizeTimeToken(value);
173
+ if (!/\d/.test(text)) return emptyTimeCell("dh");
170
174
  return `(${text.padStart(TIME_CELL_INNER_WIDTH, " ")})`;
171
175
  }
172
176
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@triflux/core",
3
- "version": "10.51.0",
4
- "description": "triflux core — CLI routing, pipeline, adapters. Zero native dependencies.",
3
+ "version": "10.52.0",
4
+ "description": "triflux core — CLI routing, adapters, account broker. Zero native dependencies.",
5
5
  "type": "module",
6
6
  "main": "hub/index.mjs",
7
7
  "exports": {
@@ -14,8 +14,6 @@
14
14
  },
15
15
  "dependencies": {
16
16
  "@iarna/toml": "^2.2.5",
17
- "pino": "^10.3.1",
18
- "pino-pretty": "^13.1.3",
19
17
  "zod": "^4.0.0"
20
18
  },
21
19
  "files": [
@@ -12,168 +12,126 @@ const policy = {
12
12
  executor: {
13
13
  profile: "gpt61_sol_high",
14
14
  timeoutSec: 1080,
15
- runMode: "fg",
16
- opusOversight: false,
17
15
  mcpHint: "implement",
18
16
  subcommand: "exec",
19
17
  },
20
18
  codex: {
21
19
  profile: "gpt61_sol_high",
22
20
  timeoutSec: 1080,
23
- runMode: "fg",
24
- opusOversight: false,
25
21
  mcpHint: "implement",
26
22
  subcommand: "exec",
27
23
  },
28
24
  "build-fixer": {
29
25
  profile: "gpt6_luna_high",
30
26
  timeoutSec: 540,
31
- runMode: "fg",
32
- opusOversight: false,
33
27
  mcpHint: "implement",
34
28
  subcommand: "exec",
35
29
  },
36
30
  cleanup: {
37
31
  profile: "gpt61_sol_med",
38
32
  timeoutSec: 540,
39
- runMode: "fg",
40
- opusOversight: false,
41
33
  mcpHint: "implement",
42
34
  subcommand: "exec",
43
35
  },
44
36
  deslop: {
45
37
  profile: "gpt61_sol_med",
46
38
  timeoutSec: 540,
47
- runMode: "fg",
48
- opusOversight: false,
49
39
  mcpHint: "implement",
50
40
  subcommand: "exec",
51
41
  },
52
42
  debugger: {
53
43
  profile: "gpt6_astra_xhigh",
54
44
  timeoutSec: 900,
55
- runMode: "bg",
56
- opusOversight: false,
57
45
  mcpHint: "implement",
58
46
  subcommand: "exec",
59
47
  },
60
48
  "deep-executor": {
61
49
  profile: "gpt6_astra_xhigh",
62
50
  timeoutSec: 3600,
63
- runMode: "bg",
64
- opusOversight: true,
65
51
  mcpHint: "implement",
66
52
  subcommand: "exec",
67
53
  },
68
54
  architect: {
69
55
  profile: "gpt6_astra_xhigh",
70
56
  timeoutSec: 3600,
71
- runMode: "bg",
72
- opusOversight: true,
73
57
  mcpHint: "analyze",
74
58
  subcommand: "exec",
75
59
  },
76
60
  critic: {
77
61
  profile: "gpt6_astra_xhigh",
78
62
  timeoutSec: 3600,
79
- runMode: "bg",
80
- opusOversight: true,
81
63
  mcpHint: "analyze",
82
64
  subcommand: "exec",
83
65
  },
84
66
  planner: {
85
67
  profile: "gpt6_astra_xhigh",
86
68
  timeoutSec: 3600,
87
- runMode: "fg",
88
- opusOversight: true,
89
69
  mcpHint: "analyze",
90
70
  subcommand: "exec",
91
71
  },
92
72
  analyst: {
93
73
  profile: "gpt6_astra_xhigh",
94
74
  timeoutSec: 3600,
95
- runMode: "fg",
96
- opusOversight: true,
97
75
  mcpHint: "analyze",
98
76
  subcommand: "exec",
99
77
  },
100
78
  "code-reviewer": {
101
79
  profile: "gpt61_sol_high",
102
80
  timeoutSec: 1800,
103
- runMode: "bg",
104
- opusOversight: false,
105
81
  mcpHint: "review",
106
82
  subcommand: "review",
107
83
  },
108
84
  "quality-reviewer": {
109
85
  profile: "gpt61_sol_high",
110
86
  timeoutSec: 1800,
111
- runMode: "bg",
112
- opusOversight: false,
113
87
  mcpHint: "review",
114
88
  subcommand: "review",
115
89
  },
116
90
  "security-reviewer": {
117
91
  profile: "gpt6_astra_xhigh",
118
92
  timeoutSec: 1800,
119
- runMode: "bg",
120
- opusOversight: true,
121
93
  mcpHint: "review",
122
94
  subcommand: "review",
123
95
  },
124
96
  scientist: {
125
97
  profile: "gpt61_sol_high",
126
98
  timeoutSec: 1440,
127
- runMode: "bg",
128
- opusOversight: false,
129
99
  mcpHint: "analyze",
130
100
  subcommand: "exec",
131
101
  },
132
102
  "document-specialist": {
133
103
  profile: "gpt61_sol_high",
134
104
  timeoutSec: 1440,
135
- runMode: "bg",
136
- opusOversight: false,
137
105
  mcpHint: "analyze",
138
106
  subcommand: "exec",
139
107
  },
140
108
  "scientist-deep": {
141
109
  profile: "gpt6_astra_xhigh",
142
110
  timeoutSec: 3600,
143
- runMode: "bg",
144
- opusOversight: false,
145
111
  mcpHint: "analyze",
146
112
  subcommand: "exec",
147
113
  },
148
114
  verifier: {
149
115
  profile: "gpt61_sol_high",
150
116
  timeoutSec: 1200,
151
- runMode: "fg",
152
- opusOversight: false,
153
117
  mcpHint: "review",
154
118
  subcommand: "review",
155
119
  },
156
120
  "test-engineer": {
157
121
  profile: "gpt61_sol_high",
158
122
  timeoutSec: 1200,
159
- runMode: "bg",
160
- opusOversight: false,
161
123
  mcpHint: "implement",
162
124
  subcommand: "exec",
163
125
  },
164
126
  "qa-tester": {
165
127
  profile: "gpt61_sol_high",
166
128
  timeoutSec: 1200,
167
- runMode: "bg",
168
- opusOversight: false,
169
129
  mcpHint: "review",
170
130
  subcommand: "review",
171
131
  },
172
132
  spark: {
173
133
  profile: "gpt6_luna_low",
174
134
  timeoutSec: 180,
175
- runMode: "fg",
176
- opusOversight: false,
177
135
  mcpHint: "implement",
178
136
  subcommand: "exec",
179
137
  },
@@ -182,16 +140,12 @@ const policy = {
182
140
  designer: {
183
141
  profile: "gpt6_astra_xhigh",
184
142
  timeoutSec: 900,
185
- runMode: "bg",
186
- opusOversight: false,
187
143
  mcpHint: "implement",
188
144
  subcommand: "exec",
189
145
  },
190
146
  writer: {
191
147
  profile: "gpt6_luna_high",
192
148
  timeoutSec: 900,
193
- runMode: "bg",
194
- opusOversight: false,
195
149
  mcpHint: "implement",
196
150
  subcommand: "exec",
197
151
  },
@@ -319,22 +273,6 @@ export function resolveNestedCodexAgentProfile(
319
273
  return effort.toLowerCase() === "ultra" ? "gpt6_astra_max" : candidate;
320
274
  }
321
275
 
322
- const MIN_TIMEOUT_BY_AGENT = Object.freeze({
323
- "deep-executor": 900,
324
- architect: 900,
325
- planner: 900,
326
- critic: 900,
327
- analyst: 900,
328
- "document-specialist": 900,
329
- scientist: 900,
330
- "scientist-deep": 900,
331
- "code-reviewer": 600,
332
- "security-reviewer": 600,
333
- "quality-reviewer": 600,
334
- executor: 300,
335
- debugger: 300,
336
- });
337
-
338
276
  const EXPECTED_DURATION_BY_AGENT = Object.freeze({
339
277
  writer: 90,
340
278
  verifier: 90,
@@ -409,14 +347,11 @@ export function resolveAgentRoute(
409
347
  provider,
410
348
  policy.profile,
411
349
  policy.timeoutSec,
412
- policy.runMode,
413
- policy.opusOversight,
414
350
  policy.mcpHint,
415
351
  policy.subcommand,
416
352
  normalizedOverride,
417
353
  resolvedOverride,
418
354
  geminiProfileResolver(agent),
419
- MIN_TIMEOUT_BY_AGENT[agent] ?? 120,
420
355
  EXPECTED_DURATION_BY_AGENT[agent] ?? 30,
421
356
  NATIVE_CODEX_AGENTS.has(agent),
422
357
  ];
@@ -495,8 +430,6 @@ async function runCli() {
495
430
  [
496
431
  resolved.profile,
497
432
  resolved.timeoutSec,
498
- resolved.runMode,
499
- resolved.opusOversight,
500
433
  resolved.mcpHint,
501
434
  resolved.subcommand,
502
435
  ].join("\t"),
@@ -12,32 +12,22 @@ export const command = null;
12
12
  const AGENT_PROFILES = {
13
13
  explore: {
14
14
  timeoutSec: 600,
15
- runMode: "fg",
16
- opusOversight: false,
17
15
  model: "haiku",
18
16
  },
19
17
  claude: {
20
18
  timeoutSec: 600,
21
- runMode: "fg",
22
- opusOversight: false,
23
19
  model: "haiku",
24
20
  },
25
21
  "test-engineer": {
26
22
  timeoutSec: 1200,
27
- runMode: "bg",
28
- opusOversight: false,
29
23
  model: "sonnet",
30
24
  },
31
25
  "qa-tester": {
32
26
  timeoutSec: 1200,
33
- runMode: "bg",
34
- opusOversight: false,
35
27
  model: "sonnet",
36
28
  },
37
29
  verifier: {
38
30
  timeoutSec: 1200,
39
- runMode: "fg",
40
- opusOversight: false,
41
31
  model: "sonnet",
42
32
  },
43
33
  };
@@ -62,8 +52,6 @@ export function plan({
62
52
  profile: "claude-native",
63
53
  effort: "n/a",
64
54
  timeoutMs: effectiveTimeoutSec * 1000,
65
- runMode: cfg.runMode,
66
- opusOversight: cfg.opusOversight,
67
55
  model: cfg.model,
68
56
  mcpProfile: mcpProfile === "auto" ? null : mcpProfile,
69
57
  promptLength: prompt.length,