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
@@ -11,10 +11,10 @@
11
11
  * L3: Prompt acknowledged (첫 tool call/텍스트, 120s)
12
12
  */
13
13
  export const PROBE_LEVELS = Object.freeze({
14
- L0: 'alive',
15
- L1: 'advancing',
16
- L2: 'mcp_connected',
17
- L3: 'prompt_ack',
14
+ L0: "alive",
15
+ L1: "advancing",
16
+ L2: "mcp_connected",
17
+ L3: "prompt_ack",
18
18
  });
19
19
 
20
20
  /** 기본 설정 (기존 stallThresholdMs/stallTimeout 값 계승) */
@@ -32,13 +32,13 @@ export const PROBE_DEFAULTS = Object.freeze({
32
32
  * Codex가 질문하며 stdin을 기다리는 경우 stall이 아니라 INPUT_WAIT로 분류.
33
33
  */
34
34
  const INPUT_WAIT_PATTERNS = [
35
- /\?\s*$/m, // 물음표로 끝나는 줄
36
- /\b(y\/n|yes\/no)\b/i, // y/n 프롬프트
37
- /\b(choose|select|pick)\b.*:/i, // choose/select 프롬프트
38
- /\b(confirm|approve|proceed)\b/i, // confirm 프롬프트
39
- /\b(enter|input|type)\b.*:/i, // 입력 요청
40
- /\[.*\]:\s*$/m, // [default]: 형태
41
- />\s*$/m, // > 프롬프트
35
+ /\?\s*$/m, // 물음표로 끝나는 줄
36
+ /\b(y\/n|yes\/no)\b/i, // y/n 프롬프트
37
+ /\b(choose|select|pick)\b.*:/i, // choose/select 프롬프트
38
+ /\b(confirm|approve|proceed)\b/i, // confirm 프롬프트
39
+ /\b(enter|input|type)\b.*:/i, // 입력 요청
40
+ /\[.*\]:\s*$/m, // [default]: 형태
41
+ />\s*$/m, // > 프롬프트
42
42
  ];
43
43
 
44
44
  /**
@@ -49,7 +49,11 @@ const INPUT_WAIT_PATTERNS = [
49
49
  export function detectInputWait(recentOutput) {
50
50
  if (!recentOutput) return { detected: false, pattern: null };
51
51
  // 마지막 5줄만 검사 (전체 output이 아닌 최근 출력)
52
- const lines = recentOutput.split(/\r?\n/).filter(Boolean).slice(-5).join('\n');
52
+ const lines = recentOutput
53
+ .split(/\r?\n/)
54
+ .filter(Boolean)
55
+ .slice(-5)
56
+ .join("\n");
53
57
  for (const re of INPUT_WAIT_PATTERNS) {
54
58
  if (re.test(lines)) {
55
59
  return { detected: true, pattern: re.source };
@@ -84,10 +88,10 @@ export function createHealthProbe(session, opts = {}) {
84
88
  let spawnedAt = Date.now();
85
89
 
86
90
  const status = {
87
- l0: null, // 'ok' | 'fail'
88
- l1: null, // 'ok' | 'stall' | 'input_wait'
89
- l2: null, // 'ok' | 'fail' | 'skip'
90
- l3: null, // 'ok' | 'timeout'
91
+ l0: null, // 'ok' | 'fail'
92
+ l1: null, // 'ok' | 'stall' | 'input_wait'
93
+ l2: null, // 'ok' | 'fail' | 'skip'
94
+ l3: null, // 'ok' | 'timeout'
91
95
  lastProbeAt: null,
92
96
  inputWaitPattern: null,
93
97
  };
@@ -96,10 +100,11 @@ export function createHealthProbe(session, opts = {}) {
96
100
  * L0: Process alive check.
97
101
  */
98
102
  function probeL0() {
99
- const alive = session.alive !== undefined
100
- ? session.alive
101
- : (session.pid != null && session.pid > 0);
102
- status.l0 = alive ? 'ok' : 'fail';
103
+ const alive =
104
+ session.alive !== undefined
105
+ ? session.alive
106
+ : session.pid != null && session.pid > 0;
107
+ status.l0 = alive ? "ok" : "fail";
103
108
  return status.l0;
104
109
  }
105
110
 
@@ -107,43 +112,45 @@ export function createHealthProbe(session, opts = {}) {
107
112
  * L1 + L1.5: Output advancing + INPUT_WAIT 감지.
108
113
  */
109
114
  function probeL1() {
110
- const currentBytes = typeof session.getOutputBytes === 'function'
111
- ? session.getOutputBytes()
112
- : 0;
115
+ const currentBytes =
116
+ typeof session.getOutputBytes === "function"
117
+ ? session.getOutputBytes()
118
+ : 0;
113
119
 
114
120
  const now = Date.now();
115
121
 
116
122
  if (currentBytes !== lastOutputBytes) {
117
123
  lastOutputBytes = currentBytes;
118
124
  lastOutputChangeAt = now;
119
- status.l1 = 'ok';
125
+ status.l1 = "ok";
120
126
  status.inputWaitPattern = null;
121
- return 'ok';
127
+ return "ok";
122
128
  }
123
129
 
124
130
  const silenceMs = now - lastOutputChangeAt;
125
131
 
126
132
  if (silenceMs >= config.l1ThresholdMs) {
127
133
  // L1.5: INPUT_WAIT 감지 — stall 전에 질문 패턴 체크
128
- const recentOutput = typeof session.getRecentOutput === 'function'
129
- ? session.getRecentOutput()
130
- : '';
134
+ const recentOutput =
135
+ typeof session.getRecentOutput === "function"
136
+ ? session.getRecentOutput()
137
+ : "";
131
138
  const inputWait = detectInputWait(recentOutput);
132
139
 
133
140
  if (inputWait.detected) {
134
- status.l1 = 'input_wait';
141
+ status.l1 = "input_wait";
135
142
  status.inputWaitPattern = inputWait.pattern;
136
- return 'input_wait';
143
+ return "input_wait";
137
144
  }
138
145
 
139
- status.l1 = 'stall';
146
+ status.l1 = "stall";
140
147
  status.inputWaitPattern = null;
141
- return 'stall';
148
+ return "stall";
142
149
  }
143
150
 
144
151
  // 아직 threshold 미달
145
- status.l1 = 'ok';
146
- return 'ok';
152
+ status.l1 = "ok";
153
+ return "ok";
147
154
  }
148
155
 
149
156
  /**
@@ -151,23 +158,26 @@ export function createHealthProbe(session, opts = {}) {
151
158
  */
152
159
  async function probeL2() {
153
160
  if (!config.enableL2) {
154
- status.l2 = 'skip';
155
- return 'skip';
161
+ status.l2 = "skip";
162
+ return "skip";
156
163
  }
157
- if (typeof config.checkMcp !== 'function') {
158
- status.l2 = 'skip';
159
- return 'skip';
164
+ if (typeof config.checkMcp !== "function") {
165
+ status.l2 = "skip";
166
+ return "skip";
160
167
  }
161
168
  try {
162
169
  const connected = await Promise.race([
163
170
  config.checkMcp(),
164
171
  new Promise((_, reject) =>
165
- setTimeout(() => reject(new Error('probe_timeout')), config.probeTimeoutMs)
172
+ setTimeout(
173
+ () => reject(new Error("probe_timeout")),
174
+ config.probeTimeoutMs,
175
+ ),
166
176
  ),
167
177
  ]);
168
- status.l2 = connected ? 'ok' : 'fail';
178
+ status.l2 = connected ? "ok" : "fail";
169
179
  } catch {
170
- status.l2 = 'fail';
180
+ status.l2 = "fail";
171
181
  }
172
182
  return status.l2;
173
183
  }
@@ -178,24 +188,25 @@ export function createHealthProbe(session, opts = {}) {
178
188
  */
179
189
  function probeL3() {
180
190
  if (promptAcked) {
181
- status.l3 = 'ok';
182
- return 'ok';
191
+ status.l3 = "ok";
192
+ return "ok";
183
193
  }
184
194
 
185
- const currentBytes = typeof session.getOutputBytes === 'function'
186
- ? session.getOutputBytes()
187
- : 0;
195
+ const currentBytes =
196
+ typeof session.getOutputBytes === "function"
197
+ ? session.getOutputBytes()
198
+ : 0;
188
199
 
189
200
  if (currentBytes > 0) {
190
201
  promptAcked = true;
191
- status.l3 = 'ok';
192
- return 'ok';
202
+ status.l3 = "ok";
203
+ return "ok";
193
204
  }
194
205
 
195
206
  const elapsed = Date.now() - spawnedAt;
196
207
  if (elapsed >= config.l3ThresholdMs) {
197
- status.l3 = 'timeout';
198
- return 'timeout';
208
+ status.l3 = "timeout";
209
+ return "timeout";
199
210
  }
200
211
 
201
212
  status.l3 = null; // 아직 판정 전
@@ -217,7 +228,7 @@ export function createHealthProbe(session, opts = {}) {
217
228
  };
218
229
  status.lastProbeAt = result.ts;
219
230
 
220
- if (typeof config.onProbe === 'function') {
231
+ if (typeof config.onProbe === "function") {
221
232
  config.onProbe(result);
222
233
  }
223
234
 
@@ -232,7 +243,9 @@ export function createHealthProbe(session, opts = {}) {
232
243
  lastOutputBytes = 0;
233
244
  promptAcked = false;
234
245
 
235
- timer = setInterval(() => { void probe(); }, config.intervalMs);
246
+ timer = setInterval(() => {
247
+ void probe();
248
+ }, config.intervalMs);
236
249
  timer.unref?.();
237
250
 
238
251
  // 즉시 첫 probe 실행
@@ -267,6 +280,8 @@ export function createHealthProbe(session, opts = {}) {
267
280
  probe,
268
281
  resetTracking,
269
282
  getStatus: () => ({ ...status }),
270
- get started() { return started; },
283
+ get started() {
284
+ return started;
285
+ },
271
286
  });
272
287
  }
@@ -5,28 +5,28 @@
5
5
  // F4 해결: codex exec "prompt" 인라인 (파이프/리다이렉트 아님)
6
6
  // F5 해결: 동일 입력 → 동일 args 배열 (런타임 분기 없음)
7
7
 
8
- import { buildExecArgs as buildCodexArgs } from '../codex-adapter.mjs';
9
- import { buildExecArgs as buildGeminiArgs } from '../gemini-adapter.mjs';
8
+ import { buildExecArgs as buildCodexArgs } from "../codex-adapter.mjs";
9
+ import { buildExecArgs as buildGeminiArgs } from "../gemini-adapter.mjs";
10
10
 
11
11
  /** CLI별 adapter 레지스트리 */
12
12
  const ADAPTERS = Object.freeze({
13
13
  codex: {
14
- bin: 'codex',
14
+ bin: "codex",
15
15
  buildArgs: buildCodexArgs,
16
16
  env: (profile) => (profile ? { CODEX_PROFILE: profile } : {}),
17
17
  },
18
18
  gemini: {
19
- bin: 'gemini',
19
+ bin: "gemini",
20
20
  buildArgs: buildGeminiArgs,
21
21
  env: () => ({}),
22
22
  },
23
23
  claude: {
24
- bin: 'claude',
24
+ bin: "claude",
25
25
  buildArgs: (opts = {}) => {
26
- const parts = ['claude'];
27
- if (opts.model) parts.push('--model', opts.model);
28
- parts.push('-p', JSON.stringify(opts.prompt || ''));
29
- return parts.join(' ');
26
+ const parts = ["claude"];
27
+ if (opts.model) parts.push("--model", opts.model);
28
+ parts.push("-p", JSON.stringify(opts.prompt || ""));
29
+ return parts.join(" ");
30
30
  },
31
31
  env: () => ({}),
32
32
  },
@@ -41,7 +41,9 @@ const ADAPTERS = Object.freeze({
41
41
  export function getAdapter(agent) {
42
42
  const adapter = ADAPTERS[agent];
43
43
  if (!adapter) {
44
- throw new Error(`Unknown agent: "${agent}". Supported: ${Object.keys(ADAPTERS).join(', ')}`);
44
+ throw new Error(
45
+ `Unknown agent: "${agent}". Supported: ${Object.keys(ADAPTERS).join(", ")}`,
46
+ );
45
47
  }
46
48
  return adapter;
47
49
  }
@@ -60,10 +62,11 @@ export function getAdapter(agent) {
60
62
  * @returns {{ bin: string, command: string, env: object, agent: string }}
61
63
  */
62
64
  export function buildLauncher(opts) {
63
- const { agent, profile, prompt, workdir, model, resultFile, mcpServers } = opts;
65
+ const { agent, profile, prompt, workdir, model, resultFile, mcpServers } =
66
+ opts;
64
67
 
65
- if (!agent) throw new Error('agent is required');
66
- if (!prompt && prompt !== '') throw new Error('prompt is required');
68
+ if (!agent) throw new Error("agent is required");
69
+ if (!prompt && prompt !== "") throw new Error("prompt is required");
67
70
 
68
71
  const adapter = getAdapter(agent);
69
72
 
@@ -1,7 +1,13 @@
1
1
  // hub/team/native-supervisor.mjs — tmux 없이 멀티 CLI를 직접 띄우는 네이티브 팀 런타임
2
+
3
+ import { execSync as execSyncSupervisor, spawn } from "node:child_process";
4
+ import {
5
+ createWriteStream,
6
+ mkdirSync,
7
+ readFileSync,
8
+ writeFileSync,
9
+ } from "node:fs";
2
10
  import { createServer } from "node:http";
3
- import { spawn, execSync as execSyncSupervisor } from "node:child_process";
4
- import { mkdirSync, readFileSync, writeFileSync, createWriteStream } from "node:fs";
5
11
  import { dirname, join } from "node:path";
6
12
  import { verifySlimWrapperRouteExecution } from "./native.mjs";
7
13
  import { forceCleanupTeam } from "./nativeProxy.mjs";
@@ -130,12 +136,14 @@ const SAFE_COMMAND_RE = /^[a-zA-Z0-9 _./:@"'=\-\\]+$/;
130
136
 
131
137
  function validateMemberCommand(command, memberName) {
132
138
  if (typeof command !== "string" || command.trim().length === 0) {
133
- throw new Error(`member "${memberName}": command must be a non-empty string`);
139
+ throw new Error(
140
+ `member "${memberName}": command must be a non-empty string`,
141
+ );
134
142
  }
135
143
  if (!SAFE_COMMAND_RE.test(command)) {
136
144
  throw new Error(
137
145
  `member "${memberName}": command contains disallowed characters — ` +
138
- `shell metacharacters (;&|$\`()<>{}\\n\\r) are not permitted`
146
+ `shell metacharacters (;&|$\`()<>{}\\n\\r) are not permitted`,
139
147
  );
140
148
  }
141
149
  }
@@ -153,7 +161,10 @@ function spawnMember(member) {
153
161
  shell: true,
154
162
  env: {
155
163
  ...process.env,
156
- TERM: process.env.TERM && process.env.TERM !== "dumb" ? process.env.TERM : "xterm-256color",
164
+ TERM:
165
+ process.env.TERM && process.env.TERM !== "dumb"
166
+ ? process.env.TERM
167
+ : "xterm-256color",
157
168
  },
158
169
  stdio: ["pipe", "pipe", "pipe"],
159
170
  windowsHide: true,
@@ -176,7 +187,8 @@ function spawnMember(member) {
176
187
  const txt = safeText(buf).trim();
177
188
  if (txt) {
178
189
  const lines = txt.split(/\r?\n/).filter(Boolean);
179
- if (lines.length) state.lastPreview = lines[lines.length - 1].slice(0, 280);
190
+ if (lines.length)
191
+ state.lastPreview = lines[lines.length - 1].slice(0, 280);
180
192
  }
181
193
  });
182
194
 
@@ -185,7 +197,8 @@ function spawnMember(member) {
185
197
  const txt = safeText(buf).trim();
186
198
  if (txt) {
187
199
  const lines = txt.split(/\r?\n/).filter(Boolean);
188
- if (lines.length) state.lastPreview = `[err] ${lines[lines.length - 1].slice(0, 260)}`;
200
+ if (lines.length)
201
+ state.lastPreview = `[err] ${lines[lines.length - 1].slice(0, 260)}`;
189
202
  }
190
203
  });
191
204
 
@@ -193,8 +206,12 @@ function spawnMember(member) {
193
206
  state.status = "exited";
194
207
  state.exitCode = code;
195
208
  finalizeRouteVerification(state);
196
- try { outWs.end(); } catch {}
197
- try { errWs.end(); } catch {}
209
+ try {
210
+ outWs.end();
211
+ } catch {}
212
+ try {
213
+ errWs.end();
214
+ } catch {}
198
215
  maybeAutoShutdown();
199
216
  });
200
217
 
@@ -202,8 +219,12 @@ function spawnMember(member) {
202
219
  state.status = "exited";
203
220
  state.exitCode = -1;
204
221
  state.lastPreview = `[spawn error] ${err.message}`;
205
- try { outWs.end(); } catch {}
206
- try { errWs.end(); } catch {}
222
+ try {
223
+ outWs.end();
224
+ } catch {}
225
+ try {
226
+ errWs.end();
227
+ } catch {}
207
228
  maybeAutoShutdown();
208
229
  });
209
230
 
@@ -213,7 +234,8 @@ function spawnMember(member) {
213
234
  function sendInput(memberName, text) {
214
235
  const state = processMap.get(memberName);
215
236
  if (!state) return { ok: false, error: "member_not_found" };
216
- if (state.status !== "running") return { ok: false, error: "member_not_running" };
237
+ if (state.status !== "running")
238
+ return { ok: false, error: "member_not_running" };
217
239
  try {
218
240
  state.child.stdin.write(`${safeText(text)}\n`);
219
241
  return { ok: true };
@@ -225,7 +247,8 @@ function sendInput(memberName, text) {
225
247
  function interruptMember(memberName) {
226
248
  const state = processMap.get(memberName);
227
249
  if (!state) return { ok: false, error: "member_not_found" };
228
- if (state.status !== "running") return { ok: false, error: "member_not_running" };
250
+ if (state.status !== "running")
251
+ return { ok: false, error: "member_not_running" };
229
252
 
230
253
  let signaled = false;
231
254
  try {
@@ -250,7 +273,9 @@ let isShuttingDown = false;
250
273
 
251
274
  function maybeAutoShutdown() {
252
275
  if (isShuttingDown) return;
253
- const allExited = [...processMap.values()].every((s) => s.status === "exited");
276
+ const allExited = [...processMap.values()].every(
277
+ (s) => s.status === "exited",
278
+ );
254
279
  if (!allExited) return;
255
280
  shutdown();
256
281
  }
@@ -261,11 +286,19 @@ async function shutdown() {
261
286
 
262
287
  for (const state of processMap.values()) {
263
288
  if (state.status === "running") {
264
- try { state.child.stdin.write("exit\n"); } catch {}
265
- try { state.child.kill("SIGTERM"); } catch {}
289
+ try {
290
+ state.child.stdin.write("exit\n");
291
+ } catch {}
292
+ try {
293
+ state.child.kill("SIGTERM");
294
+ } catch {}
266
295
  }
267
- try { state.outWs.end(); } catch {}
268
- try { state.errWs.end(); } catch {}
296
+ try {
297
+ state.outWs.end();
298
+ } catch {}
299
+ try {
300
+ state.errWs.end();
301
+ } catch {}
269
302
  }
270
303
 
271
304
  try {
@@ -278,9 +311,17 @@ async function shutdown() {
278
311
  const pid = state.child?.pid;
279
312
  if (process.platform === "win32" && Number.isInteger(pid) && pid > 0) {
280
313
  // Windows: 프로세스 트리 전체 강제 종료 (손자 MCP 서버 포함)
281
- try { execSyncSupervisor(`taskkill /T /F /PID ${pid}`, { stdio: "pipe", windowsHide: true, timeout: 5000 }); } catch {}
314
+ try {
315
+ execSyncSupervisor(`taskkill /T /F /PID ${pid}`, {
316
+ stdio: "pipe",
317
+ windowsHide: true,
318
+ timeout: 5000,
319
+ });
320
+ } catch {}
282
321
  } else {
283
- try { state.child.kill("SIGKILL"); } catch {}
322
+ try {
323
+ state.child.kill("SIGKILL");
324
+ } catch {}
284
325
  }
285
326
  }
286
327
  }
@@ -321,7 +362,10 @@ const server = createServer(async (req, res) => {
321
362
  let totalLen = 0;
322
363
  for await (const c of req) {
323
364
  totalLen += c.length;
324
- if (totalLen > MAX_BODY) { send(413, { ok: false, error: "payload_too_large" }); return; }
365
+ if (totalLen > MAX_BODY) {
366
+ send(413, { ok: false, error: "payload_too_large" });
367
+ return;
368
+ }
325
369
  chunks.push(c);
326
370
  }
327
371
  const raw = Buffer.concat(chunks).toString("utf8") || "{}";