@triflux/core 10.25.1 → 10.26.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/hooks/hook-registry.json +1 -1
- package/hooks/keyword-rules.json +8 -8
- package/hooks/safety-guard.mjs +8 -1
- package/hub/bridge.mjs +221 -1
- package/hub/delegator/contracts.mjs +6 -1
- package/hub/delegator/schema/delegator-tools.schema.json +1 -1
- package/hub/delegator/service.mjs +7 -2
- package/hub/gemini-adapter.mjs +27 -35
- package/hub/routing/q-learning.mjs +3 -3
- package/hub/team/claude-daemon-control.mjs +1130 -0
- package/hub/team/retry-state-machine.mjs +83 -14
- package/hub/workers/worker-utils.mjs +1 -1
- package/hud/constants.mjs +6 -0
- package/hud/hud-qos-status.mjs +14 -10
- package/hud/providers/gemini.mjs +26 -1
- package/hud/renderers.mjs +27 -4
- package/package.json +1 -1
- package/scripts/lib/cli-codex.mjs +3 -3
- package/scripts/lib/cli-gemini.mjs +20 -50
- package/scripts/lib/keyword-rules.mjs +1 -1
- package/scripts/lib/psmux-info.mjs +50 -5
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
readFileSync,
|
|
22
22
|
writeFileSync,
|
|
23
23
|
} from "node:fs";
|
|
24
|
-
import { dirname } from "node:path";
|
|
24
|
+
import { dirname, join } from "node:path";
|
|
25
25
|
|
|
26
26
|
export const STATES = Object.freeze({
|
|
27
27
|
PLANNING: "PLANNING",
|
|
@@ -38,16 +38,12 @@ export const MODES = Object.freeze({
|
|
|
38
38
|
ESCALATE: "auto-escalate",
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
// sonnet-4-6 단계는 제거: gpt-5.5 가 코드/추론/비용 모두 우위 + 5.3-codex 가성비
|
|
47
|
-
// 단계가 더 적합한 중간 격상.
|
|
41
|
+
const ESCALATION_CHAIN_CONFIG_PATH = ".triflux/config/escalation-chain.json";
|
|
42
|
+
|
|
43
|
+
// Escalation chain (2026-05-27 정책):
|
|
44
|
+
// 1. codex gpt-5.5 — 코드/추론/비용에서 mini/5.3-codex 중간 단계를 대체
|
|
45
|
+
// 2. claude opus-4-7 — 최종 수단
|
|
48
46
|
const DEFAULT_ESCALATION_CHAIN = Object.freeze([
|
|
49
|
-
Object.freeze({ cli: "codex", model: "gpt-5.4-mini" }),
|
|
50
|
-
Object.freeze({ cli: "codex", model: "gpt-5.3-codex" }),
|
|
51
47
|
Object.freeze({ cli: "codex", model: "gpt-5.5" }),
|
|
52
48
|
Object.freeze({ cli: "claude", model: "opus-4-7" }),
|
|
53
49
|
]);
|
|
@@ -56,9 +52,7 @@ const STUCK_THRESHOLD = 3;
|
|
|
56
52
|
|
|
57
53
|
export function createRetryStateMachine(options = {}) {
|
|
58
54
|
const mode = options.mode || MODES.BOUNDED;
|
|
59
|
-
const cliChain =
|
|
60
|
-
? options.cliChain.slice()
|
|
61
|
-
: DEFAULT_ESCALATION_CHAIN.slice();
|
|
55
|
+
const cliChain = resolveEscalationChain(options);
|
|
62
56
|
const maxIterationsInput =
|
|
63
57
|
typeof options.maxIterations === "number" ? options.maxIterations : null;
|
|
64
58
|
const maxIterations =
|
|
@@ -205,7 +199,10 @@ export function createRetryStateMachine(options = {}) {
|
|
|
205
199
|
state.lastFailureReason = snapshot.lastFailureReason || null;
|
|
206
200
|
state.cliIndex = Number(snapshot.cliIndex) || 0;
|
|
207
201
|
if (Array.isArray(snapshot.cliChain) && snapshot.cliChain.length > 0) {
|
|
208
|
-
state.cliChain =
|
|
202
|
+
state.cliChain = normalizeEscalationChain(
|
|
203
|
+
snapshot.cliChain,
|
|
204
|
+
"snapshot.cliChain",
|
|
205
|
+
);
|
|
209
206
|
}
|
|
210
207
|
if (snapshot.mode) state.mode = snapshot.mode;
|
|
211
208
|
if (snapshot.sessionId !== undefined) state.sessionId = snapshot.sessionId;
|
|
@@ -227,6 +224,78 @@ export function createRetryStateMachine(options = {}) {
|
|
|
227
224
|
};
|
|
228
225
|
}
|
|
229
226
|
|
|
227
|
+
function resolveEscalationChain(options) {
|
|
228
|
+
if (Array.isArray(options.cliChain)) {
|
|
229
|
+
return normalizeEscalationChain(options.cliChain, "options.cliChain");
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const projectRoot = options.projectRoot || process.cwd();
|
|
233
|
+
return (
|
|
234
|
+
loadEscalationChainOverride(projectRoot) ||
|
|
235
|
+
normalizeEscalationChain(
|
|
236
|
+
DEFAULT_ESCALATION_CHAIN,
|
|
237
|
+
"DEFAULT_ESCALATION_CHAIN",
|
|
238
|
+
)
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function loadEscalationChainOverride(projectRoot = process.cwd()) {
|
|
243
|
+
const configPath = join(projectRoot, ESCALATION_CHAIN_CONFIG_PATH);
|
|
244
|
+
if (!existsSync(configPath)) return null;
|
|
245
|
+
|
|
246
|
+
const raw = readFileSync(configPath, "utf8");
|
|
247
|
+
let parsed;
|
|
248
|
+
try {
|
|
249
|
+
parsed = JSON.parse(raw);
|
|
250
|
+
} catch (err) {
|
|
251
|
+
throw new Error(
|
|
252
|
+
`escalation-chain override 파싱 실패: ${configPath} — 원인: ${err.message} — 수정: JSON 문법 확인 또는 파일 삭제로 DEFAULT_ESCALATION_CHAIN 사용`,
|
|
253
|
+
{ cause: err },
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
257
|
+
throw new Error(`${configPath}: expected object`);
|
|
258
|
+
}
|
|
259
|
+
if (parsed.version !== undefined && parsed.version !== 1) {
|
|
260
|
+
throw new Error(`${configPath}: unsupported version ${parsed.version}`);
|
|
261
|
+
}
|
|
262
|
+
return normalizeEscalationChain(parsed.chain, configPath);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function normalizeEscalationChain(chain, source) {
|
|
266
|
+
if (!Array.isArray(chain) || chain.length === 0) {
|
|
267
|
+
throw new Error(`${source}: chain must be a non-empty array`);
|
|
268
|
+
}
|
|
269
|
+
return chain.map((entry, index) =>
|
|
270
|
+
normalizeEscalationChainEntry(entry, index, source),
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function normalizeEscalationChainEntry(entry, index, source) {
|
|
275
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
276
|
+
throw new Error(`${source}: chain[${index}] must be an object`);
|
|
277
|
+
}
|
|
278
|
+
if (typeof entry.cli !== "string" || entry.cli.trim() === "") {
|
|
279
|
+
throw new Error(
|
|
280
|
+
`${source}: chain[${index}].cli must be a non-empty string`,
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
if (typeof entry.model !== "string" || entry.model.trim() === "") {
|
|
284
|
+
throw new Error(
|
|
285
|
+
`${source}: chain[${index}].model must be a non-empty string`,
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const normalized = {
|
|
290
|
+
cli: entry.cli,
|
|
291
|
+
model: entry.model,
|
|
292
|
+
};
|
|
293
|
+
if (typeof entry.profile === "string" && entry.profile.trim() !== "") {
|
|
294
|
+
normalized.profile = entry.profile;
|
|
295
|
+
}
|
|
296
|
+
return normalized;
|
|
297
|
+
}
|
|
298
|
+
|
|
230
299
|
function persistTransition(stateFile, entry) {
|
|
231
300
|
const dir = dirname(stateFile);
|
|
232
301
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// hub/workers/worker-utils.mjs — 워커 공통 유틸리티
|
|
2
|
-
// claude-worker,
|
|
2
|
+
// claude-worker, Antigravity route compatibility worker, pipe 등에서 공유하는 순수 유틸 함수 모음.
|
|
3
3
|
|
|
4
4
|
export const DEFAULT_TIMEOUT_MS = 15 * 60 * 1000;
|
|
5
5
|
export const DEFAULT_KILL_GRACE_MS = 1000;
|
package/hud/constants.mjs
CHANGED
|
@@ -138,6 +138,11 @@ export const SPAWN_LOCK_TTL_MS = 30 * 1000; // 30초 spawn dedup
|
|
|
138
138
|
|
|
139
139
|
// Gemini 쿼터 API 관련
|
|
140
140
|
export const GEMINI_OAUTH_PATH = join(homedir(), ".gemini", "oauth_creds.json");
|
|
141
|
+
export const ANTIGRAVITY_OAUTH_PATHS = [
|
|
142
|
+
join(homedir(), ".gemini", "antigravity-cli", "oauth_creds.json"),
|
|
143
|
+
join(homedir(), ".gemini", "antigravity-cli", "credentials.json"),
|
|
144
|
+
GEMINI_OAUTH_PATH,
|
|
145
|
+
];
|
|
141
146
|
export const GEMINI_QUOTA_CACHE_PATH = join(
|
|
142
147
|
homedir(),
|
|
143
148
|
".claude",
|
|
@@ -162,6 +167,7 @@ export const TFX_PREFLIGHT_CACHE_PATH = join(
|
|
|
162
167
|
"cache",
|
|
163
168
|
"tfx-preflight.json",
|
|
164
169
|
);
|
|
170
|
+
export const TFX_PREFLIGHT_CACHE_STALE_MS = 60 * 60 * 1000;
|
|
165
171
|
export const GEMINI_RPM_TRACKER_PATH = join(
|
|
166
172
|
homedir(),
|
|
167
173
|
".claude",
|
package/hud/hud-qos-status.mjs
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
GEMINI_SESSION_REFRESH_FLAG,
|
|
27
27
|
QOS_PATH,
|
|
28
28
|
TFX_PREFLIGHT_CACHE_PATH,
|
|
29
|
+
TFX_PREFLIGHT_CACHE_STALE_MS,
|
|
29
30
|
} from "./constants.mjs";
|
|
30
31
|
import {
|
|
31
32
|
buildContextUsageView,
|
|
@@ -50,6 +51,7 @@ import {
|
|
|
50
51
|
import {
|
|
51
52
|
buildGeminiAuthContext,
|
|
52
53
|
fetchGeminiQuota,
|
|
54
|
+
getAntigravityEmail,
|
|
53
55
|
getGeminiEmail,
|
|
54
56
|
readGeminiQuotaSnapshot,
|
|
55
57
|
readGeminiSessionSnapshot,
|
|
@@ -111,10 +113,12 @@ async function main() {
|
|
|
111
113
|
|
|
112
114
|
const qosProfile = readJson(QOS_PATH, { providers: {} });
|
|
113
115
|
const preflightCache = readJson(TFX_PREFLIGHT_CACHE_PATH, null);
|
|
116
|
+
const preflightTimestamp = Number(preflightCache?.timestamp);
|
|
117
|
+
const preflightFresh =
|
|
118
|
+
Number.isFinite(preflightTimestamp) &&
|
|
119
|
+
Date.now() - preflightTimestamp <= TFX_PREFLIGHT_CACHE_STALE_MS;
|
|
114
120
|
const antigravityReady =
|
|
115
|
-
|
|
116
|
-
preflightCache?.antigravity?.ok === true ||
|
|
117
|
-
preflightCache?.available_agents?.includes("antigravity");
|
|
121
|
+
preflightFresh && preflightCache?.antigravity?.ok === true;
|
|
118
122
|
const accountsConfig = readJson(ACCOUNTS_CONFIG_PATH, { providers: {} });
|
|
119
123
|
const accountsState = readJson(ACCOUNTS_STATE_PATH, { providers: {} });
|
|
120
124
|
const claudeUsageSnapshot = readClaudeUsageSnapshot();
|
|
@@ -150,6 +154,7 @@ async function main() {
|
|
|
150
154
|
const claudeUsage = claudeUsageSnapshot.data;
|
|
151
155
|
const codexEmail = getCodexEmail();
|
|
152
156
|
const geminiEmail = getGeminiEmail();
|
|
157
|
+
const antigravityEmail = getAntigravityEmail();
|
|
153
158
|
const codexBuckets = codexSnapshot.buckets;
|
|
154
159
|
const geminiSession = geminiSessionSnapshot.session;
|
|
155
160
|
const geminiQuota = geminiQuotaSnapshot.quota;
|
|
@@ -206,9 +211,9 @@ async function main() {
|
|
|
206
211
|
contextView,
|
|
207
212
|
claudeUsage,
|
|
208
213
|
codexBuckets,
|
|
209
|
-
geminiSession,
|
|
210
|
-
geminiBucket,
|
|
211
|
-
combinedSvPct,
|
|
214
|
+
antigravityReady ? null : geminiSession,
|
|
215
|
+
antigravityReady ? null : geminiBucket,
|
|
216
|
+
antigravityReady ? Math.round((codexSv ?? 0) * 100) : combinedSvPct,
|
|
212
217
|
antigravityReady ? "a" : "g",
|
|
213
218
|
);
|
|
214
219
|
process.stdout.write(`\x1b[0m${microLine}\n`);
|
|
@@ -252,9 +257,9 @@ async function main() {
|
|
|
252
257
|
qosProfile,
|
|
253
258
|
accountsConfig,
|
|
254
259
|
accountsState,
|
|
255
|
-
geminiQuotaData,
|
|
256
|
-
antigravityReady ?
|
|
257
|
-
geminiSv,
|
|
260
|
+
antigravityReady ? { type: "antigravity" } : geminiQuotaData,
|
|
261
|
+
antigravityReady ? antigravityEmail : geminiEmail,
|
|
262
|
+
antigravityReady ? null : geminiSv,
|
|
258
263
|
null,
|
|
259
264
|
),
|
|
260
265
|
];
|
|
@@ -284,7 +289,6 @@ async function main() {
|
|
|
284
289
|
// 비활성 프로바이더 dim 처리: 데이터 없으면 전체 줄 dim
|
|
285
290
|
const codexActive = codexBuckets != null;
|
|
286
291
|
const geminiActive =
|
|
287
|
-
antigravityReady ||
|
|
288
292
|
(geminiSession?.total || 0) > 0 ||
|
|
289
293
|
geminiBucket != null ||
|
|
290
294
|
geminiProBucket != null ||
|
package/hud/providers/gemini.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import https from "node:https";
|
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import {
|
|
11
|
+
ANTIGRAVITY_OAUTH_PATHS,
|
|
11
12
|
GEMINI_API_TIMEOUT_MS,
|
|
12
13
|
GEMINI_OAUTH_PATH,
|
|
13
14
|
GEMINI_PROJECT_CACHE_PATH,
|
|
@@ -82,15 +83,39 @@ export function deriveGeminiLimits(bucket) {
|
|
|
82
83
|
};
|
|
83
84
|
}
|
|
84
85
|
|
|
86
|
+
function getCredentialEmail(credential) {
|
|
87
|
+
return (
|
|
88
|
+
decodeJwtEmail(credential?.id_token) ||
|
|
89
|
+
credential?.email ||
|
|
90
|
+
credential?.account?.email ||
|
|
91
|
+
credential?.user?.email ||
|
|
92
|
+
credential?.profile?.email ||
|
|
93
|
+
null
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
85
97
|
export function getGeminiEmail() {
|
|
86
98
|
try {
|
|
87
99
|
const oauth = readJson(GEMINI_OAUTH_PATH, null);
|
|
88
|
-
return
|
|
100
|
+
return getCredentialEmail(oauth);
|
|
89
101
|
} catch {
|
|
90
102
|
return null;
|
|
91
103
|
}
|
|
92
104
|
}
|
|
93
105
|
|
|
106
|
+
export function getAntigravityEmail() {
|
|
107
|
+
for (const oauthPath of ANTIGRAVITY_OAUTH_PATHS) {
|
|
108
|
+
try {
|
|
109
|
+
const oauth = readJson(oauthPath, null);
|
|
110
|
+
const email = getCredentialEmail(oauth);
|
|
111
|
+
if (email) return email;
|
|
112
|
+
} catch {
|
|
113
|
+
// Try the next known Antigravity credential path.
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
94
119
|
export function buildGeminiAuthContext(accountId) {
|
|
95
120
|
const oauth = readJson(GEMINI_OAUTH_PATH, null);
|
|
96
121
|
const tokenSource =
|
package/hud/renderers.mjs
CHANGED
|
@@ -434,6 +434,16 @@ export function getAccountLabel(
|
|
|
434
434
|
return label;
|
|
435
435
|
}
|
|
436
436
|
|
|
437
|
+
function formatAntigravityQuotaSection(currentTier) {
|
|
438
|
+
if (currentTier === "minimal") {
|
|
439
|
+
return `${dim("q:")}${dim(formatPlaceholderPercentCell())}`;
|
|
440
|
+
}
|
|
441
|
+
if (currentTier === "compact") {
|
|
442
|
+
return `${dim("q:")}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))}`;
|
|
443
|
+
}
|
|
444
|
+
return `${dim("q:")}${tierDimBar(currentTier)}${dim(formatPlaceholderPercentCell())} ${dim(formatTimeCell("n/a"))}`;
|
|
445
|
+
}
|
|
446
|
+
|
|
437
447
|
export function getProviderRow(
|
|
438
448
|
currentTier,
|
|
439
449
|
provider,
|
|
@@ -470,6 +480,9 @@ export function getProviderRow(
|
|
|
470
480
|
|
|
471
481
|
if (currentTier === "nano" || currentTier === "micro") {
|
|
472
482
|
const minPrefix = `${bold(markerColor(`${marker}`))}:`;
|
|
483
|
+
if (provider === "antigravity") {
|
|
484
|
+
return { prefix: minPrefix, left: dim("--"), right: "" };
|
|
485
|
+
}
|
|
473
486
|
if (realQuota?.type === "codex") {
|
|
474
487
|
const main =
|
|
475
488
|
realQuota.buckets.codex ||
|
|
@@ -498,7 +511,7 @@ export function getProviderRow(
|
|
|
498
511
|
};
|
|
499
512
|
}
|
|
500
513
|
}
|
|
501
|
-
if (realQuota?.type === "gemini") {
|
|
514
|
+
if (provider === "gemini" && realQuota?.type === "gemini") {
|
|
502
515
|
const pools = realQuota.pools || {};
|
|
503
516
|
if (pools.pro || pools.flash) {
|
|
504
517
|
const pP = pools.pro
|
|
@@ -526,6 +539,9 @@ export function getProviderRow(
|
|
|
526
539
|
}
|
|
527
540
|
|
|
528
541
|
if (currentTier === "minimal") {
|
|
542
|
+
if (provider === "antigravity") {
|
|
543
|
+
quotaSection = formatAntigravityQuotaSection(currentTier);
|
|
544
|
+
}
|
|
529
545
|
if (realQuota?.type === "codex") {
|
|
530
546
|
const main =
|
|
531
547
|
realQuota.buckets.codex ||
|
|
@@ -550,7 +566,7 @@ export function getProviderRow(
|
|
|
550
566
|
quotaSection = `${dim("5h:")}${fCell} ${dim("1w:")}${wCell}`;
|
|
551
567
|
}
|
|
552
568
|
}
|
|
553
|
-
if (realQuota?.type === "gemini") {
|
|
569
|
+
if (provider === "gemini" && realQuota?.type === "gemini") {
|
|
554
570
|
const pools = realQuota.pools || {};
|
|
555
571
|
if (pools.pro || pools.flash) {
|
|
556
572
|
const slot = (bucket, label) => {
|
|
@@ -579,6 +595,9 @@ export function getProviderRow(
|
|
|
579
595
|
}
|
|
580
596
|
|
|
581
597
|
if (currentTier === "compact") {
|
|
598
|
+
if (provider === "antigravity") {
|
|
599
|
+
quotaSection = formatAntigravityQuotaSection(currentTier);
|
|
600
|
+
}
|
|
582
601
|
if (realQuota?.type === "codex") {
|
|
583
602
|
const main =
|
|
584
603
|
realQuota.buckets.codex ||
|
|
@@ -610,7 +629,7 @@ export function getProviderRow(
|
|
|
610
629
|
quotaSection = `${dim("5h:")}${fCell} ${dim(formatTimeCell(fiveReset))} ${dim("1w:")}${wCell} ${dim(formatTimeCellDH(weekReset))}`;
|
|
611
630
|
}
|
|
612
631
|
}
|
|
613
|
-
if (realQuota?.type === "gemini") {
|
|
632
|
+
if (provider === "gemini" && realQuota?.type === "gemini") {
|
|
614
633
|
const pools = realQuota.pools || {};
|
|
615
634
|
const hasAnyPool = pools.pro || pools.flash;
|
|
616
635
|
if (hasAnyPool) {
|
|
@@ -644,6 +663,10 @@ export function getProviderRow(
|
|
|
644
663
|
}
|
|
645
664
|
|
|
646
665
|
// full tier
|
|
666
|
+
if (provider === "antigravity") {
|
|
667
|
+
quotaSection = formatAntigravityQuotaSection(currentTier);
|
|
668
|
+
}
|
|
669
|
+
|
|
647
670
|
if (realQuota?.type === "codex") {
|
|
648
671
|
const main =
|
|
649
672
|
realQuota.buckets.codex ||
|
|
@@ -686,7 +709,7 @@ export function getProviderRow(
|
|
|
686
709
|
}
|
|
687
710
|
}
|
|
688
711
|
|
|
689
|
-
if (realQuota?.type === "gemini") {
|
|
712
|
+
if (provider === "gemini" && realQuota?.type === "gemini") {
|
|
690
713
|
const pools = realQuota.pools || {};
|
|
691
714
|
const hasAnyPool = pools.pro || pools.flash;
|
|
692
715
|
|
package/package.json
CHANGED
|
@@ -33,14 +33,14 @@ const AGENT_PROFILES = {
|
|
|
33
33
|
mcpHint: "implement",
|
|
34
34
|
},
|
|
35
35
|
cleanup: {
|
|
36
|
-
profile: "
|
|
36
|
+
profile: "gpt55_med",
|
|
37
37
|
timeoutSec: 540,
|
|
38
38
|
runMode: "fg",
|
|
39
39
|
opusOversight: false,
|
|
40
40
|
mcpHint: "implement",
|
|
41
41
|
},
|
|
42
42
|
deslop: {
|
|
43
|
-
profile: "
|
|
43
|
+
profile: "gpt55_med",
|
|
44
44
|
timeoutSec: 540,
|
|
45
45
|
runMode: "fg",
|
|
46
46
|
opusOversight: false,
|
|
@@ -157,7 +157,7 @@ const AGENT_PROFILES = {
|
|
|
157
157
|
subcommand: "review",
|
|
158
158
|
},
|
|
159
159
|
spark: {
|
|
160
|
-
profile: "
|
|
160
|
+
profile: "gpt55_low",
|
|
161
161
|
timeoutSec: 180,
|
|
162
162
|
runMode: "fg",
|
|
163
163
|
opusOversight: false,
|
|
@@ -1,39 +1,11 @@
|
|
|
1
|
-
// cli-gemini.mjs — Gemini
|
|
2
|
-
//
|
|
3
|
-
// 책임: Gemini 프로파일/모델/timeout 매핑. resolve_gemini_profile() 의 결과는 Phase 1 에서
|
|
4
|
-
// 통합한다 (Phase 0 은 라벨만 전달).
|
|
5
|
-
//
|
|
6
|
-
// 출처: scripts/tfx-route.sh route_agent() L1045-L1050.
|
|
1
|
+
// cli-gemini.mjs — legacy Gemini adapter alias.
|
|
2
|
+
// The public "gemini" label remains accepted, but the executable plan is AGY.
|
|
7
3
|
|
|
8
|
-
|
|
9
|
-
export const cliType = "gemini";
|
|
10
|
-
export const command = "gemini";
|
|
11
|
-
|
|
12
|
-
const AGENT_PROFILES = {
|
|
13
|
-
designer: {
|
|
14
|
-
profile: "pro31",
|
|
15
|
-
timeoutSec: 900,
|
|
16
|
-
runMode: "bg",
|
|
17
|
-
opusOversight: false,
|
|
18
|
-
mcpHint: "docs",
|
|
19
|
-
},
|
|
20
|
-
gemini: {
|
|
21
|
-
profile: "pro31",
|
|
22
|
-
timeoutSec: 900,
|
|
23
|
-
runMode: "bg",
|
|
24
|
-
opusOversight: false,
|
|
25
|
-
mcpHint: "docs",
|
|
26
|
-
},
|
|
27
|
-
writer: {
|
|
28
|
-
profile: "flash3",
|
|
29
|
-
timeoutSec: 900,
|
|
30
|
-
runMode: "bg",
|
|
31
|
-
opusOversight: false,
|
|
32
|
-
mcpHint: "docs",
|
|
33
|
-
},
|
|
34
|
-
};
|
|
4
|
+
import * as agyAdapter from "./cli-agy.mjs";
|
|
35
5
|
|
|
36
|
-
const
|
|
6
|
+
export const id = "gemini";
|
|
7
|
+
export const cliType = "antigravity";
|
|
8
|
+
export const command = "agy";
|
|
37
9
|
|
|
38
10
|
export function plan({
|
|
39
11
|
agent,
|
|
@@ -45,23 +17,21 @@ export function plan({
|
|
|
45
17
|
if (!agent) {
|
|
46
18
|
throw new Error("[cli-gemini] agent required");
|
|
47
19
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
args: ["-m", cfg.profile, "-y", "--prompt"],
|
|
56
|
-
timeoutMs: effectiveTimeoutSec * 1000,
|
|
57
|
-
runMode: cfg.runMode,
|
|
58
|
-
opusOversight: cfg.opusOversight,
|
|
59
|
-
mcpProfile: mcpProfile === "auto" ? cfg.mcpHint : mcpProfile,
|
|
60
|
-
promptLength: prompt.length,
|
|
61
|
-
contextFile: contextFile || null,
|
|
62
|
-
};
|
|
20
|
+
return agyAdapter.plan({
|
|
21
|
+
agent: agent === "gemini" ? "antigravity" : agent,
|
|
22
|
+
prompt,
|
|
23
|
+
mcpProfile,
|
|
24
|
+
timeoutSec,
|
|
25
|
+
contextFile,
|
|
26
|
+
});
|
|
63
27
|
}
|
|
64
28
|
|
|
65
29
|
export function describe() {
|
|
66
|
-
return {
|
|
30
|
+
return {
|
|
31
|
+
id,
|
|
32
|
+
cliType,
|
|
33
|
+
command,
|
|
34
|
+
aliases: ["gemini"],
|
|
35
|
+
agents: ["gemini", "designer", "writer", "antigravity", "agy"],
|
|
36
|
+
};
|
|
67
37
|
}
|
|
@@ -12,14 +12,14 @@ export const PSMUX_OPTIONAL_COMMANDS = ["detach-client"];
|
|
|
12
12
|
|
|
13
13
|
// Windows 패키지 매니저 — winget/scoop/choco 는 Windows 전용
|
|
14
14
|
export const PSMUX_INSTALL_COMMANDS = [
|
|
15
|
-
"winget install
|
|
15
|
+
"winget install psmux",
|
|
16
16
|
"scoop install psmux",
|
|
17
17
|
"choco install psmux",
|
|
18
18
|
"cargo install psmux",
|
|
19
19
|
];
|
|
20
20
|
|
|
21
21
|
export const PSMUX_UPDATE_COMMANDS = [
|
|
22
|
-
"winget upgrade
|
|
22
|
+
"winget upgrade psmux",
|
|
23
23
|
"scoop update psmux",
|
|
24
24
|
"choco upgrade psmux",
|
|
25
25
|
"cargo install psmux --force",
|
|
@@ -42,14 +42,15 @@ export const PSMUX_INSTALL_COMMANDS_LINUX = ["cargo install psmux"];
|
|
|
42
42
|
export const PSMUX_UPDATE_COMMANDS_LINUX = ["cargo install psmux --force"];
|
|
43
43
|
|
|
44
44
|
// PR #258 OS-primary 분기 정책: macOS/Linux 는 tmux 가 POSIX 표준 primary,
|
|
45
|
-
// psmux 는
|
|
45
|
+
// psmux 는 선택형이다. 따라서 macOS/Linux 에서 "psmux 미설치"는 fallback 사유가
|
|
46
|
+
// 아니며, tmux 까지 없을 때만 native/in-process fallback 을 고려한다.
|
|
46
47
|
export const PSMUX_FALLBACK_NOTE_DARWIN =
|
|
47
48
|
"macOS 에서는 tmux 가 표준 멀티플렉서이며, " +
|
|
48
|
-
"
|
|
49
|
+
"psmux 미설치는 fallback 사유가 아닙니다. tmux 도 없을 때만 in-process/native fallback 을 사용합니다.";
|
|
49
50
|
|
|
50
51
|
export const PSMUX_FALLBACK_NOTE_LINUX =
|
|
51
52
|
"Linux 에서는 tmux 가 표준 멀티플렉서이며, " +
|
|
52
|
-
"
|
|
53
|
+
"psmux 미설치는 fallback 사유가 아닙니다. tmux 도 없을 때만 in-process/native fallback 을 사용합니다.";
|
|
53
54
|
|
|
54
55
|
export function getPsmuxInstallCommandsFor(platform = process.platform) {
|
|
55
56
|
if (platform === "darwin") return PSMUX_INSTALL_COMMANDS_DARWIN;
|
|
@@ -177,3 +178,47 @@ export function probePsmuxSupport(options = {}) {
|
|
|
177
178
|
};
|
|
178
179
|
}
|
|
179
180
|
}
|
|
181
|
+
|
|
182
|
+
export function probePrimaryMultiplexerSupport(options = {}) {
|
|
183
|
+
const platform = options.platform || process.platform;
|
|
184
|
+
const execFileSyncFn = options.execFileSyncFn || execFileSync;
|
|
185
|
+
|
|
186
|
+
if (platform === "win32") {
|
|
187
|
+
return {
|
|
188
|
+
kind: "psmux",
|
|
189
|
+
...probePsmuxSupport(options),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const bin = options.bin || "tmux";
|
|
194
|
+
try {
|
|
195
|
+
const versionOutput = execFileSyncFn(bin, ["-V"], {
|
|
196
|
+
encoding: "utf8",
|
|
197
|
+
timeout: 2000,
|
|
198
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
199
|
+
windowsHide: true,
|
|
200
|
+
});
|
|
201
|
+
return {
|
|
202
|
+
ok: true,
|
|
203
|
+
installed: true,
|
|
204
|
+
kind: "tmux",
|
|
205
|
+
version: String(versionOutput || "").trim() || null,
|
|
206
|
+
recommendedVersion: null,
|
|
207
|
+
recommended: true,
|
|
208
|
+
missingCommands: [],
|
|
209
|
+
missingOptionalCommands: [],
|
|
210
|
+
};
|
|
211
|
+
} catch (error) {
|
|
212
|
+
return {
|
|
213
|
+
ok: false,
|
|
214
|
+
installed: false,
|
|
215
|
+
kind: "tmux",
|
|
216
|
+
version: null,
|
|
217
|
+
recommendedVersion: null,
|
|
218
|
+
recommended: false,
|
|
219
|
+
missingCommands: ["tmux"],
|
|
220
|
+
missingOptionalCommands: [],
|
|
221
|
+
error: error?.message || String(error),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|