@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.
- package/README.md +66 -0
- package/dist/base-command.d.ts.map +1 -1
- package/dist/base-command.js +15 -0
- package/dist/base-command.js.map +1 -1
- package/dist/commands/auth/login.d.ts.map +1 -1
- package/dist/commands/auth/login.js +7 -0
- package/dist/commands/auth/login.js.map +1 -1
- package/dist/commands/crons/list.d.ts +7 -0
- package/dist/commands/crons/list.d.ts.map +1 -0
- package/dist/commands/crons/list.js +24 -0
- package/dist/commands/crons/list.js.map +1 -0
- package/dist/commands/crons/run.d.ts +13 -0
- package/dist/commands/crons/run.d.ts.map +1 -0
- package/dist/commands/crons/run.js +48 -0
- package/dist/commands/crons/run.js.map +1 -0
- package/dist/commands/evals/listen.d.ts +28 -2
- package/dist/commands/evals/listen.d.ts.map +1 -1
- package/dist/commands/evals/listen.js +159 -25
- package/dist/commands/evals/listen.js.map +1 -1
- package/dist/commands/evals/listen.test.d.ts +2 -0
- package/dist/commands/evals/listen.test.d.ts.map +1 -0
- package/dist/commands/evals/listen.test.js +48 -0
- package/dist/commands/evals/listen.test.js.map +1 -0
- package/dist/commands/gateway/ensure.js +1 -1
- package/dist/commands/gateway/ensure.js.map +1 -1
- package/dist/commands/hooks/install.d.ts.map +1 -1
- package/dist/commands/hooks/install.js +40 -3
- package/dist/commands/hooks/install.js.map +1 -1
- package/dist/commands/hooks/run.js +1 -1
- package/dist/commands/hooks/run.js.map +1 -1
- package/dist/commands/hooks/uninstall.d.ts.map +1 -1
- package/dist/commands/hooks/uninstall.js +9 -1
- package/dist/commands/hooks/uninstall.js.map +1 -1
- package/dist/commands/org/current.d.ts +7 -0
- package/dist/commands/org/current.d.ts.map +1 -0
- package/dist/commands/org/current.js +70 -0
- package/dist/commands/org/current.js.map +1 -0
- package/dist/commands/org/list.d.ts +7 -0
- package/dist/commands/org/list.d.ts.map +1 -0
- package/dist/commands/org/list.js +37 -0
- package/dist/commands/org/list.js.map +1 -0
- package/dist/commands/org/switch.d.ts +13 -0
- package/dist/commands/org/switch.d.ts.map +1 -0
- package/dist/commands/org/switch.js +52 -0
- package/dist/commands/org/switch.js.map +1 -0
- package/dist/commands/proxy/restart.d.ts +11 -0
- package/dist/commands/proxy/restart.d.ts.map +1 -0
- package/dist/commands/proxy/restart.js +36 -0
- package/dist/commands/proxy/restart.js.map +1 -0
- package/dist/commands/proxy/start.d.ts.map +1 -1
- package/dist/commands/proxy/start.js +4 -0
- package/dist/commands/proxy/start.js.map +1 -1
- package/dist/commands/service/install.d.ts +16 -0
- package/dist/commands/service/install.d.ts.map +1 -1
- package/dist/commands/service/install.js +145 -6
- package/dist/commands/service/install.js.map +1 -1
- package/dist/commands/service/uninstall.d.ts.map +1 -1
- package/dist/commands/service/uninstall.js +28 -0
- package/dist/commands/service/uninstall.js.map +1 -1
- package/dist/commands/sessions/start.d.ts.map +1 -1
- package/dist/commands/sessions/start.js +5 -0
- package/dist/commands/sessions/start.js.map +1 -1
- package/dist/commands/signals/report.d.ts +10 -0
- package/dist/commands/signals/report.d.ts.map +1 -0
- package/dist/commands/signals/report.js +35 -0
- package/dist/commands/signals/report.js.map +1 -0
- package/dist/commands/signals/report.test.d.ts +2 -0
- package/dist/commands/signals/report.test.d.ts.map +1 -0
- package/dist/commands/signals/report.test.js +65 -0
- package/dist/commands/signals/report.test.js.map +1 -0
- package/dist/commands/skills/export-approved.d.ts +10 -0
- package/dist/commands/skills/export-approved.d.ts.map +1 -0
- package/dist/commands/skills/export-approved.js +31 -0
- package/dist/commands/skills/export-approved.js.map +1 -0
- package/dist/commands/skills/publish.d.ts +17 -0
- package/dist/commands/skills/publish.d.ts.map +1 -0
- package/dist/commands/skills/publish.js +80 -0
- package/dist/commands/skills/publish.js.map +1 -0
- package/dist/commands/skills/update.d.ts +3 -0
- package/dist/commands/skills/update.d.ts.map +1 -1
- package/dist/commands/skills/update.js +17 -6
- package/dist/commands/skills/update.js.map +1 -1
- package/dist/commands/suggestions/accept.d.ts +16 -0
- package/dist/commands/suggestions/accept.d.ts.map +1 -0
- package/dist/commands/suggestions/accept.js +61 -0
- package/dist/commands/suggestions/accept.js.map +1 -0
- package/dist/commands/suggestions/accept.test.d.ts +2 -0
- package/dist/commands/suggestions/accept.test.d.ts.map +1 -0
- package/dist/commands/suggestions/accept.test.js +52 -0
- package/dist/commands/suggestions/accept.test.js.map +1 -0
- package/dist/commands/suggestions/dismiss.d.ts +10 -0
- package/dist/commands/suggestions/dismiss.d.ts.map +1 -0
- package/dist/commands/suggestions/dismiss.js +26 -0
- package/dist/commands/suggestions/dismiss.js.map +1 -0
- package/dist/commands/suggestions/list.d.ts +10 -0
- package/dist/commands/suggestions/list.d.ts.map +1 -0
- package/dist/commands/suggestions/list.js +40 -0
- package/dist/commands/suggestions/list.js.map +1 -0
- package/dist/commands/suggestions/refresh.d.ts +7 -0
- package/dist/commands/suggestions/refresh.d.ts.map +1 -0
- package/dist/commands/suggestions/refresh.js +11 -0
- package/dist/commands/suggestions/refresh.js.map +1 -0
- package/dist/dashboard/Dashboard.d.ts.map +1 -1
- package/dist/dashboard/Dashboard.js +7 -1
- package/dist/dashboard/Dashboard.js.map +1 -1
- package/dist/lib/active-org-refresh.d.ts +22 -0
- package/dist/lib/active-org-refresh.d.ts.map +1 -0
- package/dist/lib/active-org-refresh.js +108 -0
- package/dist/lib/active-org-refresh.js.map +1 -0
- package/dist/lib/active-org-refresh.test.d.ts +2 -0
- package/dist/lib/active-org-refresh.test.d.ts.map +1 -0
- package/dist/lib/active-org-refresh.test.js +190 -0
- package/dist/lib/active-org-refresh.test.js.map +1 -0
- package/dist/lib/active-org.d.ts +43 -0
- package/dist/lib/active-org.d.ts.map +1 -0
- package/dist/lib/active-org.js +96 -0
- package/dist/lib/active-org.js.map +1 -0
- package/dist/lib/active-org.test.d.ts +2 -0
- package/dist/lib/active-org.test.d.ts.map +1 -0
- package/dist/lib/active-org.test.js +147 -0
- package/dist/lib/active-org.test.js.map +1 -0
- package/dist/lib/cron-registry.d.ts +47 -0
- package/dist/lib/cron-registry.d.ts.map +1 -0
- package/dist/lib/cron-registry.js +102 -0
- package/dist/lib/cron-registry.js.map +1 -0
- package/dist/lib/cron-registry.test.d.ts +2 -0
- package/dist/lib/cron-registry.test.d.ts.map +1 -0
- package/dist/lib/cron-registry.test.js +136 -0
- package/dist/lib/cron-registry.test.js.map +1 -0
- package/dist/lib/cron-runner.d.ts +16 -0
- package/dist/lib/cron-runner.d.ts.map +1 -0
- package/dist/lib/cron-runner.js +68 -0
- package/dist/lib/cron-runner.js.map +1 -0
- package/dist/lib/detect-languages.d.ts +7 -0
- package/dist/lib/detect-languages.d.ts.map +1 -0
- package/dist/lib/detect-languages.js +113 -0
- package/dist/lib/detect-languages.js.map +1 -0
- package/dist/lib/detect-languages.test.d.ts +2 -0
- package/dist/lib/detect-languages.test.d.ts.map +1 -0
- package/dist/lib/detect-languages.test.js +29 -0
- package/dist/lib/detect-languages.test.js.map +1 -0
- package/dist/lib/eval-executor.d.ts.map +1 -1
- package/dist/lib/eval-executor.js +5 -2
- package/dist/lib/eval-executor.js.map +1 -1
- package/dist/lib/gateway-entry.js +4 -1
- package/dist/lib/gateway-entry.js.map +1 -1
- package/dist/lib/gateway-lifecycle.d.ts +1 -1
- package/dist/lib/gateway-lifecycle.d.ts.map +1 -1
- package/dist/lib/gateway-lifecycle.js +13 -2
- package/dist/lib/gateway-lifecycle.js.map +1 -1
- package/dist/lib/gateway.d.ts +4 -5
- package/dist/lib/gateway.d.ts.map +1 -1
- package/dist/lib/gateway.js +68 -2
- package/dist/lib/gateway.js.map +1 -1
- package/dist/lib/graphile-worker-runner.d.ts +124 -0
- package/dist/lib/graphile-worker-runner.d.ts.map +1 -0
- package/dist/lib/graphile-worker-runner.js +530 -0
- package/dist/lib/graphile-worker-runner.js.map +1 -0
- package/dist/lib/graphile-worker-runner.test.d.ts +2 -0
- package/dist/lib/graphile-worker-runner.test.d.ts.map +1 -0
- package/dist/lib/graphile-worker-runner.test.js +153 -0
- package/dist/lib/graphile-worker-runner.test.js.map +1 -0
- package/dist/lib/handlers/cron-close-stale-sessions.d.ts +3 -0
- package/dist/lib/handlers/cron-close-stale-sessions.d.ts.map +1 -0
- package/dist/lib/handlers/cron-close-stale-sessions.js +8 -0
- package/dist/lib/handlers/cron-close-stale-sessions.js.map +1 -0
- package/dist/lib/handlers/cron-data-integrity-audit.d.ts +3 -0
- package/dist/lib/handlers/cron-data-integrity-audit.d.ts.map +1 -0
- package/dist/lib/handlers/cron-data-integrity-audit.js +16 -0
- package/dist/lib/handlers/cron-data-integrity-audit.js.map +1 -0
- package/dist/lib/handlers/cron-refresh-skills.d.ts +3 -0
- package/dist/lib/handlers/cron-refresh-skills.d.ts.map +1 -0
- package/dist/lib/handlers/cron-refresh-skills.js +91 -0
- package/dist/lib/handlers/cron-refresh-skills.js.map +1 -0
- package/dist/lib/handlers/cron-refresh-skills.test.d.ts +2 -0
- package/dist/lib/handlers/cron-refresh-skills.test.d.ts.map +1 -0
- package/dist/lib/handlers/cron-refresh-skills.test.js +114 -0
- package/dist/lib/handlers/cron-refresh-skills.test.js.map +1 -0
- package/dist/lib/handlers/cron-regenerate-insights.d.ts +3 -0
- package/dist/lib/handlers/cron-regenerate-insights.d.ts.map +1 -0
- package/dist/lib/handlers/cron-regenerate-insights.js +6 -0
- package/dist/lib/handlers/cron-regenerate-insights.js.map +1 -0
- package/dist/lib/handlers/cron-suggest-instructions.d.ts +3 -0
- package/dist/lib/handlers/cron-suggest-instructions.d.ts.map +1 -0
- package/dist/lib/handlers/cron-suggest-instructions.js +159 -0
- package/dist/lib/handlers/cron-suggest-instructions.js.map +1 -0
- package/dist/lib/handlers/cron-suggest-instructions.test.d.ts +2 -0
- package/dist/lib/handlers/cron-suggest-instructions.test.d.ts.map +1 -0
- package/dist/lib/handlers/cron-suggest-instructions.test.js +80 -0
- package/dist/lib/handlers/cron-suggest-instructions.test.js.map +1 -0
- package/dist/lib/handlers/process-conversation.d.ts +35 -0
- package/dist/lib/handlers/process-conversation.d.ts.map +1 -0
- package/dist/lib/handlers/process-conversation.js +35 -0
- package/dist/lib/handlers/process-conversation.js.map +1 -0
- package/dist/lib/handlers/process-eval.d.ts +32 -0
- package/dist/lib/handlers/process-eval.d.ts.map +1 -0
- package/dist/lib/handlers/process-eval.js +207 -0
- package/dist/lib/handlers/process-eval.js.map +1 -0
- package/dist/lib/handlers/process-insights.d.ts +39 -0
- package/dist/lib/handlers/process-insights.d.ts.map +1 -0
- package/dist/lib/handlers/process-insights.js +300 -0
- package/dist/lib/handlers/process-insights.js.map +1 -0
- package/dist/lib/handlers/process-launch.d.ts +29 -0
- package/dist/lib/handlers/process-launch.d.ts.map +1 -0
- package/dist/lib/handlers/process-launch.js +41 -0
- package/dist/lib/handlers/process-launch.js.map +1 -0
- package/dist/lib/handlers/process-session-goal.d.ts +29 -0
- package/dist/lib/handlers/process-session-goal.d.ts.map +1 -0
- package/dist/lib/handlers/process-session-goal.js +74 -0
- package/dist/lib/handlers/process-session-goal.js.map +1 -0
- package/dist/lib/handlers/process-session-goal.test.d.ts +2 -0
- package/dist/lib/handlers/process-session-goal.test.d.ts.map +1 -0
- package/dist/lib/handlers/process-session-goal.test.js +134 -0
- package/dist/lib/handlers/process-session-goal.test.js.map +1 -0
- package/dist/lib/hooks-config.d.ts +28 -4
- package/dist/lib/hooks-config.d.ts.map +1 -1
- package/dist/lib/hooks-config.js +138 -10
- package/dist/lib/hooks-config.js.map +1 -1
- package/dist/lib/llm-client.d.ts.map +1 -1
- package/dist/lib/llm-client.js +149 -16
- package/dist/lib/llm-client.js.map +1 -1
- package/dist/lib/mirror-writer-db.d.ts +12 -0
- package/dist/lib/mirror-writer-db.d.ts.map +1 -0
- package/dist/lib/mirror-writer-db.js +51 -0
- package/dist/lib/mirror-writer-db.js.map +1 -0
- package/dist/lib/mirror-writer.d.ts +35 -0
- package/dist/lib/mirror-writer.d.ts.map +1 -0
- package/dist/lib/mirror-writer.js +27 -0
- package/dist/lib/mirror-writer.js.map +1 -0
- package/dist/lib/mirror-writer.test.d.ts +2 -0
- package/dist/lib/mirror-writer.test.d.ts.map +1 -0
- package/dist/lib/mirror-writer.test.js +47 -0
- package/dist/lib/mirror-writer.test.js.map +1 -0
- package/dist/lib/permanent-job-error.d.ts +17 -0
- package/dist/lib/permanent-job-error.d.ts.map +1 -0
- package/dist/lib/permanent-job-error.js +23 -0
- package/dist/lib/permanent-job-error.js.map +1 -0
- package/dist/lib/permanent-job-error.test.d.ts +2 -0
- package/dist/lib/permanent-job-error.test.d.ts.map +1 -0
- package/dist/lib/permanent-job-error.test.js +29 -0
- package/dist/lib/permanent-job-error.test.js.map +1 -0
- package/dist/lib/version-check.d.ts +11 -0
- package/dist/lib/version-check.d.ts.map +1 -0
- package/dist/lib/version-check.js +106 -0
- package/dist/lib/version-check.js.map +1 -0
- package/oclif.manifest.json +611 -81
- package/package.json +21 -5
- package/dist/commands/insights/sync.d.ts +0 -17
- package/dist/commands/insights/sync.d.ts.map +0 -1
- package/dist/commands/insights/sync.js +0 -477
- package/dist/commands/insights/sync.js.map +0 -1
- package/dist/lib/eval-worker.d.ts +0 -12
- package/dist/lib/eval-worker.d.ts.map +0 -1
- package/dist/lib/eval-worker.js +0 -395
- package/dist/lib/eval-worker.js.map +0 -1
- package/dist/lib/insights-worker.d.ts +0 -10
- package/dist/lib/insights-worker.d.ts.map +0 -1
- package/dist/lib/insights-worker.js +0 -329
- 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":"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"}
|
|
@@ -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 @@
|
|
|
1
|
+
{"version":3,"file":"process-session-goal.test.d.ts","sourceRoot":"","sources":["../../../src/lib/handlers/process-session-goal.test.ts"],"names":[],"mappings":""}
|