pi-mega-compact 0.12.1 → 0.12.2
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/dist/extensions/dashboard-server/gather-debug.js +63 -0
- package/dist/extensions/dashboard-server/routes-setup.js +30 -4
- package/dist/src/memoryGraph/sources.js +42 -15
- package/dist/src/memoryGraph.js +17 -5
- package/dist/src/store/turns/schema.js +18 -2
- package/extensions/dashboard-client/dist/assets/{AchievementTiles-Cwg_Ab8R.js → AchievementTiles-Cntu89Xw.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AchievementTiles-Cwg_Ab8R.js.map → AchievementTiles-Cntu89Xw.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{AchievementsTab-Dk32-4Ha.js → AchievementsTab-4ckY9z4n.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AchievementsTab-Dk32-4Ha.js.map → AchievementsTab-4ckY9z4n.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-B9zR8UgD.js → CacheTab-BfGJkiaf.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-B9zR8UgD.js.map → CacheTab-BfGJkiaf.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ConfigTab-DYRl3mJZ.js → ConfigTab-DV08rHoV.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ConfigTab-DYRl3mJZ.js.map → ConfigTab-DV08rHoV.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-r_zXcrmZ.js → EventsTab-DHytZ0xs.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-r_zXcrmZ.js.map → EventsTab-DHytZ0xs.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{GameTab-DjwA_Q1v.js → GameTab-Ofpoz-6s.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{GameTab-DjwA_Q1v.js.map → GameTab-Ofpoz-6s.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-BjH73kQr.js → HealthTab-B0klAxD8.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-BjH73kQr.js.map → HealthTab-B0klAxD8.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DzeSq9vK.js → MaintenanceTab-CHKh3KPI.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DzeSq9vK.js.map → MaintenanceTab-CHKh3KPI.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-BU9iM3ir.js → MemoryMapTab-B6FLQ-32.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-BU9iM3ir.js.map → MemoryMapTab-B6FLQ-32.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-lMVodOwY.js → MetricsTab-DvOjTDkW.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-lMVodOwY.js.map → MetricsTab-DvOjTDkW.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-D-laa6CW.js → OverviewTab-CqbONEwi.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-D-laa6CW.js.map → OverviewTab-CqbONEwi.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-5Pn2cOOO.js → ReposTab-sc1GLn7i.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-5Pn2cOOO.js.map → ReposTab-sc1GLn7i.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-Dtdmvl2C.js → SessionsTab-C5gB45oc.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-Dtdmvl2C.js.map → SessionsTab-C5gB45oc.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/SetupTab-BEEX4OF5.js +2 -0
- package/extensions/dashboard-client/dist/assets/SetupTab-BEEX4OF5.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-B5Lw6fTp.js → TimeSavedCard-BBwvb4cC.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-B5Lw6fTp.js.map → TimeSavedCard-BBwvb4cC.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TopicsTab-Ds1IHhPw.js → TopicsTab-BsCf512_.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TopicsTab-Ds1IHhPw.js.map → TopicsTab-BsCf512_.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-CG2RDRmh.js → TurnsTab-C3ti7NB5.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-CG2RDRmh.js.map → TurnsTab-C3ti7NB5.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-C9dTXvDQ.js → index-BEeaI9uo.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{index-C9dTXvDQ.js.map → index-BEeaI9uo.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-B6l8O-yi.js → useSSE-BMSSO9wB.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-B6l8O-yi.js.map → useSSE-BMSSO9wB.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/tabs/SetupTab.tsx +19 -0
- package/extensions/dashboard-server/api-contracts/maintenance.ts +1 -0
- package/extensions/dashboard-server/api-contracts/setup.ts +7 -1
- package/extensions/dashboard-server/gather-debug.ts +63 -0
- package/extensions/dashboard-server/routes-setup.ts +28 -4
- package/package.json +1 -1
- package/src/memoryGraph/sources.ts +49 -15
- package/src/memoryGraph.ts +17 -5
- package/src/store/turns/schema.ts +18 -2
- package/extensions/dashboard-client/dist/assets/SetupTab-rKGgWpI0.js +0 -2
- package/extensions/dashboard-client/dist/assets/SetupTab-rKGgWpI0.js.map +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as e}from"./index-
|
|
2
|
-
//# sourceMappingURL=useSSE-
|
|
1
|
+
import{r as e}from"./index-BEeaI9uo.js";function R(i={}){const{maxEvents:c=500,maxReconnects:l=5}=i,[f,v]=e.useState([]),[E,n]=e.useState("connecting"),[S,p]=e.useState(0),[m,d]=e.useState(null),o=e.useRef(0),s=e.useRef(null),r=e.useCallback(()=>{s.current&&s.current.close(),n("connecting");const t=new EventSource("/api/events");s.current=t,t.onopen=()=>{n("connected"),o.current=0},t.onmessage=g=>{try{const x=JSON.parse(g.data);v(u=>{const a=[...u,x];return a.length>c?a.slice(-c):a}),p(u=>u+1),d(Date.now())}catch{}},t.onerror=()=>{t.close(),o.current<l?(o.current+=1,n("connecting"),setTimeout(r,1e3)):n("disconnected")}},[c,l]);return e.useEffect(()=>(r(),()=>{s.current?.close()}),[r]),{events:f,status:E,eventCount:S,lastEventAt:m}}export{R as u};
|
|
2
|
+
//# sourceMappingURL=useSSE-BMSSO9wB.js.map
|
package/extensions/dashboard-client/dist/assets/{useSSE-B6l8O-yi.js.map → useSSE-BMSSO9wB.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSSE-
|
|
1
|
+
{"version":3,"file":"useSSE-BMSSO9wB.js","sources":["../../src/hooks/useSSE.ts"],"sourcesContent":["/**\n * dashboard-client/src/hooks/useSSE.ts — Server-Sent Events hook.\n *\n * Connects to /api/events, provides real-time event stream.\n * SPRINT-B1: basic connection. SPRINT-D1: reconnect with backoff.\n */\n\nimport { useState, useEffect, useRef, useCallback } from 'react';\nimport type { SseEvent } from '../../../dashboard-server/api-contracts';\n\nexport type SseStatus = 'connecting' | 'connected' | 'disconnected' | 'error';\n\nexport interface UseSSEResult {\n events: SseEvent[];\n status: SseStatus;\n eventCount: number;\n lastEventAt: number | null;\n}\n\nexport interface UseSSEOptions {\n /** Max events to keep in memory (ring buffer). */\n maxEvents?: number;\n /** Max reconnect attempts before giving up. */\n maxReconnects?: number;\n}\n\n// SPRINT-D1-REMAINING: exponential backoff (1s → 2s → 4s → max 30s).\n// SPRINT-D1-REMAINING: after 5 failures, set status='disconnected'.\n\nexport function useSSE(options: UseSSEOptions = {}): UseSSEResult {\n const { maxEvents = 500, maxReconnects = 5 } = options;\n const [events, setEvents] = useState<SseEvent[]>([]);\n const [status, setStatus] = useState<SseStatus>('connecting');\n const [eventCount, setEventCount] = useState(0);\n const [lastEventAt, setLastEventAt] = useState<number | null>(null);\n const reconnectCount = useRef(0);\n const sourceRef = useRef<EventSource | null>(null);\n\n const connect = useCallback(() => {\n if (sourceRef.current) {\n sourceRef.current.close();\n }\n setStatus('connecting');\n const source = new EventSource('/api/events');\n sourceRef.current = source;\n\n source.onopen = () => {\n setStatus('connected');\n reconnectCount.current = 0;\n };\n\n source.onmessage = (event) => {\n try {\n const parsed = JSON.parse(event.data) as SseEvent;\n setEvents(prev => {\n const next = [...prev, parsed];\n return next.length > maxEvents ? next.slice(-maxEvents) : next;\n });\n setEventCount(c => c + 1);\n setLastEventAt(Date.now());\n } catch {\n // non-fatal: skip malformed events\n }\n };\n\n source.onerror = () => {\n source.close();\n if (reconnectCount.current < maxReconnects) {\n reconnectCount.current += 1;\n setStatus('connecting');\n // SPRINT-D1: use exponential backoff instead of fixed delay\n setTimeout(connect, 1000);\n } else {\n setStatus('disconnected');\n }\n };\n }, [maxEvents, maxReconnects]);\n\n useEffect(() => {\n connect();\n return () => {\n sourceRef.current?.close();\n };\n }, [connect]);\n\n return { events, status, eventCount, lastEventAt };\n}\n"],"names":["useSSE","options","maxEvents","maxReconnects","events","setEvents","useState","status","setStatus","eventCount","setEventCount","lastEventAt","setLastEventAt","reconnectCount","useRef","sourceRef","connect","useCallback","source","event","parsed","prev","next","c","useEffect"],"mappings":"wCA6BO,SAASA,EAAOC,EAAyB,GAAkB,CAChE,KAAM,CAAE,UAAAC,EAAY,IAAK,cAAAC,EAAgB,GAAMF,EACzC,CAACG,EAAQC,CAAS,EAAIC,EAAAA,SAAqB,CAAA,CAAE,EAC7C,CAACC,EAAQC,CAAS,EAAIF,EAAAA,SAAoB,YAAY,EACtD,CAACG,EAAYC,CAAa,EAAIJ,EAAAA,SAAS,CAAC,EACxC,CAACK,EAAaC,CAAc,EAAIN,EAAAA,SAAwB,IAAI,EAC5DO,EAAiBC,EAAAA,OAAO,CAAC,EACzBC,EAAYD,EAAAA,OAA2B,IAAI,EAE3CE,EAAUC,EAAAA,YAAY,IAAM,CAC5BF,EAAU,SACZA,EAAU,QAAQ,MAAA,EAEpBP,EAAU,YAAY,EACtB,MAAMU,EAAS,IAAI,YAAY,aAAa,EAC5CH,EAAU,QAAUG,EAEpBA,EAAO,OAAS,IAAM,CACpBV,EAAU,WAAW,EACrBK,EAAe,QAAU,CAC3B,EAEAK,EAAO,UAAaC,GAAU,CAC5B,GAAI,CACF,MAAMC,EAAS,KAAK,MAAMD,EAAM,IAAI,EACpCd,EAAUgB,GAAQ,CAChB,MAAMC,EAAO,CAAC,GAAGD,EAAMD,CAAM,EAC7B,OAAOE,EAAK,OAASpB,EAAYoB,EAAK,MAAM,CAACpB,CAAS,EAAIoB,CAC5D,CAAC,EACDZ,EAAca,GAAKA,EAAI,CAAC,EACxBX,EAAe,KAAK,KAAK,CAC3B,MAAQ,CAER,CACF,EAEAM,EAAO,QAAU,IAAM,CACrBA,EAAO,MAAA,EACHL,EAAe,QAAUV,GAC3BU,EAAe,SAAW,EAC1BL,EAAU,YAAY,EAEtB,WAAWQ,EAAS,GAAI,GAExBR,EAAU,cAAc,CAE5B,CACF,EAAG,CAACN,EAAWC,CAAa,CAAC,EAE7BqB,OAAAA,EAAAA,UAAU,KACRR,EAAA,EACO,IAAM,CACXD,EAAU,SAAS,MAAA,CACrB,GACC,CAACC,CAAO,CAAC,EAEL,CAAE,OAAAZ,EAAQ,OAAAG,EAAQ,WAAAE,EAAY,YAAAE,CAAA,CACvC"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>mega-compact dashboard</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/index-
|
|
7
|
+
<script type="module" crossorigin src="./assets/index-BEeaI9uo.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="./assets/index-CUNn22iV.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
@@ -207,6 +207,25 @@ export default function SetupTab(): React.ReactElement {
|
|
|
207
207
|
{embedderLabel(status.currentEmbedder)}
|
|
208
208
|
</span>
|
|
209
209
|
</div>
|
|
210
|
+
{"configuredEmbedder" in status && status.configuredEmbedder !== status.currentEmbedder && (
|
|
211
|
+
<div style={styles.warning}>
|
|
212
|
+
<strong>Configured but not active:</strong>{" "}
|
|
213
|
+
{embedderLabel(status.configuredEmbedder)} is configured in
|
|
214
|
+
.mega-compact.env but not yet loaded.{" "}
|
|
215
|
+
<strong>Restart pi</strong> to activate it.
|
|
216
|
+
{status.configuredUrl && (
|
|
217
|
+
<> ({status.configuredUrl})</>
|
|
218
|
+
)}
|
|
219
|
+
</div>
|
|
220
|
+
)}
|
|
221
|
+
{"restartRequired" in status && status.restartRequired && (
|
|
222
|
+
<div style={{ ...styles.row, color: "#ff9800" }}>
|
|
223
|
+
<span style={styles.label}>Status:</span>
|
|
224
|
+
<span style={styles.value}>
|
|
225
|
+
Restart required to activate the configured embedder
|
|
226
|
+
</span>
|
|
227
|
+
</div>
|
|
228
|
+
)}
|
|
210
229
|
<div style={styles.row}>
|
|
211
230
|
<span style={styles.label}>Embedding URL:</span>
|
|
212
231
|
<span style={styles.value}>
|
|
@@ -13,8 +13,14 @@
|
|
|
13
13
|
* Returns the current embedder configuration read from the environment.
|
|
14
14
|
*/
|
|
15
15
|
export interface SetupStatusResponse {
|
|
16
|
-
/** Which embedder implementation is currently active. */
|
|
16
|
+
/** Which embedder implementation is currently active in the running process. */
|
|
17
17
|
readonly currentEmbedder: "trigram" | "http" | "minilm" | "unknown";
|
|
18
|
+
/** Which embedder was configured via the dashboard (from .mega-compact.env). */
|
|
19
|
+
readonly configuredEmbedder: "trigram" | "http" | "minilm";
|
|
20
|
+
/** The URL configured in .mega-compact.env, if any. */
|
|
21
|
+
readonly configuredUrl: string | null;
|
|
22
|
+
/** True when the configured embedder differs from the active one (restart needed). */
|
|
23
|
+
readonly restartRequired: boolean;
|
|
18
24
|
/** The embedding URL if httpEmbedder is active, otherwise null. */
|
|
19
25
|
readonly embeddingUrl: string | null;
|
|
20
26
|
/** The MEGACOMPACT_EMBED_CACHE value (number as string), or null. */
|
|
@@ -44,6 +44,7 @@ export function sendGatherDebug(res: ServerResponse, ctx: RouteContext): void {
|
|
|
44
44
|
dashboardSnapshot: { error: "not fetched" },
|
|
45
45
|
recentEvents: [],
|
|
46
46
|
criticalEvents: [],
|
|
47
|
+
dataSourceAudit: {},
|
|
47
48
|
};
|
|
48
49
|
|
|
49
50
|
// 1. Config flags (no secrets — MEGACOMPACT_* only)
|
|
@@ -123,6 +124,68 @@ export function sendGatherDebug(res: ServerResponse, ctx: RouteContext): void {
|
|
|
123
124
|
result.recentEvents = [{ error: e instanceof Error ? e.message : String(e) }];
|
|
124
125
|
}
|
|
125
126
|
|
|
127
|
+
// 6. Data source audit — per-tab data population check
|
|
128
|
+
try {
|
|
129
|
+
const audit: Record<string, unknown> = {};
|
|
130
|
+
const db = openStoreDb(ctx);
|
|
131
|
+
|
|
132
|
+
// Main db tables
|
|
133
|
+
for (const table of ["context_chunks", "memories", "raw_transcript", "checkpoint_epochs", "dedup_mirror"]) {
|
|
134
|
+
try {
|
|
135
|
+
const row = db.prepare(`SELECT COUNT(*) AS c FROM ${table}`).get() as { c: number };
|
|
136
|
+
audit[`main.${table}`] = row.c;
|
|
137
|
+
} catch {
|
|
138
|
+
audit[`main.${table}`] = "table missing";
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// context_health + cache_poison_events
|
|
143
|
+
for (const table of ["context_health", "cache_poison_events"]) {
|
|
144
|
+
try {
|
|
145
|
+
const row = db.prepare(`SELECT COUNT(*) AS c FROM ${table}`).get() as { c: number };
|
|
146
|
+
audit[`main.${table}`] = row.c;
|
|
147
|
+
} catch {
|
|
148
|
+
audit[`main.${table}`] = "table missing";
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// turns.db tables
|
|
153
|
+
try {
|
|
154
|
+
const turnMod = require("../../src/store/turns/connection.js") as typeof import("../../src/store/turns/connection.js");
|
|
155
|
+
const tdb = turnMod.openTurnDb(ctx.stateDir);
|
|
156
|
+
for (const table of ["turns", "turn_recall", "session_conversations", "topics", "memory_topics"]) {
|
|
157
|
+
try {
|
|
158
|
+
const row = tdb.prepare(`SELECT COUNT(*) AS c FROM ${table}`).get() as { c: number };
|
|
159
|
+
audit[`turns.${table}`] = row.c;
|
|
160
|
+
} catch {
|
|
161
|
+
audit[`turns.${table}`] = "table missing";
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
} catch (e) {
|
|
165
|
+
audit["turns.db"] = `error: ${e instanceof Error ? e.message : String(e)}`;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// .mega-compact.env
|
|
169
|
+
try {
|
|
170
|
+
const envPath = ctx.stateDir + "/.mega-compact.env";
|
|
171
|
+
audit["mega-compact.env"] = existsSync(envPath) ? "exists" : "not found";
|
|
172
|
+
} catch {
|
|
173
|
+
audit["mega-compact.env"] = "check failed";
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// dashboard.json
|
|
177
|
+
try {
|
|
178
|
+
const metricsPath = defaultMetricsPath(ctx.stateDir);
|
|
179
|
+
audit["dashboard.json"] = existsSync(metricsPath) ? "exists" : "not found";
|
|
180
|
+
} catch {
|
|
181
|
+
audit["dashboard.json"] = "check failed";
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
(result as { dataSourceAudit: Record<string, unknown> }).dataSourceAudit = audit;
|
|
185
|
+
} catch (e) {
|
|
186
|
+
(result as { dataSourceAudit: unknown }).dataSourceAudit = { error: e instanceof Error ? e.message : String(e) };
|
|
187
|
+
}
|
|
188
|
+
|
|
126
189
|
// Send (local helper to avoid importing sendJson from a non-exporting module)
|
|
127
190
|
try {
|
|
128
191
|
const body = JSON.stringify(result);
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import { spawnSync } from "node:child_process"; // guardrails-allow PREVENT-PI-004: local subprocess detection only
|
|
13
13
|
import { createRequire } from "node:module";
|
|
14
|
-
import { writeFileSync, mkdirSync } from "node:fs";
|
|
14
|
+
import { writeFileSync, mkdirSync, readFileSync, existsSync } from "node:fs";
|
|
15
15
|
import { join } from "node:path";
|
|
16
16
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
17
17
|
import type { RouteContext } from "./routes-core.js";
|
|
@@ -37,10 +37,29 @@ function detectCurrentEmbedder(): SetupStatusResponse["currentEmbedder"] {
|
|
|
37
37
|
return "trigram";
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/** Read the configured embedder from the .mega-compact.env file (written by
|
|
41
|
+
* the configure endpoint). This shows what the user SELECTED even before pi
|
|
42
|
+
* restarts and loads the env file into process.env. */
|
|
43
|
+
function detectConfiguredEmbedder(stateDir: string): { embedder: "trigram" | "http" | "minilm"; url: string | null } {
|
|
44
|
+
try {
|
|
45
|
+
const envPath = join(stateDir, ".mega-compact.env");
|
|
46
|
+
if (!existsSync(envPath)) return { embedder: "trigram", url: null };
|
|
47
|
+
const content = readFileSync(envPath, "utf-8");
|
|
48
|
+
const urlMatch = content.match(/^export\s+MEGACOMPACT_EMBEDDING_URL="([^"]+)"/m);
|
|
49
|
+
if (urlMatch) return { embedder: "http", url: urlMatch[1] };
|
|
50
|
+
const minilmMatch = content.match(/^export\s+MEGACOMPACT_MINILM="?(.+?)"?\s*$/m);
|
|
51
|
+
if (minilmMatch && ["1", "true", "yes"].includes(minilmMatch[1].trim().toLowerCase()))
|
|
52
|
+
return { embedder: "minilm", url: null };
|
|
53
|
+
return { embedder: "trigram", url: null };
|
|
54
|
+
} catch {
|
|
55
|
+
return { embedder: "trigram", url: null };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
40
59
|
export function handleSetupStatus(
|
|
41
60
|
req: IncomingMessage,
|
|
42
61
|
res: ServerResponse,
|
|
43
|
-
|
|
62
|
+
ctx: RouteContext,
|
|
44
63
|
): boolean {
|
|
45
64
|
if (req.url !== "/api/setup-status") return false;
|
|
46
65
|
if (req.method !== "GET") {
|
|
@@ -49,9 +68,14 @@ export function handleSetupStatus(
|
|
|
49
68
|
res.end(JSON.stringify({ error: "method_not_allowed" }));
|
|
50
69
|
return true;
|
|
51
70
|
}
|
|
71
|
+
const configured = detectConfiguredEmbedder(ctx.stateDir);
|
|
72
|
+
const active = detectCurrentEmbedder();
|
|
52
73
|
const body: SetupStatusResponse = {
|
|
53
|
-
currentEmbedder:
|
|
54
|
-
|
|
74
|
+
currentEmbedder: active,
|
|
75
|
+
configuredEmbedder: configured.embedder,
|
|
76
|
+
configuredUrl: configured.url,
|
|
77
|
+
restartRequired: active !== configured.embedder,
|
|
78
|
+
embeddingUrl: process.env["MEGACOMPACT_EMBEDDING_URL"] ?? configured.url,
|
|
55
79
|
embedCache: process.env["MEGACOMPACT_EMBED_CACHE"] ?? null,
|
|
56
80
|
minilm: ["1", "true", "yes"].includes(
|
|
57
81
|
(process.env["MEGACOMPACT_MINILM"] ?? "").trim().toLowerCase(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mega-compact",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.2",
|
|
4
4
|
"description": "Layered, local, vector-backed context compressor for pi — supersede/collapse/cluster compaction with deduped inline recall.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
@@ -206,7 +206,8 @@ export function buildTurnNodes(
|
|
|
206
206
|
// ---------------------------------------------------------------------------
|
|
207
207
|
|
|
208
208
|
export function buildTurnContentNodes(
|
|
209
|
-
|
|
209
|
+
turnDb: DatabaseSync,
|
|
210
|
+
mainDb: DatabaseSync,
|
|
210
211
|
sessionId: string,
|
|
211
212
|
nodes: MemoryGraphNode[],
|
|
212
213
|
edges: MemoryGraphEdge[],
|
|
@@ -216,25 +217,58 @@ export function buildTurnContentNodes(
|
|
|
216
217
|
// (identity_merge) merges them with the richest nodeType winning. Using the
|
|
217
218
|
// global nodes array here would skip every node (Source A already added them).
|
|
218
219
|
const seen = new Set<string>();
|
|
219
|
-
|
|
220
|
-
|
|
220
|
+
|
|
221
|
+
// turns live in turns.db, raw_transcript in the main db — can't JOIN across
|
|
222
|
+
// databases, so query separately and merge in JS.
|
|
223
|
+
const turnRows = sessionId
|
|
224
|
+
? (turnDb
|
|
221
225
|
.prepare(
|
|
222
|
-
`SELECT
|
|
223
|
-
FROM turns
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
ORDER BY t.turn_index ASC`,
|
|
226
|
+
`SELECT turn_index, role, ended_at
|
|
227
|
+
FROM turns
|
|
228
|
+
WHERE session_id = ?
|
|
229
|
+
ORDER BY turn_index ASC`,
|
|
227
230
|
)
|
|
228
231
|
.all(sessionId) as Array<Record<string, unknown>>)
|
|
229
|
-
: (
|
|
232
|
+
: (turnDb
|
|
230
233
|
.prepare(
|
|
231
|
-
`SELECT
|
|
232
|
-
FROM turns
|
|
233
|
-
|
|
234
|
-
ORDER BY t.turn_index ASC`,
|
|
234
|
+
`SELECT turn_index, role, ended_at
|
|
235
|
+
FROM turns
|
|
236
|
+
ORDER BY turn_index ASC`,
|
|
235
237
|
)
|
|
236
238
|
.all() as Array<Record<string, unknown>>);
|
|
237
239
|
|
|
240
|
+
// Build a map of (session_id, turn_index) → content from raw_transcript.
|
|
241
|
+
const contentMap = new Map<string, string>();
|
|
242
|
+
try {
|
|
243
|
+
const transcriptRows = sessionId
|
|
244
|
+
? (mainDb
|
|
245
|
+
.prepare(
|
|
246
|
+
`SELECT turn_index, content_bytes FROM raw_transcript WHERE session_id = ?`,
|
|
247
|
+
)
|
|
248
|
+
.all(sessionId) as Array<Record<string, unknown>>)
|
|
249
|
+
: (mainDb
|
|
250
|
+
.prepare(
|
|
251
|
+
`SELECT turn_index, content_bytes FROM raw_transcript`,
|
|
252
|
+
)
|
|
253
|
+
.all() as Array<Record<string, unknown>>);
|
|
254
|
+
for (const r of transcriptRows) {
|
|
255
|
+
contentMap.set(String(r.turn_index), String(r.content_bytes ?? ""));
|
|
256
|
+
}
|
|
257
|
+
} catch {
|
|
258
|
+
// raw_transcript may not exist in the main db (dbMirror off) — return empty.
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Merge: only produce nodes where both a turn row AND transcript content exist.
|
|
263
|
+
const rows = turnRows
|
|
264
|
+
.filter((t) => contentMap.has(String(t.turn_index)))
|
|
265
|
+
.map((t) => ({
|
|
266
|
+
turn_index: t.turn_index,
|
|
267
|
+
role: t.role,
|
|
268
|
+
ended_at: t.ended_at,
|
|
269
|
+
content_bytes: contentMap.get(String(t.turn_index)) ?? "",
|
|
270
|
+
}));
|
|
271
|
+
|
|
238
272
|
if (rows.length === 0) return;
|
|
239
273
|
|
|
240
274
|
const turnContentNodes: MemoryGraphNode[] = [];
|
|
@@ -269,7 +303,7 @@ export function buildTurnContentNodes(
|
|
|
269
303
|
if (turnContentNodes.length > 1) {
|
|
270
304
|
const embeddings = turnContentNodes.map((n) => {
|
|
271
305
|
try {
|
|
272
|
-
return { id: n.id, vec: getOrComputeEmbedding(
|
|
306
|
+
return { id: n.id, vec: getOrComputeEmbedding(mainDb, n.textSnippet) };
|
|
273
307
|
} catch {
|
|
274
308
|
return { id: n.id, vec: null as number[] | null };
|
|
275
309
|
}
|
|
@@ -291,7 +325,7 @@ export function buildTurnContentNodes(
|
|
|
291
325
|
}
|
|
292
326
|
}
|
|
293
327
|
|
|
294
|
-
linkTurnToCheckpointEdges(
|
|
328
|
+
linkTurnToCheckpointEdges(mainDb, sessionId, turnContentNodes, edges);
|
|
295
329
|
}
|
|
296
330
|
|
|
297
331
|
nodes.push(...turnContentNodes);
|
package/src/memoryGraph.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { getStateDir } from "./store.js";
|
|
16
16
|
import { openStore } from "./store/sqlite/utils.js";
|
|
17
|
+
import { openTurnStore } from "./store/turns/connection.js";
|
|
17
18
|
import type { DatabaseSync } from "node:sqlite";
|
|
18
19
|
import { Logger } from "./log.js";
|
|
19
20
|
import { runValidationPipeline } from "./memoryGraph/gates.js";
|
|
@@ -182,14 +183,23 @@ function buildUnvalidatedGraph(
|
|
|
182
183
|
return { nodes: [], edges: [] };
|
|
183
184
|
}
|
|
184
185
|
|
|
186
|
+
// Turns live in the isolated turns.db, not the main sqlite.db. Open it
|
|
187
|
+
// separately so buildTurnNodes/buildTurnContentNodes can query it.
|
|
188
|
+
let turnDb: DatabaseSync | null = null;
|
|
189
|
+
try {
|
|
190
|
+
turnDb = openTurnStore(stateDir);
|
|
191
|
+
} catch {
|
|
192
|
+
log.warn("graph_source_unavailable", { detail: "Cannot open turns.db" });
|
|
193
|
+
}
|
|
194
|
+
|
|
185
195
|
try {
|
|
186
196
|
// Source: checkpoints (always on)
|
|
187
197
|
buildCheckpointNodes(db, sessionId, nodes, edges);
|
|
188
198
|
|
|
189
|
-
// Source A: turns (structural, metadata-only)
|
|
190
|
-
if (areTurnsEnabled()) {
|
|
199
|
+
// Source A: turns (structural, metadata-only) — from turns.db, not main db
|
|
200
|
+
if (areTurnsEnabled() && turnDb) {
|
|
191
201
|
try {
|
|
192
|
-
buildTurnNodes(
|
|
202
|
+
buildTurnNodes(turnDb, sessionId, nodes, edges);
|
|
193
203
|
} catch (err) {
|
|
194
204
|
log.warn("graph_source_unavailable", {
|
|
195
205
|
source: "turns",
|
|
@@ -199,9 +209,11 @@ function buildUnvalidatedGraph(
|
|
|
199
209
|
}
|
|
200
210
|
|
|
201
211
|
// Source B: turn content (raw_transcript join, embedding-based)
|
|
202
|
-
|
|
212
|
+
// raw_transcript is in the main db, but turns are in turns.db — the JOIN
|
|
213
|
+
// can't span databases, so we pass both and merge in JS.
|
|
214
|
+
if (isTurnContentEnabled() && turnDb) {
|
|
203
215
|
try {
|
|
204
|
-
buildTurnContentNodes(db, sessionId, nodes, edges);
|
|
216
|
+
buildTurnContentNodes(turnDb, db, sessionId, nodes, edges);
|
|
205
217
|
} catch (err) {
|
|
206
218
|
log.warn("graph_source_unavailable", {
|
|
207
219
|
source: "turn-content",
|
|
@@ -16,10 +16,26 @@
|
|
|
16
16
|
import type { DatabaseSync } from "node:sqlite";
|
|
17
17
|
|
|
18
18
|
/** Schema version stamp (written once into turns_meta). */
|
|
19
|
-
const SCHEMA_VERSION =
|
|
19
|
+
const SCHEMA_VERSION = 2;
|
|
20
20
|
|
|
21
21
|
/** Create the unified turn-store schema if absent. Idempotent. */
|
|
22
22
|
export function initTurnSchema(db: DatabaseSync): void {
|
|
23
|
+
// Migration: v1 had a pressure_band CHECK constraint that only allowed
|
|
24
|
+
// 'green','yellow','red' — but the runtime sends 'low','medium','high',
|
|
25
|
+
// 'ultra','mega'. Every INSERT failed silently. Since the constraint
|
|
26
|
+
// blocked ALL writes, the turns table is guaranteed empty — safe to
|
|
27
|
+
// drop and recreate with the widened constraint.
|
|
28
|
+
try {
|
|
29
|
+
const tableSql = db
|
|
30
|
+
.prepare("SELECT sql FROM sqlite_master WHERE type='table' AND name='turns'")
|
|
31
|
+
.get() as { sql?: string } | undefined;
|
|
32
|
+
if (tableSql?.sql && tableSql.sql.includes("'green'") && !tableSql.sql.includes("'low'")) {
|
|
33
|
+
db.exec("DROP TABLE IF EXISTS turns");
|
|
34
|
+
}
|
|
35
|
+
} catch {
|
|
36
|
+
/* non-fatal: table may not exist yet */
|
|
37
|
+
}
|
|
38
|
+
|
|
23
39
|
// ── Contract tables (master's SqliteTurnStore shape) ──────────────────
|
|
24
40
|
db.exec(`
|
|
25
41
|
CREATE TABLE IF NOT EXISTS turns_meta (
|
|
@@ -41,7 +57,7 @@ export function initTurnSchema(db: DatabaseSync): void {
|
|
|
41
57
|
ended_at INTEGER NOT NULL,
|
|
42
58
|
ctx_tokens INTEGER,
|
|
43
59
|
ctx_percent REAL,
|
|
44
|
-
pressure_band TEXT CHECK(pressure_band IS NULL OR pressure_band IN ('green','yellow','red')),
|
|
60
|
+
pressure_band TEXT CHECK(pressure_band IS NULL OR pressure_band IN ('low','medium','high','ultra','mega','green','yellow','red')),
|
|
45
61
|
model TEXT,
|
|
46
62
|
epoch_id TEXT,
|
|
47
63
|
UNIQUE(conversation_id, turn_index)
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{r as l,e as k,g as R,h as D,j as e}from"./index-C9dTXvDQ.js";const t={container:{padding:"1rem",maxWidth:"720px",margin:"0 auto",fontFamily:"system-ui, sans-serif",color:"#e0e0e0"},section:{background:"#1a1a2e",borderRadius:"8px",padding:"1rem",marginBottom:"1rem",border:"1px solid #2a2a4e"},sectionTitle:{fontSize:"1.1rem",fontWeight:700,marginTop:0,marginBottom:"0.75rem",color:"#e0e0e0",borderBottom:"1px solid #333",paddingBottom:"0.5rem"},label:{fontWeight:600,color:"#a0a0c0",marginRight:"0.5rem"},value:{color:"#e0e0e0"},row:{marginBottom:"0.4rem",fontSize:"0.9rem"},warning:{background:"#3a2a00",border:"1px solid #665500",borderRadius:"8px",padding:"0.75rem",marginTop:"0.5rem",color:"#ffcc00",fontSize:"0.85rem"},badge:{display:"inline-block",padding:"0.15rem 0.5rem",borderRadius:"4px",fontSize:"0.8rem",fontWeight:600,marginLeft:"0.5rem"},detectRow:{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"0.5rem 0",borderBottom:"1px solid #2a2a4e"},button:{background:"#3a5a9f",color:"#fff",border:"none",borderRadius:"6px",padding:"0.5rem 1rem",cursor:"pointer",fontSize:"0.9rem",fontWeight:600}};function B(){const[s,S]=l.useState(null),[n,v]=l.useState(null),[c,E]=l.useState(null),[g,x]=l.useState(null),[f,m]=l.useState(!1),[i,h]=l.useState(null),[d,j]=l.useState(null),[b,y]=l.useState(null),o=l.useCallback(()=>{k().then(S).catch(r=>E(r instanceof Error?r.message:String(r)))},[]),u=l.useCallback(r=>{h(r),y(null),j(null),R({embedder:r}).then(a=>{j(a),h(null),o()}).catch(a=>{y(a instanceof Error?a.message:String(a)),h(null)})},[o]),C=l.useCallback(()=>{m(!0),x(null),D().then(r=>{v(r),m(!1)}).catch(r=>{x(r instanceof Error?r.message:String(r)),m(!1)})},[]);l.useEffect(()=>{o()},[o]),l.useEffect(()=>{const r=setInterval(o,3e4);return()=>clearInterval(r)},[o]);const w=r=>{switch(r){case"trigram":return"TrigramEmbedder (heuristic, default)";case"http":return"HTTP Embedder (BYO localhost)";case"minilm":return"MiniLM (experimental)";default:return"Unknown"}},z=s?.currentEmbedder==="trigram"&&e.jsxs("div",{style:t.warning,children:[e.jsx("strong",{children:"Note:"})," TrigramEmbedder is a heuristic-strength default embedder. It works without any setup but recall quality may be lower than a dedicated embedding backend. If you notice poor recall results, consider installing Ollama and running the"," ",e.jsx("code",{children:"/megasetup"})," command, or set"," ",e.jsx("code",{children:"MEGACOMPACT_EMBEDDING_URL"})," to point at your own localhost embedding server."]}),p=r=>{const a=r?"#1a5a1a":"#3a1a1a",T=r?"#4caf50":"#f44336";return e.jsx("span",{style:{...t.badge,background:a,color:T},children:r?"Detected":"Not found"})};return e.jsxs("div",{style:t.container,children:[e.jsx("h2",{style:{fontSize:"1.3rem",marginTop:0,marginBottom:"1rem"},children:"Embedder Setup"}),e.jsxs("div",{style:t.section,children:[e.jsx("h3",{style:t.sectionTitle,children:"Current Embedder Configuration"}),c&&e.jsxs("p",{style:{color:"#f44336",fontSize:"0.85rem"},children:["Error: ",c]}),s&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{style:t.row,children:[e.jsx("span",{style:t.label,children:"Active Embedder:"}),e.jsx("span",{style:t.value,children:w(s.currentEmbedder)})]}),e.jsxs("div",{style:t.row,children:[e.jsx("span",{style:t.label,children:"Embedding URL:"}),e.jsx("span",{style:t.value,children:s.embeddingUrl??e.jsx("span",{style:{color:"#888"},children:"not set"})})]}),e.jsxs("div",{style:t.row,children:[e.jsx("span",{style:t.label,children:"Embed Cache:"}),e.jsx("span",{style:t.value,children:s.embedCache??e.jsx("span",{style:{color:"#888"},children:"not set"})})]}),e.jsxs("div",{style:t.row,children:[e.jsx("span",{style:t.label,children:"MiniLM:"}),e.jsx("span",{style:t.value,children:s.minilm?"enabled":"disabled"})]}),z]}),!s&&!c&&e.jsx("p",{style:{color:"#888",fontSize:"0.85rem"},children:"Loading configuration..."})]}),e.jsxs("div",{style:t.section,children:[e.jsx("h3",{style:t.sectionTitle,children:"Local Embedder Detection"}),e.jsx("p",{style:{fontSize:"0.85rem",color:"#a0a0c0",marginTop:0},children:"Detect which local embedding backends are available on this machine. No software is installed — detection only."}),n&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{style:t.detectRow,children:[e.jsx("span",{children:e.jsx("strong",{children:"Ollama"})}),n.ollama!==null?e.jsxs("span",{children:[p(n.ollama.installed),n.ollama.installed&&e.jsx("span",{style:{fontSize:"0.8rem",color:"#a0a0c0",marginLeft:"0.5rem"},children:n.ollama.running?`server running, ${n.ollama.models.length} model(s)`:"server not running"})]}):e.jsx("span",{style:{color:"#888",fontSize:"0.85rem"},children:"not checked"})]}),e.jsxs("div",{style:t.detectRow,children:[e.jsx("span",{children:e.jsx("strong",{children:"llama.cpp"})}),n.llamaCpp!==null?e.jsxs("span",{children:[p(n.llamaCpp.installed),n.llamaCpp.installed&&n.llamaCpp.detail&&e.jsx("span",{style:{fontSize:"0.8rem",color:"#a0a0c0",marginLeft:"0.5rem"},children:n.llamaCpp.detail})]}):e.jsx("span",{style:{color:"#888",fontSize:"0.85rem"},children:"not checked"})]}),e.jsxs("div",{style:t.detectRow,children:[e.jsx("span",{children:e.jsx("strong",{children:"ONNX Runtime"})}),n.onnx!==null?e.jsxs("span",{children:[p(n.onnx.installed),n.onnx.installed&&n.onnx.detail&&e.jsx("span",{style:{fontSize:"0.8rem",color:"#a0a0c0",marginLeft:"0.5rem"},children:n.onnx.detail})]}):e.jsx("span",{style:{color:"#888",fontSize:"0.85rem"},children:"not checked"})]}),n.error&&e.jsxs("p",{style:{color:"#f44336",fontSize:"0.8rem",marginTop:"0.5rem"},children:["Detection error: ",n.error]})]}),g&&e.jsxs("p",{style:{color:"#f44336",fontSize:"0.85rem"},children:["Error: ",g]}),e.jsxs("div",{style:{marginTop:"1rem"},children:[e.jsx("button",{style:t.button,onClick:C,disabled:f,children:f?"Detecting...":"Run Detection"}),e.jsxs("span",{style:{fontSize:"0.8rem",color:"#888",marginLeft:"0.75rem"},children:["Or run ",e.jsx("code",{children:"/megasetup"})," in the chat for an interactive wizard."]})]}),n&&e.jsxs("div",{style:{marginTop:"1rem",paddingTop:"0.75rem",borderTop:"1px solid #2a2a4e"},children:[e.jsx("div",{style:{fontSize:"0.9rem",fontWeight:600,marginBottom:"0.5rem"},children:"Configure"}),e.jsxs("div",{style:{display:"flex",gap:"0.5rem",flexWrap:"wrap"},children:[e.jsx("button",{style:{...t.button,opacity:n.ollama?.installed?1:.4},onClick:()=>u("ollama"),disabled:!n.ollama?.installed||i!==null,children:i==="ollama"?"Writing...":"Use Ollama"}),e.jsx("button",{style:{...t.button,opacity:n.llamaCpp?.installed?1:.4},onClick:()=>u("llama"),disabled:!n.llamaCpp?.installed||i!==null,children:i==="llama"?"Writing...":"Use llama.cpp"}),e.jsx("button",{style:{...t.button,background:"#444"},onClick:()=>u("trigram"),disabled:i!==null,children:i==="trigram"?"Writing...":"Use Trigram (default)"})]}),d&&e.jsx("div",{style:t.warning,children:d.alreadyActive?"Already active — no restart needed.":`Configured ${d.embedder}. Restart pi to activate (config written to ${d.envPath}).`}),b&&e.jsxs("p",{style:{color:"#f44336",fontSize:"0.85rem",marginTop:"0.5rem"},children:["Configure error: ",b]})]})]}),e.jsxs("div",{style:t.section,children:[e.jsx("h3",{style:t.sectionTitle,children:"How to Upgrade"}),e.jsxs("ul",{style:{fontSize:"0.85rem",lineHeight:1.6,paddingLeft:"1.25rem"},children:[e.jsxs("li",{children:[e.jsx("strong",{children:"Ollama:"})," Install from"," ",e.jsx("a",{href:"https://ollama.com",style:{color:"#6a9fff"},target:"_blank",rel:"noopener noreferrer",children:"ollama.com"}),", then ",e.jsx("code",{children:"ollama pull nomic-embed-text"})," and restart pi. Set ",e.jsx("code",{children:"MEGACOMPACT_EMBEDDING_URL=http://localhost:11434/api/embeddings"}),"."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"llama.cpp:"})," Build or install llama.cpp, run"," ",e.jsx("code",{children:"llama-server"})," with an embedding model, and set the URL accordingly."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"ONNX Runtime:"})," Install"," ",e.jsx("code",{children:"onnxruntime-node"})," in the project root. Not yet integrated — see docs for manual setup."]})]})]})]})}export{B as default};
|
|
2
|
-
//# sourceMappingURL=SetupTab-rKGgWpI0.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SetupTab-rKGgWpI0.js","sources":["../../src/tabs/SetupTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/SetupTab.tsx — Setup wizard tab (P0b).\n *\n * Shows current embedder configuration, warns about trigram recall quality,\n * and mirrors the /megasetup command with detection results.\n *\n * PREVENT-PI-004: all data comes from localhost dashboard API endpoints —\n * no external network calls.\n */\n\nimport type React from \"react\";\nimport { useState, useEffect, useCallback } from \"react\";\nimport type { SetupStatusResponse, SetupDetectResponse, SetupConfigureResponse } from \"@contracts\";\nimport { fetchSetupStatus, fetchSetupDetect, configureEmbedder } from \"../api/client\";\n\n// ---------------------------------------------------------------------------\n// Style constants\n// ---------------------------------------------------------------------------\n\nconst styles: Record<string, React.CSSProperties> = {\n\tcontainer: {\n\t\tpadding: \"1rem\",\n\t\tmaxWidth: \"720px\",\n\t\tmargin: \"0 auto\",\n\t\tfontFamily: \"system-ui, sans-serif\",\n\t\tcolor: \"#e0e0e0\",\n\t},\n\tsection: {\n\t\tbackground: \"#1a1a2e\",\n\t\tborderRadius: \"8px\",\n\t\tpadding: \"1rem\",\n\t\tmarginBottom: \"1rem\",\n\t\tborder: \"1px solid #2a2a4e\",\n\t},\n\tsectionTitle: {\n\t\tfontSize: \"1.1rem\",\n\t\tfontWeight: 700,\n\t\tmarginTop: 0,\n\t\tmarginBottom: \"0.75rem\",\n\t\tcolor: \"#e0e0e0\",\n\t\tborderBottom: \"1px solid #333\",\n\t\tpaddingBottom: \"0.5rem\",\n\t},\n\tlabel: { fontWeight: 600, color: \"#a0a0c0\", marginRight: \"0.5rem\" },\n\tvalue: { color: \"#e0e0e0\" },\n\trow: { marginBottom: \"0.4rem\", fontSize: \"0.9rem\" },\n\twarning: {\n\t\tbackground: \"#3a2a00\",\n\t\tborder: \"1px solid #665500\",\n\t\tborderRadius: \"8px\",\n\t\tpadding: \"0.75rem\",\n\t\tmarginTop: \"0.5rem\",\n\t\tcolor: \"#ffcc00\",\n\t\tfontSize: \"0.85rem\",\n\t},\n\tbadge: {\n\t\tdisplay: \"inline-block\",\n\t\tpadding: \"0.15rem 0.5rem\",\n\t\tborderRadius: \"4px\",\n\t\tfontSize: \"0.8rem\",\n\t\tfontWeight: 600,\n\t\tmarginLeft: \"0.5rem\",\n\t},\n\tdetectRow: {\n\t\tdisplay: \"flex\",\n\t\tjustifyContent: \"space-between\",\n\t\talignItems: \"center\",\n\t\tpadding: \"0.5rem 0\",\n\t\tborderBottom: \"1px solid #2a2a4e\",\n\t},\n\tbutton: {\n\t\tbackground: \"#3a5a9f\",\n\t\tcolor: \"#fff\",\n\t\tborder: \"none\",\n\t\tborderRadius: \"6px\",\n\t\tpadding: \"0.5rem 1rem\",\n\t\tcursor: \"pointer\",\n\t\tfontSize: \"0.9rem\",\n\t\tfontWeight: 600,\n\t},\n};\n\n// ---------------------------------------------------------------------------\n// SetupTab component\n// ---------------------------------------------------------------------------\n\nexport default function SetupTab(): React.ReactElement {\n\tconst [status, setStatus] = useState<SetupStatusResponse | null>(null);\n\tconst [detect, setDetect] = useState<SetupDetectResponse | null>(null);\n\tconst [statusError, setStatusError] = useState<string | null>(null);\n\tconst [detectError, setDetectError] = useState<string | null>(null);\n\tconst [runningDetect, setRunningDetect] = useState(false);\n\tconst [configuring, setConfiguring] = useState<string | null>(null);\n\tconst [configResult, setConfigResult] = useState<SetupConfigureResponse | null>(null);\n\tconst [configError, setConfigError] = useState<string | null>(null);\n\n\tconst loadStatus = useCallback(() => {\n\t\tfetchSetupStatus()\n\t\t\t.then(setStatus)\n\t\t\t.catch((e: unknown) =>\n\t\t\t\tsetStatusError(e instanceof Error ? e.message : String(e)),\n\t\t\t);\n\t}, []);\n\n\tconst applyEmbedder = useCallback((embedder: \"ollama\" | \"llama\" | \"trigram\") => {\n\t\tsetConfiguring(embedder);\n\t\tsetConfigError(null);\n\t\tsetConfigResult(null);\n\t\tconfigureEmbedder({ embedder })\n\t\t\t.then((r) => {\n\t\t\t\tsetConfigResult(r);\n\t\t\t\tsetConfiguring(null);\n\t\t\t\tloadStatus();\n\t\t\t})\n\t\t\t.catch((e: unknown) => {\n\t\t\t\tsetConfigError(e instanceof Error ? e.message : String(e));\n\t\t\t\tsetConfiguring(null);\n\t\t\t});\n\t}, [loadStatus]);\n\n\tconst runDetect = useCallback(() => {\n\t\tsetRunningDetect(true);\n\t\tsetDetectError(null);\n\t\tfetchSetupDetect()\n\t\t\t.then((d) => {\n\t\t\t\tsetDetect(d);\n\t\t\t\tsetRunningDetect(false);\n\t\t\t})\n\t\t\t.catch((e: unknown) => {\n\t\t\t\tsetDetectError(e instanceof Error ? e.message : String(e));\n\t\t\t\tsetRunningDetect(false);\n\t\t\t});\n\t}, []);\n\n\tuseEffect(() => {\n\t\tloadStatus();\n\t}, [loadStatus]);\n\n\t// Poll status every 30s\n\tuseEffect(() => {\n\t\tconst id = setInterval(loadStatus, 30000);\n\t\treturn () => clearInterval(id);\n\t}, [loadStatus]);\n\n\t// -----------------------------------------------------------------------\n\t// Render helpers\n\t// -----------------------------------------------------------------------\n\n\tconst embedderLabel = (e: string): string => {\n\t\tswitch (e) {\n\t\t\tcase \"trigram\":\n\t\t\t\treturn \"TrigramEmbedder (heuristic, default)\";\n\t\t\tcase \"http\":\n\t\t\t\treturn \"HTTP Embedder (BYO localhost)\";\n\t\t\tcase \"minilm\":\n\t\t\t\treturn \"MiniLM (experimental)\";\n\t\t\tdefault:\n\t\t\t\treturn \"Unknown\";\n\t\t}\n\t};\n\n\tconst trigramWarning = status?.currentEmbedder === \"trigram\" && (\n\t\t<div style={styles.warning}>\n\t\t\t<strong>Note:</strong> TrigramEmbedder is a heuristic-strength default\n\t\t\tembedder. It works without any setup but recall quality may be lower\n\t\t\tthan a dedicated embedding backend. If you notice poor recall results,\n\t\t\tconsider installing Ollama and running the{\" \"}\n\t\t\t<code>/megasetup</code> command, or set{\" \"}\n\t\t\t<code>MEGACOMPACT_EMBEDDING_URL</code> to point at your own localhost\n\t\t\tembedding server.\n\t\t</div>\n\t);\n\n\tconst detectBadge = (installed: boolean): React.ReactElement => {\n\t\tconst bg = installed ? \"#1a5a1a\" : \"#3a1a1a\";\n\t\tconst color = installed ? \"#4caf50\" : \"#f44336\";\n\t\treturn (\n\t\t\t<span style={{ ...styles.badge, background: bg, color }}>\n\t\t\t\t{installed ? \"Detected\" : \"Not found\"}\n\t\t\t</span>\n\t\t);\n\t};\n\n\t// -----------------------------------------------------------------------\n\t// Main render\n\t// -----------------------------------------------------------------------\n\n\treturn (\n\t\t<div style={styles.container}>\n\t\t\t<h2 style={{ fontSize: \"1.3rem\", marginTop: 0, marginBottom: \"1rem\" }}>\n\t\t\t\tEmbedder Setup\n\t\t\t</h2>\n\n\t\t\t{/* Current Config Section */}\n\t\t\t<div style={styles.section}>\n\t\t\t\t<h3 style={styles.sectionTitle}>Current Embedder Configuration</h3>\n\t\t\t\t{statusError && (\n\t\t\t\t\t<p style={{ color: \"#f44336\", fontSize: \"0.85rem\" }}>\n\t\t\t\t\t\tError: {statusError}\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\t\t\t\t{status && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div style={styles.row}>\n\t\t\t\t\t\t\t<span style={styles.label}>Active Embedder:</span>\n\t\t\t\t\t\t\t<span style={styles.value}>\n\t\t\t\t\t\t\t\t{embedderLabel(status.currentEmbedder)}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div style={styles.row}>\n\t\t\t\t\t\t\t<span style={styles.label}>Embedding URL:</span>\n\t\t\t\t\t\t\t<span style={styles.value}>\n\t\t\t\t\t\t\t\t{status.embeddingUrl ?? (\n\t\t\t\t\t\t\t\t\t<span style={{ color: \"#888\" }}>not set</span>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div style={styles.row}>\n\t\t\t\t\t\t\t<span style={styles.label}>Embed Cache:</span>\n\t\t\t\t\t\t\t<span style={styles.value}>\n\t\t\t\t\t\t\t\t{status.embedCache ?? (\n\t\t\t\t\t\t\t\t\t<span style={{ color: \"#888\" }}>not set</span>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div style={styles.row}>\n\t\t\t\t\t\t\t<span style={styles.label}>MiniLM:</span>\n\t\t\t\t\t\t\t<span style={styles.value}>{status.minilm ? \"enabled\" : \"disabled\"}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{trigramWarning}\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t\t{!status && !statusError && (\n\t\t\t\t\t<p style={{ color: \"#888\", fontSize: \"0.85rem\" }}>\n\t\t\t\t\t\tLoading configuration...\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t{/* Detection Section */}\n\t\t\t<div style={styles.section}>\n\t\t\t\t<h3 style={styles.sectionTitle}>Local Embedder Detection</h3>\n\t\t\t\t<p style={{ fontSize: \"0.85rem\", color: \"#a0a0c0\", marginTop: 0 }}>\n\t\t\t\t\tDetect which local embedding backends are available on this machine.\n\t\t\t\t\tNo software is installed — detection only.\n\t\t\t\t</p>\n\n\t\t\t\t{detect && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t{/* Ollama */}\n\t\t\t\t\t\t<div style={styles.detectRow}>\n\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t<strong>Ollama</strong>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t{detect.ollama !== null ? (\n\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t{detectBadge(detect.ollama.installed)}\n\t\t\t\t\t\t\t\t\t{detect.ollama.installed && (\n\t\t\t\t\t\t\t\t\t\t<span style={{ fontSize: \"0.8rem\", color: \"#a0a0c0\", marginLeft: \"0.5rem\" }}>\n\t\t\t\t\t\t\t\t\t\t\t{detect.ollama.running\n\t\t\t\t\t\t\t\t\t\t\t\t? `server running, ${detect.ollama.models.length} model(s)`\n\t\t\t\t\t\t\t\t\t\t\t\t: \"server not running\"}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<span style={{ color: \"#888\", fontSize: \"0.85rem\" }}>\n\t\t\t\t\t\t\t\t\tnot checked\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* llama.cpp */}\n\t\t\t\t\t\t<div style={styles.detectRow}>\n\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t<strong>llama.cpp</strong>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t{detect.llamaCpp !== null ? (\n\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t{detectBadge(detect.llamaCpp.installed)}\n\t\t\t\t\t\t\t\t\t{detect.llamaCpp.installed && detect.llamaCpp.detail && (\n\t\t\t\t\t\t\t\t\t\t<span style={{ fontSize: \"0.8rem\", color: \"#a0a0c0\", marginLeft: \"0.5rem\" }}>\n\t\t\t\t\t\t\t\t\t\t\t{detect.llamaCpp.detail}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<span style={{ color: \"#888\", fontSize: \"0.85rem\" }}>\n\t\t\t\t\t\t\t\t\tnot checked\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* ONNX */}\n\t\t\t\t\t\t<div style={styles.detectRow}>\n\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t<strong>ONNX Runtime</strong>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t{detect.onnx !== null ? (\n\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t{detectBadge(detect.onnx.installed)}\n\t\t\t\t\t\t\t\t\t{detect.onnx.installed && detect.onnx.detail && (\n\t\t\t\t\t\t\t\t\t\t<span style={{ fontSize: \"0.8rem\", color: \"#a0a0c0\", marginLeft: \"0.5rem\" }}>\n\t\t\t\t\t\t\t\t\t\t\t{detect.onnx.detail}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<span style={{ color: \"#888\", fontSize: \"0.85rem\" }}>\n\t\t\t\t\t\t\t\t\tnot checked\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{detect.error && (\n\t\t\t\t\t\t\t<p style={{ color: \"#f44336\", fontSize: \"0.8rem\", marginTop: \"0.5rem\" }}>\n\t\t\t\t\t\t\t\tDetection error: {detect.error}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</>\n\t\t\t\t)}\n\n\t\t\t\t{detectError && (\n\t\t\t\t\t<p style={{ color: \"#f44336\", fontSize: \"0.85rem\" }}>\n\t\t\t\t\t\tError: {detectError}\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\n\t\t\t\t<div style={{ marginTop: \"1rem\" }}>\n\t\t\t\t\t<button\n\t\t\t\t\t\tstyle={styles.button}\n\t\t\t\t\t\tonClick={runDetect}\n\t\t\t\t\t\tdisabled={runningDetect}\n\t\t\t\t\t>\n\t\t\t\t\t\t{runningDetect ? \"Detecting...\" : \"Run Detection\"}\n\t\t\t\t\t</button>\n\t\t\t\t\t<span style={{ fontSize: \"0.8rem\", color: \"#888\", marginLeft: \"0.75rem\" }}>\n\t\t\t\t\t\tOr run <code>/megasetup</code> in the chat for an interactive wizard.\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Configure buttons — appear after detection runs */}\n\t\t\t\t{detect && (\n\t\t\t\t\t<div style={{ marginTop: \"1rem\", paddingTop: \"0.75rem\", borderTop: \"1px solid #2a2a4e\" }}>\n\t\t\t\t\t\t<div style={{ fontSize: \"0.9rem\", fontWeight: 600, marginBottom: \"0.5rem\" }}>\n\t\t\t\t\t\t\tConfigure\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div style={{ display: \"flex\", gap: \"0.5rem\", flexWrap: \"wrap\" }}>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\tstyle={{ ...styles.button, opacity: detect.ollama?.installed ? 1 : 0.4 }}\n\t\t\t\t\t\t\t\tonClick={() => applyEmbedder(\"ollama\")}\n\t\t\t\t\t\t\t\tdisabled={!detect.ollama?.installed || configuring !== null}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{configuring === \"ollama\" ? \"Writing...\" : \"Use Ollama\"}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\tstyle={{ ...styles.button, opacity: detect.llamaCpp?.installed ? 1 : 0.4 }}\n\t\t\t\t\t\t\t\tonClick={() => applyEmbedder(\"llama\")}\n\t\t\t\t\t\t\t\tdisabled={!detect.llamaCpp?.installed || configuring !== null}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{configuring === \"llama\" ? \"Writing...\" : \"Use llama.cpp\"}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\tstyle={{ ...styles.button, background: \"#444\" }}\n\t\t\t\t\t\t\t\tonClick={() => applyEmbedder(\"trigram\")}\n\t\t\t\t\t\t\t\tdisabled={configuring !== null}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{configuring === \"trigram\" ? \"Writing...\" : \"Use Trigram (default)\"}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{configResult && (\n\t\t\t\t\t\t\t<div style={styles.warning}>\n\t\t\t\t\t\t\t\t{configResult.alreadyActive\n\t\t\t\t\t\t\t\t\t? \"Already active — no restart needed.\"\n\t\t\t\t\t\t\t\t\t: `Configured ${configResult.embedder}. Restart pi to activate (config written to ${configResult.envPath}).`}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{configError && (\n\t\t\t\t\t\t\t<p style={{ color: \"#f44336\", fontSize: \"0.85rem\", marginTop: \"0.5rem\" }}>\n\t\t\t\t\t\t\t\tConfigure error: {configError}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t{/* Help Section */}\n\t\t\t<div style={styles.section}>\n\t\t\t\t<h3 style={styles.sectionTitle}>How to Upgrade</h3>\n\t\t\t\t<ul style={{ fontSize: \"0.85rem\", lineHeight: 1.6, paddingLeft: \"1.25rem\" }}>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<strong>Ollama:</strong> Install from{\" \"}\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\thref=\"https://ollama.com\"\n\t\t\t\t\t\t\tstyle={{ color: \"#6a9fff\" }}\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tollama.com\n\t\t\t\t\t\t</a>\n\t\t\t\t\t\t, then <code>ollama pull nomic-embed-text</code> and restart pi.\n\t\t\t\t\t\tSet <code>MEGACOMPACT_EMBEDDING_URL=http://localhost:11434/api/embeddings</code>.\n\t\t\t\t\t</li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<strong>llama.cpp:</strong> Build or install llama.cpp, run{\" \"}\n\t\t\t\t\t\t<code>llama-server</code> with an embedding model, and set\n\t\t\t\t\t\tthe URL accordingly.\n\t\t\t\t\t</li>\n\t\t\t\t\t<li>\n\t\t\t\t\t\t<strong>ONNX Runtime:</strong> Install{\" \"}\n\t\t\t\t\t\t<code>onnxruntime-node</code> in the project root. Not yet\n\t\t\t\t\t\tintegrated — see docs for manual setup.\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"],"names":["styles","SetupTab","status","setStatus","useState","detect","setDetect","statusError","setStatusError","detectError","setDetectError","runningDetect","setRunningDetect","configuring","setConfiguring","configResult","setConfigResult","configError","setConfigError","loadStatus","useCallback","fetchSetupStatus","e","applyEmbedder","embedder","configureEmbedder","r","runDetect","fetchSetupDetect","d","useEffect","id","embedderLabel","trigramWarning","jsx","detectBadge","installed","bg","color","jsxs","Fragment"],"mappings":"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"}
|