@triflux/remote 10.89.2 → 10.90.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.
@@ -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 { modelFamilyPaths } from "../../scripts/lib/model-family-resolver.mjs";
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
- { run = execFileAsync } = {},
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
- const start = await runLive(
152
- [
153
- "start",
154
- "--cli",
155
- cli,
156
- "--session",
157
- target,
158
- "--name",
159
- name,
160
- "--launcher",
161
- launcher,
162
- "--cwd",
163
- resolve(cwd),
164
- ...(model ? ["--model", model] : []),
165
- ...(effort ? ["--effort", effort] : []),
166
- ],
167
- run,
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(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@triflux/remote",
3
- "version": "10.89.2",
3
+ "version": "10.90.0",
4
4
  "description": "triflux remote — team mode, psmux, MCP workers.",
5
5
  "type": "module",
6
6
  "main": "hub/index.mjs",