pi-mega-compact 0.20.60 → 0.20.61

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.
Files changed (91) hide show
  1. package/dist/config/vector-cortex-dash-0d.js +26 -0
  2. package/dist/config/vector-cortex.js +2 -0
  3. package/dist/config.js +1 -1
  4. package/dist/extensions/dashboard-server/routes-rag-settings-vector-cortex.js +1 -0
  5. package/dist/src/config/vector-cortex-dash-0d.js +26 -0
  6. package/dist/src/config/vector-cortex.js +2 -0
  7. package/dist/src/config.js +1 -1
  8. package/extensions/dashboard-client/dist/assets/AdminTab-DEjVnlwe.js +2 -0
  9. package/extensions/dashboard-client/dist/assets/AdminTab-DEjVnlwe.js.map +1 -0
  10. package/extensions/dashboard-client/dist/assets/{AreaChart-B5Is2_EN.js → AreaChart-B1PoTqKJ.js} +2 -2
  11. package/extensions/dashboard-client/dist/assets/{AreaChart-B5Is2_EN.js.map → AreaChart-B1PoTqKJ.js.map} +1 -1
  12. package/extensions/dashboard-client/dist/assets/{BarChart-Bj2oT871.js → BarChart-DaoONT_V.js} +2 -2
  13. package/extensions/dashboard-client/dist/assets/{BarChart-Bj2oT871.js.map → BarChart-DaoONT_V.js.map} +1 -1
  14. package/extensions/dashboard-client/dist/assets/{CacheTab-DuhLeKHW.js → CacheTab-rD5sjlkW.js} +2 -2
  15. package/extensions/dashboard-client/dist/assets/{CacheTab-DuhLeKHW.js.map → CacheTab-rD5sjlkW.js.map} +1 -1
  16. package/extensions/dashboard-client/dist/assets/ConfigTab-BQkufK7t.js +2 -0
  17. package/extensions/dashboard-client/dist/assets/ConfigTab-BQkufK7t.js.map +1 -0
  18. package/extensions/dashboard-client/dist/assets/{EventsTab-BG6L8uWh.js → EventsTab-DGlQzJaT.js} +2 -2
  19. package/extensions/dashboard-client/dist/assets/{EventsTab-BG6L8uWh.js.map → EventsTab-DGlQzJaT.js.map} +1 -1
  20. package/extensions/dashboard-client/dist/assets/{HealthTab-CZmGCUJh.js → HealthTab-DW7kt4qm.js} +2 -2
  21. package/extensions/dashboard-client/dist/assets/{HealthTab-CZmGCUJh.js.map → HealthTab-DW7kt4qm.js.map} +1 -1
  22. package/extensions/dashboard-client/dist/assets/MaintenanceTab-CGiZ0OQ6.js +2 -0
  23. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-CoZ1mSjU.js.map → MaintenanceTab-CGiZ0OQ6.js.map} +1 -1
  24. package/extensions/dashboard-client/dist/assets/MemoryMapTab-BwyrxjuW.js +2 -0
  25. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-BwVaWniY.js.map → MemoryMapTab-BwyrxjuW.js.map} +1 -1
  26. package/extensions/dashboard-client/dist/assets/{MetricsCards-BCJoHe_n.js → MetricsCards-IZaCOOXU.js} +2 -2
  27. package/extensions/dashboard-client/dist/assets/{MetricsCards-BCJoHe_n.js.map → MetricsCards-IZaCOOXU.js.map} +1 -1
  28. package/extensions/dashboard-client/dist/assets/MetricsTab-wCxVpHlC.js +2 -0
  29. package/extensions/dashboard-client/dist/assets/{MetricsTab-UQSqUGXe.js.map → MetricsTab-wCxVpHlC.js.map} +1 -1
  30. package/extensions/dashboard-client/dist/assets/{OverviewTab-qi12IYvs.js → OverviewTab-DsH08Wr0.js} +2 -2
  31. package/extensions/dashboard-client/dist/assets/{OverviewTab-qi12IYvs.js.map → OverviewTab-DsH08Wr0.js.map} +1 -1
  32. package/extensions/dashboard-client/dist/assets/{ReposTab-CcHZPzOO.js → ReposTab-Dpb7sXWB.js} +2 -2
  33. package/extensions/dashboard-client/dist/assets/{ReposTab-CcHZPzOO.js.map → ReposTab-Dpb7sXWB.js.map} +1 -1
  34. package/extensions/dashboard-client/dist/assets/SessionsTab-DFMo7tKA.js +2 -0
  35. package/extensions/dashboard-client/dist/assets/SessionsTab-DFMo7tKA.js.map +1 -0
  36. package/extensions/dashboard-client/dist/assets/SetupTab-D2IEBt1u.js +3 -0
  37. package/extensions/dashboard-client/dist/assets/SetupTab-D2IEBt1u.js.map +1 -0
  38. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-DhfdRM3_.js → TimeSavedCard-BGPvmVFG.js} +2 -2
  39. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-DhfdRM3_.js.map → TimeSavedCard-BGPvmVFG.js.map} +1 -1
  40. package/extensions/dashboard-client/dist/assets/TurnMemoryView-BZo09Jri.js +5 -0
  41. package/extensions/dashboard-client/dist/assets/TurnMemoryView-BZo09Jri.js.map +1 -0
  42. package/extensions/dashboard-client/dist/assets/TurnsTab-CosKNvMp.js +2 -0
  43. package/extensions/dashboard-client/dist/assets/TurnsTab-CosKNvMp.js.map +1 -0
  44. package/extensions/dashboard-client/dist/assets/{VcStatusBadge-XGMW6u62.js → VcStatusBadge-UEZLL6Zj.js} +2 -2
  45. package/extensions/dashboard-client/dist/assets/{VcStatusBadge-XGMW6u62.js.map → VcStatusBadge-UEZLL6Zj.js.map} +1 -1
  46. package/extensions/dashboard-client/dist/assets/{VectorCortexTab-BACHKLjU.js → VectorCortexTab-fDAgD2pN.js} +2 -2
  47. package/extensions/dashboard-client/dist/assets/{VectorCortexTab-BACHKLjU.js.map → VectorCortexTab-fDAgD2pN.js.map} +1 -1
  48. package/extensions/dashboard-client/dist/assets/{WikiTab-CGQWHuRq.js → WikiTab-7TIe2MkS.js} +2 -2
  49. package/extensions/dashboard-client/dist/assets/{WikiTab-CGQWHuRq.js.map → WikiTab-7TIe2MkS.js.map} +1 -1
  50. package/extensions/dashboard-client/dist/assets/button-C9qrZYdn.js +2 -0
  51. package/extensions/dashboard-client/dist/assets/{button-SyOWCGkp.js.map → button-C9qrZYdn.js.map} +1 -1
  52. package/extensions/dashboard-client/dist/assets/{card-69DCTMel.js → card-DxZRNC4E.js} +2 -2
  53. package/extensions/dashboard-client/dist/assets/{card-69DCTMel.js.map → card-DxZRNC4E.js.map} +1 -1
  54. package/extensions/dashboard-client/dist/assets/client-extra-Dj7wJs6Z.js +2 -0
  55. package/extensions/dashboard-client/dist/assets/{client-extra-CgeDg1Md.js.map → client-extra-Dj7wJs6Z.js.map} +1 -1
  56. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-BwTtrqm5.js → generateCategoricalChart-Dj-DYUgL.js} +2 -2
  57. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-BwTtrqm5.js.map → generateCategoricalChart-Dj-DYUgL.js.map} +1 -1
  58. package/extensions/dashboard-client/dist/assets/{index-BZcYzeCr.js → index-Bq2n15a4.js} +31 -31
  59. package/extensions/dashboard-client/dist/assets/index-Bq2n15a4.js.map +1 -0
  60. package/extensions/dashboard-client/dist/assets/switch-1sEgLPHu.js +2 -0
  61. package/extensions/dashboard-client/dist/assets/{switch-DhQfmSFD.js.map → switch-1sEgLPHu.js.map} +1 -1
  62. package/extensions/dashboard-client/dist/assets/toggle-jMzQoJ2C.js +2 -0
  63. package/extensions/dashboard-client/dist/assets/{toggle-B3gNAVog.js.map → toggle-jMzQoJ2C.js.map} +1 -1
  64. package/extensions/dashboard-client/dist/assets/{useSSE-BkkjZ4bO.js → useSSE-Dxy616R1.js} +2 -2
  65. package/extensions/dashboard-client/dist/assets/{useSSE-BkkjZ4bO.js.map → useSSE-Dxy616R1.js.map} +1 -1
  66. package/extensions/dashboard-client/dist/index.html +1 -1
  67. package/extensions/dashboard-client/src/App.tsx +150 -17
  68. package/extensions/dashboard-client/src/tabs/TurnsTab.tsx +9 -366
  69. package/extensions/dashboard-client/src/tabs/dashHashRouter.ts +76 -0
  70. package/extensions/dashboard-client/src/tabs/registry.ts +9 -1
  71. package/extensions/dashboard-server/routes-rag-settings-vector-cortex.ts +6 -0
  72. package/package.json +1 -1
  73. package/src/config/vector-cortex-dash-0d.ts +28 -0
  74. package/src/config/vector-cortex.ts +2 -0
  75. package/src/config.ts +1 -0
  76. package/extensions/dashboard-client/dist/assets/HydeDetailPanel-B-P1jTtk.js +0 -5
  77. package/extensions/dashboard-client/dist/assets/HydeDetailPanel-B-P1jTtk.js.map +0 -1
  78. package/extensions/dashboard-client/dist/assets/MaintenanceTab-CoZ1mSjU.js +0 -2
  79. package/extensions/dashboard-client/dist/assets/MemoryMapTab-BwVaWniY.js +0 -2
  80. package/extensions/dashboard-client/dist/assets/MetricsTab-UQSqUGXe.js +0 -2
  81. package/extensions/dashboard-client/dist/assets/SessionsTab-nzd1408Z.js +0 -2
  82. package/extensions/dashboard-client/dist/assets/SessionsTab-nzd1408Z.js.map +0 -1
  83. package/extensions/dashboard-client/dist/assets/SetupTab-DPrG3s9V.js +0 -3
  84. package/extensions/dashboard-client/dist/assets/SetupTab-DPrG3s9V.js.map +0 -1
  85. package/extensions/dashboard-client/dist/assets/TurnsTab-oalBNLVr.js +0 -2
  86. package/extensions/dashboard-client/dist/assets/TurnsTab-oalBNLVr.js.map +0 -1
  87. package/extensions/dashboard-client/dist/assets/button-SyOWCGkp.js +0 -2
  88. package/extensions/dashboard-client/dist/assets/client-extra-CgeDg1Md.js +0 -2
  89. package/extensions/dashboard-client/dist/assets/index-BZcYzeCr.js.map +0 -1
  90. package/extensions/dashboard-client/dist/assets/switch-DhQfmSFD.js +0 -2
  91. package/extensions/dashboard-client/dist/assets/toggle-B3gNAVog.js +0 -2
@@ -4,39 +4,53 @@
4
4
  * SPRINT-B1: React scaffold with tab routing, header, error boundary.
5
5
  * SPRINT-C1+: tabs wired progressively with real content.
6
6
  * SPRINT-V2: NEW_UI flag splits OldDashboard (legacy) from NewDashboard (AppShell).
7
+ * DASH-0d: the `TabContent` lazy list + switch consolidate onto the 7 fixed
8
+ * navigational surfaces (Overview, Sessions, Cache+Performance, Memory Graph,
9
+ * Diagnostics, Setup, Admin) behind MEGACOMPACT_DASH_0D. An additive hash router
10
+ * maps every legacy #deep-link to a live consolidated surface. Flag-OFF
11
+ * reproduces the pre-rollup 13-tab surface list byte-identically (the
12
+ * DASH-0D-LEGACY region). The consolidated rendering is the DASH-0D-CONSOLIDATED
13
+ * region — both are parsed by scripts/dash-tab-count.mjs.
7
14
  */
8
15
 
9
- import React, { useState, useCallback } from "react";
16
+ import React, { useState, useEffect, useCallback } from "react";
10
17
  import { ErrorBoundary } from "./components/ErrorBoundary";
11
18
  import { TabBar } from "./components/TabBar";
12
19
  import { LoadingSpinner } from "./components/LoadingSpinner";
13
20
  import { useApi } from "./hooks/useApi";
14
- import { fetchSnapshot } from "./api/client";
15
- import type { SnapshotResponse } from "@contracts";
21
+ import { fetchSnapshot, fetchSettings } from "./api/client";
22
+ import type { SnapshotResponse, SettingsResponse } from "@contracts";
16
23
  import { NEW_UI } from "./config";
17
24
  import { AppShell } from "./components/layout/AppShell";
18
25
  import {
19
26
  PRIMARY_TABS,
20
27
  ADVANCED_TABS,
21
28
  ADVANCED_TAB_IDS as advancedTabIds,
29
+ DASH_SURFACE_IDS,
30
+ DASH_SURFACE_LABELS,
22
31
  type TabId,
32
+ type DashTabId,
23
33
  } from "./tabs/registry";
34
+ import { useHashTab } from "./tabs/dashHashRouter";
24
35
  export type { TabId };
25
36
 
26
- // Tab components — lazy-loaded. C1 fills Overview + Events; C2/C3 fill the rest.
37
+ // Tab components — lazy-loaded. DASH-0d: the consolidated 7 host components come
38
+ // from the merged barrel homes; the 13 legacy-only components stay for flag-off.
27
39
  const OverviewTab = React.lazy(() => import("./tabs/OverviewTab"));
40
+ const SessionsTab = React.lazy(() => import("./tabs/SessionsTab"));
41
+ const CacheTab = React.lazy(() => import("./tabs/CacheTab"));
42
+ const MemoryMapTab = React.lazy(() => import("./tabs/MemoryMapTab"));
43
+ const VectorCortexTab = React.lazy(() => import("./tabs/VectorCortexTab"));
44
+ const SetupTab = React.lazy(() => import("./tabs/SetupTab"));
45
+ const AdminTab = React.lazy(() => import("./tabs/AdminTab"));
46
+ // Legacy-only host components (flag-off pre-rollup surface set).
28
47
  const ReposTab = React.lazy(() => import("./tabs/ReposTab"));
29
48
  const EventsTab = React.lazy(() => import("./tabs/EventsTab"));
30
- const SetupTab = React.lazy(() => import("./tabs/SetupTab"));
31
49
  const MetricsTab = React.lazy(() => import("./tabs/MetricsTab"));
32
- const CacheTab = React.lazy(() => import("./tabs/CacheTab"));
33
- const SessionsTab = React.lazy(() => import("./tabs/SessionsTab"));
34
50
  const WikiTab = React.lazy(() => import("./tabs/WikiTab"));
35
51
  const TurnsTab = React.lazy(() => import("./tabs/TurnsTab"));
36
52
  const MaintenanceTab = React.lazy(() => import("./tabs/MaintenanceTab"));
37
- const MemoryMapTab = React.lazy(() => import("./tabs/MemoryMapTab"));
38
53
  const HealthTab = React.lazy(() => import("./tabs/HealthTab"));
39
- const VectorCortexTab = React.lazy(() => import("./tabs/VectorCortexTab"));
40
54
 
41
55
  interface AppState {
42
56
  snapshot: SnapshotResponse | null;
@@ -44,7 +58,52 @@ interface AppState {
44
58
  error: Error | null;
45
59
  }
46
60
 
47
- function TabContent({
61
+ const DASH_0D_KEY = "MEGACOMPACT_DASH_0D";
62
+
63
+ /**
64
+ * Resolve the DASH-0d consolidation flag from the server-authoritative
65
+ * `/api/rag-settings` state (the dashboard client has no `process` global).
66
+ * Absent/not-yet-loaded => true (default ON), matching the server default.
67
+ */
68
+ function dash0dEnabled(settings: SettingsResponse | null): boolean {
69
+ if (!settings) return true;
70
+ for (const cat of settings.categories) {
71
+ for (const s of cat.settings) {
72
+ if (s.key === DASH_0D_KEY && s.type === "boolean") return s.value === true;
73
+ }
74
+ }
75
+ return true;
76
+ }
77
+
78
+ /** The unified active-tab state: a legacy 13-tab TabId OR a consolidated surface. */
79
+ type ActiveTab = TabId | DashTabId;
80
+
81
+ /** Consolidated 7-surface content (DASH-0d). Parsed by scripts/dash-tab-count.mjs. */
82
+ function ConsolidatedContent({
83
+ activeTab,
84
+ snapshot,
85
+ loading,
86
+ error,
87
+ }: AppState & { activeTab: DashTabId }): React.ReactElement {
88
+ return (
89
+ <React.Suspense fallback={<LoadingSpinner />}>
90
+ {/* DASH-0D-CONSOLIDATED */}
91
+ {activeTab === "overview" && (
92
+ <OverviewTab snapshot={snapshot} loading={loading} error={error} />
93
+ )}
94
+ {activeTab === "sessions" && <SessionsTab />}
95
+ {activeTab === "cache-perf" && <CacheTab />}
96
+ {activeTab === "memory-graph" && <MemoryMapTab />}
97
+ {activeTab === "diagnostics" && <VectorCortexTab />}
98
+ {activeTab === "setup" && <SetupTab />}
99
+ {activeTab === "admin" && <AdminTab />}
100
+ {/* DASH-0D-CONSOLIDATED-END */}
101
+ </React.Suspense>
102
+ );
103
+ }
104
+
105
+ /** Pre-rollup 13-tab content (flag-off). Parsed by scripts/dash-tab-count.mjs. */
106
+ function LegacyContent({
48
107
  activeTab,
49
108
  snapshot,
50
109
  loading,
@@ -52,6 +111,7 @@ function TabContent({
52
111
  }: AppState & { activeTab: TabId }): React.ReactElement {
53
112
  return (
54
113
  <React.Suspense fallback={<LoadingSpinner />}>
114
+ {/* DASH-0D-LEGACY */}
55
115
  {activeTab === "overview" && (
56
116
  <OverviewTab snapshot={snapshot} loading={loading} error={error} />
57
117
  )}
@@ -67,6 +127,7 @@ function TabContent({
67
127
  {activeTab === "memory-map" && <MemoryMapTab />}
68
128
  {activeTab === "health" && <HealthTab />}
69
129
  {activeTab === "vector-cortex" && <VectorCortexTab />}
130
+ {/* DASH-0D-LEGACY-END */}
70
131
  </React.Suspense>
71
132
  );
72
133
  }
@@ -102,7 +163,7 @@ function OldDashboard({
102
163
  onTabChange={setActiveTab}
103
164
  />
104
165
  <main className="dashboard-content">
105
- <TabContent activeTab={activeTab} snapshot={snapshot} loading={loading} error={error} />
166
+ <LegacyContent activeTab={activeTab} snapshot={snapshot} loading={loading} error={error} />
106
167
  </main>
107
168
  </div>
108
169
  </ErrorBoundary>
@@ -122,14 +183,40 @@ function NewDashboard({
122
183
  return (
123
184
  <ErrorBoundary>
124
185
  <AppShell active={activeTab} onTabChange={setActiveTab} snapshot={snapshot}>
125
- <TabContent activeTab={activeTab} snapshot={snapshot} loading={loading} error={error} />
186
+ <LegacyContent activeTab={activeTab} snapshot={snapshot} loading={loading} error={error} />
126
187
  </AppShell>
127
188
  </ErrorBoundary>
128
189
  );
129
190
  }
130
191
 
192
+ /** DASH-0d consolidated 7-surface navigation rail (flag-ON New UI path). */
193
+ function ConsolidatedNav({
194
+ active,
195
+ onSelect,
196
+ }: {
197
+ active: DashTabId;
198
+ onSelect: (id: DashTabId) => void;
199
+ }): React.ReactElement {
200
+ return (
201
+ <nav className="consolidated-nav" role="tablist" aria-label="Dashboard surfaces">
202
+ {DASH_SURFACE_IDS.map((id) => (
203
+ <button
204
+ key={id}
205
+ type="button"
206
+ role="tab"
207
+ aria-selected={active === id}
208
+ className={active === id ? "active" : ""}
209
+ onClick={() => onSelect(id)}
210
+ >
211
+ {DASH_SURFACE_LABELS[id] ?? id}
212
+ </button>
213
+ ))}
214
+ </nav>
215
+ );
216
+ }
217
+
131
218
  export default function App(): React.ReactElement {
132
- const [activeTab, setActiveTab] = useState<TabId>("overview");
219
+ const [activeTab, setActiveTab] = useState<ActiveTab>("overview");
133
220
  const {
134
221
  data: snapshot,
135
222
  loading,
@@ -142,11 +229,57 @@ export default function App(): React.ReactElement {
142
229
  },
143
230
  );
144
231
 
232
+ // Server-resolved DASH-0d flag (default ON when settings absent).
233
+ const { data: settingsData } = useApi<SettingsResponse>(
234
+ useCallback(() => fetchSettings(), []),
235
+ { pollInterval: 0, maxRetries: 0 },
236
+ );
237
+ const dash0dOn = dash0dEnabled(settingsData);
238
+
239
+ // Additive hash→surface deep-link router: every legacy #hash maps to a live
240
+ // consolidated surface; empty hash keeps current behavior (default overview).
241
+ // Only active under the DASH-0d 7-surface rendering — flag-off keeps the
242
+ // pre-rollup 13-tab behavior unchanged (hash ignored).
243
+ const hashSurface = useHashTab();
244
+ useEffect(() => {
245
+ if (dash0dOn && hashSurface != null) setActiveTab(hashSurface);
246
+ }, [dash0dOn, hashSurface]);
247
+
248
+ // Flag-ON consolidated New-UI surface: a 7-surface rail + the consolidated
249
+ // content. Flag-OFF (or legacy NEW_UI false) renders the 13-tab path as today.
250
+ if (NEW_UI() && dash0dOn) {
251
+ const current = DASH_SURFACE_IDS.includes(activeTab as DashTabId)
252
+ ? (activeTab as DashTabId)
253
+ : "overview";
254
+ return (
255
+ <ErrorBoundary>
256
+ <div className="dashboard-app">
257
+ <header className="dashboard-header">
258
+ <h1>mega-compact dashboard <span className="tier">{snapshot?.tier ?? "unknown"}</span></h1>
259
+ </header>
260
+ <ConsolidatedNav active={current} onSelect={(id: DashTabId) => setActiveTab(id)} />
261
+ <main className="dashboard-content">
262
+ <ConsolidatedContent
263
+ activeTab={current}
264
+ snapshot={snapshot}
265
+ loading={loading}
266
+ error={error}
267
+ />
268
+ </main>
269
+ </div>
270
+ </ErrorBoundary>
271
+ );
272
+ }
273
+
274
+ // Flag-off (or legacy NEW_UI): always a legacy TabId (hash routing is gated
275
+ // on dash0dOn above, so activeTab stays a 13-tab id here).
276
+ const legacyTab = (activeTab as TabId) ?? "overview";
277
+
145
278
  if (NEW_UI()) {
146
279
  return (
147
280
  <NewDashboard
148
- activeTab={activeTab}
149
- setActiveTab={setActiveTab}
281
+ activeTab={legacyTab}
282
+ setActiveTab={(id: TabId) => setActiveTab(id)}
150
283
  snapshot={snapshot}
151
284
  loading={loading}
152
285
  error={error}
@@ -156,8 +289,8 @@ export default function App(): React.ReactElement {
156
289
 
157
290
  return (
158
291
  <OldDashboard
159
- activeTab={activeTab}
160
- setActiveTab={setActiveTab}
292
+ activeTab={legacyTab}
293
+ setActiveTab={(id: TabId) => setActiveTab(id)}
161
294
  snapshot={snapshot}
162
295
  loading={loading}
163
296
  error={error}
@@ -1,374 +1,17 @@
1
1
  /**
2
- * dashboard-client/src/tabs/TurnsTab.tsx — Turn-by-turn memory tracking + recall (S52).
2
+ * dashboard-client/src/tabs/TurnsTab.tsx — Turn-by-turn memory surface (S52).
3
3
  *
4
- * Fetches /api/turns (conversation list), expands the active conversation into
5
- * per-turn detail (/api/turns/conversation/:id), and renders:
6
- * - Conversation summary list (turn count, recall total, epochs, last turn).
7
- * - Per-turn rows: turn index, role, ctx tokens/percent + pressure band,
8
- * epoch id, and the injected-checkpoint recall set (the memory recalled
9
- * at each turn, with score + source).
10
- * - Pending rewind intents (S52A) + Fork action (S50C primitive).
11
- *
12
- * Read-mostly via /api/turns; fork/intent/prune via POST. No absolute URLs
13
- * (PREVENT-PI-004: loopback-only, same-origin static bundle).
4
+ * DASH-0d consolidation: the sessions surface owns the turns body as a drill-
5
+ * down (`SessionsTab/TurnMemoryView.tsx`, re-homed VERBATIM in DASH-0b). This
6
+ * standalone top-level file is reduced to a thin shell re-exporting
7
+ * `TurnMemoryView`, so the flag-off pre-rollup 13-tab surface set still
8
+ * resolves `TurnsTab` (deep-link + lazy-import anchor) with no duplicated
9
+ * render body. The canonical home is `SessionsTab/TurnMemoryView.tsx`.
14
10
  */
15
11
 
16
12
  import type React from "react";
17
- import { useCallback, useState } from "react";
18
- import { useApi } from "../hooks/useApi";
19
- import {
20
- fetchTurns,
21
- fetchConversationTurns,
22
- fetchTurnIntents,
23
- postFork,
24
- postTurnIntent,
25
- } from "../api/client";
26
- import type {
27
- TurnsResponse,
28
- ConversationTurnsResponse,
29
- RewindIntentsResponse,
30
- TurnRow,
31
- ConversationSummary,
32
- } from "@contracts";
33
- import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card";
34
- import { Badge } from "../components/ui/badge";
35
- import { Button } from "../components/ui/button";
36
- import { HydeDetailPanel } from "../components/HydeDetailPanel";
37
-
38
- function fmtTs(ms: number): string {
39
- if (!ms) return "—";
40
- return new Date(ms).toLocaleString();
41
- }
42
-
43
- function bandVariant(band: TurnRow["pressureBand"]): "success" | "warning" | "danger" | "default" {
44
- if (band === "green") return "success";
45
- if (band === "yellow") return "warning";
46
- if (band === "red") return "danger";
47
- return "default";
48
- }
49
-
50
- function sourceLabel(s: TurnRow["recall"][number]["source"]): string {
51
- if (s === "checkpoint") return "ckpt";
52
- if (s === "cluster_summary") return "raptor";
53
- return "mem";
54
- }
13
+ import { TurnMemoryView } from "./SessionsTab/TurnMemoryView";
55
14
 
56
15
  export default function TurnsTab(): React.ReactElement {
57
- const {
58
- data: turns,
59
- loading,
60
- error,
61
- } = useApi<TurnsResponse>(
62
- useCallback(() => fetchTurns(), []),
63
- {
64
- pollInterval: 10_000,
65
- },
66
- );
67
- const { data: intents } = useApi<RewindIntentsResponse>(
68
- useCallback(() => fetchTurnIntents(), []),
69
- { pollInterval: 5_000 },
70
- );
71
- const [expanded, setExpanded] = useState<string | null>(null);
72
- const [detail, setDetail] = useState<ConversationTurnsResponse | null>(null);
73
- const [detailLoading, setDetailLoading] = useState(false);
74
- const [busy, setBusy] = useState<string | null>(null);
75
- const [notice, setNotice] = useState<string | null>(null);
76
-
77
- const expand = useCallback(
78
- async (conversationId: string) => {
79
- if (expanded === conversationId) {
80
- setExpanded(null);
81
- setDetail(null);
82
- return;
83
- }
84
- setExpanded(conversationId);
85
- setDetailLoading(true);
86
- setDetail(null);
87
- try {
88
- const d = await fetchConversationTurns(conversationId);
89
- setDetail(d);
90
- } finally {
91
- setDetailLoading(false);
92
- }
93
- },
94
- [expanded],
95
- );
96
-
97
- const onFork = useCallback(
98
- async (conversationId: string, turnIndex: number) => {
99
- setBusy(`fork:${conversationId}:${turnIndex}`);
100
- setNotice(null);
101
- try {
102
- const out = await postFork(conversationId, turnIndex);
103
- setNotice(
104
- `Forked → ${out.childConversationId} (${out.recalledCount} recalled)`,
105
- );
106
- } catch (e) {
107
- setNotice(`Fork failed: ${(e as Error).message}`);
108
- } finally {
109
- setBusy(null);
110
- }
111
- },
112
- [],
113
- );
114
-
115
- const onRewind = useCallback(
116
- async (conversationId: string, turnIndex: number) => {
117
- setBusy(`rewind:${conversationId}:${turnIndex}`);
118
- setNotice(null);
119
- try {
120
- await postTurnIntent(conversationId, turnIndex);
121
- setNotice(`Rewind intent queued for turn ${turnIndex}`);
122
- } catch (e) {
123
- setNotice(`Rewind failed: ${(e as Error).message}`);
124
- } finally {
125
- setBusy(null);
126
- }
127
- },
128
- [],
129
- );
130
-
131
- if (error && !turns) {
132
- return <div className="tab-stub">Error loading turns: {error.message}</div>;
133
- }
134
- if (loading && !turns) {
135
- return <div className="tab-stub">Loading turns…</div>;
136
- }
137
- if (!turns || turns.conversations.length === 0) {
138
- return (
139
- <div className="tab-stub">
140
- <h3>Turn-by-turn memory</h3>
141
- <p>
142
- No turns recorded yet. Per-turn tracking starts when{" "}
143
- <code>MEGACOMPACT_TURNS_DB=1</code> and the session runs through a
144
- compaction. Each turn records its context metrics + the checkpoints
145
- recalled into it.
146
- </p>
147
- </div>
148
- );
149
- }
150
-
151
- return (
152
- <div className="flex flex-col gap-4">
153
- <h3 className="font-heading text-lg font-semibold">
154
- Turn-by-turn memory tracking + recall
155
- </h3>
156
- {notice && <Badge variant="warning">{notice}</Badge>}
157
-
158
- {intents && intents.intents.length > 0 && (
159
- <Card>
160
- <CardHeader>
161
- <CardTitle>
162
- Pending rewind intents ({intents.intents.length})
163
- </CardTitle>
164
- </CardHeader>
165
- <CardContent>
166
- <ul className="list-disc space-y-1 pl-5">
167
- {intents.intents.map((i) => (
168
- <li key={i.id}>
169
- {fmtTs(i.createdAt)} — rewind{" "}
170
- <code>{i.conversationId}</code> to turn {i.targetTurnIndex}
171
- </li>
172
- ))}
173
- </ul>
174
- </CardContent>
175
- </Card>
176
- )}
177
-
178
- <Card>
179
- <CardHeader>
180
- <CardTitle>
181
- Conversations ({turns.conversations.length})
182
- </CardTitle>
183
- </CardHeader>
184
- <CardContent>
185
- <table className="w-full border-collapse text-sm">
186
- <thead>
187
- <tr>
188
- <th className="border-b border-border px-3 py-2 text-left font-medium text-muted-foreground">Conversation</th>
189
- <th className="border-b border-border px-3 py-2 text-left font-medium text-muted-foreground">Turns</th>
190
- <th className="border-b border-border px-3 py-2 text-left font-medium text-muted-foreground">Recall</th>
191
- <th className="border-b border-border px-3 py-2 text-left font-medium text-muted-foreground">Epochs</th>
192
- <th className="border-b border-border px-3 py-2 text-left font-medium text-muted-foreground">Avg ctx%</th>
193
- <th className="border-b border-border px-3 py-2 text-left font-medium text-muted-foreground">Last turn</th>
194
- <th className="border-b border-border px-3 py-2"></th>
195
- </tr>
196
- </thead>
197
- <tbody>
198
- {turns.conversations.map((c: ConversationSummary) => (
199
- <ConversationRow
200
- key={c.conversationId}
201
- c={c}
202
- active={turns.activeConversationId === c.conversationId}
203
- expanded={expanded === c.conversationId}
204
- onExpand={() => expand(c.conversationId)}
205
- />
206
- ))}
207
- </tbody>
208
- </table>
209
- </CardContent>
210
- </Card>
211
-
212
- {expanded && (
213
- <Card>
214
- <CardHeader>
215
- <CardTitle>
216
- Turns in <code>{expanded}</code>
217
- </CardTitle>
218
- </CardHeader>
219
- <CardContent>
220
- {detailLoading ? (
221
- <div className="text-sm text-muted-foreground">Loading turns…</div>
222
- ) : detail ? (
223
- <TurnDetail
224
- detail={detail}
225
- busy={busy}
226
- onFork={onFork}
227
- onRewind={onRewind}
228
- />
229
- ) : (
230
- <div className="text-sm text-muted-foreground">No detail.</div>
231
- )}
232
- </CardContent>
233
- </Card>
234
- )}
235
- </div>
236
- );
237
- }
238
-
239
- function ConversationRow({
240
- c,
241
- active,
242
- expanded,
243
- onExpand,
244
- }: {
245
- c: ConversationSummary;
246
- active: boolean;
247
- expanded: boolean;
248
- onExpand: () => void;
249
- }): React.ReactElement {
250
- return (
251
- <>
252
- <tr
253
- className={`border-b border-border/50 hover:bg-bg-elevated/40 ${
254
- active ? "bg-primary/10" : ""
255
- }`}
256
- onClick={onExpand}
257
- style={{ cursor: "pointer" }}
258
- >
259
- <td className="px-3 py-2">
260
- {expanded ? "▾" : "▸"} <code>{c.conversationId}</code>
261
- {active && <Badge variant="accent" className="ml-2">active</Badge>}
262
- </td>
263
- <td className="px-3 py-2 text-right">{c.turnCount}</td>
264
- <td className="px-3 py-2 text-right">{c.totalRecall}</td>
265
- <td className="px-3 py-2 text-right">{c.epochCount}</td>
266
- <td className="px-3 py-2 text-right">{c.avgCtxPercent.toFixed(0)}%</td>
267
- <td className="px-3 py-2">{fmtTs(c.lastTurnAt)}</td>
268
- <td className="px-3 py-2" />
269
- </tr>
270
- </>
271
- );
272
- }
273
-
274
- function TurnDetail({
275
- detail,
276
- busy,
277
- onFork,
278
- onRewind,
279
- }: {
280
- detail: ConversationTurnsResponse;
281
- busy: string | null;
282
- onFork: (conversationId: string, turnIndex: number) => void;
283
- onRewind: (conversationId: string, turnIndex: number) => void;
284
- }): React.ReactElement {
285
- return (
286
- <table className="w-full border-collapse text-sm">
287
- <thead>
288
- <tr>
289
- <th className="border-b border-border px-3 py-2 text-left font-medium text-muted-foreground">#</th>
290
- <th className="border-b border-border px-3 py-2 text-left font-medium text-muted-foreground">Role</th>
291
- <th className="border-b border-border px-3 py-2 text-left font-medium text-muted-foreground">Ctx</th>
292
- <th className="border-b border-border px-3 py-2 text-left font-medium text-muted-foreground">Band</th>
293
- <th className="border-b border-border px-3 py-2 text-left font-medium text-muted-foreground">Epoch</th>
294
- <th className="border-b border-border px-3 py-2 text-left font-medium text-muted-foreground">Recalled checkpoints (memory recall)</th>
295
- <th className="border-b border-border px-3 py-2 text-left font-medium text-muted-foreground">HyDE</th>
296
- <th className="border-b border-border px-3 py-2 text-left font-medium text-muted-foreground">Ended</th>
297
- <th className="border-b border-border px-3 py-2 text-left font-medium text-muted-foreground">Actions</th>
298
- </tr>
299
- </thead>
300
- <tbody>
301
- {detail.turns.map((t) => (
302
- <tr
303
- key={`${t.conversationId}:${t.turnIndex}`}
304
- className="border-b border-border/50 hover:bg-bg-elevated/40"
305
- >
306
- <td className="px-3 py-2 text-right">{t.turnIndex}</td>
307
- <td className="px-3 py-2">{t.role}</td>
308
- <td className="px-3 py-2">
309
- {t.ctxTokens ?? "—"}
310
- {t.ctxPercent != null && (
311
- <span className="text-muted-foreground">
312
- {" "}
313
- · {t.ctxPercent.toFixed(0)}%
314
- </span>
315
- )}
316
- </td>
317
- <td className="px-3 py-2">
318
- <Badge variant={bandVariant(t.pressureBand)}>
319
- {t.pressureBand ?? "—"}
320
- </Badge>
321
- </td>
322
- <td className="px-3 py-2">{t.epochId ? <code>{t.epochId.slice(0, 8)}</code> : "—"}</td>
323
- <td className="px-3 py-2">
324
- {t.recall.length === 0 ? (
325
- <span className="text-muted-foreground">—</span>
326
- ) : (
327
- <ul className="space-y-1">
328
- {t.recall.map((r) => (
329
- <li key={r.checkpointId}>
330
- <code>{r.checkpointId.slice(0, 12)}</code>{" "}
331
- <span className="text-muted-foreground">
332
- {sourceLabel(r.source)} · {r.score.toFixed(2)}
333
- {r.raptorLevel != null ? ` · L${r.raptorLevel}` : ""}
334
- </span>
335
- </li>
336
- ))}
337
- </ul>
338
- )}
339
- </td>
340
- <td className="px-3 py-2 align-top">
341
- <HydeDetailPanel
342
- conversationId={t.conversationId}
343
- turnIndex={t.turnIndex}
344
- />
345
- </td>
346
- <td className="px-3 py-2">{fmtTs(t.endedAt)}</td>
347
- <td className="px-3 py-2">
348
- <div className="flex gap-2">
349
- <Button
350
- type="button"
351
- variant="outline"
352
- size="sm"
353
- disabled={busy !== null}
354
- onClick={() => onFork(t.conversationId, t.turnIndex)}
355
- >
356
- fork
357
- </Button>
358
- <Button
359
- type="button"
360
- variant="outline"
361
- size="sm"
362
- disabled={busy !== null}
363
- onClick={() => onRewind(t.conversationId, t.turnIndex)}
364
- >
365
- rewind
366
- </Button>
367
- </div>
368
- </td>
369
- </tr>
370
- ))}
371
- </tbody>
372
- </table>
373
- );
16
+ return <TurnMemoryView />;
374
17
  }