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
package/hub/router.mjs CHANGED
@@ -1,45 +1,56 @@
1
1
  // hub/router.mjs — 실시간 라우팅/수신함 상태 관리자
2
2
  // SQLite는 감사 로그만 담당하고, 실제 배달 상태는 메모리에서 관리한다.
3
- import { EventEmitter, once } from 'node:events';
4
- import { uuidv7 } from './lib/uuidv7.mjs';
3
+ import { EventEmitter, once } from "node:events";
4
+ import { uuidv7 } from "./lib/uuidv7.mjs";
5
5
 
6
- const ASSIGN_PENDING_STATUSES = new Set(['queued', 'running']);
6
+ const ASSIGN_PENDING_STATUSES = new Set(["queued", "running"]);
7
7
 
8
8
  function uniqueStrings(values = []) {
9
- return Array.from(new Set((values || []).map((value) => String(value || '').trim()).filter(Boolean)));
9
+ return Array.from(
10
+ new Set(
11
+ (values || []).map((value) => String(value || "").trim()).filter(Boolean),
12
+ ),
13
+ );
10
14
  }
11
15
 
12
- function clampAssignDuration(value, fallback = 600000, min = 1000, max = 86400000) {
16
+ function clampAssignDuration(
17
+ value,
18
+ fallback = 600000,
19
+ min = 1000,
20
+ max = 86400000,
21
+ ) {
13
22
  const num = Number(value);
14
23
  if (!Number.isFinite(num)) return fallback;
15
24
  return Math.max(min, Math.min(Math.trunc(num), max));
16
25
  }
17
26
 
18
27
  function normalizeAssignTerminalStatus(input, metadata = {}) {
19
- const status = String(input || '').trim().toLowerCase();
28
+ const status = String(input || "")
29
+ .trim()
30
+ .toLowerCase();
20
31
  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';
32
+ metadata?.result ?? metadata?.status ?? metadata?.outcome ?? "",
33
+ )
34
+ .trim()
35
+ .toLowerCase();
36
+
37
+ if (status === "queued") return "queued";
38
+ if (status === "running" || status === "in_progress") return "running";
39
+ if (status === "timed_out" || status === "timeout") return "timed_out";
40
+ if (status === "failed" || status === "error") return "failed";
41
+ if (status === "succeeded" || status === "success") return "succeeded";
42
+
43
+ if (status === "completed") {
44
+ if (resultTag === "failed" || resultTag === "error") return "failed";
45
+ if (resultTag === "timed_out" || resultTag === "timeout")
46
+ return "timed_out";
47
+ return "succeeded";
37
48
  }
38
49
 
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';
50
+ if (resultTag === "failed" || resultTag === "error") return "failed";
51
+ if (resultTag === "timed_out" || resultTag === "timeout") return "timed_out";
52
+ if (resultTag === "succeeded" || resultTag === "success") return "succeeded";
53
+ return "succeeded";
43
54
  }
44
55
 
45
56
  function normalizeAgentTopics(store, agentId, runtimeTopics) {
@@ -84,7 +95,9 @@ export function createRouter(store) {
84
95
 
85
96
  function upsertRuntimeTopics(agentId, topics, { replace = true } = {}) {
86
97
  const normalized = uniqueStrings(topics);
87
- const current = replace ? new Set() : new Set(runtimeTopics.get(agentId) || []);
98
+ const current = replace
99
+ ? new Set()
100
+ : new Set(runtimeTopics.get(agentId) || []);
88
101
  for (const topic of normalized) current.add(topic);
89
102
  runtimeTopics.set(agentId, current);
90
103
  store.updateAgentTopics(agentId, Array.from(current));
@@ -124,12 +137,12 @@ export function createRouter(store) {
124
137
  delivered_at_ms: null,
125
138
  acked_at_ms: null,
126
139
  });
127
- deliveryEmitter.emit('message', agentId, message);
140
+ deliveryEmitter.emit("message", agentId, message);
128
141
  }
129
142
 
130
143
  function resolveRecipients(msg) {
131
144
  const to = msg.to_agent ?? msg.to;
132
- if (!to?.startsWith('topic:')) {
145
+ if (!to?.startsWith("topic:")) {
133
146
  return [to];
134
147
  }
135
148
 
@@ -144,7 +157,10 @@ export function createRouter(store) {
144
157
  return Array.from(recipients);
145
158
  }
146
159
 
147
- function sortedPending(agentId, { max_messages = 20, include_topics = null } = {}) {
160
+ function sortedPending(
161
+ agentId,
162
+ { max_messages = 20, include_topics = null } = {},
163
+ ) {
148
164
  const queue = ensureAgentQueue(agentId);
149
165
  const topicFilter = include_topics?.length ? new Set(include_topics) : null;
150
166
  const now = Date.now();
@@ -173,8 +189,8 @@ export function createRouter(store) {
173
189
  delivery.attempts += 1;
174
190
  if (!delivery.delivered_at_ms) {
175
191
  delivery.delivered_at_ms = Date.now();
176
- record.message.status = 'delivered';
177
- store.updateMessageStatus(messageId, 'delivered');
192
+ record.message.status = "delivered";
193
+ store.updateMessageStatus(messageId, "delivered");
178
194
  recordLatency(delivery.delivered_at_ms - record.message.created_at_ms);
179
195
  return true;
180
196
  }
@@ -195,8 +211,8 @@ export function createRouter(store) {
195
211
  count += 1;
196
212
 
197
213
  if (record.ackedBy.size >= record.recipients.size) {
198
- record.message.status = 'acked';
199
- store.updateMessageStatus(id, 'acked');
214
+ record.message.status = "acked";
215
+ store.updateMessageStatus(id, "acked");
200
216
  removeMessage(id);
201
217
  }
202
218
  }
@@ -204,7 +220,17 @@ export function createRouter(store) {
204
220
  return count;
205
221
  }
206
222
 
207
- function dispatchMessage({ type, from, to, topic, priority = 5, ttl_ms = 300000, payload = {}, trace_id, correlation_id }) {
223
+ function dispatchMessage({
224
+ type,
225
+ from,
226
+ to,
227
+ topic,
228
+ priority = 5,
229
+ ttl_ms = 300000,
230
+ payload = {},
231
+ trace_id,
232
+ correlation_id,
233
+ }) {
208
234
  const msg = store.auditLog({
209
235
  type,
210
236
  from,
@@ -222,10 +248,10 @@ export function createRouter(store) {
222
248
  for (const agentId of recipients) {
223
249
  queueMessage(agentId, msg);
224
250
  }
225
- msg.status = 'delivered';
226
- store.updateMessageStatus(msg.id, 'delivered');
251
+ msg.status = "delivered";
252
+ store.updateMessageStatus(msg.id, "delivered");
227
253
  }
228
- if (msg.type === 'response') {
254
+ if (msg.type === "response") {
229
255
  responseEmitter.emit(msg.correlation_id, msg.payload);
230
256
  }
231
257
  return { msg, recipients };
@@ -259,10 +285,10 @@ export function createRouter(store) {
259
285
  function notifyAssignSupervisor(job, event, extra = {}) {
260
286
  if (!job?.supervisor_agent) return null;
261
287
  const { msg } = dispatchMessage({
262
- type: 'event',
263
- from: job.worker_agent || 'assign-router',
288
+ type: "event",
289
+ from: job.worker_agent || "assign-router",
264
290
  to: job.supervisor_agent,
265
- topic: 'assign.result',
291
+ topic: "assign.result",
266
292
  priority: Math.max(5, job.priority || 5),
267
293
  ttl_ms: job.ttl_ms || job.timeout_ms || 600000,
268
294
  payload: {
@@ -276,16 +302,16 @@ export function createRouter(store) {
276
302
  return msg;
277
303
  }
278
304
 
279
- function dispatchAssignJob(job, reason = 'dispatch') {
305
+ function dispatchAssignJob(job, reason = "dispatch") {
280
306
  const { msg, recipients } = dispatchMessage({
281
- type: 'handoff',
307
+ type: "handoff",
282
308
  from: job.supervisor_agent,
283
309
  to: job.worker_agent,
284
- topic: job.topic || 'assign.job',
310
+ topic: job.topic || "assign.job",
285
311
  priority: job.priority || 5,
286
312
  ttl_ms: job.ttl_ms || job.timeout_ms || 600000,
287
313
  payload: {
288
- kind: 'assign.job',
314
+ kind: "assign.job",
289
315
  reason,
290
316
  assign_job_id: job.job_id,
291
317
  attempt: job.attempt,
@@ -307,15 +333,23 @@ export function createRouter(store) {
307
333
  return { job: updated || job, recipients, message_id: msg.id };
308
334
  }
309
335
 
310
- function scheduleAssignRetry(job, reason, error = null, requested_by = 'system') {
336
+ function scheduleAssignRetry(
337
+ job,
338
+ reason,
339
+ error = null,
340
+ requested_by = "system",
341
+ ) {
311
342
  if (!job) {
312
- return { ok: false, error: { code: 'ASSIGN_NOT_FOUND', message: 'assign job not found' } };
343
+ return {
344
+ ok: false,
345
+ error: { code: "ASSIGN_NOT_FOUND", message: "assign job not found" },
346
+ };
313
347
  }
314
348
  if (job.retry_count >= job.max_retries) {
315
349
  return {
316
350
  ok: false,
317
351
  error: {
318
- code: 'ASSIGN_RETRY_EXHAUSTED',
352
+ code: "ASSIGN_RETRY_EXHAUSTED",
319
353
  message: `retry exhausted for ${job.job_id}`,
320
354
  },
321
355
  };
@@ -326,8 +360,8 @@ export function createRouter(store) {
326
360
  timeout_ms: job.timeout_ms,
327
361
  ttl_ms: job.ttl_ms,
328
362
  });
329
- const dispatched = dispatchAssignJob(queued, 'retry');
330
- notifyAssignSupervisor(dispatched.job, 'retry_scheduled', {
363
+ const dispatched = dispatchAssignJob(queued, "retry");
364
+ notifyAssignSupervisor(dispatched.job, "retry_scheduled", {
331
365
  retry_reason: reason,
332
366
  requested_by,
333
367
  });
@@ -344,18 +378,26 @@ export function createRouter(store) {
344
378
  }
345
379
 
346
380
  function handleAssignTimeout(job) {
347
- const timedOut = store.updateAssignStatus(job.job_id, 'timed_out', {
348
- error: job.error ?? { message: 'assign job timed out' },
381
+ const timedOut = store.updateAssignStatus(job.job_id, "timed_out", {
382
+ error: job.error ?? { message: "assign job timed out" },
349
383
  });
350
384
 
351
385
  if (timedOut.retry_count < timedOut.max_retries) {
352
- return scheduleAssignRetry(timedOut, 'timed_out', timedOut.error, 'sweeper');
386
+ return scheduleAssignRetry(
387
+ timedOut,
388
+ "timed_out",
389
+ timedOut.error,
390
+ "sweeper",
391
+ );
353
392
  }
354
393
 
355
- notifyAssignSupervisor(timedOut, 'completed', {
356
- completion_reason: 'timed_out',
394
+ notifyAssignSupervisor(timedOut, "completed", {
395
+ completion_reason: "timed_out",
357
396
  });
358
- return { ok: true, data: buildAssignSnapshot(timedOut, { completion_reason: 'timed_out' }) };
397
+ return {
398
+ ok: true,
399
+ data: buildAssignSnapshot(timedOut, { completion_reason: "timed_out" }),
400
+ };
359
401
  }
360
402
 
361
403
  const router = {
@@ -382,7 +424,7 @@ export function createRouter(store) {
382
424
  },
383
425
 
384
426
  updateAgentStatus(agentId, status) {
385
- if (status === 'offline') {
427
+ if (status === "offline") {
386
428
  runtimeTopics.delete(agentId);
387
429
  }
388
430
  return store.updateAgentStatus(agentId, status);
@@ -397,7 +439,7 @@ export function createRouter(store) {
397
439
  for (const agentId of recipients) {
398
440
  queueMessage(agentId, msg);
399
441
  }
400
- store.updateMessageStatus(msg.id, 'delivered');
442
+ store.updateMessageStatus(msg.id, "delivered");
401
443
  return recipients.length;
402
444
  },
403
445
 
@@ -421,13 +463,19 @@ export function createRouter(store) {
421
463
  return markDelivered(agentId, messageId);
422
464
  },
423
465
 
424
- drainAgent(agentId, { max_messages = 20, include_topics = null, auto_ack = false } = {}) {
466
+ drainAgent(
467
+ agentId,
468
+ { max_messages = 20, include_topics = null, auto_ack = false } = {},
469
+ ) {
425
470
  const messages = sortedPending(agentId, { max_messages, include_topics });
426
471
  for (const message of messages) {
427
472
  markDelivered(agentId, message.id);
428
473
  }
429
474
  if (auto_ack && messages.length) {
430
- ackMessages(messages.map((message) => message.id), agentId);
475
+ ackMessages(
476
+ messages.map((message) => message.id),
477
+ agentId,
478
+ );
431
479
  }
432
480
  return messages;
433
481
  },
@@ -437,15 +485,23 @@ export function createRouter(store) {
437
485
  },
438
486
 
439
487
  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,
488
+ from,
489
+ to,
490
+ topic,
491
+ question,
492
+ context_refs,
493
+ payload = {},
494
+ priority = 5,
495
+ ttl_ms = 300000,
496
+ await_response_ms = 0,
497
+ trace_id,
498
+ correlation_id,
443
499
  }) {
444
500
  const cid = correlation_id || uuidv7();
445
501
  const tid = trace_id || uuidv7();
446
502
 
447
503
  const { msg } = dispatchMessage({
448
- type: 'request',
504
+ type: "request",
449
505
  from,
450
506
  to,
451
507
  topic,
@@ -459,7 +515,12 @@ export function createRouter(store) {
459
515
  if (await_response_ms <= 0) {
460
516
  return {
461
517
  ok: true,
462
- data: { request_message_id: msg.id, correlation_id: cid, trace_id: tid, state: 'queued' },
518
+ data: {
519
+ request_message_id: msg.id,
520
+ correlation_id: cid,
521
+ trace_id: tid,
522
+ state: "queued",
523
+ },
463
524
  };
464
525
  }
465
526
 
@@ -469,28 +530,52 @@ export function createRouter(store) {
469
530
  });
470
531
  return {
471
532
  ok: true,
472
- data: { request_message_id: msg.id, correlation_id: cid, trace_id: tid, state: 'answered', response },
533
+ data: {
534
+ request_message_id: msg.id,
535
+ correlation_id: cid,
536
+ trace_id: tid,
537
+ state: "answered",
538
+ response,
539
+ },
473
540
  };
474
541
  } catch {
475
542
  const resp = store.getResponseByCorrelation(cid);
476
543
  if (resp) {
477
544
  return {
478
545
  ok: true,
479
- data: { request_message_id: msg.id, correlation_id: cid, trace_id: tid, state: 'answered', response: resp.payload },
546
+ data: {
547
+ request_message_id: msg.id,
548
+ correlation_id: cid,
549
+ trace_id: tid,
550
+ state: "answered",
551
+ response: resp.payload,
552
+ },
480
553
  };
481
554
  }
482
555
  return {
483
556
  ok: true,
484
- data: { request_message_id: msg.id, correlation_id: cid, trace_id: tid, state: 'delivered' },
557
+ data: {
558
+ request_message_id: msg.id,
559
+ correlation_id: cid,
560
+ trace_id: tid,
561
+ state: "delivered",
562
+ },
485
563
  };
486
564
  }
487
565
  },
488
566
 
489
567
  handlePublish({
490
- from, to, topic, priority = 5, ttl_ms = 300000,
491
- payload = {}, trace_id, correlation_id, message_type,
568
+ from,
569
+ to,
570
+ topic,
571
+ priority = 5,
572
+ ttl_ms = 300000,
573
+ payload = {},
574
+ trace_id,
575
+ correlation_id,
576
+ message_type,
492
577
  }) {
493
- const type = message_type || (correlation_id ? 'response' : 'event');
578
+ const type = message_type || (correlation_id ? "response" : "event");
494
579
  const { msg, recipients } = dispatchMessage({
495
580
  type,
496
581
  from,
@@ -513,11 +598,19 @@ export function createRouter(store) {
513
598
  },
514
599
 
515
600
  handleHandoff({
516
- from, to, topic, task, acceptance_criteria, context_refs,
517
- priority = 5, ttl_ms = 600000, trace_id, correlation_id,
601
+ from,
602
+ to,
603
+ topic,
604
+ task,
605
+ acceptance_criteria,
606
+ context_refs,
607
+ priority = 5,
608
+ ttl_ms = 600000,
609
+ trace_id,
610
+ correlation_id,
518
611
  }) {
519
612
  const { msg } = dispatchMessage({
520
- type: 'handoff',
613
+ type: "handoff",
521
614
  from,
522
615
  to,
523
616
  topic,
@@ -529,15 +622,15 @@ export function createRouter(store) {
529
622
  });
530
623
  return {
531
624
  ok: true,
532
- data: { handoff_message_id: msg.id, state: 'queued', assigned_to: to },
625
+ data: { handoff_message_id: msg.id, state: "queued", assigned_to: to },
533
626
  };
534
627
  },
535
628
 
536
629
  assignAsync({
537
630
  supervisor_agent,
538
631
  worker_agent,
539
- topic = 'assign.job',
540
- task = '',
632
+ topic = "assign.job",
633
+ task = "",
541
634
  payload = {},
542
635
  priority = 5,
543
636
  ttl_ms = 600000,
@@ -559,7 +652,7 @@ export function createRouter(store) {
559
652
  trace_id,
560
653
  correlation_id,
561
654
  });
562
- const dispatched = dispatchAssignJob(job, 'create');
655
+ const dispatched = dispatchAssignJob(job, "create");
563
656
  return {
564
657
  ok: true,
565
658
  data: {
@@ -583,20 +676,26 @@ export function createRouter(store) {
583
676
  if (!job) {
584
677
  return {
585
678
  ok: false,
586
- error: { code: 'ASSIGN_NOT_FOUND', message: `assign job not found: ${job_id}` },
679
+ error: {
680
+ code: "ASSIGN_NOT_FOUND",
681
+ message: `assign job not found: ${job_id}`,
682
+ },
587
683
  };
588
684
  }
589
685
  if (worker_agent && worker_agent !== job.worker_agent) {
590
686
  return {
591
687
  ok: false,
592
- error: { code: 'ASSIGN_WORKER_MISMATCH', message: `worker mismatch: ${worker_agent}` },
688
+ error: {
689
+ code: "ASSIGN_WORKER_MISMATCH",
690
+ message: `worker mismatch: ${worker_agent}`,
691
+ },
593
692
  };
594
693
  }
595
694
  if (Number.isFinite(Number(attempt)) && Number(attempt) !== job.attempt) {
596
695
  return {
597
696
  ok: false,
598
697
  error: {
599
- code: 'ASSIGN_ATTEMPT_MISMATCH',
698
+ code: "ASSIGN_ATTEMPT_MISMATCH",
600
699
  message: `stale assign result for attempt ${attempt} (current ${job.attempt})`,
601
700
  },
602
701
  };
@@ -610,17 +709,20 @@ export function createRouter(store) {
610
709
  status || payload?.status,
611
710
  mergedMetadata,
612
711
  );
613
- const nextResult = result ?? (Object.hasOwn(payload || {}, 'result') ? payload.result : payload);
712
+ const nextResult =
713
+ result ??
714
+ (Object.hasOwn(payload || {}, "result") ? payload.result : payload);
614
715
  const nextError = error ?? payload?.error ?? null;
615
716
 
616
- if (normalizedStatus === 'running') {
617
- const running = store.updateAssignStatus(job.job_id, 'running', {
717
+ if (normalizedStatus === "running") {
718
+ const running = store.updateAssignStatus(job.job_id, "running", {
618
719
  started_at_ms: job.started_at_ms || Date.now(),
619
- deadline_ms: Date.now() + clampAssignDuration(job.timeout_ms, job.timeout_ms),
720
+ deadline_ms:
721
+ Date.now() + clampAssignDuration(job.timeout_ms, job.timeout_ms),
620
722
  result: nextResult,
621
723
  error: nextError,
622
724
  });
623
- notifyAssignSupervisor(running, 'progress');
725
+ notifyAssignSupervisor(running, "progress");
624
726
  return { ok: true, data: buildAssignSnapshot(running) };
625
727
  }
626
728
 
@@ -629,12 +731,19 @@ export function createRouter(store) {
629
731
  error: nextError,
630
732
  });
631
733
 
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);
734
+ if (
735
+ (normalizedStatus === "failed" || normalizedStatus === "timed_out") &&
736
+ finalized.retry_count < finalized.max_retries
737
+ ) {
738
+ return scheduleAssignRetry(
739
+ finalized,
740
+ normalizedStatus,
741
+ nextError,
742
+ worker_agent || finalized.worker_agent,
743
+ );
635
744
  }
636
745
 
637
- notifyAssignSupervisor(finalized, 'completed');
746
+ notifyAssignSupervisor(finalized, "completed");
638
747
  return { ok: true, data: buildAssignSnapshot(finalized) };
639
748
  },
640
749
 
@@ -643,22 +752,33 @@ export function createRouter(store) {
643
752
  const job = store.getAssign(job_id);
644
753
  return job
645
754
  ? { ok: true, data: buildAssignSnapshot(job) }
646
- : { ok: false, error: { code: 'ASSIGN_NOT_FOUND', message: `assign job not found: ${job_id}` } };
755
+ : {
756
+ ok: false,
757
+ error: {
758
+ code: "ASSIGN_NOT_FOUND",
759
+ message: `assign job not found: ${job_id}`,
760
+ },
761
+ };
647
762
  }
648
763
  return {
649
764
  ok: true,
650
765
  data: {
651
- assigns: store.listAssigns(filters).map((job) => buildAssignSnapshot(job)),
766
+ assigns: store
767
+ .listAssigns(filters)
768
+ .map((job) => buildAssignSnapshot(job)),
652
769
  },
653
770
  };
654
771
  },
655
772
 
656
- retryAssign(job_id, { reason = 'manual', requested_by = 'manual' } = {}) {
773
+ retryAssign(job_id, { reason = "manual", requested_by = "manual" } = {}) {
657
774
  const job = store.getAssign(job_id);
658
775
  if (!job) {
659
776
  return {
660
777
  ok: false,
661
- error: { code: 'ASSIGN_NOT_FOUND', message: `assign job not found: ${job_id}` },
778
+ error: {
779
+ code: "ASSIGN_NOT_FOUND",
780
+ message: `assign job not found: ${job_id}`,
781
+ },
662
782
  };
663
783
  }
664
784
  return scheduleAssignRetry(job, reason, job.error, requested_by);
@@ -669,7 +789,7 @@ export function createRouter(store) {
669
789
  let expired = 0;
670
790
  for (const [messageId, record] of Array.from(liveMessages.entries())) {
671
791
  if (record.message.expires_at_ms > now) continue;
672
- store.moveToDeadLetter(messageId, 'ttl_expired', null);
792
+ store.moveToDeadLetter(messageId, "ttl_expired", null);
673
793
  removeMessage(messageId);
674
794
  expired += 1;
675
795
  }
@@ -703,15 +823,23 @@ export function createRouter(store) {
703
823
  } catch {}
704
824
  }, 10000);
705
825
  staleTimer = setInterval(() => {
706
- try { store.sweepStaleAgents(); } catch {}
826
+ try {
827
+ store.sweepStaleAgents();
828
+ } catch {}
707
829
  }, 120000);
708
830
  sweepTimer.unref();
709
831
  staleTimer.unref();
710
832
  },
711
833
 
712
834
  stopSweeper() {
713
- if (sweepTimer) { clearInterval(sweepTimer); sweepTimer = null; }
714
- if (staleTimer) { clearInterval(staleTimer); staleTimer = null; }
835
+ if (sweepTimer) {
836
+ clearInterval(sweepTimer);
837
+ sweepTimer = null;
838
+ }
839
+ if (staleTimer) {
840
+ clearInterval(staleTimer);
841
+ staleTimer = null;
842
+ }
715
843
  },
716
844
 
717
845
  getQueueDepths() {
@@ -730,22 +858,27 @@ export function createRouter(store) {
730
858
  return { total_deliveries: 0, avg_delivery_ms: 0 };
731
859
  }
732
860
  const filled = Math.min(latencyIdx, MAX_LATENCY_SAMPLES);
733
- const total = deliveryLatencies.slice(0, filled).reduce((sum, ms) => sum + ms, 0);
861
+ const total = deliveryLatencies
862
+ .slice(0, filled)
863
+ .reduce((sum, ms) => sum + ms, 0);
734
864
  return {
735
865
  total_deliveries: latencyIdx,
736
866
  avg_delivery_ms: Math.round(total / filled),
737
867
  };
738
868
  },
739
869
 
740
- getStatus(scope = 'hub', { agent_id, trace_id, include_metrics = true } = {}) {
870
+ getStatus(
871
+ scope = "hub",
872
+ { agent_id, trace_id, include_metrics = true } = {},
873
+ ) {
741
874
  const data = {};
742
875
 
743
- if (scope === 'hub' || scope === 'queue') {
876
+ if (scope === "hub" || scope === "queue") {
744
877
  data.hub = {
745
- state: 'healthy',
746
- uptime_ms: process.uptime() * 1000 | 0,
747
- realtime_transport: 'named-pipe',
748
- audit_store: store.type || 'sqlite',
878
+ state: "healthy",
879
+ uptime_ms: (process.uptime() * 1000) | 0,
880
+ realtime_transport: "named-pipe",
881
+ audit_store: store.type || "sqlite",
749
882
  };
750
883
  if (include_metrics) {
751
884
  const depths = router.getQueueDepths();
@@ -766,7 +899,7 @@ export function createRouter(store) {
766
899
  }
767
900
  }
768
901
 
769
- if (scope === 'agent' && agent_id) {
902
+ if (scope === "agent" && agent_id) {
770
903
  const agent = store.getAgent(agent_id);
771
904
  if (agent) {
772
905
  data.agent = {
@@ -779,7 +912,7 @@ export function createRouter(store) {
779
912
  }
780
913
  }
781
914
 
782
- if (scope === 'trace' && trace_id) {
915
+ if (scope === "trace" && trace_id) {
783
916
  data.trace = store.getMessagesByTrace(trace_id);
784
917
  }
785
918