oy-cli 0.14.11

Focused coding agent integrations with repeatable audits, reviews, and one-finding fixes
Documentation
export const cursorProviderPackage = "@ai-sdk/openai"

export const fallbackCursorModels = [
  {
    id: "composer-2.5",
    displayName: "Composer 2.5",
    parameters: [{ id: "thinking", values: [{ value: "off" }, { value: "on" }] }],
  },
  { id: "claude-opus-4-8", displayName: "Claude Opus 4.8 (via Cursor)" },
  { id: "claude-sonnet-4-6", displayName: "Claude Sonnet 4.6 (via Cursor)" },
  { id: "gpt-5.5", displayName: "GPT-5.5 (via Cursor)" },
]

const reasoningParameter = /think|reason|effort/i
const booleanValues = new Set(["true", "false"])
const contextLimits = {
  "claude-fable-5": 300_000,
  "claude-opus-4-7": 300_000,
  "claude-opus-4-8": 300_000,
  "claude-opus-5": 300_000,
  "gpt-5-mini": 272_000,
  "gpt-5.1": 272_000,
  "gpt-5.2": 272_000,
  "gpt-5.3-codex": 272_000,
  "gpt-5.4": 272_000,
  "gpt-5.4-mini": 272_000,
  "gpt-5.4-nano": 272_000,
  "gpt-5.5": 272_000,
  "gpt-5.6-luna": 272_000,
  "gpt-5.6-sol": 272_000,
  "gpt-5.6-terra": 272_000,
  "grok-4.5": 256_000,
}
const outputLimits = {
  "claude-fable-5": 64_000,
  "claude-opus-4-7": 64_000,
  "claude-opus-4-8": 64_000,
  "claude-opus-5": 64_000,
  "gpt-5.5": 64_000,
  "gpt-5.6-sol": 64_000,
}
const noAutoCompactionInputLimit = 1_000_000_000
const modelCosts = {
  "claude-fable-5": [10, 50, 1, 12.5],
  "claude-haiku-4-5": [1, 5, 0.1, 1.25],
  "claude-opus-4": [5, 25, 0.5, 6.25],
  "claude-sonnet-4": [3, 15, 0.3, 3.75],
  "claude-sonnet-5": [3, 15, 0.3, 3.75],
  "gemini-2.5-flash": [0.3, 2.5, 0.03, 0],
  "gemini-3-flash": [0.5, 3, 0.05, 0],
  "gemini-3.1-pro": [2, 12, 0.2, 0],
  "gemini-3.5-flash": [1.5, 9, 0.15, 0],
  "gemini-3.6-flash": [1.5, 7.5, 0.15, 0],
  "glm-5.2": [1.4, 4.4, 0.26, 0],
  "gpt-5-mini": [0.25, 2, 0.025, 0],
  "gpt-5.1": [1.25, 10, 0.125, 0],
  "gpt-5.2": [1.75, 14, 0.175, 0],
  "gpt-5.3-codex": [1.75, 14, 0.175, 0],
  "gpt-5.4-mini": [0.75, 4.5, 0.075, 0],
  "gpt-5.4-nano": [0.2, 1.25, 0.02, 0],
  "gpt-5.4": [2.5, 15, 0.25, 0],
  "gpt-5.5": [5, 30, 0.5, 0],
  "gpt-5.6-luna": [0.2, 1.2, 0.02, 0.25],
  "gpt-5.6-sol": [5, 30, 0.5, 6.25],
  "gpt-5.6-terra": [2, 12, 0.2, 2.5],
}

const parameterValues = (parameter) => (parameter.values ?? []).map((value) => value.value)
const isBooleanParameter = (values) =>
  values.length > 0 && values.every((value) => booleanValues.has(value))
const variantKey = (displayName) =>
  displayName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") || "variant"
const modelLimit = (modelID, limits, fallback) => {
  let match
  for (const [prefix, limit] of Object.entries(limits)) {
    if (modelID.startsWith(prefix) && (!match || prefix.length > match.prefix.length)) {
      match = { prefix, limit }
    }
  }
  return match?.limit ?? fallback
}
const modelCost = (modelID) => {
  const values = modelLimit(modelID, modelCosts, [0, 0, 0, 0])
  return [{ input: values[0], output: values[1], cache: { read: values[2], write: values[3] } }]
}

export const cursorModelLimits = (modelID, { autoCompaction = false } = {}) => ({
  context: autoCompaction
    ? modelLimit(modelID, contextLimits, 200_000)
    : noAutoCompactionInputLimit,
  ...(autoCompaction ? {} : { input: noAutoCompactionInputLimit }),
  output: modelLimit(modelID, outputLimits, 32_000),
})

export const cursorVariants = (item) => {
  const defaults = {}
  for (const parameter of item.parameters ?? []) {
    if (reasoningParameter.test(parameter.id)) continue
    if (isBooleanParameter(parameterValues(parameter))) defaults[parameter.id] = "false"
  }

  const presets = (item.variants ?? []).filter((variant) => variant.isDefault !== true)
  const presetsShareName =
    presets.length > 1 && new Set(presets.map((variant) => variant.displayName)).size === 1
  const addPlanVariant = (variants, ids) => {
    let id = "plan"
    for (let suffix = 2; ids.has(id); suffix += 1) id = `plan-${suffix}`
    ids.add(id)
    variants.push({ id, headers: {}, body: { mode: "plan", params: defaults } })
  }
  if (presets.length > 0 && !presetsShareName) {
    const variants = []
    const ids = new Set()
    for (const preset of presets) {
      const params = { ...defaults }
      for (const parameter of preset.params ?? []) params[parameter.id] = parameter.value
      const base = variantKey(preset.displayName)
      let id = base
      for (let suffix = 2; ids.has(id); suffix += 1) id = `${base}-${suffix}`
      ids.add(id)
      variants.push({ id, headers: {}, body: { params } })
    }
    addPlanVariant(variants, ids)
    return { defaults, variants }
  }

  const variants = []
  const ids = new Set()
  const hasEffortEnum = (item.parameters ?? []).some((parameter) => {
    const values = parameterValues(parameter)
    return reasoningParameter.test(parameter.id) && !isBooleanParameter(values) && values.length > 0
  })
  const addVariant = (base, params, parameterID) => {
    const id = ids.has(base) ? `${parameterID}-${base}` : base
    ids.add(id)
    variants.push({ id, headers: {}, body: { params } })
  }

  for (const parameter of item.parameters ?? []) {
    const values = parameterValues(parameter)
    if (values.length === 0) continue
    const boolean = isBooleanParameter(values)
    if (reasoningParameter.test(parameter.id)) {
      if (boolean) {
        if (!hasEffortEnum && values.includes("true")) {
          addVariant(parameter.id.toLowerCase(), { ...defaults, [parameter.id]: "true" }, parameter.id)
        }
        continue
      }
      for (const value of values) {
        if (value === "none") continue
        addVariant(
          value === "extra-high" ? "xhigh" : value,
          { ...defaults, [parameter.id]: value },
          parameter.id,
        )
      }
      continue
    }
    if (boolean && values.includes("true")) {
      addVariant(parameter.id.toLowerCase(), { ...defaults, [parameter.id]: "true" }, parameter.id)
    }
  }
  addPlanVariant(variants, ids)
  return { defaults, variants }
}

export const applyCursorCatalog = (catalog, models, bridge) => {
  let autoCompaction = false
  const bridgeHeaders = bridge ? { "x-oy-cursor-bridge": bridge.token } : {}
  catalog.provider.update("cursor", (provider) => {
    provider.name = "Cursor"
    provider.integrationID = "cursor"
    const configured = provider.request?.body ?? {}
    const legacyConfigured = provider.settings ?? {}
    autoCompaction = configured.autoCompaction === true || legacyConfigured.autoCompaction === true
    provider.api = { type: "aisdk", package: cursorProviderPackage }
    provider.package = `aisdk:${cursorProviderPackage}`
    provider.settings = {
      ...legacyConfigured,
      ...configured,
      ...(bridge
        ? {
            baseURL: bridge.url,
            headers: {
              ...(legacyConfigured.headers ?? {}),
              ...(configured.headers ?? {}),
              "x-oy-cursor-bridge": bridge.token,
            },
          }
        : {}),
    }
    provider.request = {
      headers: { ...(provider.request?.headers ?? {}), ...bridgeHeaders },
      body: { ...configured },
    }
    if (bridge) provider.api.url = bridge.url
  })
  for (const item of models) {
    const controls = cursorVariants(item)
    catalog.model.update("cursor", item.id, (model) => {
      model.name = item.displayName || item.id
      model.api = {
        id: item.id,
        type: "aisdk",
        package: cursorProviderPackage,
        ...(bridge ? { url: bridge.url } : {}),
      }
      model.package = `aisdk:${cursorProviderPackage}`
      model.capabilities = { tools: true, input: ["text"], output: ["text"] }
      model.request = {
        headers: { ...bridgeHeaders },
        body: {
          ...(Object.keys(controls.defaults).length > 0 ? { params: controls.defaults } : {}),
        },
      }
      model.settings = {
        ...(Object.keys(controls.defaults).length > 0 ? { params: controls.defaults } : {}),
        ...(Object.keys(bridgeHeaders).length > 0 ? { headers: bridgeHeaders } : {}),
      }
      model.headers = { ...bridgeHeaders }
      model.variants = controls.variants.map((variant) => ({
        ...variant,
        id: variant.id,
        settings: variant.body,
      }))
      model.status = "active"
      model.enabled = true
      model.limit = cursorModelLimits(item.id, { autoCompaction })
      model.cost = modelCost(item.id)
    })
  }
}