ocwatch 0.3.0 → 0.5.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 (33) hide show
  1. package/README.md +6 -0
  2. package/package.json +1 -1
  3. package/src/client/dist/assets/index-BIu7r5_5.css +1 -0
  4. package/src/client/dist/assets/index-CzAaOuyw.js +50 -0
  5. package/src/client/dist/index.html +2 -2
  6. package/src/server/__tests__/helpers/testDb.ts +220 -0
  7. package/src/server/index.ts +20 -5
  8. package/src/server/logic/activityLogic.ts +260 -0
  9. package/src/server/logic/index.ts +2 -0
  10. package/src/server/logic/sessionLogic.ts +107 -0
  11. package/src/server/routes/parts.ts +9 -7
  12. package/src/server/routes/poll.ts +34 -45
  13. package/src/server/routes/projects.ts +4 -4
  14. package/src/server/routes/sessions.ts +107 -68
  15. package/src/server/routes/sse.ts +10 -4
  16. package/src/server/services/parsing.ts +211 -0
  17. package/src/server/services/pollService.ts +292 -114
  18. package/src/server/services/sessionService.ts +178 -106
  19. package/src/server/storage/db.ts +71 -0
  20. package/src/server/storage/index.ts +22 -0
  21. package/src/server/storage/queries.ts +325 -0
  22. package/src/server/utils/projectResolver.ts +2 -2
  23. package/src/server/utils/sessionStatus.ts +4 -70
  24. package/src/server/watcher.ts +187 -82
  25. package/src/shared/constants.ts +1 -0
  26. package/src/shared/types/index.ts +39 -5
  27. package/src/shared/utils/formatTime.ts +3 -1
  28. package/src/client/dist/assets/index-27vUxwIP.css +0 -1
  29. package/src/client/dist/assets/index-B1aj6-ff.js +0 -41
  30. package/src/server/storage/messageParser.ts +0 -169
  31. package/src/server/storage/partParser.ts +0 -532
  32. package/src/server/storage/sessionParser.ts +0 -180
  33. package/src/shared/utils/burstGrouping.ts +0 -99
@@ -1,83 +1,151 @@
1
- import type {
2
- SessionMetadata,
3
- MessageMeta,
4
- ActivitySession,
5
- TreeNode,
6
- TreeEdge,
7
- SessionTree,
8
- AgentPhase,
1
+ import type {
2
+ SessionMetadata,
3
+ MessageMeta,
4
+ ActivitySession,
5
+ TreeNode,
6
+ TreeEdge,
7
+ SessionTree,
9
8
  PartMeta,
10
9
  SessionStatus,
10
+ ToolCallSummary,
11
11
  } from "../../shared/types";
12
12
  import { MAX_RECURSION_DEPTH } from "../../shared/constants";
13
- import { listMessages } from "../storage/messageParser";
14
- import {
15
- getPartsForSession,
13
+ import {
14
+ formatCurrentAction,
15
+ isPendingToolCall,
16
+ generateActivityMessage,
16
17
  getSessionActivityState,
17
- isPendingToolCall,
18
- getToolCallsForSession,
19
- generateActivityMessage
20
- } from "../storage/partParser";
21
- import { getSessionStatus, getSessionStatusInfo, getStatusFromTimestamp, type SessionStatusInfo } from "../utils/sessionStatus";
22
- import { deriveActivityType } from "../storage/partParser";
23
-
24
- export function isAssistantFinished(messages: MessageMeta[]): boolean {
25
- if (messages.length === 0) {
26
- return false;
18
+ deriveActivityType,
19
+ detectAgentPhases,
20
+ isAssistantFinished,
21
+ getSessionStatusInfo,
22
+ type SessionStatusInfo,
23
+ } from "../logic";
24
+ import {
25
+ querySessionChildren,
26
+ queryMessages,
27
+ queryParts,
28
+ } from "../storage/queries";
29
+ import { getStatusFromTimestamp } from "../utils/sessionStatus";
30
+ import {
31
+ toSessionMetadata as parseSessionRow,
32
+ toMessageMeta as parseMessageRow,
33
+ toPartMeta as parsePartRow,
34
+ getLatestAssistantMessage,
35
+ getMostRecentPendingPart,
36
+ } from "./parsing";
37
+
38
+ export { detectAgentPhases, isAssistantFinished };
39
+
40
+ /** Max messages to load per session for hierarchy building (effectively unlimited) */
41
+ const MAX_MESSAGE_QUERY_LIMIT = 100_000;
42
+
43
+ interface SessionContext {
44
+ allowedSessionIds: Set<string>;
45
+ sessionById: Map<string, SessionMetadata>;
46
+ messagesBySession: Map<string, MessageMeta[]>;
47
+ partsBySession: Map<string, PartMeta[]>;
48
+ childrenBySession: Map<string, SessionMetadata[]>;
49
+ }
50
+
51
+
52
+ function createSessionContext(allSessions: SessionMetadata[]): SessionContext {
53
+ const sessionById = new Map<string, SessionMetadata>();
54
+ for (const session of allSessions) {
55
+ sessionById.set(session.id, session);
27
56
  }
28
57
 
29
- const lastMessage = messages.reduce((latest, current) =>
30
- current.createdAt.getTime() > latest.createdAt.getTime() ? current : latest
31
- );
58
+ return {
59
+ allowedSessionIds: new Set(allSessions.map((session) => session.id)),
60
+ sessionById,
61
+ messagesBySession: new Map<string, MessageMeta[]>(),
62
+ partsBySession: new Map<string, PartMeta[]>(),
63
+ childrenBySession: new Map<string, SessionMetadata[]>(),
64
+ };
65
+ }
66
+
67
+ function getSessionFromContext(sessionId: string, context: SessionContext): SessionMetadata | undefined {
68
+ return context.sessionById.get(sessionId);
69
+ }
70
+
71
+ function getSessionMessages(sessionId: string, context: SessionContext): MessageMeta[] {
72
+ const cached = context.messagesBySession.get(sessionId);
73
+ if (cached) {
74
+ return cached;
75
+ }
32
76
 
33
- return lastMessage.role === "assistant" && lastMessage.finish === "stop";
77
+ const messages = queryMessages(sessionId, MAX_MESSAGE_QUERY_LIMIT).map(parseMessageRow);
78
+ context.messagesBySession.set(sessionId, messages);
79
+ return messages;
34
80
  }
35
81
 
36
- function getLatestAssistantMessage(messages: MessageMeta[]): MessageMeta | undefined {
37
- return messages
38
- .filter((message) => message.role === "assistant")
39
- .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())[0];
82
+ function getSessionParts(sessionId: string, context: SessionContext): PartMeta[] {
83
+ const cached = context.partsBySession.get(sessionId);
84
+ if (cached) {
85
+ return cached;
86
+ }
87
+
88
+ const parts = queryParts(sessionId).map(parsePartRow);
89
+ context.partsBySession.set(sessionId, parts);
90
+ return parts;
40
91
  }
41
92
 
42
- function getMostRecentPendingPart(parts: PartMeta[]): PartMeta | undefined {
43
- return parts
44
- .filter((part) => isPendingToolCall(part))
45
- .sort((a, b) => (b.startedAt?.getTime() || 0) - (a.startedAt?.getTime() || 0))[0];
93
+ function getSessionChildren(sessionId: string, context: SessionContext): SessionMetadata[] {
94
+ const cached = context.childrenBySession.get(sessionId);
95
+ if (cached) {
96
+ return cached;
97
+ }
98
+
99
+ const children = querySessionChildren(sessionId)
100
+ .map(parseSessionRow)
101
+ .filter((child) => context.allowedSessionIds.has(child.id));
102
+
103
+ for (const child of children) {
104
+ if (!context.sessionById.has(child.id)) {
105
+ context.sessionById.set(child.id, child);
106
+ }
107
+ }
108
+
109
+ context.childrenBySession.set(sessionId, children);
110
+ return children;
46
111
  }
47
112
 
113
+
48
114
  function countBlockingChildren(statuses: SessionStatus[]): number {
49
115
  return statuses.filter((status) => status === "working" || status === "waiting").length;
50
116
  }
51
117
 
52
- export function detectAgentPhases(messages: MessageMeta[]): AgentPhase[] {
53
- const sorted = messages
54
- .filter(m => m.role === 'assistant' && m.agent)
55
- .sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
56
-
57
- if (sorted.length === 0) return [];
58
-
59
- const phases: AgentPhase[] = [];
60
- let currentPhase: AgentPhase | null = null;
61
-
62
- for (const msg of sorted) {
63
- if (!currentPhase || currentPhase.agent !== msg.agent) {
64
- if (currentPhase) phases.push(currentPhase);
65
- currentPhase = {
66
- agent: msg.agent!,
67
- startTime: msg.createdAt,
68
- endTime: msg.createdAt,
69
- tokens: msg.tokens || 0,
70
- messageCount: 1,
118
+
119
+ function buildToolCalls(parts: PartMeta[], messageAgent: Map<string, string>): ToolCallSummary[] {
120
+ const toolCalls = parts
121
+ .filter((part) => part.type === "tool" && part.tool)
122
+ .map((part): ToolCallSummary => {
123
+ let state: "pending" | "complete" | "error" = "complete";
124
+ if (isPendingToolCall(part)) {
125
+ state = "pending";
126
+ } else if (part.state === "error" || part.state === "failed") {
127
+ state = "error";
128
+ }
129
+
130
+ return {
131
+ id: part.id,
132
+ name: part.tool || "unknown",
133
+ state,
134
+ summary: formatCurrentAction(part) || part.tool || "Unknown tool",
135
+ input: part.input || {},
136
+ error: part.error,
137
+ timestamp: (part.completedAt || part.startedAt)?.toISOString() || "",
138
+ agentName: messageAgent.get(part.messageID) || "unknown",
71
139
  };
72
- } else {
73
- currentPhase.endTime = msg.createdAt;
74
- currentPhase.tokens += msg.tokens || 0;
75
- currentPhase.messageCount++;
76
- }
77
- }
78
- if (currentPhase) phases.push(currentPhase);
79
-
80
- return phases;
140
+ });
141
+
142
+ toolCalls.sort((a, b) => {
143
+ const timeA = new Date(a.timestamp).getTime();
144
+ const timeB = new Date(b.timestamp).getTime();
145
+ return timeB - timeA;
146
+ });
147
+
148
+ return toolCalls.slice(0, 50);
81
149
  }
82
150
 
83
151
  export function buildAgentHierarchy(messages: MessageMeta[]): Record<string, string[]> {
@@ -107,6 +175,7 @@ export async function buildSessionTree(
107
175
  const nodes: TreeNode[] = [];
108
176
  const edges: TreeEdge[] = [];
109
177
  const visited = new Set<string>();
178
+ const context = createSessionContext(allSessions);
110
179
 
111
180
  async function processSession(sessionID: string, depth = 0) {
112
181
  if (depth > MAX_RECURSION_DEPTH) {
@@ -118,15 +187,22 @@ export async function buildSessionTree(
118
187
  }
119
188
  visited.add(sessionID);
120
189
 
121
- const session = allSessions.find((s) => s.id === sessionID);
190
+ const session = getSessionFromContext(sessionID, context);
122
191
  if (!session) {
123
192
  return;
124
193
  }
125
194
 
126
- const messages = await listMessages(sessionID);
195
+ const messages = getSessionMessages(sessionID, context);
127
196
  const lastAssistantFinished = isAssistantFinished(messages);
128
197
  const isSubagent = !!session.parentID;
129
- const status = getSessionStatus(messages, false, undefined, undefined, lastAssistantFinished, isSubagent);
198
+ const status = getSessionStatusInfo(
199
+ messages,
200
+ false,
201
+ undefined,
202
+ undefined,
203
+ lastAssistantFinished,
204
+ isSubagent
205
+ ).status;
130
206
 
131
207
  const lastMessage = messages.sort(
132
208
  (a, b) => b.createdAt.getTime() - a.createdAt.getTime()
@@ -150,7 +226,7 @@ export async function buildSessionTree(
150
226
  await processSession(session.parentID, depth + 1);
151
227
  }
152
228
 
153
- const children = allSessions.filter((s) => s.parentID === sessionID);
229
+ const children = getSessionChildren(sessionID, context);
154
230
  await Promise.all(
155
231
  children.map((child) => {
156
232
  edges.push({
@@ -173,15 +249,15 @@ export async function getSessionHierarchy(
173
249
  ): Promise<ActivitySession[]> {
174
250
  const result: ActivitySession[] = [];
175
251
  const processed = new Set<string>();
252
+ const context = createSessionContext(allSessions);
176
253
 
177
- const rootSession = allSessions.find((s) => s.id === rootSessionId);
254
+ const rootSession = getSessionFromContext(rootSessionId, context);
178
255
  if (!rootSession) return result;
179
256
 
180
- const rootMessages = await listMessages(rootSessionId);
257
+ const rootMessages = getSessionMessages(rootSessionId, context);
181
258
  const phases = detectAgentPhases(rootMessages);
182
- const childSessions = allSessions.filter((s) => s.parentID === rootSessionId);
259
+ const childSessions = getSessionChildren(rootSessionId, context);
183
260
 
184
- // Build messageAgent map for tool calls
185
261
  const messageAgent = new Map<string, string>();
186
262
  for (const msg of rootMessages) {
187
263
  if (msg.agent) {
@@ -191,30 +267,28 @@ export async function getSessionHierarchy(
191
267
 
192
268
  if (phases.length <= 1) {
193
269
  const latestAssistantMsg = getLatestAssistantMessage(rootMessages);
194
-
270
+
195
271
  const totalTokens = rootMessages
196
272
  .filter((m) => m.tokens !== undefined)
197
273
  .reduce((sum, m) => sum + (m.tokens || 0), 0);
198
274
 
199
- const parts = await getPartsForSession(rootSessionId);
275
+ const parts = getSessionParts(rootSessionId, context);
200
276
  const activityState = getSessionActivityState(parts);
201
-
277
+
202
278
  const childStatuses = await Promise.all(
203
279
  childSessions.map(async (child) => {
204
- const [childMessages, childParts] = await Promise.all([
205
- listMessages(child.id),
206
- getPartsForSession(child.id),
207
- ]);
280
+ const childMessages = getSessionMessages(child.id, context);
281
+ const childParts = getSessionParts(child.id, context);
208
282
  const childActivityState = getSessionActivityState(childParts);
209
283
  const childLastAssistantFinished = isAssistantFinished(childMessages);
210
- return getSessionStatus(
284
+ return getSessionStatusInfo(
211
285
  childMessages,
212
286
  childActivityState.hasPendingToolCall,
213
287
  childActivityState.lastToolCompletedAt || undefined,
214
288
  undefined,
215
289
  childLastAssistantFinished,
216
290
  true
217
- );
291
+ ).status;
218
292
  })
219
293
  );
220
294
  const workingChildCount = countBlockingChildren(childStatuses);
@@ -240,7 +314,7 @@ export async function getSessionHierarchy(
240
314
  );
241
315
  const activityType = deriveActivityType(activityState, rootLastAssistantFinished, false, status, statusInfo.waitingReason);
242
316
 
243
- const toolCalls = await getToolCallsForSession(rootSessionId, messageAgent, undefined, parts);
317
+ const toolCalls = buildToolCalls(parts, messageAgent);
244
318
 
245
319
  result.push({
246
320
  id: rootSession.id,
@@ -262,11 +336,11 @@ export async function getSessionHierarchy(
262
336
  processed.add(rootSessionId);
263
337
 
264
338
  for (const child of childSessions) {
265
- await processChildSession(child.id, rootSession.id, allSessions, result, processed, 1);
339
+ await processChildSession(child.id, rootSession.id, allSessions, result, processed, 1, context);
266
340
  }
267
341
  } else {
268
- const rootParts = await getPartsForSession(rootSessionId);
269
- const allToolCalls = await getToolCallsForSession(rootSessionId, messageAgent, undefined, rootParts);
342
+ const rootParts = getSessionParts(rootSessionId, context);
343
+ const allToolCalls = buildToolCalls(rootParts, messageAgent);
270
344
 
271
345
  for (let i = 0; i < phases.length; i++) {
272
346
  const phase = phases[i];
@@ -274,31 +348,29 @@ export async function getSessionHierarchy(
274
348
  const virtualId = `${rootSessionId}-phase-${i}-${phase.agent}`;
275
349
 
276
350
  const phaseMessages = rootMessages.filter(
277
- m => m.role === 'assistant' && m.agent === phase.agent &&
351
+ (m) => m.role === "assistant" && m.agent === phase.agent &&
278
352
  m.createdAt >= phase.startTime && m.createdAt <= phase.endTime
279
353
  );
280
354
  const latestPhaseMsg = phaseMessages[phaseMessages.length - 1];
281
355
 
282
- const phaseChildren = childSessions.filter(child =>
356
+ const phaseChildren = childSessions.filter((child) =>
283
357
  child.createdAt >= phase.startTime && child.createdAt < nextPhaseStart
284
358
  );
285
359
 
286
360
  const childStatuses = await Promise.all(
287
361
  phaseChildren.map(async (child) => {
288
- const [childMessages, childParts] = await Promise.all([
289
- listMessages(child.id),
290
- getPartsForSession(child.id),
291
- ]);
362
+ const childMessages = getSessionMessages(child.id, context);
363
+ const childParts = getSessionParts(child.id, context);
292
364
  const childActivityState = getSessionActivityState(childParts);
293
365
  const childLastAssistantFinished = isAssistantFinished(childMessages);
294
- return getSessionStatus(
366
+ return getSessionStatusInfo(
295
367
  childMessages,
296
368
  childActivityState.hasPendingToolCall,
297
369
  childActivityState.lastToolCompletedAt || undefined,
298
370
  undefined,
299
371
  childLastAssistantFinished,
300
372
  true
301
- );
373
+ ).status;
302
374
  })
303
375
  );
304
376
  const workingChildCount = countBlockingChildren(childStatuses);
@@ -341,7 +413,7 @@ export async function getSessionHierarchy(
341
413
  ? deriveActivityType(phaseActivityState, phaseLastAssistantFinished, false, status, statusInfo.waitingReason)
342
414
  : "idle";
343
415
 
344
- const toolCalls = allToolCalls.filter(tc => tc.agentName === phase.agent);
416
+ const toolCalls = allToolCalls.filter((tc) => tc.agentName === phase.agent);
345
417
 
346
418
  result.push({
347
419
  id: virtualId,
@@ -362,7 +434,7 @@ export async function getSessionHierarchy(
362
434
  });
363
435
 
364
436
  for (const child of phaseChildren) {
365
- await processChildSession(child.id, virtualId, allSessions, result, processed, 1);
437
+ await processChildSession(child.id, virtualId, allSessions, result, processed, 1, context);
366
438
  }
367
439
  }
368
440
  }
@@ -376,7 +448,8 @@ export async function processChildSession(
376
448
  allSessions: SessionMetadata[],
377
449
  result: ActivitySession[],
378
450
  processed: Set<string>,
379
- depth = 0
451
+ depth = 0,
452
+ contextArg?: SessionContext
380
453
  ): Promise<void> {
381
454
  if (depth > MAX_RECURSION_DEPTH) {
382
455
  console.warn(`Max recursion depth reached for child session ${sessionId}`);
@@ -385,10 +458,11 @@ export async function processChildSession(
385
458
  if (processed.has(sessionId)) return;
386
459
  processed.add(sessionId);
387
460
 
388
- const session = allSessions.find((s) => s.id === sessionId);
461
+ const context = contextArg ?? createSessionContext(allSessions);
462
+ const session = getSessionFromContext(sessionId, context);
389
463
  if (!session) return;
390
464
 
391
- const messages = await listMessages(sessionId);
465
+ const messages = getSessionMessages(sessionId, context);
392
466
  const latestAssistantMsg = getLatestAssistantMessage(messages);
393
467
 
394
468
  const totalTokens = messages
@@ -402,26 +476,24 @@ export async function processChildSession(
402
476
  }
403
477
  }
404
478
 
405
- const parts = await getPartsForSession(sessionId);
479
+ const parts = getSessionParts(sessionId, context);
406
480
  const activityState = getSessionActivityState(parts);
407
-
408
- const childSessions = allSessions.filter((s) => s.parentID === sessionId);
481
+
482
+ const childSessions = getSessionChildren(sessionId, context);
409
483
  const childStatuses = await Promise.all(
410
484
  childSessions.map(async (child) => {
411
- const [childMessages, childParts] = await Promise.all([
412
- listMessages(child.id),
413
- getPartsForSession(child.id),
414
- ]);
485
+ const childMessages = getSessionMessages(child.id, context);
486
+ const childParts = getSessionParts(child.id, context);
415
487
  const childActivityState = getSessionActivityState(childParts);
416
488
  const childLastAssistantFinished = isAssistantFinished(childMessages);
417
- return getSessionStatus(
489
+ return getSessionStatusInfo(
418
490
  childMessages,
419
491
  childActivityState.hasPendingToolCall,
420
492
  childActivityState.lastToolCompletedAt || undefined,
421
493
  undefined,
422
494
  childLastAssistantFinished,
423
495
  true
424
- );
496
+ ).status;
425
497
  })
426
498
  );
427
499
  const workingChildCount = countBlockingChildren(childStatuses);
@@ -448,7 +520,7 @@ export async function processChildSession(
448
520
  );
449
521
  const activityType = deriveActivityType(activityState, lastAssistantFinished, true, status, statusInfo.waitingReason);
450
522
 
451
- const toolCalls = await getToolCallsForSession(sessionId, messageAgent, undefined, parts);
523
+ const toolCalls = buildToolCalls(parts, messageAgent);
452
524
 
453
525
  result.push({
454
526
  id: session.id,
@@ -470,7 +542,7 @@ export async function processChildSession(
470
542
 
471
543
  await Promise.all(
472
544
  childSessions.map((child) =>
473
- processChildSession(child.id, session.id, allSessions, result, processed, depth + 1)
545
+ processChildSession(child.id, session.id, allSessions, result, processed, depth + 1, context)
474
546
  )
475
547
  );
476
548
  }
@@ -0,0 +1,71 @@
1
+ import { Database } from "bun:sqlite";
2
+ import { existsSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+
6
+ const SQLITE_BUSY_TIMEOUT_MS = 5000;
7
+ const SQLITE_CACHE_SIZE = -20000;
8
+
9
+ let dbSingleton: Database | null | undefined;
10
+
11
+ function getStorageRootPath(): string {
12
+ const xdgDataHome = process.env.XDG_DATA_HOME;
13
+ if (xdgDataHome) {
14
+ return xdgDataHome;
15
+ }
16
+
17
+ return join(homedir(), ".local", "share");
18
+ }
19
+
20
+ function getDbPath(): string {
21
+ return join(getStorageRootPath(), "opencode", "opencode.db");
22
+ }
23
+
24
+ function configureConnectionPragmas(db: Database): void {
25
+ db.query("PRAGMA busy_timeout = 5000;").run();
26
+ db.query("PRAGMA cache_size = -20000;").run();
27
+
28
+ try {
29
+ db.query("PRAGMA journal_mode = WAL;").run();
30
+ } catch (error) {
31
+ const reason = error instanceof Error ? error.message : String(error);
32
+ console.warn(`[storage/db] Failed to enforce WAL journal mode: ${reason}`);
33
+ }
34
+ }
35
+
36
+ export function checkDbExists(): boolean {
37
+ return existsSync(getDbPath());
38
+ }
39
+
40
+ export function getDb(): Database | null {
41
+ if (dbSingleton !== undefined) {
42
+ return dbSingleton;
43
+ }
44
+
45
+ if (!checkDbExists()) {
46
+ dbSingleton = null;
47
+ return dbSingleton;
48
+ }
49
+
50
+ try {
51
+ const db = new Database(getDbPath(), { readonly: true });
52
+ configureConnectionPragmas(db);
53
+ dbSingleton = db;
54
+ return dbSingleton;
55
+ } catch (error) {
56
+ const reason = error instanceof Error ? error.message : String(error);
57
+ console.warn(`[storage/db] Failed to open SQLite database: ${reason}`);
58
+ dbSingleton = null;
59
+ return dbSingleton;
60
+ }
61
+ }
62
+
63
+ export function closeDb(): void {
64
+ if (!dbSingleton) {
65
+ return;
66
+ }
67
+
68
+ dbSingleton.close();
69
+ dbSingleton = undefined;
70
+ }
71
+
@@ -0,0 +1,22 @@
1
+ export { checkDbExists, closeDb, getDb } from "./db";
2
+ export {
3
+ queryMaxTimestamp,
4
+ queryMessages,
5
+ queryPart,
6
+ queryParts,
7
+ queryProjects,
8
+ querySession,
9
+ querySessionChildren,
10
+ querySessions,
11
+ queryTodos,
12
+ listProjects,
13
+ listAllSessions,
14
+ } from "./queries";
15
+ export type {
16
+ DbMessageRow,
17
+ DbPartRow,
18
+ DbProjectRow,
19
+ DbSessionRow,
20
+ DbTodoRow,
21
+ } from "./queries";
22
+ export { parseBoulder, calculatePlanProgress } from "./boulderParser";