@rulemetric/cli 0.1.1 → 0.2.1

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 (259) hide show
  1. package/README.md +66 -0
  2. package/dist/base-command.d.ts.map +1 -1
  3. package/dist/base-command.js +15 -0
  4. package/dist/base-command.js.map +1 -1
  5. package/dist/commands/auth/login.d.ts.map +1 -1
  6. package/dist/commands/auth/login.js +7 -0
  7. package/dist/commands/auth/login.js.map +1 -1
  8. package/dist/commands/crons/list.d.ts +7 -0
  9. package/dist/commands/crons/list.d.ts.map +1 -0
  10. package/dist/commands/crons/list.js +24 -0
  11. package/dist/commands/crons/list.js.map +1 -0
  12. package/dist/commands/crons/run.d.ts +13 -0
  13. package/dist/commands/crons/run.d.ts.map +1 -0
  14. package/dist/commands/crons/run.js +48 -0
  15. package/dist/commands/crons/run.js.map +1 -0
  16. package/dist/commands/evals/listen.d.ts +28 -2
  17. package/dist/commands/evals/listen.d.ts.map +1 -1
  18. package/dist/commands/evals/listen.js +159 -25
  19. package/dist/commands/evals/listen.js.map +1 -1
  20. package/dist/commands/evals/listen.test.d.ts +2 -0
  21. package/dist/commands/evals/listen.test.d.ts.map +1 -0
  22. package/dist/commands/evals/listen.test.js +48 -0
  23. package/dist/commands/evals/listen.test.js.map +1 -0
  24. package/dist/commands/gateway/ensure.js +1 -1
  25. package/dist/commands/gateway/ensure.js.map +1 -1
  26. package/dist/commands/hooks/install.d.ts.map +1 -1
  27. package/dist/commands/hooks/install.js +40 -3
  28. package/dist/commands/hooks/install.js.map +1 -1
  29. package/dist/commands/hooks/run.js +1 -1
  30. package/dist/commands/hooks/run.js.map +1 -1
  31. package/dist/commands/hooks/uninstall.d.ts.map +1 -1
  32. package/dist/commands/hooks/uninstall.js +9 -1
  33. package/dist/commands/hooks/uninstall.js.map +1 -1
  34. package/dist/commands/org/current.d.ts +7 -0
  35. package/dist/commands/org/current.d.ts.map +1 -0
  36. package/dist/commands/org/current.js +70 -0
  37. package/dist/commands/org/current.js.map +1 -0
  38. package/dist/commands/org/list.d.ts +7 -0
  39. package/dist/commands/org/list.d.ts.map +1 -0
  40. package/dist/commands/org/list.js +37 -0
  41. package/dist/commands/org/list.js.map +1 -0
  42. package/dist/commands/org/switch.d.ts +13 -0
  43. package/dist/commands/org/switch.d.ts.map +1 -0
  44. package/dist/commands/org/switch.js +52 -0
  45. package/dist/commands/org/switch.js.map +1 -0
  46. package/dist/commands/proxy/restart.d.ts +11 -0
  47. package/dist/commands/proxy/restart.d.ts.map +1 -0
  48. package/dist/commands/proxy/restart.js +36 -0
  49. package/dist/commands/proxy/restart.js.map +1 -0
  50. package/dist/commands/proxy/start.d.ts.map +1 -1
  51. package/dist/commands/proxy/start.js +4 -0
  52. package/dist/commands/proxy/start.js.map +1 -1
  53. package/dist/commands/service/install.d.ts +16 -0
  54. package/dist/commands/service/install.d.ts.map +1 -1
  55. package/dist/commands/service/install.js +145 -6
  56. package/dist/commands/service/install.js.map +1 -1
  57. package/dist/commands/service/uninstall.d.ts.map +1 -1
  58. package/dist/commands/service/uninstall.js +28 -0
  59. package/dist/commands/service/uninstall.js.map +1 -1
  60. package/dist/commands/sessions/start.d.ts.map +1 -1
  61. package/dist/commands/sessions/start.js +5 -0
  62. package/dist/commands/sessions/start.js.map +1 -1
  63. package/dist/commands/signals/report.d.ts +10 -0
  64. package/dist/commands/signals/report.d.ts.map +1 -0
  65. package/dist/commands/signals/report.js +35 -0
  66. package/dist/commands/signals/report.js.map +1 -0
  67. package/dist/commands/signals/report.test.d.ts +2 -0
  68. package/dist/commands/signals/report.test.d.ts.map +1 -0
  69. package/dist/commands/signals/report.test.js +65 -0
  70. package/dist/commands/signals/report.test.js.map +1 -0
  71. package/dist/commands/skills/export-approved.d.ts +10 -0
  72. package/dist/commands/skills/export-approved.d.ts.map +1 -0
  73. package/dist/commands/skills/export-approved.js +31 -0
  74. package/dist/commands/skills/export-approved.js.map +1 -0
  75. package/dist/commands/skills/publish.d.ts +17 -0
  76. package/dist/commands/skills/publish.d.ts.map +1 -0
  77. package/dist/commands/skills/publish.js +80 -0
  78. package/dist/commands/skills/publish.js.map +1 -0
  79. package/dist/commands/skills/update.d.ts +3 -0
  80. package/dist/commands/skills/update.d.ts.map +1 -1
  81. package/dist/commands/skills/update.js +17 -6
  82. package/dist/commands/skills/update.js.map +1 -1
  83. package/dist/commands/suggestions/accept.d.ts +16 -0
  84. package/dist/commands/suggestions/accept.d.ts.map +1 -0
  85. package/dist/commands/suggestions/accept.js +61 -0
  86. package/dist/commands/suggestions/accept.js.map +1 -0
  87. package/dist/commands/suggestions/accept.test.d.ts +2 -0
  88. package/dist/commands/suggestions/accept.test.d.ts.map +1 -0
  89. package/dist/commands/suggestions/accept.test.js +52 -0
  90. package/dist/commands/suggestions/accept.test.js.map +1 -0
  91. package/dist/commands/suggestions/dismiss.d.ts +10 -0
  92. package/dist/commands/suggestions/dismiss.d.ts.map +1 -0
  93. package/dist/commands/suggestions/dismiss.js +26 -0
  94. package/dist/commands/suggestions/dismiss.js.map +1 -0
  95. package/dist/commands/suggestions/list.d.ts +10 -0
  96. package/dist/commands/suggestions/list.d.ts.map +1 -0
  97. package/dist/commands/suggestions/list.js +40 -0
  98. package/dist/commands/suggestions/list.js.map +1 -0
  99. package/dist/commands/suggestions/refresh.d.ts +7 -0
  100. package/dist/commands/suggestions/refresh.d.ts.map +1 -0
  101. package/dist/commands/suggestions/refresh.js +11 -0
  102. package/dist/commands/suggestions/refresh.js.map +1 -0
  103. package/dist/dashboard/Dashboard.d.ts.map +1 -1
  104. package/dist/dashboard/Dashboard.js +7 -1
  105. package/dist/dashboard/Dashboard.js.map +1 -1
  106. package/dist/lib/active-org-refresh.d.ts +22 -0
  107. package/dist/lib/active-org-refresh.d.ts.map +1 -0
  108. package/dist/lib/active-org-refresh.js +108 -0
  109. package/dist/lib/active-org-refresh.js.map +1 -0
  110. package/dist/lib/active-org-refresh.test.d.ts +2 -0
  111. package/dist/lib/active-org-refresh.test.d.ts.map +1 -0
  112. package/dist/lib/active-org-refresh.test.js +190 -0
  113. package/dist/lib/active-org-refresh.test.js.map +1 -0
  114. package/dist/lib/active-org.d.ts +43 -0
  115. package/dist/lib/active-org.d.ts.map +1 -0
  116. package/dist/lib/active-org.js +96 -0
  117. package/dist/lib/active-org.js.map +1 -0
  118. package/dist/lib/active-org.test.d.ts +2 -0
  119. package/dist/lib/active-org.test.d.ts.map +1 -0
  120. package/dist/lib/active-org.test.js +147 -0
  121. package/dist/lib/active-org.test.js.map +1 -0
  122. package/dist/lib/cron-registry.d.ts +47 -0
  123. package/dist/lib/cron-registry.d.ts.map +1 -0
  124. package/dist/lib/cron-registry.js +102 -0
  125. package/dist/lib/cron-registry.js.map +1 -0
  126. package/dist/lib/cron-registry.test.d.ts +2 -0
  127. package/dist/lib/cron-registry.test.d.ts.map +1 -0
  128. package/dist/lib/cron-registry.test.js +136 -0
  129. package/dist/lib/cron-registry.test.js.map +1 -0
  130. package/dist/lib/cron-runner.d.ts +16 -0
  131. package/dist/lib/cron-runner.d.ts.map +1 -0
  132. package/dist/lib/cron-runner.js +68 -0
  133. package/dist/lib/cron-runner.js.map +1 -0
  134. package/dist/lib/detect-languages.d.ts +7 -0
  135. package/dist/lib/detect-languages.d.ts.map +1 -0
  136. package/dist/lib/detect-languages.js +113 -0
  137. package/dist/lib/detect-languages.js.map +1 -0
  138. package/dist/lib/detect-languages.test.d.ts +2 -0
  139. package/dist/lib/detect-languages.test.d.ts.map +1 -0
  140. package/dist/lib/detect-languages.test.js +29 -0
  141. package/dist/lib/detect-languages.test.js.map +1 -0
  142. package/dist/lib/eval-executor.d.ts.map +1 -1
  143. package/dist/lib/eval-executor.js +5 -2
  144. package/dist/lib/eval-executor.js.map +1 -1
  145. package/dist/lib/gateway-entry.js +4 -1
  146. package/dist/lib/gateway-entry.js.map +1 -1
  147. package/dist/lib/gateway-lifecycle.d.ts +1 -1
  148. package/dist/lib/gateway-lifecycle.d.ts.map +1 -1
  149. package/dist/lib/gateway-lifecycle.js +13 -2
  150. package/dist/lib/gateway-lifecycle.js.map +1 -1
  151. package/dist/lib/gateway.d.ts +4 -5
  152. package/dist/lib/gateway.d.ts.map +1 -1
  153. package/dist/lib/gateway.js +68 -2
  154. package/dist/lib/gateway.js.map +1 -1
  155. package/dist/lib/graphile-worker-runner.d.ts +124 -0
  156. package/dist/lib/graphile-worker-runner.d.ts.map +1 -0
  157. package/dist/lib/graphile-worker-runner.js +530 -0
  158. package/dist/lib/graphile-worker-runner.js.map +1 -0
  159. package/dist/lib/graphile-worker-runner.test.d.ts +2 -0
  160. package/dist/lib/graphile-worker-runner.test.d.ts.map +1 -0
  161. package/dist/lib/graphile-worker-runner.test.js +153 -0
  162. package/dist/lib/graphile-worker-runner.test.js.map +1 -0
  163. package/dist/lib/handlers/cron-close-stale-sessions.d.ts +3 -0
  164. package/dist/lib/handlers/cron-close-stale-sessions.d.ts.map +1 -0
  165. package/dist/lib/handlers/cron-close-stale-sessions.js +8 -0
  166. package/dist/lib/handlers/cron-close-stale-sessions.js.map +1 -0
  167. package/dist/lib/handlers/cron-data-integrity-audit.d.ts +3 -0
  168. package/dist/lib/handlers/cron-data-integrity-audit.d.ts.map +1 -0
  169. package/dist/lib/handlers/cron-data-integrity-audit.js +16 -0
  170. package/dist/lib/handlers/cron-data-integrity-audit.js.map +1 -0
  171. package/dist/lib/handlers/cron-refresh-skills.d.ts +3 -0
  172. package/dist/lib/handlers/cron-refresh-skills.d.ts.map +1 -0
  173. package/dist/lib/handlers/cron-refresh-skills.js +91 -0
  174. package/dist/lib/handlers/cron-refresh-skills.js.map +1 -0
  175. package/dist/lib/handlers/cron-refresh-skills.test.d.ts +2 -0
  176. package/dist/lib/handlers/cron-refresh-skills.test.d.ts.map +1 -0
  177. package/dist/lib/handlers/cron-refresh-skills.test.js +114 -0
  178. package/dist/lib/handlers/cron-refresh-skills.test.js.map +1 -0
  179. package/dist/lib/handlers/cron-regenerate-insights.d.ts +3 -0
  180. package/dist/lib/handlers/cron-regenerate-insights.d.ts.map +1 -0
  181. package/dist/lib/handlers/cron-regenerate-insights.js +6 -0
  182. package/dist/lib/handlers/cron-regenerate-insights.js.map +1 -0
  183. package/dist/lib/handlers/cron-suggest-instructions.d.ts +3 -0
  184. package/dist/lib/handlers/cron-suggest-instructions.d.ts.map +1 -0
  185. package/dist/lib/handlers/cron-suggest-instructions.js +159 -0
  186. package/dist/lib/handlers/cron-suggest-instructions.js.map +1 -0
  187. package/dist/lib/handlers/cron-suggest-instructions.test.d.ts +2 -0
  188. package/dist/lib/handlers/cron-suggest-instructions.test.d.ts.map +1 -0
  189. package/dist/lib/handlers/cron-suggest-instructions.test.js +80 -0
  190. package/dist/lib/handlers/cron-suggest-instructions.test.js.map +1 -0
  191. package/dist/lib/handlers/process-conversation.d.ts +35 -0
  192. package/dist/lib/handlers/process-conversation.d.ts.map +1 -0
  193. package/dist/lib/handlers/process-conversation.js +35 -0
  194. package/dist/lib/handlers/process-conversation.js.map +1 -0
  195. package/dist/lib/handlers/process-eval.d.ts +32 -0
  196. package/dist/lib/handlers/process-eval.d.ts.map +1 -0
  197. package/dist/lib/handlers/process-eval.js +207 -0
  198. package/dist/lib/handlers/process-eval.js.map +1 -0
  199. package/dist/lib/handlers/process-insights.d.ts +39 -0
  200. package/dist/lib/handlers/process-insights.d.ts.map +1 -0
  201. package/dist/lib/handlers/process-insights.js +300 -0
  202. package/dist/lib/handlers/process-insights.js.map +1 -0
  203. package/dist/lib/handlers/process-launch.d.ts +29 -0
  204. package/dist/lib/handlers/process-launch.d.ts.map +1 -0
  205. package/dist/lib/handlers/process-launch.js +41 -0
  206. package/dist/lib/handlers/process-launch.js.map +1 -0
  207. package/dist/lib/handlers/process-session-goal.d.ts +29 -0
  208. package/dist/lib/handlers/process-session-goal.d.ts.map +1 -0
  209. package/dist/lib/handlers/process-session-goal.js +74 -0
  210. package/dist/lib/handlers/process-session-goal.js.map +1 -0
  211. package/dist/lib/handlers/process-session-goal.test.d.ts +2 -0
  212. package/dist/lib/handlers/process-session-goal.test.d.ts.map +1 -0
  213. package/dist/lib/handlers/process-session-goal.test.js +134 -0
  214. package/dist/lib/handlers/process-session-goal.test.js.map +1 -0
  215. package/dist/lib/hooks-config.d.ts +28 -4
  216. package/dist/lib/hooks-config.d.ts.map +1 -1
  217. package/dist/lib/hooks-config.js +138 -10
  218. package/dist/lib/hooks-config.js.map +1 -1
  219. package/dist/lib/llm-client.d.ts.map +1 -1
  220. package/dist/lib/llm-client.js +149 -16
  221. package/dist/lib/llm-client.js.map +1 -1
  222. package/dist/lib/mirror-writer-db.d.ts +12 -0
  223. package/dist/lib/mirror-writer-db.d.ts.map +1 -0
  224. package/dist/lib/mirror-writer-db.js +51 -0
  225. package/dist/lib/mirror-writer-db.js.map +1 -0
  226. package/dist/lib/mirror-writer.d.ts +35 -0
  227. package/dist/lib/mirror-writer.d.ts.map +1 -0
  228. package/dist/lib/mirror-writer.js +27 -0
  229. package/dist/lib/mirror-writer.js.map +1 -0
  230. package/dist/lib/mirror-writer.test.d.ts +2 -0
  231. package/dist/lib/mirror-writer.test.d.ts.map +1 -0
  232. package/dist/lib/mirror-writer.test.js +47 -0
  233. package/dist/lib/mirror-writer.test.js.map +1 -0
  234. package/dist/lib/permanent-job-error.d.ts +17 -0
  235. package/dist/lib/permanent-job-error.d.ts.map +1 -0
  236. package/dist/lib/permanent-job-error.js +23 -0
  237. package/dist/lib/permanent-job-error.js.map +1 -0
  238. package/dist/lib/permanent-job-error.test.d.ts +2 -0
  239. package/dist/lib/permanent-job-error.test.d.ts.map +1 -0
  240. package/dist/lib/permanent-job-error.test.js +29 -0
  241. package/dist/lib/permanent-job-error.test.js.map +1 -0
  242. package/dist/lib/version-check.d.ts +11 -0
  243. package/dist/lib/version-check.d.ts.map +1 -0
  244. package/dist/lib/version-check.js +106 -0
  245. package/dist/lib/version-check.js.map +1 -0
  246. package/oclif.manifest.json +611 -81
  247. package/package.json +21 -5
  248. package/dist/commands/insights/sync.d.ts +0 -17
  249. package/dist/commands/insights/sync.d.ts.map +0 -1
  250. package/dist/commands/insights/sync.js +0 -477
  251. package/dist/commands/insights/sync.js.map +0 -1
  252. package/dist/lib/eval-worker.d.ts +0 -12
  253. package/dist/lib/eval-worker.d.ts.map +0 -1
  254. package/dist/lib/eval-worker.js +0 -395
  255. package/dist/lib/eval-worker.js.map +0 -1
  256. package/dist/lib/insights-worker.d.ts +0 -10
  257. package/dist/lib/insights-worker.d.ts.map +0 -1
  258. package/dist/lib/insights-worker.js +0 -329
  259. package/dist/lib/insights-worker.js.map +0 -1
@@ -0,0 +1,39 @@
1
+ import type { JobHelpers } from 'graphile-worker';
2
+ interface InsightsPayload {
3
+ mirrorId: string;
4
+ projectPath: string | null;
5
+ config?: {
6
+ sessionId?: string;
7
+ [key: string]: unknown;
8
+ };
9
+ }
10
+ /**
11
+ * Graphile-shaped handler for the `process_insights` task.
12
+ *
13
+ * Lifted from the legacy `insights-worker.ts:processInsightsJob` and
14
+ * `processSessionAnalysisJob`, with the queue plumbing stripped out:
15
+ * - no claim — Graphile already claimed the job before invoking us
16
+ * - no /complete POST — we return the result object (or throw)
17
+ * - no "could not claim" early-return branches
18
+ *
19
+ * Mid-job progress patches still hit the existing
20
+ * `PATCH /api/insights-jobs/:id/progress` endpoint, just keyed by the
21
+ * mirror row's id.
22
+ *
23
+ * The session-analysis branch (selected when `payload.config.sessionId`
24
+ * is set) lives inline below — it's a different code path but cheap to
25
+ * keep co-located.
26
+ *
27
+ * The `buildDataSummary*` helpers are duplicated here as private locals;
28
+ * Task 21 will hoist them into a shared `insights-data.ts` once
29
+ * `insights-worker.ts` is deleted.
30
+ *
31
+ * User-facing notifications go through the API's `notifyUser()` path
32
+ * (insert into `notifications` → Supabase Realtime → service-worker
33
+ * `showNotification` with a deep-link). The worker no longer fires native
34
+ * `osascript` banners — those duplicated the browser path, weren't
35
+ * clickable to a URL, and stacked one-per-project on macOS.
36
+ */
37
+ export declare function processInsights(payload: InsightsPayload, helpers: JobHelpers): Promise<Record<string, unknown>>;
38
+ export {};
39
+ //# sourceMappingURL=process-insights.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-insights.d.ts","sourceRoot":"","sources":["../../../src/lib/handlers/process-insights.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAMlD,UAAU,eAAe;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,EAAE;QACP,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA4LlC"}
@@ -0,0 +1,300 @@
1
+ import { apiGet, apiPost, apiPatch } from '../api-client.js';
2
+ import { RECOMMENDATION_PROMPTS, AT_A_GLANCE_PROMPT, FACET_GENERATION_PROMPT } from '../insights-prompts.js';
3
+ import { runLlmPrompt, detectLlmBackend, runToolPrompt } from '../llm-client.js';
4
+ import { PermanentJobError } from '../permanent-job-error.js';
5
+ /**
6
+ * Graphile-shaped handler for the `process_insights` task.
7
+ *
8
+ * Lifted from the legacy `insights-worker.ts:processInsightsJob` and
9
+ * `processSessionAnalysisJob`, with the queue plumbing stripped out:
10
+ * - no claim — Graphile already claimed the job before invoking us
11
+ * - no /complete POST — we return the result object (or throw)
12
+ * - no "could not claim" early-return branches
13
+ *
14
+ * Mid-job progress patches still hit the existing
15
+ * `PATCH /api/insights-jobs/:id/progress` endpoint, just keyed by the
16
+ * mirror row's id.
17
+ *
18
+ * The session-analysis branch (selected when `payload.config.sessionId`
19
+ * is set) lives inline below — it's a different code path but cheap to
20
+ * keep co-located.
21
+ *
22
+ * The `buildDataSummary*` helpers are duplicated here as private locals;
23
+ * Task 21 will hoist them into a shared `insights-data.ts` once
24
+ * `insights-worker.ts` is deleted.
25
+ *
26
+ * User-facing notifications go through the API's `notifyUser()` path
27
+ * (insert into `notifications` → Supabase Realtime → service-worker
28
+ * `showNotification` with a deep-link). The worker no longer fires native
29
+ * `osascript` banners — those duplicated the browser path, weren't
30
+ * clickable to a URL, and stacked one-per-project on macOS.
31
+ */
32
+ export async function processInsights(payload, helpers) {
33
+ const { mirrorId, projectPath, config } = payload;
34
+ const log = (msg) => helpers.logger.info(msg);
35
+ // Session-analysis branch — single-session deep analysis through tool CLI
36
+ if (config?.sessionId) {
37
+ return processSessionAnalysisInline({ mirrorId, sessionId: config.sessionId }, log);
38
+ }
39
+ log(`Processing insights job ${mirrorId}...`);
40
+ // Update status to running
41
+ await apiPatch(`/api/insights-jobs/${mirrorId}/progress`, {
42
+ status: 'running',
43
+ progress: { step: 'reading_data' },
44
+ });
45
+ // Self-heal orphan analyses missing `analyzed_at`. Without this, the
46
+ // freshness banner on the dashboard nags forever — the worker's
47
+ // facet-drain query (`hasAnalysis=false`) skips already-analyzed rows,
48
+ // so pre-instrumentation orphans never get touched. Stamps a conservative
49
+ // lower bound (`analyzed_at = started_at`); idempotent on subsequent runs.
50
+ // Best-effort: a failure here must not block the recommendation pass.
51
+ try {
52
+ const { repaired } = await apiPost('/api/sessions/repair-analyzed-at', {});
53
+ if (repaired > 0) {
54
+ log(` Repaired analyzed_at on ${repaired} orphan session(s).`);
55
+ }
56
+ }
57
+ catch (repairErr) {
58
+ log(` Warning: analyzed_at repair failed: ${repairErr instanceof Error ? repairErr.message : String(repairErr)}`);
59
+ }
60
+ // API-based summary is the only source. The previous file-based fallback
61
+ // (`buildDataSummary` reading ~/.claude/usage-data/) was deleted on
62
+ // 2026-05-05 — it required `rulemetric insights sync` to populate JSON
63
+ // files locally, which nobody ever ran. The DB-derived summary is
64
+ // tool-agnostic and authoritative.
65
+ const data = await buildDataSummaryFromApi(projectPath);
66
+ if (!data) {
67
+ throw new PermanentJobError('No session data found — ensure sessions are captured and enriched.');
68
+ }
69
+ log(` Found ${data.stats.sessions} sessions, generating recommendations...`);
70
+ // Drain un-analyzed sessions in batches until empty (or until we hit the
71
+ // hard cap, to keep the per-job runtime bounded). Previously this took at
72
+ // most 20 sessions per run, which meant catch-up after a backlog could
73
+ // span days. Batching keeps memory bounded while letting one job clear
74
+ // the entire queue when needed.
75
+ const FACET_BATCH_SIZE = 20;
76
+ const FACET_HARD_CAP = 200; // protect against runaway runtime
77
+ if (detectLlmBackend() !== 'none') {
78
+ let totalFaceted = 0;
79
+ try {
80
+ while (totalFaceted < FACET_HARD_CAP) {
81
+ const unfaceted = await apiGet(`/api/sessions?hasAnalysis=false&limit=${FACET_BATCH_SIZE}`);
82
+ if (unfaceted.length === 0)
83
+ break;
84
+ log(` Generating facets for ${unfaceted.length} un-analyzed sessions (drained ${totalFaceted} so far)...`);
85
+ for (const session of unfaceted) {
86
+ if (totalFaceted >= FACET_HARD_CAP)
87
+ break;
88
+ try {
89
+ const events = await apiGet(`/api/sessions/${session.id}/events?limit=100`);
90
+ if (events.length < 2) {
91
+ totalFaceted++;
92
+ continue;
93
+ }
94
+ const eventSummary = events
95
+ .filter(e => e.eventType === 'user_message' || e.eventType === 'human')
96
+ .map(e => e.content?.slice(0, 200) ?? '')
97
+ .filter(Boolean)
98
+ .join('\n---\n');
99
+ if (!eventSummary) {
100
+ totalFaceted++;
101
+ continue;
102
+ }
103
+ const facets = await runLlmPrompt(FACET_GENERATION_PROMPT, eventSummary, 4096);
104
+ if (facets) {
105
+ const userMessageCount = events.filter(e => e.eventType === 'user_message' || e.eventType === 'human').length;
106
+ await apiPatch(`/api/sessions/${session.id}/enrich`, {
107
+ analysis: { source: 'generated_facets', ...facets },
108
+ analyzedEventCount: events.length,
109
+ analyzedTurnCount: userMessageCount,
110
+ });
111
+ log(` Generated facets for session ${session.id} (${session.tool})`);
112
+ }
113
+ totalFaceted++;
114
+ }
115
+ catch (sessionErr) {
116
+ log(` Failed to generate facets for session ${session.id}: ${sessionErr instanceof Error ? sessionErr.message : String(sessionErr)}`);
117
+ totalFaceted++; // count it so we don't infinite-loop on a poison row
118
+ }
119
+ }
120
+ // If the batch returned fewer than the requested size, the queue is
121
+ // empty — break before another fetch.
122
+ if (unfaceted.length < FACET_BATCH_SIZE)
123
+ break;
124
+ }
125
+ }
126
+ catch (facetErr) {
127
+ log(` Warning: facet generation failed after ${totalFaceted} sessions: ${facetErr instanceof Error ? facetErr.message : String(facetErr)}`);
128
+ }
129
+ }
130
+ // Run recommendation prompts sequentially
131
+ const recommendations = {};
132
+ const totalPrompts = RECOMMENDATION_PROMPTS.length + 1; // +1 for at_a_glance
133
+ // Persist sections-so-far on every progress PATCH so the web UI can render
134
+ // them incrementally — each prompt takes 30-60s and shipping a label-only
135
+ // progress update for that whole window leaves the page looking dead. The
136
+ // detail page reads `progress.partial` and renders each section card as it
137
+ // arrives.
138
+ for (let i = 0; i < RECOMMENDATION_PROMPTS.length; i++) {
139
+ const spec = RECOMMENDATION_PROMPTS[i];
140
+ log(` [${i + 1}/${totalPrompts}] Generating ${spec.name}...`);
141
+ try {
142
+ await apiPatch(`/api/insights-jobs/${mirrorId}/progress`, {
143
+ status: 'running',
144
+ progress: {
145
+ step: spec.name,
146
+ current: i + 1,
147
+ total: totalPrompts,
148
+ partial: { ...recommendations },
149
+ },
150
+ });
151
+ }
152
+ catch (patchErr) {
153
+ log(` Warning: progress update failed for ${spec.name}: ${patchErr instanceof Error ? patchErr.message : String(patchErr)}`);
154
+ }
155
+ log(` Running LLM prompt for ${spec.name}...`);
156
+ const result = await runLlmPrompt(spec.prompt, data.summary, spec.maxTokens);
157
+ if (result) {
158
+ recommendations[spec.name] = result;
159
+ log(` Got result for ${spec.name}`);
160
+ }
161
+ else {
162
+ log(` No result for ${spec.name} (LLM returned null)`);
163
+ }
164
+ }
165
+ // At a glance (uses outputs from previous prompts)
166
+ log(` [${totalPrompts}/${totalPrompts}] Generating at_a_glance...`);
167
+ await apiPatch(`/api/insights-jobs/${mirrorId}/progress`, {
168
+ status: 'running',
169
+ progress: {
170
+ step: 'at_a_glance',
171
+ current: totalPrompts,
172
+ total: totalPrompts,
173
+ partial: { ...recommendations },
174
+ },
175
+ });
176
+ const glanceData = [
177
+ data.summary,
178
+ recommendations.what_works ? `\n## What Works\n${JSON.stringify(recommendations.what_works)}` : '',
179
+ recommendations.friction_analysis ? `\n## Friction\n${JSON.stringify(recommendations.friction_analysis)}` : '',
180
+ recommendations.suggestions ? `\n## Suggestions\n${JSON.stringify(recommendations.suggestions)}` : '',
181
+ recommendations.on_the_horizon ? `\n## On the Horizon\n${JSON.stringify(recommendations.on_the_horizon)}` : '',
182
+ ].join('\n');
183
+ const glance = await runLlmPrompt(AT_A_GLANCE_PROMPT, glanceData, 8192);
184
+ if (glance) {
185
+ recommendations.at_a_glance = glance;
186
+ }
187
+ log(' Uploading results...');
188
+ log(` Done! Recommendations generated for job ${mirrorId}`);
189
+ const result = { recommendations };
190
+ // Graphile discards task return values, so persist `{ recommendations }`
191
+ // onto the mirror row here so the dashboard can read it. Best-effort.
192
+ await apiPatch(`/api/insights-jobs/${mirrorId}/state`, { result }).catch((err) => {
193
+ log(`result persist failed: ${err instanceof Error ? err.message : String(err)}`);
194
+ });
195
+ return result;
196
+ }
197
+ /**
198
+ * Single-session deep analysis branch. Runs the session's own tool CLI
199
+ * against the captured event stream to produce facets, then enriches the
200
+ * session row.
201
+ *
202
+ * Idempotency guard: if the session already has analysis from a prior
203
+ * `session_analysis` run, we short-circuit. This is independent of the
204
+ * Graphile-side `job_key` dedup added in Task 9 — that prevents duplicate
205
+ * enqueues; this prevents redoing work even if a different mirrorId
206
+ * targets the same session.
207
+ */
208
+ async function processSessionAnalysisInline(args, log) {
209
+ const { mirrorId, sessionId } = args;
210
+ log(`Analyzing session ${sessionId} via tool CLI...`);
211
+ await apiPatch(`/api/insights-jobs/${mirrorId}/progress`, {
212
+ status: 'running',
213
+ progress: { step: 'fetching_session' },
214
+ });
215
+ const session = await apiGet(`/api/sessions/${sessionId}`);
216
+ // Idempotency: skip if session already has analysis from a prior run
217
+ if (session.analysis && session.analysis.source === 'session_analysis') {
218
+ log(` Session ${sessionId} already analyzed — skipping (idempotency guard)`);
219
+ return { sessionId, skipped: true, reason: 'already_analyzed' };
220
+ }
221
+ const events = await apiGet(`/api/sessions/${sessionId}/events?limit=100`);
222
+ if (events.length < 2) {
223
+ throw new PermanentJobError('Session has too few events to analyze');
224
+ }
225
+ const eventSummary = events
226
+ .filter(e => e.eventType === 'user_message' || e.eventType === 'human')
227
+ .map(e => e.content?.slice(0, 200) ?? '')
228
+ .filter(Boolean)
229
+ .join('\n---\n');
230
+ if (!eventSummary) {
231
+ throw new PermanentJobError('No user messages found in session');
232
+ }
233
+ await apiPatch(`/api/insights-jobs/${mirrorId}/progress`, {
234
+ status: 'running',
235
+ progress: { step: 'running_analysis', tool: session.tool },
236
+ });
237
+ // Run through the session's tool CLI
238
+ const facets = runToolPrompt(session.tool, FACET_GENERATION_PROMPT, eventSummary);
239
+ if (!facets) {
240
+ throw new Error(`Analysis failed — ${session.tool} CLI returned no result`);
241
+ }
242
+ const userMessageCount = events.filter(e => e.eventType === 'user_message' || e.eventType === 'human').length;
243
+ await apiPatch(`/api/sessions/${sessionId}/enrich`, {
244
+ analysis: { source: 'session_analysis', tool: session.tool, ...facets },
245
+ analyzedEventCount: events.length,
246
+ analyzedTurnCount: userMessageCount,
247
+ });
248
+ log(` Session ${sessionId} analyzed via ${session.tool}`);
249
+ return { sessionId, tool: session.tool, facets };
250
+ }
251
+ // ---------------------------------------------------------------------------
252
+ // Local helpers (copied from `insights-worker.ts`; Task 21 hoists to shared)
253
+ // ---------------------------------------------------------------------------
254
+ async function buildDataSummaryFromApi(projectPath) {
255
+ try {
256
+ const params = new URLSearchParams();
257
+ if (projectPath)
258
+ params.set('projectPath', projectPath);
259
+ const computed = await apiGet(`/api/insights/compute?${params}`);
260
+ if (!computed || computed.total_sessions === 0)
261
+ return null;
262
+ // Build the same text summary format the prompts expect
263
+ const topTools = Object.entries(computed.tool_counts ?? {})
264
+ .sort((a, b) => b[1] - a[1])
265
+ .slice(0, 10)
266
+ .map(([t, c]) => `${t}: ${c}`)
267
+ .join(', ');
268
+ const summary = [
269
+ `Sessions: ${computed.total_sessions}, Messages: ${computed.total_messages}, Hours: ${Math.round(computed.total_duration_hours)}`,
270
+ `Top tools: ${topTools}`,
271
+ computed.goal_categories && Object.keys(computed.goal_categories).length > 0
272
+ ? `Goals: ${JSON.stringify(computed.goal_categories)}`
273
+ : '',
274
+ computed.friction && Object.keys(computed.friction).length > 0
275
+ ? `Friction: ${JSON.stringify(computed.friction)}`
276
+ : '',
277
+ computed.outcomes && Object.keys(computed.outcomes).length > 0
278
+ ? `Outcomes: ${JSON.stringify(computed.outcomes)}`
279
+ : '',
280
+ computed.projects && Object.keys(computed.projects).length > 0
281
+ ? `Projects: ${JSON.stringify(computed.projects)}`
282
+ : '',
283
+ ].filter(Boolean).join('\n');
284
+ return {
285
+ summary,
286
+ stats: {
287
+ sessions: computed.total_sessions,
288
+ totalMessages: computed.total_messages,
289
+ totalHours: computed.total_duration_hours,
290
+ toolCounts: computed.tool_counts,
291
+ goalCounts: computed.goal_categories,
292
+ frictionCounts: computed.friction,
293
+ },
294
+ };
295
+ }
296
+ catch {
297
+ return null;
298
+ }
299
+ }
300
+ //# sourceMappingURL=process-insights.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-insights.js","sourceRoot":"","sources":["../../../src/lib/handlers/process-insights.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAC7G,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAW9D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,OAAwB,EACxB,OAAmB;IAEnB,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAClD,MAAM,GAAG,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEtD,0EAA0E;IAC1E,IAAI,MAAM,EAAE,SAAS,EAAE,CAAC;QACtB,OAAO,4BAA4B,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,EAAE,GAAG,CAAC,CAAC;IACtF,CAAC;IAED,GAAG,CAAC,2BAA2B,QAAQ,KAAK,CAAC,CAAC;IAE9C,2BAA2B;IAC3B,MAAM,QAAQ,CAAC,sBAAsB,QAAQ,WAAW,EAAE;QACxD,MAAM,EAAE,SAAS;QACjB,QAAQ,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE;KACnC,CAAC,CAAC;IAEH,qEAAqE;IACrE,gEAAgE;IAChE,uEAAuE;IACvE,0EAA0E;IAC1E,2EAA2E;IAC3E,sEAAsE;IACtE,IAAI,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,OAAO,CAChC,kCAAkC,EAClC,EAAE,CACH,CAAC;QACF,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;YACjB,GAAG,CAAC,6BAA6B,QAAQ,qBAAqB,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;IAAC,OAAO,SAAS,EAAE,CAAC;QACnB,GAAG,CAAC,yCAAyC,SAAS,YAAY,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACrH,CAAC;IAED,yEAAyE;IACzE,oEAAoE;IACpE,uEAAuE;IACvE,kEAAkE;IAClE,mCAAmC;IACnC,MAAM,IAAI,GAAG,MAAM,uBAAuB,CAAC,WAAW,CAAC,CAAC;IACxD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,iBAAiB,CACzB,oEAAoE,CACrE,CAAC;IACJ,CAAC;IAED,GAAG,CAAC,WAAW,IAAI,CAAC,KAAK,CAAC,QAAQ,0CAA0C,CAAC,CAAC;IAE9E,yEAAyE;IACzE,0EAA0E;IAC1E,uEAAuE;IACvE,uEAAuE;IACvE,gCAAgC;IAChC,MAAM,gBAAgB,GAAG,EAAE,CAAC;IAC5B,MAAM,cAAc,GAAG,GAAG,CAAC,CAAC,kCAAkC;IAC9D,IAAI,gBAAgB,EAAE,KAAK,MAAM,EAAE,CAAC;QAClC,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,IAAI,CAAC;YACH,OAAO,YAAY,GAAG,cAAc,EAAE,CAAC;gBACrC,MAAM,SAAS,GAAG,MAAM,MAAM,CAC5B,yCAAyC,gBAAgB,EAAE,CAC5D,CAAC;gBACF,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;oBAAE,MAAM;gBAClC,GAAG,CAAC,2BAA2B,SAAS,CAAC,MAAM,kCAAkC,YAAY,aAAa,CAAC,CAAC;gBAC5G,KAAK,MAAM,OAAO,IAAI,SAAS,EAAE,CAAC;oBAChC,IAAI,YAAY,IAAI,cAAc;wBAAE,MAAM;oBAC1C,IAAI,CAAC;wBACH,MAAM,MAAM,GAAG,MAAM,MAAM,CACzB,iBAAiB,OAAO,CAAC,EAAE,mBAAmB,CAC/C,CAAC;wBACF,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;4BACtB,YAAY,EAAE,CAAC;4BACf,SAAS;wBACX,CAAC;wBAED,MAAM,YAAY,GAAG,MAAM;6BACxB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,cAAc,IAAI,CAAC,CAAC,SAAS,KAAK,OAAO,CAAC;6BACtE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;6BACxC,MAAM,CAAC,OAAO,CAAC;6BACf,IAAI,CAAC,SAAS,CAAC,CAAC;wBAEnB,IAAI,CAAC,YAAY,EAAE,CAAC;4BAClB,YAAY,EAAE,CAAC;4BACf,SAAS;wBACX,CAAC;wBAED,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,uBAAuB,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;wBAC/E,IAAI,MAAM,EAAE,CAAC;4BACX,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,CACpC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,cAAc,IAAI,CAAC,CAAC,SAAS,KAAK,OAAO,CAC/D,CAAC,MAAM,CAAC;4BACT,MAAM,QAAQ,CAAC,iBAAiB,OAAO,CAAC,EAAE,SAAS,EAAE;gCACnD,QAAQ,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,GAAG,MAAM,EAAE;gCACnD,kBAAkB,EAAE,MAAM,CAAC,MAAM;gCACjC,iBAAiB,EAAE,gBAAgB;6BACpC,CAAC,CAAC;4BACH,GAAG,CAAC,oCAAoC,OAAO,CAAC,EAAE,KAAK,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC;wBAC1E,CAAC;wBACD,YAAY,EAAE,CAAC;oBACjB,CAAC;oBAAC,OAAO,UAAU,EAAE,CAAC;wBACpB,GAAG,CAAC,6CAA6C,OAAO,CAAC,EAAE,KAAK,UAAU,YAAY,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;wBACzI,YAAY,EAAE,CAAC,CAAC,qDAAqD;oBACvE,CAAC;gBACH,CAAC;gBACD,oEAAoE;gBACpE,sCAAsC;gBACtC,IAAI,SAAS,CAAC,MAAM,GAAG,gBAAgB;oBAAE,MAAM;YACjD,CAAC;QACH,CAAC;QAAC,OAAO,QAAQ,EAAE,CAAC;YAClB,GAAG,CAAC,4CAA4C,YAAY,cAAc,QAAQ,YAAY,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC/I,CAAC;IACH,CAAC;IAED,0CAA0C;IAC1C,MAAM,eAAe,GAA4B,EAAE,CAAC;IACpD,MAAM,YAAY,GAAG,sBAAsB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,qBAAqB;IAE7E,2EAA2E;IAC3E,0EAA0E;IAC1E,0EAA0E;IAC1E,2EAA2E;IAC3E,WAAW;IACX,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,sBAAsB,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvD,MAAM,IAAI,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAAC;QACvC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,YAAY,gBAAgB,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC;QAE/D,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,sBAAsB,QAAQ,WAAW,EAAE;gBACxD,MAAM,EAAE,SAAS;gBACjB,QAAQ,EAAE;oBACR,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,OAAO,EAAE,CAAC,GAAG,CAAC;oBACd,KAAK,EAAE,YAAY;oBACnB,OAAO,EAAE,EAAE,GAAG,eAAe,EAAE;iBAChC;aACF,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,QAAQ,EAAE,CAAC;YAClB,GAAG,CAAC,yCAAyC,IAAI,CAAC,IAAI,KAAK,QAAQ,YAAY,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAChI,CAAC;QAED,GAAG,CAAC,4BAA4B,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC7E,IAAI,MAAM,EAAE,CAAC;YACX,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC;YACpC,GAAG,CAAC,oBAAoB,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACvC,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,mBAAmB,IAAI,CAAC,IAAI,sBAAsB,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,mDAAmD;IACnD,GAAG,CAAC,MAAM,YAAY,IAAI,YAAY,6BAA6B,CAAC,CAAC;IACrE,MAAM,QAAQ,CAAC,sBAAsB,QAAQ,WAAW,EAAE;QACxD,MAAM,EAAE,SAAS;QACjB,QAAQ,EAAE;YACR,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,YAAY;YACrB,KAAK,EAAE,YAAY;YACnB,OAAO,EAAE,EAAE,GAAG,eAAe,EAAE;SAChC;KACF,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG;QACjB,IAAI,CAAC,OAAO;QACZ,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,oBAAoB,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;QAClG,eAAe,CAAC,iBAAiB,CAAC,CAAC,CAAC,kBAAkB,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;QAC9G,eAAe,CAAC,WAAW,CAAC,CAAC,CAAC,qBAAqB,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;QACrG,eAAe,CAAC,cAAc,CAAC,CAAC,CAAC,wBAAwB,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;KAC/G,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,kBAAkB,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;IACxE,IAAI,MAAM,EAAE,CAAC;QACX,eAAe,CAAC,WAAW,GAAG,MAAM,CAAC;IACvC,CAAC;IAED,GAAG,CAAC,wBAAwB,CAAC,CAAC;IAC9B,GAAG,CAAC,6CAA6C,QAAQ,EAAE,CAAC,CAAC;IAE7D,MAAM,MAAM,GAAG,EAAE,eAAe,EAAE,CAAC;IAEnC,yEAAyE;IACzE,sEAAsE;IACtE,MAAM,QAAQ,CAAC,sBAAsB,QAAQ,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;QACxF,GAAG,CAAC,0BAA0B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACpF,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;GAUG;AACH,KAAK,UAAU,4BAA4B,CACzC,IAA6C,EAC7C,GAA0B;IAE1B,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAErC,GAAG,CAAC,qBAAqB,SAAS,kBAAkB,CAAC,CAAC;IAEtD,MAAM,QAAQ,CAAC,sBAAsB,QAAQ,WAAW,EAAE;QACxD,MAAM,EAAE,SAAS;QACjB,QAAQ,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE;KACvC,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAyE,iBAAiB,SAAS,EAAE,CAAC,CAAC;IAEnI,qEAAqE;IACrE,IAAI,OAAO,CAAC,QAAQ,IAAK,OAAO,CAAC,QAAoC,CAAC,MAAM,KAAK,kBAAkB,EAAE,CAAC;QACpG,GAAG,CAAC,aAAa,SAAS,kDAAkD,CAAC,CAAC;QAC9E,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAClE,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,MAAM,CACzB,iBAAiB,SAAS,mBAAmB,CAC9C,CAAC;IAEF,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,iBAAiB,CAAC,uCAAuC,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,YAAY,GAAG,MAAM;SACxB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,cAAc,IAAI,CAAC,CAAC,SAAS,KAAK,OAAO,CAAC;SACtE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;SACxC,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,SAAS,CAAC,CAAC;IAEnB,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,iBAAiB,CAAC,mCAAmC,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,QAAQ,CAAC,sBAAsB,QAAQ,WAAW,EAAE;QACxD,MAAM,EAAE,SAAS;QACjB,QAAQ,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE;KAC3D,CAAC,CAAC;IAEH,qCAAqC;IACrC,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,EAAE,uBAAuB,EAAE,YAAY,CAAC,CAAC;IAElF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,qBAAqB,OAAO,CAAC,IAAI,yBAAyB,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,CACpC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,cAAc,IAAI,CAAC,CAAC,SAAS,KAAK,OAAO,CAC/D,CAAC,MAAM,CAAC;IACT,MAAM,QAAQ,CAAC,iBAAiB,SAAS,SAAS,EAAE;QAClD,QAAQ,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,GAAG,MAAM,EAAE;QACvE,kBAAkB,EAAE,MAAM,CAAC,MAAM;QACjC,iBAAiB,EAAE,gBAAgB;KACpC,CAAC,CAAC;IAEH,GAAG,CAAC,aAAa,SAAS,iBAAiB,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAE3D,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;AACnD,CAAC;AAED,8EAA8E;AAC9E,6EAA6E;AAC7E,8EAA8E;AAE9E,KAAK,UAAU,uBAAuB,CAAC,WAA0B;IAC/D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QACrC,IAAI,WAAW;YAAE,MAAM,CAAC,GAAG,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;QAExD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAa1B,yBAAyB,MAAM,EAAE,CAAC,CAAC;QAEtC,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,cAAc,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAE5D,wDAAwD;QACxD,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,IAAI,EAAE,CAAC;aACxD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;aAC3B,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;aACZ,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;aAC7B,IAAI,CAAC,IAAI,CAAC,CAAC;QAEd,MAAM,OAAO,GAAG;YACd,aAAa,QAAQ,CAAC,cAAc,eAAe,QAAQ,CAAC,cAAc,YAAY,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,oBAAoB,CAAC,EAAE;YACjI,cAAc,QAAQ,EAAE;YACxB,QAAQ,CAAC,eAAe,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,MAAM,GAAG,CAAC;gBAC1E,CAAC,CAAC,UAAU,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE;gBACtD,CAAC,CAAC,EAAE;YACN,QAAQ,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC;gBAC5D,CAAC,CAAC,aAAa,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;gBAClD,CAAC,CAAC,EAAE;YACN,QAAQ,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC;gBAC5D,CAAC,CAAC,aAAa,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;gBAClD,CAAC,CAAC,EAAE;YACN,QAAQ,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC;gBAC5D,CAAC,CAAC,aAAa,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;gBAClD,CAAC,CAAC,EAAE;SACP,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE7B,OAAO;YACL,OAAO;YACP,KAAK,EAAE;gBACL,QAAQ,EAAE,QAAQ,CAAC,cAAc;gBACjC,aAAa,EAAE,QAAQ,CAAC,cAAc;gBACtC,UAAU,EAAE,QAAQ,CAAC,oBAAoB;gBACzC,UAAU,EAAE,QAAQ,CAAC,WAAW;gBAChC,UAAU,EAAE,QAAQ,CAAC,eAAe;gBACpC,cAAc,EAAE,QAAQ,CAAC,QAAQ;aAClC;SACF,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { JobHelpers } from 'graphile-worker';
2
+ interface LaunchPayload {
3
+ mirrorId: string;
4
+ tool: string;
5
+ config: {
6
+ projectPath: string;
7
+ prompt?: string;
8
+ systemPrompt?: string;
9
+ model?: string;
10
+ mode: 'headless' | 'interactive';
11
+ timeout?: number;
12
+ };
13
+ }
14
+ /**
15
+ * Graphile-shaped handler for the `process_launch` task.
16
+ *
17
+ * Lifted from the legacy `eval-worker.ts:processLaunchJob`, with the queue
18
+ * plumbing stripped out:
19
+ * - no claim — Graphile already claimed the job before invoking us
20
+ * - no /complete POST — we return the launcher result (or throw)
21
+ * - no surrounding try/catch — errors propagate to Graphile's retry path
22
+ *
23
+ * The dynamic import of `@rulemetric/session` is intentional: it avoids
24
+ * loading the launcher (and its tool-specific subprocess wrappers) unless a
25
+ * launch job actually fires.
26
+ */
27
+ export declare function processLaunch(payload: LaunchPayload, helpers: JobHelpers): Promise<Record<string, unknown>>;
28
+ export {};
29
+ //# sourceMappingURL=process-launch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-launch.d.ts","sourceRoot":"","sources":["../../../src/lib/handlers/process-launch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAIlD,UAAU,aAAa;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QACN,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,UAAU,GAAG,aAAa,CAAC;QACjC,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA+BlC"}
@@ -0,0 +1,41 @@
1
+ import { apiPatch } from '../api-client.js';
2
+ import { PermanentJobError } from '../permanent-job-error.js';
3
+ /**
4
+ * Graphile-shaped handler for the `process_launch` task.
5
+ *
6
+ * Lifted from the legacy `eval-worker.ts:processLaunchJob`, with the queue
7
+ * plumbing stripped out:
8
+ * - no claim — Graphile already claimed the job before invoking us
9
+ * - no /complete POST — we return the launcher result (or throw)
10
+ * - no surrounding try/catch — errors propagate to Graphile's retry path
11
+ *
12
+ * The dynamic import of `@rulemetric/session` is intentional: it avoids
13
+ * loading the launcher (and its tool-specific subprocess wrappers) unless a
14
+ * launch job actually fires.
15
+ */
16
+ export async function processLaunch(payload, helpers) {
17
+ const { mirrorId, tool, config } = payload;
18
+ const log = (msg) => helpers.logger.info(msg);
19
+ // Fail-fast on retry. The launcher spawns subprocesses and registers a
20
+ // session — re-running on Graphile retry would create a second session
21
+ // with no link back to the first. Until the launcher path is properly
22
+ // idempotent (resume from sessionId persisted to the mirror row),
23
+ // surface the failure to the user instead of silently double-launching.
24
+ // See docs/audits/2026-05-07-worker-audit.md "Handlers not idempotent".
25
+ if (helpers.job.attempts > 1) {
26
+ throw new PermanentJobError(`process_launch is not safe to retry (attempt ${helpers.job.attempts}). Re-launch manually.`);
27
+ }
28
+ log(`Launching ${tool}...`);
29
+ const { getLauncher } = await import('@rulemetric/session');
30
+ const launcher = getLauncher(tool);
31
+ const launcherResult = await launcher.launch(config);
32
+ log(`Launch completed: sessionId=${launcherResult.sessionId}`);
33
+ const result = { ...launcherResult };
34
+ // Graphile discards task return values, so persist the launcher result
35
+ // (sessionId, etc.) onto the mirror row. Best-effort.
36
+ await apiPatch(`/api/launch-jobs/${mirrorId}/state`, { result }).catch((err) => {
37
+ log(`result persist failed: ${err instanceof Error ? err.message : String(err)}`);
38
+ });
39
+ return result;
40
+ }
41
+ //# sourceMappingURL=process-launch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-launch.js","sourceRoot":"","sources":["../../../src/lib/handlers/process-launch.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAe9D;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAAsB,EACtB,OAAmB;IAEnB,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC3C,MAAM,GAAG,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEtD,uEAAuE;IACvE,uEAAuE;IACvE,sEAAsE;IACtE,kEAAkE;IAClE,wEAAwE;IACxE,wEAAwE;IACxE,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,iBAAiB,CACzB,gDAAgD,OAAO,CAAC,GAAG,CAAC,QAAQ,wBAAwB,CAC7F,CAAC;IACJ,CAAC;IAED,GAAG,CAAC,aAAa,IAAI,KAAK,CAAC,CAAC;IAC5B,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;IAC5D,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,cAAc,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACrD,GAAG,CAAC,+BAA+B,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC;IAE/D,MAAM,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,CAAC;IAErC,uEAAuE;IACvE,sDAAsD;IACtD,MAAM,QAAQ,CAAC,oBAAoB,QAAQ,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;QACtF,GAAG,CAAC,0BAA0B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACpF,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { runLlmPrompt } from '../llm-client.js';
2
+ export interface ProcessSessionGoalPayload {
3
+ sessionId: string;
4
+ milestone: 5 | 20 | 50;
5
+ }
6
+ export interface ProcessSessionGoalResult {
7
+ skipped?: 'not_found' | 'stale' | 'recent' | 'no_content' | 'llm_empty';
8
+ goalSummary?: string;
9
+ }
10
+ export interface ProcessSessionGoalDeps {
11
+ db?: any;
12
+ llm?: typeof runLlmPrompt;
13
+ now?: () => number;
14
+ }
15
+ /**
16
+ * Fire-and-forget background job triggered at event milestones (5/20/50).
17
+ * Generates a short "what is the user trying to do" summary and writes it
18
+ * to `sessions.analysis.goalSummary` + `goalSummaryAt`, preserving any
19
+ * existing analysis keys.
20
+ *
21
+ * Guards:
22
+ * - stale: skip if no activity in last 30 min (session is dead, don't waste an LLM call)
23
+ * - recent: skip if we already generated a summary in the last 5 min (dedup milestone burst)
24
+ *
25
+ * No mirror table — this is fire-and-forget, similar to crons. Errors
26
+ * propagate so Graphile retries.
27
+ */
28
+ export declare function processSessionGoal(payload: ProcessSessionGoalPayload, deps?: ProcessSessionGoalDeps): Promise<ProcessSessionGoalResult>;
29
+ //# sourceMappingURL=process-session-goal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-session-goal.d.ts","sourceRoot":"","sources":["../../../src/lib/handlers/process-session-goal.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAUhD,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,YAAY,GAAG,WAAW,CAAC;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,CAAC,EAAE,GAAG,CAAC;IACT,GAAG,CAAC,EAAE,OAAO,YAAY,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,yBAAyB,EAClC,IAAI,GAAE,sBAA2B,GAChC,OAAO,CAAC,wBAAwB,CAAC,CAgEnC"}
@@ -0,0 +1,74 @@
1
+ import { eq, asc, sql } from 'drizzle-orm';
2
+ import { sessions, sessionEvents, getDb } from '@rulemetric/db';
3
+ import { runLlmPrompt } from '../llm-client.js';
4
+ const ACTIVE_WINDOW_MS = 30 * 60 * 1000;
5
+ const RECENT_WINDOW_MS = 5 * 60 * 1000;
6
+ const MAX_TURNS = 10;
7
+ const PROMPT = `Given these messages from an AI coding session, write a 1-2 sentence summary of what the user is trying to accomplish. Focus on the goal, not the steps. No preamble.
8
+
9
+ Return strict JSON in the form: {"goalSummary": "..."}`;
10
+ /**
11
+ * Fire-and-forget background job triggered at event milestones (5/20/50).
12
+ * Generates a short "what is the user trying to do" summary and writes it
13
+ * to `sessions.analysis.goalSummary` + `goalSummaryAt`, preserving any
14
+ * existing analysis keys.
15
+ *
16
+ * Guards:
17
+ * - stale: skip if no activity in last 30 min (session is dead, don't waste an LLM call)
18
+ * - recent: skip if we already generated a summary in the last 5 min (dedup milestone burst)
19
+ *
20
+ * No mirror table — this is fire-and-forget, similar to crons. Errors
21
+ * propagate so Graphile retries.
22
+ */
23
+ export async function processSessionGoal(payload, deps = {}) {
24
+ const db = deps.db ?? getDb();
25
+ const llm = deps.llm ?? runLlmPrompt;
26
+ const now = deps.now ?? Date.now;
27
+ const [s] = await db
28
+ .select()
29
+ .from(sessions)
30
+ .where(eq(sessions.id, payload.sessionId))
31
+ .limit(1);
32
+ if (!s)
33
+ return { skipped: 'not_found' };
34
+ // Single-roundtrip MAX(timestamp) — fall back to startedAt if no events yet.
35
+ const lastAtRows = await db.execute(sql `SELECT MAX(timestamp) AS last_at FROM session_events WHERE session_id = ${payload.sessionId}`);
36
+ const lastAtRaw = lastAtRows?.[0]?.last_at ?? s.startedAt;
37
+ const lastAt = new Date(lastAtRaw).getTime();
38
+ if (now() - lastAt > ACTIVE_WINDOW_MS)
39
+ return { skipped: 'stale' };
40
+ const analysis = (s.analysis ?? {});
41
+ const lastGoalAt = analysis.goalSummaryAt
42
+ ? new Date(analysis.goalSummaryAt).getTime()
43
+ : 0;
44
+ if (lastGoalAt && now() - lastGoalAt < RECENT_WINDOW_MS) {
45
+ return { skipped: 'recent' };
46
+ }
47
+ const events = await db
48
+ .select()
49
+ .from(sessionEvents)
50
+ .where(eq(sessionEvents.sessionId, payload.sessionId))
51
+ .orderBy(asc(sessionEvents.timestamp))
52
+ .limit(MAX_TURNS * 2);
53
+ const turns = events
54
+ .filter((e) => e.content &&
55
+ (e.eventType === 'user_message' || e.eventType === 'assistant_response'))
56
+ .map((e) => `${e.eventType === 'user_message' ? 'USER' : 'ASSISTANT'}: ${e.content}`)
57
+ .join('\n\n');
58
+ if (!turns)
59
+ return { skipped: 'no_content' };
60
+ // Let LLM errors throw — Graphile will retry.
61
+ const parsed = await llm(PROMPT, turns, 200);
62
+ const summary = typeof parsed?.goalSummary === 'string' ? parsed.goalSummary.trim() : '';
63
+ if (!summary)
64
+ return { skipped: 'llm_empty' };
65
+ const goalSummaryAt = new Date(now()).toISOString();
66
+ await db
67
+ .update(sessions)
68
+ .set({
69
+ analysis: { ...analysis, goalSummary: summary, goalSummaryAt },
70
+ })
71
+ .where(eq(sessions.id, payload.sessionId));
72
+ return { goalSummary: summary };
73
+ }
74
+ //# sourceMappingURL=process-session-goal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-session-goal.js","sourceRoot":"","sources":["../../../src/lib/handlers/process-session-goal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=process-session-goal.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-session-goal.test.d.ts","sourceRoot":"","sources":["../../../src/lib/handlers/process-session-goal.test.ts"],"names":[],"mappings":""}