sidekick-shared 0.24.3 → 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/CHANGELOG.md +412 -0
- package/LICENSE +21 -0
- package/README.md +87 -16
- package/dist/index.d.ts +3 -3
- package/dist/index.js +12 -7
- package/dist/modelInfo.js +11 -2
- package/dist/observedContextWindows.d.ts +7 -4
- package/dist/observedContextWindows.js +34 -7
- package/dist/paths.d.ts +36 -1
- package/dist/paths.js +59 -3
- package/dist/peakHours.d.ts +16 -2
- package/dist/peakHours.js +18 -3
- package/dist/pricingCatalog.d.ts +7 -3
- package/dist/pricingCatalog.js +3 -2
- package/dist/zaiQuota.d.ts +87 -0
- package/dist/zaiQuota.js +49 -0
- package/dist/zaiQuotaWatcher.d.ts +7 -0
- package/dist/zaiQuotaWatcher.js +3 -0
- package/package.json +60 -2
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.
|
|
7
|
-
exports.
|
|
8
|
-
exports.
|
|
9
|
-
exports.
|
|
10
|
-
exports.
|
|
11
|
-
exports.
|
|
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; } });
|
package/dist/modelInfo.js
CHANGED
|
@@ -432,7 +432,12 @@ function _clearPricingOverrides() {
|
|
|
432
432
|
overrideSortedKeys = [];
|
|
433
433
|
}
|
|
434
434
|
// ── Model ID Parsing ──
|
|
435
|
-
|
|
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;
|
|
436
441
|
const LEGACY_CLAUDE_RE = /^claude-([0-9]+(?:[-.][0-9]+)?)-(haiku|sonnet|opus)(?:-|$)/i;
|
|
437
442
|
const GPT_RE = /^gpt-([0-9][0-9.A-Za-z-]*)/i;
|
|
438
443
|
const O_SERIES_RE = /^o([0-9]+)(-mini|-pro)?/i;
|
|
@@ -455,7 +460,11 @@ function parseModelId(modelId) {
|
|
|
455
460
|
.toLowerCase();
|
|
456
461
|
const claude = normalized.match(CLAUDE_RE);
|
|
457
462
|
if (claude) {
|
|
458
|
-
return {
|
|
463
|
+
return {
|
|
464
|
+
provider: 'anthropic',
|
|
465
|
+
family: claude[1].toLowerCase(),
|
|
466
|
+
version: claude[2].replace('-', '.'),
|
|
467
|
+
};
|
|
459
468
|
}
|
|
460
469
|
const legacyClaude = normalized.match(LEGACY_CLAUDE_RE);
|
|
461
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
|
|
48
|
-
* unconditional write would mean disk I/O many times
|
|
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
|
|
114
|
-
* unconditional write would mean disk I/O many times
|
|
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]
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
53
|
-
*
|
|
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
|
|
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
|
package/dist/peakHours.d.ts
CHANGED
|
@@ -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
|
}
|
package/dist/pricingCatalog.d.ts
CHANGED
|
@@ -18,8 +18,12 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { type ModelPricing } from './modelInfo';
|
|
20
20
|
export interface HydrateOptions {
|
|
21
|
-
/**
|
|
22
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Directory for the on-disk cache. Defaults to the Sidekick config dir,
|
|
23
|
+
* which honors `SIDEKICK_CONFIG_DIR` and `setConfigDir()` — pass this only
|
|
24
|
+
* to place the cache somewhere outside the config root.
|
|
25
|
+
*/
|
|
26
|
+
cacheDir?: string;
|
|
23
27
|
/** Inject a fetch implementation (tests, custom agents). Defaults to `globalThis.fetch`. */
|
|
24
28
|
fetchImpl?: typeof fetch;
|
|
25
29
|
/** Cache TTL in ms. Default 24h. */
|
|
@@ -52,7 +56,7 @@ export declare const LITELLM_CATALOG_URL = "https://raw.githubusercontent.com/Be
|
|
|
52
56
|
*
|
|
53
57
|
* Always resolves — never throws. Offline-safe.
|
|
54
58
|
*/
|
|
55
|
-
export declare function hydratePricingCatalog(options
|
|
59
|
+
export declare function hydratePricingCatalog(options?: HydrateOptions): Promise<HydrateResult>;
|
|
56
60
|
/**
|
|
57
61
|
* Convert a LiteLLM catalog payload to our `ModelPricing` override map.
|
|
58
62
|
* Exported for tests; rarely called directly.
|
package/dist/pricingCatalog.js
CHANGED
|
@@ -59,6 +59,7 @@ const node_fs_1 = require("node:fs");
|
|
|
59
59
|
const path = __importStar(require("node:path"));
|
|
60
60
|
const modelContext_1 = require("./modelContext");
|
|
61
61
|
const modelInfo_1 = require("./modelInfo");
|
|
62
|
+
const paths_1 = require("./paths");
|
|
62
63
|
// ── Constants ──
|
|
63
64
|
exports.LITELLM_CATALOG_URL = 'https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json';
|
|
64
65
|
const CACHE_FILE_NAME = 'pricing-catalog.json';
|
|
@@ -75,8 +76,8 @@ const DEFAULT_TIMEOUT_MS = 3_000;
|
|
|
75
76
|
*
|
|
76
77
|
* Always resolves — never throws. Offline-safe.
|
|
77
78
|
*/
|
|
78
|
-
async function hydratePricingCatalog(options) {
|
|
79
|
-
const { cacheDir, fetchImpl = globalThis.fetch, ttlMs = DEFAULT_TTL_MS, timeoutMs = DEFAULT_TIMEOUT_MS, url = exports.LITELLM_CATALOG_URL, logger, } = options;
|
|
79
|
+
async function hydratePricingCatalog(options = {}) {
|
|
80
|
+
const { cacheDir = (0, paths_1.getConfigDir)(), fetchImpl = globalThis.fetch, ttlMs = DEFAULT_TTL_MS, timeoutMs = DEFAULT_TIMEOUT_MS, url = exports.LITELLM_CATALOG_URL, logger, } = options;
|
|
80
81
|
const cachePath = path.join(cacheDir, CACHE_FILE_NAME);
|
|
81
82
|
const now = Date.now();
|
|
82
83
|
// 1. Fresh cache?
|