@triflux/core 10.50.2 → 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.
- package/hooks/agy-session-hook.mjs +11 -56
- package/hooks/codex-session-hook.mjs +11 -101
- package/hub/bridge.mjs +65 -1250
- package/hub/cli-adapter-base.mjs +12 -90
- package/hub/codex-adapter.mjs +0 -1
- package/hub/codex-preflight.mjs +5 -28
- package/hub/index.mjs +3 -19
- package/hub/lib/process-utils.mjs +1 -156
- package/hub/lib/spawn-trace.mjs +3 -2
- package/hub/lib/timeout-defaults.mjs +0 -1
- package/hub/team/claude-daemon-control.mjs +1 -2
- package/hub/team/claude-session-projection.mjs +5 -73
- package/hub/team/nativeProxy.mjs +759 -0
- package/hub/team/session-context.mjs +1 -1
- package/hud/cli-policy.mjs +0 -22
- package/hud/constants.mjs +13 -26
- package/hud/hud-qos-status.mjs +42 -54
- package/hud/providers/gemini.mjs +125 -20
- package/hud/renderers.mjs +101 -122
- package/hud/utils.mjs +10 -6
- package/package.json +2 -6
- package/scripts/lib/agent-route-policy.mjs +143 -62
- package/scripts/lib/cli-claude.mjs +0 -14
- package/scripts/lib/env-probe.mjs +2 -131
- package/scripts/lib/gemini-profiles.mjs +2 -69
- package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
- package/scripts/lib/machine-profile.mjs +3 -17
- package/scripts/lib/mcp-filter.mjs +109 -53
- package/scripts/lib/mcp-guard-engine.mjs +40 -98
- package/scripts/lib/mcp-health.mjs +2 -2
- package/scripts/lib/mcp-server-catalog.mjs +2 -2
- package/scripts/lib/psmux-info.mjs +11 -59
- package/scripts/lib/session-presence.mjs +2 -100
- package/scripts/lib/skill-template.mjs +0 -245
- package/hub/adaptive-diagnostic.mjs +0 -350
- package/hub/adaptive-inject.mjs +0 -224
- package/hub/adaptive-memory.mjs +0 -277
- package/hub/adaptive.mjs +0 -172
- package/hub/assign-callbacks.mjs +0 -170
- package/hub/delegator/contracts.mjs +0 -38
- package/hub/delegator/index.mjs +0 -14
- package/hub/delegator/schema/delegator-tools.schema.json +0 -250
- package/hub/delegator/service.mjs +0 -336
- package/hub/delegator/tool-definitions.mjs +0 -35
- package/hub/dynamic-routing-engine.mjs +0 -511
- package/hub/hitl.mjs +0 -202
- package/hub/lib/known-errors.json +0 -72
- package/hub/lib/memory-store.mjs +0 -1001
- package/hub/lib/phase-manager.mjs +0 -493
- package/hub/lib/risk-tier.mjs +0 -53
- package/hub/lib/ssh-command.mjs +0 -231
- package/hub/lib/staged-file-detect.mjs +0 -43
- package/hub/lib/state-snapshot.mjs +0 -293
- package/hub/lib/tfx-route-args.mjs +0 -353
- package/hub/lib/trace-recorder.mjs +0 -153
- package/hub/lib/uuidv7.mjs +0 -45
- package/hub/memory-doctor.mjs +0 -640
- package/hub/middleware/quota-middleware.mjs +0 -70
- package/hub/middleware/request-logger.mjs +0 -191
- package/hub/paths.mjs +0 -24
- package/hub/pipeline/index.mjs +0 -163
- package/hub/pipeline/state.mjs +0 -191
- package/hub/pipeline/transitions.mjs +0 -131
- package/hub/reflexion.mjs +0 -290
- package/hub/role-contract.mjs +0 -314
- package/hub/router.mjs +0 -2026
- package/hub/routing-snapshot.mjs +0 -261
- package/hub/session-fingerprint.mjs +0 -418
- package/hub/state.mjs +0 -300
- package/hub/team/synapse-http.mjs +0 -311
- package/hub/team-bridge.mjs +0 -27
- package/mesh/index.mjs +0 -69
- package/mesh/mesh-budget.mjs +0 -137
- package/mesh/mesh-heartbeat.mjs +0 -100
- package/mesh/mesh-protocol.mjs +0 -96
- package/mesh/mesh-queue.mjs +0 -163
- package/mesh/mesh-registry.mjs +0 -78
- package/mesh/mesh-router.mjs +0 -77
- package/scripts/lib/claudemd-scanner.mjs +0 -223
- package/scripts/lib/context.mjs +0 -67
- package/scripts/lib/dynamic-route-cli.mjs +0 -107
- package/scripts/lib/handoff.mjs +0 -187
- package/scripts/lib/logger.mjs +0 -122
- package/scripts/lib/stealth-fetch.mjs +0 -176
|
@@ -1,176 +1,137 @@
|
|
|
1
1
|
// Canonical Codex role policy. Concrete model IDs stay in profile config files.
|
|
2
2
|
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
3
5
|
import {
|
|
4
6
|
normalizeCodexProfileName,
|
|
5
7
|
resolveCodexProfileConfigValues,
|
|
6
8
|
} from "./codex-profile-config.mjs";
|
|
9
|
+
import { resolveGeminiProfileForPurpose } from "./gemini-profiles.mjs";
|
|
7
10
|
|
|
8
11
|
const policy = {
|
|
9
12
|
executor: {
|
|
10
13
|
profile: "gpt61_sol_high",
|
|
11
14
|
timeoutSec: 1080,
|
|
12
|
-
runMode: "fg",
|
|
13
|
-
opusOversight: false,
|
|
14
15
|
mcpHint: "implement",
|
|
15
16
|
subcommand: "exec",
|
|
16
17
|
},
|
|
17
18
|
codex: {
|
|
18
19
|
profile: "gpt61_sol_high",
|
|
19
20
|
timeoutSec: 1080,
|
|
20
|
-
runMode: "fg",
|
|
21
|
-
opusOversight: false,
|
|
22
21
|
mcpHint: "implement",
|
|
23
22
|
subcommand: "exec",
|
|
24
23
|
},
|
|
25
24
|
"build-fixer": {
|
|
26
25
|
profile: "gpt6_luna_high",
|
|
27
26
|
timeoutSec: 540,
|
|
28
|
-
runMode: "fg",
|
|
29
|
-
opusOversight: false,
|
|
30
27
|
mcpHint: "implement",
|
|
31
28
|
subcommand: "exec",
|
|
32
29
|
},
|
|
33
30
|
cleanup: {
|
|
34
31
|
profile: "gpt61_sol_med",
|
|
35
32
|
timeoutSec: 540,
|
|
36
|
-
runMode: "fg",
|
|
37
|
-
opusOversight: false,
|
|
38
33
|
mcpHint: "implement",
|
|
39
34
|
subcommand: "exec",
|
|
40
35
|
},
|
|
41
36
|
deslop: {
|
|
42
37
|
profile: "gpt61_sol_med",
|
|
43
38
|
timeoutSec: 540,
|
|
44
|
-
runMode: "fg",
|
|
45
|
-
opusOversight: false,
|
|
46
39
|
mcpHint: "implement",
|
|
47
40
|
subcommand: "exec",
|
|
48
41
|
},
|
|
49
42
|
debugger: {
|
|
50
43
|
profile: "gpt6_astra_xhigh",
|
|
51
44
|
timeoutSec: 900,
|
|
52
|
-
runMode: "bg",
|
|
53
|
-
opusOversight: false,
|
|
54
45
|
mcpHint: "implement",
|
|
55
46
|
subcommand: "exec",
|
|
56
47
|
},
|
|
57
48
|
"deep-executor": {
|
|
58
49
|
profile: "gpt6_astra_xhigh",
|
|
59
50
|
timeoutSec: 3600,
|
|
60
|
-
runMode: "bg",
|
|
61
|
-
opusOversight: true,
|
|
62
51
|
mcpHint: "implement",
|
|
63
52
|
subcommand: "exec",
|
|
64
53
|
},
|
|
65
54
|
architect: {
|
|
66
55
|
profile: "gpt6_astra_xhigh",
|
|
67
56
|
timeoutSec: 3600,
|
|
68
|
-
runMode: "bg",
|
|
69
|
-
opusOversight: true,
|
|
70
57
|
mcpHint: "analyze",
|
|
71
58
|
subcommand: "exec",
|
|
72
59
|
},
|
|
73
60
|
critic: {
|
|
74
61
|
profile: "gpt6_astra_xhigh",
|
|
75
62
|
timeoutSec: 3600,
|
|
76
|
-
runMode: "bg",
|
|
77
|
-
opusOversight: true,
|
|
78
63
|
mcpHint: "analyze",
|
|
79
64
|
subcommand: "exec",
|
|
80
65
|
},
|
|
81
66
|
planner: {
|
|
82
67
|
profile: "gpt6_astra_xhigh",
|
|
83
68
|
timeoutSec: 3600,
|
|
84
|
-
runMode: "fg",
|
|
85
|
-
opusOversight: true,
|
|
86
69
|
mcpHint: "analyze",
|
|
87
70
|
subcommand: "exec",
|
|
88
71
|
},
|
|
89
72
|
analyst: {
|
|
90
73
|
profile: "gpt6_astra_xhigh",
|
|
91
74
|
timeoutSec: 3600,
|
|
92
|
-
runMode: "fg",
|
|
93
|
-
opusOversight: true,
|
|
94
75
|
mcpHint: "analyze",
|
|
95
76
|
subcommand: "exec",
|
|
96
77
|
},
|
|
97
78
|
"code-reviewer": {
|
|
98
79
|
profile: "gpt61_sol_high",
|
|
99
80
|
timeoutSec: 1800,
|
|
100
|
-
runMode: "bg",
|
|
101
|
-
opusOversight: false,
|
|
102
81
|
mcpHint: "review",
|
|
103
82
|
subcommand: "review",
|
|
104
83
|
},
|
|
105
84
|
"quality-reviewer": {
|
|
106
85
|
profile: "gpt61_sol_high",
|
|
107
86
|
timeoutSec: 1800,
|
|
108
|
-
runMode: "bg",
|
|
109
|
-
opusOversight: false,
|
|
110
87
|
mcpHint: "review",
|
|
111
88
|
subcommand: "review",
|
|
112
89
|
},
|
|
113
90
|
"security-reviewer": {
|
|
114
91
|
profile: "gpt6_astra_xhigh",
|
|
115
92
|
timeoutSec: 1800,
|
|
116
|
-
runMode: "bg",
|
|
117
|
-
opusOversight: true,
|
|
118
93
|
mcpHint: "review",
|
|
119
94
|
subcommand: "review",
|
|
120
95
|
},
|
|
121
96
|
scientist: {
|
|
122
97
|
profile: "gpt61_sol_high",
|
|
123
98
|
timeoutSec: 1440,
|
|
124
|
-
runMode: "bg",
|
|
125
|
-
opusOversight: false,
|
|
126
99
|
mcpHint: "analyze",
|
|
127
100
|
subcommand: "exec",
|
|
128
101
|
},
|
|
129
102
|
"document-specialist": {
|
|
130
103
|
profile: "gpt61_sol_high",
|
|
131
104
|
timeoutSec: 1440,
|
|
132
|
-
runMode: "bg",
|
|
133
|
-
opusOversight: false,
|
|
134
105
|
mcpHint: "analyze",
|
|
135
106
|
subcommand: "exec",
|
|
136
107
|
},
|
|
137
108
|
"scientist-deep": {
|
|
138
109
|
profile: "gpt6_astra_xhigh",
|
|
139
110
|
timeoutSec: 3600,
|
|
140
|
-
runMode: "bg",
|
|
141
|
-
opusOversight: false,
|
|
142
111
|
mcpHint: "analyze",
|
|
143
112
|
subcommand: "exec",
|
|
144
113
|
},
|
|
145
114
|
verifier: {
|
|
146
115
|
profile: "gpt61_sol_high",
|
|
147
116
|
timeoutSec: 1200,
|
|
148
|
-
runMode: "fg",
|
|
149
|
-
opusOversight: false,
|
|
150
117
|
mcpHint: "review",
|
|
151
118
|
subcommand: "review",
|
|
152
119
|
},
|
|
153
120
|
"test-engineer": {
|
|
154
121
|
profile: "gpt61_sol_high",
|
|
155
122
|
timeoutSec: 1200,
|
|
156
|
-
runMode: "bg",
|
|
157
|
-
opusOversight: false,
|
|
158
123
|
mcpHint: "implement",
|
|
159
124
|
subcommand: "exec",
|
|
160
125
|
},
|
|
161
126
|
"qa-tester": {
|
|
162
127
|
profile: "gpt61_sol_high",
|
|
163
128
|
timeoutSec: 1200,
|
|
164
|
-
runMode: "bg",
|
|
165
|
-
opusOversight: false,
|
|
166
129
|
mcpHint: "review",
|
|
167
130
|
subcommand: "review",
|
|
168
131
|
},
|
|
169
132
|
spark: {
|
|
170
133
|
profile: "gpt6_luna_low",
|
|
171
134
|
timeoutSec: 180,
|
|
172
|
-
runMode: "fg",
|
|
173
|
-
opusOversight: false,
|
|
174
135
|
mcpHint: "implement",
|
|
175
136
|
subcommand: "exec",
|
|
176
137
|
},
|
|
@@ -179,16 +140,12 @@ const policy = {
|
|
|
179
140
|
designer: {
|
|
180
141
|
profile: "gpt6_astra_xhigh",
|
|
181
142
|
timeoutSec: 900,
|
|
182
|
-
runMode: "bg",
|
|
183
|
-
opusOversight: false,
|
|
184
143
|
mcpHint: "implement",
|
|
185
144
|
subcommand: "exec",
|
|
186
145
|
},
|
|
187
146
|
writer: {
|
|
188
147
|
profile: "gpt6_luna_high",
|
|
189
148
|
timeoutSec: 900,
|
|
190
|
-
runMode: "bg",
|
|
191
|
-
opusOversight: false,
|
|
192
149
|
mcpHint: "implement",
|
|
193
150
|
subcommand: "exec",
|
|
194
151
|
},
|
|
@@ -198,7 +155,7 @@ function freezePolicy(entry) {
|
|
|
198
155
|
return Object.freeze({ ...entry });
|
|
199
156
|
}
|
|
200
157
|
|
|
201
|
-
|
|
158
|
+
const CODEX_AGENT_POLICY = Object.freeze(
|
|
202
159
|
Object.fromEntries(
|
|
203
160
|
Object.entries(policy).map(([agent, entry]) => [
|
|
204
161
|
agent,
|
|
@@ -206,9 +163,9 @@ export const CODEX_AGENT_POLICY = Object.freeze(
|
|
|
206
163
|
]),
|
|
207
164
|
),
|
|
208
165
|
);
|
|
209
|
-
|
|
166
|
+
const DEFAULT_CODEX_AGENT = "executor";
|
|
210
167
|
|
|
211
|
-
|
|
168
|
+
const CANONICAL_CODEX_PROFILE_OVERRIDES = Object.freeze(
|
|
212
169
|
new Set([
|
|
213
170
|
"gpt6_luna_low",
|
|
214
171
|
"gpt6_luna_high",
|
|
@@ -219,7 +176,7 @@ export const CANONICAL_CODEX_PROFILE_OVERRIDES = Object.freeze(
|
|
|
219
176
|
"gpt6_astra_ultra",
|
|
220
177
|
]),
|
|
221
178
|
);
|
|
222
|
-
|
|
179
|
+
const ULTRA_ELIGIBLE_CODEX_AGENTS = Object.freeze(
|
|
223
180
|
new Set(["deep-executor", "scientist-deep"]),
|
|
224
181
|
);
|
|
225
182
|
|
|
@@ -316,39 +273,163 @@ export function resolveNestedCodexAgentProfile(
|
|
|
316
273
|
return effort.toLowerCase() === "ultra" ? "gpt6_astra_max" : candidate;
|
|
317
274
|
}
|
|
318
275
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
276
|
+
const EXPECTED_DURATION_BY_AGENT = Object.freeze({
|
|
277
|
+
writer: 90,
|
|
278
|
+
verifier: 90,
|
|
279
|
+
"qa-tester": 90,
|
|
280
|
+
executor: 300,
|
|
281
|
+
debugger: 300,
|
|
282
|
+
"test-engineer": 300,
|
|
283
|
+
"code-reviewer": 600,
|
|
284
|
+
"security-reviewer": 600,
|
|
285
|
+
architect: 600,
|
|
286
|
+
planner: 600,
|
|
287
|
+
critic: 600,
|
|
288
|
+
analyst: 600,
|
|
289
|
+
scientist: 900,
|
|
290
|
+
"scientist-deep": 900,
|
|
291
|
+
"deep-executor": 900,
|
|
292
|
+
"document-specialist": 900,
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
const NATIVE_CODEX_AGENTS = new Set([
|
|
296
|
+
"explore",
|
|
297
|
+
"verifier",
|
|
298
|
+
"test-engineer",
|
|
299
|
+
"qa-tester",
|
|
300
|
+
]);
|
|
301
|
+
|
|
302
|
+
function resolveDirectCodexRoutePolicy(agent) {
|
|
303
|
+
if (agent === "explore") {
|
|
304
|
+
return { ...CODEX_AGENT_POLICY.spark, timeoutSec: 600, mcpHint: "analyze" };
|
|
305
|
+
}
|
|
306
|
+
if (agent === "antigravity" || agent === "agy") {
|
|
307
|
+
return CODEX_AGENT_POLICY.designer;
|
|
308
|
+
}
|
|
309
|
+
return resolveCodexAgentPolicy(agent);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export function resolveAgentRoute(
|
|
313
|
+
agent,
|
|
314
|
+
map,
|
|
315
|
+
profileOverride = "auto",
|
|
316
|
+
nested = false,
|
|
317
|
+
geminiProfileResolver = resolveGeminiProfileForPurpose,
|
|
318
|
+
) {
|
|
319
|
+
if (!/^[A-Za-z0-9_.-]+$/.test(agent)) {
|
|
320
|
+
throw new Error(`invalid agent: ${agent}`);
|
|
321
|
+
}
|
|
322
|
+
if (!Object.hasOwn(map, agent)) {
|
|
323
|
+
throw new Error(
|
|
324
|
+
`알 수 없는 에이전트 타입: ${agent}; available: ${Object.keys(map).join(", ")}`,
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
const provider = map[agent];
|
|
328
|
+
if (!["codex", "antigravity", "claude"].includes(provider)) {
|
|
329
|
+
throw new Error(`invalid provider for ${agent}: ${provider}`);
|
|
330
|
+
}
|
|
331
|
+
const policy = resolveDirectCodexRoutePolicy(agent);
|
|
332
|
+
const normalizedOverride = normalizeCodexProfileName(profileOverride);
|
|
333
|
+
let resolvedOverride = policy.profile;
|
|
334
|
+
if (normalizedOverride !== "auto") {
|
|
335
|
+
try {
|
|
336
|
+
resolvedOverride = resolveCodexAgentProfile(agent, {
|
|
337
|
+
profileOverride: normalizedOverride,
|
|
338
|
+
nested,
|
|
339
|
+
});
|
|
340
|
+
} catch {
|
|
341
|
+
throw new Error(
|
|
342
|
+
`TFX_CODEX_PROFILE 값이 유효하지 않습니다: ${profileOverride}`,
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return [
|
|
347
|
+
provider,
|
|
348
|
+
policy.profile,
|
|
349
|
+
policy.timeoutSec,
|
|
350
|
+
policy.mcpHint,
|
|
351
|
+
policy.subcommand,
|
|
352
|
+
normalizedOverride,
|
|
353
|
+
resolvedOverride,
|
|
354
|
+
geminiProfileResolver(agent),
|
|
355
|
+
EXPECTED_DURATION_BY_AGENT[agent] ?? 30,
|
|
356
|
+
NATIVE_CODEX_AGENTS.has(agent),
|
|
357
|
+
];
|
|
324
358
|
}
|
|
325
359
|
|
|
326
360
|
function parseCliArgs(argv) {
|
|
327
361
|
const args = [...argv];
|
|
328
362
|
let format = "json";
|
|
329
363
|
let agent = DEFAULT_CODEX_AGENT;
|
|
364
|
+
let mapFile = "";
|
|
365
|
+
let profileOverride = "auto";
|
|
366
|
+
let geminiProfilesModule = "";
|
|
367
|
+
let nested = false;
|
|
330
368
|
while (args.length > 0) {
|
|
331
369
|
const flag = args.shift();
|
|
332
370
|
if (flag === "--format") format = args.shift() || "";
|
|
333
371
|
else if (flag === "--agent") agent = args.shift() || "";
|
|
372
|
+
else if (flag === "--map-file") mapFile = args.shift() || "";
|
|
373
|
+
else if (flag === "--profile-override")
|
|
374
|
+
profileOverride = args.shift() || "";
|
|
375
|
+
else if (flag === "--gemini-profiles-module")
|
|
376
|
+
geminiProfilesModule = args.shift() || "";
|
|
377
|
+
else if (flag === "--nested") nested = true;
|
|
334
378
|
else throw new Error(`unknown argument: ${flag}`);
|
|
335
379
|
}
|
|
336
|
-
if (!new Set(["json", "tsv"]).has(format)) {
|
|
380
|
+
if (!new Set(["json", "tsv", "route"]).has(format)) {
|
|
337
381
|
throw new Error(`unsupported format: ${format}`);
|
|
338
382
|
}
|
|
339
|
-
return {
|
|
383
|
+
return {
|
|
384
|
+
format,
|
|
385
|
+
agent,
|
|
386
|
+
mapFile,
|
|
387
|
+
profileOverride,
|
|
388
|
+
geminiProfilesModule,
|
|
389
|
+
nested,
|
|
390
|
+
};
|
|
340
391
|
}
|
|
341
392
|
|
|
342
|
-
function runCli() {
|
|
343
|
-
const {
|
|
393
|
+
async function runCli() {
|
|
394
|
+
const {
|
|
395
|
+
format,
|
|
396
|
+
agent,
|
|
397
|
+
mapFile,
|
|
398
|
+
profileOverride,
|
|
399
|
+
geminiProfilesModule,
|
|
400
|
+
nested,
|
|
401
|
+
} = parseCliArgs(process.argv.slice(2));
|
|
402
|
+
if (format === "route") {
|
|
403
|
+
if (!mapFile) throw new Error("--map-file is required for route format");
|
|
404
|
+
const map = JSON.parse(readFileSync(mapFile, "utf8"));
|
|
405
|
+
let resolver = resolveGeminiProfileForPurpose;
|
|
406
|
+
if (geminiProfilesModule) {
|
|
407
|
+
try {
|
|
408
|
+
const module = await import(pathToFileURL(geminiProfilesModule).href);
|
|
409
|
+
resolver = (agent) => {
|
|
410
|
+
try {
|
|
411
|
+
return module.resolveGeminiProfileForPurpose(agent) || "flash38";
|
|
412
|
+
} catch {
|
|
413
|
+
return "flash38";
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
} catch {
|
|
417
|
+
resolver = () => "flash38";
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
process.stdout.write(
|
|
421
|
+
resolveAgentRoute(agent, map, profileOverride, nested, resolver).join(
|
|
422
|
+
"\x1e",
|
|
423
|
+
),
|
|
424
|
+
);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
344
427
|
const resolved = resolveCodexAgentPolicy(agent);
|
|
345
428
|
if (format === "tsv") {
|
|
346
429
|
process.stdout.write(
|
|
347
430
|
[
|
|
348
431
|
resolved.profile,
|
|
349
432
|
resolved.timeoutSec,
|
|
350
|
-
resolved.runMode,
|
|
351
|
-
resolved.opusOversight,
|
|
352
433
|
resolved.mcpHint,
|
|
353
434
|
resolved.subcommand,
|
|
354
435
|
].join("\t"),
|
|
@@ -360,7 +441,7 @@ function runCli() {
|
|
|
360
441
|
|
|
361
442
|
if (import.meta.url === new URL(process.argv[1], "file:").href) {
|
|
362
443
|
try {
|
|
363
|
-
runCli();
|
|
444
|
+
await runCli();
|
|
364
445
|
} catch (error) {
|
|
365
446
|
process.stderr.write(`[agent-route-policy] ${error.message}\n`);
|
|
366
447
|
process.exitCode = 1;
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
// 책임: Claude 는 외부 CLI 가 아닌 Claude Code Agent 도구로 dispatch 된다. plan() 은
|
|
4
4
|
// command=null 을 반환하고 호출자가 Agent() 로 라우팅하도록 신호한다.
|
|
5
5
|
//
|
|
6
|
-
// 출처: scripts/tfx-route.sh route_agent() L1062-L1063 (explore|claude),
|
|
7
|
-
// L1196-L1203 (TFX_CLI_MODE=gemini fallback), TFX_VERIFIER_OVERRIDE=claude.
|
|
8
6
|
// timeoutSec은 lane 예상 소요 시간(advisory)이다. hard ceiling 집행은 Bash route가 소유한다.
|
|
9
7
|
|
|
10
8
|
export const id = "claude";
|
|
@@ -14,32 +12,22 @@ export const command = null;
|
|
|
14
12
|
const AGENT_PROFILES = {
|
|
15
13
|
explore: {
|
|
16
14
|
timeoutSec: 600,
|
|
17
|
-
runMode: "fg",
|
|
18
|
-
opusOversight: false,
|
|
19
15
|
model: "haiku",
|
|
20
16
|
},
|
|
21
17
|
claude: {
|
|
22
18
|
timeoutSec: 600,
|
|
23
|
-
runMode: "fg",
|
|
24
|
-
opusOversight: false,
|
|
25
19
|
model: "haiku",
|
|
26
20
|
},
|
|
27
21
|
"test-engineer": {
|
|
28
22
|
timeoutSec: 1200,
|
|
29
|
-
runMode: "bg",
|
|
30
|
-
opusOversight: false,
|
|
31
23
|
model: "sonnet",
|
|
32
24
|
},
|
|
33
25
|
"qa-tester": {
|
|
34
26
|
timeoutSec: 1200,
|
|
35
|
-
runMode: "bg",
|
|
36
|
-
opusOversight: false,
|
|
37
27
|
model: "sonnet",
|
|
38
28
|
},
|
|
39
29
|
verifier: {
|
|
40
30
|
timeoutSec: 1200,
|
|
41
|
-
runMode: "fg",
|
|
42
|
-
opusOversight: false,
|
|
43
31
|
model: "sonnet",
|
|
44
32
|
},
|
|
45
33
|
};
|
|
@@ -64,8 +52,6 @@ export function plan({
|
|
|
64
52
|
profile: "claude-native",
|
|
65
53
|
effort: "n/a",
|
|
66
54
|
timeoutMs: effectiveTimeoutSec * 1000,
|
|
67
|
-
runMode: cfg.runMode,
|
|
68
|
-
opusOversight: cfg.opusOversight,
|
|
69
55
|
model: cfg.model,
|
|
70
56
|
mcpProfile: mcpProfile === "auto" ? null : mcpProfile,
|
|
71
57
|
promptLength: prompt.length,
|
|
@@ -1,76 +1,12 @@
|
|
|
1
|
-
import { execSync, spawn } from "node:child_process";
|
|
2
1
|
import { createHash } from "node:crypto";
|
|
3
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
3
|
import { homedir } from "node:os";
|
|
5
|
-
import {
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
7
|
-
import { resolveHubPortForContext } from "../../hub/hub-lifecycle.mjs";
|
|
4
|
+
import { join } from "node:path";
|
|
8
5
|
import { whichCommand, whichCommandAsync } from "../../hub/platform.mjs";
|
|
9
6
|
|
|
10
|
-
const HUB_DEFAULT_PORT = 27888;
|
|
11
|
-
const DEFAULT_STATUS_URL = "http://127.0.0.1:27888/status";
|
|
12
|
-
const _sab = new Int32Array(new SharedArrayBuffer(4));
|
|
13
7
|
const CLI_PROBE_CACHE = new Map();
|
|
14
8
|
const CLI_PROBE_PROMISES = new Map();
|
|
15
9
|
|
|
16
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
-
const DEFAULT_PKG_ROOT = join(dirname(__filename), "..", "..");
|
|
18
|
-
|
|
19
|
-
function sleepSync(ms) {
|
|
20
|
-
Atomics.wait(_sab, 0, 0, ms);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function fetchHubStatus({
|
|
24
|
-
execSyncFn = execSync,
|
|
25
|
-
statusUrl = resolveDefaultStatusUrl(),
|
|
26
|
-
timeout = 3000,
|
|
27
|
-
} = {}) {
|
|
28
|
-
const response = execSyncFn(`curl -sf ${statusUrl}`, {
|
|
29
|
-
timeout,
|
|
30
|
-
encoding: "utf8",
|
|
31
|
-
windowsHide: true,
|
|
32
|
-
});
|
|
33
|
-
const data = JSON.parse(response);
|
|
34
|
-
return {
|
|
35
|
-
ok: true,
|
|
36
|
-
state: data?.hub?.state || "unknown",
|
|
37
|
-
pid: data?.pid,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function resolveDefaultStatusUrl(
|
|
42
|
-
env = process.env,
|
|
43
|
-
cwd = process.cwd(),
|
|
44
|
-
) {
|
|
45
|
-
const port = resolveHubPortForContext({
|
|
46
|
-
env,
|
|
47
|
-
cwd,
|
|
48
|
-
defaultPort: HUB_DEFAULT_PORT,
|
|
49
|
-
});
|
|
50
|
-
return `http://127.0.0.1:${port}/status`;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function resolveStatusUrlForContext({
|
|
54
|
-
statusUrl,
|
|
55
|
-
env = process.env,
|
|
56
|
-
cwd = process.cwd(),
|
|
57
|
-
} = {}) {
|
|
58
|
-
try {
|
|
59
|
-
const url = new URL(String(statusUrl));
|
|
60
|
-
url.port = String(
|
|
61
|
-
resolveHubPortForContext({
|
|
62
|
-
port: url.port,
|
|
63
|
-
env,
|
|
64
|
-
cwd,
|
|
65
|
-
defaultPort: HUB_DEFAULT_PORT,
|
|
66
|
-
}),
|
|
67
|
-
);
|
|
68
|
-
return url.toString();
|
|
69
|
-
} catch {
|
|
70
|
-
return resolveDefaultStatusUrl(env, cwd);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
10
|
function normalizeCliName(name) {
|
|
75
11
|
return String(name ?? "").trim() || null;
|
|
76
12
|
}
|
|
@@ -125,7 +61,7 @@ async function resolveCliProbe(name, options = {}) {
|
|
|
125
61
|
return toCliResult(path);
|
|
126
62
|
}
|
|
127
63
|
|
|
128
|
-
|
|
64
|
+
async function checkCli(name, options = {}) {
|
|
129
65
|
const cliName = normalizeCliName(name);
|
|
130
66
|
if (!cliName) return { ok: false };
|
|
131
67
|
|
|
@@ -168,11 +104,6 @@ export async function probeClis(names, options = {}) {
|
|
|
168
104
|
return mapCliResults(cliNames, results);
|
|
169
105
|
}
|
|
170
106
|
|
|
171
|
-
export function resetCliProbeCache() {
|
|
172
|
-
CLI_PROBE_CACHE.clear();
|
|
173
|
-
CLI_PROBE_PROMISES.clear();
|
|
174
|
-
}
|
|
175
|
-
|
|
176
107
|
export function detectCodexAuthState({
|
|
177
108
|
homeDir = homedir(),
|
|
178
109
|
existsSyncFn = existsSync,
|
|
@@ -237,63 +168,3 @@ export function detectCodexPlan(options = {}) {
|
|
|
237
168
|
const { plan, source } = detectCodexAuthState(options);
|
|
238
169
|
return { plan, source };
|
|
239
170
|
}
|
|
240
|
-
|
|
241
|
-
export function checkHub({
|
|
242
|
-
env = process.env,
|
|
243
|
-
cwd = process.cwd(),
|
|
244
|
-
pkgRoot = DEFAULT_PKG_ROOT,
|
|
245
|
-
statusUrl = resolveDefaultStatusUrl(env, cwd),
|
|
246
|
-
restart = true,
|
|
247
|
-
requestTimeoutMs = 3000,
|
|
248
|
-
pollAttempts = 8,
|
|
249
|
-
pollIntervalMs = 500,
|
|
250
|
-
execSyncFn = execSync,
|
|
251
|
-
spawnFn = spawn,
|
|
252
|
-
existsSyncFn = existsSync,
|
|
253
|
-
sleepSyncFn = sleepSync,
|
|
254
|
-
} = {}) {
|
|
255
|
-
const guardedStatusUrl = resolveStatusUrlForContext({ statusUrl, env, cwd });
|
|
256
|
-
try {
|
|
257
|
-
return fetchHubStatus({
|
|
258
|
-
execSyncFn,
|
|
259
|
-
statusUrl: guardedStatusUrl,
|
|
260
|
-
timeout: requestTimeoutMs,
|
|
261
|
-
});
|
|
262
|
-
} catch {}
|
|
263
|
-
|
|
264
|
-
if (!restart) return { ok: false, state: "unreachable", restart: "disabled" };
|
|
265
|
-
|
|
266
|
-
const serverPath = join(pkgRoot, "hub", "server.mjs");
|
|
267
|
-
if (!existsSyncFn(serverPath))
|
|
268
|
-
return { ok: false, state: "unreachable", restart: "no_server" };
|
|
269
|
-
|
|
270
|
-
try {
|
|
271
|
-
const child = spawnFn(process.execPath, [serverPath], {
|
|
272
|
-
env: { ...env, TFX_HUB_PORT: String(new URL(guardedStatusUrl).port) },
|
|
273
|
-
detached: true,
|
|
274
|
-
stdio: "ignore",
|
|
275
|
-
windowsHide: true,
|
|
276
|
-
});
|
|
277
|
-
child.unref();
|
|
278
|
-
} catch {
|
|
279
|
-
return { ok: false, state: "unreachable", restart: "spawn_failed" };
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
for (let i = 0; i < pollAttempts; i++) {
|
|
283
|
-
sleepSyncFn(pollIntervalMs);
|
|
284
|
-
try {
|
|
285
|
-
const status = fetchHubStatus({
|
|
286
|
-
execSyncFn,
|
|
287
|
-
statusUrl: guardedStatusUrl,
|
|
288
|
-
timeout: Math.min(requestTimeoutMs, 1000),
|
|
289
|
-
});
|
|
290
|
-
if (status.state === "healthy") {
|
|
291
|
-
return { ...status, restarted: true };
|
|
292
|
-
}
|
|
293
|
-
} catch {}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return { ok: false, state: "unreachable", restart: "timeout" };
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
export { DEFAULT_PKG_ROOT, DEFAULT_STATUS_URL };
|