Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 49 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: help setup build-all build-frontend build-backend build-operator build-runner build-state-sync deploy clean
.PHONY: help setup build-all build-frontend build-backend build-operator build-runner build-state-sync deploy clean check-architecture
.PHONY: local-up local-down local-clean local-status local-rebuild local-reload-backend local-reload-frontend local-reload-operator local-sync-version
.PHONY: local-dev-token
.PHONY: local-logs local-logs-backend local-logs-frontend local-logs-operator local-shell local-shell-frontend
Expand All @@ -14,7 +14,28 @@

# Configuration
CONTAINER_ENGINE ?= podman
PLATFORM ?= linux/amd64

# Auto-detect host architecture for native builds
# Override with PLATFORM=linux/amd64 or PLATFORM=linux/arm64 if needed
HOST_OS := $(shell uname -s)
HOST_ARCH := $(shell uname -m)

# Map uname output to Docker platform names
ifeq ($(HOST_ARCH),arm64)
DETECTED_PLATFORM := linux/arm64
else ifeq ($(HOST_ARCH),aarch64)
DETECTED_PLATFORM := linux/arm64
else ifeq ($(HOST_ARCH),x86_64)
DETECTED_PLATFORM := linux/amd64
else ifeq ($(HOST_ARCH),amd64)
DETECTED_PLATFORM := linux/amd64
else
DETECTED_PLATFORM := linux/amd64
$(warning Unknown architecture $(HOST_ARCH), defaulting to linux/amd64)
endif

# Allow manual override via PLATFORM=...
PLATFORM ?= $(DETECTED_PLATFORM)
BUILD_FLAGS ?=
NAMESPACE ?= ambient-code
REGISTRY ?= quay.io/your-org
Expand Down Expand Up @@ -76,7 +97,7 @@ help: ## Display this help message
@echo '$(COLOR_BOLD)Configuration Variables:$(COLOR_RESET)'
@echo ' CONTAINER_ENGINE=$(CONTAINER_ENGINE) (docker or podman)'
@echo ' NAMESPACE=$(NAMESPACE)'
@echo ' PLATFORM=$(PLATFORM)'
@echo ' PLATFORM=$(PLATFORM) (detected: $(DETECTED_PLATFORM) from $(HOST_OS)/$(HOST_ARCH))'
@echo ''
@echo '$(COLOR_BOLD)Examples:$(COLOR_RESET)'
@echo ' make local-up CONTAINER_ENGINE=docker'
Expand Down Expand Up @@ -626,15 +647,32 @@ check-kubectl: ## Check if kubectl is installed
@command -v kubectl >/dev/null 2>&1 || \
(echo "$(COLOR_RED)✗$(COLOR_RESET) kubectl not found. Install: https://kubernetes.io/docs/tasks/tools/" && exit 1)

check-architecture: ## Validate build architecture matches host
@echo "$(COLOR_BOLD)Architecture Check$(COLOR_RESET)"
@echo " Host: $(HOST_OS) / $(HOST_ARCH)"
@echo " Detected Platform: $(DETECTED_PLATFORM)"
@echo " Active Platform: $(PLATFORM)"
@if [ "$(PLATFORM)" != "$(DETECTED_PLATFORM)" ]; then \
echo ""; \
echo "$(COLOR_YELLOW)⚠ Cross-compilation active$(COLOR_RESET)"; \
echo " Building $(PLATFORM) images on $(DETECTED_PLATFORM) host"; \
echo " This will be slower (QEMU emulation)"; \
echo ""; \
echo " To use native builds:"; \
echo " make build-all PLATFORM=$(DETECTED_PLATFORM)"; \
else \
echo "$(COLOR_GREEN)✓$(COLOR_RESET) Using native architecture"; \
fi

_build-and-load: ## Internal: Build and load images
@echo " Building backend..."
@$(CONTAINER_ENGINE) build -t $(BACKEND_IMAGE) components/backend $(QUIET_REDIRECT)
@echo " Building frontend..."
@$(CONTAINER_ENGINE) build -t $(FRONTEND_IMAGE) components/frontend $(QUIET_REDIRECT)
@echo " Building operator..."
@$(CONTAINER_ENGINE) build -t $(OPERATOR_IMAGE) components/operator $(QUIET_REDIRECT)
@echo " Building runner..."
@$(CONTAINER_ENGINE) build -t $(RUNNER_IMAGE) -f components/runners/claude-code-runner/Dockerfile components/runners $(QUIET_REDIRECT)
@echo " Building backend ($(PLATFORM))..."
@$(CONTAINER_ENGINE) build $(PLATFORM_FLAG) -t $(BACKEND_IMAGE) components/backend $(QUIET_REDIRECT)
@echo " Building frontend ($(PLATFORM))..."
@$(CONTAINER_ENGINE) build $(PLATFORM_FLAG) -t $(FRONTEND_IMAGE) components/frontend $(QUIET_REDIRECT)
@echo " Building operator ($(PLATFORM))..."
@$(CONTAINER_ENGINE) build $(PLATFORM_FLAG) -t $(OPERATOR_IMAGE) components/operator $(QUIET_REDIRECT)
@echo " Building runner ($(PLATFORM))..."
@$(CONTAINER_ENGINE) build $(PLATFORM_FLAG) -t $(RUNNER_IMAGE) -f components/runners/claude-code-runner/Dockerfile components/runners $(QUIET_REDIRECT)
@echo " Tagging images with localhost prefix..."
@$(CONTAINER_ENGINE) tag $(BACKEND_IMAGE) localhost/$(BACKEND_IMAGE) 2>/dev/null || true
@$(CONTAINER_ENGINE) tag $(FRONTEND_IMAGE) localhost/$(FRONTEND_IMAGE) 2>/dev/null || true
Expand Down
2 changes: 1 addition & 1 deletion components/manifests/minikube/frontend-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ spec:
- name: GITHUB_APP_SLUG
value: "ambient-code"
- name: VTEAM_VERSION
value: "v0.0.12-22-g5553056"
value: "v0.0.19-8-g6d9251e"
- name: DISABLE_AUTH
value: "true"
- name: MOCK_USER
Expand Down
245 changes: 245 additions & 0 deletions components/runners/claude-code-runner/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
# Claude Code Runner

The Claude Code Runner is a Python-based component that wraps the Claude Code SDK to provide agentic coding capabilities within the Ambient platform.

## Architecture

The runner consists of several key components:

- **`adapter.py`** - Core adapter that wraps the Claude Code SDK and produces AG-UI protocol events
- **`main.py`** - FastAPI server that handles run requests via SSE (Server-Sent Events)
- **`observability.py`** - Langfuse integration for tracking usage and performance
- **`security_utils.py`** - Security utilities for sanitizing secrets and timeouts
- **`context.py`** - Runner context for session and workspace management

## System Prompt Configuration

The Claude Code Runner uses a hybrid system prompt approach that combines:

1. **Base Claude Code Prompt** - The built-in `claude_code` system prompt from the Claude Agent SDK
2. **Workspace Context** - Custom workspace-specific information appended to the base prompt

### Implementation

In `adapter.py` (lines 508-511), the system prompt is configured as an array:

```python
system_prompt_config = [
"claude_code",
{"type": "text", "text": workspace_prompt}
]
```

This configuration ensures that:
- Claude receives the standard Claude Code instructions and capabilities
- Additional workspace context is provided, including:
- Repository structure and locations
- Active workflow information
- Artifacts and file upload locations
- Git branch and push instructions for auto-push repos
- MCP integration setup instructions
- Workflow-specific instructions from `ambient.json`

### Workspace Context

The workspace context prompt is built by `_build_workspace_context_prompt()` (lines 1500-1575) and includes:

- **Working Directory**: Current workflow or repository location
- **Artifacts Path**: Where to create output files
- **Uploaded Files**: Files uploaded by the user
- **Repositories**: List of repos available in the session
- **Working Branch**: Feature branch for all repos (e.g., `ambient/<session-id>`)
- **Git Push Instructions**: Auto-push configuration for specific repos
- **MCP Integrations**: Instructions for setting up Google Drive and Jira access
- **Workflow Instructions**: Custom system prompt from workflow's `ambient.json`

## Environment Variables

### Authentication

- `ANTHROPIC_API_KEY` - Anthropic API key for Claude access
- `CLAUDE_CODE_USE_VERTEX` - Set to `1` to use Vertex AI instead of Anthropic API
- `GOOGLE_APPLICATION_CREDENTIALS` - Path to GCP service account key (for Vertex AI)
- `ANTHROPIC_VERTEX_PROJECT_ID` - GCP project ID (for Vertex AI)
- `CLOUD_ML_REGION` - GCP region (for Vertex AI)

### Model Configuration

- `LLM_MODEL` - Model to use (e.g., `claude-sonnet-4-5`)
- `LLM_MAX_TOKENS` / `MAX_TOKENS` - Maximum tokens per response
- `LLM_TEMPERATURE` / `TEMPERATURE` - Temperature for sampling

### Session Configuration

- `AGENTIC_SESSION_NAME` - Session name/ID
- `AGENTIC_SESSION_NAMESPACE` - K8s namespace for the session
- `IS_RESUME` - Set to `true` when resuming a session
- `INITIAL_PROMPT` - Initial user prompt

### Repository Configuration

- `REPOS_JSON` - JSON array of repository configurations
```json
[
{
"url": "https://github.com/owner/repo",
"name": "repo-name",
"branch": "ambient/session-id",
"autoPush": true
}
]
```
- `MAIN_REPO_NAME` - Name of the main repository (CWD)
- `MAIN_REPO_INDEX` - Index of main repo (if name not specified)

### Workflow Configuration

- `ACTIVE_WORKFLOW_GIT_URL` - URL of active workflow repository

### Observability

- `LANGFUSE_PUBLIC_KEY` - Langfuse public key
- `LANGFUSE_SECRET_KEY` - Langfuse secret key
- `LANGFUSE_HOST` - Langfuse host URL

### Backend Integration

- `BACKEND_API_URL` - URL of the backend API
- `PROJECT_NAME` - Project name
- `BOT_TOKEN` - Authentication token for backend API calls
- `USER_ID` - User ID for observability
- `USER_NAME` - User name for observability

### MCP Configuration

- `MCP_CONFIG_FILE` - Path to MCP servers config (default: `/app/claude-runner/.mcp.json`)

## MCP Servers

The runner supports MCP (Model Context Protocol) servers for extending Claude's capabilities:

- **webfetch** - Fetch content from URLs
- **mcp-atlassian** - Jira integration for issue management
- **google-workspace** - Google Drive integration for file access
- **session** - Session control tools (restart_session)

MCP servers are configured in `.mcp.json` and loaded at runtime.

## Development

### Running Tests

```bash
# Install dependencies
uv pip install -e ".[dev]"

# Run all tests
pytest -v

# Run with coverage
pytest --cov=. --cov-report=term-missing
```

See [tests/README.md](tests/README.md) for detailed testing documentation.

### Local Development

```bash
# Install in development mode
uv pip install -e .

# Run the server
python main.py
```

## API

The runner exposes a FastAPI server with the following endpoints:

- `POST /run` - Execute a run with Claude Code SDK
- Request: `RunAgentInput` (thread_id, run_id, messages)
- Response: Server-Sent Events (SSE) stream of AG-UI protocol events

- `POST /interrupt` - Interrupt the active execution

- `GET /health` - Health check endpoint

## AG-UI Protocol Events

The runner emits AG-UI protocol events via SSE:

- `RUN_STARTED` - Run has started
- `TEXT_MESSAGE_START` - Message started (user or assistant)
- `TEXT_MESSAGE_CONTENT` - Message content chunk
- `TEXT_MESSAGE_END` - Message completed
- `TOOL_CALL_START` - Tool invocation started
- `TOOL_CALL_ARGS` - Tool arguments
- `TOOL_CALL_END` - Tool invocation completed
- `STEP_STARTED` - Processing step started
- `STEP_FINISHED` - Processing step completed
- `STATE_DELTA` - State update (e.g., result payload)
- `RAW` - Custom events (thinking blocks, system logs, etc.)
- `RUN_FINISHED` - Run completed
- `RUN_ERROR` - Error occurred

## Workspace Structure

The runner operates within a workspace at `/workspace/` with the following structure:

```
/workspace/
├── .claude/ # Claude SDK state (conversation history)
├── repos/ # Cloned repositories
│ └── {repo-name}/ # Individual repository
├── workflows/ # Workflow repositories
│ └── {workflow-name}/ # Individual workflow
├── artifacts/ # Output files created by Claude
├── file-uploads/ # User-uploaded files
└── .google_workspace_mcp/ # Google OAuth credentials
└── credentials/
└── credentials.json
```

## Security

The runner implements several security measures:

- **Secret Sanitization**: API keys and tokens are redacted from logs
- **Timeout Protection**: Operations have configurable timeouts
- **User Context Validation**: User IDs and names are sanitized
- **Read-only Workflow Directories**: Workflows are read-only, outputs go to artifacts

See `security_utils.py` for implementation details.

## Recent Changes

### System Prompt Configuration (2026-01-28)

Changed the system prompt configuration to use a hybrid approach:

**Before:**
```python
system_prompt_config = {"type": "text", "text": workspace_prompt}
```

**After:**
```python
system_prompt_config = [
"claude_code",
{"type": "text", "text": workspace_prompt}
]
```

**Rationale:**
- Leverages the built-in Claude Code system prompt for standard capabilities
- Appends workspace-specific context for session-aware operation
- Maintains separation between standard instructions and custom context
- Ensures Claude has both general coding capabilities and workspace knowledge

**Impact:**
- Claude receives comprehensive instructions from both sources
- No breaking changes to existing functionality
- Better alignment with Claude Agent SDK best practices

**Files Changed:**
- `components/runners/claude-code-runner/adapter.py` (lines 508-511)
5 changes: 4 additions & 1 deletion components/runners/claude-code-runner/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,10 @@ async def restart_session_tool(args: dict) -> dict:
artifacts_path="artifacts",
ambient_config=ambient_config,
)
system_prompt_config = {"type": "text", "text": workspace_prompt}
system_prompt_config = [
"claude_code",
{"type": "text", "text": workspace_prompt}
]

# Configure SDK options
options = ClaudeAgentOptions(
Expand Down
32 changes: 32 additions & 0 deletions docs/developer/local-development/kind.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,25 @@ podman ps && kind --version && kubectl version --client
docker ps && kind --version && kubectl version --client
```

## Architecture Support

The platform auto-detects your host architecture and builds native images:

- **Apple Silicon (M1/M2/M3):** `linux/arm64`
- **Intel/AMD:** `linux/amd64`

**Verify native builds:**
```bash
make check-architecture # Should show "✓ Using native architecture"
```

**Manual override (if needed):**
```bash
make build-all PLATFORM=linux/arm64 # Force specific architecture
```

⚠️ **Warning:** Cross-compiling (building non-native architecture) is 4-6x slower and may crash.

## Commands

### `make kind-up`
Expand Down Expand Up @@ -206,6 +225,19 @@ lsof -i:8080 # Find what's using the port
# Kill it or edit e2e/scripts/setup-kind.sh to use different ports
```

### Build crashes with segmentation fault

**Symptom:** `qemu: uncaught target signal 11 (Segmentation fault)` during Next.js build

**Fix:**
```bash
# Auto-detect and use native architecture
make local-clean
make local-up
```

**Diagnosis:** Run `make check-architecture` to verify native builds are enabled.

### MinIO errors

```bash
Expand Down
Loading
Loading