# Options

> Everything under the precog key in nuxt.config.ts.

<table>
<thead>
  <tr>
    <th>
      Option
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      What it does
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        enabled
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Turn the whole module off without removing it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        endpoint
      </code>
    </td>
    
    <td>
      <code>
        '/_precog/predict'
      </code>
    </td>
    
    <td>
      Path of the Nitro route that talks to Jev.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        model
      </code>
    </td>
    
    <td>
      unset
    </td>
    
    <td>
      Leave it unset. See <a href="#the-model-name-is-a-trap">
        the model name is a trap
      </a>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        provider
      </code>
    </td>
    
    <td>
      unset
    </td>
    
    <td>
      <code>
        'typesafe'
      </code>
      
       or <code>
        'vercel'
      </code>
      
      . Unset, the key decides.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        baseURL
      </code>
    </td>
    
    <td>
      unset
    </td>
    
    <td>
      TypeSafe base URL. Point it at a mock in tests.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mode
      </code>
    </td>
    
    <td>
      <code>
        'prefetch'
      </code>
    </td>
    
    <td>
      <code>
        'prefetch'
      </code>
      
      , <code>
        'prerender'
      </code>
      
       or <code>
        'auto'
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        thresholds.prefetch
      </code>
    </td>
    
    <td>
      <code>
        0.25
      </code>
    </td>
    
    <td>
      Lowest click probability worth a prefetch.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        thresholds.prerender
      </code>
    </td>
    
    <td>
      <code>
        0.6
      </code>
    </td>
    
    <td>
      Lowest click probability worth a prerender.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        budget.maxPrefetch
      </code>
    </td>
    
    <td>
      <code>
        3
      </code>
    </td>
    
    <td>
      Most URLs prefetched at once.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        budget.maxPrerender
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Most URLs prerendered at once.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        budget.maxCallsPerMinute
      </code>
    </td>
    
    <td>
      <code>
        20
      </code>
    </td>
    
    <td>
      Enforced on the client and on the server.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        budget.maxCallsPerSession
      </code>
    </td>
    
    <td>
      <code>
        200
      </code>
    </td>
    
    <td>
      Hard stop for one visitor.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        maxCandidates
      </code>
    </td>
    
    <td>
      <code>
        30
      </code>
    </td>
    
    <td>
      Links sent per request. Capped at 254 by Jev's choice limit.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        timeoutMs
      </code>
    </td>
    
    <td>
      <code>
        1500
      </code>
    </td>
    
    <td>
      Client deadline. Measured round trips are 340 to 500 ms.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        minIntervalMs
      </code>
    </td>
    
    <td>
      <code>
        1200
      </code>
    </td>
    
    <td>
      Shortest gap between two predictions.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        fallback
      </code>
    </td>
    
    <td>
      <code>
        'native'
      </code>
    </td>
    
    <td>
      On failure: <code>
        'native'
      </code>
      
       document rules at <code>
        moderate
      </code>
      
      , or <code>
        'none'
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        include
      </code>
    </td>
    
    <td>
      <code>
        []
      </code>
    </td>
    
    <td>
      Only these path globs may be speculated. Empty means all.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        exclude
      </code>
    </td>
    
    <td>
      <code>
        ['/logout', '/signout', '/api/**', '/auth/**', '/cart/**']
      </code>
    </td>
    
    <td>
      Never speculated.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        cache.ttlSeconds
      </code>
    </td>
    
    <td>
      <code>
        60
      </code>
    </td>
    
    <td>
      How long a prediction stays reusable on the server.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        privacy.sendQuery
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Send query strings. Off because they carry tokens.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        privacy.sendAnchorText
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Send link text.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        privacy.sendHistory
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Send the last five paths.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        privacy.requireConsent
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Nothing runs until <code>
        grantConsent()
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        overlay
      </code>
    </td>
    
    <td>
      <code>
        'dev'
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
      
      , <code>
        false
      </code>
      
       or <code>
        'dev'
      </code>
      
      . When false, nothing ships.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        takeOverNuxtLinkPrefetch
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Turn off <code>
        NuxtLink
      </code>
      
      's viewport prefetch, keep interaction.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        documentNavigation
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Experimental, see below.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pricing
      </code>
    </td>
    
    <td>
      unset
    </td>
    
    <td>
      <code>
        { inputPerMillion, outputPerMillion }
      </code>
      
       for the overlay's cost line.
    </td>
  </tr>
</tbody>
</table>

## The API key

It never reaches the browser, and there is an e2e test that fails if it ever appears in
anything the browser receives.

There are two kinds of key and they are not interchangeable:

<table>
<thead>
  <tr>
    <th>
      Variable
    </th>
    
    <th>
      Kind of key
    </th>
    
    <th>
      Goes to
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        TYPESAFE_API_KEY
      </code>
    </td>
    
    <td>
      TypeSafe
    </td>
    
    <td>
      <code>
        api.typesafe.ai
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        AI_GATEWAY_API_KEY
      </code>
    </td>
    
    <td>
      Vercel AI Gateway
    </td>
    
    <td>
      <code>
        ai-gateway.vercel.sh
      </code>
      
      , selected automatically
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        NUXT_PRECOG_API_KEY
      </code>
    </td>
    
    <td>
      either
    </td>
    
    <td>
      set <code>
        provider
      </code>
      
       to say which
    </td>
  </tr>
</tbody>
</table>

A gateway key sent to TypeSafe directly answers `401 Cannot authenticate with the server`,
which reads like a bad key rather than the wrong door. If you see that, check which kind you
have before regenerating anything.

## The model name is a trap

Leave `model` unset. advocaat then picks the right one for the service: `jev-latest` against
TypeSafe, `typesafe-ai/jev` through the gateway.

Setting it to `jev-latest` explicitly works directly and **breaks the gateway**, where a bare
name gets a `typesafe-ai/` prefix and `typesafe-ai/jev-latest` does not exist. The error is
`404 Model 'typesafe-ai/jev-latest' not found`.

## Glob syntax

`include` and `exclude` take path globs, matched against the path with the query stripped:

<table>
<thead>
  <tr>
    <th>
      Glob
    </th>
    
    <th>
      Matches
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        /logout
      </code>
    </td>
    
    <td>
      exactly <code>
        /logout
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        /blog/*
      </code>
    </td>
    
    <td>
      one segment: <code>
        /blog/hello
      </code>
      
      , not <code>
        /blog/2026/hello
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        /api/**
      </code>
    </td>
    
    <td>
      any depth: <code>
        /api/v1/users
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        /a?
      </code>
    </td>
    
    <td>
      one character: <code>
        /a1
      </code>
    </td>
  </tr>
</tbody>
</table>

## `documentNavigation`

<warning>

Experimental.

</warning>

With it on, a click on a link the module prerendered bypasses the client router and does a real
document navigation, so the browser can hand over the copy it already has. Good for content
sites that want an MPA feel; it throws away the SPA's state on every click.

Measured behaviour: the click does leave the router out, and the browser serves the document
from whatever it already has. In a test that gives it a couple of seconds, that was the
prefetch (`deliveryType: "navigational-prefetch"`) rather than a finished prerender. The flag
promises a real document navigation that uses what is already there, not "always instant".
