@opensip-cli/session-store 0.1.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.
@@ -0,0 +1,419 @@
1
+ /**
2
+ * Generic tool-run session record. Holds only columns every tool shares;
3
+ * per-session detail lives in {@link sessionToolPayload}. The persistence
4
+ * layer holds ZERO tool-specific vocabulary. (Audit 2026-05-29.)
5
+ */
6
+ export declare const sessions: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
7
+ name: "sessions";
8
+ schema: undefined;
9
+ columns: {
10
+ id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
11
+ name: "id";
12
+ tableName: "sessions";
13
+ dataType: "string";
14
+ columnType: "SQLiteText";
15
+ data: string;
16
+ driverParam: string;
17
+ notNull: true;
18
+ hasDefault: false;
19
+ isPrimaryKey: true;
20
+ isAutoincrement: false;
21
+ hasRuntimeDefault: false;
22
+ enumValues: [string, ...string[]];
23
+ baseColumn: never;
24
+ identity: undefined;
25
+ generated: undefined;
26
+ }, {}, {
27
+ length: number | undefined;
28
+ }>;
29
+ tool: import("drizzle-orm/sqlite-core").SQLiteColumn<{
30
+ name: "tool";
31
+ tableName: "sessions";
32
+ dataType: "string";
33
+ columnType: "SQLiteText";
34
+ data: string;
35
+ driverParam: string;
36
+ notNull: true;
37
+ hasDefault: false;
38
+ isPrimaryKey: false;
39
+ isAutoincrement: false;
40
+ hasRuntimeDefault: false;
41
+ enumValues: [string, ...string[]];
42
+ baseColumn: never;
43
+ identity: undefined;
44
+ generated: undefined;
45
+ }, {}, {
46
+ length: number | undefined;
47
+ }>;
48
+ timestamp: import("drizzle-orm/sqlite-core").SQLiteColumn<{
49
+ name: "timestamp";
50
+ tableName: "sessions";
51
+ dataType: "number";
52
+ columnType: "SQLiteInteger";
53
+ data: number;
54
+ driverParam: number;
55
+ notNull: true;
56
+ hasDefault: false;
57
+ isPrimaryKey: false;
58
+ isAutoincrement: false;
59
+ hasRuntimeDefault: false;
60
+ enumValues: undefined;
61
+ baseColumn: never;
62
+ identity: undefined;
63
+ generated: undefined;
64
+ }, {}, {}>;
65
+ timestamp_iso: import("drizzle-orm/sqlite-core").SQLiteColumn<{
66
+ name: "timestamp_iso";
67
+ tableName: "sessions";
68
+ dataType: "string";
69
+ columnType: "SQLiteText";
70
+ data: string;
71
+ driverParam: string;
72
+ notNull: false;
73
+ hasDefault: false;
74
+ isPrimaryKey: false;
75
+ isAutoincrement: false;
76
+ hasRuntimeDefault: false;
77
+ enumValues: [string, ...string[]];
78
+ baseColumn: never;
79
+ identity: undefined;
80
+ generated: undefined;
81
+ }, {}, {
82
+ length: number | undefined;
83
+ }>;
84
+ completed_at: import("drizzle-orm/sqlite-core").SQLiteColumn<{
85
+ name: "completed_at";
86
+ tableName: "sessions";
87
+ dataType: "number";
88
+ columnType: "SQLiteInteger";
89
+ data: number;
90
+ driverParam: number;
91
+ notNull: false;
92
+ hasDefault: false;
93
+ isPrimaryKey: false;
94
+ isAutoincrement: false;
95
+ hasRuntimeDefault: false;
96
+ enumValues: undefined;
97
+ baseColumn: never;
98
+ identity: undefined;
99
+ generated: undefined;
100
+ }, {}, {}>;
101
+ completed_at_iso: import("drizzle-orm/sqlite-core").SQLiteColumn<{
102
+ name: "completed_at_iso";
103
+ tableName: "sessions";
104
+ dataType: "string";
105
+ columnType: "SQLiteText";
106
+ data: string;
107
+ driverParam: string;
108
+ notNull: false;
109
+ hasDefault: false;
110
+ isPrimaryKey: false;
111
+ isAutoincrement: false;
112
+ hasRuntimeDefault: false;
113
+ enumValues: [string, ...string[]];
114
+ baseColumn: never;
115
+ identity: undefined;
116
+ generated: undefined;
117
+ }, {}, {
118
+ length: number | undefined;
119
+ }>;
120
+ cwd: import("drizzle-orm/sqlite-core").SQLiteColumn<{
121
+ name: "cwd";
122
+ tableName: "sessions";
123
+ dataType: "string";
124
+ columnType: "SQLiteText";
125
+ data: string;
126
+ driverParam: string;
127
+ notNull: true;
128
+ hasDefault: false;
129
+ isPrimaryKey: false;
130
+ isAutoincrement: false;
131
+ hasRuntimeDefault: false;
132
+ enumValues: [string, ...string[]];
133
+ baseColumn: never;
134
+ identity: undefined;
135
+ generated: undefined;
136
+ }, {}, {
137
+ length: number | undefined;
138
+ }>;
139
+ recipe: import("drizzle-orm/sqlite-core").SQLiteColumn<{
140
+ name: "recipe";
141
+ tableName: "sessions";
142
+ dataType: "string";
143
+ columnType: "SQLiteText";
144
+ data: string;
145
+ driverParam: string;
146
+ notNull: false;
147
+ hasDefault: false;
148
+ isPrimaryKey: false;
149
+ isAutoincrement: false;
150
+ hasRuntimeDefault: false;
151
+ enumValues: [string, ...string[]];
152
+ baseColumn: never;
153
+ identity: undefined;
154
+ generated: undefined;
155
+ }, {}, {
156
+ length: number | undefined;
157
+ }>;
158
+ score: import("drizzle-orm/sqlite-core").SQLiteColumn<{
159
+ name: "score";
160
+ tableName: "sessions";
161
+ dataType: "number";
162
+ columnType: "SQLiteInteger";
163
+ data: number;
164
+ driverParam: number;
165
+ notNull: true;
166
+ hasDefault: false;
167
+ isPrimaryKey: false;
168
+ isAutoincrement: false;
169
+ hasRuntimeDefault: false;
170
+ enumValues: undefined;
171
+ baseColumn: never;
172
+ identity: undefined;
173
+ generated: undefined;
174
+ }, {}, {}>;
175
+ passed: import("drizzle-orm/sqlite-core").SQLiteColumn<{
176
+ name: "passed";
177
+ tableName: "sessions";
178
+ dataType: "boolean";
179
+ columnType: "SQLiteBoolean";
180
+ data: boolean;
181
+ driverParam: number;
182
+ notNull: true;
183
+ hasDefault: false;
184
+ isPrimaryKey: false;
185
+ isAutoincrement: false;
186
+ hasRuntimeDefault: false;
187
+ enumValues: undefined;
188
+ baseColumn: never;
189
+ identity: undefined;
190
+ generated: undefined;
191
+ }, {}, {}>;
192
+ durationMs: import("drizzle-orm/sqlite-core").SQLiteColumn<{
193
+ name: "duration_ms";
194
+ tableName: "sessions";
195
+ dataType: "number";
196
+ columnType: "SQLiteInteger";
197
+ data: number;
198
+ driverParam: number;
199
+ notNull: true;
200
+ hasDefault: false;
201
+ isPrimaryKey: false;
202
+ isAutoincrement: false;
203
+ hasRuntimeDefault: false;
204
+ enumValues: undefined;
205
+ baseColumn: never;
206
+ identity: undefined;
207
+ generated: undefined;
208
+ }, {}, {}>;
209
+ };
210
+ dialect: "sqlite";
211
+ }>;
212
+ /**
213
+ * Sibling host-metrics record (host-owned-run-timing §5.3/§5.4). One row per
214
+ * session id, holding host-side overhead that is known at different times:
215
+ * `persistMs` after the session write, `egressMs` after post-run delivery,
216
+ * etc. Kept separate from the `sessions` row so late-arriving metrics are a
217
+ * best-effort upsert rather than a row rewrite. Hydrated back onto
218
+ * `StoredSession.hostMetrics`. All metrics are nullable.
219
+ */
220
+ export declare const sessionHostMetrics: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
221
+ name: "session_host_metrics";
222
+ schema: undefined;
223
+ columns: {
224
+ sessionId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
225
+ name: "session_id";
226
+ tableName: "session_host_metrics";
227
+ dataType: "string";
228
+ columnType: "SQLiteText";
229
+ data: string;
230
+ driverParam: string;
231
+ notNull: true;
232
+ hasDefault: false;
233
+ isPrimaryKey: true;
234
+ isAutoincrement: false;
235
+ hasRuntimeDefault: false;
236
+ enumValues: [string, ...string[]];
237
+ baseColumn: never;
238
+ identity: undefined;
239
+ generated: undefined;
240
+ }, {}, {
241
+ length: number | undefined;
242
+ }>;
243
+ ttyBusyMs: import("drizzle-orm/sqlite-core").SQLiteColumn<{
244
+ name: "tty_busy_ms";
245
+ tableName: "session_host_metrics";
246
+ dataType: "number";
247
+ columnType: "SQLiteInteger";
248
+ data: number;
249
+ driverParam: number;
250
+ notNull: false;
251
+ hasDefault: false;
252
+ isPrimaryKey: false;
253
+ isAutoincrement: false;
254
+ hasRuntimeDefault: false;
255
+ enumValues: undefined;
256
+ baseColumn: never;
257
+ identity: undefined;
258
+ generated: undefined;
259
+ }, {}, {}>;
260
+ renderMs: import("drizzle-orm/sqlite-core").SQLiteColumn<{
261
+ name: "render_ms";
262
+ tableName: "session_host_metrics";
263
+ dataType: "number";
264
+ columnType: "SQLiteInteger";
265
+ data: number;
266
+ driverParam: number;
267
+ notNull: false;
268
+ hasDefault: false;
269
+ isPrimaryKey: false;
270
+ isAutoincrement: false;
271
+ hasRuntimeDefault: false;
272
+ enumValues: undefined;
273
+ baseColumn: never;
274
+ identity: undefined;
275
+ generated: undefined;
276
+ }, {}, {}>;
277
+ persistMs: import("drizzle-orm/sqlite-core").SQLiteColumn<{
278
+ name: "persist_ms";
279
+ tableName: "session_host_metrics";
280
+ dataType: "number";
281
+ columnType: "SQLiteInteger";
282
+ data: number;
283
+ driverParam: number;
284
+ notNull: false;
285
+ hasDefault: false;
286
+ isPrimaryKey: false;
287
+ isAutoincrement: false;
288
+ hasRuntimeDefault: false;
289
+ enumValues: undefined;
290
+ baseColumn: never;
291
+ identity: undefined;
292
+ generated: undefined;
293
+ }, {}, {}>;
294
+ egressMs: import("drizzle-orm/sqlite-core").SQLiteColumn<{
295
+ name: "egress_ms";
296
+ tableName: "session_host_metrics";
297
+ dataType: "number";
298
+ columnType: "SQLiteInteger";
299
+ data: number;
300
+ driverParam: number;
301
+ notNull: false;
302
+ hasDefault: false;
303
+ isPrimaryKey: false;
304
+ isAutoincrement: false;
305
+ hasRuntimeDefault: false;
306
+ enumValues: undefined;
307
+ baseColumn: never;
308
+ identity: undefined;
309
+ generated: undefined;
310
+ }, {}, {}>;
311
+ totalCommandMs: import("drizzle-orm/sqlite-core").SQLiteColumn<{
312
+ name: "total_command_ms";
313
+ tableName: "session_host_metrics";
314
+ dataType: "number";
315
+ columnType: "SQLiteInteger";
316
+ data: number;
317
+ driverParam: number;
318
+ notNull: false;
319
+ hasDefault: false;
320
+ isPrimaryKey: false;
321
+ isAutoincrement: false;
322
+ hasRuntimeDefault: false;
323
+ enumValues: undefined;
324
+ baseColumn: never;
325
+ identity: undefined;
326
+ generated: undefined;
327
+ }, {}, {}>;
328
+ };
329
+ dialect: "sqlite";
330
+ }>;
331
+ /**
332
+ * Tool-owned opaque per-session detail (audit 2026-05-29, session split).
333
+ *
334
+ * One row per session. `payload` is a JSON blob whose shape is owned and
335
+ * validated by the writing tool — `contracts` treats it as opaque and
336
+ * holds zero tool-specific (check/finding/summary) vocabulary. The
337
+ * dashboard, as the presentation owner, reads this payload and renders
338
+ * it — the same producer/consumer split used for `GraphCatalog`.
339
+ */
340
+ export declare const sessionToolPayload: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
341
+ name: "session_tool_payload";
342
+ schema: undefined;
343
+ columns: {
344
+ sessionId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
345
+ name: "session_id";
346
+ tableName: "session_tool_payload";
347
+ dataType: "string";
348
+ columnType: "SQLiteText";
349
+ data: string;
350
+ driverParam: string;
351
+ notNull: true;
352
+ hasDefault: false;
353
+ isPrimaryKey: true;
354
+ isAutoincrement: false;
355
+ hasRuntimeDefault: false;
356
+ enumValues: [string, ...string[]];
357
+ baseColumn: never;
358
+ identity: undefined;
359
+ generated: undefined;
360
+ }, {}, {
361
+ length: number | undefined;
362
+ }>;
363
+ tool: import("drizzle-orm/sqlite-core").SQLiteColumn<{
364
+ name: "tool";
365
+ tableName: "session_tool_payload";
366
+ dataType: "string";
367
+ columnType: "SQLiteText";
368
+ data: string;
369
+ driverParam: string;
370
+ notNull: true;
371
+ hasDefault: false;
372
+ isPrimaryKey: false;
373
+ isAutoincrement: false;
374
+ hasRuntimeDefault: false;
375
+ enumValues: [string, ...string[]];
376
+ baseColumn: never;
377
+ identity: undefined;
378
+ generated: undefined;
379
+ }, {}, {
380
+ length: number | undefined;
381
+ }>;
382
+ payload: import("drizzle-orm/sqlite-core").SQLiteColumn<{
383
+ name: "payload";
384
+ tableName: "session_tool_payload";
385
+ dataType: "json";
386
+ columnType: "SQLiteTextJson";
387
+ data: unknown;
388
+ driverParam: string;
389
+ notNull: true;
390
+ hasDefault: false;
391
+ isPrimaryKey: false;
392
+ isAutoincrement: false;
393
+ hasRuntimeDefault: false;
394
+ enumValues: undefined;
395
+ baseColumn: never;
396
+ identity: undefined;
397
+ generated: undefined;
398
+ }, {}, {}>;
399
+ payload_version: import("drizzle-orm/sqlite-core").SQLiteColumn<{
400
+ name: "payload_version";
401
+ tableName: "session_tool_payload";
402
+ dataType: "number";
403
+ columnType: "SQLiteInteger";
404
+ data: number;
405
+ driverParam: number;
406
+ notNull: true;
407
+ hasDefault: true;
408
+ isPrimaryKey: false;
409
+ isAutoincrement: false;
410
+ hasRuntimeDefault: false;
411
+ enumValues: undefined;
412
+ baseColumn: never;
413
+ identity: undefined;
414
+ generated: undefined;
415
+ }, {}, {}>;
416
+ };
417
+ dialect: "sqlite";
418
+ }>;
419
+ //# sourceMappingURL=sessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/schema/sessions.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsBpB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAS7B,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAO7B,CAAC"}
@@ -0,0 +1,62 @@
1
+ import { sql } from 'drizzle-orm';
2
+ import { index, integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';
3
+ /**
4
+ * Generic tool-run session record. Holds only columns every tool shares;
5
+ * per-session detail lives in {@link sessionToolPayload}. The persistence
6
+ * layer holds ZERO tool-specific vocabulary. (Audit 2026-05-29.)
7
+ */
8
+ export const sessions = sqliteTable('sessions', {
9
+ id: text('id').primaryKey(),
10
+ tool: text('tool').notNull(),
11
+ // `timestamp`/`timestamp_iso` are the run START (host-owned-run-timing
12
+ // `startedAt`). The physical column names are kept as an internal migration
13
+ // detail; the exported contract field is `startedAt` (mapped in SessionRepo).
14
+ timestamp: integer('timestamp').notNull(), // startedAt ms epoch for ordering/index; see timestamp_iso for fidelity
15
+ timestamp_iso: text('timestamp_iso'), // startedAt original ISO string from host for replay fidelity
16
+ // Run COMPLETION (host-owned-run-timing `completedAt`). Nullable for
17
+ // legacy/pre-migration rows; the repo synthesizes startedAt + durationMs on
18
+ // read when absent. New writes always populate it.
19
+ completed_at: integer('completed_at'), // completedAt ms epoch
20
+ completed_at_iso: text('completed_at_iso'), // completedAt original ISO string
21
+ cwd: text('cwd').notNull(),
22
+ recipe: text('recipe'),
23
+ score: integer('score').notNull(),
24
+ passed: integer('passed', { mode: 'boolean' }).notNull(),
25
+ durationMs: integer('duration_ms').notNull(),
26
+ }, (table) => [index('sessions_tool_timestamp_idx').on(table.tool, sql `${table.timestamp} DESC`)]);
27
+ /**
28
+ * Sibling host-metrics record (host-owned-run-timing §5.3/§5.4). One row per
29
+ * session id, holding host-side overhead that is known at different times:
30
+ * `persistMs` after the session write, `egressMs` after post-run delivery,
31
+ * etc. Kept separate from the `sessions` row so late-arriving metrics are a
32
+ * best-effort upsert rather than a row rewrite. Hydrated back onto
33
+ * `StoredSession.hostMetrics`. All metrics are nullable.
34
+ */
35
+ export const sessionHostMetrics = sqliteTable('session_host_metrics', {
36
+ sessionId: text('session_id')
37
+ .primaryKey()
38
+ .references(() => sessions.id, { onDelete: 'cascade' }),
39
+ ttyBusyMs: integer('tty_busy_ms'),
40
+ renderMs: integer('render_ms'),
41
+ persistMs: integer('persist_ms'),
42
+ egressMs: integer('egress_ms'),
43
+ totalCommandMs: integer('total_command_ms'),
44
+ });
45
+ /**
46
+ * Tool-owned opaque per-session detail (audit 2026-05-29, session split).
47
+ *
48
+ * One row per session. `payload` is a JSON blob whose shape is owned and
49
+ * validated by the writing tool — `contracts` treats it as opaque and
50
+ * holds zero tool-specific (check/finding/summary) vocabulary. The
51
+ * dashboard, as the presentation owner, reads this payload and renders
52
+ * it — the same producer/consumer split used for `GraphCatalog`.
53
+ */
54
+ export const sessionToolPayload = sqliteTable('session_tool_payload', {
55
+ sessionId: text('session_id')
56
+ .primaryKey()
57
+ .references(() => sessions.id, { onDelete: 'cascade' }),
58
+ tool: text('tool').notNull(),
59
+ payload: text('payload', { mode: 'json' }).notNull(),
60
+ payload_version: integer('payload_version').notNull().default(1), // tool-owned payload schema version; future versions may require CLI upgrade to interpret
61
+ });
62
+ //# sourceMappingURL=sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../src/schema/sessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAE5E;;;;GAIG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,WAAW,CACjC,UAAU,EACV;IACE,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE;IAC3B,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE;IAC5B,uEAAuE;IACvE,4EAA4E;IAC5E,8EAA8E;IAC9E,SAAS,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,EAAE,wEAAwE;IACnH,aAAa,EAAE,IAAI,CAAC,eAAe,CAAC,EAAE,8DAA8D;IACpG,qEAAqE;IACrE,4EAA4E;IAC5E,mDAAmD;IACnD,YAAY,EAAE,OAAO,CAAC,cAAc,CAAC,EAAE,uBAAuB;IAC9D,gBAAgB,EAAE,IAAI,CAAC,kBAAkB,CAAC,EAAE,kCAAkC;IAC9E,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE;IAC1B,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE;IACjC,MAAM,EAAE,OAAO,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE;IACxD,UAAU,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE;CAC7C,EACD,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAA,GAAG,KAAK,CAAC,SAAS,OAAO,CAAC,CAAC,CAC/F,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAW,CAAC,sBAAsB,EAAE;IACpE,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC;SAC1B,UAAU,EAAE;SACZ,UAAU,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACzD,SAAS,EAAE,OAAO,CAAC,aAAa,CAAC;IACjC,QAAQ,EAAE,OAAO,CAAC,WAAW,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC,YAAY,CAAC;IAChC,QAAQ,EAAE,OAAO,CAAC,WAAW,CAAC;IAC9B,cAAc,EAAE,OAAO,CAAC,kBAAkB,CAAC;CAC5C,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAW,CAAC,sBAAsB,EAAE;IACpE,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC;SAC1B,UAAU,EAAE;SACZ,UAAU,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACzD,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE;IAC5B,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE;IACpD,eAAe,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,0FAA0F;CAC7J,CAAC,CAAC"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * session-payload-decode — the shared, generic decoder for a stored session's
3
+ * opaque `payload` blob (the inverse of each tool's `build*SessionPayload`).
4
+ *
5
+ * Session replay needs every tool (`fit`/`graph`/`sim`)
6
+ * to read a persisted session back into a {@link SignalEnvelope} projection. The
7
+ * persisted detail shares ONE structural shape — `{ summary, checks[] }`, each
8
+ * check a `{ checkSlug, passed, violationCount?, durationMs, findings[] }` — so
9
+ * the decode of THAT structure lives here once, parameterised by the few
10
+ * per-tool differences (error-label, whether `filePath`/`violationCount` are
11
+ * required, whether findings carry a metadata bag).
12
+ *
13
+ * This holds NO tool vocabulary: it does not know about fit checks, graph rules,
14
+ * severity→category mapping, or signal IDs. Those projections stay in each
15
+ * engine's `session-replay.ts` (`replaySignal`). What lives here is purely the
16
+ * structural shape session-store persists — a faithful counterpart to the
17
+ * package's opaque-payload charter: session-store owns persistence AND the
18
+ * structural decode for replay, while tool semantics remain in the engines.
19
+ *
20
+ * The decoder tolerates legacy payloads that lack a top-level `__version`
21
+ * (treated as v1 with best-effort projection). Tools should prefer their own
22
+ * `*ReplayFromSession` functions (which call this decoder) for full projection
23
+ * to their live result types; this module only gives the common structural
24
+ * skeleton + the detected `payloadVersion` when present.
25
+ */
26
+ import type { SignalEnvelope } from '@opensip-cli/contracts';
27
+ /** JSON-safe scalar — the metadata-value subset the persisted shape permits. */
28
+ export type SessionPayloadScalar = string | number | boolean;
29
+ /** A decoded finding row — the structural superset across all tools. */
30
+ export interface DecodedSessionFinding {
31
+ readonly ruleId: string;
32
+ readonly message: string;
33
+ readonly severity: 'error' | 'warning';
34
+ readonly filePath?: string;
35
+ readonly line?: number;
36
+ readonly column?: number;
37
+ readonly suggestion?: string;
38
+ readonly metadata?: Readonly<Record<string, SessionPayloadScalar>>;
39
+ }
40
+ /** A decoded per-check row. */
41
+ export interface DecodedSessionCheck {
42
+ readonly checkSlug: string;
43
+ readonly passed: boolean;
44
+ readonly violationCount?: number;
45
+ readonly durationMs: number;
46
+ readonly findings: readonly DecodedSessionFinding[];
47
+ }
48
+ /** The decoded session payload — `summary` + rule/check-grouped `checks[]`. */
49
+ export interface DecodedSessionPayload {
50
+ readonly summary: SignalEnvelope['verdict']['summary'];
51
+ readonly checks: readonly DecodedSessionCheck[];
52
+ /**
53
+ * Detected inner `__version` from the opaque tool payload (or undefined for
54
+ * legacy pre-__version rows, which callers treat as v1 with projection).
55
+ * This is the value of the top-level numeric key if present and valid.
56
+ */
57
+ readonly payloadVersion?: number;
58
+ }
59
+ /** Per-tool decode options — the only points where the tools' payloads differ. */
60
+ export interface DecodeSessionPayloadOptions {
61
+ /** Tool label used in error messages (e.g. `'fit'`). */
62
+ readonly tool: string;
63
+ /** When true, every finding must carry a string `filePath` (graph). */
64
+ readonly requireFilePath?: boolean;
65
+ /** When true, every check must carry a numeric `violationCount` (graph/sim). */
66
+ readonly requireViolationCount?: boolean;
67
+ /** When true, decode each finding's scalar `metadata` bag (graph). */
68
+ readonly allowMetadata?: boolean;
69
+ }
70
+ /**
71
+ * Decode a stored session payload into its structural {@link DecodedSessionPayload}.
72
+ *
73
+ * @param payload - the opaque `StoredSession.payload` blob.
74
+ * @param opts - per-tool decode options (label + required-field toggles).
75
+ * @returns the decoded `{ summary, checks[] }` structure, plus `payloadVersion`
76
+ * (the detected inner `__version` if present and valid; undefined for legacy
77
+ * payloads that pre-date the convention — callers treat missing as v1).
78
+ * @throws {TypeError} when `payload`/`checks`/`findings` are not the expected
79
+ * object/array shapes.
80
+ * @throws {Error} when a required scalar field is missing or mistyped, or a
81
+ * finding severity is not `error`/`warning`.
82
+ */
83
+ export declare function decodeSessionPayload(payload: unknown, opts: DecodeSessionPayloadOptions): DecodedSessionPayload;
84
+ /**
85
+ * Decode the `summary` verdict-counts block.
86
+ *
87
+ * @throws {Error} when the value is missing or any count is not a number.
88
+ */
89
+ export declare function decodeSummary(value: unknown, label: string): SignalEnvelope['verdict']['summary'];
90
+ /** @throws {Error} when the field is not a number. */
91
+ export declare function numberField(source: Record<string, unknown>, field: string, label: string): number;
92
+ /** @throws {Error} when the field is not a string. */
93
+ export declare function stringField(source: Record<string, unknown>, field: string, label: string): string;
94
+ /** @throws {Error} when the field is not a boolean. */
95
+ export declare function booleanField(source: Record<string, unknown>, field: string, label: string): boolean;
96
+ //# sourceMappingURL=session-payload-decode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-payload-decode.d.ts","sourceRoot":"","sources":["../src/session-payload-decode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,gFAAgF;AAChF,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE7D,wEAAwE;AACxE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;CACpE;AAED,+BAA+B;AAC/B,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,qBAAqB,EAAE,CAAC;CACrD;AAED,+EAA+E;AAC/E,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC;IACvD,QAAQ,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAChD;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,kFAAkF;AAClF,MAAM,WAAW,2BAA2B;IAC1C,wDAAwD;IACxD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC,gFAAgF;IAChF,QAAQ,CAAC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IACzC,sEAAsE;IACtE,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,2BAA2B,GAChC,qBAAqB,CAevB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAYjG;AAkFD,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAIjG;AAED,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAIjG;AAED,uDAAuD;AACvD,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,OAAO,CAIT"}