mirror of
https://github.com/stablyai/orca.git
synced 2026-09-26 16:02:43 +00:00
The gate this replaces decided "is this profile new?" by inspecting the disk at load: a state file or any backup meant pre-existing. That inference was wrong three separate times. Telemetry seeding writes a file for a brand-new profile, so a second profile installed hooks globally before its own checkbox appeared, undoing the first profile's opt-out. A user who quit onboarding halfway read as consented on relaunch. Two candidate redesigns died on the same inference. Freshness is now recorded when it is a fact rather than discovered later. A marker under the user-data root, established in preflight before any profile or Store exists, says whether this installation predates the change. Absence means pre-change, so an upgrading user is never deferred and nothing is written for them at all. Every ambiguity installs. A corrupt marker, a malformed partial record, an unreadable root, or a failed write all resolve to install-as-today; a deferral requires no marker, none of Orca's own artifacts on disk, and a successful write. Deferring a user who already has hooks would stop maintaining them, so that direction is the one that must never happen by accident. Consent is installation-scoped, not per-profile, so a second profile inherits the answer instead of being asked again -- hooks are user-global and the person has already seen the disclosure. Profile seeding now carries agentStatusHooksEnabled too; previously a parent's opt-out was silently lost and the child reinstalled. The policy is threaded through every writer, not just startup. SSH, the WSL relay, Codex launch and resume, and the offline CLI all bypass installManagedAgentHooks and each wrote user-global config on its own. Also required, not incidental: the Codex refresh no longer runs the legacy system cleanup. That is a profile-local operation reaching into the real ~/.codex, which "existing users untouched" forbids outright.
132 lines
7.1 KiB
TypeScript
132 lines
7.1 KiB
TypeScript
import type { Store } from '../persistence'
|
|
import type { GlobalSettings } from '../../shared/global-settings-types'
|
|
import type { VoiceSettings } from '../../shared/speech-types'
|
|
|
|
export type RuntimeStore = {
|
|
getRepos: Store['getRepos']
|
|
getRepo: Store['getRepo']
|
|
addRepo: Store['addRepo']
|
|
updateRepo: Store['updateRepo']
|
|
addRetiredWorktreeName?: Store['addRetiredWorktreeName']
|
|
mergeRetiredWorktreeNames?: Store['mergeRetiredWorktreeNames']
|
|
getRetiredWorktreeNameRegistry?: Store['getRetiredWorktreeNameRegistry']
|
|
getRetiredWorktreeNameRegistryForNamespace?: Store['getRetiredWorktreeNameRegistryForNamespace']
|
|
getProjects?: Store['getProjects']
|
|
updateProject?: Store['updateProject']
|
|
getProjectHostSetups?: Store['getProjectHostSetups']
|
|
createProjectHostSetup?: Store['createProjectHostSetup']
|
|
updateProjectHostSetup?: Store['updateProjectHostSetup']
|
|
deleteProjectHostSetup?: Store['deleteProjectHostSetup']
|
|
getProjectGroups?: Store['getProjectGroups']
|
|
createProjectGroup?: Store['createProjectGroup']
|
|
updateProjectGroup?: Store['updateProjectGroup']
|
|
deleteProjectGroup?: Store['deleteProjectGroup']
|
|
moveProjectToGroup?: Store['moveProjectToGroup']
|
|
getFolderWorkspaces?: Store['getFolderWorkspaces']
|
|
createFolderWorkspace?: Store['createFolderWorkspace']
|
|
updateFolderWorkspace?: Store['updateFolderWorkspace']
|
|
removeFolderWorkspace?: Store['removeFolderWorkspace']
|
|
removeProject?: Store['removeProject']
|
|
removeProjectForHost?: Store['removeProjectForHost']
|
|
reorderRepos?: Store['reorderRepos']
|
|
getAllWorktreeMeta: Store['getAllWorktreeMeta']
|
|
captureNativeLocalWorktreeMetadataScanExpectation?: Store['captureNativeLocalWorktreeMetadataScanExpectation']
|
|
pruneSessionlessMissingLocalWorktreeMetadataForRepo?: Store['pruneSessionlessMissingLocalWorktreeMetadataForRepo']
|
|
getProfileStorageDirectory?: Store['getProfileStorageDirectory']
|
|
getWorktreeMeta: Store['getWorktreeMeta']
|
|
setWorktreeMeta: Store['setWorktreeMeta']
|
|
setWorktreeMetaForHost?: Store['setWorktreeMetaForHost']
|
|
removeWorktreeMeta: Store['removeWorktreeMeta']
|
|
getWorktreeLineage?: Store['getWorktreeLineage']
|
|
getAllWorktreeLineage?: Store['getAllWorktreeLineage']
|
|
setWorktreeLineage?: Store['setWorktreeLineage']
|
|
removeWorktreeLineage?: Store['removeWorktreeLineage']
|
|
getAllWorkspaceLineage?: Store['getAllWorkspaceLineage']
|
|
setWorkspaceLineage?: Store['setWorkspaceLineage']
|
|
removeWorkspaceLineage?: Store['removeWorkspaceLineage']
|
|
getGitHubCache: Store['getGitHubCache']
|
|
getWorkspaceSession?: Store['getWorkspaceSession']
|
|
getWorkspaceSessionHostIds?: Store['getWorkspaceSessionHostIds']
|
|
setWorkspaceSession?: Store['setWorkspaceSession']
|
|
flushOrThrow?: Store['flushOrThrow']
|
|
flushPendingOrThrowAsync?: Store['flushPendingOrThrowAsync']
|
|
persistPtyBinding?: Store['persistPtyBinding']
|
|
getSshRemotePtyLeases?: Store['getSshRemotePtyLeases']
|
|
getUI?: Store['getUI']
|
|
updateUI?: Store['updateUI']
|
|
recordFeatureInteraction?: Store['recordFeatureInteraction']
|
|
listAutomations?: Store['listAutomations']
|
|
listAutomationsForScope?: Store['listAutomationsForScope']
|
|
assertAutomationOwnerFence?: Store['assertAutomationOwnerFence']
|
|
automationOwnerPrecondition?: Store['automationOwnerPrecondition']
|
|
automationChangeSelector?: Store['automationChangeSelector']
|
|
listAutomationRuns?: Store['listAutomationRuns']
|
|
listAutomationRunsPage?: Store['listAutomationRunsPage']
|
|
createAutomation?: Store['createAutomation']
|
|
updateAutomation?: Store['updateAutomation']
|
|
deleteAutomation?: Store['deleteAutomation']
|
|
getSparsePresets?: Store['getSparsePresets']
|
|
saveSparsePreset?: Store['saveSparsePreset']
|
|
getMobileClientTabSelections?: Store['getMobileClientTabSelections']
|
|
setMobileClientTabSelections?: Store['setMobileClientTabSelections']
|
|
getSettings(): {
|
|
workspaceDir: string
|
|
nestWorkspaces: boolean
|
|
// Read by worktree placement: decides whether this project's worktrees
|
|
// mirror into a WSL distro instead of the Windows drive.
|
|
localWindowsRuntimeDefault?: GlobalSettings['localWindowsRuntimeDefault']
|
|
refreshLocalBaseRefOnWorktreeCreate: boolean
|
|
localBaseRefSuggestionDismissed?: boolean
|
|
branchPrefix: string
|
|
branchPrefixCustom: string
|
|
defaultTuiAgent?: GlobalSettings['defaultTuiAgent']
|
|
disabledTuiAgents?: GlobalSettings['disabledTuiAgents']
|
|
agentCmdOverrides?: GlobalSettings['agentCmdOverrides']
|
|
agentDefaultArgs?: GlobalSettings['agentDefaultArgs']
|
|
agentDefaultEnv?: GlobalSettings['agentDefaultEnv']
|
|
terminalWindowsShell?: GlobalSettings['terminalWindowsShell']
|
|
floatingTerminalEnabled?: GlobalSettings['floatingTerminalEnabled']
|
|
agentStatusHooksEnabled?: GlobalSettings['agentStatusHooksEnabled']
|
|
experimentalNativeChat?: GlobalSettings['experimentalNativeChat']
|
|
openAgentTabsInChatByDefault?: GlobalSettings['openAgentTabsInChatByDefault']
|
|
experimentalStructuredNativeChat?: GlobalSettings['experimentalStructuredNativeChat']
|
|
defaultTaskSource?: GlobalSettings['defaultTaskSource']
|
|
defaultTaskViewPreset?: GlobalSettings['defaultTaskViewPreset']
|
|
visibleTaskProviders?: GlobalSettings['visibleTaskProviders']
|
|
defaultRepoSelection?: GlobalSettings['defaultRepoSelection']
|
|
defaultLinearTeamSelection?: GlobalSettings['defaultLinearTeamSelection']
|
|
githubProjects?: GlobalSettings['githubProjects']
|
|
experimentalNewWorktreeCardStyle?: GlobalSettings['experimentalNewWorktreeCardStyle']
|
|
compactWorktreeCards?: GlobalSettings['compactWorktreeCards']
|
|
minimaxGroupId?: GlobalSettings['minimaxGroupId']
|
|
minimaxUsageModels?: GlobalSettings['minimaxUsageModels']
|
|
minimaxEndpoint?: GlobalSettings['minimaxEndpoint']
|
|
prBotAuthorOverrides?: GlobalSettings['prBotAuthorOverrides']
|
|
artifactSharingEnabled?: GlobalSettings['artifactSharingEnabled']
|
|
terminalQuickCommands?: GlobalSettings['terminalQuickCommands']
|
|
gitlabProjects?: GlobalSettings['gitlabProjects']
|
|
mobileAutoRestoreFitMs?: number | null
|
|
mobileEmulatorEnabled?: boolean
|
|
mobileEmulatorDefaultDeviceUdid?: string | null
|
|
voice?: VoiceSettings
|
|
claudeAgentTeamsMode?: GlobalSettings['claudeAgentTeamsMode']
|
|
// Why: Phase-5 query responder kill switches — read per chunk in
|
|
// onPtyData to capture reply ownership at ingestion.
|
|
terminalMainSideEffectAuthority?: GlobalSettings['terminalMainSideEffectAuthority']
|
|
terminalHiddenDeliveryGate?: GlobalSettings['terminalHiddenDeliveryGate']
|
|
terminalModelQueryAuthority?: GlobalSettings['terminalModelQueryAuthority']
|
|
worktreeVisibilityDefaults?: GlobalSettings['worktreeVisibilityDefaults']
|
|
hostSettingOverrides?: GlobalSettings['hostSettingOverrides']
|
|
agentSkillSharingEnabled?: GlobalSettings['agentSkillSharingEnabled']
|
|
nativeChatSessionOptions?: GlobalSettings['nativeChatSessionOptions']
|
|
}
|
|
// Why: narrow to `unknown` return so test mocks can return void without
|
|
// a cast. The runtime never reads the return value — the persisted value
|
|
// is read back via getSettings() on the next access.
|
|
updateSettings?: (
|
|
updates: Partial<GlobalSettings>,
|
|
options?: { notifyListeners?: boolean; originWebContentsId?: number }
|
|
) => unknown
|
|
onSettingsChanged?: Store['onSettingsChanged']
|
|
}
|