# Environment Variables (Current Runtime Reference) This reference is derived from current code paths in: - `packages/coding-agent/src/**` - `packages/ai/src/**` (provider/auth resolution used by coding-agent) - `packages/utils/src/**` and `packages/tui/src/**` where those vars directly affect coding-agent runtime It documents only active behavior. ## Crash relay | Variable | Used for | Trusted-source behavior | | --- | --- | --- | | `GJC_CRASH_SENTRY_DSN` | Sentry destination for the opt-in crash relay when `crashReport.upstreamDsn` is empty and global `crashReport.upstream` is `sentry` | Resolved through `$credentialEnv`; a project `.env` cannot provide it. A configured global DSN takes precedence. | ## Resolution model and precedence Most runtime lookups use `$env` from `@gajae-code/utils` (`packages/utils/src/env.ts`). `$env` loading order: 1. Existing process environment (`Bun.env`) 2. Project `.env` (`$PWD/.env`) for keys not already set 3. Agent `.env` (`~/.gjc/agent/.env`, respecting `GJC_CONFIG_DIR` / `GJC_CODING_AGENT_DIR`) for keys not already set 4. Config-root `.env` (`~/.gjc/.env`, respecting `GJC_CONFIG_DIR`) for keys not already set 5. Home `.env` (`~/.env`) for keys not already set 6. Login shell rc files (`~/.zshenv`, `~/.zprofile`, `~/.zshrc`, `~/.bash_profile`, `~/.bashrc`) for keys not already set Step 6 does not execute those files. Each is scanned line by line for literal `export NAME=value` or `NAME=value` assignments, and surrounding quotes are stripped. Values that are not literal are dropped rather than resolved: a command substitution such as `export FOO=$(...)` is discarded. Because the scan is per line and has no notion of shell block structure, it does not reflect whether an assignment would actually run. An assignment nested in an `if` or a function body is read exactly like a top-level one, so a value you guarded behind something like `if [ -n "$CI" ]` in `~/.zshrc` still reaches `$env` unconditionally. Only assignments that do not start their own line — for example one packed after `case ... in` on the same line — are missed. Keys are used exactly as written. A `PI_`-prefixed key in a `.env` file is not mirrored to its `GJC_` counterpart, or the reverse — where both spellings are accepted it is because the reading code asks for both names. --- ## 1) Model/provider authentication These are consumed via `getEnvApiKey()` (`packages/ai/src/stream.ts`) unless noted otherwise. ### Core provider credentials | Variable | Used for | Required when | Notes / precedence | | ------------------------------- | ------------------------------------------------ | -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | | `ANTHROPIC_OAUTH_TOKEN` | Anthropic API auth | Using Anthropic with OAuth token auth | Takes precedence over `ANTHROPIC_API_KEY` for provider auth resolution | | `ANTHROPIC_API_KEY` | Anthropic API auth | Using Anthropic without OAuth token | Fallback after `ANTHROPIC_OAUTH_TOKEN` | | `ANTHROPIC_FOUNDRY_API_KEY` | Anthropic via Azure Foundry / enterprise gateway | `CLAUDE_CODE_USE_FOUNDRY` enabled | Takes precedence over `ANTHROPIC_OAUTH_TOKEN` and `ANTHROPIC_API_KEY` when Foundry mode is enabled | | `OPENAI_API_KEY` | OpenAI auth | Using OpenAI-family providers without explicit apiKey argument | Used by OpenAI Completions/Responses providers | | `GEMINI_API_KEY` | Google Gemini auth | Using `google` provider models | Primary key for Gemini provider mapping | | `GOOGLE_API_KEY` | Gemini image tool auth fallback | Using `gemini_image` tool without `GEMINI_API_KEY` | Used by coding-agent image tool fallback path | | `GROQ_API_KEY` | Groq auth | Using Groq models | | | `CEREBRAS_API_KEY` | Cerebras auth | Using Cerebras models | | | `DEEPINFRA_API_KEY` | DeepInfra auth | Using `deepinfra` provider | OpenAI-compatible Chat Completions endpoint; use `serviceTier: priority` for DeepInfra priority inference | | `FIREWORKS_API_KEY` | Fireworks auth | Using Fireworks models | | | `TOGETHER_API_KEY` | Together auth | Using `together` provider | | | `HUGGINGFACE_HUB_TOKEN` | Hugging Face auth | Using `huggingface` provider | Primary Hugging Face token env var | | `HF_TOKEN` | Hugging Face auth | Using `huggingface` provider | Fallback when `HUGGINGFACE_HUB_TOKEN` is unset | | `SYNTHETIC_API_KEY` | Synthetic auth | Using Synthetic models | | | `NVIDIA_API_KEY` | NVIDIA auth | Using `nvidia` provider | | | `NANO_GPT_API_KEY` | NanoGPT auth | Using `nanogpt` provider | | | `VENICE_API_KEY` | Venice auth | Using `venice` provider | | | `LITELLM_API_KEY` | LiteLLM auth | Using `litellm` provider | OpenAI-compatible LiteLLM proxy key | | `LM_STUDIO_API_KEY` | LM Studio auth (optional) | Using `lm-studio` provider with authenticated hosts | Local LM Studio usually runs without auth; any non-empty token works when a key is required | | `OMLX_API_KEY` | oMLX auth (optional) | Using `omlx` provider with authenticated hosts | Local oMLX usually runs without auth; any non-empty token works when a key is required | | `OLLAMA_API_KEY` | Ollama auth (optional) | Using `ollama` provider with authenticated hosts | Local Ollama usually runs without auth; any non-empty token works when a key is required | | `LLAMA_CPP_API_KEY` | llama.cpp auth (optional) | Using `llama.cpp` provider with authenticated hosts | Local llama.cpp usually runs without auth; any non-empty token works when a key is configured | | `XIAOMI_API_KEY` | Xiaomi MiMo auth | Using `xiaomi` provider | | | `MOONSHOT_API_KEY` | Moonshot auth | Using `moonshot` provider | | | `XAI_API_KEY` | xAI auth | Using xAI models | | | `OPENROUTER_API_KEY` | OpenRouter auth | Using OpenRouter models | Also used by image tool when preferred/auto provider is OpenRouter | | `MISTRAL_API_KEY` | Mistral auth | Using Mistral models | | | `ZAI_API_KEY` | z.ai auth | Using z.ai models | Also used by z.ai web search provider | | `JUNIE_API_KEY` | JetBrains AI (Junie) auth | Using `jetbrains-junie` models | Access token from [junie.jetbrains.com/cli](https://junie.jetbrains.com/cli); sent as `Authorization: Bearer` | | `MINIMAX_API_KEY` | MiniMax auth | Using `minimax` provider | | | `AZURE_OPENAI_API_KEY` | Azure OpenAI auth | Using `azure-openai` / `azure-openai-responses` models | Pair with `AZURE_OPENAI_BASE_URL` or `AZURE_OPENAI_RESOURCE_NAME` | | `MINIMAX_CODE_API_KEY` | MiniMax Code auth | Using `minimax-code` provider | | | `MINIMAX_CODE_CN_API_KEY` | MiniMax Code CN auth | Using `minimax-code-cn` provider | | | `OPENCODE_API_KEY` | OpenCode auth | Using `opencode-go` / `opencode-zen` models | | | `QIANFAN_API_KEY` | Qianfan auth | Using `qianfan` provider | | | `QWEN_OAUTH_TOKEN` | Qwen Portal auth | Using `qwen-portal` with OAuth token | Takes precedence over `QWEN_PORTAL_API_KEY` | | `QWEN_PORTAL_API_KEY` | Qwen Portal auth | Using `qwen-portal` with API key | Fallback after `QWEN_OAUTH_TOKEN` | | `ZENMUX_API_KEY` | ZenMux auth | Using `zenmux` provider | Used for ZenMux OpenAI and Anthropic-compatible routes | | `OPENGATEWAY_API_KEY` | OpenGateway (by Sionic AI) auth | Using `opengateway` provider | OpenAI-compatible gateway; models discovered via `/v1/models` | | `BIZROUTER_API_KEY` | BizRouter auth | Using `bizrouter` provider | Korean enterprise LLM gateway; OpenAI-compatible, models discovered via `/v1/models` | | `MARA_API_KEY` | Mara Cloud auth | Using `mara` provider | OpenAI-compatible enterprise inference platform; models discovered via `/v1/models` | | `VLLM_API_KEY` | vLLM auth/discovery opt-in | Using `vllm` provider (local OpenAI-compatible servers) | Any non-empty value works for no-auth local servers | | `CURSOR_ACCESS_TOKEN` | Cursor provider auth | Using Cursor provider | | | `AI_GATEWAY_API_KEY` | Vercel AI Gateway auth | Using `vercel-ai-gateway` provider | | | `CLOUDFLARE_AI_GATEWAY_API_KEY` | Cloudflare AI Gateway auth | Using `cloudflare-ai-gateway` provider | Base URL must be configured as `https://gateway.ai.cloudflare.com/v1///anthropic` | | `ALIBABA_TOKEN_PLAN_API_KEY` | Alibaba Token Plan auth | Using `alibaba-token-plan` provider | | | `CLINE_API_KEY` | Cline API / ClinePass auth | Using the `cline-pass` provider preset | Create under Settings > API Keys in the Cline dashboard | | `CMD_API_KEY` | Command Code Provider API auth | Using the `commandcode-goat` provider preset | The GOAT coding plan may use this API according to its plan entitlement | | `DEEPSEEK_API_KEY` | DeepSeek auth | Using DeepSeek models | | | `KILO_API_KEY` | Kilo auth | Using Kilo models | | | `OLLAMA_CLOUD_API_KEY` | Ollama Cloud auth | Using `ollama-cloud` provider | | | `GITLAB_TOKEN` | GitLab Duo auth | Using `gitlab-duo` provider | | ### GitHub/Copilot token chains | Variable | Used for | Chain | | ---------------------- | ------------------------------------------------ | ---------------------------------------------------- | | `COPILOT_GITHUB_TOKEN` | GitHub Copilot provider auth | `COPILOT_GITHUB_TOKEN` → `GH_TOKEN` → `GITHUB_TOKEN` | | `GH_TOKEN` | Copilot fallback; GitHub API auth in web scraper | In web scraper: `GITHUB_TOKEN` → `GH_TOKEN` | | `GITHUB_TOKEN` | Copilot fallback; GitHub API auth in web scraper | In web scraper: checked before `GH_TOKEN` | ### Auth broker / auth gateway (remote credential vault) When the broker is enabled, the local SQLite credential store is bypassed and all OAuth refresh / access tokens live on the broker host. See [`auth-broker-gateway.md`](./auth-broker-gateway.md) for the full protocol, CLI surface, and 5-min/15-s usage cache layering. | Variable | Used for | Required when | Notes / precedence | | ----------------------- | ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `GJC_AUTH_BROKER_URL` | Base URL of the remote auth-broker (e.g. `https://broker.tailnet:8765`); selects broker mode | Resolving credentials through a broker; also required by `gjc auth-gateway serve` (the gateway is itself a broker client) | Wins over nested `auth.broker.url` in the global `config.yml`. A resolved URL without a token is a hard startup error; GJC does not fall back to local SQLite. | | `GJC_AUTH_BROKER_TOKEN` | Bearer token sent on every broker endpoint except `/v1/healthz` | A broker URL is set and no token is available from nested `auth.broker.token` or `/auth-broker.token` | Resolution: this env → nested `auth.broker.token` → `/auth-broker.token` (mode `0600`). Nested URL/token values may be exact `$ENV_NAME` references resolved from the trusted process environment. `` is `~/.gjc/` (respecting `GJC_CONFIG_DIR`). | The gateway has no dedicated env vars — it inherits `GJC_AUTH_BROKER_*`. Its own inbound bearer token lives at `/auth-gateway.token` and is managed via `gjc auth-gateway token`. ### Multi-account credential ranking When more than one OAuth credential is stored for the same provider (e.g. several Anthropic accounts), `AuthStorage` ranks them at session start to pick which one serves the session. This env var selects the ranking strategy; it is fully opt-in and does not change the default. | Variable | Used for | Required when | Notes / precedence | | ----------------------------- | ------------------------------------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `GJC_CREDENTIAL_RANKING_MODE` | Multi-account OAuth credential selection strategy | Never (opt-in) | `balanced` (default) prefers the least-drained account (spreads load, keeps burst headroom). `earliest-reset` prefers the soonest-to-reset non-blocked account (earliest-expiry-first) so perishable tumbling-window quota (e.g. Claude 5h/7d) is drained before reset. Unset/unknown → `balanced`. Only affects session-start ranking; blocked/exhausted accounts still sort last. | ### External CLI credential import roots `gjc setup credentials`, the TUI "import existing credentials" action, and the startup auto-import discover Claude Code and Codex CLI credentials on disk. Both CLIs relocate their own config root through the environment, so gjc follows the same variables instead of assuming the home-directory default. This is what makes an account selected by an external account switcher (which launches the shell with these variables set) the account gjc imports. | Variable | Used for | Required when | Notes / precedence | | -------------------- | --------------------------------------------------------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | `CLAUDE_CONFIG_DIR` | Directory holding Claude Code's `.credentials.json` | Claude Code's config root is not `~/.claude` | Read through `$credentialEnv` (project `.env` cannot redirect it). Must be absolute; relative or blank values fall back to `~/.claude`. | | `CODEX_HOME` | Directory holding Codex CLI's `auth.json` | Codex CLI's home is not `~/.codex` | Read through `$credentialEnv` (project `.env` cannot redirect it). Must be absolute; relative or blank values fall back to `~/.codex`. | Redacted summaries name the variable (`Claude Code ($CLAUDE_CONFIG_DIR/.credentials.json)`), never the resolved path. macOS Keychain discovery is unaffected: it is still only consulted when no credential file is found. --- ## 2) Provider-specific runtime configuration ### Anthropic Foundry Gateway (Azure / enterprise proxy) When `CLAUDE_CODE_USE_FOUNDRY` is enabled, Anthropic requests switch to Foundry mode: - Base URL resolves from `FOUNDRY_BASE_URL` (fallback remains model/default base URL if unset). - API key resolution for provider `anthropic` becomes: `ANTHROPIC_FOUNDRY_API_KEY` → `ANTHROPIC_OAUTH_TOKEN` → `ANTHROPIC_API_KEY`. - `ANTHROPIC_CUSTOM_HEADERS` is parsed as comma/newline-separated `key: value` pairs and merged into request headers. - TLS client/server material can be injected from env values: `NODE_EXTRA_CA_CERTS`, `CLAUDE_CODE_CLIENT_CERT`, `CLAUDE_CODE_CLIENT_KEY`. Each accepts either: - a filesystem path to PEM content, or - inline PEM (including escaped `\n` sequences). | Variable | Value type | Behavior | | --------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------- | | `CLAUDE_CODE_USE_FOUNDRY` | Boolean-like string (`1`, `true`, `yes`, `on`) | Enables Foundry mode for Anthropic provider | | `FOUNDRY_BASE_URL` | URL string | Anthropic endpoint base URL in Foundry mode | | `ANTHROPIC_FOUNDRY_API_KEY` | Token string | Used for `Authorization: Bearer ` | | `ANTHROPIC_CUSTOM_HEADERS` | Header list string | Extra headers; format `header-a: value, header-b: value` or newline-separated | | `NODE_EXTRA_CA_CERTS` | PEM path or inline PEM | Extra CA chain for server certificate validation | | `CLAUDE_CODE_CLIENT_CERT` | PEM path or inline PEM | mTLS client certificate | | `CLAUDE_CODE_CLIENT_KEY` | PEM path or inline PEM | mTLS client private key (must be paired with cert) | ### Amazon Bedrock | Variable | Default / behavior | | --- | --- | | `AWS_REGION` | Primary region source | | `AWS_DEFAULT_REGION` | Fallback if `AWS_REGION` is unset | | `AWS_BEARER_TOKEN_BEDROCK` | Uses bearer-token authentication (`Authorization: Bearer `) instead of SigV4 | | `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` + optional `AWS_SESSION_TOKEN` | Static environment credentials for SigV4 authentication | | `AWS_PROFILE` | Selects a named `~/.aws/credentials` / `~/.aws/config` profile; static, SSO, and `credential_process` profiles are supported | | `AWS_SHARED_CREDENTIALS_FILE` / `AWS_CONFIG_FILE` | Override the named profile credentials and config file paths | | `AWS_EC2_METADATA_DISABLED` | Set to `true` to disable the final EC2 IMDSv2 credential fallback | | `AWS_BEDROCK_SKIP_AUTH` | Truthy values (`1`, `y`, `true`, `yes`, or `on`, case-insensitive) use dummy SigV4 credentials for non-auth proxy scenarios | | `HTTPS_PROXY` | Honored by Bun's native HTTPS proxy support | Region fallback in provider code: `options.region` → `AWS_REGION` → `AWS_DEFAULT_REGION` → `us-east-1`. Authentication uses `AWS_BEARER_TOKEN_BEDROCK` when set; otherwise credential fallback order is complete static environment credentials, the selected named profile (static, SSO, or `credential_process`), then EC2 IMDSv2 unless `AWS_EC2_METADATA_DISABLED=true`. Region and IMDS controls use the normal merged environment, including project `cwd/.env`; bearer tokens, static credentials, profiles, and credential file selectors use the credential environment, so project `cwd/.env` credential values are excluded. ECS task credentials and IRSA/web-identity credentials are not implemented. `models.yml` Bedrock entries use `api: bedrock-converse-stream` and do not require `apiKey` or `apiKeyEnv` because the provider authenticates through this AWS chain. ### Azure OpenAI Responses | Variable | Default / behavior | | ---------------------------------- | --------------------------------------------------------------------------- | | `AZURE_OPENAI_API_KEY` | Required unless API key passed as option | | `AZURE_OPENAI_API_VERSION` | Default `v1` | | `AZURE_OPENAI_BASE_URL` | Direct base URL override | | `AZURE_OPENAI_RESOURCE_NAME` | Used to construct base URL: `https://.openai.azure.com/openai/v1` | | `AZURE_OPENAI_DEPLOYMENT_NAME_MAP` | Optional mapping string: `modelId=deploymentName,model2=deployment2` | Base URL resolution: option `azureBaseUrl` → env `AZURE_OPENAI_BASE_URL` → option/env resource name → `model.baseUrl`. ### Model provider base URL overrides Built-in model provider base URLs resolve with this precedence: 1. `models.yml` / model config provider `baseUrl` 2. provider-specific base URL environment variable 3. bundled provider default Supported aliases: | Provider | Variables | | --- | --- | | OpenAI | `OPENAI_BASE_URL` | | Anthropic | `ANTHROPIC_BASE_URL` | | Google Gemini | `GOOGLE_BASE_URL`, `GEMINI_BASE_URL` | | Google Antigravity | `GOOGLE_ANTIGRAVITY_BASE_URL`, then `GOOGLE_BASE_URL`, then `GEMINI_BASE_URL` | | Google Gemini CLI | `GOOGLE_GEMINI_CLI_BASE_URL`, then `GOOGLE_BASE_URL`, then `GEMINI_BASE_URL` | | Google Vertex | `GOOGLE_VERTEX_BASE_URL`, then `GOOGLE_BASE_URL`, then `GEMINI_BASE_URL` | | Any provider id | derived `_BASE_URL`, uppercased with non-alphanumerics converted to `_` (for example `my-proxy` → `MY_PROXY_BASE_URL`) | OpenAI-compatible proxy note: the built-in `openai` provider keeps its bundled API transport (`openai-responses`). Setting `OPENAI_BASE_URL` changes the host but still calls `/responses`. If your proxy only supports Chat Completions, configure a custom `models.yml` provider with `api: openai-completions` instead of using the built-in OpenAI provider override: ```yaml providers: openai-compatible: baseUrl: https://proxy.example.com/v1 apiKey: OPENAI_API_KEY api: openai-completions models: - id: gpt-4o name: GPT-4o via proxy api: openai-completions ``` For OpenRouter traffic, GJC explicitly sends `User-Agent: Gajae-Code/` plus OpenRouter attribution headers. For the built-in OpenAI Responses transport and generic OpenAI-compatible Chat Completions transport, GJC passes model/provider headers through the OpenAI JavaScript SDK and does not set a GJC user-agent unless the provider-specific code adds one. ### OpenAI-compatible proxy provider config For OpenAI-compatible proxies that only implement Chat Completions, prefer a custom `models.yml` provider over `OPENAI_BASE_URL`: ```yaml providers: openai-compatible: baseUrl: https://proxy.example.com/v1 apiKeyEnv: OPENAI_API_KEY api: openai-completions auth: apiKey headers: User-Agent: curl/8.7.1 models: - id: gpt-4o name: GPT-4o via proxy reasoning: false input: [text] cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 } ``` `models.yml` is strict: unsupported provider/model keys fail validation before the provider request is dispatched. ### GJC workflow bridge commands `gjc ralplan`, `gjc deep-interview`, and `gjc state` are private runtime bridge commands. They require `GJC_RUNTIME_BINARY` (or legacy `GJC_LEGACY_RUNTIME_BINARY`) to point at the private runtime executable; public bundled workflow use remains through `/skill:ralplan` and `/skill:deep-interview` inside a GJC session. | Variable | Behavior | | --- | --- | | `GJC_RUNTIME_BINARY` | Private runtime bridge binary for `gjc ralplan`, `gjc deep-interview`, and `gjc state` | | `GJC_LEGACY_RUNTIME_BINARY` | Legacy fallback bridge binary name | ### Interactive `--tmux` startup and scroll/mouse profile `gjc --tmux` launches the interactive TUI inside a fresh GJC-managed tmux session. Plain `gjc --tmux` does not auto-attach a scoped managed session from the same project/branch; use `gjc --tmux --continue` or `gjc session attach ` when you intend to continue existing tmux context. `gjc --tmux --resume` still reaches the inner GJC session resolver, so value-less resume shows the session picker and `--resume ` honors that target instead of reusing a branch tmux session. Older-version sessions are not auto-attached after upgrades. When GJC creates a session it applies a profile that is **scoped to the GJC session only** (it never runs `set -g` / global tmux options), including: - `mouse on` — enables tmux copy-mode scrolling when GJC mouse support is disabled. - `set-clipboard on` and a readable copy-mode `mode-style`. - GJC ownership/identity tags (`@gjc-profile`, version, branch/project markers). This profile is applied on macOS, Linux, WSL (Linux), and native Windows when a compatible tmux provider is available. It is applied **only to sessions GJC itself creates**. If you start tmux yourself and then run `gjc` inside it, GJC leaves your tmux configuration untouched. GJC's own mouse support is disabled by default, so the host terminal or tmux retains wheel and selection behavior. Add `set -g mouse on` to your own `~/.tmux.conf` when you want tmux copy-mode scrolling. Set `mouse.enabled: true` to let GJC capture the wheel for virtual session scrolling (three rows per notch, not a full page). When GJC owns mouse input, dragging across rendered text highlights the selection and copies it to the system clipboard on release. | Variable | Behavior | | --- | --- | | `GJC_LAUNCH_POLICY` | Launch policy for `--tmux` startup: `tmux` (default) or `direct` (skip the tmux session) | | `GJC_TMUX_SESSION` | Explicit tmux session name override for `--tmux` startup. Use a unique value (for example `GJC_TMUX_SESSION=gjc-fresh-$(date +%s) gjc --tmux`) to force a fresh named session. | | `GJC_TMUX_COMMAND` | tmux binary/name override for every GJC tmux flow. This is not a shell command line; include only the executable path/name, not flags. | | `GJC_TMUX_PROFILE` | Set `0`/`false`/`off` to apply only the required ownership tags and skip the scroll/mouse/clipboard profile | | `GJC_MOUSE` | Set `0`/`false`/`off` to skip the managed profile's tmux `mouse on`; this does not disable GJC's own mouse support | | `GJC_PSMUX_COMMAND` | Identifies a psmux wrapper for Windows alias resolution. The value must resolve to the same executable identity as the selected `tmux` command; unresolved or conflicting evidence fails closed. | | `GJC_PSMUX_DETECTION` | Set `0`/`false`/`off` to skip banner-based psmux detection. Executable-name and alias-identity safety checks still apply. | | `GJC_PSMUX_FORCE_DETECT` | Set `1`/`true`/`on` to re-probe the multiplexer on every call instead of caching the per-process verdict. | #### Windows psmux detection boundary On native Windows, [psmux](https://github.com/psmux/psmux) may be installed as `psmux.exe`, `pmux.exe`, or a `tmux.exe` alias. The alias can report only a generic `tmux 3.3.6` banner, so GJC compares the selected `tmux.exe` executable identity with resolved `psmux.exe` / `pmux.exe` companions. A matching identity is classified as psmux; distinct identities preserve native-tmux semantics. If the selected command, an explicit `GJC_PSMUX_COMMAND`, or a resolved companion cannot be identified consistently, GJC reports `gjc_tmux_provider_ambiguous` and refuses before applying native-tmux target or mutation semantics. Correct `PATH`, set `GJC_TMUX_COMMAND` to a verified executable, or make `GJC_PSMUX_COMMAND` resolve to the same wrapper identity. GJC-managed Windows psmux flows persist a `ProviderAuthority` for each owner generation. It binds the resolved absolute executable's identity and GJC's isolated server namespace; a missing, changed, or ambiguous identity fails closed. GJC recovery reads and re-proves that persisted authority rather than using an ambient multiplexer. #### Windows psmux namespace boundary psmux follows tmux-style server semantics: `new-session -c `, `new-window -c `, and GJC's `gjc --tmux` cwd only choose the start directory for the session/window/pane. They do **not** create a per-project server namespace. For a managed Windows psmux owner, GJC creates and persists an isolated namespace and invokes the bound executable with `-L ` on every operation. GJC does not expose a `GJC_TMUX_NAMESPACE` runtime knob or parse flags from `GJC_TMUX_COMMAND`. Do not set `GJC_TMUX_COMMAND="psmux -L my-project"` and do not recover with ambient `tmux`/`psmux` or a manually supplied `-L` value; `GJC_TMUX_COMMAND` is one executable path/name. Use the GJC session or lifecycle operation so it reuses the persisted ProviderAuthority. If that authority cannot be read and re-proved, GJC refuses the operation. #### WSL / Windows Terminal scrolling GJC's SGR mouse support is disabled by default, so tmux or Windows Terminal retains wheel ownership. In a GJC-managed tmux session, the default profile's `mouse on` enters tmux copy-mode and scrolls pane history. Set `mouse.enabled: true` to make the wheel scroll GJC's virtual session viewport three rows at a time, including inside `gjc --tmux`. PageUp/PageDown page the visible transcript lane, moving by its height minus one row. Set `GJC_MOUSE=off` as well as leaving GJC mouse support disabled to skip tmux mouse capture and let Windows Terminal handle its native scrollback. Keyboard fallback for tmux copy-mode remains `Ctrl-b [`, followed by `PgUp`/arrows; press `q` to exit. ### Hermes MCP bridge `gjc mcp-serve coordinator` exposes a GJC-native outward MCP bridge for Hermes-style coordinators. `gjc mcp-serve hermes` is a compatibility alias for the same bridge. The bridge is read-only by default and fails closed until roots and mutation classes are explicitly configured. Coordinator MCP currently exposes durable polling/await tools, not push subscriptions. Consume `gjc_coordinator_read_coordination_status`, `gjc_coordinator_read_turn`, or bounded `gjc_coordinator_await_turn` for state changes. | Variable | Behavior | | --- | --- | | `GJC_COORDINATOR_MCP_WORKDIR_ROOTS` | Required allowlist for workdir and artifact paths. `gjc setup hermes` renders absolute normalized paths joined with the platform path delimiter (`:` on POSIX, `;` on Windows). The bridge parser also accepts commas, semicolons, and newlines for legacy manual configs. | | `GJC_COORDINATOR_MCP_MUTATIONS` | Enables mutating tool classes as a comma-separated list (`sessions`, `questions`, `reports`) or `all`. `sessions` covers session startup, prompt delivery, durable turn journal updates, queue, and force operations. Per-call `allow_mutation: true` is still required. | | `GJC_COORDINATOR_MCP_ARTIFACT_BYTE_CAP` | Max bytes returned by artifact reads (default `65536`, capped at `1048576`). | | `GJC_COORDINATOR_MCP_STATE_ROOT` | Bridge coordination state root (default `/.gjc/state/coordinator-mcp`). | | `GJC_COORDINATOR_MCP_PROFILE` | Optional profile namespace for session/question/report state. Missing scope never widens to global session enumeration. | | `GJC_COORDINATOR_MCP_REPO` | Optional repo namespace for session/question/report state. Missing scope never widens to global session enumeration. | | `GJC_COORDINATOR_MCP_SESSION_COMMAND` | Optional **typed SDK lifecycle selector**, never a shell command that the coordinator executes. The only supported values are exactly `gjc` and `gjc --worktree [name]`; the latter optionally selects the GJC-managed worktree name. Wrapper binaries, shell syntax, model/provider flags, tmux flags, and other legacy command shapes fail closed before session creation. `gjc setup hermes` renders `gjc --worktree` by default. When omitted, SDK lifecycle creation still uses the requested coordinator workdir; no coordinator-owned tmux startup or prompt injection is performed. | | `GJC_COORDINATOR_MCP_SETUP_MANAGED_BY` | Marker written by `gjc setup hermes` for safe managed config updates. | | `GJC_COORDINATOR_MCP_SETUP_SCHEMA_VERSION` | Managed setup schema version written by `gjc setup hermes`. | | `GJC_COORDINATOR_MCP_SETUP_SIGNATURE` | Deterministic managed setup signature used to detect safe updates versus unmanaged conflicts. | | `GJC_COORDINATOR_MCP_EVENT_WEBHOOK_URL` | Opt-in webhook destination for existing `watch_events` journal rows (`https:` anywhere, or `http:` loopback only). Unset or empty = the feature is fully off. Resolved through the trusted credential environment, never the checkout's `.env`; a project `.env` cannot select where coordinator rows are POSTed. No redirect following. | | `GJC_COORDINATOR_MCP_EVENT_WEBHOOK_TOKEN_FILE` | Absolute path to a file whose trimmed content is sent as `Authorization: Bearer …`; raw tokens are never accepted inline in env. Resolved through the trusted credential environment. | | `GJC_COORDINATOR_MCP_EVENT_WEBHOOK_SESSION_IDS` | Optional comma-separated session-id allowlist; only journal rows carrying one of these `session_id` values are delivered. | | `GJC_COORDINATOR_MCP_EVENT_WEBHOOK_TIMEOUT_MS` | Per-attempt webhook POST timeout (default `5000`, capped at `30000`). | | `GJC_COORDINATOR_MCP_EVENT_WEBHOOK_MAX_ATTEMPTS` | Delivery attempts per journal row (default `5`, capped at `10`) with exponential backoff (`500ms` base, `15s` cap) through a durable per-row outbox. | ### Google Vertex AI | Variable | Required? | Notes | | -------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------- | | `GOOGLE_CLOUD_PROJECT` | Yes (unless passed in options) | Fallback: `GCLOUD_PROJECT` | | `GCLOUD_PROJECT` | Fallback | Used as alternate project ID source | | `GOOGLE_CLOUD_PROJECT_ID` | OAuth login helper only | Used by Gemini CLI OAuth project discovery | | `GOOGLE_CLOUD_LOCATION` | Yes (unless passed in options) | No default in provider | | `GOOGLE_CLOUD_API_KEY` | Conditional | Direct Vertex API-key auth; otherwise ADC fallback can authenticate when project and location are set | | `GOOGLE_APPLICATION_CREDENTIALS` | Conditional | If set, file must exist; otherwise ADC fallback path is checked (`~/.config/gcloud/application_default_credentials.json`) | ### Kimi | Variable | Default / behavior | | ---------------------- | -------------------------------------------------------- | | `KIMI_CODE_OAUTH_HOST` | Primary OAuth host override | | `KIMI_OAUTH_HOST` | Fallback OAuth host override | | `KIMI_CODE_BASE_URL` | Overrides Kimi usage endpoint base URL (`usage/kimi.ts`) | OAuth host chain: `KIMI_CODE_OAUTH_HOST` → `KIMI_OAUTH_HOST` → `https://auth.kimi.com`. ### Gemini CLI compatibility | Variable | Default / behavior | | -------------------------- | --------------------------------------------------------------- | | `GJC_AI_GEMINI_CLI_VERSION` | Overrides Gemini CLI user-agent version tag (`0.49.0` if unset). `PI_AI_GEMINI_CLI_VERSION` remains supported as a legacy fallback. | ### OpenAI code provider responses (feature/debug controls) | Variable | Behavior | | ------------------------------------ | ---------------------------------------------------- | | `GJC_OPENAI_CODE_DEBUG` | `1`/`true` enables OpenAI code provider debug logging | | `GJC_NO_STRICT` | Global bypass for OpenAI-style strict schema enforcement (`adaptSchemaForStrict`); legacy alias `PI_NO_STRICT` | | `GJC_OPENAI_CODE_WEBSOCKET` | `1`/`true` enables websocket transport preference | | `GJC_OPENAI_CODE_WEBSOCKET_V2` | `1`/`true` enables websocket v2 path | | `GJC_OPENAI_CODE_WEBSOCKET_IDLE_TIMEOUT_MS` | Positive integer override (default 300000) | | `GJC_OPENAI_CODE_WEBSOCKET_RETRY_BUDGET` | Non-negative integer override (default 5) | | `GJC_OPENAI_CODE_WEBSOCKET_RETRY_DELAY_MS` | Positive integer base backoff override (default 500) | | `GJC_OPENAI_STREAM_IDLE_TIMEOUT_MS` | Positive integer OpenAI stream idle timeout override. Unset: 120s, except xAI Grok / Grok Build providers and Grok model ids on any OpenAI-compatible host use 300s (same floor as Anthropic long-reasoning). `0` disables. | ### Cursor provider debug | Variable | Behavior | | ------------------ | ------------------------------------------------------------------------ | | `DEBUG_CURSOR` | Enables provider debug logs; `2`/`verbose` for detailed payload snippets | | `DEBUG_CURSOR_LOG` | Optional file path for JSONL debug log output | ### Prompt cache compatibility switch | Variable | Behavior | | -------------------- | ----------------------------------------------------------------------------------------------------------------- | | `GJC_CACHE_RETENTION` | If `long`, enables long retention where supported (`anthropic`, `openai-responses`, Bedrock retention resolution); any other value forces `short`. The Anthropic provider already defaults to `long` (1h) when unset, so this is mainly an opt-out (`short`) or a way to extend long retention to other providers. | --- ## 3) Web search subsystem ### Search provider credentials | Variable | Used by | | --------------------------------------------------- | ------------------------------------------------------------- | | `EXA_API_KEY` | Exa search provider | | `BRAVE_API_KEY` | Brave search provider | | `PERPLEXITY_API_KEY` | Perplexity search provider API-key mode | | `PERPLEXITY_COOKIES` | Perplexity cookie-auth search mode | | `TAVILY_API_KEY` | Tavily search provider | | `ZAI_API_KEY` | z.ai search provider (also checks stored OAuth in `agent.db`) | | `OPENAI_API_KEY` / OpenAI code OAuth in DB | OpenAI code search provider availability/auth | | `GJC_OPENAI_CODE_WEB_SEARCH_MODEL` | OpenAI code search provider model override | | `MOONSHOT_SEARCH_API_KEY` / `KIMI_SEARCH_API_KEY` | Kimi/Moonshot search provider env auth | | `MOONSHOT_SEARCH_BASE_URL` / `KIMI_SEARCH_BASE_URL` | Kimi/Moonshot search endpoint override | | `KAGI_API_KEY` | Kagi search provider | | `JINA_API_KEY` | Jina search provider | | `PARALLEL_API_KEY` | Parallel search provider | | `SEARXNG_ENDPOINT`, `SEARXNG_TOKEN` | SearXNG endpoint and optional bearer token | | `SEARXNG_BASIC_USERNAME`, `SEARXNG_BASIC_PASSWORD` | SearXNG HTTP Basic Auth credentials | SearXNG also reads the equivalent `searxng.endpoint`, `searxng.token`, `searxng.basicUsername`, and `searxng.basicPassword` settings from `~/.gjc/agent/config.yml`; environment variables are fallbacks. ### Anthropic web search auth chain Anthropic web search uses `findAnthropicAuth()` from `packages/ai/src/utils/anthropic-auth.ts` in this order: 1. `ANTHROPIC_SEARCH_API_KEY` (+ optional `ANTHROPIC_SEARCH_BASE_URL`) 2. `ANTHROPIC_FOUNDRY_API_KEY` when `CLAUDE_CODE_USE_FOUNDRY` is enabled 3. Anthropic OAuth credentials from `agent.db` (must not expire within 5-minute buffer) 4. Anthropic API-key credentials from `agent.db` 5. Generic Anthropic env fallback: provider key (`ANTHROPIC_FOUNDRY_API_KEY` in Foundry mode, otherwise `ANTHROPIC_OAUTH_TOKEN`/`ANTHROPIC_API_KEY`) + optional `ANTHROPIC_BASE_URL` (`FOUNDRY_BASE_URL` when Foundry mode is enabled) Related vars: | Variable | Default / behavior | | --------------------------- | ---------------------------------------------------- | | `ANTHROPIC_SEARCH_API_KEY` | Highest-priority explicit search key | | `ANTHROPIC_SEARCH_BASE_URL` | Defaults to `https://api.anthropic.com` when omitted | | `ANTHROPIC_SEARCH_MODEL` | Defaults to `anthropic-model-haiku-4-5` | | `ANTHROPIC_BASE_URL` | Generic fallback base URL for tier-4 auth path | ### Perplexity OAuth flow behavior flag | Variable | Behavior | | ------------------- | ------------------------------------------------------------------------------- | | `GJC_AUTH_NO_BORROW` | If set, disables macOS native-app token borrowing path in Perplexity login flow | --- ## 4) Python tooling and kernel runtime | Variable | Default / behavior | | ------------------------- | ------------------------------------------------------------------------------------------------------------------- | | `GJC_PY` | Eval backend override: `0`/`bash`=JavaScript only, `1`/`py`=Python only, `mix`/`both`=both; invalid values ignored | | `GJC_PYTHON_SKIP_CHECK` | If `1`, skips Python interpreter availability checks (subprocess runner still starts on demand) | | `GJC_PYTHON_INTEGRATION` | If `1`, opts gated integration tests in (e.g. `python-runner.integration.test.ts`) into running against real Python | | `GJC_PYTHON_IPC_TRACE` | If `1`, logs NDJSON frames exchanged with the Python runner subprocess | | `VIRTUAL_ENV` | Highest-priority venv path for Python runtime resolution | Extra conditional behavior: - If `BUN_ENV=test` or `NODE_ENV=test`, Python availability checks are treated as OK and warming is skipped. - Python env filtering denies common API keys and allows safe base vars + `LC_`, `XDG_`, `GJC_` prefixes. --- ## 5) Agent/runtime behavior toggles | Variable | Default / behavior | | ---------------------------- | -------------------------------------------------------------------------------------------------- | | `GJC_SMOL_MODEL` | Ephemeral model-role override for `smol` (CLI `--smol` takes precedence) | | `GJC_SLOW_MODEL` | Ephemeral model-role override for `slow` (CLI `--slow` takes precedence) | | `GJC_PLAN_MODEL` | Ephemeral model-role override for `plan` (CLI `--plan` takes precedence) | | `GJC_NO_TITLE` | If set (any non-empty value), disables auto session title generation on first user message | | `GJC_NO_CMUX_RENAME` | If set (any non-empty value), disables renaming the containing cmux workspace to the current session name | | `NULL_PROMPT` | If `true`, system prompt builder returns empty string | | `GJC_BLOCKED_AGENT` | Blocks a specific subagent type in task tool | | `GJC_SUBPROCESS_CMD` | Overrides subagent spawn command (`gjc` / `gjc.cmd` resolution bypass) | | `GJC_TASK_MAX_OUTPUT_BYTES` | Max captured output bytes per subagent (default `500000`) | | `GJC_TASK_MAX_OUTPUT_LINES` | Max captured output lines per subagent (default `5000`) | | `GJC_FALLBACK_MAX_STAGED_EVENTS` | Positive-integer cap on events staged by the provisional staging transaction before it is rejected as a local overflow (default `10000`, hard ceiling `2000000`). Surrounding whitespace is ignored by the trusted environment resolver. Applies to both managed fallback and ordinary (non-managed lossless) sessions; in non-managed sessions the cap only decides how much reasoning buffers before the batch flushes and streams through. Invalid or non-positive values fall back to the default; values above the ceiling clamp to it with a warning — the staging guard stays bounded. Resolved from trusted environment sources only (process/agent/user config); a project `.env` cannot change these guardrails. | | `GJC_FALLBACK_MAX_STAGED_BYTES` | Positive-integer byte cap on the provisional staging transaction (default `16777216` = 16 MiB, hard ceiling `1073741824` = 1 GiB). Surrounding whitespace is ignored by the trusted environment resolver. Applies to both managed fallback and ordinary (non-managed lossless) sessions; in non-managed sessions the cap only decides how much reasoning buffers before the batch flushes and streams through; raising it raises peak memory of ordinary runs by delaying that flush. A staged streaming frame is counted once as the message and once as the event's partial snapshot of that message, so a reasoning-heavy turn is charged roughly twice its retained volume — size the cap accordingly. Invalid or non-positive values fall back to the default; values above the ceiling clamp to it with a warning — the staging guard stays bounded. Resolved from trusted environment sources only (process/agent/user config); a project `.env` cannot change these guardrails. | | `GJC_TIMING` | If set (any non-empty value), prints a hierarchical timing-span tree to **stderr** via `logger.printTimings()`. In interactive mode the tree prints once the agent is ready (before the TUI starts); in print mode it prints after the whole prompt batch completes. Print-mode prompts are wrapped in `print:prompt:initial` / `print:prompt:next` spans so each user message shows up as its own row. `GJC_TIMING=x` exits the process with code 0 right after printing in interactive mode (use to measure cold startup only). `GJC_TIMING=full` lists every module-load entry instead of just the top N. | | `GJC_PACKAGE_DIR` | Overrides package asset base dir resolution (docs/examples/changelog path lookup) | | `GJC_DISABLE_LSPMUX` | Canonical lspmux opt-out. A truthy value disables lspmux probing and wrapping; `PI_DISABLE_LSPMUX` is a supported compatibility alias with the same effect. | | `PI_DISABLE_LSPMUX` | Supported compatibility alias for `GJC_DISABLE_LSPMUX`; a truthy value also disables lspmux probing and wrapping. | | `SMITHERY_URL` | Smithery web URL override (default `https://smithery.ai`) | | `SMITHERY_API_URL` | Smithery API base URL override (default `https://api.smithery.ai`) | | `PUPPETEER_EXECUTABLE_PATH` | Browser tool Chromium executable override | | `LM_STUDIO_BASE_URL` | Default implicit LM Studio discovery base URL override (`http://127.0.0.1:1234/v1` if unset) | | `OMLX_BASE_URL` | Default implicit oMLX discovery base URL override (`http://127.0.0.1:8080/v1` if unset); only HTTP(S) loopback URLs are accepted to prevent credential forwarding to remote hosts | | `OLLAMA_BASE_URL` | Default implicit Ollama discovery base URL override (`http://127.0.0.1:11434` if unset) | | `LLAMA_CPP_BASE_URL` | Default implicit Llama.cpp discovery base URL override (`http://127.0.0.1:8080` if unset) | | `GJC_EDIT_VARIANT` | Forces edit tool variant (`patch`, `replace`, `hashline`, `vim`, `apply_patch`). The force beats `edit.modelVariants`, `edit.mode`, and automatic model-family routing; invalid values fail fast at startup. `PI_EDIT_VARIANT` is the legacy alias. | | `GJC_FORCE_IMAGE_PROTOCOL` | Forces supported image protocol (`kitty`, `iterm2`/`iterm`, `sixel`, `none`) where used | | `GJC_ALLOW_SIXEL_PASSTHROUGH` | Allows SIXEL passthrough when `GJC_FORCE_IMAGE_PROTOCOL=sixel` | | `GJC_NO_PTY` | If `1`, disables interactive PTY path for bash tool | | `GJC_SESSION_CONTEXT_BUDGET_BYTES` | Overrides the synchronous session-context materialization budget in bytes (default `536870912` = 512 MiB, ceiling `8589934592` = 8 GiB). Only a canonical positive-integer value is honored; anything invalid (empty, non-numeric, negative, zero, overflowing a safe integer, or above the ceiling) fail-closes to the 512 MiB default with a warning. Raise it above your measured session size to suppress the `SessionContextTooLargeError` preflight, or lower it to restore the old tight bound. | LSP project configuration may control declarative matching, activation, and capabilities, but it cannot define a command, arguments, executable, client factory, initialization options, or opaque server settings. Trusted user-wide configuration outside the project—including the recommended `~/.gjc/agent/lsp.*` files and supported legacy user locations—can override LSP launches and server options; automatic discovery uses trusted external executables and rejects project-owned lexical paths as well as symlink-resolved project binaries. `GJC_NO_PTY` is also set internally when CLI `--no-pty` is used. --- ## 6) Storage and config root paths `GJC_CONFIG_DIR`, `GJC_CODING_AGENT_DIR`, and `PWD` are consumed via `@gajae-code/utils/dirs` and affect where coding-agent stores data. `GJC_WORKTREE_DIR` is read at launch, before settings load, by `gjc-runtime/launch-worktree.ts`. | Variable | Default / behavior | | --------------------- | ----------------------------------------------------------------------------- | | `GJC_CONFIG_DIR` | Config root dirname under home (default `.gjc`) | | `GJC_CODING_AGENT_DIR` | Full override for agent directory (default `~//agent`) | | `PWD` | Used when matching canonical current working directory in path helpers | | `GJC_WORKTREE_DIR` | Directory holding `--worktree` launch worktrees (default `{repo}.gajae-code-worktrees`) | `GJC_WORKTREE_DIR` is a path template. `{repo}` expands to the repository directory name, which keeps one exported value repo-scoped so two repositories that share a branch name never resolve to the same worktree. A relative value resolves against the repository's parent directory — the default's own shape — so `{repo}.worktrees` adopts an existing sibling bucket and `.worktrees` parks a hidden bucket beside the repository; an absolute value (or a leading `~/`) is used as given. An unset or blank value keeps the default bucket. ```sh # Reuse an existing .worktrees convention instead of a second bucket export GJC_WORKTREE_DIR='{repo}.worktrees' # Or park every repo's worktrees on one volume, still repo-scoped export GJC_WORKTREE_DIR='/Volumes/dev/worktrees/{repo}' ``` --- ## 7) Shell/tool execution environment (From `packages/utils/src/procmgr.ts` and coding-agent bash tool integration.) | Variable | Behavior | | -------------------------- | ------------------------------------------------------------------------------ | | `GJC_BASH_NO_CI` | Suppresses automatic `CI=true` injection into spawned shell env | | `PI_BASH_NO_CI` | Legacy alias fallback for `GJC_BASH_NO_CI` | | `CLAUDE_BASH_NO_CI` | Legacy alias fallback for `GJC_BASH_NO_CI` | | `GJC_BASH_NO_LOGIN` | Disables login-shell mode; shell args become `['-c']` instead of `['-l','-c']` | | `PI_BASH_NO_LOGIN` | Legacy alias fallback for `GJC_BASH_NO_LOGIN` | | `CLAUDE_BASH_NO_LOGIN` | Legacy alias fallback for `GJC_BASH_NO_LOGIN` | | `PI_SHELL_PREFIX` | Optional command prefix wrapper | | `CLAUDE_CODE_SHELL_PREFIX` | Legacy alias fallback for `PI_SHELL_PREFIX` | | `VISUAL` | Preferred external editor command | | `EDITOR` | Fallback external editor command | Current implementation: `GJC_BASH_NO_CI` and `GJC_BASH_NO_LOGIN` are resolved first, then the `PI_*` and `CLAUDE_*` aliases above. Both are boolean-like: only `1`/`Y`/`TRUE`/`YES`/`ON` (case-insensitive) enable them, so an explicit `GJC_BASH_NO_LOGIN=0` keeps the login shell even when a legacy alias is truthy. The shell prefix is read from `PI_SHELL_PREFIX`/`CLAUDE_CODE_SHELL_PREFIX` only; `GJC_SHELL_PREFIX` is not currently honored. --- ## 8) UI/theme/session detection (auto-detected env) These are read as runtime signals; they are usually set by the terminal/OS rather than manually configured. | Variable | Used for | | ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------- | | `COLORTERM`, `TERM`, `WT_SESSION` | Color capability detection (theme color mode) | | `COLORFGBG` | Terminal background light/dark auto-detection | | `TERM_PROGRAM`, `TERM_PROGRAM_VERSION`, `TERMINAL_EMULATOR` | Terminal identity in system prompt/context | | `KDE_FULL_SESSION`, `XDG_CURRENT_DESKTOP`, `DESKTOP_SESSION`, `XDG_SESSION_DESKTOP`, `GDMSESSION`, `WINDOWMANAGER` | Desktop/window-manager detection in system prompt/context | | `KITTY_WINDOW_ID`, `TMUX_PANE`, `TERM_SESSION_ID`, `WT_SESSION` | Stable per-terminal session breadcrumb IDs | | `SHELL`, `ComSpec`, `TERM_PROGRAM`, `TERM` | System info diagnostics | | `APPDATA`, `XDG_CONFIG_HOME` | lspmux config path resolution | | `HOME` | Path shortening in command UI | --- ## 9) TUI runtime flags (shared package, affects coding-agent UX) | Variable | Behavior | | ------------------------- | ------------------------------------------------------------------------------------- | | `GJC_NOTIFICATIONS` | `0` is a hard notification runtime opt-out; `1` explicitly enables the generic current-session path even without a globally configured adapter. | | `GJC_NOTIFICATIONS_TOKEN` | An explicit generic current-session opt-in token. It has the same runtime precedence as `GJC_NOTIFICATIONS=1`; it does not supply or override global Telegram credentials. | | `GJC_NOTIFICATIONS_STREAM` | `1` forces live assistant-output streaming for this process; `0` / `off` / `false` disables it. Unset or unknown values defer to the global `notifications.telegram.streaming.enabled` preference, which defaults to `true` and activates durable streaming only for a configured Telegram adapter. | | `GJC_NOTIFICATIONS_STREAM_INTERVAL_MS` | Minimum interval between live Telegram stream edits; defaults to `500` and clamps to at least `200`. | | `GJC_NOTIFICATIONS_TURN_MAX` | Optional finalized turn-text cap for notification streaming; defaults to the bounded full-turn ceiling for split-capable clients. | | `GJC_NOTIFY` | `off` / `0` / `false` suppresses the notification control surface for this process, including completion notifications; global config is untouched and child processes inherit it. It wins over explicit notification opt-in. Use it for non-interactive runs (`gjc -p --no-session`) that must remain silent. | | `GJC_TUI_WRITE_LOG` | If set, logs TUI writes to file | | `GJC_HARDWARE_CURSOR` | If `1`, enables hardware cursor mode | | `GJC_CLEAR_ON_SHRINK` | If `1`, clears empty rows when content shrinks | | `GJC_DEBUG_REDRAW` | If `1`, enables redraw debug logging | | `GJC_TUI_DEBUG` | If `1`, enables deep TUI debug dump path | | `GJC_FORCE_IMAGE_PROTOCOL` | Forces terminal image protocol detection (`kitty`, `iterm2`/`iterm`, `sixel`, `none`) | | `GJC_TUI_KEYBOARD_PROTOCOL` | Enhanced keyboard input (Kitty keyboard protocol + xterm modifyOtherKeys). Enabled by default; set `0` / `false` to leave the keyboard in its default mode. GJC automatically skips the modifyOtherKeys fallback on Windows and Apple Terminal because it breaks CJK/Hangul IME composition there; use the full opt-out for other affected terminals such as Android Termius. | | `GJC_TUI_SYNCHRONIZED_OUTPUT` | Synchronized-output framing (`CSI ?2026h/l`) is enabled by default. Set `0` / `false` / `off` / `no` before starting or restarting GJC to remove that framing for terminal parsers that render it incorrectly. This is a process-wide compatibility and diagnostic switch, not tmux/Byobu client detection or per-client negotiation. Disabling it may expose visible tearing; return to the default after diagnosis unless the client requires the workaround. | --- ## 10) Commit generation controls | Variable | Behavior | | ------------------------- | ------------------------------------------------------------------- | | `GJC_COMMIT_TEST_FALLBACK` | If `true` (case-insensitive), force commit fallback generation path | | `GJC_COMMIT_NO_FALLBACK` | If `true`, disables fallback when agent returns no proposal | | `GJC_COMMIT_MAP_REDUCE` | If `false`, disables map-reduce commit analysis path | | `DEBUG` | If set, commit agent error stack traces are printed | --- ## 11) ACP permission handling | Variable | Values | Default | Behavior | | --- | --- | --- | --- | | `GJC_ACP_PERMISSION_MODE` | `prompt`, `auto`, `always-allow` | `prompt` | Controls whether ACP tool calls use the client's permission prompt or the SDK allow policy. `auto` and `always-allow` both allow gated tool calls without prompting. Invalid values fail safely to `prompt`. | | `GJC_ACP_ABORT_SCOPE` | `turn`, `owned` | `turn` | Selects the C04 terminal-abort scope for ACP `session/cancel`. `turn` (the default) aborts only the active turn and leaves owned work running so its completion can resume the root worker; `owned` also stops exact owned subagents and background tasks. Invalid values fail safely to `turn`. | ACP client metadata at `_meta.gjc.permissionHandling` takes precedence when the client supplies that field; the process environment is the fallback. The same precedence applies to the cancel scope: `_meta.gjc.abortScope` on the `session/cancel` notification wins over `GJC_ACP_ABORT_SCOPE` (when `_meta.gjc.abortScope` is present but invalid, the value fails safe to `turn` and the environment fallback is not consulted). JetBrains Air custom agents can set the fallback per agent in `acp.json`: ```json { "agent_servers": { "Gajae-Local-Opus": { "command": "/absolute/path/to/gjc", "args": ["acp", "--mpreset", "opus-codex"], "env": { "GJC_ACP_PERMISSION_MODE": "always-allow" } } } } ``` Use `always-allow` only for workspaces and tool configurations you trust. It removes the approval boundary for gated shell, monitor, eval, delete, and move operations. Changes apply to newly launched ACP agent processes. GJC does not expose a separate ACP `--yolo` flag. See [External control readiness](./external-control-readiness.md#jetbrains-air-custom-agent) for the Air setup flow. --- ## 12) Removed ingress modes `--mode rpc`, `--mode rpc-ui`, and `--mode bridge` have been removed. The retired bridge-prefixed variables and `GJC_RPC_EMIT_TITLE` are not runtime configuration variables. Use the [SDK machine interface](./sdk.md) for external machine control. --- ## Security-sensitive variables Treat these as secrets; do not log or commit them: - Provider/API keys and OAuth/bearer credentials (all `*_API_KEY`, `*_TOKEN`, OAuth access/refresh tokens) - Cloud credentials (`AWS_*`, `GOOGLE_APPLICATION_CREDENTIALS` path may expose service-account material) - Search/provider auth vars (`EXA_API_KEY`, `BRAVE_API_KEY`, `PERPLEXITY_API_KEY`, Anthropic search keys) - Foundry mTLS material (`CLAUDE_CODE_CLIENT_CERT`, `CLAUDE_CODE_CLIENT_KEY`, `NODE_EXTRA_CA_CERTS` when it points to private CA bundles) - Credential-root redirects (`CLAUDE_CONFIG_DIR`, `CODEX_HOME`) — not secrets themselves, but they select which account's credential file the import path reads Python runtime also explicitly strips many common key vars before spawning kernel subprocesses (`packages/coding-agent/src/eval/py/runtime.ts`).