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/store.mjs CHANGED
@@ -1,11 +1,12 @@
1
1
  // hub/store.mjs — SQLite 감사 로그/메타데이터 저장소
2
2
  // 실시간 배달 큐는 router/pipe가 담당하고, SQLite는 재생/감사 용도로만 유지한다.
3
- import { recalcConfidence } from './reflexion.mjs';
4
- import { readFileSync, mkdirSync } from 'node:fs';
5
- import { join, dirname } from 'node:path';
6
- import { fileURLToPath } from 'node:url';
7
- import { createRequire } from 'node:module';
8
- import { uuidv7 } from './lib/uuidv7.mjs';
3
+
4
+ import { mkdirSync, readFileSync } from "node:fs";
5
+ import { createRequire } from "node:module";
6
+ import { dirname, join } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ import { uuidv7 } from "./lib/uuidv7.mjs";
9
+ import { recalcConfidence } from "./reflexion.mjs";
9
10
 
10
11
  export { uuidv7 };
11
12
 
@@ -14,7 +15,11 @@ const require = createRequire(import.meta.url);
14
15
 
15
16
  function parseJson(str, fallback = null) {
16
17
  if (str == null) return fallback;
17
- try { return JSON.parse(str); } catch { return fallback; }
18
+ try {
19
+ return JSON.parse(str);
20
+ } catch {
21
+ return fallback;
22
+ }
18
23
  }
19
24
 
20
25
  function parseAgentRow(row) {
@@ -60,7 +65,7 @@ function parseReflexionRow(row) {
60
65
  const { context_json, adaptive_state_json, ...rest } = row;
61
66
  return {
62
67
  ...rest,
63
- type: rest.type || 'reflexion',
68
+ type: rest.type || "reflexion",
64
69
  context: parseJson(context_json, {}),
65
70
  adaptive_state: parseJson(adaptive_state_json, {}),
66
71
  };
@@ -81,13 +86,13 @@ function ensureColumn(db, tableName, columnName, definition) {
81
86
  * @param {string} dbPath
82
87
  */
83
88
  export async function importBetterSqlite3() {
84
- const mod = await import('better-sqlite3');
89
+ const mod = await import("better-sqlite3");
85
90
  return mod.default ?? mod;
86
91
  }
87
92
 
88
93
  function resolveBetterSqlite3(options = {}) {
89
94
  if (options.DatabaseCtor) return options.DatabaseCtor;
90
- const mod = require('better-sqlite3');
95
+ const mod = require("better-sqlite3");
91
96
  return mod.default ?? mod;
92
97
  }
93
98
 
@@ -96,18 +101,26 @@ export function createStore(dbPath, options = {}) {
96
101
  const Database = resolveBetterSqlite3(options);
97
102
  const db = new Database(dbPath);
98
103
 
99
- db.pragma('journal_mode = WAL');
100
- db.pragma('synchronous = NORMAL');
101
- db.pragma('foreign_keys = ON');
102
- db.pragma('busy_timeout = 5000');
103
- db.pragma('wal_autocheckpoint = 1000');
104
-
105
- const schemaSQL = readFileSync(join(__dirname, 'schema.sql'), 'utf8');
106
- db.exec("CREATE TABLE IF NOT EXISTS _meta (key TEXT PRIMARY KEY, value TEXT)");
107
- const SCHEMA_VERSION = '4';
104
+ db.pragma("journal_mode = WAL");
105
+ db.pragma("synchronous = NORMAL");
106
+ db.pragma("foreign_keys = ON");
107
+ db.pragma("busy_timeout = 5000");
108
+ db.pragma("wal_autocheckpoint = 1000");
109
+
110
+ const schemaSQL = readFileSync(join(__dirname, "schema.sql"), "utf8");
111
+ db.exec(
112
+ "CREATE TABLE IF NOT EXISTS _meta (key TEXT PRIMARY KEY, value TEXT)",
113
+ );
114
+ const SCHEMA_VERSION = "4";
108
115
  const curVer = (() => {
109
- try { return db.prepare("SELECT value FROM _meta WHERE key='schema_version'").pluck().get(); }
110
- catch { return null; }
116
+ try {
117
+ return db
118
+ .prepare("SELECT value FROM _meta WHERE key='schema_version'")
119
+ .pluck()
120
+ .get();
121
+ } catch {
122
+ return null;
123
+ }
111
124
  })();
112
125
  // 마이그레이션 전략: 스키마 버전이 다르면 schema.sql을 재실행한다.
113
126
  // schema.sql은 CREATE TABLE IF NOT EXISTS 패턴을 사용하므로 멱등하게 적용된다.
@@ -115,13 +128,27 @@ export function createStore(dbPath, options = {}) {
115
128
  if (curVer !== SCHEMA_VERSION) {
116
129
  if (curVer != null) {
117
130
  // 이미 버전이 기록된 DB에서 버전 불일치가 발생한 경우 경고한다.
118
- console.warn(`[store] schema version mismatch: found=${curVer} expected=${SCHEMA_VERSION}. Applying schema.sql (idempotent).`);
131
+ console.warn(
132
+ `[store] schema version mismatch: found=${curVer} expected=${SCHEMA_VERSION}. Applying schema.sql (idempotent).`,
133
+ );
119
134
  }
120
135
  db.exec(schemaSQL);
121
- db.prepare("INSERT OR REPLACE INTO _meta (key, value) VALUES ('schema_version', ?)").run(SCHEMA_VERSION);
136
+ db.prepare(
137
+ "INSERT OR REPLACE INTO _meta (key, value) VALUES ('schema_version', ?)",
138
+ ).run(SCHEMA_VERSION);
122
139
  }
123
- ensureColumn(db, 'reflexion_entries', 'type', "TEXT NOT NULL DEFAULT 'reflexion'");
124
- ensureColumn(db, 'reflexion_entries', 'adaptive_state_json', "TEXT NOT NULL DEFAULT '{}'");
140
+ ensureColumn(
141
+ db,
142
+ "reflexion_entries",
143
+ "type",
144
+ "TEXT NOT NULL DEFAULT 'reflexion'",
145
+ );
146
+ ensureColumn(
147
+ db,
148
+ "reflexion_entries",
149
+ "adaptive_state_json",
150
+ "TEXT NOT NULL DEFAULT '{}'",
151
+ );
125
152
 
126
153
  const S = {
127
154
  upsertAgent: db.prepare(`
@@ -136,23 +163,37 @@ export function createStore(dbPath, options = {}) {
136
163
  lease_expires_ms=excluded.lease_expires_ms,
137
164
  status=excluded.status,
138
165
  metadata_json=excluded.metadata_json`),
139
- getAgent: db.prepare('SELECT * FROM agents WHERE agent_id = ?'),
140
- setAgentTopics: db.prepare('UPDATE agents SET topics_json=?, last_seen_ms=? WHERE agent_id=?'),
141
- heartbeat: db.prepare("UPDATE agents SET last_seen_ms=?, lease_expires_ms=?, status='online' WHERE agent_id=?"),
142
- setAgentStatus: db.prepare('UPDATE agents SET status=? WHERE agent_id=?'),
166
+ getAgent: db.prepare("SELECT * FROM agents WHERE agent_id = ?"),
167
+ setAgentTopics: db.prepare(
168
+ "UPDATE agents SET topics_json=?, last_seen_ms=? WHERE agent_id=?",
169
+ ),
170
+ heartbeat: db.prepare(
171
+ "UPDATE agents SET last_seen_ms=?, lease_expires_ms=?, status='online' WHERE agent_id=?",
172
+ ),
173
+ setAgentStatus: db.prepare("UPDATE agents SET status=? WHERE agent_id=?"),
143
174
  onlineAgents: db.prepare("SELECT * FROM agents WHERE status != 'offline'"),
144
- allAgents: db.prepare('SELECT * FROM agents'),
145
- agentsByTopic: db.prepare("SELECT a.* FROM agents a, json_each(a.topics_json) t WHERE t.value=? AND a.status != 'offline'"),
146
- markStale: db.prepare("UPDATE agents SET status='stale' WHERE status='online' AND lease_expires_ms < ?"),
147
- markOffline: db.prepare("UPDATE agents SET status='offline' WHERE status='stale' AND lease_expires_ms < ? - 300000"),
175
+ allAgents: db.prepare("SELECT * FROM agents"),
176
+ agentsByTopic: db.prepare(
177
+ "SELECT a.* FROM agents a, json_each(a.topics_json) t WHERE t.value=? AND a.status != 'offline'",
178
+ ),
179
+ markStale: db.prepare(
180
+ "UPDATE agents SET status='stale' WHERE status='online' AND lease_expires_ms < ?",
181
+ ),
182
+ markOffline: db.prepare(
183
+ "UPDATE agents SET status='offline' WHERE status='stale' AND lease_expires_ms < ? - 300000",
184
+ ),
148
185
 
149
186
  insertAuditMessage: db.prepare(`
150
187
  INSERT INTO messages (id, type, from_agent, to_agent, topic, priority, ttl_ms, created_at_ms, expires_at_ms, correlation_id, trace_id, payload_json, status)
151
188
  VALUES (@id, @type, @from_agent, @to_agent, @topic, @priority, @ttl_ms, @created_at_ms, @expires_at_ms, @correlation_id, @trace_id, @payload_json, @status)`),
152
- getMsg: db.prepare('SELECT * FROM messages WHERE id=?'),
153
- getResponse: db.prepare("SELECT * FROM messages WHERE correlation_id=? AND type='response' ORDER BY created_at_ms DESC LIMIT 1"),
154
- getMsgsByTrace: db.prepare('SELECT * FROM messages WHERE trace_id=? ORDER BY created_at_ms'),
155
- setMsgStatus: db.prepare('UPDATE messages SET status=? WHERE id=?'),
189
+ getMsg: db.prepare("SELECT * FROM messages WHERE id=?"),
190
+ getResponse: db.prepare(
191
+ "SELECT * FROM messages WHERE correlation_id=? AND type='response' ORDER BY created_at_ms DESC LIMIT 1",
192
+ ),
193
+ getMsgsByTrace: db.prepare(
194
+ "SELECT * FROM messages WHERE trace_id=? ORDER BY created_at_ms",
195
+ ),
196
+ setMsgStatus: db.prepare("UPDATE messages SET status=? WHERE id=?"),
156
197
  recentAgentMessages: db.prepare(`
157
198
  SELECT * FROM messages
158
199
  WHERE to_agent=?
@@ -171,13 +212,21 @@ export function createStore(dbPath, options = {}) {
171
212
  insertHR: db.prepare(`
172
213
  INSERT INTO human_requests (request_id, requester_agent, kind, prompt, schema_json, state, deadline_ms, default_action, correlation_id, trace_id, response_json)
173
214
  VALUES (@request_id, @requester_agent, @kind, @prompt, @schema_json, @state, @deadline_ms, @default_action, @correlation_id, @trace_id, @response_json)`),
174
- getHR: db.prepare('SELECT * FROM human_requests WHERE request_id=?'),
175
- updateHR: db.prepare('UPDATE human_requests SET state=?, response_json=? WHERE request_id=?'),
215
+ getHR: db.prepare("SELECT * FROM human_requests WHERE request_id=?"),
216
+ updateHR: db.prepare(
217
+ "UPDATE human_requests SET state=?, response_json=? WHERE request_id=?",
218
+ ),
176
219
  pendingHR: db.prepare("SELECT * FROM human_requests WHERE state='pending'"),
177
- expireHR: db.prepare("UPDATE human_requests SET state='timed_out' WHERE state='pending' AND deadline_ms < ?"),
220
+ expireHR: db.prepare(
221
+ "UPDATE human_requests SET state='timed_out' WHERE state='pending' AND deadline_ms < ?",
222
+ ),
178
223
 
179
- insertDL: db.prepare('INSERT OR REPLACE INTO dead_letters (message_id, reason, failed_at_ms, last_error) VALUES (?,?,?,?)'),
180
- getDL: db.prepare('SELECT * FROM dead_letters ORDER BY failed_at_ms DESC LIMIT ?'),
224
+ insertDL: db.prepare(
225
+ "INSERT OR REPLACE INTO dead_letters (message_id, reason, failed_at_ms, last_error) VALUES (?,?,?,?)",
226
+ ),
227
+ getDL: db.prepare(
228
+ "SELECT * FROM dead_letters ORDER BY failed_at_ms DESC LIMIT ?",
229
+ ),
181
230
 
182
231
  insertAssign: db.prepare(`
183
232
  INSERT INTO assign_jobs (
@@ -191,7 +240,7 @@ export function createStore(dbPath, options = {}) {
191
240
  @trace_id, @correlation_id, @last_message_id, @result_json, @error_json,
192
241
  @created_at_ms, @updated_at_ms, @started_at_ms, @completed_at_ms, @last_retry_at_ms
193
242
  )`),
194
- getAssign: db.prepare('SELECT * FROM assign_jobs WHERE job_id = ?'),
243
+ getAssign: db.prepare("SELECT * FROM assign_jobs WHERE job_id = ?"),
195
244
  updateAssign: db.prepare(`
196
245
  UPDATE assign_jobs SET
197
246
  supervisor_agent=@supervisor_agent,
@@ -218,29 +267,61 @@ export function createStore(dbPath, options = {}) {
218
267
  last_retry_at_ms=@last_retry_at_ms
219
268
  WHERE job_id=@job_id`),
220
269
 
221
- findExpired: db.prepare("SELECT id FROM messages WHERE status='queued' AND expires_at_ms < ?"),
222
- urgentDepth: db.prepare("SELECT COUNT(*) as cnt FROM messages WHERE status='queued' AND priority >= 7"),
223
- normalDepth: db.prepare("SELECT COUNT(*) as cnt FROM messages WHERE status='queued' AND priority < 7"),
224
- onlineCount: db.prepare("SELECT COUNT(*) as cnt FROM agents WHERE status='online'"),
225
- msgCount: db.prepare('SELECT COUNT(*) as cnt FROM messages'),
226
- dlqDepth: db.prepare('SELECT COUNT(*) as cnt FROM dead_letters'),
227
- ackedRecent: db.prepare("SELECT COUNT(*) as cnt FROM messages WHERE status='acked' AND created_at_ms > ? - 300000"),
228
- assignCountByStatus: db.prepare('SELECT COUNT(*) as cnt FROM assign_jobs WHERE status = ?'),
229
- activeAssignCount: db.prepare("SELECT COUNT(*) as cnt FROM assign_jobs WHERE status IN ('queued','running')"),
270
+ findExpired: db.prepare(
271
+ "SELECT id FROM messages WHERE status='queued' AND expires_at_ms < ?",
272
+ ),
273
+ urgentDepth: db.prepare(
274
+ "SELECT COUNT(*) as cnt FROM messages WHERE status='queued' AND priority >= 7",
275
+ ),
276
+ normalDepth: db.prepare(
277
+ "SELECT COUNT(*) as cnt FROM messages WHERE status='queued' AND priority < 7",
278
+ ),
279
+ onlineCount: db.prepare(
280
+ "SELECT COUNT(*) as cnt FROM agents WHERE status='online'",
281
+ ),
282
+ msgCount: db.prepare("SELECT COUNT(*) as cnt FROM messages"),
283
+ dlqDepth: db.prepare("SELECT COUNT(*) as cnt FROM dead_letters"),
284
+ ackedRecent: db.prepare(
285
+ "SELECT COUNT(*) as cnt FROM messages WHERE status='acked' AND created_at_ms > ? - 300000",
286
+ ),
287
+ assignCountByStatus: db.prepare(
288
+ "SELECT COUNT(*) as cnt FROM assign_jobs WHERE status = ?",
289
+ ),
290
+ activeAssignCount: db.prepare(
291
+ "SELECT COUNT(*) as cnt FROM assign_jobs WHERE status IN ('queued','running')",
292
+ ),
230
293
 
231
294
  // reflexion
232
295
  insertReflexion: db.prepare(`
233
296
  INSERT INTO reflexion_entries (id, type, error_pattern, error_message, context_json, solution, solution_code, adaptive_state_json, confidence, hit_count, success_count, last_hit_ms, created_at_ms, updated_at_ms)
234
297
  VALUES (@id, @type, @error_pattern, @error_message, @context_json, @solution, @solution_code, @adaptive_state_json, @confidence, @hit_count, @success_count, @last_hit_ms, @created_at_ms, @updated_at_ms)`),
235
- getReflexionById: db.prepare('SELECT * FROM reflexion_entries WHERE id = ?'),
236
- findReflexionExact: db.prepare('SELECT * FROM reflexion_entries WHERE error_pattern = ? ORDER BY confidence DESC'),
237
- findReflexionLike: db.prepare("SELECT * FROM reflexion_entries WHERE error_pattern LIKE ? ESCAPE '\\' ORDER BY confidence DESC LIMIT 10"),
238
- updateReflexionHitSuccess: db.prepare('UPDATE reflexion_entries SET hit_count = hit_count + 1, success_count = success_count + 1, last_hit_ms = ?, updated_at_ms = ? WHERE id = ?'),
239
- updateReflexionHitOnly: db.prepare('UPDATE reflexion_entries SET hit_count = hit_count + 1, last_hit_ms = ?, updated_at_ms = ? WHERE id = ?'),
240
- updateReflexionConfidence: db.prepare('UPDATE reflexion_entries SET confidence = ?, updated_at_ms = ? WHERE id = ?'),
241
- pruneReflexionEntries: db.prepare('DELETE FROM reflexion_entries WHERE updated_at_ms < ? AND confidence < ?'),
242
- listReflexionEntries: db.prepare('SELECT * FROM reflexion_entries ORDER BY confidence DESC, updated_at_ms DESC'),
243
- deleteReflexionEntry: db.prepare('DELETE FROM reflexion_entries WHERE id = ?'),
298
+ getReflexionById: db.prepare(
299
+ "SELECT * FROM reflexion_entries WHERE id = ?",
300
+ ),
301
+ findReflexionExact: db.prepare(
302
+ "SELECT * FROM reflexion_entries WHERE error_pattern = ? ORDER BY confidence DESC",
303
+ ),
304
+ findReflexionLike: db.prepare(
305
+ "SELECT * FROM reflexion_entries WHERE error_pattern LIKE ? ESCAPE '\\' ORDER BY confidence DESC LIMIT 10",
306
+ ),
307
+ updateReflexionHitSuccess: db.prepare(
308
+ "UPDATE reflexion_entries SET hit_count = hit_count + 1, success_count = success_count + 1, last_hit_ms = ?, updated_at_ms = ? WHERE id = ?",
309
+ ),
310
+ updateReflexionHitOnly: db.prepare(
311
+ "UPDATE reflexion_entries SET hit_count = hit_count + 1, last_hit_ms = ?, updated_at_ms = ? WHERE id = ?",
312
+ ),
313
+ updateReflexionConfidence: db.prepare(
314
+ "UPDATE reflexion_entries SET confidence = ?, updated_at_ms = ? WHERE id = ?",
315
+ ),
316
+ pruneReflexionEntries: db.prepare(
317
+ "DELETE FROM reflexion_entries WHERE updated_at_ms < ? AND confidence < ?",
318
+ ),
319
+ listReflexionEntries: db.prepare(
320
+ "SELECT * FROM reflexion_entries ORDER BY confidence DESC, updated_at_ms DESC",
321
+ ),
322
+ deleteReflexionEntry: db.prepare(
323
+ "DELETE FROM reflexion_entries WHERE id = ?",
324
+ ),
244
325
  };
245
326
 
246
327
  const assignStatusListeners = new Set();
@@ -257,7 +338,9 @@ export function createStore(dbPath, options = {}) {
257
338
  function notifyAssignStatusListeners(row) {
258
339
  const event = buildAssignCallbackEvent(row);
259
340
  for (const listener of Array.from(assignStatusListeners)) {
260
- try { listener(event, row); } catch {}
341
+ try {
342
+ listener(event, row);
343
+ } catch {}
261
344
  }
262
345
  }
263
346
 
@@ -287,7 +370,15 @@ export function createStore(dbPath, options = {}) {
287
370
  db.close();
288
371
  },
289
372
 
290
- registerAgent({ agent_id, cli, pid, capabilities = [], topics = [], heartbeat_ttl_ms = 30000, metadata = {} }) {
373
+ registerAgent({
374
+ agent_id,
375
+ cli,
376
+ pid,
377
+ capabilities = [],
378
+ topics = [],
379
+ heartbeat_ttl_ms = 30000,
380
+ metadata = {},
381
+ }) {
291
382
  const now = Date.now();
292
383
  const leaseExpires = now + heartbeat_ttl_ms;
293
384
  S.upsertAgent.run({
@@ -298,10 +389,15 @@ export function createStore(dbPath, options = {}) {
298
389
  topics_json: JSON.stringify(topics),
299
390
  last_seen_ms: now,
300
391
  lease_expires_ms: leaseExpires,
301
- status: 'online',
392
+ status: "online",
302
393
  metadata_json: JSON.stringify(metadata),
303
394
  });
304
- return { agent_id, lease_id: uuidv7(), lease_expires_ms: leaseExpires, server_time_ms: now };
395
+ return {
396
+ agent_id,
397
+ lease_id: uuidv7(),
398
+ lease_expires_ms: leaseExpires,
399
+ server_time_ms: now,
400
+ };
305
401
  },
306
402
 
307
403
  getAgent(id) {
@@ -311,12 +407,18 @@ export function createStore(dbPath, options = {}) {
311
407
  refreshLease(agentId, ttlMs = 30000) {
312
408
  const now = Date.now();
313
409
  S.heartbeat.run(now, now + ttlMs, agentId);
314
- return { agent_id: agentId, lease_expires_ms: now + ttlMs, server_time_ms: now };
410
+ return {
411
+ agent_id: agentId,
412
+ lease_expires_ms: now + ttlMs,
413
+ server_time_ms: now,
414
+ };
315
415
  },
316
416
 
317
417
  updateAgentTopics(agentId, topics = []) {
318
418
  const now = Date.now();
319
- return S.setAgentTopics.run(JSON.stringify(topics), now, agentId).changes > 0;
419
+ return (
420
+ S.setAgentTopics.run(JSON.stringify(topics), now, agentId).changes > 0
421
+ );
320
422
  },
321
423
 
322
424
  listOnlineAgents() {
@@ -343,7 +445,18 @@ export function createStore(dbPath, options = {}) {
343
445
  return S.setAgentStatus.run(status, agentId).changes > 0;
344
446
  },
345
447
 
346
- auditLog({ type, from, to, topic, priority = 5, ttl_ms = 300000, payload = {}, trace_id, correlation_id, status = 'queued' }) {
448
+ auditLog({
449
+ type,
450
+ from,
451
+ to,
452
+ topic,
453
+ priority = 5,
454
+ ttl_ms = 300000,
455
+ payload = {},
456
+ trace_id,
457
+ correlation_id,
458
+ status = "queued",
459
+ }) {
347
460
  const now = Date.now();
348
461
  const row = {
349
462
  id: uuidv7(),
@@ -385,14 +498,22 @@ export function createStore(dbPath, options = {}) {
385
498
  return S.setMsgStatus.run(status, id).changes > 0;
386
499
  },
387
500
 
388
- getAuditMessagesForAgent(agentId, { max_messages = 20, include_topics = null } = {}) {
501
+ getAuditMessagesForAgent(
502
+ agentId,
503
+ { max_messages = 20, include_topics = null } = {},
504
+ ) {
389
505
  const limit = clampMaxMessages(max_messages);
390
- const topics = Array.isArray(include_topics) && include_topics.length
391
- ? include_topics
392
- : (store.getAgent(agentId)?.topics || []);
506
+ const topics =
507
+ Array.isArray(include_topics) && include_topics.length
508
+ ? include_topics
509
+ : store.getAgent(agentId)?.topics || [];
393
510
 
394
511
  const rows = topics.length
395
- ? S.recentAgentMessagesWithTopics.all(agentId, JSON.stringify(topics), limit)
512
+ ? S.recentAgentMessagesWithTopics.all(
513
+ agentId,
514
+ JSON.stringify(topics),
515
+ limit,
516
+ )
396
517
  : S.recentAgentMessages.all(agentId, limit);
397
518
 
398
519
  return rows.map(parseMessageRow);
@@ -419,7 +540,16 @@ export function createStore(dbPath, options = {}) {
419
540
  return 0;
420
541
  },
421
542
 
422
- insertHumanRequest({ requester_agent, kind, prompt, requested_schema = {}, deadline_ms, default_action, correlation_id, trace_id }) {
543
+ insertHumanRequest({
544
+ requester_agent,
545
+ kind,
546
+ prompt,
547
+ requested_schema = {},
548
+ deadline_ms,
549
+ default_action,
550
+ correlation_id,
551
+ trace_id,
552
+ }) {
423
553
  const requestId = uuidv7();
424
554
  const now = Date.now();
425
555
  const deadlineAt = now + deadline_ms;
@@ -429,14 +559,18 @@ export function createStore(dbPath, options = {}) {
429
559
  kind,
430
560
  prompt,
431
561
  schema_json: JSON.stringify(requested_schema),
432
- state: 'pending',
562
+ state: "pending",
433
563
  deadline_ms: deadlineAt,
434
564
  default_action,
435
565
  correlation_id: correlation_id || uuidv7(),
436
566
  trace_id: trace_id || uuidv7(),
437
567
  response_json: null,
438
568
  });
439
- return { request_id: requestId, state: 'pending', deadline_ms: deadlineAt };
569
+ return {
570
+ request_id: requestId,
571
+ state: "pending",
572
+ deadline_ms: deadlineAt,
573
+ };
440
574
  },
441
575
 
442
576
  getHumanRequest(id) {
@@ -444,7 +578,10 @@ export function createStore(dbPath, options = {}) {
444
578
  },
445
579
 
446
580
  updateHumanRequest(id, state, resp = null) {
447
- return S.updateHR.run(state, resp ? JSON.stringify(resp) : null, id).changes > 0;
581
+ return (
582
+ S.updateHR.run(state, resp ? JSON.stringify(resp) : null, id).changes >
583
+ 0
584
+ );
448
585
  },
449
586
 
450
587
  getPendingHumanRequests() {
@@ -457,7 +594,7 @@ export function createStore(dbPath, options = {}) {
457
594
 
458
595
  moveToDeadLetter(messageId, reason, lastError = null) {
459
596
  db.transaction(() => {
460
- S.setMsgStatus.run('dead_letter', messageId);
597
+ S.setMsgStatus.run("dead_letter", messageId);
461
598
  S.insertDL.run(messageId, reason, Date.now(), lastError);
462
599
  })();
463
600
  return true;
@@ -471,10 +608,10 @@ export function createStore(dbPath, options = {}) {
471
608
  job_id,
472
609
  supervisor_agent,
473
610
  worker_agent,
474
- topic = 'assign.job',
475
- task = '',
611
+ topic = "assign.job",
612
+ task = "",
476
613
  payload = {},
477
- status = 'queued',
614
+ status = "queued",
478
615
  attempt = 1,
479
616
  retry_count = 0,
480
617
  max_retries = 0,
@@ -494,8 +631,8 @@ export function createStore(dbPath, options = {}) {
494
631
  job_id: job_id || uuidv7(),
495
632
  supervisor_agent,
496
633
  worker_agent,
497
- topic: String(topic || 'assign.job'),
498
- task: String(task || ''),
634
+ topic: String(topic || "assign.job"),
635
+ task: String(task || ""),
499
636
  payload_json: JSON.stringify(payload || {}),
500
637
  status,
501
638
  attempt: Math.max(1, Number(attempt) || 1),
@@ -514,8 +651,10 @@ export function createStore(dbPath, options = {}) {
514
651
  error_json: error == null ? null : JSON.stringify(error),
515
652
  created_at_ms: now,
516
653
  updated_at_ms: now,
517
- started_at_ms: status === 'running' ? now : null,
518
- completed_at_ms: ['succeeded', 'failed', 'timed_out'].includes(status) ? now : null,
654
+ started_at_ms: status === "running" ? now : null,
655
+ completed_at_ms: ["succeeded", "failed", "timed_out"].includes(status)
656
+ ? now
657
+ : null,
519
658
  last_retry_at_ms: retry_count > 0 ? now : null,
520
659
  };
521
660
  S.insertAssign.run(row);
@@ -534,8 +673,13 @@ export function createStore(dbPath, options = {}) {
534
673
 
535
674
  const now = Date.now();
536
675
  const nextStatus = status || current.status;
537
- const isTerminal = ['succeeded', 'failed', 'timed_out'].includes(nextStatus);
538
- const nextTimeout = clampDuration(patch.timeout_ms ?? current.timeout_ms, current.timeout_ms);
676
+ const isTerminal = ["succeeded", "failed", "timed_out"].includes(
677
+ nextStatus,
678
+ );
679
+ const nextTimeout = clampDuration(
680
+ patch.timeout_ms ?? current.timeout_ms,
681
+ current.timeout_ms,
682
+ );
539
683
  const nextRow = {
540
684
  job_id: current.job_id,
541
685
  supervisor_agent: patch.supervisor_agent ?? current.supervisor_agent,
@@ -544,39 +688,69 @@ export function createStore(dbPath, options = {}) {
544
688
  task: patch.task ?? current.task,
545
689
  payload_json: JSON.stringify(patch.payload ?? current.payload ?? {}),
546
690
  status: nextStatus,
547
- attempt: Math.max(1, Number(patch.attempt ?? current.attempt) || current.attempt || 1),
548
- retry_count: Math.max(0, Number(patch.retry_count ?? current.retry_count) || 0),
549
- max_retries: Math.max(0, Number(patch.max_retries ?? current.max_retries) || 0),
550
- priority: clampPriority(patch.priority ?? current.priority, current.priority || 5),
551
- ttl_ms: clampDuration(patch.ttl_ms ?? current.ttl_ms, current.ttl_ms || nextTimeout),
691
+ attempt: Math.max(
692
+ 1,
693
+ Number(patch.attempt ?? current.attempt) || current.attempt || 1,
694
+ ),
695
+ retry_count: Math.max(
696
+ 0,
697
+ Number(patch.retry_count ?? current.retry_count) || 0,
698
+ ),
699
+ max_retries: Math.max(
700
+ 0,
701
+ Number(patch.max_retries ?? current.max_retries) || 0,
702
+ ),
703
+ priority: clampPriority(
704
+ patch.priority ?? current.priority,
705
+ current.priority || 5,
706
+ ),
707
+ ttl_ms: clampDuration(
708
+ patch.ttl_ms ?? current.ttl_ms,
709
+ current.ttl_ms || nextTimeout,
710
+ ),
552
711
  timeout_ms: nextTimeout,
553
712
  deadline_ms: (() => {
554
- if (Object.hasOwn(patch, 'deadline_ms')) {
555
- return patch.deadline_ms == null ? null : Math.trunc(Number(patch.deadline_ms));
713
+ if (Object.hasOwn(patch, "deadline_ms")) {
714
+ return patch.deadline_ms == null
715
+ ? null
716
+ : Math.trunc(Number(patch.deadline_ms));
556
717
  }
557
718
  if (isTerminal) return null;
558
- if (nextStatus === 'running' && !current.deadline_ms) return now + nextTimeout;
719
+ if (nextStatus === "running" && !current.deadline_ms)
720
+ return now + nextTimeout;
559
721
  return current.deadline_ms;
560
722
  })(),
561
723
  trace_id: patch.trace_id ?? current.trace_id,
562
724
  correlation_id: patch.correlation_id ?? current.correlation_id,
563
- last_message_id: Object.hasOwn(patch, 'last_message_id')
725
+ last_message_id: Object.hasOwn(patch, "last_message_id")
564
726
  ? patch.last_message_id
565
727
  : current.last_message_id,
566
- result_json: Object.hasOwn(patch, 'result')
567
- ? (patch.result == null ? null : JSON.stringify(patch.result))
568
- : (current.result == null ? null : JSON.stringify(current.result)),
569
- error_json: Object.hasOwn(patch, 'error')
570
- ? (patch.error == null ? null : JSON.stringify(patch.error))
571
- : (current.error == null ? null : JSON.stringify(current.error)),
728
+ result_json: Object.hasOwn(patch, "result")
729
+ ? patch.result == null
730
+ ? null
731
+ : JSON.stringify(patch.result)
732
+ : current.result == null
733
+ ? null
734
+ : JSON.stringify(current.result),
735
+ error_json: Object.hasOwn(patch, "error")
736
+ ? patch.error == null
737
+ ? null
738
+ : JSON.stringify(patch.error)
739
+ : current.error == null
740
+ ? null
741
+ : JSON.stringify(current.error),
572
742
  updated_at_ms: now,
573
- started_at_ms: Object.hasOwn(patch, 'started_at_ms')
743
+ started_at_ms: Object.hasOwn(patch, "started_at_ms")
574
744
  ? patch.started_at_ms
575
- : (nextStatus === 'running' ? (current.started_at_ms || now) : current.started_at_ms),
576
- completed_at_ms: Object.hasOwn(patch, 'completed_at_ms')
745
+ : nextStatus === "running"
746
+ ? current.started_at_ms || now
747
+ : current.started_at_ms,
748
+ completed_at_ms: Object.hasOwn(patch, "completed_at_ms")
577
749
  ? patch.completed_at_ms
578
- : (isTerminal ? (current.completed_at_ms || now) : current.completed_at_ms),
579
- last_retry_at_ms: Object.hasOwn(patch, 'last_retry_at_ms')
750
+ : isTerminal
751
+ ? current.completed_at_ms || now
752
+ : current.completed_at_ms,
753
+ last_retry_at_ms: Object.hasOwn(patch, "last_retry_at_ms")
580
754
  ? patch.last_retry_at_ms
581
755
  : current.last_retry_at_ms,
582
756
  };
@@ -602,32 +776,35 @@ export function createStore(dbPath, options = {}) {
602
776
  const values = [];
603
777
 
604
778
  if (supervisor_agent) {
605
- clauses.push('supervisor_agent = ?');
779
+ clauses.push("supervisor_agent = ?");
606
780
  values.push(supervisor_agent);
607
781
  }
608
782
  if (worker_agent) {
609
- clauses.push('worker_agent = ?');
783
+ clauses.push("worker_agent = ?");
610
784
  values.push(worker_agent);
611
785
  }
612
786
  if (trace_id) {
613
- clauses.push('trace_id = ?');
787
+ clauses.push("trace_id = ?");
614
788
  values.push(trace_id);
615
789
  }
616
790
  if (correlation_id) {
617
- clauses.push('correlation_id = ?');
791
+ clauses.push("correlation_id = ?");
618
792
  values.push(correlation_id);
619
793
  }
620
794
 
621
- const statusList = Array.isArray(statuses) && statuses.length
622
- ? statuses
623
- : (status ? [status] : []);
795
+ const statusList =
796
+ Array.isArray(statuses) && statuses.length
797
+ ? statuses
798
+ : status
799
+ ? [status]
800
+ : [];
624
801
  if (statusList.length) {
625
- clauses.push(`status IN (${statusList.map(() => '?').join(',')})`);
802
+ clauses.push(`status IN (${statusList.map(() => "?").join(",")})`);
626
803
  values.push(...statusList);
627
804
  }
628
805
 
629
806
  if (Number.isFinite(Number(active_before_ms))) {
630
- clauses.push('deadline_ms IS NOT NULL AND deadline_ms <= ?');
807
+ clauses.push("deadline_ms IS NOT NULL AND deadline_ms <= ?");
631
808
  values.push(Math.trunc(Number(active_before_ms)));
632
809
  }
633
810
 
@@ -637,21 +814,33 @@ export function createStore(dbPath, options = {}) {
637
814
  // 이 함수는 hot path(heartbeat/poll)가 아닌 관리/조회 경로에서만 호출되므로 허용한다.
638
815
  const sql = `
639
816
  SELECT * FROM assign_jobs
640
- ${clauses.length ? `WHERE ${clauses.join(' AND ')}` : ''}
817
+ ${clauses.length ? `WHERE ${clauses.join(" AND ")}` : ""}
641
818
  ORDER BY updated_at_ms DESC
642
819
  LIMIT ?`;
643
820
  values.push(clampMaxMessages(limit, 50));
644
- return db.prepare(sql).all(...values).map(parseAssignRow);
821
+ return db
822
+ .prepare(sql)
823
+ .all(...values)
824
+ .map(parseAssignRow);
645
825
  },
646
826
 
647
827
  retryAssign(jobId, patch = {}) {
648
828
  const current = store.getAssign(jobId);
649
829
  if (!current) return null;
650
830
 
651
- const nextRetryCount = Math.max(0, Number(patch.retry_count ?? current.retry_count + 1) || 0);
652
- const nextAttempt = Math.max(current.attempt + 1, Number(patch.attempt ?? current.attempt + 1) || 1);
653
- const nextTimeout = clampDuration(patch.timeout_ms ?? current.timeout_ms, current.timeout_ms);
654
- return store.updateAssignStatus(jobId, 'queued', {
831
+ const nextRetryCount = Math.max(
832
+ 0,
833
+ Number(patch.retry_count ?? current.retry_count + 1) || 0,
834
+ );
835
+ const nextAttempt = Math.max(
836
+ current.attempt + 1,
837
+ Number(patch.attempt ?? current.attempt + 1) || 1,
838
+ );
839
+ const nextTimeout = clampDuration(
840
+ patch.timeout_ms ?? current.timeout_ms,
841
+ current.timeout_ms,
842
+ );
843
+ return store.updateAssignStatus(jobId, "queued", {
655
844
  retry_count: nextRetryCount,
656
845
  attempt: nextAttempt,
657
846
  timeout_ms: nextTimeout,
@@ -661,7 +850,7 @@ export function createStore(dbPath, options = {}) {
661
850
  started_at_ms: null,
662
851
  last_retry_at_ms: Date.now(),
663
852
  result: patch.result ?? null,
664
- error: Object.hasOwn(patch, 'error') ? patch.error : current.error,
853
+ error: Object.hasOwn(patch, "error") ? patch.error : current.error,
665
854
  last_message_id: null,
666
855
  });
667
856
  },
@@ -671,8 +860,8 @@ export function createStore(dbPath, options = {}) {
671
860
  return db.transaction(() => {
672
861
  const expired = S.findExpired.all(now);
673
862
  for (const { id } of expired) {
674
- S.setMsgStatus.run('dead_letter', id);
675
- S.insertDL.run(id, 'ttl_expired', now, null);
863
+ S.setMsgStatus.run("dead_letter", id);
864
+ S.insertDL.run(id, "ttl_expired", now, null);
676
865
  }
677
866
  const humanRequests = S.expireHR.run(now).changes;
678
867
  return { messages: expired.length, human_requests: humanRequests };
@@ -688,7 +877,7 @@ export function createStore(dbPath, options = {}) {
688
877
  },
689
878
 
690
879
  onAssignStatusChange(listener) {
691
- if (typeof listener !== 'function') {
880
+ if (typeof listener !== "function") {
692
881
  return () => {};
693
882
  }
694
883
  assignStatusListeners.add(listener);
@@ -718,17 +907,17 @@ export function createStore(dbPath, options = {}) {
718
907
  online_agents: S.onlineCount.get().cnt,
719
908
  total_messages: S.msgCount.get().cnt,
720
909
  dlq: S.dlqDepth.get().cnt,
721
- assign_queued: S.assignCountByStatus.get('queued').cnt,
722
- assign_running: S.assignCountByStatus.get('running').cnt,
723
- assign_failed: S.assignCountByStatus.get('failed').cnt,
724
- assign_timed_out: S.assignCountByStatus.get('timed_out').cnt,
910
+ assign_queued: S.assignCountByStatus.get("queued").cnt,
911
+ assign_running: S.assignCountByStatus.get("running").cnt,
912
+ assign_failed: S.assignCountByStatus.get("failed").cnt,
913
+ assign_timed_out: S.assignCountByStatus.get("timed_out").cnt,
725
914
  };
726
915
  },
727
916
 
728
917
  // --- Reflexion CRUD ---
729
918
 
730
919
  addReflexion({
731
- type = 'reflexion',
920
+ type = "reflexion",
732
921
  error_pattern,
733
922
  error_message,
734
923
  context = {},
@@ -768,14 +957,16 @@ export function createStore(dbPath, options = {}) {
768
957
  },
769
958
 
770
959
  findReflexion(errorPattern, context = {}) {
771
- const ctxKeys = Object.keys(context).filter(k => context[k] != null);
772
- const ctxWhere = ctxKeys.map(k => ` AND json_extract(context_json, '$.${k}') = ?`).join('');
773
- const ctxVals = ctxKeys.map(k => context[k]);
960
+ const ctxKeys = Object.keys(context).filter((k) => context[k] != null);
961
+ const ctxWhere = ctxKeys
962
+ .map((k) => ` AND json_extract(context_json, '$.${k}') = ?`)
963
+ .join("");
964
+ const ctxVals = ctxKeys.map((k) => context[k]);
774
965
 
775
966
  if (ctxKeys.length === 0) {
776
967
  let rows = S.findReflexionExact.all(errorPattern);
777
968
  if (rows.length) return rows.map(parseReflexionRow);
778
- const escaped = errorPattern.replace(/[%_\\]/g, '\\$&');
969
+ const escaped = errorPattern.replace(/[%_\\]/g, "\\$&");
779
970
  rows = S.findReflexionLike.all(`%${escaped.slice(0, 100)}%`);
780
971
  return rows.map(parseReflexionRow);
781
972
  }
@@ -784,7 +975,7 @@ export function createStore(dbPath, options = {}) {
784
975
  let rows = db.prepare(exactSql).all(errorPattern, ...ctxVals);
785
976
  if (rows.length) return rows.map(parseReflexionRow);
786
977
 
787
- const escaped = errorPattern.replace(/[%_\\]/g, '\\$&');
978
+ const escaped = errorPattern.replace(/[%_\\]/g, "\\$&");
788
979
  const likeSql = `SELECT * FROM reflexion_entries WHERE error_pattern LIKE ? ESCAPE '\\'${ctxWhere} ORDER BY confidence DESC LIMIT 10`;
789
980
  rows = db.prepare(likeSql).all(`%${escaped.slice(0, 100)}%`, ...ctxVals);
790
981
  return rows.map(parseReflexionRow);
@@ -800,18 +991,30 @@ export function createStore(dbPath, options = {}) {
800
991
  const entry = store.getReflexion(id);
801
992
  if (entry && entry.hit_count > 0) {
802
993
  const conf = recalcConfidence(entry);
803
- S.updateReflexionConfidence.run(Math.max(0, Math.min(1, conf)), now, id);
994
+ S.updateReflexionConfidence.run(
995
+ Math.max(0, Math.min(1, conf)),
996
+ now,
997
+ id,
998
+ );
804
999
  }
805
1000
  return store.getReflexion(id);
806
1001
  },
807
1002
 
808
1003
  listReflexion(filters = {}) {
809
- const { type, minConfidence = Number.NEGATIVE_INFINITY, projectSlug } = filters;
810
- return S.listReflexionEntries.all()
1004
+ const {
1005
+ type,
1006
+ minConfidence = Number.NEGATIVE_INFINITY,
1007
+ projectSlug,
1008
+ } = filters;
1009
+ return S.listReflexionEntries
1010
+ .all()
811
1011
  .map(parseReflexionRow)
812
1012
  .filter((entry) => !type || entry.type === type)
813
1013
  .filter((entry) => entry.confidence >= minConfidence)
814
- .filter((entry) => !projectSlug || entry.adaptive_state?.project_slug === projectSlug);
1014
+ .filter(
1015
+ (entry) =>
1016
+ !projectSlug || entry.adaptive_state?.project_slug === projectSlug,
1017
+ );
815
1018
  },
816
1019
 
817
1020
  patchReflexion(id, patch = {}) {
@@ -825,20 +1028,20 @@ export function createStore(dbPath, options = {}) {
825
1028
  updated_at_ms: patch.updated_at_ms ?? Date.now(),
826
1029
  };
827
1030
  const sets = [
828
- ['type', next.type],
829
- ['error_pattern', next.error_pattern],
830
- ['error_message', next.error_message],
831
- ['context_json', JSON.stringify(next.context ?? {})],
832
- ['solution', next.solution],
833
- ['solution_code', next.solution_code ?? null],
834
- ['adaptive_state_json', JSON.stringify(next.adaptive_state ?? {})],
835
- ['confidence', next.confidence],
836
- ['hit_count', next.hit_count],
837
- ['success_count', next.success_count],
838
- ['last_hit_ms', next.last_hit_ms],
839
- ['updated_at_ms', next.updated_at_ms],
1031
+ ["type", next.type],
1032
+ ["error_pattern", next.error_pattern],
1033
+ ["error_message", next.error_message],
1034
+ ["context_json", JSON.stringify(next.context ?? {})],
1035
+ ["solution", next.solution],
1036
+ ["solution_code", next.solution_code ?? null],
1037
+ ["adaptive_state_json", JSON.stringify(next.adaptive_state ?? {})],
1038
+ ["confidence", next.confidence],
1039
+ ["hit_count", next.hit_count],
1040
+ ["success_count", next.success_count],
1041
+ ["last_hit_ms", next.last_hit_ms],
1042
+ ["updated_at_ms", next.updated_at_ms],
840
1043
  ];
841
- const sql = `UPDATE reflexion_entries SET ${sets.map(([key]) => `${key} = ?`).join(', ')} WHERE id = ?`;
1044
+ const sql = `UPDATE reflexion_entries SET ${sets.map(([key]) => `${key} = ?`).join(", ")} WHERE id = ?`;
842
1045
  db.prepare(sql).run(...sets.map(([, value]) => value), id);
843
1046
  return store.getReflexion(id);
844
1047
  },