Gajae-Code
Gajae-Codev0.9.1

Models & credentials

How Gajae-Code loads models from ~/.gjc/agent/models.yml, applies provider presets, resolves model roles and thinking levels, builds canonical equivalence, discovers local runtimes, and resolves API keys.

Gajae-Code (gjc) loads every concrete provider model, then builds a canonical layer above them so you can pin an exact provider/model or select a canonical id that coalesces across providers. All of this is driven by one file — ~/.gjc/agent/models.yml — plus auth resolution and runtime discovery.

This page is the practical reference. It does not paste the full schema; it covers the shape, the presets, roles, equivalence, discovery, and auth order.

Config file location

The default config path is:

~/.gjc/agent/models.yml

Legacy behavior is still present:

  • If models.yml is missing and models.json exists at the same location, it is migrated to models.yml.
  • Explicit .json / .jsonc paths are still supported when passed programmatically.

If models.yml fails schema or validation checks, the registry keeps operating with built-in models and surfaces the error in the UI.

models.yml shape

At a high level, models.yml has three top-level sections — providers, equivalence, and modelBindings:

providers:
  <provider-id>:
    # provider-level config (baseUrl, api, auth, models, ...)
equivalence:
  overrides:
    <provider-id>/<model-id>: <canonical-model-id>
  exclude:
    - <provider-id>/<model-id>
modelBindings:
  modelRoles:
    default: <provider/model-id or canonical-id>[:thinkingLevel]
  agentModelOverrides:
    executor: <provider/model-id>

provider-id is the canonical provider key used across selection and auth lookup. equivalence is optional and configures canonical model grouping on top of concrete provider models.

models.yml is strict: unknown provider/model keys fail validation before provider dispatch, so remove stale keys rather than relying on them being ignored.

Provider-level fields

A full custom provider declares a transport and a list of models:

providers:
  my-provider:
    baseUrl: https://api.example.com/v1
    apiKey: MY_PROVIDER_API_KEY
    api: openai-completions
    auth: apiKey
    headers:
      X-Team: platform
    disableStrictTools: false  # set true for Anthropic-compatible endpoints that reject `strict`
    models:
      - id: some-model-id
        name: Some Model
        reasoning: false
        input: [text]
        contextWindow: 128000
        maxTokens: 16384

Allowed provider/model api values:

openai-completions, openai-responses, openai-codex-responses, azure-openai-responses, bedrock-converse-stream, anthropic-messages, google-generative-ai, google-vertex, google-gemini-cli, ollama-chat, cursor-agent.

Allowed auth values: apiKey (default), none, or oauth. (For models.yml custom models, oauth is accepted by the schema but does not waive the apiKey requirement.)

Validation rules

  • Full custom provider (models non-empty): requires baseUrl, apiKey unless auth: none, and api at the provider level or each model.
  • Override-only provider (models missing/empty): must define at least one of baseUrl, headers, compat, requestTransform, disableStrictTools, modelOverrides, or discovery.
  • Model checks: id is required; contextWindow and maxTokens must be positive if provided.

Provider presets

For common MiniMax and GLM/zAI setups, prefer the provider presets so the OpenAI-compatible API, base URL, env var, model id, and compatibility flags are written together.

From the CLI:

gjc setup provider --preset minimax
gjc setup provider --preset minimax-cn
gjc setup provider --preset glm

The same presets are available inside the TUI:

/provider add --preset minimax
/provider add --preset glm
/provider add zai

Presets only write models.yml entries that reference documented environment variable names (MINIMAX_CODE_API_KEY, MINIMAX_CODE_CN_API_KEY, or ZAI_API_KEY); they do not store or validate real credentials. The GLM aliases (glm, zai, z-ai) write an OpenAI-compatible custom provider named glm-proxy and do not replace the first-class zai provider.

Model roles and thinking levels

Model roles let you bind a model to a job instead of hard-coding ids everywhere. Supported roles:

default, smol, slow, vision, plan, designer, commit, task

Role values live under modelRoles (in settings / modelBindings). Each role value can be:

  • provider/modelId — pins a concrete provider variant, or
  • a canonical id such as gpt-5.3-openai-code — allows provider coalescing.

Each role value can append a thinking selector suffix:

:off | :minimal | :low | :medium | :high | :xhigh

For example default: layofflabs/gpt-5.5:high. If a role points at another role, the target model is inherited normally and any explicit suffix on the referring role wins for that role-specific use.

Related settings:

SettingPurpose
modelRolesMap roles to a provider/modelId or canonical id (record).
enabledModelsScoped pattern list of selectable models.
modelProviderOrderGlobal canonical-provider precedence.
providers.kimiApiFormatopenai or anthropic request format.
providers.openaiWebsocketsauto / off / on websocket preference for the OpenAI code transport.

enabledModels and disabledProviders entries may also be scoped to a path prefix (using path, paths, pathPrefix, or pathPrefixes), so a project directory can pin a different set of models than your home directory.

Canonical equivalence

The registry keeps every concrete provider model and then builds a canonical layer above them. Canonical ids are official upstream ids only — for example anthropic-model-opus-4-6, anthropic-model-haiku-4-5, or gpt-5.3-openai-code.

Configure grouping with the equivalence block:

equivalence:
  overrides:
    zenmux/openai-code: gpt-5.3-openai-code
    p-openai-code/openai-code: gpt-5.3-openai-code
  exclude:
    - demo/openai-code-preview

Build order for canonical grouping:

Exact user override from equivalence.overrides.
Bundled official-id matches from built-in model metadata.
Conservative heuristic normalization for gateway/provider variants (e.g. stripping anthropic/ or openai/ prefixes, normalizing 4.6 -> 4-6).
Fallback to the concrete model's own id.

Heuristics are intentionally narrow: ambiguous families or versions are never merged without a bundled match or an explicit override.

When multiple concrete variants share a canonical id, resolution uses availability + auth, then modelProviderOrder, then existing registry order. Disabled or unauthenticated providers are skipped. Session state and transcripts always record the concrete provider/model that actually executed the turn.

In /model and --list-models, a canonical view appears alongside provider tabs/rows. Selecting a canonical entry stores the canonical selector; selecting a provider row stores the explicit provider/modelId.

Runtime discovery

If you do not configure them explicitly, Gajae-Code adds implicit, keyless discoverable providers for local runtimes:

ProviderDefault base URL (env override)API
ollamahttp://127.0.0.1:11434 (OLLAMA_BASE_URL)openai-responses
llama.cpphttp://127.0.0.1:8080 (LLAMA_CPP_BASE_URL)openai-responses
lm-studiohttp://127.0.0.1:1234/v1 (LM_STUDIO_BASE_URL)openai-completions

All three behave as auth: none. Discovery calls the runtime's model endpoints and synthesizes model entries with local defaults. You can also configure discovery yourself:

providers:
  ollama:
    baseUrl: http://127.0.0.1:11434
    api: openai-responses
    auth: none
    discovery:
      type: ollama

discovery.type accepts ollama, llama.cpp, or lm-studio, and requires a provider-level api.

Auth and API key resolution order

When requesting a key for a provider, the effective order is:

Runtime override (CLI --api-key).
Stored API key credential in agent.db.
Stored OAuth credential in agent.db (with refresh).
Environment variable mapping (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.).
ModelRegistry fallback resolver (provider apiKey from models.yml).

The models.yml apiKey value is first treated as an environment variable name; if no such env var exists, the literal string is used as the token. If authHeader: true and a provider apiKey is set, models get an Authorization: Bearer <resolved-key> header injected. Providers marked auth: none are treated as available without credentials.

A model can exist in the registry (getAll()) but only be selectable once auth resolves (getAvailable()).

Broker mode

When GJC_AUTH_BROKER_URL (or auth.broker.url) is set, the local SQLite credential store is replaced by a remote broker store: stored API-key and OAuth credentials are served from a broker-supplied snapshot with redacted refresh tokens, and expiry triggers a refresh on the broker rather than locally.

Treat every *_API_KEY, *_TOKEN, and cloud credential as a secret — never log or commit them.

On this page