@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.
- package/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +31 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/resolve-session.d.ts +34 -0
- package/dist/resolve-session.d.ts.map +1 -0
- package/dist/resolve-session.js +45 -0
- package/dist/resolve-session.js.map +1 -0
- package/dist/schema/sessions.d.ts +419 -0
- package/dist/schema/sessions.d.ts.map +1 -0
- package/dist/schema/sessions.js +62 -0
- package/dist/schema/sessions.js.map +1 -0
- package/dist/session-payload-decode.d.ts +96 -0
- package/dist/session-payload-decode.d.ts.map +1 -0
- package/dist/session-payload-decode.js +178 -0
- package/dist/session-payload-decode.js.map +1 -0
- package/dist/session-repo.d.ts +60 -0
- package/dist/session-repo.d.ts.map +1 -0
- package/dist/session-repo.js +328 -0
- package/dist/session-repo.js.map +1 -0
- package/dist/store.d.ts +12 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +17 -0
- package/dist/store.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,178 @@
|
|
|
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 { extractPayloadVersion } from '@opensip-cli/core';
|
|
27
|
+
/**
|
|
28
|
+
* Decode a stored session payload into its structural {@link DecodedSessionPayload}.
|
|
29
|
+
*
|
|
30
|
+
* @param payload - the opaque `StoredSession.payload` blob.
|
|
31
|
+
* @param opts - per-tool decode options (label + required-field toggles).
|
|
32
|
+
* @returns the decoded `{ summary, checks[] }` structure, plus `payloadVersion`
|
|
33
|
+
* (the detected inner `__version` if present and valid; undefined for legacy
|
|
34
|
+
* payloads that pre-date the convention — callers treat missing as v1).
|
|
35
|
+
* @throws {TypeError} when `payload`/`checks`/`findings` are not the expected
|
|
36
|
+
* object/array shapes.
|
|
37
|
+
* @throws {Error} when a required scalar field is missing or mistyped, or a
|
|
38
|
+
* finding severity is not `error`/`warning`.
|
|
39
|
+
*/
|
|
40
|
+
export function decodeSessionPayload(payload, opts) {
|
|
41
|
+
if (payload === null || typeof payload !== 'object') {
|
|
42
|
+
throw new Error(`${opts.tool} session has no replay payload`);
|
|
43
|
+
}
|
|
44
|
+
const candidate = payload;
|
|
45
|
+
const summary = decodeSummary(candidate.summary, `${opts.tool} session summary`);
|
|
46
|
+
if (!Array.isArray(candidate.checks)) {
|
|
47
|
+
throw new TypeError(`${opts.tool} session payload is missing checks[]`);
|
|
48
|
+
}
|
|
49
|
+
const payloadVersion = extractPayloadVersion(payload);
|
|
50
|
+
return {
|
|
51
|
+
summary,
|
|
52
|
+
checks: candidate.checks.map((check) => decodeCheck(check, opts)),
|
|
53
|
+
...(payloadVersion === undefined ? {} : { payloadVersion }),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Decode the `summary` verdict-counts block.
|
|
58
|
+
*
|
|
59
|
+
* @throws {Error} when the value is missing or any count is not a number.
|
|
60
|
+
*/
|
|
61
|
+
export function decodeSummary(value, label) {
|
|
62
|
+
if (value === null || typeof value !== 'object') {
|
|
63
|
+
throw new Error(`${label} is missing`);
|
|
64
|
+
}
|
|
65
|
+
const summary = value;
|
|
66
|
+
return {
|
|
67
|
+
total: numberField(summary, 'total', label),
|
|
68
|
+
passed: numberField(summary, 'passed', label),
|
|
69
|
+
failed: numberField(summary, 'failed', label),
|
|
70
|
+
errors: numberField(summary, 'errors', label),
|
|
71
|
+
warnings: numberField(summary, 'warnings', label),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Decode one check row.
|
|
76
|
+
*
|
|
77
|
+
* @throws {Error} when the row is not an object or a required field is mistyped.
|
|
78
|
+
* @throws {TypeError} when `findings` is not an array.
|
|
79
|
+
*/
|
|
80
|
+
function decodeCheck(value, opts) {
|
|
81
|
+
if (value === null || typeof value !== 'object') {
|
|
82
|
+
throw new Error(`${opts.tool} session check row is invalid`);
|
|
83
|
+
}
|
|
84
|
+
const check = value;
|
|
85
|
+
const label = `${opts.tool} session check`;
|
|
86
|
+
const checkSlug = stringField(check, 'checkSlug', label);
|
|
87
|
+
let violationCount;
|
|
88
|
+
if (opts.requireViolationCount) {
|
|
89
|
+
violationCount = numberField(check, 'violationCount', label);
|
|
90
|
+
}
|
|
91
|
+
else if (typeof check.violationCount === 'number') {
|
|
92
|
+
violationCount = check.violationCount;
|
|
93
|
+
}
|
|
94
|
+
if (!Array.isArray(check.findings)) {
|
|
95
|
+
throw new TypeError(`${opts.tool} session check ${checkSlug} is missing findings[]`);
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
checkSlug,
|
|
99
|
+
passed: booleanField(check, 'passed', label),
|
|
100
|
+
...(violationCount === undefined ? {} : { violationCount }),
|
|
101
|
+
durationMs: numberField(check, 'durationMs', label),
|
|
102
|
+
findings: check.findings.map((finding) => decodeFinding(finding, opts)),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Decode one finding row.
|
|
107
|
+
*
|
|
108
|
+
* @throws {Error} when the row is not an object, a required string is mistyped,
|
|
109
|
+
* or the severity is not `error`/`warning`.
|
|
110
|
+
*/
|
|
111
|
+
function decodeFinding(value, opts) {
|
|
112
|
+
if (value === null || typeof value !== 'object') {
|
|
113
|
+
throw new Error(`${opts.tool} session finding is invalid`);
|
|
114
|
+
}
|
|
115
|
+
const finding = value;
|
|
116
|
+
const label = `${opts.tool} session finding`;
|
|
117
|
+
const severity = finding.severity;
|
|
118
|
+
if (severity !== 'error' && severity !== 'warning') {
|
|
119
|
+
throw new Error(`${opts.tool} session finding has invalid severity`);
|
|
120
|
+
}
|
|
121
|
+
const filePath = opts.requireFilePath
|
|
122
|
+
? stringField(finding, 'filePath', label)
|
|
123
|
+
: optionalString(finding.filePath);
|
|
124
|
+
const line = optionalNumber(finding.line);
|
|
125
|
+
const column = optionalNumber(finding.column);
|
|
126
|
+
const suggestion = optionalString(finding.suggestion);
|
|
127
|
+
const metadata = opts.allowMetadata ? decodeMetadata(finding.metadata) : undefined;
|
|
128
|
+
return {
|
|
129
|
+
ruleId: stringField(finding, 'ruleId', label),
|
|
130
|
+
message: stringField(finding, 'message', label),
|
|
131
|
+
severity,
|
|
132
|
+
...(filePath === undefined ? {} : { filePath }),
|
|
133
|
+
...(line === undefined ? {} : { line }),
|
|
134
|
+
...(column === undefined ? {} : { column }),
|
|
135
|
+
...(suggestion === undefined ? {} : { suggestion }),
|
|
136
|
+
...(metadata === undefined ? {} : { metadata }),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/** Narrow an open metadata bag to its scalar subset; undefined when nothing survives. */
|
|
140
|
+
function decodeMetadata(value) {
|
|
141
|
+
if (value === null || typeof value !== 'object')
|
|
142
|
+
return undefined;
|
|
143
|
+
const out = {};
|
|
144
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
145
|
+
if (typeof entry === 'string' || typeof entry === 'number' || typeof entry === 'boolean') {
|
|
146
|
+
out[key] = entry;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return Object.keys(out).length === 0 ? undefined : out;
|
|
150
|
+
}
|
|
151
|
+
/** @throws {Error} when the field is not a number. */
|
|
152
|
+
export function numberField(source, field, label) {
|
|
153
|
+
const value = source[field];
|
|
154
|
+
if (typeof value !== 'number')
|
|
155
|
+
throw new Error(`${label}.${field} must be a number`);
|
|
156
|
+
return value;
|
|
157
|
+
}
|
|
158
|
+
/** @throws {Error} when the field is not a string. */
|
|
159
|
+
export function stringField(source, field, label) {
|
|
160
|
+
const value = source[field];
|
|
161
|
+
if (typeof value !== 'string')
|
|
162
|
+
throw new Error(`${label}.${field} must be a string`);
|
|
163
|
+
return value;
|
|
164
|
+
}
|
|
165
|
+
/** @throws {Error} when the field is not a boolean. */
|
|
166
|
+
export function booleanField(source, field, label) {
|
|
167
|
+
const value = source[field];
|
|
168
|
+
if (typeof value !== 'boolean')
|
|
169
|
+
throw new Error(`${label}.${field} must be a boolean`);
|
|
170
|
+
return value;
|
|
171
|
+
}
|
|
172
|
+
function optionalString(value) {
|
|
173
|
+
return typeof value === 'string' ? value : undefined;
|
|
174
|
+
}
|
|
175
|
+
function optionalNumber(value) {
|
|
176
|
+
return typeof value === 'number' ? value : undefined;
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=session-payload-decode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-payload-decode.js","sourceRoot":"","sources":["../src/session-payload-decode.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type DataStore } from '@opensip-cli/datastore';
|
|
2
|
+
import type { StoredSession, StoredSessionHostMetrics } from '@opensip-cli/contracts';
|
|
3
|
+
import type { ToolShortId } from '@opensip-cli/core';
|
|
4
|
+
/** Filters for {@link SessionRepo.list}: tool short-id and/or max row count. */
|
|
5
|
+
export interface SessionListOptions {
|
|
6
|
+
readonly tool?: ToolShortId;
|
|
7
|
+
readonly limit?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Persistence layer for tool-run sessions. Stores generic session columns plus
|
|
11
|
+
* one opaque per-tool `payload` blob; holds ZERO tool vocabulary — it never
|
|
12
|
+
* inspects/validates the payload shape (the producing tool owns that). (Audit
|
|
13
|
+
* 2026-05-29, session split.)
|
|
14
|
+
*/
|
|
15
|
+
export declare class SessionRepo {
|
|
16
|
+
private readonly datastore;
|
|
17
|
+
constructor(datastore: DataStore);
|
|
18
|
+
/**
|
|
19
|
+
* Persist a completed session row and its tool payload. Writes the generic
|
|
20
|
+
* columns including host-owned `startedAt` / `completedAt` (mapped to the
|
|
21
|
+
* physical `timestamp*` / `completed_at*` columns); `session.hostMetrics` is
|
|
22
|
+
* IGNORED here — metrics arrive later and attach via {@link SessionRepo.upsertHostMetrics}.
|
|
23
|
+
*
|
|
24
|
+
* @throws {ValidationError} When `startedAt` / `completedAt` are not finite
|
|
25
|
+
* dates — guarded eagerly so a bad value never corrupts the durable log.
|
|
26
|
+
*/
|
|
27
|
+
save(session: StoredSession): void;
|
|
28
|
+
list(opts?: SessionListOptions): readonly StoredSession[];
|
|
29
|
+
get(id: string): StoredSession | null;
|
|
30
|
+
latest(opts?: {
|
|
31
|
+
tool?: ToolShortId;
|
|
32
|
+
}): StoredSession | null;
|
|
33
|
+
count(): number;
|
|
34
|
+
/** Delete sessions older than the given Date. Returns affected rowcount. */
|
|
35
|
+
purge(before: Date): number;
|
|
36
|
+
/** Delete every session. Returns affected rowcount. */
|
|
37
|
+
clearAll(): number;
|
|
38
|
+
/**
|
|
39
|
+
* Delete ONE tool's sessions (`tools data purge`, ADR-0042). Associated
|
|
40
|
+
* `session_tool_payload` rows go via the schema's `onDelete: 'cascade'` FK
|
|
41
|
+
* (the sqlite backend runs `PRAGMA foreign_keys = ON`). Returns the deleted
|
|
42
|
+
* session count.
|
|
43
|
+
*/
|
|
44
|
+
clearForTool(toolId: string): number;
|
|
45
|
+
private hydrateSession;
|
|
46
|
+
/**
|
|
47
|
+
* Read the sibling host-metrics record for a session, or `undefined` when no
|
|
48
|
+
* metrics row exists. Null columns are dropped so the projection only carries
|
|
49
|
+
* metrics that were actually captured.
|
|
50
|
+
*/
|
|
51
|
+
private readHostMetrics;
|
|
52
|
+
/**
|
|
53
|
+
* Best-effort upsert of host-side overhead metrics for a session
|
|
54
|
+
* (host-owned-run-timing §5.3). The host run plane calls this as render /
|
|
55
|
+
* persist / egress metrics become known; only the provided fields are written,
|
|
56
|
+
* merging onto any existing row. Never throws — metrics are observability.
|
|
57
|
+
*/
|
|
58
|
+
upsertHostMetrics(sessionId: string, metrics: StoredSessionHostMetrics): void;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=session-repo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-repo.d.ts","sourceRoot":"","sources":["../src/session-repo.ts"],"names":[],"mappings":"AAQA,OAAO,EAEL,KAAK,SAAS,EAEf,MAAM,wBAAwB,CAAC;AAKhC,OAAO,KAAK,EAAE,aAAa,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,gFAAgF;AAChF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;gBAEjC,SAAS,EAAE,SAAS;IAIhC;;;;;;;;OAQG;IACH,IAAI,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAkFlC,IAAI,CAAC,IAAI,GAAE,kBAAuB,GAAG,SAAS,aAAa,EAAE;IA6B7D,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAKrC,MAAM,CAAC,IAAI,GAAE;QAAE,IAAI,CAAC,EAAE,WAAW,CAAA;KAAO,GAAG,aAAa,GAAG,IAAI;IAK/D,KAAK,IAAI,MAAM;IAKf,4EAA4E;IAC5E,KAAK,CAAC,MAAM,EAAE,IAAI,GAAG,MAAM;IA0B3B,uDAAuD;IACvD,QAAQ,IAAI,MAAM;IAWlB;;;;;OAKG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAYpC,OAAO,CAAC,cAAc;IA4EtB;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAgBvB;;;;;OAKG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,wBAAwB,GAAG,IAAI;CAiC9E"}
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { SystemError, ValidationError, currentScope, extractPayloadVersion, isToolShortId, logger, } from '@opensip-cli/core';
|
|
2
|
+
import { requireDrizzleDataStore, } from '@opensip-cli/datastore';
|
|
3
|
+
import { desc, eq, lt } from 'drizzle-orm';
|
|
4
|
+
import { sessionHostMetrics, sessions, sessionToolPayload } from './schema/sessions.js';
|
|
5
|
+
const MODULE_NAME = 'session-store:session-repo';
|
|
6
|
+
/**
|
|
7
|
+
* Persistence layer for tool-run sessions. Stores generic session columns plus
|
|
8
|
+
* one opaque per-tool `payload` blob; holds ZERO tool vocabulary — it never
|
|
9
|
+
* inspects/validates the payload shape (the producing tool owns that). (Audit
|
|
10
|
+
* 2026-05-29, session split.)
|
|
11
|
+
*/
|
|
12
|
+
export class SessionRepo {
|
|
13
|
+
datastore;
|
|
14
|
+
constructor(datastore) {
|
|
15
|
+
this.datastore = requireDrizzleDataStore(datastore);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Persist a completed session row and its tool payload. Writes the generic
|
|
19
|
+
* columns including host-owned `startedAt` / `completedAt` (mapped to the
|
|
20
|
+
* physical `timestamp*` / `completed_at*` columns); `session.hostMetrics` is
|
|
21
|
+
* IGNORED here — metrics arrive later and attach via {@link SessionRepo.upsertHostMetrics}.
|
|
22
|
+
*
|
|
23
|
+
* @throws {ValidationError} When `startedAt` / `completedAt` are not finite
|
|
24
|
+
* dates — guarded eagerly so a bad value never corrupts the durable log.
|
|
25
|
+
*/
|
|
26
|
+
save(session) {
|
|
27
|
+
try {
|
|
28
|
+
// Validate timing eagerly: a bad value becomes a clear ValidationError
|
|
29
|
+
// instead of silent NaN/"Invalid Date" corruption in the durable log.
|
|
30
|
+
const startedMs = new Date(session.startedAt).getTime();
|
|
31
|
+
const completedMs = new Date(session.completedAt).getTime();
|
|
32
|
+
if (!Number.isFinite(startedMs) || !Number.isFinite(completedMs)) {
|
|
33
|
+
// @fitness-ignore-next-line result-pattern-consistency -- persistence boundary (DEC-015): invalid run timing is a write-time data-integrity guard the caller cannot recover from, so throw (not Result) is correct
|
|
34
|
+
throw new ValidationError(`Invalid session timing for session ${session.id} (tool=${session.tool}): startedAt=${JSON.stringify(session.startedAt)} completedAt=${JSON.stringify(session.completedAt)}`, { code: 'VALIDATION.SESSION.INVALID_TIMESTAMP' });
|
|
35
|
+
}
|
|
36
|
+
this.datastore.transaction((tx) => {
|
|
37
|
+
tx.insert(sessions)
|
|
38
|
+
.values({
|
|
39
|
+
id: session.id,
|
|
40
|
+
tool: session.tool,
|
|
41
|
+
timestamp: startedMs,
|
|
42
|
+
timestamp_iso: session.startedAt, // preserve original for fidelity (avoids Date roundtrip loss of sub-ms/lexical form)
|
|
43
|
+
completed_at: completedMs,
|
|
44
|
+
completed_at_iso: session.completedAt,
|
|
45
|
+
cwd: session.cwd,
|
|
46
|
+
recipe: session.recipe ?? null,
|
|
47
|
+
score: session.score,
|
|
48
|
+
passed: session.passed,
|
|
49
|
+
durationMs: session.durationMs,
|
|
50
|
+
})
|
|
51
|
+
.run();
|
|
52
|
+
// Tool-owned opaque detail (contracts never inspects the shape).
|
|
53
|
+
if (session.payload !== undefined) {
|
|
54
|
+
const hasInnerVersion = extractPayloadVersion(session.payload) !== undefined;
|
|
55
|
+
if (!hasInnerVersion) {
|
|
56
|
+
// Encourage tools to adopt the __version convention on new writes.
|
|
57
|
+
logger.warn({
|
|
58
|
+
evt: 'session.payload.missing_version',
|
|
59
|
+
module: MODULE_NAME,
|
|
60
|
+
sessionId: session.id,
|
|
61
|
+
tool: session.tool,
|
|
62
|
+
msg: "Tool wrote a session payload without top-level __version (treated as legacy v1). Update the tool's build*SessionPayload to include __version: 1.",
|
|
63
|
+
});
|
|
64
|
+
const scope = currentScope();
|
|
65
|
+
scope?.diagnostics?.event('persist', 'warn', 'session payload written without __version (legacy v1 treatment)', { sessionId: session.id, tool: session.tool });
|
|
66
|
+
}
|
|
67
|
+
tx.insert(sessionToolPayload)
|
|
68
|
+
.values({
|
|
69
|
+
sessionId: session.id,
|
|
70
|
+
tool: session.tool,
|
|
71
|
+
payload: session.payload,
|
|
72
|
+
payload_version: 1, // outer storage contract version (inner __version lives inside the JSON blob)
|
|
73
|
+
})
|
|
74
|
+
.run();
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
logger.info({
|
|
78
|
+
evt: 'session.save.complete',
|
|
79
|
+
module: MODULE_NAME,
|
|
80
|
+
msg: 'Session saved',
|
|
81
|
+
sessionId: session.id,
|
|
82
|
+
tool: session.tool,
|
|
83
|
+
hasPayload: session.payload !== undefined,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
logger.error({
|
|
88
|
+
evt: 'session.save.error',
|
|
89
|
+
module: MODULE_NAME,
|
|
90
|
+
msg: 'Failed to save session',
|
|
91
|
+
sessionId: session.id,
|
|
92
|
+
error: error instanceof Error ? error.message : String(error),
|
|
93
|
+
...(error instanceof Error && error.stack ? { stack: error.stack } : {}),
|
|
94
|
+
});
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
list(opts = {}) {
|
|
99
|
+
try {
|
|
100
|
+
const baseQuery = opts.tool
|
|
101
|
+
? this.datastore.db.select().from(sessions).where(eq(sessions.tool, opts.tool))
|
|
102
|
+
: this.datastore.db.select().from(sessions);
|
|
103
|
+
const ordered = baseQuery.orderBy(desc(sessions.timestamp));
|
|
104
|
+
const sessionRows = opts.limit ? ordered.limit(opts.limit).all() : ordered.all();
|
|
105
|
+
const results = [];
|
|
106
|
+
for (const row of sessionRows) {
|
|
107
|
+
results.push(this.hydrateSession(row));
|
|
108
|
+
}
|
|
109
|
+
logger.info({
|
|
110
|
+
evt: 'session.list.complete',
|
|
111
|
+
module: MODULE_NAME,
|
|
112
|
+
msg: 'Listed sessions',
|
|
113
|
+
count: results.length,
|
|
114
|
+
});
|
|
115
|
+
return results;
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
logger.error({
|
|
119
|
+
evt: 'session.list.error',
|
|
120
|
+
module: MODULE_NAME,
|
|
121
|
+
msg: 'Failed to list sessions',
|
|
122
|
+
error: error instanceof Error ? error.message : String(error),
|
|
123
|
+
});
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
get(id) {
|
|
128
|
+
const row = this.datastore.db.select().from(sessions).where(eq(sessions.id, id)).get();
|
|
129
|
+
return row ? this.hydrateSession(row) : null;
|
|
130
|
+
}
|
|
131
|
+
latest(opts = {}) {
|
|
132
|
+
const rows = this.list({ ...opts, limit: 1 });
|
|
133
|
+
return rows[0] ?? null;
|
|
134
|
+
}
|
|
135
|
+
count() {
|
|
136
|
+
const rows = this.datastore.db.select({ id: sessions.id }).from(sessions).all();
|
|
137
|
+
return rows.length;
|
|
138
|
+
}
|
|
139
|
+
/** Delete sessions older than the given Date. Returns affected rowcount. */
|
|
140
|
+
purge(before) {
|
|
141
|
+
try {
|
|
142
|
+
const cutoff = before.getTime();
|
|
143
|
+
const removed = this.datastore.db
|
|
144
|
+
.delete(sessions)
|
|
145
|
+
.where(lt(sessions.timestamp, cutoff))
|
|
146
|
+
.run();
|
|
147
|
+
logger.info({
|
|
148
|
+
evt: 'session.purge.complete',
|
|
149
|
+
module: MODULE_NAME,
|
|
150
|
+
msg: 'Purged sessions older than cutoff',
|
|
151
|
+
cutoff: before.toISOString(),
|
|
152
|
+
deleted: removed.changes,
|
|
153
|
+
});
|
|
154
|
+
return removed.changes;
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
logger.error({
|
|
158
|
+
evt: 'session.purge.error',
|
|
159
|
+
module: MODULE_NAME,
|
|
160
|
+
msg: 'Failed to purge sessions',
|
|
161
|
+
error: error instanceof Error ? error.message : String(error),
|
|
162
|
+
});
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/** Delete every session. Returns affected rowcount. */
|
|
167
|
+
clearAll() {
|
|
168
|
+
const removed = this.datastore.db.delete(sessions).run();
|
|
169
|
+
logger.info({
|
|
170
|
+
evt: 'session.clear.complete',
|
|
171
|
+
module: MODULE_NAME,
|
|
172
|
+
msg: 'Cleared all sessions',
|
|
173
|
+
deleted: removed.changes,
|
|
174
|
+
});
|
|
175
|
+
return removed.changes;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Delete ONE tool's sessions (`tools data purge`, ADR-0042). Associated
|
|
179
|
+
* `session_tool_payload` rows go via the schema's `onDelete: 'cascade'` FK
|
|
180
|
+
* (the sqlite backend runs `PRAGMA foreign_keys = ON`). Returns the deleted
|
|
181
|
+
* session count.
|
|
182
|
+
*/
|
|
183
|
+
clearForTool(toolId) {
|
|
184
|
+
const removed = this.datastore.db.delete(sessions).where(eq(sessions.tool, toolId)).run();
|
|
185
|
+
logger.info({
|
|
186
|
+
evt: 'session.clear_for_tool.complete',
|
|
187
|
+
module: MODULE_NAME,
|
|
188
|
+
msg: 'Cleared sessions for tool',
|
|
189
|
+
tool: toolId,
|
|
190
|
+
deleted: removed.changes,
|
|
191
|
+
});
|
|
192
|
+
return removed.changes;
|
|
193
|
+
}
|
|
194
|
+
hydrateSession(row) {
|
|
195
|
+
// Validate row.tool against the documented union — the SQLite column
|
|
196
|
+
// is plain text with no CHECK constraint, so a legacy or hand-edited
|
|
197
|
+
// row could carry a value outside the type. Casting blindly would
|
|
198
|
+
// silently misroute downstream consumers that branch on `tool`.
|
|
199
|
+
if (!isToolShortId(row.tool)) {
|
|
200
|
+
throw new SystemError(`Session ${row.id} has unknown tool value: ${JSON.stringify(row.tool)}`, { code: 'SYSTEM.DATA.UNKNOWN_TOOL' });
|
|
201
|
+
}
|
|
202
|
+
// Tool-owned opaque detail. drizzle returns the JSON column already
|
|
203
|
+
// parsed; contracts does not inspect or validate the shape — that is
|
|
204
|
+
// the owning tool's responsibility.
|
|
205
|
+
const payloadRow = this.datastore.db
|
|
206
|
+
.select({
|
|
207
|
+
payload: sessionToolPayload.payload,
|
|
208
|
+
payload_version: sessionToolPayload.payload_version,
|
|
209
|
+
})
|
|
210
|
+
.from(sessionToolPayload)
|
|
211
|
+
.where(eq(sessionToolPayload.sessionId, row.id))
|
|
212
|
+
.get();
|
|
213
|
+
const outerVersion = payloadRow?.payload_version ?? 1;
|
|
214
|
+
const innerVersion = extractPayloadVersion(payloadRow?.payload);
|
|
215
|
+
if (outerVersion > 1 || (innerVersion !== undefined && innerVersion > 1)) {
|
|
216
|
+
logger.warn({
|
|
217
|
+
evt: 'session.payload.future_version',
|
|
218
|
+
module: MODULE_NAME,
|
|
219
|
+
sessionId: row.id,
|
|
220
|
+
outerVersion,
|
|
221
|
+
innerVersion: innerVersion ?? null,
|
|
222
|
+
msg: 'Payload schema version newer than this CLI knows; treating as opaque (may lose fields on display).',
|
|
223
|
+
});
|
|
224
|
+
// Emit on the per-run DiagnosticsBus for --json / CommandOutcome consumers (cross-cutting observability).
|
|
225
|
+
// Uses currentScope per RunScope rules; safe no-op when no scope (e.g. some tests).
|
|
226
|
+
const scope = currentScope();
|
|
227
|
+
scope?.diagnostics?.event('load', 'warn', `session payload future version (outer=${outerVersion}, inner=${innerVersion ?? 'legacy'})`, {
|
|
228
|
+
sessionId: row.id,
|
|
229
|
+
outerVersion,
|
|
230
|
+
innerVersion: innerVersion ?? undefined,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
const startedAt = row.timestamp_iso ?? new Date(row.timestamp).toISOString(); // prefer original for fidelity
|
|
234
|
+
// completedAt: prefer the stored ISO, else the stored ms, else (legacy rows
|
|
235
|
+
// written before completedAt existed) synthesize startedAt + durationMs.
|
|
236
|
+
const completedAt = row.completed_at_iso ??
|
|
237
|
+
(row.completed_at == null
|
|
238
|
+
? new Date(row.timestamp + row.durationMs).toISOString() // legacy synth
|
|
239
|
+
: new Date(row.completed_at).toISOString());
|
|
240
|
+
const hostMetrics = this.readHostMetrics(row.id);
|
|
241
|
+
return {
|
|
242
|
+
id: row.id,
|
|
243
|
+
tool: row.tool,
|
|
244
|
+
startedAt,
|
|
245
|
+
completedAt,
|
|
246
|
+
cwd: row.cwd,
|
|
247
|
+
recipe: row.recipe ?? undefined,
|
|
248
|
+
score: row.score,
|
|
249
|
+
passed: row.passed,
|
|
250
|
+
durationMs: row.durationMs,
|
|
251
|
+
...(hostMetrics ? { hostMetrics } : {}),
|
|
252
|
+
payload: payloadRow?.payload,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Read the sibling host-metrics record for a session, or `undefined` when no
|
|
257
|
+
* metrics row exists. Null columns are dropped so the projection only carries
|
|
258
|
+
* metrics that were actually captured.
|
|
259
|
+
*/
|
|
260
|
+
readHostMetrics(sessionId) {
|
|
261
|
+
const row = this.datastore.db
|
|
262
|
+
.select()
|
|
263
|
+
.from(sessionHostMetrics)
|
|
264
|
+
.where(eq(sessionHostMetrics.sessionId, sessionId))
|
|
265
|
+
.get();
|
|
266
|
+
if (!row)
|
|
267
|
+
return undefined;
|
|
268
|
+
const metrics = {};
|
|
269
|
+
if (row.ttyBusyMs != null)
|
|
270
|
+
metrics.ttyBusyMs = row.ttyBusyMs;
|
|
271
|
+
if (row.renderMs != null)
|
|
272
|
+
metrics.renderMs = row.renderMs;
|
|
273
|
+
if (row.persistMs != null)
|
|
274
|
+
metrics.persistMs = row.persistMs;
|
|
275
|
+
if (row.egressMs != null)
|
|
276
|
+
metrics.egressMs = row.egressMs;
|
|
277
|
+
if (row.totalCommandMs != null)
|
|
278
|
+
metrics.totalCommandMs = row.totalCommandMs;
|
|
279
|
+
return Object.keys(metrics).length > 0 ? metrics : undefined;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Best-effort upsert of host-side overhead metrics for a session
|
|
283
|
+
* (host-owned-run-timing §5.3). The host run plane calls this as render /
|
|
284
|
+
* persist / egress metrics become known; only the provided fields are written,
|
|
285
|
+
* merging onto any existing row. Never throws — metrics are observability.
|
|
286
|
+
*/
|
|
287
|
+
upsertHostMetrics(sessionId, metrics) {
|
|
288
|
+
try {
|
|
289
|
+
// `set` keys are the Drizzle COLUMN PROPERTY names (camelCase), NOT the
|
|
290
|
+
// SQL column names — Drizzle silently ignores unknown keys, so snake_case
|
|
291
|
+
// here would no-op the ON CONFLICT update and the merge would be lost.
|
|
292
|
+
const patch = {};
|
|
293
|
+
if (metrics.ttyBusyMs !== undefined)
|
|
294
|
+
patch.ttyBusyMs = metrics.ttyBusyMs;
|
|
295
|
+
if (metrics.renderMs !== undefined)
|
|
296
|
+
patch.renderMs = metrics.renderMs;
|
|
297
|
+
if (metrics.persistMs !== undefined)
|
|
298
|
+
patch.persistMs = metrics.persistMs;
|
|
299
|
+
if (metrics.egressMs !== undefined)
|
|
300
|
+
patch.egressMs = metrics.egressMs;
|
|
301
|
+
if (metrics.totalCommandMs !== undefined)
|
|
302
|
+
patch.totalCommandMs = metrics.totalCommandMs;
|
|
303
|
+
if (Object.keys(patch).length === 0)
|
|
304
|
+
return;
|
|
305
|
+
this.datastore.db
|
|
306
|
+
.insert(sessionHostMetrics)
|
|
307
|
+
.values({
|
|
308
|
+
sessionId,
|
|
309
|
+
ttyBusyMs: metrics.ttyBusyMs ?? null,
|
|
310
|
+
renderMs: metrics.renderMs ?? null,
|
|
311
|
+
persistMs: metrics.persistMs ?? null,
|
|
312
|
+
egressMs: metrics.egressMs ?? null,
|
|
313
|
+
totalCommandMs: metrics.totalCommandMs ?? null,
|
|
314
|
+
})
|
|
315
|
+
.onConflictDoUpdate({ target: sessionHostMetrics.sessionId, set: patch })
|
|
316
|
+
.run();
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
logger.warn({
|
|
320
|
+
evt: 'session.host_metrics.upsert_failed',
|
|
321
|
+
module: MODULE_NAME,
|
|
322
|
+
sessionId,
|
|
323
|
+
error: error instanceof Error ? error.message : String(error),
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
//# sourceMappingURL=session-repo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-repo.js","sourceRoot":"","sources":["../src/session-repo.ts"],"names":[],"mappings":"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"}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session id / filename runtime helpers.
|
|
3
|
+
*
|
|
4
|
+
* Moved out of `@opensip-cli/contracts` (audit 2026-05-29, contracts
|
|
5
|
+
* split) so contracts carries no runtime. The `StoredSession` type stays
|
|
6
|
+
* in contracts as the cross-tool contract surface.
|
|
7
|
+
*/
|
|
8
|
+
/** Generate a unique session ID */
|
|
9
|
+
export declare function generateSessionId(): string;
|
|
10
|
+
/** Sanitize a string for use in a filename — strip path separators and special chars */
|
|
11
|
+
export declare function sanitizeForFilename(s: string): string;
|
|
12
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,mCAAmC;AACnC,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wFAAwF;AACxF,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAErD"}
|
package/dist/store.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session id / filename runtime helpers.
|
|
3
|
+
*
|
|
4
|
+
* Moved out of `@opensip-cli/contracts` (audit 2026-05-29, contracts
|
|
5
|
+
* split) so contracts carries no runtime. The `StoredSession` type stays
|
|
6
|
+
* in contracts as the cross-tool contract surface.
|
|
7
|
+
*/
|
|
8
|
+
import { randomUUID } from 'node:crypto';
|
|
9
|
+
/** Generate a unique session ID */
|
|
10
|
+
export function generateSessionId() {
|
|
11
|
+
return randomUUID();
|
|
12
|
+
}
|
|
13
|
+
/** Sanitize a string for use in a filename — strip path separators and special chars */
|
|
14
|
+
export function sanitizeForFilename(s) {
|
|
15
|
+
return s.replaceAll('..', '-').replaceAll(/[/\\:*?"<>|.]/g, '-');
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,mCAAmC;AACnC,MAAM,UAAU,iBAAiB;IAC/B,OAAO,UAAU,EAAE,CAAC;AACtB,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,mBAAmB,CAAC,CAAS;IAC3C,OAAO,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,UAAU,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;AACnE,CAAC"}
|