triflux 10.0.0 → 10.0.2

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