lattice-orchestrator 0.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 (440) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +58 -0
  3. package/config/logrotate.conf +15 -0
  4. package/dist/cli-parser.d.ts +11 -0
  5. package/dist/cli-parser.d.ts.map +1 -0
  6. package/dist/cli-parser.js +48 -0
  7. package/dist/cli-parser.js.map +1 -0
  8. package/dist/lattice-server.d.ts +70 -0
  9. package/dist/lattice-server.d.ts.map +1 -0
  10. package/dist/lattice-server.js +969 -0
  11. package/dist/lattice-server.js.map +1 -0
  12. package/dist/mcp-server/index.d.ts +3 -0
  13. package/dist/mcp-server/index.d.ts.map +1 -0
  14. package/dist/mcp-server/index.js +190 -0
  15. package/dist/mcp-server/index.js.map +1 -0
  16. package/dist/mcp-server/lattice-tools.d.ts +15 -0
  17. package/dist/mcp-server/lattice-tools.d.ts.map +1 -0
  18. package/dist/mcp-server/lattice-tools.js +366 -0
  19. package/dist/mcp-server/lattice-tools.js.map +1 -0
  20. package/dist/middleware/cors-setup.d.ts +7 -0
  21. package/dist/middleware/cors-setup.d.ts.map +1 -0
  22. package/dist/middleware/cors-setup.js +8 -0
  23. package/dist/middleware/cors-setup.js.map +1 -0
  24. package/dist/middleware/error-handler.d.ts +4 -0
  25. package/dist/middleware/error-handler.d.ts.map +1 -0
  26. package/dist/middleware/error-handler.js +27 -0
  27. package/dist/middleware/error-handler.js.map +1 -0
  28. package/dist/middleware/query-parser.d.ts +11 -0
  29. package/dist/middleware/query-parser.d.ts.map +1 -0
  30. package/dist/middleware/query-parser.js +68 -0
  31. package/dist/middleware/query-parser.js.map +1 -0
  32. package/dist/middleware/request-logger.d.ts +4 -0
  33. package/dist/middleware/request-logger.d.ts.map +1 -0
  34. package/dist/middleware/request-logger.js +6 -0
  35. package/dist/middleware/request-logger.js.map +1 -0
  36. package/dist/process-daemon/index.d.ts +14 -0
  37. package/dist/process-daemon/index.d.ts.map +1 -0
  38. package/dist/process-daemon/index.js +51 -0
  39. package/dist/process-daemon/index.js.map +1 -0
  40. package/dist/process-daemon/process-daemon.d.ts +101 -0
  41. package/dist/process-daemon/process-daemon.d.ts.map +1 -0
  42. package/dist/process-daemon/process-daemon.js +846 -0
  43. package/dist/process-daemon/process-daemon.js.map +1 -0
  44. package/dist/process-daemon/process-manager-client.d.ts +123 -0
  45. package/dist/process-daemon/process-manager-client.d.ts.map +1 -0
  46. package/dist/process-daemon/process-manager-client.js +329 -0
  47. package/dist/process-daemon/process-manager-client.js.map +1 -0
  48. package/dist/process-daemon/process-manager-interface.d.ts +61 -0
  49. package/dist/process-daemon/process-manager-interface.d.ts.map +1 -0
  50. package/dist/process-daemon/process-manager-interface.js +8 -0
  51. package/dist/process-daemon/process-manager-interface.js.map +1 -0
  52. package/dist/process-daemon/test-daemon.d.ts +12 -0
  53. package/dist/process-daemon/test-daemon.d.ts.map +1 -0
  54. package/dist/process-daemon/test-daemon.js +81 -0
  55. package/dist/process-daemon/test-daemon.js.map +1 -0
  56. package/dist/process-daemon/types.d.ts +97 -0
  57. package/dist/process-daemon/types.d.ts.map +1 -0
  58. package/dist/process-daemon/types.js +8 -0
  59. package/dist/process-daemon/types.js.map +1 -0
  60. package/dist/routes/analysis.routes.d.ts +13 -0
  61. package/dist/routes/analysis.routes.d.ts.map +1 -0
  62. package/dist/routes/analysis.routes.js +520 -0
  63. package/dist/routes/analysis.routes.js.map +1 -0
  64. package/dist/routes/config.routes.d.ts +4 -0
  65. package/dist/routes/config.routes.d.ts.map +1 -0
  66. package/dist/routes/config.routes.js +27 -0
  67. package/dist/routes/config.routes.js.map +1 -0
  68. package/dist/routes/conversation.routes.d.ts +43 -0
  69. package/dist/routes/conversation.routes.d.ts.map +1 -0
  70. package/dist/routes/conversation.routes.js +79 -0
  71. package/dist/routes/conversation.routes.js.map +1 -0
  72. package/dist/routes/filesystem.routes.d.ts +4 -0
  73. package/dist/routes/filesystem.routes.d.ts.map +1 -0
  74. package/dist/routes/filesystem.routes.js +86 -0
  75. package/dist/routes/filesystem.routes.js.map +1 -0
  76. package/dist/routes/insights.routes.d.ts +17 -0
  77. package/dist/routes/insights.routes.d.ts.map +1 -0
  78. package/dist/routes/insights.routes.js +633 -0
  79. package/dist/routes/insights.routes.js.map +1 -0
  80. package/dist/routes/lattice.routes.d.ts +10 -0
  81. package/dist/routes/lattice.routes.d.ts.map +1 -0
  82. package/dist/routes/lattice.routes.js +123 -0
  83. package/dist/routes/lattice.routes.js.map +1 -0
  84. package/dist/routes/license.routes.d.ts +3 -0
  85. package/dist/routes/license.routes.d.ts.map +1 -0
  86. package/dist/routes/license.routes.js +95 -0
  87. package/dist/routes/license.routes.js.map +1 -0
  88. package/dist/routes/log.routes.d.ts +3 -0
  89. package/dist/routes/log.routes.d.ts.map +1 -0
  90. package/dist/routes/log.routes.js +184 -0
  91. package/dist/routes/log.routes.js.map +1 -0
  92. package/dist/routes/pending-question.routes.d.ts +9 -0
  93. package/dist/routes/pending-question.routes.d.ts.map +1 -0
  94. package/dist/routes/pending-question.routes.js +162 -0
  95. package/dist/routes/pending-question.routes.js.map +1 -0
  96. package/dist/routes/permission.routes.d.ts +18 -0
  97. package/dist/routes/permission.routes.d.ts.map +1 -0
  98. package/dist/routes/permission.routes.js +370 -0
  99. package/dist/routes/permission.routes.js.map +1 -0
  100. package/dist/routes/process-events.routes.d.ts +9 -0
  101. package/dist/routes/process-events.routes.d.ts.map +1 -0
  102. package/dist/routes/process-events.routes.js +141 -0
  103. package/dist/routes/process-events.routes.js.map +1 -0
  104. package/dist/routes/prototype.routes.d.ts +9 -0
  105. package/dist/routes/prototype.routes.d.ts.map +1 -0
  106. package/dist/routes/prototype.routes.js +757 -0
  107. package/dist/routes/prototype.routes.js.map +1 -0
  108. package/dist/routes/question.routes.d.ts +8 -0
  109. package/dist/routes/question.routes.d.ts.map +1 -0
  110. package/dist/routes/question.routes.js +83 -0
  111. package/dist/routes/question.routes.js.map +1 -0
  112. package/dist/routes/session-control.routes.d.ts +29 -0
  113. package/dist/routes/session-control.routes.d.ts.map +1 -0
  114. package/dist/routes/session-control.routes.js +455 -0
  115. package/dist/routes/session-control.routes.js.map +1 -0
  116. package/dist/routes/session-lifecycle.routes.d.ts +21 -0
  117. package/dist/routes/session-lifecycle.routes.d.ts.map +1 -0
  118. package/dist/routes/session-lifecycle.routes.js +256 -0
  119. package/dist/routes/session-lifecycle.routes.js.map +1 -0
  120. package/dist/routes/session-query.routes.d.ts +25 -0
  121. package/dist/routes/session-query.routes.d.ts.map +1 -0
  122. package/dist/routes/session-query.routes.js +363 -0
  123. package/dist/routes/session-query.routes.js.map +1 -0
  124. package/dist/routes/session-stream.routes.d.ts +21 -0
  125. package/dist/routes/session-stream.routes.d.ts.map +1 -0
  126. package/dist/routes/session-stream.routes.js +235 -0
  127. package/dist/routes/session-stream.routes.js.map +1 -0
  128. package/dist/routes/streaming.routes.d.ts +4 -0
  129. package/dist/routes/streaming.routes.d.ts.map +1 -0
  130. package/dist/routes/streaming.routes.js +33 -0
  131. package/dist/routes/streaming.routes.js.map +1 -0
  132. package/dist/routes/system.routes.d.ts +7 -0
  133. package/dist/routes/system.routes.d.ts.map +1 -0
  134. package/dist/routes/system.routes.js +214 -0
  135. package/dist/routes/system.routes.js.map +1 -0
  136. package/dist/routes/walkthrough.routes.d.ts +19 -0
  137. package/dist/routes/walkthrough.routes.d.ts.map +1 -0
  138. package/dist/routes/walkthrough.routes.js +688 -0
  139. package/dist/routes/walkthrough.routes.js.map +1 -0
  140. package/dist/routes/working-directories.routes.d.ts +4 -0
  141. package/dist/routes/working-directories.routes.d.ts.map +1 -0
  142. package/dist/routes/working-directories.routes.js +25 -0
  143. package/dist/routes/working-directories.routes.js.map +1 -0
  144. package/dist/server.d.ts +3 -0
  145. package/dist/server.d.ts.map +1 -0
  146. package/dist/server.js +34 -0
  147. package/dist/server.js.map +1 -0
  148. package/dist/services/ToolMetricsService.d.ts +53 -0
  149. package/dist/services/ToolMetricsService.d.ts.map +1 -0
  150. package/dist/services/ToolMetricsService.js +230 -0
  151. package/dist/services/ToolMetricsService.js.map +1 -0
  152. package/dist/services/claude-router-service.d.ts +19 -0
  153. package/dist/services/claude-router-service.d.ts.map +1 -0
  154. package/dist/services/claude-router-service.js +160 -0
  155. package/dist/services/claude-router-service.js.map +1 -0
  156. package/dist/services/commands-service.d.ts +20 -0
  157. package/dist/services/commands-service.d.ts.map +1 -0
  158. package/dist/services/commands-service.js +115 -0
  159. package/dist/services/commands-service.js.map +1 -0
  160. package/dist/services/connection-debug-logger.d.ts +85 -0
  161. package/dist/services/connection-debug-logger.d.ts.map +1 -0
  162. package/dist/services/connection-debug-logger.js +221 -0
  163. package/dist/services/connection-debug-logger.js.map +1 -0
  164. package/dist/services/debug-log.d.ts +6 -0
  165. package/dist/services/debug-log.d.ts.map +1 -0
  166. package/dist/services/debug-log.js +27 -0
  167. package/dist/services/debug-log.js.map +1 -0
  168. package/dist/services/gemini-service.d.ts +35 -0
  169. package/dist/services/gemini-service.d.ts.map +1 -0
  170. package/dist/services/gemini-service.js +256 -0
  171. package/dist/services/gemini-service.js.map +1 -0
  172. package/dist/services/infrastructure/config-service.d.ts +79 -0
  173. package/dist/services/infrastructure/config-service.d.ts.map +1 -0
  174. package/dist/services/infrastructure/config-service.js +431 -0
  175. package/dist/services/infrastructure/config-service.js.map +1 -0
  176. package/dist/services/infrastructure/cost-tracker.d.ts +112 -0
  177. package/dist/services/infrastructure/cost-tracker.d.ts.map +1 -0
  178. package/dist/services/infrastructure/cost-tracker.js +423 -0
  179. package/dist/services/infrastructure/cost-tracker.js.map +1 -0
  180. package/dist/services/infrastructure/file-system-service.d.ts +61 -0
  181. package/dist/services/infrastructure/file-system-service.d.ts.map +1 -0
  182. package/dist/services/infrastructure/file-system-service.js +348 -0
  183. package/dist/services/infrastructure/file-system-service.js.map +1 -0
  184. package/dist/services/infrastructure/log-formatter.d.ts +5 -0
  185. package/dist/services/infrastructure/log-formatter.d.ts.map +1 -0
  186. package/dist/services/infrastructure/log-formatter.js +77 -0
  187. package/dist/services/infrastructure/log-formatter.js.map +1 -0
  188. package/dist/services/infrastructure/log-stream-buffer.d.ts +11 -0
  189. package/dist/services/infrastructure/log-stream-buffer.d.ts.map +1 -0
  190. package/dist/services/infrastructure/log-stream-buffer.js +36 -0
  191. package/dist/services/infrastructure/log-stream-buffer.js.map +1 -0
  192. package/dist/services/infrastructure/logger.d.ts +71 -0
  193. package/dist/services/infrastructure/logger.d.ts.map +1 -0
  194. package/dist/services/infrastructure/logger.js +215 -0
  195. package/dist/services/infrastructure/logger.js.map +1 -0
  196. package/dist/services/infrastructure/service-registry.d.ts +86 -0
  197. package/dist/services/infrastructure/service-registry.d.ts.map +1 -0
  198. package/dist/services/infrastructure/service-registry.js +162 -0
  199. package/dist/services/infrastructure/service-registry.js.map +1 -0
  200. package/dist/services/infrastructure/stream-manager.d.ts +87 -0
  201. package/dist/services/infrastructure/stream-manager.d.ts.map +1 -0
  202. package/dist/services/infrastructure/stream-manager.js +436 -0
  203. package/dist/services/infrastructure/stream-manager.js.map +1 -0
  204. package/dist/services/infrastructure/timing.d.ts +72 -0
  205. package/dist/services/infrastructure/timing.d.ts.map +1 -0
  206. package/dist/services/infrastructure/timing.js +115 -0
  207. package/dist/services/infrastructure/timing.js.map +1 -0
  208. package/dist/services/insights/anthropic-service.d.ts +224 -0
  209. package/dist/services/insights/anthropic-service.d.ts.map +1 -0
  210. package/dist/services/insights/anthropic-service.js +1062 -0
  211. package/dist/services/insights/anthropic-service.js.map +1 -0
  212. package/dist/services/insights/insight-audit-repository.d.ts +119 -0
  213. package/dist/services/insights/insight-audit-repository.d.ts.map +1 -0
  214. package/dist/services/insights/insight-audit-repository.js +242 -0
  215. package/dist/services/insights/insight-audit-repository.js.map +1 -0
  216. package/dist/services/insights/insight-queue.d.ts +99 -0
  217. package/dist/services/insights/insight-queue.d.ts.map +1 -0
  218. package/dist/services/insights/insight-queue.js +277 -0
  219. package/dist/services/insights/insight-queue.js.map +1 -0
  220. package/dist/services/insights/insights-computer.d.ts +132 -0
  221. package/dist/services/insights/insights-computer.d.ts.map +1 -0
  222. package/dist/services/insights/insights-computer.js +936 -0
  223. package/dist/services/insights/insights-computer.js.map +1 -0
  224. package/dist/services/insights/insights-coordinator.d.ts +165 -0
  225. package/dist/services/insights/insights-coordinator.d.ts.map +1 -0
  226. package/dist/services/insights/insights-coordinator.js +1678 -0
  227. package/dist/services/insights/insights-coordinator.js.map +1 -0
  228. package/dist/services/insights/insights-event-log.d.ts +196 -0
  229. package/dist/services/insights/insights-event-log.d.ts.map +1 -0
  230. package/dist/services/insights/insights-event-log.js +319 -0
  231. package/dist/services/insights/insights-event-log.js.map +1 -0
  232. package/dist/services/lattice-service.d.ts +77 -0
  233. package/dist/services/lattice-service.d.ts.map +1 -0
  234. package/dist/services/lattice-service.js +195 -0
  235. package/dist/services/lattice-service.js.map +1 -0
  236. package/dist/services/license-service.d.ts +69 -0
  237. package/dist/services/license-service.d.ts.map +1 -0
  238. package/dist/services/license-service.js +330 -0
  239. package/dist/services/license-service.js.map +1 -0
  240. package/dist/services/mcp-config-generator.d.ts +32 -0
  241. package/dist/services/mcp-config-generator.d.ts.map +1 -0
  242. package/dist/services/mcp-config-generator.js +126 -0
  243. package/dist/services/mcp-config-generator.js.map +1 -0
  244. package/dist/services/message-filter.d.ts +22 -0
  245. package/dist/services/message-filter.d.ts.map +1 -0
  246. package/dist/services/message-filter.js +57 -0
  247. package/dist/services/message-filter.js.map +1 -0
  248. package/dist/services/notification-service.d.ts +45 -0
  249. package/dist/services/notification-service.d.ts.map +1 -0
  250. package/dist/services/notification-service.js +184 -0
  251. package/dist/services/notification-service.js.map +1 -0
  252. package/dist/services/pending-question-service.d.ts +97 -0
  253. package/dist/services/pending-question-service.d.ts.map +1 -0
  254. package/dist/services/pending-question-service.js +223 -0
  255. package/dist/services/pending-question-service.js.map +1 -0
  256. package/dist/services/permission-event-log.d.ts +136 -0
  257. package/dist/services/permission-event-log.d.ts.map +1 -0
  258. package/dist/services/permission-event-log.js +252 -0
  259. package/dist/services/permission-event-log.js.map +1 -0
  260. package/dist/services/permission-pattern-matcher.d.ts +82 -0
  261. package/dist/services/permission-pattern-matcher.d.ts.map +1 -0
  262. package/dist/services/permission-pattern-matcher.js +294 -0
  263. package/dist/services/permission-pattern-matcher.js.map +1 -0
  264. package/dist/services/permission-tracker.d.ts +67 -0
  265. package/dist/services/permission-tracker.d.ts.map +1 -0
  266. package/dist/services/permission-tracker.js +162 -0
  267. package/dist/services/permission-tracker.js.map +1 -0
  268. package/dist/services/process/claude-process-manager.d.ts +142 -0
  269. package/dist/services/process/claude-process-manager.d.ts.map +1 -0
  270. package/dist/services/process/claude-process-manager.js +1218 -0
  271. package/dist/services/process/claude-process-manager.js.map +1 -0
  272. package/dist/services/process/conversation-status-manager.d.ts +110 -0
  273. package/dist/services/process/conversation-status-manager.d.ts.map +1 -0
  274. package/dist/services/process/conversation-status-manager.js +349 -0
  275. package/dist/services/process/conversation-status-manager.js.map +1 -0
  276. package/dist/services/process/json-lines-parser.d.ts +19 -0
  277. package/dist/services/process/json-lines-parser.d.ts.map +1 -0
  278. package/dist/services/process/json-lines-parser.js +59 -0
  279. package/dist/services/process/json-lines-parser.js.map +1 -0
  280. package/dist/services/process/process-event-log.d.ts +263 -0
  281. package/dist/services/process/process-event-log.d.ts.map +1 -0
  282. package/dist/services/process/process-event-log.js +509 -0
  283. package/dist/services/process/process-event-log.js.map +1 -0
  284. package/dist/services/process/process-manager-factory.d.ts +109 -0
  285. package/dist/services/process/process-manager-factory.d.ts.map +1 -0
  286. package/dist/services/process/process-manager-factory.js +338 -0
  287. package/dist/services/process/process-manager-factory.js.map +1 -0
  288. package/dist/services/question-tracker.d.ts +51 -0
  289. package/dist/services/question-tracker.d.ts.map +1 -0
  290. package/dist/services/question-tracker.js +111 -0
  291. package/dist/services/question-tracker.js.map +1 -0
  292. package/dist/services/sessions/claude-history-reader.d.ts +139 -0
  293. package/dist/services/sessions/claude-history-reader.d.ts.map +1 -0
  294. package/dist/services/sessions/claude-history-reader.js +864 -0
  295. package/dist/services/sessions/claude-history-reader.js.map +1 -0
  296. package/dist/services/sessions/conversation-cache.d.ts +98 -0
  297. package/dist/services/sessions/conversation-cache.d.ts.map +1 -0
  298. package/dist/services/sessions/conversation-cache.js +329 -0
  299. package/dist/services/sessions/conversation-cache.js.map +1 -0
  300. package/dist/services/sessions/session-activity-watcher.d.ts +67 -0
  301. package/dist/services/sessions/session-activity-watcher.d.ts.map +1 -0
  302. package/dist/services/sessions/session-activity-watcher.js +236 -0
  303. package/dist/services/sessions/session-activity-watcher.js.map +1 -0
  304. package/dist/services/sessions/session-analysis-service.d.ts +72 -0
  305. package/dist/services/sessions/session-analysis-service.d.ts.map +1 -0
  306. package/dist/services/sessions/session-analysis-service.js +373 -0
  307. package/dist/services/sessions/session-analysis-service.js.map +1 -0
  308. package/dist/services/sessions/session-branch-service.d.ts +76 -0
  309. package/dist/services/sessions/session-branch-service.d.ts.map +1 -0
  310. package/dist/services/sessions/session-branch-service.js +355 -0
  311. package/dist/services/sessions/session-branch-service.js.map +1 -0
  312. package/dist/services/sessions/session-info-service.d.ts +455 -0
  313. package/dist/services/sessions/session-info-service.d.ts.map +1 -0
  314. package/dist/services/sessions/session-info-service.js +1640 -0
  315. package/dist/services/sessions/session-info-service.js.map +1 -0
  316. package/dist/services/sessions/session-marks-repository.d.ts +78 -0
  317. package/dist/services/sessions/session-marks-repository.d.ts.map +1 -0
  318. package/dist/services/sessions/session-marks-repository.js +263 -0
  319. package/dist/services/sessions/session-marks-repository.js.map +1 -0
  320. package/dist/services/sessions/session-marks-service.d.ts +137 -0
  321. package/dist/services/sessions/session-marks-service.d.ts.map +1 -0
  322. package/dist/services/sessions/session-marks-service.js +562 -0
  323. package/dist/services/sessions/session-marks-service.js.map +1 -0
  324. package/dist/services/sessions/session-review-service.d.ts +98 -0
  325. package/dist/services/sessions/session-review-service.d.ts.map +1 -0
  326. package/dist/services/sessions/session-review-service.js +629 -0
  327. package/dist/services/sessions/session-review-service.js.map +1 -0
  328. package/dist/services/sessions/turn-capture-service.d.ts +83 -0
  329. package/dist/services/sessions/turn-capture-service.d.ts.map +1 -0
  330. package/dist/services/sessions/turn-capture-service.js +477 -0
  331. package/dist/services/sessions/turn-capture-service.js.map +1 -0
  332. package/dist/services/sessions/turn-repository.d.ts +48 -0
  333. package/dist/services/sessions/turn-repository.d.ts.map +1 -0
  334. package/dist/services/sessions/turn-repository.js +103 -0
  335. package/dist/services/sessions/turn-repository.js.map +1 -0
  336. package/dist/services/walkthrough-service.d.ts +226 -0
  337. package/dist/services/walkthrough-service.d.ts.map +1 -0
  338. package/dist/services/walkthrough-service.js +1112 -0
  339. package/dist/services/walkthrough-service.js.map +1 -0
  340. package/dist/services/walkthrough-skill-prompt.d.ts +34 -0
  341. package/dist/services/walkthrough-skill-prompt.d.ts.map +1 -0
  342. package/dist/services/walkthrough-skill-prompt.js +313 -0
  343. package/dist/services/walkthrough-skill-prompt.js.map +1 -0
  344. package/dist/services/web-push-service.d.ts +48 -0
  345. package/dist/services/web-push-service.d.ts.map +1 -0
  346. package/dist/services/web-push-service.js +186 -0
  347. package/dist/services/web-push-service.js.map +1 -0
  348. package/dist/services/working-directories-service.d.ts +19 -0
  349. package/dist/services/working-directories-service.d.ts.map +1 -0
  350. package/dist/services/working-directories-service.js +103 -0
  351. package/dist/services/working-directories-service.js.map +1 -0
  352. package/dist/types/config.d.ts +122 -0
  353. package/dist/types/config.d.ts.map +1 -0
  354. package/dist/types/config.js +21 -0
  355. package/dist/types/config.js.map +1 -0
  356. package/dist/types/express.d.ts +5 -0
  357. package/dist/types/express.d.ts.map +1 -0
  358. package/dist/types/express.js +2 -0
  359. package/dist/types/express.js.map +1 -0
  360. package/dist/types/index.d.ts +400 -0
  361. package/dist/types/index.d.ts.map +1 -0
  362. package/dist/types/index.js +41 -0
  363. package/dist/types/index.js.map +1 -0
  364. package/dist/types/insights.d.ts +176 -0
  365. package/dist/types/insights.d.ts.map +1 -0
  366. package/dist/types/insights.js +23 -0
  367. package/dist/types/insights.js.map +1 -0
  368. package/dist/types/license.d.ts +70 -0
  369. package/dist/types/license.d.ts.map +1 -0
  370. package/dist/types/license.js +5 -0
  371. package/dist/types/license.js.map +1 -0
  372. package/dist/types/router-config.d.ts +13 -0
  373. package/dist/types/router-config.d.ts.map +1 -0
  374. package/dist/types/router-config.js +2 -0
  375. package/dist/types/router-config.js.map +1 -0
  376. package/dist/utils/constants.d.ts +26 -0
  377. package/dist/utils/constants.d.ts.map +1 -0
  378. package/dist/utils/constants.js +28 -0
  379. package/dist/utils/constants.js.map +1 -0
  380. package/dist/utils/machine-id.d.ts +7 -0
  381. package/dist/utils/machine-id.d.ts.map +1 -0
  382. package/dist/utils/machine-id.js +76 -0
  383. package/dist/utils/machine-id.js.map +1 -0
  384. package/dist/utils/server-startup.d.ts +11 -0
  385. package/dist/utils/server-startup.d.ts.map +1 -0
  386. package/dist/utils/server-startup.js +9 -0
  387. package/dist/utils/server-startup.js.map +1 -0
  388. package/dist/utils/update-check.d.ts +13 -0
  389. package/dist/utils/update-check.d.ts.map +1 -0
  390. package/dist/utils/update-check.js +90 -0
  391. package/dist/utils/update-check.js.map +1 -0
  392. package/dist/web/assets/ArchivedCardPrototype-S9ifiasa.js +5 -0
  393. package/dist/web/assets/BannerGallery-B__rJV6F.js +1 -0
  394. package/dist/web/assets/BannerPrototype-DBKP9Uiu.js +52 -0
  395. package/dist/web/assets/CodeHikeExperiment-B8jjWAFy.js +15 -0
  396. package/dist/web/assets/ContextTooltipVariations-DzklAFam.js +1 -0
  397. package/dist/web/assets/FontShowcasePrototype-KIMEWeP2.js +13 -0
  398. package/dist/web/assets/GeometricGallery-DddlWhHK.js +1 -0
  399. package/dist/web/assets/HistoryWalkthroughPrototype-DeniRRdw.js +18 -0
  400. package/dist/web/assets/InlineWalkthroughPrototype-Csd5r_Hk.js +1 -0
  401. package/dist/web/assets/MarkButtonPrototype-CxhxE0RP.js +1 -0
  402. package/dist/web/assets/MenuStylesPrototype-D7neA6YM.js +1 -0
  403. package/dist/web/assets/MomentCardVariations-2GT7GyFn.js +1 -0
  404. package/dist/web/assets/MomentHeaderVariations-DhGEw4XC.js +1 -0
  405. package/dist/web/assets/NarrativeWalkthroughDemo-B5C566fu.js +389 -0
  406. package/dist/web/assets/OutcomeVariations-BrZfsVcs.js +1 -0
  407. package/dist/web/assets/PermissionPatternPickerPrototype-CBOhe2Me.js +1 -0
  408. package/dist/web/assets/PermissionPrototype-BcF-a5an.js +1 -0
  409. package/dist/web/assets/PipelineGallery-BJhyM0rx.js +1 -0
  410. package/dist/web/assets/ScopeHeaderPrototype-GD1HNfaO.js +1 -0
  411. package/dist/web/assets/ScopeHeaderStylesPrototype-aa4uNJJ1.js +1 -0
  412. package/dist/web/assets/ScrollycodingPrototype-CKW1bf72.js +70 -0
  413. package/dist/web/assets/SectionHeaderVariations-DM8vUwfj.js +1 -0
  414. package/dist/web/assets/SemanticGallery-CtQEo7St.js +1 -0
  415. package/dist/web/assets/SessionCardPrototype-CgHCIMHe.js +1 -0
  416. package/dist/web/assets/SessionSidebarVariations-DMQL3Azj.js +3 -0
  417. package/dist/web/assets/SessionStartPrototype-Cwsv01Rr.js +1 -0
  418. package/dist/web/assets/SmartMenuPrototype-Br37Qbs_.js +1 -0
  419. package/dist/web/assets/StyleGallery-rZgrploB.js +1 -0
  420. package/dist/web/assets/TimelineCardPrototype-lzPc5mhe.js +19 -0
  421. package/dist/web/assets/ToolbarPrototype-Dm4BNZra.js +1 -0
  422. package/dist/web/assets/TooltipExperiment-Dy8QzTIP.js +13 -0
  423. package/dist/web/assets/WalkthroughCTAPrototype-uHoovujd.js +1 -0
  424. package/dist/web/assets/WalkthroughHeaderVariations-Do7Di1g1.js +1 -0
  425. package/dist/web/assets/WalkthroughShowcase-sGmRoPoM.js +112 -0
  426. package/dist/web/assets/arrow-right-D46Nx1mC.js +1 -0
  427. package/dist/web/assets/brain-BXIZKtOZ.js +1 -0
  428. package/dist/web/assets/grid-3x3-Cb81B62m.js +1 -0
  429. package/dist/web/assets/main-B1fyog77.js +321 -0
  430. package/dist/web/assets/main-C2PK2Klg.css +1 -0
  431. package/dist/web/assets/semantic-variations-Bd-W7ea2.js +1 -0
  432. package/dist/web/assets/target-Cf92wDTW.js +1 -0
  433. package/dist/web/favicon.png +0 -0
  434. package/dist/web/favicon.svg +22 -0
  435. package/dist/web/icon-192x192.png +0 -0
  436. package/dist/web/icon-512x512.png +0 -0
  437. package/dist/web/index.html +45 -0
  438. package/dist/web/manifest.json +61 -0
  439. package/package.json +192 -0
  440. package/scripts/postinstall.js +60 -0
@@ -0,0 +1,936 @@
1
+ import crypto from 'crypto';
2
+ import { createLogger } from '../infrastructure/logger.js';
3
+ import { anthropicService } from '../insights/anthropic-service.js';
4
+ import { SessionInfoService } from '../sessions/session-info-service.js';
5
+ import { geminiService } from '../gemini-service.js';
6
+ import { getSessionActivityWatcher } from '../sessions/session-activity-watcher.js';
7
+ /**
8
+ * Generate a trace ID for correlating recompute events through the system.
9
+ * Format: <session-prefix>-<timestamp-hex>-<random>
10
+ */
11
+ function generateTraceId(sessionId) {
12
+ const sessionPrefix = sessionId.slice(0, 8);
13
+ const timestampHex = Date.now().toString(16);
14
+ const random = crypto.randomBytes(2).toString('hex');
15
+ return `${sessionPrefix}-${timestampHex}-${random}`;
16
+ }
17
+ function buildStateSnapshot(cache, insights) {
18
+ if (insights) {
19
+ return {
20
+ mission: insights.context?.mission || undefined,
21
+ description: insights.description || undefined,
22
+ theme: insights.theme || undefined,
23
+ milestones: insights.milestones?.map(m => `${m.label}:${m.status}`) || [],
24
+ notableCount: insights.notable?.length || 0,
25
+ };
26
+ }
27
+ if (cache) {
28
+ return {
29
+ mission: cache.context?.mission || undefined,
30
+ description: cache.description || undefined,
31
+ theme: cache.theme || undefined,
32
+ milestones: cache.milestones?.map((m) => `${m.label}:${m.status}`) || [],
33
+ notableCount: cache.notable?.length || 0,
34
+ };
35
+ }
36
+ return {};
37
+ }
38
+ /**
39
+ * InsightsComputer - The computational engine for generating insights.
40
+ *
41
+ * This service handles the actual LLM calls and prompt construction for insights.
42
+ * It does NOT coordinate when to generate insights - that's InsightsCoordinator's job.
43
+ *
44
+ * Responsibilities:
45
+ * - Building prompts for insight generation
46
+ * - Calling Anthropic/Gemini APIs
47
+ * - Caching computed insights
48
+ * - Generating identity images
49
+ *
50
+ * Related services:
51
+ * - InsightsCoordinator (insights-coordinator.ts) - orchestrates when to compute
52
+ * - AnthropicService (anthropic-service.ts) - actual API calls
53
+ */
54
+ export class InsightsComputer {
55
+ logger;
56
+ historyReader;
57
+ sessionInfoService;
58
+ constructor(historyReader, sessionInfoService) {
59
+ this.logger = createLogger('InsightsComputer');
60
+ this.historyReader = historyReader;
61
+ this.sessionInfoService = sessionInfoService || SessionInfoService.getInstance();
62
+ }
63
+ /**
64
+ * Check if a session is automated (e.g., Claudia sessions).
65
+ * Automated sessions should skip expensive AI insight generation.
66
+ */
67
+ async isAutomatedSession(sessionId) {
68
+ try {
69
+ const { metadata } = await this.historyReader.fetchConversationDirect(sessionId);
70
+ return metadata.projectPath?.toLowerCase().includes('/claudia') || false;
71
+ }
72
+ catch {
73
+ // If we can't determine, assume not automated
74
+ return false;
75
+ }
76
+ }
77
+ /**
78
+ * Extract the final todo state from conversation messages
79
+ */
80
+ extractTodoState(messages) {
81
+ let lastTodoState = null;
82
+ for (const msg of messages) {
83
+ const content = msg.message?.content;
84
+ if (!Array.isArray(content))
85
+ continue;
86
+ for (const block of content) {
87
+ if (typeof block === 'object' &&
88
+ block !== null &&
89
+ 'type' in block &&
90
+ block.type === 'tool_use' &&
91
+ 'name' in block &&
92
+ block.name === 'TodoWrite' &&
93
+ 'input' in block &&
94
+ typeof block.input === 'object' &&
95
+ block.input !== null &&
96
+ 'todos' in block.input &&
97
+ Array.isArray(block.input.todos)) {
98
+ lastTodoState = block.input.todos;
99
+ }
100
+ }
101
+ }
102
+ return lastTodoState;
103
+ }
104
+ /**
105
+ * Compute progress from todo state
106
+ */
107
+ computeProgress(todos) {
108
+ const completed = todos.filter(t => t.status === 'completed').length;
109
+ const inProgress = todos.filter(t => t.status === 'in_progress').length;
110
+ const pending = todos.filter(t => t.status === 'pending').length;
111
+ const total = todos.length;
112
+ const percent = total > 0 ? Math.round((completed / total) * 100) : 0;
113
+ return { completed, inProgress, pending, total, percent };
114
+ }
115
+ /**
116
+ * Extract the last N actions from recent messages (tool uses, user messages, assistant text)
117
+ * Returns an array of {tool, timestamp} for mini action log display
118
+ */
119
+ extractRecentActions(messages, limit = 14) {
120
+ const actions = [];
121
+ // Walk backwards through messages
122
+ for (let i = messages.length - 1; i >= 0 && actions.length < limit; i--) {
123
+ const msg = messages[i];
124
+ const content = msg.message?.content;
125
+ const timestamp = msg.timestamp ? new Date(msg.timestamp).getTime() : Date.now();
126
+ // User messages - handle both string and array content
127
+ if (msg.type === 'user') {
128
+ // String content (most common for typed user prompts)
129
+ if (typeof content === 'string' && content.trim().length > 0) {
130
+ actions.push({
131
+ tool: 'User',
132
+ timestamp
133
+ });
134
+ if (actions.length >= limit)
135
+ break;
136
+ continue;
137
+ }
138
+ // Array content - look for text blocks
139
+ if (Array.isArray(content)) {
140
+ for (const block of content) {
141
+ if (typeof block === 'object' &&
142
+ block !== null &&
143
+ 'type' in block &&
144
+ block.type === 'text' &&
145
+ 'text' in block &&
146
+ typeof block.text === 'string' &&
147
+ block.text.trim().length > 0) {
148
+ actions.push({
149
+ tool: 'User',
150
+ timestamp
151
+ });
152
+ // Only count one user action per message
153
+ break;
154
+ }
155
+ }
156
+ if (actions.length >= limit)
157
+ break;
158
+ }
159
+ continue;
160
+ }
161
+ // Skip non-array content for assistant messages
162
+ if (!Array.isArray(content))
163
+ continue;
164
+ // Assistant messages - both tool uses and text responses
165
+ if (msg.type === 'assistant') {
166
+ let hasToolUse = false;
167
+ let _hasText = false;
168
+ // First pass: check what types we have
169
+ for (const block of content) {
170
+ if (typeof block === 'object' && block !== null && 'type' in block) {
171
+ if (block.type === 'tool_use')
172
+ hasToolUse = true;
173
+ if (block.type === 'text')
174
+ _hasText = true;
175
+ }
176
+ }
177
+ // Second pass: add actions
178
+ for (const block of content) {
179
+ if (typeof block !== 'object' || block === null || !('type' in block))
180
+ continue;
181
+ if (block.type === 'tool_use' && 'name' in block && typeof block.name === 'string') {
182
+ actions.push({
183
+ tool: block.name,
184
+ timestamp
185
+ });
186
+ if (actions.length >= limit)
187
+ break;
188
+ }
189
+ else if (block.type === 'text' && 'text' in block && typeof block.text === 'string') {
190
+ // Only add text response if there's no tool use in this message
191
+ if (!hasToolUse && block.text.trim().length > 0) {
192
+ actions.push({
193
+ tool: 'Response',
194
+ timestamp
195
+ });
196
+ // Only count one text response per message
197
+ break;
198
+ }
199
+ }
200
+ }
201
+ if (actions.length >= limit)
202
+ break;
203
+ }
204
+ }
205
+ // Reverse to get chronological order (oldest first, newest last)
206
+ return actions.reverse();
207
+ }
208
+ /**
209
+ * Extract user prompts from conversation
210
+ */
211
+ extractUserPrompts(messages) {
212
+ const prompts = [];
213
+ for (const msg of messages) {
214
+ if (msg.type !== 'user')
215
+ continue;
216
+ const content = msg.message?.content;
217
+ if (typeof content === 'string' && content.trim()) {
218
+ prompts.push(content.trim());
219
+ }
220
+ else if (Array.isArray(content)) {
221
+ for (const block of content) {
222
+ if (typeof block === 'object' &&
223
+ block !== null &&
224
+ 'type' in block &&
225
+ block.type === 'text' &&
226
+ 'text' in block &&
227
+ typeof block.text === 'string') {
228
+ prompts.push(block.text.trim());
229
+ }
230
+ }
231
+ }
232
+ }
233
+ return prompts.filter(p => p.length > 0 && !p.startsWith('['));
234
+ }
235
+ /**
236
+ * Extract recent assistant text responses (last N messages)
237
+ * Used to capture outcomes and progress for insight generation
238
+ */
239
+ extractRecentAssistantText(messages, limit = 3) {
240
+ const texts = [];
241
+ // Work backwards from the end to get most recent first
242
+ for (let i = messages.length - 1; i >= 0 && texts.length < limit; i--) {
243
+ const msg = messages[i];
244
+ if (msg.type !== 'assistant')
245
+ continue;
246
+ const content = msg.message?.content;
247
+ if (Array.isArray(content)) {
248
+ for (const block of content) {
249
+ if (typeof block === 'object' &&
250
+ block !== null &&
251
+ 'type' in block &&
252
+ block.type === 'text' &&
253
+ 'text' in block &&
254
+ typeof block.text === 'string' &&
255
+ block.text.trim()) {
256
+ texts.push(block.text.trim());
257
+ break; // Only one text block per message
258
+ }
259
+ }
260
+ }
261
+ }
262
+ return texts;
263
+ }
264
+ /**
265
+ * Format a tool call into a human-readable action description
266
+ */
267
+ formatToolAction(toolName, input) {
268
+ switch (toolName) {
269
+ case 'Read':
270
+ return `Reading ${this.extractFilename(input.file_path)}`;
271
+ case 'Edit':
272
+ return `Editing ${this.extractFilename(input.file_path)}`;
273
+ case 'Write':
274
+ return `Writing ${this.extractFilename(input.file_path)}`;
275
+ case 'Grep':
276
+ return `Searching: "${input.pattern?.slice(0, 40)}"`;
277
+ case 'Glob':
278
+ return `Finding: ${input.pattern?.slice(0, 40)}`;
279
+ case 'Bash': {
280
+ const description = input.description;
281
+ if (description) {
282
+ return description.slice(0, 50);
283
+ }
284
+ const command = input.command || '';
285
+ return command.slice(0, 50) || 'Running command';
286
+ }
287
+ case 'Task':
288
+ return input.description?.slice(0, 50) || 'Delegating task';
289
+ case 'TodoWrite': {
290
+ const todos = input.todos;
291
+ if (todos && todos.length > 0) {
292
+ const inProgress = todos.find(t => t.status === 'in_progress');
293
+ if (inProgress?.content) {
294
+ return inProgress.content.slice(0, 50);
295
+ }
296
+ }
297
+ return 'Updating todo list';
298
+ }
299
+ default:
300
+ return toolName;
301
+ }
302
+ }
303
+ /**
304
+ * Extract filename from a path for display
305
+ */
306
+ extractFilename(path) {
307
+ if (!path)
308
+ return 'file';
309
+ const parts = path.split('/');
310
+ return parts[parts.length - 1] || 'file';
311
+ }
312
+ /**
313
+ * Generate rich structured session insights using Opus 4.5
314
+ */
315
+ async generateStructuredInsights(messages, sessionId) {
316
+ // Check if Anthropic is configured
317
+ if (!anthropicService.isConfigured()) {
318
+ this.logger.debug('Anthropic service not configured, skipping insight extraction');
319
+ return { context: null, milestones: [], notable: [], recentActions: [], theme: null, tags: null };
320
+ }
321
+ try {
322
+ // Get user prompts for context
323
+ const userPrompts = this.extractUserPrompts(messages);
324
+ if (userPrompts.length === 0) {
325
+ return { context: null, milestones: [], notable: [], recentActions: [], theme: null, tags: null };
326
+ }
327
+ // Get todo items for context
328
+ const todos = this.extractTodoState(messages);
329
+ const todoContext = todos
330
+ ? `\nTask list:\n${todos.map(t => `- [${t.status}] ${t.content}`).join('\n')}`
331
+ : '';
332
+ // Get recent assistant responses for outcome/progress context
333
+ const recentAssistantText = this.extractRecentAssistantText(messages, 3);
334
+ const assistantContext = recentAssistantText.length > 0
335
+ ? `\nRecent assistant responses:\n${recentAssistantText.map(t => `- "${t}"`).join('\n')}`
336
+ : '';
337
+ // Build conversation context - no truncation, models have plenty of context
338
+ const conversationText = `User requests (chronological):
339
+ ${userPrompts.slice(-12).map(p => `- "${p}"`).join('\n')}
340
+ ${todoContext}${assistantContext}`;
341
+ const result = await anthropicService.extractSessionInsights(conversationText, sessionId);
342
+ return {
343
+ context: result.context,
344
+ milestones: result.milestones,
345
+ notable: result.notable || [],
346
+ recentActions: result.recentActions,
347
+ theme: result.theme || null,
348
+ tags: result.tags || null
349
+ };
350
+ }
351
+ catch (error) {
352
+ this.logger.error('Failed to generate structured insights - re-throwing to preserve existing cache', { error });
353
+ // Re-throw so callers can decide whether to use cached data or handle the error
354
+ throw error;
355
+ }
356
+ }
357
+ /**
358
+ * Get full insights for a session (checks cache first)
359
+ * For automated sessions, returns quick insights without AI generation.
360
+ *
361
+ * NON-BLOCKING BEHAVIOR:
362
+ * - If cache is fresh → return it immediately
363
+ * - If cache is stale → return stale data immediately, trigger background regeneration
364
+ * - If no cache → generate synchronously (first time only)
365
+ */
366
+ async getInsights(sessionId) {
367
+ this.logger.debug('[GET INSIGHTS] Called', {
368
+ sessionId: sessionId.slice(0, 8),
369
+ timestamp: new Date().toISOString()
370
+ });
371
+ // Check cache first
372
+ const cached = await this.sessionInfoService.getInsights(sessionId);
373
+ this.logger.debug('[GET INSIGHTS] Cache lookup result', {
374
+ sessionId: sessionId.slice(0, 8),
375
+ hasCached: !!cached,
376
+ isStale: cached?.stale || false,
377
+ cachedMission: cached?.context ? (typeof cached.context === 'string' ? JSON.parse(cached.context).mission : cached.context.mission)?.slice(0, 50) : null
378
+ });
379
+ if (cached && !cached.stale) {
380
+ this.logger.debug('[GET INSIGHTS] Returning cached (not stale)', {
381
+ sessionId: sessionId.slice(0, 8)
382
+ });
383
+ return this.cachedToSessionInsights(cached);
384
+ }
385
+ // For automated sessions, skip AI generation entirely
386
+ const isAutomated = await this.isAutomatedSession(sessionId);
387
+ if (isAutomated) {
388
+ this.logger.debug('Skipping AI insights for automated session', { sessionId });
389
+ return this.getInsightsQuick(sessionId);
390
+ }
391
+ // NON-BLOCKING: If we have stale cache, return it immediately and regenerate in background
392
+ if (cached && cached.stale) {
393
+ this.logger.debug('[GET INSIGHTS] Returning stale cache, triggering background regeneration', {
394
+ sessionId: sessionId.slice(0, 8)
395
+ });
396
+ // Fire off background regeneration (don't await)
397
+ this.regenerateInsightsInBackground(sessionId, cached).catch(err => this.logger.debug('Background regeneration failed', { sessionId, error: err }));
398
+ // Return stale data immediately for fast UX
399
+ return this.cachedToSessionInsights(cached);
400
+ }
401
+ // No cache at all - must generate synchronously (first time for this session)
402
+ this.logger.warn('[GET INSIGHTS] No cache - triggering synchronous generation', {
403
+ sessionId: sessionId.slice(0, 8)
404
+ });
405
+ const traceId = generateTraceId(sessionId);
406
+ const startTime = Date.now();
407
+ const beforeState = buildStateSnapshot(cached);
408
+ const insights = await this.computeInsights(sessionId);
409
+ // Cache the result and audit the recompute
410
+ const afterState = buildStateSnapshot(null, insights);
411
+ const changedFields = [];
412
+ if (beforeState.mission !== afterState.mission)
413
+ changedFields.push('mission');
414
+ if (beforeState.description !== afterState.description)
415
+ changedFields.push('description');
416
+ if (beforeState.theme !== afterState.theme)
417
+ changedFields.push('theme');
418
+ if (JSON.stringify(beforeState.milestones) !== JSON.stringify(afterState.milestones))
419
+ changedFields.push('milestones');
420
+ const durationMs = Date.now() - startTime;
421
+ // Audit the recompute
422
+ this.sessionInfoService.auditEventInsight({
423
+ traceId,
424
+ sessionId,
425
+ eventType: 'recompute',
426
+ trigger: 'api_request',
427
+ beforeState,
428
+ afterState,
429
+ patchedFields: changedFields,
430
+ durationMs,
431
+ llmResponse: `Opus returned: context=${!!insights.context}, description=${!!insights.description}, milestones=${(insights.milestones || []).length}`,
432
+ });
433
+ // Cache the result (don't await - fire and forget)
434
+ this.cacheInsights(sessionId, insights).catch(err => this.logger.debug('Failed to cache insights', { sessionId, error: err }));
435
+ // Build V9 fields inline for immediate return (before cache is written)
436
+ const now = new Date().toISOString();
437
+ const progressItems = (insights.milestones || []).map(m => ({
438
+ label: m.label,
439
+ status: m.status,
440
+ addedAt: now,
441
+ updatedAt: now
442
+ }));
443
+ const progressSummary = {
444
+ done: progressItems.filter(p => p.status === 'done').length,
445
+ current: progressItems.filter(p => p.status === 'current').length,
446
+ pending: progressItems.filter(p => p.status === 'pending').length,
447
+ paused: 0,
448
+ abandoned: 0,
449
+ total: progressItems.length
450
+ };
451
+ const notableEvents = (insights.notable || []).map(n => ({
452
+ text: n.text || n.description || '',
453
+ icon: n.icon,
454
+ addedAt: now
455
+ }));
456
+ const recentActionsV2 = (insights.recentActions || []).map(text => ({
457
+ text,
458
+ addedAt: now
459
+ }));
460
+ return {
461
+ ...insights,
462
+ progressItems,
463
+ progressSummary,
464
+ notableEvents,
465
+ recentActionsV2
466
+ };
467
+ }
468
+ /**
469
+ * Regenerate insights in the background (for stale cache refresh).
470
+ * This runs async and updates the cache when done.
471
+ */
472
+ async regenerateInsightsInBackground(sessionId, existingCache) {
473
+ const traceId = generateTraceId(sessionId);
474
+ const startTime = Date.now();
475
+ const beforeState = buildStateSnapshot(existingCache);
476
+ this.logger.info('[BACKGROUND REGEN] Starting', {
477
+ traceId,
478
+ sessionId: sessionId.slice(0, 8)
479
+ });
480
+ try {
481
+ const insights = await this.computeInsights(sessionId);
482
+ // Cache the result and audit the recompute
483
+ const afterState = buildStateSnapshot(null, insights);
484
+ const changedFields = [];
485
+ if (beforeState.mission !== afterState.mission)
486
+ changedFields.push('mission');
487
+ if (beforeState.description !== afterState.description)
488
+ changedFields.push('description');
489
+ if (beforeState.theme !== afterState.theme)
490
+ changedFields.push('theme');
491
+ if (JSON.stringify(beforeState.milestones) !== JSON.stringify(afterState.milestones))
492
+ changedFields.push('milestones');
493
+ const durationMs = Date.now() - startTime;
494
+ // Audit the recompute
495
+ this.sessionInfoService.auditEventInsight({
496
+ traceId,
497
+ sessionId,
498
+ eventType: 'recompute',
499
+ trigger: 'background_stale_refresh',
500
+ beforeState,
501
+ afterState,
502
+ patchedFields: changedFields,
503
+ durationMs,
504
+ llmResponse: `Opus returned: context=${!!insights.context}, description=${!!insights.description}, milestones=${(insights.milestones || []).length}`,
505
+ });
506
+ await this.cacheInsights(sessionId, insights);
507
+ this.logger.info('[BACKGROUND REGEN] Completed', {
508
+ traceId,
509
+ sessionId: sessionId.slice(0, 8),
510
+ durationMs,
511
+ changedFields
512
+ });
513
+ }
514
+ catch (error) {
515
+ this.logger.warn('[BACKGROUND REGEN] Failed - existing cache preserved', {
516
+ traceId,
517
+ sessionId: sessionId.slice(0, 8),
518
+ error
519
+ });
520
+ // Don't throw - this is a background operation
521
+ // IMPORTANT: We intentionally don't cache anything on failure to preserve existing good data
522
+ }
523
+ }
524
+ /**
525
+ * Compute insights from conversation (bypasses cache)
526
+ */
527
+ async computeInsights(sessionId) {
528
+ const { messages } = await this.historyReader.fetchConversationDirect(sessionId);
529
+ const messageCount = messages.length;
530
+ // Extract todo state
531
+ const todos = this.extractTodoState(messages);
532
+ // Compute progress
533
+ const progress = todos ? this.computeProgress(todos) : null;
534
+ // Get recently completed (last 3)
535
+ const recentlyCompleted = todos
536
+ ? todos
537
+ .filter(t => t.status === 'completed')
538
+ .slice(-3)
539
+ .map(t => t.content)
540
+ : [];
541
+ // Get outstanding tasks
542
+ const outstanding = todos
543
+ ? todos
544
+ .filter(t => t.status === 'pending' || t.status === 'in_progress')
545
+ .map(t => t.content)
546
+ : [];
547
+ // Get current task (in_progress)
548
+ const currentTask = todos
549
+ ? todos.find(t => t.status === 'in_progress')?.content || null
550
+ : null;
551
+ // Generate rich structured insights using Opus
552
+ const structured = await this.generateStructuredInsights(messages, sessionId);
553
+ // Use Opus theme (no heuristic fallback - theme is evaluated on user message)
554
+ const theme = structured.theme || null;
555
+ // Generate brief description for title (backward compat)
556
+ const description = structured.context?.mission || null;
557
+ // Build purposes array from context for backward compatibility
558
+ const purposes = structured.context
559
+ ? [structured.context.mission]
560
+ : [];
561
+ return {
562
+ sessionId,
563
+ context: structured.context,
564
+ tags: structured.tags,
565
+ theme,
566
+ purposes,
567
+ description,
568
+ progress,
569
+ recentlyCompleted,
570
+ outstanding,
571
+ currentTask,
572
+ messageCount,
573
+ // purpose is set by fast patch (Haiku), not full compute (Opus)
574
+ // It will be preserved from existing cache during cacheInsights
575
+ // === Deprecated fields (kept for backward compat with cached data) ===
576
+ // These are no longer generated by LLM - always empty defaults.
577
+ // V8 fields will be converted to V9 equivalents in cacheInsights()
578
+ currentState: null,
579
+ panels: [],
580
+ milestones: structured.milestones, // Always [] from anthropic-service
581
+ notable: structured.notable, // Always [] from anthropic-service
582
+ recentActions: structured.recentActions, // Always [] from anthropic-service
583
+ };
584
+ }
585
+ /**
586
+ * Cache insights to database
587
+ */
588
+ async cacheInsights(sessionId, insights) {
589
+ const now = new Date().toISOString();
590
+ // Preserve existing purpose if new insights don't have one
591
+ // (purpose is set by fast patch/Haiku, not full compute/Opus)
592
+ let purposeToCache = insights.purpose;
593
+ if (!purposeToCache) {
594
+ const existing = await this.sessionInfoService.getInsights(sessionId);
595
+ purposeToCache = existing?.purpose;
596
+ }
597
+ // Initialize V9 progress items from milestones
598
+ const progressItems = (insights.milestones || []).map(m => ({
599
+ label: m.label,
600
+ status: m.status,
601
+ addedAt: now,
602
+ updatedAt: now
603
+ }));
604
+ // Compute progress summary
605
+ const progressSummary = {
606
+ done: progressItems.filter(p => p.status === 'done').length,
607
+ current: progressItems.filter(p => p.status === 'current').length,
608
+ pending: progressItems.filter(p => p.status === 'pending').length,
609
+ paused: 0,
610
+ abandoned: 0,
611
+ total: progressItems.length
612
+ };
613
+ // Initialize V9 notable events from notable
614
+ const notableEvents = (insights.notable || []).map(n => ({
615
+ text: n.text || n.description || '',
616
+ icon: n.icon,
617
+ addedAt: now
618
+ }));
619
+ // Initialize V9 recent actions
620
+ const recentActionsV2 = (insights.recentActions || []).map(text => ({
621
+ text,
622
+ addedAt: now
623
+ }));
624
+ const cached = {
625
+ session_id: sessionId,
626
+ description: insights.description ?? null,
627
+ context: insights.context,
628
+ current_state: insights.currentState ?? null,
629
+ last_patch_trace_id: null,
630
+ purposes: insights.purposes || [],
631
+ milestones: insights.milestones || [],
632
+ notable: insights.notable || [],
633
+ tags: insights.tags,
634
+ recent_actions: insights.recentActions || [],
635
+ panels: insights.panels || [],
636
+ progress_completed: insights.progress?.completed || 0,
637
+ progress_total: insights.progress?.total || 0,
638
+ outstanding_tasks: insights.outstanding || [],
639
+ completed_tasks: insights.recentlyCompleted || [],
640
+ current_task: insights.currentTask ?? null,
641
+ theme: insights.theme,
642
+ computed_at: now,
643
+ stale: false,
644
+ message_count: insights.messageCount,
645
+ // V9 accumulative fields
646
+ progress_items: progressItems,
647
+ notable_events: notableEvents,
648
+ recent_actions_v2: recentActionsV2,
649
+ progress_summary: progressSummary,
650
+ // Evolving purpose (preserved from existing cache if not in new insights)
651
+ purpose: purposeToCache
652
+ };
653
+ await this.sessionInfoService.setInsights(cached);
654
+ // Generate identity image if session doesn't have one yet (fire and forget)
655
+ this.maybeGenerateIdentityImage(sessionId, insights).catch(err => {
656
+ const errorMessage = err instanceof Error ? err.message : String(err);
657
+ this.logger.warn('Failed to generate identity image (background)', {
658
+ sessionId,
659
+ errorMessage,
660
+ errorType: err?.constructor?.name
661
+ });
662
+ });
663
+ }
664
+ /**
665
+ * Generate identity image for a session if it doesn't already have one.
666
+ * This is called after insights are cached/patched, and runs in the background.
667
+ * Public so InsightsCoordinator can call it after patching too.
668
+ */
669
+ async maybeGenerateIdentityImage(sessionId, insights) {
670
+ // Check if session already has an identity image
671
+ const hasImage = await this.sessionInfoService.hasIdentityImage(sessionId);
672
+ if (hasImage) {
673
+ return;
674
+ }
675
+ // Need context to generate meaningful image
676
+ if (!insights.context?.mission) {
677
+ this.logger.debug('Skipping identity image - no mission context', { sessionId });
678
+ return;
679
+ }
680
+ const imageContext = {
681
+ mission: insights.context.mission,
682
+ project: insights.context.project,
683
+ theme: insights.theme ?? undefined,
684
+ workTypes: insights.tags?.workType
685
+ };
686
+ this.logger.info('Generating identity image for session', {
687
+ sessionId,
688
+ imageContext
689
+ });
690
+ try {
691
+ const result = await geminiService.generateSessionImage(imageContext);
692
+ await this.sessionInfoService.setIdentityImage(sessionId, result.imageData);
693
+ this.logger.info('Identity image generated and saved', { sessionId });
694
+ // Emit a follow-up SSE event so clients can update their cache with the new image
695
+ // This solves the race condition where the initial insights SSE is sent before
696
+ // the identity image is generated
697
+ try {
698
+ await getSessionActivityWatcher().emitInsightsUpdate(sessionId, 'patched');
699
+ this.logger.debug('Emitted SSE update for new identity image', { sessionId });
700
+ }
701
+ catch (sseError) {
702
+ this.logger.debug('Failed to emit SSE for identity image', { sessionId, error: sseError });
703
+ }
704
+ }
705
+ catch (error) {
706
+ const errorMessage = error instanceof Error ? error.message : String(error);
707
+ this.logger.warn('Failed to generate identity image', {
708
+ sessionId,
709
+ errorMessage,
710
+ errorType: error?.constructor?.name
711
+ });
712
+ // Don't throw - this is a non-critical background operation
713
+ }
714
+ }
715
+ /**
716
+ * Convert cached insights to SessionInsights format
717
+ */
718
+ cachedToSessionInsights(cached) {
719
+ return {
720
+ sessionId: cached.session_id,
721
+ context: cached.context,
722
+ tags: cached.tags,
723
+ purposes: cached.purposes || [],
724
+ description: cached.description,
725
+ progress: cached.progress_total > 0 ? {
726
+ completed: cached.progress_completed,
727
+ inProgress: 0,
728
+ pending: cached.progress_total - cached.progress_completed,
729
+ total: cached.progress_total,
730
+ percent: Math.round((cached.progress_completed / cached.progress_total) * 100)
731
+ } : null,
732
+ recentlyCompleted: cached.completed_tasks,
733
+ outstanding: cached.outstanding_tasks,
734
+ currentTask: cached.current_task,
735
+ theme: cached.theme,
736
+ computedAt: cached.computed_at,
737
+ patchedAt: cached.patched_at,
738
+ messageCountAtPatch: cached.message_count,
739
+ purpose: cached.purpose,
740
+ // V9 accumulative fields (actively used)
741
+ progressItems: cached.progress_items,
742
+ notableEvents: cached.notable_events,
743
+ recentActionsV2: cached.recent_actions_v2,
744
+ progressSummary: cached.progress_summary,
745
+ // === Deprecated fields (kept for backward compat) ===
746
+ // These may have data from older sessions but are no longer generated
747
+ currentState: null,
748
+ lastPatchTraceId: cached.last_patch_trace_id,
749
+ panels: [],
750
+ milestones: (cached.milestones || []),
751
+ notable: (cached.notable || []),
752
+ recentActions: cached.recent_actions || [],
753
+ };
754
+ }
755
+ /**
756
+ * Get cached insights for multiple sessions (for list view)
757
+ */
758
+ async getCachedInsightsForSessions(sessionIds) {
759
+ const allCached = await this.sessionInfoService.getAllInsights();
760
+ const result = new Map();
761
+ for (const id of sessionIds) {
762
+ const cached = allCached.get(id);
763
+ if (cached) {
764
+ result.set(id, this.cachedToSessionInsights(cached));
765
+ }
766
+ }
767
+ return result;
768
+ }
769
+ /**
770
+ * Compute and cache insights for sessions that don't have cached insights
771
+ */
772
+ async computeMissingInsights(sessionIds, maxConcurrent = 3) {
773
+ const missing = await this.sessionInfoService.getMissingInsightsSessionIds(sessionIds);
774
+ if (missing.length === 0) {
775
+ return 0;
776
+ }
777
+ this.logger.info('Computing missing insights', { count: missing.length });
778
+ // Process in batches to avoid overwhelming the system
779
+ let computed = 0;
780
+ for (let i = 0; i < missing.length; i += maxConcurrent) {
781
+ const batch = missing.slice(i, i + maxConcurrent);
782
+ await Promise.all(batch.map(async (sessionId) => {
783
+ try {
784
+ const insights = await this.computeInsights(sessionId);
785
+ await this.cacheInsights(sessionId, insights);
786
+ computed++;
787
+ }
788
+ catch (error) {
789
+ this.logger.debug('Failed to compute insights for session', { sessionId, error });
790
+ }
791
+ }));
792
+ }
793
+ return computed;
794
+ }
795
+ /**
796
+ * Recompute insights for stale sessions (force refresh)
797
+ * Preserves accumulated milestones/notable from existing cache if new result is empty
798
+ */
799
+ async recomputeStaleInsights(sessionIds, maxConcurrent = 3) {
800
+ if (sessionIds.length === 0) {
801
+ return 0;
802
+ }
803
+ this.logger.info('Recomputing stale insights', { count: sessionIds.length });
804
+ // Process in batches to avoid overwhelming the system
805
+ let computed = 0;
806
+ for (let i = 0; i < sessionIds.length; i += maxConcurrent) {
807
+ const batch = sessionIds.slice(i, i + maxConcurrent);
808
+ await Promise.all(batch.map(async (sessionId) => {
809
+ const traceId = generateTraceId(sessionId);
810
+ const startTime = Date.now();
811
+ try {
812
+ // Get existing cache BEFORE regenerating - we may need to preserve accumulated state
813
+ const existingCache = await this.sessionInfoService.getInsights(sessionId);
814
+ const beforeState = buildStateSnapshot(existingCache);
815
+ this.logger.info('Computing insights for session', { traceId, sessionId });
816
+ const insights = await this.computeInsights(sessionId);
817
+ // Preserve accumulated milestones/notable if new result came back empty
818
+ // This handles cases where the LLM regeneration doesn't capture the full history
819
+ // Note: These V8 fields are deprecated and always [] for new sessions,
820
+ // but old cached data may have values we want to preserve during recompute.
821
+ if (existingCache) {
822
+ const existingMilestones = existingCache.milestones || [];
823
+ const existingNotable = existingCache.notable || [];
824
+ // If new milestones are empty but we had some before, preserve them
825
+ if ((insights.milestones || []).length === 0 && existingMilestones.length > 0) {
826
+ this.logger.info('Preserving accumulated milestones from cache', {
827
+ traceId,
828
+ sessionId,
829
+ preservedCount: existingMilestones.length
830
+ });
831
+ insights.milestones = existingMilestones;
832
+ }
833
+ // If new notable events are empty but we had some before, preserve them
834
+ if ((insights.notable || []).length === 0 && existingNotable.length > 0) {
835
+ this.logger.info('Preserving accumulated notable events from cache', {
836
+ traceId,
837
+ sessionId,
838
+ preservedCount: existingNotable.length
839
+ });
840
+ insights.notable = existingNotable;
841
+ }
842
+ }
843
+ await this.cacheInsights(sessionId, insights);
844
+ computed++;
845
+ // Build after state and determine what changed
846
+ const afterState = buildStateSnapshot(null, insights);
847
+ const changedFields = [];
848
+ if (beforeState.mission !== afterState.mission)
849
+ changedFields.push('mission');
850
+ if (beforeState.description !== afterState.description)
851
+ changedFields.push('description');
852
+ if (beforeState.theme !== afterState.theme)
853
+ changedFields.push('theme');
854
+ if (JSON.stringify(beforeState.milestones) !== JSON.stringify(afterState.milestones))
855
+ changedFields.push('milestones');
856
+ const durationMs = Date.now() - startTime;
857
+ // Audit the recompute with full before/after state
858
+ this.sessionInfoService.auditEventInsight({
859
+ traceId,
860
+ sessionId,
861
+ eventType: 'recompute',
862
+ trigger: 'stale_refresh',
863
+ beforeState,
864
+ afterState,
865
+ patchedFields: changedFields,
866
+ durationMs,
867
+ llmResponse: `Opus returned: context=${!!insights.context}, description=${!!insights.description}, milestones=${(insights.milestones || []).length}`,
868
+ });
869
+ this.logger.info('Insights computed successfully', {
870
+ traceId,
871
+ sessionId,
872
+ milestones: (insights.milestones || []).length,
873
+ notable: (insights.notable || []).length,
874
+ changedFields,
875
+ durationMs,
876
+ });
877
+ }
878
+ catch (error) {
879
+ this.logger.warn('Failed to compute insights for session', { traceId, sessionId, error });
880
+ }
881
+ }));
882
+ }
883
+ return computed;
884
+ }
885
+ /**
886
+ * Get insights without AI generation (faster, for list views)
887
+ */
888
+ async getInsightsQuick(sessionId) {
889
+ // Check cache first - return cached AI insights if available
890
+ const cached = await this.sessionInfoService.getInsights(sessionId);
891
+ if (cached) {
892
+ return this.cachedToSessionInsights(cached);
893
+ }
894
+ // No cached insights - fall back to local extraction (no AI call)
895
+ const { messages } = await this.historyReader.fetchConversationDirect(sessionId);
896
+ const todos = this.extractTodoState(messages);
897
+ const progress = todos ? this.computeProgress(todos) : null;
898
+ const recentlyCompleted = todos
899
+ ? todos
900
+ .filter(t => t.status === 'completed')
901
+ .slice(-3)
902
+ .map(t => t.content)
903
+ : [];
904
+ const outstanding = todos
905
+ ? todos
906
+ .filter(t => t.status === 'pending' || t.status === 'in_progress')
907
+ .map(t => t.content)
908
+ : [];
909
+ const currentTask = todos
910
+ ? todos.find(t => t.status === 'in_progress')?.content || null
911
+ : null;
912
+ // No theme in quick mode - will be set on user message
913
+ return {
914
+ sessionId,
915
+ context: null,
916
+ tags: null,
917
+ purposes: [],
918
+ description: null, // Skip AI generation for quick mode
919
+ progress,
920
+ recentlyCompleted,
921
+ outstanding,
922
+ currentTask,
923
+ theme: null,
924
+ // Deprecated fields (empty defaults)
925
+ currentState: null,
926
+ panels: [],
927
+ milestones: [],
928
+ notable: [],
929
+ recentActions: [],
930
+ };
931
+ }
932
+ }
933
+ // Legacy alias for backward compatibility during migration
934
+ /** @deprecated Use InsightsComputer instead */
935
+ export const SessionInsightsService = InsightsComputer;
936
+ //# sourceMappingURL=insights-computer.js.map