sidekick-shared 0.13.5 → 0.13.6

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/dist/index.d.ts CHANGED
@@ -25,7 +25,7 @@ export { readHistory } from './readers/history';
25
25
  export { readLatestHandoff } from './readers/handoff';
26
26
  export { readPlans, getLatestPlan, writePlans, getPlanAnalytics, readClaudeCodePlanFiles } from './readers/plans';
27
27
  export type { ReadPlansOptions, PlanAnalytics } from './readers/plans';
28
- export type { ProviderId, SessionProvider, SessionProviderBase, SessionFileStats, SessionFileInfo, SearchHit, ProjectFolderInfo, SessionReader } from './providers/types';
28
+ export type { ProviderId, SessionProvider, SessionProviderBase, SessionFileStats, SessionFileInfo, SearchHit, ProjectFolderInfo, SessionReader, ProviderRuntimeStatus } from './providers/types';
29
29
  export { detectProvider, getAllDetectedProviders } from './providers/detect';
30
30
  export { ClaudeCodeProvider } from './providers/claudeCode';
31
31
  export { OpenCodeProvider } from './providers/openCode';
@@ -47,6 +47,11 @@ function getOpenCodeDataDir() {
47
47
  const xdg = process.env.XDG_DATA_HOME;
48
48
  if (xdg)
49
49
  return path.join(xdg, 'opencode');
50
+ if (process.platform === 'darwin')
51
+ return path.join(os.homedir(), 'Library', 'Application Support', 'opencode');
52
+ if (process.platform === 'win32') {
53
+ return path.join(process.env.LOCALAPPDATA || process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Local'), 'opencode');
54
+ }
50
55
  return path.join(os.homedir(), '.local', 'share', 'opencode');
51
56
  }
52
57
  function getCodexHome() {
@@ -15,7 +15,7 @@
15
15
  *
16
16
  * @module providers/openCode
17
17
  */
18
- import type { SessionProviderBase, SessionReader, ProjectFolderInfo, SearchHit, SessionFileStats, ProviderId } from './types';
18
+ import type { SessionProviderBase, SessionReader, ProjectFolderInfo, SearchHit, SessionFileStats, ProviderId, ProviderRuntimeStatus } from './types';
19
19
  import type { TokenUsage, SubagentStats, ContextAttribution } from '../types/sessionEvent';
20
20
  /**
21
21
  * Session provider for OpenCode CLI.
@@ -28,10 +28,14 @@ export declare class OpenCodeProvider implements SessionProviderBase {
28
28
  readonly displayName = "OpenCode";
29
29
  private db;
30
30
  private dbInitialized;
31
+ private dbStatus;
31
32
  /** Cache of session metadata populated during listing */
32
33
  private sessionMetaCache;
33
34
  /** Lazy-initialize the database connection. */
34
35
  private ensureDb;
36
+ getRuntimeStatus(): ProviderRuntimeStatus;
37
+ canMonitorDirectory(dir: string): boolean;
38
+ private mapDbSessions;
35
39
  getSessionDirectory(workspacePath: string): string;
36
40
  discoverSessionDirectory(workspacePath: string): string | null;
37
41
  findActiveSession(workspacePath: string): string | null;
@@ -69,6 +69,12 @@ function getOpenCodeDataDir() {
69
69
  if (xdg) {
70
70
  return path.join(xdg, 'opencode');
71
71
  }
72
+ if (process.platform === 'darwin') {
73
+ return path.join(os.homedir(), 'Library', 'Application Support', 'opencode');
74
+ }
75
+ if (process.platform === 'win32') {
76
+ return path.join(process.env.LOCALAPPDATA || process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Local'), 'opencode');
77
+ }
72
78
  return path.join(os.homedir(), '.local', 'share', 'opencode');
73
79
  }
74
80
  /**
@@ -170,12 +176,18 @@ function extractParentIdFromDbMessage(row) {
170
176
  *
171
177
  * Tries DB first, then file-based project metadata, then git root commit hash.
172
178
  */
173
- function resolveProjectId(workspacePath, db) {
179
+ function resolveProjectId(workspacePath, db, runtimeStatus) {
174
180
  // Strategy 1: DB lookup
175
181
  if (db) {
176
182
  const dbProject = db.findProjectByWorktree(workspacePath);
177
183
  if (dbProject)
178
184
  return dbProject.id;
185
+ const byDirectory = db.findProjectBySessionDirectory(workspacePath);
186
+ if (byDirectory)
187
+ return byDirectory.id;
188
+ }
189
+ if (runtimeStatus.kind !== 'db_missing') {
190
+ return null;
179
191
  }
180
192
  // Strategy 2: scan project files to find matching path
181
193
  return resolveProjectIdFromFiles(workspacePath);
@@ -596,6 +608,7 @@ class OpenCodeProvider {
596
608
  displayName = 'OpenCode';
597
609
  db = null;
598
610
  dbInitialized = false;
611
+ dbStatus = { available: false, kind: 'db_missing' };
599
612
  /** Cache of session metadata populated during listing */
600
613
  sessionMetaCache = new Map();
601
614
  /** Lazy-initialize the database connection. */
@@ -605,15 +618,47 @@ class OpenCodeProvider {
605
618
  this.dbInitialized = true;
606
619
  const dataDir = getOpenCodeDataDir();
607
620
  const db = new openCodeDatabase_1.OpenCodeDatabase(dataDir);
608
- if (db.isAvailable() && db.open()) {
621
+ if (db.open()) {
609
622
  this.db = db;
610
623
  }
624
+ this.dbStatus = db.getRuntimeStatus();
611
625
  return this.db;
612
626
  }
627
+ getRuntimeStatus() {
628
+ const db = this.ensureDb();
629
+ if (db)
630
+ return db.getRuntimeStatus();
631
+ return this.dbStatus;
632
+ }
633
+ canMonitorDirectory(dir) {
634
+ if (fs.existsSync(dir))
635
+ return true;
636
+ const db = this.ensureDb();
637
+ if (!db)
638
+ return false;
639
+ const projectId = extractProjectIdFromDbPath(dir + path.sep + 'dummy.json') || path.basename(dir);
640
+ return projectId.length > 0 && db.hasProject(projectId);
641
+ }
642
+ mapDbSessions(projectId) {
643
+ const db = this.ensureDb();
644
+ if (!db)
645
+ return [];
646
+ const sessions = db.getSessionsForProject(projectId);
647
+ const dataDir = getOpenCodeDataDir();
648
+ return sessions.map(session => {
649
+ const syntheticPath = makeDbSessionPath(dataDir, projectId, session.id);
650
+ this.sessionMetaCache.set(syntheticPath, {
651
+ title: session.title,
652
+ timeUpdated: session.time_updated,
653
+ });
654
+ return syntheticPath;
655
+ });
656
+ }
613
657
  // --- Path resolution ---
614
658
  getSessionDirectory(workspacePath) {
615
659
  const db = this.ensureDb();
616
- const projectId = resolveProjectId(workspacePath, db);
660
+ const dbStatus = this.getRuntimeStatus();
661
+ const projectId = resolveProjectId(workspacePath, db, dbStatus);
617
662
  if (projectId) {
618
663
  // For DB sessions, return a synthetic directory path
619
664
  if (db) {
@@ -621,20 +666,26 @@ class OpenCodeProvider {
621
666
  }
622
667
  return path.join(getStorageDir(), 'session', projectId);
623
668
  }
669
+ if (dbStatus.kind !== 'db_missing') {
670
+ return path.join(getOpenCodeDataDir(), DB_SESSION_PREFIX);
671
+ }
624
672
  return path.join(getStorageDir(), 'session');
625
673
  }
626
674
  discoverSessionDirectory(workspacePath) {
627
675
  const db = this.ensureDb();
628
- const projectId = resolveProjectId(workspacePath, db);
676
+ const dbStatus = this.getRuntimeStatus();
677
+ const projectId = resolveProjectId(workspacePath, db, dbStatus);
629
678
  if (!projectId)
630
679
  return null;
631
680
  // DB: check if project has sessions
632
681
  if (db) {
633
- const sessions = db.getSessionsForProject(projectId);
634
- if (sessions.length > 0) {
682
+ if (db.hasProject(projectId)) {
635
683
  return path.join(getOpenCodeDataDir(), DB_SESSION_PREFIX, projectId);
636
684
  }
637
685
  }
686
+ if (dbStatus.kind !== 'db_missing') {
687
+ return null;
688
+ }
638
689
  // File fallback
639
690
  const dir = path.join(getStorageDir(), 'session', projectId);
640
691
  return fs.existsSync(dir) ? dir : null;
@@ -642,7 +693,8 @@ class OpenCodeProvider {
642
693
  // --- Session discovery ---
643
694
  findActiveSession(workspacePath) {
644
695
  const db = this.ensureDb();
645
- const projectId = resolveProjectId(workspacePath, db);
696
+ const dbStatus = this.getRuntimeStatus();
697
+ const projectId = resolveProjectId(workspacePath, db, dbStatus);
646
698
  if (!projectId)
647
699
  return null;
648
700
  // DB primary
@@ -657,6 +709,9 @@ class OpenCodeProvider {
657
709
  return syntheticPath;
658
710
  }
659
711
  }
712
+ if (dbStatus.kind !== 'db_missing') {
713
+ return null;
714
+ }
660
715
  // File fallback
661
716
  return this.findActiveSessionFromFiles(projectId);
662
717
  }
@@ -717,23 +772,16 @@ class OpenCodeProvider {
717
772
  }
718
773
  findAllSessions(workspacePath) {
719
774
  const db = this.ensureDb();
720
- const projectId = resolveProjectId(workspacePath, db);
775
+ const dbStatus = this.getRuntimeStatus();
776
+ const projectId = resolveProjectId(workspacePath, db, dbStatus);
721
777
  if (!projectId)
722
778
  return [];
723
779
  // DB primary
724
780
  if (db) {
725
- const sessions = db.getSessionsForProject(projectId);
726
- if (sessions.length > 0) {
727
- const dataDir = getOpenCodeDataDir();
728
- return sessions.map(s => {
729
- const syntheticPath = makeDbSessionPath(dataDir, projectId, s.id);
730
- this.sessionMetaCache.set(syntheticPath, {
731
- title: s.title,
732
- timeUpdated: s.time_updated,
733
- });
734
- return syntheticPath;
735
- });
736
- }
781
+ return this.mapDbSessions(projectId);
782
+ }
783
+ if (dbStatus.kind !== 'db_missing') {
784
+ return [];
737
785
  }
738
786
  // File fallback
739
787
  const sessionDir = path.join(getStorageDir(), 'session', projectId);
@@ -741,39 +789,25 @@ class OpenCodeProvider {
741
789
  }
742
790
  findSessionsInDirectory(dir) {
743
791
  const db = this.ensureDb();
792
+ const dbStatus = this.getRuntimeStatus();
744
793
  // Check if this is a synthetic DB session directory
745
794
  if (db && dir.includes(path.sep + DB_SESSION_PREFIX + path.sep)) {
746
795
  const projectId = extractProjectIdFromDbPath(dir + path.sep + 'dummy.json');
747
796
  if (projectId) {
748
- const sessions = db.getSessionsForProject(projectId);
749
- const dataDir = getOpenCodeDataDir();
750
- return sessions.map(s => {
751
- const syntheticPath = makeDbSessionPath(dataDir, projectId, s.id);
752
- this.sessionMetaCache.set(syntheticPath, {
753
- title: s.title,
754
- timeUpdated: s.time_updated,
755
- });
756
- return syntheticPath;
757
- });
797
+ return this.mapDbSessions(projectId);
758
798
  }
759
799
  }
760
800
  // For a DB-backed directory path ending with the project ID,
761
801
  // try to extract project ID from dir name
762
802
  if (db) {
763
803
  const dirName = path.basename(dir);
764
- const sessions = db.getSessionsForProject(dirName);
765
- if (sessions.length > 0) {
766
- const dataDir = getOpenCodeDataDir();
767
- return sessions.map(s => {
768
- const syntheticPath = makeDbSessionPath(dataDir, dirName, s.id);
769
- this.sessionMetaCache.set(syntheticPath, {
770
- title: s.title,
771
- timeUpdated: s.time_updated,
772
- });
773
- return syntheticPath;
774
- });
804
+ if (db.hasProject(dirName)) {
805
+ return this.mapDbSessions(dirName);
775
806
  }
776
807
  }
808
+ if (dbStatus.kind !== 'db_missing') {
809
+ return [];
810
+ }
777
811
  // File fallback
778
812
  return this.findSessionsInDirectoryFromFiles(dir);
779
813
  }
@@ -804,6 +838,7 @@ class OpenCodeProvider {
804
838
  getAllProjectFolders(workspacePath) {
805
839
  const db = this.ensureDb();
806
840
  const folders = [];
841
+ const dbStatus = this.getRuntimeStatus();
807
842
  // DB primary
808
843
  if (db) {
809
844
  const projects = db.getAllProjects();
@@ -811,7 +846,7 @@ class OpenCodeProvider {
811
846
  const statsMap = new Map(stats.map(s => [s.projectId, s]));
812
847
  let currentProjectId = null;
813
848
  if (workspacePath) {
814
- currentProjectId = resolveProjectId(workspacePath, db);
849
+ currentProjectId = resolveProjectId(workspacePath, db, dbStatus);
815
850
  }
816
851
  const dataDir = getOpenCodeDataDir();
817
852
  for (const project of projects) {
@@ -841,6 +876,9 @@ class OpenCodeProvider {
841
876
  if (folders.length > 0)
842
877
  return folders;
843
878
  }
879
+ if (dbStatus.kind !== 'db_missing') {
880
+ return [];
881
+ }
844
882
  // File fallback
845
883
  return this.getAllProjectFoldersFromFiles(workspacePath);
846
884
  }
@@ -935,7 +973,7 @@ class OpenCodeProvider {
935
973
  }
936
974
  encodeWorkspacePath(workspacePath) {
937
975
  const db = this.ensureDb();
938
- return resolveProjectId(workspacePath, db) || workspacePath;
976
+ return resolveProjectId(workspacePath, db, this.getRuntimeStatus()) || workspacePath;
939
977
  }
940
978
  extractSessionLabel(sessionPath) {
941
979
  const db = this.ensureDb();
@@ -6,6 +6,7 @@ export interface DbProject {
6
6
  id: string;
7
7
  worktree: string;
8
8
  name: string | null;
9
+ sandboxes: string[];
9
10
  time_created: number;
10
11
  time_updated: number;
11
12
  }
@@ -32,17 +33,25 @@ export interface DbPart {
32
33
  time_updated: number;
33
34
  data: string;
34
35
  }
36
+ export interface OpenCodeDbRuntimeStatus {
37
+ available: boolean;
38
+ kind: 'available' | 'db_missing' | 'sqlite_missing' | 'sqlite_blocked' | 'query_failed';
39
+ message?: string;
40
+ }
35
41
  export declare class OpenCodeDatabase {
36
42
  private readonly dbPath;
37
- private sqlite3Available;
43
+ private runtimeStatus;
38
44
  constructor(dataDir: string);
39
45
  isAvailable(): boolean;
46
+ getRuntimeStatus(): OpenCodeDbRuntimeStatus;
40
47
  open(): boolean;
41
48
  close(): void;
42
49
  private query;
43
50
  private queryOne;
44
51
  findProjectByWorktree(workspacePath: string): DbProject | null;
52
+ findProjectBySessionDirectory(workspacePath: string): DbProject | null;
45
53
  getAllProjects(): DbProject[];
54
+ hasProject(projectId: string): boolean;
46
55
  getSessionsForProject(projectId: string): DbSession[];
47
56
  /** Get the most recently updated session for a project (excludes subagent child sessions). */
48
57
  getMostRecentSession(projectId: string): DbSession | null;
@@ -43,27 +43,40 @@ const path = __importStar(require("path"));
43
43
  const child_process_1 = require("child_process");
44
44
  class OpenCodeDatabase {
45
45
  dbPath;
46
- sqlite3Available = null;
46
+ runtimeStatus = null;
47
47
  constructor(dataDir) {
48
48
  this.dbPath = path.join(dataDir, 'opencode.db');
49
49
  }
50
50
  isAvailable() { return fs.existsSync(this.dbPath); }
51
+ getRuntimeStatus() {
52
+ if (this.runtimeStatus)
53
+ return this.runtimeStatus;
54
+ if (!this.isAvailable()) {
55
+ this.runtimeStatus = { available: false, kind: 'db_missing' };
56
+ return this.runtimeStatus;
57
+ }
58
+ return { available: false, kind: 'query_failed', message: 'OpenCode database has not been initialized yet.' };
59
+ }
51
60
  open() {
52
- if (this.sqlite3Available !== null)
53
- return this.sqlite3Available;
61
+ if (this.runtimeStatus)
62
+ return this.runtimeStatus.available;
63
+ if (!this.isAvailable()) {
64
+ this.runtimeStatus = { available: false, kind: 'db_missing' };
65
+ return false;
66
+ }
54
67
  try {
55
68
  (0, child_process_1.execFileSync)('sqlite3', ['--version'], { encoding: 'utf-8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'] });
56
- this.sqlite3Available = true;
69
+ this.runtimeStatus = { available: true, kind: 'available' };
57
70
  return true;
58
71
  }
59
- catch {
60
- this.sqlite3Available = false;
72
+ catch (error) {
73
+ this.runtimeStatus = toRuntimeStatus(error);
61
74
  return false;
62
75
  }
63
76
  }
64
77
  close() { }
65
78
  query(sql, params = []) {
66
- if (!this.sqlite3Available)
79
+ if (!this.open())
67
80
  return [];
68
81
  let query = sql;
69
82
  for (const param of params) {
@@ -78,12 +91,14 @@ class OpenCodeDatabase {
78
91
  const result = (0, child_process_1.execFileSync)('sqlite3', ['-json', '-readonly', this.dbPath, query], {
79
92
  encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'], maxBuffer: 50 * 1024 * 1024,
80
93
  });
94
+ this.runtimeStatus = { available: true, kind: 'available' };
81
95
  const trimmed = result.trim();
82
96
  if (!trimmed)
83
97
  return [];
84
98
  return JSON.parse(trimmed);
85
99
  }
86
- catch {
100
+ catch (error) {
101
+ this.runtimeStatus = toRuntimeStatus(error, 'query_failed');
87
102
  return [];
88
103
  }
89
104
  }
@@ -93,27 +108,50 @@ class OpenCodeDatabase {
93
108
  }
94
109
  findProjectByWorktree(workspacePath) {
95
110
  const normalized = normalizePath(workspacePath);
96
- const exact = this.queryOne('SELECT id, worktree, name, time_created, time_updated FROM project WHERE worktree = ?', [normalized]);
97
- if (exact)
98
- return exact;
99
- const all = this.query('SELECT id, worktree, name, time_created, time_updated FROM project');
100
- const matches = [];
101
- for (const proj of all) {
102
- const projPath = normalizePath(proj.worktree);
103
- if (projPath === normalized)
104
- return proj;
105
- if (normalized.startsWith(projPath + path.sep) || projPath.startsWith(normalized + path.sep)) {
106
- matches.push({ ...proj, pathLen: projPath.length });
111
+ const all = this.getAllProjects();
112
+ let best = null;
113
+ let bestScore = -1;
114
+ for (const project of all) {
115
+ const worktreeScore = pathMatchScore(project.worktree, normalized);
116
+ if (worktreeScore > bestScore) {
117
+ best = project;
118
+ bestScore = worktreeScore;
119
+ }
120
+ for (const sandbox of project.sandboxes) {
121
+ const sandboxScore = pathMatchScore(sandbox, normalized);
122
+ if (sandboxScore > bestScore) {
123
+ best = project;
124
+ bestScore = sandboxScore;
125
+ }
107
126
  }
108
127
  }
109
- if (matches.length > 0) {
110
- matches.sort((a, b) => b.pathLen - a.pathLen);
111
- return matches[0];
128
+ return bestScore >= 0 ? best : null;
129
+ }
130
+ findProjectBySessionDirectory(workspacePath) {
131
+ const normalized = normalizePath(workspacePath);
132
+ const sessions = this.query('SELECT id, project_id, title, directory, time_created, time_updated FROM session WHERE parent_id IS NULL ORDER BY time_updated DESC');
133
+ let bestProjectId = null;
134
+ let bestScore = -1;
135
+ for (const session of sessions) {
136
+ const score = pathMatchScore(session.directory, normalized);
137
+ if (score > bestScore) {
138
+ bestProjectId = session.project_id;
139
+ bestScore = score;
140
+ }
112
141
  }
113
- return null;
142
+ if (!bestProjectId)
143
+ return null;
144
+ return this.getAllProjects().find(project => project.id === bestProjectId) ?? null;
114
145
  }
115
146
  getAllProjects() {
116
- return this.query('SELECT id, worktree, name, time_created, time_updated FROM project');
147
+ return this.query('SELECT id, worktree, name, sandboxes, time_created, time_updated FROM project')
148
+ .map(project => ({
149
+ ...project,
150
+ sandboxes: parseStringArray(project.sandboxes),
151
+ }));
152
+ }
153
+ hasProject(projectId) {
154
+ return this.queryOne('SELECT id FROM project WHERE id = ? LIMIT 1', [projectId]) !== null;
117
155
  }
118
156
  getSessionsForProject(projectId) {
119
157
  return this.query('SELECT id, project_id, title, directory, time_created, time_updated FROM session WHERE project_id = ? AND parent_id IS NULL ORDER BY time_updated DESC', [projectId]);
@@ -230,3 +268,55 @@ function normalizePath(input) {
230
268
  return path.resolve(input);
231
269
  }
232
270
  }
271
+ function parseStringArray(value) {
272
+ if (!value)
273
+ return [];
274
+ try {
275
+ const parsed = JSON.parse(value);
276
+ return Array.isArray(parsed)
277
+ ? parsed.filter((entry) => typeof entry === 'string' && entry.length > 0).map(normalizePath)
278
+ : [];
279
+ }
280
+ catch {
281
+ return [];
282
+ }
283
+ }
284
+ function normalizeForCompare(input) {
285
+ const normalized = normalizePath(input);
286
+ return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
287
+ }
288
+ function pathMatchScore(candidate, workspacePath) {
289
+ if (!candidate)
290
+ return -1;
291
+ const normalizedCandidate = normalizeForCompare(candidate);
292
+ const normalizedWorkspace = normalizeForCompare(workspacePath);
293
+ if (normalizedCandidate === normalizedWorkspace)
294
+ return 10_000 + normalizedCandidate.length;
295
+ const candidatePrefix = normalizedCandidate.endsWith(path.sep)
296
+ ? normalizedCandidate
297
+ : normalizedCandidate + path.sep;
298
+ const workspacePrefix = normalizedWorkspace.endsWith(path.sep)
299
+ ? normalizedWorkspace
300
+ : normalizedWorkspace + path.sep;
301
+ if (normalizedWorkspace.startsWith(candidatePrefix))
302
+ return 5_000 + normalizedCandidate.length;
303
+ if (normalizedCandidate.startsWith(workspacePrefix))
304
+ return 1_000 + normalizedCandidate.length;
305
+ return -1;
306
+ }
307
+ function toRuntimeStatus(error, fallback = 'sqlite_missing') {
308
+ if (isErrno(error, 'ENOENT')) {
309
+ return { available: false, kind: 'sqlite_missing', message: 'sqlite3 executable not found in PATH.' };
310
+ }
311
+ if (isErrno(error, 'EPERM') || isErrno(error, 'EACCES')) {
312
+ return { available: false, kind: 'sqlite_blocked', message: 'sqlite3 exists but could not be executed.' };
313
+ }
314
+ const message = error instanceof Error ? error.message : String(error);
315
+ return { available: false, kind: fallback, message };
316
+ }
317
+ function isErrno(error, code) {
318
+ return typeof error === 'object'
319
+ && error !== null
320
+ && 'code' in error
321
+ && error.code === code;
322
+ }
@@ -55,6 +55,11 @@ export interface ProjectFolderInfo {
55
55
  sessionCount: number;
56
56
  lastModified: Date;
57
57
  }
58
+ export interface ProviderRuntimeStatus {
59
+ available: boolean;
60
+ kind: 'available' | 'db_missing' | 'sqlite_missing' | 'sqlite_blocked' | 'query_failed';
61
+ message?: string;
62
+ }
58
63
  /**
59
64
  * Incremental reader for session data.
60
65
  *
@@ -133,6 +138,10 @@ export interface SessionProviderBase {
133
138
  getCurrentUsageSnapshot?(sessionPath: string): TokenUsage | null;
134
139
  /** Gets context attribution breakdown from provider data, if available. */
135
140
  getContextAttribution?(sessionPath: string): ContextAttribution | null;
141
+ /** Reports provider runtime readiness for DB-backed providers. */
142
+ getRuntimeStatus?(): ProviderRuntimeStatus;
143
+ /** Tests whether a provider can monitor a directory path, including synthetic DB-backed paths. */
144
+ canMonitorDirectory?(dir: string): boolean;
136
145
  /** Release resources. */
137
146
  dispose(): void;
138
147
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sidekick-shared",
3
- "version": "0.13.5",
3
+ "version": "0.13.6",
4
4
  "description": "Shared data access layer for Sidekick — readers, types, providers, credentials, quota",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",