/ utils / backgroundHousekeeping.ts
backgroundHousekeeping.ts
 1  import { feature } from 'bun:bundle'
 2  import { initAutoDream } from '../services/autoDream/autoDream.js'
 3  import { initMagicDocs } from '../services/MagicDocs/magicDocs.js'
 4  import { initSkillImprovement } from './hooks/skillImprovement.js'
 5  
 6  /* eslint-disable @typescript-eslint/no-require-imports */
 7  const extractMemoriesModule = feature('EXTRACT_MEMORIES')
 8    ? (require('../services/extractMemories/extractMemories.js') as typeof import('../services/extractMemories/extractMemories.js'))
 9    : null
10  const registerProtocolModule = feature('LODESTONE')
11    ? (require('./deepLink/registerProtocol.js') as typeof import('./deepLink/registerProtocol.js'))
12    : null
13  
14  /* eslint-enable @typescript-eslint/no-require-imports */
15  
16  import { getIsInteractive, getLastInteractionTime } from '../bootstrap/state.js'
17  import {
18    cleanupNpmCacheForAnthropicPackages,
19    cleanupOldMessageFilesInBackground,
20    cleanupOldVersionsThrottled,
21  } from './cleanup.js'
22  import { cleanupOldVersions } from './nativeInstaller/index.js'
23  import { autoUpdateMarketplacesAndPluginsInBackground } from './plugins/pluginAutoupdate.js'
24  
25  // 24 hours in milliseconds
26  const RECURRING_CLEANUP_INTERVAL_MS = 24 * 60 * 60 * 1000
27  
28  // 10 minutes after start.
29  const DELAY_VERY_SLOW_OPERATIONS_THAT_HAPPEN_EVERY_SESSION = 10 * 60 * 1000
30  
31  export function startBackgroundHousekeeping(): void {
32    void initMagicDocs()
33    void initSkillImprovement()
34    if (feature('EXTRACT_MEMORIES')) {
35      extractMemoriesModule!.initExtractMemories()
36    }
37    initAutoDream()
38    void autoUpdateMarketplacesAndPluginsInBackground()
39    if (feature('LODESTONE') && getIsInteractive()) {
40      void registerProtocolModule!.ensureDeepLinkProtocolRegistered()
41    }
42  
43    let needsCleanup = true
44    async function runVerySlowOps(): Promise<void> {
45      // If the user did something in the last minute, don't make them wait for these slow operations to run.
46      if (
47        getIsInteractive() &&
48        getLastInteractionTime() > Date.now() - 1000 * 60
49      ) {
50        setTimeout(
51          runVerySlowOps,
52          DELAY_VERY_SLOW_OPERATIONS_THAT_HAPPEN_EVERY_SESSION,
53        ).unref()
54        return
55      }
56  
57      if (needsCleanup) {
58        needsCleanup = false
59        await cleanupOldMessageFilesInBackground()
60      }
61  
62      // If the user did something in the last minute, don't make them wait for these slow operations to run.
63      if (
64        getIsInteractive() &&
65        getLastInteractionTime() > Date.now() - 1000 * 60
66      ) {
67        setTimeout(
68          runVerySlowOps,
69          DELAY_VERY_SLOW_OPERATIONS_THAT_HAPPEN_EVERY_SESSION,
70        ).unref()
71        return
72      }
73  
74      await cleanupOldVersions()
75    }
76  
77    setTimeout(
78      runVerySlowOps,
79      DELAY_VERY_SLOW_OPERATIONS_THAT_HAPPEN_EVERY_SESSION,
80    ).unref()
81  
82    // For long-running sessions, schedule recurring cleanup every 24 hours.
83    // Both cleanup functions use marker files and locks to throttle to once per day
84    // and skip immediately if another process holds the lock.
85    if (process.env.USER_TYPE === 'ant') {
86      const interval = setInterval(() => {
87        void cleanupNpmCacheForAnthropicPackages()
88        void cleanupOldVersionsThrottled()
89      }, RECURRING_CLEANUP_INTERVAL_MS)
90  
91      // Don't let this interval keep the process alive
92      interval.unref()
93    }
94  }