Skip to content

Commit 966f43c

Browse files
authored
fix: Codespaces — skip machine-scoped GITHUB_TOKEN, cap retries, fix phantom command (#415)
Closes #413 - Skip auto-enabling `github-models` and `github-copilot` providers in machine environments (Codespaces: `CODESPACES=true`, GitHub Actions: `GITHUB_ACTIONS=true`) when only machine-scoped tokens (`GITHUB_TOKEN`, `GH_TOKEN`) are available. The Codespace/Actions token lacks `models:read` scope needed for GitHub Models API. - Cap retry attempts at 5 (`RETRY_MAX_ATTEMPTS`) to prevent infinite retry loops. Log actionable warning when retries exhaust. - Replace phantom `/discover-and-add-mcps` toast with actionable message. - Add `.devcontainer/` config (Node 22, Bun 1.3.10) for Codespaces. - Add 32 adversarial e2e tests covering full Codespace/Actions env simulation, `GH_TOKEN`, token variations, config overrides, retry bounds. - Update docs to reference `mcp_discover` tool.
1 parent 38bfb52 commit 966f43c

File tree

14 files changed

+640
-17
lines changed

14 files changed

+640
-17
lines changed

.devcontainer/devcontainer.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "Altimate Code",
3+
"image": "mcr.microsoft.com/devcontainers/javascript-node:22",
4+
"features": {
5+
"ghcr.io/devcontainers/features/git:1": {},
6+
"ghcr.io/shyim/devcontainers-features/bun:0": {
7+
"version": "1.3.10"
8+
}
9+
},
10+
"postCreateCommand": ".devcontainer/post-create.sh",
11+
"customizations": {
12+
"vscode": {
13+
"extensions": [
14+
"biomejs.biome",
15+
"dbaeumer.vscode-eslint"
16+
],
17+
"settings": {
18+
"terminal.integrated.defaultProfile.linux": "bash"
19+
}
20+
},
21+
"codespaces": {
22+
"openFiles": [
23+
"README.md"
24+
]
25+
}
26+
},
27+
"forwardPorts": [],
28+
"remoteEnv": {
29+
"BUN_INSTALL": "/home/node/.bun",
30+
"PATH": "/home/node/.bun/bin:${containerEnv:PATH}"
31+
}
32+
}

.devcontainer/post-create.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/bin/bash
2+
set -e
3+
4+
echo "=== Altimate Code: Codespace Setup ==="
5+
6+
# Configure git (required for tests)
7+
git config --global user.name "${GITHUB_USER:-codespace}"
8+
git config --global user.email "${GITHUB_USER:-codespace}@users.noreply.github.com"
9+
10+
# Install dependencies
11+
echo "Installing dependencies with Bun..."
12+
bun install
13+
14+
echo ""
15+
echo "=== Setup complete! ==="
16+
echo ""
17+
echo "Quick start:"
18+
echo " bun run build # Build the CLI"
19+
echo " bun test # Run tests"
20+
echo " bun turbo typecheck # Type-check all packages"
21+
echo ""
22+
echo "To install altimate globally after building:"
23+
echo " bun link"
24+
echo ""

.github/meta/commit.txt

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
fix: viewer UX improvements from 100-trace analysis
1+
fix: Codespaces — skip machine-scoped `GITHUB_TOKEN`, cap retries, fix phantom command
22

3-
- Collapse Files Changed after 5 entries with "Show all N files" toggle
4-
- Rename "GENS" → "LLM Calls" in header cards
5-
- Hide Tokens card when cost is $0 (not actionable without cost context)
6-
- Hide Cost metric card when $0.00 (wasted space)
7-
- Add prominent error summary banner right after header metrics
8-
- Improved dbt outcome detection: catch [PASS], [ERROR], N of M, Compilation Error
9-
- Outcome detection rate improved from 18% → 33% across 100 real traces
10-
- Updated doc screenshots with cleaner samples
3+
Closes #413
114

12-
Tested across 100 real production traces: 0 crashes, 0 JS errors.
13-
14-
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5+
- Skip auto-enabling `github-models` and `github-copilot` providers in
6+
machine environments (Codespaces: `CODESPACES=true`, GitHub Actions:
7+
`GITHUB_ACTIONS=true`) when only machine-scoped tokens (`GITHUB_TOKEN`,
8+
`GH_TOKEN`) are available. The Codespace/Actions token lacks
9+
`models:read` scope needed for GitHub Models API.
10+
- Cap retry attempts at 5 (`RETRY_MAX_ATTEMPTS`) to prevent infinite
11+
retry loops. Log actionable warning when retries exhaust.
12+
- Replace phantom `/discover-and-add-mcps` toast with actionable message.
13+
- Add `.devcontainer/` config (Node 22, Bun 1.3.10) for Codespaces.
14+
- Add 32 adversarial e2e tests covering full Codespace/Actions env
15+
simulation, `GH_TOKEN`, token variations, config overrides, retry bounds.
16+
- Update docs to reference `mcp_discover` tool.

docs/docs/configure/providers.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,9 @@ Access 150+ models through a single API key.
218218

219219
Uses your GitHub Copilot subscription. Authenticate with `altimate auth`.
220220

221+
!!! note "Codespaces & GitHub Actions"
222+
In GitHub Codespaces and GitHub Actions, the machine-scoped `GITHUB_TOKEN` lacks `models:read` permission and cannot be used for GitHub Copilot or GitHub Models inference. altimate automatically skips these providers in machine environments. To use them, authenticate explicitly with `altimate auth` or set a personal access token with `models:read` scope as a Codespace secret.
223+
221224
## Snowflake Cortex
222225

223226
```json

docs/docs/configure/tools.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,10 @@ The `mcp_discover` tool finds MCP servers configured in other AI coding tools an
131131
- `mcp_discover(action: "add", scope: "project")` — Write new servers to `.altimate-code/altimate-code.json`
132132
- `mcp_discover(action: "add", scope: "global")` — Write to the global config dir (`~/.config/opencode/`)
133133

134-
**Auto-discovery:** At startup, altimate-code discovers external MCP servers and shows a toast notification. Servers from your home directory (`~/.claude.json`, `~/.gemini/settings.json`) are auto-enabled since they're user-owned. Servers from project-level files (`.vscode/mcp.json`, `.mcp.json`, `.cursor/mcp.json`) are discovered but **disabled by default** for security — run `/discover-and-add-mcps` to review and enable them.
134+
**Auto-discovery:** At startup, altimate-code discovers external MCP servers and shows a toast notification. Servers from your home directory (`~/.claude.json`, `~/.gemini/settings.json`) are auto-enabled since they're user-owned. Servers from project-level files (`.vscode/mcp.json`, `.mcp.json`, `.cursor/mcp.json`) are discovered but **disabled by default** for security — ask the assistant to add them or use `mcp_discover(action: "add")`.
135135

136136
!!! tip
137-
Home-directory MCP servers (from `~/.claude.json`, `~/.gemini/settings.json`) are loaded automatically. Project-scoped servers require explicit approval via `/discover-and-add-mcps` or `mcp_discover(action: "add")`.
137+
Home-directory MCP servers (from `~/.claude.json`, `~/.gemini/settings.json`) are loaded automatically. Project-scoped servers require explicit approval via `mcp_discover(action: "add")`.
138138

139139
!!! warning "Security: untrusted repositories"
140140
Project-level MCP configs (`.vscode/mcp.json`, `.mcp.json`, `.cursor/mcp.json`) are discovered but not auto-connected. This prevents malicious repositories from executing arbitrary commands. You must explicitly approve project-scoped servers before they run.

docs/docs/reference/security-faq.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ Altimate Code can automatically discover MCP server definitions from other AI to
168168
**Security model:**
169169

170170
- **Home-directory configs** (your personal machine config) are treated as trusted and auto-enabled, since you installed them.
171-
- **Project-scoped configs** (checked into a repo) are discovered but **disabled by default**. You must explicitly approve them via the `/discover-and-add-mcps` tool before they run.
171+
- **Project-scoped configs** (checked into a repo) are discovered but **not auto-connected**. They are loaded with `enabled: false` and shown in a notification. Ask the assistant to enable them, or disable auto-discovery entirely with `experimental.auto_mcp_discovery: false`.
172172
- **Sensitive details are redacted** in discovery notifications. Server commands and URLs are only shown when you explicitly inspect them.
173173
- **Prototype pollution, command injection, and path traversal** are hardened against with input validation and `Object.create(null)` result objects.
174174

docs/docs/usage/github.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ jobs:
3939
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
4040
```
4141
42+
!!! important "LLM provider required"
43+
The workflow `GITHUB_TOKEN` is for repository access only — it cannot be used for LLM inference. You must provide a separate API key (e.g., `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`) as a repository secret. GitHub Copilot and GitHub Models providers are automatically disabled in Actions environments.
44+
4245
### Triggers
4346

4447
| Event | Behavior |

packages/opencode/src/mcp/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ export namespace MCP {
215215

216216
// altimate_change start — show discovery toast after MCP connections complete
217217
if (discoveryResult) {
218-
const message = `Discovered ${discoveryResult.serverNames.length} new MCP server(s): ${discoveryResult.serverNames.join(", ")}. Run /discover-and-add-mcps to enable and add them.`
218+
const message = `Discovered ${discoveryResult.serverNames.length} new MCP server(s): ${discoveryResult.serverNames.join(", ")}. Ask the assistant to add them, or they will be available automatically in the current session.`
219219
Bus.publish(TuiEvent.ToastShow, {
220220
title: "MCP Servers Discovered",
221221
message,

packages/opencode/src/provider/provider.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,11 +1071,41 @@ export namespace Provider {
10711071

10721072
// load env
10731073
const env = Env.all()
1074+
// altimate_change start — skip github-models/github-copilot auto-enable from machine-scoped GITHUB_TOKEN
1075+
// In GitHub Codespaces and GitHub Actions, GITHUB_TOKEN is a machine-scoped
1076+
// token for repo operations, not for model inference. Auto-enabling these
1077+
// providers leads to immediate rate limiting ("Too Many Requests") and long
1078+
// retry loops. We detect these environments and skip auto-enable unless the
1079+
// user has explicitly set a different token.
1080+
//
1081+
// Environment detection (official GitHub docs):
1082+
// Codespaces: CODESPACES=true, CODESPACE_NAME set
1083+
// Actions: GITHUB_ACTIONS=true, CI=true
1084+
//
1085+
// Machine-scoped tokens to ignore: GITHUB_TOKEN and GH_TOKEN (gh CLI alias)
1086+
const isMachineEnv = env["CODESPACES"] === "true" || env["GITHUB_ACTIONS"] === "true"
1087+
const machineTokenNames = new Set(["GITHUB_TOKEN", "GH_TOKEN"])
1088+
const skipGithubProviders = new Set(["github-models", "github-copilot", "github-copilot-enterprise"])
1089+
// altimate_change end
10741090
for (const [id, provider] of Object.entries(database)) {
10751091
const providerID = ProviderID.make(id)
10761092
if (disabled.has(providerID)) continue
10771093
const apiKey = provider.env.map((item) => env[item]).find(Boolean)
10781094
if (!apiKey) continue
1095+
// altimate_change start — skip GitHub providers when only machine-scoped tokens exist
1096+
if (isMachineEnv && skipGithubProviders.has(id)) {
1097+
// Check if ALL env vars for this provider are machine-scoped tokens
1098+
const matchedEnvVars = provider.env.filter((item) => env[item])
1099+
const allMachineScoped = matchedEnvVars.every((item) => machineTokenNames.has(item))
1100+
if (allMachineScoped) {
1101+
log.info("skipping provider in machine environment (token is not for model inference)", {
1102+
providerID,
1103+
environment: env["CODESPACES"] ? "codespace" : "github-actions",
1104+
})
1105+
continue
1106+
}
1107+
}
1108+
// altimate_change end
10791109
mergeProvider(providerID, {
10801110
source: "env",
10811111
key: provider.env.length === 1 ? apiKey : undefined,

packages/opencode/src/session/processor.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,9 @@ export namespace SessionProcessor {
394394
})
395395
} else {
396396
const retry = SessionRetry.retryable(error)
397-
if (retry !== undefined) {
397+
// altimate_change start — cap retries to avoid infinite loops, log on exhaustion
398+
if (retry !== undefined && attempt < SessionRetry.RETRY_MAX_ATTEMPTS) {
399+
// altimate_change end
398400
attempt++
399401
const delay = SessionRetry.delay(attempt, error.name === "APIError" ? error : undefined)
400402
SessionStatus.set(input.sessionID, {
@@ -406,6 +408,16 @@ export namespace SessionProcessor {
406408
await SessionRetry.sleep(delay, input.abort).catch(() => {})
407409
continue
408410
}
411+
// altimate_change start — log when retries exhausted for debugging
412+
if (retry !== undefined) {
413+
log.warn("max retry attempts reached, giving up", {
414+
attempt,
415+
message: retry,
416+
providerID: input.model.providerID,
417+
modelID: input.model.id,
418+
})
419+
}
420+
// altimate_change end
409421
input.assistantMessage.error = error
410422
Bus.publish(Session.Event.Error, {
411423
sessionID: input.assistantMessage.sessionID,

0 commit comments

Comments
 (0)