pi-mega-compact 0.15.4 → 0.16.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 (81) hide show
  1. package/dist/extensions/dashboard-server/api-contracts/endpoints/registry.js +7 -0
  2. package/dist/extensions/dashboard-server/api-contracts/model-thresholds.js +1 -0
  3. package/dist/extensions/dashboard-server/routes-model-thresholds.js +167 -0
  4. package/dist/extensions/dashboard-server/routes.js +3 -2
  5. package/dist/extensions/dashboard-server/server.js +6 -2
  6. package/dist/extensions/mega-events/context-handler.js +102 -9
  7. package/dist/src/store/sqlite/model-snapshots.js +21 -0
  8. package/dist/src/store/sqlite/model-thresholds.js +93 -0
  9. package/dist/src/store/sqlite/schema/core.js +10 -0
  10. package/dist/src/store/sqlite.js +1 -0
  11. package/extensions/dashboard-client/dist/assets/{AreaChart-CrmTcE7w.js → AreaChart-Y_X0Bbjd.js} +2 -2
  12. package/extensions/dashboard-client/dist/assets/{AreaChart-CrmTcE7w.js.map → AreaChart-Y_X0Bbjd.js.map} +1 -1
  13. package/extensions/dashboard-client/dist/assets/{BarChart-Cf8DTaXS.js → BarChart-B9XDb-Ob.js} +2 -2
  14. package/extensions/dashboard-client/dist/assets/{BarChart-Cf8DTaXS.js.map → BarChart-B9XDb-Ob.js.map} +1 -1
  15. package/extensions/dashboard-client/dist/assets/{CacheTab-Ce0MES6p.js → CacheTab-BUGVb1G1.js} +2 -2
  16. package/extensions/dashboard-client/dist/assets/{CacheTab-Ce0MES6p.js.map → CacheTab-BUGVb1G1.js.map} +1 -1
  17. package/extensions/dashboard-client/dist/assets/{EventsTab-DbaUuA58.js → EventsTab-dOqLYTsy.js} +2 -2
  18. package/extensions/dashboard-client/dist/assets/{EventsTab-DbaUuA58.js.map → EventsTab-dOqLYTsy.js.map} +1 -1
  19. package/extensions/dashboard-client/dist/assets/{HealthTab-BmEXkcfL.js → HealthTab-c2vezKg1.js} +2 -2
  20. package/extensions/dashboard-client/dist/assets/{HealthTab-BmEXkcfL.js.map → HealthTab-c2vezKg1.js.map} +1 -1
  21. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-n2JQrMy7.js → MaintenanceTab-bQYHIcdx.js} +2 -2
  22. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-n2JQrMy7.js.map → MaintenanceTab-bQYHIcdx.js.map} +1 -1
  23. package/extensions/dashboard-client/dist/assets/MemoryMapTab-2L8pH6y2.js +2 -0
  24. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-Dvu7vkOm.js.map → MemoryMapTab-2L8pH6y2.js.map} +1 -1
  25. package/extensions/dashboard-client/dist/assets/{MetricsTab-Q0g32AmP.js → MetricsTab-CzUx1jpo.js} +2 -2
  26. package/extensions/dashboard-client/dist/assets/{MetricsTab-Q0g32AmP.js.map → MetricsTab-CzUx1jpo.js.map} +1 -1
  27. package/extensions/dashboard-client/dist/assets/{OverviewTab-iKiEcOLL.js → OverviewTab-eCNoaclv.js} +2 -2
  28. package/extensions/dashboard-client/dist/assets/OverviewTab-eCNoaclv.js.map +1 -0
  29. package/extensions/dashboard-client/dist/assets/{ReposTab-DWpnv77y.js → ReposTab-tb0IW3NF.js} +2 -2
  30. package/extensions/dashboard-client/dist/assets/{ReposTab-DWpnv77y.js.map → ReposTab-tb0IW3NF.js.map} +1 -1
  31. package/extensions/dashboard-client/dist/assets/{SessionsTab-G0YxR1vG.js → SessionsTab-C-IoLw5h.js} +2 -2
  32. package/extensions/dashboard-client/dist/assets/{SessionsTab-G0YxR1vG.js.map → SessionsTab-C-IoLw5h.js.map} +1 -1
  33. package/extensions/dashboard-client/dist/assets/SetupTab-BvmFi8gf.js +2 -0
  34. package/extensions/dashboard-client/dist/assets/SetupTab-BvmFi8gf.js.map +1 -0
  35. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-1_LCFiJp.js → TimeSavedCard-DBH-cOlA.js} +2 -2
  36. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-1_LCFiJp.js.map → TimeSavedCard-DBH-cOlA.js.map} +1 -1
  37. package/extensions/dashboard-client/dist/assets/{TurnsTab-pqOkjE4L.js → TurnsTab-DBMgmvnX.js} +3 -3
  38. package/extensions/dashboard-client/dist/assets/{TurnsTab-pqOkjE4L.js.map → TurnsTab-DBMgmvnX.js.map} +1 -1
  39. package/extensions/dashboard-client/dist/assets/WikiTab-1LcQrXk3.js +2 -0
  40. package/extensions/dashboard-client/dist/assets/{WikiTab-99sKE4nB.js.map → WikiTab-1LcQrXk3.js.map} +1 -1
  41. package/extensions/dashboard-client/dist/assets/button-DtEsZTUE.js +2 -0
  42. package/extensions/dashboard-client/dist/assets/{button-YcV2VQEP.js.map → button-DtEsZTUE.js.map} +1 -1
  43. package/extensions/dashboard-client/dist/assets/{card-Bwxzo50p.js → card-DcfTmw-4.js} +2 -2
  44. package/extensions/dashboard-client/dist/assets/{card-Bwxzo50p.js.map → card-DcfTmw-4.js.map} +1 -1
  45. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-C6hlfMYG.js → generateCategoricalChart-BsIxUDyw.js} +2 -2
  46. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-C6hlfMYG.js.map → generateCategoricalChart-BsIxUDyw.js.map} +1 -1
  47. package/extensions/dashboard-client/dist/assets/{index-DRa2d4TF.js → index-B93cewiX.js} +14 -14
  48. package/extensions/dashboard-client/dist/assets/index-B93cewiX.js.map +1 -0
  49. package/extensions/dashboard-client/dist/assets/index-C_tCbOI0.css +1 -0
  50. package/extensions/dashboard-client/dist/assets/{switch-rU-lor0v.js → switch-Bowd7jCD.js} +2 -2
  51. package/extensions/dashboard-client/dist/assets/{switch-rU-lor0v.js.map → switch-Bowd7jCD.js.map} +1 -1
  52. package/extensions/dashboard-client/dist/assets/{toggle-h-9B4w9X.js → toggle-Bo7kJZ4u.js} +2 -2
  53. package/extensions/dashboard-client/dist/assets/{toggle-h-9B4w9X.js.map → toggle-Bo7kJZ4u.js.map} +1 -1
  54. package/extensions/dashboard-client/dist/assets/{useSSE-BxJORWx8.js → useSSE-zNhb0c3u.js} +2 -2
  55. package/extensions/dashboard-client/dist/assets/{useSSE-BxJORWx8.js.map → useSSE-zNhb0c3u.js.map} +1 -1
  56. package/extensions/dashboard-client/dist/index.html +2 -2
  57. package/extensions/dashboard-client/src/api/client.ts +38 -13
  58. package/extensions/dashboard-client/src/components/SessionContextGauges.tsx +6 -1
  59. package/extensions/dashboard-client/src/tabs/SetupTab/ThresholdsPanel.tsx +334 -0
  60. package/extensions/dashboard-client/src/tabs/SetupTab.tsx +9 -1
  61. package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +50 -17
  62. package/extensions/dashboard-server/api-contracts/index.ts +28 -3
  63. package/extensions/dashboard-server/api-contracts/model-thresholds.ts +76 -0
  64. package/extensions/dashboard-server/resolve-snapshot.ts +1 -1
  65. package/extensions/dashboard-server/routes-model-thresholds.ts +222 -0
  66. package/extensions/dashboard-server/routes.ts +11 -2
  67. package/extensions/dashboard-server/server.ts +5 -1
  68. package/extensions/mega-events/context-handler.ts +159 -50
  69. package/package.json +1 -1
  70. package/src/store/sqlite/model-snapshots.ts +99 -62
  71. package/src/store/sqlite/model-thresholds.ts +168 -0
  72. package/src/store/sqlite/schema/core.ts +10 -0
  73. package/src/store/sqlite.ts +1 -0
  74. package/extensions/dashboard-client/dist/assets/MemoryMapTab-Dvu7vkOm.js +0 -2
  75. package/extensions/dashboard-client/dist/assets/OverviewTab-iKiEcOLL.js.map +0 -1
  76. package/extensions/dashboard-client/dist/assets/SetupTab-C-y6H-F-.js +0 -2
  77. package/extensions/dashboard-client/dist/assets/SetupTab-C-y6H-F-.js.map +0 -1
  78. package/extensions/dashboard-client/dist/assets/WikiTab-99sKE4nB.js +0 -2
  79. package/extensions/dashboard-client/dist/assets/button-YcV2VQEP.js +0 -2
  80. package/extensions/dashboard-client/dist/assets/index-DRa2d4TF.js.map +0 -1
  81. package/extensions/dashboard-client/dist/assets/index-za_gmEmQ.css +0 -1
@@ -6,78 +6,115 @@ import { openStore } from "./utils.js";
6
6
 
7
7
  /** A captured model/provider snapshot (for cost estimation + dashboard). */
8
8
  export interface ModelSnapshot {
9
- provider: string;
10
- providerName: string | null;
11
- modelId: string;
12
- modelName: string | null;
13
- inputRate: number; // USD per input token
14
- outputRate: number; // USD per output token
15
- contextWindow: number;
16
- maxTokens: number;
17
- reasoning: boolean;
18
- capturedAt: number;
9
+ provider: string;
10
+ providerName: string | null;
11
+ modelId: string;
12
+ modelName: string | null;
13
+ inputRate: number; // USD per input token
14
+ outputRate: number; // USD per output token
15
+ contextWindow: number;
16
+ maxTokens: number;
17
+ reasoning: boolean;
18
+ capturedAt: number;
19
19
  }
20
20
 
21
21
  /** Persist the active model/provider for a repo (latest row wins per repo). */
22
22
  export function recordModelSnapshot(
23
- repoRoot: string,
24
- snap: Omit<ModelSnapshot, "capturedAt">,
25
- stateDir: string = getStateDir(),
23
+ repoRoot: string,
24
+ snap: Omit<ModelSnapshot, "capturedAt">,
25
+ stateDir: string = getStateDir(),
26
26
  ): void {
27
- const db = openStore(stateDir);
28
- db.prepare(
29
- `INSERT INTO model_snapshots
27
+ const db = openStore(stateDir);
28
+ db.prepare(
29
+ `INSERT INTO model_snapshots
30
30
  (repo_root, provider, provider_name, model_id, model_name, input_rate,
31
31
  output_rate, context_window, max_tokens, reasoning, captured_at)
32
32
  VALUES (@repo_root, @provider, @provider_name, @model_id, @model_name,
33
33
  @input_rate, @output_rate, @context_window, @max_tokens, @reasoning, @captured_at)`,
34
- ).run({
35
- repo_root: repoRoot,
36
- provider: snap.provider,
37
- provider_name: snap.providerName,
38
- model_id: snap.modelId,
39
- model_name: snap.modelName,
40
- input_rate: snap.inputRate,
41
- output_rate: snap.outputRate,
42
- context_window: snap.contextWindow,
43
- max_tokens: snap.maxTokens,
44
- reasoning: snap.reasoning ? 1 : 0,
45
- captured_at: Date.now(),
46
- });
34
+ ).run({
35
+ repo_root: repoRoot,
36
+ provider: snap.provider,
37
+ provider_name: snap.providerName,
38
+ model_id: snap.modelId,
39
+ model_name: snap.modelName,
40
+ input_rate: snap.inputRate,
41
+ output_rate: snap.outputRate,
42
+ context_window: snap.contextWindow,
43
+ max_tokens: snap.maxTokens,
44
+ reasoning: snap.reasoning ? 1 : 0,
45
+ captured_at: Date.now(),
46
+ });
47
47
  }
48
48
 
49
49
  /** Most recent model/provider snapshot for a repo, or undefined. */
50
- export function latestModelSnapshot(stateDir: string = getStateDir()): ModelSnapshot | undefined {
51
- const db = openStore(stateDir);
52
- const row = db
53
- .prepare(
54
- `SELECT * FROM model_snapshots ORDER BY captured_at DESC LIMIT 1`,
55
- )
56
- .get() as
57
- | {
58
- provider: string;
59
- provider_name: string | null;
60
- model_id: string;
61
- model_name: string | null;
62
- input_rate: number;
63
- output_rate: number;
64
- context_window: number;
65
- max_tokens: number;
66
- reasoning: number;
67
- captured_at: number;
68
- }
69
- | undefined;
70
- if (!row) return undefined;
71
- return {
72
- provider: row.provider,
73
- providerName: row.provider_name,
74
- modelId: row.model_id,
75
- modelName: row.model_name,
76
- inputRate: row.input_rate,
77
- outputRate: row.output_rate,
78
- contextWindow: row.context_window,
79
- maxTokens: row.max_tokens,
80
- reasoning: row.reasoning === 1,
81
- capturedAt: row.captured_at,
82
- };
50
+ export function latestModelSnapshot(
51
+ stateDir: string = getStateDir(),
52
+ ): ModelSnapshot | undefined {
53
+ const db = openStore(stateDir);
54
+ const row = db
55
+ .prepare(`SELECT * FROM model_snapshots ORDER BY captured_at DESC LIMIT 1`)
56
+ .get() as
57
+ | {
58
+ repo_root: string;
59
+ provider: string;
60
+ provider_name: string | null;
61
+ model_id: string;
62
+ model_name: string | null;
63
+ input_rate: number;
64
+ output_rate: number;
65
+ context_window: number;
66
+ max_tokens: number;
67
+ reasoning: number;
68
+ captured_at: number;
69
+ }
70
+ | undefined;
71
+ if (!row) return undefined;
72
+ return {
73
+ provider: row.provider,
74
+ providerName: row.provider_name,
75
+ modelId: row.model_id,
76
+ modelName: row.model_name,
77
+ inputRate: row.input_rate,
78
+ outputRate: row.output_rate,
79
+ contextWindow: row.context_window,
80
+ maxTokens: row.max_tokens,
81
+ reasoning: row.reasoning === 1,
82
+ capturedAt: row.captured_at,
83
+ };
84
+ }
85
+
86
+ /** All model snapshots (for the dashboard Thresholds table — deduped by
87
+ * caller). Returns NULL columns as-is; callers should default missing
88
+ * context_window/max_tokens to 0. */
89
+ export function listModelSnapshots(
90
+ stateDir: string = getStateDir(),
91
+ ): ModelSnapshot[] {
92
+ const db = openStore(stateDir);
93
+ const rows = db
94
+ .prepare(`SELECT * FROM model_snapshots ORDER BY captured_at DESC`)
95
+ .all() as Array<{
96
+ repo_root: string;
97
+ provider: string;
98
+ provider_name: string | null;
99
+ model_id: string;
100
+ model_name: string | null;
101
+ input_rate: number;
102
+ output_rate: number;
103
+ context_window: number;
104
+ max_tokens: number;
105
+ reasoning: number;
106
+ captured_at: number;
107
+ }>;
108
+ return rows.map((row) => ({
109
+ provider: row.provider,
110
+ providerName: row.provider_name,
111
+ modelId: row.model_id,
112
+ modelName: row.model_name,
113
+ inputRate: row.input_rate,
114
+ outputRate: row.output_rate,
115
+ contextWindow: row.context_window,
116
+ maxTokens: row.max_tokens,
117
+ reasoning: row.reasoning === 1,
118
+ capturedAt: row.captured_at,
119
+ }));
83
120
  }
@@ -0,0 +1,168 @@
1
+ /**
2
+ * model-thresholds.ts — `model_thresholds` table (per-model compaction
3
+ * thresholds, S52 / v0.16.1).
4
+ *
5
+ * Users wanted (2026-08-03 incident follow-up) to tune the compaction safety
6
+ * margin + fire point PER MODEL — different providers'/models' context
7
+ * windows range 8K to 1M+, so one global % is wrong. This table stores the
8
+ * override; absence of a row means "use env/default" (the existing
9
+ * MEGACOMPACT_TIER + the fix-2 default 5% safety margin).
10
+ *
11
+ * Guardrails: PREVENT-001 (null-safe JSON never touches this), PREVENT-002
12
+ * (parameterized — no string concat into SQL), PREVENT-011 (no `any`).
13
+ */
14
+ import type { DatabaseSync } from "node:sqlite";
15
+ import { getStateDir } from "../../store.js";
16
+ import { openStore } from "./utils.js";
17
+
18
+ /** A per-model compaction threshold override. */
19
+ export interface ModelThreshold {
20
+ modelId: string;
21
+ /** 0-20: % of context window reserved AFTER summary + maxOutput as a
22
+ * safety buffer against turn-growth-between-gates. 0% = aggressive
23
+ * (risks "too long even after compaction" errors); 20% = conservative. */
24
+ safetyMarginPct: number;
25
+ /** 10-90: % of context window at which compaction FIRES. Lower = compact
26
+ * earlier (more headroom); higher = compact later (more context before
27
+ * compaction). Default 70 (matches the high tier in config/dedup.ts). */
28
+ firePointPct: number;
29
+ updatedAt: number | null;
30
+ }
31
+
32
+ /** Default safety margin (%) when no per-model override exists. */
33
+ export const DEFAULT_SAFETY_MARGIN_PCT = 5;
34
+ /** Default fire point (%) when no per-model + no env override exists. */
35
+ export const DEFAULT_FIRE_POINT_PCT = 70;
36
+ export const MIN_SAFETY_MARGIN_PCT = 0;
37
+ export const MAX_SAFETY_MARGIN_PCT = 20;
38
+ export const MIN_FIRE_POINT_PCT = 10;
39
+ export const MAX_FIRE_POINT_PCT = 90;
40
+
41
+ function rowToThreshold(r: Record<string, unknown>): ModelThreshold {
42
+ return {
43
+ modelId: r.model_id as string,
44
+ safetyMarginPct: r.safety_margin_pct as number,
45
+ firePointPct: r.fire_point_pct as number,
46
+ updatedAt: (r.updated_at as number) ?? null,
47
+ };
48
+ }
49
+
50
+ /** Get the per-model threshold override, or null when none exists. */
51
+ export function getModelThreshold(
52
+ modelId: string,
53
+ stateDir: string = getStateDir(),
54
+ ): ModelThreshold | null {
55
+ const db = openStore(stateDir);
56
+ const row = db
57
+ .prepare(
58
+ "SELECT model_id, safety_margin_pct, fire_point_pct, updated_at FROM model_thresholds WHERE model_id = ?",
59
+ )
60
+ .get(modelId) as Record<string, unknown> | undefined;
61
+ return row ? rowToThreshold(row) : null;
62
+ }
63
+
64
+ /** List ALL stored per-model thresholds (for the dashboard table). */
65
+ export function listModelThresholds(
66
+ stateDir: string = getStateDir(),
67
+ ): ModelThreshold[] {
68
+ const db = openStore(stateDir);
69
+ const rows = db
70
+ .prepare(
71
+ "SELECT model_id, safety_margin_pct, fire_point_pct, updated_at FROM model_thresholds ORDER BY updated_at DESC",
72
+ )
73
+ .all() as Record<string, unknown>[];
74
+ return rows.map(rowToThreshold);
75
+ }
76
+
77
+ /** Validate a threshold value range. Throws on out-of-bounds. */
78
+ export function assertValidPct(
79
+ pct: number,
80
+ min: number,
81
+ max: number,
82
+ label: string,
83
+ ): void {
84
+ if (!Number.isFinite(pct)) {
85
+ throw new Error(`${label} must be a finite number, got ${pct}`);
86
+ }
87
+ if (pct < min || pct > max) {
88
+ throw new Error(`${label} must be in [${min}, ${max}], got ${pct}`);
89
+ }
90
+ }
91
+
92
+ /** Upsert a per-model threshold. Validates ranges (0-20, 10-90). */
93
+ export function putModelThreshold(
94
+ modelId: string,
95
+ safetyMarginPct: number,
96
+ firePointPct: number,
97
+ stateDir: string = getStateDir(),
98
+ ): ModelThreshold {
99
+ if (!modelId || typeof modelId !== "string") {
100
+ throw new Error("modelId is required");
101
+ }
102
+ assertValidPct(
103
+ safetyMarginPct,
104
+ MIN_SAFETY_MARGIN_PCT,
105
+ MAX_SAFETY_MARGIN_PCT,
106
+ "safetyMarginPct",
107
+ );
108
+ assertValidPct(
109
+ firePointPct,
110
+ MIN_FIRE_POINT_PCT,
111
+ MAX_FIRE_POINT_PCT,
112
+ "firePointPct",
113
+ );
114
+ const db: DatabaseSync = openStore(stateDir);
115
+ const now = Date.now();
116
+ db.prepare(
117
+ `INSERT INTO model_thresholds (model_id, safety_margin_pct, fire_point_pct, updated_at)
118
+ VALUES (?, ?, ?, ?)
119
+ ON CONFLICT(model_id) DO UPDATE SET
120
+ safety_margin_pct = excluded.safety_margin_pct,
121
+ fire_point_pct = excluded.fire_point_pct,
122
+ updated_at = excluded.updated_at`,
123
+ ).run(modelId, safetyMarginPct, firePointPct, now);
124
+ return {
125
+ modelId,
126
+ safetyMarginPct,
127
+ firePointPct,
128
+ updatedAt: now,
129
+ };
130
+ }
131
+
132
+ /** Delete a per-model threshold (revert to env/defaults). */
133
+ export function deleteModelThreshold(
134
+ modelId: string,
135
+ stateDir: string = getStateDir(),
136
+ ): boolean {
137
+ const db = openStore(stateDir);
138
+ const r = db
139
+ .prepare("DELETE FROM model_thresholds WHERE model_id = ?")
140
+ .run(modelId);
141
+ return (r.changes ?? 0) > 0;
142
+ }
143
+
144
+ /**
145
+ * Resolve the EFFECTIVE threshold value for a model: per-model override if
146
+ * present, otherwise the fallback. Used by context-handler.ts to pick the
147
+ * safety margin + fire point without each call duplicating the lookup.
148
+ */
149
+ export function resolveModelThreshold(
150
+ modelId: string | null | undefined,
151
+ opts: {
152
+ safetyMarginFallback: number;
153
+ firePointFallback: number;
154
+ stateDir?: string;
155
+ },
156
+ ): { safetyMarginPct: number; firePointPct: number } {
157
+ const fallback = {
158
+ safetyMarginPct: opts.safetyMarginFallback,
159
+ firePointPct: opts.firePointFallback,
160
+ };
161
+ if (!modelId) return fallback;
162
+ const row = getModelThreshold(modelId, opts.stateDir ?? getStateDir());
163
+ if (!row) return fallback;
164
+ return {
165
+ safetyMarginPct: row.safetyMarginPct,
166
+ firePointPct: row.firePointPct,
167
+ };
168
+ }
@@ -159,6 +159,16 @@ export const CORE_DDL = `
159
159
  );
160
160
  CREATE INDEX IF NOT EXISTS idx_model_repo ON model_snapshots(repo_root);
161
161
 
162
+ -- Per-model compaction thresholds (S52 / v0.16.1). User-tunable % values
163
+ -- that override the env tier defaults + the fix-2 safety margin. One row
164
+ -- per model_id; NULL fallback to env/defaults when no row exists.
165
+ CREATE TABLE IF NOT EXISTS model_thresholds (
166
+ model_id TEXT PRIMARY KEY,
167
+ safety_margin_pct REAL NOT NULL, -- 0-20: reserve after summary+maxOutput
168
+ fire_point_pct REAL NOT NULL, -- 10-90: % of window where compaction fires
169
+ updated_at INTEGER
170
+ );
171
+
162
172
  -- Lessons learned (future recall/browse feature seed).
163
173
  CREATE TABLE IF NOT EXISTS lessons (
164
174
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -16,6 +16,7 @@ export * from "./sqlite/checkpoints.js";
16
16
  export * from "./sqlite/session-state.js";
17
17
  export * from "./sqlite/stats.js";
18
18
  export * from "./sqlite/model-snapshots.js";
19
+ export * from "./sqlite/model-thresholds.js";
19
20
  export * from "./sqlite/raptor.js";
20
21
  export * from "./sqlite/raw-transcript.js";
21
22
  export * from "./sqlite/dedup-mirror.js";
@@ -1,2 +0,0 @@
1
- import{j as e,r as u,Z as V,$ as z}from"./index-DRa2d4TF.js";import{T as P}from"./toggle-h-9B4w9X.js";const S={checkpoint:"#6366f1",turn:"#58a6ff","turn-content":"#22c55e",memory:"#f59e0b"},U={checkpoint:"Checkpoint",turn:"Turn","turn-content":"Turn Content",memory:"Memory"};function Y(t,s){return`M 0,-${s} L ${t},0 L 0,${s} L -${t},0 Z`}function Q(t){return!t||t==="active"?1:.5}function Z(t,s,l,h,n,p,c){const x=p?S[h]??"#6366f1":"#d1d5db",i=n?"#ef4444":"none",m=Q(c);switch(h){case"checkpoint":return e.jsx("circle",{cx:t,cy:s,r:n?l+3:l,fill:x,opacity:m,stroke:i,strokeWidth:n?2:0});case"turn":return e.jsx("circle",{cx:t,cy:s,r:n?l+3:l,fill:"none",stroke:x,strokeWidth:2.5,opacity:m});case"turn-content":return e.jsxs("g",{children:[e.jsx("circle",{cx:t,cy:s,r:n?l+3:l,fill:"none",stroke:x,strokeWidth:2.5,opacity:m}),e.jsx("circle",{cx:t,cy:s,r:Math.max(3,l*.3),fill:x,opacity:m})]});case"memory":const d=l*.85,g=l*.85;return e.jsx("path",{d:Y(d,g),transform:`translate(${t}, ${s})`,fill:x,opacity:m,stroke:i,strokeWidth:n?2:0})}}const X=5e3,O=.005,D=.01,_=.9,J=.1,L=50;function K(t){const s=t.nodes.map((n,p)=>{const c=2*Math.PI*p/Math.max(t.nodes.length,1),x=200;return{...n,pos:{x:Math.cos(c)*x+300,y:Math.sin(c)*x+200},vx:0,vy:0,pinned:!1}}),l=new Map;s.forEach((n,p)=>l.set(n.id,p));const h=t.edges.map(n=>{const p=l.get(n.source),c=l.get(n.target);return p===void 0||c===void 0||p===c?null:{source:p,target:c,weight:n.weight,type:n.type}}).filter(n=>n!==null);return{nodes:s,edges:h}}function ee(t){const{nodes:s,edges:l}=t,h=s.length;if(h!==0)for(let n=0;n<h;n++){if(s[n].pinned)continue;let p=0,c=0;for(let i=0;i<h;i++){if(n===i)continue;const m=s[n].pos.x-s[i].pos.x,d=s[n].pos.y-s[i].pos.y,g=m*m+d*d||1,v=X/g,y=Math.sqrt(g);p+=m/y*v,c+=d/y*v}for(const i of l){let m;if(i.source===n)m=i.target;else if(i.target===n)m=i.source;else continue;const d=s[m].pos.x-s[n].pos.x,g=s[m].pos.y-s[n].pos.y;p+=d*O*i.weight,c+=g*O*i.weight}p-=s[n].pos.x*D,c-=s[n].pos.y*D,s[n].vx=(s[n].vx+p)*_,s[n].vy=(s[n].vy+c)*_;const x=Math.sqrt(s[n].vx*s[n].vx+s[n].vy*s[n].vy);x>L&&(s[n].vx=s[n].vx/x*L,s[n].vy=s[n].vy/x*L),x>J&&(s[n].pos.x+=s[n].vx,s[n].pos.y+=s[n].vy)}}const te=300,re=.3,k=900,w=600,f=40;function M(t){switch(t){case"temporal":return"#6366f1";case"semantic":return"#22c55e";case"raptor_parent":return"#f59e0b"}}const N=({color:t,shape:s,label:l})=>{const h={display:"inline-block",width:"10px",height:"10px",verticalAlign:"middle",marginRight:"3px",borderRadius:s.startsWith("hollow")||s==="filled-circle"?"50%":void 0};let n;return s==="line"?e.jsx("span",{style:{color:t,marginRight:"6px"},children:l}):(s==="filled-circle"?n=e.jsx("span",{style:{...h,backgroundColor:t}}):s==="hollow-circle"?n=e.jsx("span",{style:{...h,border:`2px solid ${t}`,backgroundColor:"transparent"}}):s==="hollow-ring"?n=e.jsx("span",{style:{...h,border:`2px solid ${t}`,backgroundColor:"transparent",position:"relative"},children:e.jsx("span",{style:{display:"block",width:"4px",height:"4px",borderRadius:"50%",backgroundColor:t,margin:"2px auto"}})}):n=e.jsx("span",{style:{...h,clipPath:"polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)",backgroundColor:t}}),e.jsxs(e.Fragment,{children:[e.jsxs("span",{className:"memory-map-legend-item",children:[n,l]})," "]}))},se=()=>{const[t,s]=u.useState(null),[l,h]=u.useState(!0),[n,p]=u.useState(null),[c,x]=u.useState(null),[i,m]=u.useState(""),[d]=u.useState(""),[g,v]=u.useState(0),y=u.useRef(null),T=u.useRef(0);u.useEffect(()=>{let r=!1;const o=new URLSearchParams;return d&&o.set("sessionId",d),o.set("threshold","0.7"),o.set("maxEdgesPerNode","4"),h(!0),p(null),fetch(`/api/memory-map?${o.toString()}`).then(a=>{if(!a.ok)throw new Error(`HTTP ${a.status}`);return a.json()}).then(a=>{r||(s(a),y.current=K(a),v(1),h(!1))}).catch(a=>{r||(p(a instanceof Error?a.message:"Unknown error"),h(!1))}),()=>{r=!0,cancelAnimationFrame(T.current)}},[d]),u.useEffect(()=>{if(!y.current||g===0)return;const r=y.current;let o=0;const a=()=>{o>=te||(ee(r),o++,v(o),T.current=requestAnimationFrame(a))};return T.current=requestAnimationFrame(a),()=>cancelAnimationFrame(T.current)},[g]);const F=u.useCallback((r,o)=>{const a=y.current;a&&(a.nodes[o].pinned=!a.nodes[o].pinned,a.nodes[o].pinned||(a.nodes[o].vx=0,a.nodes[o].vy=0),x(b=>b?.id===r.id?null:r),v(b=>b+1))},[]),A=u.useMemo(()=>{const r=y.current;if(!r||!i)return null;const o=i.toLowerCase();return new Set(r.nodes.map((a,b)=>a.summaryTruncated.toLowerCase().includes(o)||a.label.toLowerCase().includes(o)?b:-1).filter(a=>a>=0))},[i]),C=u.useMemo(()=>{const r=t?.validation;return r?r.gatesPassed<r.gatesRun?{level:"red",label:"Gate failure"}:r.warnings.length>0?{level:"yellow",label:`${r.warnings.length} warning(s)`}:{level:"green",label:"Healthy"}:{level:"yellow",label:"No validation"}},[t?.validation]),W={green:"#22c55e",yellow:"#eab308",red:"#ef4444"},H=u.useMemo(()=>{const r=t?.validation;if(!r)return`${t?.metadata.totalNodes??0} nodes`;const o=[];return r.sources.checkpoint>0&&o.push(`${r.sources.checkpoint} checkpoints`),r.sources.turn>0&&o.push(`${r.sources.turn} turns`),r.sources.turnContent>0&&o.push(`${r.sources.turnContent} turn-content`),r.sources.memory>0&&o.push(`${r.sources.memory} memories`),o.length===0&&o.push(`${t?.metadata.totalNodes??0} nodes`),o.join(" · ")},[t?.validation,t?.metadata.totalNodes]),I=u.useMemo(()=>{const r=t?.validation;if(!r)return"";const o=[];return o.push(r.sources.checkpoint>0?"✓ checkpoints":"✗ checkpoints"),o.push(r.sources.turn>0?"✓ turns":"✗ turns"),o.push(r.sources.turnContent>0?"✓ turn-content":"✗ turn-content"),o.push(r.sources.memory>0?"✓ memories":"✗ memories"),o.join(" | ")},[t?.validation]),B=u.useMemo(()=>{const r=t?.validation;return r?r.sources.checkpoint===0&&r.sources.turn===0&&r.sources.turnContent===0&&r.sources.memory===0:t?.metadata.totalNodes===0},[t?.validation,t?.metadata.totalNodes]);if(l)return e.jsx("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-center text-sm text-muted-foreground",children:"Loading memory graph..."});if(n)return e.jsxs("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-center text-sm",children:[e.jsxs("p",{className:"text-red-400",children:["Failed to load memory map: ",n]}),e.jsx("button",{className:"mt-3 rounded-md border border-border px-3 py-1.5 text-sm transition-colors hover:bg-bg-elevated hover:text-foreground",onClick:()=>v(r=>r+1),children:"Retry"})]});if(!t||!y.current||B)return e.jsx("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground",children:e.jsx("p",{children:"Memories appear after your first compaction. The graph shows checkpoints (compaction summaries) linked by semantic similarity and time. Run a longer session or lower the compaction tier to see it sooner."})});const j=y.current;return e.jsxs("div",{className:"flex flex-col gap-3",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[e.jsx("input",{type:"text",placeholder:"Search memories...",value:i,onInput:r=>m(r.target.value),className:"w-56 rounded-md border border-border bg-bg-elevated/50 px-3 py-2 text-sm outline-none transition-colors focus:border-primary"}),e.jsxs("span",{className:"text-xs text-muted-foreground",children:[e.jsx("span",{children:H})," · ",e.jsxs("span",{children:[t.edges.length," edges"]})]}),e.jsx("span",{className:"ml-auto rounded-full px-2 py-1 text-[11px] font-semibold text-white",style:{backgroundColor:W[C.level]??"#6b7280"},title:`Validation: ${C.label}`,children:C.level==="green"?"Healthy":C.level==="yellow"?"Warnings":"Critical"})]}),I?e.jsx("div",{className:"text-[11px] text-muted-foreground",children:I}):null,e.jsxs("div",{className:"flex flex-wrap items-center gap-x-4 gap-y-1 rounded-lg border border-border/60 bg-bg-card px-3 py-2 text-xs",children:[e.jsx("span",{className:"font-semibold text-muted-foreground",children:"Nodes:"}),e.jsx(N,{type:"node",color:S.checkpoint,shape:"filled-circle",label:"Checkpoint"}),e.jsx(N,{type:"node",color:S.turn,shape:"hollow-circle",label:"Turn"}),e.jsx(N,{type:"node",color:S["turn-content"],shape:"hollow-ring",label:"Turn Content"}),e.jsx(N,{type:"node",color:S.memory,shape:"diamond",label:"Memory"}),e.jsx("span",{className:"font-semibold text-muted-foreground",children:"Edges:"}),e.jsx(N,{type:"edge",color:M("semantic"),shape:"line",label:"Semantic"}),e.jsx(N,{type:"edge",color:M("temporal"),shape:"line",label:"Temporal"}),e.jsx(N,{type:"edge",color:M("raptor_parent"),shape:"line",label:"Raptor"})]}),e.jsxs("svg",{width:k,height:w,viewBox:`0 0 ${k} ${w}`,className:"memory-map-svg",children:[j.edges.map((r,o)=>{const a=j.nodes[r.source],b=j.nodes[r.target],R=M(r.type),E=Math.max(1,r.weight*2.5);return e.jsx("line",{x1:f+a.pos.x/(j.nodes.length||1)*(k-2*f),y1:f+a.pos.y/(j.nodes.length||1)*(w-2*f),x2:f+b.pos.x/(j.nodes.length||1)*(k-2*f),y2:f+b.pos.y/(j.nodes.length||1)*(w-2*f),stroke:R,strokeWidth:E,opacity:re},`edge-${o}`)}),j.nodes.map((r,o)=>{const a=f+r.pos.x/(j.nodes.length||1)*(k-2*f),b=f+r.pos.y/(j.nodes.length||1)*(w-2*f),R=Math.min(20,8+r.tokenEstimate/500),E=A?A.has(o):!0,G=c?.id===r.id,q=r.label.length>20?r.label.slice(0,18)+"...":r.label;return e.jsxs("g",{className:"memory-map-node-group",onClick:()=>F(r,o),style:{cursor:"pointer"},children:[Z(a,b,R,r.nodeType,G,E,r.dedupStatus),e.jsx("text",{x:a,y:b+R+12,textAnchor:"middle",fontSize:"9px",fill:"#374151",className:"memory-map-label",children:q})]},`node-${o}`)})]}),c?e.jsxs("div",{className:"rounded-lg border border-border/60 bg-bg-card p-4 text-sm",children:[e.jsxs("h3",{className:"font-heading text-base font-semibold",children:[e.jsx("span",{className:"mr-1.5 text-xs font-normal text-muted-foreground",children:U[c.nodeType]}),c.label]}),e.jsx("p",{className:"mt-1",children:c.summaryTruncated}),c.topicSummary?e.jsxs("p",{className:"mt-1",children:["Topic: ",c.topicSummary]}):null,e.jsxs("p",{className:"mt-1",children:["Tokens: ",c.tokenEstimate," | Decisions: ",c.decisionCount]}),e.jsxs("p",{children:["Session: ",c.sessionId]}),e.jsxs("p",{children:["Raptor Level: ",c.raptorLevel]}),e.jsx("p",{className:"mt-1 text-muted-foreground",children:c.textSnippet})]}):null]})};function ne(t){return t===0?"#d4a017":t===1?"#3b82f6":"#14b8a6"}function $(t){return t?new Date(t).toLocaleString():"—"}function oe({node:t}){const s=ne(t.level),l=t.children.length>0?`${t.children.length} child${t.children.length===1?"":"ren"}`:"leaf";return e.jsxs("div",{style:{background:"#14142b",border:`1px solid ${s}`,borderRadius:"8px",padding:"0.75rem 1rem",marginBottom:"0.6rem",marginLeft:t.level*24,maxWidth:"90%"},children:[e.jsxs("div",{style:{display:"flex",gap:"0.75rem",alignItems:"center",marginBottom:"0.35rem"},children:[e.jsxs("span",{style:{display:"inline-block",background:s,color:"#fff",borderRadius:"10px",padding:"1px 8px",fontSize:"11px",fontWeight:600},children:["L",t.level]}),e.jsxs("span",{style:{fontSize:"12px",color:"#6b7280"},children:[l," · ",t.tokenEstimate," tok · ",t.qualityMarker]}),e.jsx("span",{style:{fontSize:"12px",color:"#6b7280",marginLeft:"auto"},children:$(t.builtAt)})]}),e.jsx("div",{style:{fontSize:"0.92rem",color:"#e5e7eb"},children:t.summary})]})}function le(){const[t,s]=u.useState(null),[l,h]=u.useState(null),[n,p]=u.useState(!0),[c,x]=u.useState(null);if(u.useEffect(()=>{let d=!1;return p(!0),x(null),Promise.all([V(),z()]).then(([g,v])=>{d||(s(g),h(v),p(!1))}).catch(g=>{d||(x(g instanceof Error?g.message:"Unknown error"),p(!1))}),()=>{d=!0}},[]),n)return e.jsx("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-center text-sm text-muted-foreground",children:"Loading RAPTOR tree..."});if(c)return e.jsx("div",{className:"rounded-lg border border-dashed border-border bg-bg-card p-8 text-center text-sm",children:e.jsxs("p",{className:"text-red-400",children:["Failed to load RAPTOR tree: ",c]})});if(!t||t.empty||t.nodes.length===0)return e.jsx("div",{className:"rounded-lg border border-dashed border-border bg-bg-card p-8 text-center text-sm text-muted-foreground",children:e.jsx("p",{children:"No RAPTOR tree built yet — run a compaction (the tree builds during compaction)."})});const i=l&&!l.empty?l.builds[0]:null,m=i?.coherenceScore!=null?Math.round(i.coherenceScore*100):null;return e.jsxs("div",{className:"w-full p-4",children:[e.jsxs("div",{className:"mb-4 flex items-center gap-6 text-sm text-muted-foreground",children:[e.jsxs("span",{children:[e.jsx("strong",{children:t.nodes.length})," nodes"]}),e.jsxs("span",{children:[e.jsx("strong",{children:t.levels+1})," levels"]}),e.jsxs("span",{children:["Built ",$(t.builtAt??0)]}),i&&e.jsxs(e.Fragment,{children:[e.jsxs("span",{children:["Coherence:"," ",e.jsx("strong",{style:{color:m!=null&&m>=70?"#22c55e":"#f59e0b"},children:m!=null?`${m}%`:"—"})]}),e.jsxs("span",{children:["Leaves: ",e.jsx("strong",{children:i.leafCount})]}),e.jsxs("span",{children:["Depth: ",e.jsx("strong",{children:i.depth})]}),i.timedOut&&e.jsx("span",{style:{color:"#ef4444"},children:"budget timeout"})]})]}),l&&!l.empty&&l.builds.length>1&&e.jsxs("details",{className:"mb-4",children:[e.jsxs("summary",{className:"cursor-pointer text-xs text-muted-foreground",children:["Build history (",l.builds.length," builds)"]}),e.jsx("div",{className:"mt-2 space-y-1",children:l.builds.slice(0,10).map(d=>e.jsxs("div",{className:"flex items-center gap-4 text-xs text-muted-foreground",children:[e.jsx("span",{children:$(d.completedAt)}),e.jsxs("span",{children:[d.nodeCount," nodes"]}),e.jsxs("span",{children:[d.leafCount," leaves"]}),e.jsxs("span",{children:["depth ",d.depth]}),e.jsxs("span",{children:["coherence"," ",d.coherenceScore!=null?`${Math.round(d.coherenceScore*100)}%`:"—"]}),d.timedOut&&e.jsx("span",{style:{color:"#ef4444"},children:"timeout"})]},d.buildId))})]}),t.nodes.map(d=>e.jsx(oe,{node:d},d.id))]})}const ie=()=>{const[t,s]=u.useState("map");return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("nav",{className:"flex gap-2 border-b border-border pb-2 pt-2","aria-label":"Memory map sections",children:[e.jsx(P,{pressed:t==="map",onClick:()=>s("map"),"aria-label":"Memory Map view",children:"Memory Map"}),e.jsx(P,{pressed:t==="raptor",onClick:()=>s("raptor"),"aria-label":"RAPTOR Tree view",children:"RAPTOR Tree"})]}),t==="map"&&e.jsx(se,{}),t==="raptor"&&e.jsx(le,{})]})};export{ie as default};
2
- //# sourceMappingURL=MemoryMapTab-Dvu7vkOm.js.map