pi-mega-compact 0.10.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extensions/dashboard-server/routes-turns.js +22 -9
- package/dist/extensions/mega-events/session-handlers.js +1 -1
- package/extensions/dashboard-client/dist/assets/{AchievementTiles-DUulvXUY.js → AchievementTiles-DZp_xQOW.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AchievementTiles-DUulvXUY.js.map → AchievementTiles-DZp_xQOW.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{AchievementsTab-Dx0YtCNf.js → AchievementsTab-BzFHpAHM.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AchievementsTab-Dx0YtCNf.js.map → AchievementsTab-BzFHpAHM.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-DtkaUo1v.js → CacheTab-DEQbTq2T.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-DtkaUo1v.js.map → CacheTab-DEQbTq2T.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ConfigTab-CLa4KfiZ.js → ConfigTab-DQ4FG33J.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ConfigTab-CLa4KfiZ.js.map → ConfigTab-DQ4FG33J.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-Q6tEIBBi.js → EventsTab-D5HoEVYo.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-Q6tEIBBi.js.map → EventsTab-D5HoEVYo.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{GameTab-Hq7GNWSY.js → GameTab-Jzsc4yeY.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{GameTab-Hq7GNWSY.js.map → GameTab-Jzsc4yeY.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-BtmBV6lU.js → MetricsTab-D_XnsEYX.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-BtmBV6lU.js.map → MetricsTab-D_XnsEYX.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-CL10PwVU.js → OverviewTab-BMREvAth.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-CL10PwVU.js.map → OverviewTab-BMREvAth.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-DAwANwbI.js → ReposTab-DuyhOktG.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-DAwANwbI.js.map → ReposTab-DuyhOktG.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-Ec08TZRQ.js → SessionsTab-mz_vz8i2.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-Ec08TZRQ.js.map → SessionsTab-mz_vz8i2.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-COb8dpTy.js → TimeSavedCard-B8AIjNTY.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-COb8dpTy.js.map → TimeSavedCard-B8AIjNTY.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TopicsTab-kvX3F3dY.js → TopicsTab-DKuct4oD.js} +2 -2
- package/extensions/dashboard-client/dist/assets/TopicsTab-DKuct4oD.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{TurnsTab-CCUE73Aa.js → TurnsTab-11MGLxY8.js} +2 -2
- package/extensions/dashboard-client/dist/assets/TurnsTab-11MGLxY8.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{index-UMrussFX.js → index-CqLN7STl.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{index-UMrussFX.js.map → index-CqLN7STl.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-BDHZ7CeS.js → useSSE-CXrQSbVY.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-BDHZ7CeS.js.map → useSSE-CXrQSbVY.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/App.tsx +1 -1
- package/extensions/dashboard-client/src/api/client.ts +8 -2
- package/extensions/dashboard-client/src/tabs/TopicsTab.tsx +2 -1
- package/extensions/dashboard-client/src/tabs/TurnsTab.tsx +61 -51
- package/extensions/dashboard-server/api-contracts/endpoints.ts +10 -5
- package/extensions/dashboard-server/routes-turns.ts +38 -15
- package/extensions/mega-events/session-handlers.ts +3 -5
- package/package.json +1 -1
- package/extensions/dashboard-client/dist/assets/TopicsTab-kvX3F3dY.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/TurnsTab-CCUE73Aa.js.map +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as e}from"./index-
|
|
2
|
-
//# sourceMappingURL=useSSE-
|
|
1
|
+
import{r as e}from"./index-CqLN7STl.js";function R(i={}){const{maxEvents:c=500,maxReconnects:l=5}=i,[f,v]=e.useState([]),[E,n]=e.useState("connecting"),[S,p]=e.useState(0),[m,d]=e.useState(null),o=e.useRef(0),s=e.useRef(null),r=e.useCallback(()=>{s.current&&s.current.close(),n("connecting");const t=new EventSource("/api/events");s.current=t,t.onopen=()=>{n("connected"),o.current=0},t.onmessage=g=>{try{const x=JSON.parse(g.data);v(u=>{const a=[...u,x];return a.length>c?a.slice(-c):a}),p(u=>u+1),d(Date.now())}catch{}},t.onerror=()=>{t.close(),o.current<l?(o.current+=1,n("connecting"),setTimeout(r,1e3)):n("disconnected")}},[c,l]);return e.useEffect(()=>(r(),()=>{s.current?.close()}),[r]),{events:f,status:E,eventCount:S,lastEventAt:m}}export{R as u};
|
|
2
|
+
//# sourceMappingURL=useSSE-CXrQSbVY.js.map
|
package/extensions/dashboard-client/dist/assets/{useSSE-BDHZ7CeS.js.map → useSSE-CXrQSbVY.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSSE-
|
|
1
|
+
{"version":3,"file":"useSSE-CXrQSbVY.js","sources":["../../src/hooks/useSSE.ts"],"sourcesContent":["/**\n * dashboard-client/src/hooks/useSSE.ts — Server-Sent Events hook.\n *\n * Connects to /api/events, provides real-time event stream.\n * SPRINT-B1: basic connection. SPRINT-D1: reconnect with backoff.\n */\n\nimport { useState, useEffect, useRef, useCallback } from 'react';\nimport type { SseEvent } from '../../../dashboard-server/api-contracts';\n\nexport type SseStatus = 'connecting' | 'connected' | 'disconnected' | 'error';\n\nexport interface UseSSEResult {\n events: SseEvent[];\n status: SseStatus;\n eventCount: number;\n lastEventAt: number | null;\n}\n\nexport interface UseSSEOptions {\n /** Max events to keep in memory (ring buffer). */\n maxEvents?: number;\n /** Max reconnect attempts before giving up. */\n maxReconnects?: number;\n}\n\n// SPRINT-D1-REMAINING: exponential backoff (1s → 2s → 4s → max 30s).\n// SPRINT-D1-REMAINING: after 5 failures, set status='disconnected'.\n\nexport function useSSE(options: UseSSEOptions = {}): UseSSEResult {\n const { maxEvents = 500, maxReconnects = 5 } = options;\n const [events, setEvents] = useState<SseEvent[]>([]);\n const [status, setStatus] = useState<SseStatus>('connecting');\n const [eventCount, setEventCount] = useState(0);\n const [lastEventAt, setLastEventAt] = useState<number | null>(null);\n const reconnectCount = useRef(0);\n const sourceRef = useRef<EventSource | null>(null);\n\n const connect = useCallback(() => {\n if (sourceRef.current) {\n sourceRef.current.close();\n }\n setStatus('connecting');\n const source = new EventSource('/api/events');\n sourceRef.current = source;\n\n source.onopen = () => {\n setStatus('connected');\n reconnectCount.current = 0;\n };\n\n source.onmessage = (event) => {\n try {\n const parsed = JSON.parse(event.data) as SseEvent;\n setEvents(prev => {\n const next = [...prev, parsed];\n return next.length > maxEvents ? next.slice(-maxEvents) : next;\n });\n setEventCount(c => c + 1);\n setLastEventAt(Date.now());\n } catch {\n // non-fatal: skip malformed events\n }\n };\n\n source.onerror = () => {\n source.close();\n if (reconnectCount.current < maxReconnects) {\n reconnectCount.current += 1;\n setStatus('connecting');\n // SPRINT-D1: use exponential backoff instead of fixed delay\n setTimeout(connect, 1000);\n } else {\n setStatus('disconnected');\n }\n };\n }, [maxEvents, maxReconnects]);\n\n useEffect(() => {\n connect();\n return () => {\n sourceRef.current?.close();\n };\n }, [connect]);\n\n return { events, status, eventCount, lastEventAt };\n}\n"],"names":["useSSE","options","maxEvents","maxReconnects","events","setEvents","useState","status","setStatus","eventCount","setEventCount","lastEventAt","setLastEventAt","reconnectCount","useRef","sourceRef","connect","useCallback","source","event","parsed","prev","next","c","useEffect"],"mappings":"wCA6BO,SAASA,EAAOC,EAAyB,GAAkB,CAChE,KAAM,CAAE,UAAAC,EAAY,IAAK,cAAAC,EAAgB,GAAMF,EACzC,CAACG,EAAQC,CAAS,EAAIC,EAAAA,SAAqB,CAAA,CAAE,EAC7C,CAACC,EAAQC,CAAS,EAAIF,EAAAA,SAAoB,YAAY,EACtD,CAACG,EAAYC,CAAa,EAAIJ,EAAAA,SAAS,CAAC,EACxC,CAACK,EAAaC,CAAc,EAAIN,EAAAA,SAAwB,IAAI,EAC5DO,EAAiBC,EAAAA,OAAO,CAAC,EACzBC,EAAYD,EAAAA,OAA2B,IAAI,EAE3CE,EAAUC,EAAAA,YAAY,IAAM,CAC5BF,EAAU,SACZA,EAAU,QAAQ,MAAA,EAEpBP,EAAU,YAAY,EACtB,MAAMU,EAAS,IAAI,YAAY,aAAa,EAC5CH,EAAU,QAAUG,EAEpBA,EAAO,OAAS,IAAM,CACpBV,EAAU,WAAW,EACrBK,EAAe,QAAU,CAC3B,EAEAK,EAAO,UAAaC,GAAU,CAC5B,GAAI,CACF,MAAMC,EAAS,KAAK,MAAMD,EAAM,IAAI,EACpCd,EAAUgB,GAAQ,CAChB,MAAMC,EAAO,CAAC,GAAGD,EAAMD,CAAM,EAC7B,OAAOE,EAAK,OAASpB,EAAYoB,EAAK,MAAM,CAACpB,CAAS,EAAIoB,CAC5D,CAAC,EACDZ,EAAca,GAAKA,EAAI,CAAC,EACxBX,EAAe,KAAK,KAAK,CAC3B,MAAQ,CAER,CACF,EAEAM,EAAO,QAAU,IAAM,CACrBA,EAAO,MAAA,EACHL,EAAe,QAAUV,GAC3BU,EAAe,SAAW,EAC1BL,EAAU,YAAY,EAEtB,WAAWQ,EAAS,GAAI,GAExBR,EAAU,cAAc,CAE5B,CACF,EAAG,CAACN,EAAWC,CAAa,CAAC,EAE7BqB,OAAAA,EAAAA,UAAU,KACRR,EAAA,EACO,IAAM,CACXD,EAAU,SAAS,MAAA,CACrB,GACC,CAACC,CAAO,CAAC,EAEL,CAAE,OAAAZ,EAAQ,OAAAG,EAAQ,WAAAE,EAAY,YAAAE,CAAA,CACvC"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>mega-compact dashboard</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/index-
|
|
7
|
+
<script type="module" crossorigin src="./assets/index-CqLN7STl.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="./assets/index-gn66lYD2.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
@@ -99,7 +99,7 @@ export default function App(): React.ReactElement {
|
|
|
99
99
|
{activeTab === "achievements" && <AchievementsTab />}
|
|
100
100
|
{activeTab === "sessions" && <SessionsTab />}
|
|
101
101
|
{activeTab === "topics" && <TopicsTab />}
|
|
102
|
-
|
|
102
|
+
{activeTab === "turns" && <TurnsTab />}
|
|
103
103
|
</React.Suspense>
|
|
104
104
|
</main>
|
|
105
105
|
</div>
|
|
@@ -218,7 +218,10 @@ export function postFork(
|
|
|
218
218
|
conversationId: string,
|
|
219
219
|
turnIndex: number,
|
|
220
220
|
): Promise<ForkResponse> {
|
|
221
|
-
return postJson<ForkResponse>(ENDPOINTS.fork.path, {
|
|
221
|
+
return postJson<ForkResponse>(ENDPOINTS.fork.path, {
|
|
222
|
+
conversationId,
|
|
223
|
+
turnIndex,
|
|
224
|
+
});
|
|
222
225
|
}
|
|
223
226
|
|
|
224
227
|
export function postPruneTurns(
|
|
@@ -235,6 +238,9 @@ export function fetchTopicMemories(
|
|
|
235
238
|
topicId: string,
|
|
236
239
|
): Promise<TopicMemoriesResponse> {
|
|
237
240
|
return getJson<TopicMemoriesResponse>(
|
|
238
|
-
ENDPOINTS.topicMemories.path.replace(
|
|
241
|
+
ENDPOINTS.topicMemories.path.replace(
|
|
242
|
+
":topicId",
|
|
243
|
+
encodeURIComponent(topicId),
|
|
244
|
+
),
|
|
239
245
|
);
|
|
240
246
|
}
|
|
@@ -28,7 +28,8 @@ export default function TopicsTab(): React.ReactElement {
|
|
|
28
28
|
|
|
29
29
|
const drill = useApi<TopicMemoriesResponse>(
|
|
30
30
|
useCallback(
|
|
31
|
-
() =>
|
|
31
|
+
() =>
|
|
32
|
+
drillTopicId ? fetchTopicMemories(drillTopicId) : Promise.reject(),
|
|
32
33
|
[drillTopicId],
|
|
33
34
|
),
|
|
34
35
|
{ pollInterval: 0 },
|
|
@@ -54,9 +54,12 @@ export default function TurnsTab(): React.ReactElement {
|
|
|
54
54
|
data: turns,
|
|
55
55
|
loading,
|
|
56
56
|
error,
|
|
57
|
-
} = useApi<TurnsResponse>(
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
} = useApi<TurnsResponse>(
|
|
58
|
+
useCallback(() => fetchTurns(), []),
|
|
59
|
+
{
|
|
60
|
+
pollInterval: 10_000,
|
|
61
|
+
},
|
|
62
|
+
);
|
|
60
63
|
const { data: intents } = useApi<RewindIntentsResponse>(
|
|
61
64
|
useCallback(() => fetchTurnIntents(), []),
|
|
62
65
|
{ pollInterval: 5_000 },
|
|
@@ -67,22 +70,25 @@ export default function TurnsTab(): React.ReactElement {
|
|
|
67
70
|
const [busy, setBusy] = useState<string | null>(null);
|
|
68
71
|
const [notice, setNotice] = useState<string | null>(null);
|
|
69
72
|
|
|
70
|
-
const expand = useCallback(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
const expand = useCallback(
|
|
74
|
+
async (conversationId: string) => {
|
|
75
|
+
if (expanded === conversationId) {
|
|
76
|
+
setExpanded(null);
|
|
77
|
+
setDetail(null);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
setExpanded(conversationId);
|
|
81
|
+
setDetailLoading(true);
|
|
73
82
|
setDetail(null);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
setDetailLoading(false);
|
|
84
|
-
}
|
|
85
|
-
}, [expanded]);
|
|
83
|
+
try {
|
|
84
|
+
const d = await fetchConversationTurns(conversationId);
|
|
85
|
+
setDetail(d);
|
|
86
|
+
} finally {
|
|
87
|
+
setDetailLoading(false);
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
[expanded],
|
|
91
|
+
);
|
|
86
92
|
|
|
87
93
|
const onFork = useCallback(
|
|
88
94
|
async (conversationId: string, turnIndex: number) => {
|
|
@@ -102,18 +108,21 @@ export default function TurnsTab(): React.ReactElement {
|
|
|
102
108
|
[],
|
|
103
109
|
);
|
|
104
110
|
|
|
105
|
-
const onRewind = useCallback(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
111
|
+
const onRewind = useCallback(
|
|
112
|
+
async (conversationId: string, turnIndex: number) => {
|
|
113
|
+
setBusy(`rewind:${conversationId}:${turnIndex}`);
|
|
114
|
+
setNotice(null);
|
|
115
|
+
try {
|
|
116
|
+
await postTurnIntent(conversationId, turnIndex);
|
|
117
|
+
setNotice(`Rewind intent queued for turn ${turnIndex}`);
|
|
118
|
+
} catch (e) {
|
|
119
|
+
setNotice(`Rewind failed: ${(e as Error).message}`);
|
|
120
|
+
} finally {
|
|
121
|
+
setBusy(null);
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
[],
|
|
125
|
+
);
|
|
117
126
|
|
|
118
127
|
if (error && !turns) {
|
|
119
128
|
return <div className="tab-stub">Error loading turns: {error.message}</div>;
|
|
@@ -146,8 +155,8 @@ export default function TurnsTab(): React.ReactElement {
|
|
|
146
155
|
<ul>
|
|
147
156
|
{intents.intents.map((i) => (
|
|
148
157
|
<li key={i.id}>
|
|
149
|
-
{fmtTs(i.createdAt)} — rewind <code>{i.conversationId}</code>
|
|
150
|
-
|
|
158
|
+
{fmtTs(i.createdAt)} — rewind <code>{i.conversationId}</code> to
|
|
159
|
+
turn {i.targetTurnIndex}
|
|
151
160
|
</li>
|
|
152
161
|
))}
|
|
153
162
|
</ul>
|
|
@@ -184,21 +193,21 @@ export default function TurnsTab(): React.ReactElement {
|
|
|
184
193
|
|
|
185
194
|
{expanded && (
|
|
186
195
|
<section className="turns-detail">
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
196
|
+
<h4>
|
|
197
|
+
Turns in <code>{expanded}</code>
|
|
198
|
+
</h4>
|
|
199
|
+
{detailLoading ? (
|
|
200
|
+
<div className="tab-stub">Loading turns…</div>
|
|
201
|
+
) : detail ? (
|
|
202
|
+
<TurnDetail
|
|
203
|
+
detail={detail}
|
|
204
|
+
busy={busy}
|
|
205
|
+
onFork={onFork}
|
|
206
|
+
onRewind={onRewind}
|
|
207
|
+
/>
|
|
208
|
+
) : (
|
|
209
|
+
<div className="tab-stub">No detail.</div>
|
|
210
|
+
)}
|
|
202
211
|
</section>
|
|
203
212
|
)}
|
|
204
213
|
</div>
|
|
@@ -271,7 +280,10 @@ function TurnDetail({
|
|
|
271
280
|
<td>
|
|
272
281
|
{t.ctxTokens ?? "—"}
|
|
273
282
|
{t.ctxPercent != null && (
|
|
274
|
-
<span className="muted">
|
|
283
|
+
<span className="muted">
|
|
284
|
+
{" "}
|
|
285
|
+
· {(t.ctxPercent * 100).toFixed(0)}%
|
|
286
|
+
</span>
|
|
275
287
|
)}
|
|
276
288
|
</td>
|
|
277
289
|
<td>
|
|
@@ -279,9 +291,7 @@ function TurnDetail({
|
|
|
279
291
|
{t.pressureBand ?? "—"}
|
|
280
292
|
</span>
|
|
281
293
|
</td>
|
|
282
|
-
<td>
|
|
283
|
-
{t.epochId ? <code>{t.epochId.slice(0, 8)}</code> : "—"}
|
|
284
|
-
</td>
|
|
294
|
+
<td>{t.epochId ? <code>{t.epochId.slice(0, 8)}</code> : "—"}</td>
|
|
285
295
|
<td className="recall-cell">
|
|
286
296
|
{t.recall.length === 0 ? (
|
|
287
297
|
<span className="muted">—</span>
|
|
@@ -511,35 +511,40 @@ export const ENDPOINTS = {
|
|
|
511
511
|
conversationTurns: {
|
|
512
512
|
method: "GET",
|
|
513
513
|
path: "/api/turns/conversation/:convId",
|
|
514
|
-
description:
|
|
514
|
+
description:
|
|
515
|
+
"Per-turn metrics + injected-checkpoint recall provenance for one conversation.",
|
|
515
516
|
} as const satisfies EndpointDef<"GET", undefined, ConversationTurnsResponse>,
|
|
516
517
|
|
|
517
518
|
/** GET /api/turns/intents — pending rewind intents (S52A). */
|
|
518
519
|
turnIntents: {
|
|
519
520
|
method: "GET",
|
|
520
521
|
path: "/api/turns/intents",
|
|
521
|
-
description:
|
|
522
|
+
description:
|
|
523
|
+
"Pending rewind intents queued by the dashboard for the host to consume.",
|
|
522
524
|
} as const satisfies EndpointDef<"GET", undefined, RewindIntentsResponse>,
|
|
523
525
|
|
|
524
526
|
/** POST /api/turns/intent — post a rewind intent. */
|
|
525
527
|
postTurnIntent: {
|
|
526
528
|
method: "POST",
|
|
527
529
|
path: "/api/turns/intent",
|
|
528
|
-
description:
|
|
530
|
+
description:
|
|
531
|
+
"Queue a rewind-to-turn-N intent for the host to apply at before_agent_start.",
|
|
529
532
|
} as const satisfies EndpointDef<"POST", PostIntentRequest, unknown>,
|
|
530
533
|
|
|
531
534
|
/** POST /api/fork — fork a conversation at a turn. */
|
|
532
535
|
fork: {
|
|
533
536
|
method: "POST",
|
|
534
537
|
path: "/api/fork",
|
|
535
|
-
description:
|
|
538
|
+
description:
|
|
539
|
+
"Branch a child conversation off a parent turn + return the recall set to rehydrate.",
|
|
536
540
|
} as const satisfies EndpointDef<"POST", ForkRequest, ForkResponse>,
|
|
537
541
|
|
|
538
542
|
/** POST /api/turns/prune — admin prune (capability-gated). */
|
|
539
543
|
pruneTurns: {
|
|
540
544
|
method: "POST",
|
|
541
545
|
path: "/api/turns/prune",
|
|
542
|
-
description:
|
|
546
|
+
description:
|
|
547
|
+
"Prune old turns (admin capability — dashboard uses asAdmin()).",
|
|
543
548
|
} as const satisfies EndpointDef<"POST", PruneRequest, PruneTurnsResponse>,
|
|
544
549
|
|
|
545
550
|
/** GET /api/topics/:topicId/memories — wiki topic drill-down (S52). */
|
|
@@ -41,7 +41,11 @@ function sendJson(res: ServerResponse, status: number, body: unknown): void {
|
|
|
41
41
|
/** Read a capped JSON body; returns { ok, value } or { error }. */
|
|
42
42
|
function readJsonBody(
|
|
43
43
|
req: IncomingMessage,
|
|
44
|
-
cb: (
|
|
44
|
+
cb: (
|
|
45
|
+
result:
|
|
46
|
+
| { ok: true; value: Record<string, unknown> }
|
|
47
|
+
| { ok: false; error: string },
|
|
48
|
+
) => void,
|
|
45
49
|
): void {
|
|
46
50
|
let body = "";
|
|
47
51
|
let tooBig = false;
|
|
@@ -122,7 +126,10 @@ export function handleTurns(
|
|
|
122
126
|
activeConversationId = c.conversationId;
|
|
123
127
|
}
|
|
124
128
|
}
|
|
125
|
-
sendJson(res, 200, {
|
|
129
|
+
sendJson(res, 200, {
|
|
130
|
+
conversations,
|
|
131
|
+
activeConversationId,
|
|
132
|
+
} satisfies TurnsResponse);
|
|
126
133
|
return true;
|
|
127
134
|
} catch (e) {
|
|
128
135
|
sendJson(res, 500, { error: String(e) });
|
|
@@ -152,16 +159,19 @@ export function handleTurns(
|
|
|
152
159
|
pressureBand: e.pressureBand ?? null,
|
|
153
160
|
model: e.model ?? null,
|
|
154
161
|
epochId: e.epochId ?? null,
|
|
155
|
-
recall: reader
|
|
156
|
-
|
|
157
|
-
.map((r): RecallHit => ({
|
|
162
|
+
recall: reader.listRecallByIndex(convId, e.turnIndex).map(
|
|
163
|
+
(r): RecallHit => ({
|
|
158
164
|
checkpointId: r.checkpointId,
|
|
159
165
|
score: r.score,
|
|
160
166
|
source: r.source,
|
|
161
167
|
raptorLevel: r.raptorLevel ?? null,
|
|
162
|
-
})
|
|
168
|
+
}),
|
|
169
|
+
),
|
|
163
170
|
}));
|
|
164
|
-
sendJson(res, 200, {
|
|
171
|
+
sendJson(res, 200, {
|
|
172
|
+
conversationId: convId,
|
|
173
|
+
turns,
|
|
174
|
+
} satisfies ConversationTurnsResponse);
|
|
165
175
|
return true;
|
|
166
176
|
} catch (e) {
|
|
167
177
|
sendJson(res, 500, { error: String(e) });
|
|
@@ -194,15 +204,22 @@ export function handleTurns(
|
|
|
194
204
|
readJsonBody(req, (result) => {
|
|
195
205
|
if (!result.ok) return sendJson(res, 400, { error: result.error });
|
|
196
206
|
const v = result.value;
|
|
197
|
-
const conversationId =
|
|
198
|
-
|
|
207
|
+
const conversationId =
|
|
208
|
+
typeof v.conversationId === "string" ? v.conversationId : "";
|
|
209
|
+
const targetTurnIndex =
|
|
210
|
+
typeof v.targetTurnIndex === "number" ? v.targetTurnIndex : -1;
|
|
199
211
|
if (!conversationId || targetTurnIndex < 0) {
|
|
200
|
-
return sendJson(res, 400, {
|
|
212
|
+
return sendJson(res, 400, {
|
|
213
|
+
error: "missing_conversationId_or_targetTurnIndex",
|
|
214
|
+
});
|
|
201
215
|
}
|
|
202
216
|
try {
|
|
203
217
|
const tdb = openTurnStore(ctx.stateDir);
|
|
204
218
|
const q = openIntentQueue(tdb);
|
|
205
|
-
const intent = q.postIntent({
|
|
219
|
+
const intent = q.postIntent({
|
|
220
|
+
conversationId,
|
|
221
|
+
targetTurnIndex,
|
|
222
|
+
} satisfies PostIntentRequest);
|
|
206
223
|
sendJson(res, 201, intent);
|
|
207
224
|
} catch (e) {
|
|
208
225
|
sendJson(res, 500, { error: String(e) });
|
|
@@ -216,10 +233,13 @@ export function handleTurns(
|
|
|
216
233
|
readJsonBody(req, (result) => {
|
|
217
234
|
if (!result.ok) return sendJson(res, 400, { error: result.error });
|
|
218
235
|
const v = result.value;
|
|
219
|
-
const conversationId =
|
|
236
|
+
const conversationId =
|
|
237
|
+
typeof v.conversationId === "string" ? v.conversationId : "";
|
|
220
238
|
const turnIndex = typeof v.turnIndex === "number" ? v.turnIndex : -1;
|
|
221
239
|
if (!conversationId || turnIndex < 0) {
|
|
222
|
-
return sendJson(res, 400, {
|
|
240
|
+
return sendJson(res, 400, {
|
|
241
|
+
error: "missing_conversationId_or_turnIndex",
|
|
242
|
+
});
|
|
223
243
|
}
|
|
224
244
|
try {
|
|
225
245
|
const store = createTurnStore({ stateDir: ctx.stateDir });
|
|
@@ -241,9 +261,12 @@ export function handleTurns(
|
|
|
241
261
|
readJsonBody(req, (result) => {
|
|
242
262
|
if (!result.ok) return sendJson(res, 400, { error: result.error });
|
|
243
263
|
const v = result.value;
|
|
244
|
-
const maxTurnAgeMs =
|
|
264
|
+
const maxTurnAgeMs =
|
|
265
|
+
typeof v.maxTurnAgeMs === "number" ? v.maxTurnAgeMs : 0;
|
|
245
266
|
const keepMinPerConversation =
|
|
246
|
-
typeof v.keepMinPerConversation === "number"
|
|
267
|
+
typeof v.keepMinPerConversation === "number"
|
|
268
|
+
? v.keepMinPerConversation
|
|
269
|
+
: 50;
|
|
247
270
|
if (maxTurnAgeMs <= 0) {
|
|
248
271
|
return sendJson(res, 400, { error: "invalid_maxTurnAgeMs" });
|
|
249
272
|
}
|
|
@@ -12,10 +12,7 @@ import {
|
|
|
12
12
|
recentUserQuery,
|
|
13
13
|
WIDGET_KEY,
|
|
14
14
|
} from "../mega-runtime.js";
|
|
15
|
-
import {
|
|
16
|
-
doRecall,
|
|
17
|
-
doRecallAsync,
|
|
18
|
-
} from "../mega-pipeline.js";
|
|
15
|
+
import { doRecall, doRecallAsync } from "../mega-pipeline.js";
|
|
19
16
|
import { recallMemoriesAndInline } from "../../src/recall.js";
|
|
20
17
|
import { vectorStats } from "../../src/vectorStore.js";
|
|
21
18
|
import { openTurnStore } from "../../src/store/turns/connection.js";
|
|
@@ -98,7 +95,8 @@ export function registerSessionHandlers(
|
|
|
98
95
|
// one-line summary for diagnostics.
|
|
99
96
|
try {
|
|
100
97
|
const m = autoMaintain(runtime.currentStateDir);
|
|
101
|
-
if (m && !m.endsWith("nothing to do"))
|
|
98
|
+
if (m && !m.endsWith("nothing to do"))
|
|
99
|
+
runtime.logger.info("db-auto-maintain", { result: m });
|
|
102
100
|
} catch (e) {
|
|
103
101
|
runtime.logger.warn("db-auto-maintain-fail", { error: String(e) });
|
|
104
102
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mega-compact",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
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",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TopicsTab-kvX3F3dY.js","sources":["../../src/tabs/TopicsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/TopicsTab.tsx — Auto-categorizing wiki (S51 + S52 polish).\n *\n * Fetches /api/topics and renders the k-means + TF-IDF topic clusters. S52 polish\n * adds: (1) a search box filtering topics by label / discriminative term, and\n * (2) a topic drill-down — click a topic to load its member memories\n * (/api/topics/:id/memories). Read-only; no write operations.\n */\n\nimport type React from \"react\";\nimport { useCallback, useMemo, useState } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchTopics, fetchTopicMemories } from \"../api/client\";\nimport type { TopicsResponse, TopicMemoriesResponse } from \"@contracts\";\n\nfunction fmtTs(ms: number | null): string {\n\tif (!ms) return \"—\";\n\treturn new Date(ms).toLocaleString();\n}\n\nexport default function TopicsTab(): React.ReactElement {\n\tconst { data, loading, error } = useApi<TopicsResponse>(\n\t\tuseCallback(() => fetchTopics(), []),\n\t\t{ pollInterval: 30_000 },\n\t);\n\tconst [query, setQuery] = useState(\"\");\n\tconst [drillTopicId, setDrillTopicId] = useState<string | null>(null);\n\n\tconst drill = useApi<TopicMemoriesResponse>(\n\t\tuseCallback(\n\t\t\t() => (drillTopicId ? fetchTopicMemories(drillTopicId) : Promise.reject()),\n\t\t\t[drillTopicId],\n\t\t),\n\t\t{ pollInterval: 0 },\n\t);\n\n\tconst filtered = useMemo(() => {\n\t\tif (!data) return [];\n\t\tconst q = query.trim().toLowerCase();\n\t\tif (!q) return data.topics;\n\t\treturn data.topics.filter(\n\t\t\t(t) =>\n\t\t\t\tt.label.toLowerCase().includes(q) ||\n\t\t\t\tt.termScore.some((s) => s.term.toLowerCase().includes(q)),\n\t\t);\n\t}, [data, query]);\n\n\tif (error && !data) {\n\t\treturn (\n\t\t\t<div className=\"tab-stub\">Error loading topics: {error.message}</div>\n\t\t);\n\t}\n\tif (loading && !data) {\n\t\treturn <div className=\"tab-stub\">Loading topics…</div>;\n\t}\n\tif (!data) {\n\t\treturn <div className=\"tab-stub\">No topic data available.</div>;\n\t}\n\n\tif (data.totalTopics === 0) {\n\t\treturn (\n\t\t\t<div className=\"tab-stub\">\n\t\t\t\t<h3>Wiki</h3>\n\t\t\t\t<p>\n\t\t\t\t\tNo topics yet. Topics are auto-generated after every 10th compaction\n\t\t\t\t\tfrom real memory embeddings (k-means + TF-IDF).\n\t\t\t\t</p>\n\t\t\t\t<p>Check back after a few more compaction cycles.</p>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"tab-content topics-tab\">\n\t\t\t<h3>Wiki</h3>\n\t\t\t<p className=\"subtitle\">\n\t\t\t\t{data.totalTopics} topic{data.totalTopics !== 1 ? \"s\" : \"\"} ·{\" \"}\n\t\t\t\t{data.totalAssigned} assigned memori\n\t\t\t\t{data.totalAssigned !== 1 ? \"es\" : \"y\"}\n\t\t\t\t{data.lastRebuildAt != null && (\n\t\t\t\t\t<> · last rebuild {new Date(data.lastRebuildAt).toLocaleString()}</>\n\t\t\t\t)}\n\t\t\t</p>\n\n\t\t\t<input\n\t\t\t\ttype=\"search\"\n\t\t\t\tclassName=\"topics-search\"\n\t\t\t\tplaceholder=\"Filter topics by label or term…\"\n\t\t\t\tvalue={query}\n\t\t\t\tonChange={(e) => setQuery(e.target.value)}\n\t\t\t/>\n\n\t\t\t{drillTopicId && (\n\t\t\t\t<section className=\"topic-drilldown\">\n\t\t\t\t\t<h4>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclassName=\"link-btn\"\n\t\t\t\t\t\t\tonClick={() => setDrillTopicId(null)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t← back\n\t\t\t\t\t\t</button>{\" \"}\n\t\t\t\t\t\tTopic <code>{drillTopicId}</code>\n\t\t\t\t\t\t{drill.data && <> · {drill.data.label}</>}\n\t\t\t\t\t\t{drill.data && <> · {drill.data.assignments.length} memories</>}\n\t\t\t\t\t</h4>\n\t\t\t\t\t{drill.error ? (\n\t\t\t\t\t\t<div className=\"tab-stub\">\n\t\t\t\t\t\t\tError: {(drill.error as Error).message}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : drill.loading && !drill.data ? (\n\t\t\t\t\t\t<div className=\"tab-stub\">Loading memories…</div>\n\t\t\t\t\t) : drill.data ? (\n\t\t\t\t\t\t<table className=\"data-table\">\n\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t<th>Memory id</th>\n\t\t\t\t\t\t\t\t\t<th>Confidence</th>\n\t\t\t\t\t\t\t\t\t<th>Assigned</th>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t{drill.data.assignments.map((a) => (\n\t\t\t\t\t\t\t\t\t<tr key={a.memoryId}>\n\t\t\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t\t\t<code>{a.memoryId}</code>\n\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t\t\t{a.confidence != null ? a.confidence.toFixed(2) : \"—\"}\n\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t<td>{fmtTs(a.assignedAt)}</td>\n\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t) : null}\n\t\t\t\t</section>\n\t\t\t)}\n\n\t\t\t{!drillTopicId && (\n\t\t\t\t<table className=\"data-table\">\n\t\t\t\t\t<thead>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<th>Label</th>\n\t\t\t\t\t\t\t<th>Memories</th>\n\t\t\t\t\t\t\t<th>Top Terms</th>\n\t\t\t\t\t\t\t<th></th>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</thead>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{filtered.map((t) => (\n\t\t\t\t\t\t\t<tr\n\t\t\t\t\t\t\t\tkey={t.id}\n\t\t\t\t\t\t\t\tclassName=\"topic-row\"\n\t\t\t\t\t\t\t\tonClick={() => setDrillTopicId(t.id)}\n\t\t\t\t\t\t\t\tstyle={{ cursor: \"pointer\" }}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<td className=\"topic-label\">{t.label}</td>\n\t\t\t\t\t\t\t\t<td className=\"topic-count\">{t.memoryCount}</td>\n\t\t\t\t\t\t\t\t<td className=\"topic-terms\">\n\t\t\t\t\t\t\t\t\t{t.termScore\n\t\t\t\t\t\t\t\t\t\t.slice(0, 8)\n\t\t\t\t\t\t\t\t\t\t.map((s) => s.term)\n\t\t\t\t\t\t\t\t\t\t.join(\", \")}\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t<button type=\"button\" className=\"mini-btn\">\n\t\t\t\t\t\t\t\t\t\tdrill\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t))}\n\t\t\t\t\t\t{filtered.length === 0 && (\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td colSpan={4} className=\"muted\">\n\t\t\t\t\t\t\t\t\tNo topics match “{query}”.\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"names":["fmtTs","ms","TopicsTab","data","loading","error","useApi","useCallback","fetchTopics","query","setQuery","useState","drillTopicId","setDrillTopicId","drill","fetchTopicMemories","filtered","useMemo","q","t","s","jsxs","jsx","Fragment","e","a"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TurnsTab-CCUE73Aa.js","sources":["../../src/tabs/TurnsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/TurnsTab.tsx — Turn-by-turn memory tracking + recall (S52).\n *\n * Fetches /api/turns (conversation list), expands the active conversation into\n * per-turn detail (/api/turns/conversation/:id), and renders:\n * - Conversation summary list (turn count, recall total, epochs, last turn).\n * - Per-turn rows: turn index, role, ctx tokens/percent + pressure band,\n * epoch id, and the injected-checkpoint recall set (the memory recalled\n * at each turn, with score + source).\n * - Pending rewind intents (S52A) + Fork action (S50C primitive).\n *\n * Read-mostly via /api/turns; fork/intent/prune via POST. No absolute URLs\n * (PREVENT-PI-004: loopback-only, same-origin static bundle).\n */\n\nimport type React from \"react\";\nimport { useCallback, useState } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport {\n\tfetchTurns,\n\tfetchConversationTurns,\n\tfetchTurnIntents,\n\tpostFork,\n\tpostTurnIntent,\n} from \"../api/client\";\nimport type {\n\tTurnsResponse,\n\tConversationTurnsResponse,\n\tRewindIntentsResponse,\n\tTurnRow,\n\tConversationSummary,\n} from \"@contracts\";\n\nfunction fmtTs(ms: number): string {\n\tif (!ms) return \"—\";\n\treturn new Date(ms).toLocaleString();\n}\n\nfunction bandClass(band: TurnRow[\"pressureBand\"]): string {\n\tif (band === \"green\") return \"band band-green\";\n\tif (band === \"yellow\") return \"band band-yellow\";\n\tif (band === \"red\") return \"band band-red\";\n\treturn \"band\";\n}\n\nfunction sourceLabel(s: TurnRow[\"recall\"][number][\"source\"]): string {\n\tif (s === \"checkpoint\") return \"ckpt\";\n\tif (s === \"cluster_summary\") return \"raptor\";\n\treturn \"mem\";\n}\n\nexport default function TurnsTab(): React.ReactElement {\n\tconst {\n\t\tdata: turns,\n\t\tloading,\n\t\terror,\n\t} = useApi<TurnsResponse>(useCallback(() => fetchTurns(), []), {\n\t\tpollInterval: 10_000,\n\t});\n\tconst { data: intents } = useApi<RewindIntentsResponse>(\n\t\tuseCallback(() => fetchTurnIntents(), []),\n\t\t{ pollInterval: 5_000 },\n\t);\n\tconst [expanded, setExpanded] = useState<string | null>(null);\n\tconst [detail, setDetail] = useState<ConversationTurnsResponse | null>(null);\n\tconst [detailLoading, setDetailLoading] = useState(false);\n\tconst [busy, setBusy] = useState<string | null>(null);\n\tconst [notice, setNotice] = useState<string | null>(null);\n\n\tconst expand = useCallback(async (conversationId: string) => {\n\t\tif (expanded === conversationId) {\n\t\t\tsetExpanded(null);\n\t\t\tsetDetail(null);\n\t\t\treturn;\n\t\t}\n\t\tsetExpanded(conversationId);\n\t\tsetDetailLoading(true);\n\t\tsetDetail(null);\n\t\ttry {\n\t\t\tconst d = await fetchConversationTurns(conversationId);\n\t\t\tsetDetail(d);\n\t\t} finally {\n\t\t\tsetDetailLoading(false);\n\t\t}\n\t}, [expanded]);\n\n\tconst onFork = useCallback(\n\t\tasync (conversationId: string, turnIndex: number) => {\n\t\t\tsetBusy(`fork:${conversationId}:${turnIndex}`);\n\t\t\tsetNotice(null);\n\t\t\ttry {\n\t\t\t\tconst out = await postFork(conversationId, turnIndex);\n\t\t\t\tsetNotice(\n\t\t\t\t\t`Forked → ${out.childConversationId} (${out.recalledCount} recalled)`,\n\t\t\t\t);\n\t\t\t} catch (e) {\n\t\t\t\tsetNotice(`Fork failed: ${(e as Error).message}`);\n\t\t\t} finally {\n\t\t\t\tsetBusy(null);\n\t\t\t}\n\t\t},\n\t\t[],\n\t);\n\n\tconst onRewind = useCallback(async (conversationId: string, turnIndex: number) => {\n\t\tsetBusy(`rewind:${conversationId}:${turnIndex}`);\n\t\tsetNotice(null);\n\t\ttry {\n\t\t\tawait postTurnIntent(conversationId, turnIndex);\n\t\t\tsetNotice(`Rewind intent queued for turn ${turnIndex}`);\n\t\t} catch (e) {\n\t\t\tsetNotice(`Rewind failed: ${(e as Error).message}`);\n\t\t} finally {\n\t\t\tsetBusy(null);\n\t\t}\n\t}, []);\n\n\tif (error && !turns) {\n\t\treturn <div className=\"tab-stub\">Error loading turns: {error.message}</div>;\n\t}\n\tif (loading && !turns) {\n\t\treturn <div className=\"tab-stub\">Loading turns…</div>;\n\t}\n\tif (!turns || turns.conversations.length === 0) {\n\t\treturn (\n\t\t\t<div className=\"tab-stub\">\n\t\t\t\t<h3>Turn-by-turn memory</h3>\n\t\t\t\t<p>\n\t\t\t\t\tNo turns recorded yet. Per-turn tracking starts when{\" \"}\n\t\t\t\t\t<code>MEGACOMPACT_TURNS_DB=1</code> and the session runs through a\n\t\t\t\t\tcompaction. Each turn records its context metrics + the checkpoints\n\t\t\t\t\trecalled into it.\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"turns-tab\">\n\t\t\t<h3>Turn-by-turn memory tracking + recall</h3>\n\t\t\t{notice && <div className=\"turns-notice\">{notice}</div>}\n\n\t\t\t{intents && intents.intents.length > 0 && (\n\t\t\t\t<section className=\"turns-intents\">\n\t\t\t\t\t<h4>Pending rewind intents ({intents.intents.length})</h4>\n\t\t\t\t\t<ul>\n\t\t\t\t\t\t{intents.intents.map((i) => (\n\t\t\t\t\t\t\t<li key={i.id}>\n\t\t\t\t\t\t\t\t{fmtTs(i.createdAt)} — rewind <code>{i.conversationId}</code>{\" \"}\n\t\t\t\t\t\t\t\tto turn {i.targetTurnIndex}\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</ul>\n\t\t\t\t</section>\n\t\t\t)}\n\n\t\t\t<section className=\"turns-conv-list\">\n\t\t\t\t<h4>Conversations ({turns.conversations.length})</h4>\n\t\t\t\t<table className=\"turns-table\">\n\t\t\t\t\t<thead>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<th>Conversation</th>\n\t\t\t\t\t\t\t<th>Turns</th>\n\t\t\t\t\t\t\t<th>Recall</th>\n\t\t\t\t\t\t\t<th>Epochs</th>\n\t\t\t\t\t\t\t<th>Avg ctx%</th>\n\t\t\t\t\t\t\t<th>Last turn</th>\n\t\t\t\t\t\t\t<th></th>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</thead>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{turns.conversations.map((c: ConversationSummary) => (\n\t\t\t\t\t\t\t<ConversationRow\n\t\t\t\t\t\t\t\tkey={c.conversationId}\n\t\t\t\t\t\t\t\tc={c}\n\t\t\t\t\t\t\t\tactive={turns.activeConversationId === c.conversationId}\n\t\t\t\t\t\t\t\texpanded={expanded === c.conversationId}\n\t\t\t\t\t\t\t\tonExpand={() => expand(c.conversationId)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t</section>\n\n\t\t\t{expanded && (\n\t\t\t\t<section className=\"turns-detail\">\n\t\t\t\t<h4>\n\t\t\t\t\tTurns in <code>{expanded}</code>\n\t\t\t\t</h4>\n\t\t\t\t{detailLoading ? (\n\t\t\t\t\t<div className=\"tab-stub\">Loading turns…</div>\n\t\t\t\t) : detail ? (\n\t\t\t\t\t<TurnDetail\n\t\t\t\t\t\tdetail={detail}\n\t\t\t\t\t\tbusy={busy}\n\t\t\t\t\t\tonFork={onFork}\n\t\t\t\t\t\tonRewind={onRewind}\n\t\t\t\t\t/>\n\t\t\t\t) : (\n\t\t\t\t\t<div className=\"tab-stub\">No detail.</div>\n\t\t\t\t)}\n\t\t\t\t</section>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nfunction ConversationRow({\n\tc,\n\tactive,\n\texpanded,\n\tonExpand,\n}: {\n\tc: ConversationSummary;\n\tactive: boolean;\n\texpanded: boolean;\n\tonExpand: () => void;\n}): React.ReactElement {\n\treturn (\n\t\t<>\n\t\t\t<tr\n\t\t\t\tclassName={active ? \"turns-row active\" : \"turns-row\"}\n\t\t\t\tonClick={onExpand}\n\t\t\t\tstyle={{ cursor: \"pointer\" }}\n\t\t\t>\n\t\t\t\t<td>\n\t\t\t\t\t{expanded ? \"▾\" : \"▸\"} <code>{c.conversationId}</code>\n\t\t\t\t\t{active && <span className=\"pill\">active</span>}\n\t\t\t\t</td>\n\t\t\t\t<td>{c.turnCount}</td>\n\t\t\t\t<td>{c.totalRecall}</td>\n\t\t\t\t<td>{c.epochCount}</td>\n\t\t\t\t<td>{(c.avgCtxPercent * 100).toFixed(0)}%</td>\n\t\t\t\t<td>{fmtTs(c.lastTurnAt)}</td>\n\t\t\t\t<td />\n\t\t\t</tr>\n\t\t</>\n\t);\n}\n\nfunction TurnDetail({\n\tdetail,\n\tbusy,\n\tonFork,\n\tonRewind,\n}: {\n\tdetail: ConversationTurnsResponse;\n\tbusy: string | null;\n\tonFork: (conversationId: string, turnIndex: number) => void;\n\tonRewind: (conversationId: string, turnIndex: number) => void;\n}): React.ReactElement {\n\treturn (\n\t\t<table className=\"turns-detail-table\">\n\t\t\t<thead>\n\t\t\t\t<tr>\n\t\t\t\t\t<th>#</th>\n\t\t\t\t\t<th>Role</th>\n\t\t\t\t\t<th>Ctx</th>\n\t\t\t\t\t<th>Band</th>\n\t\t\t\t\t<th>Epoch</th>\n\t\t\t\t\t<th>Recalled checkpoints (memory recall)</th>\n\t\t\t\t\t<th>Ended</th>\n\t\t\t\t\t<th>Actions</th>\n\t\t\t\t</tr>\n\t\t\t</thead>\n\t\t\t<tbody>\n\t\t\t\t{detail.turns.map((t) => (\n\t\t\t\t\t<tr key={`${t.conversationId}:${t.turnIndex}`}>\n\t\t\t\t\t\t<td>{t.turnIndex}</td>\n\t\t\t\t\t\t<td>{t.role}</td>\n\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t{t.ctxTokens ?? \"—\"}\n\t\t\t\t\t\t\t{t.ctxPercent != null && (\n\t\t\t\t\t\t\t\t<span className=\"muted\"> · {(t.ctxPercent * 100).toFixed(0)}%</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t<span className={bandClass(t.pressureBand)}>\n\t\t\t\t\t\t\t\t{t.pressureBand ?? \"—\"}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t{t.epochId ? <code>{t.epochId.slice(0, 8)}</code> : \"—\"}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td className=\"recall-cell\">\n\t\t\t\t\t\t\t{t.recall.length === 0 ? (\n\t\t\t\t\t\t\t\t<span className=\"muted\">—</span>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<ul className=\"recall-list\">\n\t\t\t\t\t\t\t\t\t{t.recall.map((r) => (\n\t\t\t\t\t\t\t\t\t\t<li key={r.checkpointId}>\n\t\t\t\t\t\t\t\t\t\t\t<code>{r.checkpointId.slice(0, 12)}</code>{\" \"}\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"muted\">\n\t\t\t\t\t\t\t\t\t\t\t\t{sourceLabel(r.source)} · {r.score.toFixed(2)}\n\t\t\t\t\t\t\t\t\t\t\t\t{r.raptorLevel != null ? ` · L${r.raptorLevel}` : \"\"}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td>{fmtTs(t.endedAt)}</td>\n\t\t\t\t\t\t<td className=\"actions-cell\">\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclassName=\"mini-btn\"\n\t\t\t\t\t\t\t\tdisabled={busy !== null}\n\t\t\t\t\t\t\t\tonClick={() => onFork(t.conversationId, t.turnIndex)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tfork\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclassName=\"mini-btn\"\n\t\t\t\t\t\t\t\tdisabled={busy !== null}\n\t\t\t\t\t\t\t\tonClick={() => onRewind(t.conversationId, t.turnIndex)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\trewind\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t))}\n\t\t\t</tbody>\n\t\t</table>\n\t);\n}\n"],"names":["fmtTs","ms","bandClass","band","sourceLabel","s","TurnsTab","turns","loading","error","useApi","useCallback","fetchTurns","intents","fetchTurnIntents","expanded","setExpanded","useState","detail","setDetail","detailLoading","setDetailLoading","busy","setBusy","notice","setNotice","expand","conversationId","d","fetchConversationTurns","onFork","turnIndex","out","postFork","e","onRewind","postTurnIntent","jsxs","jsx","i","c","ConversationRow","TurnDetail","active","onExpand","Fragment","t","r"],"mappings":"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"}
|