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,1062 @@
1
+ import Anthropic from '@anthropic-ai/sdk';
2
+ import { LatticeError, } from '../../types/index.js';
3
+ import { createLogger } from '../../services/infrastructure/logger.js';
4
+ import { ConfigService } from '../infrastructure/config-service.js';
5
+ import { getCostTracker } from '../infrastructure/cost-tracker.js';
6
+ const DEFAULT_RETRY_CONFIG = {
7
+ maxRetries: 3,
8
+ baseDelayMs: 1000,
9
+ maxDelayMs: 10000,
10
+ };
11
+ /**
12
+ * Determines if an error is retryable (transient network/rate limit issues).
13
+ * Non-retryable: auth errors, credit exhaustion, invalid requests.
14
+ */
15
+ function isRetryableError(error) {
16
+ const message = error instanceof Error ? error.message : String(error);
17
+ const errorName = error instanceof Error ? error.name : '';
18
+ // Non-retryable errors - don't waste retries on these
19
+ if (message.includes('credit balance') || message.includes('billing'))
20
+ return false;
21
+ if (message.includes('invalid_api_key') || message.includes('authentication'))
22
+ return false;
23
+ if (message.includes('invalid_request_error'))
24
+ return false;
25
+ // Retryable: rate limits, overloaded, server errors, network issues
26
+ if (message.includes('rate_limit') || message.includes('overloaded'))
27
+ return true;
28
+ if (message.includes('529') || message.includes('503') || message.includes('500'))
29
+ return true;
30
+ if (errorName === 'APIConnectionError' || message.includes('ECONNRESET'))
31
+ return true;
32
+ if (errorName === 'RateLimitError' || errorName === 'InternalServerError')
33
+ return true;
34
+ // Default: retry on unknown errors (might be transient)
35
+ return true;
36
+ }
37
+ /**
38
+ * Calculates exponential backoff delay with jitter.
39
+ */
40
+ function calculateBackoff(attempt, config) {
41
+ const exponentialDelay = config.baseDelayMs * Math.pow(2, attempt);
42
+ const jitter = Math.random() * 0.3 * exponentialDelay; // 0-30% jitter
43
+ return Math.min(exponentialDelay + jitter, config.maxDelayMs);
44
+ }
45
+ /**
46
+ * Sleep for a specified duration.
47
+ */
48
+ function sleep(ms) {
49
+ return new Promise(resolve => setTimeout(resolve, ms));
50
+ }
51
+ // ============================================================================
52
+ // Model Configuration
53
+ // ============================================================================
54
+ // Centralized model IDs - update these when Anthropic releases new versions.
55
+ // These are defaults; can be overridden via config.anthropic.models.*
56
+ const DEFAULT_MODELS = {
57
+ /** Full insight generation - Sonnet balances quality/cost well for structured extraction */
58
+ generation: 'claude-sonnet-4-5-20250929',
59
+ /** Quick staleness checks - needs speed over capability */
60
+ quickCheck: 'claude-haiku-4-5-20251001',
61
+ /** Fast patch generation - speed over quality */
62
+ patch: 'claude-haiku-4-5-20251001',
63
+ /** Full patch generation (fallback) - for complex patches when fast path fails */
64
+ fullPatch: 'claude-haiku-4-5-20251001',
65
+ /** Proposition extraction - factual extraction task */
66
+ propositions: 'claude-haiku-4-5-20251001',
67
+ };
68
+ export class AnthropicService {
69
+ logger;
70
+ client = null;
71
+ // Model configuration - populated from config or defaults
72
+ // Uses explicit mutable type rather than `typeof DEFAULT_MODELS` which is readonly due to `as const`
73
+ models;
74
+ constructor() {
75
+ this.logger = createLogger('AnthropicService');
76
+ this.models = { ...DEFAULT_MODELS };
77
+ }
78
+ async initialize() {
79
+ const config = ConfigService.getInstance().getConfig();
80
+ const apiKey = config.anthropic?.apiKey || process.env.ANTHROPIC_API_KEY;
81
+ if (!apiKey) {
82
+ this.logger.warn('Anthropic API key not configured');
83
+ return;
84
+ }
85
+ try {
86
+ this.client = new Anthropic({ apiKey });
87
+ // Load model overrides from config
88
+ const configModels = config.anthropic?.models;
89
+ if (configModels) {
90
+ if (configModels.generation)
91
+ this.models.generation = configModels.generation;
92
+ if (configModels.quickCheck)
93
+ this.models.quickCheck = configModels.quickCheck;
94
+ if (configModels.patch)
95
+ this.models.patch = configModels.patch;
96
+ if (configModels.fullPatch)
97
+ this.models.fullPatch = configModels.fullPatch;
98
+ if (configModels.propositions)
99
+ this.models.propositions = configModels.propositions;
100
+ }
101
+ // Also support legacy config.anthropic.model for generation
102
+ if (config.anthropic?.model) {
103
+ this.models.generation = config.anthropic.model;
104
+ }
105
+ this.logger.info('Anthropic service initialized', { models: this.models });
106
+ }
107
+ catch (error) {
108
+ this.logger.error('Failed to initialize Anthropic service', { error });
109
+ throw new LatticeError('ANTHROPIC_INIT_ERROR', 'Failed to initialize Anthropic service', 500);
110
+ }
111
+ }
112
+ /**
113
+ * Execute a function with exponential backoff retry on transient errors.
114
+ */
115
+ async withRetry(operation, fn, config = DEFAULT_RETRY_CONFIG) {
116
+ let lastError;
117
+ for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
118
+ try {
119
+ return await fn();
120
+ }
121
+ catch (error) {
122
+ lastError = error;
123
+ // Don't retry non-retryable errors
124
+ if (!isRetryableError(error)) {
125
+ throw error;
126
+ }
127
+ // Don't retry after max attempts
128
+ if (attempt >= config.maxRetries) {
129
+ this.logger.warn(`${operation}: All ${config.maxRetries} retries exhausted`, {
130
+ error: error instanceof Error ? error.message : String(error)
131
+ });
132
+ throw error;
133
+ }
134
+ const delayMs = calculateBackoff(attempt, config);
135
+ this.logger.info(`${operation}: Retry ${attempt + 1}/${config.maxRetries} after ${Math.round(delayMs)}ms`, {
136
+ error: error instanceof Error ? error.message : String(error)
137
+ });
138
+ await sleep(delayMs);
139
+ }
140
+ }
141
+ throw lastError;
142
+ }
143
+ async checkHealth() {
144
+ if (!this.client) {
145
+ return {
146
+ status: 'unhealthy',
147
+ message: 'Anthropic API key not configured',
148
+ apiKeyValid: false
149
+ };
150
+ }
151
+ try {
152
+ const response = await this.withRetry('checkHealth', () => this.client.messages.create({
153
+ model: this.models.generation,
154
+ max_tokens: 50,
155
+ messages: [{ role: 'user', content: 'Say "ok" and nothing else.' }]
156
+ }));
157
+ const text = response.content[0]?.type === 'text' ? response.content[0].text : null;
158
+ if (text) {
159
+ return {
160
+ status: 'healthy',
161
+ message: 'Anthropic API is accessible',
162
+ apiKeyValid: true
163
+ };
164
+ }
165
+ return {
166
+ status: 'unhealthy',
167
+ message: 'Unexpected response from Anthropic API',
168
+ apiKeyValid: true
169
+ };
170
+ }
171
+ catch (error) {
172
+ this.logger.error('Health check failed', { error });
173
+ return {
174
+ status: 'unhealthy',
175
+ message: error instanceof Error ? error.message : 'Unknown error',
176
+ apiKeyValid: false
177
+ };
178
+ }
179
+ }
180
+ /**
181
+ * Log cost for an LLM API call.
182
+ * Call this after each API call to track costs.
183
+ */
184
+ logCost(response, operation, model, durationMs, sessionId) {
185
+ try {
186
+ const costTracker = getCostTracker();
187
+ costTracker.log({
188
+ sessionId: sessionId || 'unknown',
189
+ operation,
190
+ model,
191
+ inputTokens: response.usage?.input_tokens || 0,
192
+ outputTokens: response.usage?.output_tokens || 0,
193
+ durationMs,
194
+ });
195
+ }
196
+ catch (error) {
197
+ // Don't let cost logging failures break the main flow
198
+ this.logger.debug('Failed to log cost', { error, operation });
199
+ }
200
+ }
201
+ /**
202
+ * Extract rich, structured session insights using Opus 4.5
203
+ */
204
+ async extractSessionInsights(conversationText, sessionId) {
205
+ if (!this.client) {
206
+ throw new LatticeError('ANTHROPIC_API_KEY_MISSING', 'Anthropic API key not configured', 400);
207
+ }
208
+ const systemPrompt = `You are analyzing a coding session transcript to extract structured information for a dashboard display.
209
+
210
+ The goal: Someone glancing at this dashboard should immediately understand what this session is about.
211
+
212
+ Extract the following:
213
+
214
+ 1. CONTEXT - Identity of the session:
215
+ - project: The codebase/repo name
216
+ - area: The specific component/module/domain if applicable (null if general)
217
+ - mission: The overarching goal in one sentence - be specific
218
+ - scope: "minor" (quick fix), "feature" (meaningful addition), "major" (significant change)
219
+
220
+ 2. THEME - A single word capturing the session's current mode/vibe:
221
+ Examples: "debugging", "building", "exploring", "firefighting", "refactoring", "designing", "investigating", "polishing"
222
+
223
+ 3. TAGS - Quick categorization for filtering/display:
224
+ - workType: Array of what's happening - pick ONLY from: "implementing", "debugging", "refactoring", "exploring", "designing", "investigating"
225
+ (implementing=writing new code, debugging=fixing bugs, refactoring=restructuring, exploring=reading/understanding, designing=planning architecture, investigating=diagnosing/researching)
226
+ - domain: Array of technical areas - e.g., "UI", "backend", "database", "API", "infra", "auth", "testing"
227
+ - collaboration: How the session is proceeding - "autonomous" (Claude working independently), "iterative" (back-and-forth), "guided" (user directing each step)
228
+ - complexity: Session difficulty - "routine" (straightforward), "tricky" (requires care), "gnarly" (complex/hairy - reserved for multi-system debugging or architecture-spanning changes)
229
+
230
+ CRITICAL OUTPUT INSTRUCTIONS:
231
+ - Respond with ONLY valid JSON
232
+ - Do NOT include explanatory text before or after the JSON
233
+ - Do NOT include markdown code blocks or formatting
234
+ - Start your response with { and end with }
235
+ - No prose, no commentary, ONLY the JSON object
236
+
237
+ JSON Structure:
238
+ {
239
+ "context": { "project": "string", "area": "string|null", "mission": "string", "scope": "minor|feature|major" },
240
+ "theme": "string",
241
+ "tags": { "workType": ["string", ...], "domain": ["string", ...], "collaboration": "autonomous|iterative|guided", "complexity": "routine|tricky|gnarly" }
242
+ }`;
243
+ let responseText = null;
244
+ const startTime = Date.now();
245
+ try {
246
+ const response = await this.withRetry('extractSessionInsights', () => this.client.messages.create({
247
+ model: this.models.generation,
248
+ max_tokens: 500, // Reduced - only extracting context, theme, tags now
249
+ system: systemPrompt,
250
+ messages: [
251
+ {
252
+ role: 'user',
253
+ content: `Analyze this coding session and extract structured insights:\n\n${conversationText}`
254
+ },
255
+ {
256
+ role: 'assistant',
257
+ content: '{' // Prefill to force JSON output
258
+ }
259
+ ]
260
+ }));
261
+ const durationMs = Date.now() - startTime;
262
+ // Log cost for GENERATE operation
263
+ this.logCost(response, 'GENERATE', this.models.generation, durationMs, sessionId);
264
+ responseText = response.content[0]?.type === 'text' ? response.content[0].text : null;
265
+ if (!responseText) {
266
+ throw new LatticeError('ANTHROPIC_INSIGHTS_ERROR', 'No response text returned', 500);
267
+ }
268
+ // Prepend the '{' we used for prefill
269
+ responseText = '{' + responseText;
270
+ // Extract JSON from response with multiple fallback strategies
271
+ let jsonText = responseText.trim();
272
+ // Strategy 1: Try to extract from markdown code blocks
273
+ const codeBlockMatch = responseText.match(/```(?:json)?\s*([\s\S]*?)```/);
274
+ if (codeBlockMatch) {
275
+ jsonText = codeBlockMatch[1].trim();
276
+ }
277
+ else {
278
+ // Strategy 2: Look for content between first { and last }
279
+ const firstBrace = jsonText.indexOf('{');
280
+ const lastBrace = jsonText.lastIndexOf('}');
281
+ if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) {
282
+ jsonText = jsonText.substring(firstBrace, lastBrace + 1);
283
+ }
284
+ }
285
+ // Log the extracted JSON for debugging
286
+ this.logger.debug('Extracted JSON from LLM response', {
287
+ responsePreview: responseText.slice(0, 100),
288
+ extractedPreview: jsonText.slice(0, 100),
289
+ usedCodeBlock: !!codeBlockMatch
290
+ });
291
+ const result = JSON.parse(jsonText);
292
+ // Validate structure - be lenient, provide defaults
293
+ if (!result.context) {
294
+ throw new LatticeError('ANTHROPIC_INSIGHTS_ERROR', 'Invalid response structure', 500);
295
+ }
296
+ // Theme and tags come from the LLM prompt
297
+ result.theme = result.theme || 'working';
298
+ result.tags = result.tags || null;
299
+ // These fields are deprecated and no longer requested in the LLM prompt.
300
+ // They're set to empty defaults for type compatibility.
301
+ // V8 fields (milestones, notable, recentActions) were removed from prompt
302
+ // but types still require them for backward compat with cached data.
303
+ result.milestones = [];
304
+ result.notable = [];
305
+ result.recentActions = [];
306
+ result.currentState = null;
307
+ result.panels = [];
308
+ this.logger.info('Session insights extracted (Opus)', {
309
+ model: this.models.generation,
310
+ inputTokens: response.usage?.input_tokens,
311
+ outputTokens: response.usage?.output_tokens,
312
+ project: result.context.project,
313
+ theme: result.theme
314
+ });
315
+ return result;
316
+ }
317
+ catch (error) {
318
+ if (error instanceof LatticeError) {
319
+ throw error;
320
+ }
321
+ // Check for credit/billing errors and make them LOUD
322
+ const errorMessage = error instanceof Error ? error.message : String(error);
323
+ if (errorMessage.includes('credit balance') || errorMessage.includes('billing')) {
324
+ this.logger.error('🚨 ANTHROPIC API CREDITS EXHAUSTED 🚨', {
325
+ errorMessage,
326
+ action: 'ADD CREDITS AT https://console.anthropic.com/settings/billing'
327
+ });
328
+ throw new LatticeError('ANTHROPIC_NO_CREDITS', '🚨 Anthropic API credits exhausted. Add credits at https://console.anthropic.com/settings/billing', 402);
329
+ }
330
+ // Log error with proper serialization AND the response that failed to parse
331
+ this.logger.error('Session insights extraction failed', {
332
+ errorMessage,
333
+ errorStack: error instanceof Error ? error.stack : undefined,
334
+ errorType: error?.constructor?.name,
335
+ // Include the actual response for debugging
336
+ responsePreview: responseText?.slice(0, 500) || 'no response'
337
+ });
338
+ throw new LatticeError('ANTHROPIC_INSIGHTS_ERROR', 'Failed to extract session insights', 500);
339
+ }
340
+ }
341
+ /**
342
+ * Generate a session name (simpler extraction for naming)
343
+ */
344
+ async generateSessionName(conversationText) {
345
+ if (!this.client) {
346
+ throw new LatticeError('ANTHROPIC_API_KEY_MISSING', 'Anthropic API key not configured', 400);
347
+ }
348
+ try {
349
+ const response = await this.withRetry('generateSessionName', () => this.client.messages.create({
350
+ model: this.models.generation,
351
+ max_tokens: 100,
352
+ messages: [{
353
+ role: 'user',
354
+ content: `Generate a brief, descriptive name (3-6 words) for this coding session. Include the project/area context. Focus on the main task or feature being worked on. Do not include generic words like "session", "conversation", or "chat".
355
+
356
+ Examples of good names:
357
+ - "CUI: Session insights UI redesign"
358
+ - "Auth: Fix redirect loop bug"
359
+ - "API: Add rate limiting middleware"
360
+
361
+ Respond with ONLY the name, nothing else.
362
+
363
+ Conversation:
364
+ ${conversationText}`
365
+ }]
366
+ }));
367
+ const name = response.content[0]?.type === 'text' ? response.content[0].text.trim() : null;
368
+ if (!name) {
369
+ throw new LatticeError('ANTHROPIC_NAME_ERROR', 'No name returned', 500);
370
+ }
371
+ return { name };
372
+ }
373
+ catch (error) {
374
+ if (error instanceof LatticeError) {
375
+ throw error;
376
+ }
377
+ this.logger.error('Session name generation failed', { error });
378
+ throw new LatticeError('ANTHROPIC_NAME_ERROR', 'Failed to generate session name', 500);
379
+ }
380
+ }
381
+ isConfigured() {
382
+ return this.client !== null;
383
+ }
384
+ /**
385
+ * Quick check using Haiku to determine if insights need patching.
386
+ *
387
+ * This is a fast, cheap call that just answers "has anything meaningful changed?"
388
+ * based on recent actions. If yes, we'll follow up with Sonnet for actual patching.
389
+ */
390
+ async quickCheckInsightsStale(currentInsights, recentActions, sessionId) {
391
+ if (!this.client) {
392
+ return { needsPatch: false, reason: 'Anthropic not configured' };
393
+ }
394
+ const startTime = Date.now();
395
+ try {
396
+ const milestonesStr = currentInsights.milestones
397
+ .map(m => `[${m.status}] ${m.label}`)
398
+ .join(', ');
399
+ const actionsStr = recentActions.join('\n');
400
+ const prompt = `Current session insights (a living summary of the conversation):
401
+ - Mission: "${currentInsights.mission}"
402
+ - Milestones: ${milestonesStr || '(none)'}
403
+
404
+ Recent activity:
405
+ ${actionsStr}
406
+
407
+ Should these insights be updated to reflect the recent activity?
408
+
409
+ Say YES (needsPatch: true) if ANY of these apply:
410
+ - Progress was made on any task or milestone
411
+ - Focus shifted to a new area or topic
412
+ - Something notable happened worth capturing
413
+ - The summary feels stale compared to what just happened
414
+
415
+ Say NO only if the recent actions are trivial or already reflected in current insights.
416
+
417
+ Err on the side of updating - the insights should feel LIVE and current.
418
+
419
+ Respond with JSON only:
420
+ {"needsPatch": true/false, "reason": "brief explanation"}`;
421
+ const response = await this.withRetry('quickCheckInsightsStale', () => this.client.messages.create({
422
+ model: this.models.quickCheck,
423
+ max_tokens: 100,
424
+ messages: [{ role: 'user', content: prompt }]
425
+ }));
426
+ const duration = Date.now() - startTime;
427
+ const text = response.content[0]?.type === 'text' ? response.content[0].text : '';
428
+ // Log cost for QUICK_CHECK operation
429
+ this.logCost(response, 'QUICK_CHECK', this.models.quickCheck, duration, sessionId);
430
+ // Log for observability
431
+ this.logger.info('Quick check (Haiku) completed', {
432
+ durationMs: duration,
433
+ inputTokens: response.usage?.input_tokens,
434
+ outputTokens: response.usage?.output_tokens,
435
+ recentActionsCount: recentActions.length,
436
+ responsePreview: text.slice(0, 100)
437
+ });
438
+ try {
439
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
440
+ const result = JSON.parse(jsonMatch?.[0] || text);
441
+ return {
442
+ needsPatch: result.needsPatch === true,
443
+ reason: result.reason || 'No reason provided'
444
+ };
445
+ }
446
+ catch {
447
+ this.logger.debug('Failed to parse quick check response', { text });
448
+ return { needsPatch: false, reason: 'Parse error - keeping cached' };
449
+ }
450
+ }
451
+ catch (error) {
452
+ // Check for credit/billing errors and make them LOUD
453
+ const errorMessage = error instanceof Error ? error.message : String(error);
454
+ if (errorMessage.includes('credit balance') || errorMessage.includes('billing')) {
455
+ this.logger.error('🚨 ANTHROPIC API CREDITS EXHAUSTED 🚨', {
456
+ errorMessage,
457
+ action: 'ADD CREDITS AT https://console.anthropic.com/settings/billing',
458
+ context: 'Quick check failed due to billing issue'
459
+ });
460
+ }
461
+ else {
462
+ this.logger.error('Quick check failed', { error });
463
+ }
464
+ return { needsPatch: false, reason: 'Check error - keeping cached' };
465
+ }
466
+ }
467
+ /**
468
+ * Decompose recent activity into exhaustive propositional facts.
469
+ * Separates "what happened" (facts) from "what does this mean" (interpretation).
470
+ * Returns structured bullet points of all tool uses, user messages, and Claude messages.
471
+ */
472
+ async decomposeActivityIntoPropositions(previousState, recentActivity, sessionId) {
473
+ if (!this.client) {
474
+ return [];
475
+ }
476
+ const previousStateStr = previousState
477
+ ? `${previousState.icon} [${previousState.color}] ${previousState.content}`
478
+ : '(no previous state)';
479
+ const activityStr = recentActivity
480
+ .map(a => {
481
+ // Format timestamp as HH:MM:SS for readability
482
+ const timeStr = a.timestamp
483
+ ? new Date(a.timestamp).toLocaleTimeString('en-US', { hour12: false })
484
+ : '';
485
+ const prefix = timeStr ? `[${timeStr}] ` : '';
486
+ return `${prefix}[${a.type}] ${a.content}`;
487
+ })
488
+ .join('\n\n');
489
+ const prompt = `You are analyzing a transcript of a coding session between a user and Claude (an AI assistant).
490
+
491
+ **Previous Current State:**
492
+ ${previousStateStr}
493
+
494
+ **Recent Activity:**
495
+ ${activityStr}
496
+
497
+ **Your Task:**
498
+ Create an exhaustive list of propositional facts about what happened in this activity.
499
+ Be thorough and explicit - list EVERYTHING that was done and said.
500
+
501
+ Format as bullet points:
502
+ - Tool uses: "Claude used [tool] to [action]"
503
+ - Claude statements: "Claude said/stated/explained [fact]"
504
+ - User messages: "User asked/requested/said [fact]"
505
+
506
+ Be exhaustive - every action, every statement, every request should become a bullet point.
507
+ Keep each bullet point factual and specific, not interpretive.
508
+
509
+ Examples:
510
+ ✓ "Claude used Grep to search for 'validateUser' in the codebase"
511
+ ✓ "Claude stated that the bug was caused by missing null checks"
512
+ ✓ "Claude said the fix is ready to test after server restart"
513
+ ✓ "User asked about the performance impact"
514
+ ✗ "Claude made progress on the bug" (too vague)
515
+ ✗ "The session is going well" (interpretive, not factual)
516
+
517
+ Return ONLY a JSON array of strings, one per proposition:
518
+ ["proposition 1", "proposition 2", ...]`;
519
+ let rawResponse = '';
520
+ const startTime = Date.now();
521
+ try {
522
+ const response = await this.withRetry('decomposeActivityIntoPropositions', () => this.client.messages.create({
523
+ model: this.models.propositions, // Use Haiku - faster for factual extraction
524
+ max_tokens: 2000,
525
+ temperature: 0,
526
+ messages: [{
527
+ role: 'user',
528
+ content: prompt
529
+ }]
530
+ }));
531
+ const durationMs = Date.now() - startTime;
532
+ // Log cost for PROPOSITIONS operation (Haiku)
533
+ this.logCost(response, 'PROPOSITIONS', this.models.propositions, durationMs, sessionId);
534
+ const textContent = response.content.find(block => block.type === 'text');
535
+ if (!textContent || textContent.type !== 'text') {
536
+ return [];
537
+ }
538
+ rawResponse = textContent.text;
539
+ let jsonText = textContent.text.trim();
540
+ // Strip markdown code blocks if present (```json ... ```)
541
+ if (jsonText.startsWith('```')) {
542
+ jsonText = jsonText.replace(/^```(?:json)?\n?/, '').replace(/\n?```$/, '').trim();
543
+ }
544
+ // Strip markdown headings and prose before JSON (e.g., "# Summary\nHere are...")
545
+ // Always try to extract JSON array first, even if text looks like it starts with [
546
+ const jsonMatch = jsonText.match(/\[[\s\S]*?\](?=\s*$)/);
547
+ if (jsonMatch) {
548
+ jsonText = jsonMatch[0];
549
+ }
550
+ else if (!jsonText.startsWith('[')) {
551
+ // No JSON array found and doesn't start with [
552
+ this.logger.debug('Decomposition response is not JSON', { preview: jsonText.slice(0, 200) });
553
+ return [];
554
+ }
555
+ const propositions = JSON.parse(jsonText);
556
+ // Log token usage for cost tracking
557
+ this.logger.info('Propositional decomposition (Haiku)', {
558
+ model: this.models.propositions,
559
+ inputTokens: response.usage?.input_tokens,
560
+ outputTokens: response.usage?.output_tokens,
561
+ propositionCount: Array.isArray(propositions) ? propositions.length : 0,
562
+ activityCount: recentActivity.length
563
+ });
564
+ return Array.isArray(propositions) ? propositions : [];
565
+ }
566
+ catch (error) {
567
+ this.logger.debug('Failed to decompose activity into propositions', {
568
+ error: error instanceof Error ? error.message : String(error),
569
+ responsePreview: rawResponse.slice(0, 200),
570
+ });
571
+ return [];
572
+ }
573
+ }
574
+ /**
575
+ * Generate fast patch for session purpose only.
576
+ *
577
+ * Theme/tags are now evaluated on user message (evaluateSessionMetadata),
578
+ * so this method only updates the evolving session purpose based on
579
+ * what Claude actually did (vs what user asked for).
580
+ */
581
+ async generateFastPatch(currentInsights, propositions, _recentActivity, sessionId) {
582
+ if (!this.client || propositions.length === 0) {
583
+ return null;
584
+ }
585
+ const missionStr = currentInsights.mission || '(not set)';
586
+ const purposeStr = currentInsights.purpose || missionStr;
587
+ const propositionsStr = propositions.map((p, i) => `${i + 1}. ${p}`).join('\n');
588
+ // Simplified prompt - only evaluate purpose, not theme/tags
589
+ // Theme/tags are now evaluated on user message to prevent flip-flopping
590
+ const prompt = `You are updating a coding session dashboard based on recent activity.
591
+
592
+ ORIGINAL SESSION MISSION (frozen at start):
593
+ ${missionStr}
594
+
595
+ CURRENT PURPOSE (what session has evolved to):
596
+ ${purposeStr}
597
+
598
+ RECENT ACTIVITY (propositional facts):
599
+ ${propositionsStr}
600
+
601
+ Based on what was just accomplished, update the session purpose:
602
+
603
+ **purpose** (30-50 chars): What is this session about NOW?
604
+ - The session's current identity/focus
605
+ - Keep concise: "Building auth system" not "We are currently..."
606
+ - May match the original mission, or may have evolved
607
+
608
+ Respond with ONLY valid JSON:
609
+ {"purpose": "current session focus"}`;
610
+ let rawResponse = '';
611
+ const startTime = Date.now();
612
+ try {
613
+ const response = await this.withRetry('generateFastPatch', () => this.client.messages.create({
614
+ model: this.models.patch,
615
+ max_tokens: 100, // Reduced - only extracting purpose now
616
+ temperature: 0,
617
+ messages: [{
618
+ role: 'user',
619
+ content: prompt
620
+ }]
621
+ }));
622
+ const durationMs = Date.now() - startTime;
623
+ // Log cost for FAST_PATCH operation (Haiku)
624
+ this.logCost(response, 'FAST_PATCH', this.models.patch, durationMs, sessionId);
625
+ const textContent = response.content.find(block => block.type === 'text');
626
+ if (!textContent || textContent.type !== 'text') {
627
+ return null;
628
+ }
629
+ rawResponse = textContent.text;
630
+ let jsonText = textContent.text.trim();
631
+ // Strip markdown code blocks if present
632
+ if (jsonText.startsWith('```')) {
633
+ jsonText = jsonText.replace(/^```(?:json)?\n?/, '').replace(/\n?```$/, '').trim();
634
+ }
635
+ // Extract JSON object
636
+ const jsonMatch = jsonText.match(/\{[\s\S]*\}(?=\s*$)/);
637
+ if (jsonMatch) {
638
+ jsonText = jsonMatch[0];
639
+ }
640
+ else if (!jsonText.startsWith('{')) {
641
+ this.logger.debug('FastPatch response is not JSON', { preview: jsonText.slice(0, 200) });
642
+ return null;
643
+ }
644
+ const result = JSON.parse(jsonText);
645
+ // Validate structure - purpose is required
646
+ if (!result.purpose || typeof result.purpose !== 'string') {
647
+ this.logger.debug('FastPatch missing purpose', { keys: Object.keys(result) });
648
+ return null;
649
+ }
650
+ return {
651
+ milestones: [],
652
+ recentActions: [],
653
+ purpose: result.purpose,
654
+ };
655
+ }
656
+ catch (error) {
657
+ this.logger.debug('FastPatch generation failed', {
658
+ error: error instanceof Error ? error.message : String(error),
659
+ responsePreview: rawResponse.slice(0, 200),
660
+ });
661
+ return null;
662
+ }
663
+ }
664
+ /**
665
+ * Generate targeted patches to insights using Sonnet.
666
+ *
667
+ * Called when quickCheckInsightsStale returns needsPatch=true.
668
+ * Returns specific patches to apply rather than regenerating everything.
669
+ *
670
+ * @param useFastPath - If true, only generate currentState using fast Haiku-only pipeline (~5s vs ~12s)
671
+ */
672
+ async generateInsightsPatch(currentInsights, recentActivity, useFastPath = true, // Default to fast path for better UX
673
+ sessionId) {
674
+ if (!this.client) {
675
+ return { patches: {}, reason: 'Anthropic not configured', propositions: [] };
676
+ }
677
+ const startTime = Date.now();
678
+ try {
679
+ // Step 1: Decompose activity into propositional facts
680
+ const decompositionStart = Date.now();
681
+ const propositions = await this.decomposeActivityIntoPropositions(null, recentActivity, sessionId);
682
+ const decompositionTime = Date.now() - decompositionStart;
683
+ this.logger.debug('Propositional decomposition completed', {
684
+ durationMs: decompositionTime,
685
+ propositionCount: propositions.length,
686
+ });
687
+ // Fast path: Generate purpose, theme, tags using Haiku (~1-2s vs ~12s full patch)
688
+ // This is the "Haiku-heavy" strategy from Architecture V2
689
+ if (useFastPath) {
690
+ this.logger.debug('Using fast path for patch generation');
691
+ const fastPatch = await this.generateFastPatch({
692
+ milestones: currentInsights.milestones,
693
+ recentActions: currentInsights.recentActions,
694
+ notable: currentInsights.notable, // For deduplication
695
+ mission: currentInsights.mission, // For pivot detection
696
+ purpose: currentInsights.purpose, // Current evolving purpose
697
+ theme: currentInsights.theme, // Current theme for re-evaluation
698
+ tags: currentInsights.tags, // Current tags for re-evaluation
699
+ }, propositions, recentActivity, // Pass activity for intent extraction (Strategy 3)
700
+ sessionId);
701
+ const totalTime = Date.now() - startTime;
702
+ if (fastPatch && fastPatch.purpose) {
703
+ this.logger.info('Fast path patch completed', {
704
+ durationMs: totalTime,
705
+ purpose: fastPatch.purpose,
706
+ });
707
+ return {
708
+ patches: { purpose: fastPatch.purpose },
709
+ reason: `Fast path: Updated purpose based on propositional analysis`,
710
+ propositions
711
+ };
712
+ }
713
+ // Fall through to full path if fast path fails
714
+ this.logger.debug('Fast path returned null, falling back to full patch generation');
715
+ }
716
+ // Use propositions if available, fallback to raw activity
717
+ const activityStr = propositions.length > 0
718
+ ? propositions.map(p => `• ${p}`).join('\n')
719
+ : recentActivity.slice(-10).map(a => `[${a.type}] ${a.content}`).join('\n');
720
+ const prompt = `You are analyzing a coding session transcript to update a dashboard display.
721
+
722
+ CURRENT SESSION:
723
+ Mission: "${currentInsights.mission}"
724
+ Theme: ${currentInsights.theme}
725
+ Purpose: ${currentInsights.purpose || currentInsights.mission}
726
+
727
+ RECENT ACTIVITY:
728
+ ${activityStr}
729
+
730
+ Based on what happened, extract the session's current **purpose** (what it's about NOW).
731
+
732
+ **purpose**: 30-50 chars describing the session's current focus
733
+ - What someone would say if asked "what are you working on?"
734
+ - Keep it concise: "Building auth system" not "We are currently..."
735
+ - May match the original mission, or may have pivoted
736
+
737
+ Response format (JSON only):
738
+ {
739
+ "reason": "brief explanation",
740
+ "patches": {
741
+ "purpose": "current session focus"
742
+ }
743
+ }`;
744
+ const patchGenStart = Date.now();
745
+ const response = await this.withRetry('generateInsightsPatch', () => this.client.messages.create({
746
+ model: this.models.fullPatch,
747
+ max_tokens: 150, // Only extracting purpose now
748
+ temperature: 0,
749
+ messages: [{ role: 'user', content: prompt }]
750
+ }));
751
+ const duration = Date.now() - startTime;
752
+ const text = response.content[0]?.type === 'text' ? response.content[0].text : '';
753
+ // Log cost for PATCH operation (full path)
754
+ this.logCost(response, 'PATCH', this.models.fullPatch, duration, sessionId);
755
+ this.logger.info('Fallback patch generation (Sonnet) completed', {
756
+ durationMs: duration,
757
+ inputTokens: response.usage?.input_tokens,
758
+ outputTokens: response.usage?.output_tokens,
759
+ });
760
+ try {
761
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
762
+ const jsonToParse = jsonMatch?.[0] || text;
763
+ const result = JSON.parse(jsonToParse);
764
+ const patches = result.patches || {};
765
+ return {
766
+ patches,
767
+ reason: result.reason || 'Fallback patch generated',
768
+ propositions
769
+ };
770
+ }
771
+ catch (parseError) {
772
+ this.logger.warn('Failed to parse fallback patch response', {
773
+ error: parseError instanceof Error ? parseError.message : String(parseError),
774
+ textPreview: text.slice(0, 200),
775
+ });
776
+ return { patches: {}, reason: 'Parse error - no patches applied', propositions };
777
+ }
778
+ }
779
+ catch (error) {
780
+ // Check for credit/billing errors and make them LOUD
781
+ const errorMessage = error instanceof Error ? error.message : String(error);
782
+ if (errorMessage.includes('credit balance') || errorMessage.includes('billing')) {
783
+ this.logger.error('🚨 ANTHROPIC API CREDITS EXHAUSTED 🚨', {
784
+ errorMessage,
785
+ action: 'ADD CREDITS AT https://console.anthropic.com/settings/billing',
786
+ context: 'Patch generation failed due to billing issue'
787
+ });
788
+ }
789
+ else {
790
+ this.logger.error('Patch generation failed', { error });
791
+ }
792
+ return { patches: {}, reason: 'Generation error - no patches applied', propositions: [] };
793
+ }
794
+ }
795
+ /**
796
+ * Summarize what the user is currently asking Claude to do.
797
+ *
798
+ * Called immediately when a user message is detected, this generates a brief
799
+ * description that makes sense even if the user just said "do it" - because
800
+ * we provide recent conversation context.
801
+ *
802
+ * Uses Haiku for speed (~1-2s). The goal is "what would you tell someone
803
+ * who walked in and asked what you're working on right now?"
804
+ */
805
+ async summarizeCurrentWork(userMessage, recentContext, sessionMission, sessionId) {
806
+ if (!this.client) {
807
+ return null;
808
+ }
809
+ const startTime = Date.now();
810
+ try {
811
+ // Build context from recent conversation (last few exchanges)
812
+ const contextStr = recentContext.length > 0
813
+ ? recentContext.map(m => `[${m.type === 'user' ? 'User' : 'Claude'}]: ${m.content.slice(0, 500)}`).join('\n\n')
814
+ : '(new conversation)';
815
+ const missionStr = sessionMission ? `Session mission: ${sessionMission}\n\n` : '';
816
+ const prompt = `${missionStr}Recent conversation context:
817
+ ${contextStr}
818
+
819
+ The user just sent this message:
820
+ "${userMessage}"
821
+
822
+ In 5-10 words, what is the user asking Claude to do RIGHT NOW?
823
+ - Be specific and concrete (not "continuing work" or "helping with code")
824
+ - If the user said something vague like "do it" or "yes" or "go ahead", infer from context what "it" refers to
825
+ - Use action verbs: "Implementing...", "Fixing...", "Adding...", "Debugging..."
826
+ - If truly unclear, describe what can be inferred
827
+
828
+ Respond with ONLY the summary text, nothing else. No quotes, no explanation.`;
829
+ const response = await this.withRetry('summarizeCurrentWork', () => this.client.messages.create({
830
+ model: this.models.quickCheck, // Haiku - speed is critical
831
+ max_tokens: 50,
832
+ temperature: 0,
833
+ messages: [{ role: 'user', content: prompt }]
834
+ }));
835
+ const durationMs = Date.now() - startTime;
836
+ // Log cost
837
+ this.logCost(response, 'CURRENT_WORK', this.models.quickCheck, durationMs, sessionId);
838
+ // Strip trailing period - we want consistency with history items which never end with periods
839
+ const rawText = response.content[0]?.type === 'text' ? response.content[0].text.trim() : null;
840
+ const text = rawText?.replace(/\.+$/, '') || null;
841
+ if (!text) {
842
+ return null;
843
+ }
844
+ this.logger.info('Current work summarized', {
845
+ durationMs,
846
+ inputTokens: response.usage?.input_tokens,
847
+ outputTokens: response.usage?.output_tokens,
848
+ summary: text.slice(0, 50),
849
+ });
850
+ return { summary: text };
851
+ }
852
+ catch (error) {
853
+ this.logger.debug('Failed to summarize current work', {
854
+ error: error instanceof Error ? error.message : String(error),
855
+ });
856
+ return null;
857
+ }
858
+ }
859
+ /**
860
+ * Evaluate session metadata (purpose/theme/tags) based on user message and context.
861
+ * Called on user message to update session classification. This replaces the
862
+ * timer-based fast patch approach with explicit user-message triggering.
863
+ */
864
+ async evaluateSessionMetadata(userMessage, recentContext, current, sessionId) {
865
+ if (!this.client) {
866
+ return null;
867
+ }
868
+ const startTime = Date.now();
869
+ try {
870
+ // Build context from recent conversation
871
+ const contextStr = recentContext.length > 0
872
+ ? recentContext.map(m => `[${m.type === 'user' ? 'User' : 'Claude'}]: ${m.content.slice(0, 400)}`).join('\n\n')
873
+ : '(new conversation)';
874
+ const missionStr = current.mission || '(not set)';
875
+ const purposeStr = current.purpose || missionStr;
876
+ const currentTheme = current.theme || 'exploring';
877
+ const currentTags = current.tags || { workType: ['exploring'], collaboration: 'iterative', complexity: 'routine' };
878
+ const prompt = `You classify coding session activity based on what the user just asked for.
879
+
880
+ SESSION MISSION: ${missionStr}
881
+ CURRENT PURPOSE: ${purposeStr}
882
+ CURRENT THEME: ${currentTheme}
883
+ CURRENT TAGS: workType=${JSON.stringify(currentTags.workType)}, collaboration=${currentTags.collaboration}, complexity=${currentTags.complexity}
884
+
885
+ Recent conversation:
886
+ ${contextStr}
887
+
888
+ User just sent: "${userMessage.slice(0, 500)}"
889
+
890
+ Based on what the user is now asking Claude to do, update:
891
+
892
+ 1. **purpose** (30-50 chars): Session's current focus. Keep concise.
893
+ 2. **theme** (single word): "debugging" | "building" | "exploring" | "firefighting" | "refactoring" | "designing" | "investigating" | "polishing"
894
+ 3. **tags**:
895
+ - workType: ["implementing" | "debugging" | "refactoring" | "exploring" | "designing" | "investigating"]
896
+ - collaboration: "autonomous" | "iterative" | "guided"
897
+ - complexity: "routine" | "tricky" | "gnarly"
898
+
899
+ IMPORTANT: Only change values if this message clearly shifts the session's focus. Preserve current values for continuation messages like "yes", "do it", "continue".
900
+
901
+ Respond with ONLY valid JSON:
902
+ {"purpose": "...", "theme": "...", "tags": {"workType": [...], "collaboration": "...", "complexity": "..."}}`;
903
+ const response = await this.withRetry('evaluateSessionMetadata', () => this.client.messages.create({
904
+ model: this.models.quickCheck, // Haiku - speed is critical
905
+ max_tokens: 150,
906
+ temperature: 0,
907
+ messages: [{ role: 'user', content: prompt }]
908
+ }));
909
+ const durationMs = Date.now() - startTime;
910
+ // Log cost
911
+ this.logCost(response, 'METADATA_EVAL', this.models.quickCheck, durationMs, sessionId);
912
+ const text = response.content[0]?.type === 'text' ? response.content[0].text.trim() : null;
913
+ if (!text) {
914
+ return null;
915
+ }
916
+ // Parse JSON response
917
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
918
+ if (!jsonMatch) {
919
+ this.logger.debug('No JSON found in metadata eval response', { text });
920
+ return null;
921
+ }
922
+ const parsed = JSON.parse(jsonMatch[0]);
923
+ this.logger.info('Session metadata evaluated', {
924
+ durationMs,
925
+ inputTokens: response.usage?.input_tokens,
926
+ outputTokens: response.usage?.output_tokens,
927
+ theme: parsed.theme,
928
+ purpose: parsed.purpose?.slice(0, 30),
929
+ });
930
+ return {
931
+ purpose: parsed.purpose,
932
+ theme: parsed.theme,
933
+ tags: parsed.tags,
934
+ };
935
+ }
936
+ catch (error) {
937
+ this.logger.debug('Failed to evaluate session metadata', {
938
+ error: error instanceof Error ? error.message : String(error),
939
+ });
940
+ return null;
941
+ }
942
+ }
943
+ /**
944
+ * Generate intelligent permission pattern suggestions using Haiku.
945
+ *
946
+ * Called immediately when a permission request arrives, this generates
947
+ * contextually-aware pattern options. By the time the user clicks
948
+ * "Similar", suggestions should already be ready.
949
+ *
950
+ * @param toolName - The tool being invoked (e.g., "Bash", "Write")
951
+ * @param toolInput - The tool input parameters (e.g., { command: "..." })
952
+ * @returns Array of suggested patterns from most specific to broadest
953
+ */
954
+ async suggestPermissionPatterns(toolName, toolInput) {
955
+ if (!this.client) {
956
+ return [];
957
+ }
958
+ const startTime = Date.now();
959
+ try {
960
+ // Build context based on tool type
961
+ let contextStr;
962
+ let valueStr;
963
+ if (toolName === 'Bash') {
964
+ const command = toolInput.command || '';
965
+ valueStr = command;
966
+ contextStr = `Bash command: ${command}`;
967
+ }
968
+ else if (['Write', 'Read', 'Edit'].includes(toolName)) {
969
+ const filePath = toolInput.file_path || '';
970
+ valueStr = filePath;
971
+ contextStr = `${toolName} file: ${filePath}`;
972
+ }
973
+ else {
974
+ // Unknown tool - return basic suggestions
975
+ return [
976
+ `${toolName}(${JSON.stringify(toolInput).slice(0, 50)})`,
977
+ toolName
978
+ ];
979
+ }
980
+ const prompt = `You help users create permission patterns for a coding assistant tool.
981
+
982
+ The user is being asked to approve: ${contextStr}
983
+
984
+ Generate permission patterns from MOST SPECIFIC (safest) to LEAST SPECIFIC (broadest).
985
+ Each pattern should be meaningfully different and useful.
986
+
987
+ For Bash commands:
988
+ - Understand the INTENT behind the command, not just the literal text
989
+ - Extract the core tool and action (e.g., "git commit" from "git commit -m 'msg'")
990
+ - For piped commands like "fd -e tsx | head", focus on the primary command (fd)
991
+ - For chained commands like "npm install && npm run build", focus on the first command (npm install)
992
+
993
+ Pattern syntax:
994
+ - Bash(exact command) - matches only this exact command
995
+ - Bash(git commit *) - matches any git commit command
996
+ - Bash(git *) - matches any git command
997
+ - Bash - matches all bash commands
998
+
999
+ For file operations:
1000
+ - Write(/exact/path/file.tsx) - exact file only
1001
+ - Write(/path/to/dir/*.tsx) - any .tsx in that directory
1002
+ - Write(/path/**/*.tsx) - any .tsx recursively under path
1003
+ - Write(*.tsx) - any .tsx file anywhere
1004
+ - Write - all write operations
1005
+
1006
+ Generate 3-5 patterns, ordered from safest to broadest.
1007
+ IMPORTANT: Only return patterns that make semantic sense for this operation.
1008
+
1009
+ Respond with ONLY a JSON array of pattern strings:
1010
+ ["pattern1", "pattern2", ...]`;
1011
+ const response = await this.withRetry('suggestPermissionPatterns', () => this.client.messages.create({
1012
+ model: this.models.quickCheck, // Haiku - speed is critical
1013
+ max_tokens: 200,
1014
+ temperature: 0,
1015
+ messages: [{ role: 'user', content: prompt }]
1016
+ }));
1017
+ const durationMs = Date.now() - startTime;
1018
+ // Log for observability (no cost tracking category for this yet)
1019
+ this.logger.info('Permission patterns suggested', {
1020
+ durationMs,
1021
+ inputTokens: response.usage?.input_tokens,
1022
+ outputTokens: response.usage?.output_tokens,
1023
+ toolName,
1024
+ valuePreview: valueStr.slice(0, 50),
1025
+ });
1026
+ const text = response.content[0]?.type === 'text' ? response.content[0].text.trim() : null;
1027
+ if (!text) {
1028
+ return [];
1029
+ }
1030
+ // Parse JSON array response
1031
+ let jsonText = text;
1032
+ // Strip markdown code blocks if present
1033
+ if (jsonText.startsWith('```')) {
1034
+ jsonText = jsonText.replace(/^```(?:json)?\n?/, '').replace(/\n?```$/, '').trim();
1035
+ }
1036
+ // Extract JSON array
1037
+ const jsonMatch = jsonText.match(/\[[\s\S]*\]/);
1038
+ if (!jsonMatch) {
1039
+ this.logger.debug('No JSON array found in pattern response', { text });
1040
+ return [];
1041
+ }
1042
+ const patterns = JSON.parse(jsonMatch[0]);
1043
+ // Validate patterns - ensure they're strings and not empty
1044
+ const validPatterns = patterns.filter(p => typeof p === 'string' && p.length > 0);
1045
+ // Always ensure the broadest pattern is included
1046
+ if (validPatterns.length > 0 && !validPatterns.includes(toolName)) {
1047
+ validPatterns.push(toolName);
1048
+ }
1049
+ return validPatterns;
1050
+ }
1051
+ catch (error) {
1052
+ this.logger.debug('Failed to suggest permission patterns', {
1053
+ error: error instanceof Error ? error.message : String(error),
1054
+ toolName,
1055
+ });
1056
+ return [];
1057
+ }
1058
+ }
1059
+ }
1060
+ // Export singleton instance
1061
+ export const anthropicService = new AnthropicService();
1062
+ //# sourceMappingURL=anthropic-service.js.map