pi-mega-compact 0.21.2 → 0.21.4
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-rag-settings-helpers.js +1 -0
- package/dist/extensions/mega-compact-child.js +126 -0
- package/dist/extensions/mega-config.js +5 -0
- package/dist/extensions/mega-events/agent-handlers/turnEndHandler/recordTurnRow.js +12 -2
- package/dist/extensions/mega-events/context-handler/afterCompact.js +11 -0
- package/dist/extensions/mega-events/context-handler/dbMirrorAppend.js +8 -0
- package/dist/extensions/mega-events/health-handler.js +12 -2
- package/dist/extensions/mega-events/session-handlers.js +1 -0
- package/dist/extensions/mega-pipeline/compact/run.js +11 -0
- package/dist/extensions/mega-runtime/helpers.js +45 -0
- package/dist/extensions/mega-runtime/internal-errors.js +14 -0
- package/dist/extensions/mega-runtime/runtime-instrumentation.js +166 -0
- package/dist/extensions/mega-runtime/runtime.js +27 -193
- package/dist/extensions/mega-runtime/state.js +1 -1
- package/dist/extensions/mega-runtime/widget.js +1 -1
- package/dist/extensions/mega-turn-store.js +5 -0
- package/dist/src/bridge/factory.js +195 -0
- package/dist/src/bridge/types.js +10 -0
- package/dist/src/bridge.js +1 -0
- package/dist/src/contextHealth.js +13 -5
- package/dist/src/metrics/turns.js +6 -1
- package/dist/src/store/sqlite/context-health-cache-poison.js +60 -0
- package/dist/src/store/sqlite/context-health.js +6 -48
- package/dist/src/store/sqlite/embedding-cache.js +4 -0
- package/dist/src/store/sqlite/schema.js +4 -0
- package/dist/src/store/turns/memory-store/admin.js +119 -0
- package/dist/src/store/turns/memory-store/ctx.js +28 -0
- package/dist/src/store/turns/memory-store/read.js +110 -0
- package/dist/src/store/turns/memory-store/write.js +88 -0
- package/dist/src/store/turns/memory-store.js +44 -273
- package/dist/src/store/turns/migrations.js +4 -0
- package/dist/src/store/turns/schema.js +1 -0
- package/dist/src/store/turns/sqlite-store/read.js +13 -0
- package/dist/src/store/turns/sqlite-store/rows.js +4 -0
- package/dist/src/store/turns/sqlite-store/write.js +3 -3
- package/dist/src/store/turns/sqlite-store.js +4 -0
- package/extensions/dashboard-client/dist/assets/{AdminTab-BsA8-_bX.js → AdminTab-C4Y0_UVx.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AdminTab-BsA8-_bX.js.map → AdminTab-C4Y0_UVx.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{AreaChart-BRHEfWhc.js → AreaChart-MCWK9rn6.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-BRHEfWhc.js.map → AreaChart-MCWK9rn6.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{BarChart-C-M0LaFc.js → BarChart-CvSGAiWV.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{BarChart-C-M0LaFc.js.map → BarChart-CvSGAiWV.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-Cj3n59U3.js → CacheTab-DnMEVRro.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-Cj3n59U3.js.map → CacheTab-DnMEVRro.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ConfigTab-C016XTNC.js → ConfigTab-BExuZYy2.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ConfigTab-C016XTNC.js.map → ConfigTab-BExuZYy2.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-DE29fuqe.js → EventsTab-CspsEslY.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-DE29fuqe.js.map → EventsTab-CspsEslY.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/HealthTab-DiWzo2ML.js +2 -0
- package/extensions/dashboard-client/dist/assets/HealthTab-DiWzo2ML.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-4fJ7M4S4.js → MaintenanceTab-9NJuv3dB.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-4fJ7M4S4.js.map → MaintenanceTab-9NJuv3dB.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-CDbcK9e4.js → MemoryMapTab-YpJK1h17.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-CDbcK9e4.js.map → MemoryMapTab-YpJK1h17.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsCards-VUEcgGzo.js → MetricsCards-D2Mom1M7.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsCards-VUEcgGzo.js.map → MetricsCards-D2Mom1M7.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/MetricsTab-DqWF7wud.js +2 -0
- package/extensions/dashboard-client/dist/assets/{MetricsTab-CKqJ0Zl-.js.map → MetricsTab-DqWF7wud.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-9Kxo79mi.js → OverviewTab-YuRxjV9U.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-9Kxo79mi.js.map → OverviewTab-YuRxjV9U.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-C4iveqrP.js → ReposTab-sXMrR7je.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-C4iveqrP.js.map → ReposTab-sXMrR7je.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-DSEwkMQm.js → SessionsTab-4YCXeGNR.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-DSEwkMQm.js.map → SessionsTab-4YCXeGNR.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SetupTab-CCyWgy4O.js → SetupTab-67pZcM_W.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SetupTab-CCyWgy4O.js.map → SetupTab-67pZcM_W.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CyD8e_e_.js → TimeSavedCard-DInjNETP.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CyD8e_e_.js.map → TimeSavedCard-DInjNETP.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnMemoryView-5tLbWkRv.js → TurnMemoryView-C3Bif8ix.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnMemoryView-5tLbWkRv.js.map → TurnMemoryView-C3Bif8ix.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/TurnsTab-BUGfiANp.js +2 -0
- package/extensions/dashboard-client/dist/assets/{TurnsTab-C7pm92Hg.js.map → TurnsTab-BUGfiANp.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{VcStatusBadge-ObhcIyfN.js → VcStatusBadge-DKU7qz9f.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{VcStatusBadge-ObhcIyfN.js.map → VcStatusBadge-DKU7qz9f.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{VectorCortexTab-BtHZb7o7.js → VectorCortexTab-BsVSf4xm.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{VectorCortexTab-BtHZb7o7.js.map → VectorCortexTab-BsVSf4xm.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{WikiTab-lQ4l3QIK.js → WikiTab-DZw9_Fwd.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{WikiTab-lQ4l3QIK.js.map → WikiTab-DZw9_Fwd.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{button-DhQQAJGm.js → button-BLRPN489.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{button-DhQQAJGm.js.map → button-BLRPN489.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-Cfhz9X31.js → card-BGkkcM4Q.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-Cfhz9X31.js.map → card-BGkkcM4Q.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{client-extra-CKNOGEz_.js → client-extra-0OJClxHD.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{client-extra-CKNOGEz_.js.map → client-extra-0OJClxHD.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-JKjK9_cB.js → generateCategoricalChart-Dl5lmrHF.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-JKjK9_cB.js.map → generateCategoricalChart-Dl5lmrHF.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/health-lSeDFTz7.js.map +1 -1
- package/extensions/dashboard-client/dist/assets/{index-iuxgtzEW.js → index-CH_AD-S_.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{index-iuxgtzEW.js.map → index-CH_AD-S_.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{switch-7EHEQMSP.js → switch-BPP2-d12.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-7EHEQMSP.js.map → switch-BPP2-d12.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-z1lWY4UM.js → toggle-Dez0JaMV.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-z1lWY4UM.js.map → toggle-Dez0JaMV.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-sctOZuRM.js → useSSE-Cx2W7BTo.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-sctOZuRM.js.map → useSSE-Cx2W7BTo.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/api/health.ts +1 -0
- package/extensions/dashboard-client/src/tabs/HealthTab.tsx +1 -0
- package/extensions/dashboard-server/routes-rag-settings-helpers.ts +6 -0
- package/extensions/mega-compact-child.ts +129 -0
- package/extensions/mega-config-types.ts +5 -0
- package/extensions/mega-config.ts +5 -0
- package/extensions/mega-events/agent-handlers/turnEndHandler/recordTurnRow.ts +12 -2
- package/extensions/mega-events/context-handler/afterCompact.ts +11 -0
- package/extensions/mega-events/context-handler/dbMirrorAppend.ts +8 -0
- package/extensions/mega-events/health-handler.ts +13 -3
- package/extensions/mega-events/session-handlers.ts +1 -0
- package/extensions/mega-pipeline/compact/run.ts +11 -0
- package/extensions/mega-runtime/helpers.ts +46 -0
- package/extensions/mega-runtime/internal-errors.ts +30 -0
- package/extensions/mega-runtime/runtime-instrumentation.ts +195 -0
- package/extensions/mega-runtime/runtime.ts +29 -209
- package/extensions/mega-runtime/state.ts +1 -1
- package/extensions/mega-runtime/widget.ts +1 -1
- package/extensions/mega-turn-store.ts +9 -0
- package/package.json +2 -2
- package/src/bridge/factory.ts +245 -0
- package/src/bridge/types.ts +140 -0
- package/src/bridge.ts +24 -0
- package/src/contextHealth.ts +26 -6
- package/src/metrics/turns.ts +6 -1
- package/src/store/sqlite/context-health-cache-poison.ts +97 -0
- package/src/store/sqlite/context-health.ts +12 -84
- package/src/store/sqlite/embedding-cache.ts +4 -0
- package/src/store/sqlite/schema.ts +4 -0
- package/src/store/turns/memory-store/admin.ts +171 -0
- package/src/store/turns/memory-store/ctx.ts +62 -0
- package/src/store/turns/memory-store/read.ts +172 -0
- package/src/store/turns/memory-store/write.ts +126 -0
- package/src/store/turns/memory-store.ts +51 -330
- package/src/store/turns/migrations.ts +4 -0
- package/src/store/turns/schema.ts +1 -0
- package/src/store/turns/sqlite-store/read.ts +19 -0
- package/src/store/turns/sqlite-store/rows.ts +5 -0
- package/src/store/turns/sqlite-store/write.ts +3 -2
- package/src/store/turns/sqlite-store.ts +5 -0
- package/src/store/turns/types.ts +15 -0
- package/extensions/dashboard-client/dist/assets/HealthTab-D4eVI8ar.js +0 -2
- package/extensions/dashboard-client/dist/assets/HealthTab-D4eVI8ar.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/MetricsTab-CKqJ0Zl-.js +0 -2
- package/extensions/dashboard-client/dist/assets/TurnsTab-C7pm92Hg.js +0 -2
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* memory-store.ts — InMemoryTurnStore: test harness + embeddable backend.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Thin delegate shell: each method body lives as a free function in
|
|
5
|
+
* ./memory-store/* (read.ts / write.ts / admin.ts) operating on an internal
|
|
6
|
+
* ctx ({turns, recall, forks, convIndex, sessionConv}); this class wires those
|
|
7
|
+
* to the contract. The public API surface — InMemoryTurnStore implements
|
|
8
|
+
* TurnStore, all methods, asReader/asWriter/asAdmin, close — is unchanged
|
|
9
|
+
* for existing callers.
|
|
10
|
+
*
|
|
11
|
+
* Same contract as SqliteTurnStore, backed by Maps. No file I/O. Used for
|
|
12
|
+
* tests and for hosts that don't want SQLite. Mirrors sqlite-store.ts.
|
|
6
13
|
*
|
|
7
14
|
* PREVENT-PI-004: no network. PREVENT-002: no SQL here.
|
|
8
15
|
*/
|
|
9
16
|
|
|
10
|
-
import {
|
|
11
|
-
import { DuplicateTurnError, type
|
|
17
|
+
import type {
|
|
12
18
|
TurnStore,
|
|
13
19
|
TurnReader,
|
|
14
20
|
TurnWriter,
|
|
@@ -26,35 +32,15 @@ import { DuplicateTurnError, type
|
|
|
26
32
|
ConversationStats,
|
|
27
33
|
TurnStoreOptions,
|
|
28
34
|
} from "./types.js";
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// Auto-increment counter
|
|
39
|
-
let nextId = 1;
|
|
40
|
-
function allocId(): number {
|
|
41
|
-
return nextId++;
|
|
42
|
-
}
|
|
43
|
-
function resetIdCounter(): void {
|
|
44
|
-
nextId = 1;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Internal row with numeric id
|
|
48
|
-
interface TurnRow {
|
|
49
|
-
id: number;
|
|
50
|
-
entry: TurnEntry;
|
|
51
|
-
epochId?: string;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
interface RecallRow {
|
|
55
|
-
turnId: number;
|
|
56
|
-
entry: TurnRecallEntry;
|
|
57
|
-
}
|
|
35
|
+
import {
|
|
36
|
+
resetIdCounter,
|
|
37
|
+
type MemoryTurnStoreCtx,
|
|
38
|
+
type RecallRow,
|
|
39
|
+
type TurnRow,
|
|
40
|
+
} from "./memory-store/ctx.js";
|
|
41
|
+
import * as read from "./memory-store/read.js";
|
|
42
|
+
import * as write from "./memory-store/write.js";
|
|
43
|
+
import * as admin from "./memory-store/admin.js";
|
|
58
44
|
|
|
59
45
|
export class InMemoryTurnStore implements TurnStore {
|
|
60
46
|
private turns: Map<number, TurnRow> = new Map();
|
|
@@ -69,371 +55,105 @@ export class InMemoryTurnStore implements TurnStore {
|
|
|
69
55
|
resetIdCounter();
|
|
70
56
|
}
|
|
71
57
|
|
|
58
|
+
private ctx(): MemoryTurnStoreCtx {
|
|
59
|
+
return {
|
|
60
|
+
turns: this.turns,
|
|
61
|
+
recall: this.recall,
|
|
62
|
+
forks: this.forks,
|
|
63
|
+
convIndex: this.convIndex,
|
|
64
|
+
sessionConv: this.sessionConv,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
72
68
|
// ── TurnReader ──────────────────────────────────────────────
|
|
73
69
|
|
|
74
70
|
query(filter: TurnFilter): TurnEntry[] {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (filter.conversationId !== undefined) {
|
|
78
|
-
results = results.filter(
|
|
79
|
-
(t) => t.conversationId === filter.conversationId,
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
if (filter.sessionId !== undefined) {
|
|
83
|
-
results = results.filter(
|
|
84
|
-
(t) =>
|
|
85
|
-
normalizeSessionId(t.sessionId) ===
|
|
86
|
-
normalizeSessionId(filter.sessionId!),
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
if (filter.sinceMs !== undefined) {
|
|
90
|
-
results = results.filter((t) => t.endedAt >= filter.sinceMs!);
|
|
91
|
-
}
|
|
92
|
-
if (filter.untilMs !== undefined) {
|
|
93
|
-
results = results.filter((t) => t.endedAt <= filter.untilMs!);
|
|
94
|
-
}
|
|
95
|
-
if (filter.pressureBand !== undefined) {
|
|
96
|
-
results = results.filter((t) => t.pressureBand === filter.pressureBand);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
results.sort((a, b) => a.endedAt - b.endedAt);
|
|
100
|
-
|
|
101
|
-
const offset = filter.offset ?? 0;
|
|
102
|
-
const limit = filter.limit ?? 1000;
|
|
103
|
-
return results.slice(offset, offset + limit);
|
|
71
|
+
return read.query(this.ctx(), filter);
|
|
104
72
|
}
|
|
105
73
|
|
|
106
74
|
getTurn(turnId: TurnId): TurnEntry | undefined {
|
|
107
|
-
|
|
108
|
-
return row ? { ...row.entry, epochId: row.epochId } : undefined;
|
|
75
|
+
return read.getTurn(this.ctx(), turnId);
|
|
109
76
|
}
|
|
110
77
|
|
|
111
78
|
getTurnByIndex(
|
|
112
79
|
conversationId: ConversationId,
|
|
113
80
|
turnIndex: number,
|
|
114
81
|
): TurnEntry | undefined {
|
|
115
|
-
|
|
116
|
-
for (const id of ids) {
|
|
117
|
-
const row = this.turns.get(id);
|
|
118
|
-
if (row && row.entry.turnIndex === turnIndex) {
|
|
119
|
-
return { ...row.entry, epochId: row.epochId };
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return undefined;
|
|
82
|
+
return read.getTurnByIndex(this.ctx(), conversationId, turnIndex);
|
|
123
83
|
}
|
|
124
84
|
|
|
125
85
|
listRecall(turnId: TurnId): TurnRecallEntry[] {
|
|
126
|
-
return this.
|
|
127
|
-
.filter((r) => r.turnId === Number(turnId))
|
|
128
|
-
.map((r) => r.entry)
|
|
129
|
-
.sort((a, b) => b.score - a.score);
|
|
86
|
+
return read.listRecall(this.ctx(), turnId);
|
|
130
87
|
}
|
|
131
88
|
|
|
132
89
|
listRecallByIndex(
|
|
133
90
|
conversationId: ConversationId,
|
|
134
91
|
turnIndex: number,
|
|
135
92
|
): TurnRecallEntry[] {
|
|
136
|
-
|
|
137
|
-
if (!turn) return [];
|
|
138
|
-
// Resolve the numeric row id for this turn via the convIndex.
|
|
139
|
-
const ids = this.convIndex.get(conversationId) ?? [];
|
|
140
|
-
for (const id of ids) {
|
|
141
|
-
const row = this.turns.get(id);
|
|
142
|
-
if (row && row.entry.turnIndex === turnIndex) {
|
|
143
|
-
return this.listRecall(String(id));
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return [];
|
|
93
|
+
return read.listRecallByIndex(this.ctx(), conversationId, turnIndex);
|
|
147
94
|
}
|
|
148
95
|
|
|
149
96
|
listForks(conversationId: ConversationId): ConversationFork[] {
|
|
150
|
-
return this.
|
|
151
|
-
.filter((f) => f.parentConversationId === conversationId)
|
|
152
|
-
.sort((a, b) => a.createdAt - b.createdAt);
|
|
97
|
+
return read.listForks(this.ctx(), conversationId);
|
|
153
98
|
}
|
|
154
99
|
|
|
155
100
|
countTurns(conversationId: ConversationId): number {
|
|
156
|
-
|
|
157
|
-
return ids?.length ?? 0;
|
|
101
|
+
return read.countTurns(this.ctx(), conversationId);
|
|
158
102
|
}
|
|
159
103
|
|
|
160
104
|
conversationStats(conversationId: ConversationId): ConversationStats {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
.map((id) => this.turns.get(id)?.entry)
|
|
164
|
-
.filter((e): e is TurnEntry => e !== undefined);
|
|
165
|
-
|
|
166
|
-
if (entries.length === 0) {
|
|
167
|
-
return {
|
|
168
|
-
turnCount: 0,
|
|
169
|
-
firstTurnAt: 0,
|
|
170
|
-
lastTurnAt: 0,
|
|
171
|
-
avgCtxPercent: 0,
|
|
172
|
-
pressureBands: {},
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
let ctxSum = 0;
|
|
177
|
-
let ctxCount = 0;
|
|
178
|
-
const bands: Record<string, number> = {};
|
|
179
|
-
|
|
180
|
-
for (const e of entries) {
|
|
181
|
-
if (e.ctxPercent !== undefined) {
|
|
182
|
-
ctxSum += e.ctxPercent;
|
|
183
|
-
ctxCount++;
|
|
184
|
-
}
|
|
185
|
-
if (e.pressureBand)
|
|
186
|
-
bands[e.pressureBand] = (bands[e.pressureBand] ?? 0) + 1;
|
|
187
|
-
}
|
|
105
|
+
return read.conversationStats(this.ctx(), conversationId);
|
|
106
|
+
}
|
|
188
107
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
firstTurnAt: entries[0].endedAt,
|
|
192
|
-
lastTurnAt: entries[entries.length - 1].endedAt,
|
|
193
|
-
avgCtxPercent: ctxCount > 0 ? ctxSum / ctxCount : 0,
|
|
194
|
-
pressureBands: bands,
|
|
195
|
-
};
|
|
108
|
+
nextTurnIndexFor(conversationId: ConversationId): number {
|
|
109
|
+
return read.nextTurnIndexFor(this.ctx(), conversationId);
|
|
196
110
|
}
|
|
197
111
|
|
|
198
112
|
// ── TurnWriter ──────────────────────────────────────────────
|
|
199
113
|
|
|
200
114
|
appendTurn(entry: TurnEntry): TurnId {
|
|
201
|
-
|
|
202
|
-
for (const [, row] of this.turns) {
|
|
203
|
-
if (
|
|
204
|
-
row.entry.conversationId === entry.conversationId &&
|
|
205
|
-
row.entry.turnIndex === entry.turnIndex
|
|
206
|
-
) {
|
|
207
|
-
throw new DuplicateTurnError(entry.conversationId, entry.turnIndex);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
const id = allocId();
|
|
211
|
-
const sid = normalizeSessionId(entry.sessionId);
|
|
212
|
-
const normalized = { ...entry, sessionId: sid };
|
|
213
|
-
this.turns.set(id, { id, entry: normalized });
|
|
214
|
-
|
|
215
|
-
// Update indices
|
|
216
|
-
const convIds = this.convIndex.get(entry.conversationId) ?? [];
|
|
217
|
-
convIds.push(id);
|
|
218
|
-
this.convIndex.set(entry.conversationId, convIds);
|
|
219
|
-
|
|
220
|
-
this.sessionConv.set(sid, entry.conversationId);
|
|
221
|
-
|
|
222
|
-
return String(id);
|
|
115
|
+
return write.appendTurn(this.ctx(), entry);
|
|
223
116
|
}
|
|
224
117
|
|
|
225
118
|
appendRecall(entry: TurnRecallEntry): void {
|
|
226
|
-
|
|
227
|
-
const exists = this.recall.some(
|
|
228
|
-
(r) =>
|
|
229
|
-
r.turnId === Number(entry.turnId) &&
|
|
230
|
-
r.entry.checkpointId === entry.checkpointId,
|
|
231
|
-
);
|
|
232
|
-
if (!exists) {
|
|
233
|
-
this.recall.push({ turnId: Number(entry.turnId), entry });
|
|
234
|
-
}
|
|
119
|
+
write.appendRecall(this.ctx(), entry);
|
|
235
120
|
}
|
|
236
121
|
|
|
237
122
|
ensureConversationId(sessionId: SessionId): ConversationId {
|
|
238
|
-
|
|
239
|
-
const existing = this.sessionConv.get(sid);
|
|
240
|
-
if (existing) return existing;
|
|
241
|
-
// Generate and store a new conversation ID for this session
|
|
242
|
-
const convId = newConversationId();
|
|
243
|
-
this.sessionConv.set(sid, convId);
|
|
244
|
-
return convId;
|
|
123
|
+
return write.ensureConversationId(this.ctx(), sessionId);
|
|
245
124
|
}
|
|
246
125
|
|
|
247
126
|
forkConversation(
|
|
248
127
|
parentId: ConversationId,
|
|
249
128
|
forkTurnIndex: number,
|
|
250
129
|
): ConversationId {
|
|
251
|
-
|
|
252
|
-
const now = Date.now();
|
|
253
|
-
|
|
254
|
-
// 1. Record the fork lineage
|
|
255
|
-
this.forks.push({
|
|
256
|
-
parentConversationId: parentId,
|
|
257
|
-
childConversationId: childId,
|
|
258
|
-
forkTurnIndex,
|
|
259
|
-
createdAt: now,
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
// 2. Find the parent's turn at forkTurnIndex
|
|
263
|
-
const parentTurnIds = this.convIndex.get(parentId) ?? [];
|
|
264
|
-
const parentTurnRow = parentTurnIds
|
|
265
|
-
.map((id) => this.turns.get(id))
|
|
266
|
-
.find((r) => r?.entry.turnIndex === forkTurnIndex);
|
|
267
|
-
|
|
268
|
-
if (!parentTurnRow) return childId; // no parent turn — nothing to seed
|
|
269
|
-
|
|
270
|
-
// 3. Create a seed turn in the child conversation
|
|
271
|
-
const seedId = allocId();
|
|
272
|
-
const seedEntry: TurnEntry = {
|
|
273
|
-
conversationId: childId,
|
|
274
|
-
sessionId: `fork_${childId}`,
|
|
275
|
-
turnIndex: 0,
|
|
276
|
-
role: "system",
|
|
277
|
-
endedAt: now,
|
|
278
|
-
};
|
|
279
|
-
this.turns.set(seedId, { id: seedId, entry: seedEntry });
|
|
280
|
-
this.convIndex.set(childId, [seedId]);
|
|
281
|
-
|
|
282
|
-
// 4. Copy the parent's recall entries into the child's seed turn
|
|
283
|
-
const parentRecall = this.recall.filter(
|
|
284
|
-
(r) => r.turnId === parentTurnRow.id,
|
|
285
|
-
);
|
|
286
|
-
for (const r of parentRecall) {
|
|
287
|
-
this.recall.push({
|
|
288
|
-
turnId: seedId,
|
|
289
|
-
entry: {
|
|
290
|
-
turnId: String(seedId),
|
|
291
|
-
checkpointId: r.entry.checkpointId,
|
|
292
|
-
score: r.entry.score,
|
|
293
|
-
source: r.entry.source,
|
|
294
|
-
raptorLevel: r.entry.raptorLevel,
|
|
295
|
-
},
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return childId;
|
|
130
|
+
return write.forkConversation(this.ctx(), parentId, forkTurnIndex);
|
|
300
131
|
}
|
|
301
132
|
|
|
302
133
|
// ── TurnAdmin ───────────────────────────────────────────────
|
|
303
134
|
|
|
304
135
|
prune(policy: RetentionPolicy): PruneReport {
|
|
305
|
-
|
|
306
|
-
let turnsRemoved = 0;
|
|
307
|
-
let recallRemoved = 0;
|
|
308
|
-
|
|
309
|
-
for (const [convId, turnIds] of this.convIndex.entries()) {
|
|
310
|
-
const total = turnIds.length;
|
|
311
|
-
const canDelete = Math.max(0, total - policy.keepMinPerConversation);
|
|
312
|
-
if (canDelete === 0) continue;
|
|
313
|
-
|
|
314
|
-
// Find old turns to delete
|
|
315
|
-
const oldIds = turnIds
|
|
316
|
-
.filter((id) => {
|
|
317
|
-
const row = this.turns.get(id);
|
|
318
|
-
return row && row.entry.endedAt < cutoff;
|
|
319
|
-
})
|
|
320
|
-
.slice(0, canDelete);
|
|
321
|
-
|
|
322
|
-
for (const id of oldIds) {
|
|
323
|
-
// Remove recall
|
|
324
|
-
const before = this.recall.length;
|
|
325
|
-
this.recall = this.recall.filter((r) => r.turnId !== id);
|
|
326
|
-
recallRemoved += before - this.recall.length;
|
|
327
|
-
|
|
328
|
-
this.turns.delete(id);
|
|
329
|
-
turnsRemoved++;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Update convIndex
|
|
333
|
-
this.convIndex.set(
|
|
334
|
-
convId,
|
|
335
|
-
turnIds.filter((id) => this.turns.has(id)),
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
const branchesPreserved = this.forks.length;
|
|
340
|
-
|
|
341
|
-
return {
|
|
342
|
-
turnsRemoved,
|
|
343
|
-
recallRemoved,
|
|
344
|
-
branchesPreserved,
|
|
345
|
-
freedBytes: 0, // in-memory — no file size to measure
|
|
346
|
-
};
|
|
136
|
+
return admin.prune(this.ctx(), policy);
|
|
347
137
|
}
|
|
348
138
|
|
|
349
139
|
vacuum(): void {
|
|
350
|
-
|
|
140
|
+
admin.vacuum(this.ctx());
|
|
351
141
|
}
|
|
352
142
|
|
|
353
143
|
checkpoint(): StoreSnapshot {
|
|
354
|
-
|
|
355
|
-
const sorted = [...this.turns.values()].sort((a, b) => a.id - b.id);
|
|
356
|
-
const turns = sorted.map((r) => r.entry);
|
|
357
|
-
|
|
358
|
-
// Build mapping: internal ID → 1-based position
|
|
359
|
-
const idToPos = new Map<number, number>();
|
|
360
|
-
for (let i = 0; i < sorted.length; i++) {
|
|
361
|
-
idToPos.set(sorted[i].id, i + 1);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// Replace recall's turnId with the stable 1-based position
|
|
365
|
-
const recall = this.recall.map((r) => ({
|
|
366
|
-
turnId: String(idToPos.get(r.turnId) ?? r.turnId),
|
|
367
|
-
checkpointId: r.entry.checkpointId,
|
|
368
|
-
score: r.entry.score,
|
|
369
|
-
source: r.entry.source,
|
|
370
|
-
raptorLevel: r.entry.raptorLevel,
|
|
371
|
-
}));
|
|
372
|
-
|
|
373
|
-
return {
|
|
374
|
-
version: 1,
|
|
375
|
-
exportedAt: Date.now(),
|
|
376
|
-
turns,
|
|
377
|
-
recall,
|
|
378
|
-
forks: [...this.forks],
|
|
379
|
-
};
|
|
144
|
+
return admin.checkpoint(this.ctx());
|
|
380
145
|
}
|
|
381
146
|
|
|
382
147
|
restore(from: StoreSnapshot): void {
|
|
383
|
-
this.
|
|
384
|
-
|
|
385
|
-
// Map: 1-based position → new internal ID
|
|
386
|
-
const posToNewId = new Map<number, number>();
|
|
387
|
-
|
|
388
|
-
for (let i = 0; i < from.turns.length; i++) {
|
|
389
|
-
const t = from.turns[i];
|
|
390
|
-
const newId = allocId();
|
|
391
|
-
const sid = normalizeSessionId(t.sessionId);
|
|
392
|
-
const normalized = { ...t, sessionId: sid };
|
|
393
|
-
this.turns.set(newId, { id: newId, entry: normalized });
|
|
394
|
-
|
|
395
|
-
// Update indices
|
|
396
|
-
const convIds = this.convIndex.get(t.conversationId) ?? [];
|
|
397
|
-
convIds.push(newId);
|
|
398
|
-
this.convIndex.set(t.conversationId, convIds);
|
|
399
|
-
this.sessionConv.set(sid, t.conversationId);
|
|
400
|
-
|
|
401
|
-
posToNewId.set(i + 1, newId);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// Re-attach recall with position-based TurnId → new internal ID mapping
|
|
405
|
-
for (const r of from.recall) {
|
|
406
|
-
const targetInternalId = posToNewId.get(Number(r.turnId));
|
|
407
|
-
if (targetInternalId !== undefined) {
|
|
408
|
-
this.recall.push({
|
|
409
|
-
turnId: targetInternalId,
|
|
410
|
-
entry: r,
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
this.forks = [...from.forks];
|
|
148
|
+
admin.restore(this.ctx(), from);
|
|
416
149
|
}
|
|
417
150
|
|
|
418
151
|
clear(): void {
|
|
419
|
-
|
|
420
|
-
this.recall = [];
|
|
421
|
-
this.forks = [];
|
|
422
|
-
this.convIndex.clear();
|
|
423
|
-
this.sessionConv.clear();
|
|
424
|
-
resetIdCounter();
|
|
152
|
+
admin.clear(this.ctx());
|
|
425
153
|
}
|
|
426
154
|
|
|
427
155
|
stampTurnsEpoch(sessionId: SessionId, epochId: string): number {
|
|
428
|
-
|
|
429
|
-
let stamped = 0;
|
|
430
|
-
for (const row of this.turns.values()) {
|
|
431
|
-
if (row.epochId === undefined && row.entry.sessionId === sid) {
|
|
432
|
-
row.epochId = epochId;
|
|
433
|
-
stamped++;
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
return stamped;
|
|
156
|
+
return admin.stampTurnsEpoch(this.ctx(), sessionId, epochId);
|
|
437
157
|
}
|
|
438
158
|
|
|
439
159
|
// ── Capability gating ───────────────────────────────────────
|
|
@@ -448,6 +168,7 @@ export class InMemoryTurnStore implements TurnStore {
|
|
|
448
168
|
listForks: (id) => this.listForks(id),
|
|
449
169
|
countTurns: (id) => this.countTurns(id),
|
|
450
170
|
conversationStats: (id) => this.conversationStats(id),
|
|
171
|
+
nextTurnIndexFor: (id) => this.nextTurnIndexFor(id),
|
|
451
172
|
};
|
|
452
173
|
}
|
|
453
174
|
|
|
@@ -196,6 +196,10 @@ export function migrateTurnTablesIfNeeded(
|
|
|
196
196
|
log(
|
|
197
197
|
`migration failed (will retry on next open): ${e instanceof Error ? e.message : String(e)}`,
|
|
198
198
|
);
|
|
199
|
+
// Sprint H (Option A): this src/ turn-table migration has no runtime handle
|
|
200
|
+
// (it runs at store-open via `log`). The migration_failure category is
|
|
201
|
+
// recorded by the store-open caller that owns a runtime, not here.
|
|
202
|
+
// guardrails-allow INTERNAL-ERR: src/ turn-table migration, no runtime handle — store-open caller records migration failures
|
|
199
203
|
}
|
|
200
204
|
}
|
|
201
205
|
|
|
@@ -185,6 +185,7 @@ export function initTurnSchema(db: DatabaseSync): void {
|
|
|
185
185
|
ensureCol("recall_coverage", "recall_coverage REAL DEFAULT 0");
|
|
186
186
|
ensureCol("recall_diversity", "recall_diversity REAL DEFAULT 0");
|
|
187
187
|
ensureCol("recall_specificity", "recall_specificity REAL DEFAULT 0");
|
|
188
|
+
ensureCol("session_turn_index", "session_turn_index INTEGER");
|
|
188
189
|
|
|
189
190
|
// ── Wiki Revival (Spec 3): user curation + topic evolution ────────
|
|
190
191
|
const sessionCol = (
|
|
@@ -172,3 +172,22 @@ export function conversationStats(
|
|
|
172
172
|
pressureBands: bands,
|
|
173
173
|
};
|
|
174
174
|
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* S49R: next conversation-monotonic turn index for a conversation.
|
|
178
|
+
* `MAX(turn_index) + 1`, or 0 when the conversation has no turns yet. A
|
|
179
|
+
* resumed session (pi's per-session `turnIndex` resets to 0) uses this instead
|
|
180
|
+
* of `event.turnIndex` so it never collides with UNIQUE(conversation_id, turn_index).
|
|
181
|
+
*/
|
|
182
|
+
export function nextTurnIndexFor(
|
|
183
|
+
ctx: SqliteTurnStoreCtx,
|
|
184
|
+
conversationId: ConversationId,
|
|
185
|
+
): number {
|
|
186
|
+
const { db } = ctx;
|
|
187
|
+
const row = db
|
|
188
|
+
.prepare(
|
|
189
|
+
"SELECT COALESCE(MAX(turn_index), -1) AS m FROM turns WHERE conversation_id = ?",
|
|
190
|
+
)
|
|
191
|
+
.get(conversationId) as { m: number };
|
|
192
|
+
return row.m + 1;
|
|
193
|
+
}
|
|
@@ -57,6 +57,11 @@ export function rowToEntry(r: Record<string, unknown>): TurnEntry {
|
|
|
57
57
|
diversity: (r.recall_diversity as number) ?? 0,
|
|
58
58
|
specificity: (r.recall_specificity as number) ?? 0,
|
|
59
59
|
},
|
|
60
|
+
// S49R: carried per-session counter (null on pre-migration rows).
|
|
61
|
+
sessionTurnIndex:
|
|
62
|
+
r.session_turn_index == null
|
|
63
|
+
? undefined
|
|
64
|
+
: (r.session_turn_index as number),
|
|
60
65
|
};
|
|
61
66
|
}
|
|
62
67
|
|
|
@@ -26,8 +26,8 @@ export function appendTurn(ctx: SqliteTurnStoreCtx, entry: TurnEntry): TurnId {
|
|
|
26
26
|
hyde_ran, hyde_doc, hyde_raw_count, hyde_hyde_count,
|
|
27
27
|
hyde_fused_count, hyde_lift, hyde_generation_ms, hyde_reason,
|
|
28
28
|
recall_score, recall_pass, recall_relevance, recall_coverage,
|
|
29
|
-
recall_diversity, recall_specificity)
|
|
30
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
29
|
+
recall_diversity, recall_specificity, session_turn_index)
|
|
30
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
31
31
|
)
|
|
32
32
|
.run(
|
|
33
33
|
entry.conversationId,
|
|
@@ -54,6 +54,7 @@ export function appendTurn(ctx: SqliteTurnStoreCtx, entry: TurnEntry): TurnId {
|
|
|
54
54
|
entry.recallMetrics?.coverage ?? null,
|
|
55
55
|
entry.recallMetrics?.diversity ?? null,
|
|
56
56
|
entry.recallMetrics?.specificity ?? null,
|
|
57
|
+
entry.sessionTurnIndex ?? null,
|
|
57
58
|
);
|
|
58
59
|
} catch (e) {
|
|
59
60
|
if (
|
|
@@ -98,6 +98,10 @@ export class SqliteTurnStore implements TurnStore {
|
|
|
98
98
|
return read.conversationStats(this.ctx(), conversationId);
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
nextTurnIndexFor(conversationId: ConversationId): number {
|
|
102
|
+
return read.nextTurnIndexFor(this.ctx(), conversationId);
|
|
103
|
+
}
|
|
104
|
+
|
|
101
105
|
// ── TurnWriter ──────────────────────────────────────────────
|
|
102
106
|
|
|
103
107
|
appendTurn(entry: TurnEntry): TurnId {
|
|
@@ -157,6 +161,7 @@ export class SqliteTurnStore implements TurnStore {
|
|
|
157
161
|
listForks: (id) => this.listForks(id),
|
|
158
162
|
countTurns: (id) => this.countTurns(id),
|
|
159
163
|
conversationStats: (id) => this.conversationStats(id),
|
|
164
|
+
nextTurnIndexFor: (id) => this.nextTurnIndexFor(id),
|
|
160
165
|
};
|
|
161
166
|
}
|
|
162
167
|
|
package/src/store/turns/types.ts
CHANGED
|
@@ -63,6 +63,14 @@ export interface TurnEntry {
|
|
|
63
63
|
hyde?: TurnHydeTelemetry;
|
|
64
64
|
/** H1: recall-quality snapshot for this turn (persisted to recall_* columns). */
|
|
65
65
|
recallMetrics?: TurnRecallTelemetry;
|
|
66
|
+
/**
|
|
67
|
+
* S49R: pi's per-session turn counter, carried alongside the
|
|
68
|
+
* conversation-monotonic `turnIndex`. `turnIndex` restarts at 0 on resume,
|
|
69
|
+
* so it is unsafe to use as a join key against `raw_transcript` (which is
|
|
70
|
+
* seeded from the session counter). `sessionTurnIndex` preserves that
|
|
71
|
+
* per-session value for the metrics join. Null on pre-migration rows.
|
|
72
|
+
*/
|
|
73
|
+
sessionTurnIndex?: number;
|
|
66
74
|
}
|
|
67
75
|
|
|
68
76
|
/**
|
|
@@ -176,6 +184,13 @@ export interface TurnReader {
|
|
|
176
184
|
listForks(conversationId: ConversationId): ConversationFork[];
|
|
177
185
|
countTurns(conversationId: ConversationId): number;
|
|
178
186
|
conversationStats(conversationId: ConversationId): ConversationStats;
|
|
187
|
+
/**
|
|
188
|
+
* S49R: the next conversation-monotonic turn index for a conversation —
|
|
189
|
+
* `MAX(turn_index) + 1` (or 0 if the conversation has no turns yet). Used by
|
|
190
|
+
* writers so resumed sessions (where pi's per-session `turnIndex` restarts
|
|
191
|
+
* at 0) never collide with `UNIQUE(conversation_id, turn_index)`.
|
|
192
|
+
*/
|
|
193
|
+
nextTurnIndexFor(conversationId: ConversationId): number;
|
|
179
194
|
}
|
|
180
195
|
|
|
181
196
|
/** Append-only writer — compaction engine, event handlers. Cannot prune. */
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{r as d,j as e,B as g}from"./index-iuxgtzEW.js";import{fetchContextHealth as N}from"./health-lSeDFTz7.js";import{C as i,c as h,a as u,b as j}from"./card-Cfhz9X31.js";function m(s){return s>.8?"#4ade80":s>=.5?"#facc15":"#f87171"}function v({score:s}){const l=2*Math.PI*70,o=l*(1-Math.max(0,Math.min(1,s))),n=m(s);return e.jsxs("div",{className:"health-gauge-wrap",children:[e.jsxs("svg",{width:"180",height:"180",viewBox:"0 0 180 180",children:[e.jsx("circle",{cx:"90",cy:"90",r:70,fill:"none",stroke:"#1e293b",strokeWidth:"14"}),e.jsx("circle",{cx:"90",cy:"90",r:70,fill:"none",stroke:n,strokeWidth:"14",strokeDasharray:l,strokeDashoffset:o,strokeLinecap:"round",transform:"rotate(-90 90 90)",style:{transition:"stroke-dashoffset 0.5s ease"}}),e.jsx("text",{x:"90",y:"85",textAnchor:"middle",fontSize:"32",fontWeight:"bold",fill:n,children:(s*100).toFixed(0)}),e.jsx("text",{x:"90",y:"108",textAnchor:"middle",fontSize:"14",fill:"#94a3b8",children:"/ 100"})]}),e.jsx("div",{className:"health-gauge-label",children:"Context Health"})]})}function y({trend:s}){if(s.length<2)return e.jsx("div",{className:"health-spark-empty",children:"No trend data yet"});const r=400,l=60,o=1,n=0,f=s.map((a,p)=>{const t=p/(s.length-1)*r,b=l-(a-n)/(o-n)*l;return`${t.toFixed(1)},${b.toFixed(1)}`}),c=s[s.length-1];return e.jsx("svg",{width:r,height:l,viewBox:`0 0 ${r} ${l}`,className:"health-spark",children:e.jsx("polyline",{points:f.join(" "),fill:"none",stroke:m(c),strokeWidth:"2"})})}function x({label:s,value:r}){return e.jsxs("div",{className:"health-subscore",children:[e.jsx("span",{className:"health-subscore-label",children:s}),e.jsx("div",{className:"health-subscore-bar",children:e.jsx("div",{className:"health-subscore-fill",style:{width:`${r*100}%`,background:m(r)}})}),e.jsxs("span",{className:"health-subscore-val",children:[(r*100).toFixed(0),"%"]})]})}function H(){const[s,r]=d.useState(null),[l,o]=d.useState(null),[n,f]=d.useState(!0),c=d.useCallback(()=>{N().then(r).catch(t=>o(t instanceof Error?t.message:String(t))).finally(()=>f(!1))},[]);if(d.useEffect(()=>{c();const t=setInterval(c,5e3);return()=>clearInterval(t)},[c]),n&&!s)return e.jsx("div",{className:"health-loading",children:"Loading health data…"});if(l&&!s)return e.jsxs("div",{className:"health-error",children:["Error: ",l]});if(!s)return e.jsx("div",{className:"health-empty",children:"No health data available."});const a=s.latest,p=a?.composite??0;return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2",children:[e.jsx(i,{children:e.jsx(h,{className:"flex justify-center",children:e.jsx(v,{score:p})})}),e.jsxs(i,{children:[e.jsx(u,{children:e.jsx(j,{children:"Sub-Scores"})}),e.jsxs(h,{children:[e.jsx(x,{label:"Drift",value:a?.driftScore??0}),e.jsx(x,{label:"Output Quality",value:a?.outputQuality??0}),e.jsx(x,{label:"Error Rate",value:a?.errorScore??0}),e.jsx(x,{label:"Cache Health",value:a?.cacheHealth??0}),e.jsx(x,{label:"Cache Poison",value:a?.cachePoison??0})]})]})]}),e.jsxs(i,{children:[e.jsx(u,{children:e.jsxs(j,{children:["Health Trend (last ",s.trend.length," turns)"]})}),e.jsx(h,{children:e.jsx(y,{trend:s.trend})})]}),s.alerts.length>0&&e.jsxs(i,{children:[e.jsx(u,{children:e.jsxs(j,{children:["Recent Alerts (",s.alerts.length,")"]})}),e.jsx(h,{children:e.jsx("div",{className:"flex flex-col gap-1",children:s.alerts.slice(-10).map(t=>e.jsxs("div",{className:"flex items-center gap-3 border-b border-border/50 py-1 text-sm",children:[e.jsx("span",{className:"text-xs text-muted-foreground",children:new Date(t.ts).toLocaleTimeString()}),e.jsx("span",{className:"text-muted-foreground",children:t.modelId??"(unknown)"}),e.jsx("span",{style:{color:m(t.composite)},children:(t.composite*100).toFixed(0)}),t.cachePoison<.3&&e.jsx(g,{variant:"danger",children:"CACHE POISON"})]},`${t.ts}-${t.turnIndex}`))})})]}),s.perModel.length>0&&e.jsxs(i,{children:[e.jsx(u,{children:e.jsx(j,{children:"Health by Model"})}),e.jsx(h,{children:e.jsxs("table",{className:"w-full border-collapse text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{className:"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground",children:"Model"}),e.jsx("th",{className:"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground",children:"Avg Health"}),e.jsx("th",{className:"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground",children:"Samples"})]})}),e.jsx("tbody",{children:s.perModel.map(t=>e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-3 py-2",children:t.modelId}),e.jsxs("td",{className:"px-3 py-2",style:{color:m(t.avgComposite)},children:[(t.avgComposite*100).toFixed(0),"%"]}),e.jsx("td",{className:"px-3 py-2",children:t.sampleCount})]},t.modelId))})]})})]})]})}export{H as default};
|
|
2
|
-
//# sourceMappingURL=HealthTab-D4eVI8ar.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"HealthTab-D4eVI8ar.js","sources":["../../src/tabs/HealthTab.tsx"],"sourcesContent":["/**\n * HealthTab.tsx — Context Health dashboard tab.\n * Shows composite health gauge, sparkline trend, sub-score bars,\n * alerts, and per-model comparison. Polls /api/context-health every 5s.\n */\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { fetchContextHealth, type ContextHealthResponse } from \"../api/health\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../components/ui/card\";\nimport { Badge } from \"../components/ui/badge\";\n\nfunction healthColor(score: number): string {\n\tif (score > 0.8) return \"#4ade80\";\n\tif (score >= 0.5) return \"#facc15\";\n\treturn \"#f87171\";\n}\n\nfunction HealthGauge({ score }: { score: number }): React.ReactElement {\n\tconst r = 70;\n\tconst circ = 2 * Math.PI * r;\n\tconst offset = circ * (1 - Math.max(0, Math.min(1, score)));\n\tconst color = healthColor(score);\n\treturn (\n\t\t<div className=\"health-gauge-wrap\">\n\t\t\t<svg width=\"180\" height=\"180\" viewBox=\"0 0 180 180\">\n\t\t\t\t<circle cx=\"90\" cy=\"90\" r={r} fill=\"none\" stroke=\"#1e293b\" strokeWidth=\"14\" />\n\t\t\t\t<circle\n\t\t\t\t\tcx=\"90\"\n\t\t\t\t\tcy=\"90\"\n\t\t\t\t\tr={r}\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke={color}\n\t\t\t\t\tstrokeWidth=\"14\"\n\t\t\t\t\tstrokeDasharray={circ}\n\t\t\t\t\tstrokeDashoffset={offset}\n\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\ttransform=\"rotate(-90 90 90)\"\n\t\t\t\t\tstyle={{ transition: \"stroke-dashoffset 0.5s ease\" }}\n\t\t\t\t/>\n\t\t\t\t<text x=\"90\" y=\"85\" textAnchor=\"middle\" fontSize=\"32\" fontWeight=\"bold\" fill={color}>\n\t\t\t\t\t{(score * 100).toFixed(0)}\n\t\t\t\t</text>\n\t\t\t\t<text x=\"90\" y=\"108\" textAnchor=\"middle\" fontSize=\"14\" fill=\"#94a3b8\">/ 100</text>\n\t\t\t</svg>\n\t\t\t<div className=\"health-gauge-label\">Context Health</div>\n\t\t</div>\n\t);\n}\n\nfunction HealthSparkline({ trend }: { trend: number[] }): React.ReactElement {\n\tif (trend.length < 2) return <div className=\"health-spark-empty\">No trend data yet</div>;\n\tconst w = 400;\n\tconst h = 60;\n\tconst max = 1;\n\tconst min = 0;\n\tconst pts = trend.map((v, i) => {\n\t\tconst x = (i / (trend.length - 1)) * w;\n\t\tconst y = h - ((v - min) / (max - min)) * h;\n\t\treturn `${x.toFixed(1)},${y.toFixed(1)}`;\n\t});\n\tconst last = trend[trend.length - 1];\n\treturn (\n\t\t<svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} className=\"health-spark\">\n\t\t\t<polyline points={pts.join(\" \")} fill=\"none\" stroke={healthColor(last)} strokeWidth=\"2\" />\n\t\t</svg>\n\t);\n}\n\nfunction SubScoreBar({ label, value }: { label: string; value: number }): React.ReactElement {\n\treturn (\n\t\t<div className=\"health-subscore\">\n\t\t\t<span className=\"health-subscore-label\">{label}</span>\n\t\t\t<div className=\"health-subscore-bar\">\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"health-subscore-fill\"\n\t\t\t\t\tstyle={{ width: `${value * 100}%`, background: healthColor(value) }}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t\t<span className=\"health-subscore-val\">{(value * 100).toFixed(0)}%</span>\n\t\t</div>\n\t);\n}\n\nexport default function HealthTab(): React.ReactElement {\n\tconst [data, setData] = useState<ContextHealthResponse | null>(null);\n\tconst [err, setError] = useState<string | null>(null);\n\tconst [loading, setLoading] = useState(true);\n\n\tconst poll = useCallback(() => {\n\t\tfetchContextHealth()\n\t\t\t.then(setData)\n\t\t\t.catch((e: unknown) => setError(e instanceof Error ? e.message : String(e)))\n\t\t\t.finally(() => setLoading(false));\n\t}, []);\n\n\tuseEffect(() => {\n\t\tpoll();\n\t\tconst id = setInterval(poll, 5000);\n\t\treturn () => clearInterval(id);\n\t}, [poll]);\n\n\tif (loading && !data) return <div className=\"health-loading\">Loading health data…</div>;\n\tif (err && !data) return <div className=\"health-error\">Error: {err}</div>;\n\tif (!data) return <div className=\"health-empty\">No health data available.</div>;\n\n\tconst latest = data.latest;\n\tconst composite = latest?.composite ?? 0;\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t<div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n\t\t\t\t<Card>\n\t\t\t\t\t<CardContent className=\"flex justify-center\">\n\t\t\t\t\t\t<HealthGauge score={composite} />\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Sub-Scores</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<SubScoreBar label=\"Drift\" value={latest?.driftScore ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Output Quality\" value={latest?.outputQuality ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Error Rate\" value={latest?.errorScore ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Cache Health\" value={latest?.cacheHealth ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Cache Poison\" value={latest?.cachePoison ?? 0} />\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t</div>\n\n\t\t\t<Card>\n\t\t\t\t<CardHeader>\n\t\t\t\t\t<CardTitle>Health Trend (last {data.trend.length} turns)</CardTitle>\n\t\t\t\t</CardHeader>\n\t\t\t\t<CardContent>\n\t\t\t\t\t<HealthSparkline trend={data.trend} />\n\t\t\t\t</CardContent>\n\t\t\t</Card>\n\n\t\t\t{data.alerts.length > 0 && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Recent Alerts ({data.alerts.length})</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<div className=\"flex flex-col gap-1\">\n\t\t\t\t\t\t\t{data.alerts.slice(-10).map((a) => (\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tkey={`${a.ts}-${a.turnIndex}`}\n\t\t\t\t\t\t\t\t\tclassName=\"flex items-center gap-3 border-b border-border/50 py-1 text-sm\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">{new Date(a.ts).toLocaleTimeString()}</span>\n\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">{a.modelId ?? \"(unknown)\"}</span>\n\t\t\t\t\t\t\t\t\t<span style={{ color: healthColor(a.composite) }}>\n\t\t\t\t\t\t\t\t\t\t{(a.composite * 100).toFixed(0)}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{a.cachePoison < 0.3 && <Badge variant=\"danger\">CACHE POISON</Badge>}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\n\t\t\t{data.perModel.length > 0 && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Health by Model</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<table className=\"w-full border-collapse text-sm\">\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 className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Model</th>\n\t\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Avg Health</th>\n\t\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Samples</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{data.perModel.map((m) => (\n\t\t\t\t\t\t\t\t\t<tr key={m.modelId} className=\"border-b border-border/50\">\n\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2\">{m.modelId}</td>\n\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2\" style={{ color: healthColor(m.avgComposite) }}>\n\t\t\t\t\t\t\t\t\t\t\t{(m.avgComposite * 100).toFixed(0)}%\n\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2\">{m.sampleCount}</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</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"names":["healthColor","score","HealthGauge","circ","offset","color","jsxs","jsx","HealthSparkline","trend","w","h","max","min","pts","v","i","x","y","last","SubScoreBar","label","value","HealthTab","data","setData","useState","err","setError","loading","setLoading","poll","useCallback","fetchContextHealth","e","useEffect","id","latest","composite","Card","CardContent","CardHeader","CardTitle","a","Badge","m"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{j as r}from"./index-iuxgtzEW.js";import{M as t}from"./MetricsCards-VUEcgGzo.js";import"./card-Cfhz9X31.js";import"./generateCategoricalChart-JKjK9_cB.js";import"./BarChart-C-M0LaFc.js";import"./AreaChart-BRHEfWhc.js";function e(){return r.jsx(t,{})}export{e as default};
|
|
2
|
-
//# sourceMappingURL=MetricsTab-CKqJ0Zl-.js.map
|