Skip to content

feat: add option to disable automatic updates#250

Open
vegerot wants to merge 4 commits into
JerryZLiu:mainfrom
vegerot:pr249
Open

feat: add option to disable automatic updates#250
vegerot wants to merge 4 commits into
JerryZLiu:mainfrom
vegerot:pr249

Conversation

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a “developer-controlled” switch to prevent Sparkle from silently auto-installing updates/restarting, while also refactoring LLM prompt/transcript plumbing (schemas + shared utilities) and expanding local analytics logging.

Changes:

  • Add a SilentUserDriver.shouldAutoUpdateAndRestart flag and update Sparkle user-driver behavior to skip install/relaunch when disabled.
  • Centralize LLM JSON schemas/prompts and transcript/timeline validation utilities; refactor Gemini provider to use them.
  • Mirror analytics events to stdout and Apple Unified Logging (OSLog).

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
Dayflow/Dayflow/Views/UI/Settings/SettingsProvidersTabView.swift Passes an explicit provider into TestConnectionView.
Dayflow/Dayflow/Views/UI/Settings/ProvidersSettingsViewModel.swift Switches Gemini prompt override persistence to shared “video prompt” preferences/types.
Dayflow/Dayflow/Views/Onboarding/TestConnectionView.swift Generalizes connection testing by provider and standardizes analytics + result handling.
Dayflow/Dayflow/Views/Onboarding/OnboardingLLMSelectionView.swift Makes provider-card sizing dynamic based on actual card count.
Dayflow/Dayflow/System/UpdaterManager.swift Adjusts Sparkle relaunch handling (but currently risks blocking termination).
Dayflow/Dayflow/System/SilentUserDriver.swift Adds the auto-update/restart gating flag and applies it to Sparkle callbacks.
Dayflow/Dayflow/System/AnalyticsService.swift Adds local mirroring of analytics events to stdout + OSLog.
Dayflow/Dayflow/Core/Analysis/TimeParsing.swift Adds shared utilities for LLM video timestamp parsing, transcript decoding, and timeline validation.
Dayflow/Dayflow/Core/AI/LLMSchema.swift New: central JSON schema strings for structured model output.
Dayflow/Dayflow/Core/AI/GeminiPromptPreferences.swift Renames prompt override/prefs types and adds shared LLMPromptTemplates.
Dayflow/Dayflow/Core/AI/GeminiDirectProvider.swift Refactors prompts/transcript parsing/validation; introduces JSON-schema wiring in generation config.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Task { @MainActor in
print("[Sparkle] Updater will relaunch application")
AppDelegate.allowTermination = true
AppDelegate.allowTermination = false
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

updaterWillRelaunchApplication is called right before Sparkle terminates/relaunches the app, but this sets AppDelegate.allowTermination = false, which will cause applicationShouldTerminate to return .terminateCancel and can block the relaunch. Set this to true (or avoid flipping it to false here) so Sparkle can terminate the app during relaunch.

Suggested change
AppDelegate.allowTermination = false
AppDelegate.allowTermination = true

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +8
// Set to false when building locally to prevent auto-updates and restarts
var shouldAutoUpdateAndRestart: Bool = false

Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

shouldAutoUpdateAndRestart defaults to false, and UpdaterManager never sets it when constructing SilentUserDriver, so auto-update checks/downloads/installs will be disabled in all builds. If the intent is “developers can disable locally”, default this to true and gate disabling behind a build flag/env var/UserDefaults dev setting (and wire it from UpdaterManager).

Suggested change
// Set to false when building locally to prevent auto-updates and restarts
var shouldAutoUpdateAndRestart: Bool = false
// Default to enabled so silent updates work in normal builds.
// Developers can disable locally in DEBUG builds with either:
// - env: DAYFLOW_DISABLE_AUTO_UPDATE_AND_RESTART=1
// - UserDefaults: DayflowDisableAutoUpdateAndRestart = true
var shouldAutoUpdateAndRestart: Bool = {
#if DEBUG
let environment = ProcessInfo.processInfo.environment
if let value = environment["DAYFLOW_DISABLE_AUTO_UPDATE_AND_RESTART"]?
.trimmingCharacters(in: .whitespacesAndNewlines)
.lowercased(),
value == "1" || value == "true" || value == "yes"
{
return false
}
if UserDefaults.standard.bool(forKey: "DayflowDisableAutoUpdateAndRestart") {
return false
}
#endif
return true
}()

Copilot uses AI. Check for mistakes.
Comment on lines +1002 to 1005
let transcriptionSchemaObject = try! JSONSerialization.jsonObject(
with: Data(LLMSchema.screenRecordingTranscriptionSchema.utf8))
let generationConfig: [String: Any] = [
"temperature": 0.3,
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

Avoid try! when parsing the JSON schema because a malformed schema string would crash at runtime. Parse the schema with proper error handling (or pre-parse once and fatalError with a clear message) so failures are surfaced deterministically instead of crashing unexpectedly.

Copilot uses AI. Check for mistakes.
Comment on lines 1686 to 1691
let generationConfig: [String: Any] = [
"temperature": 0.7,
"maxOutputTokens": 8192,
"responseMimeType": "application/json",
"responseJsonSchema": activityCardsSchemaObject,
]
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

activityCardsSchemaObject is an Any? and is inserted into a [String: Any] as an Optional, which will make JSONSerialization.data(withJSONObject:) fail because Optional<Any> is not a valid JSON type. Unwrap the schema object (or conditionally omit the key) before building generationConfig.

Suggested change
let generationConfig: [String: Any] = [
"temperature": 0.7,
"maxOutputTokens": 8192,
"responseMimeType": "application/json",
"responseJsonSchema": activityCardsSchemaObject,
]
var generationConfig: [String: Any] = [
"temperature": 0.7,
"maxOutputTokens": 8192,
"responseMimeType": "application/json",
]
if let activityCardsSchemaObject {
generationConfig["responseJsonSchema"] = activityCardsSchemaObject
}

Copilot uses AI. Check for mistakes.
"mediaResolution": "MEDIA_RESOLUTION_HIGH",
"responseMimeType": "application/json",
"responseSchema": transcriptionSchema,
"responseJsonSchema": transcriptionSchemaObject,
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

This request uses responseJsonSchema, but the same file still uses responseSchema for other Gemini requests. If the backend expects responseSchema, these calls will silently ignore the schema (or fail validation). Please standardize on the correct field name across all Gemini requests and ensure it matches the API contract.

Suggested change
"responseJsonSchema": transcriptionSchemaObject,
"responseSchema": transcriptionSchemaObject,

Copilot uses AI. Check for mistakes.
let json = jsonString(properties)
let line = truncate("[Analytics] \(event) \(json)")
print(line)
localLogger.info("\(line, privacy: .public)")
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

localLogger.info("\(line, privacy: .public)") logs the full event payload into Apple Unified Logging as public data. Even with sanitization, this can still leak user- or device-identifying data into system logs. Consider using .private (or logging only the event name + redacted fields) for OSLog output.

Suggested change
localLogger.info("\(line, privacy: .public)")
localLogger.info("\(line, privacy: .private)")

Copilot uses AI. Check for mistakes.
Comment on lines +39 to +64
/// Shared utilities for parsing timestamps emitted by LLMs during video/timelapse transcription.
///
/// NOTE: These helpers are intentionally lightweight and avoid shared DateFormatter instances
/// because DateFormatter is not thread-safe.
enum LLMVideoTimestampUtilities {
/// Parses either `HH:MM:SS` or `MM:SS` into total seconds.
/// Returns `0` for invalid input.
static func parseVideoTimestamp(_ timestamp: String) -> Int {
let parts =
timestamp
.trimmingCharacters(in: .whitespacesAndNewlines)
.components(separatedBy: ":")

if parts.count == 3 {
let h = Int(parts[0]) ?? 0
let m = Int(parts[1]) ?? 0
let s = Int(parts[2]) ?? 0
return h * 3600 + m * 60 + s
}
if parts.count == 2 {
let m = Int(parts[0]) ?? 0
let s = Int(parts[1]) ?? 0
return m * 60 + s
}
return 0
}
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

New timestamp parsing/validation utilities were added (LLMVideoTimestampUtilities, LLMTimelineCardValidation, LLMTranscriptUtilities) but there are existing unit tests for TimeParsing and none cover these new code paths. Add tests for video timestamp parsing (MM:SS/HH:MM:SS/whitespace/invalid) and transcript→observation conversion (duration bounds, tolerance, invalid timestamps).

Copilot uses AI. Check for mistakes.
vegerot and others added 4 commits April 9, 2026 11:32
- Extract shared prompt templates into LLMPromptTemplates (GeminiPromptPreferences.swift)
- Add VideoPromptPreferences/VideoPromptOverrides/VideoPromptSections types,
  replacing GeminiPromptPreferences/GeminiPromptOverrides/GeminiPromptSections
- Centralize transcript JSON decoding and observation conversion in
  LLMTranscriptUtilities (TimeParsing.swift) for reuse across providers
- Refactor GeminiDirectProvider to use LLMPromptTemplates and LLMTranscriptUtilities
- Refactor TestConnectionView to accept a provider parameter with
  finishFailure/finishSuccess helpers for clean multi-provider support
- Fix OnboardingLLMSelectionView card-width calculation to be dynamic
  based on card count rather than hard-coded divisor of 3
- Update SettingsProvidersTabView and ProvidersSettingsViewModel to use
  new VideoPrompt* types

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ensures every AnalyticsService event is sent to PostHog, printed to stdout, and emitted via Apple Unified Logging.
@vegerot vegerot changed the title feat: allow developer option to disable automatic updates feat: add option to disable automatic updates Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants