pi-mega-compact 0.13.4 → 0.13.6
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 +11 -8
- package/dist/extensions/dashboard-server/api-contracts/endpoints/registry.js +50 -0
- package/dist/extensions/dashboard-server/api-contracts/rag-metrics.js +1 -0
- package/dist/extensions/dashboard-server/api-contracts/rag-settings.js +3 -3
- package/dist/extensions/dashboard-server/api-contracts/wiki.js +1 -0
- package/dist/extensions/dashboard-server/routes-rag-metrics.js +60 -0
- package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +167 -0
- package/dist/extensions/dashboard-server/routes-rag-settings.js +136 -77
- package/dist/extensions/dashboard-server/routes-wiki-helpers.js +97 -0
- package/dist/extensions/dashboard-server/routes-wiki.js +330 -0
- package/dist/extensions/dashboard-server/routes.js +2 -0
- package/dist/extensions/dashboard-server/server.js +17 -3
- package/dist/extensions/mega-events/context-handler/afterCompact.js +35 -2
- package/dist/extensions/mega-pipeline/recall.js +31 -0
- package/dist/extensions/mega-turn-store.js +2 -1
- package/dist/src/config/turns.js +10 -0
- package/dist/src/config.js +2 -0
- package/dist/src/recall/async.js +10 -1
- package/dist/src/recall/format.js +10 -1
- package/dist/src/recall/hydeTelemetry.js +22 -0
- package/dist/src/recall/sync.js +28 -2
- package/dist/src/store/turns/hydeStore.js +32 -0
- package/dist/src/store/turns/index.js +1 -0
- package/dist/src/store/turns/schema.js +102 -1
- package/dist/src/store/turns/sqlite-store/rows.js +23 -0
- package/dist/src/store/turns/sqlite-store/write.js +7 -3
- package/dist/src/topics/durability.js +187 -0
- package/dist/src/topics/index.js +2 -1
- package/dist/src/topics/store.js +28 -9
- package/dist/src/wiki/curation-helpers.js +71 -0
- package/dist/src/wiki/curation.js +147 -0
- package/dist/src/wiki/incremental.js +126 -0
- package/dist/src/wiki/index.js +5 -0
- package/extensions/dashboard-client/dist/assets/AreaChart-KAmp_6bL.js +70 -0
- package/extensions/dashboard-client/dist/assets/{SessionsTab-C30AwyoF.js.map → AreaChart-KAmp_6bL.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/CacheTab-Bk-gm-R0.js +2 -0
- package/extensions/dashboard-client/dist/assets/CacheTab-Bk-gm-R0.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/EventsTab-Cyylo0of.js +2 -0
- package/extensions/dashboard-client/dist/assets/EventsTab-Cyylo0of.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/HealthTab-D-IzmV29.js +2 -0
- package/extensions/dashboard-client/dist/assets/HealthTab-D-IzmV29.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/MaintenanceTab-CrlkCFVj.js +2 -0
- package/extensions/dashboard-client/dist/assets/MaintenanceTab-CrlkCFVj.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-DHUdZ7RR.js +2 -0
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-DHUdZ7RR.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/MetricsTab-B7I4HWVg.js +2 -0
- package/extensions/dashboard-client/dist/assets/MetricsTab-B7I4HWVg.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/OverviewTab-K6j4ZI7u.js +2 -0
- package/extensions/dashboard-client/dist/assets/OverviewTab-K6j4ZI7u.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/ReposTab-BSRWhn56.js +2 -0
- package/extensions/dashboard-client/dist/assets/ReposTab-BSRWhn56.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/SessionsTab-BXdizHN1.js +2 -0
- package/extensions/dashboard-client/dist/assets/SessionsTab-BXdizHN1.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/SetupTab-CAgyfqY3.js +2 -0
- package/extensions/dashboard-client/dist/assets/SetupTab-CAgyfqY3.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-DwgCzPD9.js +2 -0
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-DwgCzPD9.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/TurnsTab-CVKcStBp.js +5 -0
- package/extensions/dashboard-client/dist/assets/TurnsTab-CVKcStBp.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/WikiTab-BNohxTbs.js +2 -0
- package/extensions/dashboard-client/dist/assets/WikiTab-BNohxTbs.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/button-B4A-_dCA.js +2 -0
- package/extensions/dashboard-client/dist/assets/button-B4A-_dCA.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/card-Cjn7Ifoe.js +2 -0
- package/extensions/dashboard-client/dist/assets/card-Cjn7Ifoe.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/index-CQjCLvv7.js +137 -0
- package/extensions/dashboard-client/dist/assets/index-CQjCLvv7.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/index-CUA6EDRF.css +1 -0
- package/extensions/dashboard-client/dist/assets/switch-CcdXX0Zm.js +2 -0
- package/extensions/dashboard-client/dist/assets/switch-CcdXX0Zm.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/toggle-KdBnFLu3.js +2 -0
- package/extensions/dashboard-client/dist/assets/toggle-KdBnFLu3.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{useSSE-p1pOI_-P.js → useSSE-B6t44vzc.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-p1pOI_-P.js.map → useSSE-B6t44vzc.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +2 -2
- package/extensions/dashboard-client/package-lock.json +1995 -191
- package/extensions/dashboard-client/package.json +12 -1
- package/extensions/dashboard-client/postcss.config.js +6 -0
- package/extensions/dashboard-client/src/App.tsx +127 -88
- package/extensions/dashboard-client/src/api/client.ts +109 -10
- package/extensions/dashboard-client/src/components/ActiveReposTable.tsx +15 -7
- package/extensions/dashboard-client/src/components/ActiveSessionsTable.tsx +67 -55
- package/extensions/dashboard-client/src/components/CacheHitRateTrendCard.tsx +17 -13
- package/extensions/dashboard-client/src/components/CacheHitsCard.tsx +35 -30
- package/extensions/dashboard-client/src/components/CacheStatusPerModel.tsx +33 -28
- package/extensions/dashboard-client/src/components/EventCategoryFilter.tsx +5 -6
- package/extensions/dashboard-client/src/components/EventStream.tsx +17 -13
- package/extensions/dashboard-client/src/components/HydeDetailPanel.tsx +173 -0
- package/extensions/dashboard-client/src/components/LegendCard.tsx +16 -9
- package/extensions/dashboard-client/src/components/PerfCards.tsx +28 -20
- package/extensions/dashboard-client/src/components/PerfChart.tsx +9 -6
- package/extensions/dashboard-client/src/components/ProviderCacheCard.tsx +15 -7
- package/extensions/dashboard-client/src/components/RagDashboard.tsx +253 -0
- package/extensions/dashboard-client/src/components/RepoAllSessionsCard.tsx +15 -10
- package/extensions/dashboard-client/src/components/RepoContextStack.tsx +24 -16
- package/extensions/dashboard-client/src/components/RepoDetailModal.tsx +52 -49
- package/extensions/dashboard-client/src/components/RepoTable.tsx +26 -17
- package/extensions/dashboard-client/src/components/SessionContextGauges.tsx +11 -14
- package/extensions/dashboard-client/src/components/SessionInfo.tsx +20 -15
- package/extensions/dashboard-client/src/components/SessionsMemoryChart.tsx +8 -5
- package/extensions/dashboard-client/src/components/SummaryTiles.tsx +26 -17
- package/extensions/dashboard-client/src/components/TabBar.tsx +1 -3
- package/extensions/dashboard-client/src/components/layout/AppShell.tsx +42 -0
- package/extensions/dashboard-client/src/components/layout/BottomBar.tsx +88 -0
- package/extensions/dashboard-client/src/components/layout/Sidebar.tsx +83 -0
- package/extensions/dashboard-client/src/components/ui/badge.tsx +30 -0
- package/extensions/dashboard-client/src/components/ui/button.tsx +44 -0
- package/extensions/dashboard-client/src/components/ui/card.tsx +32 -0
- package/extensions/dashboard-client/src/components/ui/select.tsx +64 -0
- package/extensions/dashboard-client/src/components/ui/switch.tsx +26 -0
- package/extensions/dashboard-client/src/components/ui/tabs.tsx +36 -0
- package/extensions/dashboard-client/src/components/ui/toggle.tsx +28 -0
- package/extensions/dashboard-client/src/components/ui/tooltip.tsx +27 -0
- package/extensions/dashboard-client/src/config.ts +10 -0
- package/extensions/dashboard-client/src/main.tsx +2 -2
- package/extensions/dashboard-client/src/memory-map-layout.ts +1 -2
- package/extensions/dashboard-client/src/styles/index.css +79 -0
- package/extensions/dashboard-client/src/tabs/CacheTab.tsx +33 -26
- package/extensions/dashboard-client/src/tabs/EventsTab.tsx +22 -7
- package/extensions/dashboard-client/src/tabs/HealthTab.tsx +77 -46
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/ActionsCard.tsx +60 -50
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/DbStatsCard.tsx +61 -35
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/DebugBundleCard.tsx +34 -23
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/HealthMitigationCard.tsx +62 -43
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/SchemaHealthCard.tsx +53 -32
- package/extensions/dashboard-client/src/tabs/MaintenanceTab.tsx +2 -2
- package/extensions/dashboard-client/src/tabs/MemoryMapTab/MemoryMapView.tsx +29 -46
- package/extensions/dashboard-client/src/tabs/MemoryMapTab/RaptorTreeView.tsx +10 -34
- package/extensions/dashboard-client/src/tabs/MemoryMapTab.tsx +14 -34
- package/extensions/dashboard-client/src/tabs/MetricsTab.tsx +5 -2
- package/extensions/dashboard-client/src/tabs/OverviewTab.tsx +11 -5
- package/extensions/dashboard-client/src/tabs/ReposTab.tsx +5 -5
- package/extensions/dashboard-client/src/tabs/SessionsTab.tsx +47 -33
- package/extensions/dashboard-client/src/tabs/SetupTab/EmbedderSetup.tsx +6 -2
- package/extensions/dashboard-client/src/tabs/SetupTab/RagHealthCard.tsx +78 -0
- package/extensions/dashboard-client/src/tabs/SetupTab/SettingsPanel.tsx +198 -0
- package/extensions/dashboard-client/src/tabs/SetupTab/SettingsSection.tsx +50 -0
- package/extensions/dashboard-client/src/tabs/SetupTab.tsx +21 -59
- package/extensions/dashboard-client/src/tabs/TopicsTab.tsx +75 -40
- package/extensions/dashboard-client/src/tabs/TurnsTab.tsx +150 -112
- package/extensions/dashboard-client/src/tabs/WikiTab/TopicEvolutionGraph.tsx +274 -0
- package/extensions/dashboard-client/src/tabs/WikiTab/TopicEvolutionView.tsx +71 -0
- package/extensions/dashboard-client/src/tabs/WikiTab/TopicTimeline.tsx +103 -0
- package/extensions/dashboard-client/src/tabs/WikiTab/WikiPage.tsx +213 -0
- package/extensions/dashboard-client/src/tabs/WikiTab/WikiPageControls.tsx +188 -0
- package/extensions/dashboard-client/src/tabs/WikiTab.tsx +222 -0
- package/extensions/dashboard-client/src/tabs/registry.ts +57 -0
- package/extensions/dashboard-client/src/utils/cn.ts +7 -0
- package/extensions/dashboard-client/tailwind.config.js +46 -0
- package/extensions/dashboard-server/api-contracts/core.ts +59 -0
- package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +79 -0
- package/extensions/dashboard-server/api-contracts/index.ts +48 -2
- package/extensions/dashboard-server/api-contracts/rag-metrics.ts +22 -0
- package/extensions/dashboard-server/api-contracts/rag-settings.ts +30 -20
- package/extensions/dashboard-server/api-contracts/wiki.ts +138 -0
- package/extensions/dashboard-server/routes-rag-metrics.ts +82 -0
- package/extensions/dashboard-server/routes-rag-settings-helpers.ts +314 -0
- package/extensions/dashboard-server/routes-rag-settings.ts +147 -97
- package/extensions/dashboard-server/routes-wiki-helpers.ts +126 -0
- package/extensions/dashboard-server/routes-wiki.ts +376 -0
- package/extensions/dashboard-server/routes.ts +2 -0
- package/extensions/dashboard-server/server.ts +18 -2
- package/extensions/mega-events/context-handler/afterCompact.ts +44 -1
- package/extensions/mega-pipeline/recall.ts +31 -0
- package/extensions/mega-turn-store.ts +13 -0
- package/package.json +1 -1
- package/src/config/turns.ts +17 -0
- package/src/config.ts +3 -0
- package/src/recall/async.ts +10 -1
- package/src/recall/format.ts +15 -2
- package/src/recall/hydeTelemetry.ts +44 -0
- package/src/recall/sync.ts +34 -2
- package/src/recall/types.ts +36 -0
- package/src/store/turns/hydeStore.ts +82 -0
- package/src/store/turns/index.ts +4 -0
- package/src/store/turns/schema.ts +126 -1
- package/src/store/turns/sqlite-store/rows.ts +25 -0
- package/src/store/turns/sqlite-store/write.ts +20 -2
- package/src/store/turns/types.ts +33 -0
- package/src/topics/durability.ts +231 -0
- package/src/topics/index.ts +7 -1
- package/src/topics/store.ts +35 -9
- package/src/wiki/curation-helpers.ts +140 -0
- package/src/wiki/curation.ts +217 -0
- package/src/wiki/incremental.ts +164 -0
- package/src/wiki/index.ts +13 -0
- package/extensions/dashboard-client/dist/assets/CacheTab-Cqnc7lMg.js +0 -2
- package/extensions/dashboard-client/dist/assets/CacheTab-Cqnc7lMg.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/EventsTab-tYk3VFAp.js +0 -2
- package/extensions/dashboard-client/dist/assets/EventsTab-tYk3VFAp.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/HealthTab-D2_kjgxw.js +0 -2
- package/extensions/dashboard-client/dist/assets/HealthTab-D2_kjgxw.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/MaintenanceTab-Bc-uPYrz.js +0 -2
- package/extensions/dashboard-client/dist/assets/MaintenanceTab-Bc-uPYrz.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-D1V9g_NC.js +0 -2
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-D1V9g_NC.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/MetricsTab-DT_XfL96.js +0 -2
- package/extensions/dashboard-client/dist/assets/MetricsTab-DT_XfL96.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/OverviewTab-BY6KZCme.js +0 -2
- package/extensions/dashboard-client/dist/assets/OverviewTab-BY6KZCme.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/ReposTab-CeS-tpDM.js +0 -2
- package/extensions/dashboard-client/dist/assets/ReposTab-CeS-tpDM.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/SessionsTab-C30AwyoF.js +0 -70
- package/extensions/dashboard-client/dist/assets/SetupTab-BJUHOVK2.js +0 -2
- package/extensions/dashboard-client/dist/assets/SetupTab-BJUHOVK2.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-p55K1tFP.js +0 -2
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-p55K1tFP.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/TopicsTab-D8FqyAbK.js +0 -2
- package/extensions/dashboard-client/dist/assets/TopicsTab-D8FqyAbK.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/TurnsTab-ju-nXDys.js +0 -2
- package/extensions/dashboard-client/dist/assets/TurnsTab-ju-nXDys.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/index-BEj1Zahy.js +0 -42
- package/extensions/dashboard-client/dist/assets/index-BEj1Zahy.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/index-CUNn22iV.css +0 -1
- package/extensions/dashboard-client/src/styles/metrics-extra.css +0 -69
- package/extensions/dashboard-client/src/styles/turns.css +0 -142
- package/extensions/dashboard-client/src/tabs/SetupTab/RagSettingsCard.tsx +0 -160
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* dashboard-server/routes-rag-settings.ts —
|
|
2
|
+
* dashboard-server/routes-rag-settings.ts — comprehensive settings route handler.
|
|
3
3
|
*
|
|
4
|
-
* GET /api/rag-settings — Returns
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* GET /api/rag-settings — Returns every adjustable MEGACOMPACT_* setting,
|
|
5
|
+
* grouped by category, with live values from env.
|
|
6
|
+
* POST /api/rag-settings — Updates a single setting by writing its line to the
|
|
7
|
+
* per-repo .mega-compact.env file.
|
|
8
|
+
*
|
|
9
|
+
* The SETTINGS inventory lives in routes-rag-settings-helpers.ts so this file
|
|
10
|
+
* stays within the line budget.
|
|
7
11
|
*
|
|
8
12
|
* Guardrails: PREVENT-PI-004 (loopback-only), PREVENT-001 (null-safe JSON),
|
|
9
13
|
* PREVENT-011 (no `any`). Each JSON write carries a guardrails-allow annotation.
|
|
@@ -14,68 +18,101 @@ import { join } from "node:path";
|
|
|
14
18
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
15
19
|
import type { RouteContext } from "./routes-core.js";
|
|
16
20
|
import { detectCurrentEmbedder } from "./routes-setup.js";
|
|
21
|
+
import { SETTINGS, SETTING_BY_KEY } from "./routes-rag-settings-helpers.js";
|
|
22
|
+
import type { SettingSpec } from "./routes-rag-settings-helpers.js";
|
|
17
23
|
import type {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
SettingState,
|
|
25
|
+
SettingsResponse,
|
|
26
|
+
SettingsUpdateRequest,
|
|
27
|
+
SettingsResponsePost,
|
|
22
28
|
} from "./api-contracts/rag-settings.js";
|
|
23
29
|
|
|
24
30
|
// ---------------------------------------------------------------------------
|
|
25
|
-
//
|
|
31
|
+
// Env-reading helpers — one per setting type.
|
|
26
32
|
// ---------------------------------------------------------------------------
|
|
27
33
|
|
|
28
|
-
const RAG_FLAGS: {
|
|
29
|
-
key: string;
|
|
30
|
-
label: string;
|
|
31
|
-
description: string;
|
|
32
|
-
requiresLlm: boolean;
|
|
33
|
-
}[] = [
|
|
34
|
-
{
|
|
35
|
-
key: "MEGACOMPACT_QUERY_REFORMULATION",
|
|
36
|
-
label: "Query Reformulation",
|
|
37
|
-
description:
|
|
38
|
-
"TF-IDF keyword expansion for vague recall queries (TrigramEmbedder path)",
|
|
39
|
-
requiresLlm: false,
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
key: "MEGACOMPACT_TIERED_ROUTER",
|
|
43
|
-
label: "Tiered Recall Router",
|
|
44
|
-
description:
|
|
45
|
-
"L0 cache → L1 FTS5 → L2 HNSW routing for faster recall",
|
|
46
|
-
requiresLlm: false,
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
key: "MEGACOMPACT_RECALL_METRICS",
|
|
50
|
-
label: "Recall Quality Metrics",
|
|
51
|
-
description:
|
|
52
|
-
"Precision/recall scoring and logging for recall evaluation",
|
|
53
|
-
requiresLlm: false,
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
key: "MEGACOMPACT_MEMORY_GRAPH",
|
|
57
|
-
label: "Memory Graph",
|
|
58
|
-
description:
|
|
59
|
-
"Dashboard-oriented memory graph traversal across sessions",
|
|
60
|
-
requiresLlm: false,
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
key: "MEGACOMPACT_HYDE",
|
|
64
|
-
label: "HyDE (Hypothetical Document Embeddings)",
|
|
65
|
-
description:
|
|
66
|
-
"Generate hypothetical answer via LLM, embed it, RRF-fuse with raw-query results",
|
|
67
|
-
requiresLlm: true,
|
|
68
|
-
},
|
|
69
|
-
];
|
|
70
|
-
|
|
71
|
-
/** Strip any MEGACOMPACT_*_DISABLED assignment lines from env file content. */
|
|
72
|
-
const DISABLED_LINE = /^export\s+MEGACOMPACT_\w+_DISABLED=/;
|
|
73
|
-
|
|
74
34
|
function isDisabled(key: string): boolean {
|
|
75
35
|
const v = process.env[key + "_DISABLED"];
|
|
76
36
|
return v === "true" || v === "1";
|
|
77
37
|
}
|
|
78
38
|
|
|
39
|
+
function isEnabled(key: string, def: boolean): boolean {
|
|
40
|
+
const v = process.env[key];
|
|
41
|
+
if (v === undefined) return def;
|
|
42
|
+
return v === "true" || v === "1";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function readNumber(key: string, def: number): number {
|
|
46
|
+
const v = process.env[key];
|
|
47
|
+
if (v === undefined) return def;
|
|
48
|
+
const n = Number(v);
|
|
49
|
+
return Number.isNaN(n) ? def : n;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function resolveValue(spec: SettingSpec): string | number | boolean {
|
|
53
|
+
if (spec.type === "boolean") {
|
|
54
|
+
return spec.disabledConvention
|
|
55
|
+
? !isDisabled(spec.key)
|
|
56
|
+
: isEnabled(spec.key, spec.default as boolean);
|
|
57
|
+
}
|
|
58
|
+
if (spec.type === "number") {
|
|
59
|
+
return readNumber(spec.key, spec.default as number);
|
|
60
|
+
}
|
|
61
|
+
return process.env[spec.key] ?? spec.default;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function toSettingState(spec: SettingSpec, category: string): SettingState {
|
|
65
|
+
return {
|
|
66
|
+
key: spec.key,
|
|
67
|
+
label: spec.label,
|
|
68
|
+
description: spec.description,
|
|
69
|
+
category,
|
|
70
|
+
type: spec.type,
|
|
71
|
+
value: resolveValue(spec),
|
|
72
|
+
default: spec.default,
|
|
73
|
+
disabledConvention: spec.disabledConvention,
|
|
74
|
+
requiresLlm: spec.requiresLlm,
|
|
75
|
+
...(spec.unit !== undefined ? { unit: spec.unit } : {}),
|
|
76
|
+
...(spec.min !== undefined ? { min: spec.min } : {}),
|
|
77
|
+
...(spec.max !== undefined ? { max: spec.max } : {}),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Env file I/O.
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
/** Strip lines assigning KEY or KEY_DISABLED from env file content. */
|
|
86
|
+
const keyLinePattern = (key: string): RegExp =>
|
|
87
|
+
new RegExp(`^export\\s+${key}(_DISABLED)?=`);
|
|
88
|
+
|
|
89
|
+
function envPathOf(ctx: RouteContext): string {
|
|
90
|
+
return join(ctx.stateDir, ".mega-compact.env");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function readEnvLines(envPath: string, key: string): string[] {
|
|
94
|
+
if (!existsSync(envPath)) return [];
|
|
95
|
+
return readFileSync(envPath, "utf-8")
|
|
96
|
+
.split("\n")
|
|
97
|
+
.filter((l) => !keyLinePattern(key).test(l));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function writeSetting(envPath: string, key: string, line: string): void {
|
|
101
|
+
const lines = readEnvLines(envPath, key);
|
|
102
|
+
lines.push(line);
|
|
103
|
+
if (lines[lines.length - 1] !== "") lines.push("");
|
|
104
|
+
writeFileSync(envPath, lines.join("\n"), "utf-8");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Strip KEY/KEY_DISABLED lines, leaving the file otherwise intact. */
|
|
108
|
+
function removeSetting(envPath: string, key: string): void {
|
|
109
|
+
writeFileSync(envPath, readEnvLines(envPath, key).join("\n"), "utf-8");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// Request body parsing.
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
|
|
79
116
|
function readJsonBody(
|
|
80
117
|
req: IncomingMessage,
|
|
81
118
|
cb: (
|
|
@@ -96,7 +133,7 @@ function readJsonBody(
|
|
|
96
133
|
req.on("end", () => {
|
|
97
134
|
if (tooBig) return cb({ ok: false, error: "body_too_large" });
|
|
98
135
|
try {
|
|
99
|
-
const v = body ? JSON.parse(body) : {}; // PREVENT-001: parsed value type-checked below
|
|
136
|
+
const v: unknown = body ? JSON.parse(body) : {}; // PREVENT-001: parsed value type-checked below
|
|
100
137
|
if (typeof v !== "object" || v === null || Array.isArray(v)) {
|
|
101
138
|
return cb({ ok: false, error: "invalid_object" });
|
|
102
139
|
}
|
|
@@ -120,15 +157,12 @@ export function handleRagSettings(
|
|
|
120
157
|
if (req.url !== "/api/rag-settings") return false;
|
|
121
158
|
|
|
122
159
|
if (req.method === "GET") {
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
description: f.description,
|
|
127
|
-
enabled: !isDisabled(f.key),
|
|
128
|
-
requiresLlm: f.requiresLlm,
|
|
160
|
+
const categories = SETTINGS.map((cat) => ({
|
|
161
|
+
name: cat.name,
|
|
162
|
+
settings: cat.settings.map((s) => toSettingState(s, cat.name)),
|
|
129
163
|
}));
|
|
130
|
-
const body:
|
|
131
|
-
|
|
164
|
+
const body: SettingsResponse = {
|
|
165
|
+
categories,
|
|
132
166
|
llmActive: detectCurrentEmbedder() === "http",
|
|
133
167
|
};
|
|
134
168
|
// guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
|
|
@@ -145,51 +179,60 @@ export function handleRagSettings(
|
|
|
145
179
|
res.end(JSON.stringify({ error: parsed.error }));
|
|
146
180
|
return;
|
|
147
181
|
}
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
|
|
182
|
+
const reqBody = parsed.value as unknown as SettingsUpdateRequest;
|
|
183
|
+
const key = reqBody.key;
|
|
184
|
+
const value = reqBody.value;
|
|
185
|
+
if (typeof key !== "string" || typeof value !== "string") {
|
|
151
186
|
// guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
|
|
152
187
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
153
|
-
res.end(JSON.stringify({ error: "
|
|
188
|
+
res.end(JSON.stringify({ error: "invalid_body" }));
|
|
154
189
|
return;
|
|
155
190
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
res.end(JSON.stringify({ error: `unknown_flag: ${key}` }));
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
191
|
+
const spec = SETTING_BY_KEY.get(key);
|
|
192
|
+
if (!spec) {
|
|
193
|
+
// guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
|
|
194
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
195
|
+
res.end(JSON.stringify({ error: `unknown_setting: ${key}` }));
|
|
196
|
+
return;
|
|
165
197
|
}
|
|
166
198
|
|
|
167
|
-
const envPath =
|
|
168
|
-
let lines: string[] = [];
|
|
169
|
-
if (existsSync(envPath)) {
|
|
170
|
-
const content = readFileSync(envPath, "utf-8");
|
|
171
|
-
lines = content.split("\n").filter((line) => !DISABLED_LINE.test(line));
|
|
172
|
-
}
|
|
173
|
-
for (const key of Object.keys(desired)) {
|
|
174
|
-
if (desired[key] === false) {
|
|
175
|
-
lines.push(`export ${key}_DISABLED="true"`);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
if (lines.length > 0 && lines[lines.length - 1] !== "") lines.push("");
|
|
199
|
+
const envPath = envPathOf(ctx);
|
|
179
200
|
try {
|
|
180
201
|
mkdirSync(ctx.stateDir, { recursive: true });
|
|
181
|
-
|
|
202
|
+
if (spec.type === "boolean") {
|
|
203
|
+
if (value !== "true" && value !== "false") {
|
|
204
|
+
throw new SettingError("boolean_value_required");
|
|
205
|
+
}
|
|
206
|
+
if (spec.disabledConvention) {
|
|
207
|
+
// false → opt out via KEY_DISABLED="true"; true → strip the line.
|
|
208
|
+
if (value === "false") {
|
|
209
|
+
writeSetting(envPath, spec.key, `export ${spec.key}_DISABLED="true"`);
|
|
210
|
+
} else {
|
|
211
|
+
removeSetting(envPath, spec.key);
|
|
212
|
+
}
|
|
213
|
+
} else {
|
|
214
|
+
writeSetting(envPath, spec.key, `export ${spec.key}="${value}"`);
|
|
215
|
+
}
|
|
216
|
+
} else if (spec.type === "number") {
|
|
217
|
+
const n = Number(value);
|
|
218
|
+
if (Number.isNaN(n)) throw new SettingError("invalid_number");
|
|
219
|
+
if (spec.min !== undefined && n < spec.min) throw new SettingError("below_min");
|
|
220
|
+
if (spec.max !== undefined && n > spec.max) throw new SettingError("above_max");
|
|
221
|
+
writeSetting(envPath, spec.key, `export ${spec.key}="${n}"`);
|
|
222
|
+
} else {
|
|
223
|
+
writeSetting(envPath, spec.key, `export ${spec.key}="${value}"`);
|
|
224
|
+
}
|
|
182
225
|
} catch (e) {
|
|
226
|
+
const msg =
|
|
227
|
+
e instanceof SettingError
|
|
228
|
+
? e.message
|
|
229
|
+
: `write_failed: ${e instanceof Error ? e.message : String(e)}`;
|
|
183
230
|
// guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
|
|
184
|
-
res.writeHead(
|
|
185
|
-
res.end(
|
|
186
|
-
JSON.stringify({
|
|
187
|
-
error: `write_failed: ${e instanceof Error ? e.message : String(e)}`,
|
|
188
|
-
}),
|
|
189
|
-
);
|
|
231
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
232
|
+
res.end(JSON.stringify({ error: msg }));
|
|
190
233
|
return;
|
|
191
234
|
}
|
|
192
|
-
const resp:
|
|
235
|
+
const resp: SettingsResponsePost = {
|
|
193
236
|
envPath,
|
|
194
237
|
restartRequired: true,
|
|
195
238
|
};
|
|
@@ -205,3 +248,10 @@ export function handleRagSettings(
|
|
|
205
248
|
res.end(JSON.stringify({ error: "method_not_allowed" }));
|
|
206
249
|
return true;
|
|
207
250
|
}
|
|
251
|
+
|
|
252
|
+
class SettingError extends Error {
|
|
253
|
+
constructor(message: string) {
|
|
254
|
+
super(message);
|
|
255
|
+
this.name = "SettingError";
|
|
256
|
+
}
|
|
257
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* routes-wiki-helpers.ts — shared read/build helpers for routes-wiki.ts.
|
|
3
|
+
*
|
|
4
|
+
* Content access, index-entry resolution, provenance + time-bucket builders,
|
|
5
|
+
* lazy topic-model build, and SSE emission. Kept out of the dispatcher so
|
|
6
|
+
* routes-wiki.ts stays a thin pointer. Loopback-only (PREVENT-PI-004),
|
|
7
|
+
* parameterized (PREVENT-002).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { appendFileSync } from "node:fs";
|
|
11
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
12
|
+
import type { RouteContext } from "./routes-core.js";
|
|
13
|
+
import { openStore } from "../../src/store/sqlite.js";
|
|
14
|
+
import { createTopicStore } from "../../src/topics/store.js";
|
|
15
|
+
import { buildTopicModel } from "../../src/topics/cluster.js";
|
|
16
|
+
import { createWikiCuration } from "../../src/wiki/curation.js";
|
|
17
|
+
import type { TopicAssignment, Topic } from "../../src/topics/types.js";
|
|
18
|
+
import type {
|
|
19
|
+
WikiIndexEntry,
|
|
20
|
+
MemoryProvenance,
|
|
21
|
+
} from "./api-contracts/wiki.js";
|
|
22
|
+
|
|
23
|
+
/** Append a JSONL SSE line to events.log (best-effort, non-fatal). */
|
|
24
|
+
export function emitWikiEvent(
|
|
25
|
+
ctx: RouteContext,
|
|
26
|
+
event: { type: string } & Record<string, unknown>,
|
|
27
|
+
): void {
|
|
28
|
+
try {
|
|
29
|
+
appendFileSync(
|
|
30
|
+
ctx.eventsPath,
|
|
31
|
+
JSON.stringify({ ts: new Date().toISOString(), ...event }) + "\n",
|
|
32
|
+
);
|
|
33
|
+
} catch {
|
|
34
|
+
/* non-fatal: SSE push best-effort */
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Lazy-build the topic model when the topics table is empty (best-effort). */
|
|
39
|
+
export function ensureWikiBuilt(ctx: RouteContext, tdb: DatabaseSync): void {
|
|
40
|
+
const topicCount = (
|
|
41
|
+
tdb.prepare("SELECT COUNT(*) AS c FROM topics").get() as { c: number }
|
|
42
|
+
).c;
|
|
43
|
+
if (topicCount > 0) return;
|
|
44
|
+
try {
|
|
45
|
+
const mainDb = openStore(ctx.stateDir);
|
|
46
|
+
const model = buildTopicModel(mainDb);
|
|
47
|
+
if (model.k > 0 && model.totalChunks > 0) {
|
|
48
|
+
createTopicStore(ctx.stateDir).replaceTopicModel(model);
|
|
49
|
+
}
|
|
50
|
+
} catch {
|
|
51
|
+
/* non-fatal: lazy wiki build is best-effort */
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Build a topic index entry with resolved label + override badges. */
|
|
56
|
+
export function toIndexEntry(
|
|
57
|
+
topic: Topic,
|
|
58
|
+
curation: ReturnType<typeof createWikiCuration>,
|
|
59
|
+
): WikiIndexEntry {
|
|
60
|
+
const resolved = curation.resolveLabel(topic.id, topic.label);
|
|
61
|
+
return {
|
|
62
|
+
id: topic.id,
|
|
63
|
+
label: resolved.label,
|
|
64
|
+
memoryCount: topic.memoryCount,
|
|
65
|
+
lastUpdated: topic.lastUpdated,
|
|
66
|
+
edited: resolved.edited,
|
|
67
|
+
overrideKinds: curation.overrideKinds(topic.id),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Convert a member assignment into contract provenance. */
|
|
72
|
+
export function toProvenance(member: TopicAssignment): MemoryProvenance {
|
|
73
|
+
return {
|
|
74
|
+
memoryId: member.memoryId,
|
|
75
|
+
sessionId: member.sessionId ?? "",
|
|
76
|
+
assignedAt: member.assignedAt,
|
|
77
|
+
method: member.method,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Bucket epoch-ms values by day; returns ascending [{bucket, count}]. */
|
|
82
|
+
export function bucketize(
|
|
83
|
+
values: number[],
|
|
84
|
+
): Array<{ bucket: number; count: number }> {
|
|
85
|
+
const DAY = 86_400_000;
|
|
86
|
+
const counts = new Map<number, number>();
|
|
87
|
+
for (const v of values) {
|
|
88
|
+
if (!Number.isFinite(v) || v <= 0) continue;
|
|
89
|
+
const b = Math.floor(v / DAY) * DAY;
|
|
90
|
+
counts.set(b, (counts.get(b) ?? 0) + 1);
|
|
91
|
+
}
|
|
92
|
+
return [...counts.entries()]
|
|
93
|
+
.sort((a, b) => a[0] - b[0])
|
|
94
|
+
.map(([bucket, count]) => ({ bucket, count }));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Fetch member content (text + timestamp) from the main DB. */
|
|
98
|
+
export function memberContent(
|
|
99
|
+
ctx: RouteContext,
|
|
100
|
+
members: TopicAssignment[],
|
|
101
|
+
): Map<string, { text: string; timestamp: number }> {
|
|
102
|
+
const out = new Map<string, { text: string; timestamp: number }>();
|
|
103
|
+
if (members.length === 0) return out;
|
|
104
|
+
try {
|
|
105
|
+
const mainDb = openStore(ctx.stateDir);
|
|
106
|
+
for (const m of members) {
|
|
107
|
+
const row = mainDb
|
|
108
|
+
.prepare(
|
|
109
|
+
`SELECT COALESCE(normalized_text, summary, topic_summary) AS text, timestamp
|
|
110
|
+
FROM context_chunks WHERE session_id = ? AND id = ?`,
|
|
111
|
+
)
|
|
112
|
+
.get(m.sessionId ?? "", m.memoryId) as
|
|
113
|
+
| { text: string | null; timestamp: number | null }
|
|
114
|
+
| undefined;
|
|
115
|
+
if (row) {
|
|
116
|
+
out.set(m.memoryId, {
|
|
117
|
+
text: (row.text ?? "").trim(),
|
|
118
|
+
timestamp: row.timestamp ?? 0,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
} catch {
|
|
123
|
+
/* content fetch is best-effort */
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|