Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Resources/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.2.2</string>
<string>0.2.4</string>
<key>CFBundleVersion</key>
<string>4</string>
<string>5</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.developer-tools</string>
<key>LSMinimumSystemVersion</key>
Expand Down
74 changes: 73 additions & 1 deletion Sources/CodexLimits/AnalyticsWorkspace.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,19 @@ enum AnalyticsGraph: String, CaseIterable, Codable, Identifiable, Sendable {

var id: String { rawValue }

static let coreCases: [AnalyticsGraph] = [
.usageRemaining,
.tokenActivity
]

var usesAccountScope: Bool {
self == .usageRemaining || self == .usagePerToken
self == .usageRemaining
|| self == .tokenActivity
|| self == .usagePerToken
}

var usesLocalAnalytics: Bool {
self == .usagePerToken || self == .concurrency
}
}

Expand Down Expand Up @@ -62,6 +73,57 @@ enum AnalyticsTimeRange: String, CaseIterable, Codable, Identifiable, Sendable {
}
}

struct AccountTokenActivityRange: Equatable, Sendable {
let days: [TokenDay]
let completeDayCount: Int
let completeTokens: Int64?

init(days: [TokenDay], interval: DateInterval) {
let day: TimeInterval = 86_400
self.days = days
.filter {
$0.date < interval.end
&& $0.date.addingTimeInterval(day) > interval.start
}
.sorted { $0.date < $1.date }

var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = TimeZone(secondsFromGMT: 0)
?? calendar.timeZone
let startOfDay = calendar.startOfDay(for: interval.start)
var expected = startOfDay < interval.start
? startOfDay.addingTimeInterval(day)
: startOfDay
var expectedDates: [Date] = []
while expected.addingTimeInterval(day) <= interval.end {
expectedDates.append(expected)
expected = expected.addingTimeInterval(day)
}
completeDayCount = expectedDates.count

guard !expectedDates.isEmpty else {
completeTokens = nil
return
}
var total: Int64 = 0
for date in expectedDates {
guard let bucket = self.days.first(where: { $0.date == date }),
bucket.completeness == .complete,
bucket.tokens >= 0 else {
completeTokens = nil
return
}
let result = total.addingReportingOverflow(bucket.tokens)
guard !result.overflow else {
completeTokens = nil
return
}
total = result.partialValue
}
completeTokens = total
}
}

struct WorkspaceFilters: Codable, Equatable, Sendable {
var projectID: String?
var taskTreeID: String?
Expand Down Expand Up @@ -101,6 +163,10 @@ struct AnalyticsExplorationState: Codable, Equatable, Sendable {
pinnedUsageBaselineID: nil,
pinnedUsageBaselineAccountPartitionID: nil
)

var usesLocalAnalytics: Bool {
section == .graphs && graph.usesLocalAnalytics
}
}

@MainActor
Expand Down Expand Up @@ -133,6 +199,12 @@ final class AnalyticsWorkspaceStore: ObservableObject {
) else {
return .initial
}
guard AnalyticsGraph.coreCases.contains(restored.graph) else {
var core = restored
core.graph = .usageRemaining
core.filters = .all
return core
}
return restored
}

Expand Down
19 changes: 17 additions & 2 deletions Sources/CodexLimits/LocalActivityCollector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,21 @@ actor LocalActivityCollector {
importContinuationPending
}

func releaseCachedFacts() {
refreshGeneration = nextRevision(after: refreshGeneration)
guard stateURL != nil else { return }
if !changedPaths.isEmpty, !persist() {
return
}
guard changedPaths.isEmpty,
pendingFactWrites.isEmpty else {
return
}
restartPartialFactRestores()
clearPublishedContent()
unloadPersistedFacts(activePaths: [])
}

func deleteHistory(at deletedAt: Date = Date()) throws {
historyCutoff = deletedAt
historyDeletionPending = stateDirectory != nil
Expand Down Expand Up @@ -1256,9 +1271,9 @@ actor LocalActivityCollector {
guard let directory = stateURL else { return }
for path in Array(files.keys) {
guard var state = files[path],
state.factsLoaded,
!changedPaths.contains(path),
pendingFactWrites[path] == nil else {
pendingFactWrites[path] == nil,
state.factsLoaded || !activePaths.contains(path) else {
continue
}
let file = factsURL(
Expand Down
Loading