jfl 0.4.4 → 0.6.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 (533) hide show
  1. package/dist/commands/context-hub.d.ts +1 -0
  2. package/dist/commands/context-hub.d.ts.map +1 -1
  3. package/dist/commands/context-hub.js +1064 -41
  4. package/dist/commands/context-hub.js.map +1 -1
  5. package/dist/commands/eval.d.ts +1 -1
  6. package/dist/commands/eval.d.ts.map +1 -1
  7. package/dist/commands/eval.js +192 -1
  8. package/dist/commands/eval.js.map +1 -1
  9. package/dist/commands/findings.d.ts +6 -0
  10. package/dist/commands/findings.d.ts.map +1 -0
  11. package/dist/commands/findings.js +203 -0
  12. package/dist/commands/findings.js.map +1 -0
  13. package/dist/commands/hud.d.ts.map +1 -1
  14. package/dist/commands/hud.js +47 -9
  15. package/dist/commands/hud.js.map +1 -1
  16. package/dist/commands/ide.d.ts +27 -0
  17. package/dist/commands/ide.d.ts.map +1 -0
  18. package/dist/commands/ide.js +546 -0
  19. package/dist/commands/ide.js.map +1 -0
  20. package/dist/commands/onboard.d.ts.map +1 -1
  21. package/dist/commands/onboard.js +212 -2
  22. package/dist/commands/onboard.js.map +1 -1
  23. package/dist/commands/openclaw.d.ts +3 -0
  24. package/dist/commands/openclaw.d.ts.map +1 -1
  25. package/dist/commands/openclaw.js +76 -2
  26. package/dist/commands/openclaw.js.map +1 -1
  27. package/dist/commands/peter.d.ts +3 -0
  28. package/dist/commands/peter.d.ts.map +1 -1
  29. package/dist/commands/peter.js +1168 -58
  30. package/dist/commands/peter.js.map +1 -1
  31. package/dist/commands/pi-fleet.d.ts +18 -0
  32. package/dist/commands/pi-fleet.d.ts.map +1 -0
  33. package/dist/commands/pi-fleet.js +382 -0
  34. package/dist/commands/pi-fleet.js.map +1 -0
  35. package/dist/commands/pi.d.ts.map +1 -1
  36. package/dist/commands/pi.js +18 -3
  37. package/dist/commands/pi.js.map +1 -1
  38. package/dist/commands/scope.d.ts.map +1 -1
  39. package/dist/commands/scope.js +90 -1
  40. package/dist/commands/scope.js.map +1 -1
  41. package/dist/commands/services.d.ts.map +1 -1
  42. package/dist/commands/services.js +18 -0
  43. package/dist/commands/services.js.map +1 -1
  44. package/dist/commands/setup.d.ts +12 -0
  45. package/dist/commands/setup.d.ts.map +1 -0
  46. package/dist/commands/setup.js +322 -0
  47. package/dist/commands/setup.js.map +1 -0
  48. package/dist/commands/status.d.ts.map +1 -1
  49. package/dist/commands/status.js +22 -4
  50. package/dist/commands/status.js.map +1 -1
  51. package/dist/commands/train.d.ts +33 -0
  52. package/dist/commands/train.d.ts.map +1 -0
  53. package/dist/commands/train.js +510 -0
  54. package/dist/commands/train.js.map +1 -0
  55. package/dist/commands/verify.d.ts +14 -0
  56. package/dist/commands/verify.d.ts.map +1 -0
  57. package/dist/commands/verify.js +276 -0
  58. package/dist/commands/verify.js.map +1 -0
  59. package/dist/commands/viz.d.ts.map +1 -1
  60. package/dist/commands/viz.js +417 -0
  61. package/dist/commands/viz.js.map +1 -1
  62. package/dist/dashboard-static/assets/index-CW9ZxqX8.css +1 -0
  63. package/dist/dashboard-static/assets/index-DNN__p4K.js +121 -0
  64. package/dist/dashboard-static/index.html +2 -2
  65. package/dist/index.js +324 -64
  66. package/dist/index.js.map +1 -1
  67. package/dist/lib/agent-config.d.ts +52 -0
  68. package/dist/lib/agent-config.d.ts.map +1 -0
  69. package/dist/lib/agent-config.js +231 -0
  70. package/dist/lib/agent-config.js.map +1 -0
  71. package/dist/lib/agent-generator.d.ts +10 -0
  72. package/dist/lib/agent-generator.d.ts.map +1 -1
  73. package/dist/lib/agent-generator.js +64 -10
  74. package/dist/lib/agent-generator.js.map +1 -1
  75. package/dist/lib/agent-session.d.ts +104 -0
  76. package/dist/lib/agent-session.d.ts.map +1 -0
  77. package/dist/lib/agent-session.js +635 -0
  78. package/dist/lib/agent-session.js.map +1 -0
  79. package/dist/lib/eval-snapshot.d.ts +47 -0
  80. package/dist/lib/eval-snapshot.d.ts.map +1 -0
  81. package/dist/lib/eval-snapshot.js +315 -0
  82. package/dist/lib/eval-snapshot.js.map +1 -0
  83. package/dist/lib/eval-store.d.ts +5 -0
  84. package/dist/lib/eval-store.d.ts.map +1 -1
  85. package/dist/lib/eval-store.js +33 -3
  86. package/dist/lib/eval-store.js.map +1 -1
  87. package/dist/lib/findings-engine.d.ts +51 -0
  88. package/dist/lib/findings-engine.d.ts.map +1 -0
  89. package/dist/lib/findings-engine.js +338 -0
  90. package/dist/lib/findings-engine.js.map +1 -0
  91. package/dist/lib/flow-engine.d.ts +8 -0
  92. package/dist/lib/flow-engine.d.ts.map +1 -1
  93. package/dist/lib/flow-engine.js +84 -2
  94. package/dist/lib/flow-engine.js.map +1 -1
  95. package/dist/lib/hub-client.d.ts +1 -0
  96. package/dist/lib/hub-client.d.ts.map +1 -1
  97. package/dist/lib/hub-client.js +33 -6
  98. package/dist/lib/hub-client.js.map +1 -1
  99. package/dist/lib/ide-panes.d.ts +58 -0
  100. package/dist/lib/ide-panes.d.ts.map +1 -0
  101. package/dist/lib/ide-panes.js +508 -0
  102. package/dist/lib/ide-panes.js.map +1 -0
  103. package/dist/lib/memory-db.js +4 -4
  104. package/dist/lib/memory-db.js.map +1 -1
  105. package/dist/lib/memory-indexer.d.ts.map +1 -1
  106. package/dist/lib/memory-indexer.js +3 -0
  107. package/dist/lib/memory-indexer.js.map +1 -1
  108. package/dist/lib/memory-search.d.ts +148 -4
  109. package/dist/lib/memory-search.d.ts.map +1 -1
  110. package/dist/lib/memory-search.js +496 -58
  111. package/dist/lib/memory-search.js.map +1 -1
  112. package/dist/lib/meta-orchestrator.d.ts +104 -0
  113. package/dist/lib/meta-orchestrator.d.ts.map +1 -0
  114. package/dist/lib/meta-orchestrator.js +373 -0
  115. package/dist/lib/meta-orchestrator.js.map +1 -0
  116. package/dist/lib/peer-agent-generator.d.ts.map +1 -1
  117. package/dist/lib/peer-agent-generator.js +43 -19
  118. package/dist/lib/peer-agent-generator.js.map +1 -1
  119. package/dist/lib/pi-sky/bridge.d.ts +55 -0
  120. package/dist/lib/pi-sky/bridge.d.ts.map +1 -0
  121. package/dist/lib/pi-sky/bridge.js +264 -0
  122. package/dist/lib/pi-sky/bridge.js.map +1 -0
  123. package/dist/lib/pi-sky/cost-monitor.d.ts +21 -0
  124. package/dist/lib/pi-sky/cost-monitor.d.ts.map +1 -0
  125. package/dist/lib/pi-sky/cost-monitor.js +126 -0
  126. package/dist/lib/pi-sky/cost-monitor.js.map +1 -0
  127. package/dist/lib/pi-sky/eval-sweep.d.ts +27 -0
  128. package/dist/lib/pi-sky/eval-sweep.d.ts.map +1 -0
  129. package/dist/lib/pi-sky/eval-sweep.js +141 -0
  130. package/dist/lib/pi-sky/eval-sweep.js.map +1 -0
  131. package/dist/lib/pi-sky/event-router.d.ts +32 -0
  132. package/dist/lib/pi-sky/event-router.d.ts.map +1 -0
  133. package/dist/lib/pi-sky/event-router.js +176 -0
  134. package/dist/lib/pi-sky/event-router.js.map +1 -0
  135. package/dist/lib/pi-sky/experiment.d.ts +9 -0
  136. package/dist/lib/pi-sky/experiment.d.ts.map +1 -0
  137. package/dist/lib/pi-sky/experiment.js +83 -0
  138. package/dist/lib/pi-sky/experiment.js.map +1 -0
  139. package/dist/lib/pi-sky/index.d.ts +16 -0
  140. package/dist/lib/pi-sky/index.d.ts.map +1 -0
  141. package/dist/lib/pi-sky/index.js +16 -0
  142. package/dist/lib/pi-sky/index.js.map +1 -0
  143. package/dist/lib/pi-sky/stratus-gate.d.ts +28 -0
  144. package/dist/lib/pi-sky/stratus-gate.d.ts.map +1 -0
  145. package/dist/lib/pi-sky/stratus-gate.js +61 -0
  146. package/dist/lib/pi-sky/stratus-gate.js.map +1 -0
  147. package/dist/lib/pi-sky/swarm.d.ts +28 -0
  148. package/dist/lib/pi-sky/swarm.d.ts.map +1 -0
  149. package/dist/lib/pi-sky/swarm.js +208 -0
  150. package/dist/lib/pi-sky/swarm.js.map +1 -0
  151. package/dist/lib/pi-sky/types.d.ts +139 -0
  152. package/dist/lib/pi-sky/types.d.ts.map +1 -0
  153. package/dist/lib/pi-sky/types.js +2 -0
  154. package/dist/lib/pi-sky/types.js.map +1 -0
  155. package/dist/lib/pi-sky/voice-bridge.d.ts +20 -0
  156. package/dist/lib/pi-sky/voice-bridge.d.ts.map +1 -0
  157. package/dist/lib/pi-sky/voice-bridge.js +91 -0
  158. package/dist/lib/pi-sky/voice-bridge.js.map +1 -0
  159. package/dist/lib/policy-head.d.ts +40 -0
  160. package/dist/lib/policy-head.d.ts.map +1 -0
  161. package/dist/lib/policy-head.js +234 -0
  162. package/dist/lib/policy-head.js.map +1 -0
  163. package/dist/lib/predictor.d.ts +10 -0
  164. package/dist/lib/predictor.d.ts.map +1 -1
  165. package/dist/lib/predictor.js +46 -7
  166. package/dist/lib/predictor.js.map +1 -1
  167. package/dist/lib/replay-buffer.d.ts +93 -0
  168. package/dist/lib/replay-buffer.d.ts.map +1 -0
  169. package/dist/lib/replay-buffer.js +302 -0
  170. package/dist/lib/replay-buffer.js.map +1 -0
  171. package/dist/lib/sentinel-rl.d.ts +97 -0
  172. package/dist/lib/sentinel-rl.d.ts.map +1 -0
  173. package/dist/lib/sentinel-rl.js +430 -0
  174. package/dist/lib/sentinel-rl.js.map +1 -0
  175. package/dist/lib/session-lock.d.ts +61 -0
  176. package/dist/lib/session-lock.d.ts.map +1 -0
  177. package/dist/lib/session-lock.js +438 -0
  178. package/dist/lib/session-lock.js.map +1 -0
  179. package/dist/lib/setup/agent-generator.d.ts +18 -0
  180. package/dist/lib/setup/agent-generator.d.ts.map +1 -0
  181. package/dist/lib/setup/agent-generator.js +114 -0
  182. package/dist/lib/setup/agent-generator.js.map +1 -0
  183. package/dist/lib/setup/context-analyzer.d.ts +16 -0
  184. package/dist/lib/setup/context-analyzer.d.ts.map +1 -0
  185. package/dist/lib/setup/context-analyzer.js +112 -0
  186. package/dist/lib/setup/context-analyzer.js.map +1 -0
  187. package/dist/lib/setup/doc-auditor.d.ts +54 -0
  188. package/dist/lib/setup/doc-auditor.d.ts.map +1 -0
  189. package/dist/lib/setup/doc-auditor.js +629 -0
  190. package/dist/lib/setup/doc-auditor.js.map +1 -0
  191. package/dist/lib/setup/domain-generator.d.ts +7 -0
  192. package/dist/lib/setup/domain-generator.d.ts.map +1 -0
  193. package/dist/lib/setup/domain-generator.js +58 -0
  194. package/dist/lib/setup/domain-generator.js.map +1 -0
  195. package/dist/lib/setup/smart-eval-generator.d.ts +38 -0
  196. package/dist/lib/setup/smart-eval-generator.d.ts.map +1 -0
  197. package/dist/lib/setup/smart-eval-generator.js +378 -0
  198. package/dist/lib/setup/smart-eval-generator.js.map +1 -0
  199. package/dist/lib/setup/smart-recommender.d.ts +63 -0
  200. package/dist/lib/setup/smart-recommender.d.ts.map +1 -0
  201. package/dist/lib/setup/smart-recommender.js +329 -0
  202. package/dist/lib/setup/smart-recommender.js.map +1 -0
  203. package/dist/lib/setup/spec-generator.d.ts +63 -0
  204. package/dist/lib/setup/spec-generator.d.ts.map +1 -0
  205. package/dist/lib/setup/spec-generator.js +310 -0
  206. package/dist/lib/setup/spec-generator.js.map +1 -0
  207. package/dist/lib/setup/violation-agent-generator.d.ts +32 -0
  208. package/dist/lib/setup/violation-agent-generator.d.ts.map +1 -0
  209. package/dist/lib/setup/violation-agent-generator.js +255 -0
  210. package/dist/lib/setup/violation-agent-generator.js.map +1 -0
  211. package/dist/lib/stratus-client.d.ts +1 -0
  212. package/dist/lib/stratus-client.d.ts.map +1 -1
  213. package/dist/lib/stratus-client.js +24 -2
  214. package/dist/lib/stratus-client.js.map +1 -1
  215. package/dist/lib/telemetry-agent-v2.d.ts +128 -0
  216. package/dist/lib/telemetry-agent-v2.d.ts.map +1 -0
  217. package/dist/lib/telemetry-agent-v2.js +1042 -0
  218. package/dist/lib/telemetry-agent-v2.js.map +1 -0
  219. package/dist/lib/telemetry-agent.d.ts.map +1 -1
  220. package/dist/lib/telemetry-agent.js +27 -6
  221. package/dist/lib/telemetry-agent.js.map +1 -1
  222. package/dist/lib/telemetry-digest.d.ts.map +1 -1
  223. package/dist/lib/telemetry-digest.js +27 -5
  224. package/dist/lib/telemetry-digest.js.map +1 -1
  225. package/dist/lib/telemetry.d.ts.map +1 -1
  226. package/dist/lib/telemetry.js +29 -4
  227. package/dist/lib/telemetry.js.map +1 -1
  228. package/dist/lib/text-preprocessing.d.ts +83 -0
  229. package/dist/lib/text-preprocessing.d.ts.map +1 -0
  230. package/dist/lib/text-preprocessing.js +261 -0
  231. package/dist/lib/text-preprocessing.js.map +1 -0
  232. package/dist/lib/training-buffer.d.ts +86 -0
  233. package/dist/lib/training-buffer.d.ts.map +1 -0
  234. package/dist/lib/training-buffer.js +139 -0
  235. package/dist/lib/training-buffer.js.map +1 -0
  236. package/dist/lib/tuple-miner.d.ts +30 -0
  237. package/dist/lib/tuple-miner.d.ts.map +1 -0
  238. package/dist/lib/tuple-miner.js +427 -0
  239. package/dist/lib/tuple-miner.js.map +1 -0
  240. package/dist/lib/vm-backend.d.ts +72 -0
  241. package/dist/lib/vm-backend.d.ts.map +1 -0
  242. package/dist/lib/vm-backend.js +175 -0
  243. package/dist/lib/vm-backend.js.map +1 -0
  244. package/dist/lib/workspace/backend.d.ts +53 -0
  245. package/dist/lib/workspace/backend.d.ts.map +1 -0
  246. package/dist/lib/workspace/backend.js +37 -0
  247. package/dist/lib/workspace/backend.js.map +1 -0
  248. package/dist/lib/workspace/cmux-adapter.d.ts +46 -0
  249. package/dist/lib/workspace/cmux-adapter.d.ts.map +1 -0
  250. package/dist/lib/workspace/cmux-adapter.js +261 -0
  251. package/dist/lib/workspace/cmux-adapter.js.map +1 -0
  252. package/dist/lib/workspace/data-pipeline.d.ts +35 -0
  253. package/dist/lib/workspace/data-pipeline.d.ts.map +1 -0
  254. package/dist/lib/workspace/data-pipeline.js +463 -0
  255. package/dist/lib/workspace/data-pipeline.js.map +1 -0
  256. package/dist/lib/workspace/engine.d.ts +64 -0
  257. package/dist/lib/workspace/engine.d.ts.map +1 -0
  258. package/dist/lib/workspace/engine.js +397 -0
  259. package/dist/lib/workspace/engine.js.map +1 -0
  260. package/dist/lib/workspace/notifications.d.ts +14 -0
  261. package/dist/lib/workspace/notifications.d.ts.map +1 -0
  262. package/dist/lib/workspace/notifications.js +41 -0
  263. package/dist/lib/workspace/notifications.js.map +1 -0
  264. package/dist/lib/workspace/surface-registry.d.ts +49 -0
  265. package/dist/lib/workspace/surface-registry.d.ts.map +1 -0
  266. package/dist/lib/workspace/surface-registry.js +217 -0
  267. package/dist/lib/workspace/surface-registry.js.map +1 -0
  268. package/dist/lib/workspace/surface-type.d.ts +153 -0
  269. package/dist/lib/workspace/surface-type.d.ts.map +1 -0
  270. package/dist/lib/workspace/surface-type.js +9 -0
  271. package/dist/lib/workspace/surface-type.js.map +1 -0
  272. package/dist/lib/workspace/surfaces/agent-overview.d.ts +16 -0
  273. package/dist/lib/workspace/surfaces/agent-overview.d.ts.map +1 -0
  274. package/dist/lib/workspace/surfaces/agent-overview.js +116 -0
  275. package/dist/lib/workspace/surfaces/agent-overview.js.map +1 -0
  276. package/dist/lib/workspace/surfaces/agent.d.ts +16 -0
  277. package/dist/lib/workspace/surfaces/agent.d.ts.map +1 -0
  278. package/dist/lib/workspace/surfaces/agent.js +112 -0
  279. package/dist/lib/workspace/surfaces/agent.js.map +1 -0
  280. package/dist/lib/workspace/surfaces/claude.d.ts +15 -0
  281. package/dist/lib/workspace/surfaces/claude.d.ts.map +1 -0
  282. package/dist/lib/workspace/surfaces/claude.js +23 -0
  283. package/dist/lib/workspace/surfaces/claude.js.map +1 -0
  284. package/dist/lib/workspace/surfaces/dashboard.d.ts +21 -0
  285. package/dist/lib/workspace/surfaces/dashboard.d.ts.map +1 -0
  286. package/dist/lib/workspace/surfaces/dashboard.js +32 -0
  287. package/dist/lib/workspace/surfaces/dashboard.js.map +1 -0
  288. package/dist/lib/workspace/surfaces/eval.d.ts +15 -0
  289. package/dist/lib/workspace/surfaces/eval.d.ts.map +1 -0
  290. package/dist/lib/workspace/surfaces/eval.js +42 -0
  291. package/dist/lib/workspace/surfaces/eval.js.map +1 -0
  292. package/dist/lib/workspace/surfaces/event-stream.d.ts +16 -0
  293. package/dist/lib/workspace/surfaces/event-stream.d.ts.map +1 -0
  294. package/dist/lib/workspace/surfaces/event-stream.js +40 -0
  295. package/dist/lib/workspace/surfaces/event-stream.js.map +1 -0
  296. package/dist/lib/workspace/surfaces/flow.d.ts +16 -0
  297. package/dist/lib/workspace/surfaces/flow.d.ts.map +1 -0
  298. package/dist/lib/workspace/surfaces/flow.js +49 -0
  299. package/dist/lib/workspace/surfaces/flow.js.map +1 -0
  300. package/dist/lib/workspace/surfaces/index.d.ts +16 -0
  301. package/dist/lib/workspace/surfaces/index.d.ts.map +1 -0
  302. package/dist/lib/workspace/surfaces/index.js +16 -0
  303. package/dist/lib/workspace/surfaces/index.js.map +1 -0
  304. package/dist/lib/workspace/surfaces/portfolio.d.ts +16 -0
  305. package/dist/lib/workspace/surfaces/portfolio.d.ts.map +1 -0
  306. package/dist/lib/workspace/surfaces/portfolio.js +102 -0
  307. package/dist/lib/workspace/surfaces/portfolio.js.map +1 -0
  308. package/dist/lib/workspace/surfaces/service.d.ts +16 -0
  309. package/dist/lib/workspace/surfaces/service.d.ts.map +1 -0
  310. package/dist/lib/workspace/surfaces/service.js +45 -0
  311. package/dist/lib/workspace/surfaces/service.js.map +1 -0
  312. package/dist/lib/workspace/surfaces/shell.d.ts +15 -0
  313. package/dist/lib/workspace/surfaces/shell.d.ts.map +1 -0
  314. package/dist/lib/workspace/surfaces/shell.js +19 -0
  315. package/dist/lib/workspace/surfaces/shell.js.map +1 -0
  316. package/dist/lib/workspace/surfaces/telemetry.d.ts +16 -0
  317. package/dist/lib/workspace/surfaces/telemetry.d.ts.map +1 -0
  318. package/dist/lib/workspace/surfaces/telemetry.js +48 -0
  319. package/dist/lib/workspace/surfaces/telemetry.js.map +1 -0
  320. package/dist/lib/workspace/surfaces/topology.d.ts +15 -0
  321. package/dist/lib/workspace/surfaces/topology.d.ts.map +1 -0
  322. package/dist/lib/workspace/surfaces/topology.js +19 -0
  323. package/dist/lib/workspace/surfaces/topology.js.map +1 -0
  324. package/dist/lib/workspace/surfaces/training.d.ts +16 -0
  325. package/dist/lib/workspace/surfaces/training.d.ts.map +1 -0
  326. package/dist/lib/workspace/surfaces/training.js +22 -0
  327. package/dist/lib/workspace/surfaces/training.js.map +1 -0
  328. package/dist/lib/workspace/tmux-adapter.d.ts +27 -0
  329. package/dist/lib/workspace/tmux-adapter.d.ts.map +1 -0
  330. package/dist/lib/workspace/tmux-adapter.js +106 -0
  331. package/dist/lib/workspace/tmux-adapter.js.map +1 -0
  332. package/dist/mcp/context-hub-mcp.js +7 -24
  333. package/dist/mcp/context-hub-mcp.js.map +1 -1
  334. package/dist/types/flows.d.ts +2 -0
  335. package/dist/types/flows.d.ts.map +1 -1
  336. package/dist/types/ide.d.ts +49 -0
  337. package/dist/types/ide.d.ts.map +1 -0
  338. package/dist/types/ide.js +5 -0
  339. package/dist/types/ide.js.map +1 -0
  340. package/dist/types/platform-digest.d.ts +228 -0
  341. package/dist/types/platform-digest.d.ts.map +1 -0
  342. package/dist/types/platform-digest.js +5 -0
  343. package/dist/types/platform-digest.js.map +1 -0
  344. package/dist/types/telemetry-digest.d.ts +2 -0
  345. package/dist/types/telemetry-digest.d.ts.map +1 -1
  346. package/dist/utils/ensure-project.d.ts +1 -0
  347. package/dist/utils/ensure-project.d.ts.map +1 -1
  348. package/dist/utils/ensure-project.js +19 -7
  349. package/dist/utils/ensure-project.js.map +1 -1
  350. package/dist/utils/jfl-config.d.ts +1 -0
  351. package/dist/utils/jfl-config.d.ts.map +1 -1
  352. package/dist/utils/jfl-config.js +19 -1
  353. package/dist/utils/jfl-config.js.map +1 -1
  354. package/dist/utils/jfl-paths.d.ts +5 -0
  355. package/dist/utils/jfl-paths.d.ts.map +1 -1
  356. package/dist/utils/jfl-paths.js +25 -3
  357. package/dist/utils/jfl-paths.js.map +1 -1
  358. package/package.json +3 -2
  359. package/packages/pi/AGENTS.md +112 -0
  360. package/packages/pi/extensions/agent-grid.ts +191 -0
  361. package/packages/pi/extensions/agent-names.ts +178 -0
  362. package/packages/pi/extensions/autoresearch.ts +427 -0
  363. package/packages/pi/extensions/bookmarks.ts +85 -0
  364. package/packages/pi/extensions/context.ts +184 -0
  365. package/packages/pi/extensions/crm-tool.ts +61 -0
  366. package/packages/pi/extensions/eval-tool.ts +224 -0
  367. package/packages/pi/extensions/eval.ts +60 -0
  368. package/packages/pi/extensions/footer.ts +239 -0
  369. package/packages/pi/extensions/hub-resolver.ts +63 -0
  370. package/packages/pi/extensions/hud-tool.ts +145 -0
  371. package/packages/pi/extensions/index.ts +405 -0
  372. package/packages/pi/extensions/journal.ts +224 -0
  373. package/packages/pi/extensions/map-bridge.ts +178 -0
  374. package/packages/pi/extensions/memory-tool.ts +73 -0
  375. package/packages/pi/extensions/notifications.ts +73 -0
  376. package/packages/pi/extensions/peter-parker.ts +202 -0
  377. package/packages/pi/extensions/policy-head-tool.ts +276 -0
  378. package/packages/pi/extensions/portfolio-bridge.ts +90 -0
  379. package/packages/pi/extensions/session.ts +142 -0
  380. package/packages/pi/extensions/shortcuts.ts +259 -0
  381. package/packages/pi/extensions/stratus-bridge.ts +115 -0
  382. package/packages/pi/extensions/synopsis-tool.ts +83 -0
  383. package/packages/pi/extensions/tool-renderers.ts +353 -0
  384. package/packages/pi/extensions/training-buffer-tool.ts +368 -0
  385. package/packages/pi/extensions/types.ts +163 -0
  386. package/packages/pi/package-lock.json +346 -0
  387. package/packages/pi/package.json +44 -0
  388. package/packages/pi/skills/agent-browser/SKILL.md +116 -0
  389. package/packages/pi/skills/brand-architect/SKILL.md +240 -0
  390. package/packages/pi/skills/brand-architect/config.yaml +137 -0
  391. package/packages/pi/skills/campaign-hud/config.yaml +112 -0
  392. package/packages/pi/skills/content-creator/SKILL.md +294 -0
  393. package/packages/pi/skills/context/SKILL.md +65 -0
  394. package/packages/pi/skills/debug/MULTI_AGENT.md +360 -0
  395. package/packages/pi/skills/debug/SKILL.md +554 -0
  396. package/packages/pi/skills/end/SKILL.md +1782 -0
  397. package/packages/pi/skills/eval/SKILL.md +75 -0
  398. package/packages/pi/skills/fly-deploy/SKILL.md +676 -0
  399. package/packages/pi/skills/founder-video/SKILL.md +467 -0
  400. package/packages/pi/skills/hud/SKILL.md +160 -0
  401. package/packages/pi/skills/orchestrate/SKILL.md +74 -0
  402. package/packages/pi/skills/pi-agents/SKILL.md +78 -0
  403. package/packages/pi/skills/react-best-practices/AGENTS.md +2249 -0
  404. package/packages/pi/skills/react-best-practices/README.md +123 -0
  405. package/packages/pi/skills/react-best-practices/SKILL.md +125 -0
  406. package/packages/pi/skills/react-best-practices/metadata.json +15 -0
  407. package/packages/pi/skills/react-best-practices/rules/_sections.md +46 -0
  408. package/packages/pi/skills/react-best-practices/rules/_template.md +28 -0
  409. package/packages/pi/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  410. package/packages/pi/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
  411. package/packages/pi/skills/react-best-practices/rules/async-api-routes.md +38 -0
  412. package/packages/pi/skills/react-best-practices/rules/async-defer-await.md +80 -0
  413. package/packages/pi/skills/react-best-practices/rules/async-dependencies.md +36 -0
  414. package/packages/pi/skills/react-best-practices/rules/async-parallel.md +28 -0
  415. package/packages/pi/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  416. package/packages/pi/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
  417. package/packages/pi/skills/react-best-practices/rules/bundle-conditional.md +31 -0
  418. package/packages/pi/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
  419. package/packages/pi/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  420. package/packages/pi/skills/react-best-practices/rules/bundle-preload.md +50 -0
  421. package/packages/pi/skills/react-best-practices/rules/client-event-listeners.md +74 -0
  422. package/packages/pi/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
  423. package/packages/pi/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
  424. package/packages/pi/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
  425. package/packages/pi/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  426. package/packages/pi/skills/react-best-practices/rules/js-cache-storage.md +70 -0
  427. package/packages/pi/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  428. package/packages/pi/skills/react-best-practices/rules/js-early-exit.md +50 -0
  429. package/packages/pi/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  430. package/packages/pi/skills/react-best-practices/rules/js-index-maps.md +37 -0
  431. package/packages/pi/skills/react-best-practices/rules/js-length-check-first.md +49 -0
  432. package/packages/pi/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  433. package/packages/pi/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  434. package/packages/pi/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  435. package/packages/pi/skills/react-best-practices/rules/rendering-activity.md +26 -0
  436. package/packages/pi/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  437. package/packages/pi/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
  438. package/packages/pi/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  439. package/packages/pi/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  440. package/packages/pi/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  441. package/packages/pi/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  442. package/packages/pi/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  443. package/packages/pi/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  444. package/packages/pi/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  445. package/packages/pi/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  446. package/packages/pi/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  447. package/packages/pi/skills/react-best-practices/rules/rerender-memo.md +44 -0
  448. package/packages/pi/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  449. package/packages/pi/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
  450. package/packages/pi/skills/react-best-practices/rules/server-cache-lru.md +41 -0
  451. package/packages/pi/skills/react-best-practices/rules/server-cache-react.md +26 -0
  452. package/packages/pi/skills/react-best-practices/rules/server-parallel-fetching.md +79 -0
  453. package/packages/pi/skills/react-best-practices/rules/server-serialization.md +38 -0
  454. package/packages/pi/skills/remotion-best-practices/SKILL.md +43 -0
  455. package/packages/pi/skills/remotion-best-practices/rules/3d.md +86 -0
  456. package/packages/pi/skills/remotion-best-practices/rules/animations.md +29 -0
  457. package/packages/pi/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
  458. package/packages/pi/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
  459. package/packages/pi/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +108 -0
  460. package/packages/pi/skills/remotion-best-practices/rules/assets.md +78 -0
  461. package/packages/pi/skills/remotion-best-practices/rules/audio.md +172 -0
  462. package/packages/pi/skills/remotion-best-practices/rules/calculate-metadata.md +104 -0
  463. package/packages/pi/skills/remotion-best-practices/rules/can-decode.md +75 -0
  464. package/packages/pi/skills/remotion-best-practices/rules/charts.md +58 -0
  465. package/packages/pi/skills/remotion-best-practices/rules/compositions.md +146 -0
  466. package/packages/pi/skills/remotion-best-practices/rules/display-captions.md +126 -0
  467. package/packages/pi/skills/remotion-best-practices/rules/extract-frames.md +229 -0
  468. package/packages/pi/skills/remotion-best-practices/rules/fonts.md +152 -0
  469. package/packages/pi/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
  470. package/packages/pi/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
  471. package/packages/pi/skills/remotion-best-practices/rules/get-video-duration.md +58 -0
  472. package/packages/pi/skills/remotion-best-practices/rules/gifs.md +138 -0
  473. package/packages/pi/skills/remotion-best-practices/rules/images.md +130 -0
  474. package/packages/pi/skills/remotion-best-practices/rules/import-srt-captions.md +67 -0
  475. package/packages/pi/skills/remotion-best-practices/rules/lottie.md +68 -0
  476. package/packages/pi/skills/remotion-best-practices/rules/measuring-dom-nodes.md +35 -0
  477. package/packages/pi/skills/remotion-best-practices/rules/measuring-text.md +143 -0
  478. package/packages/pi/skills/remotion-best-practices/rules/sequencing.md +106 -0
  479. package/packages/pi/skills/remotion-best-practices/rules/tailwind.md +11 -0
  480. package/packages/pi/skills/remotion-best-practices/rules/text-animations.md +20 -0
  481. package/packages/pi/skills/remotion-best-practices/rules/timing.md +179 -0
  482. package/packages/pi/skills/remotion-best-practices/rules/transcribe-captions.md +19 -0
  483. package/packages/pi/skills/remotion-best-practices/rules/transitions.md +122 -0
  484. package/packages/pi/skills/remotion-best-practices/rules/trimming.md +53 -0
  485. package/packages/pi/skills/remotion-best-practices/rules/videos.md +171 -0
  486. package/packages/pi/skills/search/SKILL.md +220 -0
  487. package/packages/pi/skills/spec/SKILL.md +377 -0
  488. package/packages/pi/skills/startup/SKILL.md +315 -0
  489. package/packages/pi/skills/web-architect/SKILL.md +309 -0
  490. package/packages/pi/skills/x-algorithm/SKILL.md +305 -0
  491. package/packages/pi/teams/dev-team.yaml +63 -0
  492. package/packages/pi/teams/gtm-team.yaml +79 -0
  493. package/packages/pi/themes/jfl.theme.json +76 -0
  494. package/packages/pi/tsconfig.json +21 -0
  495. package/scripts/collect-tuples.sh +124 -0
  496. package/scripts/destroy-fleet.sh +37 -0
  497. package/scripts/jfl-ide.sh +48 -0
  498. package/scripts/session/session-cleanup.sh +4 -11
  499. package/scripts/session/session-init.sh +6 -0
  500. package/scripts/session/session-sync.sh +25 -0
  501. package/scripts/setup-branch-protection.sh +106 -0
  502. package/scripts/spawn-fleet.sh +144 -0
  503. package/scripts/train/requirements.txt +5 -0
  504. package/scripts/train/train-policy-head.py +477 -0
  505. package/scripts/train/v2/dataset.py +81 -0
  506. package/scripts/train/v2/domain.json +18 -0
  507. package/scripts/train/v2/eval.py +196 -0
  508. package/scripts/train/v2/generate_data.py +219 -0
  509. package/scripts/train/v2/infer.py +188 -0
  510. package/scripts/train/v2/model.py +112 -0
  511. package/scripts/train/v2/precompute.py +132 -0
  512. package/scripts/train/v2/train.py +302 -0
  513. package/scripts/train/v2/transform_buffer.py +227 -0
  514. package/scripts/train/v2/validate_data.py +115 -0
  515. package/scripts/train-policy-head.py +434 -0
  516. package/scripts/vm-swarm/README.md +301 -0
  517. package/scripts/vm-swarm/collect-tuples.sh +331 -0
  518. package/scripts/vm-swarm/create-base-template.sh +339 -0
  519. package/scripts/vm-swarm/kill-fleet.sh +204 -0
  520. package/scripts/vm-swarm/monitor-fleet.sh +346 -0
  521. package/scripts/vm-swarm/spawn-fleet.sh +304 -0
  522. package/template/.claude/settings.json +2 -15
  523. package/template/.github/workflows/jfl-eval.yml +6 -1
  524. package/template/.github/workflows/jfl-review.yml +4 -0
  525. package/template/scripts/session/session-cleanup.sh +2 -11
  526. package/template/scripts/session/session-end-hub.sh +72 -0
  527. package/template/scripts/session/session-end.sh +69 -6
  528. package/template/scripts/session/session-init.sh +55 -30
  529. package/template/scripts/session/session-lock.sh +464 -0
  530. package/template/scripts/session/session-start-hub.sh +105 -0
  531. package/template/templates/service-agent/workflows/jfl-eval.yml +19 -0
  532. package/dist/dashboard-static/assets/index-B6kRK9Rq.js +0 -116
  533. package/dist/dashboard-static/assets/index-BpdKJPLu.css +0 -1
@@ -0,0 +1,1042 @@
1
+ /**
2
+ * Telemetry Agent V2 - Real Platform Digest Consumer
3
+ *
4
+ * Fetches production telemetry from jfl-platform digest API,
5
+ * extracts real metrics, compares to previous runs, generates
6
+ * training tuples, and proposes scoped RL agents.
7
+ *
8
+ * @purpose Real telemetry agent consuming platform digest API for metrics-driven RL
9
+ */
10
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, appendFileSync } from 'fs';
11
+ import { join } from 'path';
12
+ import { spawnSync } from 'child_process';
13
+ import { TrainingBuffer } from './training-buffer.js';
14
+ // ============================================================================
15
+ // Implementation
16
+ // ============================================================================
17
+ export class TelemetryAgentV2 {
18
+ projectRoot;
19
+ platformUrl;
20
+ installId;
21
+ stratusUrl;
22
+ stratusKey;
23
+ emitEvent;
24
+ statePath;
25
+ state;
26
+ constructor(opts) {
27
+ this.projectRoot = opts.projectRoot;
28
+ this.platformUrl = opts.platformUrl || process.env.JFL_PLATFORM_URL || this.loadPlatformUrl();
29
+ this.installId = opts.installId || this.loadInstallId();
30
+ this.stratusUrl = opts.stratusUrl || process.env.STRATUS_API_URL || 'https://api.stratus.run';
31
+ this.stratusKey = opts.stratusKey || process.env.STRATUS_API_KEY || '';
32
+ this.emitEvent = opts.emitEvent || (() => { });
33
+ this.statePath = join(this.projectRoot, '.jfl', 'telemetry-agent-v2-state.json');
34
+ this.state = this.loadState();
35
+ }
36
+ loadPlatformUrl() {
37
+ // Try project config first
38
+ const configPath = join(this.projectRoot, '.jfl', 'config.json');
39
+ if (existsSync(configPath)) {
40
+ try {
41
+ const config = JSON.parse(readFileSync(configPath, 'utf-8'));
42
+ if (config.platformUrl)
43
+ return config.platformUrl;
44
+ }
45
+ catch { }
46
+ }
47
+ return 'https://jfl-platform.fly.dev';
48
+ }
49
+ loadInstallId() {
50
+ // Try to get from telemetry config
51
+ const telemetryConfigPath = join(this.projectRoot, '.jfl', 'telemetry-config.json');
52
+ if (existsSync(telemetryConfigPath)) {
53
+ try {
54
+ const config = JSON.parse(readFileSync(telemetryConfigPath, 'utf-8'));
55
+ if (config.installId)
56
+ return config.installId;
57
+ }
58
+ catch { }
59
+ }
60
+ // Generate new one
61
+ return `jfl_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
62
+ }
63
+ loadState() {
64
+ const defaults = {
65
+ lastRun: '',
66
+ runCount: 0,
67
+ recentTrainingTuples: 0,
68
+ totalTrainingTuples: 0,
69
+ proposedAgents: [],
70
+ lastStratusRun: '',
71
+ stratusFailures: 0,
72
+ healthTrajectory: [],
73
+ };
74
+ if (existsSync(this.statePath)) {
75
+ try {
76
+ return { ...defaults, ...JSON.parse(readFileSync(this.statePath, 'utf-8')) };
77
+ }
78
+ catch { }
79
+ }
80
+ return defaults;
81
+ }
82
+ saveState() {
83
+ try {
84
+ const dir = join(this.projectRoot, '.jfl');
85
+ if (!existsSync(dir))
86
+ mkdirSync(dir, { recursive: true });
87
+ writeFileSync(this.statePath, JSON.stringify(this.state, null, 2));
88
+ }
89
+ catch { }
90
+ }
91
+ /**
92
+ * Fetch digest from platform API
93
+ */
94
+ async fetchDigest(hours = 24) {
95
+ try {
96
+ // Wake up Fly.io first (cold start can take 10+ seconds)
97
+ try {
98
+ await fetch(`${this.platformUrl}`, { method: 'HEAD', signal: AbortSignal.timeout(10000) }).catch(() => { });
99
+ }
100
+ catch { }
101
+ const controller = new AbortController();
102
+ const timer = setTimeout(() => controller.abort(), 60000); // 60s after warmup
103
+ const response = await fetch(`${this.platformUrl}/api/v1/telemetry/digest`, {
104
+ method: 'POST',
105
+ headers: {
106
+ 'Content-Type': 'application/json',
107
+ 'x-install-id': this.installId,
108
+ },
109
+ body: JSON.stringify({ hours }),
110
+ signal: controller.signal,
111
+ });
112
+ clearTimeout(timer);
113
+ if (response.status === 429) {
114
+ // Rate limited — wait and retry once
115
+ const retryAfter = parseInt(response.headers.get('retry-after') || '30', 10);
116
+ console.log(`Digest API rate limited, waiting ${retryAfter}s...`);
117
+ await new Promise(r => setTimeout(r, retryAfter * 1000));
118
+ const retry = await fetch(`${this.platformUrl}/api/v1/telemetry/digest`, {
119
+ method: 'POST',
120
+ headers: { 'Content-Type': 'application/json', 'x-install-id': this.installId },
121
+ body: JSON.stringify({ hours }),
122
+ signal: AbortSignal.timeout(60000),
123
+ });
124
+ if (!retry.ok) {
125
+ console.error(`Digest API retry returned ${retry.status}`);
126
+ return null;
127
+ }
128
+ const raw = await retry.json();
129
+ return this.normalizeDigest(raw);
130
+ }
131
+ if (!response.ok) {
132
+ console.error(`Digest API returned ${response.status}`);
133
+ return null;
134
+ }
135
+ const raw = await response.json();
136
+ return this.normalizeDigest(raw);
137
+ }
138
+ catch (err) {
139
+ console.error(`Failed to fetch digest: ${err.message}`);
140
+ return null;
141
+ }
142
+ }
143
+ normalizeDigest(raw) {
144
+ // Normalize API field names to our PlatformDigest interface
145
+ // API returns: commandUsage, toolFrequency, flowActivity, hookUsage
146
+ // Our types expect: commands, toolStats, flowStats, hookStats
147
+ const normalized = {
148
+ periodHours: raw.periodHours,
149
+ generatedAt: raw.generatedAt,
150
+ activeInstalls: raw.activeInstalls || 0,
151
+ totalEvents: raw.totalEvents || 0,
152
+ totalSessions: raw.sessionCosts?.length || raw.totalSessions || 0,
153
+ // Commands: normalize commandUsage → commands
154
+ commands: (raw.commandUsage || raw.commands || []).map((c) => ({
155
+ command: c.command,
156
+ count: c.count,
157
+ avgDurationMs: c.avgDurationMs || 0,
158
+ p90DurationMs: c.p90DurationMs || 0,
159
+ p99DurationMs: c.p99DurationMs || 0,
160
+ successRate: c.successRate ?? 1,
161
+ errorCount: c.errorCount || 0,
162
+ })),
163
+ commandSuccessRate: raw.commandSuccessRate ?? 1,
164
+ worstP90Command: raw.worstP90Command,
165
+ // Errors
166
+ errorClusters: raw.errorClusters || [],
167
+ totalErrors: raw.totalErrors || 0,
168
+ // Session health
169
+ sessionHealth: raw.sessionHealth || { started: 0, ended: 0, crashed: 0, avgDurationS: 0, crashRate: 0 },
170
+ // Hub health
171
+ hubHealth: raw.hubHealth || { starts: 0, stops: 0, crashes: 0, mcpCalls: 0, avgMcpLatencyMs: 0, p90McpLatencyMs: 0, p99McpLatencyMs: 0 },
172
+ // Hooks: normalize hookUsage → hookStats
173
+ hookStats: raw.hookStats || {
174
+ totalReceived: (raw.hookUsage || []).reduce((s, h) => s + (h.count || 0), 0),
175
+ byEvent: Object.fromEntries((raw.hookUsage || []).map((h) => [h.hookEventName, h.count])),
176
+ byTool: {},
177
+ fileHotspots: (raw.fileHotspots || []).map((f) => ({ file: f.file || f.path, edits: f.edits || f.count || 0 })),
178
+ },
179
+ // Tools: normalize toolFrequency → toolStats
180
+ toolStats: (raw.toolFrequency || raw.toolStats || []).map((t) => ({
181
+ toolName: t.toolName,
182
+ callCount: t.callCount || t.count || 0,
183
+ avgLatencyMs: t.avgLatencyMs || t.avgDurationMs || 0,
184
+ p90LatencyMs: t.p90LatencyMs || 0,
185
+ errorRate: t.errorRate || 0,
186
+ })),
187
+ // Flows: normalize flowActivity → flowStats
188
+ flowStats: raw.flowStats || {
189
+ triggered: (raw.flowActivity || []).reduce((s, f) => s + (f.triggerCount || 0), 0),
190
+ completed: (raw.flowActivity || []).reduce((s, f) => s + (f.completedCount || 0), 0),
191
+ failed: (raw.flowActivity || []).reduce((s, f) => s + (f.failedActions || 0), 0),
192
+ byFlow: Object.fromEntries((raw.flowActivity || []).map((f) => [f.flowName, {
193
+ triggered: f.triggerCount || 0,
194
+ completed: f.completedCount || 0,
195
+ failed: f.failedActions || 0,
196
+ }])),
197
+ completionRate: (() => {
198
+ const t = (raw.flowActivity || []).reduce((s, f) => s + (f.triggerCount || 0), 0);
199
+ const c = (raw.flowActivity || []).reduce((s, f) => s + (f.completedCount || 0), 0);
200
+ return t > 0 ? c / t : 1;
201
+ })(),
202
+ },
203
+ // Latency
204
+ latencyPercentiles: raw.latencyPercentiles || [],
205
+ // Costs
206
+ modelCosts: raw.modelCosts || [],
207
+ sessionCosts: (raw.sessionCosts || []).map((s) => ({
208
+ sessionId: s.sessionId,
209
+ installId: s.installId,
210
+ totalTokens: s.totalTokens || 0,
211
+ estimatedCostUsd: s.estimatedCostUsd || 0,
212
+ durationS: s.durationS || 0,
213
+ modelBreakdown: s.modelBreakdown || [],
214
+ })),
215
+ totalCostUsd: raw.totalCostUsd || 0,
216
+ costPerSessionUsd: raw.costPerSessionUsd || 0,
217
+ };
218
+ return normalized;
219
+ }
220
+ /**
221
+ * Extract real metrics from platform digest
222
+ */
223
+ extractMetrics(digest) {
224
+ // Command success rate: weighted average across all commands
225
+ let totalCommands = 0;
226
+ let successfulCommands = 0;
227
+ let worstP90 = 0;
228
+ for (const cmd of digest.commands) {
229
+ totalCommands += cmd.count;
230
+ successfulCommands += cmd.count * cmd.successRate;
231
+ if (cmd.p90DurationMs > worstP90)
232
+ worstP90 = cmd.p90DurationMs;
233
+ }
234
+ const commandSuccessRate = totalCommands > 0 ? successfulCommands / totalCommands : 1;
235
+ // Hub crash rate
236
+ const hubTotal = digest.hubHealth.starts + digest.hubHealth.stops + digest.hubHealth.crashes;
237
+ const hubCrashRate = hubTotal > 0 ? digest.hubHealth.crashes / hubTotal : 0;
238
+ // Session crash rate
239
+ const sessionCrashRate = digest.sessionHealth.started > 0
240
+ ? digest.sessionHealth.crashed / digest.sessionHealth.started
241
+ : 0;
242
+ // Hook hit rate: total hooks received vs expected (approx)
243
+ // We use total received as a signal - more hits = better coverage
244
+ const hookHitRate = Math.min(1, digest.hookStats.totalReceived / Math.max(100, digest.totalSessions * 10));
245
+ // Flow completion rate
246
+ const flowCompletionRate = digest.flowStats.triggered > 0
247
+ ? digest.flowStats.completed / digest.flowStats.triggered
248
+ : 1;
249
+ // Cost per session
250
+ const costPerSession = digest.totalSessions > 0
251
+ ? digest.totalCostUsd / digest.totalSessions
252
+ : 0;
253
+ return {
254
+ command_success_rate: commandSuccessRate,
255
+ command_p90_latency_ms: worstP90,
256
+ hub_crash_rate: hubCrashRate,
257
+ session_crash_rate: sessionCrashRate,
258
+ hook_hit_rate: hookHitRate,
259
+ mcp_avg_latency_ms: digest.hubHealth.avgMcpLatencyMs,
260
+ error_cluster_count: digest.errorClusters.length,
261
+ flow_completion_rate: flowCompletionRate,
262
+ cost_per_session_usd: costPerSession,
263
+ active_installs: digest.activeInstalls,
264
+ };
265
+ }
266
+ /**
267
+ * Compare metrics to previous run
268
+ */
269
+ compareMetrics(current) {
270
+ const comparisons = [];
271
+ const previous = this.state.previousDigest?.metrics || {};
272
+ // Define which direction is "better" for each metric
273
+ const metricDirections = {
274
+ command_success_rate: 'higher',
275
+ command_p90_latency_ms: 'lower',
276
+ hub_crash_rate: 'lower',
277
+ session_crash_rate: 'lower',
278
+ hook_hit_rate: 'higher',
279
+ mcp_avg_latency_ms: 'lower',
280
+ error_cluster_count: 'lower',
281
+ flow_completion_rate: 'higher',
282
+ cost_per_session_usd: 'lower',
283
+ active_installs: 'higher',
284
+ };
285
+ for (const [name, currentValue] of Object.entries(current)) {
286
+ const previousValue = previous[name] ?? currentValue;
287
+ const delta = currentValue - previousValue;
288
+ const percentChange = previousValue !== 0
289
+ ? (delta / Math.abs(previousValue)) * 100
290
+ : (currentValue !== 0 ? 100 : 0);
291
+ const direction = metricDirections[name] || 'higher';
292
+ const isImproving = direction === 'higher' ? delta > 0 : delta < 0;
293
+ const isDeclining = direction === 'higher' ? delta < 0 : delta > 0;
294
+ // Threshold: >10% decline = alert, >20% improvement = win
295
+ const significantChange = Math.abs(percentChange) > 10;
296
+ const bigWin = isImproving && Math.abs(percentChange) > 20;
297
+ comparisons.push({
298
+ name,
299
+ current: currentValue,
300
+ previous: previousValue,
301
+ delta,
302
+ percentChange,
303
+ trend: isImproving ? 'improving' : (isDeclining && significantChange ? 'declining' : 'stable'),
304
+ isAlert: isDeclining && significantChange,
305
+ isWin: bigWin,
306
+ });
307
+ }
308
+ return comparisons;
309
+ }
310
+ /**
311
+ * Generate training tuple from digest comparison
312
+ */
313
+ generateTrainingTuple(digest, metrics, comparisons) {
314
+ // Get recent commits as the "action"
315
+ const recentCommits = this.getRecentCommits();
316
+ if (!recentCommits || recentCommits.length === 0)
317
+ return;
318
+ // Build state
319
+ const state = {
320
+ composite_score: metrics.command_success_rate * (1 - metrics.session_crash_rate),
321
+ dimension_scores: {
322
+ command_success: metrics.command_success_rate,
323
+ hub_stability: 1 - metrics.hub_crash_rate,
324
+ session_stability: 1 - metrics.session_crash_rate,
325
+ flow_completion: metrics.flow_completion_rate,
326
+ cost_efficiency: 1 / (1 + metrics.cost_per_session_usd),
327
+ },
328
+ tests_passing: 0, // Not tracked here
329
+ tests_total: 0,
330
+ trajectory_length: this.state.runCount,
331
+ recent_deltas: this.state.healthTrajectory.slice(-5),
332
+ agent: 'telemetry-agent-v2',
333
+ };
334
+ // Build action from recent commits
335
+ const action = {
336
+ type: 'feature', // Could be 'fix' based on commit messages
337
+ description: recentCommits.slice(0, 3).join('; '),
338
+ files_affected: this.getRecentChangedFiles(),
339
+ scope: 'medium',
340
+ branch: this.getCurrentBranch(),
341
+ };
342
+ // Calculate composite reward from metric changes
343
+ const improvingMetrics = comparisons.filter(c => c.trend === 'improving');
344
+ const decliningMetrics = comparisons.filter(c => c.trend === 'declining');
345
+ const reward = {
346
+ composite_delta: improvingMetrics.length * 0.1 - decliningMetrics.length * 0.2,
347
+ dimension_deltas: Object.fromEntries(comparisons.map(c => [c.name, c.delta])),
348
+ tests_added: 0,
349
+ quality_score: metrics.command_success_rate,
350
+ improved: improvingMetrics.length > decliningMetrics.length,
351
+ };
352
+ // Write to training buffer
353
+ const buffer = new TrainingBuffer(this.projectRoot);
354
+ buffer.append({
355
+ agent: 'telemetry-agent-v2',
356
+ state,
357
+ action,
358
+ reward,
359
+ metadata: {
360
+ branch: this.getCurrentBranch(),
361
+ source: 'autoresearch',
362
+ },
363
+ });
364
+ this.state.recentTrainingTuples++;
365
+ this.state.totalTrainingTuples++;
366
+ }
367
+ /**
368
+ * Propose scoped agents based on metric analysis
369
+ */
370
+ proposeAgents(metrics, comparisons) {
371
+ const proposed = [];
372
+ // CLI speed agent if p90 latency is high
373
+ if (metrics.command_p90_latency_ms > 1000) {
374
+ proposed.push({
375
+ name: 'cli-speed',
376
+ reason: `Command p90 latency is ${metrics.command_p90_latency_ms}ms (>1000ms threshold)`,
377
+ triggeredBy: 'command_p90_latency_ms',
378
+ priority: metrics.command_p90_latency_ms > 2000 ? 'high' : 'medium',
379
+ config: {
380
+ metric: 'avg_ms',
381
+ direction: 'minimize',
382
+ scope: 'performance',
383
+ filesInScope: ['src/commands/**', 'src/lib/**'],
384
+ timeBudgetSeconds: 300,
385
+ },
386
+ });
387
+ }
388
+ // Bug-fix agent if error clusters are growing
389
+ const errorComparison = comparisons.find(c => c.name === 'error_cluster_count');
390
+ if (errorComparison && errorComparison.trend === 'declining' && metrics.error_cluster_count > 3) {
391
+ proposed.push({
392
+ name: 'bug-fix',
393
+ reason: `Error clusters increased to ${metrics.error_cluster_count} (was ${errorComparison.previous})`,
394
+ triggeredBy: 'error_cluster_count',
395
+ priority: 'high',
396
+ config: {
397
+ metric: 'error_count_inverse',
398
+ direction: 'maximize',
399
+ scope: 'reliability',
400
+ filesInScope: ['src/**'],
401
+ timeBudgetSeconds: 300,
402
+ },
403
+ });
404
+ }
405
+ // Hook optimization agent if hit rate is low
406
+ if (metrics.hook_hit_rate < 0.3) {
407
+ proposed.push({
408
+ name: 'hook-optimization',
409
+ reason: `Hook hit rate is ${(metrics.hook_hit_rate * 100).toFixed(1)}% (<30% threshold)`,
410
+ triggeredBy: 'hook_hit_rate',
411
+ priority: 'low',
412
+ config: {
413
+ metric: 'hook_hit_rate',
414
+ direction: 'maximize',
415
+ scope: 'hooks',
416
+ filesInScope: ['src/lib/**/hooks*', 'src/commands/hooks.ts'],
417
+ timeBudgetSeconds: 300,
418
+ },
419
+ });
420
+ }
421
+ // Flow reliability agent if completion rate is dropping
422
+ const flowComparison = comparisons.find(c => c.name === 'flow_completion_rate');
423
+ if (flowComparison && flowComparison.trend === 'declining' && metrics.flow_completion_rate < 0.9) {
424
+ proposed.push({
425
+ name: 'flow-reliability',
426
+ reason: `Flow completion rate dropped to ${(metrics.flow_completion_rate * 100).toFixed(1)}%`,
427
+ triggeredBy: 'flow_completion_rate',
428
+ priority: 'medium',
429
+ config: {
430
+ metric: 'flow_completion_rate',
431
+ direction: 'maximize',
432
+ scope: 'flows',
433
+ filesInScope: ['src/lib/flows*', 'src/commands/flows.ts'],
434
+ timeBudgetSeconds: 300,
435
+ },
436
+ });
437
+ }
438
+ // Cost efficiency agent if cost per session is high
439
+ if (metrics.cost_per_session_usd > 0.50) {
440
+ proposed.push({
441
+ name: 'cost-efficiency',
442
+ reason: `Cost per session is $${metrics.cost_per_session_usd.toFixed(4)} (>$0.50 threshold)`,
443
+ triggeredBy: 'cost_per_session_usd',
444
+ priority: 'medium',
445
+ config: {
446
+ metric: 'cost_inverse',
447
+ direction: 'maximize',
448
+ scope: 'cost',
449
+ filesInScope: ['src/lib/**'],
450
+ timeBudgetSeconds: 300,
451
+ },
452
+ });
453
+ }
454
+ return proposed;
455
+ }
456
+ /**
457
+ * Write proposed agents to .jfl/agents/proposed/
458
+ */
459
+ writeProposedAgents(agents) {
460
+ const proposedDir = join(this.projectRoot, '.jfl', 'agents', 'proposed');
461
+ if (!existsSync(proposedDir)) {
462
+ mkdirSync(proposedDir, { recursive: true });
463
+ }
464
+ for (const agent of agents) {
465
+ const toml = this.generateAgentToml(agent);
466
+ const path = join(proposedDir, `${agent.name}.toml`);
467
+ writeFileSync(path, toml);
468
+ this.state.proposedAgents.push({
469
+ timestamp: new Date().toISOString(),
470
+ agent: agent.name,
471
+ status: 'proposed',
472
+ });
473
+ }
474
+ }
475
+ generateAgentToml(agent) {
476
+ return `# ${agent.name} - Auto-proposed by telemetry-agent-v2
477
+ # Reason: ${agent.reason}
478
+ # Triggered by: ${agent.triggeredBy}
479
+ # Priority: ${agent.priority}
480
+
481
+ [agent]
482
+ name = "${agent.name}"
483
+ scope = "${agent.config.scope}"
484
+ metric = "${agent.config.metric}"
485
+ direction = "${agent.config.direction}"
486
+ time_budget_seconds = ${agent.config.timeBudgetSeconds}
487
+
488
+ [eval]
489
+ script = "eval/${agent.name}.sh"
490
+ data = "eval/fixtures/${agent.name}.jsonl"
491
+
492
+ [constraints]
493
+ files_in_scope = [${agent.config.filesInScope.map(f => `"${f}"`).join(', ')}]
494
+ files_readonly = ["eval/**", "node_modules/**"]
495
+ max_file_changes = 10
496
+
497
+ [policy]
498
+ embedding_model = "stratus-x1ac-base-claude-sonnet-4-6"
499
+ exploration_rate = 0.2
500
+ decay_per_round = 0.01
501
+ min_exploration = 0.05
502
+
503
+ [context_scope]
504
+ produces = ["${agent.config.scope}:improved"]
505
+ consumes = []
506
+ `;
507
+ }
508
+ /**
509
+ * Analyze ALL data sources — platform telemetry + local data — for product improvement insights
510
+ */
511
+ async analyzeProduct(platformDigest) {
512
+ const insights = [];
513
+ const claudeMdUpdates = [];
514
+ const staleDocs = [];
515
+ const trainingGaps = [];
516
+ // ══════════════════════════════════════════════════════════════════
517
+ // PLATFORM TELEMETRY — the real shit from the database
518
+ // ══════════════════════════════════════════════════════════════════
519
+ // Fetch digests if not provided
520
+ const digest24h = platformDigest || await this.fetchDigest(24);
521
+ const digest7d = await this.fetchDigest(168);
522
+ let platformMetrics = null;
523
+ if (digest24h) {
524
+ platformMetrics = {};
525
+ // ── Command performance analysis ──
526
+ const slowCommands = digest24h.commands.filter(c => c.p90DurationMs > 1000);
527
+ if (slowCommands.length > 0) {
528
+ insights.push({
529
+ type: 'performance', severity: 'warning',
530
+ title: `${slowCommands.length} slow commands (p90 > 1s)`,
531
+ detail: slowCommands.map(c => `${c.command}: p90=${c.p90DurationMs}ms, avg=${c.avgDurationMs}ms (${c.count} calls)`).join('; ')
532
+ });
533
+ }
534
+ const failingCommands = digest24h.commands.filter(c => c.successRate < 0.9 && c.count >= 3);
535
+ if (failingCommands.length > 0) {
536
+ insights.push({
537
+ type: 'performance', severity: 'critical',
538
+ title: `${failingCommands.length} unreliable commands (<90% success)`,
539
+ detail: failingCommands.map(c => `${c.command}: ${(c.successRate * 100).toFixed(0)}% success, ${c.errorCount} errors out of ${c.count}`).join('; ')
540
+ });
541
+ }
542
+ // ── Which command is used most? That's what to optimize first ──
543
+ const sorted = [...digest24h.commands].sort((a, b) => b.count - a.count);
544
+ if (sorted.length > 0) {
545
+ platformMetrics.mostUsedCommand = sorted[0].command;
546
+ platformMetrics.mostUsedCount = sorted[0].count;
547
+ platformMetrics.leastUsedCommand = sorted[sorted.length - 1].command;
548
+ platformMetrics.leastUsedCount = sorted[sorted.length - 1].count;
549
+ }
550
+ // ── Error cluster analysis ──
551
+ if (digest24h.errorClusters.length > 0) {
552
+ const growingErrors = digest24h.errorClusters.filter(e => e.count >= 3);
553
+ if (growingErrors.length > 0) {
554
+ insights.push({
555
+ type: 'system', severity: 'critical',
556
+ title: `${growingErrors.length} recurring error clusters`,
557
+ detail: growingErrors.map(e => `[${e.errorType}] "${e.message}" — ${e.count}x across ${e.affectedInstalls} install(s), last seen ${e.lastSeen}`).join('\n')
558
+ });
559
+ }
560
+ platformMetrics.errorClusters = digest24h.errorClusters.length;
561
+ platformMetrics.totalErrors = digest24h.totalErrors;
562
+ }
563
+ // ── Cost analysis ──
564
+ if (digest24h.modelCosts.length > 0) {
565
+ const totalCost = digest24h.totalCostUsd;
566
+ const costPerSession = digest24h.costPerSessionUsd;
567
+ platformMetrics.totalCost24h = totalCost;
568
+ platformMetrics.costPerSession = costPerSession;
569
+ // Which model burns the most?
570
+ const sortedCosts = [...digest24h.modelCosts].sort((a, b) => b.estimatedCostUsd - a.estimatedCostUsd);
571
+ const topCostModel = sortedCosts[0];
572
+ if (topCostModel && topCostModel.estimatedCostUsd > 0.10) {
573
+ insights.push({
574
+ type: 'performance', severity: 'info',
575
+ title: `Top cost: ${topCostModel.model} ($${topCostModel.estimatedCostUsd.toFixed(4)})`,
576
+ detail: `${topCostModel.callCount} calls, ${topCostModel.totalTokens} tokens. Total 24h cost: $${totalCost.toFixed(4)} across ${digest24h.totalSessions} sessions ($${costPerSession.toFixed(4)}/session).`
577
+ });
578
+ }
579
+ // Cost trend: compare 24h vs 7d average
580
+ if (digest7d && digest7d.totalCostUsd > 0) {
581
+ const dailyAvg7d = digest7d.totalCostUsd / 7;
582
+ const costTrend = totalCost / dailyAvg7d;
583
+ if (costTrend > 1.5) {
584
+ insights.push({
585
+ type: 'performance', severity: 'warning',
586
+ title: `Cost spike: ${(costTrend * 100).toFixed(0)}% of 7-day daily average`,
587
+ detail: `24h cost: $${totalCost.toFixed(4)} vs 7d daily avg: $${dailyAvg7d.toFixed(4)}. Check for runaway sessions or expensive model upgrades.`
588
+ });
589
+ }
590
+ platformMetrics.costTrend = costTrend;
591
+ }
592
+ }
593
+ // ── Session health ──
594
+ if (digest24h.sessionHealth.crashRate > 0.05) {
595
+ insights.push({
596
+ type: 'system', severity: 'critical',
597
+ title: `Session crash rate: ${(digest24h.sessionHealth.crashRate * 100).toFixed(1)}%`,
598
+ detail: `${digest24h.sessionHealth.crashed} crashed out of ${digest24h.sessionHealth.started} started. Avg session duration: ${digest24h.sessionHealth.avgDurationS.toFixed(0)}s.`
599
+ });
600
+ }
601
+ platformMetrics.sessionCrashRate = digest24h.sessionHealth.crashRate;
602
+ platformMetrics.avgSessionDuration = digest24h.sessionHealth.avgDurationS;
603
+ // ── Hub health ──
604
+ if (digest24h.hubHealth.crashes > 0) {
605
+ insights.push({
606
+ type: 'system', severity: 'warning',
607
+ title: `Hub crashed ${digest24h.hubHealth.crashes}x in 24h`,
608
+ detail: `${digest24h.hubHealth.starts} starts, ${digest24h.hubHealth.stops} stops, ${digest24h.hubHealth.crashes} crashes. MCP latency: avg=${digest24h.hubHealth.avgMcpLatencyMs}ms p90=${digest24h.hubHealth.p90McpLatencyMs}ms.`
609
+ });
610
+ }
611
+ if (digest24h.hubHealth.p90McpLatencyMs > 500) {
612
+ insights.push({
613
+ type: 'performance', severity: 'warning',
614
+ title: `MCP p90 latency: ${digest24h.hubHealth.p90McpLatencyMs}ms`,
615
+ detail: `Context hub MCP calls are slow. Avg: ${digest24h.hubHealth.avgMcpLatencyMs}ms, p90: ${digest24h.hubHealth.p90McpLatencyMs}ms, p99: ${digest24h.hubHealth.p99McpLatencyMs}ms.`
616
+ });
617
+ }
618
+ // ── Hook coverage ──
619
+ if (digest24h.hookStats.totalReceived > 0 && digest24h.hookStats.fileHotspots.length > 0) {
620
+ const hotFiles = digest24h.hookStats.fileHotspots.slice(0, 5);
621
+ platformMetrics.fileHotspots = hotFiles;
622
+ insights.push({
623
+ type: 'product', severity: 'info',
624
+ title: `File hotspots (most edited)`,
625
+ detail: hotFiles.map(f => `${f.file}: ${f.edits} edits`).join(', ')
626
+ });
627
+ }
628
+ // ── Tool usage patterns ──
629
+ if (digest24h.toolStats.length > 0) {
630
+ const unusedTools = digest24h.toolStats.filter(t => t.callCount === 0);
631
+ const errorProneTools = digest24h.toolStats.filter(t => t.errorRate > 0.1 && t.callCount >= 5);
632
+ if (errorProneTools.length > 0) {
633
+ insights.push({
634
+ type: 'system', severity: 'warning',
635
+ title: `${errorProneTools.length} MCP tools with >10% error rate`,
636
+ detail: errorProneTools.map(t => `${t.toolName}: ${(t.errorRate * 100).toFixed(0)}% errors, ${t.callCount} calls`).join('; ')
637
+ });
638
+ }
639
+ platformMetrics.totalToolCalls = digest24h.toolStats.reduce((sum, t) => sum + t.callCount, 0);
640
+ }
641
+ // ── Flow health ──
642
+ if (digest24h.flowStats.completionRate < 0.8 && digest24h.flowStats.triggered >= 5) {
643
+ insights.push({
644
+ type: 'system', severity: 'warning',
645
+ title: `Flow completion rate: ${(digest24h.flowStats.completionRate * 100).toFixed(0)}%`,
646
+ detail: `${digest24h.flowStats.completed}/${digest24h.flowStats.triggered} flows completed. ${digest24h.flowStats.failed} failed.`
647
+ });
648
+ }
649
+ // ── Session cost outliers ──
650
+ if (digest24h.sessionCosts.length > 0) {
651
+ const avgCost = digest24h.totalCostUsd / digest24h.sessionCosts.length;
652
+ const expensive = digest24h.sessionCosts.filter(s => s.estimatedCostUsd > avgCost * 3);
653
+ if (expensive.length > 0) {
654
+ insights.push({
655
+ type: 'performance', severity: 'info',
656
+ title: `${expensive.length} expensive sessions (>3x avg cost)`,
657
+ detail: expensive.map(s => `Session ${s.sessionId.slice(0, 8)}: $${s.estimatedCostUsd.toFixed(4)}, ${s.durationS}s, ${s.totalTokens} tokens`).join('; ')
658
+ });
659
+ }
660
+ }
661
+ platformMetrics.totalEvents24h = digest24h.totalEvents;
662
+ platformMetrics.totalSessions24h = digest24h.totalSessions;
663
+ platformMetrics.activeInstalls = digest24h.activeInstalls;
664
+ platformMetrics.commandSuccessRate = digest24h.commandSuccessRate;
665
+ }
666
+ // ══════════════════════════════════════════════════════════════════
667
+ // LOCAL DATA — journals, MAP events, training buffer, agent configs
668
+ // ══════════════════════════════════════════════════════════════════
669
+ // 1. Read MAP events
670
+ const eventsPath = join(this.projectRoot, '.jfl', 'map-events.jsonl');
671
+ let recentEvents = [];
672
+ if (existsSync(eventsPath)) {
673
+ const lines = readFileSync(eventsPath, 'utf-8').trim().split('\n').slice(-200);
674
+ for (const line of lines) {
675
+ try {
676
+ recentEvents.push(JSON.parse(line));
677
+ }
678
+ catch { }
679
+ }
680
+ }
681
+ const eventTypes = new Map();
682
+ for (const e of recentEvents) {
683
+ eventTypes.set(e.type, (eventTypes.get(e.type) || 0) + 1);
684
+ }
685
+ // 2. Read journals
686
+ const journalDir = join(this.projectRoot, '.jfl', 'journal');
687
+ let recentJournalEntries = [];
688
+ if (existsSync(journalDir)) {
689
+ const files = readdirSync(journalDir).filter(f => f.endsWith('.jsonl')).sort().slice(-5);
690
+ for (const f of files) {
691
+ const lines = readFileSync(join(journalDir, f), 'utf-8').trim().split('\n');
692
+ for (const line of lines) {
693
+ try {
694
+ recentJournalEntries.push(JSON.parse(line));
695
+ }
696
+ catch { }
697
+ }
698
+ }
699
+ }
700
+ // 3. Training buffer analysis
701
+ const bufferPath = join(this.projectRoot, '.jfl', 'training-buffer.jsonl');
702
+ let tupleCount = 0;
703
+ let improvedCount = 0;
704
+ let tuplesWithDiffs = 0;
705
+ let agentDistribution = new Map();
706
+ if (existsSync(bufferPath)) {
707
+ const lines = readFileSync(bufferPath, 'utf-8').trim().split('\n');
708
+ tupleCount = lines.length;
709
+ for (const line of lines.slice(-100)) {
710
+ try {
711
+ const t = JSON.parse(line);
712
+ agentDistribution.set(t.agent || 'unknown', (agentDistribution.get(t.agent || 'unknown') || 0) + 1);
713
+ if (t.reward?.improved)
714
+ improvedCount++;
715
+ if (t.action?.code_diff)
716
+ tuplesWithDiffs++;
717
+ }
718
+ catch { }
719
+ }
720
+ }
721
+ // 4. Agent configs
722
+ const agentDir = join(this.projectRoot, '.jfl', 'agents');
723
+ const agentConfigs = [];
724
+ if (existsSync(agentDir)) {
725
+ for (const f of readdirSync(agentDir).filter(f => f.endsWith('.toml'))) {
726
+ agentConfigs.push(f.replace('.toml', ''));
727
+ }
728
+ }
729
+ // 5. Product context freshness
730
+ const contextPath = join(this.projectRoot, '.jfl', 'product-context.md');
731
+ let contextAge = 999;
732
+ if (existsSync(contextPath)) {
733
+ const stat = spawnSync('stat', ['-f', '%m', contextPath], { encoding: 'utf-8' });
734
+ if (stat.stdout) {
735
+ contextAge = Math.floor((Date.now() / 1000 - parseInt(stat.stdout.trim())) / 3600);
736
+ }
737
+ }
738
+ // 6. CLAUDE.md drift detection
739
+ const claudeMdPath = join(this.projectRoot, 'CLAUDE.md');
740
+ let claudeMdContent = '';
741
+ if (existsSync(claudeMdPath)) {
742
+ claudeMdContent = readFileSync(claudeMdPath, 'utf-8');
743
+ }
744
+ // 7. Dashboard telemetry from local events
745
+ const dashboardEvents = recentEvents.filter(e => e.type.startsWith('dashboard:'));
746
+ const pageViews = new Map();
747
+ for (const e of dashboardEvents) {
748
+ if (e.type === 'dashboard:page-view' && e.data?.page) {
749
+ const page = String(e.data.page);
750
+ pageViews.set(page, (pageViews.get(page) || 0) + 1);
751
+ }
752
+ }
753
+ // ══════════════════════════════════════════════════════════════════
754
+ // LOCAL INSIGHTS
755
+ // ══════════════════════════════════════════════════════════════════
756
+ // Training data health
757
+ if (tupleCount < 100) {
758
+ insights.push({ type: 'training', severity: 'warning', title: 'Low training data', detail: `Only ${tupleCount} tuples in buffer. Need 500+ for meaningful policy head training. Run: jfl eval mine --all` });
759
+ }
760
+ if (tupleCount > 100 && improvedCount === 0) {
761
+ insights.push({ type: 'training', severity: 'critical', title: 'No improvements in recent tuples', detail: `Last 100 tuples have 0 improvements. Agents may be stuck or metrics are wrong.` });
762
+ }
763
+ if (tupleCount > 0 && tuplesWithDiffs === 0) {
764
+ insights.push({ type: 'training', severity: 'info', title: 'No code diffs in training data', detail: `${tupleCount} tuples but none have code_diff attached. Run autoresearch to generate diff-enriched tuples for code-policy training.` });
765
+ }
766
+ trainingGaps.push(`Buffer: ${tupleCount} tuples, ${improvedCount}/100 recent improved, ${tuplesWithDiffs} with code diffs`);
767
+ trainingGaps.push(`Agent distribution: ${[...agentDistribution].map(([k, v]) => `${k}=${v}`).join(', ')}`);
768
+ if (contextAge > 24) {
769
+ insights.push({ type: 'product', severity: 'warning', title: 'Product context stale', detail: `Product context is ${contextAge}h old. Run: jfl peter synthesize` });
770
+ }
771
+ if (recentEvents.length < 10) {
772
+ insights.push({ type: 'system', severity: 'warning', title: 'Low event flow', detail: `Only ${recentEvents.length} recent events. Context hub may not be running.` });
773
+ }
774
+ const recentJournals = recentJournalEntries.filter(j => {
775
+ const age = Date.now() - new Date(j.ts).getTime();
776
+ return age < 24 * 60 * 60 * 1000;
777
+ });
778
+ if (recentJournals.length === 0) {
779
+ insights.push({ type: 'product', severity: 'info', title: 'No journal entries in 24h', detail: 'No sessions have produced journal entries recently. System may be idle.' });
780
+ }
781
+ const agentsWithTuples = new Set([...agentDistribution.keys()]);
782
+ const agentsWithoutData = agentConfigs.filter(a => !agentsWithTuples.has(a));
783
+ if (agentsWithoutData.length > 0) {
784
+ insights.push({ type: 'training', severity: 'info', title: 'Agents with no training data', detail: `${agentsWithoutData.join(', ')} have never produced tuples. Run autoresearch for them.` });
785
+ }
786
+ if (pageViews.size > 0) {
787
+ const sorted = [...pageViews].sort((a, b) => b[1] - a[1]);
788
+ insights.push({ type: 'product', severity: 'info', title: 'Dashboard usage', detail: `Most visited: ${sorted[0][0]} (${sorted[0][1]}x). Least visited: ${sorted[sorted.length - 1][0]} (${sorted[sorted.length - 1][1]}x).` });
789
+ }
790
+ // CLAUDE.md drift
791
+ const servicesPath = join(this.projectRoot, '.jfl', 'services.json');
792
+ if (existsSync(servicesPath)) {
793
+ try {
794
+ const services = JSON.parse(readFileSync(servicesPath, 'utf-8'));
795
+ const serviceNames = (services.services || services || []).map((s) => s.name || s).filter(Boolean);
796
+ for (const svc of serviceNames) {
797
+ if (!claudeMdContent.includes(svc)) {
798
+ claudeMdUpdates.push(`Service "${svc}" is registered but not mentioned in CLAUDE.md`);
799
+ }
800
+ }
801
+ }
802
+ catch { }
803
+ }
804
+ for (const agent of agentConfigs) {
805
+ if (!claudeMdContent.includes(agent)) {
806
+ claudeMdUpdates.push(`Agent "${agent}" exists in .jfl/agents/ but not mentioned in CLAUDE.md`);
807
+ }
808
+ }
809
+ // Stale docs
810
+ const knowledgeDir = join(this.projectRoot, 'knowledge');
811
+ if (existsSync(knowledgeDir)) {
812
+ const checkDir = (dir, prefix) => {
813
+ if (!existsSync(dir))
814
+ return;
815
+ for (const f of readdirSync(dir)) {
816
+ const full = join(dir, f);
817
+ const stat2 = spawnSync('stat', ['-f', '%m', full], { encoding: 'utf-8' });
818
+ if (f.endsWith('.md') && stat2.stdout) {
819
+ const age = Math.floor((Date.now() / 1000 - parseInt(stat2.stdout.trim())) / (3600 * 24));
820
+ if (age > 14)
821
+ staleDocs.push(`${prefix}${f} (${age} days old)`);
822
+ }
823
+ }
824
+ };
825
+ checkDir(knowledgeDir, '');
826
+ checkDir(join(knowledgeDir, 'research'), 'research/');
827
+ }
828
+ // Write insights to journal
829
+ if (insights.length > 0) {
830
+ const journalPath2 = join(this.projectRoot, '.jfl', 'journal');
831
+ if (existsSync(journalPath2)) {
832
+ const files = readdirSync(journalPath2).filter(f => f.endsWith('.jsonl')).sort();
833
+ const latest = files[files.length - 1];
834
+ if (latest) {
835
+ const entry = {
836
+ v: 2, ts: new Date().toISOString(), session: 'telemetry-agent',
837
+ type: 'discovery', status: 'complete',
838
+ title: `Product analysis: ${insights.length} insights (${insights.filter(i => i.severity === 'critical').length} critical)`,
839
+ summary: insights.map(i => `[${i.severity}] ${i.title}`).join('; '),
840
+ detail: JSON.stringify({ insights, claudeMdUpdates, staleDocs, trainingGaps, platformMetrics }),
841
+ };
842
+ appendFileSync(join(journalPath2, latest), '\n' + JSON.stringify(entry));
843
+ }
844
+ }
845
+ }
846
+ // Emit event
847
+ this.emitEvent('telemetry:product-analysis', {
848
+ insightCount: insights.length,
849
+ criticalCount: insights.filter(i => i.severity === 'critical').length,
850
+ warningCount: insights.filter(i => i.severity === 'warning').length,
851
+ claudeMdUpdates: claudeMdUpdates.length,
852
+ staleDocs: staleDocs.length,
853
+ trainingGaps: trainingGaps.length,
854
+ tupleCount,
855
+ tuplesWithDiffs,
856
+ improvedRate: tupleCount > 0 ? (improvedCount / Math.min(100, tupleCount)) : 0,
857
+ contextAgeH: contextAge,
858
+ hasPlatformData: !!digest24h,
859
+ }, 'telemetry-agent-v2');
860
+ return { insights, claudeMdUpdates, staleDocs, trainingGaps, platformMetrics };
861
+ }
862
+ /**
863
+ * Run the telemetry agent
864
+ */
865
+ async run() {
866
+ const now = new Date().toISOString();
867
+ // Fetch 24h and 7d digests
868
+ const [digest24h, digest7d] = await Promise.all([
869
+ this.fetchDigest(24),
870
+ this.fetchDigest(168), // 7 days
871
+ ]);
872
+ if (!digest24h) {
873
+ // Even without platform digest, run local product analysis
874
+ let localAnalysis = null;
875
+ try {
876
+ localAnalysis = await this.analyzeProduct(null);
877
+ }
878
+ catch { }
879
+ return {
880
+ digest24h: null,
881
+ digest7d: null,
882
+ metrics: null,
883
+ comparisons: [],
884
+ proposedAgents: [],
885
+ alerts: [],
886
+ wins: [],
887
+ productAnalysis: localAnalysis,
888
+ };
889
+ }
890
+ // Extract metrics
891
+ const metrics = this.extractMetrics(digest24h);
892
+ // Compare to previous run
893
+ const comparisons = this.compareMetrics(metrics);
894
+ // Identify alerts and wins
895
+ const alerts = comparisons.filter(c => c.isAlert);
896
+ const wins = comparisons.filter(c => c.isWin);
897
+ // Generate training tuple
898
+ this.generateTrainingTuple(digest24h, metrics, comparisons);
899
+ // Propose agents based on metrics
900
+ const proposedAgents = this.proposeAgents(metrics, comparisons);
901
+ if (proposedAgents.length > 0) {
902
+ this.writeProposedAgents(proposedAgents);
903
+ }
904
+ // Update health trajectory
905
+ const healthScore = metrics.command_success_rate * (1 - metrics.session_crash_rate);
906
+ this.state.healthTrajectory.push(healthScore);
907
+ if (this.state.healthTrajectory.length > 50) {
908
+ this.state.healthTrajectory = this.state.healthTrajectory.slice(-50);
909
+ }
910
+ // Emit events
911
+ this.emitEvent('telemetry:digest-analyzed', {
912
+ timestamp: now,
913
+ metrics,
914
+ alerts: alerts.length,
915
+ wins: wins.length,
916
+ proposed_agents: proposedAgents.length,
917
+ }, 'telemetry-agent-v2');
918
+ for (const alert of alerts) {
919
+ this.emitEvent('telemetry:metric-alert', {
920
+ metric: alert.name,
921
+ current: alert.current,
922
+ previous: alert.previous,
923
+ delta: alert.delta,
924
+ percentChange: alert.percentChange,
925
+ }, 'telemetry-agent-v2');
926
+ }
927
+ for (const agent of proposedAgents) {
928
+ this.emitEvent('telemetry:agent-proposed', {
929
+ agent: agent.name,
930
+ reason: agent.reason,
931
+ triggeredBy: agent.triggeredBy,
932
+ priority: agent.priority,
933
+ }, 'telemetry-agent-v2');
934
+ }
935
+ // Run product analysis (platform telemetry + local data)
936
+ let productAnalysis = null;
937
+ try {
938
+ productAnalysis = await this.analyzeProduct(digest24h);
939
+ if (productAnalysis.insights.length > 0) {
940
+ console.log(` Product analysis: ${productAnalysis.insights.length} insights`);
941
+ for (const insight of productAnalysis.insights) {
942
+ console.log(` [${insight.severity}] ${insight.title}`);
943
+ }
944
+ }
945
+ if (productAnalysis.claudeMdUpdates.length > 0) {
946
+ console.log(` CLAUDE.md drift: ${productAnalysis.claudeMdUpdates.length} items need updating`);
947
+ }
948
+ if (productAnalysis.staleDocs.length > 0) {
949
+ console.log(` Stale docs: ${productAnalysis.staleDocs.join(', ')}`);
950
+ }
951
+ }
952
+ catch (err) {
953
+ console.error(` Product analysis failed: ${err.message}`);
954
+ }
955
+ // Save state
956
+ this.state.lastRun = now;
957
+ this.state.runCount++;
958
+ this.state.previousDigest = {
959
+ timestamp: now,
960
+ metrics: metrics,
961
+ };
962
+ this.saveState();
963
+ return {
964
+ digest24h,
965
+ digest7d,
966
+ metrics,
967
+ comparisons,
968
+ proposedAgents,
969
+ alerts,
970
+ wins,
971
+ productAnalysis,
972
+ };
973
+ }
974
+ /**
975
+ * Get current metrics (cached from last run or fetch fresh)
976
+ */
977
+ async getMetrics() {
978
+ const digest = await this.fetchDigest(24);
979
+ if (!digest)
980
+ return null;
981
+ return this.extractMetrics(digest);
982
+ }
983
+ /**
984
+ * Helper to get recent commits
985
+ */
986
+ getRecentCommits() {
987
+ try {
988
+ const result = spawnSync('git', ['log', '--oneline', '-10', '--pretty=format:%s'], {
989
+ cwd: this.projectRoot,
990
+ encoding: 'utf-8',
991
+ stdio: 'pipe',
992
+ });
993
+ if (result.status !== 0)
994
+ return [];
995
+ return (result.stdout || '').split('\n').filter(Boolean);
996
+ }
997
+ catch {
998
+ return [];
999
+ }
1000
+ }
1001
+ /**
1002
+ * Helper to get recent changed files
1003
+ */
1004
+ getRecentChangedFiles() {
1005
+ try {
1006
+ const result = spawnSync('git', ['diff', '--name-only', 'HEAD~5..HEAD'], {
1007
+ cwd: this.projectRoot,
1008
+ encoding: 'utf-8',
1009
+ stdio: 'pipe',
1010
+ });
1011
+ if (result.status !== 0)
1012
+ return [];
1013
+ return (result.stdout || '').split('\n').filter(Boolean).slice(0, 20);
1014
+ }
1015
+ catch {
1016
+ return [];
1017
+ }
1018
+ }
1019
+ /**
1020
+ * Helper to get current branch
1021
+ */
1022
+ getCurrentBranch() {
1023
+ try {
1024
+ const result = spawnSync('git', ['branch', '--show-current'], {
1025
+ cwd: this.projectRoot,
1026
+ encoding: 'utf-8',
1027
+ stdio: 'pipe',
1028
+ });
1029
+ return (result.stdout || '').trim() || 'main';
1030
+ }
1031
+ catch {
1032
+ return 'main';
1033
+ }
1034
+ }
1035
+ /**
1036
+ * Get agent status
1037
+ */
1038
+ getStatus() {
1039
+ return this.state;
1040
+ }
1041
+ }
1042
+ //# sourceMappingURL=telemetry-agent-v2.js.map