Harness

Routing

The router reads the rules in sequence. The rule with the highest priority number has control. An explicit /local or /cloud command always has control. The token limits are the last test, not the first test of the difficulty of a task.

The priority sequence (default profile)

PriorityRuleResult
100 / 99Explicit /local /fast / /cloud /heavySets the route. No other rule can change it.
95composer_wrapup_originSends a Cursor Composer summary message to the cloud or to the origin
~85–70task_classifierSends a tool task or a cloud-only task to the cloud. Sends a small task to the local model.
75Complexity testYou must turn this on with complexity.enabled
55 / 50Starlark scriptsA message with /swarm or [fanout] goes to the local model and uses fan-out. A refactor task also goes local. This profile sets orchestration.fan_out.enabled, so fan-out operates.
10 / 5Context sizeMore than 8000 tokens goes to the cloud. A smaller context goes to the local model.
0DefaultGoes to the cloud. In the pure local profile it goes to the local model.

Glider finds a delegate flag (/claude, /cursor-agent, /agy) before it reads these rules. Refer to Delegation.

How to write a rule

Each entry of routing.rules has this structure:

routing:
  rules:
    - name: Explicit Local        # the name in the log and on the dashboard
      priority: 100               # a larger number has control
      enabled: true               # omit it, and the rule operates
      trigger:
        type: explicit            # one of the six types below
        commands: ["/local", "/fast"]
      action:
        target: local             # local | cloud
        model: qwen2.5-coder:14b
FieldFunction
nameThe name that the dashboard and the log show. Give a name that you can find.
priorityAn integer. The router reads the rules from the largest number to the smallest. The first rule with a match has control.
enabledfalse keeps the rule in the file, and the router does not use it. With no value, the rule operates.

The six trigger types

A type that is not in this table gives an error when Glider starts. The error names the rule.

TypeFields that it usesMatch condition
explicitcommandsThe message contains one of these commands. The command can go at any position.
regexpatternThe regular expression agrees with the most recent message.
context_sizeoperator, valueThe count of tokens agrees with the test.
alwaysEach message. Use it at priority 0, as the last rule.
scriptfileA Starlark script decides. Refer to below.
composer_wrapupA summary message of Cursor Composer. The names wrapup_origin and composer_wrapup_origin also operate.

These are the operators of context_size:

OperatorTest
> · >=More than the value, or the value.
< · <=Less than the value, or the value.
== · =The same as the value.

The action

FieldValuesFunction
targetlocal · cloudWhich side answers.
backenda name in backendsWhich backend, when the target is the cloud.
modela model nameWhich model that backend uses.
adaptera LoRA nameAn adapter for that model. vLLM only.

A rule in Starlark

A script rule gives the decision to a file. The file must supply a function evaluate(request). The function returns nothing for no match, or a dictionary. A script that returns an action wins against the action block of the rule. Therefore keep the two in agreement.

def evaluate(request):
    if "refactor" in request.messages[-1].content:
        return {
            "matched": True,
            "action": {"target": "local", "model": "qwen2.5-coder:14b"},
        }

These are the fields of request:

FieldTypeContent
modelstringThe model that the CLI asked for.
streamboolThe CLI asked for a stream.
messageslistEach item has role and content.
estimated_tokensintThe size of the context.
has_toolsboolThe request carries tools[].
complexity_scoreint0 to 100.
complexity_sourcestringheuristic or cursor.
temperaturefloatPresent only when the CLI sent it.
max_tokensintPresent only when the CLI sent it.

The dictionary that you return accepts these keys:

KeyFunction
matchedTrue for a match.
action.target · .backend · .model · .adapterThe same as the action above.
action.strategysingle, fan_out, pipeline or ensemble. fan_out also needs orchestration.fan_out.enabled.

POST /api/playground/parse reads a script and gives its errors. POST /api/router/explain shows which rule a message obeys, and GET /api/router/lint finds an error in the full set.

The rules that Glider adds

Two sections put rules in the sequence which are not in routing.rules. The configuration reference gives each setting.

SectionWhat it adds
routing.task_classifierThree rules: a request with tools goes to the cloud (85), a message that agrees with the cloud patterns goes to the cloud (80), and a message that agrees with the small patterns goes to the local model (70).
routing.complexityOne rule at priority 75. A score of cloud_above or more goes to the cloud. It is off until you turn it on.

routing.tool_followup is different. It adds no rule. It decides where the calls of a tool go after a parent turn has its route, with an allowlist and a denylist of tool names.

The Cursor MITM turn policy

This mechanism applies only to the Cursor MITM protocol. Refer to MITM for the details. The full policy is in planning/routing_and_context.md.