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
package/hub/router.mjs ADDED
@@ -0,0 +1,791 @@
1
+ // hub/router.mjs — 실시간 라우팅/수신함 상태 관리자
2
+ // SQLite는 감사 로그만 담당하고, 실제 배달 상태는 메모리에서 관리한다.
3
+ import { EventEmitter, once } from 'node:events';
4
+ import { uuidv7 } from './lib/uuidv7.mjs';
5
+
6
+ const ASSIGN_PENDING_STATUSES = new Set(['queued', 'running']);
7
+
8
+ function uniqueStrings(values = []) {
9
+ return Array.from(new Set((values || []).map((value) => String(value || '').trim()).filter(Boolean)));
10
+ }
11
+
12
+ function clampAssignDuration(value, fallback = 600000, min = 1000, max = 86400000) {
13
+ const num = Number(value);
14
+ if (!Number.isFinite(num)) return fallback;
15
+ return Math.max(min, Math.min(Math.trunc(num), max));
16
+ }
17
+
18
+ function normalizeAssignTerminalStatus(input, metadata = {}) {
19
+ const status = String(input || '').trim().toLowerCase();
20
+ const resultTag = String(
21
+ metadata?.result
22
+ ?? metadata?.status
23
+ ?? metadata?.outcome
24
+ ?? '',
25
+ ).trim().toLowerCase();
26
+
27
+ if (status === 'queued') return 'queued';
28
+ if (status === 'running' || status === 'in_progress') return 'running';
29
+ if (status === 'timed_out' || status === 'timeout') return 'timed_out';
30
+ if (status === 'failed' || status === 'error') return 'failed';
31
+ if (status === 'succeeded' || status === 'success') return 'succeeded';
32
+
33
+ if (status === 'completed') {
34
+ if (resultTag === 'failed' || resultTag === 'error') return 'failed';
35
+ if (resultTag === 'timed_out' || resultTag === 'timeout') return 'timed_out';
36
+ return 'succeeded';
37
+ }
38
+
39
+ if (resultTag === 'failed' || resultTag === 'error') return 'failed';
40
+ if (resultTag === 'timed_out' || resultTag === 'timeout') return 'timed_out';
41
+ if (resultTag === 'succeeded' || resultTag === 'success') return 'succeeded';
42
+ return 'succeeded';
43
+ }
44
+
45
+ function normalizeAgentTopics(store, agentId, runtimeTopics) {
46
+ const topics = new Set(runtimeTopics || []);
47
+ const persisted = store.getAgent(agentId)?.topics || [];
48
+ for (const topic of persisted) topics.add(topic);
49
+ return Array.from(topics);
50
+ }
51
+
52
+ /**
53
+ * 라우터 생성
54
+ * @param {object} store
55
+ */
56
+ export function createRouter(store) {
57
+ let sweepTimer = null;
58
+ let staleTimer = null;
59
+ const responseEmitter = new EventEmitter();
60
+ const deliveryEmitter = new EventEmitter();
61
+ responseEmitter.setMaxListeners(200);
62
+ deliveryEmitter.setMaxListeners(200);
63
+
64
+ const runtimeTopics = new Map();
65
+ const queuesByAgent = new Map();
66
+ const liveMessages = new Map();
67
+ const MAX_LATENCY_SAMPLES = 100;
68
+ let latencyIdx = 0;
69
+ const deliveryLatencies = new Array(MAX_LATENCY_SAMPLES).fill(0);
70
+
71
+ function ensureAgentQueue(agentId) {
72
+ let queue = queuesByAgent.get(agentId);
73
+ if (!queue) {
74
+ queue = new Map();
75
+ queuesByAgent.set(agentId, queue);
76
+ }
77
+ return queue;
78
+ }
79
+
80
+ function recordLatency(ms) {
81
+ deliveryLatencies[latencyIdx % MAX_LATENCY_SAMPLES] = ms;
82
+ latencyIdx++;
83
+ }
84
+
85
+ function upsertRuntimeTopics(agentId, topics, { replace = true } = {}) {
86
+ const normalized = uniqueStrings(topics);
87
+ const current = replace ? new Set() : new Set(runtimeTopics.get(agentId) || []);
88
+ for (const topic of normalized) current.add(topic);
89
+ runtimeTopics.set(agentId, current);
90
+ store.updateAgentTopics(agentId, Array.from(current));
91
+ return Array.from(current);
92
+ }
93
+
94
+ function listRuntimeTopics(agentId) {
95
+ return normalizeAgentTopics(store, agentId, runtimeTopics.get(agentId));
96
+ }
97
+
98
+ function trackMessage(message, recipients) {
99
+ liveMessages.set(message.id, {
100
+ message,
101
+ recipients: new Set(recipients),
102
+ ackedBy: new Set(),
103
+ });
104
+ }
105
+
106
+ function getMessageRecord(messageId) {
107
+ return liveMessages.get(messageId) || null;
108
+ }
109
+
110
+ function removeMessage(messageId) {
111
+ const record = liveMessages.get(messageId);
112
+ if (!record) return;
113
+ for (const agentId of record.recipients) {
114
+ queuesByAgent.get(agentId)?.delete(messageId);
115
+ }
116
+ liveMessages.delete(messageId);
117
+ }
118
+
119
+ function queueMessage(agentId, message) {
120
+ const queue = ensureAgentQueue(agentId);
121
+ queue.set(message.id, {
122
+ message,
123
+ attempts: 0,
124
+ delivered_at_ms: null,
125
+ acked_at_ms: null,
126
+ });
127
+ deliveryEmitter.emit('message', agentId, message);
128
+ }
129
+
130
+ function resolveRecipients(msg) {
131
+ const to = msg.to_agent ?? msg.to;
132
+ if (!to?.startsWith('topic:')) {
133
+ return [to];
134
+ }
135
+
136
+ const topic = to.slice(6);
137
+ const recipients = new Set();
138
+ for (const [agentId, topics] of runtimeTopics) {
139
+ if (topics.has(topic)) recipients.add(agentId);
140
+ }
141
+ for (const agent of store.getAgentsByTopic(topic)) {
142
+ recipients.add(agent.agent_id);
143
+ }
144
+ return Array.from(recipients);
145
+ }
146
+
147
+ function sortedPending(agentId, { max_messages = 20, include_topics = null } = {}) {
148
+ const queue = ensureAgentQueue(agentId);
149
+ const topicFilter = include_topics?.length ? new Set(include_topics) : null;
150
+ const now = Date.now();
151
+ const pending = [];
152
+
153
+ for (const delivery of queue.values()) {
154
+ const { message } = delivery;
155
+ if (delivery.acked_at_ms) continue;
156
+ if (message.expires_at_ms <= now) continue;
157
+ if (topicFilter && !topicFilter.has(message.topic)) continue;
158
+ pending.push(message);
159
+ }
160
+
161
+ pending.sort((a, b) => {
162
+ if (b.priority !== a.priority) return b.priority - a.priority;
163
+ return a.created_at_ms - b.created_at_ms;
164
+ });
165
+ return pending.slice(0, max_messages);
166
+ }
167
+
168
+ function markDelivered(agentId, messageId) {
169
+ const delivery = queuesByAgent.get(agentId)?.get(messageId);
170
+ const record = getMessageRecord(messageId);
171
+ if (!delivery || !record) return false;
172
+
173
+ delivery.attempts += 1;
174
+ if (!delivery.delivered_at_ms) {
175
+ delivery.delivered_at_ms = Date.now();
176
+ record.message.status = 'delivered';
177
+ store.updateMessageStatus(messageId, 'delivered');
178
+ recordLatency(delivery.delivered_at_ms - record.message.created_at_ms);
179
+ return true;
180
+ }
181
+ return false;
182
+ }
183
+
184
+ function ackMessages(ids, agentId) {
185
+ const now = Date.now();
186
+ let count = 0;
187
+
188
+ for (const id of ids || []) {
189
+ const delivery = queuesByAgent.get(agentId)?.get(id);
190
+ const record = getMessageRecord(id);
191
+ if (!delivery || !record || delivery.acked_at_ms) continue;
192
+
193
+ delivery.acked_at_ms = now;
194
+ record.ackedBy.add(agentId);
195
+ count += 1;
196
+
197
+ if (record.ackedBy.size >= record.recipients.size) {
198
+ record.message.status = 'acked';
199
+ store.updateMessageStatus(id, 'acked');
200
+ removeMessage(id);
201
+ }
202
+ }
203
+
204
+ return count;
205
+ }
206
+
207
+ function dispatchMessage({ type, from, to, topic, priority = 5, ttl_ms = 300000, payload = {}, trace_id, correlation_id }) {
208
+ const msg = store.auditLog({
209
+ type,
210
+ from,
211
+ to,
212
+ topic,
213
+ priority,
214
+ ttl_ms,
215
+ payload,
216
+ trace_id,
217
+ correlation_id,
218
+ });
219
+ const recipients = uniqueStrings(resolveRecipients(msg));
220
+ if (recipients.length) {
221
+ trackMessage(msg, recipients);
222
+ for (const agentId of recipients) {
223
+ queueMessage(agentId, msg);
224
+ }
225
+ msg.status = 'delivered';
226
+ store.updateMessageStatus(msg.id, 'delivered');
227
+ }
228
+ if (msg.type === 'response') {
229
+ responseEmitter.emit(msg.correlation_id, msg.payload);
230
+ }
231
+ return { msg, recipients };
232
+ }
233
+
234
+ function buildAssignSnapshot(job, extra = {}) {
235
+ if (!job) return null;
236
+ return {
237
+ job_id: job.job_id,
238
+ supervisor_agent: job.supervisor_agent,
239
+ worker_agent: job.worker_agent,
240
+ topic: job.topic,
241
+ task: job.task,
242
+ status: job.status,
243
+ attempt: job.attempt,
244
+ retry_count: job.retry_count,
245
+ max_retries: job.max_retries,
246
+ timeout_ms: job.timeout_ms,
247
+ deadline_ms: job.deadline_ms,
248
+ trace_id: job.trace_id,
249
+ correlation_id: job.correlation_id,
250
+ last_message_id: job.last_message_id,
251
+ result: job.result,
252
+ error: job.error,
253
+ updated_at_ms: job.updated_at_ms,
254
+ completed_at_ms: job.completed_at_ms,
255
+ ...extra,
256
+ };
257
+ }
258
+
259
+ function notifyAssignSupervisor(job, event, extra = {}) {
260
+ if (!job?.supervisor_agent) return null;
261
+ const { msg } = dispatchMessage({
262
+ type: 'event',
263
+ from: job.worker_agent || 'assign-router',
264
+ to: job.supervisor_agent,
265
+ topic: 'assign.result',
266
+ priority: Math.max(5, job.priority || 5),
267
+ ttl_ms: job.ttl_ms || job.timeout_ms || 600000,
268
+ payload: {
269
+ event,
270
+ ...buildAssignSnapshot(job),
271
+ ...extra,
272
+ },
273
+ trace_id: job.trace_id,
274
+ correlation_id: job.correlation_id,
275
+ });
276
+ return msg;
277
+ }
278
+
279
+ function dispatchAssignJob(job, reason = 'dispatch') {
280
+ const { msg, recipients } = dispatchMessage({
281
+ type: 'handoff',
282
+ from: job.supervisor_agent,
283
+ to: job.worker_agent,
284
+ topic: job.topic || 'assign.job',
285
+ priority: job.priority || 5,
286
+ ttl_ms: job.ttl_ms || job.timeout_ms || 600000,
287
+ payload: {
288
+ kind: 'assign.job',
289
+ reason,
290
+ assign_job_id: job.job_id,
291
+ attempt: job.attempt,
292
+ retry_count: job.retry_count,
293
+ max_retries: job.max_retries,
294
+ timeout_ms: job.timeout_ms,
295
+ supervisor_agent: job.supervisor_agent,
296
+ worker_agent: job.worker_agent,
297
+ task: job.task,
298
+ payload: job.payload || {},
299
+ },
300
+ trace_id: job.trace_id,
301
+ correlation_id: job.correlation_id,
302
+ });
303
+
304
+ const updated = store.updateAssignStatus(job.job_id, job.status, {
305
+ last_message_id: msg.id,
306
+ });
307
+ return { job: updated || job, recipients, message_id: msg.id };
308
+ }
309
+
310
+ function scheduleAssignRetry(job, reason, error = null, requested_by = 'system') {
311
+ if (!job) {
312
+ return { ok: false, error: { code: 'ASSIGN_NOT_FOUND', message: 'assign job not found' } };
313
+ }
314
+ if (job.retry_count >= job.max_retries) {
315
+ return {
316
+ ok: false,
317
+ error: {
318
+ code: 'ASSIGN_RETRY_EXHAUSTED',
319
+ message: `retry exhausted for ${job.job_id}`,
320
+ },
321
+ };
322
+ }
323
+
324
+ const queued = store.retryAssign(job.job_id, {
325
+ error,
326
+ timeout_ms: job.timeout_ms,
327
+ ttl_ms: job.ttl_ms,
328
+ });
329
+ const dispatched = dispatchAssignJob(queued, 'retry');
330
+ notifyAssignSupervisor(dispatched.job, 'retry_scheduled', {
331
+ retry_reason: reason,
332
+ requested_by,
333
+ });
334
+ return {
335
+ ok: true,
336
+ data: {
337
+ retried: true,
338
+ ...buildAssignSnapshot(dispatched.job, {
339
+ retry_reason: reason,
340
+ requested_by,
341
+ }),
342
+ },
343
+ };
344
+ }
345
+
346
+ function handleAssignTimeout(job) {
347
+ const timedOut = store.updateAssignStatus(job.job_id, 'timed_out', {
348
+ error: job.error ?? { message: 'assign job timed out' },
349
+ });
350
+
351
+ if (timedOut.retry_count < timedOut.max_retries) {
352
+ return scheduleAssignRetry(timedOut, 'timed_out', timedOut.error, 'sweeper');
353
+ }
354
+
355
+ notifyAssignSupervisor(timedOut, 'completed', {
356
+ completion_reason: 'timed_out',
357
+ });
358
+ return { ok: true, data: buildAssignSnapshot(timedOut, { completion_reason: 'timed_out' }) };
359
+ }
360
+
361
+ const router = {
362
+ responseEmitter,
363
+ deliveryEmitter,
364
+
365
+ registerAgent(args) {
366
+ const result = store.registerAgent(args);
367
+ upsertRuntimeTopics(args.agent_id, args.topics || [], { replace: true });
368
+ return result;
369
+ },
370
+
371
+ refreshAgentLease(agentId, ttlMs = 30000) {
372
+ return store.refreshLease(agentId, ttlMs);
373
+ },
374
+
375
+ subscribeAgent(agentId, topics, { replace = false } = {}) {
376
+ const nextTopics = upsertRuntimeTopics(agentId, topics, { replace });
377
+ return { agent_id: agentId, topics: nextTopics };
378
+ },
379
+
380
+ getSubscribedTopics(agentId) {
381
+ return listRuntimeTopics(agentId);
382
+ },
383
+
384
+ updateAgentStatus(agentId, status) {
385
+ if (status === 'offline') {
386
+ runtimeTopics.delete(agentId);
387
+ }
388
+ return store.updateAgentStatus(agentId, status);
389
+ },
390
+
391
+ route(msg) {
392
+ const recipients = uniqueStrings(resolveRecipients(msg));
393
+ if (!recipients.length) return 0;
394
+ if (!getMessageRecord(msg.id)) {
395
+ trackMessage(msg, recipients);
396
+ }
397
+ for (const agentId of recipients) {
398
+ queueMessage(agentId, msg);
399
+ }
400
+ store.updateMessageStatus(msg.id, 'delivered');
401
+ return recipients.length;
402
+ },
403
+
404
+ getPendingMessages(agentId, options = {}) {
405
+ return sortedPending(agentId, options);
406
+ },
407
+
408
+ countPendingMessages(agentId) {
409
+ const queue = ensureAgentQueue(agentId);
410
+ const now = Date.now();
411
+ let count = 0;
412
+ for (const delivery of queue.values()) {
413
+ if (delivery.acked_at_ms) continue;
414
+ if (delivery.message.expires_at_ms <= now) continue;
415
+ count++;
416
+ }
417
+ return count;
418
+ },
419
+
420
+ markMessagePushed(agentId, messageId) {
421
+ return markDelivered(agentId, messageId);
422
+ },
423
+
424
+ drainAgent(agentId, { max_messages = 20, include_topics = null, auto_ack = false } = {}) {
425
+ const messages = sortedPending(agentId, { max_messages, include_topics });
426
+ for (const message of messages) {
427
+ markDelivered(agentId, message.id);
428
+ }
429
+ if (auto_ack && messages.length) {
430
+ ackMessages(messages.map((message) => message.id), agentId);
431
+ }
432
+ return messages;
433
+ },
434
+
435
+ ackMessages(ids, agentId) {
436
+ return ackMessages(ids, agentId);
437
+ },
438
+
439
+ async handleAsk({
440
+ from, to, topic, question, context_refs,
441
+ payload = {}, priority = 5, ttl_ms = 300000,
442
+ await_response_ms = 0, trace_id, correlation_id,
443
+ }) {
444
+ const cid = correlation_id || uuidv7();
445
+ const tid = trace_id || uuidv7();
446
+
447
+ const { msg } = dispatchMessage({
448
+ type: 'request',
449
+ from,
450
+ to,
451
+ topic,
452
+ priority,
453
+ ttl_ms,
454
+ payload: { question, context_refs, ...payload },
455
+ correlation_id: cid,
456
+ trace_id: tid,
457
+ });
458
+
459
+ if (await_response_ms <= 0) {
460
+ return {
461
+ ok: true,
462
+ data: { request_message_id: msg.id, correlation_id: cid, trace_id: tid, state: 'queued' },
463
+ };
464
+ }
465
+
466
+ try {
467
+ const [response] = await once(responseEmitter, cid, {
468
+ signal: AbortSignal.timeout(Math.min(await_response_ms, 30000)),
469
+ });
470
+ return {
471
+ ok: true,
472
+ data: { request_message_id: msg.id, correlation_id: cid, trace_id: tid, state: 'answered', response },
473
+ };
474
+ } catch {
475
+ const resp = store.getResponseByCorrelation(cid);
476
+ if (resp) {
477
+ return {
478
+ ok: true,
479
+ data: { request_message_id: msg.id, correlation_id: cid, trace_id: tid, state: 'answered', response: resp.payload },
480
+ };
481
+ }
482
+ return {
483
+ ok: true,
484
+ data: { request_message_id: msg.id, correlation_id: cid, trace_id: tid, state: 'delivered' },
485
+ };
486
+ }
487
+ },
488
+
489
+ handlePublish({
490
+ from, to, topic, priority = 5, ttl_ms = 300000,
491
+ payload = {}, trace_id, correlation_id, message_type,
492
+ }) {
493
+ const type = message_type || (correlation_id ? 'response' : 'event');
494
+ const { msg, recipients } = dispatchMessage({
495
+ type,
496
+ from,
497
+ to,
498
+ topic,
499
+ priority,
500
+ ttl_ms,
501
+ payload,
502
+ trace_id: trace_id || uuidv7(),
503
+ correlation_id: correlation_id || uuidv7(),
504
+ });
505
+ return {
506
+ ok: true,
507
+ data: {
508
+ message_id: msg.id,
509
+ fanout_count: recipients.length,
510
+ expires_at_ms: msg.expires_at_ms,
511
+ },
512
+ };
513
+ },
514
+
515
+ handleHandoff({
516
+ from, to, topic, task, acceptance_criteria, context_refs,
517
+ priority = 5, ttl_ms = 600000, trace_id, correlation_id,
518
+ }) {
519
+ const { msg } = dispatchMessage({
520
+ type: 'handoff',
521
+ from,
522
+ to,
523
+ topic,
524
+ priority,
525
+ ttl_ms,
526
+ payload: { task, acceptance_criteria, context_refs },
527
+ trace_id: trace_id || uuidv7(),
528
+ correlation_id: correlation_id || uuidv7(),
529
+ });
530
+ return {
531
+ ok: true,
532
+ data: { handoff_message_id: msg.id, state: 'queued', assigned_to: to },
533
+ };
534
+ },
535
+
536
+ assignAsync({
537
+ supervisor_agent,
538
+ worker_agent,
539
+ topic = 'assign.job',
540
+ task = '',
541
+ payload = {},
542
+ priority = 5,
543
+ ttl_ms = 600000,
544
+ timeout_ms = 600000,
545
+ max_retries = 0,
546
+ trace_id,
547
+ correlation_id,
548
+ }) {
549
+ const job = store.createAssign({
550
+ supervisor_agent,
551
+ worker_agent,
552
+ topic,
553
+ task,
554
+ payload,
555
+ priority,
556
+ ttl_ms,
557
+ timeout_ms,
558
+ max_retries,
559
+ trace_id,
560
+ correlation_id,
561
+ });
562
+ const dispatched = dispatchAssignJob(job, 'create');
563
+ return {
564
+ ok: true,
565
+ data: {
566
+ assigned_to: worker_agent,
567
+ ...buildAssignSnapshot(dispatched.job),
568
+ },
569
+ };
570
+ },
571
+
572
+ reportAssignResult({
573
+ job_id,
574
+ worker_agent,
575
+ status,
576
+ attempt,
577
+ result,
578
+ error,
579
+ payload = {},
580
+ metadata = {},
581
+ }) {
582
+ const job = store.getAssign(job_id);
583
+ if (!job) {
584
+ return {
585
+ ok: false,
586
+ error: { code: 'ASSIGN_NOT_FOUND', message: `assign job not found: ${job_id}` },
587
+ };
588
+ }
589
+ if (worker_agent && worker_agent !== job.worker_agent) {
590
+ return {
591
+ ok: false,
592
+ error: { code: 'ASSIGN_WORKER_MISMATCH', message: `worker mismatch: ${worker_agent}` },
593
+ };
594
+ }
595
+ if (Number.isFinite(Number(attempt)) && Number(attempt) !== job.attempt) {
596
+ return {
597
+ ok: false,
598
+ error: {
599
+ code: 'ASSIGN_ATTEMPT_MISMATCH',
600
+ message: `stale assign result for attempt ${attempt} (current ${job.attempt})`,
601
+ },
602
+ };
603
+ }
604
+
605
+ const mergedMetadata = {
606
+ ...(payload?.metadata || {}),
607
+ ...(metadata || {}),
608
+ };
609
+ const normalizedStatus = normalizeAssignTerminalStatus(
610
+ status || payload?.status,
611
+ mergedMetadata,
612
+ );
613
+ const nextResult = result ?? (Object.prototype.hasOwnProperty.call(payload || {}, 'result') ? payload.result : payload);
614
+ const nextError = error ?? payload?.error ?? null;
615
+
616
+ if (normalizedStatus === 'running') {
617
+ const running = store.updateAssignStatus(job.job_id, 'running', {
618
+ started_at_ms: job.started_at_ms || Date.now(),
619
+ deadline_ms: Date.now() + clampAssignDuration(job.timeout_ms, job.timeout_ms),
620
+ result: nextResult,
621
+ error: nextError,
622
+ });
623
+ notifyAssignSupervisor(running, 'progress');
624
+ return { ok: true, data: buildAssignSnapshot(running) };
625
+ }
626
+
627
+ const finalized = store.updateAssignStatus(job.job_id, normalizedStatus, {
628
+ result: nextResult,
629
+ error: nextError,
630
+ });
631
+
632
+ if ((normalizedStatus === 'failed' || normalizedStatus === 'timed_out')
633
+ && finalized.retry_count < finalized.max_retries) {
634
+ return scheduleAssignRetry(finalized, normalizedStatus, nextError, worker_agent || finalized.worker_agent);
635
+ }
636
+
637
+ notifyAssignSupervisor(finalized, 'completed');
638
+ return { ok: true, data: buildAssignSnapshot(finalized) };
639
+ },
640
+
641
+ getAssignStatus({ job_id, ...filters } = {}) {
642
+ if (job_id) {
643
+ const job = store.getAssign(job_id);
644
+ return job
645
+ ? { ok: true, data: buildAssignSnapshot(job) }
646
+ : { ok: false, error: { code: 'ASSIGN_NOT_FOUND', message: `assign job not found: ${job_id}` } };
647
+ }
648
+ return {
649
+ ok: true,
650
+ data: {
651
+ assigns: store.listAssigns(filters).map((job) => buildAssignSnapshot(job)),
652
+ },
653
+ };
654
+ },
655
+
656
+ retryAssign(job_id, { reason = 'manual', requested_by = 'manual' } = {}) {
657
+ const job = store.getAssign(job_id);
658
+ if (!job) {
659
+ return {
660
+ ok: false,
661
+ error: { code: 'ASSIGN_NOT_FOUND', message: `assign job not found: ${job_id}` },
662
+ };
663
+ }
664
+ return scheduleAssignRetry(job, reason, job.error, requested_by);
665
+ },
666
+
667
+ sweepExpired() {
668
+ const now = Date.now();
669
+ let expired = 0;
670
+ for (const [messageId, record] of Array.from(liveMessages.entries())) {
671
+ if (record.message.expires_at_ms > now) continue;
672
+ store.moveToDeadLetter(messageId, 'ttl_expired', null);
673
+ removeMessage(messageId);
674
+ expired += 1;
675
+ }
676
+ return { messages: expired };
677
+ },
678
+
679
+ sweepTimedOutAssigns() {
680
+ const expiredAssigns = store.listAssigns({
681
+ statuses: Array.from(ASSIGN_PENDING_STATUSES),
682
+ active_before_ms: Date.now(),
683
+ limit: 100,
684
+ });
685
+ let timed_out = 0;
686
+ let retried = 0;
687
+
688
+ for (const job of expiredAssigns) {
689
+ const result = handleAssignTimeout(job);
690
+ timed_out += 1;
691
+ if (result?.data?.retried) retried += 1;
692
+ }
693
+
694
+ return { timed_out, retried };
695
+ },
696
+
697
+ startSweeper() {
698
+ if (sweepTimer) return;
699
+ sweepTimer = setInterval(() => {
700
+ try {
701
+ router.sweepExpired();
702
+ router.sweepTimedOutAssigns();
703
+ } catch {}
704
+ }, 10000);
705
+ staleTimer = setInterval(() => {
706
+ try { store.sweepStaleAgents(); } catch {}
707
+ }, 120000);
708
+ sweepTimer.unref();
709
+ staleTimer.unref();
710
+ },
711
+
712
+ stopSweeper() {
713
+ if (sweepTimer) { clearInterval(sweepTimer); sweepTimer = null; }
714
+ if (staleTimer) { clearInterval(staleTimer); staleTimer = null; }
715
+ },
716
+
717
+ getQueueDepths() {
718
+ const counts = { urgent: 0, normal: 0, dlq: store.getAuditStats().dlq };
719
+ for (const record of liveMessages.values()) {
720
+ const pending = record.recipients.size > record.ackedBy.size;
721
+ if (!pending) continue;
722
+ if (record.message.priority >= 7) counts.urgent += 1;
723
+ else counts.normal += 1;
724
+ }
725
+ return counts;
726
+ },
727
+
728
+ getDeliveryStats() {
729
+ if (latencyIdx === 0) {
730
+ return { total_deliveries: 0, avg_delivery_ms: 0 };
731
+ }
732
+ const filled = Math.min(latencyIdx, MAX_LATENCY_SAMPLES);
733
+ const total = deliveryLatencies.slice(0, filled).reduce((sum, ms) => sum + ms, 0);
734
+ return {
735
+ total_deliveries: latencyIdx,
736
+ avg_delivery_ms: Math.round(total / filled),
737
+ };
738
+ },
739
+
740
+ getStatus(scope = 'hub', { agent_id, trace_id, include_metrics = true } = {}) {
741
+ const data = {};
742
+
743
+ if (scope === 'hub' || scope === 'queue') {
744
+ data.hub = {
745
+ state: 'healthy',
746
+ uptime_ms: process.uptime() * 1000 | 0,
747
+ realtime_transport: 'named-pipe',
748
+ audit_store: store.type || 'sqlite',
749
+ };
750
+ if (include_metrics) {
751
+ const depths = router.getQueueDepths();
752
+ const stats = router.getDeliveryStats();
753
+ const auditStats = store.getAuditStats();
754
+ data.queues = {
755
+ urgent_depth: depths.urgent,
756
+ normal_depth: depths.normal,
757
+ dlq_depth: depths.dlq,
758
+ avg_delivery_ms: stats.avg_delivery_ms,
759
+ };
760
+ data.assigns = {
761
+ queued: auditStats.assign_queued,
762
+ running: auditStats.assign_running,
763
+ failed: auditStats.assign_failed,
764
+ timed_out: auditStats.assign_timed_out,
765
+ };
766
+ }
767
+ }
768
+
769
+ if (scope === 'agent' && agent_id) {
770
+ const agent = store.getAgent(agent_id);
771
+ if (agent) {
772
+ data.agent = {
773
+ agent_id: agent.agent_id,
774
+ status: agent.status,
775
+ pending: sortedPending(agent_id, { max_messages: 1000 }).length,
776
+ last_seen_ms: agent.last_seen_ms,
777
+ topics: listRuntimeTopics(agent_id),
778
+ };
779
+ }
780
+ }
781
+
782
+ if (scope === 'trace' && trace_id) {
783
+ data.trace = store.getMessagesByTrace(trace_id);
784
+ }
785
+
786
+ return { ok: true, data };
787
+ },
788
+ };
789
+
790
+ return router;
791
+ }