sandbox-agent/research/agents/amp.md
Nathan Flurry 5744712f06
fix: opencode compat tool call rendering and default to no-token
- Fix tool name lost on ToolResult events (persist via tool_name_by_call)
- Fix tool input lost on ToolResult events (persist via tool_args_by_call)
- Fix tool output in wrong field (error -> output)
- Fix text doubling in streaming (defer emit to ItemCompleted)
- Fix missing delta field in text streaming events
- Default server mode to no-token when --token not specified
- Add install-fast-sa and install-fast-gigacode justfile targets
2026-02-06 10:56:24 +00:00

15 KiB

Amp Research

Research notes on Sourcegraph Amp's configuration, credential discovery, and runtime behavior.

Overview

  • Provider: Anthropic (via Sourcegraph, proxied through ampcode.com)
  • Execution Method: CLI subprocess (amp command)
  • Session Persistence: Session ID (string)
  • SDK: @sourcegraph/amp-sdk (closed source)
  • Binary: Bun-bundled JS application (ELF wrapping Bun runtime + embedded JS)
  • Binary Location: /usr/local/bin/amp
  • Backend: https://ampcode.com/ (server-side proxy for all LLM requests)

CLI Usage

Interactive Mode

amp "your prompt here"
amp --model claude-sonnet-4 "your prompt"

Non-Interactive Mode

amp --print --output-format stream-json "your prompt"
amp --print --output-format stream-json --dangerously-skip-permissions "prompt"
amp --continue SESSION_ID "follow up"

Key CLI Flags

Flag Description
--print Output mode (non-interactive)
--output-format stream-json JSONL streaming output
--dangerously-skip-permissions Skip permission prompts
--continue SESSION_ID Resume existing session
--model MODEL Specify model
--toolbox TOOLBOX Toolbox configuration

Credential Discovery

Priority Order

  1. Environment variable: ANTHROPIC_API_KEY
  2. Sourcegraph authentication
  3. Claude Code credentials (shared)

Config File Locations

Path Description
~/.amp/config.json Primary config
~/.claude/.credentials.json Shared with Claude Code

Amp can use Claude Code's OAuth credentials as fallback.

Streaming Response Format

Amp outputs newline-delimited JSON events:

{"type": "system", "subtype": "init", "session_id": "...", "tools": [...]}
{"type": "assistant", "message": {...}, "session_id": "..."}
{"type": "user", "message": {...}, "session_id": "..."}
{"type": "result", "subtype": "success", "result": "...", "session_id": "..."}

Event Types

Type Description
system System initialization with tools list
assistant Assistant message with content blocks
user User message (tool results)
result Final result with session ID

Content Block Types

type ContentBlock =
  | { type: "text"; text: string }
  | { type: "tool_use"; id: string; name: string; input: Record<string, unknown> }
  | { type: "thinking"; thinking: string }
  | { type: "redacted_thinking"; data: string };

Response Schema

interface AmpResultMessage {
  type: "result";
  subtype: "success";
  duration_ms: number;
  is_error: boolean;
  num_turns: number;
  result: string;
  session_id: string;
}

Session Management

  • Session ID captured from streaming events
  • Use --continue SESSION_ID to resume
  • Sessions stored internally by Amp CLI

Agent Modes vs Permission Modes

Permission Modes (Declarative Rules)

Amp uses declarative permission rules configured before execution:

interface PermissionRule {
  tool: string;  // Glob pattern: "Bash", "mcp__playwright__*"
  matches?: { [argumentName: string]: string | string[] | boolean };
  action: "allow" | "reject" | "ask" | "delegate";
  context?: "thread" | "subagent";
}
Action Behavior
allow Automatically permit
reject Automatically deny
ask Prompt user (CLI handles internally)
delegate Delegate to subagent context

Example Rules

const permissions: PermissionRule[] = [
  { tool: "Read", action: "allow" },
  { tool: "Bash", matches: { command: "git *" }, action: "allow" },
  { tool: "Write", action: "ask" },
  { tool: "mcp__*", action: "reject" }
];

Agent Modes

No documented agent mode concept. Behavior controlled via:

  • --toolbox flag for different tool configurations
  • Permission rules for feature coverage restrictions

Bypass All Permissions

amp --dangerously-skip-permissions "prompt"

Or via SDK:

execute(prompt, { dangerouslyAllowAll: true });

Human-in-the-Loop

No Interactive HITL API

While permission rules support "ask" action, Amp does not expose an SDK-level API for programmatically responding to permission requests. The CLI handles user interaction internally.

For universal API integration, Amp should be run with:

  • Pre-configured permission rules, or
  • dangerouslyAllowAll: true to bypass

SDK Usage

import { execute, type AmpOptions } from '@sourcegraph/amp-sdk';

interface AmpOptions {
  cwd?: string;
  dangerouslyAllowAll?: boolean;
  toolbox?: string;
  mcpConfig?: MCPConfig;
  permissions?: PermissionRule[];
  continue?: boolean | string;
}

const result = await execute(prompt, options);

Installation

# Get latest version
VERSION=$(curl -s https://storage.googleapis.com/amp-public-assets-prod-0/cli/cli-version.txt)

# Linux x64
curl -fsSL "https://storage.googleapis.com/amp-public-assets-prod-0/cli/${VERSION}/amp-linux-x64" \
  -o /usr/local/bin/amp && chmod +x /usr/local/bin/amp

# Linux ARM64
curl -fsSL "https://storage.googleapis.com/amp-public-assets-prod-0/cli/${VERSION}/amp-linux-arm64" \
  -o /usr/local/bin/amp && chmod +x /usr/local/bin/amp

# macOS ARM64
curl -fsSL "https://storage.googleapis.com/amp-public-assets-prod-0/cli/${VERSION}/amp-darwin-arm64" \
  -o /usr/local/bin/amp && chmod +x /usr/local/bin/amp

# macOS x64
curl -fsSL "https://storage.googleapis.com/amp-public-assets-prod-0/cli/${VERSION}/amp-darwin-x64" \
  -o /usr/local/bin/amp && chmod +x /usr/local/bin/amp

Timeout

  • Default timeout: 5 minutes (300,000 ms)
  • Process killed with SIGTERM on timeout

Model Discovery

No model discovery mechanism exists. Amp uses a server-side proxy architecture where model selection is abstracted behind "modes".

Architecture (Reverse Engineered)

Amp is NOT a Go binary as previously thought — it is a Bun-bundled JavaScript application (ELF binary wrapping Bun runtime + embedded JS). The CLI logs confirm: "argv":["bun","/$bunfs/root/amp-linux-x64",...].

Amp is a server-side proxy. All LLM requests go through https://ampcode.com/:

  1. CLI authenticates via AMP_API_KEY env var or browser-based OAuth to https://ampcode.com/auth/cli-login
  2. On startup, calls getUserInfo against https://ampcode.com/
  3. Model selection is handled server-side, not client-side

Modes Instead of Models

Amp uses modes (--mode / -m flag) instead of direct model selection. Each mode bundles a model, system prompt, and tool selection together server-side.

Agent Modes

Mode Primary Model Description
smart Claude Opus 4.6 Default. Unconstrained state-of-the-art model use, maximum capability and autonomy
rush Claude Haiku 4.5 Faster and cheaper, suitable for small, well-defined tasks
deep GPT-5.2 Codex Deep reasoning with extended thinking for complex problems. Requires amp.experimental.modes: ["deep"]
free Unknown Free tier (listed in CLI --help but not on docs site)
large Unknown Hidden/undocumented mode (referenced in docs but no details)

Source: ampcode.com/manual, ampcode.com/models

Specialized Models (not user-selectable)

Amp also uses additional models for specific subtasks:

Role Model Purpose
Review Gemini 3 Pro Code review and bug detection
Search subagent Gemini 3 Flash Codebase retrieval
Oracle subagent GPT-5.2 Complex code reasoning
Librarian subagent Claude Sonnet 4.5 External code research
Image/PDF analysis Gemini 3 Flash Multimodal input processing
Content generation Gemini 3 Pro Image (Painter) Image generation
Handoff (context) Gemini 2.5 Flash Context management
Thread categorization Gemini 2.5 Flash-Lite Thread organization
Title generation Claude Haiku 4.5 Thread title generation

Mode Subsettings

  • amp.experimental.modes — Array of experimental mode names to enable. Currently only ["deep"] is documented.
  • amp.internal.deepReasoningEffort — Override reasoning effort for GPT-5.2 Codex in deep mode. Options: medium, high, xhigh. Default: medium. Keyboard shortcut Alt+D cycles through deepdeep²deep³ (corresponding to medium → high → xhigh).

Switching Modes

  • CLI flag: --mode <value> or -m <value>
  • Interactive TUI: Ctrl+O → type "mode"
  • Editor extension: Mode selector in the prompt field

No Programmatic Mode Listing

There is no CLI command (amp modes list) or API endpoint to list available modes. The modes are:

The --model flag also still exists on the CLI but modes are the primary interface. It's unclear if --model bypasses mode selection or if it's ignored.

Reverse Engineering Methodology

Step 1: CLI help analysis

amp --help

Revealed:

  • -m, --mode <value> flag with deep, free, rush, smart options (not --model for models)
  • AMP_URL env var defaults to https://ampcode.com/
  • AMP_API_KEY env var for authentication
  • Settings at ~/.config/amp/settings.json
  • Logs at ~/.cache/amp/logs/cli.log

Step 2: Binary analysis

file ~/.local/bin/amp   # → ELF 64-bit LSB executable, 117MB
ls -lh ~/.local/bin/amp # → 117M
strings ~/.local/bin/amp | grep 'ampcode' # → 43 matches, embedded JS visible

The file command showed an ELF binary, initially suggesting a compiled Go binary. But strings revealed embedded JavaScript source code, and the debug logs later confirmed it's actually a Bun-bundled application (argv: ["bun", "/$bunfs/root/amp-linux-x64", ...]).

The embedded JS is minified but partially readable via strings. Found tool definitions (edit_file, write_file, create_file), skill loading code, and MCP integration code. Did not find hardcoded model lists or mode→model mappings — these are server-side.

Step 3: strace (failed for network, useful for file IO)

strace -e trace=connect -f amp --execute "say hello" ...

Result: No AF_INET connections captured. Only saw:

  • AF_UNIX socket to /tmp/tmux-1000/default (tmux IPC)
  • socketpair() for internal IPC between threads

Why it failed: Bun uses io_uring for async network IO on Linux, which bypasses traditional connect()/sendto() syscalls. strace hooks into the syscall layer, but io_uring submits work directly to the kernel via shared memory rings, making it invisible to strace.

Even with full syscall tracing (strace -f -s 512 capturing 27,000 lines), zero TCP connections appeared.

Step 4: Process network inspection (partial success)

# While amp was running:
ss -tnp | grep amp
cat /proc/<pid>/net/tcp6

From /proc/net/tcp6, decoded a connection to port 01BB (443/HTTPS). Resolved the destination to 34.54.147.251 via:

dig ampcode.com +short  # → 34.54.147.251

Confirmed Amp connects to ampcode.com:443. But ss -tnp couldn't attribute the connection to the amp process (process had already exited or Bun's process model confused ss).

Step 5: Debug logging (most useful)

env AMP_API_KEY=fake-key amp --execute "say hello" --stream-json --log-level debug
# Then read: ~/.cache/amp/logs/cli.log

The debug log revealed the complete startup sequence and API flow. Key log messages:

  • "Initializing CLI context" — shows hasAmpAPIKey, hasAmpURL, hasSettingsFile
  • "Resolved Amp URL"https://ampcode.com/
  • "API key lookup before login"found: true/false
  • "API request for getUserInfo failed: 401" — confirms API call to ampcode.com with our fake key
  • "Starting Amp background services" — proceeds even after auth failure

Step 6: Fake API key to bypass login (success)

Without AMP_API_KEY, Amp hangs indefinitely trying to open a browser for OAuth at https://ampcode.com/auth/cli-login?authToken=...&callbackPort=.... Setting AMP_API_KEY=fake-key bypasses the browser login flow and reaches the API call stage (where it gets a 401).

Step 7: NODE_DEBUG (failed)

env NODE_DEBUG=http,https,net amp ...

No output — Bun ignores Node.js debug environment variables.

What Was NOT Captured

  • Actual HTTP request/response bodies — Would require mitmproxy with HTTPS interception (set amp.proxy or HTTPS_PROXY env var, install custom CA cert). Not attempted.
  • Mode→model mappings — These are server-side in ampcode.com. The CLI sends a mode name and the server selects the model.
  • Full API schema — Only saw getUserInfo endpoint name in error message. Thread creation, message streaming, and other endpoints are unknown.
  • Whether --model bypasses mode selection — Couldn't test without a valid API key.

Future Investigation

To capture full HTTP traffic, set up mitmproxy:

# Install mitmproxy
pip install mitmproxy

# Start proxy
mitmproxy --listen-port 8080

# Run amp through proxy (amp.proxy setting or env var)
# amp respects amp.proxy setting in ~/.config/amp/settings.json:
# { "amp.proxy": "http://localhost:8080" }
#
# Then install mitmproxy's CA cert for TLS interception.

Alternatively, since amp is a Bun binary, it may respect HTTPS_PROXY env var by default (Go's net/http does, Bun's fetch may as well).

API Flow (from debug logs)

1. "Starting Amp CLI" (version 0.0.1770352274-gd36e02)
2. "Initializing CLI context" (hasAmpAPIKey: true/false)
3. "Resolved Amp URL" → https://ampcode.com/
4. Skills loading, MCP initialization, toolbox registration
5. "API key lookup before login"
6. getUserInfo API call → https://ampcode.com/ (401 with invalid key)
7. "Starting Amp background services"
8. Thread creation + message streaming via ampcode.com

Current Behavior

The sandbox-agent passes --model through to Amp without validation:

if let Some(model) = options.model.as_deref() {
    command.arg("--model").arg(model);
}

Possible Approaches

  1. Proxy provider APIs — Not applicable; Amp proxies through ampcode.com, not directly to model providers
  2. Hardcode known modes — Expose the four modes (deep, free, rush, smart) as the available "model" options
  3. Wait for Amp API — Amp may add model/mode discovery in a future release
  4. Scrape ampcode.com — Check if the web UI exposes available modes/models

Notes

  • Amp is similar to Claude Code (same streaming format)
  • Can share credentials with Claude Code
  • No interactive HITL - must use pre-configured permissions
  • SDK is closed source but types are documented
  • MCP server integration supported via mcpConfig