/ utils / computerUse / gates.ts
gates.ts
 1  import type { CoordinateMode, CuSubGates } from '@ant/computer-use-mcp/types'
 2  
 3  import { getDynamicConfig_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js'
 4  import { getSubscriptionType } from '../auth.js'
 5  import { isEnvTruthy } from '../envUtils.js'
 6  
 7  type ChicagoConfig = CuSubGates & {
 8    enabled: boolean
 9    coordinateMode: CoordinateMode
10  }
11  
12  const DEFAULTS: ChicagoConfig = {
13    enabled: false,
14    pixelValidation: false,
15    clipboardPasteMultiline: true,
16    mouseAnimation: true,
17    hideBeforeAction: true,
18    autoTargetDisplay: true,
19    clipboardGuard: true,
20    coordinateMode: 'pixels',
21  }
22  
23  // Spread over defaults so a partial JSON ({"enabled": true} alone) inherits the
24  // rest. The generic on getDynamicConfig is a type assertion, not a validator —
25  // GB returning a partial object would otherwise surface undefined fields.
26  function readConfig(): ChicagoConfig {
27    return {
28      ...DEFAULTS,
29      ...getDynamicConfig_CACHED_MAY_BE_STALE<Partial<ChicagoConfig>>(
30        'tengu_malort_pedway',
31        DEFAULTS,
32      ),
33    }
34  }
35  
36  // Max/Pro only for external rollout. Ant bypass so dogfooding continues
37  // regardless of subscription tier — not all ants are max/pro, and per
38  // CLAUDE.md:281, USER_TYPE !== 'ant' branches get zero antfooding.
39  function hasRequiredSubscription(): boolean {
40    if (process.env.USER_TYPE === 'ant') return true
41    const tier = getSubscriptionType()
42    return tier === 'max' || tier === 'pro'
43  }
44  
45  export function getChicagoEnabled(): boolean {
46    // Disable for ants whose shell inherited monorepo dev config.
47    // MONOREPO_ROOT_DIR is exported by config/local/zsh/zshrc, which
48    // laptop-setup.sh wires into ~/.zshrc — its presence is the cheap
49    // proxy for "has monorepo access". Override: ALLOW_ANT_COMPUTER_USE_MCP=1.
50    if (
51      process.env.USER_TYPE === 'ant' &&
52      process.env.MONOREPO_ROOT_DIR &&
53      !isEnvTruthy(process.env.ALLOW_ANT_COMPUTER_USE_MCP)
54    ) {
55      return false
56    }
57    return hasRequiredSubscription() && readConfig().enabled
58  }
59  
60  export function getChicagoSubGates(): CuSubGates {
61    const { enabled: _e, coordinateMode: _c, ...subGates } = readConfig()
62    return subGates
63  }
64  
65  // Frozen at first read — setup.ts builds tool descriptions and executor.ts
66  // scales coordinates off the same value. A live read here lets a mid-session
67  // GB flip tell the model "pixels" while transforming clicks as normalized.
68  let frozenCoordinateMode: CoordinateMode | undefined
69  export function getChicagoCoordinateMode(): CoordinateMode {
70    frozenCoordinateMode ??= readConfig().coordinateMode
71    return frozenCoordinateMode
72  }