ur-agent 1.65.6 → 1.65.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,192 @@
1
+ # 05 — Providers & Models
2
+
3
+ Source of truth: `src/services/providers/providerRegistry.ts`, `src/utils/model/*`,
4
+ `src/services/agents/{modelPool,modelRouter,escalation}.ts`, `src/commands/{model,provider,connect,effort,fast}`.
5
+
6
+ ## Provider registry (`PROVIDERS` in providerRegistry.ts)
7
+
8
+ | Provider id | Display name | Access | Credential | Status |
9
+ |---|---|---|---|---|
10
+ | `ollama` | Ollama | local runtime | none (localhost:11434) | **default local backend** |
11
+ | `llama.cpp` | llama.cpp | local/server | OpenAI-compatible endpoint (localhost:8080/v1) | enabled |
12
+ | `vllm` | vLLM | server | OpenAI-compatible endpoint (localhost:8000/v1) | enabled |
13
+ | `openai-compatible` | OpenAI-compatible | server/api | any base URL + optional `OPENAI_COMPATIBLE_API_KEY` | enabled |
14
+ | `openai-api` | OpenAI API | api | `OPENAI_API_KEY` | enabled |
15
+ | `anthropic-api` | Claude API | api | `ANTHROPIC_API_KEY` | enabled |
16
+ | `gemini-api` | Gemini API | api | `GEMINI_API_KEY` | enabled |
17
+ | `openrouter` | OpenRouter | api | `OPENROUTER_API_KEY` | enabled |
18
+ | `subscription` | Subscription | subscription login | OAuth | placeholder |
19
+ | `codex-cli` | Codex CLI | subscription via official CLI | `codex login` | `disabled: true` in registry |
20
+ | `claude-code-cli` | Claude Code | subscription via official CLI | `claude auth login` | `disabled: true` |
21
+ | `gemini-cli` | Gemini CLI | subscription via official CLI (Code Assist Std/Ent only) | — | `disabled: true` |
22
+ | `antigravity-cli` | Antigravity | subscription via official CLI | — | `disabled: true` |
23
+ | `lmstudio` | LM Studio | local server | OpenAI-compatible (localhost:1234/v1) | `disabled: true` |
24
+
25
+ Provider aliases are normalized by provider commands and interactive pickers (e.g.
26
+ `chatgpt`, `codex`, `openai codex` → `codex-cli`). Raw `settings.json` is stricter:
27
+ `provider.active` and `provider.fallback` must use canonical registry IDs. Each definition
28
+ declares capability metadata (native tool calls, native streaming, safety boundary label)
29
+ used by the runtime and `/provider` UI. UR-native adapters—including LM Studio—use the
30
+ UR-native request/tool boundary and never inherit an external subscription-CLI capability
31
+ record.
32
+
33
+ ### How to use
34
+
35
+ ```
36
+ /provider # interactive picker
37
+ /provider ollama # switch provider
38
+ /connect status # show all provider connection states
39
+ /connect openrouter --key sk-or-… # store an API key (keychain-backed)
40
+ /connect logout openrouter
41
+ ur provider models openrouter # list models a provider serves
42
+ ur provider doctor ollama # diagnose connectivity
43
+ ```
44
+
45
+ ## Ollama Cloud and the deployment enum
46
+
47
+ **Authentication.** The Ollama client sends `Authorization: Bearer` when
48
+ `OLLAMA_API_KEY` is set, and nothing otherwise. A local daemon needs no
49
+ credential — it holds the account itself, which is how `:cloud` model suffixes
50
+ resolve locally. A direct connection to the hosted API does need one, which is
51
+ why CI (with no signed-in daemon) previously could not use Ollama at all. The
52
+ key is trimmed, so a pasted trailing newline cannot corrupt the header, and
53
+ read per request so a rotated key applies without restarting.
54
+
55
+ Base-URL precedence: session override → `OLLAMA_HOST` / `OLLAMA_BASE_URL` →
56
+ `ollama.host` setting → `https://ollama.com` when a key is set with no host →
57
+ `http://localhost:11434`. An explicit host always wins, so self-hosted
58
+ gateways that require a key are unaffected.
59
+
60
+ `OLLAMA_API_KEY` is on the Agentic CI provider-credential allowlist, so it
61
+ reaches the isolated agent while platform write tokens do not.
62
+
63
+ **`APIProvider` is not the provider registry.** It is a deployment enum for
64
+ request shaping, narrowed to `'foundry' | 'ollama'` — the only values
65
+ `getAPIProvider()` can return. Comparisons against `'firstParty'`, `'bedrock'`
66
+ or `'vertex'` were silently false and disabled advertised features; the
67
+ typechecker now rejects them. Where such a branch is genuinely wanted, use the
68
+ named predicates `isFirstPartyRuntime()`, `isBedrockRuntime()` and
69
+ `isVertexRuntime()` (all currently `false`) so the intent stays greppable.
70
+ `DeploymentKey` widens the type for legacy per-deployment lookup tables in
71
+ `configs.ts`, `deprecation.ts` and `modelStrings.ts`, which carry rows for
72
+ deployments this build cannot select.
73
+
74
+ ## Model selection
75
+
76
+ ```
77
+ /model # interactive model picker for current provider
78
+ /model qwen2.5-coder:7b
79
+ ur --model llama3.3 # per-session
80
+ ur --ollama-host http://gpu-box:11434 # remote Ollama server
81
+ ur --discover-ollama # scan the LAN for Ollama servers (ollamaDiscovery.ts)
82
+ ```
83
+
84
+ - `startupModelSelection.ts` distinguishes deliberate model sources from
85
+ silent defaults. With no project/local/flag/managed, CLI/environment, agent,
86
+ or restored-session model, interactive startup requires
87
+ `ProviderFirstModelPicker`; headless startup exits before model execution.
88
+ - The startup picker validates the provider/model pair through the provider
89
+ registry and persists it to `.ur/settings.local.json`. User-global model
90
+ settings are intentionally insufficient for a new workspace.
91
+ - `settings.json → model`, `provider.active`, top-level `availableModels`, and top-level
92
+ `modelOverrides` persist choices per scope.
93
+ - `src/utils/model/aliases.ts` maps friendly aliases; `validateModel.ts` checks against the
94
+ provider's discovered list; `ollamaTuning.ts` adjusts context/params for local models.
95
+ - Deprecation warnings and 1M-context upgrade checks live in `deprecation.ts` /
96
+ `check1mAccess.ts`.
97
+ - Ollama remains the default provider endpoint, but no model is silently chosen
98
+ for a fresh workspace. Configured Ollama base URLs are honored consistently.
99
+ - OpenAI-compatible endpoints use a dedicated credential key so an OpenAI API
100
+ key is never forwarded to an arbitrary compatible base URL. Provider switches
101
+ clear stale endpoint/command overrides.
102
+ - Request adapters preserve system prompts, tools, images, stops, sampling,
103
+ reasoning, metadata, and structured-output settings supported by each
104
+ provider. Provider error payloads, empty responses, and truncated streams fail
105
+ instead of becoming synthetic empty successes.
106
+ - OpenAI API keeps Chat Completions as the default. Setting
107
+ `provider.openaiTransport` through
108
+ `ur config set openai_transport responses` selects the native Responses
109
+ adapter with `store=false` by default, semantic SSE, background
110
+ retrieve/poll/cancel, WebSocket continuation, compaction, deferred tool
111
+ search, and bounded private cursor state. Compacted context persistence
112
+ requires a 32-byte `UR_OPENAI_RESPONSES_STATE_KEY`.
113
+ - `ollama.ts` selects timeouts from explicit request options, then
114
+ `API_TIMEOUT_MS`, then runtime defaults. `:cloud` models and remote sessions
115
+ use 120 seconds; local models use 300 seconds. The same model-aware value is
116
+ applied while waiting for `/api/chat` response headers and as the absolute
117
+ deadline in `readOllamaChunks`.
118
+ - `ur.ts` identifies an Ollama Cloud runtime from both the selected provider and
119
+ the `:cloud` suffix. It disables shared automatic request retries for that
120
+ route, applies the same 120-second bound to any permitted non-streaming
121
+ fallback, and skips fallback entirely when the Ollama stream deadline itself
122
+ caused the failure. Explicit `API_TIMEOUT_MS` remains authoritative.
123
+
124
+ ## Capability-aware routing
125
+
126
+ ### Model pools (`src/services/agents/modelPool.ts`)
127
+ Pools named `cheap` / `strong` / `default`, loaded in priority order:
128
+ 1. `.ur/model-pool.json` in the repo — e.g. `{"cheap":["gemma2:2b"],"strong":["gpt-5.5"]}`
129
+ 2. Env: `UR_MODEL_POOL_CHEAP`, `UR_MODEL_POOL_STRONG`, `UR_MODEL_POOL_DEFAULT` (comma lists)
130
+ 3. Defaults: cheap `qwen2.5-coder:1.5b, gemma2:2b`; strong `qwen2.5-coder:32b, codex,
131
+ claude-3-5-sonnet, gpt-4o`; default `qwen2.5-coder`.
132
+
133
+ ### `/model-route` (modelRouter.ts)
134
+ Classifies a task and recommends a model + strategy:
135
+ ```
136
+ /model-route "port this service to Rust" --strategy strong
137
+ /model-route "rename a variable" --strategy auto --json
138
+ ```
139
+
140
+ ### `/escalate` (escalation.ts)
141
+ Run on a fast model, auto-escalate hard steps to an "oracle":
142
+ ```
143
+ /escalate plan "design a consensus protocol" # show the split
144
+ /escalate run "…" --fast qwen2.5-coder:7b --oracle gpt-5.5
145
+ /escalate policy # view escalation policy
146
+ ```
147
+
148
+ ### `/model-doctor` (ollamaModels.ts + modelCapabilities.ts)
149
+ Probes an installed Ollama model: tool-call support, context length, speed class, and
150
+ reports "likely agent capabilities":
151
+ ```
152
+ /model-doctor llama3.3 --json
153
+ ```
154
+
155
+ ### Automatic learning loop (learning.ts)
156
+ Every ci-loop, arena, escalation, test-first, and completed **local** cloud
157
+ best-of-N run
158
+ **automatically** records its pass/fail outcome (per task category and model) into
159
+ `.ur/learning/stats.json` — a pure JSON fold, no model calls. The `auto` routing strategy
160
+ and `/escalate`'s difficulty bias consume this evidence: a model with ≥3 recorded runs and
161
+ a ≥60% success rate for the task's category is preferred when selectable; thin evidence
162
+ falls back to the static heuristics unchanged. The store is idempotent (outcome keys
163
+ dedupe) and best-effort (a broken store never fails a run). `/learn` remains for
164
+ inspection and the optional LLM reflection pass:
165
+ ```
166
+ /learn stats # view what the agent has learned
167
+ /learn run --reflect # optional: distill failures into lessons (uses a model)
168
+ ```
169
+ Disable automatic learning with `automaticLearningEnabled: false` or
170
+ `UR_CODE_DISABLE_AUTO_LEARNING=1`.
171
+
172
+ ## Session behavior knobs
173
+
174
+ | Feature | Command | Notes |
175
+ |---|---|---|
176
+ | Effort level | `/effort low·medium·high·max·auto` | low/medium/high persist as `effortLevel`; max is session-only in the external build; auto clears the override |
177
+ | Fast mode | `/fast` source exists but reports unavailable | requires the hosted first-party serving tier, which the standard npm build does not use; setting `fastMode` cannot manufacture that tier |
178
+ | Advisor model | `/advisor` is hidden in the standard build | requires first-party beta headers plus an enabled runtime feature configuration; `advisorModel` alone does not enable it |
179
+ | Always thinking | `alwaysThinkingEnabled` setting | force extended thinking |
180
+ | Thinking summaries | `showThinkingSummaries` setting | UI display of thinking |
181
+ | Fallback model | `--fallback-model` (print mode) | on overload |
182
+
183
+ ## Offline / local-first
184
+
185
+ - `ur --offline` or `offline` setting: no cloud APIs, telemetry, auto-update, remote control.
186
+ - Offline dispatch permits only loopback local/server endpoints; cloud,
187
+ subscription, remote Ollama, and remote compatible endpoints are blocked.
188
+ - `/local-first` reports readiness for no-cloud/private/lab/edge deployment: which features
189
+ degrade, which local deps (Ollama, ripgrep, playwright, ffmpeg…) are present.
190
+ - `--bare` forces the minimal local pipeline (`UR_CODE_SIMPLE=1`) and always uses Ollama.
191
+ - Ollama config: `ollama.host` and `ollama.lanDiscovery` settings; per-session
192
+ `--ollama-host`; router in `ollamaRouter.ts` load-balances across discovered hosts.
@@ -0,0 +1,407 @@
1
+ # 06 — Configuration
2
+
3
+ Source of truth: `src/utils/settings/{types.ts,constants.ts,settings.ts}`,
4
+ `src/utils/hooks/`, `src/keybindings/`, `src/utils/permissions/`.
5
+
6
+ ## Settings scopes (later overrides earlier)
7
+
8
+ | Scope | File | Notes |
9
+ |---|---|---|
10
+ | user | `~/.ur/settings.json` | global |
11
+ | project | `.ur/settings.json` | shared, committed |
12
+ | local | `.ur/settings.local.json` | gitignored |
13
+ | flag | `--settings <file-or-json>` | per-invocation |
14
+ | managed/policy | managed-settings.json or remote org settings | read-only, always loaded |
15
+
16
+ `--setting-sources user,project,local` restricts which editable scopes load.
17
+ Schema URL for editors: `https://json.schemastore.org/ur-settings.json`.
18
+
19
+ Edit interactively with `/config`, by natural language with `/update-config`
20
+ (bundled skill), or directly in the JSON files.
21
+
22
+ Model selection has one intentional exception to ordinary merged precedence:
23
+ a user-global model alone does not initialize a fresh workspace. Interactive
24
+ startup requires a provider/model choice and writes it to
25
+ `.ur/settings.local.json`; fresh headless startup requires `--model`, a model
26
+ environment variable, or project/flag/managed configuration. Resumed sessions
27
+ restore their session model without showing the picker.
28
+
29
+ ## settings.json keys (from `SettingsSchema`, `src/utils/settings/types.ts`)
30
+
31
+ ### Model & provider
32
+ ```jsonc
33
+ {
34
+ "model": "qwen2.5-coder:7b",
35
+ "provider": {
36
+ "active": "ollama",
37
+ "model": "qwen2.5-coder:7b",
38
+ "baseUrl": "http://localhost:11434",
39
+ "timeoutMs": 30000,
40
+ "fallback": "disabled",
41
+ "openaiTransport": "responses", // chat-completions (default) | responses
42
+ "responses": {
43
+ "store": false,
44
+ "compactThreshold": 20000,
45
+ "toolSearch": "hosted" // off (default) | hosted
46
+ },
47
+ "preferences": {}
48
+ },
49
+ "ollama": { "host": "http://localhost:11434", "lanDiscovery": true },
50
+ "offline": false,
51
+ "effortLevel": "high",
52
+ "fastMode": false, "fastModePerSessionOptIn": false,
53
+ "advisorModel": "qwen2.5-coder:32b",
54
+ "alwaysThinkingEnabled": false, "showThinkingSummaries": true,
55
+ "availableModels": [], "modelOverrides": {}
56
+ }
57
+ ```
58
+
59
+ ### Permissions & safety
60
+ ```jsonc
61
+ {
62
+ "permissions": {
63
+ "allow": ["Bash(git:*)", "Read", "WebFetch(domain:docs.example.com)"],
64
+ "deny": ["Bash(rm -rf:*)", "mcp__untrusted-server"],
65
+ "ask": ["Bash(git push:*)"],
66
+ "additionalDirectories": ["../lib"],
67
+ "defaultMode": "acceptEdits", // default | plan | acceptEdits | autoApprove | bypassPermissions
68
+ "profiles": { // named rule sets, appended when active
69
+ "reviewing": { "deny": ["Edit", "Write", "Bash"], "description": "read-only" },
70
+ "trusted": { "allow": ["Bash(git:*)"] }
71
+ },
72
+ "activeProfile": "reviewing" // switch with /permission-profile use <name>
73
+ },
74
+ "agents": { // subagent fan-out limits (doc 09)
75
+ "maxDepth": 3, // default 3, hard ceiling 10
76
+ "maxConcurrent": 20 // default 20, hard ceiling 100
77
+ },
78
+ "voice": { // end-of-turn speech, off by default
79
+ "speakResponses": false,
80
+ "name": "Samantha", "rate": 210
81
+ },
82
+ "memory": { // end-of-turn suggestions, off by default
83
+ "suggest": false,
84
+ "suggestMinConfidence": 0.75
85
+ },
86
+ "sandbox": { /* SandboxSettingsSchema — OS sandbox for shell commands */ },
87
+ "tasks": {
88
+ "requireBeforeChanges": {
89
+ "enabled": true, // default: true
90
+ "freeReads": 3 // calls allowed before ordinary mutations require a plan
91
+ }
92
+ },
93
+ "disableAutoMode": "disable",
94
+ "skipDangerousModePermissionPrompt": false,
95
+ "allowManagedPermissionRulesOnly": false
96
+ }
97
+ ```
98
+ Profiles are appended to the base `allow`/`deny`/`ask` lists from the same
99
+ settings source, so a profile can only narrow or extend — `deny` still beats
100
+ `allow`. A missing or misnamed `activeProfile` contributes nothing rather than
101
+ failing open. `/permission-profile use <name>` writes the switch to whichever
102
+ source defines the profile, so it lands beside its definition.
103
+
104
+ Fan-out limits clamp rather than disable: out-of-range, negative and
105
+ non-numeric values fall back to the default or the ceiling, so a settings file
106
+ cannot switch the governor off.
107
+
108
+ Rule syntax: `ToolName` (blanket) or `ToolName(specifier)` — e.g. `Bash(npm run *)`,
109
+ `Edit(src/**)`, `mcp__server__tool`. Managed via `/permissions` UI as well.
110
+
111
+ Permission modes:
112
+ - `default`: normal permission checks; operations that need review ask first.
113
+ - `plan`: planning-only mode until the user approves execution.
114
+ - `acceptEdits`: auto-approve safe in-workspace file edits and safe commands.
115
+ - `autoApprove`: auto-approve command/tool permission approvals, while
116
+ user-input dialogs still ask and explicit denials remain enforced.
117
+ - `bypassPermissions`: bypass permission prompts after the separate dangerous-mode
118
+ acknowledgement/CLI opt-in; use only in an external sandbox with no sensitive access.
119
+
120
+ `autoMode`, `useAutoModeDuringPlan`, `skipAutoPermissionPrompt`, and
121
+ `permissions.disableAutoMode` exist only in builds compiled with
122
+ `TRANSCRIPT_CLASSIFIER`; `classifierPermissionsEnabled` is additionally internal-only.
123
+ The standard npm build accepts `autoApprove`, which is deterministic permission approval,
124
+ not model-based classification.
125
+
126
+ ### Hooks
127
+ ```jsonc
128
+ {
129
+ "hooks": {
130
+ "PreToolUse": [
131
+ { "matcher": "Bash",
132
+ "hooks": [ { "type": "command", "command": "./scripts/lint-command.sh" } ] }
133
+ ],
134
+ "PostToolUse": [], "UserPromptSubmit": []
135
+ },
136
+ "disableAllHooks": false,
137
+ "allowManagedHooksOnly": false,
138
+ "allowedHttpHookUrls": [], "httpHookAllowedEnvVars": []
139
+ }
140
+ ```
141
+ Hook events (`src/entrypoints/sdk/coreTypes.ts:HOOK_EVENTS`): `PreToolUse`, `PostToolUse`,
142
+ `PostToolUseFailure`, `Notification`, `UserPromptSubmit`, `SessionStart`, `SessionEnd`,
143
+ `Stop`, `StopFailure`, `SubagentStart`, `SubagentStop`, `PreCompact`, `PostCompact`,
144
+ `PermissionRequest`, `PermissionDenied`, `Setup`, `TeammateIdle`, `TaskCreated`,
145
+ `TaskCompleted`, `Elicitation`, `ElicitationResult`, `ConfigChange`, `WorktreeCreate`,
146
+ `WorktreeRemove`, `InstructionsLoaded`, `CwdChanged`, `FileChanged`, `BeforeEdit`,
147
+ `AfterEdit`, `BeforeCommand`, `AfterCommand`, `BeforeCommit`, `OnFailure`.
148
+ Hook types: `command` (shell), plus prompt/agent hooks (`execPromptHook.ts`,
149
+ `execAgentHook.ts` — run a model prompt or subagent as the hook). View with `/hooks`.
150
+
151
+ ### MCP policy
152
+ ```jsonc
153
+ {
154
+ "enableAllProjectMcpServers": false,
155
+ "enabledMcpjsonServers": [], "disabledMcpjsonServers": [],
156
+ "allowedMcpServers": [], "deniedMcpServers": [],
157
+ "allowManagedMcpServersOnly": false
158
+ }
159
+ ```
160
+
161
+ Server definitions do not live under `mcpServers` in `settings.json`; configure them through
162
+ `ur mcp` / `.mcp.json`. The keys above are approval and enterprise-policy controls.
163
+
164
+ ### Git & attribution
165
+ ```jsonc
166
+ {
167
+ "attribution": {
168
+ "commit": "Co-authored-by: UR-Nexus <noreply@example.invalid>",
169
+ "pr": "Generated with UR-Nexus"
170
+ },
171
+ "includeCoAuthoredBy": true,
172
+ "includeGitInstructions": true
173
+ }
174
+ ```
175
+
176
+ ### UI & terminal
177
+ ```jsonc
178
+ {
179
+ "statusLine": { "type": "command", "command": "./scripts/status.sh", "padding": 1 },
180
+ "language": "en",
181
+ "spinnerTipsEnabled": true, "spinnerVerbs": { "mode": "append", "verbs": [] },
182
+ "spinnerTipsOverride": { "excludeDefault": false, "tips": [] },
183
+ "syntaxHighlightingDisabled": false,
184
+ "terminalTitleFromRename": true,
185
+ "prefersReducedMotion": false,
186
+ "outputStyle": "…", // output style name (src/outputStyles)
187
+ "promptSuggestionEnabled": true,
188
+ "showClearContextOnPlanAccept": true,
189
+ "feedbackSurveyRate": 1
190
+ }
191
+ ```
192
+
193
+ `theme` is global application config managed by `/theme`, not a `SettingsSchema` key.
194
+
195
+ ### Memory & verification
196
+ ```jsonc
197
+ {
198
+ "autoMemoryEnabled": true, "autoMemoryDirectory": "~/.ur/project-memory",
199
+ "autoMemoryExtractionInterval": 1, // run extraction every N turns (token dial)
200
+ "automaticLearningEnabled": true, // local outcome stats, no model calls
201
+ "verifier": { "askBeforeGates": true }, // one approval request per user turn
202
+ "autoDreamEnabled": false,
203
+ "plansDirectory": ".ur/plans"
204
+ }
205
+ ```
206
+
207
+ ### Plugins & marketplaces
208
+ ```jsonc
209
+ {
210
+ "enabledPlugins": { "fmt@acme": true },
211
+ "pluginConfigs": {},
212
+ "extraKnownMarketplaces": {},
213
+ "strictKnownMarketplaces": [
214
+ { "source": "github", "repo": "acme/approved-plugins" }
215
+ ],
216
+ "blockedMarketplaces": [],
217
+ "strictPluginOnlyCustomization": false
218
+ }
219
+ ```
220
+
221
+ ### Auth, org & misc
222
+ ```jsonc
223
+ {
224
+ "apiKeyHelper": "./get-key.sh",
225
+ "awsCredentialExport": "./scripts/aws-env.sh",
226
+ "awsAuthRefresh": "./scripts/aws-refresh.sh",
227
+ "gcpAuthRefresh": "gcloud auth application-default login",
228
+ "forceLoginMethod": "urai",
229
+ "forceLoginOrgUUID": "00000000-0000-0000-0000-000000000000",
230
+ "otelHeadersHelper": "./scripts/otel-headers.sh",
231
+ "env": { "FOO": "bar" }, // extra env for the session
232
+ "companyAnnouncements": [],
233
+ "remote": { "defaultEnvironmentId": "dev-lab" },
234
+ "autoUpdatesChannel": "stable",
235
+ "minimumVersion": "1.65.6",
236
+ "cleanupPeriodDays": 30,
237
+ "fileSuggestion": { "type": "command", "command": "./scripts/files.sh" },
238
+ "respectGitignore": true,
239
+ "defaultShell": "bash",
240
+ "skipWebFetchPreflight": false,
241
+ "voiceEnabled": false,
242
+ "sshConfigs": [
243
+ { "id": "lab", "name": "Lab server", "sshHost": "dev@lab.example" }
244
+ ],
245
+ "agent": "reviewer" // default agent config
246
+ }
247
+ ```
248
+
249
+ `xaaIdp` is conditional on `UR_CODE_ENABLE_XAA`; when enabled,
250
+ `callbackPort` is optional but must be a positive integer. The
251
+ `disableDeepLinkRegistration: "disable"` key exists only in LODESTONE builds.
252
+ `assistantName` is KAIROS-only. There are no top-level `environment`,
253
+ `marketplace`, or `plugin` objects in the standard settings schema.
254
+
255
+ ## Supported environment variables
256
+
257
+ These tables cover user-facing runtime controls. Platform-detection variables,
258
+ CI-provider metadata, test fixtures, and compile-time-only/internal branches
259
+ are not presented as supported configuration merely because source code reads
260
+ them.
261
+
262
+ ### Providers
263
+
264
+ | Variable | Effect |
265
+ |---|---|
266
+ | `OLLAMA_API_KEY` | Bearer token for Ollama's hosted API. With no host set, also switches the base URL to `https://ollama.com` — a local daemon needs no key, a direct connection does. Allowlisted through the Agentic CI env scrub |
267
+ | `OLLAMA_HOST` / `OLLAMA_BASE_URL` | Explicit Ollama endpoint; always wins over the key-implied cloud default |
268
+ | `OLLAMA_CONTEXT_TOKENS` | Override the detected context window |
269
+ | `API_TIMEOUT_MS` | Explicit Ollama request timeout in milliseconds; overrides the model-aware runtime default |
270
+ | `UR_API_TIMEOUT_MS` | Default provider HTTP-client timeout where supported |
271
+ | `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, `OPENROUTER_API_KEY` | Provider credentials; also allowlisted for Agentic CI |
272
+
273
+ ### Core behavior
274
+ | Variable | Effect |
275
+ |---|---|
276
+ | `UR_CODE_SIMPLE=1` | Minimal tool set (Bash/Read/Edit); set by `--bare` |
277
+ | `UR_CODE_REMOTE=true` | Remote/CCR container mode (raises heap to 8GB) |
278
+ | `UR_CODE_DISABLE_BACKGROUND_TASKS=1` | Remove background mode from the Bash, PowerShell, and Agent tools. It does not disable the separate public `ur bg` command |
279
+ | `UR_CODE_DISABLE_AUTO_MEMORY=1` | Disable auto-memory |
280
+ | `UR_CODE_DISABLE_COMMAND_INJECTION_CHECK=1` | Skip bash injection analysis (not recommended) |
281
+ | `UR_CODE_MAX_OUTPUT_TOKENS` | Cap model output tokens |
282
+ | `UR_CODE_MAX_RETRIES` | API retry cap |
283
+ | `UR_CODE_EXTRA_BODY` | Extra JSON merged into API requests |
284
+ | `UR_CODE_EXPERIMENTAL_AGENT_TEAMS=1` | Opt into agent teams/swarm mode in external builds (also available through hidden `--agent-teams`); the runtime kill-switch still applies |
285
+ | `UR_CODE_USE_POWERSHELL_TOOL=1` | Enable the PowerShell tool on Windows |
286
+ | `UR_CODE_INDEX=1` | Force-enable the semantic code index + CodeSearch tool; an existing built index enables it automatically, while `0`, `false`, or `off` force-disable it |
287
+ | `UR_CODE_ENABLE_TASKS=1` | Use structured TaskCreate/Get/Update/List tools in headless/SDK sessions; interactive sessions use them by default |
288
+ | `UR_CODE_MAX_TOOL_USE_CONCURRENCY` | Cap concurrent-safe tools in the ordinary agent loop (default 10, clamped to 1–32) |
289
+ | `UR_MAX_CONCURRENT_TOOLS` | Cap concurrent-safe tools in the streaming executor (default 8, clamped to 1–32); set both concurrency variables when one limit is desired across both paths |
290
+ | `ENABLE_LSP_TOOL=1` | LSP tool |
291
+ | `UR_BROWSER_TOOL=1` / `WEB_BROWSER_TOOL=1` | Enable the model-invocable Browser tool. Its guarded `fetch` action is runtime-independent; interactive actions require `playwright-core` and an installed Chromium/Chrome executable |
292
+ | `UR_CODE_SYNTAX_HIGHLIGHT=0` | Disable syntax highlighting |
293
+ | `UR_CODE_ACCESSIBILITY=1` | Accessibility rendering |
294
+ | `UR_CODE_SHELL_PREFIX` | Prefix every shell command |
295
+ | `UR_CODE_TAGS` | Add one opaque `tags` value to analytics environment metadata; it does not tag a conversation for `/resume` |
296
+ | `UR_CODE_OVERRIDE_DATE` | Fake "today" (testing) |
297
+ | `UR_CODE_DISABLE_AUTO_LEARNING=1` | Disable automatic local pass/fail outcome recording |
298
+
299
+ ### Providers & auth
300
+ | Variable | Effect |
301
+ |---|---|
302
+ | `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` / `GEMINI_API_KEY` / `OPENROUTER_API_KEY` | API-key providers |
303
+ | `UR_MODEL_POOL_CHEAP/STRONG/DEFAULT` | Model pools for routing |
304
+ | `UR_CODE_OAUTH_TOKEN` / `UR_CODE_OAUTH_REFRESH_TOKEN` / `UR_CODE_OAUTH_SCOPES` | OAuth token injection |
305
+ | `UR_CODE_SESSION_ACCESS_TOKEN` | Remote session token |
306
+ | `URHQ_DEFAULT_MODELO_MODEL` / `URHQ_DEFAULT_MODELS_MODEL` / `URHQ_DEFAULT_MODELH_MODEL` | Default model tiers (opus/sonnet/haiku-class) |
307
+ | `MCP_CLIENT_SECRET` | OAuth client secret for `ur mcp add` |
308
+ | `UR_OPENAI_RESPONSES_STATE_KEY` | 32-byte hex/base64 key required to persist encrypted compacted Responses context |
309
+
310
+ ### Protocol, skill, and telemetry controls
311
+ | Variable | Effect |
312
+ |---|---|
313
+ | `UR_MCP_HTTP_TOKEN` / `UR_MCP_HTTP_*` | Authenticate and bound the opt-in stateless MCP 2026 Tasks/Apps server |
314
+ | `UR_A2A_TOKEN` / `UR_A2A_DELEGATION_SECRET` / `UR_A2A_*` | Authenticate, scope, and bound A2A v0.3/v1 serving |
315
+ | `UR_ACP_STDIO_*` | Bound ACP durable sessions, prompts, output, and runtime |
316
+ | `UR_SKILLS_STRICT_SPEC=true` | Reject file skills that violate the Agent Skills specification |
317
+ | `UR_SKILLS_REQUIRE_TRUSTED_SIGNATURE=true` | Require a trusted Ed25519 skill signature at load and invocation |
318
+ | `UR_SKILL_TRUSTED_KEYS_FILE` | Override the trusted **public** skill-key store used to verify signatures |
319
+ | `OTEL_TRACES_EXPORTER` / `OTEL_METRICS_EXPORTER` / `OTEL_LOGS_EXPORTER` | Explicitly enable `otlp` or `console`; unset/`none` is off |
320
+ | `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true` | Opt into bounded prompt/tool/memory content attributes (off by default) |
321
+ | `OTEL_SDK_DISABLED=true` | Disable all OpenTelemetry SDK export |
322
+
323
+ ### Internal / build
324
+ `USER_TYPE=ant` (internal commands/tools), `IS_DEMO`, `NODE_OPTIONS`,
325
+ `COREPACK_ENABLE_AUTO_PIN=0` (forced), `UR_CODE_ENTRYPOINT`, `UR_CODE_WORKER_EPOCH`,
326
+ `UR_CODE_ENVIRONMENT_KIND`, `UR_CODE_IS_COWORK`, `UR_CODE_BRIEF`, `UR_CODE_PROACTIVE`,
327
+ `UR_CODE_EAGER_FLUSH`, `UR_CODE_STREAMLINED_OUTPUT`, `UR_CODE_DEBUG_REPAINTS`,
328
+ `UR_CODE_EXIT_AFTER_FIRST_RENDER`, `UR_CODE_TEST_FIXTURES_ROOT`.
329
+
330
+ Names found only in dead feature branches are not runtime switches. In particular,
331
+ `UR_CODE_REPL`, `UR_CODE_VERIFY_PLAN`, `UR_CODE_USE_BEDROCK`,
332
+ `UR_CODE_USE_VERTEX`, `UR_CODE_DISABLE_CRON`, `UR_CODE_COORDINATOR_MODE`, and
333
+ `UR_CODE_ABLATION_BASELINE` do not enable their named backend/tool/mode in the standard
334
+ build. The last three are read only inside `AGENT_TRIGGERS`, `COORDINATOR_MODE`, and
335
+ `ABLATION_BASELINE` compile-time branches respectively; none of those features is enabled
336
+ by `scripts/bundle.mjs`.
337
+
338
+ ## Keybindings
339
+
340
+ `~/.claude`-style keybindings live at `~/.ur/keybindings.json`; open with `/keybindings`,
341
+ get help with `/keybindings-help`. Managed by `src/keybindings/` (chords supported,
342
+ global + command-scoped bindings; see `useGlobalKeybindings.tsx` / `useCommandKeybindings.tsx`).
343
+
344
+ ## Output styles
345
+
346
+ `outputStyle` setting selects a style; custom styles load from an output-styles directory
347
+ (`src/outputStyles/loadOutputStylesDir.ts`). `/output-style` is deprecated in favor of
348
+ `/config`. Built-in styles (`src/constants/outputStyles.ts`) are Explanatory,
349
+ Game Designer, Learning, Concise, JSON-strict (every response a parseable JSON object),
350
+ Debug-verbose (hypothesis-driven diagnostics), and Release-notes (changelog tone).
351
+
352
+ ## Settings not covered above
353
+
354
+ These keys exist in `SettingsSchema` (`src/utils/settings/types.ts`) and were
355
+ previously undocumented — the gap that let several releases ship settings no
356
+ one could discover. `test/settingsDocCoverage.test.ts` now fails if any schema
357
+ key is missing from this file.
358
+
359
+ | Key | What it does |
360
+ |---|---|
361
+ | `$schema` | JSON Schema URL for editor completion in `settings.json`. Not a UR setting; ignored at runtime. |
362
+ | `worktree.symlinkDirectories` | Directories symlinked from the main repository into each worktree instead of being copied, to avoid disk bloat. Nothing is symlinked unless listed; `node_modules`, `.cache` and `.bin` are the usual candidates. |
363
+ | `worktree.sparsePaths` | Paths to materialize when creating a worktree, via `git sparse-checkout` in cone mode. In a large monorepo only the listed paths are written to disk, which is dramatically faster. |
364
+ | `channelsEnabled` | Teams/Enterprise opt-in for channel notifications from MCP servers that declare the capability. Off unless set. |
365
+ | `allowedChannelPlugins` | Allow-list of `{ marketplace, plugin }` pairs permitted to deliver channel notifications. Used with `channelsEnabled` to bound which plugins can notify. |
366
+ | `urMdExcludes` | Glob patterns or absolute paths of `UR.md` files to skip when loading project memory. Use it to keep vendored or generated `UR.md` files out of context. |
367
+ | `pluginTrustMessage` | Extra text appended to the plugin trust warning shown before installation, for organizations that need to state their own policy at that moment. |
368
+
369
+ ## Tool-result pruning (`context.pruneToolResults`)
370
+
371
+ Superseded tool results — old file reads, greps, shell output — are cleared
372
+ from context once doing so would free a worthwhile amount, keeping the most
373
+ recent ones untouched.
374
+
375
+ | Key | Default | What it does |
376
+ |---|---|---|
377
+ | `context.pruneToolResults.enabled` | `true` | Master switch. |
378
+ | `context.pruneToolResults.minTokensFreed` | `20000` | Prune only when it would free at least this many tokens. Clearing invalidates the cached prefix, so a small cleanup costs more in cache misses than it reclaims; short sessions are never touched. |
379
+ | `context.pruneToolResults.keepRecent` | `8` | Protected zone. The most recent N compactable tool results are never cleared, so the model keeps the working set it is reasoning about. Floored at 1. |
380
+
381
+ Why it is on by default: the alternative when context fills is autocompact,
382
+ which replaces the entire history with a summary. Dropping a superseded file
383
+ read is strictly less destructive than losing the conversation.
384
+
385
+ Compactable tools are `Read`, shell, `Grep`, `Glob`, `WebSearch`, `WebFetch`,
386
+ `Edit` and `Write`. Cleared results are replaced with a marker, not deleted, so
387
+ the tool call itself remains visible in the transcript.
388
+
389
+ This is separate from the time-based trigger (`tengu_slate_heron`), which fires
390
+ only after an hour of idling and is configured through GrowthBook — a service a
391
+ local install never reaches, so it is effectively always off.
392
+
393
+ ## Memory integrity signing (`UR_MEMORY_INTEGRITY_KEY`)
394
+
395
+ Unsigned, the manifest defends against accident and unaware tampering only:
396
+ anyone who can write a memory file can also rewrite the manifest to match, and
397
+ verification passes. Setting `UR_MEMORY_INTEGRITY_KEY` adds an HMAC over the
398
+ file digests, so a forged manifest is detected even when every digest matches
399
+ the file beside it.
400
+
401
+ Off by default, deliberately. A key has to live somewhere, and a key stored
402
+ next to the data it protects adds no security — enable this only when the key
403
+ comes from somewhere the memory directory is not (password manager, CI secret).
404
+
405
+ `ur memory-integrity verify` exits non-zero on an invalid signature, and also
406
+ when a manifest is signed but no key is available: an unverifiable signature is
407
+ not a pass.