pi-mega-compact 0.9.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.md +3 -2
  2. package/dist/extensions/dashboard-server/api-contracts/endpoints.js +95 -47
  3. package/dist/extensions/dashboard-server/api-contracts/index.js +1 -1
  4. package/dist/extensions/dashboard-server/api-contracts/turns.js +10 -0
  5. package/dist/extensions/dashboard-server/api-contracts.test.js +3 -2
  6. package/dist/extensions/dashboard-server/routes-topics.js +87 -0
  7. package/dist/extensions/dashboard-server/routes-turns.js +264 -0
  8. package/dist/extensions/dashboard-server/routes.js +3 -1
  9. package/dist/extensions/dashboard-server/server.js +6 -2
  10. package/dist/extensions/mega-compact.js +10 -6
  11. package/dist/extensions/mega-config.js +4 -0
  12. package/dist/extensions/mega-events/agent-handlers.js +62 -49
  13. package/dist/extensions/mega-events/context-handler.js +48 -2
  14. package/dist/extensions/mega-events/session-handlers.js +25 -1
  15. package/dist/extensions/mega-metrics-cmds.js +90 -0
  16. package/dist/extensions/mega-pipeline/recall.js +47 -17
  17. package/dist/extensions/mega-topics-cmds.js +111 -0
  18. package/dist/extensions/mega-turn-store.js +110 -0
  19. package/dist/extensions/mega-turn-store.test.js +68 -0
  20. package/dist/src/config/turns.js +48 -0
  21. package/dist/src/fork.js +33 -0
  22. package/dist/src/fork.test.js +81 -0
  23. package/dist/src/intent.js +51 -0
  24. package/dist/src/intent.test.js +78 -0
  25. package/dist/src/metrics/index.js +4 -0
  26. package/dist/src/metrics/turns.js +118 -0
  27. package/dist/src/metrics/turns.test.js +152 -0
  28. package/dist/src/store/sqlite/raw-transcript.js +5 -3
  29. package/dist/src/store/sqlite.dbmirror.test.js +9 -0
  30. package/dist/src/store/turns/connection.js +123 -0
  31. package/dist/src/store/turns/contract-compliance.test.js +428 -0
  32. package/dist/src/store/turns/index.js +31 -0
  33. package/dist/src/store/turns/memory-store.js +357 -0
  34. package/dist/src/store/turns/memory-store.test.js +9 -0
  35. package/dist/src/store/turns/migrations.js +178 -0
  36. package/dist/src/store/turns/migrations.test.js +138 -0
  37. package/dist/src/store/turns/schema.js +115 -0
  38. package/dist/src/store/turns/sqlite-store.js +455 -0
  39. package/dist/src/store/turns/sqlite-store.test.js +144 -0
  40. package/dist/src/store/turns/types.js +16 -0
  41. package/dist/src/topics/cluster.js +141 -0
  42. package/dist/src/topics/cluster.test.js +150 -0
  43. package/dist/src/topics/index.js +6 -0
  44. package/dist/src/topics/kselection.js +142 -0
  45. package/dist/src/topics/labels.js +56 -0
  46. package/dist/src/topics/store.js +119 -0
  47. package/dist/src/topics/store.test.js +117 -0
  48. package/dist/src/topics/types.js +8 -0
  49. package/dist/src/wiki.js +133 -0
  50. package/dist/src/wiki.test.js +171 -0
  51. package/extensions/dashboard-client/dist/assets/{AchievementTiles-QPo46Pwg.js → AchievementTiles-DZp_xQOW.js} +2 -2
  52. package/extensions/dashboard-client/dist/assets/{AchievementTiles-QPo46Pwg.js.map → AchievementTiles-DZp_xQOW.js.map} +1 -1
  53. package/extensions/dashboard-client/dist/assets/{AchievementsTab-BDJxRXRY.js → AchievementsTab-BzFHpAHM.js} +2 -2
  54. package/extensions/dashboard-client/dist/assets/{AchievementsTab-BDJxRXRY.js.map → AchievementsTab-BzFHpAHM.js.map} +1 -1
  55. package/extensions/dashboard-client/dist/assets/{CacheTab-CUMqXQjd.js → CacheTab-DEQbTq2T.js} +2 -2
  56. package/extensions/dashboard-client/dist/assets/{CacheTab-CUMqXQjd.js.map → CacheTab-DEQbTq2T.js.map} +1 -1
  57. package/extensions/dashboard-client/dist/assets/{ConfigTab-j1dRzurG.js → ConfigTab-DQ4FG33J.js} +2 -2
  58. package/extensions/dashboard-client/dist/assets/{ConfigTab-j1dRzurG.js.map → ConfigTab-DQ4FG33J.js.map} +1 -1
  59. package/extensions/dashboard-client/dist/assets/{EventsTab-CMglXK4p.js → EventsTab-D5HoEVYo.js} +2 -2
  60. package/extensions/dashboard-client/dist/assets/{EventsTab-CMglXK4p.js.map → EventsTab-D5HoEVYo.js.map} +1 -1
  61. package/extensions/dashboard-client/dist/assets/{GameTab-fpaDoyxi.js → GameTab-Jzsc4yeY.js} +2 -2
  62. package/extensions/dashboard-client/dist/assets/{GameTab-fpaDoyxi.js.map → GameTab-Jzsc4yeY.js.map} +1 -1
  63. package/extensions/dashboard-client/dist/assets/{MetricsTab-CWG2crG_.js → MetricsTab-D_XnsEYX.js} +2 -2
  64. package/extensions/dashboard-client/dist/assets/{MetricsTab-CWG2crG_.js.map → MetricsTab-D_XnsEYX.js.map} +1 -1
  65. package/extensions/dashboard-client/dist/assets/{OverviewTab-BaV2D2nf.js → OverviewTab-BMREvAth.js} +2 -2
  66. package/extensions/dashboard-client/dist/assets/{OverviewTab-BaV2D2nf.js.map → OverviewTab-BMREvAth.js.map} +1 -1
  67. package/extensions/dashboard-client/dist/assets/{ReposTab-C-0kuZ_0.js → ReposTab-DuyhOktG.js} +2 -2
  68. package/extensions/dashboard-client/dist/assets/{ReposTab-C-0kuZ_0.js.map → ReposTab-DuyhOktG.js.map} +1 -1
  69. package/extensions/dashboard-client/dist/assets/{SessionsTab-CSYhddCA.js → SessionsTab-mz_vz8i2.js} +2 -2
  70. package/extensions/dashboard-client/dist/assets/{SessionsTab-CSYhddCA.js.map → SessionsTab-mz_vz8i2.js.map} +1 -1
  71. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-Dy4KRjSM.js → TimeSavedCard-B8AIjNTY.js} +2 -2
  72. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-Dy4KRjSM.js.map → TimeSavedCard-B8AIjNTY.js.map} +1 -1
  73. package/extensions/dashboard-client/dist/assets/TopicsTab-DKuct4oD.js +2 -0
  74. package/extensions/dashboard-client/dist/assets/TopicsTab-DKuct4oD.js.map +1 -0
  75. package/extensions/dashboard-client/dist/assets/TurnsTab-11MGLxY8.js +2 -0
  76. package/extensions/dashboard-client/dist/assets/TurnsTab-11MGLxY8.js.map +1 -0
  77. package/extensions/dashboard-client/dist/assets/{index-D_WtU2TV.js → index-CqLN7STl.js} +10 -10
  78. package/extensions/dashboard-client/dist/assets/index-CqLN7STl.js.map +1 -0
  79. package/extensions/dashboard-client/dist/assets/{index-og3ghxyY.css → index-gn66lYD2.css} +1 -1
  80. package/extensions/dashboard-client/dist/assets/{useSSE-C9yVJlGP.js → useSSE-CXrQSbVY.js} +2 -2
  81. package/extensions/dashboard-client/dist/assets/{useSSE-C9yVJlGP.js.map → useSSE-CXrQSbVY.js.map} +1 -1
  82. package/extensions/dashboard-client/dist/index.html +2 -2
  83. package/extensions/dashboard-client/src/App.tsx +10 -2
  84. package/extensions/dashboard-client/src/api/client.ts +90 -1
  85. package/extensions/dashboard-client/src/main.tsx +1 -0
  86. package/extensions/dashboard-client/src/styles/turns.css +142 -0
  87. package/extensions/dashboard-client/src/tabs/TopicsTab.tsx +186 -0
  88. package/extensions/dashboard-client/src/tabs/TurnsTab.tsx +336 -0
  89. package/extensions/dashboard-server/api-contracts/endpoints.ts +394 -291
  90. package/extensions/dashboard-server/api-contracts/game-types.ts +141 -88
  91. package/extensions/dashboard-server/api-contracts/index.ts +153 -133
  92. package/extensions/dashboard-server/api-contracts/turns.ts +114 -0
  93. package/extensions/dashboard-server/api-contracts.test.ts +920 -919
  94. package/extensions/dashboard-server/routes-topics.ts +119 -0
  95. package/extensions/dashboard-server/routes-turns.ts +305 -0
  96. package/extensions/dashboard-server/routes.ts +8 -1
  97. package/extensions/dashboard-server/server.ts +6 -2
  98. package/extensions/mega-compact.ts +75 -59
  99. package/extensions/mega-config.ts +232 -220
  100. package/extensions/mega-events/agent-handlers.ts +194 -151
  101. package/extensions/mega-events/context-handler.ts +50 -1
  102. package/extensions/mega-events/session-handlers.ts +26 -5
  103. package/extensions/mega-metrics-cmds.ts +114 -0
  104. package/extensions/mega-pipeline/recall.ts +215 -152
  105. package/extensions/mega-topics-cmds.ts +151 -0
  106. package/extensions/mega-turn-store.test.ts +108 -0
  107. package/extensions/mega-turn-store.ts +169 -0
  108. package/package.json +3 -1
  109. package/src/config/turns.ts +69 -0
  110. package/src/fork.test.ts +94 -0
  111. package/src/fork.ts +78 -0
  112. package/src/intent.test.ts +87 -0
  113. package/src/intent.ts +115 -0
  114. package/src/metrics/index.ts +9 -0
  115. package/src/metrics/turns.test.ts +165 -0
  116. package/src/metrics/turns.ts +201 -0
  117. package/src/store/sqlite/raw-transcript.ts +9 -3
  118. package/src/store/sqlite.dbmirror.test.ts +10 -0
  119. package/src/store/turns/connection.ts +137 -0
  120. package/src/store/turns/contract-compliance.test.ts +562 -0
  121. package/src/store/turns/index.ts +65 -0
  122. package/src/store/turns/memory-store.test.ts +15 -0
  123. package/src/store/turns/memory-store.ts +469 -0
  124. package/src/store/turns/migrations.test.ts +161 -0
  125. package/src/store/turns/migrations.ts +203 -0
  126. package/src/store/turns/schema.ts +158 -0
  127. package/src/store/turns/sqlite-store.test.ts +161 -0
  128. package/src/store/turns/sqlite-store.ts +656 -0
  129. package/src/store/turns/types.ts +181 -0
  130. package/src/topics/cluster.test.ts +176 -0
  131. package/src/topics/cluster.ts +191 -0
  132. package/src/topics/index.ts +18 -0
  133. package/src/topics/kselection.ts +182 -0
  134. package/src/topics/labels.ts +67 -0
  135. package/src/topics/store.test.ts +132 -0
  136. package/src/topics/store.ts +208 -0
  137. package/src/topics/types.ts +64 -0
  138. package/src/wiki.test.ts +217 -0
  139. package/src/wiki.ts +198 -0
  140. package/dist/extensions/openclaw-mega-compact.js +0 -291
  141. package/dist/src/minilm.js +0 -92
  142. package/dist/src/wordpiece.js +0 -129
  143. package/extensions/dashboard-client/dist/assets/index-D_WtU2TV.js.map +0 -1
@@ -19,61 +19,61 @@ import { test, describe } from "node:test";
19
19
  import assert from "node:assert/strict";
20
20
  import { ENDPOINTS } from "./api-contracts/index.js";
21
21
  import type {
22
- EndpointDef,
23
- SseEndpointDef,
24
- SnapshotResponse,
25
- VersionResponse,
26
- IndexesSummaryResponse,
27
- IndexFallbackResponse,
28
- ReposResponse,
29
- SummaryResponse,
30
- DriftReportResponse,
31
- ServersResponse,
32
- GameStateResponse,
33
- GameScoreRow,
34
- PerfResponse,
35
- AchievementRow,
36
- SseEvent,
22
+ EndpointDef,
23
+ SseEndpointDef,
24
+ SnapshotResponse,
25
+ VersionResponse,
26
+ IndexesSummaryResponse,
27
+ IndexFallbackResponse,
28
+ ReposResponse,
29
+ SummaryResponse,
30
+ DriftReportResponse,
31
+ ServersResponse,
32
+ GameStateResponse,
33
+ GameScoreRow,
34
+ PerfResponse,
35
+ AchievementRow,
36
+ SseEvent,
37
37
  } from "./api-contracts/index.js";
38
38
 
39
39
  // ─── Helpers ────────────────────────────────────────────────────────────────
40
40
 
41
41
  /** Primitive type name from a JSON-parsed value (matches typeof for JSON types). */
42
42
  function primitiveType(v: unknown): string {
43
- if (v === null) return "null";
44
- if (Array.isArray(v)) return "array";
45
- return typeof v;
43
+ if (v === null) return "null";
44
+ if (Array.isArray(v)) return "array";
45
+ return typeof v;
46
46
  }
47
47
 
48
48
  /** Assert that a field exists on an object and has one of the expected primitive types. */
49
49
  function assertField(
50
- obj: Record<string, unknown>,
51
- field: string,
52
- expected: string[],
50
+ obj: Record<string, unknown>,
51
+ field: string,
52
+ expected: string[],
53
53
  ): void {
54
- assert.ok(field in obj, `field "${field}" must exist`);
55
- const actual = primitiveType(obj[field]);
56
- assert.ok(
57
- expected.includes(actual),
58
- `field "${field}" expected type ${expected.join("|")}, got ${actual}`,
59
- );
54
+ assert.ok(field in obj, `field "${field}" must exist`);
55
+ const actual = primitiveType(obj[field]);
56
+ assert.ok(
57
+ expected.includes(actual),
58
+ `field "${field}" expected type ${expected.join("|")}, got ${actual}`,
59
+ );
60
60
  }
61
61
 
62
62
  /** Assert that a nested object field exists and is an object (or null if allowed). */
63
63
  function assertObject(
64
- obj: Record<string, unknown>,
65
- field: string,
66
- allowNull: boolean = false,
64
+ obj: Record<string, unknown>,
65
+ field: string,
66
+ allowNull: boolean = false,
67
67
  ): Record<string, unknown> | null {
68
- assert.ok(field in obj, `field "${field}" must exist`);
69
- const val = obj[field];
70
- if (allowNull && val === null) return null;
71
- assert.equal(
72
- primitiveType(val),
73
- "object",
74
- `field "${field}" must be an object${allowNull ? " or null" : ""}`,
75
- );
76
- return val as Record<string, unknown>;
68
+ assert.ok(field in obj, `field "${field}" must exist`);
69
+ const val = obj[field];
70
+ if (allowNull && val === null) return null;
71
+ assert.equal(
72
+ primitiveType(val),
73
+ "object",
74
+ `field "${field}" must be an object${allowNull ? " or null" : ""}`,
75
+ );
76
+ return val as Record<string, unknown>;
77
77
  }
78
78
 
79
79
  // ─── Compile-Time: ENDPOINTS registry satisfies checks ──────────────────────
@@ -81,65 +81,65 @@ function assertObject(
81
81
  // match the expected interface, tsc will fail at compile time.
82
82
 
83
83
  const _c_snapshot = ENDPOINTS.snapshot satisfies EndpointDef<
84
- "GET",
85
- undefined,
86
- SnapshotResponse
84
+ "GET",
85
+ undefined,
86
+ SnapshotResponse
87
87
  >;
88
88
  const _c_version = ENDPOINTS.version satisfies EndpointDef<
89
- "GET",
90
- undefined,
91
- VersionResponse
89
+ "GET",
90
+ undefined,
91
+ VersionResponse
92
92
  >;
93
93
  const _c_index = ENDPOINTS.index satisfies EndpointDef<
94
- "GET",
95
- undefined,
96
- IndexesSummaryResponse | IndexFallbackResponse
94
+ "GET",
95
+ undefined,
96
+ IndexesSummaryResponse | IndexFallbackResponse
97
97
  >;
98
98
  const _c_repos = ENDPOINTS.repos satisfies EndpointDef<
99
- "GET",
100
- unknown,
101
- ReposResponse
99
+ "GET",
100
+ unknown,
101
+ ReposResponse
102
102
  >;
103
103
  const _c_summary = ENDPOINTS.summary satisfies EndpointDef<
104
- "GET",
105
- undefined,
106
- SummaryResponse
104
+ "GET",
105
+ undefined,
106
+ SummaryResponse
107
107
  >;
108
108
  const _c_drift = ENDPOINTS.drift satisfies EndpointDef<
109
- "GET",
110
- undefined,
111
- DriftReportResponse
109
+ "GET",
110
+ undefined,
111
+ DriftReportResponse
112
112
  >;
113
113
  const _c_servers = ENDPOINTS.servers satisfies EndpointDef<
114
- "GET",
115
- undefined,
116
- ServersResponse
114
+ "GET",
115
+ undefined,
116
+ ServersResponse
117
117
  >;
118
118
  const _c_events = ENDPOINTS.events satisfies SseEndpointDef<SseEvent>;
119
119
  const _c_getGameState = ENDPOINTS.getGameState satisfies EndpointDef<
120
- "GET",
121
- undefined,
122
- GameStateResponse
120
+ "GET",
121
+ undefined,
122
+ GameStateResponse
123
123
  >;
124
124
  const _c_putGameState = ENDPOINTS.putGameState satisfies EndpointDef<
125
- "PUT",
126
- unknown,
127
- GameStateResponse
125
+ "PUT",
126
+ unknown,
127
+ GameStateResponse
128
128
  >;
129
129
  const _c_gameScores = ENDPOINTS.gameScores satisfies EndpointDef<
130
- "GET",
131
- unknown,
132
- GameScoreRow[]
130
+ "GET",
131
+ unknown,
132
+ GameScoreRow[]
133
133
  >;
134
134
  const _c_perf = ENDPOINTS.perf satisfies EndpointDef<
135
- "GET",
136
- unknown,
137
- PerfResponse
135
+ "GET",
136
+ unknown,
137
+ PerfResponse
138
138
  >;
139
139
  const _c_achievements = ENDPOINTS.achievements satisfies EndpointDef<
140
- "GET",
141
- undefined,
142
- AchievementRow[]
140
+ "GET",
141
+ undefined,
142
+ AchievementRow[]
143
143
  >;
144
144
 
145
145
  // Silence unused-variable warnings — these are compile-time-only checks.
@@ -162,643 +162,644 @@ void _c_achievements;
162
162
  // (Sprint 39 added /api/sessions + /api/sessions/timeseries.)
163
163
 
164
164
  const ENDPOINT_KEYS = Object.keys(ENDPOINTS) as (keyof typeof ENDPOINTS)[];
165
- const EXPECTED_ENDPOINT_COUNT = 15;
165
+ const EXPECTED_ENDPOINT_COUNT = 23;
166
166
 
167
167
  /** All `/api/*` paths served by server.ts (extracted from the route handlers). */
168
168
  const SERVER_TS_PATHS: string[] = [
169
- "/api/snapshot",
170
- "/api/version",
171
- "/api/index",
172
- "/api/repos",
173
- "/api/summary",
174
- "/api/drift",
175
- "/api/servers",
176
- "/api/events",
177
- "/api/game-state",
178
- "/api/game-scores",
179
- "/api/perf",
180
- "/api/achievements",
181
- "/api/sessions",
182
- "/api/sessions/timeseries",
169
+ "/api/snapshot",
170
+ "/api/version",
171
+ "/api/index",
172
+ "/api/repos",
173
+ "/api/summary",
174
+ "/api/drift",
175
+ "/api/servers",
176
+ "/api/events",
177
+ "/api/game-state",
178
+ "/api/game-scores",
179
+ "/api/perf",
180
+ "/api/achievements",
181
+ "/api/sessions",
182
+ "/api/sessions/timeseries",
183
+ "/api/topics",
183
184
  ];
184
185
 
185
186
  // ─── Tests ───────────────────────────────────────────────────────────────────
186
187
 
187
188
  describe("ENDPOINTS registry", () => {
188
- test("has exactly 15 endpoint entries", () => {
189
- assert.equal(
190
- ENDPOINT_KEYS.length,
191
- EXPECTED_ENDPOINT_COUNT,
192
- `ENDPOINTS must have ${EXPECTED_ENDPOINT_COUNT} entries, got ${ENDPOINT_KEYS.length}`,
193
- );
194
- });
195
-
196
- test("every server.ts /api/* path has a corresponding ENDPOINTS entry", () => {
197
- const registryPaths = new Set<string>(
198
- ENDPOINT_KEYS.map((k) => ENDPOINTS[k].path as string),
199
- );
200
- for (const path of SERVER_TS_PATHS) {
201
- assert.ok(
202
- registryPaths.has(path),
203
- `server.ts path "${path}" has no ENDPOINTS entry`,
204
- );
205
- }
206
- });
207
-
208
- test("every ENDPOINTS path starts with /api/", () => {
209
- for (const key of ENDPOINT_KEYS) {
210
- assert.ok(
211
- ENDPOINTS[key].path.startsWith("/api/"),
212
- `ENDPOINTS.${key}.path must start with /api/ — got "${ENDPOINTS[key].path}"`,
213
- );
214
- }
215
- });
216
-
217
- test("every ENDPOINTS entry has method and description", () => {
218
- for (const key of ENDPOINT_KEYS) {
219
- const ep = ENDPOINTS[key];
220
- assert.ok(ep.method, `ENDPOINTS.${key} must have method`);
221
- assert.ok(ep.description, `ENDPOINTS.${key} must have description`);
222
- }
223
- });
189
+ test("has exactly 16 endpoint entries", () => {
190
+ assert.equal(
191
+ ENDPOINT_KEYS.length,
192
+ EXPECTED_ENDPOINT_COUNT,
193
+ `ENDPOINTS must have ${EXPECTED_ENDPOINT_COUNT} entries, got ${ENDPOINT_KEYS.length}`,
194
+ );
195
+ });
196
+
197
+ test("every server.ts /api/* path has a corresponding ENDPOINTS entry", () => {
198
+ const registryPaths = new Set<string>(
199
+ ENDPOINT_KEYS.map((k) => ENDPOINTS[k].path as string),
200
+ );
201
+ for (const path of SERVER_TS_PATHS) {
202
+ assert.ok(
203
+ registryPaths.has(path),
204
+ `server.ts path "${path}" has no ENDPOINTS entry`,
205
+ );
206
+ }
207
+ });
208
+
209
+ test("every ENDPOINTS path starts with /api/", () => {
210
+ for (const key of ENDPOINT_KEYS) {
211
+ assert.ok(
212
+ ENDPOINTS[key].path.startsWith("/api/"),
213
+ `ENDPOINTS.${key}.path must start with /api/ — got "${ENDPOINTS[key].path}"`,
214
+ );
215
+ }
216
+ });
217
+
218
+ test("every ENDPOINTS entry has method and description", () => {
219
+ for (const key of ENDPOINT_KEYS) {
220
+ const ep = ENDPOINTS[key];
221
+ assert.ok(ep.method, `ENDPOINTS.${key} must have method`);
222
+ assert.ok(ep.description, `ENDPOINTS.${key} must have description`);
223
+ }
224
+ });
224
225
  });
225
226
 
226
227
  // ─── Per-endpoint runtime validation ─────────────────────────────────────────
227
228
 
228
229
  describe("GET /api/snapshot", () => {
229
- test("sample payload validates field presence and types", () => {
230
- const raw = JSON.stringify({
231
- updatedAt: "2025-01-01T00:00:00Z",
232
- tier: "ultra-compact",
233
- presetTier: "super-compact",
234
- pressure: 45.2,
235
- config: {
236
- fastGatePct: 60,
237
- thresholdTokens: 100000,
238
- anchorUserMessages: 3,
239
- preserveRecent: 10,
240
- auto: true,
241
- autoInlineK: 5,
242
- },
243
- session: {
244
- id: "sess-123",
245
- state: "idle",
246
- persistedThisSession: true,
247
- lastCheckpointId: "ckpt-456",
248
- lastCompactedFrom: 50000,
249
- },
250
- context: {
251
- tokens: 42000,
252
- percent: 42,
253
- contextWindow: 200000,
254
- },
255
- trigger: {
256
- armed: true,
257
- ready: false,
258
- currentTokens: 42000,
259
- thresholdTokens: 100000,
260
- fastGatePct: 60,
261
- },
262
- store: {
263
- checkpointCount: 15,
264
- totalTokenEstimate: 120000,
265
- originalTokens: 200000,
266
- tokensSaved: 80000,
267
- injectedCount: 30,
268
- dedupHitRate: 12.5,
269
- storageDedupRate: 8.3,
270
- dedupCollapsed: 5,
271
- },
272
- crew: { activeAgents: 2, currentTurn: 1 },
273
- repo: {
274
- checkpointCount: 15,
275
- totalTokenEstimate: 120000,
276
- originalTokens: 200000,
277
- tokensSaved: 80000,
278
- sessionCount: 5,
279
- dedupAttempts: 10,
280
- dedupCollapsed: 5,
281
- storageDedupRate: 8.3,
282
- },
283
- integrity: {
284
- regionsRetained: 8,
285
- compressedOriginalBytes: 4096,
286
- duplicatesCollapsed: 5,
287
- bytesPermanentlyDeleted: 1024,
288
- },
289
- cacheHits: {
290
- session: 3,
291
- total: 20,
292
- sessionTokensSaved: 1500,
293
- totalTokensSaved: 10000,
294
- },
295
- compacts: { session: 3, total: 20 },
296
- timeSaved: {
297
- compact: { sessionSec: 120, totalSec: 600 },
298
- cacheHit: { sessionSec: 30, totalSec: 200 },
299
- },
300
- compression: {
301
- session: {
302
- tokensIn: 50000,
303
- tokensOut: 20000,
304
- tokensFreed: 30000,
305
- compressionPct: 60,
306
- dedupPct: 5,
307
- },
308
- repo: {
309
- tokensIn: 200000,
310
- tokensOut: 80000,
311
- tokensFreed: 120000,
312
- compressionPct: 60,
313
- dedupPct: 8,
314
- },
315
- },
316
- });
317
- const obj: Record<string, unknown> = JSON.parse(raw);
318
-
319
- assertField(obj, "updatedAt", ["string", "null"]);
320
- assertField(obj, "tier", ["string"]);
321
- assertField(obj, "presetTier", ["string"]);
322
- assertField(obj, "pressure", ["number"]);
323
- assertField(obj, "config", ["object"]);
324
- assertField(obj, "session", ["object"]);
325
- assertField(obj, "context", ["object"]);
326
- assertField(obj, "trigger", ["object"]);
327
- assertField(obj, "store", ["object"]);
328
- assertField(obj, "crew", ["object"]);
329
- assertField(obj, "repo", ["object"]);
330
- assertField(obj, "integrity", ["object"]);
331
- assertField(obj, "cacheHits", ["object"]);
332
- assertField(obj, "compacts", ["object"]);
333
- assertField(obj, "timeSaved", ["object"]);
334
- assertField(obj, "compression", ["object"]);
335
-
336
- // Spot-check nested fields
337
- const config = assertObject(obj, "config")!;
338
- assertField(config, "fastGatePct", ["number"]);
339
- assertField(config, "auto", ["boolean"]);
340
-
341
- const session = assertObject(obj, "session")!;
342
- assertField(session, "id", ["string", "null"]);
343
- assertField(session, "persistedThisSession", ["boolean"]);
344
-
345
- const context = assertObject(obj, "context")!;
346
- assertField(context, "tokens", ["number", "null"]);
347
- assertField(context, "contextWindow", ["number"]);
348
- });
230
+ test("sample payload validates field presence and types", () => {
231
+ const raw = JSON.stringify({
232
+ updatedAt: "2025-01-01T00:00:00Z",
233
+ tier: "ultra-compact",
234
+ presetTier: "super-compact",
235
+ pressure: 45.2,
236
+ config: {
237
+ fastGatePct: 60,
238
+ thresholdTokens: 100000,
239
+ anchorUserMessages: 3,
240
+ preserveRecent: 10,
241
+ auto: true,
242
+ autoInlineK: 5,
243
+ },
244
+ session: {
245
+ id: "sess-123",
246
+ state: "idle",
247
+ persistedThisSession: true,
248
+ lastCheckpointId: "ckpt-456",
249
+ lastCompactedFrom: 50000,
250
+ },
251
+ context: {
252
+ tokens: 42000,
253
+ percent: 42,
254
+ contextWindow: 200000,
255
+ },
256
+ trigger: {
257
+ armed: true,
258
+ ready: false,
259
+ currentTokens: 42000,
260
+ thresholdTokens: 100000,
261
+ fastGatePct: 60,
262
+ },
263
+ store: {
264
+ checkpointCount: 15,
265
+ totalTokenEstimate: 120000,
266
+ originalTokens: 200000,
267
+ tokensSaved: 80000,
268
+ injectedCount: 30,
269
+ dedupHitRate: 12.5,
270
+ storageDedupRate: 8.3,
271
+ dedupCollapsed: 5,
272
+ },
273
+ crew: { activeAgents: 2, currentTurn: 1 },
274
+ repo: {
275
+ checkpointCount: 15,
276
+ totalTokenEstimate: 120000,
277
+ originalTokens: 200000,
278
+ tokensSaved: 80000,
279
+ sessionCount: 5,
280
+ dedupAttempts: 10,
281
+ dedupCollapsed: 5,
282
+ storageDedupRate: 8.3,
283
+ },
284
+ integrity: {
285
+ regionsRetained: 8,
286
+ compressedOriginalBytes: 4096,
287
+ duplicatesCollapsed: 5,
288
+ bytesPermanentlyDeleted: 1024,
289
+ },
290
+ cacheHits: {
291
+ session: 3,
292
+ total: 20,
293
+ sessionTokensSaved: 1500,
294
+ totalTokensSaved: 10000,
295
+ },
296
+ compacts: { session: 3, total: 20 },
297
+ timeSaved: {
298
+ compact: { sessionSec: 120, totalSec: 600 },
299
+ cacheHit: { sessionSec: 30, totalSec: 200 },
300
+ },
301
+ compression: {
302
+ session: {
303
+ tokensIn: 50000,
304
+ tokensOut: 20000,
305
+ tokensFreed: 30000,
306
+ compressionPct: 60,
307
+ dedupPct: 5,
308
+ },
309
+ repo: {
310
+ tokensIn: 200000,
311
+ tokensOut: 80000,
312
+ tokensFreed: 120000,
313
+ compressionPct: 60,
314
+ dedupPct: 8,
315
+ },
316
+ },
317
+ });
318
+ const obj: Record<string, unknown> = JSON.parse(raw);
319
+
320
+ assertField(obj, "updatedAt", ["string", "null"]);
321
+ assertField(obj, "tier", ["string"]);
322
+ assertField(obj, "presetTier", ["string"]);
323
+ assertField(obj, "pressure", ["number"]);
324
+ assertField(obj, "config", ["object"]);
325
+ assertField(obj, "session", ["object"]);
326
+ assertField(obj, "context", ["object"]);
327
+ assertField(obj, "trigger", ["object"]);
328
+ assertField(obj, "store", ["object"]);
329
+ assertField(obj, "crew", ["object"]);
330
+ assertField(obj, "repo", ["object"]);
331
+ assertField(obj, "integrity", ["object"]);
332
+ assertField(obj, "cacheHits", ["object"]);
333
+ assertField(obj, "compacts", ["object"]);
334
+ assertField(obj, "timeSaved", ["object"]);
335
+ assertField(obj, "compression", ["object"]);
336
+
337
+ // Spot-check nested fields
338
+ const config = assertObject(obj, "config")!;
339
+ assertField(config, "fastGatePct", ["number"]);
340
+ assertField(config, "auto", ["boolean"]);
341
+
342
+ const session = assertObject(obj, "session")!;
343
+ assertField(session, "id", ["string", "null"]);
344
+ assertField(session, "persistedThisSession", ["boolean"]);
345
+
346
+ const context = assertObject(obj, "context")!;
347
+ assertField(context, "tokens", ["number", "null"]);
348
+ assertField(context, "contextWindow", ["number"]);
349
+ });
349
350
  });
350
351
 
351
352
  describe("GET /api/version", () => {
352
- test("sample payload validates field presence and types", () => {
353
- const raw = JSON.stringify({ version: "1.2.3" });
354
- const obj: Record<string, unknown> = JSON.parse(raw);
353
+ test("sample payload validates field presence and types", () => {
354
+ const raw = JSON.stringify({ version: "1.2.3" });
355
+ const obj: Record<string, unknown> = JSON.parse(raw);
355
356
 
356
- assertField(obj, "version", ["string"]);
357
- });
357
+ assertField(obj, "version", ["string"]);
358
+ });
358
359
  });
359
360
 
360
361
  describe("GET /api/index", () => {
361
- test("summary payload validates field presence and types", () => {
362
- const raw = JSON.stringify({
363
- updatedAt: "2025-01-01T00:00:00Z",
364
- totalRepos: 3,
365
- totalCheckpoints: 45,
366
- totalTokensSaved: 150000,
367
- totalCompressedOriginalBytes: 40960,
368
- repos: [
369
- {
370
- repoRoot: "/home/user/repo1",
371
- displayName: "repo1",
372
- stateDir: "/home/user/.pi/mega-compact/repo1",
373
- checkpointCount: 15,
374
- tokensSaved: 50000,
375
- compressedOriginalBytes: 16384,
376
- lastCompactedAt: 1700000000000,
377
- provider: "anthropic",
378
- providerName: "Anthropic",
379
- modelName: "claude-3",
380
- inputRate: 100,
381
- outputRate: 80,
382
- lastSeen: 1700000000000,
383
- tokensKept: 20000,
384
- tokensDropped: 30000,
385
- sessions: 5,
386
- contextWindow: 200000,
387
- maxTokens: 180000,
388
- reasoning: false,
389
- },
390
- ],
391
- });
392
- const obj: Record<string, unknown> = JSON.parse(raw);
393
-
394
- assertField(obj, "updatedAt", ["string"]);
395
- assertField(obj, "totalRepos", ["number"]);
396
- assertField(obj, "totalCheckpoints", ["number"]);
397
- assertField(obj, "totalTokensSaved", ["number"]);
398
- assertField(obj, "totalCompressedOriginalBytes", ["number"]);
399
- assertField(obj, "repos", ["array"]);
400
-
401
- const repos = obj["repos"] as unknown[];
402
- assert.ok(repos.length > 0, "repos array should have at least one entry");
403
- const first = repos[0] as Record<string, unknown>;
404
- assertField(first, "repoRoot", ["string"]);
405
- assertField(first, "displayName", ["string"]);
406
- assertField(first, "checkpointCount", ["number"]);
407
- assertField(first, "lastCompactedAt", ["number", "null"]);
408
- assertField(first, "provider", ["string", "null"]);
409
- assertField(first, "lastSeen", ["number"]);
410
- });
411
-
412
- test("fallback payload validates field presence and types", () => {
413
- const raw = JSON.stringify({
414
- updatedAt: null,
415
- summary: null,
416
- repos: [],
417
- });
418
- const obj: Record<string, unknown> = JSON.parse(raw);
419
-
420
- assertField(obj, "updatedAt", ["null"]);
421
- assertField(obj, "summary", ["null"]);
422
- assertField(obj, "repos", ["array"]);
423
- assert.equal((obj["repos"] as unknown[]).length, 0);
424
- });
362
+ test("summary payload validates field presence and types", () => {
363
+ const raw = JSON.stringify({
364
+ updatedAt: "2025-01-01T00:00:00Z",
365
+ totalRepos: 3,
366
+ totalCheckpoints: 45,
367
+ totalTokensSaved: 150000,
368
+ totalCompressedOriginalBytes: 40960,
369
+ repos: [
370
+ {
371
+ repoRoot: "/home/user/repo1",
372
+ displayName: "repo1",
373
+ stateDir: "/home/user/.pi/mega-compact/repo1",
374
+ checkpointCount: 15,
375
+ tokensSaved: 50000,
376
+ compressedOriginalBytes: 16384,
377
+ lastCompactedAt: 1700000000000,
378
+ provider: "anthropic",
379
+ providerName: "Anthropic",
380
+ modelName: "claude-3",
381
+ inputRate: 100,
382
+ outputRate: 80,
383
+ lastSeen: 1700000000000,
384
+ tokensKept: 20000,
385
+ tokensDropped: 30000,
386
+ sessions: 5,
387
+ contextWindow: 200000,
388
+ maxTokens: 180000,
389
+ reasoning: false,
390
+ },
391
+ ],
392
+ });
393
+ const obj: Record<string, unknown> = JSON.parse(raw);
394
+
395
+ assertField(obj, "updatedAt", ["string"]);
396
+ assertField(obj, "totalRepos", ["number"]);
397
+ assertField(obj, "totalCheckpoints", ["number"]);
398
+ assertField(obj, "totalTokensSaved", ["number"]);
399
+ assertField(obj, "totalCompressedOriginalBytes", ["number"]);
400
+ assertField(obj, "repos", ["array"]);
401
+
402
+ const repos = obj["repos"] as unknown[];
403
+ assert.ok(repos.length > 0, "repos array should have at least one entry");
404
+ const first = repos[0] as Record<string, unknown>;
405
+ assertField(first, "repoRoot", ["string"]);
406
+ assertField(first, "displayName", ["string"]);
407
+ assertField(first, "checkpointCount", ["number"]);
408
+ assertField(first, "lastCompactedAt", ["number", "null"]);
409
+ assertField(first, "provider", ["string", "null"]);
410
+ assertField(first, "lastSeen", ["number"]);
411
+ });
412
+
413
+ test("fallback payload validates field presence and types", () => {
414
+ const raw = JSON.stringify({
415
+ updatedAt: null,
416
+ summary: null,
417
+ repos: [],
418
+ });
419
+ const obj: Record<string, unknown> = JSON.parse(raw);
420
+
421
+ assertField(obj, "updatedAt", ["null"]);
422
+ assertField(obj, "summary", ["null"]);
423
+ assertField(obj, "repos", ["array"]);
424
+ assert.equal((obj["repos"] as unknown[]).length, 0);
425
+ });
425
426
  });
426
427
 
427
428
  describe("GET /api/repos", () => {
428
- test("sample payload validates field presence and types", () => {
429
- const raw = JSON.stringify({
430
- updatedAt: "2025-01-01T00:00:00Z",
431
- repos: [
432
- {
433
- repoRoot: "/home/user/repo1",
434
- displayName: "repo1",
435
- stateDir: "/home/user/.pi/repo1",
436
- checkpointCount: 15,
437
- tokensSaved: 50000,
438
- compressedOriginalBytes: 16384,
439
- lastCompactedAt: null,
440
- provider: null,
441
- providerName: null,
442
- modelName: null,
443
- inputRate: null,
444
- outputRate: null,
445
- lastSeen: 1700000000000,
446
- tokensKept: 20000,
447
- tokensDropped: 30000,
448
- sessions: 5,
449
- contextWindow: null,
450
- maxTokens: null,
451
- reasoning: null,
452
- },
453
- ],
454
- count: 1,
455
- });
456
- const obj: Record<string, unknown> = JSON.parse(raw);
457
-
458
- assertField(obj, "updatedAt", ["string", "null"]);
459
- assertField(obj, "repos", ["array"]);
460
- assertField(obj, "count", ["number"]);
461
- });
429
+ test("sample payload validates field presence and types", () => {
430
+ const raw = JSON.stringify({
431
+ updatedAt: "2025-01-01T00:00:00Z",
432
+ repos: [
433
+ {
434
+ repoRoot: "/home/user/repo1",
435
+ displayName: "repo1",
436
+ stateDir: "/home/user/.pi/repo1",
437
+ checkpointCount: 15,
438
+ tokensSaved: 50000,
439
+ compressedOriginalBytes: 16384,
440
+ lastCompactedAt: null,
441
+ provider: null,
442
+ providerName: null,
443
+ modelName: null,
444
+ inputRate: null,
445
+ outputRate: null,
446
+ lastSeen: 1700000000000,
447
+ tokensKept: 20000,
448
+ tokensDropped: 30000,
449
+ sessions: 5,
450
+ contextWindow: null,
451
+ maxTokens: null,
452
+ reasoning: null,
453
+ },
454
+ ],
455
+ count: 1,
456
+ });
457
+ const obj: Record<string, unknown> = JSON.parse(raw);
458
+
459
+ assertField(obj, "updatedAt", ["string", "null"]);
460
+ assertField(obj, "repos", ["array"]);
461
+ assertField(obj, "count", ["number"]);
462
+ });
462
463
  });
463
464
 
464
465
  describe("GET /api/summary", () => {
465
- test("sample payload validates field presence and types", () => {
466
- const raw = JSON.stringify({
467
- updatedAt: "2025-01-01T00:00:00Z",
468
- summary: {
469
- totalRepos: 3,
470
- totalCheckpoints: 45,
471
- totalTokensSaved: 150000,
472
- totalCompressedOriginalBytes: 40960,
473
- },
474
- activeRepos: 2,
475
- totalRepos: 3,
476
- });
477
- const obj: Record<string, unknown> = JSON.parse(raw);
478
-
479
- assertField(obj, "updatedAt", ["string", "null"]);
480
- assertField(obj, "summary", ["object", "null"]);
481
- assertField(obj, "activeRepos", ["number"]);
482
- assertField(obj, "totalRepos", ["number"]);
483
-
484
- const summary = assertObject(obj, "summary")!;
485
- assertField(summary, "totalRepos", ["number"]);
486
- assertField(summary, "totalCheckpoints", ["number"]);
487
- assertField(summary, "totalTokensSaved", ["number"]);
488
- assertField(summary, "totalCompressedOriginalBytes", ["number"]);
489
- });
490
-
491
- test("null summary validates", () => {
492
- const raw = JSON.stringify({
493
- updatedAt: null,
494
- summary: null,
495
- activeRepos: 0,
496
- totalRepos: 0,
497
- });
498
- const obj: Record<string, unknown> = JSON.parse(raw);
499
-
500
- assertField(obj, "updatedAt", ["null"]);
501
- assertField(obj, "summary", ["null"]);
502
- assertField(obj, "activeRepos", ["number"]);
503
- assertField(obj, "totalRepos", ["number"]);
504
- });
466
+ test("sample payload validates field presence and types", () => {
467
+ const raw = JSON.stringify({
468
+ updatedAt: "2025-01-01T00:00:00Z",
469
+ summary: {
470
+ totalRepos: 3,
471
+ totalCheckpoints: 45,
472
+ totalTokensSaved: 150000,
473
+ totalCompressedOriginalBytes: 40960,
474
+ },
475
+ activeRepos: 2,
476
+ totalRepos: 3,
477
+ });
478
+ const obj: Record<string, unknown> = JSON.parse(raw);
479
+
480
+ assertField(obj, "updatedAt", ["string", "null"]);
481
+ assertField(obj, "summary", ["object", "null"]);
482
+ assertField(obj, "activeRepos", ["number"]);
483
+ assertField(obj, "totalRepos", ["number"]);
484
+
485
+ const summary = assertObject(obj, "summary")!;
486
+ assertField(summary, "totalRepos", ["number"]);
487
+ assertField(summary, "totalCheckpoints", ["number"]);
488
+ assertField(summary, "totalTokensSaved", ["number"]);
489
+ assertField(summary, "totalCompressedOriginalBytes", ["number"]);
490
+ });
491
+
492
+ test("null summary validates", () => {
493
+ const raw = JSON.stringify({
494
+ updatedAt: null,
495
+ summary: null,
496
+ activeRepos: 0,
497
+ totalRepos: 0,
498
+ });
499
+ const obj: Record<string, unknown> = JSON.parse(raw);
500
+
501
+ assertField(obj, "updatedAt", ["null"]);
502
+ assertField(obj, "summary", ["null"]);
503
+ assertField(obj, "activeRepos", ["number"]);
504
+ assertField(obj, "totalRepos", ["number"]);
505
+ });
505
506
  });
506
507
 
507
508
  describe("GET /api/drift", () => {
508
- test("sample payload validates field presence and types", () => {
509
- const raw = JSON.stringify({
510
- generatedAt: 1700000000,
511
- totals: {
512
- ok: 2,
513
- warn: 1,
514
- stale: 1,
515
- compactionLag: 0,
516
- modelChurn: 0,
517
- },
518
- repos: [
519
- {
520
- repoRoot: "/home/user/repo1",
521
- displayName: "repo1",
522
- lastSeen: 1700000000,
523
- lastCompactedAt: 1699990000,
524
- modelCapturedAt: null,
525
- signals: [
526
- {
527
- kind: "stale",
528
- severity: "warn",
529
- detail: "No activity for 48h",
530
- },
531
- ],
532
- status: "warn",
533
- },
534
- ],
535
- });
536
- const obj: Record<string, unknown> = JSON.parse(raw);
537
-
538
- assertField(obj, "generatedAt", ["number"]);
539
- assertField(obj, "totals", ["object"]);
540
- assertField(obj, "repos", ["array"]);
541
-
542
- const totals = assertObject(obj, "totals")!;
543
- assertField(totals, "ok", ["number"]);
544
- assertField(totals, "warn", ["number"]);
545
- assertField(totals, "stale", ["number"]);
546
- assertField(totals, "compactionLag", ["number"]);
547
- assertField(totals, "modelChurn", ["number"]);
548
-
549
- const repos = obj["repos"] as unknown[];
550
- const first = repos[0] as Record<string, unknown>;
551
- assertField(first, "repoRoot", ["string"]);
552
- assertField(first, "displayName", ["string"]);
553
- assertField(first, "lastSeen", ["number"]);
554
- assertField(first, "lastCompactedAt", ["number", "null"]);
555
- assertField(first, "modelCapturedAt", ["number", "null"]);
556
- assertField(first, "signals", ["array"]);
557
- assertField(first, "status", ["string"]);
558
-
559
- const signals = first["signals"] as unknown[];
560
- const sig = signals[0] as Record<string, unknown>;
561
- assertField(sig, "kind", ["string"]);
562
- assertField(sig, "severity", ["string"]);
563
- assertField(sig, "detail", ["string"]);
564
- });
509
+ test("sample payload validates field presence and types", () => {
510
+ const raw = JSON.stringify({
511
+ generatedAt: 1700000000,
512
+ totals: {
513
+ ok: 2,
514
+ warn: 1,
515
+ stale: 1,
516
+ compactionLag: 0,
517
+ modelChurn: 0,
518
+ },
519
+ repos: [
520
+ {
521
+ repoRoot: "/home/user/repo1",
522
+ displayName: "repo1",
523
+ lastSeen: 1700000000,
524
+ lastCompactedAt: 1699990000,
525
+ modelCapturedAt: null,
526
+ signals: [
527
+ {
528
+ kind: "stale",
529
+ severity: "warn",
530
+ detail: "No activity for 48h",
531
+ },
532
+ ],
533
+ status: "warn",
534
+ },
535
+ ],
536
+ });
537
+ const obj: Record<string, unknown> = JSON.parse(raw);
538
+
539
+ assertField(obj, "generatedAt", ["number"]);
540
+ assertField(obj, "totals", ["object"]);
541
+ assertField(obj, "repos", ["array"]);
542
+
543
+ const totals = assertObject(obj, "totals")!;
544
+ assertField(totals, "ok", ["number"]);
545
+ assertField(totals, "warn", ["number"]);
546
+ assertField(totals, "stale", ["number"]);
547
+ assertField(totals, "compactionLag", ["number"]);
548
+ assertField(totals, "modelChurn", ["number"]);
549
+
550
+ const repos = obj["repos"] as unknown[];
551
+ const first = repos[0] as Record<string, unknown>;
552
+ assertField(first, "repoRoot", ["string"]);
553
+ assertField(first, "displayName", ["string"]);
554
+ assertField(first, "lastSeen", ["number"]);
555
+ assertField(first, "lastCompactedAt", ["number", "null"]);
556
+ assertField(first, "modelCapturedAt", ["number", "null"]);
557
+ assertField(first, "signals", ["array"]);
558
+ assertField(first, "status", ["string"]);
559
+
560
+ const signals = first["signals"] as unknown[];
561
+ const sig = signals[0] as Record<string, unknown>;
562
+ assertField(sig, "kind", ["string"]);
563
+ assertField(sig, "severity", ["string"]);
564
+ assertField(sig, "detail", ["string"]);
565
+ });
565
566
  });
566
567
 
567
568
  describe("GET /api/servers", () => {
568
- test("sample payload validates field presence and types", () => {
569
- const raw = JSON.stringify({
570
- updatedAt: "2025-01-01T00:00:00Z",
571
- servers: [
572
- {
573
- repoRoot: "/home/user/repo1",
574
- displayName: "repo1",
575
- model: "claude-3",
576
- provider: "anthropic",
577
- lastSeen: 1700000000,
578
- lastCompactedAt: 1699990000,
579
- tier: "ultra-compact",
580
- contextPct: 42,
581
- state: "idle",
582
- },
583
- ],
584
- });
585
- const obj: Record<string, unknown> = JSON.parse(raw);
586
-
587
- assertField(obj, "updatedAt", ["string"]);
588
- assertField(obj, "servers", ["array"]);
589
-
590
- const servers = obj["servers"] as unknown[];
591
- const first = servers[0] as Record<string, unknown>;
592
- assertField(first, "repoRoot", ["string"]);
593
- assertField(first, "displayName", ["string"]);
594
- assertField(first, "model", ["string", "null"]);
595
- assertField(first, "provider", ["string", "null"]);
596
- assertField(first, "lastSeen", ["number"]);
597
- assertField(first, "lastCompactedAt", ["number", "null"]);
598
- });
569
+ test("sample payload validates field presence and types", () => {
570
+ const raw = JSON.stringify({
571
+ updatedAt: "2025-01-01T00:00:00Z",
572
+ servers: [
573
+ {
574
+ repoRoot: "/home/user/repo1",
575
+ displayName: "repo1",
576
+ model: "claude-3",
577
+ provider: "anthropic",
578
+ lastSeen: 1700000000,
579
+ lastCompactedAt: 1699990000,
580
+ tier: "ultra-compact",
581
+ contextPct: 42,
582
+ state: "idle",
583
+ },
584
+ ],
585
+ });
586
+ const obj: Record<string, unknown> = JSON.parse(raw);
587
+
588
+ assertField(obj, "updatedAt", ["string"]);
589
+ assertField(obj, "servers", ["array"]);
590
+
591
+ const servers = obj["servers"] as unknown[];
592
+ const first = servers[0] as Record<string, unknown>;
593
+ assertField(first, "repoRoot", ["string"]);
594
+ assertField(first, "displayName", ["string"]);
595
+ assertField(first, "model", ["string", "null"]);
596
+ assertField(first, "provider", ["string", "null"]);
597
+ assertField(first, "lastSeen", ["number"]);
598
+ assertField(first, "lastCompactedAt", ["number", "null"]);
599
+ });
599
600
  });
600
601
 
601
602
  describe("GET /api/game-state", () => {
602
- test("sample payload with active ritual validates", () => {
603
- const raw = JSON.stringify({
604
- config: {
605
- enabled: true,
606
- theme: "ocean",
607
- displayMode: "full",
608
- },
609
- activeRitual: {
610
- sessionId: "ritual-1",
611
- stageIndex: 2,
612
- startedAt: "2025-01-01T00:00:00Z",
613
- elapsed: 300000,
614
- },
615
- });
616
- const obj: Record<string, unknown> = JSON.parse(raw);
617
-
618
- assertField(obj, "config", ["object"]);
619
- assertField(obj, "activeRitual", ["object", "null"]);
620
-
621
- const config = assertObject(obj, "config")!;
622
- assertField(config, "enabled", ["boolean"]);
623
- assertField(config, "theme", ["string"]);
624
- assertField(config, "displayMode", ["string"]);
625
-
626
- const ritual = assertObject(obj, "activeRitual")!;
627
- assertField(ritual, "sessionId", ["string"]);
628
- assertField(ritual, "stageIndex", ["number"]);
629
- assertField(ritual, "startedAt", ["string"]);
630
- assertField(ritual, "elapsed", ["number"]);
631
- });
632
-
633
- test("sample payload with null ritual validates", () => {
634
- const raw = JSON.stringify({
635
- config: {
636
- enabled: false,
637
- theme: "mono",
638
- displayMode: "minimal",
639
- },
640
- activeRitual: null,
641
- });
642
- const obj: Record<string, unknown> = JSON.parse(raw);
643
-
644
- assertField(obj, "config", ["object"]);
645
- assertField(obj, "activeRitual", ["null"]);
646
- });
603
+ test("sample payload with active ritual validates", () => {
604
+ const raw = JSON.stringify({
605
+ config: {
606
+ enabled: true,
607
+ theme: "ocean",
608
+ displayMode: "full",
609
+ },
610
+ activeRitual: {
611
+ sessionId: "ritual-1",
612
+ stageIndex: 2,
613
+ startedAt: "2025-01-01T00:00:00Z",
614
+ elapsed: 300000,
615
+ },
616
+ });
617
+ const obj: Record<string, unknown> = JSON.parse(raw);
618
+
619
+ assertField(obj, "config", ["object"]);
620
+ assertField(obj, "activeRitual", ["object", "null"]);
621
+
622
+ const config = assertObject(obj, "config")!;
623
+ assertField(config, "enabled", ["boolean"]);
624
+ assertField(config, "theme", ["string"]);
625
+ assertField(config, "displayMode", ["string"]);
626
+
627
+ const ritual = assertObject(obj, "activeRitual")!;
628
+ assertField(ritual, "sessionId", ["string"]);
629
+ assertField(ritual, "stageIndex", ["number"]);
630
+ assertField(ritual, "startedAt", ["string"]);
631
+ assertField(ritual, "elapsed", ["number"]);
632
+ });
633
+
634
+ test("sample payload with null ritual validates", () => {
635
+ const raw = JSON.stringify({
636
+ config: {
637
+ enabled: false,
638
+ theme: "mono",
639
+ displayMode: "minimal",
640
+ },
641
+ activeRitual: null,
642
+ });
643
+ const obj: Record<string, unknown> = JSON.parse(raw);
644
+
645
+ assertField(obj, "config", ["object"]);
646
+ assertField(obj, "activeRitual", ["null"]);
647
+ });
647
648
  });
648
649
 
649
650
  describe("PUT /api/game-state", () => {
650
- test("response payload validates field presence and types", () => {
651
- // PUT /api/game-state returns GameStateResponse (same as GET)
652
- const raw = JSON.stringify({
653
- config: {
654
- enabled: true,
655
- theme: "ember",
656
- displayMode: "full",
657
- },
658
- activeRitual: null,
659
- });
660
- const obj: Record<string, unknown> = JSON.parse(raw);
661
-
662
- assertField(obj, "config", ["object"]);
663
- assertField(obj, "activeRitual", ["object", "null"]);
664
-
665
- const config = assertObject(obj, "config")!;
666
- assertField(config, "enabled", ["boolean"]);
667
- assertField(config, "theme", ["string"]);
668
- assertField(config, "displayMode", ["string"]);
669
- });
651
+ test("response payload validates field presence and types", () => {
652
+ // PUT /api/game-state returns GameStateResponse (same as GET)
653
+ const raw = JSON.stringify({
654
+ config: {
655
+ enabled: true,
656
+ theme: "ember",
657
+ displayMode: "full",
658
+ },
659
+ activeRitual: null,
660
+ });
661
+ const obj: Record<string, unknown> = JSON.parse(raw);
662
+
663
+ assertField(obj, "config", ["object"]);
664
+ assertField(obj, "activeRitual", ["object", "null"]);
665
+
666
+ const config = assertObject(obj, "config")!;
667
+ assertField(config, "enabled", ["boolean"]);
668
+ assertField(config, "theme", ["string"]);
669
+ assertField(config, "displayMode", ["string"]);
670
+ });
670
671
  });
671
672
 
672
673
  describe("GET /api/game-scores", () => {
673
- test("sample payload (array) validates element field presence and types", () => {
674
- const raw = JSON.stringify([
675
- {
676
- repo_root: "/home/user/repo1",
677
- value: 1500,
678
- ts: 1700000000000,
679
- meta: { metric: "cache" },
680
- },
681
- ]);
682
- const arr = JSON.parse(raw) as unknown[];
683
- assert.ok(Array.isArray(arr), "response must be an array");
684
-
685
- const first = arr[0] as Record<string, unknown>;
686
- assertField(first, "repo_root", ["string"]);
687
- assertField(first, "value", ["number"]);
688
- assertField(first, "ts", ["number"]);
689
- assert.ok("meta" in first, 'field "meta" must exist');
690
- });
674
+ test("sample payload (array) validates element field presence and types", () => {
675
+ const raw = JSON.stringify([
676
+ {
677
+ repo_root: "/home/user/repo1",
678
+ value: 1500,
679
+ ts: 1700000000000,
680
+ meta: { metric: "cache" },
681
+ },
682
+ ]);
683
+ const arr = JSON.parse(raw) as unknown[];
684
+ assert.ok(Array.isArray(arr), "response must be an array");
685
+
686
+ const first = arr[0] as Record<string, unknown>;
687
+ assertField(first, "repo_root", ["string"]);
688
+ assertField(first, "value", ["number"]);
689
+ assertField(first, "ts", ["number"]);
690
+ assert.ok("meta" in first, 'field "meta" must exist');
691
+ });
691
692
  });
692
693
 
693
694
  describe("GET /api/perf", () => {
694
- test("sample payload validates field presence and types", () => {
695
- const raw = JSON.stringify({
696
- updatedAt: "2025-01-01T00:00:00Z",
697
- windowMinutes: 30,
698
- sampleCount: 100,
699
- turn_latency_ms: { p50: 500, p95: 2000, n: 100 },
700
- provider_latency_ms: { p50: 300, p95: 1500, n: 100 },
701
- tps: { avg: 45.5, n: 100 },
702
- cache_hit_pct: { avg: 30, latest: 25, n: 100 },
703
- db_recompute_ms: { p50: 50, p95: 200, n: 50 },
704
- disk_write_ms: { p50: 10, p95: 50, n: 50 },
705
- rss_mb: { latest: 256, n: 10 },
706
- heap_mb: { latest: 128, n: 10 },
707
- cpu_user_ms: { latest: 5000, n: 10 },
708
- cpu_sys_ms: { latest: 1000, n: 10 },
709
- diag: {
710
- ctxFastGate: 2,
711
- liveTrimFires: 5,
712
- liveTrimReplays: 3,
713
- },
714
- });
715
- const obj: Record<string, unknown> = JSON.parse(raw);
716
-
717
- assertField(obj, "updatedAt", ["string"]);
718
- assertField(obj, "windowMinutes", ["number"]);
719
- assertField(obj, "sampleCount", ["number"]);
720
- assertField(obj, "turn_latency_ms", ["object"]);
721
- assertField(obj, "provider_latency_ms", ["object"]);
722
- assertField(obj, "tps", ["object"]);
723
- assertField(obj, "cache_hit_pct", ["object"]);
724
- assertField(obj, "db_recompute_ms", ["object"]);
725
- assertField(obj, "disk_write_ms", ["object"]);
726
- assertField(obj, "rss_mb", ["object"]);
727
- assertField(obj, "heap_mb", ["object"]);
728
- assertField(obj, "cpu_user_ms", ["object"]);
729
- assertField(obj, "cpu_sys_ms", ["object"]);
730
- assertField(obj, "diag", ["object", "null"]);
731
-
732
- // Spot-check percentile sub-object
733
- const tl = assertObject(obj, "turn_latency_ms")!;
734
- assertField(tl, "p50", ["number"]);
735
- assertField(tl, "p95", ["number"]);
736
- assertField(tl, "n", ["number"]);
737
-
738
- const diag = assertObject(obj, "diag")!;
739
- assertField(diag, "ctxFastGate", ["number"]);
740
- assertField(diag, "liveTrimFires", ["number"]);
741
- assertField(diag, "liveTrimReplays", ["number"]);
742
- });
743
-
744
- test("null diag validates", () => {
745
- const raw = JSON.stringify({
746
- updatedAt: "2025-01-01T00:00:00Z",
747
- windowMinutes: 30,
748
- sampleCount: 0,
749
- turn_latency_ms: { p50: 0, p95: 0, n: 0 },
750
- provider_latency_ms: { p50: 0, p95: 0, n: 0 },
751
- tps: { avg: 0, n: 0 },
752
- cache_hit_pct: { avg: 0, latest: 0, n: 0 },
753
- db_recompute_ms: { p50: 0, p95: 0, n: 0 },
754
- disk_write_ms: { p50: 0, p95: 0, n: 0 },
755
- rss_mb: { latest: 0, n: 0 },
756
- heap_mb: { latest: 0, n: 0 },
757
- cpu_user_ms: { latest: 0, n: 0 },
758
- cpu_sys_ms: { latest: 0, n: 0 },
759
- diag: null,
760
- });
761
- const obj: Record<string, unknown> = JSON.parse(raw);
762
-
763
- assertField(obj, "diag", ["null"]);
764
- });
695
+ test("sample payload validates field presence and types", () => {
696
+ const raw = JSON.stringify({
697
+ updatedAt: "2025-01-01T00:00:00Z",
698
+ windowMinutes: 30,
699
+ sampleCount: 100,
700
+ turn_latency_ms: { p50: 500, p95: 2000, n: 100 },
701
+ provider_latency_ms: { p50: 300, p95: 1500, n: 100 },
702
+ tps: { avg: 45.5, n: 100 },
703
+ cache_hit_pct: { avg: 30, latest: 25, n: 100 },
704
+ db_recompute_ms: { p50: 50, p95: 200, n: 50 },
705
+ disk_write_ms: { p50: 10, p95: 50, n: 50 },
706
+ rss_mb: { latest: 256, n: 10 },
707
+ heap_mb: { latest: 128, n: 10 },
708
+ cpu_user_ms: { latest: 5000, n: 10 },
709
+ cpu_sys_ms: { latest: 1000, n: 10 },
710
+ diag: {
711
+ ctxFastGate: 2,
712
+ liveTrimFires: 5,
713
+ liveTrimReplays: 3,
714
+ },
715
+ });
716
+ const obj: Record<string, unknown> = JSON.parse(raw);
717
+
718
+ assertField(obj, "updatedAt", ["string"]);
719
+ assertField(obj, "windowMinutes", ["number"]);
720
+ assertField(obj, "sampleCount", ["number"]);
721
+ assertField(obj, "turn_latency_ms", ["object"]);
722
+ assertField(obj, "provider_latency_ms", ["object"]);
723
+ assertField(obj, "tps", ["object"]);
724
+ assertField(obj, "cache_hit_pct", ["object"]);
725
+ assertField(obj, "db_recompute_ms", ["object"]);
726
+ assertField(obj, "disk_write_ms", ["object"]);
727
+ assertField(obj, "rss_mb", ["object"]);
728
+ assertField(obj, "heap_mb", ["object"]);
729
+ assertField(obj, "cpu_user_ms", ["object"]);
730
+ assertField(obj, "cpu_sys_ms", ["object"]);
731
+ assertField(obj, "diag", ["object", "null"]);
732
+
733
+ // Spot-check percentile sub-object
734
+ const tl = assertObject(obj, "turn_latency_ms")!;
735
+ assertField(tl, "p50", ["number"]);
736
+ assertField(tl, "p95", ["number"]);
737
+ assertField(tl, "n", ["number"]);
738
+
739
+ const diag = assertObject(obj, "diag")!;
740
+ assertField(diag, "ctxFastGate", ["number"]);
741
+ assertField(diag, "liveTrimFires", ["number"]);
742
+ assertField(diag, "liveTrimReplays", ["number"]);
743
+ });
744
+
745
+ test("null diag validates", () => {
746
+ const raw = JSON.stringify({
747
+ updatedAt: "2025-01-01T00:00:00Z",
748
+ windowMinutes: 30,
749
+ sampleCount: 0,
750
+ turn_latency_ms: { p50: 0, p95: 0, n: 0 },
751
+ provider_latency_ms: { p50: 0, p95: 0, n: 0 },
752
+ tps: { avg: 0, n: 0 },
753
+ cache_hit_pct: { avg: 0, latest: 0, n: 0 },
754
+ db_recompute_ms: { p50: 0, p95: 0, n: 0 },
755
+ disk_write_ms: { p50: 0, p95: 0, n: 0 },
756
+ rss_mb: { latest: 0, n: 0 },
757
+ heap_mb: { latest: 0, n: 0 },
758
+ cpu_user_ms: { latest: 0, n: 0 },
759
+ cpu_sys_ms: { latest: 0, n: 0 },
760
+ diag: null,
761
+ });
762
+ const obj: Record<string, unknown> = JSON.parse(raw);
763
+
764
+ assertField(obj, "diag", ["null"]);
765
+ });
765
766
  });
766
767
 
767
768
  describe("GET /api/achievements", () => {
768
- test("sample payload (array) validates element field presence and types", () => {
769
- const raw = JSON.stringify([
770
- {
771
- id: "first-compact",
772
- title: "First Compaction",
773
- description: "Complete your first compaction",
774
- hidden: 0,
775
- icon: "trophy",
776
- unlocked_at: 1700000000,
777
- },
778
- {
779
- id: "secret-ach",
780
- title: "Secret Achievement",
781
- description: "???",
782
- hidden: 1,
783
- icon: null,
784
- unlocked_at: null,
785
- },
786
- ]);
787
- const arr = JSON.parse(raw) as unknown[];
788
- assert.ok(Array.isArray(arr), "response must be an array");
789
-
790
- const first = arr[0] as Record<string, unknown>;
791
- assertField(first, "id", ["string"]);
792
- assertField(first, "title", ["string"]);
793
- assertField(first, "description", ["string"]);
794
- assertField(first, "hidden", ["number"]);
795
- assertField(first, "icon", ["string", "null"]);
796
- assertField(first, "unlocked_at", ["number", "null"]);
797
-
798
- const second = arr[1] as Record<string, unknown>;
799
- assertField(second, "icon", ["null"]);
800
- assertField(second, "unlocked_at", ["null"]);
801
- });
769
+ test("sample payload (array) validates element field presence and types", () => {
770
+ const raw = JSON.stringify([
771
+ {
772
+ id: "first-compact",
773
+ title: "First Compaction",
774
+ description: "Complete your first compaction",
775
+ hidden: 0,
776
+ icon: "trophy",
777
+ unlocked_at: 1700000000,
778
+ },
779
+ {
780
+ id: "secret-ach",
781
+ title: "Secret Achievement",
782
+ description: "???",
783
+ hidden: 1,
784
+ icon: null,
785
+ unlocked_at: null,
786
+ },
787
+ ]);
788
+ const arr = JSON.parse(raw) as unknown[];
789
+ assert.ok(Array.isArray(arr), "response must be an array");
790
+
791
+ const first = arr[0] as Record<string, unknown>;
792
+ assertField(first, "id", ["string"]);
793
+ assertField(first, "title", ["string"]);
794
+ assertField(first, "description", ["string"]);
795
+ assertField(first, "hidden", ["number"]);
796
+ assertField(first, "icon", ["string", "null"]);
797
+ assertField(first, "unlocked_at", ["number", "null"]);
798
+
799
+ const second = arr[1] as Record<string, unknown>;
800
+ assertField(second, "icon", ["null"]);
801
+ assertField(second, "unlocked_at", ["null"]);
802
+ });
802
803
  });
803
804
 
804
805
  // ─── SSE Event Union Validation ──────────────────────────────────────────────
@@ -806,258 +807,258 @@ describe("GET /api/achievements", () => {
806
807
  // matching its discriminator.
807
808
 
808
809
  describe("SSE /api/events — SseEvent union discriminator validation", () => {
809
- /** All 21 SseEvent variants with their discriminator `type` value and a minimal payload. */
810
- const SSE_VARIANTS: ReadonlyArray<{
811
- discriminator: string;
812
- payload: string;
813
- }> = [
814
- {
815
- discriminator: "compact_start",
816
- payload: JSON.stringify({
817
- type: "compact_start",
818
- ts: "2025-01-01T00:00:00Z",
819
- trigger: "auto",
820
- sessionId: "s1",
821
- }),
822
- },
823
- {
824
- discriminator: "compact_end",
825
- payload: JSON.stringify({
826
- type: "compact_end",
827
- ts: "2025-01-01T00:00:00Z",
828
- sessionId: "s1",
829
- checkpointId: "c1",
830
- tokensIn: 10000,
831
- tokensOut: 4000,
832
- tokensFreed: 6000,
833
- success: true,
834
- }),
835
- },
836
- {
837
- discriminator: "compact_trigger",
838
- payload: JSON.stringify({
839
- type: "compact_trigger",
840
- ts: "2025-01-01T00:00:00Z",
841
- pressure: 75,
842
- threshold: 80,
843
- armed: true,
844
- }),
845
- },
846
- {
847
- discriminator: "compact_skip",
848
- payload: JSON.stringify({
849
- type: "compact_skip",
850
- ts: "2025-01-01T00:00:00Z",
851
- reason: "pressure insufficient",
852
- }),
853
- },
854
- {
855
- discriminator: "tier_changed",
856
- payload: JSON.stringify({
857
- type: "tier_changed",
858
- ts: "2025-01-01T00:00:00Z",
859
- from: "super-compact",
860
- to: "ultra-compact",
861
- contextPct: 82,
862
- }),
863
- },
864
- {
865
- discriminator: "model_changed",
866
- payload: JSON.stringify({
867
- type: "model_changed",
868
- ts: "2025-01-01T00:00:00Z",
869
- provider: "anthropic",
870
- providerName: "Anthropic",
871
- model: "claude-3.5-sonnet",
872
- }),
873
- },
874
- {
875
- discriminator: "pressure_lifted",
876
- payload: JSON.stringify({
877
- type: "pressure_lifted",
878
- ts: "2025-01-01T00:00:00Z",
879
- beforePct: 85,
880
- afterPct: 40,
881
- }),
882
- },
883
- {
884
- discriminator: "checkpoint_persisted",
885
- payload: JSON.stringify({
886
- type: "checkpoint_persisted",
887
- ts: "2025-01-01T00:00:00Z",
888
- checkpointId: "ckpt-1",
889
- sessionTokens: 8000,
890
- }),
891
- },
892
- {
893
- discriminator: "recall_inject",
894
- payload: JSON.stringify({
895
- type: "recall_inject",
896
- ts: "2025-01-01T00:00:00Z",
897
- query: "compaction history",
898
- chunks: 5,
899
- tokens: 2000,
900
- }),
901
- },
902
- {
903
- discriminator: "anchors_updated",
904
- payload: JSON.stringify({
905
- type: "anchors_updated",
906
- ts: "2025-01-01T00:00:00Z",
907
- count: 10,
908
- pinned: 3,
909
- }),
910
- },
911
- {
912
- discriminator: "config_updated",
913
- payload: JSON.stringify({
914
- type: "config_updated",
915
- ts: "2025-01-01T00:00:00Z",
916
- key: "thresholdTokens",
917
- value: 120000,
918
- }),
919
- },
920
- {
921
- discriminator: "config_preset",
922
- payload: JSON.stringify({
923
- type: "config_preset",
924
- ts: "2025-01-01T00:00:00Z",
925
- preset: "aggressive",
926
- }),
927
- },
928
- {
929
- discriminator: "crew_presence_changed",
930
- payload: JSON.stringify({
931
- type: "crew_presence_changed",
932
- ts: "2025-01-01T00:00:00Z",
933
- activeAgents: 3,
934
- currentTurn: 1,
935
- }),
936
- },
937
- {
938
- discriminator: "crew_turn_changed",
939
- payload: JSON.stringify({
940
- type: "crew_turn_changed",
941
- ts: "2025-01-01T00:00:00Z",
942
- turnIndex: 2,
943
- agentName: "worker-2",
944
- }),
945
- },
946
- {
947
- discriminator: "crew_bandit_chosen",
948
- payload: JSON.stringify({
949
- type: "crew_bandit_chosen",
950
- ts: "2025-01-01T00:00:00Z",
951
- chosenAgent: "worker-1",
952
- score: 0.85,
953
- regret: 0.12,
954
- }),
955
- },
956
- {
957
- discriminator: "game_ritual_start",
958
- payload: JSON.stringify({
959
- type: "game_ritual_start",
960
- ts: "2025-01-01T00:00:00Z",
961
- sessionId: "ritual-1",
962
- stages: [
963
- { index: 0, name: "warmup", durationMs: 60000, status: "pending" },
964
- ],
965
- }),
966
- },
967
- {
968
- discriminator: "game_ritual_stage",
969
- payload: JSON.stringify({
970
- type: "game_ritual_stage",
971
- ts: "2025-01-01T00:00:00Z",
972
- sessionId: "ritual-1",
973
- stageIndex: 1,
974
- stageName: "deep-work",
975
- }),
976
- },
977
- {
978
- discriminator: "game_ritual_end",
979
- payload: JSON.stringify({
980
- type: "game_ritual_end",
981
- ts: "2025-01-01T00:00:00Z",
982
- sessionId: "ritual-1",
983
- success: true,
984
- }),
985
- },
986
- {
987
- discriminator: "game_mode_changed",
988
- payload: JSON.stringify({
989
- type: "game_mode_changed",
990
- ts: "2025-01-01T00:00:00Z",
991
- config: { enabled: true, theme: "neon", displayMode: "full" },
992
- }),
993
- },
994
- {
995
- discriminator: "game_render",
996
- payload: JSON.stringify({
997
- type: "game_render",
998
- ts: "2025-01-01T00:00:00Z",
999
- frame: "\x1b[2J\x1b[H",
1000
- }),
1001
- },
1002
- {
1003
- discriminator: "session_sample",
1004
- payload: JSON.stringify({
1005
- type: "session_sample",
1006
- ts: "2025-01-01T00:00:00Z",
1007
- sessionId: "sess-abc",
1008
- tokens: 42_000,
1009
- percent: 35.5,
1010
- }),
1011
- },
1012
- ];
1013
-
1014
- test("all 21 SseEvent variants have a type field matching their discriminator", () => {
1015
- assert.equal(
1016
- SSE_VARIANTS.length,
1017
- 21,
1018
- "SseEvent union should have 21 variants",
1019
- );
1020
-
1021
- for (const variant of SSE_VARIANTS) {
1022
- const obj: Record<string, unknown> = JSON.parse(variant.payload);
1023
- assert.ok(
1024
- "type" in obj,
1025
- `SSE variant "${variant.discriminator}" must have a "type" field`,
1026
- );
1027
- assert.equal(
1028
- typeof obj["type"],
1029
- "string",
1030
- `SSE variant "${variant.discriminator}" type field must be a string`,
1031
- );
1032
- assert.equal(
1033
- obj["type"],
1034
- variant.discriminator,
1035
- `SSE variant type field must match discriminator "${variant.discriminator}"`,
1036
- );
1037
- }
1038
- });
1039
-
1040
- test("every SSE variant has a ts field of type string", () => {
1041
- for (const variant of SSE_VARIANTS) {
1042
- const obj: Record<string, unknown> = JSON.parse(variant.payload);
1043
- assertField(obj, "ts", ["string"]);
1044
- }
1045
- });
1046
-
1047
- test("SSE ENDPOINTS entry is correctly configured as SSE type", () => {
1048
- assert.equal(ENDPOINTS.events.type, "sse");
1049
- assert.equal(ENDPOINTS.events.method, "GET");
1050
- assert.equal(ENDPOINTS.events.path, "/api/events");
1051
- assert.ok(ENDPOINTS.events.event, "SSE endpoint must have an event name");
1052
- });
1053
-
1054
- test("SSE discriminators are unique across all variants", () => {
1055
- const discriminators = SSE_VARIANTS.map((v) => v.discriminator);
1056
- const unique = new Set(discriminators);
1057
- assert.equal(
1058
- unique.size,
1059
- discriminators.length,
1060
- "SSE discriminators must be unique",
1061
- );
1062
- });
810
+ /** All 21 SseEvent variants with their discriminator `type` value and a minimal payload. */
811
+ const SSE_VARIANTS: ReadonlyArray<{
812
+ discriminator: string;
813
+ payload: string;
814
+ }> = [
815
+ {
816
+ discriminator: "compact_start",
817
+ payload: JSON.stringify({
818
+ type: "compact_start",
819
+ ts: "2025-01-01T00:00:00Z",
820
+ trigger: "auto",
821
+ sessionId: "s1",
822
+ }),
823
+ },
824
+ {
825
+ discriminator: "compact_end",
826
+ payload: JSON.stringify({
827
+ type: "compact_end",
828
+ ts: "2025-01-01T00:00:00Z",
829
+ sessionId: "s1",
830
+ checkpointId: "c1",
831
+ tokensIn: 10000,
832
+ tokensOut: 4000,
833
+ tokensFreed: 6000,
834
+ success: true,
835
+ }),
836
+ },
837
+ {
838
+ discriminator: "compact_trigger",
839
+ payload: JSON.stringify({
840
+ type: "compact_trigger",
841
+ ts: "2025-01-01T00:00:00Z",
842
+ pressure: 75,
843
+ threshold: 80,
844
+ armed: true,
845
+ }),
846
+ },
847
+ {
848
+ discriminator: "compact_skip",
849
+ payload: JSON.stringify({
850
+ type: "compact_skip",
851
+ ts: "2025-01-01T00:00:00Z",
852
+ reason: "pressure insufficient",
853
+ }),
854
+ },
855
+ {
856
+ discriminator: "tier_changed",
857
+ payload: JSON.stringify({
858
+ type: "tier_changed",
859
+ ts: "2025-01-01T00:00:00Z",
860
+ from: "super-compact",
861
+ to: "ultra-compact",
862
+ contextPct: 82,
863
+ }),
864
+ },
865
+ {
866
+ discriminator: "model_changed",
867
+ payload: JSON.stringify({
868
+ type: "model_changed",
869
+ ts: "2025-01-01T00:00:00Z",
870
+ provider: "anthropic",
871
+ providerName: "Anthropic",
872
+ model: "claude-3.5-sonnet",
873
+ }),
874
+ },
875
+ {
876
+ discriminator: "pressure_lifted",
877
+ payload: JSON.stringify({
878
+ type: "pressure_lifted",
879
+ ts: "2025-01-01T00:00:00Z",
880
+ beforePct: 85,
881
+ afterPct: 40,
882
+ }),
883
+ },
884
+ {
885
+ discriminator: "checkpoint_persisted",
886
+ payload: JSON.stringify({
887
+ type: "checkpoint_persisted",
888
+ ts: "2025-01-01T00:00:00Z",
889
+ checkpointId: "ckpt-1",
890
+ sessionTokens: 8000,
891
+ }),
892
+ },
893
+ {
894
+ discriminator: "recall_inject",
895
+ payload: JSON.stringify({
896
+ type: "recall_inject",
897
+ ts: "2025-01-01T00:00:00Z",
898
+ query: "compaction history",
899
+ chunks: 5,
900
+ tokens: 2000,
901
+ }),
902
+ },
903
+ {
904
+ discriminator: "anchors_updated",
905
+ payload: JSON.stringify({
906
+ type: "anchors_updated",
907
+ ts: "2025-01-01T00:00:00Z",
908
+ count: 10,
909
+ pinned: 3,
910
+ }),
911
+ },
912
+ {
913
+ discriminator: "config_updated",
914
+ payload: JSON.stringify({
915
+ type: "config_updated",
916
+ ts: "2025-01-01T00:00:00Z",
917
+ key: "thresholdTokens",
918
+ value: 120000,
919
+ }),
920
+ },
921
+ {
922
+ discriminator: "config_preset",
923
+ payload: JSON.stringify({
924
+ type: "config_preset",
925
+ ts: "2025-01-01T00:00:00Z",
926
+ preset: "aggressive",
927
+ }),
928
+ },
929
+ {
930
+ discriminator: "crew_presence_changed",
931
+ payload: JSON.stringify({
932
+ type: "crew_presence_changed",
933
+ ts: "2025-01-01T00:00:00Z",
934
+ activeAgents: 3,
935
+ currentTurn: 1,
936
+ }),
937
+ },
938
+ {
939
+ discriminator: "crew_turn_changed",
940
+ payload: JSON.stringify({
941
+ type: "crew_turn_changed",
942
+ ts: "2025-01-01T00:00:00Z",
943
+ turnIndex: 2,
944
+ agentName: "worker-2",
945
+ }),
946
+ },
947
+ {
948
+ discriminator: "crew_bandit_chosen",
949
+ payload: JSON.stringify({
950
+ type: "crew_bandit_chosen",
951
+ ts: "2025-01-01T00:00:00Z",
952
+ chosenAgent: "worker-1",
953
+ score: 0.85,
954
+ regret: 0.12,
955
+ }),
956
+ },
957
+ {
958
+ discriminator: "game_ritual_start",
959
+ payload: JSON.stringify({
960
+ type: "game_ritual_start",
961
+ ts: "2025-01-01T00:00:00Z",
962
+ sessionId: "ritual-1",
963
+ stages: [
964
+ { index: 0, name: "warmup", durationMs: 60000, status: "pending" },
965
+ ],
966
+ }),
967
+ },
968
+ {
969
+ discriminator: "game_ritual_stage",
970
+ payload: JSON.stringify({
971
+ type: "game_ritual_stage",
972
+ ts: "2025-01-01T00:00:00Z",
973
+ sessionId: "ritual-1",
974
+ stageIndex: 1,
975
+ stageName: "deep-work",
976
+ }),
977
+ },
978
+ {
979
+ discriminator: "game_ritual_end",
980
+ payload: JSON.stringify({
981
+ type: "game_ritual_end",
982
+ ts: "2025-01-01T00:00:00Z",
983
+ sessionId: "ritual-1",
984
+ success: true,
985
+ }),
986
+ },
987
+ {
988
+ discriminator: "game_mode_changed",
989
+ payload: JSON.stringify({
990
+ type: "game_mode_changed",
991
+ ts: "2025-01-01T00:00:00Z",
992
+ config: { enabled: true, theme: "neon", displayMode: "full" },
993
+ }),
994
+ },
995
+ {
996
+ discriminator: "game_render",
997
+ payload: JSON.stringify({
998
+ type: "game_render",
999
+ ts: "2025-01-01T00:00:00Z",
1000
+ frame: "\x1b[2J\x1b[H",
1001
+ }),
1002
+ },
1003
+ {
1004
+ discriminator: "session_sample",
1005
+ payload: JSON.stringify({
1006
+ type: "session_sample",
1007
+ ts: "2025-01-01T00:00:00Z",
1008
+ sessionId: "sess-abc",
1009
+ tokens: 42_000,
1010
+ percent: 35.5,
1011
+ }),
1012
+ },
1013
+ ];
1014
+
1015
+ test("all 21 SseEvent variants have a type field matching their discriminator", () => {
1016
+ assert.equal(
1017
+ SSE_VARIANTS.length,
1018
+ 21,
1019
+ "SseEvent union should have 21 variants",
1020
+ );
1021
+
1022
+ for (const variant of SSE_VARIANTS) {
1023
+ const obj: Record<string, unknown> = JSON.parse(variant.payload);
1024
+ assert.ok(
1025
+ "type" in obj,
1026
+ `SSE variant "${variant.discriminator}" must have a "type" field`,
1027
+ );
1028
+ assert.equal(
1029
+ typeof obj["type"],
1030
+ "string",
1031
+ `SSE variant "${variant.discriminator}" type field must be a string`,
1032
+ );
1033
+ assert.equal(
1034
+ obj["type"],
1035
+ variant.discriminator,
1036
+ `SSE variant type field must match discriminator "${variant.discriminator}"`,
1037
+ );
1038
+ }
1039
+ });
1040
+
1041
+ test("every SSE variant has a ts field of type string", () => {
1042
+ for (const variant of SSE_VARIANTS) {
1043
+ const obj: Record<string, unknown> = JSON.parse(variant.payload);
1044
+ assertField(obj, "ts", ["string"]);
1045
+ }
1046
+ });
1047
+
1048
+ test("SSE ENDPOINTS entry is correctly configured as SSE type", () => {
1049
+ assert.equal(ENDPOINTS.events.type, "sse");
1050
+ assert.equal(ENDPOINTS.events.method, "GET");
1051
+ assert.equal(ENDPOINTS.events.path, "/api/events");
1052
+ assert.ok(ENDPOINTS.events.event, "SSE endpoint must have an event name");
1053
+ });
1054
+
1055
+ test("SSE discriminators are unique across all variants", () => {
1056
+ const discriminators = SSE_VARIANTS.map((v) => v.discriminator);
1057
+ const unique = new Set(discriminators);
1058
+ assert.equal(
1059
+ unique.size,
1060
+ discriminators.length,
1061
+ "SSE discriminators must be unique",
1062
+ );
1063
+ });
1063
1064
  });