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
|
@@ -0,0 +1,376 @@
|
|
|
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
|
+
|
|
17
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
18
|
+
import type { RouteContext } from "./routes-core.js";
|
|
19
|
+
import { TurnsConfig } from "../../src/config/turns.js";
|
|
20
|
+
import { openTurnStore } from "../../src/store/turns/connection.js";
|
|
21
|
+
import { createTopicStore } from "../../src/topics/store.js";
|
|
22
|
+
import { extractiveSummary } from "../../src/wiki.js";
|
|
23
|
+
import { createWikiCuration } from "../../src/wiki/curation.js";
|
|
24
|
+
import {
|
|
25
|
+
emitWikiEvent,
|
|
26
|
+
ensureWikiBuilt,
|
|
27
|
+
toIndexEntry,
|
|
28
|
+
toProvenance,
|
|
29
|
+
bucketize,
|
|
30
|
+
memberContent,
|
|
31
|
+
} from "./routes-wiki-helpers.js";
|
|
32
|
+
import type {
|
|
33
|
+
WikiIndexResponse,
|
|
34
|
+
WikiPageResponse,
|
|
35
|
+
TopicEvolutionResponse,
|
|
36
|
+
TopicTimelineResponse,
|
|
37
|
+
CurationResult,
|
|
38
|
+
} from "./api-contracts/wiki.js";
|
|
39
|
+
|
|
40
|
+
function sendJson(res: ServerResponse, status: number, body: unknown): void {
|
|
41
|
+
// guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
|
|
42
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
43
|
+
res.end(JSON.stringify(body));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Decode a percent-encoded path segment; null on malformed encoding (%zz). */
|
|
47
|
+
function decodeSegment(raw: string): string | null {
|
|
48
|
+
try {
|
|
49
|
+
return decodeURIComponent(raw);
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Read a capped JSON body; returns { ok, value } or { error }. */
|
|
56
|
+
function readJsonBody(
|
|
57
|
+
req: IncomingMessage,
|
|
58
|
+
cb: (
|
|
59
|
+
result:
|
|
60
|
+
| { ok: true; value: Record<string, unknown> }
|
|
61
|
+
| { ok: false; error: string },
|
|
62
|
+
) => void,
|
|
63
|
+
): void {
|
|
64
|
+
let body = "";
|
|
65
|
+
let tooBig = false;
|
|
66
|
+
req.on("data", (chunk: Buffer) => {
|
|
67
|
+
if (body.length > 65536) {
|
|
68
|
+
tooBig = true;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
body += chunk.toString();
|
|
72
|
+
});
|
|
73
|
+
req.on("end", () => {
|
|
74
|
+
if (tooBig) return cb({ ok: false, error: "body_too_large" });
|
|
75
|
+
try {
|
|
76
|
+
const v = body ? JSON.parse(body) : {};
|
|
77
|
+
if (typeof v !== "object" || v === null || Array.isArray(v)) {
|
|
78
|
+
return cb({ ok: false, error: "invalid_object" });
|
|
79
|
+
}
|
|
80
|
+
cb({ ok: true, value: v as Record<string, unknown> });
|
|
81
|
+
} catch {
|
|
82
|
+
cb({ ok: false, error: "invalid_json" });
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function handleWiki(
|
|
88
|
+
req: IncomingMessage,
|
|
89
|
+
res: ServerResponse,
|
|
90
|
+
ctx: RouteContext,
|
|
91
|
+
): boolean {
|
|
92
|
+
const url = req.url ?? "";
|
|
93
|
+
if (!url.startsWith("/api/wiki")) return false;
|
|
94
|
+
|
|
95
|
+
// Flag gate: feature-OFF → 404 for every wiki endpoint.
|
|
96
|
+
if (!TurnsConfig.WIKI_ENHANCED_ENABLED) {
|
|
97
|
+
sendJson(res, 404, { error: "wiki_enhanced_disabled" });
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ── GET /api/wiki/index ──────────────────────────────────────────
|
|
102
|
+
if (req.method === "GET" && url === "/api/wiki/index") {
|
|
103
|
+
try {
|
|
104
|
+
const tdb = openTurnStore(ctx.stateDir);
|
|
105
|
+
ensureWikiBuilt(ctx, tdb);
|
|
106
|
+
const store = createTopicStore(ctx.stateDir);
|
|
107
|
+
const curation = createWikiCuration(ctx.stateDir);
|
|
108
|
+
const entries = store.getTopics().map((t) => toIndexEntry(t, curation));
|
|
109
|
+
const totalMemories = entries.reduce((s, e) => s + e.memoryCount, 0);
|
|
110
|
+
const lastRebuildAt =
|
|
111
|
+
entries.length > 0 ? Math.max(...entries.map((e) => e.lastUpdated)) : null;
|
|
112
|
+
const body: WikiIndexResponse = {
|
|
113
|
+
topics: entries,
|
|
114
|
+
totalTopics: entries.length,
|
|
115
|
+
totalMemories,
|
|
116
|
+
lastRebuildAt,
|
|
117
|
+
};
|
|
118
|
+
sendJson(res, 200, body);
|
|
119
|
+
} catch (e) {
|
|
120
|
+
sendJson(res, 500, { error: String(e) });
|
|
121
|
+
}
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ── GET /api/wiki/evolution ──────────────────────────────────────
|
|
126
|
+
if (req.method === "GET" && url === "/api/wiki/evolution") {
|
|
127
|
+
try {
|
|
128
|
+
const tdb = openTurnStore(ctx.stateDir);
|
|
129
|
+
const curation = createWikiCuration(ctx.stateDir);
|
|
130
|
+
const nodes = createTopicStore(ctx.stateDir)
|
|
131
|
+
.getTopics()
|
|
132
|
+
.map((t) => ({
|
|
133
|
+
id: t.id,
|
|
134
|
+
label: curation.resolveLabel(t.id, t.label).label,
|
|
135
|
+
memoryCount: t.memoryCount,
|
|
136
|
+
}));
|
|
137
|
+
const overrides = tdb
|
|
138
|
+
.prepare(
|
|
139
|
+
`SELECT kind, topic_id, merged_into, split_from, overridden_at
|
|
140
|
+
FROM topic_overrides WHERE kind IN ('merge','split')`,
|
|
141
|
+
)
|
|
142
|
+
.all() as Array<{
|
|
143
|
+
kind: string;
|
|
144
|
+
topic_id: string;
|
|
145
|
+
merged_into: string | null;
|
|
146
|
+
split_from: string | null;
|
|
147
|
+
overridden_at: number;
|
|
148
|
+
}>;
|
|
149
|
+
const edges = overrides.flatMap(
|
|
150
|
+
(o): TopicEvolutionResponse["edges"][number][] => {
|
|
151
|
+
if (o.kind === "merge" && o.merged_into) {
|
|
152
|
+
return [
|
|
153
|
+
{
|
|
154
|
+
source: o.topic_id,
|
|
155
|
+
target: o.merged_into,
|
|
156
|
+
kind: "merge",
|
|
157
|
+
at: o.overridden_at,
|
|
158
|
+
},
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
if (o.kind === "split" && o.split_from) {
|
|
162
|
+
return [
|
|
163
|
+
{
|
|
164
|
+
source: o.split_from,
|
|
165
|
+
target: o.topic_id,
|
|
166
|
+
kind: "split",
|
|
167
|
+
at: o.overridden_at,
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
}
|
|
171
|
+
return [];
|
|
172
|
+
},
|
|
173
|
+
);
|
|
174
|
+
const evoRows = tdb
|
|
175
|
+
.prepare("SELECT assigned_at FROM topic_evolution")
|
|
176
|
+
.all() as Array<{ assigned_at: number }>;
|
|
177
|
+
const body: TopicEvolutionResponse = {
|
|
178
|
+
nodes,
|
|
179
|
+
edges,
|
|
180
|
+
buckets: bucketize(evoRows.map((r) => r.assigned_at)),
|
|
181
|
+
};
|
|
182
|
+
sendJson(res, 200, body);
|
|
183
|
+
} catch (e) {
|
|
184
|
+
sendJson(res, 500, { error: String(e) });
|
|
185
|
+
}
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ── GET /api/wiki/topic/:topicId/timeline ────────────────────────
|
|
190
|
+
const timelineMatch =
|
|
191
|
+
req.method === "GET"
|
|
192
|
+
? url.match(/^\/api\/wiki\/topic\/([^/?]+)\/timeline$/)
|
|
193
|
+
: null;
|
|
194
|
+
if (timelineMatch) {
|
|
195
|
+
const topicId = decodeSegment(timelineMatch[1]);
|
|
196
|
+
if (topicId === null) {
|
|
197
|
+
sendJson(res, 400, { error: "malformed_topic_id" });
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
const tdb = openTurnStore(ctx.stateDir);
|
|
202
|
+
const rows = tdb
|
|
203
|
+
.prepare("SELECT assigned_at FROM memory_topics WHERE topic_id = ?")
|
|
204
|
+
.all(topicId) as Array<{ assigned_at: number }>;
|
|
205
|
+
const body: TopicTimelineResponse = {
|
|
206
|
+
topicId,
|
|
207
|
+
buckets: bucketize(rows.map((r) => r.assigned_at)),
|
|
208
|
+
total: rows.length,
|
|
209
|
+
};
|
|
210
|
+
sendJson(res, 200, body);
|
|
211
|
+
} catch (e) {
|
|
212
|
+
sendJson(res, 500, { error: String(e) });
|
|
213
|
+
}
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ── GET /api/wiki/topic/:topicId ─────────────────────────────────
|
|
218
|
+
const topicMatch =
|
|
219
|
+
req.method === "GET" ? url.match(/^\/api\/wiki\/topic\/([^/?]+)$/) : null;
|
|
220
|
+
if (topicMatch) {
|
|
221
|
+
const topicId = decodeSegment(topicMatch[1]);
|
|
222
|
+
if (topicId === null) {
|
|
223
|
+
sendJson(res, 400, { error: "malformed_topic_id" });
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
try {
|
|
227
|
+
const store = createTopicStore(ctx.stateDir);
|
|
228
|
+
const topic = store.getTopics().find((t) => t.id === topicId);
|
|
229
|
+
if (!topic) {
|
|
230
|
+
sendJson(res, 404, { error: "topic_not_found" });
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
const members = store.getMemoriesForTopic(topicId, 200);
|
|
234
|
+
const content = memberContent(ctx, members);
|
|
235
|
+
const getText = (memId: string): string => content.get(memId)?.text ?? "";
|
|
236
|
+
const summary = extractiveSummary(topic, members, getText);
|
|
237
|
+
const present = members.filter((m) => content.has(m.memoryId));
|
|
238
|
+
const keyMemories = [...present]
|
|
239
|
+
.sort(
|
|
240
|
+
(a, b) =>
|
|
241
|
+
(content.get(b.memoryId)?.text.length ?? 0) -
|
|
242
|
+
(content.get(a.memoryId)?.text.length ?? 0),
|
|
243
|
+
)
|
|
244
|
+
.slice(0, 10)
|
|
245
|
+
.map((m) => ({
|
|
246
|
+
memoryId: m.memoryId,
|
|
247
|
+
content: content.get(m.memoryId)?.text ?? "",
|
|
248
|
+
timestamp: content.get(m.memoryId)?.timestamp ?? 0,
|
|
249
|
+
importance: Math.round(
|
|
250
|
+
(content.get(m.memoryId)?.text.length ?? 0) / 40,
|
|
251
|
+
),
|
|
252
|
+
}));
|
|
253
|
+
const relatedTopicIds = store
|
|
254
|
+
.getTopics()
|
|
255
|
+
.filter((t) => t.id !== topicId && t.memoryCount > 0)
|
|
256
|
+
.slice(0, 3)
|
|
257
|
+
.map((t) => t.id);
|
|
258
|
+
const body: WikiPageResponse = {
|
|
259
|
+
topic: toIndexEntry(topic, createWikiCuration(ctx.stateDir)),
|
|
260
|
+
summary,
|
|
261
|
+
keyMemories,
|
|
262
|
+
provenance: members.map(toProvenance),
|
|
263
|
+
relatedTopicIds,
|
|
264
|
+
generatedAt: Date.now(),
|
|
265
|
+
};
|
|
266
|
+
sendJson(res, 200, body);
|
|
267
|
+
} catch (e) {
|
|
268
|
+
sendJson(res, 500, { error: String(e) });
|
|
269
|
+
}
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ── PUT /api/wiki/topic/:topicId/label ───────────────────────────
|
|
274
|
+
const renameMatch =
|
|
275
|
+
req.method === "PUT"
|
|
276
|
+
? url.match(/^\/api\/wiki\/topic\/([^/?]+)\/label$/)
|
|
277
|
+
: null;
|
|
278
|
+
if (renameMatch) {
|
|
279
|
+
readJsonBody(req, (result) => {
|
|
280
|
+
if (!result.ok) return sendJson(res, 400, { error: result.error });
|
|
281
|
+
const topicId = decodeSegment(renameMatch[1]);
|
|
282
|
+
if (topicId === null) return sendJson(res, 400, { error: "malformed_topic_id" });
|
|
283
|
+
const label = typeof result.value.label === "string" ? result.value.label : "";
|
|
284
|
+
try {
|
|
285
|
+
const curation = createWikiCuration(ctx.stateDir);
|
|
286
|
+
const out = curation.renameTopic(topicId, label);
|
|
287
|
+
if (!out.ok) return sendJson(res, 404, { error: "topic_not_found" });
|
|
288
|
+
emitWikiEvent(ctx, {
|
|
289
|
+
type: "wiki_topic_renamed",
|
|
290
|
+
topicId,
|
|
291
|
+
label: out.label,
|
|
292
|
+
edited: out.edited,
|
|
293
|
+
});
|
|
294
|
+
sendJson(res, 200, out satisfies CurationResult);
|
|
295
|
+
} catch (e) {
|
|
296
|
+
sendJson(res, 500, { error: String(e) });
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// ── POST /api/wiki/merge ─────────────────────────────────────────
|
|
303
|
+
if (req.method === "POST" && url === "/api/wiki/merge") {
|
|
304
|
+
readJsonBody(req, (result) => {
|
|
305
|
+
if (!result.ok) return sendJson(res, 400, { error: result.error });
|
|
306
|
+
const sourceTopicId =
|
|
307
|
+
typeof result.value.sourceTopicId === "string"
|
|
308
|
+
? result.value.sourceTopicId
|
|
309
|
+
: "";
|
|
310
|
+
const targetTopicId =
|
|
311
|
+
typeof result.value.targetTopicId === "string"
|
|
312
|
+
? result.value.targetTopicId
|
|
313
|
+
: "";
|
|
314
|
+
if (!sourceTopicId || !targetTopicId) {
|
|
315
|
+
return sendJson(res, 400, { error: "missing_source_or_target" });
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
const curation = createWikiCuration(ctx.stateDir);
|
|
319
|
+
const out = curation.mergeTopics(sourceTopicId, targetTopicId);
|
|
320
|
+
if (!out.ok) {
|
|
321
|
+
return sendJson(res, 400, {
|
|
322
|
+
error:
|
|
323
|
+
sourceTopicId === targetTopicId
|
|
324
|
+
? "cannot_merge_into_itself"
|
|
325
|
+
: "topic_not_found",
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
emitWikiEvent(ctx, {
|
|
329
|
+
type: "wiki_topics_merged",
|
|
330
|
+
sourceTopicId,
|
|
331
|
+
targetTopicId,
|
|
332
|
+
});
|
|
333
|
+
sendJson(res, 200, out satisfies CurationResult);
|
|
334
|
+
} catch (e) {
|
|
335
|
+
sendJson(res, 500, { error: String(e) });
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// ── POST /api/wiki/topic/:topicId/split ──────────────────────────
|
|
342
|
+
const splitMatch =
|
|
343
|
+
req.method === "POST"
|
|
344
|
+
? url.match(/^\/api\/wiki\/topic\/([^/?]+)\/split$/)
|
|
345
|
+
: null;
|
|
346
|
+
if (splitMatch) {
|
|
347
|
+
readJsonBody(req, (result) => {
|
|
348
|
+
if (!result.ok) return sendJson(res, 400, { error: result.error });
|
|
349
|
+
const topicId = decodeSegment(splitMatch[1]);
|
|
350
|
+
if (topicId === null) return sendJson(res, 400, { error: "malformed_topic_id" });
|
|
351
|
+
const raw = result.value.memoryIds;
|
|
352
|
+
const memoryIds = Array.isArray(raw)
|
|
353
|
+
? raw.filter((m): m is string => typeof m === "string")
|
|
354
|
+
: [];
|
|
355
|
+
if (memoryIds.length === 0) {
|
|
356
|
+
return sendJson(res, 400, { error: "empty_memoryIds" });
|
|
357
|
+
}
|
|
358
|
+
try {
|
|
359
|
+
const curation = createWikiCuration(ctx.stateDir);
|
|
360
|
+
const out = curation.splitTopic(topicId, memoryIds);
|
|
361
|
+
if (!out.ok) return sendJson(res, 404, { error: "topic_not_found" });
|
|
362
|
+
emitWikiEvent(ctx, {
|
|
363
|
+
type: "wiki_topic_split",
|
|
364
|
+
fromTopicId: topicId,
|
|
365
|
+
toTopicId: out.topicId,
|
|
366
|
+
});
|
|
367
|
+
sendJson(res, 200, out satisfies CurationResult);
|
|
368
|
+
} catch (e) {
|
|
369
|
+
sendJson(res, 500, { error: String(e) });
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
@@ -28,3 +28,5 @@ export { handleCacheStripes } from "./routes-cache.js";
|
|
|
28
28
|
export { handleContextHealth, handleCachePoison, handleHealthSettings } from "./routes-health.js";
|
|
29
29
|
export { handleEmbedderHealth } from "./routes-embedder-health.js";
|
|
30
30
|
export { handleRagSettings } from "./routes-rag-settings.js";
|
|
31
|
+
export { handleRagMetrics } from "./routes-rag-metrics.js";
|
|
32
|
+
export { handleWiki } from "./routes-wiki.js";
|
|
@@ -23,6 +23,7 @@ import { join, dirname } from "node:path";
|
|
|
23
23
|
import { fileURLToPath } from "node:url";
|
|
24
24
|
import { createRequire } from "node:module";
|
|
25
25
|
|
|
26
|
+
import { NEW_UI } from "../../src/config.js";
|
|
26
27
|
import { log, setLogPath, setDashboardServerVersion } from "./state.js";
|
|
27
28
|
import {
|
|
28
29
|
buildRouteContext,
|
|
@@ -50,6 +51,8 @@ import {
|
|
|
50
51
|
handleHealthSettings,
|
|
51
52
|
handleEmbedderHealth,
|
|
52
53
|
handleRagSettings,
|
|
54
|
+
handleRagMetrics,
|
|
55
|
+
handleWiki,
|
|
53
56
|
handleStatic,
|
|
54
57
|
} from "./routes.js";
|
|
55
58
|
|
|
@@ -119,6 +122,15 @@ export async function launchDashboardServer(
|
|
|
119
122
|
const hasClientBuild = existsSync(clientIndexHtml);
|
|
120
123
|
if (hasClientBuild) log("client build present", { clientDist });
|
|
121
124
|
|
|
125
|
+
function injectUiFlag(html: string): string {
|
|
126
|
+
const flag = NEW_UI() ? "true" : "false";
|
|
127
|
+
const script = `<script>window.MEGACOMPACT_NEW_UI=${flag}</script>`;
|
|
128
|
+
if (html.includes("</head>")) {
|
|
129
|
+
return html.replace("</head>", `${script}</head>`);
|
|
130
|
+
}
|
|
131
|
+
return html + script;
|
|
132
|
+
}
|
|
133
|
+
|
|
122
134
|
// guardrails-allow PREVENT-PI-004: read-only static file serving from the local dashboard-client/dist bundle (loopback-only UI).
|
|
123
135
|
const serveClientAsset = (reqPath: string, res: ServerResponse): boolean => {
|
|
124
136
|
if (!hasClientBuild) return false;
|
|
@@ -131,7 +143,7 @@ export async function launchDashboardServer(
|
|
|
131
143
|
if (!file.startsWith(clientDist) || !existsSync(file)) {
|
|
132
144
|
// SPA fallback: unknown non-asset routes serve index.html (client-side routing).
|
|
133
145
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
134
|
-
res.end(readFileSync(clientIndexHtml));
|
|
146
|
+
res.end(injectUiFlag(readFileSync(clientIndexHtml, "utf-8")));
|
|
135
147
|
return true;
|
|
136
148
|
}
|
|
137
149
|
const ext = rel.slice(rel.lastIndexOf(".") + 1);
|
|
@@ -145,10 +157,12 @@ export async function launchDashboardServer(
|
|
|
145
157
|
ico: "image/x-icon",
|
|
146
158
|
map: "application/json",
|
|
147
159
|
};
|
|
160
|
+
const payload: string | Uint8Array =
|
|
161
|
+
ext === "html" ? injectUiFlag(readFileSync(file, "utf-8")) : readFileSync(file);
|
|
148
162
|
res.writeHead(200, {
|
|
149
163
|
"Content-Type": types[ext] ?? "application/octet-stream",
|
|
150
164
|
});
|
|
151
|
-
res.end(
|
|
165
|
+
res.end(payload);
|
|
152
166
|
return true;
|
|
153
167
|
};
|
|
154
168
|
|
|
@@ -251,6 +265,8 @@ export async function launchDashboardServer(
|
|
|
251
265
|
if (handleHealthSettings(req, res, ctx)) return;
|
|
252
266
|
if (handleEmbedderHealth(req, res, ctx)) return;
|
|
253
267
|
if (handleRagSettings(req, res, ctx)) return;
|
|
268
|
+
if (handleRagMetrics(req, res, ctx)) return;
|
|
269
|
+
if (handleWiki(req, res, ctx)) return;
|
|
254
270
|
handleStatic(req, res, ctx);
|
|
255
271
|
});
|
|
256
272
|
|
|
@@ -21,7 +21,10 @@ import {
|
|
|
21
21
|
buildTopicModel,
|
|
22
22
|
createTopicStore,
|
|
23
23
|
bumpWikiCompactCounter,
|
|
24
|
+
applyOverridesAfterRebuild,
|
|
25
|
+
applyFullOverridesAfterRebuild,
|
|
24
26
|
} from "../../../src/topics/index.js";
|
|
27
|
+
import { assignNewMemoriesIncremental } from "../../../src/wiki/index.js";
|
|
25
28
|
import { TrigramEmbedder } from "../../../src/embedder.js";
|
|
26
29
|
import type { EmbeddedChunk } from "../../../src/topics/types.js";
|
|
27
30
|
import type { MegaConfig } from "../../mega-config.js";
|
|
@@ -89,7 +92,36 @@ export async function persistEpochAndMaintain(
|
|
|
89
92
|
);
|
|
90
93
|
const tdb = openTurnStore(runtime.currentStateDir);
|
|
91
94
|
const n = bumpWikiCompactCounter(tdb);
|
|
92
|
-
|
|
95
|
+
// W5-B: on the Nth compact we full-rebuild; otherwise try the
|
|
96
|
+
// cheap incremental path (assign new memories to nearest
|
|
97
|
+
// centroid) and only force a full rebuild when the mean
|
|
98
|
+
// silhouette has degraded below the configured floor.
|
|
99
|
+
let rebuildNow = n % every === 0;
|
|
100
|
+
if (
|
|
101
|
+
!rebuildNow &&
|
|
102
|
+
TurnsConfig.WIKI_INCREMENTAL_ENABLED &&
|
|
103
|
+
TurnsConfig.WIKI_ENHANCED_ENABLED
|
|
104
|
+
) {
|
|
105
|
+
const inc = assignNewMemoriesIncremental(
|
|
106
|
+
db,
|
|
107
|
+
runtime.currentStateDir,
|
|
108
|
+
);
|
|
109
|
+
if (
|
|
110
|
+
inc.silhouette !== null &&
|
|
111
|
+
inc.silhouette < TurnsConfig.WIKI_INCREMENTAL_SILHOUETTE_MIN
|
|
112
|
+
) {
|
|
113
|
+
// Silhouette degraded — fall through to a full rebuild.
|
|
114
|
+
rebuildNow = true;
|
|
115
|
+
} else {
|
|
116
|
+
runtime.logger.info("wiki_incremental", {
|
|
117
|
+
assigned: inc.assigned,
|
|
118
|
+
pending: inc.pending,
|
|
119
|
+
silhouetteScore: inc.silhouette,
|
|
120
|
+
uncalibrated: false,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (rebuildNow) {
|
|
93
125
|
const model = buildTopicModel(db, {
|
|
94
126
|
kRange: [TurnsConfig.WIKI_K_MIN, TurnsConfig.WIKI_K_MAX],
|
|
95
127
|
labelTopTerms: TurnsConfig.WIKI_LABEL_TOP_TERMS,
|
|
@@ -99,6 +131,13 @@ export async function persistEpochAndMaintain(
|
|
|
99
131
|
createTopicStore(runtime.currentStateDir).replaceTopicModel(
|
|
100
132
|
model,
|
|
101
133
|
);
|
|
134
|
+
// W5-A: full hybrid replay — label + merge + split overrides
|
|
135
|
+
// survive the rebuild (feature A, not just the label fast path).
|
|
136
|
+
applyFullOverridesAfterRebuild(tdb);
|
|
137
|
+
// SSE push so the dashboard Wiki/Evolution views refresh (non-fatal).
|
|
138
|
+
runtime.dashboard.event("wiki_rebuilt", {
|
|
139
|
+
topicCount: model.k,
|
|
140
|
+
});
|
|
102
141
|
runtime.logger.info("wiki_rebuild", {
|
|
103
142
|
clusterCount: model.k,
|
|
104
143
|
totalChunks: model.totalChunks,
|
|
@@ -177,6 +216,10 @@ export async function persistEpochAndMaintain(
|
|
|
177
216
|
createTopicStore(
|
|
178
217
|
runtime.currentStateDir,
|
|
179
218
|
).replaceTopicModel(model);
|
|
219
|
+
// Re-stamp custom label overrides wiped by the seed rebuild.
|
|
220
|
+
applyOverridesAfterRebuild(
|
|
221
|
+
openTurnStore(runtime.currentStateDir),
|
|
222
|
+
);
|
|
180
223
|
runtime.logger.info("wiki_seed", {
|
|
181
224
|
clusterCount: model.k,
|
|
182
225
|
sourceChunks: seedChunks.length,
|
|
@@ -66,6 +66,32 @@ export function doRecall(
|
|
|
66
66
|
injected: result.toInject.length,
|
|
67
67
|
empty: result.empty,
|
|
68
68
|
});
|
|
69
|
+
if (config.ragRecallMetrics && result.hydeInfo) {
|
|
70
|
+
runtime.dashboard.event("hyde_executed", {
|
|
71
|
+
sessionId: sid,
|
|
72
|
+
ran: result.hydeInfo.ran,
|
|
73
|
+
skipped: result.hydeInfo.skipped,
|
|
74
|
+
reason: result.hydeInfo.reason,
|
|
75
|
+
hypotheticalDoc: result.hydeInfo.hypotheticalDoc.slice(0, 400),
|
|
76
|
+
generationMs: result.hydeInfo.generationMs,
|
|
77
|
+
rawHitCount: result.hydeInfo.rawHitCount,
|
|
78
|
+
hydeHitCount: result.hydeInfo.hydeHitCount,
|
|
79
|
+
fusedHitCount: result.hydeInfo.fusedHitCount,
|
|
80
|
+
lift: result.hydeInfo.lift,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (config.ragRecallMetrics && result.recallMetrics) {
|
|
84
|
+
runtime.dashboard.event("recall_metrics", {
|
|
85
|
+
sessionId: sid,
|
|
86
|
+
hitCount: result.recallMetrics.hitCount,
|
|
87
|
+
score: result.recallMetrics.score,
|
|
88
|
+
pass: result.recallMetrics.pass,
|
|
89
|
+
relevance: result.recallMetrics.relevance,
|
|
90
|
+
coverage: result.recallMetrics.coverage,
|
|
91
|
+
diversity: result.recallMetrics.diversity,
|
|
92
|
+
specificity: result.recallMetrics.specificity,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
69
95
|
if (!result.empty && result.toInject.length > 0) {
|
|
70
96
|
const top = result.toInject[0];
|
|
71
97
|
const scorePct = Math.round((top.score ?? 0) * 100);
|
|
@@ -105,6 +131,8 @@ export function doRecall(
|
|
|
105
131
|
turnIndex: runtime.currentTurn,
|
|
106
132
|
role: "assistant",
|
|
107
133
|
startedAt: Date.now(),
|
|
134
|
+
hyde: result.hydeInfo ?? undefined,
|
|
135
|
+
recallMetrics: result.recallMetrics ?? undefined,
|
|
108
136
|
},
|
|
109
137
|
runtime.currentStateDir,
|
|
110
138
|
);
|
|
@@ -229,6 +257,9 @@ export async function doRecallAsync(
|
|
|
229
257
|
),
|
|
230
258
|
block,
|
|
231
259
|
empty: merged.length === 0,
|
|
260
|
+
// H1: merged cross-repo result reuses the same-repo pass's telemetry.
|
|
261
|
+
hydeInfo: sameRepo.hydeInfo,
|
|
262
|
+
recallMetrics: sameRepo.recallMetrics,
|
|
232
263
|
};
|
|
233
264
|
} catch {
|
|
234
265
|
return sameRepo; // cross-repo failure → same-repo only (non-fatal)
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
type TurnStore,
|
|
15
15
|
type TurnEntry,
|
|
16
16
|
type TurnRecallEntry,
|
|
17
|
+
type TurnHydeTelemetry,
|
|
18
|
+
type TurnRecallTelemetry,
|
|
17
19
|
} from "../src/store/turns/index.js";
|
|
18
20
|
import {
|
|
19
21
|
recordTurn as legacyRecordTurn,
|
|
@@ -64,6 +66,11 @@ export function ensureConversationIdFor(
|
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
/** Record one turn row (turn_end). Returns the contract TurnId (string). */
|
|
69
|
+
/** H1: HyDE telemetry write — a structural subset of HydeInvocationInfo. */
|
|
70
|
+
export type RecallHydeWrite = TurnHydeTelemetry;
|
|
71
|
+
/** H1: recall-quality write — a structural subset of RecallMetricsSnapshot. */
|
|
72
|
+
export type RecallMetricsWrite = TurnRecallTelemetry;
|
|
73
|
+
|
|
67
74
|
export function recordTurnWrite(
|
|
68
75
|
config: MegaConfig,
|
|
69
76
|
input: {
|
|
@@ -78,6 +85,10 @@ export function recordTurnWrite(
|
|
|
78
85
|
pressureBand?: string;
|
|
79
86
|
modelId?: string;
|
|
80
87
|
epochId?: string;
|
|
88
|
+
/** H1: HyDE invocation telemetry (persisted to hyde_* columns). */
|
|
89
|
+
hyde?: RecallHydeWrite;
|
|
90
|
+
/** H1: recall-quality snapshot (persisted to recall_* columns). */
|
|
91
|
+
recallMetrics?: RecallMetricsWrite;
|
|
81
92
|
},
|
|
82
93
|
stateDir: string,
|
|
83
94
|
): string {
|
|
@@ -96,6 +107,8 @@ export function recordTurnWrite(
|
|
|
96
107
|
pressureBand: input.pressureBand as TurnEntry["pressureBand"],
|
|
97
108
|
model: input.modelId,
|
|
98
109
|
epochId: input.epochId,
|
|
110
|
+
hyde: input.hyde,
|
|
111
|
+
recallMetrics: input.recallMetrics,
|
|
99
112
|
};
|
|
100
113
|
return storeFor(stateDir).appendTurn(entry);
|
|
101
114
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mega-compact",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.6",
|
|
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",
|
package/src/config/turns.ts
CHANGED
|
@@ -23,6 +23,14 @@ function envNum(name: string, def: number): number {
|
|
|
23
23
|
return Number.isFinite(n) ? n : def;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/** Default-ON toggle honoring the MEGACOMPACT_<NAME>_DISABLED opt-out convention. */
|
|
27
|
+
function envEnabled(name: string, def: boolean): boolean {
|
|
28
|
+
if (process.env[`${name}_DISABLED`] === "true" || process.env[`${name}_DISABLED`] === "1") {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return envBool(name, def);
|
|
32
|
+
}
|
|
33
|
+
|
|
26
34
|
export interface TurnsConfigShape {
|
|
27
35
|
/** Isolated turns.db store enabled (default true). OFF = legacy main-db path. */
|
|
28
36
|
TURNS_DB_ENABLED: boolean;
|
|
@@ -41,6 +49,12 @@ export interface TurnsConfigShape {
|
|
|
41
49
|
WIKI_REBUILD_EVERY_N_COMPACTS: number;
|
|
42
50
|
/** D1: seed the topic model from raw_transcript when context_chunks is thin (default true). */
|
|
43
51
|
WIKI_SEED_FROM_TURNS: boolean;
|
|
52
|
+
/** W2: wiki revival curation + provenance endpoints. Default ON; off via MEGACOMPACT_WIKI_ENHANCED_DISABLED. */
|
|
53
|
+
WIKI_ENHANCED_ENABLED: boolean;
|
|
54
|
+
/** W5: incremental topic assignment for new memories (default ON when WIKI_ENHANCED is ON). */
|
|
55
|
+
WIKI_INCREMENTAL_ENABLED: boolean;
|
|
56
|
+
/** W5: full rebuild triggers when mean silhouette drops below this (default 0.2). */
|
|
57
|
+
WIKI_INCREMENTAL_SILHOUETTE_MIN: number;
|
|
44
58
|
}
|
|
45
59
|
|
|
46
60
|
function envKRange(defMin: number, defMax: number): [number, number] {
|
|
@@ -66,6 +80,9 @@ export function loadTurnsConfig(): TurnsConfigShape {
|
|
|
66
80
|
WIKI_LABEL_TOP_TERMS: envNum("MEGACOMPACT_WIKI_LABEL_TOP_TERMS", 5),
|
|
67
81
|
WIKI_REBUILD_EVERY_N_COMPACTS: envNum("MEGACOMPACT_WIKI_REBUILD_EVERY", 3),
|
|
68
82
|
WIKI_SEED_FROM_TURNS: envBool("MEGACOMPACT_WIKI_SEED_FROM_TURNS", true),
|
|
83
|
+
WIKI_ENHANCED_ENABLED: envEnabled("MEGACOMPACT_WIKI_ENHANCED", true),
|
|
84
|
+
WIKI_INCREMENTAL_ENABLED: envEnabled("MEGACOMPACT_WIKI_INCREMENTAL", true),
|
|
85
|
+
WIKI_INCREMENTAL_SILHOUETTE_MIN: envNum("MEGACOMPACT_WIKI_SILHOUETTE_MIN", 0.2),
|
|
69
86
|
};
|
|
70
87
|
}
|
|
71
88
|
|
package/src/config.ts
CHANGED
|
@@ -140,3 +140,6 @@ export const RAG_MEMORY_GRAPH = (): boolean =>
|
|
|
140
140
|
* MEGACOMPACT_HYDE_DISABLED=true. TrigramEmbedder path is unaffected. */
|
|
141
141
|
export const RAG_HYDE_ENABLED = (): boolean =>
|
|
142
142
|
ragEnabled("MEGACOMPACT_HYDE");
|
|
143
|
+
|
|
144
|
+
/** Spec 1: vbrainstorm visual design migration for the dashboard. */
|
|
145
|
+
export const NEW_UI = (): boolean => ragEnabled("MEGACOMPACT_NEW_UI");
|
package/src/recall/async.ts
CHANGED
|
@@ -138,5 +138,14 @@ export async function recallAndInlineAsync(
|
|
|
138
138
|
` • ${h.checkpoint.checkpointId} (${h.checkpoint.summary.slice(0, 60).replace(/\n/g, " ")}…)`,
|
|
139
139
|
);
|
|
140
140
|
|
|
141
|
-
return {
|
|
141
|
+
return {
|
|
142
|
+
toInject,
|
|
143
|
+
report,
|
|
144
|
+
block,
|
|
145
|
+
empty: toInject.length === 0,
|
|
146
|
+
// H1: cross-repo async path is HyDE-free; telemetry stays null until a
|
|
147
|
+
// future sprint threads it here.
|
|
148
|
+
hydeInfo: null,
|
|
149
|
+
recallMetrics: null,
|
|
150
|
+
};
|
|
142
151
|
}
|