sidekick-shared 0.24.2 → 0.24.4

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.js CHANGED
@@ -3,13 +3,13 @@
3
3
  * Public API for sidekick-shared.
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.addNote = exports.decisionFingerprint = exports.addDecision = exports.completeTask = exports.addTask = exports.updateJsonStoreAtomic = exports.atomicWriteJsonSync = exports.atomicWriteJson = exports.readClaudeCodePlanFiles = exports.getPlanAnalytics = exports.writePlans = exports.getLatestPlan = exports.readPlans = exports.readLatestHandoff = exports.readHistory = exports.readNotes = exports.readDecisions = exports.readTasks = exports.migrateLegacyProjectStores = exports.resolveProjectIdentity = exports.getProjectSlugRaw = exports.getProjectSlug = exports.encodeWorkspacePath = exports.getGlobalDataPath = exports.getProjectDataPath = exports.getConfigDir = exports.MAX_PLANS_PER_PROJECT = exports.PLAN_SCHEMA_VERSION = exports.createEmptyDataStore = exports.createEmptyTokenTotals = exports.HISTORICAL_SESSION_RETENTION_LIMIT = 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 = exports.isAggregateCodexLimit = exports.fileFingerprint = exports.observedSessionSourceFromProvider = exports.ObservedSessionCollector = exports.readSessionTranscript = exports.listRecentSessions = exports.projectSessionTranscript = exports.estimateSerializedTokens = exports.estimateTextTokens = exports.calculateNormalizedUsageCost = exports.extractNormalizedUsage = exports.normalizeProviderUsage = void 0;
7
- exports.createSessionContextProjector = exports.calculateSessionContextPressure = exports.buildSessionContextSnapshot = 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 = 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.getOpenCodeDataDir = exports.OpenCodeProvider = exports.ClaudeCodeProvider = exports.getAllDetectedProviders = exports.detectProvider = exports.derivePendingUserRequestV1 = exports.createProviderSessionAdapterV1 = exports.renderHandoffUrlTemplate = void 0;
8
- exports.scoreSessionQuality = exports.calculateQualityTrend = exports.PatternExtractor = exports.HeatmapTracker = exports.FrequencyTracker = exports.readErrorHistory = exports.getTopFailingTools = exports.appendErrorHistory = exports.MAX_ERROR_HISTORY_SESSIONS = exports.ERROR_HISTORY_SCHEMA_VERSION = exports.getSnapshotPath = exports.isSnapshotValid = exports.deleteSnapshot = exports.loadSnapshot = exports.saveSnapshot = exports.SessionMonitor = exports.parseTodoDependencies = exports.EventAggregator = exports.getRandomPhrase = exports.PHRASE_CATEGORIES = 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.formatTokenCount = exports.formatDurationMs = exports.createJsonlTail = exports.toFollowEvents = exports.createWatcher = exports.parseChangelog = exports.extractProposedPlanShared = exports.parsePlanMarkdownShared = exports.PlanExtractor = exports.segmentAssistantTurn = exports.reasoningSummary = exports.isAssistantTurnSubagentTool = exports.extractTurnSubagents = exports.assistantTurnEventsFromSessionEvents = exports.readSessionContextSnapshot = void 0;
9
- exports.getCodexProfileHome = exports.getCodexProfilesDir = exports.readClaudeProfileIdentity = exports.isClaudeProfileAuthenticated = exports.claudeKeychainService = exports.claudeKeychainSuffix = exports.getClaudeProfileHome = exports.getClaudeProfilesDir = exports.getActiveAccountStatus = exports.removeSavedAccountProfile = exports.replaceSavedAccountProfiles = exports.setActiveSavedAccount = exports.upsertSavedAccountProfile = exports.getActiveSavedAccount = exports.listSavedAccountProfiles = exports.writeSavedAccountRegistry = exports.readSavedAccountRegistry = exports.getAccountsDir = exports.resolveClaudeLoginCommand = exports.listAllAccounts = exports.switchAccount = exports.spawnAccountLogin = exports.finalizeAccountLogin = exports.getAccountLoginStatus = exports.beginAccountLogin = exports.isMultiAccountEnabled = exports.resolveActiveClaudeAccount = exports.getActiveAccount = exports.listAccounts = exports.removeAccount = exports.reconcileClaudeAuthState = exports.applyActiveClaudeToLiveHome = exports.resolveActiveClaudeHome = exports.switchToAccount = exports.addCurrentAccount = exports.readActiveClaudeAccount = exports.writeAccountRegistry = exports.readAccountRegistry = exports.ensureDefaultAccounts = exports.readClaudeMaxAccessTokenSync = exports.readClaudeMaxCredentials = exports.writeActiveCredentials = exports.readActiveCredentials = exports.openInBrowser = exports.parseTranscriptFromEvents = exports.parseTranscript = exports.generateHtmlReport = exports.formatCompactionLedger = exports.calculateCompactionLedger = exports.calculateCodeImpact = void 0;
10
- exports.selectStatuslineAccount = exports.formatStatusline = exports.estimateTimeToQuota = exports.BurnRateCalculator = exports.MultiProviderQuotaService = exports.resolveZaiQuota = exports.readZaiCredentials = exports.quotaStateFromZaiQuotaLimitPayload = exports.fetchZaiQuotaFromApi = exports.CodexQuotaWatcher = exports.resolveCodexQuotaFromLocalSources = exports.resolveCodexQuota = exports.readLatestCodexQuotaFromRollouts = exports.quotaFromCodexRateLimits = exports.fetchCodexQuotaFromApi = exports.fetchCodexResetCreditsFromApi = exports.getWorkspaceIdFromPath = exports.pruneQuotaHistory = exports.readQuotaHistoryDailyBuckets = exports.readQuotaHistoryRange = exports.appendQuotaHistorySample = exports.writeQuotaSnapshot = exports.readQuotaSnapshot = exports.QuotaPoller = exports.describeQuotaFailure = exports.withQuotaProjections = exports.projectQuotaWindow = exports.fetchQuota = exports.SEVEN_DAY_WINDOW_MS = exports.FIVE_HOUR_WINDOW_MS = exports.AutoSwitchController = exports.decideAutoSwitch = exports.DEFAULT_AUTO_SWITCH_CONFIG = exports.removeLauncher = exports.writeLauncher = exports.isShellHookInstalled = exports.uninstallShellHook = exports.installShellHook = exports.setTerminalActiveProfile = exports.removeCodexAccount = exports.switchToCodexAccount = exports.finalizeCodexAccount = exports.prepareCodexAccount = exports.getCodexExecutionEnv = exports.resolveSidekickCodexHome = exports.resolveActiveCodexAccount = exports.getActiveCodexAccount = exports.listCodexAccounts = exports.getSystemCodexHome = exports.getCodexMonitoringHomes = void 0;
11
- exports.runtimeQuotaProviderSchema = exports.quotaFailureDescriptorSchema = exports.peakHoursStateSchema = exports.codexResetCreditsSnapshotSchema = exports.codexResetCreditSchema = exports.quotaSourceSchema = exports.quotaProviderIdSchema = exports.quotaFailureKindSchema = exports.quotaStateSchema = exports.quotaWindowSchema = exports.extractSessionEvents = exports.permissionModeSchema = exports.sessionEventSchema = exports.sessionMessageSchema = exports.messageUsageSchema = exports.ToolCallTracker = exports.extractToolCalls = exports.extractToolCall = exports.extractErrorMessage = exports.categorizeError = exports.gatherAssetsForCwd = exports.codexSessions = exports.readCodexAssets = exports.claudeSessions = exports.readClaudeAssets = exports.extractCommands = exports.extractFilePaths = exports.extractUrls = exports.extractTokenUsage = exports.getObservedContextWindowPath = exports.recordObservedContextWindow = exports.loadObservedContextWindows = exports.LITELLM_CATALOG_URL = exports.normalizeLiteLlmContextWindows = exports.normalizeLiteLlmCatalog = exports.hydratePricingCatalog = exports.formatCost = exports.sortModelIds = exports.compareModelIds = exports.getModelDisplayInfo = exports.shortModelName = exports.mergeCostSources = exports.calculateCostWithProvenance = exports.calculateCostWithPricing = exports.calculateCost = exports.getModelInfo = exports.getModelPricing = exports.parseModelId = exports.DEFAULT_CONTEXT_WINDOW = exports.getModelContextWindowSize = void 0;
12
- exports.scopePeakHoursToSessionProvider = exports.isClaudeCodeSessionProvider = exports.fetchPeakHoursStatus = exports.createPeakHoursNotApplicableState = exports.runDoctor = exports.getSessionDiagnostics = exports.formatHealthReport = exports.fetchOpenAIStatus = exports.fetchProviderStatus = exports.assistantTurnToolRefSchema = exports.assistantTurnToolGroupStepSchema = exports.assistantTurnTimelineItemSchema = exports.assistantTurnSubagentStatusSchema = exports.assistantTurnSubagentSchema = exports.assistantTurnReasoningTimelineItemSchema = exports.assistantTurnProjectionSchema = exports.assistantTurnProcessStepSchema = exports.assistantTurnProcessSchema = exports.assistantTurnNarrationStepSchema = exports.assistantTurnEventTypeSchema = exports.assistantTurnEventSchema = exports.listAllAccountsResultSchema = exports.savedAccountProfileSchema = exports.accountEntrySchema = exports.accountLoginStatusSchema = exports.beginAccountLoginResultSchema = exports.accountManagerResultSchema = exports.accountProviderIdSchema = exports.activeAccountStatusSchema = exports.activeProviderAccountStatusSchema = exports.quotaHistoryDailyBucketSchema = exports.quotaHistorySampleSchema = exports.quotaHistoryRuntimeProviderSchema = exports.providerQuotaMapSchema = exports.zaiProviderQuotaStateSchema = exports.codexProviderQuotaStateSchema = exports.claudeProviderQuotaStateSchema = exports.providerQuotaStateSchema = void 0;
6
+ exports.addTask = exports.updateJsonStoreAtomic = exports.atomicWriteJsonSync = exports.atomicWriteJson = exports.readClaudeCodePlanFiles = exports.getPlanAnalytics = exports.writePlans = exports.getLatestPlan = exports.readPlans = exports.readLatestHandoff = exports.readHistory = exports.readNotes = exports.readDecisions = exports.readTasks = exports.migrateLegacyProjectStores = exports.resolveProjectIdentity = exports.getProjectSlugRaw = exports.getProjectSlug = exports.encodeWorkspacePath = exports.getGlobalDataPath = exports.getProjectDataPath = exports.setConfigDir = exports.getDefaultConfigDir = exports.getConfigDirOverride = exports.getConfigDir = exports.CONFIG_DIR_ENV_VAR = exports.MAX_PLANS_PER_PROJECT = exports.PLAN_SCHEMA_VERSION = exports.createEmptyDataStore = exports.createEmptyTokenTotals = exports.HISTORICAL_SESSION_RETENTION_LIMIT = 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 = exports.isAggregateCodexLimit = exports.fileFingerprint = exports.observedSessionSourceFromProvider = exports.ObservedSessionCollector = exports.readSessionTranscript = exports.listRecentSessions = exports.projectSessionTranscript = exports.estimateSerializedTokens = exports.estimateTextTokens = exports.calculateNormalizedUsageCost = exports.extractNormalizedUsage = exports.normalizeProviderUsage = void 0;
7
+ 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 = 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.getOpenCodeDataDir = exports.OpenCodeProvider = exports.ClaudeCodeProvider = exports.getAllDetectedProviders = exports.detectProvider = exports.derivePendingUserRequestV1 = exports.createProviderSessionAdapterV1 = exports.renderHandoffUrlTemplate = exports.addNote = exports.decisionFingerprint = exports.addDecision = exports.completeTask = void 0;
8
+ exports.FrequencyTracker = exports.readErrorHistory = exports.getTopFailingTools = exports.appendErrorHistory = exports.MAX_ERROR_HISTORY_SESSIONS = exports.ERROR_HISTORY_SCHEMA_VERSION = exports.getSnapshotPath = exports.isSnapshotValid = exports.deleteSnapshot = exports.loadSnapshot = exports.saveSnapshot = exports.SessionMonitor = exports.parseTodoDependencies = exports.EventAggregator = exports.getRandomPhrase = exports.PHRASE_CATEGORIES = 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.formatTokenCount = exports.formatDurationMs = exports.createJsonlTail = exports.toFollowEvents = exports.createWatcher = exports.parseChangelog = exports.extractProposedPlanShared = exports.parsePlanMarkdownShared = exports.PlanExtractor = exports.segmentAssistantTurn = exports.reasoningSummary = exports.isAssistantTurnSubagentTool = exports.extractTurnSubagents = exports.assistantTurnEventsFromSessionEvents = exports.readSessionContextSnapshot = exports.createSessionContextProjector = exports.calculateSessionContextPressure = exports.buildSessionContextSnapshot = exports.composeContext = void 0;
9
+ exports.claudeKeychainService = exports.claudeKeychainSuffix = exports.getClaudeProfileHome = exports.getClaudeProfilesDir = exports.getActiveAccountStatus = exports.removeSavedAccountProfile = exports.replaceSavedAccountProfiles = exports.setActiveSavedAccount = exports.upsertSavedAccountProfile = exports.getActiveSavedAccount = exports.listSavedAccountProfiles = exports.writeSavedAccountRegistry = exports.readSavedAccountRegistry = exports.getAccountsDir = exports.resolveClaudeLoginCommand = exports.listAllAccounts = exports.switchAccount = exports.spawnAccountLogin = exports.finalizeAccountLogin = exports.getAccountLoginStatus = exports.beginAccountLogin = exports.isMultiAccountEnabled = exports.resolveActiveClaudeAccount = exports.getActiveAccount = exports.listAccounts = exports.removeAccount = exports.reconcileClaudeAuthState = exports.applyActiveClaudeToLiveHome = exports.resolveActiveClaudeHome = exports.switchToAccount = exports.addCurrentAccount = exports.readActiveClaudeAccount = exports.writeAccountRegistry = exports.readAccountRegistry = exports.ensureDefaultAccounts = exports.readClaudeMaxAccessTokenSync = exports.readClaudeMaxCredentials = exports.writeActiveCredentials = exports.readActiveCredentials = exports.openInBrowser = exports.parseTranscriptFromEvents = exports.parseTranscript = exports.generateHtmlReport = exports.formatCompactionLedger = exports.calculateCompactionLedger = exports.calculateCodeImpact = exports.scoreSessionQuality = exports.calculateQualityTrend = exports.PatternExtractor = exports.HeatmapTracker = void 0;
10
+ exports.MultiProviderQuotaService = exports.resolveZaiQuota = exports.readZaiCredentials = exports.quotaStateFromZaiQuotaLimitPayload = exports.fetchZaiQuotaFromApi = exports.CodexQuotaWatcher = exports.resolveCodexQuotaFromLocalSources = exports.resolveCodexQuota = exports.readLatestCodexQuotaFromRollouts = exports.quotaFromCodexRateLimits = exports.fetchCodexQuotaFromApi = exports.fetchCodexResetCreditsFromApi = exports.getWorkspaceIdFromPath = exports.pruneQuotaHistory = exports.readQuotaHistoryDailyBuckets = exports.readQuotaHistoryRange = exports.appendQuotaHistorySample = exports.writeQuotaSnapshot = exports.readQuotaSnapshot = exports.QuotaPoller = exports.describeQuotaFailure = exports.withQuotaProjections = exports.projectQuotaWindow = exports.fetchQuota = exports.SEVEN_DAY_WINDOW_MS = exports.FIVE_HOUR_WINDOW_MS = exports.AutoSwitchController = exports.decideAutoSwitch = exports.DEFAULT_AUTO_SWITCH_CONFIG = exports.removeLauncher = exports.writeLauncher = exports.isShellHookInstalled = exports.uninstallShellHook = exports.installShellHook = exports.setTerminalActiveProfile = exports.removeCodexAccount = exports.switchToCodexAccount = exports.finalizeCodexAccount = exports.prepareCodexAccount = exports.getCodexExecutionEnv = exports.resolveSidekickCodexHome = exports.resolveActiveCodexAccount = exports.getActiveCodexAccount = exports.listCodexAccounts = exports.getSystemCodexHome = exports.getCodexMonitoringHomes = exports.getCodexProfileHome = exports.getCodexProfilesDir = exports.readClaudeProfileIdentity = exports.isClaudeProfileAuthenticated = void 0;
11
+ exports.codexResetCreditSchema = exports.quotaSourceSchema = exports.quotaProviderIdSchema = exports.quotaFailureKindSchema = exports.quotaStateSchema = exports.quotaWindowSchema = exports.extractSessionEvents = exports.permissionModeSchema = exports.sessionEventSchema = exports.sessionMessageSchema = exports.messageUsageSchema = exports.ToolCallTracker = exports.extractToolCalls = exports.extractToolCall = exports.extractErrorMessage = exports.categorizeError = exports.gatherAssetsForCwd = exports.codexSessions = exports.readCodexAssets = exports.claudeSessions = exports.readClaudeAssets = exports.extractCommands = exports.extractFilePaths = exports.extractUrls = exports.extractTokenUsage = exports.getObservedContextWindowPath = exports.recordObservedContextWindow = exports.loadObservedContextWindows = exports.LITELLM_CATALOG_URL = exports.normalizeLiteLlmContextWindows = exports.normalizeLiteLlmCatalog = exports.hydratePricingCatalog = exports.formatCost = exports.sortModelIds = exports.compareModelIds = exports.getModelDisplayInfo = exports.shortModelName = exports.mergeCostSources = exports.calculateCostWithProvenance = exports.calculateCostWithPricing = exports.calculateCost = exports.getModelInfo = exports.getModelPricing = exports.parseModelId = exports.DEFAULT_CONTEXT_WINDOW = exports.getModelContextWindowSize = exports.selectStatuslineAccount = exports.formatStatusline = exports.estimateTimeToQuota = exports.BurnRateCalculator = void 0;
12
+ exports.scopePeakHoursToSessionProvider = exports.isClaudeCodeSessionProvider = exports.fetchPeakHoursStatus = exports.DEFAULT_PEAK_HOURS_TIMEOUT_MS = exports.createPeakHoursNotApplicableState = exports.runDoctor = exports.getSessionDiagnostics = exports.formatHealthReport = exports.fetchOpenAIStatus = exports.fetchProviderStatus = exports.assistantTurnToolRefSchema = exports.assistantTurnToolGroupStepSchema = exports.assistantTurnTimelineItemSchema = exports.assistantTurnSubagentStatusSchema = exports.assistantTurnSubagentSchema = exports.assistantTurnReasoningTimelineItemSchema = exports.assistantTurnProjectionSchema = exports.assistantTurnProcessStepSchema = exports.assistantTurnProcessSchema = exports.assistantTurnNarrationStepSchema = exports.assistantTurnEventTypeSchema = exports.assistantTurnEventSchema = exports.listAllAccountsResultSchema = exports.savedAccountProfileSchema = exports.accountEntrySchema = exports.accountLoginStatusSchema = exports.beginAccountLoginResultSchema = exports.accountManagerResultSchema = exports.accountProviderIdSchema = exports.activeAccountStatusSchema = exports.activeProviderAccountStatusSchema = exports.quotaHistoryDailyBucketSchema = exports.quotaHistorySampleSchema = exports.quotaHistoryRuntimeProviderSchema = exports.providerQuotaMapSchema = exports.zaiProviderQuotaStateSchema = exports.codexProviderQuotaStateSchema = exports.claudeProviderQuotaStateSchema = exports.providerQuotaStateSchema = exports.runtimeQuotaProviderSchema = exports.quotaFailureDescriptorSchema = exports.peakHoursStateSchema = exports.codexResetCreditsSnapshotSchema = void 0;
13
13
  var usageNormalization_1 = require("./usageNormalization");
14
14
  Object.defineProperty(exports, "normalizeProviderUsage", { enumerable: true, get: function () { return usageNormalization_1.normalizeProviderUsage; } });
15
15
  Object.defineProperty(exports, "extractNormalizedUsage", { enumerable: true, get: function () { return usageNormalization_1.extractNormalizedUsage; } });
@@ -47,7 +47,11 @@ Object.defineProperty(exports, "PLAN_SCHEMA_VERSION", { enumerable: true, get: f
47
47
  Object.defineProperty(exports, "MAX_PLANS_PER_PROJECT", { enumerable: true, get: function () { return plan_1.MAX_PLANS_PER_PROJECT; } });
48
48
  // Paths
49
49
  var paths_1 = require("./paths");
50
+ Object.defineProperty(exports, "CONFIG_DIR_ENV_VAR", { enumerable: true, get: function () { return paths_1.CONFIG_DIR_ENV_VAR; } });
50
51
  Object.defineProperty(exports, "getConfigDir", { enumerable: true, get: function () { return paths_1.getConfigDir; } });
52
+ Object.defineProperty(exports, "getConfigDirOverride", { enumerable: true, get: function () { return paths_1.getConfigDirOverride; } });
53
+ Object.defineProperty(exports, "getDefaultConfigDir", { enumerable: true, get: function () { return paths_1.getDefaultConfigDir; } });
54
+ Object.defineProperty(exports, "setConfigDir", { enumerable: true, get: function () { return paths_1.setConfigDir; } });
51
55
  Object.defineProperty(exports, "getProjectDataPath", { enumerable: true, get: function () { return paths_1.getProjectDataPath; } });
52
56
  Object.defineProperty(exports, "getGlobalDataPath", { enumerable: true, get: function () { return paths_1.getGlobalDataPath; } });
53
57
  Object.defineProperty(exports, "encodeWorkspacePath", { enumerable: true, get: function () { return paths_1.encodeWorkspacePath; } });
@@ -494,6 +498,7 @@ Object.defineProperty(exports, "runDoctor", { enumerable: true, get: function ()
494
498
  // Peak Hours (PromoClock — third-party)
495
499
  var peakHours_1 = require("./peakHours");
496
500
  Object.defineProperty(exports, "createPeakHoursNotApplicableState", { enumerable: true, get: function () { return peakHours_1.createPeakHoursNotApplicableState; } });
501
+ Object.defineProperty(exports, "DEFAULT_PEAK_HOURS_TIMEOUT_MS", { enumerable: true, get: function () { return peakHours_1.DEFAULT_PEAK_HOURS_TIMEOUT_MS; } });
497
502
  Object.defineProperty(exports, "fetchPeakHoursStatus", { enumerable: true, get: function () { return peakHours_1.fetchPeakHoursStatus; } });
498
503
  Object.defineProperty(exports, "isClaudeCodeSessionProvider", { enumerable: true, get: function () { return peakHours_1.isClaudeCodeSessionProvider; } });
499
504
  Object.defineProperty(exports, "scopePeakHoursToSessionProvider", { enumerable: true, get: function () { return peakHours_1.scopePeakHoursToSessionProvider; } });
@@ -63,6 +63,7 @@ const MODEL_CONTEXT_SIZES = {
63
63
  // observed-override layer.
64
64
  'gpt-5.6-sol': 1_050_000,
65
65
  'gpt-5.6-terra': 1_050_000,
66
+ 'gpt-5.6-luna': 1_050_000,
66
67
  'gpt-5.6': 1_050_000,
67
68
  'gpt-5.5': 1_050_000,
68
69
  'gpt-5.4': 1_050_000,
package/dist/modelInfo.js CHANGED
@@ -78,11 +78,14 @@ const PRICING_TABLE = {
78
78
  },
79
79
  // Sonnet 5 carries the standard rate here. The introductory $2/$10 (through
80
80
  // 2026-08-31) arrives via catalog hydration, which supersedes this baseline.
81
+ // Sonnet 5 is $2/$10, not the $3/$15 every earlier Sonnet charged, and it
82
+ // carries no >200K surcharge the way Sonnet 4.5 does. The catalog agrees
83
+ // across all eleven of its Sonnet 5 entries.
81
84
  'claude-sonnet-5': {
82
- inputCostPerMillion: 3.0,
83
- outputCostPerMillion: 15.0,
84
- cacheWriteCostPerMillion: 3.75,
85
- cacheReadCostPerMillion: 0.3,
85
+ inputCostPerMillion: 2.0,
86
+ outputCostPerMillion: 10.0,
87
+ cacheWriteCostPerMillion: 2.5,
88
+ cacheReadCostPerMillion: 0.2,
86
89
  },
87
90
  'claude-sonnet-4-6': {
88
91
  inputCostPerMillion: 3.0,
@@ -176,6 +179,21 @@ const PRICING_TABLE = {
176
179
  cacheReadCostPerMillion: 1.5,
177
180
  },
178
181
  // ── OpenAI: GPT-4.x family ──
182
+ // The `-tts`, `-transcribe`, and `-realtime-preview` variants also bill above
183
+ // the key they inherit, but no coding agent routes to them, so they are left
184
+ // out rather than carried here. Catalog hydration still prices them correctly.
185
+ 'gpt-4.1-mini': {
186
+ inputCostPerMillion: 0.4,
187
+ outputCostPerMillion: 1.6,
188
+ cacheWriteCostPerMillion: 0,
189
+ cacheReadCostPerMillion: 0.1,
190
+ },
191
+ 'gpt-4.1-nano': {
192
+ inputCostPerMillion: 0.1,
193
+ outputCostPerMillion: 0.4,
194
+ cacheWriteCostPerMillion: 0,
195
+ cacheReadCostPerMillion: 0.025,
196
+ },
179
197
  'gpt-4.1': {
180
198
  inputCostPerMillion: 2.0,
181
199
  outputCostPerMillion: 8.0,
@@ -188,6 +206,14 @@ const PRICING_TABLE = {
188
206
  cacheWriteCostPerMillion: 0,
189
207
  cacheReadCostPerMillion: 0.075,
190
208
  },
209
+ // The launch snapshot kept its original $5/$15 when gpt-4o was cut to
210
+ // $2.50/$10, so a pinned id must not read the current rate.
211
+ 'gpt-4o-2024-05-13': {
212
+ inputCostPerMillion: 5.0,
213
+ outputCostPerMillion: 15.0,
214
+ cacheWriteCostPerMillion: 0,
215
+ cacheReadCostPerMillion: 0,
216
+ },
191
217
  'gpt-4o': {
192
218
  inputCostPerMillion: 2.5,
193
219
  outputCostPerMillion: 10.0,
@@ -202,8 +228,12 @@ const PRICING_TABLE = {
202
228
  },
203
229
  // ── OpenAI: GPT-5 family ──
204
230
  // Published rates, verified against the LiteLLM catalog. Runtime hydration
205
- // still supersedes these; they are the offline baseline. Entries below
206
- // `gpt-5.4` predate the published-rate pass and remain rough estimates.
231
+ // still supersedes these; they are the offline baseline.
232
+ //
233
+ // Every tier that ships at a rate of its own needs an entry of its own. Keys
234
+ // are matched longest-first, so a `-mini`/`-nano`/`-pro` variant with no entry
235
+ // silently inherits its base model's rate and reports a wrong number rather
236
+ // than none — `gpt-5-nano` read 25x its true cost this way.
207
237
  'gpt-5.6-sol': {
208
238
  inputCostPerMillion: 5.0,
209
239
  outputCostPerMillion: 30.0,
@@ -216,12 +246,24 @@ const PRICING_TABLE = {
216
246
  cacheWriteCostPerMillion: 3.13,
217
247
  cacheReadCostPerMillion: 0.25,
218
248
  },
249
+ 'gpt-5.6-luna': {
250
+ inputCostPerMillion: 1.0,
251
+ outputCostPerMillion: 6.0,
252
+ cacheWriteCostPerMillion: 1.25,
253
+ cacheReadCostPerMillion: 0.1,
254
+ },
219
255
  'gpt-5.6': {
220
256
  inputCostPerMillion: 5.0,
221
257
  outputCostPerMillion: 30.0,
222
258
  cacheWriteCostPerMillion: 6.25,
223
259
  cacheReadCostPerMillion: 0.5,
224
260
  },
261
+ 'gpt-5.5-pro': {
262
+ inputCostPerMillion: 30.0,
263
+ outputCostPerMillion: 180.0,
264
+ cacheWriteCostPerMillion: 0,
265
+ cacheReadCostPerMillion: 3.0,
266
+ },
225
267
  'gpt-5.5': {
226
268
  inputCostPerMillion: 5.0,
227
269
  outputCostPerMillion: 30.0,
@@ -234,6 +276,18 @@ const PRICING_TABLE = {
234
276
  cacheWriteCostPerMillion: 0,
235
277
  cacheReadCostPerMillion: 0.07,
236
278
  },
279
+ 'gpt-5.4-nano': {
280
+ inputCostPerMillion: 0.2,
281
+ outputCostPerMillion: 1.25,
282
+ cacheWriteCostPerMillion: 0,
283
+ cacheReadCostPerMillion: 0.02,
284
+ },
285
+ 'gpt-5.4-pro': {
286
+ inputCostPerMillion: 30.0,
287
+ outputCostPerMillion: 180.0,
288
+ cacheWriteCostPerMillion: 0,
289
+ cacheReadCostPerMillion: 3.0,
290
+ },
237
291
  'gpt-5.4': {
238
292
  inputCostPerMillion: 2.5,
239
293
  outputCostPerMillion: 15.0,
@@ -241,17 +295,63 @@ const PRICING_TABLE = {
241
295
  cacheReadCostPerMillion: 0.25,
242
296
  },
243
297
  'gpt-5.3-codex': {
244
- inputCostPerMillion: 1.25,
245
- outputCostPerMillion: 10.0,
298
+ inputCostPerMillion: 1.75,
299
+ outputCostPerMillion: 14.0,
246
300
  cacheWriteCostPerMillion: 0,
247
- cacheReadCostPerMillion: 0.125,
301
+ cacheReadCostPerMillion: 0.175,
302
+ },
303
+ 'gpt-5.3-chat-latest': {
304
+ inputCostPerMillion: 1.75,
305
+ outputCostPerMillion: 14.0,
306
+ cacheWriteCostPerMillion: 0,
307
+ cacheReadCostPerMillion: 0.175,
248
308
  },
309
+ // No bare `gpt-5.3` ships in the catalog; both real variants above are
310
+ // $1.75/$14. This entry stays an estimate and only answers ids that match
311
+ // neither variant.
249
312
  'gpt-5.3': {
250
313
  inputCostPerMillion: 1.25,
251
314
  outputCostPerMillion: 10.0,
252
315
  cacheWriteCostPerMillion: 0,
253
316
  cacheReadCostPerMillion: 0.125,
254
317
  },
318
+ 'gpt-5.2-pro': {
319
+ inputCostPerMillion: 21.0,
320
+ outputCostPerMillion: 168.0,
321
+ cacheWriteCostPerMillion: 0,
322
+ cacheReadCostPerMillion: 0,
323
+ },
324
+ // Also answers `gpt-5.2-codex` and `gpt-5.2-chat*`, which bill at this rate.
325
+ 'gpt-5.2': {
326
+ inputCostPerMillion: 1.75,
327
+ outputCostPerMillion: 14.0,
328
+ cacheWriteCostPerMillion: 0,
329
+ cacheReadCostPerMillion: 0.175,
330
+ },
331
+ 'gpt-5.1-codex-mini': {
332
+ inputCostPerMillion: 0.25,
333
+ outputCostPerMillion: 2.0,
334
+ cacheWriteCostPerMillion: 0,
335
+ cacheReadCostPerMillion: 0.025,
336
+ },
337
+ 'gpt-5-pro': {
338
+ inputCostPerMillion: 15.0,
339
+ outputCostPerMillion: 120.0,
340
+ cacheWriteCostPerMillion: 0,
341
+ cacheReadCostPerMillion: 0,
342
+ },
343
+ 'gpt-5-mini': {
344
+ inputCostPerMillion: 0.25,
345
+ outputCostPerMillion: 2.0,
346
+ cacheWriteCostPerMillion: 0,
347
+ cacheReadCostPerMillion: 0.025,
348
+ },
349
+ 'gpt-5-nano': {
350
+ inputCostPerMillion: 0.05,
351
+ outputCostPerMillion: 0.4,
352
+ cacheWriteCostPerMillion: 0,
353
+ cacheReadCostPerMillion: 0.005,
354
+ },
255
355
  'gpt-5': {
256
356
  inputCostPerMillion: 1.25,
257
357
  outputCostPerMillion: 10.0,
@@ -259,23 +359,46 @@ const PRICING_TABLE = {
259
359
  cacheReadCostPerMillion: 0.125,
260
360
  },
261
361
  // ── OpenAI: o-series (reasoning models) ──
362
+ // The `-pro` and deep-research tiers cost an order of magnitude more than the
363
+ // base model whose key they would otherwise match.
364
+ 'o3-deep-research': {
365
+ inputCostPerMillion: 10.0,
366
+ outputCostPerMillion: 40.0,
367
+ cacheWriteCostPerMillion: 0,
368
+ cacheReadCostPerMillion: 2.5,
369
+ },
262
370
  'o3-mini': {
263
371
  inputCostPerMillion: 1.1,
264
372
  outputCostPerMillion: 4.4,
265
373
  cacheWriteCostPerMillion: 0,
266
374
  cacheReadCostPerMillion: 0.55,
267
375
  },
376
+ 'o3-pro': {
377
+ inputCostPerMillion: 20.0,
378
+ outputCostPerMillion: 80.0,
379
+ cacheWriteCostPerMillion: 0,
380
+ cacheReadCostPerMillion: 0,
381
+ },
268
382
  o3: {
269
383
  inputCostPerMillion: 2.0,
270
384
  outputCostPerMillion: 8.0,
271
385
  cacheWriteCostPerMillion: 0,
272
386
  cacheReadCostPerMillion: 0.5,
273
387
  },
388
+ // OpenAI cut o1-mini to $1.10/$4.40 after launch; $3/$12 was the launch rate.
389
+ // No bare catalog key settles it — Azure's $1.21/$4.84 is its usual 1.1x
390
+ // uplift on exactly this number, and Replicate's OpenAI passthrough agrees.
274
391
  'o1-mini': {
275
- inputCostPerMillion: 3.0,
276
- outputCostPerMillion: 12.0,
392
+ inputCostPerMillion: 1.1,
393
+ outputCostPerMillion: 4.4,
277
394
  cacheWriteCostPerMillion: 0,
278
- cacheReadCostPerMillion: 1.5,
395
+ cacheReadCostPerMillion: 0.55,
396
+ },
397
+ 'o1-pro': {
398
+ inputCostPerMillion: 150.0,
399
+ outputCostPerMillion: 600.0,
400
+ cacheWriteCostPerMillion: 0,
401
+ cacheReadCostPerMillion: 0,
279
402
  },
280
403
  o1: {
281
404
  inputCostPerMillion: 15.0,
@@ -309,7 +432,12 @@ function _clearPricingOverrides() {
309
432
  overrideSortedKeys = [];
310
433
  }
311
434
  // ── Model ID Parsing ──
312
- const CLAUDE_RE = /^claude-(haiku|sonnet|opus|fable)-([0-9.]+)/i;
435
+ // Anthropic writes the minor version with a hyphen in modern IDs
436
+ // (`claude-sonnet-4-6-20260321`) and a dot in older ones
437
+ // (`claude-sonnet-4.5-20241022`). Accept both, then normalize to a dot.
438
+ // The `{1,2}` bound plus the trailing `(?![0-9])` keep the 8-digit release
439
+ // date from being mistaken for a minor version in `claude-opus-4-20250514`.
440
+ const CLAUDE_RE = /^claude-(haiku|sonnet|opus|fable)-([0-9]+(?:[-.][0-9]{1,2})?)(?![0-9])/i;
313
441
  const LEGACY_CLAUDE_RE = /^claude-([0-9]+(?:[-.][0-9]+)?)-(haiku|sonnet|opus)(?:-|$)/i;
314
442
  const GPT_RE = /^gpt-([0-9][0-9.A-Za-z-]*)/i;
315
443
  const O_SERIES_RE = /^o([0-9]+)(-mini|-pro)?/i;
@@ -332,7 +460,11 @@ function parseModelId(modelId) {
332
460
  .toLowerCase();
333
461
  const claude = normalized.match(CLAUDE_RE);
334
462
  if (claude) {
335
- return { provider: 'anthropic', family: claude[1].toLowerCase(), version: claude[2] };
463
+ return {
464
+ provider: 'anthropic',
465
+ family: claude[1].toLowerCase(),
466
+ version: claude[2].replace('-', '.'),
467
+ };
336
468
  }
337
469
  const legacyClaude = normalized.match(LEGACY_CLAUDE_RE);
338
470
  if (legacyClaude) {
@@ -33,6 +33,8 @@ export interface ObservedContextWindowStore {
33
33
  }
34
34
  /** Resolve the on-disk store path. */
35
35
  export declare function getObservedContextWindowPath(cacheDir?: string): string;
36
+ /** Reset the per-store write dedupe. Test-only; not part of the public API. */
37
+ export declare function _clearObservedContextWindowWriteCache(): void;
36
38
  /**
37
39
  * Load persisted observations and apply them as context-window overrides.
38
40
  *
@@ -44,11 +46,12 @@ export declare function loadObservedContextWindows(options?: ObservedContextWind
44
46
  * Record a provider-reported context window for a model.
45
47
  *
46
48
  * Applies in memory immediately, then persists — but only when the value
47
- * actually changed. Codex reports a window on every `token_count` event, so an
48
- * unconditional write would mean disk I/O many times per session for a value
49
- * that almost never moves.
49
+ * actually changed *for that store*. Codex reports a window on every
50
+ * `token_count` event, so an unconditional write would mean disk I/O many times
51
+ * per session for a value that almost never moves.
50
52
  *
51
53
  * Always resolves — never throws. Persistence failures are logged and dropped;
52
- * the in-memory override still stands for this process.
54
+ * the in-memory override still stands for this process, and the next call
55
+ * retries the write.
53
56
  */
54
57
  export declare function recordObservedContextWindow(modelId: string, contextWindow: number, options?: ObservedContextWindowOptions): Promise<void>;
@@ -52,6 +52,7 @@ var __importStar = (this && this.__importStar) || (function () {
52
52
  })();
53
53
  Object.defineProperty(exports, "__esModule", { value: true });
54
54
  exports.getObservedContextWindowPath = getObservedContextWindowPath;
55
+ exports._clearObservedContextWindowWriteCache = _clearObservedContextWindowWriteCache;
55
56
  exports.loadObservedContextWindows = loadObservedContextWindows;
56
57
  exports.recordObservedContextWindow = recordObservedContextWindow;
57
58
  const node_fs_1 = require("node:fs");
@@ -69,6 +70,21 @@ function getObservedContextWindowPath(cacheDir) {
69
70
  function emptyStore() {
70
71
  return { version: STORE_VERSION, models: {} };
71
72
  }
73
+ /**
74
+ * Last value successfully persisted, per resolved store path.
75
+ *
76
+ * The in-memory override table in `modelContext` is process-global by design —
77
+ * a lookup should answer the same way everywhere. That makes it unusable as the
78
+ * write dedupe: with two `cacheDir`s in one process (two temp dirs in a test
79
+ * suite, a multi-tenant host, a CLI and extension host sharing a process), the
80
+ * global would already hold the value from the first store and the second store
81
+ * would never be written at all.
82
+ */
83
+ const lastPersistedByStore = new Map();
84
+ /** Reset the per-store write dedupe. Test-only; not part of the public API. */
85
+ function _clearObservedContextWindowWriteCache() {
86
+ lastPersistedByStore.clear();
87
+ }
72
88
  /** Keep only well-formed entries; a hand-edited or partly-corrupt file shouldn't poison lookups. */
73
89
  function toOverrideMap(store) {
74
90
  const out = {};
@@ -110,12 +126,13 @@ async function loadObservedContextWindows(options = {}) {
110
126
  * Record a provider-reported context window for a model.
111
127
  *
112
128
  * Applies in memory immediately, then persists — but only when the value
113
- * actually changed. Codex reports a window on every `token_count` event, so an
114
- * unconditional write would mean disk I/O many times per session for a value
115
- * that almost never moves.
129
+ * actually changed *for that store*. Codex reports a window on every
130
+ * `token_count` event, so an unconditional write would mean disk I/O many times
131
+ * per session for a value that almost never moves.
116
132
  *
117
133
  * Always resolves — never throws. Persistence failures are logged and dropped;
118
- * the in-memory override still stands for this process.
134
+ * the in-memory override still stands for this process, and the next call
135
+ * retries the write.
119
136
  */
120
137
  async function recordObservedContextWindow(modelId, contextWindow, options = {}) {
121
138
  const normalizedId = modelId?.trim().toLowerCase();
@@ -124,10 +141,13 @@ async function recordObservedContextWindow(modelId, contextWindow, options = {})
124
141
  if (!Number.isFinite(contextWindow) || contextWindow <= 0)
125
142
  return;
126
143
  const current = (0, modelContext_1._getObservedContextWindows)();
127
- if (current[normalizedId] === contextWindow)
128
- return;
129
- (0, modelContext_1._setObservedContextWindows)({ ...current, [normalizedId]: contextWindow });
144
+ if (current[normalizedId] !== contextWindow) {
145
+ (0, modelContext_1._setObservedContextWindows)({ ...current, [normalizedId]: contextWindow });
146
+ }
130
147
  const storePath = getObservedContextWindowPath(options.cacheDir);
148
+ const persisted = lastPersistedByStore.get(storePath);
149
+ if (persisted?.get(normalizedId) === contextWindow)
150
+ return;
131
151
  try {
132
152
  // Lock-guarded read-modify-write: the CLI and the extension host can both
133
153
  // be observing sessions at once, and each should merge rather than clobber.
@@ -143,6 +163,13 @@ async function recordObservedContextWindow(modelId, contextWindow, options = {})
143
163
  },
144
164
  };
145
165
  });
166
+ // Recorded only on success, so a transient EACCES retries next time.
167
+ let store = lastPersistedByStore.get(storePath);
168
+ if (!store) {
169
+ store = new Map();
170
+ lastPersistedByStore.set(storePath, store);
171
+ }
172
+ store.set(normalizedId, contextWindow);
146
173
  }
147
174
  catch (err) {
148
175
  options.logger?.(`observedContextWindows: failed to persist ${storePath}: ${String(err)}`);
package/dist/paths.d.ts CHANGED
@@ -3,9 +3,44 @@
3
3
  * Mirrors patterns from sidekick-vscode/src/services/SessionPathResolver.ts
4
4
  * and TaskPersistenceService.ts
5
5
  */
6
+ /** Environment variable that redirects the Sidekick config root. */
7
+ export declare const CONFIG_DIR_ENV_VAR = "SIDEKICK_CONFIG_DIR";
8
+ /**
9
+ * The platform default config directory, ignoring both {@link setConfigDir}
10
+ * and `SIDEKICK_CONFIG_DIR`.
11
+ *
12
+ * `~/.config/sidekick` on Unix, `%APPDATA%/sidekick` on Windows.
13
+ */
14
+ export declare function getDefaultConfigDir(): string;
15
+ /**
16
+ * Redirect the Sidekick config root for this process.
17
+ *
18
+ * Takes precedence over `SIDEKICK_CONFIG_DIR`. Pass `null` (or an empty
19
+ * string) to clear it. Every reader and writer in the package resolves the
20
+ * root lazily through {@link getConfigDir}, so this takes effect immediately
21
+ * for all of them — including transitive callers of `getProjectDataPath`,
22
+ * `getGlobalDataPath`, and the account registry.
23
+ *
24
+ * Prefer `SIDEKICK_CONFIG_DIR` when the value is known before the process
25
+ * starts: it is read fresh on every call, so it stays correct even if a
26
+ * bundler ends up with more than one copy of this module.
27
+ *
28
+ * @example
29
+ * setConfigDir(path.join(os.tmpdir(), 'sidekick-fixture'));
30
+ * try {
31
+ * await addTask(project, { subject: 'isolated' });
32
+ * } finally {
33
+ * setConfigDir(null);
34
+ * }
35
+ */
36
+ export declare function setConfigDir(dir: string | null | undefined): void;
37
+ /** The active {@link setConfigDir} override, or `null` when unset. */
38
+ export declare function getConfigDirOverride(): string | null;
6
39
  /**
7
40
  * Gets the Sidekick config directory.
8
- * ~/.config/sidekick on Unix, %APPDATA%/sidekick on Windows.
41
+ *
42
+ * Resolution order: {@link setConfigDir} override → `SIDEKICK_CONFIG_DIR`
43
+ * → {@link getDefaultConfigDir}.
9
44
  */
10
45
  export declare function getConfigDir(): string;
11
46
  /**
package/dist/paths.js CHANGED
@@ -38,6 +38,10 @@ var __importStar = (this && this.__importStar) || (function () {
38
38
  };
39
39
  })();
40
40
  Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.CONFIG_DIR_ENV_VAR = void 0;
42
+ exports.getDefaultConfigDir = getDefaultConfigDir;
43
+ exports.setConfigDir = setConfigDir;
44
+ exports.getConfigDirOverride = getConfigDirOverride;
41
45
  exports.getConfigDir = getConfigDir;
42
46
  exports.getProjectDataPath = getProjectDataPath;
43
47
  exports.getGlobalDataPath = getGlobalDataPath;
@@ -48,16 +52,68 @@ exports.getProjectSlugRaw = getProjectSlugRaw;
48
52
  const fs = __importStar(require("fs"));
49
53
  const path = __importStar(require("path"));
50
54
  const os = __importStar(require("os"));
55
+ /** Environment variable that redirects the Sidekick config root. */
56
+ exports.CONFIG_DIR_ENV_VAR = 'SIDEKICK_CONFIG_DIR';
57
+ let configDirOverride = null;
51
58
  /**
52
- * Gets the Sidekick config directory.
53
- * ~/.config/sidekick on Unix, %APPDATA%/sidekick on Windows.
59
+ * The platform default config directory, ignoring both {@link setConfigDir}
60
+ * and `SIDEKICK_CONFIG_DIR`.
61
+ *
62
+ * `~/.config/sidekick` on Unix, `%APPDATA%/sidekick` on Windows.
54
63
  */
55
- function getConfigDir() {
64
+ function getDefaultConfigDir() {
56
65
  if (process.platform === 'win32') {
57
66
  return path.join(process.env.APPDATA || os.homedir(), 'sidekick');
58
67
  }
59
68
  return path.join(os.homedir(), '.config', 'sidekick');
60
69
  }
70
+ /**
71
+ * Redirect the Sidekick config root for this process.
72
+ *
73
+ * Takes precedence over `SIDEKICK_CONFIG_DIR`. Pass `null` (or an empty
74
+ * string) to clear it. Every reader and writer in the package resolves the
75
+ * root lazily through {@link getConfigDir}, so this takes effect immediately
76
+ * for all of them — including transitive callers of `getProjectDataPath`,
77
+ * `getGlobalDataPath`, and the account registry.
78
+ *
79
+ * Prefer `SIDEKICK_CONFIG_DIR` when the value is known before the process
80
+ * starts: it is read fresh on every call, so it stays correct even if a
81
+ * bundler ends up with more than one copy of this module.
82
+ *
83
+ * @example
84
+ * setConfigDir(path.join(os.tmpdir(), 'sidekick-fixture'));
85
+ * try {
86
+ * await addTask(project, { subject: 'isolated' });
87
+ * } finally {
88
+ * setConfigDir(null);
89
+ * }
90
+ */
91
+ function setConfigDir(dir) {
92
+ // Resolve the trimmed value, not the original: `path.resolve(' /tmp/x ')`
93
+ // treats the leading spaces as a relative segment and lands under cwd. The
94
+ // `SIDEKICK_CONFIG_DIR` branch in getConfigDir() already trims, and the two
95
+ // routes are documented as interchangeable.
96
+ const trimmed = dir?.trim();
97
+ configDirOverride = trimmed ? path.resolve(trimmed) : null;
98
+ }
99
+ /** The active {@link setConfigDir} override, or `null` when unset. */
100
+ function getConfigDirOverride() {
101
+ return configDirOverride;
102
+ }
103
+ /**
104
+ * Gets the Sidekick config directory.
105
+ *
106
+ * Resolution order: {@link setConfigDir} override → `SIDEKICK_CONFIG_DIR`
107
+ * → {@link getDefaultConfigDir}.
108
+ */
109
+ function getConfigDir() {
110
+ if (configDirOverride)
111
+ return configDirOverride;
112
+ const fromEnv = process.env[exports.CONFIG_DIR_ENV_VAR]?.trim();
113
+ if (fromEnv)
114
+ return path.resolve(fromEnv);
115
+ return getDefaultConfigDir();
116
+ }
61
117
  /**
62
118
  * Gets the path to a project-specific data file.
63
119
  * e.g., ~/.config/sidekick/tasks/my-project.json
@@ -23,6 +23,16 @@ export interface PeakHoursState {
23
23
  unavailable: boolean;
24
24
  notApplicable?: boolean;
25
25
  }
26
+ /** Default budget for the promoclock.co request, in milliseconds. */
27
+ export declare const DEFAULT_PEAK_HOURS_TIMEOUT_MS = 10000;
28
+ /** Options for {@link fetchPeakHoursStatus}. */
29
+ export interface FetchPeakHoursOptions {
30
+ /**
31
+ * Abort the request after this many milliseconds.
32
+ * Defaults to {@link DEFAULT_PEAK_HOURS_TIMEOUT_MS}.
33
+ */
34
+ timeoutMs?: number;
35
+ }
26
36
  export declare function isClaudeCodeSessionProvider(providerId: ProviderId): boolean;
27
37
  export declare function createPeakHoursNotApplicableState(providerId: ProviderId): PeakHoursState;
28
38
  export declare function scopePeakHoursToSessionProvider(providerId: ProviderId, status: PeakHoursState | null | undefined): PeakHoursState | null;
@@ -30,6 +40,10 @@ export declare function scopePeakHoursToSessionProvider(providerId: ProviderId,
30
40
  * Fetch current Claude peak-hours state from promoclock.co.
31
41
  *
32
42
  * Single-shot — caller wraps in polling loop, EventEmitter, or interval.
33
- * Returns `unavailable: true` on any network or parse error.
43
+ * Returns `unavailable: true` on any network, timeout, or parse error.
44
+ *
45
+ * promoclock.co is a third-party host, so the request is always bounded by
46
+ * an abort timeout. Without one a hung host would stall the caller — and
47
+ * this is polled, so the stall would repeat.
34
48
  */
35
- export declare function fetchPeakHoursStatus(): Promise<PeakHoursState>;
49
+ export declare function fetchPeakHoursStatus(options?: FetchPeakHoursOptions): Promise<PeakHoursState>;
package/dist/peakHours.js CHANGED
@@ -11,11 +11,14 @@
11
11
  * Polling / eventing is the caller's responsibility.
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.DEFAULT_PEAK_HOURS_TIMEOUT_MS = void 0;
14
15
  exports.isClaudeCodeSessionProvider = isClaudeCodeSessionProvider;
15
16
  exports.createPeakHoursNotApplicableState = createPeakHoursNotApplicableState;
16
17
  exports.scopePeakHoursToSessionProvider = scopePeakHoursToSessionProvider;
17
18
  exports.fetchPeakHoursStatus = fetchPeakHoursStatus;
18
19
  const PROMOCLOCK_ENDPOINT = 'https://promoclock.co/api/status';
20
+ /** Default budget for the promoclock.co request, in milliseconds. */
21
+ exports.DEFAULT_PEAK_HOURS_TIMEOUT_MS = 10_000;
19
22
  function unavailableState() {
20
23
  return {
21
24
  status: 'unknown',
@@ -73,11 +76,19 @@ function normalizeSpeed(raw) {
73
76
  * Fetch current Claude peak-hours state from promoclock.co.
74
77
  *
75
78
  * Single-shot — caller wraps in polling loop, EventEmitter, or interval.
76
- * Returns `unavailable: true` on any network or parse error.
79
+ * Returns `unavailable: true` on any network, timeout, or parse error.
80
+ *
81
+ * promoclock.co is a third-party host, so the request is always bounded by
82
+ * an abort timeout. Without one a hung host would stall the caller — and
83
+ * this is polled, so the stall would repeat.
77
84
  */
78
- async function fetchPeakHoursStatus() {
85
+ async function fetchPeakHoursStatus(options = {}) {
86
+ const timeoutMs = options.timeoutMs ?? exports.DEFAULT_PEAK_HOURS_TIMEOUT_MS;
87
+ const controller = new AbortController();
88
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
89
+ timeout.unref();
79
90
  try {
80
- const res = await fetch(PROMOCLOCK_ENDPOINT);
91
+ const res = await fetch(PROMOCLOCK_ENDPOINT, { signal: controller.signal });
81
92
  if (!res.ok)
82
93
  return unavailableState();
83
94
  const data = await res.json();
@@ -98,6 +109,10 @@ async function fetchPeakHoursStatus() {
98
109
  };
99
110
  }
100
111
  catch {
112
+ // Covers AbortError from the timeout above alongside network/parse failures.
101
113
  return unavailableState();
102
114
  }
115
+ finally {
116
+ clearTimeout(timeout);
117
+ }
103
118
  }