Skip to content
Download

Providers and runs

Providers ​

This page explains the coding-agent providers sMind by Spacing Mind runs (Claude Code, Codex, GLM, Kimi, OpenCode, GitHub Copilot, Cline, Goose and Cursor), including login and each provider's config directory. It is for anyone setting up accounts.

sMind by Spacing Mind, version 0.17.0. This page describes the 0.17.0 release.

smind runs the coding-agent providers listed below. Each one is a coding agent that smind starts for a task. Five of them are new: OpenCode, GitHub Copilot, Cline, Goose and Cursor. See Native ACP providers.

Provider idName in the UIWhat smind starts
claude-nativeClaude Codethe claude command
codex-nativeCodexcodex app-server
glmGLMnpx -y glm-acp-agent (needs Node.js)
kimiKimikimi acp
opencodeOpenCodeopencode acp
copilotGitHub Copilotcopilot --acp
clineClinecline --acp
gooseGoosegoose acp
cursorCursorcursor-agent acp

Install the agent's own command first. smind does not install it for you, except that GLM downloads through npx.

Manage providers and accounts in the web UI under Settings → Providers, or with smind account.

How to check a provider is ready ​

sh
smind account test claude-native
smind account test codex-native
smind account test glm
smind account test kimi
smind account test opencode
smind account test copilot
smind account test cline
smind account test goose
smind account test cursor
smind account ls

account test prints ok and a detail line when the provider can start. For claude-native, codex-native and glm it checks that the command is on the daemon's PATH, and the five native ACP providers are checked the same way. For kimi it checks for a stored, unexpired credential. It never starts a run and never calls the network.

In the UI, use the Test button on a provider's page or on an account row.

Claude Code (claude-native) ​

Agent runs use the claude command's own login. Log in with Claude Code first, the way its documentation says. Then run smind account test claude-native.

smind can also hold a Claude account for itself. It uses the account for usage windows and for the daemon's /v1 proxy. Register one with a browser login:

sh
smind account login anthropic work

The command prints a URL and tries to open it in a browser. Sign in, and the account is stored. The label (work) is yours to choose. The provider name for the CLI is anthropic.

To add an account from a credential you already have, pipe it in on stdin:

sh
smind account add anthropic work < credential.json

The credential is read from stdin so it never appears in your shell history.

In the UI, choose Settings → Providers → Claude Code and use Connect Claude Code.

Codex (codex-native) ​

Agent runs use the codex command's own login. Log in with Codex first, then run smind account test codex-native.

For smind's own OpenAI account, the CLI provider name is openai:

sh
smind account login openai work

This opens a browser login, like the Claude one. On a machine without a browser, such as a server you reach over SSH, use a device code instead:

sh
smind account login --device openai work

It prints a verification URL and a code. Open the URL on any device and enter the code. --device is only for openai.

In the UI, choose Settings → Providers → Codex and use Connect Codex.

GLM ​

GLM manages its own login through its own CLI. smind keeps no credential for it, and accounts added in the UI are not read by it. The provider page says Managed externally via CLI.

GLM needs npx, so install Node.js. Check it with smind account test glm. To limit how many turns one prompt may use, set agents.glm.maxTurns in config.yaml. See Configuration.

Kimi ​

kimi acp expects an installed, already logged-in Kimi Code CLI (MoonshotAI/kimi-code). Install it with the vendor's script, curl -fsSL https://code.kimi.com/kimi-code/install.sh | bash (Windows: irm https://code.kimi.com/kimi-code/install.ps1 | iex), then run kimi once and log in with /login. smind does not do that login. The older Python kimi-cli is archived and no longer supported by smind.

You can also register a Kimi API key as an account. smind has no browser login for Kimi, so the credential comes in on stdin:

sh
smind account add kimi work < kimi-key.txt

--base-url <url> sets another API address.

Native ACP providers ​

These five providers speak the Agent Client Protocol. smind starts each one with its own command and does not install it.

Provider idInstallLoginVerified
opencodenpm i -g opencode-ai or brew install sst/tap/opencodenone for the built-in free models; opencode providers login or its config for your own provider keyyes, the real CLI (v1.18.33) against a local fake server
copilotnpm i -g @github/copilotpaid GitHub Copilot plan; run copilot once and use /login. ACP support is a public previewnot verified against a real login
clinenpm i -g clineprovider key from cline auth or the CLINE_API_KEY variablenot verified against a real login
goosebrew install block-goose-cli or the binary from block.github.io/goosegoose configure to pick a provider and keynot verified against a real login
cursorcurl https://cursor.com/install -fsS | bashpaid Cursor account: cursor-agent login or CURSOR_API_KEYnot verified against a real login

For OpenCode, smind sets OPENCODE_PERMISSION so it asks before bash and edit. Without that, OpenCode would allow everything without asking. smind also sets OPENCODE_DISABLE_AUTOUPDATE=1. Keep the same HOME and XDG data dir between daemon restarts, or OpenCode sessions cannot resume.

Cursor needs a 2026 build. The 2025.09.18 build has no acp command, and the check says it is too old.

smind account test <id> and the Test button check that the command is on the daemon's PATH, run <command> --version once, and say what is missing: the install command when it is not found, the login it needs when it is found, whether the version is too old (Cursor), and "not verified against a real login" for the four unverified ones. No model call is made.

Models and modes come from the agent itself, through its ACP config options, the first time smind probes the provider. The probe sets NO_BROWSER=true, so it never opens a browser.

Breaking: the ids opencode, copilot, cline, goose and cursor are now reserved built-in ids. A providers.custom entry that used one of them must be renamed.

Provider launch environment ​

Claude Code and Codex keep their settings and login in a config directory. Set that directory per provider in Settings → Providers. This is how you run another login, for example a second Claude config for work.

ProviderField labelVariable smind setsDefault directory
Claude CodeClaude config directoryCLAUDE_CONFIG_DIR~/.claude
CodexCodex homeCODEX_HOME~/.codex

Type an absolute path, or one that starts with ~/, and choose Save. Clear removes your setting. Under the field, smind shows which directory it uses and where that value came from: set in smind, from the daemon's environment or CLI default. It warns when the directory does not exist yet.

The value applies to the next run. A run that already started keeps its old setting. smind adds the variable to every launch of that provider, and it wins over the daemon's own environment. For the base providers, these two variables are the only launch environment you can set, and only as directories. An instance can set more: see Launch env and secrets.

The same setting lives in config.yaml:

yaml
providers:
  claude-native:
    env:
      CLAUDE_CONFIG_DIR: ~/.claude-work

GLM and Kimi have no launch environment field.

Recipes: compatible endpoints ​

A recipe is a built-in template for one provider instance that points Claude Code (or Codex) at another vendor's API. smind ships the catalog inside the daemon: there is no remote catalog, and it changes only with a release. provider.recipes lists it (read-only). provider.addFromRecipe {recipe, id?, label?, baseUrl?, models?, secrets} creates the instance from one row. It is for Humans only: agents and plugins are refused. For Z.AI you only paste a key: secrets: {"api-key": "<key>"}. The key goes into the secret store and is never written to config.yaml, returned, logged or put on a command line. Adding the same recipe with the same id again changes nothing in the config; an id used by another entry is refused.

Every Claude Code recipe disallows WebSearch (third-party endpoints have no hosted search tool) and runs with its own CLAUDE_CONFIG_DIR under <SMIND_HOME>/providers/<id>/claude, so its files never mix with ~/.claude. Set inheritClaudeConfig: true on the entry to use ~/.claude instead. A recipe whose endpoint cannot turn thinking off is marked thinking: hidden: the run ignores a requested thinking level and provider.list reports thinking: hidden so a client hides the control. A model id smind does not know is shown as the raw id.

Rate limits follow the vendor: an instance has a vendor profile (zai, anthropic, openai or generic). A GLM-worded 429 on the Z.AI instance (vendor: zai) puts that instance on cooldown until its UTC+8 reset and leaves claude-native running. Any other vendor's 429 is classified by the generic patterns, without a reset time.

Add from recipe in Settings ​

In Settings → Providers, Add from recipe opens the catalog as a list. Each row shows the recipe's name, a one-line description, the wire protocol it speaks, and an Unverified badge when the row was never run against a live endpoint. Pick a row to open its form:

  1. Read the caveats and open the vendor's docs link if you need to.
  2. Set an id and label (both optional; the id defaults to the recipe id).
  3. For a recipe that needs one, type the endpoint URL.
  4. Edit the model list: it starts with the recipe's models; remove one with its ✕ or add an id and press Enter. A recipe that needs a model id (Ollama, LM Studio, LiteLLM) will not submit with an empty list.
  5. Paste the key in the password field and press Add provider.

The key leaves the field as soon as you submit, whether or not the daemon accepts it, and is never shown again. If the daemon refuses (for example the id is used by another entry), its message appears under the form and you re-enter the key. The new instance shows up in the provider list with a recipe badge, plus unverified when the row is unverified, and wears the icon of the provider it extends.

On a recipe instance's detail page, the Keys card lists each key as Set · ••••abcd (the last four characters of a fingerprint, not of the key) or Not set. Replace key opens a password dialog; Clear key asks for confirmation. A recipe whose endpoint cannot turn thinking off says so under Recipe, and the thinking picker is hidden for that provider. To change an existing instance's models, disallowed tools or the inherit-~/.claude setting, see Edit an instance.

On the phone, the new-task form shows a read-only note under the provider picker ("Recipe zai-glm · Unverified · Key set (…ab12)"); keys are never entered or shown on mobile.

Recipe idHarnessNeedsSourceVerified
zai-glmClaude Codekeyvendor docsno
qwen-dashscopeClaude CodekeyPaseo docsno
deepseekClaude Codekeyvendor docsno
kimi-moonshotClaude Codekeyinferredno: the Claude Code base URL is a guess
minimaxClaude Codekeyvendor docsno
openrouterClaude Codekeyvendor docsno
ollamaClaude Codea model id (local, no key)vendor docsno
lm-studioClaude Codea model id (local, no key)vendor docsno
litellmClaude CodeURL, key, model idsvendor docsno
anthropic-compatibleClaude CodeURL, key, model idsPaseo docsno
openai-responses-compatibleCodexURL, key, model idsPaseo docsno
chat-completions-via-opencodeOpenCodeURL, key, model idsvendor docsno

"Verified: no" means no row has been run against a live endpoint yet; the catalog records source and verifiedAt per row, and provider.recipes returns unverified: true for each. Treat the base URLs and model ids as the vendor's documentation said on 2026-10-30. The Kimi row is inferred and should be tried with care. The openai-responses-compatible row speaks only the OpenAI Responses API (Codex has no chat-completions mode); a vendor with only a chat-completions endpoint uses the OpenCode row, described under Chat-completions endpoints.

For GLM, the default way to add a new login is now the zai-glm recipe (Claude Code against the Z.AI endpoint). The built-in glm provider (the glm-acp-agent wrapper) stays for one more release.

Provider instances ​

A provider instance is a second login of Claude Code or Codex that has its own config directory. It runs next to the base provider: both can have runs at the same time, each with its own login, sessions, usage and cooldowns.

Add an instance from Settings ​

  1. Open Settings → Providers and choose Add instance.
  2. Under Found in your home folder, smind lists the ~/.claude* and ~/.codex* directories it found, for example ~/.claude-work → Claude work. Choose one and the instance is created at once and appears in the provider list. The chip stays in place with a check mark. smind only reads the names of the entries in your home folder, never the files inside them. Directories that a built-in provider or an existing instance already uses are not suggested.
  3. For any other directory, open Another directory, pick Claude Code or Codex, fill in Id, Label and Config directory, and choose Add instance.

If smind refuses the entry, for example because the id is already used, the message appears under the control you used and nothing is added.

Log in once inside the new directory with the provider's own CLI (for example run claude with CLAUDE_CONFIG_DIR set to that directory). Open the instance in Providers and choose Test to check it.

The same thing in config.yaml ​

Instances are entries under providers.custom:

yaml
providers:
  custom:
    - id: claude-work
      label: Claude work
      extends: claude-native
      env:
        CLAUDE_CONFIG_DIR: ~/.claude-work
    - id: codex-alt
      label: Codex alt
      extends: codex-native
      env:
        CODEX_HOME: ~/.codex-alt
  • id is 1 to 64 characters of a-z, 0-9, ., _ and -, starting with a letter or digit. It may not be a built-in id (claude-native, codex-native, glm, kimi, opencode, copilot, cline, goose, cursor) and must be unique.
  • extends is a built-in provider id or the id of an earlier ACP entry in the list. An instance cannot extend another instance.
  • env takes typed values, and only the names the provider's harness allows; see Launch env and secrets.
  • command replaces the executable the instance runs (for an ACP provider, with args; for claude-native and codex-native, the executable only).
  • models replaces the model list of the provider; additionalModels adds to it (merged by id). Set one or the other. Each entry has an id and an optional label, description and default: true. Without either, an instance of claude-native lists the Claude catalog. The first model (or the one marked default) is the instance's default, never the base's. smind accepts any model id of a valid shape and shows an id it does not know as it is.
  • disallowedTools lists Claude Code tools the instance may not use (for example WebSearch); it is passed to the CLI as --disallowedTools and applies to instances of claude-native only.
  • endpoint points an instance of codex-native at an OpenAI Responses-compatible API instead of the Codex login; see Codex endpoint instances.
  • An instance may not set modes.

A bad entry is rejected with the field named, for example providers.custom[2].command: required. smind reads providers.custom at startup. After you edit the file by hand, choose Reload providers in the detail of any custom provider in Settings → Providers to apply it without a restart. To remove an instance, delete its entry and reload. The Settings screen has no remove button.

Edit an instance ​

Open Settings → Providers and choose the instance. Three things can be changed there:

  • Models: Replace list makes your list the whole model list; Add to list merges your models into the provider's own list by id.
  • Disallowed tools: a chip list of Claude Code tools the instance may not use (instances of claude-native only).
  • Use ~/.claude: a toggle that makes the instance use ~/.claude instead of a private config directory (instances of claude-native only).

The same edit from the command line:

smind provider update <id> --models a,b --add-models a,b \
  --disallow T,U --inherit-claude-config=true|false

--models replaces the list, --add-models adds to it, --disallow sets the disallowed tools. An empty value (for example --models "") removes that field from the entry.

The edit changes only those fields of that one entry in config.yaml. Everything else is left byte for byte as it was: the other entries, comments and blank lines. Keys stay under Keys (or provider.setSecret); this edit never touches them. An entry written in flow style (- {id: x, ...}) is refused with a message to edit it by hand.

Only a person can edit an instance: agents and plugins cannot. The change applies live, with no restart and no Reload providers.

Codex endpoint instances ​

An instance of codex-native can talk to any API that implements the OpenAI Responses API (a gateway such as LiteLLM, OpenRouter's Responses route, or a vendor's own /v1/responses):

yaml
providers:
  custom:
    - id: gateway
      label: Team gateway
      extends: codex-native
      endpoint:
        kind: openai-responses
        baseUrl: https://llm.example.com
      models:
        - id: gpt-5.1
          default: true
  • baseUrl gets /v1 appended exactly once and trailing slashes are dropped: https://h, https://h/ and https://h/v1/ all become https://h/v1. It must be an http(s) URL with no userinfo, query or fragment. Put the key in the instance environment (OPENAI_API_KEY), never in the URL.
  • models is required: smind does not discover the models of a custom endpoint. additionalModels is not allowed together with endpoint.
  • smind tells Codex about the endpoint on every thread start and resume (model_providers.<id> with wire_api = "responses"). env_key and requires_openai_auth = false are sent only when a key is configured, so a keyless local endpoint does not demand one.
  • The instance gets a private CODEX_HOME under $SMIND_HOME/providers/<id>/codex (mode 0700) unless you set CODEX_HOME yourself, so it never shares sessions or login state with your own ~/.codex.
  • Responses only. Codex does not speak the Chat Completions API. An endpoint of kind openai-chat (or chat-completions) is rejected at load time with endpoint.kind: ... unsupported by the Codex harness, not with a 404 in the middle of a run. For a chat-completions-only vendor use an Anthropic-compatible route into Claude Code, a Responses gateway such as LiteLLM in front of it, or OpenCode.
  • Not yet verified against a live endpoint: tested with a fake app-server only.

Chat-completions endpoints through OpenCode ​

A vendor that offers only an OpenAI chat-completions endpoint (no Responses API, no Anthropic API) runs through an instance of the opencode provider. Add it with the chat-completions-via-opencode recipe, or by hand:

yaml
providers:
  custom:
    - id: vendor-x
      extends: opencode
      endpoint:
        kind: chat-completions
        baseUrl: https://api.vendor.example/v1
      models:
        - id: chat-1
        - id: qwen/qwen3-coder
      env:
        SMIND_OPENCODE_API_KEY: { secret: api-key }
  • baseUrl is the vendor's chat-completions root (OpenCode adds /chat/completions). models is required: nothing is discovered for a custom endpoint, and the first model is the default. Leave the key out for a local server that needs none.
  • The key stays out of every file. It is stored write-only like any provider secret and handed to OpenCode through its environment; the generated OpenCode configuration holds only a reference to it. It never appears on a command line, in a log, or in OpenCode's own files.
  • A private OpenCode home. The instance runs OpenCode with its own configuration, data, state and cache under $SMIND_HOME/providers/<id>/opencode, readable by you only (mode 0700; on Windows an access list granting only your account and the system), so it never reads your own OpenCode login or settings and its conversations are not readable by other accounts.
  • The repository's own opencode.json is ignored, along with auto-update, sharing and model-catalog downloads. A repository could otherwise define a provider of its own that reads the key and send it to a server of its choosing.
  • Permissions are unchanged. OpenCode still asks before running a shell command or editing a file, and those questions reach you like any other provider's.
  • Refused when you add it: plain http to any host but a loopback address (the key would cross the network in the clear: use https, or tunnel it); a URL with a user name, password, query or fragment; an endpoint kind other than chat-completions; no models; additionalModels; and setting the environment names smind manages itself (OPENCODE_CONFIG, OPENCODE_CONFIG_CONTENT, OPENCODE_DISABLE_PROJECT_CONFIG, the XDG_* directories and a few more).
  • Checked against OpenCode itself and a local fake endpoint only, never a real vendor. OpenCode needs a model that supports tool calls.

Where an instance shows up ​

An instance appears wherever a provider does: the model picker in the composer, the provider choice when you create or edit an agent profile, the provider list in Settings → Providers (with its config directory and where it came from), the readiness Test, and the Provider grouping in Usage. It wears the base provider's mark. An instance has no smind account row, so Connect account does not apply to it.

Custom ACP providers ​

A providers.custom entry without extends adds an agent that speaks the Agent Client Protocol:

yaml
providers:
  custom:
    - id: my-agent
      label: My agent
      command: /usr/local/bin/my-agent
      args: ["--acp"]
      env:
        MY_AGENT_HOME: ~/.my-agent
      models:
        - id: my-model
          label: My model
          default: true
      modes:
        - id: default
          label: Default

command is required. env accepts any valid variable name, and smind never prints its values in an error; secret-looking names must be references, see Launch env and secrets. models and modes are optional lists of id, label, description and, for a mode, autoApproves. A custom ACP provider shows the generic provider icon and the label you gave it.

For an agent that cannot take MCP servers, set acp.supportsMcpServers: false. smind then sends it none. The same setting works on an instance of an ACP provider:

yaml
providers:
  custom:
    - id: my-agent
      label: My agent
      command: /usr/local/bin/my-agent
      args: ["--acp"]
      acp:
        supportsMcpServers: false

Launch env and secrets ​

An instance's env does more than point at a config directory. Every value is typed:

  • a plain string is a literal: a non-secret value, kept as written in config.yaml;
  • {secret: <name>} is a reference to a key smind stores for you. The key itself never goes in config.yaml.
  • {passthrough: true} copies the variable from the daemon's own environment at launch, for keys you export outside smind.

A Claude Code instance pointed at Z.AI:

yaml
providers:
  custom:
    - id: zai
      extends: claude-native
      env:
        ANTHROPIC_BASE_URL: https://api.z.ai/api/anthropic
        ANTHROPIC_AUTH_TOKEN: {secret: zai-key}
        API_TIMEOUT_MS: 3000000

Each harness allows only certain names:

HarnessAllowed names
claude-codeANTHROPIC_BASE_URL (url); ANTHROPIC_AUTH_TOKEN and ANTHROPIC_API_KEY (secret); ANTHROPIC_MODEL, ANTHROPIC_DEFAULT_OPUS_MODEL, ANTHROPIC_DEFAULT_SONNET_MODEL, ANTHROPIC_DEFAULT_HAIKU_MODEL, ANTHROPIC_DEFAULT_FABLE_MODEL, ANTHROPIC_SMALL_FAST_MODEL, CLAUDE_CODE_SUBAGENT_MODEL, CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC, CLAUDE_CODE_EFFORT_LEVEL, CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY, ENABLE_TOOL_SEARCH, CLAUDE_CODE_SUBPROCESS_ENV_SCRUB (plain); API_TIMEOUT_MS and CLAUDE_CODE_AUTO_COMPACT_WINDOW (int); CLAUDE_CONFIG_DIR (path)
codex-app-serverOPENAI_BASE_URL (url), OPENAI_API_KEY (secret), CODEX_HOME (path)
acpany valid variable name; names ending _KEY, _TOKEN, _SECRET or _PASSWORD are secret-kind

A name outside the list is rejected with the field named, for example providers.custom[1].env.PATH. A secret-kind name with its value written inline is rejected too: a key is never written in config.yaml. The one exception is ANTHROPIC_API_KEY, which accepts an empty value where a vendor needs the variable set but empty.

This is a breaking change from the earlier rule that an instance's env could only carry CLAUDE_CONFIG_DIR or CODEX_HOME as a path. An entry that put a key inline in env must move it to the secret store.

Setting a key ​

Keys are stored by smind and referenced by name from env. Two write-only RPCs manage them: provider.setSecret {instanceId, name, value} stores a value, and provider.clearSecret {instanceId, name} removes one. Both are for Humans: calls from agents and plugins are refused. No method ever returns a stored value. provider.list shows, per secret, only {name, env, set, fingerprint}, where the fingerprint is the last 4 characters of a SHA-256 — enough to see that a key changed, not enough to use it.

Values live in smind.db, with the same protection as account credentials. A secret is resolved only when a run launches, into the child process environment. Deleting an instance from config.yaml and reloading providers (or restarting smind) removes its stored secrets.

What is and is not protected ​

Protected. A stored key never appears in config.yaml, in the launch argv, in logs, in events, in provider.list, in error text or in a relay response. The secret store is on secretdeny's deny list for the Claude CLI's tools.

Not protected (residual). The agent's own shell can read its environment. By default Claude Code does not scrub the token from its Bash tool (measured 2026-10-30, claude 2.1.310, macOS: printenv shows ANTHROPIC_AUTH_TOKEN). For claude-code instances that hold a secret, smind adds best-effort deny rules: Bash env, printenv, set, export -p, declare -x and echo $NAME, plus Read of /proc/*/environ and <SMIND_HOME>/providers/**. These are pattern matches and are trivially bypassed (python -c, a script file, a command assembled in pieces). They raise the cost of a lazy prompt injection; they do not stop a determined one. Codex and ACP agents have no equivalent: their shell tools inherit the env.

The recommendation that follows from this: use a narrowly scoped key with a spend limit, and for Codex/ACP vendors put a local gateway (for example LiteLLM) in front, so the agent holds only a throwaway token.

CLAUDE_CODE_SUBPROCESS_ENV_SCRUB ​

smind does not set this variable: it is off by default, on purpose. Measured (2026-10-30, claude 2.1.310): with it on, Claude Code forces the permission mode back to default — a run in bypassPermissions then refuses a Bash command that uses a shell variable — which conflicts with smind's permission modes. smind's own MCP stdio env (run id, validator token) was unaffected. The variable does hide ANTHROPIC_AUTH_TOKEN, ANTHROPIC_API_KEY and most *_KEY/*_TOKEN names from the Bash tool. If you accept default permissions, you may opt in per instance with CLAUDE_CODE_SUBPROCESS_ENV_SCRUB: "1" in the instance env.