sidekick-shared 0.13.8 → 0.14.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.
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Multi-account management for Claude Max subscriptions.
3
+ *
4
+ * Handles saving, listing, switching, and removing Claude accounts.
5
+ * Both the VS Code extension and CLI consume this module.
6
+ */
7
+ export interface AccountEntry {
8
+ uuid: string;
9
+ email: string;
10
+ label?: string;
11
+ addedAt: string;
12
+ }
13
+ export interface AccountRegistry {
14
+ version: 1;
15
+ activeAccountUuid: string | null;
16
+ accounts: AccountEntry[];
17
+ }
18
+ export interface ActiveAccountInfo {
19
+ email: string;
20
+ uuid: string;
21
+ }
22
+ export interface AccountManagerResult {
23
+ success: boolean;
24
+ error?: string;
25
+ }
26
+ export declare function getAccountsDir(): string;
27
+ export declare function readAccountRegistry(): AccountRegistry | null;
28
+ export declare function writeAccountRegistry(registry: AccountRegistry): void;
29
+ export declare function readActiveClaudeAccount(): ActiveAccountInfo | null;
30
+ export declare function addCurrentAccount(label?: string): AccountManagerResult;
31
+ export declare function switchToAccount(uuid: string): AccountManagerResult;
32
+ export declare function removeAccount(uuid: string): AccountManagerResult;
33
+ export declare function listAccounts(): AccountEntry[];
34
+ export declare function getActiveAccount(): AccountEntry | null;
35
+ export declare function isMultiAccountEnabled(): boolean;
@@ -0,0 +1,325 @@
1
+ "use strict";
2
+ /**
3
+ * Multi-account management for Claude Max subscriptions.
4
+ *
5
+ * Handles saving, listing, switching, and removing Claude accounts.
6
+ * Both the VS Code extension and CLI consume this module.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.getAccountsDir = getAccountsDir;
43
+ exports.readAccountRegistry = readAccountRegistry;
44
+ exports.writeAccountRegistry = writeAccountRegistry;
45
+ exports.readActiveClaudeAccount = readActiveClaudeAccount;
46
+ exports.addCurrentAccount = addCurrentAccount;
47
+ exports.switchToAccount = switchToAccount;
48
+ exports.removeAccount = removeAccount;
49
+ exports.listAccounts = listAccounts;
50
+ exports.getActiveAccount = getActiveAccount;
51
+ exports.isMultiAccountEnabled = isMultiAccountEnabled;
52
+ const fs = __importStar(require("fs"));
53
+ const path = __importStar(require("path"));
54
+ const os = __importStar(require("os"));
55
+ const paths_1 = require("./paths");
56
+ const credentialIO_1 = require("./credentialIO");
57
+ // ── Paths ────────────────────────────────────────────────────────────────
58
+ function getClaudeDir() {
59
+ return path.join(os.homedir(), '.claude');
60
+ }
61
+ function getClaudeConfigPath() {
62
+ // Primary: ~/.claude/.claude.json
63
+ const primary = path.join(getClaudeDir(), '.claude.json');
64
+ try {
65
+ const data = JSON.parse(fs.readFileSync(primary, 'utf8'));
66
+ if (data?.oauthAccount)
67
+ return primary;
68
+ }
69
+ catch { /* fall through */ }
70
+ // Fallback: ~/.claude.json (some Claude Code versions store config here)
71
+ return path.join(os.homedir(), '.claude.json');
72
+ }
73
+ function getAccountsDir() {
74
+ return path.join((0, paths_1.getConfigDir)(), 'accounts');
75
+ }
76
+ function getCredentialsDir() {
77
+ return path.join(getAccountsDir(), 'credentials');
78
+ }
79
+ function getConfigsDir() {
80
+ return path.join(getAccountsDir(), 'configs');
81
+ }
82
+ function getRegistryPath() {
83
+ return path.join(getAccountsDir(), 'accounts.json');
84
+ }
85
+ // ── Directory bootstrap ──────────────────────────────────────────────────
86
+ function ensureDirs() {
87
+ for (const dir of [getAccountsDir(), getCredentialsDir(), getConfigsDir()]) {
88
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
89
+ }
90
+ }
91
+ // ── Atomic file write ────────────────────────────────────────────────────
92
+ function atomicWriteJson(filePath, data, mode = 0o600) {
93
+ const tmp = filePath + '.tmp';
94
+ const json = JSON.stringify(data, null, 2);
95
+ // Validate we can re-parse before writing
96
+ JSON.parse(json);
97
+ fs.writeFileSync(tmp, json, { encoding: 'utf8', mode });
98
+ fs.renameSync(tmp, filePath);
99
+ }
100
+ // ── Registry operations ──────────────────────────────────────────────────
101
+ function readAccountRegistry() {
102
+ try {
103
+ const content = fs.readFileSync(getRegistryPath(), 'utf8');
104
+ const parsed = JSON.parse(content);
105
+ if (parsed?.version !== 1 || !Array.isArray(parsed.accounts))
106
+ return null;
107
+ return parsed;
108
+ }
109
+ catch {
110
+ return null;
111
+ }
112
+ }
113
+ function writeAccountRegistry(registry) {
114
+ ensureDirs();
115
+ atomicWriteJson(getRegistryPath(), registry);
116
+ }
117
+ // ── Read active Claude account ───────────────────────────────────────────
118
+ function readActiveClaudeAccount() {
119
+ try {
120
+ const content = fs.readFileSync(getClaudeConfigPath(), 'utf8');
121
+ const parsed = JSON.parse(content);
122
+ const email = parsed?.oauthAccount?.emailAddress;
123
+ const uuid = parsed?.oauthAccount?.accountUuid;
124
+ if (!email || !uuid)
125
+ return null;
126
+ return { email, uuid };
127
+ }
128
+ catch {
129
+ return null;
130
+ }
131
+ }
132
+ // ── Add current account ──────────────────────────────────────────────────
133
+ function addCurrentAccount(label) {
134
+ // 1. Read current Claude identity
135
+ const active = readActiveClaudeAccount();
136
+ if (!active) {
137
+ return { success: false, error: 'No active Claude account found. Sign in with `claude` first.' };
138
+ }
139
+ // 2. Read current credentials (platform-aware: Keychain on macOS, file on Linux)
140
+ const credBlob = (0, credentialIO_1.readActiveCredentials)();
141
+ if (!credBlob) {
142
+ return { success: false, error: 'Could not read Claude credentials.' };
143
+ }
144
+ // 3. Read oauthAccount section from .claude.json
145
+ let configBlob;
146
+ try {
147
+ const raw = JSON.parse(fs.readFileSync(getClaudeConfigPath(), 'utf8'));
148
+ configBlob = raw.oauthAccount;
149
+ }
150
+ catch {
151
+ return { success: false, error: 'Could not read Claude config file.' };
152
+ }
153
+ ensureDirs();
154
+ // 4. Load or create registry
155
+ const registry = readAccountRegistry() ?? {
156
+ version: 1,
157
+ activeAccountUuid: null,
158
+ accounts: [],
159
+ };
160
+ // 5. Check if account already exists — update credentials + label
161
+ const existing = registry.accounts.find(a => a.uuid === active.uuid);
162
+ if (existing) {
163
+ if (label !== undefined)
164
+ existing.label = label;
165
+ atomicWriteJson(path.join(getCredentialsDir(), `${active.uuid}.credentials.json`), credBlob);
166
+ atomicWriteJson(path.join(getConfigsDir(), `${active.uuid}.config.json`), configBlob);
167
+ registry.activeAccountUuid = active.uuid;
168
+ writeAccountRegistry(registry);
169
+ return { success: true };
170
+ }
171
+ // 6. New account — back up credentials
172
+ atomicWriteJson(path.join(getCredentialsDir(), `${active.uuid}.credentials.json`), credBlob);
173
+ atomicWriteJson(path.join(getConfigsDir(), `${active.uuid}.config.json`), configBlob);
174
+ // 7. Add entry
175
+ registry.accounts.push({
176
+ uuid: active.uuid,
177
+ email: active.email,
178
+ label,
179
+ addedAt: new Date().toISOString(),
180
+ });
181
+ registry.activeAccountUuid = active.uuid;
182
+ writeAccountRegistry(registry);
183
+ return { success: true };
184
+ }
185
+ // ── Switch to account ────────────────────────────────────────────────────
186
+ function switchToAccount(uuid) {
187
+ const registry = readAccountRegistry();
188
+ if (!registry) {
189
+ return { success: false, error: 'No account registry found. Save an account first.' };
190
+ }
191
+ const target = registry.accounts.find(a => a.uuid === uuid);
192
+ if (!target) {
193
+ return { success: false, error: `Account ${uuid} not found in registry.` };
194
+ }
195
+ if (registry.activeAccountUuid === uuid) {
196
+ return { success: true }; // Already active
197
+ }
198
+ // 1. Back up current credentials + config (for the currently-active account)
199
+ let originalCreds = null;
200
+ let originalConfig = null;
201
+ originalCreds = (0, credentialIO_1.readActiveCredentials)();
202
+ try {
203
+ originalConfig = fs.readFileSync(getClaudeConfigPath(), 'utf8');
204
+ }
205
+ catch { /* no existing config to back up */ }
206
+ // Save current account's backup if we know who's active
207
+ const currentActive = readActiveClaudeAccount();
208
+ if (currentActive) {
209
+ if (originalCreds) {
210
+ try {
211
+ atomicWriteJson(path.join(getCredentialsDir(), `${currentActive.uuid}.credentials.json`), originalCreds);
212
+ }
213
+ catch { /* best effort */ }
214
+ }
215
+ if (originalConfig) {
216
+ try {
217
+ const parsed = JSON.parse(originalConfig);
218
+ if (parsed.oauthAccount) {
219
+ atomicWriteJson(path.join(getConfigsDir(), `${currentActive.uuid}.config.json`), parsed.oauthAccount);
220
+ }
221
+ }
222
+ catch { /* best effort */ }
223
+ }
224
+ }
225
+ // 2. Read target account's backed-up credentials + config
226
+ let targetCreds;
227
+ let targetOauthAccount;
228
+ try {
229
+ targetCreds = JSON.parse(fs.readFileSync(path.join(getCredentialsDir(), `${uuid}.credentials.json`), 'utf8'));
230
+ }
231
+ catch {
232
+ return { success: false, error: `Backed-up credentials for ${target.email} not found.` };
233
+ }
234
+ try {
235
+ targetOauthAccount = JSON.parse(fs.readFileSync(path.join(getConfigsDir(), `${uuid}.config.json`), 'utf8'));
236
+ }
237
+ catch {
238
+ return { success: false, error: `Backed-up config for ${target.email} not found.` };
239
+ }
240
+ // 3. Write target credentials (platform-aware: Keychain on macOS, file on Linux)
241
+ try {
242
+ (0, credentialIO_1.writeActiveCredentials)(targetCreds);
243
+ }
244
+ catch (err) {
245
+ // Rollback: restore original credentials
246
+ if (originalCreds) {
247
+ try {
248
+ (0, credentialIO_1.writeActiveCredentials)(originalCreds);
249
+ }
250
+ catch { /* rollback failed */ }
251
+ }
252
+ return { success: false, error: `Failed to write credentials: ${err}` };
253
+ }
254
+ // 4. Merge target oauthAccount into current .claude.json
255
+ try {
256
+ let configObj = {};
257
+ try {
258
+ configObj = JSON.parse(fs.readFileSync(getClaudeConfigPath(), 'utf8'));
259
+ }
260
+ catch { /* file may not exist yet */ }
261
+ configObj.oauthAccount = targetOauthAccount;
262
+ atomicWriteJson(getClaudeConfigPath(), configObj);
263
+ }
264
+ catch (err) {
265
+ // Rollback: restore original credentials and config
266
+ if (originalCreds) {
267
+ try {
268
+ (0, credentialIO_1.writeActiveCredentials)(originalCreds);
269
+ }
270
+ catch { /* rollback failed */ }
271
+ }
272
+ if (originalConfig) {
273
+ try {
274
+ fs.writeFileSync(getClaudeConfigPath(), originalConfig);
275
+ }
276
+ catch { /* rollback failed */ }
277
+ }
278
+ return { success: false, error: `Failed to write config: ${err}` };
279
+ }
280
+ // 5. Update registry
281
+ registry.activeAccountUuid = uuid;
282
+ writeAccountRegistry(registry);
283
+ return { success: true };
284
+ }
285
+ // ── Remove account ───────────────────────────────────────────────────────
286
+ function removeAccount(uuid) {
287
+ const registry = readAccountRegistry();
288
+ if (!registry) {
289
+ return { success: false, error: 'No account registry found.' };
290
+ }
291
+ const idx = registry.accounts.findIndex(a => a.uuid === uuid);
292
+ if (idx === -1) {
293
+ return { success: false, error: `Account ${uuid} not found.` };
294
+ }
295
+ // Remove backed-up files
296
+ try {
297
+ fs.unlinkSync(path.join(getCredentialsDir(), `${uuid}.credentials.json`));
298
+ }
299
+ catch { /* ok */ }
300
+ try {
301
+ fs.unlinkSync(path.join(getConfigsDir(), `${uuid}.config.json`));
302
+ }
303
+ catch { /* ok */ }
304
+ // Remove from registry
305
+ registry.accounts.splice(idx, 1);
306
+ if (registry.activeAccountUuid === uuid) {
307
+ registry.activeAccountUuid = registry.accounts[0]?.uuid ?? null;
308
+ }
309
+ writeAccountRegistry(registry);
310
+ return { success: true };
311
+ }
312
+ // ── Query helpers ────────────────────────────────────────────────────────
313
+ function listAccounts() {
314
+ return readAccountRegistry()?.accounts ?? [];
315
+ }
316
+ function getActiveAccount() {
317
+ const registry = readAccountRegistry();
318
+ if (!registry?.activeAccountUuid)
319
+ return null;
320
+ return registry.accounts.find(a => a.uuid === registry.activeAccountUuid) ?? null;
321
+ }
322
+ function isMultiAccountEnabled() {
323
+ const registry = readAccountRegistry();
324
+ return (registry?.accounts.length ?? 0) >= 1;
325
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Platform-aware credential I/O for Claude Code.
3
+ *
4
+ * On macOS, Claude Code stores OAuth credentials in the system Keychain
5
+ * (service "Claude Code-credentials"). On Linux/WSL/Windows, credentials
6
+ * live in ~/.claude/.credentials.json.
7
+ *
8
+ * Both accounts.ts and credentials.ts consume this module so that all
9
+ * credential access is platform-correct in one place.
10
+ */
11
+ /**
12
+ * Read the active Claude Code credentials as a parsed object.
13
+ *
14
+ * - macOS: reads from system Keychain via the `security` CLI
15
+ * - Linux / WSL / Windows: reads `~/.claude/.credentials.json`
16
+ *
17
+ * Returns `null` when credentials are absent or unreadable.
18
+ */
19
+ export declare function readActiveCredentials(): unknown;
20
+ /**
21
+ * Write credentials to the active Claude Code credential store.
22
+ *
23
+ * - macOS: writes to system Keychain via the `security` CLI
24
+ * - Linux / WSL / Windows: atomic-writes `~/.claude/.credentials.json`
25
+ *
26
+ * Throws on failure.
27
+ */
28
+ export declare function writeActiveCredentials(credentials: unknown): void;
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ /**
3
+ * Platform-aware credential I/O for Claude Code.
4
+ *
5
+ * On macOS, Claude Code stores OAuth credentials in the system Keychain
6
+ * (service "Claude Code-credentials"). On Linux/WSL/Windows, credentials
7
+ * live in ~/.claude/.credentials.json.
8
+ *
9
+ * Both accounts.ts and credentials.ts consume this module so that all
10
+ * credential access is platform-correct in one place.
11
+ */
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.readActiveCredentials = readActiveCredentials;
47
+ exports.writeActiveCredentials = writeActiveCredentials;
48
+ const fs = __importStar(require("fs"));
49
+ const path = __importStar(require("path"));
50
+ const os = __importStar(require("os"));
51
+ const child_process_1 = require("child_process");
52
+ const KEYCHAIN_SERVICE = 'Claude Code-credentials';
53
+ function getCredentialsFilePath() {
54
+ return path.join(os.homedir(), '.claude', '.credentials.json');
55
+ }
56
+ /**
57
+ * Read the active Claude Code credentials as a parsed object.
58
+ *
59
+ * - macOS: reads from system Keychain via the `security` CLI
60
+ * - Linux / WSL / Windows: reads `~/.claude/.credentials.json`
61
+ *
62
+ * Returns `null` when credentials are absent or unreadable.
63
+ */
64
+ function readActiveCredentials() {
65
+ if (process.platform === 'darwin') {
66
+ try {
67
+ const raw = (0, child_process_1.execFileSync)('security', [
68
+ 'find-generic-password', '-s', KEYCHAIN_SERVICE, '-w',
69
+ ], { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
70
+ return JSON.parse(raw.trim());
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ }
76
+ // Linux / WSL / Windows — file-based
77
+ try {
78
+ return JSON.parse(fs.readFileSync(getCredentialsFilePath(), 'utf8'));
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }
84
+ /**
85
+ * Write credentials to the active Claude Code credential store.
86
+ *
87
+ * - macOS: writes to system Keychain via the `security` CLI
88
+ * - Linux / WSL / Windows: atomic-writes `~/.claude/.credentials.json`
89
+ *
90
+ * Throws on failure.
91
+ */
92
+ function writeActiveCredentials(credentials) {
93
+ const json = JSON.stringify(credentials);
94
+ JSON.parse(json); // validate round-trip
95
+ if (process.platform === 'darwin') {
96
+ (0, child_process_1.execFileSync)('security', [
97
+ 'add-generic-password', '-U',
98
+ '-s', KEYCHAIN_SERVICE,
99
+ '-a', process.env.USER || 'user',
100
+ '-w', json,
101
+ ], { stdio: ['pipe', 'pipe', 'pipe'] });
102
+ return;
103
+ }
104
+ // Linux / WSL / Windows — file-based
105
+ const credPath = getCredentialsFilePath();
106
+ const tmp = credPath + '.tmp';
107
+ fs.writeFileSync(tmp, json, { encoding: 'utf8', mode: 0o600 });
108
+ fs.renameSync(tmp, credPath);
109
+ }
@@ -11,10 +11,11 @@ export interface ClaudeMaxCredentials {
11
11
  subscriptionType?: string;
12
12
  }
13
13
  /**
14
- * Reads Claude Max OAuth credentials from `~/.claude/.credentials.json`.
14
+ * Reads Claude Max OAuth credentials.
15
15
  *
16
- * Returns `null` if the file does not exist, the token is missing, or the
17
- * token is expired. Never throws.
16
+ * On macOS reads from system Keychain; on Linux/Windows reads from
17
+ * `~/.claude/.credentials.json`. Returns `null` if credentials are
18
+ * absent, the token is missing, or the token is expired. Never throws.
18
19
  */
19
20
  export declare function readClaudeMaxCredentials(): Promise<ClaudeMaxCredentials | null>;
20
21
  /**
@@ -4,59 +4,23 @@
4
4
  *
5
5
  * Shared by sidekick-cli, sidekick-vscode, and any external consumer.
6
6
  */
7
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
- if (k2 === undefined) k2 = k;
9
- var desc = Object.getOwnPropertyDescriptor(m, k);
10
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
- desc = { enumerable: true, get: function() { return m[k]; } };
12
- }
13
- Object.defineProperty(o, k2, desc);
14
- }) : (function(o, m, k, k2) {
15
- if (k2 === undefined) k2 = k;
16
- o[k2] = m[k];
17
- }));
18
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
- Object.defineProperty(o, "default", { enumerable: true, value: v });
20
- }) : function(o, v) {
21
- o["default"] = v;
22
- });
23
- var __importStar = (this && this.__importStar) || (function () {
24
- var ownKeys = function(o) {
25
- ownKeys = Object.getOwnPropertyNames || function (o) {
26
- var ar = [];
27
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
- return ar;
29
- };
30
- return ownKeys(o);
31
- };
32
- return function (mod) {
33
- if (mod && mod.__esModule) return mod;
34
- var result = {};
35
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
- __setModuleDefault(result, mod);
37
- return result;
38
- };
39
- })();
40
7
  Object.defineProperty(exports, "__esModule", { value: true });
41
8
  exports.readClaudeMaxCredentials = readClaudeMaxCredentials;
42
9
  exports.readClaudeMaxAccessTokenSync = readClaudeMaxAccessTokenSync;
43
- const fs = __importStar(require("fs"));
44
- const path = __importStar(require("path"));
45
- const os = __importStar(require("os"));
10
+ const credentialIO_1 = require("./credentialIO");
46
11
  /**
47
- * Reads Claude Max OAuth credentials from `~/.claude/.credentials.json`.
12
+ * Reads Claude Max OAuth credentials.
48
13
  *
49
- * Returns `null` if the file does not exist, the token is missing, or the
50
- * token is expired. Never throws.
14
+ * On macOS reads from system Keychain; on Linux/Windows reads from
15
+ * `~/.claude/.credentials.json`. Returns `null` if credentials are
16
+ * absent, the token is missing, or the token is expired. Never throws.
51
17
  */
52
18
  async function readClaudeMaxCredentials() {
53
- const credPath = path.join(os.homedir(), '.claude', '.credentials.json');
54
19
  try {
55
- if (!fs.existsSync(credPath))
20
+ const raw = (0, credentialIO_1.readActiveCredentials)();
21
+ if (!raw)
56
22
  return null;
57
- const content = await fs.promises.readFile(credPath, 'utf8');
58
- const parsed = JSON.parse(content);
59
- const oauth = parsed?.claudeAiOauth;
23
+ const oauth = raw.claudeAiOauth;
60
24
  if (!oauth?.accessToken)
61
25
  return null;
62
26
  if (oauth.expiresAt && Date.now() > oauth.expiresAt)
@@ -77,13 +41,11 @@ async function readClaudeMaxCredentials() {
77
41
  * Synchronous convenience — returns just the access token or `null`.
78
42
  */
79
43
  function readClaudeMaxAccessTokenSync() {
80
- const credPath = path.join(os.homedir(), '.claude', '.credentials.json');
81
44
  try {
82
- if (!fs.existsSync(credPath))
45
+ const raw = (0, credentialIO_1.readActiveCredentials)();
46
+ if (!raw)
83
47
  return null;
84
- const content = fs.readFileSync(credPath, 'utf8');
85
- const parsed = JSON.parse(content);
86
- const oauth = parsed?.claudeAiOauth;
48
+ const oauth = raw.claudeAiOauth;
87
49
  if (!oauth?.accessToken)
88
50
  return null;
89
51
  if (oauth.expiresAt && Date.now() > oauth.expiresAt)
package/dist/index.d.ts CHANGED
@@ -79,11 +79,15 @@ export { PatternExtractor } from './aggregation/PatternExtractor';
79
79
  export type { PatternCluster, SerializedPatternState } from './aggregation/PatternExtractor';
80
80
  export { generateHtmlReport, parseTranscript, openInBrowser } from './report';
81
81
  export type { TranscriptContentBlock, TranscriptEntry, HtmlReportOptions } from './report';
82
+ export { readActiveCredentials, writeActiveCredentials } from './credentialIO';
82
83
  export { readClaudeMaxCredentials, readClaudeMaxAccessTokenSync } from './credentials';
83
84
  export type { ClaudeMaxCredentials } from './credentials';
85
+ export { getAccountsDir, readAccountRegistry, writeAccountRegistry, readActiveClaudeAccount, addCurrentAccount, switchToAccount, removeAccount, listAccounts, getActiveAccount, isMultiAccountEnabled, } from './accounts';
86
+ export type { AccountEntry, AccountRegistry, ActiveAccountInfo, AccountManagerResult } from './accounts';
84
87
  export { fetchQuota } from './quota';
85
88
  export type { QuotaWindow, QuotaState } from './quota';
86
89
  export { describeQuotaFailure } from './quotaPresentation';
87
90
  export type { QuotaFailureDescriptor } from './quotaPresentation';
91
+ export { getModelContextWindowSize, DEFAULT_CONTEXT_WINDOW } from './modelContext';
88
92
  export { fetchProviderStatus } from './providerStatus';
89
93
  export type { ProviderStatusState } from './providerStatus';
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.findActiveClaudeSession = exports.discoverSessionDirectory = exports.getClaudeSessionDirectory = exports.encodeClaudeWorkspacePath = exports.detectSessionActivity = exports.extractTaskInfo = exports.scanSubagentDir = exports.normalizeCodexToolInput = exports.normalizeCodexToolName = exports.extractPatchFilePaths = exports.CodexRolloutParser = exports.parseDbPartData = exports.parseDbMessageData = exports.convertOpenCodeMessage = exports.detectPlanModeFromText = exports.normalizeToolInput = exports.normalizeToolName = exports.TRUNCATION_PATTERNS = exports.JsonlParser = exports.CodexProvider = exports.OpenCodeProvider = exports.ClaudeCodeProvider = exports.getAllDetectedProviders = exports.detectProvider = exports.readClaudeCodePlanFiles = exports.getPlanAnalytics = exports.writePlans = exports.getLatestPlan = exports.readPlans = exports.readLatestHandoff = exports.readHistory = exports.readNotes = exports.readDecisions = exports.readTasks = exports.getProjectSlugRaw = exports.getProjectSlug = exports.encodeWorkspacePath = exports.getGlobalDataPath = exports.getProjectDataPath = exports.getConfigDir = exports.MAX_PLANS_PER_PROJECT = exports.PLAN_SCHEMA_VERSION = exports.createEmptyTokenTotals = exports.HISTORICAL_DATA_SCHEMA_VERSION = exports.STALENESS_THRESHOLDS = exports.IMPORTANCE_DECAY_FACTORS = exports.KNOWLEDGE_NOTE_SCHEMA_VERSION = exports.DECISION_LOG_SCHEMA_VERSION = exports.normalizeTaskStatus = exports.TASK_PERSISTENCE_SCHEMA_VERSION = void 0;
7
7
  exports.HeatmapTracker = exports.FrequencyTracker = exports.getSnapshotPath = exports.isSnapshotValid = exports.deleteSnapshot = exports.loadSnapshot = exports.saveSnapshot = exports.parseTodoDependencies = exports.EventAggregator = exports.getRandomPhrase = exports.ALL_PHRASES = exports.HIGHLIGHT_CSS = exports.clearHighlightCache = exports.highlightEvent = exports.formatSessionJson = exports.formatSessionMarkdown = exports.formatSessionText = exports.classifyNoise = exports.shouldMergeWithPrevious = exports.classifyFollowEvent = exports.classifyMessage = exports.getSoftNoiseReason = exports.isHardNoiseFollowEvent = exports.isHardNoise = exports.formatToolSummary = exports.toFollowEvents = exports.createWatcher = exports.parseChangelog = exports.extractProposedPlanShared = exports.parsePlanMarkdownShared = exports.PlanExtractor = exports.composeContext = exports.FilterEngine = exports.searchSessions = exports.CodexDatabase = exports.OpenCodeDatabase = exports.discoverDebugLogs = exports.collapseDuplicates = exports.filterByLevel = exports.parseDebugLog = exports.scanSubagentTraces = exports.findAllSessionsWithWorktrees = exports.discoverWorktreeSiblings = exports.resolveWorktreeMainRepo = exports.getAllClaudeProjectFolders = exports.decodeEncodedPath = exports.getMostRecentlyActiveSessionDir = exports.findSubdirectorySessionDirs = exports.findSessionsInDirectory = exports.findAllClaudeSessions = void 0;
8
- exports.fetchProviderStatus = exports.describeQuotaFailure = exports.fetchQuota = exports.readClaudeMaxAccessTokenSync = exports.readClaudeMaxCredentials = exports.openInBrowser = exports.parseTranscript = exports.generateHtmlReport = exports.PatternExtractor = void 0;
8
+ exports.fetchProviderStatus = exports.DEFAULT_CONTEXT_WINDOW = exports.getModelContextWindowSize = exports.describeQuotaFailure = exports.fetchQuota = exports.isMultiAccountEnabled = exports.getActiveAccount = exports.listAccounts = exports.removeAccount = exports.switchToAccount = exports.addCurrentAccount = exports.readActiveClaudeAccount = exports.writeAccountRegistry = exports.readAccountRegistry = exports.getAccountsDir = exports.readClaudeMaxAccessTokenSync = exports.readClaudeMaxCredentials = exports.writeActiveCredentials = exports.readActiveCredentials = exports.openInBrowser = exports.parseTranscript = exports.generateHtmlReport = exports.PatternExtractor = void 0;
9
9
  var taskPersistence_1 = require("./types/taskPersistence");
10
10
  Object.defineProperty(exports, "TASK_PERSISTENCE_SCHEMA_VERSION", { enumerable: true, get: function () { return taskPersistence_1.TASK_PERSISTENCE_SCHEMA_VERSION; } });
11
11
  Object.defineProperty(exports, "normalizeTaskStatus", { enumerable: true, get: function () { return taskPersistence_1.normalizeTaskStatus; } });
@@ -174,15 +174,35 @@ var report_1 = require("./report");
174
174
  Object.defineProperty(exports, "generateHtmlReport", { enumerable: true, get: function () { return report_1.generateHtmlReport; } });
175
175
  Object.defineProperty(exports, "parseTranscript", { enumerable: true, get: function () { return report_1.parseTranscript; } });
176
176
  Object.defineProperty(exports, "openInBrowser", { enumerable: true, get: function () { return report_1.openInBrowser; } });
177
+ // Credential I/O (platform-aware: Keychain on macOS, file on Linux/Windows)
178
+ var credentialIO_1 = require("./credentialIO");
179
+ Object.defineProperty(exports, "readActiveCredentials", { enumerable: true, get: function () { return credentialIO_1.readActiveCredentials; } });
180
+ Object.defineProperty(exports, "writeActiveCredentials", { enumerable: true, get: function () { return credentialIO_1.writeActiveCredentials; } });
177
181
  // Credentials
178
182
  var credentials_1 = require("./credentials");
179
183
  Object.defineProperty(exports, "readClaudeMaxCredentials", { enumerable: true, get: function () { return credentials_1.readClaudeMaxCredentials; } });
180
184
  Object.defineProperty(exports, "readClaudeMaxAccessTokenSync", { enumerable: true, get: function () { return credentials_1.readClaudeMaxAccessTokenSync; } });
185
+ // Accounts
186
+ var accounts_1 = require("./accounts");
187
+ Object.defineProperty(exports, "getAccountsDir", { enumerable: true, get: function () { return accounts_1.getAccountsDir; } });
188
+ Object.defineProperty(exports, "readAccountRegistry", { enumerable: true, get: function () { return accounts_1.readAccountRegistry; } });
189
+ Object.defineProperty(exports, "writeAccountRegistry", { enumerable: true, get: function () { return accounts_1.writeAccountRegistry; } });
190
+ Object.defineProperty(exports, "readActiveClaudeAccount", { enumerable: true, get: function () { return accounts_1.readActiveClaudeAccount; } });
191
+ Object.defineProperty(exports, "addCurrentAccount", { enumerable: true, get: function () { return accounts_1.addCurrentAccount; } });
192
+ Object.defineProperty(exports, "switchToAccount", { enumerable: true, get: function () { return accounts_1.switchToAccount; } });
193
+ Object.defineProperty(exports, "removeAccount", { enumerable: true, get: function () { return accounts_1.removeAccount; } });
194
+ Object.defineProperty(exports, "listAccounts", { enumerable: true, get: function () { return accounts_1.listAccounts; } });
195
+ Object.defineProperty(exports, "getActiveAccount", { enumerable: true, get: function () { return accounts_1.getActiveAccount; } });
196
+ Object.defineProperty(exports, "isMultiAccountEnabled", { enumerable: true, get: function () { return accounts_1.isMultiAccountEnabled; } });
181
197
  // Quota
182
198
  var quota_1 = require("./quota");
183
199
  Object.defineProperty(exports, "fetchQuota", { enumerable: true, get: function () { return quota_1.fetchQuota; } });
184
200
  var quotaPresentation_1 = require("./quotaPresentation");
185
201
  Object.defineProperty(exports, "describeQuotaFailure", { enumerable: true, get: function () { return quotaPresentation_1.describeQuotaFailure; } });
202
+ // Model Context
203
+ var modelContext_1 = require("./modelContext");
204
+ Object.defineProperty(exports, "getModelContextWindowSize", { enumerable: true, get: function () { return modelContext_1.getModelContextWindowSize; } });
205
+ Object.defineProperty(exports, "DEFAULT_CONTEXT_WINDOW", { enumerable: true, get: function () { return modelContext_1.DEFAULT_CONTEXT_WINDOW; } });
186
206
  // Provider Status
187
207
  var providerStatus_1 = require("./providerStatus");
188
208
  Object.defineProperty(exports, "fetchProviderStatus", { enumerable: true, get: function () { return providerStatus_1.fetchProviderStatus; } });
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Centralized model ID → context window size lookup.
3
+ * Single source of truth used by all providers and the CLI dashboard.
4
+ */
5
+ /** Default context window size when model is unknown. */
6
+ export declare const DEFAULT_CONTEXT_WINDOW = 200000;
7
+ /**
8
+ * Returns the context window size for a model ID.
9
+ *
10
+ * Lookup order:
11
+ * 1. Exact match against MODEL_CONTEXT_SIZES
12
+ * 2. Longest-prefix match (e.g. "claude-opus-4-6-20250414" → "claude-opus-4-6")
13
+ * 3. DEFAULT_CONTEXT_WINDOW
14
+ */
15
+ export declare function getModelContextWindowSize(modelId?: string): number;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ /**
3
+ * Centralized model ID → context window size lookup.
4
+ * Single source of truth used by all providers and the CLI dashboard.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.DEFAULT_CONTEXT_WINDOW = void 0;
8
+ exports.getModelContextWindowSize = getModelContextWindowSize;
9
+ /** Known model context window sizes (in tokens). */
10
+ const MODEL_CONTEXT_SIZES = {
11
+ // Claude 4.6 family (1M context)
12
+ 'claude-opus-4-6': 1_000_000,
13
+ 'claude-sonnet-4-6': 1_000_000,
14
+ // Claude 4 family
15
+ 'claude-opus-4': 200_000,
16
+ 'claude-sonnet-4': 200_000,
17
+ // Claude 3.5 family
18
+ 'claude-3-5-sonnet': 200_000,
19
+ 'claude-3-5-haiku': 200_000,
20
+ // Claude 4.5 / Haiku
21
+ 'claude-haiku-4-5': 200_000,
22
+ // Claude 3 family
23
+ 'claude-3-opus': 200_000,
24
+ 'claude-3-sonnet': 200_000,
25
+ 'claude-3-haiku': 200_000,
26
+ // OpenAI GPT-4.1 series (1M context)
27
+ 'gpt-4.1': 1_048_576,
28
+ // OpenAI GPT-5 series
29
+ 'gpt-5': 400_000,
30
+ // OpenAI reasoning
31
+ 'o1': 200_000,
32
+ 'o3': 200_000,
33
+ 'o4': 200_000,
34
+ // OpenAI GPT-4 series
35
+ 'gpt-4o': 128_000,
36
+ 'gpt-4-turbo': 128_000,
37
+ 'gpt-4': 8_192,
38
+ // Gemini
39
+ 'gemini': 1_000_000,
40
+ // DeepSeek
41
+ 'deepseek': 128_000,
42
+ };
43
+ /** Prefix keys sorted longest-first for correct prefix matching. */
44
+ const SORTED_KEYS = Object.keys(MODEL_CONTEXT_SIZES)
45
+ .sort((a, b) => b.length - a.length);
46
+ /** Default context window size when model is unknown. */
47
+ exports.DEFAULT_CONTEXT_WINDOW = 200_000;
48
+ /**
49
+ * Returns the context window size for a model ID.
50
+ *
51
+ * Lookup order:
52
+ * 1. Exact match against MODEL_CONTEXT_SIZES
53
+ * 2. Longest-prefix match (e.g. "claude-opus-4-6-20250414" → "claude-opus-4-6")
54
+ * 3. DEFAULT_CONTEXT_WINDOW
55
+ */
56
+ function getModelContextWindowSize(modelId) {
57
+ if (!modelId)
58
+ return exports.DEFAULT_CONTEXT_WINDOW;
59
+ // Exact match
60
+ if (MODEL_CONTEXT_SIZES[modelId] !== undefined) {
61
+ return MODEL_CONTEXT_SIZES[modelId];
62
+ }
63
+ // Longest-prefix match
64
+ for (const key of SORTED_KEYS) {
65
+ if (modelId.startsWith(key)) {
66
+ return MODEL_CONTEXT_SIZES[key];
67
+ }
68
+ }
69
+ return exports.DEFAULT_CONTEXT_WINDOW;
70
+ }
@@ -19,6 +19,8 @@ import type { SessionProviderBase, SessionReader, SessionFileStats, SearchHit, P
19
19
  export declare class ClaudeCodeProvider implements SessionProviderBase {
20
20
  readonly id: ProviderId;
21
21
  readonly displayName = "Claude Code";
22
+ /** Runtime-reported context window limit (overrides static map when set). */
23
+ private dynamicContextWindowLimit;
22
24
  getSessionDirectory(workspacePath: string): string;
23
25
  discoverSessionDirectory(workspacePath: string): string | null;
24
26
  findActiveSession(workspacePath: string): string | null;
@@ -36,7 +38,9 @@ export declare class ClaudeCodeProvider implements SessionProviderBase {
36
38
  searchInSession(sessionPath: string, query: string, maxResults: number): SearchHit[];
37
39
  getProjectsBaseDir(): string;
38
40
  readSessionStats(sessionPath: string): SessionFileStats;
39
- getContextWindowLimit(_modelId?: string): number;
41
+ getContextWindowLimit(modelId?: string): number;
42
+ /** Set a runtime-reported context window limit (overrides static map). */
43
+ setDynamicContextWindowLimit(limit: number): void;
40
44
  /**
41
45
  * Returns the latest assistant message's token usage snapshot.
42
46
  *
@@ -49,6 +49,7 @@ const path = __importStar(require("path"));
49
49
  const jsonl_1 = require("../parsers/jsonl");
50
50
  const sessionPathResolver_1 = require("../parsers/sessionPathResolver");
51
51
  const subagentScanner_1 = require("../parsers/subagentScanner");
52
+ const modelContext_1 = require("../modelContext");
52
53
  /** Type guard for content blocks with a `type` string property */
53
54
  function isTypedBlock(block) {
54
55
  return block !== null && typeof block === 'object' && typeof block.type === 'string';
@@ -172,6 +173,8 @@ class ClaudeCodeReader {
172
173
  class ClaudeCodeProvider {
173
174
  id = 'claude-code';
174
175
  displayName = 'Claude Code';
176
+ /** Runtime-reported context window limit (overrides static map when set). */
177
+ dynamicContextWindowLimit = null;
175
178
  // --- Path resolution ---
176
179
  getSessionDirectory(workspacePath) {
177
180
  return (0, sessionPathResolver_1.getSessionDirectory)(workspacePath);
@@ -406,8 +409,14 @@ class ClaudeCodeProvider {
406
409
  };
407
410
  }
408
411
  // --- Optional methods ---
409
- getContextWindowLimit(_modelId) {
410
- return 200_000;
412
+ getContextWindowLimit(modelId) {
413
+ if (this.dynamicContextWindowLimit)
414
+ return this.dynamicContextWindowLimit;
415
+ return (0, modelContext_1.getModelContextWindowSize)(modelId);
416
+ }
417
+ /** Set a runtime-reported context window limit (overrides static map). */
418
+ setDynamicContextWindowLimit(limit) {
419
+ this.dynamicContextWindowLimit = limit;
411
420
  }
412
421
  /**
413
422
  * Returns the latest assistant message's token usage snapshot.
@@ -459,7 +468,7 @@ class ClaudeCodeProvider {
459
468
  }
460
469
  // --- Lifecycle ---
461
470
  dispose() {
462
- // No resources to clean up
471
+ this.dynamicContextWindowLimit = null;
463
472
  }
464
473
  }
465
474
  exports.ClaudeCodeProvider = ClaudeCodeProvider;
@@ -49,6 +49,7 @@ const os = __importStar(require("os"));
49
49
  const path = __importStar(require("path"));
50
50
  const codexParser_1 = require("../parsers/codexParser");
51
51
  const codexDatabase_1 = require("./codexDatabase");
52
+ const modelContext_1 = require("../modelContext");
52
53
  // ---------------------------------------------------------------------------
53
54
  // Helper functions
54
55
  // ---------------------------------------------------------------------------
@@ -897,22 +898,7 @@ class CodexProvider {
897
898
  // Prefer actual model_context_window reported by token_count events
898
899
  if (this.dynamicContextWindowLimit)
899
900
  return this.dynamicContextWindowLimit;
900
- if (!modelId)
901
- return 128_000;
902
- const id = modelId.toLowerCase();
903
- // GPT-4.1 series: 1M context
904
- if (id.startsWith('gpt-4.1'))
905
- return 1_048_576;
906
- // GPT-4o: 128K
907
- if (id.startsWith('gpt-4o') || id.startsWith('gpt-4'))
908
- return 128_000;
909
- // o3 / o4-mini: 200K
910
- if (id.startsWith('o3') || id.startsWith('o4'))
911
- return 200_000;
912
- // o1: 200K
913
- if (id.startsWith('o1'))
914
- return 200_000;
915
- return 128_000;
901
+ return (0, modelContext_1.getModelContextWindowSize)(modelId);
916
902
  }
917
903
  getCurrentUsageSnapshot(_sessionPath) {
918
904
  // Return the last token usage snapshot received via the CodexReader callback.
@@ -31,6 +31,8 @@ export declare class OpenCodeProvider implements SessionProviderBase {
31
31
  private dbStatus;
32
32
  /** Cache of session metadata populated during listing */
33
33
  private sessionMetaCache;
34
+ /** Runtime-reported context window limit (overrides static map when set). */
35
+ private dynamicContextWindowLimit;
34
36
  /** Lazy-initialize the database connection. */
35
37
  private ensureDb;
36
38
  getRuntimeStatus(): ProviderRuntimeStatus;
@@ -70,5 +72,7 @@ export declare class OpenCodeProvider implements SessionProviderBase {
70
72
  computeContextSize(usage: TokenUsage): number;
71
73
  getContextAttribution(sessionPath: string): ContextAttribution | null;
72
74
  getContextWindowLimit(modelId?: string): number;
75
+ /** Set a runtime-reported context window limit (overrides static map). */
76
+ setDynamicContextWindowLimit(limit: number): void;
73
77
  dispose(): void;
74
78
  }
@@ -57,6 +57,7 @@ const path = __importStar(require("path"));
57
57
  const child_process_1 = require("child_process");
58
58
  const openCodeParser_1 = require("../parsers/openCodeParser");
59
59
  const openCodeDatabase_1 = require("./openCodeDatabase");
60
+ const modelContext_1 = require("../modelContext");
60
61
  // ---------------------------------------------------------------------------
61
62
  // Helper functions
62
63
  // ---------------------------------------------------------------------------
@@ -611,6 +612,8 @@ class OpenCodeProvider {
611
612
  dbStatus = { available: false, kind: 'db_missing' };
612
613
  /** Cache of session metadata populated during listing */
613
614
  sessionMetaCache = new Map();
615
+ /** Runtime-reported context window limit (overrides static map when set). */
616
+ dynamicContextWindowLimit = null;
614
617
  /** Lazy-initialize the database connection. */
615
618
  ensureDb() {
616
619
  if (this.dbInitialized)
@@ -1525,31 +1528,13 @@ class OpenCodeProvider {
1525
1528
  }
1526
1529
  }
1527
1530
  getContextWindowLimit(modelId) {
1528
- if (!modelId)
1529
- return 200_000;
1530
- const id = modelId.toLowerCase();
1531
- // GPT-4.1 series: 1M context
1532
- if (id.startsWith('gpt-4.1'))
1533
- return 1_000_000;
1534
- // GPT-5 series: 400K context
1535
- if (id.startsWith('gpt-5'))
1536
- return 400_000;
1537
- // o1, o3, o4 series: 200K context
1538
- if (id.startsWith('o1') || id.startsWith('o3') || id.startsWith('o4'))
1539
- return 200_000;
1540
- // GPT-4o / GPT-4 series: 128K context
1541
- if (id.startsWith('gpt-4'))
1542
- return 128_000;
1543
- // Claude models: 200K context
1544
- if (id.startsWith('claude'))
1545
- return 200_000;
1546
- // Gemini models: 1M context
1547
- if (id.startsWith('gemini'))
1548
- return 1_000_000;
1549
- // DeepSeek models: 128K context
1550
- if (id.startsWith('deepseek'))
1551
- return 128_000;
1552
- return 200_000;
1531
+ if (this.dynamicContextWindowLimit)
1532
+ return this.dynamicContextWindowLimit;
1533
+ return (0, modelContext_1.getModelContextWindowSize)(modelId);
1534
+ }
1535
+ /** Set a runtime-reported context window limit (overrides static map). */
1536
+ setDynamicContextWindowLimit(limit) {
1537
+ this.dynamicContextWindowLimit = limit;
1553
1538
  }
1554
1539
  // --- Lifecycle ---
1555
1540
  dispose() {
@@ -1557,6 +1542,7 @@ class OpenCodeProvider {
1557
1542
  this.db = null;
1558
1543
  this.dbInitialized = false;
1559
1544
  this.sessionMetaCache.clear();
1545
+ this.dynamicContextWindowLimit = null;
1560
1546
  }
1561
1547
  }
1562
1548
  exports.OpenCodeProvider = OpenCodeProvider;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sidekick-shared",
3
- "version": "0.13.8",
3
+ "version": "0.14.1",
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",