triflux 10.9.22 → 10.9.23
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/workers/delegator-mcp.mjs +129 -1
- package/package.json +1 -1
- package/scripts/lib/mcp-filter.mjs +3 -19
|
@@ -14,6 +14,7 @@ import * as z from "zod";
|
|
|
14
14
|
import { resolveBashExecutable } from "../lib/bash-path.mjs";
|
|
15
15
|
import { CodexMcpWorker } from "./codex-mcp.mjs";
|
|
16
16
|
import { GeminiWorker } from "./gemini-worker.mjs";
|
|
17
|
+
import { runHeadlessWithCleanup } from "../team/headless.mjs";
|
|
17
18
|
|
|
18
19
|
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
19
20
|
|
|
@@ -321,6 +322,18 @@ const DelegateInputSchema = z.object({
|
|
|
321
322
|
teamAgentName: z.string().optional().describe("TFX_TEAM_AGENT_NAME"),
|
|
322
323
|
teamLeadName: z.string().optional().describe("TFX_TEAM_LEAD_NAME"),
|
|
323
324
|
hubUrl: z.string().optional().describe("TFX_HUB_URL"),
|
|
325
|
+
workers: z
|
|
326
|
+
.array(
|
|
327
|
+
z.object({
|
|
328
|
+
provider: z.enum(["codex", "gemini"]).describe("워커 provider"),
|
|
329
|
+
agentType: z.string().default("executor").describe("역할명"),
|
|
330
|
+
prompt: z.string().describe("워커별 프롬프트"),
|
|
331
|
+
mcpProfile: z.string().default("auto").describe("MCP 프로필"),
|
|
332
|
+
model: z.string().optional().describe("모델 오버라이드"),
|
|
333
|
+
}),
|
|
334
|
+
)
|
|
335
|
+
.optional()
|
|
336
|
+
.describe("병렬 멀티워커 목록. 지정 시 psmux 기반 병렬 실행"),
|
|
324
337
|
});
|
|
325
338
|
|
|
326
339
|
const DelegateStatusInputSchema = z.object({
|
|
@@ -341,7 +354,7 @@ const DelegateOutputSchema = z.object({
|
|
|
341
354
|
jobId: z.string().optional(),
|
|
342
355
|
job_id: z.string().optional(),
|
|
343
356
|
mode: z.enum(["sync", "async"]).optional(),
|
|
344
|
-
status: z.enum(["running", "completed", "failed"]).optional(),
|
|
357
|
+
status: z.enum(["running", "completed", "failed", "partial"]).optional(),
|
|
345
358
|
error: z.string().optional(),
|
|
346
359
|
providerRequested: z.string().optional(),
|
|
347
360
|
providerResolved: z.string().nullable().optional(),
|
|
@@ -357,6 +370,20 @@ const DelegateOutputSchema = z.object({
|
|
|
357
370
|
threadId: z.string().nullable().optional(),
|
|
358
371
|
sessionKey: z.string().nullable().optional(),
|
|
359
372
|
conversationOpen: z.boolean().optional(),
|
|
373
|
+
workerResults: z
|
|
374
|
+
.array(
|
|
375
|
+
z.object({
|
|
376
|
+
cli: z.string(),
|
|
377
|
+
role: z.string().optional(),
|
|
378
|
+
paneName: z.string(),
|
|
379
|
+
matched: z.boolean(),
|
|
380
|
+
exitCode: z.number().nullable(),
|
|
381
|
+
output: z.string(),
|
|
382
|
+
}),
|
|
383
|
+
)
|
|
384
|
+
.optional()
|
|
385
|
+
.describe("멀티워커 개별 결과"),
|
|
386
|
+
sessionName: z.string().optional().describe("psmux 세션명"),
|
|
360
387
|
});
|
|
361
388
|
|
|
362
389
|
function isTeamRouteRequested(args) {
|
|
@@ -791,6 +818,23 @@ export class DelegatorMcpWorker {
|
|
|
791
818
|
"위임 실행을 시작합니다.",
|
|
792
819
|
);
|
|
793
820
|
|
|
821
|
+
// 멀티워커 분기: workers 배열이 있으면 headless psmux 병렬 실행
|
|
822
|
+
if (Array.isArray(args.workers) && args.workers.length > 0) {
|
|
823
|
+
try {
|
|
824
|
+
const result = await this._executeMultiWorker(args, extra);
|
|
825
|
+
await emitProgress(extra, DIRECT_PROGRESS_DONE, 100, "위임이 완료되었습니다.");
|
|
826
|
+
return result;
|
|
827
|
+
} catch (error) {
|
|
828
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
829
|
+
return createErrorPayload(message, {
|
|
830
|
+
mode: "sync",
|
|
831
|
+
providerRequested: "multi",
|
|
832
|
+
agentType: args.agentType,
|
|
833
|
+
transport: "headless-psmux",
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
794
838
|
const runViaRoute = this._shouldUseRoute(args);
|
|
795
839
|
|
|
796
840
|
try {
|
|
@@ -816,6 +860,90 @@ export class DelegatorMcpWorker {
|
|
|
816
860
|
}
|
|
817
861
|
}
|
|
818
862
|
|
|
863
|
+
async _executeMultiWorker(args, extra) {
|
|
864
|
+
await emitProgress(
|
|
865
|
+
extra,
|
|
866
|
+
DIRECT_PROGRESS_START,
|
|
867
|
+
100,
|
|
868
|
+
"멀티워커 psmux 병렬 실행을 시작합니다.",
|
|
869
|
+
);
|
|
870
|
+
|
|
871
|
+
// workers 배열을 headless assignments 형식으로 변환
|
|
872
|
+
const assignments = args.workers.map((w) => {
|
|
873
|
+
// provider를 CLI 이름으로 매핑 (codex/gemini 그대로)
|
|
874
|
+
const cli = w.provider;
|
|
875
|
+
const role = w.agentType || "executor";
|
|
876
|
+
// buildDirectPrompt와 동일한 context 처리
|
|
877
|
+
const prompt = withContext(String(w.prompt || ""), args.contextFile);
|
|
878
|
+
return { cli, prompt, role };
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
// 타임아웃: 워커 중 가장 긴 타임아웃 사용
|
|
882
|
+
const maxTimeoutSec = Math.max(
|
|
883
|
+
...args.workers.map((w) =>
|
|
884
|
+
Math.ceil(resolveTimeoutMs(w.agentType || "executor", args.timeoutMs) / 1000),
|
|
885
|
+
),
|
|
886
|
+
300,
|
|
887
|
+
);
|
|
888
|
+
|
|
889
|
+
try {
|
|
890
|
+
const { results, sessionName } = await runHeadlessWithCleanup(assignments, {
|
|
891
|
+
sessionPrefix: "dlg",
|
|
892
|
+
timeoutSec: maxTimeoutSec,
|
|
893
|
+
layout: assignments.length <= 2 ? "even-horizontal" : "2x2",
|
|
894
|
+
progressive: true,
|
|
895
|
+
dashboard: true,
|
|
896
|
+
dashboardLayout: "single",
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
await emitProgress(
|
|
900
|
+
extra,
|
|
901
|
+
DIRECT_PROGRESS_DONE,
|
|
902
|
+
100,
|
|
903
|
+
`멀티워커 실행 완료: ${results.length}개 워커`,
|
|
904
|
+
);
|
|
905
|
+
|
|
906
|
+
// 전체 성공 판단: 모든 워커가 matched && exitCode === 0
|
|
907
|
+
const allOk = results.every((r) => r.matched && r.exitCode === 0);
|
|
908
|
+
// 개별 출력을 합산
|
|
909
|
+
const combinedOutput = results
|
|
910
|
+
.map(
|
|
911
|
+
(r, i) =>
|
|
912
|
+
`=== Worker ${i + 1} (${r.cli}/${assignments[i].role}) ===\n${r.output || "(no output)"}`,
|
|
913
|
+
)
|
|
914
|
+
.join("\n\n");
|
|
915
|
+
|
|
916
|
+
return {
|
|
917
|
+
ok: allOk,
|
|
918
|
+
mode: "sync",
|
|
919
|
+
status: allOk ? "completed" : "partial",
|
|
920
|
+
providerRequested: "multi",
|
|
921
|
+
providerResolved: "multi",
|
|
922
|
+
agentType: args.agentType || "executor",
|
|
923
|
+
transport: "headless-psmux",
|
|
924
|
+
exitCode: allOk ? 0 : 1,
|
|
925
|
+
output: combinedOutput,
|
|
926
|
+
workerResults: results.map((r) => ({
|
|
927
|
+
cli: r.cli,
|
|
928
|
+
role: r.role || "",
|
|
929
|
+
paneName: r.paneName,
|
|
930
|
+
matched: r.matched,
|
|
931
|
+
exitCode: r.exitCode,
|
|
932
|
+
output: r.output || "",
|
|
933
|
+
})),
|
|
934
|
+
sessionName,
|
|
935
|
+
};
|
|
936
|
+
} catch (error) {
|
|
937
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
938
|
+
return createErrorPayload(message, {
|
|
939
|
+
mode: "sync",
|
|
940
|
+
providerRequested: "multi",
|
|
941
|
+
agentType: args.agentType || "executor",
|
|
942
|
+
transport: "headless-psmux",
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
819
947
|
async _executeWorker(args, extra) {
|
|
820
948
|
await emitProgress(
|
|
821
949
|
extra,
|
package/package.json
CHANGED
|
@@ -36,28 +36,12 @@ const PROFILE_DEFINITIONS = Object.freeze({
|
|
|
36
36
|
}),
|
|
37
37
|
}),
|
|
38
38
|
executor: Object.freeze({
|
|
39
|
-
description: "구현 워커용.
|
|
40
|
-
allowedServers: Object.freeze([
|
|
41
|
-
"context7",
|
|
42
|
-
"playwright",
|
|
43
|
-
"brave-search",
|
|
44
|
-
"tavily",
|
|
45
|
-
"exa",
|
|
46
|
-
]),
|
|
39
|
+
description: "구현 워커용. 문서 조회 전용 MCP만 허용 (검색/브라우징은 Codex stall 유발)",
|
|
40
|
+
allowedServers: Object.freeze(["context7"]),
|
|
47
41
|
alwaysOnServers: Object.freeze(["context7"]),
|
|
48
|
-
maxSearchServers:
|
|
42
|
+
maxSearchServers: 0,
|
|
49
43
|
allowedToolsByServer: Object.freeze({
|
|
50
44
|
context7: Object.freeze(["resolve-library-id", "query-docs"]),
|
|
51
|
-
"brave-search": Object.freeze(["brave_web_search", "brave_news_search"]),
|
|
52
|
-
exa: Object.freeze(["web_search_exa", "get_code_context_exa"]),
|
|
53
|
-
tavily: Object.freeze(["tavily_search", "tavily_extract"]),
|
|
54
|
-
playwright: Object.freeze([
|
|
55
|
-
"browser_navigate",
|
|
56
|
-
"browser_navigate_back",
|
|
57
|
-
"browser_snapshot",
|
|
58
|
-
"browser_take_screenshot",
|
|
59
|
-
"browser_wait_for",
|
|
60
|
-
]),
|
|
61
45
|
}),
|
|
62
46
|
}),
|
|
63
47
|
designer: Object.freeze({
|