triflux 10.3.4 → 10.7.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.
Files changed (329) hide show
  1. package/LICENSE +21 -21
  2. package/bin/tfx-doctor-tui.mjs +1 -1
  3. package/bin/tfx-doctor.mjs +6 -1
  4. package/bin/tfx-profile.mjs +1 -1
  5. package/bin/tfx-setup-tui.mjs +1 -1
  6. package/bin/tfx-setup.mjs +6 -1
  7. package/bin/triflux.mjs +2396 -1140
  8. package/hooks/agent-route-guard.mjs +12 -8
  9. package/hooks/cross-review-tracker.mjs +21 -8
  10. package/hooks/error-context.mjs +19 -7
  11. package/hooks/hook-adaptive-collector.mjs +18 -16
  12. package/hooks/hook-manager.mjs +93 -32
  13. package/hooks/hook-orchestrator.mjs +108 -24
  14. package/hooks/hook-registry.json +11 -0
  15. package/hooks/keyword-rules.json +6 -10
  16. package/hooks/lib/resolve-root.mjs +1 -1
  17. package/hooks/mcp-config-watcher.mjs +6 -2
  18. package/hooks/pipeline-stop.mjs +3 -6
  19. package/hooks/safety-guard.mjs +99 -28
  20. package/hooks/session-start-fast.mjs +143 -0
  21. package/hooks/subagent-verifier.mjs +5 -4
  22. package/hub/account-broker.mjs +256 -60
  23. package/hub/adaptive-diagnostic.mjs +75 -48
  24. package/hub/adaptive-inject.mjs +95 -57
  25. package/hub/adaptive-memory.mjs +156 -42
  26. package/hub/adaptive.mjs +60 -31
  27. package/hub/assign-callbacks.mjs +67 -30
  28. package/hub/bridge.mjs +0 -1
  29. package/hub/cli-adapter-base.mjs +200 -48
  30. package/hub/codex-adapter.mjs +76 -96
  31. package/hub/codex-compat.mjs +3 -3
  32. package/hub/codex-preflight.mjs +63 -37
  33. package/hub/delegator/contracts.mjs +19 -23
  34. package/hub/delegator/index.mjs +3 -3
  35. package/hub/delegator/service.mjs +88 -64
  36. package/hub/delegator/tool-definitions.mjs +5 -5
  37. package/hub/fullcycle.mjs +33 -17
  38. package/hub/gemini-adapter.mjs +69 -94
  39. package/hub/hitl.mjs +89 -30
  40. package/hub/intent.mjs +161 -38
  41. package/hub/lib/cache-guard.mjs +43 -17
  42. package/hub/lib/mcp-response-cache.mjs +66 -32
  43. package/hub/lib/memory-store.mjs +285 -111
  44. package/hub/lib/path-utils.mjs +35 -37
  45. package/hub/lib/process-utils.mjs +106 -37
  46. package/hub/lib/spawn-trace.mjs +527 -0
  47. package/hub/lib/ssh-command.mjs +34 -4
  48. package/hub/lib/ssh-retry.mjs +5 -1
  49. package/hub/lib/uuidv7.mjs +4 -3
  50. package/hub/memory-doctor.mjs +266 -106
  51. package/hub/middleware/request-logger.mjs +61 -34
  52. package/hub/paths.mjs +9 -9
  53. package/hub/pipeline/gates/confidence.mjs +34 -15
  54. package/hub/pipeline/gates/consensus.mjs +27 -15
  55. package/hub/pipeline/gates/index.mjs +7 -3
  56. package/hub/pipeline/gates/selfcheck.mjs +57 -19
  57. package/hub/pipeline/index.mjs +77 -42
  58. package/hub/pipeline/state.mjs +10 -10
  59. package/hub/pipeline/transitions.mjs +40 -23
  60. package/hub/platform.mjs +57 -48
  61. package/hub/promote-penalties.mjs +25 -7
  62. package/hub/quality/deslop.mjs +70 -49
  63. package/hub/research.mjs +32 -25
  64. package/hub/router.mjs +240 -107
  65. package/hub/routing/complexity.mjs +132 -29
  66. package/hub/routing/index.mjs +17 -12
  67. package/hub/routing/q-learning.mjs +76 -28
  68. package/hub/server.mjs +4 -4
  69. package/hub/session-fingerprint.mjs +126 -60
  70. package/hub/state.mjs +84 -43
  71. package/hub/store-adapter.mjs +59 -26
  72. package/hub/store.mjs +356 -153
  73. package/hub/team/agent-map.json +22 -7
  74. package/hub/team/ansi.mjs +186 -122
  75. package/hub/team/backend.mjs +28 -10
  76. package/hub/team/cli/commands/attach.mjs +29 -9
  77. package/hub/team/cli/commands/control.mjs +29 -8
  78. package/hub/team/cli/commands/debug.mjs +32 -11
  79. package/hub/team/cli/commands/focus.mjs +38 -11
  80. package/hub/team/cli/commands/interrupt.mjs +18 -6
  81. package/hub/team/cli/commands/kill.mjs +16 -5
  82. package/hub/team/cli/commands/list.mjs +11 -4
  83. package/hub/team/cli/commands/send.mjs +19 -6
  84. package/hub/team/cli/commands/start/index.mjs +154 -31
  85. package/hub/team/cli/commands/start/parse-args.mjs +38 -11
  86. package/hub/team/cli/commands/start/start-headless.mjs +112 -36
  87. package/hub/team/cli/commands/start/start-in-process.mjs +12 -2
  88. package/hub/team/cli/commands/start/start-mux.mjs +70 -21
  89. package/hub/team/cli/commands/start/start-wt.mjs +29 -12
  90. package/hub/team/cli/commands/status.mjs +43 -14
  91. package/hub/team/cli/commands/stop.mjs +11 -4
  92. package/hub/team/cli/commands/task.mjs +8 -3
  93. package/hub/team/cli/commands/tasks.mjs +1 -1
  94. package/hub/team/cli/index.mjs +2 -2
  95. package/hub/team/cli/manifest.mjs +38 -8
  96. package/hub/team/cli/render.mjs +30 -8
  97. package/hub/team/cli/services/attach-fallback.mjs +31 -11
  98. package/hub/team/cli/services/hub-client.mjs +42 -14
  99. package/hub/team/cli/services/member-selector.mjs +11 -4
  100. package/hub/team/cli/services/native-control.mjs +48 -21
  101. package/hub/team/cli/services/runtime-mode.mjs +2 -1
  102. package/hub/team/cli/services/state-store.mjs +25 -8
  103. package/hub/team/cli/services/task-model.mjs +16 -6
  104. package/hub/team/conductor-mesh-bridge.mjs +24 -23
  105. package/hub/team/conductor.mjs +8 -4
  106. package/hub/team/dashboard-anchor.mjs +4 -5
  107. package/hub/team/dashboard-layout.mjs +3 -1
  108. package/hub/team/dashboard-open.mjs +41 -21
  109. package/hub/team/dashboard.mjs +76 -28
  110. package/hub/team/event-log.mjs +18 -10
  111. package/hub/team/handoff.mjs +31 -15
  112. package/hub/team/headless.mjs +2 -1
  113. package/hub/team/health-probe.mjs +69 -54
  114. package/hub/team/launcher-template.mjs +16 -13
  115. package/hub/team/native-supervisor.mjs +65 -21
  116. package/hub/team/native.mjs +74 -35
  117. package/hub/team/nativeProxy.mjs +184 -113
  118. package/hub/team/notify.mjs +119 -76
  119. package/hub/team/orchestrator.mjs +9 -4
  120. package/hub/team/pane.mjs +12 -7
  121. package/hub/team/process-cleanup.mjs +25 -16
  122. package/hub/team/psmux.mjs +491 -201
  123. package/hub/team/remote-probe.mjs +68 -52
  124. package/hub/team/remote-session.mjs +117 -59
  125. package/hub/team/remote-watcher.mjs +61 -33
  126. package/hub/team/routing.mjs +51 -25
  127. package/hub/team/runtime-strategy.mjs +3 -1
  128. package/hub/team/session.mjs +98 -34
  129. package/hub/team/staleState.mjs +72 -30
  130. package/hub/team/swarm-locks.mjs +15 -13
  131. package/hub/team/swarm-planner.mjs +32 -21
  132. package/hub/team/swarm-reconciler.mjs +48 -23
  133. package/hub/team/tui-lite.mjs +266 -68
  134. package/hub/team/tui-remote-adapter.mjs +14 -10
  135. package/hub/team/tui-viewer.mjs +99 -43
  136. package/hub/team/tui.mjs +708 -271
  137. package/hub/team/worktree-lifecycle.mjs +152 -58
  138. package/hub/team/wt-manager.mjs +24 -14
  139. package/hub/token-mode.mjs +71 -71
  140. package/hub/tray.mjs +66 -23
  141. package/hub/workers/claude-worker.mjs +162 -118
  142. package/hub/workers/codex-mcp.mjs +192 -141
  143. package/hub/workers/delegator-mcp.mjs +507 -333
  144. package/hub/workers/factory.mjs +8 -8
  145. package/hub/workers/gemini-worker.mjs +115 -84
  146. package/hub/workers/interface.mjs +6 -1
  147. package/hub/workers/worker-utils.mjs +21 -14
  148. package/hud/colors.mjs +27 -9
  149. package/hud/constants.mjs +162 -26
  150. package/hud/context-monitor.mjs +82 -41
  151. package/hud/hud-qos-status.mjs +129 -49
  152. package/hud/mission-board.mjs +6 -3
  153. package/hud/providers/claude.mjs +226 -115
  154. package/hud/providers/codex.mjs +62 -22
  155. package/hud/providers/gemini.mjs +168 -56
  156. package/hud/renderers.mjs +384 -119
  157. package/hud/terminal.mjs +101 -31
  158. package/hud/utils.mjs +78 -38
  159. package/mesh/index.mjs +11 -5
  160. package/mesh/mesh-budget.mjs +18 -9
  161. package/mesh/mesh-heartbeat.mjs +1 -1
  162. package/mesh/mesh-queue.mjs +3 -5
  163. package/mesh/mesh-router.mjs +5 -4
  164. package/package.json +2 -1
  165. package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
  166. package/scripts/__tests__/keyword-detector.test.mjs +77 -28
  167. package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
  168. package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
  169. package/scripts/__tests__/remote-spawn.test.mjs +10 -4
  170. package/scripts/__tests__/session-start-fast.test.mjs +36 -0
  171. package/scripts/__tests__/skill-template.test.mjs +98 -50
  172. package/scripts/__tests__/smoke.test.mjs +1 -1
  173. package/scripts/__tests__/spawn-trace.test.mjs +102 -0
  174. package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
  175. package/scripts/cache-doctor.mjs +11 -4
  176. package/scripts/cache-warmup.mjs +96 -37
  177. package/scripts/claudemd-sync.mjs +27 -17
  178. package/scripts/codex-gateway-preflight.mjs +52 -37
  179. package/scripts/codex-mcp-gateway-sync.mjs +59 -39
  180. package/scripts/completions/tfx.bash +47 -47
  181. package/scripts/completions/tfx.fish +44 -44
  182. package/scripts/completions/tfx.zsh +83 -83
  183. package/scripts/config-audit.mjs +232 -0
  184. package/scripts/convert-to-tmpl.mjs +54 -0
  185. package/scripts/cross-review-gate.mjs +35 -12
  186. package/scripts/cross-review-tracker.mjs +21 -8
  187. package/scripts/demo.mjs +35 -17
  188. package/scripts/doctor-diagnose.mjs +284 -0
  189. package/scripts/gen-skill-docs.mjs +7 -2
  190. package/scripts/gen-skill-manifest.mjs +2 -1
  191. package/scripts/headless-guard.mjs +86 -48
  192. package/scripts/hub-ensure.mjs +45 -26
  193. package/scripts/keyword-detector.mjs +41 -20
  194. package/scripts/keyword-rules-expander.mjs +47 -30
  195. package/scripts/lib/claudemd-scanner.mjs +6 -1
  196. package/scripts/lib/context.mjs +3 -3
  197. package/scripts/lib/cross-review-utils.mjs +6 -3
  198. package/scripts/lib/env-probe.mjs +47 -28
  199. package/scripts/lib/gemini-profiles.mjs +44 -10
  200. package/scripts/lib/handoff.mjs +33 -17
  201. package/scripts/lib/hook-utils.mjs +8 -6
  202. package/scripts/lib/keyword-rules.mjs +43 -19
  203. package/scripts/lib/logger.mjs +24 -24
  204. package/scripts/lib/mcp-filter.mjs +377 -239
  205. package/scripts/lib/mcp-guard-engine.mjs +194 -79
  206. package/scripts/lib/mcp-manifest.mjs +23 -13
  207. package/scripts/lib/mcp-server-catalog.mjs +300 -63
  208. package/scripts/lib/psmux-info.mjs +11 -6
  209. package/scripts/lib/remote-spawn-transfer.mjs +44 -14
  210. package/scripts/lib/skill-template.mjs +30 -7
  211. package/scripts/mcp-check.mjs +58 -39
  212. package/scripts/mcp-gateway-config.mjs +83 -39
  213. package/scripts/mcp-gateway-ensure.mjs +43 -35
  214. package/scripts/mcp-gateway-integration-test.mjs +70 -58
  215. package/scripts/mcp-gateway-start.mjs +126 -60
  216. package/scripts/mcp-gateway-verify.mjs +24 -22
  217. package/scripts/mcp-safety-guard.mjs +44 -11
  218. package/scripts/notion-read.mjs +199 -84
  219. package/scripts/pack.mjs +94 -89
  220. package/scripts/preflight-cache.mjs +27 -10
  221. package/scripts/preinstall.mjs +42 -13
  222. package/scripts/remote-spawn.mjs +309 -94
  223. package/scripts/run.cjs +8 -5
  224. package/scripts/session-spawn-helper.mjs +130 -39
  225. package/scripts/session-stale-cleanup.mjs +123 -0
  226. package/scripts/setup.mjs +941 -492
  227. package/scripts/test-lock.mjs +20 -7
  228. package/scripts/test-tfx-route-no-claude-native.mjs +16 -12
  229. package/scripts/tfx-batch-stats.mjs +32 -11
  230. package/scripts/tfx-gate-activate.mjs +11 -4
  231. package/scripts/tfx-route-post.mjs +87 -20
  232. package/scripts/tfx-route-worker.mjs +57 -51
  233. package/scripts/tfx-route.sh +41 -124
  234. package/scripts/tmp-cleanup.mjs +21 -7
  235. package/scripts/token-snapshot.mjs +204 -85
  236. package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
  237. package/skills/.omc/state/idle-notif-cooldown.json +3 -0
  238. package/skills/.omc/state/last-tool-error.json +7 -0
  239. package/skills/.omc/state/subagent-tracking.json +7 -0
  240. package/skills/_templates/base.md +1 -6
  241. package/skills/merge-worktree/SKILL.md.tmpl +144 -0
  242. package/skills/shared/telemetry-segment.md +6 -0
  243. package/skills/star-prompt/SKILL.md.tmpl +222 -0
  244. package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
  245. package/skills/tfx-analysis/skill.json +1 -6
  246. package/skills/tfx-auto/SKILL.md +1 -0
  247. package/skills/tfx-auto-codex/SKILL.md.tmpl +106 -0
  248. package/skills/tfx-auto-codex/skill.json +1 -3
  249. package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
  250. package/skills/tfx-autopilot/skill.json +1 -5
  251. package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
  252. package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
  253. package/skills/tfx-autoroute/skill.json +1 -7
  254. package/skills/tfx-codex/SKILL.md +1 -0
  255. package/skills/tfx-codex/skill.json +1 -3
  256. package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
  257. package/skills/tfx-codex-swarm/evals/evals.json +1 -1
  258. package/skills/tfx-codex-swarm/skill.json +1 -4
  259. package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
  260. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
  261. package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
  262. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
  263. package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
  264. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
  265. package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
  266. package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
  267. package/skills/tfx-debate/SKILL.md.tmpl +192 -0
  268. package/skills/tfx-debate/skill.json +1 -7
  269. package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
  270. package/skills/tfx-deep-analysis/skill.json +1 -5
  271. package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
  272. package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
  273. package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
  274. package/skills/tfx-deep-qa/skill.json +1 -6
  275. package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
  276. package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
  277. package/skills/tfx-doctor/SKILL.md +21 -0
  278. package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
  279. package/skills/tfx-doctor/skill.json +1 -3
  280. package/skills/tfx-find/SKILL.md +1 -0
  281. package/skills/tfx-forge/SKILL.md.tmpl +187 -0
  282. package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
  283. package/skills/tfx-fullcycle/skill.json +1 -6
  284. package/skills/tfx-gemini/SKILL.md.tmpl +91 -0
  285. package/skills/tfx-gemini/skill.json +1 -3
  286. package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
  287. package/skills/tfx-hooks/skill.json +1 -3
  288. package/skills/tfx-hub/SKILL.md.tmpl +212 -0
  289. package/skills/tfx-hub/skill.json +1 -3
  290. package/skills/tfx-index/SKILL.md +1 -0
  291. package/skills/tfx-index/skill.json +1 -6
  292. package/skills/tfx-interview/SKILL.md.tmpl +285 -0
  293. package/skills/tfx-multi/SKILL.md.tmpl +183 -0
  294. package/skills/tfx-multi/skill.json +1 -3
  295. package/skills/tfx-panel/SKILL.md.tmpl +189 -0
  296. package/skills/tfx-panel/skill.json +1 -7
  297. package/skills/tfx-persist/SKILL.md.tmpl +270 -0
  298. package/skills/tfx-persist/skill.json +1 -7
  299. package/skills/tfx-plan/SKILL.md +1 -0
  300. package/skills/tfx-plan/skill.json +1 -6
  301. package/skills/tfx-profile/SKILL.md.tmpl +239 -0
  302. package/skills/tfx-profile/skill.json +1 -3
  303. package/skills/tfx-prune/SKILL.md.tmpl +200 -0
  304. package/skills/tfx-prune/skill.json +1 -7
  305. package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
  306. package/skills/tfx-psmux-rules/skill.json +1 -4
  307. package/skills/tfx-qa/SKILL.md +1 -0
  308. package/skills/tfx-qa/skill.json +1 -6
  309. package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
  310. package/skills/tfx-ralph/skill.json +1 -4
  311. package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
  312. package/skills/tfx-remote-setup/skill.json +1 -3
  313. package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
  314. package/skills/tfx-remote-spawn/references/hosts.json +16 -0
  315. package/skills/tfx-remote-spawn/skill.json +1 -4
  316. package/skills/tfx-research/SKILL.md +1 -0
  317. package/skills/tfx-review/SKILL.md +1 -0
  318. package/skills/tfx-review/skill.json +1 -6
  319. package/skills/tfx-setup/SKILL.md.tmpl +504 -0
  320. package/skills/tfx-setup/skill.json +1 -3
  321. package/skills/tfx-swarm/SKILL.md +22 -0
  322. package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
  323. package/tui/codex-profile.mjs +88 -33
  324. package/tui/core.mjs +45 -15
  325. package/tui/doctor.mjs +75 -28
  326. package/tui/gemini-profile.mjs +74 -29
  327. package/tui/monitor-data.mjs +8 -4
  328. package/tui/monitor.mjs +71 -27
  329. package/tui/setup.mjs +133 -42
@@ -1,30 +1,32 @@
1
1
  #!/usr/bin/env node
2
2
  // hub/workers/delegator-mcp.mjs — triflux 위임용 MCP stdio 서버
3
3
 
4
- import { spawn } from 'node:child_process';
5
- import { randomUUID } from 'node:crypto';
6
- import { existsSync, readFileSync } from 'node:fs';
7
- import { dirname, isAbsolute, resolve } from 'node:path';
8
- import process from 'node:process';
9
- import { fileURLToPath, pathToFileURL } from 'node:url';
4
+ import { spawn } from "node:child_process";
5
+ import { randomUUID } from "node:crypto";
6
+ import { existsSync, readFileSync } from "node:fs";
7
+ import { dirname, isAbsolute, resolve } from "node:path";
8
+ import process from "node:process";
9
+ import { fileURLToPath, pathToFileURL } from "node:url";
10
10
 
11
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
12
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
13
- import * as z from 'zod';
11
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
12
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
13
+ import * as z from "zod";
14
14
 
15
- import { CodexMcpWorker } from './codex-mcp.mjs';
16
- import { GeminiWorker } from './gemini-worker.mjs';
15
+ import { CodexMcpWorker } from "./codex-mcp.mjs";
16
+ import { GeminiWorker } from "./gemini-worker.mjs";
17
17
 
18
18
  const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
19
19
 
20
20
  // mcp-filter.mjs 동적 해석 — 프로젝트(hub/workers/)와 배포(scripts/hub/workers/) 양쪽 대응
21
21
  const MCP_FILTER_CANDIDATES = [
22
- resolve(SCRIPT_DIR, '../../scripts/lib/mcp-filter.mjs'), // 프로젝트 원본
23
- resolve(SCRIPT_DIR, '../../lib/mcp-filter.mjs'), // 배포 (~/.claude/scripts/)
22
+ resolve(SCRIPT_DIR, "../../scripts/lib/mcp-filter.mjs"), // 프로젝트 원본
23
+ resolve(SCRIPT_DIR, "../../lib/mcp-filter.mjs"), // 배포 (~/.claude/scripts/)
24
24
  ];
25
25
  const mcpFilterPath = MCP_FILTER_CANDIDATES.find((p) => existsSync(p));
26
26
  if (!mcpFilterPath) {
27
- throw new Error(`mcp-filter.mjs not found. candidates: ${MCP_FILTER_CANDIDATES.join(', ')}`);
27
+ throw new Error(
28
+ `mcp-filter.mjs not found. candidates: ${MCP_FILTER_CANDIDATES.join(", ")}`,
29
+ );
28
30
  }
29
31
  const {
30
32
  buildPromptHint,
@@ -32,74 +34,77 @@ const {
32
34
  getGeminiAllowedServers,
33
35
  SUPPORTED_MCP_PROFILES,
34
36
  } = await import(pathToFileURL(mcpFilterPath).href);
35
- const SERVER_INFO = { name: 'triflux-delegator', version: '1.0.0' };
37
+ const SERVER_INFO = { name: "triflux-delegator", version: "1.0.0" };
36
38
  const DEFAULT_CONTEXT_BYTES = 32 * 1024;
37
39
  const DEFAULT_ROUTE_TIMEOUT_SEC = 120;
38
40
  const DIRECT_PROGRESS_START = 5;
39
41
  const DIRECT_PROGRESS_RUNNING = 60;
40
42
  const DIRECT_PROGRESS_DONE = 100;
41
- const SEARCH_ENGINE_CACHE_PATH = ['.omc', 'state', 'search-engines.json'];
43
+ const SEARCH_ENGINE_CACHE_PATH = [".omc", "state", "search-engines.json"];
42
44
 
43
45
  const AGENT_TIMEOUT_SEC = Object.freeze({
44
46
  executor: 1080,
45
- 'build-fixer': 540,
47
+ "build-fixer": 540,
46
48
  debugger: 900,
47
- 'deep-executor': 3600,
49
+ "deep-executor": 3600,
48
50
  architect: 3600,
49
51
  planner: 3600,
50
52
  critic: 3600,
51
53
  analyst: 3600,
52
- 'code-reviewer': 1800,
53
- 'security-reviewer': 1800,
54
- 'quality-reviewer': 1800,
54
+ "code-reviewer": 1800,
55
+ "security-reviewer": 1800,
56
+ "quality-reviewer": 1800,
55
57
  scientist: 1440,
56
- 'scientist-deep': 3600,
57
- 'document-specialist': 1440,
58
+ "scientist-deep": 3600,
59
+ "document-specialist": 1440,
58
60
  designer: 900,
59
61
  writer: 900,
60
62
  explore: 300,
61
63
  verifier: 1200,
62
- 'test-engineer': 300,
63
- 'qa-tester': 300,
64
+ "test-engineer": 300,
65
+ "qa-tester": 300,
64
66
  spark: 180,
65
67
  });
66
68
 
67
69
  const CODEX_PROFILE_BY_AGENT = Object.freeze({
68
- executor: 'codex53_high',
69
- 'build-fixer': 'codex53_low',
70
- debugger: 'codex53_high',
71
- 'deep-executor': 'gpt54_xhigh',
72
- architect: 'gpt54_xhigh',
73
- planner: 'gpt54_xhigh',
74
- critic: 'gpt54_xhigh',
75
- analyst: 'gpt54_xhigh',
76
- 'code-reviewer': 'codex53_high',
77
- 'security-reviewer': 'codex53_high',
78
- 'quality-reviewer': 'codex53_high',
79
- scientist: 'codex53_high',
80
- 'scientist-deep': 'gpt54_high',
81
- 'document-specialist': 'codex53_high',
82
- verifier: 'codex53_high',
83
- designer: 'gpt54_xhigh', // Gemini primary, codex fallback — UI/UX는 5.4 에이전틱
84
- writer: 'codex53_high', // Gemini primary, codex fallback용
85
- spark: 'spark53_low',
70
+ executor: "codex53_high",
71
+ "build-fixer": "codex53_low",
72
+ debugger: "codex53_high",
73
+ "deep-executor": "gpt54_xhigh",
74
+ architect: "gpt54_xhigh",
75
+ planner: "gpt54_xhigh",
76
+ critic: "gpt54_xhigh",
77
+ analyst: "gpt54_xhigh",
78
+ "code-reviewer": "codex53_high",
79
+ "security-reviewer": "codex53_high",
80
+ "quality-reviewer": "codex53_high",
81
+ scientist: "codex53_high",
82
+ "scientist-deep": "gpt54_high",
83
+ "document-specialist": "codex53_high",
84
+ verifier: "codex53_high",
85
+ designer: "gpt54_xhigh", // Gemini primary, codex fallback — UI/UX는 5.4 에이전틱
86
+ writer: "codex53_high", // Gemini primary, codex fallback용
87
+ spark: "spark53_low",
86
88
  });
87
89
 
88
90
  const GEMINI_MODEL_BY_AGENT = Object.freeze({
89
- 'build-fixer': 'gemini-3-flash-preview',
90
- writer: 'gemini-3-flash-preview',
91
- spark: 'gemini-3-flash-preview',
91
+ "build-fixer": "gemini-3-flash-preview",
92
+ writer: "gemini-3-flash-preview",
93
+ spark: "gemini-3-flash-preview",
92
94
  });
93
95
 
94
96
  const REVIEW_INSTRUCTION_BY_AGENT = Object.freeze({
95
- 'code-reviewer': '코드 리뷰 모드로 동작하라. 버그, 리스크, 회귀, 테스트 누락을 우선 식별하라.',
96
- 'security-reviewer': '보안 리뷰 모드로 동작하라. 취약점, 권한 경계, 비밀정보 노출 가능성을 우선 식별하라.',
97
- 'quality-reviewer': '품질 리뷰 모드로 동작하라. 로직 결함, 유지보수성 저하, 테스트 누락을 우선 식별하라.',
97
+ "code-reviewer":
98
+ "코드 리뷰 모드로 동작하라. 버그, 리스크, 회귀, 테스트 누락을 우선 식별하라.",
99
+ "security-reviewer":
100
+ "보안 리뷰 모드로 동작하라. 취약점, 권한 경계, 비밀정보 노출 가능성을 우선 식별하라.",
101
+ "quality-reviewer":
102
+ "품질 리뷰 모드로 동작하라. 로직 결함, 유지보수성 저하, 테스트 누락을 우선 식별하라.",
98
103
  });
99
104
 
100
105
  function cloneEnv(env = process.env) {
101
106
  return Object.fromEntries(
102
- Object.entries(env).filter(([, value]) => typeof value === 'string'),
107
+ Object.entries(env).filter(([, value]) => typeof value === "string"),
103
108
  );
104
109
  }
105
110
 
@@ -108,7 +113,7 @@ function parseJsonArray(raw, fallback = []) {
108
113
  try {
109
114
  const parsed = JSON.parse(raw);
110
115
  return Array.isArray(parsed)
111
- ? parsed.map((item) => String(item ?? '')).filter(Boolean)
116
+ ? parsed.map((item) => String(item ?? "")).filter(Boolean)
112
117
  : [...fallback];
113
118
  } catch {
114
119
  return [...fallback];
@@ -117,7 +122,9 @@ function parseJsonArray(raw, fallback = []) {
117
122
 
118
123
  function resolveCandidatePath(candidate, cwd = process.cwd()) {
119
124
  if (!candidate) return null;
120
- const normalized = isAbsolute(candidate) ? candidate : resolve(cwd, candidate);
125
+ const normalized = isAbsolute(candidate)
126
+ ? candidate
127
+ : resolve(cwd, candidate);
121
128
  return existsSync(normalized) ? normalized : null;
122
129
  }
123
130
 
@@ -126,8 +133,8 @@ function resolveRouteScript(explicitPath, cwd = process.cwd()) {
126
133
  explicitPath,
127
134
  process.env.TFX_DELEGATOR_ROUTE_SCRIPT,
128
135
  process.env.TFX_ROUTE_SCRIPT,
129
- resolve(SCRIPT_DIR, '..', '..', 'scripts', 'tfx-route.sh'),
130
- resolve(cwd, 'scripts', 'tfx-route.sh'),
136
+ resolve(SCRIPT_DIR, "..", "..", "scripts", "tfx-route.sh"),
137
+ resolve(cwd, "scripts", "tfx-route.sh"),
131
138
  ];
132
139
 
133
140
  for (const candidate of candidates) {
@@ -139,11 +146,11 @@ function resolveRouteScript(explicitPath, cwd = process.cwd()) {
139
146
  }
140
147
 
141
148
  function resolveCodexProfile(agentType) {
142
- return CODEX_PROFILE_BY_AGENT[agentType] || 'high';
149
+ return CODEX_PROFILE_BY_AGENT[agentType] || "high";
143
150
  }
144
151
 
145
152
  function resolveGeminiModel(agentType) {
146
- return GEMINI_MODEL_BY_AGENT[agentType] || 'gemini-3.1-pro-preview';
153
+ return GEMINI_MODEL_BY_AGENT[agentType] || "gemini-3.1-pro-preview";
147
154
  }
148
155
 
149
156
  function resolveTimeoutMs(agentType, timeoutMs) {
@@ -161,13 +168,13 @@ function resolveTimeoutSec(agentType, timeoutMs) {
161
168
  }
162
169
 
163
170
  function loadContextFromFile(contextFile) {
164
- if (!contextFile) return '';
171
+ if (!contextFile) return "";
165
172
  const resolved = resolveCandidatePath(contextFile);
166
- if (!resolved) return '';
173
+ if (!resolved) return "";
167
174
  try {
168
- return readFileSync(resolved, 'utf8').slice(0, DEFAULT_CONTEXT_BYTES);
175
+ return readFileSync(resolved, "utf8").slice(0, DEFAULT_CONTEXT_BYTES);
169
176
  } catch {
170
- return '';
177
+ return "";
171
178
  }
172
179
  }
173
180
 
@@ -183,7 +190,9 @@ function withPromptHint(prompt, args) {
183
190
  agentType: args.agentType,
184
191
  requestedProfile: args.mcpProfile,
185
192
  searchTool: args.searchTool,
186
- workerIndex: Number.isInteger(args.workerIndex) ? args.workerIndex : undefined,
193
+ workerIndex: Number.isInteger(args.workerIndex)
194
+ ? args.workerIndex
195
+ : undefined,
187
196
  taskText: promptWithContext,
188
197
  });
189
198
  if (!hint) return promptWithContext;
@@ -192,8 +201,8 @@ function withPromptHint(prompt, args) {
192
201
 
193
202
  function joinInstructions(...values) {
194
203
  return values
195
- .filter((value) => typeof value === 'string' && value.trim())
196
- .join('\n')
204
+ .filter((value) => typeof value === "string" && value.trim())
205
+ .join("\n")
197
206
  .trim();
198
207
  }
199
208
 
@@ -202,36 +211,40 @@ function _loadAvailableServersFromSearchEngineCache(cwd = process.cwd()) {
202
211
  if (!existsSync(cacheFile)) return null;
203
212
 
204
213
  try {
205
- const parsed = JSON.parse(readFileSync(cacheFile, 'utf8'));
214
+ const parsed = JSON.parse(readFileSync(cacheFile, "utf8"));
206
215
  if (!Array.isArray(parsed?.engines)) return null;
207
216
  return parsed.engines
208
- .filter((engine) => engine?.status === 'available')
209
- .map((engine) => (typeof engine?.name === 'string' ? engine.name.trim() : ''))
217
+ .filter((engine) => engine?.status === "available")
218
+ .map((engine) =>
219
+ typeof engine?.name === "string" ? engine.name.trim() : "",
220
+ )
210
221
  .filter(Boolean);
211
222
  } catch {
212
223
  return null;
213
224
  }
214
225
  }
215
226
 
216
- function parseRouteType(stderr = '') {
227
+ function parseRouteType(stderr = "") {
217
228
  const match = stderr.match(/type=([a-z-]+)/);
218
229
  if (!match) return null;
219
- if (match[1] === 'codex') return 'codex';
220
- if (match[1] === 'gemini') return 'gemini';
230
+ if (match[1] === "codex") return "codex";
231
+ if (match[1] === "gemini") return "gemini";
221
232
  return match[1];
222
233
  }
223
234
 
224
235
  function summarizePayload(payload) {
225
- if (typeof payload.output === 'string' && payload.output.trim()) return payload.output.trim();
226
- if (payload.mode === 'async' && payload.job_id) return `비동기 위임이 시작되었습니다. jobId=${payload.job_id}`;
236
+ if (typeof payload.output === "string" && payload.output.trim())
237
+ return payload.output.trim();
238
+ if (payload.mode === "async" && payload.job_id)
239
+ return `비동기 위임이 시작되었습니다. jobId=${payload.job_id}`;
227
240
  if (payload.job_id) return `jobId=${payload.job_id} 상태=${payload.status}`;
228
241
  if (payload.status) return `상태=${payload.status}`;
229
- return payload.ok ? '완료되었습니다.' : '실패했습니다.';
242
+ return payload.ok ? "완료되었습니다." : "실패했습니다.";
230
243
  }
231
244
 
232
245
  function createToolResponse(payload, { isError = false } = {}) {
233
246
  return {
234
- content: [{ type: 'text', text: summarizePayload(payload) }],
247
+ content: [{ type: "text", text: summarizePayload(payload) }],
235
248
  structuredContent: payload,
236
249
  isError,
237
250
  };
@@ -240,55 +253,95 @@ function createToolResponse(payload, { isError = false } = {}) {
240
253
  function createErrorPayload(message, extras = {}) {
241
254
  return {
242
255
  ok: false,
243
- status: 'failed',
256
+ status: "failed",
244
257
  error: message,
245
258
  ...extras,
246
259
  };
247
260
  }
248
261
 
249
262
  const DelegateInputSchema = z.object({
250
- prompt: z.string().min(1).describe('위임할 프롬프트'),
251
- provider: z.enum(['auto', 'codex', 'gemini']).default('auto').describe('사용할 provider'),
252
- mode: z.enum(['sync', 'async']).default('sync').describe('동기 또는 비동기 실행'),
253
- agentType: z.string().default('executor').describe('tfx-route 역할명 또는 direct 실행 역할'),
254
- cwd: z.string().optional().describe('작업 디렉터리'),
255
- timeoutMs: z.number().int().positive().optional().describe('요청 타임아웃(ms)'),
256
- sessionKey: z.string().optional().describe('Codex warm session 재사용 키'),
257
- resetSession: z.boolean().optional().describe('기존 Codex 세션 초기화 여부'),
258
- mcpProfile: z.enum(SUPPORTED_MCP_PROFILES).default('auto'),
259
- contextFile: z.string().optional().describe('tfx-route prior_context 파일 경로'),
260
- availableServers: z.array(z.string()).optional().describe('Codex에 등록된 MCP 서버 이름 목록 (config override 대상)'),
261
- searchTool: z.enum(['brave-search', 'tavily', 'exa']).optional().describe('검색 우선 도구'),
262
- workerIndex: z.number().int().positive().optional().describe('병렬 워커 인덱스'),
263
- model: z.string().optional().describe('직접 실행 시 모델 오버라이드'),
264
- developerInstructions: z.string().optional().describe('직접 실행 시 추가 개발자 지침'),
265
- compactPrompt: z.string().optional().describe('Codex compact prompt'),
266
- threadId: z.string().optional().describe('Codex 직접 실행 시 기존 threadId'),
267
- codexTransport: z.enum(['auto', 'mcp', 'exec']).optional().describe('route 경로용 Codex transport'),
268
- noClaudeNative: z.boolean().optional().describe('route 경로용 TFX_NO_CLAUDE_NATIVE'),
269
- teamName: z.string().optional().describe('TFX_TEAM_NAME'),
270
- teamTaskId: z.string().optional().describe('TFX_TEAM_TASK_ID'),
271
- teamAgentName: z.string().optional().describe('TFX_TEAM_AGENT_NAME'),
272
- teamLeadName: z.string().optional().describe('TFX_TEAM_LEAD_NAME'),
273
- hubUrl: z.string().optional().describe('TFX_HUB_URL'),
263
+ prompt: z.string().min(1).describe("위임할 프롬프트"),
264
+ provider: z
265
+ .enum(["auto", "codex", "gemini"])
266
+ .default("auto")
267
+ .describe("사용할 provider"),
268
+ mode: z
269
+ .enum(["sync", "async"])
270
+ .default("sync")
271
+ .describe("동기 또는 비동기 실행"),
272
+ agentType: z
273
+ .string()
274
+ .default("executor")
275
+ .describe("tfx-route 역할명 또는 direct 실행 역할"),
276
+ cwd: z.string().optional().describe("작업 디렉터리"),
277
+ timeoutMs: z
278
+ .number()
279
+ .int()
280
+ .positive()
281
+ .optional()
282
+ .describe("요청 타임아웃(ms)"),
283
+ sessionKey: z.string().optional().describe("Codex warm session 재사용 키"),
284
+ resetSession: z.boolean().optional().describe("기존 Codex 세션 초기화 여부"),
285
+ mcpProfile: z.enum(SUPPORTED_MCP_PROFILES).default("auto"),
286
+ contextFile: z
287
+ .string()
288
+ .optional()
289
+ .describe("tfx-route prior_context 파일 경로"),
290
+ availableServers: z
291
+ .array(z.string())
292
+ .optional()
293
+ .describe("Codex에 등록된 MCP 서버 이름 목록 (config override 대상)"),
294
+ searchTool: z
295
+ .enum(["brave-search", "tavily", "exa"])
296
+ .optional()
297
+ .describe("검색 우선 도구"),
298
+ workerIndex: z
299
+ .number()
300
+ .int()
301
+ .positive()
302
+ .optional()
303
+ .describe("병렬 워커 인덱스"),
304
+ model: z.string().optional().describe("직접 실행 시 모델 오버라이드"),
305
+ developerInstructions: z
306
+ .string()
307
+ .optional()
308
+ .describe("직접 실행 시 추가 개발자 지침"),
309
+ compactPrompt: z.string().optional().describe("Codex compact prompt"),
310
+ threadId: z.string().optional().describe("Codex 직접 실행 시 기존 threadId"),
311
+ codexTransport: z
312
+ .enum(["auto", "mcp", "exec"])
313
+ .optional()
314
+ .describe("route 경로용 Codex transport"),
315
+ noClaudeNative: z
316
+ .boolean()
317
+ .optional()
318
+ .describe("route 경로용 TFX_NO_CLAUDE_NATIVE"),
319
+ teamName: z.string().optional().describe("TFX_TEAM_NAME"),
320
+ teamTaskId: z.string().optional().describe("TFX_TEAM_TASK_ID"),
321
+ teamAgentName: z.string().optional().describe("TFX_TEAM_AGENT_NAME"),
322
+ teamLeadName: z.string().optional().describe("TFX_TEAM_LEAD_NAME"),
323
+ hubUrl: z.string().optional().describe("TFX_HUB_URL"),
274
324
  });
275
325
 
276
326
  const DelegateStatusInputSchema = z.object({
277
- jobId: z.string().min(1).describe('조회할 비동기 job ID'),
327
+ jobId: z.string().min(1).describe("조회할 비동기 job ID"),
278
328
  });
279
329
 
280
330
  const DelegateReplyInputSchema = z.object({
281
- job_id: z.string().min(1).describe('후속 응답을 보낼 기존 delegate job ID'),
282
- reply: z.string().min(1).describe('후속 사용자 응답'),
283
- done: z.boolean().default(false).describe('true이면 응답 처리 후 대화를 종료'),
331
+ job_id: z.string().min(1).describe("후속 응답을 보낼 기존 delegate job ID"),
332
+ reply: z.string().min(1).describe("후속 사용자 응답"),
333
+ done: z
334
+ .boolean()
335
+ .default(false)
336
+ .describe("true이면 응답 처리 후 대화를 종료"),
284
337
  });
285
338
 
286
339
  const DelegateOutputSchema = z.object({
287
340
  ok: z.boolean(),
288
341
  jobId: z.string().optional(),
289
342
  job_id: z.string().optional(),
290
- mode: z.enum(['sync', 'async']).optional(),
291
- status: z.enum(['running', 'completed', 'failed']).optional(),
343
+ mode: z.enum(["sync", "async"]).optional(),
344
+ status: z.enum(["running", "completed", "failed"]).optional(),
292
345
  error: z.string().optional(),
293
346
  providerRequested: z.string().optional(),
294
347
  providerResolved: z.string().nullable().optional(),
@@ -308,29 +361,33 @@ const DelegateOutputSchema = z.object({
308
361
 
309
362
  function isTeamRouteRequested(args) {
310
363
  return Boolean(
311
- args.teamName
312
- || args.teamTaskId
313
- || args.teamAgentName
314
- || args.teamLeadName
315
- || args.hubUrl
364
+ args.teamName ||
365
+ args.teamTaskId ||
366
+ args.teamAgentName ||
367
+ args.teamLeadName ||
368
+ args.hubUrl,
316
369
  );
317
370
  }
318
371
 
319
372
  function pickRouteMode(provider) {
320
- return provider === 'auto' ? 'auto' : provider;
373
+ return provider === "auto" ? "auto" : provider;
321
374
  }
322
375
 
323
376
  function sanitizeDelegateArgs(args = {}) {
324
377
  return {
325
- provider: args.provider || 'auto',
326
- agentType: args.agentType || 'executor',
378
+ provider: args.provider || "auto",
379
+ agentType: args.agentType || "executor",
327
380
  cwd: args.cwd || null,
328
- timeoutMs: Number.isFinite(Number(args.timeoutMs)) ? Math.trunc(Number(args.timeoutMs)) : null,
381
+ timeoutMs: Number.isFinite(Number(args.timeoutMs))
382
+ ? Math.trunc(Number(args.timeoutMs))
383
+ : null,
329
384
  sessionKey: args.sessionKey || null,
330
385
  resetSession: Boolean(args.resetSession),
331
- mcpProfile: args.mcpProfile || 'auto',
386
+ mcpProfile: args.mcpProfile || "auto",
332
387
  contextFile: args.contextFile || null,
333
- availableServers: Array.isArray(args.availableServers) ? args.availableServers : null,
388
+ availableServers: Array.isArray(args.availableServers)
389
+ ? args.availableServers
390
+ : null,
334
391
  searchTool: args.searchTool || null,
335
392
  workerIndex: Number.isInteger(args.workerIndex) ? args.workerIndex : null,
336
393
  model: args.model || null,
@@ -348,21 +405,21 @@ function sanitizeDelegateArgs(args = {}) {
348
405
  }
349
406
 
350
407
  function formatConversationTranscript(turns = []) {
351
- return turns.map((turn, index) => {
352
- const parts = [
353
- `Turn ${index + 1} user:\n${turn.user}`,
354
- ];
355
- if (typeof turn.assistant === 'string' && turn.assistant.trim()) {
356
- parts.push(`Turn ${index + 1} assistant:\n${turn.assistant}`);
357
- }
358
- return parts.join('\n\n');
359
- }).join('\n\n');
408
+ return turns
409
+ .map((turn, index) => {
410
+ const parts = [`Turn ${index + 1} user:\n${turn.user}`];
411
+ if (typeof turn.assistant === "string" && turn.assistant.trim()) {
412
+ parts.push(`Turn ${index + 1} assistant:\n${turn.assistant}`);
413
+ }
414
+ return parts.join("\n\n");
415
+ })
416
+ .join("\n\n");
360
417
  }
361
418
 
362
419
  async function emitProgress(extra, progress, total, message) {
363
420
  if (extra?._meta?.progressToken === undefined) return;
364
421
  await extra.sendNotification({
365
- method: 'notifications/progress',
422
+ method: "notifications/progress",
366
423
  params: {
367
424
  progressToken: extra._meta.progressToken,
368
425
  progress,
@@ -373,34 +430,39 @@ async function emitProgress(extra, progress, total, message) {
373
430
  }
374
431
 
375
432
  export class DelegatorMcpWorker {
376
- type = 'delegator';
433
+ type = "delegator";
377
434
 
378
435
  constructor(options = {}) {
379
436
  this.cwd = options.cwd || process.cwd();
380
437
  this.env = cloneEnv({ ...cloneEnv(process.env), ...cloneEnv(options.env) });
381
438
  this.routeScript = resolveRouteScript(options.routeScript, this.cwd);
382
- this.bashCommand = options.bashCommand
383
- || this.env.TFX_DELEGATOR_BASH_COMMAND
384
- || this.env.BASH_BIN
385
- || 'bash';
439
+ this.bashCommand =
440
+ options.bashCommand ||
441
+ this.env.TFX_DELEGATOR_BASH_COMMAND ||
442
+ this.env.BASH_BIN ||
443
+ "bash";
386
444
 
387
445
  this.codexWorker = new CodexMcpWorker({
388
- command: options.codexCommand
389
- || this.env.TFX_DELEGATOR_CODEX_COMMAND
390
- || this.env.CODEX_BIN
391
- || 'codex',
392
- args: Array.isArray(options.codexArgs) && options.codexArgs.length
393
- ? options.codexArgs
394
- : parseJsonArray(this.env.TFX_DELEGATOR_CODEX_ARGS_JSON, []),
446
+ command:
447
+ options.codexCommand ||
448
+ this.env.TFX_DELEGATOR_CODEX_COMMAND ||
449
+ this.env.CODEX_BIN ||
450
+ "codex",
451
+ args:
452
+ Array.isArray(options.codexArgs) && options.codexArgs.length
453
+ ? options.codexArgs
454
+ : parseJsonArray(this.env.TFX_DELEGATOR_CODEX_ARGS_JSON, []),
395
455
  cwd: this.cwd,
396
456
  env: this.env,
397
457
  clientInfo: { name: SERVER_INFO.name, version: SERVER_INFO.version },
398
458
  });
399
459
 
400
- this.geminiCommand = options.geminiCommand || this.env.GEMINI_BIN || 'gemini';
401
- this.geminiCommandArgs = Array.isArray(options.geminiArgs) && options.geminiArgs.length
402
- ? [...options.geminiArgs]
403
- : parseJsonArray(this.env.GEMINI_BIN_ARGS_JSON, []);
460
+ this.geminiCommand =
461
+ options.geminiCommand || this.env.GEMINI_BIN || "gemini";
462
+ this.geminiCommandArgs =
463
+ Array.isArray(options.geminiArgs) && options.geminiArgs.length
464
+ ? [...options.geminiArgs]
465
+ : parseJsonArray(this.env.GEMINI_BIN_ARGS_JSON, []);
404
466
 
405
467
  this.server = null;
406
468
  this.transport = null;
@@ -424,32 +486,48 @@ export class DelegatorMcpWorker {
424
486
  capabilities: { logging: {} },
425
487
  });
426
488
 
427
- server.registerTool('triflux-delegate', {
428
- description: '새 위임을 실행합니다. codex/gemini direct 경로와 tfx-route 기반 auto 라우팅을 모두 지원합니다.',
429
- inputSchema: DelegateInputSchema,
430
- outputSchema: DelegateOutputSchema,
431
- }, async (args, extra) => {
432
- const payload = await this.delegate(args, extra);
433
- return createToolResponse(payload, { isError: payload.ok === false && payload.mode !== 'async' });
434
- });
435
-
436
- server.registerTool('triflux-delegate-status', {
437
- description: '비동기 위임 job 상태를 조회합니다.',
438
- inputSchema: DelegateStatusInputSchema,
439
- outputSchema: DelegateOutputSchema,
440
- }, async ({ jobId }, extra) => {
441
- const payload = await this.getJobStatus(jobId, extra);
442
- return createToolResponse(payload, { isError: payload.ok === false });
443
- });
444
-
445
- server.registerTool('triflux-delegate-reply', {
446
- description: '기존 delegate job 후속 응답을 보내고, Gemini direct job이면 multi-turn 대화를 이어갑니다.',
447
- inputSchema: DelegateReplyInputSchema,
448
- outputSchema: DelegateOutputSchema,
449
- }, async (args, extra) => {
450
- const payload = await this.reply(args, extra);
451
- return createToolResponse(payload, { isError: payload.ok === false });
452
- });
489
+ server.registerTool(
490
+ "triflux-delegate",
491
+ {
492
+ description:
493
+ "새 위임을 실행합니다. codex/gemini direct 경로와 tfx-route 기반 auto 라우팅을 모두 지원합니다.",
494
+ inputSchema: DelegateInputSchema,
495
+ outputSchema: DelegateOutputSchema,
496
+ },
497
+ async (args, extra) => {
498
+ const payload = await this.delegate(args, extra);
499
+ return createToolResponse(payload, {
500
+ isError: payload.ok === false && payload.mode !== "async",
501
+ });
502
+ },
503
+ );
504
+
505
+ server.registerTool(
506
+ "triflux-delegate-status",
507
+ {
508
+ description: "비동기 위임 job 상태를 조회합니다.",
509
+ inputSchema: DelegateStatusInputSchema,
510
+ outputSchema: DelegateOutputSchema,
511
+ },
512
+ async ({ jobId }, extra) => {
513
+ const payload = await this.getJobStatus(jobId, extra);
514
+ return createToolResponse(payload, { isError: payload.ok === false });
515
+ },
516
+ );
517
+
518
+ server.registerTool(
519
+ "triflux-delegate-reply",
520
+ {
521
+ description:
522
+ "기존 delegate job에 후속 응답을 보내고, Gemini direct job이면 multi-turn 대화를 이어갑니다.",
523
+ inputSchema: DelegateReplyInputSchema,
524
+ outputSchema: DelegateOutputSchema,
525
+ },
526
+ async (args, extra) => {
527
+ const payload = await this.reply(args, extra);
528
+ return createToolResponse(payload, { isError: payload.ok === false });
529
+ },
530
+ );
453
531
 
454
532
  this.server = server;
455
533
  this.ready = true;
@@ -467,7 +545,9 @@ export class DelegatorMcpWorker {
467
545
  this.ready = false;
468
546
 
469
547
  for (const child of this.routeChildren) {
470
- try { child.kill(); } catch {}
548
+ try {
549
+ child.kill();
550
+ } catch {}
471
551
  }
472
552
  this.routeChildren.clear();
473
553
 
@@ -496,7 +576,7 @@ export class DelegatorMcpWorker {
496
576
  async execute(prompt, options = {}) {
497
577
  const result = await this._executeDirect({ prompt, ...options });
498
578
  return {
499
- output: result.output || result.error || '',
579
+ output: result.output || result.error || "",
500
580
  exitCode: result.exitCode ?? (result.ok ? 0 : 1),
501
581
  threadId: result.threadId || null,
502
582
  sessionKey: result.sessionKey || null,
@@ -505,7 +585,7 @@ export class DelegatorMcpWorker {
505
585
  }
506
586
 
507
587
  async delegate(args, extra) {
508
- if (args.mode === 'async') {
588
+ if (args.mode === "async") {
509
589
  return this._startAsyncJob(args, extra);
510
590
  }
511
591
  return this._runSyncJob(args, extra);
@@ -518,11 +598,11 @@ export class DelegatorMcpWorker {
518
598
  }
519
599
 
520
600
  const payload = this._serializeJob(job);
521
- if (job.status === 'running') {
601
+ if (job.status === "running") {
522
602
  await emitProgress(extra, 25, 100, `job ${jobId} 실행 중`);
523
- } else if (job.status === 'completed') {
603
+ } else if (job.status === "completed") {
524
604
  await emitProgress(extra, DIRECT_PROGRESS_DONE, 100, `job ${jobId} 완료`);
525
- } else if (job.status === 'failed') {
605
+ } else if (job.status === "failed") {
526
606
  await emitProgress(extra, DIRECT_PROGRESS_DONE, 100, `job ${jobId} 실패`);
527
607
  }
528
608
  return payload;
@@ -531,28 +611,51 @@ export class DelegatorMcpWorker {
531
611
  async reply({ job_id, reply, done = false }, extra) {
532
612
  const job = this.jobs.get(job_id);
533
613
  if (!job) {
534
- return createErrorPayload(`알 수 없는 jobId: ${job_id}`, { jobId: job_id, job_id });
535
- }
536
- if (job.status === 'running') {
537
- return createErrorPayload(`job ${job_id}가 아직 실행 중입니다.`, { jobId: job_id, job_id });
614
+ return createErrorPayload(`알 수 없는 jobId: ${job_id}`, {
615
+ jobId: job_id,
616
+ job_id,
617
+ });
538
618
  }
539
- if (job.providerRequested !== 'gemini' || job.transport !== 'gemini-worker') {
540
- return createErrorPayload('delegate-reply는 현재 direct Gemini job에만 지원됩니다.', {
619
+ if (job.status === "running") {
620
+ return createErrorPayload(`job ${job_id}가 아직 실행 중입니다.`, {
541
621
  jobId: job_id,
542
622
  job_id,
543
623
  });
544
624
  }
625
+ if (
626
+ job.providerRequested !== "gemini" ||
627
+ job.transport !== "gemini-worker"
628
+ ) {
629
+ return createErrorPayload(
630
+ "delegate-reply는 현재 direct Gemini job에만 지원됩니다.",
631
+ {
632
+ jobId: job_id,
633
+ job_id,
634
+ },
635
+ );
636
+ }
545
637
 
546
638
  const conversation = this.geminiConversations.get(job_id);
547
639
  if (!conversation) {
548
- return createErrorPayload(`Gemini 대화 컨텍스트가 없습니다: ${job_id}`, { jobId: job_id, job_id });
640
+ return createErrorPayload(`Gemini 대화 컨텍스트가 없습니다: ${job_id}`, {
641
+ jobId: job_id,
642
+ job_id,
643
+ });
549
644
  }
550
645
  if (conversation.closed) {
551
- return createErrorPayload(`이미 종료된 대화입니다: ${job_id}`, { jobId: job_id, job_id });
646
+ return createErrorPayload(`이미 종료된 대화입니다: ${job_id}`, {
647
+ jobId: job_id,
648
+ job_id,
649
+ });
552
650
  }
553
651
 
554
- await emitProgress(extra, DIRECT_PROGRESS_START, 100, `job ${job_id} 후속 응답을 시작합니다.`);
555
- job.status = 'running';
652
+ await emitProgress(
653
+ extra,
654
+ DIRECT_PROGRESS_START,
655
+ 100,
656
+ `job ${job_id} 후속 응답을 시작합니다.`,
657
+ );
658
+ job.status = "running";
556
659
  job.updatedAt = new Date().toISOString();
557
660
 
558
661
  const worker = this._createGeminiWorker();
@@ -564,8 +667,10 @@ export class DelegatorMcpWorker {
564
667
  cwd: job.requestArgs.cwd || this.cwd,
565
668
  timeoutMs: resolveTimeoutMs(job.agentType, job.requestArgs.timeoutMs),
566
669
  model: job.requestArgs.model || resolveGeminiModel(job.agentType),
567
- approvalMode: 'yolo',
568
- allowedMcpServerNames: getGeminiAllowedServers(this._getMcpPolicyOptions(job.requestArgs)),
670
+ approvalMode: "yolo",
671
+ allowedMcpServerNames: getGeminiAllowedServers(
672
+ this._getMcpPolicyOptions(job.requestArgs),
673
+ ),
569
674
  });
570
675
 
571
676
  conversation.turns.push({
@@ -580,26 +685,34 @@ export class DelegatorMcpWorker {
580
685
 
581
686
  this._applyJobResult(job, {
582
687
  ok: result.exitCode === 0,
583
- status: result.exitCode === 0 ? 'completed' : 'failed',
584
- providerRequested: 'gemini',
585
- providerResolved: 'gemini',
688
+ status: result.exitCode === 0 ? "completed" : "failed",
689
+ providerRequested: "gemini",
690
+ providerResolved: "gemini",
586
691
  agentType: job.agentType,
587
- transport: 'gemini-worker',
692
+ transport: "gemini-worker",
588
693
  exitCode: result.exitCode,
589
694
  output: result.output,
590
695
  sessionKey: result.sessionKey || job.sessionKey || null,
591
696
  });
592
- await emitProgress(extra, DIRECT_PROGRESS_DONE, 100, `job ${job_id} 후속 응답이 완료되었습니다.`);
697
+ await emitProgress(
698
+ extra,
699
+ DIRECT_PROGRESS_DONE,
700
+ 100,
701
+ `job ${job_id} 후속 응답이 완료되었습니다.`,
702
+ );
593
703
  return this._serializeJob(job);
594
704
  } catch (error) {
595
705
  const message = error instanceof Error ? error.message : String(error);
596
- this._applyJobResult(job, createErrorPayload(message, {
597
- mode: job.mode,
598
- providerRequested: 'gemini',
599
- providerResolved: 'gemini',
600
- agentType: job.agentType,
601
- transport: 'gemini-worker',
602
- }));
706
+ this._applyJobResult(
707
+ job,
708
+ createErrorPayload(message, {
709
+ mode: job.mode,
710
+ providerRequested: "gemini",
711
+ providerResolved: "gemini",
712
+ agentType: job.agentType,
713
+ transport: "gemini-worker",
714
+ }),
715
+ );
603
716
  return this._serializeJob(job);
604
717
  } finally {
605
718
  await worker.stop().catch(() => {});
@@ -617,15 +730,17 @@ export class DelegatorMcpWorker {
617
730
  }
618
731
 
619
732
  _buildDirectPrompt(args) {
620
- return withContext(String(args.prompt ?? ''), args.contextFile);
733
+ return withContext(String(args.prompt ?? ""), args.contextFile);
621
734
  }
622
735
 
623
736
  _buildDirectPromptWithHint(args) {
624
- return withPromptHint(String(args.prompt ?? ''), {
625
- agentType: args.agentType || 'executor',
626
- mcpProfile: args.mcpProfile || 'auto',
737
+ return withPromptHint(String(args.prompt ?? ""), {
738
+ agentType: args.agentType || "executor",
739
+ mcpProfile: args.mcpProfile || "auto",
627
740
  searchTool: args.searchTool,
628
- workerIndex: Number.isInteger(args.workerIndex) ? args.workerIndex : undefined,
741
+ workerIndex: Number.isInteger(args.workerIndex)
742
+ ? args.workerIndex
743
+ : undefined,
629
744
  contextFile: args.contextFile,
630
745
  });
631
746
  }
@@ -633,26 +748,30 @@ export class DelegatorMcpWorker {
633
748
  _buildGeminiReplyPrompt(conversation, reply) {
634
749
  const transcript = formatConversationTranscript(conversation.turns);
635
750
  return [
636
- 'Continue the conversation using the prior transcript below.',
637
- '',
638
- '<conversation_history>',
751
+ "Continue the conversation using the prior transcript below.",
752
+ "",
753
+ "<conversation_history>",
639
754
  transcript,
640
- '</conversation_history>',
641
- '',
642
- '<latest_user_reply>',
755
+ "</conversation_history>",
756
+ "",
757
+ "<latest_user_reply>",
643
758
  reply,
644
- '</latest_user_reply>',
645
- ].join('\n');
759
+ "</latest_user_reply>",
760
+ ].join("\n");
646
761
  }
647
762
 
648
763
  _getMcpPolicyOptions(args) {
649
764
  return {
650
- agentType: args.agentType || 'executor',
651
- requestedProfile: args.mcpProfile || 'auto',
765
+ agentType: args.agentType || "executor",
766
+ requestedProfile: args.mcpProfile || "auto",
652
767
  searchTool: args.searchTool,
653
- workerIndex: Number.isInteger(args.workerIndex) ? args.workerIndex : undefined,
654
- taskText: withContext(String(args.prompt ?? ''), args.contextFile),
655
- availableServers: Array.isArray(args.availableServers) ? args.availableServers : undefined,
768
+ workerIndex: Number.isInteger(args.workerIndex)
769
+ ? args.workerIndex
770
+ : undefined,
771
+ taskText: withContext(String(args.prompt ?? ""), args.contextFile),
772
+ availableServers: Array.isArray(args.availableServers)
773
+ ? args.availableServers
774
+ : undefined,
656
775
  };
657
776
  }
658
777
 
@@ -661,11 +780,16 @@ export class DelegatorMcpWorker {
661
780
  }
662
781
 
663
782
  _shouldUseRoute(args) {
664
- return args.provider === 'auto' || isTeamRouteRequested(args);
783
+ return args.provider === "auto" || isTeamRouteRequested(args);
665
784
  }
666
785
 
667
786
  async _executeDirect(args, extra = null) {
668
- await emitProgress(extra, DIRECT_PROGRESS_START, 100, '위임 실행을 시작합니다.');
787
+ await emitProgress(
788
+ extra,
789
+ DIRECT_PROGRESS_START,
790
+ 100,
791
+ "위임 실행을 시작합니다.",
792
+ );
669
793
 
670
794
  const runViaRoute = this._shouldUseRoute(args);
671
795
 
@@ -674,50 +798,63 @@ export class DelegatorMcpWorker {
674
798
  ? await this._executeRoute(args, extra)
675
799
  : await this._executeWorker(args, extra);
676
800
 
677
- await emitProgress(extra, DIRECT_PROGRESS_DONE, 100, '위임이 완료되었습니다.');
801
+ await emitProgress(
802
+ extra,
803
+ DIRECT_PROGRESS_DONE,
804
+ 100,
805
+ "위임이 완료되었습니다.",
806
+ );
678
807
  return result;
679
808
  } catch (error) {
680
809
  const message = error instanceof Error ? error.message : String(error);
681
810
  return createErrorPayload(message, {
682
- mode: 'sync',
811
+ mode: "sync",
683
812
  providerRequested: args.provider,
684
813
  agentType: args.agentType,
685
- transport: runViaRoute ? 'route-script' : `${args.provider}-worker`,
814
+ transport: runViaRoute ? "route-script" : `${args.provider}-worker`,
686
815
  });
687
816
  }
688
817
  }
689
818
 
690
819
  async _executeWorker(args, extra) {
691
- await emitProgress(extra, DIRECT_PROGRESS_RUNNING, 100, '직접 워커 경로로 실행 중입니다.');
692
-
693
- if (args.provider === 'codex') {
694
- const result = await this.codexWorker.execute(this._buildDirectPrompt(args), {
695
- cwd: args.cwd || this.cwd,
696
- timeoutMs: resolveTimeoutMs(args.agentType, args.timeoutMs),
697
- sessionKey: args.sessionKey,
698
- threadId: args.threadId,
699
- resetSession: args.resetSession,
700
- profile: resolveCodexProfile(args.agentType),
701
- sandbox: 'danger-full-access',
702
- approvalPolicy: 'never',
703
- developerInstructions: joinInstructions(
704
- REVIEW_INSTRUCTION_BY_AGENT[args.agentType],
705
- this._buildPromptHintInstruction(args),
706
- args.developerInstructions,
707
- ),
708
- config: getCodexMcpConfig(this._getMcpPolicyOptions(args)),
709
- compactPrompt: args.compactPrompt,
710
- model: args.model,
711
- });
820
+ await emitProgress(
821
+ extra,
822
+ DIRECT_PROGRESS_RUNNING,
823
+ 100,
824
+ "직접 워커 경로로 실행 중입니다.",
825
+ );
826
+
827
+ if (args.provider === "codex") {
828
+ const result = await this.codexWorker.execute(
829
+ this._buildDirectPrompt(args),
830
+ {
831
+ cwd: args.cwd || this.cwd,
832
+ timeoutMs: resolveTimeoutMs(args.agentType, args.timeoutMs),
833
+ sessionKey: args.sessionKey,
834
+ threadId: args.threadId,
835
+ resetSession: args.resetSession,
836
+ profile: resolveCodexProfile(args.agentType),
837
+ sandbox: "danger-full-access",
838
+ approvalPolicy: "never",
839
+ developerInstructions: joinInstructions(
840
+ REVIEW_INSTRUCTION_BY_AGENT[args.agentType],
841
+ this._buildPromptHintInstruction(args),
842
+ args.developerInstructions,
843
+ ),
844
+ config: getCodexMcpConfig(this._getMcpPolicyOptions(args)),
845
+ compactPrompt: args.compactPrompt,
846
+ model: args.model,
847
+ },
848
+ );
712
849
 
713
850
  return {
714
851
  ok: result.exitCode === 0,
715
- mode: 'sync',
716
- status: result.exitCode === 0 ? 'completed' : 'failed',
717
- providerRequested: 'codex',
718
- providerResolved: 'codex',
852
+ mode: "sync",
853
+ status: result.exitCode === 0 ? "completed" : "failed",
854
+ providerRequested: "codex",
855
+ providerResolved: "codex",
719
856
  agentType: args.agentType,
720
- transport: 'codex-mcp',
857
+ transport: "codex-mcp",
721
858
  exitCode: result.exitCode,
722
859
  output: result.output,
723
860
  sessionKey: result.sessionKey,
@@ -725,7 +862,7 @@ export class DelegatorMcpWorker {
725
862
  };
726
863
  }
727
864
 
728
- if (args.provider === 'gemini') {
865
+ if (args.provider === "gemini") {
729
866
  const worker = this._createGeminiWorker();
730
867
  const prompt = this._buildDirectPromptWithHint(args);
731
868
  try {
@@ -733,18 +870,20 @@ export class DelegatorMcpWorker {
733
870
  cwd: args.cwd || this.cwd,
734
871
  timeoutMs: resolveTimeoutMs(args.agentType, args.timeoutMs),
735
872
  model: args.model || resolveGeminiModel(args.agentType),
736
- approvalMode: 'yolo',
737
- allowedMcpServerNames: getGeminiAllowedServers(this._getMcpPolicyOptions(args)),
873
+ approvalMode: "yolo",
874
+ allowedMcpServerNames: getGeminiAllowedServers(
875
+ this._getMcpPolicyOptions(args),
876
+ ),
738
877
  });
739
878
 
740
879
  return {
741
880
  ok: result.exitCode === 0,
742
- mode: 'sync',
743
- status: result.exitCode === 0 ? 'completed' : 'failed',
744
- providerRequested: 'gemini',
745
- providerResolved: 'gemini',
881
+ mode: "sync",
882
+ status: result.exitCode === 0 ? "completed" : "failed",
883
+ providerRequested: "gemini",
884
+ providerResolved: "gemini",
746
885
  agentType: args.agentType,
747
- transport: 'gemini-worker',
886
+ transport: "gemini-worker",
748
887
  exitCode: result.exitCode,
749
888
  output: result.output,
750
889
  sessionKey: result.sessionKey,
@@ -755,34 +894,42 @@ export class DelegatorMcpWorker {
755
894
  }
756
895
  }
757
896
 
758
- return createErrorPayload(`지원하지 않는 direct provider: ${args.provider}`, {
759
- mode: 'sync',
760
- providerRequested: args.provider,
761
- agentType: args.agentType,
762
- transport: 'direct-worker',
763
- });
897
+ return createErrorPayload(
898
+ `지원하지 않는 direct provider: ${args.provider}`,
899
+ {
900
+ mode: "sync",
901
+ providerRequested: args.provider,
902
+ agentType: args.agentType,
903
+ transport: "direct-worker",
904
+ },
905
+ );
764
906
  }
765
907
 
766
908
  async _executeRoute(args, extra) {
767
909
  if (!this.routeScript) {
768
- return createErrorPayload('tfx-route.sh 경로를 찾지 못했습니다.', {
769
- mode: 'sync',
910
+ return createErrorPayload("tfx-route.sh 경로를 찾지 못했습니다.", {
911
+ mode: "sync",
770
912
  providerRequested: args.provider,
771
913
  agentType: args.agentType,
772
- transport: 'route-script',
914
+ transport: "route-script",
773
915
  });
774
916
  }
775
917
 
776
- await emitProgress(extra, DIRECT_PROGRESS_RUNNING, 100, 'tfx-route.sh 경로로 실행 중입니다.');
918
+ await emitProgress(
919
+ extra,
920
+ DIRECT_PROGRESS_RUNNING,
921
+ 100,
922
+ "tfx-route.sh 경로로 실행 중입니다.",
923
+ );
777
924
  const result = await this._spawnRoute(args);
778
925
  return {
779
926
  ok: result.exitCode === 0,
780
- mode: 'sync',
781
- status: result.exitCode === 0 ? 'completed' : 'failed',
927
+ mode: "sync",
928
+ status: result.exitCode === 0 ? "completed" : "failed",
782
929
  providerRequested: args.provider,
783
930
  providerResolved: parseRouteType(result.stderr) || args.provider,
784
931
  agentType: args.agentType,
785
- transport: 'route-script',
932
+ transport: "route-script",
786
933
  exitCode: result.exitCode,
787
934
  output: result.stdout.trim() || result.stderr.trim(),
788
935
  stderr: result.stderr.trim(),
@@ -790,10 +937,15 @@ export class DelegatorMcpWorker {
790
937
  }
791
938
 
792
939
  async _startAsyncJob(args, extra) {
793
- const job = this._createJob(args, 'async');
940
+ const job = this._createJob(args, "async");
794
941
  this.jobs.set(job.jobId, job);
795
942
 
796
- await emitProgress(extra, DIRECT_PROGRESS_START, 100, `비동기 job ${job.jobId}를 시작합니다.`);
943
+ await emitProgress(
944
+ extra,
945
+ DIRECT_PROGRESS_START,
946
+ 100,
947
+ `비동기 job ${job.jobId}를 시작합니다.`,
948
+ );
797
949
 
798
950
  void (async () => {
799
951
  try {
@@ -802,15 +954,20 @@ export class DelegatorMcpWorker {
802
954
  : await this._runAsyncWorker(args, job);
803
955
  this._applyJobResult(job, result);
804
956
  } catch (error) {
805
- this._applyJobResult(job, createErrorPayload(
806
- error instanceof Error ? error.message : String(error),
807
- {
808
- mode: 'async',
809
- providerRequested: args.provider,
810
- agentType: args.agentType,
811
- transport: this._shouldUseRoute(args) ? 'route-script' : `${args.provider}-worker`,
812
- },
813
- ));
957
+ this._applyJobResult(
958
+ job,
959
+ createErrorPayload(
960
+ error instanceof Error ? error.message : String(error),
961
+ {
962
+ mode: "async",
963
+ providerRequested: args.provider,
964
+ agentType: args.agentType,
965
+ transport: this._shouldUseRoute(args)
966
+ ? "route-script"
967
+ : `${args.provider}-worker`,
968
+ },
969
+ ),
970
+ );
814
971
  } finally {
815
972
  if (job.worker) {
816
973
  await job.worker.stop().catch(() => {});
@@ -824,29 +981,32 @@ export class DelegatorMcpWorker {
824
981
  }
825
982
 
826
983
  async _runAsyncWorker(args, job) {
827
- if (args.provider === 'codex') {
828
- const result = await this.codexWorker.execute(this._buildDirectPrompt(args), {
829
- cwd: args.cwd || this.cwd,
830
- timeoutMs: resolveTimeoutMs(args.agentType, args.timeoutMs),
831
- sessionKey: args.sessionKey,
832
- threadId: args.threadId,
833
- resetSession: args.resetSession,
834
- profile: resolveCodexProfile(args.agentType),
835
- sandbox: 'danger-full-access',
836
- approvalPolicy: 'never',
837
- developerInstructions: joinInstructions(
838
- REVIEW_INSTRUCTION_BY_AGENT[args.agentType],
839
- this._buildPromptHintInstruction(args),
840
- args.developerInstructions,
841
- ),
842
- config: getCodexMcpConfig(this._getMcpPolicyOptions(args)),
843
- compactPrompt: args.compactPrompt,
844
- model: args.model,
845
- });
984
+ if (args.provider === "codex") {
985
+ const result = await this.codexWorker.execute(
986
+ this._buildDirectPrompt(args),
987
+ {
988
+ cwd: args.cwd || this.cwd,
989
+ timeoutMs: resolveTimeoutMs(args.agentType, args.timeoutMs),
990
+ sessionKey: args.sessionKey,
991
+ threadId: args.threadId,
992
+ resetSession: args.resetSession,
993
+ profile: resolveCodexProfile(args.agentType),
994
+ sandbox: "danger-full-access",
995
+ approvalPolicy: "never",
996
+ developerInstructions: joinInstructions(
997
+ REVIEW_INSTRUCTION_BY_AGENT[args.agentType],
998
+ this._buildPromptHintInstruction(args),
999
+ args.developerInstructions,
1000
+ ),
1001
+ config: getCodexMcpConfig(this._getMcpPolicyOptions(args)),
1002
+ compactPrompt: args.compactPrompt,
1003
+ model: args.model,
1004
+ },
1005
+ );
846
1006
 
847
1007
  return {
848
1008
  ok: result.exitCode === 0,
849
- providerResolved: 'codex',
1009
+ providerResolved: "codex",
850
1010
  output: result.output,
851
1011
  exitCode: result.exitCode,
852
1012
  threadId: result.threadId,
@@ -854,7 +1014,7 @@ export class DelegatorMcpWorker {
854
1014
  };
855
1015
  }
856
1016
 
857
- if (args.provider === 'gemini') {
1017
+ if (args.provider === "gemini") {
858
1018
  const worker = this._createGeminiWorker();
859
1019
  job.worker = worker;
860
1020
  const prompt = this._buildDirectPromptWithHint(args);
@@ -862,13 +1022,15 @@ export class DelegatorMcpWorker {
862
1022
  cwd: args.cwd || this.cwd,
863
1023
  timeoutMs: resolveTimeoutMs(args.agentType, args.timeoutMs),
864
1024
  model: args.model || resolveGeminiModel(args.agentType),
865
- approvalMode: 'yolo',
866
- allowedMcpServerNames: getGeminiAllowedServers(this._getMcpPolicyOptions(args)),
1025
+ approvalMode: "yolo",
1026
+ allowedMcpServerNames: getGeminiAllowedServers(
1027
+ this._getMcpPolicyOptions(args),
1028
+ ),
867
1029
  });
868
1030
 
869
1031
  return {
870
1032
  ok: result.exitCode === 0,
871
- providerResolved: 'gemini',
1033
+ providerResolved: "gemini",
872
1034
  output: result.output,
873
1035
  exitCode: result.exitCode,
874
1036
  sessionKey: result.sessionKey,
@@ -887,7 +1049,7 @@ export class DelegatorMcpWorker {
887
1049
  env.TFX_CODEX_TRANSPORT = args.codexTransport;
888
1050
  }
889
1051
  if (args.noClaudeNative === true) {
890
- env.TFX_NO_CLAUDE_NATIVE = '1';
1052
+ env.TFX_NO_CLAUDE_NATIVE = "1";
891
1053
  }
892
1054
  if (args.searchTool) {
893
1055
  env.TFX_SEARCH_TOOL = args.searchTool;
@@ -905,19 +1067,19 @@ export class DelegatorMcpWorker {
905
1067
  }
906
1068
 
907
1069
  async _spawnRoute(args, job = null) {
908
- const prompt = withContext(String(args.prompt ?? ''), args.contextFile);
1070
+ const prompt = withContext(String(args.prompt ?? ""), args.contextFile);
909
1071
  const childArgs = [
910
1072
  this.routeScript,
911
- args.agentType || 'executor',
1073
+ args.agentType || "executor",
912
1074
  prompt,
913
- args.mcpProfile || 'auto',
1075
+ args.mcpProfile || "auto",
914
1076
  String(resolveTimeoutSec(args.agentType, args.timeoutMs)),
915
1077
  ];
916
1078
 
917
1079
  const child = spawn(this.bashCommand, childArgs, {
918
1080
  cwd: args.cwd || this.cwd,
919
1081
  env: this._buildRouteEnv(args),
920
- stdio: ['ignore', 'pipe', 'pipe'],
1082
+ stdio: ["ignore", "pipe", "pipe"],
921
1083
  windowsHide: true,
922
1084
  });
923
1085
 
@@ -931,16 +1093,16 @@ export class DelegatorMcpWorker {
931
1093
  const stdoutChunks = [];
932
1094
  const stderrChunks = [];
933
1095
 
934
- child.stdout.on('data', (chunk) => stdoutChunks.push(Buffer.from(chunk)));
935
- child.stderr.on('data', (chunk) => stderrChunks.push(Buffer.from(chunk)));
936
- child.once('error', (error) => {
1096
+ child.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.from(chunk)));
1097
+ child.stderr.on("data", (chunk) => stderrChunks.push(Buffer.from(chunk)));
1098
+ child.once("error", (error) => {
937
1099
  this.routeChildren.delete(child);
938
1100
  rejectPromise(error);
939
1101
  });
940
- child.once('close', (code) => {
1102
+ child.once("close", (code) => {
941
1103
  this.routeChildren.delete(child);
942
- const stdout = Buffer.concat(stdoutChunks).toString('utf8');
943
- const stderr = Buffer.concat(stderrChunks).toString('utf8');
1104
+ const stdout = Buffer.concat(stdoutChunks).toString("utf8");
1105
+ const stderr = Buffer.concat(stderrChunks).toString("utf8");
944
1106
  resolvePromise({
945
1107
  ok: code === 0,
946
1108
  providerResolved: parseRouteType(stderr) || args.provider,
@@ -957,7 +1119,7 @@ export class DelegatorMcpWorker {
957
1119
  return {
958
1120
  ok: job.ok,
959
1121
  job_id: job.jobId,
960
- mode: job.mode || 'async',
1122
+ mode: job.mode || "async",
961
1123
  status: job.status,
962
1124
  provider_requested: job.providerRequested,
963
1125
  provider_resolved: job.providerResolved,
@@ -969,7 +1131,7 @@ export class DelegatorMcpWorker {
969
1131
  completed_at: job.completedAt,
970
1132
  output: job.output,
971
1133
  stderr: job.stderr,
972
- error: '',
1134
+ error: "",
973
1135
  thread_id: job.threadId,
974
1136
  session_key: job.sessionKey,
975
1137
  conversation_open: this.geminiConversations.has(job.jobId),
@@ -983,17 +1145,19 @@ export class DelegatorMcpWorker {
983
1145
  ok: true,
984
1146
  jobId,
985
1147
  mode,
986
- status: 'running',
1148
+ status: "running",
987
1149
  providerRequested: args.provider,
988
1150
  providerResolved: null,
989
1151
  agentType: args.agentType,
990
- transport: this._shouldUseRoute(args) ? 'route-script' : `${args.provider}-worker`,
1152
+ transport: this._shouldUseRoute(args)
1153
+ ? "route-script"
1154
+ : `${args.provider}-worker`,
991
1155
  createdAt: now,
992
1156
  startedAt: now,
993
1157
  updatedAt: now,
994
1158
  completedAt: null,
995
- output: '',
996
- stderr: '',
1159
+ output: "",
1160
+ stderr: "",
997
1161
  exitCode: null,
998
1162
  threadId: null,
999
1163
  sessionKey: args.sessionKey || null,
@@ -1005,28 +1169,34 @@ export class DelegatorMcpWorker {
1005
1169
 
1006
1170
  _applyJobResult(job, result = {}) {
1007
1171
  job.ok = result.ok !== false;
1008
- job.status = job.ok ? 'completed' : 'failed';
1172
+ job.status = job.ok ? "completed" : "failed";
1009
1173
  job.providerResolved = result.providerResolved || job.providerRequested;
1010
1174
  job.transport = result.transport || job.transport;
1011
- job.output = result.output || '';
1012
- job.stderr = result.stderr || result.error || '';
1175
+ job.output = result.output || "";
1176
+ job.stderr = result.stderr || result.error || "";
1013
1177
  job.exitCode = result.exitCode ?? (job.ok ? 0 : 1);
1014
1178
  job.threadId = result.threadId || job.threadId || null;
1015
1179
  job.sessionKey = result.sessionKey || job.sessionKey || null;
1016
1180
  job.completedAt = new Date().toISOString();
1017
1181
  job.updatedAt = job.completedAt;
1018
1182
 
1019
- if (job.providerRequested === 'gemini'
1020
- && job.transport === 'gemini-worker'
1021
- && typeof result._geminiPrompt === 'string') {
1022
- this._storeGeminiConversation(job, result._geminiPrompt, result.output || '');
1183
+ if (
1184
+ job.providerRequested === "gemini" &&
1185
+ job.transport === "gemini-worker" &&
1186
+ typeof result._geminiPrompt === "string"
1187
+ ) {
1188
+ this._storeGeminiConversation(
1189
+ job,
1190
+ result._geminiPrompt,
1191
+ result.output || "",
1192
+ );
1023
1193
  }
1024
1194
  }
1025
1195
 
1026
1196
  _storeGeminiConversation(job, userPrompt, assistantReply) {
1027
1197
  const existing = this.geminiConversations.get(job.jobId);
1028
1198
  if (existing) {
1029
- if (typeof assistantReply === 'string') {
1199
+ if (typeof assistantReply === "string") {
1030
1200
  const lastTurn = existing.turns.at(-1);
1031
1201
  if (lastTurn && lastTurn.assistant !== assistantReply) {
1032
1202
  lastTurn.assistant = assistantReply;
@@ -1040,15 +1210,17 @@ export class DelegatorMcpWorker {
1040
1210
  jobId: job.jobId,
1041
1211
  closed: false,
1042
1212
  updatedAt: new Date().toISOString(),
1043
- turns: [{
1044
- user: userPrompt,
1045
- assistant: assistantReply,
1046
- }],
1213
+ turns: [
1214
+ {
1215
+ user: userPrompt,
1216
+ assistant: assistantReply,
1217
+ },
1218
+ ],
1047
1219
  });
1048
1220
  }
1049
1221
 
1050
1222
  async _runSyncJob(args, extra) {
1051
- const job = this._createJob(args, 'sync');
1223
+ const job = this._createJob(args, "sync");
1052
1224
  this.jobs.set(job.jobId, job);
1053
1225
  const result = await this._executeDirect(args, extra);
1054
1226
  this._applyJobResult(job, result);
@@ -1065,7 +1237,9 @@ export async function runDelegatorMcpCli() {
1065
1237
  try {
1066
1238
  await worker.serveStdio();
1067
1239
  } catch (error) {
1068
- console.error(`[delegator-mcp] ${error instanceof Error ? error.message : String(error)}`);
1240
+ console.error(
1241
+ `[delegator-mcp] ${error instanceof Error ? error.message : String(error)}`,
1242
+ );
1069
1243
  process.exitCode = 1;
1070
1244
  }
1071
1245
  }