link-assistant-router 1.14.2

Link.Assistant.Router — Claude MAX OAuth proxy and token gateway for Anthropic APIs
Documentation
use std::ffi::OsString;

use clap::Parser as _;
use serde_json::{Value, json};

use super::*;
use crate::cli::{Cli, Command};
use crate::clients::{ANTHROPIC_MODEL_OWNER, OPENAI_MODEL_OWNER, RouterModel};

fn args(values: &[&str]) -> WithArgs {
    let invocation = std::iter::once(OsString::from("router"))
        .chain(std::iter::once(OsString::from("with")))
        .chain(values.iter().map(OsString::from))
        .collect();
    let protected = crate::cli::protect_client_arguments(invocation, true);
    let cli = Cli::try_parse_from(protected).expect("parse with arguments");
    let Some(Command::With(args)) = cli.command else {
        panic!("with command expected");
    };
    args
}

fn model(id: &str, owner: &str) -> RouterModel {
    RouterModel {
        id: id.to_string(),
        owned_by: owner.to_string(),
        ..RouterModel::default()
    }
}

#[test]
fn request_rejects_conflicts_widening_without_a_selector_and_empty_ids() {
    let conflict = args(&["--model", "wrapper", "codex", "--model", "client"]);
    let error = request(&conflict)
        .err()
        .expect("conflict must fail")
        .to_string();
    assert!(error.contains("conflicting model selectors"), "{error}");
    assert!(error.contains("no token was minted"), "{error}");

    for values in [
        &["--allow-model", "extra", "codex"][..],
        &["--allow-model-substitution", "codex"][..],
    ] {
        let error = request(&args(values))
            .err()
            .expect("unanchored widening must fail")
            .to_string();
        assert!(error.contains("widen an explicit model grant"), "{error}");
    }

    let empty = args(&["--model", "", "codex"]);
    let error = request(&empty)
        .err()
        .expect("empty selector must fail")
        .to_string();
    assert!(
        error.contains("invalid exact-model token policy"),
        "{error}"
    );
}

#[test]
fn request_builds_a_sorted_deduplicated_exact_grant() {
    let parsed = args(&[
        "--model",
        "model-b",
        "--allow-model",
        "model-a",
        "--allow-model",
        "model-b",
        "--allow-model-substitution",
        "codex",
        "--model=model-b",
    ]);
    let parsed_request = request(&parsed).expect("valid exact grant");
    assert_eq!(parsed_request.forwarded.as_deref(), Some("model-b"));
    assert_eq!(
        parsed_request.requested_policy.allowed_models,
        vec!["model-a".to_string(), "model-b".to_string()]
    );
    assert!(parsed_request.requested_policy.allow_substitution);
    assert_eq!(
        parsed_request
            .requested_policy
            .substitution_source
            .as_deref(),
        Some("router with --allow-model-substitution")
    );

    let forwarded = args(&["codex", "-m", "forwarded-model"]);
    let parsed_request = request(&forwarded).expect("forwarded selector grant");
    assert_eq!(parsed_request.forwarded.as_deref(), Some("forwarded-model"));
    assert_eq!(
        parsed_request.requested_policy.allowed_models,
        vec!["forwarded-model".to_string()]
    );

    let unpinned = request(&args(&["codex"])).expect("legacy unpinned launch");
    assert!(unpinned.forwarded.is_none());
    assert!(unpinned.requested_policy.allowed_models.is_empty());
}

#[test]
fn resolution_requires_explicit_authority_for_configured_clients() {
    let credential =
        RunCredential::for_model_policy_test(vec![model("gpt-current", OPENAI_MODEL_OWNER)]);
    let explicit = args(&["--model", "gpt-current", "codex"]);
    assert_eq!(
        resolve(&explicit, None, &credential).unwrap().as_deref(),
        Some("gpt-current")
    );

    let forwarded = args(&["codex", "--model", "gpt-current"]);
    assert_eq!(
        resolve(&forwarded, Some("gpt-current"), &credential)
            .unwrap()
            .as_deref(),
        Some("gpt-current")
    );

    let agent = args(&["agent"]);
    let error = resolve(&agent, None, &credential).unwrap_err().to_string();
    assert!(error.contains("requires an explicit model"), "{error}");

    let codex = args(&["codex"]);
    assert!(resolve(&codex, None, &credential).unwrap().is_none());
}

#[test]
fn picker_selects_only_a_client_compatible_live_model() {
    let credential = RunCredential::for_model_policy_test(vec![
        model("other", ANTHROPIC_MODEL_OWNER),
        model("gpt-current", OPENAI_MODEL_OWNER),
    ]);
    let codex = args(&["--pick-model", "codex"]);
    assert_eq!(
        resolve(&codex, None, &credential).unwrap().as_deref(),
        Some("gpt-current")
    );

    let incompatible =
        RunCredential::for_model_policy_test(vec![model("other", "unrelated-provider")]);
    let error = resolve(&codex, None, &incompatible)
        .unwrap_err()
        .to_string();
    assert!(
        error.contains("advertises no model for Codex CLI"),
        "{error}"
    );

    let generic = args(&["--pick-model", "agent"]);
    assert_eq!(
        resolve(&generic, None, &incompatible).unwrap().as_deref(),
        Some("other")
    );
}

#[test]
fn validation_checks_native_claude_families_and_every_exact_grant() {
    let credential =
        RunCredential::for_model_policy_test(vec![model("claude-current", ANTHROPIC_MODEL_OWNER)]);
    let claude = args(&["--model", "claude-current", "claude"]);
    let valid = ModelAccessPolicy {
        allowed_models: vec!["claude-current".to_string()],
        ..ModelAccessPolicy::default()
    };
    validate_selection(&claude, Some("claude-current"), &credential, &valid)
        .expect("available exact selection");

    let missing = ModelAccessPolicy {
        allowed_models: vec!["missing".to_string()],
        ..ModelAccessPolicy::default()
    };
    let error = validate_selection(&claude, None, &credential, &missing)
        .unwrap_err()
        .to_string();
    assert!(
        error.contains("model `missing` is not available"),
        "{error}"
    );

    let no_anthropic =
        RunCredential::for_model_policy_test(vec![model("gpt-current", OPENAI_MODEL_OWNER)]);
    let error = validate_selection(&claude, Some("opus"), &no_anthropic, &valid)
        .unwrap_err()
        .to_string();
    assert!(error.contains("requires an Anthropic provider"), "{error}");

    let error = validate_selection(
        &claude,
        Some("another-missing-model"),
        &credential,
        &ModelAccessPolicy::default(),
    )
    .unwrap_err()
    .to_string();
    assert!(error.contains("another-missing-model"), "{error}");

    let codex = args(&["--model", "gpt-current", "codex"]);
    let empty_catalog = RunCredential::for_model_policy_test(Vec::new());
    let error = validate_selection(
        &codex,
        Some("gpt-current"),
        &empty_catalog,
        &ModelAccessPolicy::default(),
    )
    .unwrap_err()
    .to_string();
    assert!(error.contains("router has no available models"), "{error}");
}

#[test]
fn launch_diagnostic_preserves_scope_capabilities_and_policy_source() {
    let mut advertised = model("claude-current", ANTHROPIC_MODEL_OWNER);
    advertised.selector_kind = ModelSelectorKind::ProviderDynamicAlias;
    advertised.capability_provenance = json!({
        "scope": {
            "provider": "anthropic",
            "account": "account-1",
            "endpoint": "https://api.anthropic.example",
            "protocols": ["anthropic-messages"]
        },
        "fields": {
            "context_window": {"value": 200_000},
            "max_output_tokens": {"value": 32_000}
        }
    });
    let credential = RunCredential::for_model_policy_test(vec![advertised]);
    let server = ResolvedServer::at(
        "https://router.example",
        Some("ordinary-token".to_string()),
        "test",
    );
    let parsed = args(&[
        "--model",
        "claude-current[1m]",
        "--allow-model",
        "claude-backup",
        "--allow-model-substitution",
        "claude",
    ]);
    let policy = ModelAccessPolicy {
        allowed_models: vec![
            "claude-backup".to_string(),
            "claude-current[1m]".to_string(),
        ],
        allow_substitution: true,
        substitution_source: Some("router with --allow-model-substitution".to_string()),
    };
    let value = launch_diagnostic(
        &parsed,
        &server,
        &credential,
        Some("claude-current[1m]"),
        None,
        &policy,
    );
    let launch = &value["router_model_launch"];
    assert_eq!(launch["request_source"], "with --model");
    assert_eq!(launch["token_constraint"]["state"], "exact");
    assert_eq!(launch["provider"], "anthropic");
    assert_eq!(launch["account"], "account-1");
    assert_eq!(launch["protocols"][0], "anthropic-messages");
    assert_eq!(
        launch["model_descriptor"]["capabilities"]["context_window"],
        200_000
    );
    assert_eq!(launch["switching_setting"], "with --allow-model");
    assert_eq!(
        launch["substitution_setting"],
        "router with --allow-model-substitution"
    );
    print_diagnostic(
        &parsed,
        &server,
        &credential,
        Some("claude-current[1m]"),
        None,
        &policy,
    );
}

#[test]
fn launch_diagnostic_names_every_request_source_and_uses_safe_fallbacks() {
    let credential =
        RunCredential::for_model_policy_test(vec![model("gpt-current", OPENAI_MODEL_OWNER)]);
    let server = ResolvedServer::at("http://router.test", None, "test");
    let policy = ModelAccessPolicy::default();

    let forwarded = args(&["codex", "--model", "gpt-current"]);
    let value = launch_diagnostic(
        &forwarded,
        &server,
        &credential,
        Some("gpt-current"),
        Some("gpt-current"),
        &policy,
    );
    let launch = &value["router_model_launch"];
    assert_eq!(launch["request_source"], "forwarded client model argument");
    assert_eq!(launch["provider"], OPENAI_MODEL_OWNER);
    assert_eq!(launch["account"], "test-principal");
    assert_eq!(launch["protocols"], Value::Null);
    assert_eq!(launch["token_constraint"]["state"], "unpinned");

    let picked = args(&["--pick-model", "codex"]);
    assert_eq!(
        launch_diagnostic(&picked, &server, &credential, None, None, &policy)["router_model_launch"]
            ["request_source"],
        "with --pick-model"
    );

    let configured = args(&["codex"]);
    assert_eq!(
        launch_diagnostic(&configured, &server, &credential, None, None, &policy)["router_model_launch"]
            ["request_source"],
        "client configuration"
    );
}

#[tokio::test]
async fn cleanup_is_a_noop_for_an_explicit_test_credential() {
    cleanup_after_setup_failure(RunCredential::for_model_policy_test(Vec::new())).await;
}