@triflux/remote 10.89.2 → 10.91.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/hub/team/lead-launch.mjs +146 -22
- package/package.json +1 -1
package/hub/team/lead-launch.mjs
CHANGED
|
@@ -7,7 +7,15 @@ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
7
7
|
import { basename, dirname, join, resolve } from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
import { promisify } from "node:util";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
modelFamilyPaths,
|
|
12
|
+
resolveModelFamilies,
|
|
13
|
+
} from "../../scripts/lib/model-family-resolver.mjs";
|
|
14
|
+
import {
|
|
15
|
+
isJevEnabled,
|
|
16
|
+
judgeTaskRouting,
|
|
17
|
+
recordJevStat,
|
|
18
|
+
} from "@triflux/core/hub/lib/jev-addon.mjs";
|
|
11
19
|
|
|
12
20
|
const execFileAsync = promisify(execFile);
|
|
13
21
|
const PKG_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
@@ -138,34 +146,150 @@ export async function launchLead(
|
|
|
138
146
|
session,
|
|
139
147
|
model,
|
|
140
148
|
effort,
|
|
141
|
-
launcher,
|
|
149
|
+
launcher = cli,
|
|
150
|
+
task,
|
|
142
151
|
},
|
|
143
|
-
{
|
|
152
|
+
{
|
|
153
|
+
run = execFileAsync,
|
|
154
|
+
jevOptions = {},
|
|
155
|
+
modelOptions = {},
|
|
156
|
+
judgeRouting = judgeTaskRouting,
|
|
157
|
+
} = {},
|
|
144
158
|
) {
|
|
145
159
|
if (!LEAD_CLIS.includes(cli))
|
|
146
160
|
throw new Error(`--cli 는 ${LEAD_CLIS.join(", ")} 중 하나여야 한다`);
|
|
147
161
|
if (!role?.trim()) throw new Error("역할이 필요하다: tfx lead <역할>");
|
|
162
|
+
const routing = {
|
|
163
|
+
enabled: isJevEnabled(jevOptions),
|
|
164
|
+
attempted: false,
|
|
165
|
+
complexity: null,
|
|
166
|
+
recommendedProfile: null,
|
|
167
|
+
recommendedModel: null,
|
|
168
|
+
recommendedEffort: null,
|
|
169
|
+
effectiveModel: model ?? null,
|
|
170
|
+
effectiveEffort: effort ?? null,
|
|
171
|
+
confidence: null,
|
|
172
|
+
source: "default",
|
|
173
|
+
reason: "jev_disabled",
|
|
174
|
+
appliedTo: null,
|
|
175
|
+
};
|
|
176
|
+
if (routing.enabled) {
|
|
177
|
+
routing.attempted = true;
|
|
178
|
+
routing.reason = "no_accepted_recommendation";
|
|
179
|
+
try {
|
|
180
|
+
const recommendation = await judgeRouting(
|
|
181
|
+
{
|
|
182
|
+
prompt: [
|
|
183
|
+
`Role: ${role.trim().slice(0, 200)}. This lead continuously coordinates multiple workers and evaluates their results.`,
|
|
184
|
+
task?.trim() ? `Task: ${task.trim().slice(0, 1000)}` : "",
|
|
185
|
+
]
|
|
186
|
+
.filter(Boolean)
|
|
187
|
+
.join("\n"),
|
|
188
|
+
agentType: "lead",
|
|
189
|
+
cliType: cli,
|
|
190
|
+
},
|
|
191
|
+
jevOptions,
|
|
192
|
+
);
|
|
193
|
+
routing.complexity = recommendation?.complexity ?? null;
|
|
194
|
+
routing.confidence = recommendation?.confidence ?? null;
|
|
195
|
+
if (recommendation?.complexity) {
|
|
196
|
+
routing.recommendedProfile =
|
|
197
|
+
cli === "codex"
|
|
198
|
+
? recommendation.codexProfile
|
|
199
|
+
: cli === "agy"
|
|
200
|
+
? recommendation.agyProfile
|
|
201
|
+
: recommendation.claudeModel;
|
|
202
|
+
const selected =
|
|
203
|
+
cli === "claude"
|
|
204
|
+
? {
|
|
205
|
+
model: recommendation.claudeModel,
|
|
206
|
+
effort: recommendation.claudeEffort,
|
|
207
|
+
}
|
|
208
|
+
: resolveModelFamilies(cli, {
|
|
209
|
+
...modelOptions,
|
|
210
|
+
allowCommand: false,
|
|
211
|
+
persist: false,
|
|
212
|
+
}).profiles[routing.recommendedProfile];
|
|
213
|
+
if (selected?.model && selected?.effort) {
|
|
214
|
+
routing.recommendedModel = selected.model;
|
|
215
|
+
routing.recommendedEffort = selected.effort;
|
|
216
|
+
model = selected.model;
|
|
217
|
+
effort = selected.effort;
|
|
218
|
+
routing.source = "jev";
|
|
219
|
+
routing.reason = "jev_applied";
|
|
220
|
+
routing.appliedTo = "start_args";
|
|
221
|
+
} else routing.reason = "unsupported_profile";
|
|
222
|
+
}
|
|
223
|
+
} catch {
|
|
224
|
+
routing.reason = "jev_or_resolver_failed";
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// 한쪽만 명시해도 쌍을 보존한다. 임의의 래퍼와 셸 명령은 안전하게 해석할 수 없다.
|
|
228
|
+
if (
|
|
229
|
+
routing.effectiveModel !== null ||
|
|
230
|
+
routing.effectiveEffort !== null ||
|
|
231
|
+
launcher !== cli
|
|
232
|
+
) {
|
|
233
|
+
model = routing.effectiveModel ?? undefined;
|
|
234
|
+
effort = routing.effectiveEffort ?? undefined;
|
|
235
|
+
routing.source = "explicit";
|
|
236
|
+
routing.reason =
|
|
237
|
+
routing.effectiveModel !== null || routing.effectiveEffort !== null
|
|
238
|
+
? "explicit_model_effort_preserved"
|
|
239
|
+
: "launcher_preserved";
|
|
240
|
+
routing.appliedTo = null;
|
|
241
|
+
}
|
|
242
|
+
routing.effectiveModel = model ?? null;
|
|
243
|
+
routing.effectiveEffort = effort ?? null;
|
|
148
244
|
const target = session || leadSessionName(cwd);
|
|
149
245
|
const name = leadDisplayName(role.trim());
|
|
150
|
-
const summary = { cli, session: target, name, launcher };
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
246
|
+
const summary = { cli, session: target, name, launcher, routing };
|
|
247
|
+
let start;
|
|
248
|
+
let startReturned = false;
|
|
249
|
+
try {
|
|
250
|
+
start = await runLive(
|
|
251
|
+
[
|
|
252
|
+
"start",
|
|
253
|
+
"--cli",
|
|
254
|
+
cli,
|
|
255
|
+
"--session",
|
|
256
|
+
target,
|
|
257
|
+
"--name",
|
|
258
|
+
name,
|
|
259
|
+
"--launcher",
|
|
260
|
+
launcher,
|
|
261
|
+
"--cwd",
|
|
262
|
+
resolve(cwd),
|
|
263
|
+
...(model ? ["--model", model] : []),
|
|
264
|
+
...(effort ? ["--effort", effort] : []),
|
|
265
|
+
],
|
|
266
|
+
run,
|
|
267
|
+
);
|
|
268
|
+
startReturned = true;
|
|
269
|
+
} finally {
|
|
270
|
+
if (routing.enabled) {
|
|
271
|
+
recordJevStat(
|
|
272
|
+
{
|
|
273
|
+
hook: "lead_model_selection",
|
|
274
|
+
kind: "model_selection",
|
|
275
|
+
source: routing.source,
|
|
276
|
+
reason: startReturned ? routing.reason : "start_failed",
|
|
277
|
+
selectionReason: routing.reason,
|
|
278
|
+
recommendedProfile: routing.recommendedProfile,
|
|
279
|
+
recommendedModel: routing.recommendedModel,
|
|
280
|
+
recommendedEffort: routing.recommendedEffort,
|
|
281
|
+
effectiveModel: routing.effectiveModel,
|
|
282
|
+
effectiveEffort: routing.effectiveEffort,
|
|
283
|
+
confidence: routing.confidence,
|
|
284
|
+
appliedTo: startReturned ? routing.appliedTo : null,
|
|
285
|
+
intervened: startReturned && routing.appliedTo === "start_args",
|
|
286
|
+
startReady: Boolean(start?.ready),
|
|
287
|
+
startStatus: startReturned ? "returned" : "threw",
|
|
288
|
+
},
|
|
289
|
+
jevOptions,
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
169
293
|
if (!start.ready) return { ok: false, ...summary, start };
|
|
170
294
|
// Claude 슬래시 명령은 tmux 입력으로만 실행되고, Claude 안에서 호출해도 SendMessage 로 바뀌지 않게 한다.
|
|
171
295
|
const ask = await runLive(
|