Skip to content

Add macOS usage and cost center - #430

Merged
SergeSerb2 merged 10 commits into
mainfrom
surgecode/usage-cost-center
Jul 31, 2026
Merged

Add macOS usage and cost center#430
SergeSerb2 merged 10 commits into
mainfrom
surgecode/usage-cost-center

Conversation

@SergeSerb2

@SergeSerb2 SergeSerb2 commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add a native macOS Usage center with date presets and custom ranges, project/provider/thread filters, cache-efficiency and token totals, partial-cost signaling, and provider/model rollups.
  • Persist token-efficiency visibility and cost-warning preferences through the existing server settings contract.
  • Extend the usage summary RPC with exact project filtering so totals include persisted threads, including archived or unloaded sessions.
  • Add Swift wire/model coverage, usage-center logic tests, mock data, and a UI probe snapshot.

Area

  • apps/mac — native macOS app
  • apps/windows — Windows desktop app
  • apps/mobile — iPhone companion app
  • apps/server — backend server
  • Shared packages (packages/contracts, packages/shared, packages/client-runtime, …) or relay (infra/relay)
  • Build, CI, or release tooling
  • Docs

Release size

  • size:XS — tiny internal, documentation, or narrowly scoped fix
  • size:S — small user-visible fix or contained improvement
  • size:M — normal feature or meaningful behavior change
  • size:L — broad feature or substantial cross-package change
  • size:XL — release-system, architecture, or ecosystem-scale change

Verification

  • pnpm run verify --all passes (check + typecheck + tests; adds lint:mobile and the Swift suite)
  • pnpm run test:mac passes (required for apps/mac changes)
  • Manually verified the Usage settings screen from a packaged debug app using mock usage data.

Screenshots / Recordings

The Usage-center screen was captured and inspected during the macOS UI probe. The local capture is available at /tmp/surgecode-usage-probe.m7s8fr/13b-settings-usage.png.

Release notes

Adds a Usage center for inspecting token consumption, cache efficiency, estimated cost, and provider/model breakdowns across configurable date windows and filters.

@SergeSerb2 SergeSerb2 added the size:M Normal feature or meaningful behavior change label Jul 31, 2026

@SergeSerb2 SergeSerb2 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR adds macOS usage and cost tracking. Architecture is sound (protocol + impl + mocks aligned), but the diff of ServerMetaRpc.swift is truncated mid-function, blocking verification of RPC types. Critical: verify the server-side AppSettings schema includes tokenEfficiency as non-optional before merge — if missing, settings loads will crash.

Could not anchor

  • important apps/mac/Sources/T3Kit/ServerMetaRpc.swift — ServerMetaRpc.swift diff ends mid-function at the encode() method. Cannot verify complete RPC type definitions for providerUsageSummary() call or wire schema. Confirm the full file changes include ProviderUsageSummaryInput and ProviderUsageSummary types with server API alignment.

SurgeCode auto-review · model=codex/gpt-5.6-luna · head=e6155d760f6c

projectOverrides: overrides),
tokenEfficiency: AppTokenEfficiencySettings(
showComposerHints: settings.tokenEfficiency.showComposerHints,
showCostWarnings: settings.tokenEfficiency.showCostWarnings,

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking: Settings decoding will crash if server response omits tokenEfficiency field. Verify server-side AppSettings schema includes this field (non-optional or with default). This is a breaking change if server support is incomplete.

.number.precision(.fractionLength(0...2)))
},
set: { text in
let normalized = text.replacingOccurrences(of: ",", with: ".")

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Invalid USD threshold input (e.g. "abc") silently converts to nil via Double() with no user feedback. TextField clears silently. Consider validating on blur or showing an error hint so users know input was rejected.

@SergeSerb2
SergeSerb2 marked this pull request as ready for review July 31, 2026 15:45

@SergeSerb2 SergeSerb2 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adds usage tracking and cost center configuration to macOS app. Implementation includes new entity types for usage queries/summaries, settings for token efficiency (profile, cost warnings, composer hints), and a comprehensive usage reporting UI with filtering by date, project, provider, and thread.

Review blocker: ServerMetaRpc.swift diff is truncated mid-method (line ~3600), preventing review of the TokenEfficiencySettingsPatch.encode() wire protocol implementation. Cannot verify complete serialization correctness. Clarify: verify textFieldStyle(.settings) is defined in the project (not a standard SwiftUI style; if missing, compilation fails).

Could not anchor

  • blocking apps/mac/Sources/T3Kit/ServerMetaRpc.swift — Diff is truncated mid-implementation of TokenEfficiencySettingsPatch.encode(to:) method. Cannot review wire protocol serialization logic or verify field mappings to server contract. Provide complete file or confirm the truncation is not due to incomplete implementation.
  • important apps/mac/Sources/SurgeCodeMac/UI/Settings/UsageSettingsTab.swift — New usage querying, cost calculations, and date-window logic added with no unit tests. Recommend tests for: AppUsageSummary filtering (by provider/thread/date), AppUsageTokens.cacheEfficiency edge cases (zero values), UsageDateWindow.bounds() calculations, and UsageFormat number/currency formatting with edge cases (zero, partial costs, nil values).

SurgeCode auto-review · model=codex/gpt-5.6-luna · head=46ea9c615bd8

normalized.isEmpty ? nil : Double(normalized)
}))
.textFieldStyle(.settings)
.multilineTextAlignment(.trailing)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

important: textFieldStyle(.settings) is not a standard SwiftUI style. Verify this is defined as a custom TextFieldStyle extension in the project; if missing, will cause compilation failure.

@@ -1836,6 +1929,7 @@ public struct AppSettings: Hashable, Sendable {
self.addProjectBaseDirectory = addProjectBaseDirectory
self.workflowModelRouting = workflowModelRouting

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

important: AppSettings adds new field tokenEfficiency with non-optional default. Verify JSON decoding/encoding round-trips correctly when the field is missing from persisted settings (e.g., old app versions). The default initializer provides AppTokenEfficiencySettings(), which should work if Codable is synthesized, but confirm integration tests cover settings load/save cycle.

showCostWarnings: settings.tokenEfficiency.showCostWarnings,
efficiencyProfile: settings.tokenEfficiency.profile.rawValue,
perTurnEstimatedCostWarningUsd: settings.tokenEfficiency.perTurnWarningUsd,
dailyEstimatedCostWarningUsd: settings.tokenEfficiency.dailyWarningUsd,

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

info: Date formatting uses ISO8601DateFormatter with .withFractionalSeconds. Confirm the server's ProviderUsageSummaryInput accepts this format; if server uses a different ISO8601 variant, the query may fail silently with mismatched timestamps.

@SergeSerb2 SergeSerb2 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR adds usage tracking, cost reporting, and worktree lifecycle management to the macOS app. Core model, protocol, and backend implementations look sound. No blocking issues found.

Notable points:

  • Worktree operations (create/repair/remove) properly check thread status and have rollback on error.
  • Usage query filtering by project/provider/thread follows expected patterns.
  • New ChatThread fields (branch, worktreePath) correctly added to equality check.
  • Task group concurrency in worktreeSnapshots() is idiomatic.

Minor concerns: refreshVcsStatus() errors silently fall back; directoryAllocatedSize() could be slow on large repos; verify UsageDateWindow.bounds() handles date math correctly.


SurgeCode auto-review · model=codex/gpt-5.6-luna · head=cf2c79be03c8

let page = try? await client.getArchivedShellSnapshot(
cursor: archivedCursor, limit: 100)
else { break }
ownedThreads.append(contentsOf: page.threads.map(mapThread))

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: refreshVcsStatus() error is silently ignored, leaving isDirty and refName at fallback values. For status-critical operations, consider logging the failure or exposing a degraded-status flag so callers know the result may be stale.

guard let values = try? url.resourceValues(forKeys: keys),
values.isSymbolicLink != true, values.isRegularFile == true
else { continue }
total += Int64(values.totalFileAllocatedSize ?? values.fileAllocatedSize ?? 0)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

info: directoryAllocatedSize() enumerates every file under the worktree. On large repos (multi-GB), this can block. If performance becomes an issue, consider depth limits or sampling. For now this is fine, but worth documenting if the snapshot call ever feels slow.

let providers = all.byProvider.filter { $0.id == providerID }
guard let bucket = providers.first else {
return AppUsageSummary(
turns: 0,

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: When providerInstanceID is not found in byProvider, the mock returns an empty byProvider array but still filters byModel by that provider prefix. This can result in byModel buckets with no matching provider entry. Either filter both or filter neither to keep the structure consistent.

@SergeSerb2
SergeSerb2 merged commit a1dfddf into main Jul 31, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M Normal feature or meaningful behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant