pi-mega-compact 0.9.2 → 0.10.1

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 (143) hide show
  1. package/README.md +3 -2
  2. package/dist/extensions/dashboard-server/api-contracts/endpoints.js +95 -47
  3. package/dist/extensions/dashboard-server/api-contracts/index.js +1 -1
  4. package/dist/extensions/dashboard-server/api-contracts/turns.js +10 -0
  5. package/dist/extensions/dashboard-server/api-contracts.test.js +3 -2
  6. package/dist/extensions/dashboard-server/routes-topics.js +87 -0
  7. package/dist/extensions/dashboard-server/routes-turns.js +251 -0
  8. package/dist/extensions/dashboard-server/routes.js +3 -1
  9. package/dist/extensions/dashboard-server/server.js +6 -2
  10. package/dist/extensions/mega-compact.js +10 -6
  11. package/dist/extensions/mega-config.js +4 -0
  12. package/dist/extensions/mega-events/agent-handlers.js +62 -49
  13. package/dist/extensions/mega-events/context-handler.js +48 -2
  14. package/dist/extensions/mega-events/session-handlers.js +24 -0
  15. package/dist/extensions/mega-metrics-cmds.js +90 -0
  16. package/dist/extensions/mega-pipeline/recall.js +47 -17
  17. package/dist/extensions/mega-topics-cmds.js +111 -0
  18. package/dist/extensions/mega-turn-store.js +110 -0
  19. package/dist/extensions/mega-turn-store.test.js +68 -0
  20. package/dist/src/config/turns.js +48 -0
  21. package/dist/src/fork.js +33 -0
  22. package/dist/src/fork.test.js +81 -0
  23. package/dist/src/intent.js +51 -0
  24. package/dist/src/intent.test.js +78 -0
  25. package/dist/src/metrics/index.js +4 -0
  26. package/dist/src/metrics/turns.js +118 -0
  27. package/dist/src/metrics/turns.test.js +152 -0
  28. package/dist/src/store/sqlite/raw-transcript.js +5 -3
  29. package/dist/src/store/sqlite.dbmirror.test.js +9 -0
  30. package/dist/src/store/turns/connection.js +123 -0
  31. package/dist/src/store/turns/contract-compliance.test.js +428 -0
  32. package/dist/src/store/turns/index.js +31 -0
  33. package/dist/src/store/turns/memory-store.js +357 -0
  34. package/dist/src/store/turns/memory-store.test.js +9 -0
  35. package/dist/src/store/turns/migrations.js +178 -0
  36. package/dist/src/store/turns/migrations.test.js +138 -0
  37. package/dist/src/store/turns/schema.js +115 -0
  38. package/dist/src/store/turns/sqlite-store.js +455 -0
  39. package/dist/src/store/turns/sqlite-store.test.js +144 -0
  40. package/dist/src/store/turns/types.js +16 -0
  41. package/dist/src/topics/cluster.js +141 -0
  42. package/dist/src/topics/cluster.test.js +150 -0
  43. package/dist/src/topics/index.js +6 -0
  44. package/dist/src/topics/kselection.js +142 -0
  45. package/dist/src/topics/labels.js +56 -0
  46. package/dist/src/topics/store.js +119 -0
  47. package/dist/src/topics/store.test.js +117 -0
  48. package/dist/src/topics/types.js +8 -0
  49. package/dist/src/wiki.js +133 -0
  50. package/dist/src/wiki.test.js +171 -0
  51. package/extensions/dashboard-client/dist/assets/{AchievementTiles-QPo46Pwg.js → AchievementTiles-DUulvXUY.js} +2 -2
  52. package/extensions/dashboard-client/dist/assets/{AchievementTiles-QPo46Pwg.js.map → AchievementTiles-DUulvXUY.js.map} +1 -1
  53. package/extensions/dashboard-client/dist/assets/{AchievementsTab-BDJxRXRY.js → AchievementsTab-Dx0YtCNf.js} +2 -2
  54. package/extensions/dashboard-client/dist/assets/{AchievementsTab-BDJxRXRY.js.map → AchievementsTab-Dx0YtCNf.js.map} +1 -1
  55. package/extensions/dashboard-client/dist/assets/{CacheTab-CUMqXQjd.js → CacheTab-DtkaUo1v.js} +2 -2
  56. package/extensions/dashboard-client/dist/assets/{CacheTab-CUMqXQjd.js.map → CacheTab-DtkaUo1v.js.map} +1 -1
  57. package/extensions/dashboard-client/dist/assets/{ConfigTab-j1dRzurG.js → ConfigTab-CLa4KfiZ.js} +2 -2
  58. package/extensions/dashboard-client/dist/assets/{ConfigTab-j1dRzurG.js.map → ConfigTab-CLa4KfiZ.js.map} +1 -1
  59. package/extensions/dashboard-client/dist/assets/{EventsTab-CMglXK4p.js → EventsTab-Q6tEIBBi.js} +2 -2
  60. package/extensions/dashboard-client/dist/assets/{EventsTab-CMglXK4p.js.map → EventsTab-Q6tEIBBi.js.map} +1 -1
  61. package/extensions/dashboard-client/dist/assets/{GameTab-fpaDoyxi.js → GameTab-Hq7GNWSY.js} +2 -2
  62. package/extensions/dashboard-client/dist/assets/{GameTab-fpaDoyxi.js.map → GameTab-Hq7GNWSY.js.map} +1 -1
  63. package/extensions/dashboard-client/dist/assets/{MetricsTab-CWG2crG_.js → MetricsTab-BtmBV6lU.js} +2 -2
  64. package/extensions/dashboard-client/dist/assets/{MetricsTab-CWG2crG_.js.map → MetricsTab-BtmBV6lU.js.map} +1 -1
  65. package/extensions/dashboard-client/dist/assets/{OverviewTab-BaV2D2nf.js → OverviewTab-CL10PwVU.js} +2 -2
  66. package/extensions/dashboard-client/dist/assets/{OverviewTab-BaV2D2nf.js.map → OverviewTab-CL10PwVU.js.map} +1 -1
  67. package/extensions/dashboard-client/dist/assets/{ReposTab-C-0kuZ_0.js → ReposTab-DAwANwbI.js} +2 -2
  68. package/extensions/dashboard-client/dist/assets/{ReposTab-C-0kuZ_0.js.map → ReposTab-DAwANwbI.js.map} +1 -1
  69. package/extensions/dashboard-client/dist/assets/{SessionsTab-CSYhddCA.js → SessionsTab-Ec08TZRQ.js} +2 -2
  70. package/extensions/dashboard-client/dist/assets/{SessionsTab-CSYhddCA.js.map → SessionsTab-Ec08TZRQ.js.map} +1 -1
  71. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-Dy4KRjSM.js → TimeSavedCard-COb8dpTy.js} +2 -2
  72. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-Dy4KRjSM.js.map → TimeSavedCard-COb8dpTy.js.map} +1 -1
  73. package/extensions/dashboard-client/dist/assets/TopicsTab-kvX3F3dY.js +2 -0
  74. package/extensions/dashboard-client/dist/assets/TopicsTab-kvX3F3dY.js.map +1 -0
  75. package/extensions/dashboard-client/dist/assets/TurnsTab-CCUE73Aa.js +2 -0
  76. package/extensions/dashboard-client/dist/assets/TurnsTab-CCUE73Aa.js.map +1 -0
  77. package/extensions/dashboard-client/dist/assets/{index-D_WtU2TV.js → index-UMrussFX.js} +10 -10
  78. package/extensions/dashboard-client/dist/assets/index-UMrussFX.js.map +1 -0
  79. package/extensions/dashboard-client/dist/assets/{index-og3ghxyY.css → index-gn66lYD2.css} +1 -1
  80. package/extensions/dashboard-client/dist/assets/{useSSE-C9yVJlGP.js → useSSE-BDHZ7CeS.js} +2 -2
  81. package/extensions/dashboard-client/dist/assets/{useSSE-C9yVJlGP.js.map → useSSE-BDHZ7CeS.js.map} +1 -1
  82. package/extensions/dashboard-client/dist/index.html +2 -2
  83. package/extensions/dashboard-client/src/App.tsx +10 -2
  84. package/extensions/dashboard-client/src/api/client.ts +84 -1
  85. package/extensions/dashboard-client/src/main.tsx +1 -0
  86. package/extensions/dashboard-client/src/styles/turns.css +142 -0
  87. package/extensions/dashboard-client/src/tabs/TopicsTab.tsx +185 -0
  88. package/extensions/dashboard-client/src/tabs/TurnsTab.tsx +326 -0
  89. package/extensions/dashboard-server/api-contracts/endpoints.ts +389 -291
  90. package/extensions/dashboard-server/api-contracts/game-types.ts +141 -88
  91. package/extensions/dashboard-server/api-contracts/index.ts +153 -133
  92. package/extensions/dashboard-server/api-contracts/turns.ts +114 -0
  93. package/extensions/dashboard-server/api-contracts.test.ts +920 -919
  94. package/extensions/dashboard-server/routes-topics.ts +119 -0
  95. package/extensions/dashboard-server/routes-turns.ts +282 -0
  96. package/extensions/dashboard-server/routes.ts +8 -1
  97. package/extensions/dashboard-server/server.ts +6 -2
  98. package/extensions/mega-compact.ts +75 -59
  99. package/extensions/mega-config.ts +232 -220
  100. package/extensions/mega-events/agent-handlers.ts +194 -151
  101. package/extensions/mega-events/context-handler.ts +50 -1
  102. package/extensions/mega-events/session-handlers.ts +23 -0
  103. package/extensions/mega-metrics-cmds.ts +114 -0
  104. package/extensions/mega-pipeline/recall.ts +215 -152
  105. package/extensions/mega-topics-cmds.ts +151 -0
  106. package/extensions/mega-turn-store.test.ts +108 -0
  107. package/extensions/mega-turn-store.ts +169 -0
  108. package/package.json +3 -1
  109. package/src/config/turns.ts +69 -0
  110. package/src/fork.test.ts +94 -0
  111. package/src/fork.ts +78 -0
  112. package/src/intent.test.ts +87 -0
  113. package/src/intent.ts +115 -0
  114. package/src/metrics/index.ts +9 -0
  115. package/src/metrics/turns.test.ts +165 -0
  116. package/src/metrics/turns.ts +201 -0
  117. package/src/store/sqlite/raw-transcript.ts +9 -3
  118. package/src/store/sqlite.dbmirror.test.ts +10 -0
  119. package/src/store/turns/connection.ts +137 -0
  120. package/src/store/turns/contract-compliance.test.ts +562 -0
  121. package/src/store/turns/index.ts +65 -0
  122. package/src/store/turns/memory-store.test.ts +15 -0
  123. package/src/store/turns/memory-store.ts +469 -0
  124. package/src/store/turns/migrations.test.ts +161 -0
  125. package/src/store/turns/migrations.ts +203 -0
  126. package/src/store/turns/schema.ts +158 -0
  127. package/src/store/turns/sqlite-store.test.ts +161 -0
  128. package/src/store/turns/sqlite-store.ts +656 -0
  129. package/src/store/turns/types.ts +181 -0
  130. package/src/topics/cluster.test.ts +176 -0
  131. package/src/topics/cluster.ts +191 -0
  132. package/src/topics/index.ts +18 -0
  133. package/src/topics/kselection.ts +182 -0
  134. package/src/topics/labels.ts +67 -0
  135. package/src/topics/store.test.ts +132 -0
  136. package/src/topics/store.ts +208 -0
  137. package/src/topics/types.ts +64 -0
  138. package/src/wiki.test.ts +217 -0
  139. package/src/wiki.ts +198 -0
  140. package/dist/extensions/openclaw-mega-compact.js +0 -291
  141. package/dist/src/minilm.js +0 -92
  142. package/dist/src/wordpiece.js +0 -129
  143. package/extensions/dashboard-client/dist/assets/index-D_WtU2TV.js.map +0 -1
@@ -0,0 +1,326 @@
1
+ /**
2
+ * dashboard-client/src/tabs/TurnsTab.tsx — Turn-by-turn memory tracking + recall (S52).
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).
14
+ */
15
+
16
+ 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
+
34
+ function fmtTs(ms: number): string {
35
+ if (!ms) return "—";
36
+ return new Date(ms).toLocaleString();
37
+ }
38
+
39
+ function bandClass(band: TurnRow["pressureBand"]): string {
40
+ if (band === "green") return "band band-green";
41
+ if (band === "yellow") return "band band-yellow";
42
+ if (band === "red") return "band band-red";
43
+ return "band";
44
+ }
45
+
46
+ function sourceLabel(s: TurnRow["recall"][number]["source"]): string {
47
+ if (s === "checkpoint") return "ckpt";
48
+ if (s === "cluster_summary") return "raptor";
49
+ return "mem";
50
+ }
51
+
52
+ export default function TurnsTab(): React.ReactElement {
53
+ const {
54
+ data: turns,
55
+ loading,
56
+ error,
57
+ } = useApi<TurnsResponse>(useCallback(() => fetchTurns(), []), {
58
+ pollInterval: 10_000,
59
+ });
60
+ const { data: intents } = useApi<RewindIntentsResponse>(
61
+ useCallback(() => fetchTurnIntents(), []),
62
+ { pollInterval: 5_000 },
63
+ );
64
+ const [expanded, setExpanded] = useState<string | null>(null);
65
+ const [detail, setDetail] = useState<ConversationTurnsResponse | null>(null);
66
+ const [detailLoading, setDetailLoading] = useState(false);
67
+ const [busy, setBusy] = useState<string | null>(null);
68
+ const [notice, setNotice] = useState<string | null>(null);
69
+
70
+ const expand = useCallback(async (conversationId: string) => {
71
+ if (expanded === conversationId) {
72
+ setExpanded(null);
73
+ setDetail(null);
74
+ return;
75
+ }
76
+ setExpanded(conversationId);
77
+ setDetailLoading(true);
78
+ setDetail(null);
79
+ try {
80
+ const d = await fetchConversationTurns(conversationId);
81
+ setDetail(d);
82
+ } finally {
83
+ setDetailLoading(false);
84
+ }
85
+ }, [expanded]);
86
+
87
+ const onFork = useCallback(
88
+ async (conversationId: string, turnIndex: number) => {
89
+ setBusy(`fork:${conversationId}:${turnIndex}`);
90
+ setNotice(null);
91
+ try {
92
+ const out = await postFork(conversationId, turnIndex);
93
+ setNotice(
94
+ `Forked → ${out.childConversationId} (${out.recalledCount} recalled)`,
95
+ );
96
+ } catch (e) {
97
+ setNotice(`Fork failed: ${(e as Error).message}`);
98
+ } finally {
99
+ setBusy(null);
100
+ }
101
+ },
102
+ [],
103
+ );
104
+
105
+ const onRewind = useCallback(async (conversationId: string, turnIndex: number) => {
106
+ setBusy(`rewind:${conversationId}:${turnIndex}`);
107
+ setNotice(null);
108
+ try {
109
+ await postTurnIntent(conversationId, turnIndex);
110
+ setNotice(`Rewind intent queued for turn ${turnIndex}`);
111
+ } catch (e) {
112
+ setNotice(`Rewind failed: ${(e as Error).message}`);
113
+ } finally {
114
+ setBusy(null);
115
+ }
116
+ }, []);
117
+
118
+ if (error && !turns) {
119
+ return <div className="tab-stub">Error loading turns: {error.message}</div>;
120
+ }
121
+ if (loading && !turns) {
122
+ return <div className="tab-stub">Loading turns…</div>;
123
+ }
124
+ if (!turns || turns.conversations.length === 0) {
125
+ return (
126
+ <div className="tab-stub">
127
+ <h3>Turn-by-turn memory</h3>
128
+ <p>
129
+ No turns recorded yet. Per-turn tracking starts when{" "}
130
+ <code>MEGACOMPACT_TURNS_DB=1</code> and the session runs through a
131
+ compaction. Each turn records its context metrics + the checkpoints
132
+ recalled into it.
133
+ </p>
134
+ </div>
135
+ );
136
+ }
137
+
138
+ return (
139
+ <div className="turns-tab">
140
+ <h3>Turn-by-turn memory tracking + recall</h3>
141
+ {notice && <div className="turns-notice">{notice}</div>}
142
+
143
+ {intents && intents.intents.length > 0 && (
144
+ <section className="turns-intents">
145
+ <h4>Pending rewind intents ({intents.intents.length})</h4>
146
+ <ul>
147
+ {intents.intents.map((i) => (
148
+ <li key={i.id}>
149
+ {fmtTs(i.createdAt)} — rewind <code>{i.conversationId}</code>{" "}
150
+ to turn {i.targetTurnIndex}
151
+ </li>
152
+ ))}
153
+ </ul>
154
+ </section>
155
+ )}
156
+
157
+ <section className="turns-conv-list">
158
+ <h4>Conversations ({turns.conversations.length})</h4>
159
+ <table className="turns-table">
160
+ <thead>
161
+ <tr>
162
+ <th>Conversation</th>
163
+ <th>Turns</th>
164
+ <th>Recall</th>
165
+ <th>Epochs</th>
166
+ <th>Avg ctx%</th>
167
+ <th>Last turn</th>
168
+ <th></th>
169
+ </tr>
170
+ </thead>
171
+ <tbody>
172
+ {turns.conversations.map((c: ConversationSummary) => (
173
+ <ConversationRow
174
+ key={c.conversationId}
175
+ c={c}
176
+ active={turns.activeConversationId === c.conversationId}
177
+ expanded={expanded === c.conversationId}
178
+ onExpand={() => expand(c.conversationId)}
179
+ />
180
+ ))}
181
+ </tbody>
182
+ </table>
183
+ </section>
184
+
185
+ {expanded && (
186
+ <section className="turns-detail">
187
+ <h4>
188
+ Turns in <code>{expanded}</code>
189
+ </h4>
190
+ {detailLoading ? (
191
+ <div className="tab-stub">Loading turns…</div>
192
+ ) : detail ? (
193
+ <TurnDetail
194
+ detail={detail}
195
+ busy={busy}
196
+ onFork={onFork}
197
+ onRewind={onRewind}
198
+ />
199
+ ) : (
200
+ <div className="tab-stub">No detail.</div>
201
+ )}
202
+ </section>
203
+ )}
204
+ </div>
205
+ );
206
+ }
207
+
208
+ function ConversationRow({
209
+ c,
210
+ active,
211
+ expanded,
212
+ onExpand,
213
+ }: {
214
+ c: ConversationSummary;
215
+ active: boolean;
216
+ expanded: boolean;
217
+ onExpand: () => void;
218
+ }): React.ReactElement {
219
+ return (
220
+ <>
221
+ <tr
222
+ className={active ? "turns-row active" : "turns-row"}
223
+ onClick={onExpand}
224
+ style={{ cursor: "pointer" }}
225
+ >
226
+ <td>
227
+ {expanded ? "▾" : "▸"} <code>{c.conversationId}</code>
228
+ {active && <span className="pill">active</span>}
229
+ </td>
230
+ <td>{c.turnCount}</td>
231
+ <td>{c.totalRecall}</td>
232
+ <td>{c.epochCount}</td>
233
+ <td>{(c.avgCtxPercent * 100).toFixed(0)}%</td>
234
+ <td>{fmtTs(c.lastTurnAt)}</td>
235
+ <td />
236
+ </tr>
237
+ </>
238
+ );
239
+ }
240
+
241
+ function TurnDetail({
242
+ detail,
243
+ busy,
244
+ onFork,
245
+ onRewind,
246
+ }: {
247
+ detail: ConversationTurnsResponse;
248
+ busy: string | null;
249
+ onFork: (conversationId: string, turnIndex: number) => void;
250
+ onRewind: (conversationId: string, turnIndex: number) => void;
251
+ }): React.ReactElement {
252
+ return (
253
+ <table className="turns-detail-table">
254
+ <thead>
255
+ <tr>
256
+ <th>#</th>
257
+ <th>Role</th>
258
+ <th>Ctx</th>
259
+ <th>Band</th>
260
+ <th>Epoch</th>
261
+ <th>Recalled checkpoints (memory recall)</th>
262
+ <th>Ended</th>
263
+ <th>Actions</th>
264
+ </tr>
265
+ </thead>
266
+ <tbody>
267
+ {detail.turns.map((t) => (
268
+ <tr key={`${t.conversationId}:${t.turnIndex}`}>
269
+ <td>{t.turnIndex}</td>
270
+ <td>{t.role}</td>
271
+ <td>
272
+ {t.ctxTokens ?? "—"}
273
+ {t.ctxPercent != null && (
274
+ <span className="muted"> · {(t.ctxPercent * 100).toFixed(0)}%</span>
275
+ )}
276
+ </td>
277
+ <td>
278
+ <span className={bandClass(t.pressureBand)}>
279
+ {t.pressureBand ?? "—"}
280
+ </span>
281
+ </td>
282
+ <td>
283
+ {t.epochId ? <code>{t.epochId.slice(0, 8)}</code> : "—"}
284
+ </td>
285
+ <td className="recall-cell">
286
+ {t.recall.length === 0 ? (
287
+ <span className="muted">—</span>
288
+ ) : (
289
+ <ul className="recall-list">
290
+ {t.recall.map((r) => (
291
+ <li key={r.checkpointId}>
292
+ <code>{r.checkpointId.slice(0, 12)}</code>{" "}
293
+ <span className="muted">
294
+ {sourceLabel(r.source)} · {r.score.toFixed(2)}
295
+ {r.raptorLevel != null ? ` · L${r.raptorLevel}` : ""}
296
+ </span>
297
+ </li>
298
+ ))}
299
+ </ul>
300
+ )}
301
+ </td>
302
+ <td>{fmtTs(t.endedAt)}</td>
303
+ <td className="actions-cell">
304
+ <button
305
+ type="button"
306
+ className="mini-btn"
307
+ disabled={busy !== null}
308
+ onClick={() => onFork(t.conversationId, t.turnIndex)}
309
+ >
310
+ fork
311
+ </button>
312
+ <button
313
+ type="button"
314
+ className="mini-btn"
315
+ disabled={busy !== null}
316
+ onClick={() => onRewind(t.conversationId, t.turnIndex)}
317
+ >
318
+ rewind
319
+ </button>
320
+ </td>
321
+ </tr>
322
+ ))}
323
+ </tbody>
324
+ </table>
325
+ );
326
+ }