pi-mega-compact 0.8.8 → 0.8.10

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 (88) hide show
  1. package/dist/extensions/dashboard-server/api-contracts/core.js +7 -0
  2. package/dist/extensions/dashboard-server/api-contracts/endpoints.js +104 -0
  3. package/dist/extensions/dashboard-server/api-contracts/game.js +9 -0
  4. package/dist/extensions/dashboard-server/api-contracts/index.js +9 -0
  5. package/dist/extensions/dashboard-server/api-contracts/infrastructure.js +10 -0
  6. package/dist/extensions/dashboard-server/api-contracts/multi-repo.js +9 -0
  7. package/dist/extensions/dashboard-server/api-contracts/snapshot.js +8 -0
  8. package/dist/extensions/dashboard-server/api-contracts.js +8 -0
  9. package/dist/extensions/dashboard-server/api-contracts.test.js +869 -0
  10. package/dist/extensions/dashboard-server/auth.js +18 -0
  11. package/dist/extensions/dashboard-server/helpers.js +37 -0
  12. package/dist/extensions/dashboard-server/html/all-repos-tab.js +26 -0
  13. package/dist/extensions/dashboard-server/html/body-open.js +23 -0
  14. package/dist/extensions/dashboard-server/html/current-repo-tab.js +130 -0
  15. package/dist/extensions/dashboard-server/html/head-open.js +16 -0
  16. package/dist/extensions/dashboard-server/html/high-score-tab.js +25 -0
  17. package/dist/extensions/dashboard-server/html/repo-detail-modal.js +26 -0
  18. package/dist/extensions/dashboard-server/html/script.js +259 -0
  19. package/dist/extensions/dashboard-server/html/styles.js +103 -0
  20. package/dist/extensions/dashboard-server/html/summary-tab.js +19 -0
  21. package/dist/extensions/dashboard-server/html-template.js +41 -0
  22. package/dist/extensions/dashboard-server/server.js +191 -39
  23. package/dist/extensions/dashboard-server/tailscale.js +8 -0
  24. package/dist/extensions/dashboard-server/types.js +4 -0
  25. package/dist/src/store/sqlite/connection.js +35 -0
  26. package/dist/src/store/sqlite/index-store.js +167 -0
  27. package/dist/src/store/sqlite/memory.js +54 -0
  28. package/dist/src/store/sqlite/minhash-lsh.js +47 -0
  29. package/dist/src/store/sqlite/sessions.js +39 -0
  30. package/dist/src/store/sqlite/transaction.js +19 -0
  31. package/dist/src/vectorStore/add.js +260 -0
  32. package/dist/src/vectorStore/dedup.js +52 -0
  33. package/dist/src/vectorStore/index.js +10 -0
  34. package/dist/src/vectorStore/queries.js +83 -0
  35. package/dist/src/vectorStore/search.js +95 -0
  36. package/dist/src/vectorStore/session.js +19 -0
  37. package/dist/src/vectorStore/store.js +105 -0
  38. package/dist/src/vectorStore/types.js +6 -0
  39. package/dist/src/vectorStore/utils.js +23 -0
  40. package/extensions/dashboard-client/package-lock.json +1771 -0
  41. package/extensions/dashboard-client/package.json +27 -0
  42. package/extensions/dashboard-client/src/App.tsx +82 -0
  43. package/extensions/dashboard-client/src/api/client.ts +145 -0
  44. package/extensions/dashboard-client/src/components/CacheStatusPerModel.tsx +44 -0
  45. package/extensions/dashboard-client/src/components/CompressionCard.tsx +61 -0
  46. package/extensions/dashboard-client/src/components/ContextGauge.tsx +59 -0
  47. package/extensions/dashboard-client/src/components/DataSafetyCard.tsx +49 -0
  48. package/extensions/dashboard-client/src/components/ErrorBoundary.tsx +53 -0
  49. package/extensions/dashboard-client/src/components/EventCategoryFilter.tsx +16 -0
  50. package/extensions/dashboard-client/src/components/EventStream.tsx +152 -0
  51. package/extensions/dashboard-client/src/components/LoadingSpinner.tsx +7 -0
  52. package/extensions/dashboard-client/src/components/MemoryStatusCard.tsx +24 -0
  53. package/extensions/dashboard-client/src/components/ModelBadge.tsx +41 -0
  54. package/extensions/dashboard-client/src/components/PerfChart.tsx +160 -0
  55. package/extensions/dashboard-client/src/components/RepoDetailModal.tsx +126 -0
  56. package/extensions/dashboard-client/src/components/RepoTable.tsx +150 -0
  57. package/extensions/dashboard-client/src/components/SessionInfo.tsx +65 -0
  58. package/extensions/dashboard-client/src/components/SummaryTiles.tsx +52 -0
  59. package/extensions/dashboard-client/src/components/TabBar.tsx +34 -0
  60. package/extensions/dashboard-client/src/components/TriggerStatus.tsx +62 -0
  61. package/extensions/dashboard-client/src/hooks/useApi.ts +77 -0
  62. package/extensions/dashboard-client/src/hooks/useSSE.ts +87 -0
  63. package/extensions/dashboard-client/src/index.html +12 -0
  64. package/extensions/dashboard-client/src/main.tsx +23 -0
  65. package/extensions/dashboard-client/src/styles/base.css +121 -0
  66. package/extensions/dashboard-client/src/styles/overview-events.css +318 -0
  67. package/extensions/dashboard-client/src/styles/repos-metrics.css +307 -0
  68. package/extensions/dashboard-client/src/tabs/ConfigTab.tsx +16 -0
  69. package/extensions/dashboard-client/src/tabs/EventsTab.tsx +37 -0
  70. package/extensions/dashboard-client/src/tabs/MetricsTab.tsx +49 -0
  71. package/extensions/dashboard-client/src/tabs/OverviewTab.tsx +80 -0
  72. package/extensions/dashboard-client/src/tabs/ReposTab.tsx +59 -0
  73. package/extensions/dashboard-client/tsconfig.json +28 -0
  74. package/extensions/dashboard-client/vite.config.ts +38 -0
  75. package/extensions/dashboard-server/api-contracts/core.ts +302 -0
  76. package/extensions/dashboard-server/api-contracts/endpoints.ts +476 -0
  77. package/extensions/dashboard-server/api-contracts/game.ts +145 -0
  78. package/extensions/dashboard-server/api-contracts/index.ts +159 -0
  79. package/extensions/dashboard-server/api-contracts/infrastructure.ts +465 -0
  80. package/extensions/dashboard-server/api-contracts/multi-repo.ts +328 -0
  81. package/extensions/dashboard-server/api-contracts/snapshot.ts +386 -0
  82. package/extensions/dashboard-server/api-contracts.test.ts +1050 -0
  83. package/extensions/dashboard-server/api-contracts.ts +96 -0
  84. package/extensions/dashboard-server/auth.ts +20 -0
  85. package/extensions/dashboard-server/server.ts +862 -582
  86. package/extensions/dashboard-server/tailscale.ts +7 -0
  87. package/extensions/dashboard-server/types.ts +23 -114
  88. package/package.json +2 -1
@@ -0,0 +1,476 @@
1
+ /**
2
+ * api-contracts/endpoints.ts — Central registry of all dashboard API endpoints.
3
+ *
4
+ * Single source of truth for all 13 dashboard API routes. Each entry is an
5
+ * `EndpointDef` (or `SseEndpointDef` for SSE) instance with method, path,
6
+ * description, and typed request/response references.
7
+ *
8
+ * Sprint A1 — PREVENT-PI-004: zero network code (type definitions only).
9
+ * PREVENT-011: no `any` type — all types are explicit.
10
+ */
11
+
12
+ import type { EndpointDef } from './core.js';
13
+ import type { SnapshotResponse } from './snapshot.js';
14
+ import type { IndexesIndexRow, IndexesSummaryResponse } from './multi-repo.js';
15
+ import type { GameConfig, GameStateResponse } from './game.js';
16
+ import type { SseEvent } from './index.js';
17
+
18
+ // ─── New Response Types (inline) ───────────────────────────────────────────
19
+
20
+ /**
21
+ * Response for GET /api/version. Returns the dashboard server version string
22
+ * so the launcher can detect stale servers from older builds.
23
+ */
24
+ export interface VersionResponse {
25
+ /** Semver-style version string of the running dashboard server. */
26
+ readonly version: string;
27
+ }
28
+
29
+ /**
30
+ * Aggregated summary of the multi-repo index (subset of IndexesSummaryResponse).
31
+ * Used by GET /api/summary for lightweight header tiles.
32
+ */
33
+ export type IndexSummary = Pick<
34
+ IndexesSummaryResponse,
35
+ 'totalRepos' | 'totalCheckpoints' | 'totalTokensSaved' | 'totalCompressedOriginalBytes'
36
+ >;
37
+
38
+ /**
39
+ * Fallback response for GET /api/index when the multi-repo index is unavailable
40
+ * (no index.sqlite file or read error). All fields are null/empty.
41
+ */
42
+ export interface IndexFallbackResponse {
43
+ /** Always null — no index data available. */
44
+ readonly updatedAt: null;
45
+ /** Always null — no summary available. */
46
+ readonly summary: null;
47
+ /** Always an empty array — no repos registered. */
48
+ readonly repos: IndexesIndexRow[];
49
+ }
50
+
51
+ /**
52
+ * Query parameters for GET /api/repos. The `active` parameter filters to repos
53
+ * seen within the last N hours (format: "<N>h", e.g. "24h").
54
+ */
55
+ export interface ReposQuery {
56
+ /** Active-window filter in format "<hours>h" (e.g. "24h"). Optional. */
57
+ readonly active?: string;
58
+ }
59
+
60
+ /**
61
+ * Response for GET /api/repos. Returns the registry list with an optional
62
+ * active-window filter applied.
63
+ */
64
+ export interface ReposResponse {
65
+ /** ISO timestamp of the last index update, or null if unavailable. */
66
+ readonly updatedAt: string | null;
67
+ /** Array of repo index rows. */
68
+ readonly repos: IndexesIndexRow[];
69
+ /** Number of repos in the response (after filtering). */
70
+ readonly count: number;
71
+ }
72
+
73
+ /**
74
+ * Response for GET /api/summary. Lightweight header tiles without the full
75
+ * repo list (keeps payload small for embed scenarios).
76
+ */
77
+ export interface SummaryResponse {
78
+ /** ISO timestamp of the last index update, or null if unavailable. */
79
+ readonly updatedAt: string | null;
80
+ /** Aggregate index summary, or null if no index exists. */
81
+ readonly summary: IndexSummary | null;
82
+ /** Number of repos active within the last 24 hours. */
83
+ readonly activeRepos: number;
84
+ /** Total number of repos in the registry. */
85
+ readonly totalRepos: number;
86
+ }
87
+
88
+ // ─── Drift Report Types ─────────────────────────────────────────────────────
89
+
90
+ /** Severity level for a drift signal. */
91
+ export type DriftSeverity = 'warn' | 'info';
92
+
93
+ /**
94
+ * A single drift signal for a repo.
95
+ */
96
+ export interface DriftSignal {
97
+ /** The kind of drift detected. */
98
+ readonly kind: 'stale' | 'compaction_lag' | 'model_churn';
99
+ /** Severity level of the signal. */
100
+ readonly severity: DriftSeverity;
101
+ /** Human-readable detail describing the drift. */
102
+ readonly detail: string;
103
+ }
104
+
105
+ /**
106
+ * Drift classification for a single repo.
107
+ */
108
+ export interface RepoDrift {
109
+ /** Absolute path to the repo root. */
110
+ readonly repoRoot: string;
111
+ /** Display name of the repo. */
112
+ readonly displayName: string;
113
+ /** Unix timestamp (seconds) of the last dashboard activity. */
114
+ readonly lastSeen: number;
115
+ /** Unix timestamp (seconds) of the last compaction, or null if never. */
116
+ readonly lastCompactedAt: number | null;
117
+ /** Unix timestamp (seconds) of the last model capture, or null. */
118
+ readonly modelCapturedAt: number | null;
119
+ /** Array of drift signals detected for this repo. */
120
+ readonly signals: DriftSignal[];
121
+ /** Highest severity across signals; "ok" if none. */
122
+ readonly status: 'ok' | 'warn';
123
+ }
124
+
125
+ /**
126
+ * Response for GET /api/drift. Cross-repo drift report over the machine-wide
127
+ * repo_registry, flagging stale repos, compaction lag, and model churn.
128
+ */
129
+ export interface DriftReportResponse {
130
+ /** Unix timestamp (seconds) when the report was generated. */
131
+ readonly generatedAt: number;
132
+ /** Aggregate counts by status/signal. */
133
+ readonly totals: {
134
+ /** Repos with no drift signals. */
135
+ readonly ok: number;
136
+ /** Repos with at least one warning signal. */
137
+ readonly warn: number;
138
+ /** Repos with the "stale" signal. */
139
+ readonly stale: number;
140
+ /** Repos with the "compaction_lag" signal. */
141
+ readonly compactionLag: number;
142
+ /** Repos with the "model_churn" signal. */
143
+ readonly modelChurn: number;
144
+ };
145
+ /** Per-repo drift classifications. */
146
+ readonly repos: RepoDrift[];
147
+ }
148
+
149
+ // ─── Servers Types ──────────────────────────────────────────────────────────
150
+
151
+ /**
152
+ * A single server entry in the GET /api/servers response. Represents a
153
+ * recently-active repo with its live dashboard snapshot data.
154
+ */
155
+ export interface ServerEntry {
156
+ /** Absolute path to the repo root. */
157
+ readonly repoRoot: string;
158
+ /** Display name of the repo. */
159
+ readonly displayName: string;
160
+ /** Model name, or null if not set. */
161
+ readonly model: string | null;
162
+ /** Provider display name, or null if not set. */
163
+ readonly provider: string | null;
164
+ /** Unix timestamp (seconds) of the last dashboard activity. */
165
+ readonly lastSeen: number;
166
+ /** Unix timestamp (seconds) of the last compaction, or null. */
167
+ readonly lastCompactedAt: number | null;
168
+ /** Current compaction tier, or null if no live snapshot. Present when a live dashboard.json exists. */
169
+ readonly tier?: string | null;
170
+ /** Current context pressure percentage (0–100), or null. Present when a live snapshot has context data. */
171
+ readonly contextPct?: number | null;
172
+ /** Current session state string, or null. Present when a live snapshot has session data. */
173
+ readonly state?: string | null;
174
+ /** Cache hit counters from the live snapshot, or null. Present when a live snapshot has cacheHits. */
175
+ readonly cacheHits?: {
176
+ readonly session: number;
177
+ readonly total: number;
178
+ readonly sessionTokensSaved: number;
179
+ readonly totalTokensSaved: number;
180
+ } | null;
181
+ /** Compaction counters from the live snapshot, or null. Present when a live snapshot has compacts. */
182
+ readonly compacts?: { readonly session: number; readonly total: number } | null;
183
+ /** Time saved counters from the live snapshot, or null. Present when a live snapshot has timeSaved. */
184
+ readonly timeSaved?: {
185
+ readonly compact: { readonly sessionSec: number; readonly totalSec: number };
186
+ readonly cacheHit: { readonly sessionSec: number; readonly totalSec: number };
187
+ } | null;
188
+ /** ISO timestamp of the live snapshot, or null. Present when a live snapshot has updatedAt. */
189
+ readonly updatedAt?: string | null;
190
+ }
191
+
192
+ /**
193
+ * Response for GET /api/servers. Lists recently-active repo servers with
194
+ * live snapshot data.
195
+ */
196
+ export interface ServersResponse {
197
+ /** ISO timestamp when the response was generated. */
198
+ readonly updatedAt: string;
199
+ /** Array of server entries, sorted by lastSeen descending. */
200
+ readonly servers: ServerEntry[];
201
+ }
202
+
203
+ // ─── Perf Types ─────────────────────────────────────────────────────────────
204
+
205
+ /** Percentile statistics for a latency metric (in milliseconds). */
206
+ export interface PerfPercentile {
207
+ /** 50th percentile (median) in milliseconds. */
208
+ readonly p50: number;
209
+ /** 95th percentile in milliseconds. */
210
+ readonly p95: number;
211
+ /** Number of samples in the window. */
212
+ readonly n: number;
213
+ }
214
+
215
+ /** Average statistics for a rate metric. */
216
+ export interface PerfAverage {
217
+ /** Average value across all samples in the window. */
218
+ readonly avg: number;
219
+ /** Number of samples in the window. */
220
+ readonly n: number;
221
+ }
222
+
223
+ /** Latest-value statistics for a gauge metric. */
224
+ export interface PerfLatest {
225
+ /** Most recent value in the window. */
226
+ readonly latest: number;
227
+ /** Number of samples in the window. */
228
+ readonly n: number;
229
+ }
230
+
231
+ /** Average + latest statistics for cache hit percentage. */
232
+ export interface PerfCacheHit {
233
+ /** Average cache hit percentage (0–100). */
234
+ readonly avg: number;
235
+ /** Most recent cache hit percentage (0–100). */
236
+ readonly latest: number;
237
+ /** Number of samples in the window. */
238
+ readonly n: number;
239
+ }
240
+
241
+ /** Diagnostic counters from the live dashboard snapshot. */
242
+ export interface PerfDiag {
243
+ /** Number of fast-gate context trim fires. */
244
+ readonly ctxFastGate: number;
245
+ /** Number of live trim fires. */
246
+ readonly liveTrimFires: number;
247
+ /** Number of live trim replays. */
248
+ readonly liveTrimReplays: number;
249
+ }
250
+
251
+ /**
252
+ * Query parameters for GET /api/perf. The `minutes` parameter controls the
253
+ * rolling window size.
254
+ */
255
+ export interface PerfQuery {
256
+ /** Rolling window size in minutes (default: 30, max: 1440). Optional. */
257
+ readonly minutes?: number;
258
+ }
259
+
260
+ /**
261
+ * Response for GET /api/perf. Rolling-window aggregates over perf_samples
262
+ * with per-kind p50/p95, latest rss/heap, cpu counters, and diagnostic data.
263
+ */
264
+ export interface PerfResponse {
265
+ /** ISO timestamp when the response was generated. */
266
+ readonly updatedAt: string;
267
+ /** Rolling window size in minutes. */
268
+ readonly windowMinutes: number;
269
+ /** Total number of perf samples in the window. */
270
+ readonly sampleCount: number;
271
+ /** Turn latency statistics in milliseconds. */
272
+ readonly turn_latency_ms: PerfPercentile;
273
+ /** Provider latency statistics in milliseconds. */
274
+ readonly provider_latency_ms: PerfPercentile;
275
+ /** Tokens-per-second statistics. */
276
+ readonly tps: PerfAverage;
277
+ /** Cache hit percentage statistics. */
278
+ readonly cache_hit_pct: PerfCacheHit;
279
+ /** Database recompute duration statistics in milliseconds. */
280
+ readonly db_recompute_ms: PerfPercentile;
281
+ /** Disk write duration statistics in milliseconds. */
282
+ readonly disk_write_ms: PerfPercentile;
283
+ /** RSS memory usage in MB (latest value). */
284
+ readonly rss_mb: PerfLatest;
285
+ /** Heap memory usage in MB (latest value). */
286
+ readonly heap_mb: PerfLatest;
287
+ /** CPU user time in milliseconds (latest value). */
288
+ readonly cpu_user_ms: PerfLatest;
289
+ /** CPU system time in milliseconds (latest value). */
290
+ readonly cpu_sys_ms: PerfLatest;
291
+ /** Diagnostic counters from the live snapshot, or null if unavailable. */
292
+ readonly diag: PerfDiag | null;
293
+ }
294
+
295
+ // ─── Game Score & Achievement Types ─────────────────────────────────────────
296
+
297
+ /**
298
+ * A leaderboard row for GET /api/game-scores. One row per repo per metric.
299
+ */
300
+ export interface GameScoreRow {
301
+ /** Absolute path to the repo root. */
302
+ readonly repo_root: string;
303
+ /** Score value (interpretation depends on the metric). */
304
+ readonly value: number;
305
+ /** Unix timestamp (milliseconds) when the score was recorded. */
306
+ readonly ts: number;
307
+ /** Optional metadata associated with the score event. */
308
+ readonly meta: unknown;
309
+ }
310
+
311
+ /**
312
+ * Query parameters for GET /api/game-scores.
313
+ */
314
+ export interface GameScoresQuery {
315
+ /** Leaderboard metric (must be one of: cache, dedupe, turns, repos, mega_cache). Optional. */
316
+ readonly metric?: string;
317
+ /** Maximum number of rows to return (default: 10, clamped to [1, 100]). Optional. */
318
+ readonly limit?: number;
319
+ }
320
+
321
+ /**
322
+ * An achievement row for GET /api/achievements. One row per seeded achievement.
323
+ */
324
+ export interface AchievementRow {
325
+ /** Achievement identifier. */
326
+ readonly id: string;
327
+ /** Display title of the achievement. */
328
+ readonly title: string;
329
+ /** Description of the unlock condition. */
330
+ readonly description: string;
331
+ /** Whether the achievement is hidden (1 = hidden, 0 = visible). */
332
+ readonly hidden: number;
333
+ /** Icon identifier, or null if no icon. */
334
+ readonly icon: string | null;
335
+ /** Unix timestamp (seconds) when unlocked, or null if not yet unlocked. */
336
+ readonly unlocked_at: number | null;
337
+ }
338
+
339
+ // ─── Game State Patch (PUT request body) ────────────────────────────────────
340
+
341
+ /**
342
+ * Request body for PUT /api/game-state. A partial patch of the game config.
343
+ * Unknown keys are ignored; invalid values result in a 400 response.
344
+ */
345
+ export type GameStatePatch = Partial<GameConfig>;
346
+
347
+ // ─── SSE Endpoint Definition ────────────────────────────────────────────────
348
+
349
+ /**
350
+ * Endpoint definition for SSE (Server-Sent Events) streaming endpoints.
351
+ * Unlike standard REST endpoints, the response is a continuous `text/event-stream`
352
+ * with `data:` frames containing JSON-serialized event objects.
353
+ * @template Data - The SSE event data type streamed by this endpoint.
354
+ */
355
+ export interface SseEndpointDef<Data extends SseEvent = SseEvent> {
356
+ /** Discriminator: always 'sse' for streaming endpoints. */
357
+ readonly type: 'sse';
358
+ /** HTTP method (always 'GET' for SSE). */
359
+ readonly method: 'GET';
360
+ /** URL path of the endpoint. */
361
+ readonly path: string;
362
+ /** Human-readable description of the endpoint. */
363
+ readonly description: string;
364
+ /** SSE event name sent with each data frame. */
365
+ readonly event: string;
366
+ /** The SSE event data type streamed by this endpoint. */
367
+ readonly dataType?: Data;
368
+ }
369
+
370
+ // ─── ENDPOINTS Registry ─────────────────────────────────────────────────────
371
+
372
+ /**
373
+ * Central registry of all 13 dashboard API endpoints. Each entry is an
374
+ * `EndpointDef` or `SseEndpointDef` instance serving as the single source of
375
+ * truth for route paths, methods, descriptions, and typed request/response
376
+ * contracts.
377
+ *
378
+ * Usage:
379
+ * import { ENDPOINTS } from './api-contracts';
380
+ * ENDPOINTS.snapshot.path // '/api/snapshot'
381
+ * ENDPOINTS.snapshot.method // 'GET'
382
+ */
383
+ export const ENDPOINTS = {
384
+ /** GET /api/snapshot — Full session, store, compression, and context snapshot. */
385
+ snapshot: {
386
+ method: 'GET',
387
+ path: '/api/snapshot',
388
+ description: 'Full session, store, compression, and context snapshot.',
389
+ } as const satisfies EndpointDef<'GET', undefined, SnapshotResponse>,
390
+
391
+ /** GET /api/version — Dashboard server version string. */
392
+ version: {
393
+ method: 'GET',
394
+ path: '/api/version',
395
+ description: 'Dashboard server version for stale-server detection.',
396
+ } as const satisfies EndpointDef<'GET', undefined, VersionResponse>,
397
+
398
+ /** GET /api/index — Multi-repo aggregate index (or fallback when unavailable). */
399
+ index: {
400
+ method: 'GET',
401
+ path: '/api/index',
402
+ description: 'Machine-wide multi-repo registry with checkpoints, tokens, and model info.',
403
+ } as const satisfies EndpointDef<'GET', undefined, IndexesSummaryResponse | IndexFallbackResponse>,
404
+
405
+ /** GET /api/repos — Registry list with optional active-window filter. */
406
+ repos: {
407
+ method: 'GET',
408
+ path: '/api/repos',
409
+ description: 'Repo registry list, optionally filtered by recent activity.',
410
+ } as const satisfies EndpointDef<'GET', ReposQuery, ReposResponse>,
411
+
412
+ /** GET /api/summary — Lightweight header tiles without the full repo list. */
413
+ summary: {
414
+ method: 'GET',
415
+ path: '/api/summary',
416
+ description: 'Aggregate summary with active/total repo counts (no repo list).',
417
+ } as const satisfies EndpointDef<'GET', undefined, SummaryResponse>,
418
+
419
+ /** GET /api/drift — Cross-repo drift report. */
420
+ drift: {
421
+ method: 'GET',
422
+ path: '/api/drift',
423
+ description: 'Cross-repo drift report flagging stale repos, compaction lag, and model churn.',
424
+ } as const satisfies EndpointDef<'GET', undefined, DriftReportResponse>,
425
+
426
+ /** GET /api/servers — Recently-active repo servers with live snapshot data. */
427
+ servers: {
428
+ method: 'GET',
429
+ path: '/api/servers',
430
+ description: 'Recently-active repo servers with live snapshot data.',
431
+ } as const satisfies EndpointDef<'GET', undefined, ServersResponse>,
432
+
433
+ /** GET /api/events — SSE stream of dashboard events. */
434
+ events: {
435
+ type: 'sse',
436
+ method: 'GET',
437
+ path: '/api/events',
438
+ description: 'Server-Sent Events stream of all dashboard events.',
439
+ event: 'data',
440
+ } as const satisfies SseEndpointDef<SseEvent>,
441
+
442
+ /** GET /api/game-state — Current game-mode settings. */
443
+ getGameState: {
444
+ method: 'GET',
445
+ path: '/api/game-state',
446
+ description: 'Current game-mode configuration and active ritual state.',
447
+ } as const satisfies EndpointDef<'GET', undefined, GameStateResponse>,
448
+
449
+ /** PUT /api/game-state — Apply a partial patch to game-mode settings. */
450
+ putGameState: {
451
+ method: 'PUT',
452
+ path: '/api/game-state',
453
+ description: 'Apply a partial patch to game-mode settings and return the updated state.',
454
+ } as const satisfies EndpointDef<'PUT', GameStatePatch, GameStateResponse>,
455
+
456
+ /** GET /api/game-scores — High-score leaderboard for a metric. */
457
+ gameScores: {
458
+ method: 'GET',
459
+ path: '/api/game-scores',
460
+ description: 'High-score leaderboard for a game metric.',
461
+ } as const satisfies EndpointDef<'GET', GameScoresQuery, GameScoreRow[]>,
462
+
463
+ /** GET /api/perf — Rolling-window performance aggregates. */
464
+ perf: {
465
+ method: 'GET',
466
+ path: '/api/perf',
467
+ description: 'Rolling-window performance aggregates over perf_samples.',
468
+ } as const satisfies EndpointDef<'GET', PerfQuery, PerfResponse>,
469
+
470
+ /** GET /api/achievements — Achievement tiles with unlock state. */
471
+ achievements: {
472
+ method: 'GET',
473
+ path: '/api/achievements',
474
+ description: 'All achievement rows with unlock state.',
475
+ } as const satisfies EndpointDef<'GET', undefined, AchievementRow[]>,
476
+ } as const;
@@ -0,0 +1,145 @@
1
+ /**
2
+ * api-contracts/game.ts — Game mode and mega-game contracts.
3
+ *
4
+ * Contains: GameConfig, GameStateResponse, GameRitualStage,
5
+ * SseGameRitualStart, SseGameRitualStage, SseGameRitualEnd,
6
+ * SseGameModeChanged, SseGameRender.
7
+ * Extracted from api-contracts.ts (Sprint A1 split).
8
+ */
9
+
10
+ // ─── /api/game-state ──────────────────────────────────────────────────────
11
+
12
+ /**
13
+ * Game mode configuration.
14
+ *
15
+ * Used in `GET /api/game-state` and `PUT /api/game-state` responses, and in
16
+ * the `SseGameModeChanged` SSE event.
17
+ */
18
+ export interface GameConfig {
19
+ /** Whether game mode is enabled. */
20
+ enabled: boolean;
21
+ /** Visual theme for the TUI game renderer. */
22
+ theme: 'moss' | 'ocean' | 'ember' | 'slate' | 'neon' | 'mono';
23
+ /** How much of the TUI game UI to show. */
24
+ displayMode: 'full' | 'minimal';
25
+ }
26
+
27
+ /**
28
+ * Response body for `GET /api/game-state` and `PUT /api/game-state`.
29
+ *
30
+ * Reports the current game configuration and any active ritual session.
31
+ */
32
+ export interface GameStateResponse {
33
+ /** Current game mode configuration. */
34
+ config: GameConfig;
35
+ /** Active ritual session, if one is in progress. */
36
+ activeRitual: {
37
+ /** Unique ritual session identifier. */
38
+ sessionId: string;
39
+ /** Zero-based index of the current ritual stage. */
40
+ stageIndex: number;
41
+ /** ISO 8601 timestamp when the ritual started. */
42
+ startedAt: string;
43
+ /** Time elapsed since the ritual started (milliseconds). */
44
+ elapsed: number;
45
+ } | null;
46
+ }
47
+
48
+ /**
49
+ * Definition of a single stage in a game ritual.
50
+ *
51
+ * Used in the `SseGameRitualStart` SSE event's `stages` array.
52
+ */
53
+ export interface GameRitualStage {
54
+ /** Zero-based stage index in the ritual sequence. */
55
+ index: number;
56
+ /** Human-readable stage name. */
57
+ name: string;
58
+ /** Planned duration of this stage (milliseconds). */
59
+ durationMs: number;
60
+ /**
61
+ * Current status of this stage.
62
+ * Allowed values: `'pending'`, `'active'`, `'done'`, `'cancelled'`.
63
+ */
64
+ status: 'pending' | 'active' | 'done' | 'cancelled';
65
+ }
66
+
67
+ // ─── Game SSE Events ──────────────────────────────────────────────────────
68
+
69
+ /**
70
+ * SSE event emitted when a game ritual starts.
71
+ *
72
+ * Served via `GET /api/events` (Server-Sent Events stream).
73
+ */
74
+ export interface SseGameRitualStart {
75
+ /** Discriminator. Always `'game_ritual_start'`. */
76
+ type: 'game_ritual_start';
77
+ /** ISO 8601 timestamp of the event. */
78
+ ts: string;
79
+ /** Unique ritual session identifier. */
80
+ sessionId: string;
81
+ /** Full list of ritual stages in order. */
82
+ stages: GameRitualStage[];
83
+ }
84
+
85
+ /**
86
+ * SSE event emitted when a game ritual advances to a new stage.
87
+ *
88
+ * Served via `GET /api/events` (Server-Sent Events stream).
89
+ */
90
+ export interface SseGameRitualStage {
91
+ /** Discriminator. Always `'game_ritual_stage'`. */
92
+ type: 'game_ritual_stage';
93
+ /** ISO 8601 timestamp of the event. */
94
+ ts: string;
95
+ /** Unique ritual session identifier. */
96
+ sessionId: string;
97
+ /** Zero-based index of the stage that was entered. */
98
+ stageIndex: number;
99
+ /** Name of the stage that was entered. */
100
+ stageName: string;
101
+ }
102
+
103
+ /**
104
+ * SSE event emitted when a game ritual ends.
105
+ *
106
+ * Served via `GET /api/events` (Server-Sent Events stream).
107
+ */
108
+ export interface SseGameRitualEnd {
109
+ /** Discriminator. Always `'game_ritual_end'`. */
110
+ type: 'game_ritual_end';
111
+ /** ISO 8601 timestamp of the event. */
112
+ ts: string;
113
+ /** Unique ritual session identifier. */
114
+ sessionId: string;
115
+ /** Whether the ritual completed successfully. */
116
+ success: boolean;
117
+ }
118
+
119
+ /**
120
+ * SSE event emitted when the game mode configuration changes.
121
+ *
122
+ * Served via `GET /api/events` (Server-Sent Events stream).
123
+ */
124
+ export interface SseGameModeChanged {
125
+ /** Discriminator. Always `'game_mode_changed'`. */
126
+ type: 'game_mode_changed';
127
+ /** ISO 8601 timestamp of the event. */
128
+ ts: string;
129
+ /** New game mode configuration. */
130
+ config: GameConfig;
131
+ }
132
+
133
+ /**
134
+ * SSE event emitted when a game render frame is produced.
135
+ *
136
+ * Served via `GET /api/events` (Server-Sent Events stream).
137
+ */
138
+ export interface SseGameRender {
139
+ /** Discriminator. Always `'game_render'`. */
140
+ type: 'game_render';
141
+ /** ISO 8601 timestamp of the event. */
142
+ ts: string;
143
+ /** Serialized render frame content (TUI/ANSI text). */
144
+ frame: string;
145
+ }