opencode-lcm 0.12.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/store-doctor.d.ts +73 -0
- package/dist/store-doctor.js +106 -0
- package/dist/store-schema.d.ts +8 -0
- package/dist/store-schema.js +23 -0
- package/dist/store-session-read.d.ts +97 -0
- package/dist/store-session-read.js +80 -0
- package/dist/store.d.ts +12 -0
- package/dist/store.js +477 -271
- package/package.json +1 -1
- package/src/store.ts +577 -299
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.13.1] - 2026-04-07
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Archive transform now removes malformed messages from the outbound message array before returning control to OpenCode, preventing follow-on backend `Bad Request` failures
|
|
14
|
+
- Archive, resume, describe, search indexing, and capture paths now skip malformed `message.info` metadata defensively instead of throwing when required fields are missing
|
|
15
|
+
|
|
10
16
|
### Added
|
|
11
17
|
- Opt-in `perf:archive` harness for large-archive regression coverage across transform, grep, snapshot, reopen, resume, and retention paths
|
|
12
18
|
- Separate advisory `Archive Performance` workflow for scheduled/manual perf runs with JSON artifact upload
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { SqlDatabaseLike } from './store-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Doctor diagnostics operations.
|
|
4
|
+
* Analyzes store health: summary graph integrity, FTS index consistency, orphan detection.
|
|
5
|
+
*/
|
|
6
|
+
export type DoctorSessionIssue = {
|
|
7
|
+
sessionID: string;
|
|
8
|
+
issues: string[];
|
|
9
|
+
};
|
|
10
|
+
export type DoctorReport = {
|
|
11
|
+
scope: string;
|
|
12
|
+
checkedSessions: number;
|
|
13
|
+
summarySessionsNeedingRebuild: DoctorSessionIssue[];
|
|
14
|
+
lineageSessionsNeedingRefresh: string[];
|
|
15
|
+
orphanSummaryEdges: number;
|
|
16
|
+
messageFts: {
|
|
17
|
+
expected: number;
|
|
18
|
+
actual: number;
|
|
19
|
+
};
|
|
20
|
+
summaryFts: {
|
|
21
|
+
expected: number;
|
|
22
|
+
actual: number;
|
|
23
|
+
};
|
|
24
|
+
artifactFts: {
|
|
25
|
+
expected: number;
|
|
26
|
+
actual: number;
|
|
27
|
+
};
|
|
28
|
+
orphanArtifactBlobs: number;
|
|
29
|
+
status: 'clean' | 'issues-found';
|
|
30
|
+
};
|
|
31
|
+
type SessionSnapshot = {
|
|
32
|
+
sessionID: string;
|
|
33
|
+
messages: {
|
|
34
|
+
info: {
|
|
35
|
+
id: string;
|
|
36
|
+
time: {
|
|
37
|
+
created: number;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
parts: unknown[];
|
|
41
|
+
}[];
|
|
42
|
+
rootSessionID?: string;
|
|
43
|
+
lineageDepth?: number;
|
|
44
|
+
};
|
|
45
|
+
type SummaryNodeRow = {
|
|
46
|
+
node_id: string;
|
|
47
|
+
session_id: string;
|
|
48
|
+
level: number;
|
|
49
|
+
slot: number;
|
|
50
|
+
archived_message_ids_json: string;
|
|
51
|
+
summary_text: string;
|
|
52
|
+
created_at: number;
|
|
53
|
+
};
|
|
54
|
+
type DoctorDeps = {
|
|
55
|
+
db: SqlDatabaseLike;
|
|
56
|
+
getArchivedMessages: (messages: SessionSnapshot['messages']) => SessionSnapshot['messages'];
|
|
57
|
+
buildArchivedSignature: (messages: SessionSnapshot['messages']) => string;
|
|
58
|
+
readSummaryNode: (nodeID: string) => SummaryNodeRow | undefined;
|
|
59
|
+
canReuseSummaryGraph: (sessionID: string, archived: SessionSnapshot['messages'], roots: SummaryNodeRow[]) => boolean;
|
|
60
|
+
readScopedSummaryRows: (sessionIDs?: string[]) => unknown[];
|
|
61
|
+
readScopedArtifactRows: (sessionIDs?: string[]) => unknown[];
|
|
62
|
+
readOrphanArtifactBlobRows: () => unknown[];
|
|
63
|
+
countScopedFtsRows: (table: 'message_fts' | 'summary_fts' | 'artifact_fts', sessionIDs?: string[]) => number;
|
|
64
|
+
countScopedOrphanSummaryEdges: (sessionIDs?: string[]) => number;
|
|
65
|
+
guessMessageText: (message: SessionSnapshot['messages'][number], ignorePrefixes: string[]) => string;
|
|
66
|
+
ignoreToolPrefixes: string[];
|
|
67
|
+
parseJson: <T>(value: string) => T;
|
|
68
|
+
};
|
|
69
|
+
export declare function collectDoctorReport(sessions: SessionSnapshot[], sessionID: string | undefined, deps: DoctorDeps, readLineageChain: (sessionID: string) => {
|
|
70
|
+
sessionID: string;
|
|
71
|
+
}[]): DoctorReport;
|
|
72
|
+
export declare function hasDoctorIssues(report: DoctorReport): boolean;
|
|
73
|
+
export {};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
function countFtsExpected(sessions, deps) {
|
|
2
|
+
return sessions.reduce((count, session) => {
|
|
3
|
+
return (count +
|
|
4
|
+
session.messages.filter((message) => deps.guessMessageText(message, deps.ignoreToolPrefixes).length > 0).length);
|
|
5
|
+
}, 0);
|
|
6
|
+
}
|
|
7
|
+
function diagnoseSummarySession(session, deps) {
|
|
8
|
+
const issues = [];
|
|
9
|
+
const archived = deps.getArchivedMessages(session.messages);
|
|
10
|
+
const state = deps.db
|
|
11
|
+
.prepare('SELECT * FROM summary_state WHERE session_id = ?')
|
|
12
|
+
.get(session.sessionID);
|
|
13
|
+
const summaryNodeCount = deps.db
|
|
14
|
+
.prepare('SELECT COUNT(*) AS count FROM summary_nodes WHERE session_id = ?')
|
|
15
|
+
.get(session.sessionID);
|
|
16
|
+
const summaryEdgeCount = deps.db
|
|
17
|
+
.prepare('SELECT COUNT(*) AS count FROM summary_edges WHERE session_id = ?')
|
|
18
|
+
.get(session.sessionID);
|
|
19
|
+
if (archived.length === 0) {
|
|
20
|
+
if (state)
|
|
21
|
+
issues.push('unexpected-summary-state');
|
|
22
|
+
if (summaryNodeCount.count > 0)
|
|
23
|
+
issues.push('unexpected-summary-nodes');
|
|
24
|
+
if (summaryEdgeCount.count > 0)
|
|
25
|
+
issues.push('unexpected-summary-edges');
|
|
26
|
+
return issues.length > 0 ? { sessionID: session.sessionID, issues } : undefined;
|
|
27
|
+
}
|
|
28
|
+
const latestMessageCreated = archived.at(-1)?.info.time.created ?? 0;
|
|
29
|
+
const archivedSignature = deps.buildArchivedSignature(archived);
|
|
30
|
+
const rootIDs = state ? deps.parseJson(state.root_node_ids_json) : [];
|
|
31
|
+
const roots = rootIDs
|
|
32
|
+
.map((nodeID) => deps.readSummaryNode(nodeID))
|
|
33
|
+
.filter((node) => Boolean(node));
|
|
34
|
+
if (!state) {
|
|
35
|
+
issues.push('missing-summary-state');
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
if (state.archived_count !== archived.length)
|
|
39
|
+
issues.push('archived-count-mismatch');
|
|
40
|
+
if (state.latest_message_created !== latestMessageCreated)
|
|
41
|
+
issues.push('latest-message-mismatch');
|
|
42
|
+
if (state.archived_signature !== archivedSignature)
|
|
43
|
+
issues.push('archived-signature-mismatch');
|
|
44
|
+
if (rootIDs.length === 0)
|
|
45
|
+
issues.push('missing-root-node-ids');
|
|
46
|
+
if (roots.length !== rootIDs.length) {
|
|
47
|
+
issues.push('missing-root-node-record');
|
|
48
|
+
}
|
|
49
|
+
else if (rootIDs.length > 0 &&
|
|
50
|
+
!deps.canReuseSummaryGraph(session.sessionID, archived, roots)) {
|
|
51
|
+
issues.push('invalid-summary-graph');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (summaryNodeCount.count === 0)
|
|
55
|
+
issues.push('missing-summary-nodes');
|
|
56
|
+
return issues.length > 0 ? { sessionID: session.sessionID, issues } : undefined;
|
|
57
|
+
}
|
|
58
|
+
function needsLineageRefresh(session, readLineageChain) {
|
|
59
|
+
const chain = readLineageChain(session.sessionID);
|
|
60
|
+
const expectedRoot = chain[0]?.sessionID ?? session.sessionID;
|
|
61
|
+
const expectedDepth = Math.max(0, chain.length - 1);
|
|
62
|
+
return ((session.rootSessionID ?? session.sessionID) !== expectedRoot ||
|
|
63
|
+
(session.lineageDepth ?? 0) !== expectedDepth);
|
|
64
|
+
}
|
|
65
|
+
export function collectDoctorReport(sessions, sessionID, deps, readLineageChain) {
|
|
66
|
+
const sessionIDs = sessions.map((session) => session.sessionID);
|
|
67
|
+
const summarySessionsNeedingRebuild = sessions
|
|
68
|
+
.map((session) => diagnoseSummarySession(session, deps))
|
|
69
|
+
.filter((issue) => Boolean(issue));
|
|
70
|
+
const lineageSessionsNeedingRefresh = sessions
|
|
71
|
+
.filter((session) => needsLineageRefresh(session, readLineageChain))
|
|
72
|
+
.map((session) => session.sessionID);
|
|
73
|
+
const messageFtsExpected = countFtsExpected(sessions, deps);
|
|
74
|
+
const report = {
|
|
75
|
+
scope: sessionID ? `session:${sessionID}` : 'all',
|
|
76
|
+
checkedSessions: sessions.length,
|
|
77
|
+
summarySessionsNeedingRebuild,
|
|
78
|
+
lineageSessionsNeedingRefresh,
|
|
79
|
+
orphanSummaryEdges: deps.countScopedOrphanSummaryEdges(sessionIDs),
|
|
80
|
+
messageFts: {
|
|
81
|
+
expected: messageFtsExpected,
|
|
82
|
+
actual: deps.countScopedFtsRows('message_fts', sessionIDs),
|
|
83
|
+
},
|
|
84
|
+
summaryFts: {
|
|
85
|
+
expected: deps.readScopedSummaryRows(sessionIDs).length,
|
|
86
|
+
actual: deps.countScopedFtsRows('summary_fts', sessionIDs),
|
|
87
|
+
},
|
|
88
|
+
artifactFts: {
|
|
89
|
+
expected: deps.readScopedArtifactRows(sessionIDs).length,
|
|
90
|
+
actual: deps.countScopedFtsRows('artifact_fts', sessionIDs),
|
|
91
|
+
},
|
|
92
|
+
orphanArtifactBlobs: deps.readOrphanArtifactBlobRows().length,
|
|
93
|
+
status: 'clean',
|
|
94
|
+
};
|
|
95
|
+
report.status = hasDoctorIssues(report) ? 'issues-found' : 'clean';
|
|
96
|
+
return report;
|
|
97
|
+
}
|
|
98
|
+
export function hasDoctorIssues(report) {
|
|
99
|
+
return (report.summarySessionsNeedingRebuild.length > 0 ||
|
|
100
|
+
report.lineageSessionsNeedingRefresh.length > 0 ||
|
|
101
|
+
report.orphanSummaryEdges > 0 ||
|
|
102
|
+
report.messageFts.expected !== report.messageFts.actual ||
|
|
103
|
+
report.summaryFts.expected !== report.summaryFts.actual ||
|
|
104
|
+
report.artifactFts.expected !== report.artifactFts.actual ||
|
|
105
|
+
report.orphanArtifactBlobs > 0);
|
|
106
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SqlDatabaseLike } from './store-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Schema version management operations.
|
|
4
|
+
* Handles reading, writing, and validating the SQLite store schema version.
|
|
5
|
+
*/
|
|
6
|
+
export declare function readSchemaVersionSync(db: SqlDatabaseLike): number;
|
|
7
|
+
export declare function assertSupportedSchemaVersionSync(db: SqlDatabaseLike, maxVersion: number): void;
|
|
8
|
+
export declare function writeSchemaVersionSync(db: SqlDatabaseLike, version: number): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema version management operations.
|
|
3
|
+
* Handles reading, writing, and validating the SQLite store schema version.
|
|
4
|
+
*/
|
|
5
|
+
export function readSchemaVersionSync(db) {
|
|
6
|
+
const result = db.prepare('PRAGMA user_version').get();
|
|
7
|
+
if (!result || typeof result !== 'object')
|
|
8
|
+
return 0;
|
|
9
|
+
for (const value of Object.values(result)) {
|
|
10
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
export function assertSupportedSchemaVersionSync(db, maxVersion) {
|
|
16
|
+
const schemaVersion = readSchemaVersionSync(db);
|
|
17
|
+
if (schemaVersion <= maxVersion)
|
|
18
|
+
return;
|
|
19
|
+
throw new Error(`Unsupported store schema version: ${schemaVersion}. This build supports up to ${maxVersion}.`);
|
|
20
|
+
}
|
|
21
|
+
export function writeSchemaVersionSync(db, version) {
|
|
22
|
+
db.exec(`PRAGMA user_version = ${Math.max(0, Math.trunc(version))}`);
|
|
23
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { SqlDatabaseLike } from './store-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Session read operations.
|
|
4
|
+
* Handles reading sessions, messages, parts, artifacts from the store.
|
|
5
|
+
*/
|
|
6
|
+
export type SessionRow = {
|
|
7
|
+
session_id: string;
|
|
8
|
+
title: string | null;
|
|
9
|
+
parent_session_id: string | null;
|
|
10
|
+
root_session_id: string | null;
|
|
11
|
+
lineage_depth: number | null;
|
|
12
|
+
session_directory: string | null;
|
|
13
|
+
worktree_key: string | null;
|
|
14
|
+
pinned: number;
|
|
15
|
+
pin_reason: string | null;
|
|
16
|
+
deleted: number;
|
|
17
|
+
updated_at: number;
|
|
18
|
+
created_at: number;
|
|
19
|
+
event_count: number;
|
|
20
|
+
};
|
|
21
|
+
export type MessageRow = {
|
|
22
|
+
session_id: string;
|
|
23
|
+
message_id: string;
|
|
24
|
+
role: string;
|
|
25
|
+
created_at: number;
|
|
26
|
+
};
|
|
27
|
+
export type PartRow = {
|
|
28
|
+
session_id: string;
|
|
29
|
+
message_id: string;
|
|
30
|
+
part_id: string;
|
|
31
|
+
part_type: string;
|
|
32
|
+
sort_key: number;
|
|
33
|
+
state_json: string;
|
|
34
|
+
created_at: number;
|
|
35
|
+
};
|
|
36
|
+
export type ArtifactRow = {
|
|
37
|
+
artifact_id: string;
|
|
38
|
+
session_id: string;
|
|
39
|
+
message_id: string;
|
|
40
|
+
part_id: string;
|
|
41
|
+
artifact_kind: string;
|
|
42
|
+
field_name: string;
|
|
43
|
+
content_hash: string | null;
|
|
44
|
+
preview_text: string;
|
|
45
|
+
metadata_json: string;
|
|
46
|
+
char_count: number;
|
|
47
|
+
created_at: number;
|
|
48
|
+
};
|
|
49
|
+
export type ArtifactBlobRow = {
|
|
50
|
+
content_hash: string;
|
|
51
|
+
content_text: string;
|
|
52
|
+
char_count: number;
|
|
53
|
+
created_at: number;
|
|
54
|
+
};
|
|
55
|
+
export type SummaryNodeRow = {
|
|
56
|
+
node_id: string;
|
|
57
|
+
session_id: string;
|
|
58
|
+
level: number;
|
|
59
|
+
slot: number;
|
|
60
|
+
archived_message_ids_json: string;
|
|
61
|
+
summary_text: string;
|
|
62
|
+
created_at: number;
|
|
63
|
+
};
|
|
64
|
+
export type SummaryEdgeRow = {
|
|
65
|
+
session_id: string;
|
|
66
|
+
parent_id: string;
|
|
67
|
+
child_id: string;
|
|
68
|
+
child_position: number;
|
|
69
|
+
};
|
|
70
|
+
export type SummaryStateRow = {
|
|
71
|
+
session_id: string;
|
|
72
|
+
archived_count: number;
|
|
73
|
+
latest_message_created: number;
|
|
74
|
+
archived_signature: string;
|
|
75
|
+
root_node_ids_json: string;
|
|
76
|
+
updated_at: number;
|
|
77
|
+
};
|
|
78
|
+
export declare function readSessionHeader(db: SqlDatabaseLike, sessionID: string): SessionRow | undefined;
|
|
79
|
+
export declare function readAllSessions(db: SqlDatabaseLike): SessionRow[];
|
|
80
|
+
export declare function readChildSessions(db: SqlDatabaseLike, parentSessionID: string): SessionRow[];
|
|
81
|
+
export declare function readLineageChain(db: SqlDatabaseLike, sessionID: string): SessionRow[];
|
|
82
|
+
export declare function readMessagesForSession(db: SqlDatabaseLike, sessionID: string): MessageRow[];
|
|
83
|
+
export declare function readPartsForSession(db: SqlDatabaseLike, sessionID: string): PartRow[];
|
|
84
|
+
export declare function readArtifactsForSession(db: SqlDatabaseLike, sessionID: string): ArtifactRow[];
|
|
85
|
+
export declare function readArtifact(db: SqlDatabaseLike, artifactID: string): ArtifactRow | undefined;
|
|
86
|
+
export declare function readArtifactBlob(db: SqlDatabaseLike, contentHash: string): ArtifactBlobRow | undefined;
|
|
87
|
+
export declare function readOrphanArtifactBlobRows(db: SqlDatabaseLike): ArtifactBlobRow[];
|
|
88
|
+
export declare function readLatestSessionID(db: SqlDatabaseLike): string | undefined;
|
|
89
|
+
export declare function readSessionStats(db: SqlDatabaseLike): {
|
|
90
|
+
sessionCount: number;
|
|
91
|
+
messageCount: number;
|
|
92
|
+
artifactCount: number;
|
|
93
|
+
summaryNodeCount: number;
|
|
94
|
+
blobCount: number;
|
|
95
|
+
orphanBlobCount: number;
|
|
96
|
+
orphanBlobChars: number;
|
|
97
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export function readSessionHeader(db, sessionID) {
|
|
2
|
+
return db.prepare('SELECT * FROM sessions WHERE session_id = ?').get(sessionID);
|
|
3
|
+
}
|
|
4
|
+
export function readAllSessions(db) {
|
|
5
|
+
return db.prepare('SELECT * FROM sessions ORDER BY updated_at DESC').all();
|
|
6
|
+
}
|
|
7
|
+
export function readChildSessions(db, parentSessionID) {
|
|
8
|
+
return db
|
|
9
|
+
.prepare('SELECT * FROM sessions WHERE parent_session_id = ? ORDER BY updated_at DESC')
|
|
10
|
+
.all(parentSessionID);
|
|
11
|
+
}
|
|
12
|
+
export function readLineageChain(db, sessionID) {
|
|
13
|
+
const chain = [];
|
|
14
|
+
let current = readSessionHeader(db, sessionID);
|
|
15
|
+
while (current) {
|
|
16
|
+
chain.unshift(current);
|
|
17
|
+
if (!current.parent_session_id)
|
|
18
|
+
break;
|
|
19
|
+
current = readSessionHeader(db, current.parent_session_id);
|
|
20
|
+
}
|
|
21
|
+
return chain;
|
|
22
|
+
}
|
|
23
|
+
export function readMessagesForSession(db, sessionID) {
|
|
24
|
+
return db
|
|
25
|
+
.prepare('SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC')
|
|
26
|
+
.all(sessionID);
|
|
27
|
+
}
|
|
28
|
+
export function readPartsForSession(db, sessionID) {
|
|
29
|
+
return db
|
|
30
|
+
.prepare('SELECT * FROM parts WHERE session_id = ? ORDER BY message_id ASC, sort_key ASC')
|
|
31
|
+
.all(sessionID);
|
|
32
|
+
}
|
|
33
|
+
export function readArtifactsForSession(db, sessionID) {
|
|
34
|
+
return db
|
|
35
|
+
.prepare('SELECT * FROM artifacts WHERE session_id = ? ORDER BY created_at DESC')
|
|
36
|
+
.all(sessionID);
|
|
37
|
+
}
|
|
38
|
+
export function readArtifact(db, artifactID) {
|
|
39
|
+
return db.prepare('SELECT * FROM artifacts WHERE artifact_id = ?').get(artifactID);
|
|
40
|
+
}
|
|
41
|
+
export function readArtifactBlob(db, contentHash) {
|
|
42
|
+
return db.prepare('SELECT * FROM artifact_blobs WHERE content_hash = ?').get(contentHash);
|
|
43
|
+
}
|
|
44
|
+
export function readOrphanArtifactBlobRows(db) {
|
|
45
|
+
return db
|
|
46
|
+
.prepare(`SELECT b.* FROM artifact_blobs b
|
|
47
|
+
WHERE NOT EXISTS (
|
|
48
|
+
SELECT 1 FROM artifacts a WHERE a.content_hash = b.content_hash
|
|
49
|
+
)
|
|
50
|
+
ORDER BY b.created_at ASC`)
|
|
51
|
+
.all();
|
|
52
|
+
}
|
|
53
|
+
export function readLatestSessionID(db) {
|
|
54
|
+
const row = db
|
|
55
|
+
.prepare('SELECT session_id FROM sessions ORDER BY updated_at DESC LIMIT 1')
|
|
56
|
+
.get();
|
|
57
|
+
return row?.session_id;
|
|
58
|
+
}
|
|
59
|
+
export function readSessionStats(db) {
|
|
60
|
+
const sessions = db.prepare('SELECT COUNT(*) AS count FROM sessions').get();
|
|
61
|
+
const messages = db.prepare('SELECT COUNT(*) AS count FROM messages').get();
|
|
62
|
+
const artifacts = db.prepare('SELECT COUNT(*) AS count FROM artifacts').get();
|
|
63
|
+
const summaryNodes = db.prepare('SELECT COUNT(*) AS count FROM summary_nodes').get();
|
|
64
|
+
const blobs = db
|
|
65
|
+
.prepare(`SELECT COUNT(*) AS count, COALESCE(SUM(char_count), 0) AS chars
|
|
66
|
+
FROM artifact_blobs b
|
|
67
|
+
WHERE NOT EXISTS (
|
|
68
|
+
SELECT 1 FROM artifacts a WHERE a.content_hash = b.content_hash
|
|
69
|
+
)`)
|
|
70
|
+
.get();
|
|
71
|
+
return {
|
|
72
|
+
sessionCount: sessions.count,
|
|
73
|
+
messageCount: messages.count,
|
|
74
|
+
artifactCount: artifacts.count,
|
|
75
|
+
summaryNodeCount: summaryNodes.count,
|
|
76
|
+
blobCount: blobs.count,
|
|
77
|
+
orphanBlobCount: blobs.count,
|
|
78
|
+
orphanBlobChars: blobs.chars,
|
|
79
|
+
};
|
|
80
|
+
}
|
package/dist/store.d.ts
CHANGED
|
@@ -95,11 +95,17 @@ export declare class SqliteLcmStore {
|
|
|
95
95
|
private readonly workspaceDirectory;
|
|
96
96
|
private db?;
|
|
97
97
|
private dbReadyPromise?;
|
|
98
|
+
private deferredInitTimer?;
|
|
99
|
+
private deferredInitPromise?;
|
|
100
|
+
private deferredInitRequested;
|
|
101
|
+
private activeOperationCount;
|
|
98
102
|
private readonly pendingPartUpdates;
|
|
99
103
|
private pendingPartUpdateTimer?;
|
|
100
104
|
private pendingPartUpdateFlushPromise?;
|
|
101
105
|
constructor(projectDir: string, options: OpencodeLcmOptions);
|
|
102
106
|
init(): Promise<void>;
|
|
107
|
+
private withStoreActivity;
|
|
108
|
+
private waitForDeferredInitIfRunning;
|
|
103
109
|
private prepareForRead;
|
|
104
110
|
private scheduleDeferredPartUpdateFlush;
|
|
105
111
|
private clearDeferredPartUpdateTimer;
|
|
@@ -111,6 +117,9 @@ export declare class SqliteLcmStore {
|
|
|
111
117
|
private ensureDbReady;
|
|
112
118
|
private openAndInitializeDb;
|
|
113
119
|
private deferredInitCompleted;
|
|
120
|
+
private runDeferredInit;
|
|
121
|
+
private scheduleDeferredInit;
|
|
122
|
+
private ensureDeferredInitComplete;
|
|
114
123
|
private readSchemaVersionSync;
|
|
115
124
|
private assertSupportedSchemaVersionSync;
|
|
116
125
|
private writeSchemaVersionSync;
|
|
@@ -239,6 +248,8 @@ export declare class SqliteLcmStore {
|
|
|
239
248
|
buildCompactionContext(sessionID: string): Promise<string | undefined>;
|
|
240
249
|
transformMessages(messages: ConversationMessage[]): Promise<boolean>;
|
|
241
250
|
systemHint(): string | undefined;
|
|
251
|
+
private sanitizeSessionMessages;
|
|
252
|
+
private shouldSkipMalformedCapturedEvent;
|
|
242
253
|
private buildAutomaticRetrievalContext;
|
|
243
254
|
private buildAutomaticRetrievalQuery;
|
|
244
255
|
private buildAutomaticRetrievalQueries;
|
|
@@ -289,6 +300,7 @@ export declare class SqliteLcmStore {
|
|
|
289
300
|
private resolveLineageSync;
|
|
290
301
|
private applyEvent;
|
|
291
302
|
private getResumeSync;
|
|
303
|
+
private materializeSessionRow;
|
|
292
304
|
private readSessionHeaderSync;
|
|
293
305
|
private clearSessionDataSync;
|
|
294
306
|
private readChildSessionsSync;
|