pi-mega-compact 0.13.5 → 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/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
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dashboard-server/routes-wiki.ts — W2 wiki revival routes (curation + provenance).
|
|
3
|
+
*
|
|
4
|
+
* GET /api/wiki/index — topics with resolved labels
|
|
5
|
+
* GET /api/wiki/evolution — D3 nodes/edges/time buckets
|
|
6
|
+
* GET /api/wiki/topic/:topicId — single topic page + provenance
|
|
7
|
+
* GET /api/wiki/topic/:topicId/timeline — per-topic time buckets
|
|
8
|
+
* PUT /api/wiki/topic/:topicId/label — rename (emits wiki_topic_renamed)
|
|
9
|
+
* POST /api/wiki/merge — merge (emits wiki_topics_merged)
|
|
10
|
+
* POST /api/wiki/topic/:topicId/split — split (emits wiki_topic_split)
|
|
11
|
+
*
|
|
12
|
+
* Flag-gated by MEGACOMPACT_WIKI_ENHANCED (flag-OFF → 404 for all /api/wiki/*).
|
|
13
|
+
* Read/build helpers live in routes-wiki-helpers.ts (pointer-file split).
|
|
14
|
+
* Loopback-only (PREVENT-PI-004); parameterized (PREVENT-002); no `any`.
|
|
15
|
+
*/
|
|
16
|
+
import { TurnsConfig } from "../../src/config/turns.js";
|
|
17
|
+
import { openTurnStore } from "../../src/store/turns/connection.js";
|
|
18
|
+
import { createTopicStore } from "../../src/topics/store.js";
|
|
19
|
+
import { extractiveSummary } from "../../src/wiki.js";
|
|
20
|
+
import { createWikiCuration } from "../../src/wiki/curation.js";
|
|
21
|
+
import { emitWikiEvent, ensureWikiBuilt, toIndexEntry, toProvenance, bucketize, memberContent, } from "./routes-wiki-helpers.js";
|
|
22
|
+
function sendJson(res, status, body) {
|
|
23
|
+
// guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
|
|
24
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
25
|
+
res.end(JSON.stringify(body));
|
|
26
|
+
}
|
|
27
|
+
/** Decode a percent-encoded path segment; null on malformed encoding (%zz). */
|
|
28
|
+
function decodeSegment(raw) {
|
|
29
|
+
try {
|
|
30
|
+
return decodeURIComponent(raw);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Read a capped JSON body; returns { ok, value } or { error }. */
|
|
37
|
+
function readJsonBody(req, cb) {
|
|
38
|
+
let body = "";
|
|
39
|
+
let tooBig = false;
|
|
40
|
+
req.on("data", (chunk) => {
|
|
41
|
+
if (body.length > 65536) {
|
|
42
|
+
tooBig = true;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
body += chunk.toString();
|
|
46
|
+
});
|
|
47
|
+
req.on("end", () => {
|
|
48
|
+
if (tooBig)
|
|
49
|
+
return cb({ ok: false, error: "body_too_large" });
|
|
50
|
+
try {
|
|
51
|
+
const v = body ? JSON.parse(body) : {};
|
|
52
|
+
if (typeof v !== "object" || v === null || Array.isArray(v)) {
|
|
53
|
+
return cb({ ok: false, error: "invalid_object" });
|
|
54
|
+
}
|
|
55
|
+
cb({ ok: true, value: v });
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
cb({ ok: false, error: "invalid_json" });
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
export function handleWiki(req, res, ctx) {
|
|
63
|
+
const url = req.url ?? "";
|
|
64
|
+
if (!url.startsWith("/api/wiki"))
|
|
65
|
+
return false;
|
|
66
|
+
// Flag gate: feature-OFF → 404 for every wiki endpoint.
|
|
67
|
+
if (!TurnsConfig.WIKI_ENHANCED_ENABLED) {
|
|
68
|
+
sendJson(res, 404, { error: "wiki_enhanced_disabled" });
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
// ── GET /api/wiki/index ──────────────────────────────────────────
|
|
72
|
+
if (req.method === "GET" && url === "/api/wiki/index") {
|
|
73
|
+
try {
|
|
74
|
+
const tdb = openTurnStore(ctx.stateDir);
|
|
75
|
+
ensureWikiBuilt(ctx, tdb);
|
|
76
|
+
const store = createTopicStore(ctx.stateDir);
|
|
77
|
+
const curation = createWikiCuration(ctx.stateDir);
|
|
78
|
+
const entries = store.getTopics().map((t) => toIndexEntry(t, curation));
|
|
79
|
+
const totalMemories = entries.reduce((s, e) => s + e.memoryCount, 0);
|
|
80
|
+
const lastRebuildAt = entries.length > 0 ? Math.max(...entries.map((e) => e.lastUpdated)) : null;
|
|
81
|
+
const body = {
|
|
82
|
+
topics: entries,
|
|
83
|
+
totalTopics: entries.length,
|
|
84
|
+
totalMemories,
|
|
85
|
+
lastRebuildAt,
|
|
86
|
+
};
|
|
87
|
+
sendJson(res, 200, body);
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
sendJson(res, 500, { error: String(e) });
|
|
91
|
+
}
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
// ── GET /api/wiki/evolution ──────────────────────────────────────
|
|
95
|
+
if (req.method === "GET" && url === "/api/wiki/evolution") {
|
|
96
|
+
try {
|
|
97
|
+
const tdb = openTurnStore(ctx.stateDir);
|
|
98
|
+
const curation = createWikiCuration(ctx.stateDir);
|
|
99
|
+
const nodes = createTopicStore(ctx.stateDir)
|
|
100
|
+
.getTopics()
|
|
101
|
+
.map((t) => ({
|
|
102
|
+
id: t.id,
|
|
103
|
+
label: curation.resolveLabel(t.id, t.label).label,
|
|
104
|
+
memoryCount: t.memoryCount,
|
|
105
|
+
}));
|
|
106
|
+
const overrides = tdb
|
|
107
|
+
.prepare(`SELECT kind, topic_id, merged_into, split_from, overridden_at
|
|
108
|
+
FROM topic_overrides WHERE kind IN ('merge','split')`)
|
|
109
|
+
.all();
|
|
110
|
+
const edges = overrides.flatMap((o) => {
|
|
111
|
+
if (o.kind === "merge" && o.merged_into) {
|
|
112
|
+
return [
|
|
113
|
+
{
|
|
114
|
+
source: o.topic_id,
|
|
115
|
+
target: o.merged_into,
|
|
116
|
+
kind: "merge",
|
|
117
|
+
at: o.overridden_at,
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
}
|
|
121
|
+
if (o.kind === "split" && o.split_from) {
|
|
122
|
+
return [
|
|
123
|
+
{
|
|
124
|
+
source: o.split_from,
|
|
125
|
+
target: o.topic_id,
|
|
126
|
+
kind: "split",
|
|
127
|
+
at: o.overridden_at,
|
|
128
|
+
},
|
|
129
|
+
];
|
|
130
|
+
}
|
|
131
|
+
return [];
|
|
132
|
+
});
|
|
133
|
+
const evoRows = tdb
|
|
134
|
+
.prepare("SELECT assigned_at FROM topic_evolution")
|
|
135
|
+
.all();
|
|
136
|
+
const body = {
|
|
137
|
+
nodes,
|
|
138
|
+
edges,
|
|
139
|
+
buckets: bucketize(evoRows.map((r) => r.assigned_at)),
|
|
140
|
+
};
|
|
141
|
+
sendJson(res, 200, body);
|
|
142
|
+
}
|
|
143
|
+
catch (e) {
|
|
144
|
+
sendJson(res, 500, { error: String(e) });
|
|
145
|
+
}
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
// ── GET /api/wiki/topic/:topicId/timeline ────────────────────────
|
|
149
|
+
const timelineMatch = req.method === "GET"
|
|
150
|
+
? url.match(/^\/api\/wiki\/topic\/([^/?]+)\/timeline$/)
|
|
151
|
+
: null;
|
|
152
|
+
if (timelineMatch) {
|
|
153
|
+
const topicId = decodeSegment(timelineMatch[1]);
|
|
154
|
+
if (topicId === null) {
|
|
155
|
+
sendJson(res, 400, { error: "malformed_topic_id" });
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
const tdb = openTurnStore(ctx.stateDir);
|
|
160
|
+
const rows = tdb
|
|
161
|
+
.prepare("SELECT assigned_at FROM memory_topics WHERE topic_id = ?")
|
|
162
|
+
.all(topicId);
|
|
163
|
+
const body = {
|
|
164
|
+
topicId,
|
|
165
|
+
buckets: bucketize(rows.map((r) => r.assigned_at)),
|
|
166
|
+
total: rows.length,
|
|
167
|
+
};
|
|
168
|
+
sendJson(res, 200, body);
|
|
169
|
+
}
|
|
170
|
+
catch (e) {
|
|
171
|
+
sendJson(res, 500, { error: String(e) });
|
|
172
|
+
}
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
// ── GET /api/wiki/topic/:topicId ─────────────────────────────────
|
|
176
|
+
const topicMatch = req.method === "GET" ? url.match(/^\/api\/wiki\/topic\/([^/?]+)$/) : null;
|
|
177
|
+
if (topicMatch) {
|
|
178
|
+
const topicId = decodeSegment(topicMatch[1]);
|
|
179
|
+
if (topicId === null) {
|
|
180
|
+
sendJson(res, 400, { error: "malformed_topic_id" });
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
const store = createTopicStore(ctx.stateDir);
|
|
185
|
+
const topic = store.getTopics().find((t) => t.id === topicId);
|
|
186
|
+
if (!topic) {
|
|
187
|
+
sendJson(res, 404, { error: "topic_not_found" });
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
const members = store.getMemoriesForTopic(topicId, 200);
|
|
191
|
+
const content = memberContent(ctx, members);
|
|
192
|
+
const getText = (memId) => content.get(memId)?.text ?? "";
|
|
193
|
+
const summary = extractiveSummary(topic, members, getText);
|
|
194
|
+
const present = members.filter((m) => content.has(m.memoryId));
|
|
195
|
+
const keyMemories = [...present]
|
|
196
|
+
.sort((a, b) => (content.get(b.memoryId)?.text.length ?? 0) -
|
|
197
|
+
(content.get(a.memoryId)?.text.length ?? 0))
|
|
198
|
+
.slice(0, 10)
|
|
199
|
+
.map((m) => ({
|
|
200
|
+
memoryId: m.memoryId,
|
|
201
|
+
content: content.get(m.memoryId)?.text ?? "",
|
|
202
|
+
timestamp: content.get(m.memoryId)?.timestamp ?? 0,
|
|
203
|
+
importance: Math.round((content.get(m.memoryId)?.text.length ?? 0) / 40),
|
|
204
|
+
}));
|
|
205
|
+
const relatedTopicIds = store
|
|
206
|
+
.getTopics()
|
|
207
|
+
.filter((t) => t.id !== topicId && t.memoryCount > 0)
|
|
208
|
+
.slice(0, 3)
|
|
209
|
+
.map((t) => t.id);
|
|
210
|
+
const body = {
|
|
211
|
+
topic: toIndexEntry(topic, createWikiCuration(ctx.stateDir)),
|
|
212
|
+
summary,
|
|
213
|
+
keyMemories,
|
|
214
|
+
provenance: members.map(toProvenance),
|
|
215
|
+
relatedTopicIds,
|
|
216
|
+
generatedAt: Date.now(),
|
|
217
|
+
};
|
|
218
|
+
sendJson(res, 200, body);
|
|
219
|
+
}
|
|
220
|
+
catch (e) {
|
|
221
|
+
sendJson(res, 500, { error: String(e) });
|
|
222
|
+
}
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
// ── PUT /api/wiki/topic/:topicId/label ───────────────────────────
|
|
226
|
+
const renameMatch = req.method === "PUT"
|
|
227
|
+
? url.match(/^\/api\/wiki\/topic\/([^/?]+)\/label$/)
|
|
228
|
+
: null;
|
|
229
|
+
if (renameMatch) {
|
|
230
|
+
readJsonBody(req, (result) => {
|
|
231
|
+
if (!result.ok)
|
|
232
|
+
return sendJson(res, 400, { error: result.error });
|
|
233
|
+
const topicId = decodeSegment(renameMatch[1]);
|
|
234
|
+
if (topicId === null)
|
|
235
|
+
return sendJson(res, 400, { error: "malformed_topic_id" });
|
|
236
|
+
const label = typeof result.value.label === "string" ? result.value.label : "";
|
|
237
|
+
try {
|
|
238
|
+
const curation = createWikiCuration(ctx.stateDir);
|
|
239
|
+
const out = curation.renameTopic(topicId, label);
|
|
240
|
+
if (!out.ok)
|
|
241
|
+
return sendJson(res, 404, { error: "topic_not_found" });
|
|
242
|
+
emitWikiEvent(ctx, {
|
|
243
|
+
type: "wiki_topic_renamed",
|
|
244
|
+
topicId,
|
|
245
|
+
label: out.label,
|
|
246
|
+
edited: out.edited,
|
|
247
|
+
});
|
|
248
|
+
sendJson(res, 200, out);
|
|
249
|
+
}
|
|
250
|
+
catch (e) {
|
|
251
|
+
sendJson(res, 500, { error: String(e) });
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
// ── POST /api/wiki/merge ─────────────────────────────────────────
|
|
257
|
+
if (req.method === "POST" && url === "/api/wiki/merge") {
|
|
258
|
+
readJsonBody(req, (result) => {
|
|
259
|
+
if (!result.ok)
|
|
260
|
+
return sendJson(res, 400, { error: result.error });
|
|
261
|
+
const sourceTopicId = typeof result.value.sourceTopicId === "string"
|
|
262
|
+
? result.value.sourceTopicId
|
|
263
|
+
: "";
|
|
264
|
+
const targetTopicId = typeof result.value.targetTopicId === "string"
|
|
265
|
+
? result.value.targetTopicId
|
|
266
|
+
: "";
|
|
267
|
+
if (!sourceTopicId || !targetTopicId) {
|
|
268
|
+
return sendJson(res, 400, { error: "missing_source_or_target" });
|
|
269
|
+
}
|
|
270
|
+
try {
|
|
271
|
+
const curation = createWikiCuration(ctx.stateDir);
|
|
272
|
+
const out = curation.mergeTopics(sourceTopicId, targetTopicId);
|
|
273
|
+
if (!out.ok) {
|
|
274
|
+
return sendJson(res, 400, {
|
|
275
|
+
error: sourceTopicId === targetTopicId
|
|
276
|
+
? "cannot_merge_into_itself"
|
|
277
|
+
: "topic_not_found",
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
emitWikiEvent(ctx, {
|
|
281
|
+
type: "wiki_topics_merged",
|
|
282
|
+
sourceTopicId,
|
|
283
|
+
targetTopicId,
|
|
284
|
+
});
|
|
285
|
+
sendJson(res, 200, out);
|
|
286
|
+
}
|
|
287
|
+
catch (e) {
|
|
288
|
+
sendJson(res, 500, { error: String(e) });
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
// ── POST /api/wiki/topic/:topicId/split ──────────────────────────
|
|
294
|
+
const splitMatch = req.method === "POST"
|
|
295
|
+
? url.match(/^\/api\/wiki\/topic\/([^/?]+)\/split$/)
|
|
296
|
+
: null;
|
|
297
|
+
if (splitMatch) {
|
|
298
|
+
readJsonBody(req, (result) => {
|
|
299
|
+
if (!result.ok)
|
|
300
|
+
return sendJson(res, 400, { error: result.error });
|
|
301
|
+
const topicId = decodeSegment(splitMatch[1]);
|
|
302
|
+
if (topicId === null)
|
|
303
|
+
return sendJson(res, 400, { error: "malformed_topic_id" });
|
|
304
|
+
const raw = result.value.memoryIds;
|
|
305
|
+
const memoryIds = Array.isArray(raw)
|
|
306
|
+
? raw.filter((m) => typeof m === "string")
|
|
307
|
+
: [];
|
|
308
|
+
if (memoryIds.length === 0) {
|
|
309
|
+
return sendJson(res, 400, { error: "empty_memoryIds" });
|
|
310
|
+
}
|
|
311
|
+
try {
|
|
312
|
+
const curation = createWikiCuration(ctx.stateDir);
|
|
313
|
+
const out = curation.splitTopic(topicId, memoryIds);
|
|
314
|
+
if (!out.ok)
|
|
315
|
+
return sendJson(res, 404, { error: "topic_not_found" });
|
|
316
|
+
emitWikiEvent(ctx, {
|
|
317
|
+
type: "wiki_topic_split",
|
|
318
|
+
fromTopicId: topicId,
|
|
319
|
+
toTopicId: out.topicId,
|
|
320
|
+
});
|
|
321
|
+
sendJson(res, 200, out);
|
|
322
|
+
}
|
|
323
|
+
catch (e) {
|
|
324
|
+
sendJson(res, 500, { error: String(e) });
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
@@ -20,3 +20,5 @@ export { handleCacheStripes } from "./routes-cache.js";
|
|
|
20
20
|
export { handleContextHealth, handleCachePoison, handleHealthSettings } from "./routes-health.js";
|
|
21
21
|
export { handleEmbedderHealth } from "./routes-embedder-health.js";
|
|
22
22
|
export { handleRagSettings } from "./routes-rag-settings.js";
|
|
23
|
+
export { handleRagMetrics } from "./routes-rag-metrics.js";
|
|
24
|
+
export { handleWiki } from "./routes-wiki.js";
|
|
@@ -11,8 +11,9 @@ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync, } from
|
|
|
11
11
|
import { join, dirname } from "node:path";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import { createRequire } from "node:module";
|
|
14
|
+
import { NEW_UI } from "../../src/config.js";
|
|
14
15
|
import { log, setLogPath, setDashboardServerVersion } from "./state.js";
|
|
15
|
-
import { buildRouteContext, handleIndex, handleRepoIndex, handleEvents, handleGameState, handleGameScores, handlePerf, handleAchievements, handleSessions, handleTopics, handleTurns, handleMaintenance, handleProviderCache, handleCacheStripes, handleMemoryStatus, handleSetupStatus, handleSetupDetect, handleSetupConfigure, handleMemoryMap, handleRaptorTree, handleContextHealth, handleCachePoison, handleHealthSettings, handleEmbedderHealth, handleRagSettings, handleStatic, } from "./routes.js";
|
|
16
|
+
import { buildRouteContext, handleIndex, handleRepoIndex, handleEvents, handleGameState, handleGameScores, handlePerf, handleAchievements, handleSessions, handleTopics, handleTurns, handleMaintenance, handleProviderCache, handleCacheStripes, handleMemoryStatus, handleSetupStatus, handleSetupDetect, handleSetupConfigure, handleMemoryMap, handleRaptorTree, handleContextHealth, handleCachePoison, handleHealthSettings, handleEmbedderHealth, handleRagSettings, handleRagMetrics, handleWiki, handleStatic, } from "./routes.js";
|
|
16
17
|
export async function launchDashboardServer(stateDir) {
|
|
17
18
|
// Our own package version — exposed at /api/version so the launcher can
|
|
18
19
|
// detect a stale server (started by an older build) and replace it on
|
|
@@ -71,6 +72,14 @@ export async function launchDashboardServer(stateDir) {
|
|
|
71
72
|
const hasClientBuild = existsSync(clientIndexHtml);
|
|
72
73
|
if (hasClientBuild)
|
|
73
74
|
log("client build present", { clientDist });
|
|
75
|
+
function injectUiFlag(html) {
|
|
76
|
+
const flag = NEW_UI() ? "true" : "false";
|
|
77
|
+
const script = `<script>window.MEGACOMPACT_NEW_UI=${flag}</script>`;
|
|
78
|
+
if (html.includes("</head>")) {
|
|
79
|
+
return html.replace("</head>", `${script}</head>`);
|
|
80
|
+
}
|
|
81
|
+
return html + script;
|
|
82
|
+
}
|
|
74
83
|
// guardrails-allow PREVENT-PI-004: read-only static file serving from the local dashboard-client/dist bundle (loopback-only UI).
|
|
75
84
|
const serveClientAsset = (reqPath, res) => {
|
|
76
85
|
if (!hasClientBuild)
|
|
@@ -84,7 +93,7 @@ export async function launchDashboardServer(stateDir) {
|
|
|
84
93
|
if (!file.startsWith(clientDist) || !existsSync(file)) {
|
|
85
94
|
// SPA fallback: unknown non-asset routes serve index.html (client-side routing).
|
|
86
95
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
87
|
-
res.end(readFileSync(clientIndexHtml));
|
|
96
|
+
res.end(injectUiFlag(readFileSync(clientIndexHtml, "utf-8")));
|
|
88
97
|
return true;
|
|
89
98
|
}
|
|
90
99
|
const ext = rel.slice(rel.lastIndexOf(".") + 1);
|
|
@@ -98,10 +107,11 @@ export async function launchDashboardServer(stateDir) {
|
|
|
98
107
|
ico: "image/x-icon",
|
|
99
108
|
map: "application/json",
|
|
100
109
|
};
|
|
110
|
+
const payload = ext === "html" ? injectUiFlag(readFileSync(file, "utf-8")) : readFileSync(file);
|
|
101
111
|
res.writeHead(200, {
|
|
102
112
|
"Content-Type": types[ext] ?? "application/octet-stream",
|
|
103
113
|
});
|
|
104
|
-
res.end(
|
|
114
|
+
res.end(payload);
|
|
105
115
|
return true;
|
|
106
116
|
};
|
|
107
117
|
log("launch invoked", { stateDir });
|
|
@@ -219,6 +229,10 @@ export async function launchDashboardServer(stateDir) {
|
|
|
219
229
|
return;
|
|
220
230
|
if (handleRagSettings(req, res, ctx))
|
|
221
231
|
return;
|
|
232
|
+
if (handleRagMetrics(req, res, ctx))
|
|
233
|
+
return;
|
|
234
|
+
if (handleWiki(req, res, ctx))
|
|
235
|
+
return;
|
|
222
236
|
handleStatic(req, res, ctx);
|
|
223
237
|
});
|
|
224
238
|
// Bind base + range are env-configurable so tests can use a private,
|
|
@@ -12,7 +12,8 @@ import { epochIdFor } from "../../../src/mirror/epoch.js";
|
|
|
12
12
|
import { stampTurnsEpochFor } from "../../mega-turn-store.js";
|
|
13
13
|
import { TurnsConfig } from "../../../src/config/turns.js";
|
|
14
14
|
import { openTurnStore } from "../../../src/store/turns/connection.js";
|
|
15
|
-
import { buildTopicModel, createTopicStore, bumpWikiCompactCounter, } from "../../../src/topics/index.js";
|
|
15
|
+
import { buildTopicModel, createTopicStore, bumpWikiCompactCounter, applyOverridesAfterRebuild, applyFullOverridesAfterRebuild, } from "../../../src/topics/index.js";
|
|
16
|
+
import { assignNewMemoriesIncremental } from "../../../src/wiki/index.js";
|
|
16
17
|
import { TrigramEmbedder } from "../../../src/embedder.js";
|
|
17
18
|
/**
|
|
18
19
|
* Persist the checkpoint_epoch, stamp turns, rebuild the auto-wiki, seed the
|
|
@@ -58,7 +59,30 @@ export async function persistEpochAndMaintain(runtime, config, ran) {
|
|
|
58
59
|
const every = Math.max(1, TurnsConfig.WIKI_REBUILD_EVERY_N_COMPACTS);
|
|
59
60
|
const tdb = openTurnStore(runtime.currentStateDir);
|
|
60
61
|
const n = bumpWikiCompactCounter(tdb);
|
|
61
|
-
|
|
62
|
+
// W5-B: on the Nth compact we full-rebuild; otherwise try the
|
|
63
|
+
// cheap incremental path (assign new memories to nearest
|
|
64
|
+
// centroid) and only force a full rebuild when the mean
|
|
65
|
+
// silhouette has degraded below the configured floor.
|
|
66
|
+
let rebuildNow = n % every === 0;
|
|
67
|
+
if (!rebuildNow &&
|
|
68
|
+
TurnsConfig.WIKI_INCREMENTAL_ENABLED &&
|
|
69
|
+
TurnsConfig.WIKI_ENHANCED_ENABLED) {
|
|
70
|
+
const inc = assignNewMemoriesIncremental(db, runtime.currentStateDir);
|
|
71
|
+
if (inc.silhouette !== null &&
|
|
72
|
+
inc.silhouette < TurnsConfig.WIKI_INCREMENTAL_SILHOUETTE_MIN) {
|
|
73
|
+
// Silhouette degraded — fall through to a full rebuild.
|
|
74
|
+
rebuildNow = true;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
runtime.logger.info("wiki_incremental", {
|
|
78
|
+
assigned: inc.assigned,
|
|
79
|
+
pending: inc.pending,
|
|
80
|
+
silhouetteScore: inc.silhouette,
|
|
81
|
+
uncalibrated: false,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (rebuildNow) {
|
|
62
86
|
const model = buildTopicModel(db, {
|
|
63
87
|
kRange: [TurnsConfig.WIKI_K_MIN, TurnsConfig.WIKI_K_MAX],
|
|
64
88
|
labelTopTerms: TurnsConfig.WIKI_LABEL_TOP_TERMS,
|
|
@@ -66,6 +90,13 @@ export async function persistEpochAndMaintain(runtime, config, ran) {
|
|
|
66
90
|
seed: 0x9e3779b9,
|
|
67
91
|
});
|
|
68
92
|
createTopicStore(runtime.currentStateDir).replaceTopicModel(model);
|
|
93
|
+
// W5-A: full hybrid replay — label + merge + split overrides
|
|
94
|
+
// survive the rebuild (feature A, not just the label fast path).
|
|
95
|
+
applyFullOverridesAfterRebuild(tdb);
|
|
96
|
+
// SSE push so the dashboard Wiki/Evolution views refresh (non-fatal).
|
|
97
|
+
runtime.dashboard.event("wiki_rebuilt", {
|
|
98
|
+
topicCount: model.k,
|
|
99
|
+
});
|
|
69
100
|
runtime.logger.info("wiki_rebuild", {
|
|
70
101
|
clusterCount: model.k,
|
|
71
102
|
totalChunks: model.totalChunks,
|
|
@@ -130,6 +161,8 @@ export async function persistEpochAndMaintain(runtime, config, ran) {
|
|
|
130
161
|
seed: 0x9e3779b9,
|
|
131
162
|
}, seedChunks);
|
|
132
163
|
createTopicStore(runtime.currentStateDir).replaceTopicModel(model);
|
|
164
|
+
// Re-stamp custom label overrides wiped by the seed rebuild.
|
|
165
|
+
applyOverridesAfterRebuild(openTurnStore(runtime.currentStateDir));
|
|
133
166
|
runtime.logger.info("wiki_seed", {
|
|
134
167
|
clusterCount: model.k,
|
|
135
168
|
sourceChunks: seedChunks.length,
|
|
@@ -40,6 +40,32 @@ export function doRecall(runtime, config, ctx, query, source) {
|
|
|
40
40
|
injected: result.toInject.length,
|
|
41
41
|
empty: result.empty,
|
|
42
42
|
});
|
|
43
|
+
if (config.ragRecallMetrics && result.hydeInfo) {
|
|
44
|
+
runtime.dashboard.event("hyde_executed", {
|
|
45
|
+
sessionId: sid,
|
|
46
|
+
ran: result.hydeInfo.ran,
|
|
47
|
+
skipped: result.hydeInfo.skipped,
|
|
48
|
+
reason: result.hydeInfo.reason,
|
|
49
|
+
hypotheticalDoc: result.hydeInfo.hypotheticalDoc.slice(0, 400),
|
|
50
|
+
generationMs: result.hydeInfo.generationMs,
|
|
51
|
+
rawHitCount: result.hydeInfo.rawHitCount,
|
|
52
|
+
hydeHitCount: result.hydeInfo.hydeHitCount,
|
|
53
|
+
fusedHitCount: result.hydeInfo.fusedHitCount,
|
|
54
|
+
lift: result.hydeInfo.lift,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (config.ragRecallMetrics && result.recallMetrics) {
|
|
58
|
+
runtime.dashboard.event("recall_metrics", {
|
|
59
|
+
sessionId: sid,
|
|
60
|
+
hitCount: result.recallMetrics.hitCount,
|
|
61
|
+
score: result.recallMetrics.score,
|
|
62
|
+
pass: result.recallMetrics.pass,
|
|
63
|
+
relevance: result.recallMetrics.relevance,
|
|
64
|
+
coverage: result.recallMetrics.coverage,
|
|
65
|
+
diversity: result.recallMetrics.diversity,
|
|
66
|
+
specificity: result.recallMetrics.specificity,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
43
69
|
if (!result.empty && result.toInject.length > 0) {
|
|
44
70
|
const top = result.toInject[0];
|
|
45
71
|
const scorePct = Math.round((top.score ?? 0) * 100);
|
|
@@ -72,6 +98,8 @@ export function doRecall(runtime, config, ctx, query, source) {
|
|
|
72
98
|
turnIndex: runtime.currentTurn,
|
|
73
99
|
role: "assistant",
|
|
74
100
|
startedAt: Date.now(),
|
|
101
|
+
hyde: result.hydeInfo ?? undefined,
|
|
102
|
+
recallMetrics: result.recallMetrics ?? undefined,
|
|
75
103
|
}, runtime.currentStateDir);
|
|
76
104
|
recordRecallWrite(config, turnId, result.toInject.map((h) => ({
|
|
77
105
|
checkpointId: h.checkpoint.checkpointId,
|
|
@@ -173,6 +201,9 @@ export async function doRecallAsync(runtime, config, ctx, query, source, opts =
|
|
|
173
201
|
report: merged.map((h) => ` • ${h.checkpoint.checkpointId}${h.repoId ? ` (from ${h.repoId.split("/").filter(Boolean).pop()})` : ""}`),
|
|
174
202
|
block,
|
|
175
203
|
empty: merged.length === 0,
|
|
204
|
+
// H1: merged cross-repo result reuses the same-repo pass's telemetry.
|
|
205
|
+
hydeInfo: sameRepo.hydeInfo,
|
|
206
|
+
recallMetrics: sameRepo.recallMetrics,
|
|
176
207
|
};
|
|
177
208
|
}
|
|
178
209
|
catch {
|
|
@@ -43,7 +43,6 @@ export function ensureConversationIdFor(config, sessionId, stateDir) {
|
|
|
43
43
|
? storeFor(stateDir).ensureConversationId(sessionId)
|
|
44
44
|
: legacyEnsureConversationId(sessionId, stateDir);
|
|
45
45
|
}
|
|
46
|
-
/** Record one turn row (turn_end). Returns the contract TurnId (string). */
|
|
47
46
|
export function recordTurnWrite(config, input, stateDir) {
|
|
48
47
|
if (!config.turnsDbEnabled) {
|
|
49
48
|
legacyRecordTurn(input, stateDir);
|
|
@@ -60,6 +59,8 @@ export function recordTurnWrite(config, input, stateDir) {
|
|
|
60
59
|
pressureBand: input.pressureBand,
|
|
61
60
|
model: input.modelId,
|
|
62
61
|
epochId: input.epochId,
|
|
62
|
+
hyde: input.hyde,
|
|
63
|
+
recallMetrics: input.recallMetrics,
|
|
63
64
|
};
|
|
64
65
|
return storeFor(stateDir).appendTurn(entry);
|
|
65
66
|
}
|
package/dist/src/config/turns.js
CHANGED
|
@@ -22,6 +22,13 @@ function envNum(name, def) {
|
|
|
22
22
|
const n = Number(v);
|
|
23
23
|
return Number.isFinite(n) ? n : def;
|
|
24
24
|
}
|
|
25
|
+
/** Default-ON toggle honoring the MEGACOMPACT_<NAME>_DISABLED opt-out convention. */
|
|
26
|
+
function envEnabled(name, def) {
|
|
27
|
+
if (process.env[`${name}_DISABLED`] === "true" || process.env[`${name}_DISABLED`] === "1") {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
return envBool(name, def);
|
|
31
|
+
}
|
|
25
32
|
function envKRange(defMin, defMax) {
|
|
26
33
|
const v = process.env.MEGACOMPACT_WIKI_K_RANGE;
|
|
27
34
|
if (v) {
|
|
@@ -44,6 +51,9 @@ export function loadTurnsConfig() {
|
|
|
44
51
|
WIKI_LABEL_TOP_TERMS: envNum("MEGACOMPACT_WIKI_LABEL_TOP_TERMS", 5),
|
|
45
52
|
WIKI_REBUILD_EVERY_N_COMPACTS: envNum("MEGACOMPACT_WIKI_REBUILD_EVERY", 3),
|
|
46
53
|
WIKI_SEED_FROM_TURNS: envBool("MEGACOMPACT_WIKI_SEED_FROM_TURNS", true),
|
|
54
|
+
WIKI_ENHANCED_ENABLED: envEnabled("MEGACOMPACT_WIKI_ENHANCED", true),
|
|
55
|
+
WIKI_INCREMENTAL_ENABLED: envEnabled("MEGACOMPACT_WIKI_INCREMENTAL", true),
|
|
56
|
+
WIKI_INCREMENTAL_SILHOUETTE_MIN: envNum("MEGACOMPACT_WIKI_SILHOUETTE_MIN", 0.2),
|
|
47
57
|
};
|
|
48
58
|
}
|
|
49
59
|
export const TurnsConfig = loadTurnsConfig();
|
package/dist/src/config.js
CHANGED
|
@@ -107,3 +107,5 @@ export const RAG_MEMORY_GRAPH = () => ragEnabled("MEGACOMPACT_MEMORY_GRAPH");
|
|
|
107
107
|
* HttpEmbedder is active (the LLM is configured for indexing); opt OUT with
|
|
108
108
|
* MEGACOMPACT_HYDE_DISABLED=true. TrigramEmbedder path is unaffected. */
|
|
109
109
|
export const RAG_HYDE_ENABLED = () => ragEnabled("MEGACOMPACT_HYDE");
|
|
110
|
+
/** Spec 1: vbrainstorm visual design migration for the dashboard. */
|
|
111
|
+
export const NEW_UI = () => ragEnabled("MEGACOMPACT_NEW_UI");
|
package/dist/src/recall/async.js
CHANGED
|
@@ -108,5 +108,14 @@ export async function recallAndInlineAsync(opts, store) {
|
|
|
108
108
|
// has repoId + score for proper labeling.
|
|
109
109
|
const block = toInject.length > 0 ? formatRecallBlock(toInject) : "";
|
|
110
110
|
const report = toInject.map((h) => ` • ${h.checkpoint.checkpointId} (${h.checkpoint.summary.slice(0, 60).replace(/\n/g, " ")}…)`);
|
|
111
|
-
return {
|
|
111
|
+
return {
|
|
112
|
+
toInject,
|
|
113
|
+
report,
|
|
114
|
+
block,
|
|
115
|
+
empty: toInject.length === 0,
|
|
116
|
+
// H1: cross-repo async path is HyDE-free; telemetry stays null until a
|
|
117
|
+
// future sprint threads it here.
|
|
118
|
+
hydeInfo: null,
|
|
119
|
+
recallMetrics: null,
|
|
120
|
+
};
|
|
112
121
|
}
|
|
@@ -142,8 +142,17 @@ export function scoreAndLogRecallMetrics(query, toInject) {
|
|
|
142
142
|
diversity: metrics.breakdown.diversity,
|
|
143
143
|
});
|
|
144
144
|
}
|
|
145
|
+
return {
|
|
146
|
+
hitCount: toInject.length,
|
|
147
|
+
score: metrics.score,
|
|
148
|
+
pass: metrics.pass,
|
|
149
|
+
relevance: metrics.breakdown.relevance,
|
|
150
|
+
coverage: metrics.breakdown.coverage,
|
|
151
|
+
diversity: metrics.breakdown.diversity,
|
|
152
|
+
specificity: metrics.breakdown.specificity,
|
|
153
|
+
};
|
|
145
154
|
}
|
|
146
155
|
catch {
|
|
147
|
-
|
|
156
|
+
return null; // non-fatal: metrics never break recall
|
|
148
157
|
}
|
|
149
158
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function buildHydeInfo(outcome, hypotheticalDoc, generationMs, rawHitCount, hydeHitCount, fusedHitCount) {
|
|
2
|
+
const ran = outcome === "ran";
|
|
3
|
+
const skipped = !ran;
|
|
4
|
+
// Lift = fusedHitCount / max(1, rawHitCount) — how much HyDE changed recall
|
|
5
|
+
// breadth. Skipped passes yield 1 (raw hits cascade unchanged) unless there
|
|
6
|
+
// were no raw hits to compare against.
|
|
7
|
+
const lift = fusedHitCount / Math.max(1, rawHitCount);
|
|
8
|
+
return {
|
|
9
|
+
ran,
|
|
10
|
+
skipped,
|
|
11
|
+
reason: outcome,
|
|
12
|
+
hypotheticalDoc,
|
|
13
|
+
generationMs: ran ? generationMs : 0,
|
|
14
|
+
rawHitCount,
|
|
15
|
+
hydeHitCount: ran ? hydeHitCount : 0,
|
|
16
|
+
fusedHitCount,
|
|
17
|
+
lift: Math.round(lift * 100) / 100,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function hydeSkipped(reason, rawHitCount, fusedHitCount) {
|
|
21
|
+
return buildHydeInfo(reason, "", 0, rawHitCount, 0, fusedHitCount);
|
|
22
|
+
}
|