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,97 @@
|
|
|
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
|
+
import { appendFileSync } from "node:fs";
|
|
10
|
+
import { openStore } from "../../src/store/sqlite.js";
|
|
11
|
+
import { createTopicStore } from "../../src/topics/store.js";
|
|
12
|
+
import { buildTopicModel } from "../../src/topics/cluster.js";
|
|
13
|
+
/** Append a JSONL SSE line to events.log (best-effort, non-fatal). */
|
|
14
|
+
export function emitWikiEvent(ctx, event) {
|
|
15
|
+
try {
|
|
16
|
+
appendFileSync(ctx.eventsPath, JSON.stringify({ ts: new Date().toISOString(), ...event }) + "\n");
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
/* non-fatal: SSE push best-effort */
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** Lazy-build the topic model when the topics table is empty (best-effort). */
|
|
23
|
+
export function ensureWikiBuilt(ctx, tdb) {
|
|
24
|
+
const topicCount = tdb.prepare("SELECT COUNT(*) AS c FROM topics").get().c;
|
|
25
|
+
if (topicCount > 0)
|
|
26
|
+
return;
|
|
27
|
+
try {
|
|
28
|
+
const mainDb = openStore(ctx.stateDir);
|
|
29
|
+
const model = buildTopicModel(mainDb);
|
|
30
|
+
if (model.k > 0 && model.totalChunks > 0) {
|
|
31
|
+
createTopicStore(ctx.stateDir).replaceTopicModel(model);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
/* non-fatal: lazy wiki build is best-effort */
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** Build a topic index entry with resolved label + override badges. */
|
|
39
|
+
export function toIndexEntry(topic, curation) {
|
|
40
|
+
const resolved = curation.resolveLabel(topic.id, topic.label);
|
|
41
|
+
return {
|
|
42
|
+
id: topic.id,
|
|
43
|
+
label: resolved.label,
|
|
44
|
+
memoryCount: topic.memoryCount,
|
|
45
|
+
lastUpdated: topic.lastUpdated,
|
|
46
|
+
edited: resolved.edited,
|
|
47
|
+
overrideKinds: curation.overrideKinds(topic.id),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** Convert a member assignment into contract provenance. */
|
|
51
|
+
export function toProvenance(member) {
|
|
52
|
+
return {
|
|
53
|
+
memoryId: member.memoryId,
|
|
54
|
+
sessionId: member.sessionId ?? "",
|
|
55
|
+
assignedAt: member.assignedAt,
|
|
56
|
+
method: member.method,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Bucket epoch-ms values by day; returns ascending [{bucket, count}]. */
|
|
60
|
+
export function bucketize(values) {
|
|
61
|
+
const DAY = 86_400_000;
|
|
62
|
+
const counts = new Map();
|
|
63
|
+
for (const v of values) {
|
|
64
|
+
if (!Number.isFinite(v) || v <= 0)
|
|
65
|
+
continue;
|
|
66
|
+
const b = Math.floor(v / DAY) * DAY;
|
|
67
|
+
counts.set(b, (counts.get(b) ?? 0) + 1);
|
|
68
|
+
}
|
|
69
|
+
return [...counts.entries()]
|
|
70
|
+
.sort((a, b) => a[0] - b[0])
|
|
71
|
+
.map(([bucket, count]) => ({ bucket, count }));
|
|
72
|
+
}
|
|
73
|
+
/** Fetch member content (text + timestamp) from the main DB. */
|
|
74
|
+
export function memberContent(ctx, members) {
|
|
75
|
+
const out = new Map();
|
|
76
|
+
if (members.length === 0)
|
|
77
|
+
return out;
|
|
78
|
+
try {
|
|
79
|
+
const mainDb = openStore(ctx.stateDir);
|
|
80
|
+
for (const m of members) {
|
|
81
|
+
const row = mainDb
|
|
82
|
+
.prepare(`SELECT COALESCE(normalized_text, summary, topic_summary) AS text, timestamp
|
|
83
|
+
FROM context_chunks WHERE session_id = ? AND id = ?`)
|
|
84
|
+
.get(m.sessionId ?? "", m.memoryId);
|
|
85
|
+
if (row) {
|
|
86
|
+
out.set(m.memoryId, {
|
|
87
|
+
text: (row.text ?? "").trim(),
|
|
88
|
+
timestamp: row.timestamp ?? 0,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
/* content fetch is best-effort */
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
@@ -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
|
}
|