diff --git a/llms-full.txt b/llms-full.txt
index 3554a845..a1764d55 100644
--- a/llms-full.txt
+++ b/llms-full.txt
@@ -9155,15 +9155,67 @@ The `acp_command` is the shell command used to spawn the server process. The SDK
**Key difference from standard agents:** With `ACPAgent`, you don't need an `LLM_API_KEY` in your code. The ACP server handles its own LLM authentication and API calls. This is *delegation* — your code sends messages to the ACP server, which manages all LLM interactions internally.
+### Prompt Context (AgentContext)
+
+`ACPAgent` supports `agent_context` for **prompt-only extensions** — skills, repository context, current datetime, and system/user message suffixes are appended to the user message before it reaches the ACP server. This lets you inject the same skill catalog and repo-specific guidance that the built-in Agent receives, without interfering with the server's own tools or execution model.
+
+```python icon="python" highlight={4-12,16}
+from openhands.sdk.agent import ACPAgent
+from openhands.sdk import AgentContext
+from openhands.sdk.context import Skill
+
+context = AgentContext(
+ skills=[
+ Skill(
+ name="code-style",
+ content="Always use type hints in Python.",
+ trigger=None, # always active
+ ),
+ ],
+ system_message_suffix="You are reviewing a Python project.",
+)
+
+agent = ACPAgent(
+ acp_command=["npx", "-y", "@agentclientprotocol/claude-agent-acp"],
+ agent_context=context,
+)
+```
+
+The prompt assembly works as follows:
+
+1. The conversation layer builds the user `MessageEvent`, including any per-turn `extended_content` (e.g. triggered-skill injections).
+2. `ACPAgent._build_acp_prompt()` collects all text blocks from the message and appends the rendered `AgentContext` prompt (datetime, repo context, available skills, system suffix) via `to_acp_prompt_context()`.
+3. The combined text is sent as a single user message to the ACP server.
+
+
+`user_message_suffix` is an ACP-compatible field, but it is **not** duplicated in `to_acp_prompt_context()` because the conversation layer already applies it through `MessageEvent.to_llm_message()`.
+
+
+#### Compatible AgentContext Fields
+
+Each `AgentContext` field is tagged as ACP-compatible or not. At initialization, `validate_acp_compatibility()` rejects any context that uses unsupported fields.
+
+| Field | ACP Compatible | Notes |
+|-------|:-:|-------|
+| `skills` | ✅ | Skill catalog and trigger-based injections |
+| `system_message_suffix` | ✅ | Appended to the prompt context |
+| `user_message_suffix` | ✅ | Applied by the conversation layer |
+| `current_datetime` | ✅ | Included in the rendered prompt |
+| `load_user_skills` | ✅ | Load skills from `~/.openhands/skills/` |
+| `load_public_skills` | ✅ | Load skills from the public extensions repo |
+| `marketplace_path` | ✅ | Filter public skills via marketplace JSON |
+| `secrets` | ❌ | ACP subprocesses do not use OpenHands secret injection |
+
+Passing `secrets` (or any future field marked `acp_compatible: False`) raises `NotImplementedError`.
+
### What ACPAgent Does Not Support
-Because the ACP server manages its own tools and context, these `AgentBase` features are not available on `ACPAgent`:
+Because the ACP server manages its own tools, context window, and execution, these `AgentBase` features are not available on `ACPAgent`:
- `tools` / `include_default_tools` — the server has its own tools
- `mcp_config` — configure MCP on the server side
- `condenser` — the server manages its own context window
- `critic` — the server manages its own evaluation
-- `agent_context` — configure the server directly
Passing any of these raises `NotImplementedError` at initialization.
@@ -9208,6 +9260,15 @@ agent = ACPAgent(
| `acp_args` | Additional arguments appended to the command |
| `acp_env` | Additional environment variables for the server process |
+### Authentication
+
+When the ACP server advertises authentication methods, `ACPAgent` automatically selects a credential source:
+
+1. **ChatGPT subscription login** — If the server supports a `chatgpt` auth method and `~/.codex/auth.json` exists (created by `LLM.subscription_login()`), this is selected first. This enables ACP-backed workflows to use device-code login credentials without an explicit API key.
+2. **API key environment variables** — Falls back to checking for `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or `GEMINI_API_KEY` depending on which auth methods the server supports.
+
+If no supported credential source is found, the server may proceed without authentication (some servers don't require it).
+
## Metrics
Token usage and cost data are automatically captured from the ACP server's responses. You can inspect them through the standard `LLM.metrics` interface:
@@ -23746,6 +23807,57 @@ This guide shows how to implement skills in the SDK. For conceptual overview, se
OpenHands supports an **extended version** of the [AgentSkills standard](https://agentskills.io/specification) with optional keyword triggers.
+## Skill Injection Behavior
+
+Understanding where skill content appears in the prompt is critical. The behavior differs based on skill format and trigger configuration:
+
+| Skill Format | Trigger | Where Content Appears | Model Mediated? |
+|--------------|---------|----------------------|-----------------|
+| **AgentSkills** (`SKILL.md`) | Any | `` (description only) | ✅ Yes — agent calls `invoke_skill()` |
+| **AgentSkills** (`SKILL.md`) | Has triggers | `` + auto-inject on match | ✅ Yes |
+| **Legacy** (inline/`*.md`) | `None` | **`` (full content in the initial system prompt; included in LLM context for each turn)** | ❌ No |
+| **Legacy** (inline/`*.md`) | Has triggers | `` + auto-inject on match | ✅ Yes |
+
+
+**Token Usage Warning**: Legacy skills with `trigger=None` add their **full content** to `` in the initial `SystemPromptEvent`. That system message remains part of the conversation context for subsequent LLM calls, so the content still affects token usage on each turn. Consider using AgentSkills format (`SKILL.md`) for progressive disclosure instead.
+
+
+### Prompt Structure
+
+Skills appear in different parts of the system prompt:
+
+```xml icon="file"
+
+
+
+
+ [BEGIN context from [agents]]
+ ... AGENTS.md content ...
+ [END Context]
+
+
+
+
+
+
+ github
+ Interact with GitHub...
+
+
+
+```
+
+When a trigger matches, content is injected into the **user message**:
+
+```xml icon="file"
+
+The following information has been included based on a keyword match for "github".
+Skill location: /path/to/skill
+... skill content ...
+
+```
+
## Context Loading Methods
| Method | When Content Loads | Use Case |
@@ -23787,6 +23899,10 @@ agent_context = AgentContext(
)
```
+
+**Important**: Inline skills with `trigger=None` use **legacy format** behavior — full content is added to `` in the initial system prompt and remains part of the conversation context for subsequent LLM calls. For large skills, consider using the AgentSkills `SKILL.md` format for progressive disclosure.
+
+
## Trigger-Loaded Context
Content injected when keywords appear in user messages. See [Keyword-Triggered Skills](/overview/skills/keyword).
@@ -24573,9 +24689,15 @@ from openhands.sdk.context.skills import load_skills_from_dir
repo_skills, knowledge_skills, agent_skills = load_skills_from_dir(skills_dir)
```
-- **repo_skills**: Skills from `repo.md` files (always active)
-- **knowledge_skills**: Skills from `knowledge/` subdirectories
-- **agent_skills**: Skills from `SKILL.md` files (AgentSkills standard)
+| Return Value | Source Files | Injection Behavior |
+|--------------|--------------|-------------------|
+| **repo_skills** | `repo.md`, `AGENTS.md`, `.cursorrules` | Full content in `` in the initial system prompt; included in LLM context for each turn |
+| **knowledge_skills** | `knowledge/` subdirectories, `*.md` with triggers | Listed in ``, auto-inject on trigger |
+| **agent_skills** | `SKILL.md` files (AgentSkills standard) | Listed in ``, agent calls `invoke_skill()` |
+
+
+When passing to `AgentContext(skills=...)`, all three types are accepted. The injection behavior depends on the skill's `is_agentskills_format` flag and `trigger` field — see [Skill Injection Behavior](#skill-injection-behavior).
+
#### `discover_skill_resources()`
@@ -24902,6 +25024,57 @@ print(rendered) # Commands replaced with output
The `working_dir` parameter sets the current directory for command execution, enabling workspace-relative commands like `git status`.
+## Migrating from Legacy to AgentSkills Format
+
+If you have legacy inline skills consuming many tokens, convert them to AgentSkills format for progressive disclosure:
+
+### Before (Legacy Format)
+
+```python icon="python"
+# Legacy: Full content in in the initial system prompt
+Skill(
+ name="api-guidelines",
+ content="""
+ # API Guidelines
+ ... 2000 lines of detailed documentation ...
+ """,
+ trigger=None, # Always-on context - affects token usage on each turn!
+)
+```
+
+### After (AgentSkills Format)
+
+Create a directory `api-guidelines/SKILL.md`:
+
+```markdown icon="markdown"
+---
+name: api-guidelines
+description: Comprehensive API design guidelines for the project. Invoke when designing or reviewing API endpoints.
+---
+
+# API Guidelines
+
+... 2000 lines of detailed documentation ...
+```
+
+Then load it:
+
+```python icon="python"
+from openhands.sdk.skills import load_skills_from_dir
+
+# AgentSkills: Only description in prompt, agent reads full content on demand
+_, _, skills = load_skills_from_dir("/path/to/skills")
+agent_context = AgentContext(skills=list(skills.values()))
+```
+
+### Benefits
+
+| Aspect | Legacy `trigger=None` | AgentSkills `SKILL.md` |
+|--------|----------------------|------------------------|
+| Token usage | Full content in system prompt; included in LLM context for each turn | Description only (~100 chars) |
+| Model control | None — always present | Agent decides when to read |
+| Scalability | Limited by context window | Many skills without token bloat |
+
## Next Steps
- **[Custom Tools](/sdk/guides/custom-tools)** - Create specialized tools
@@ -24971,7 +25144,7 @@ sequenceDiagram
```python icon="python" focus={23-27}
from openhands.sdk import LLM, Agent, AgentContext
from openhands.sdk.context import Skill
- from openhands.tools.delegate import register_agent
+ from openhands.sdk.subagent import register_agent
def create_code_reviewer(llm: LLM) -> Agent:
return Agent(
@@ -25115,7 +25288,8 @@ from pydantic import SecretStr
from openhands.sdk import LLM, Agent, AgentContext, Conversation, Tool
from openhands.sdk.context import Skill
-from openhands.tools.delegate import DelegationVisualizer, register_agent
+from openhands.sdk.subagent import register_agent
+from openhands.tools.delegate import DelegationVisualizer
from openhands.tools.task import TaskToolSet
@@ -28445,6 +28619,214 @@ Once your automation is created:
- [Automations overview & examples](/openhands/usage/automations/overview)
- [Manage your automations](/openhands/usage/automations/managing-automations)
+### Event-Based Automations
+Source: https://docs.openhands.dev/openhands/usage/automations/event-automations.md
+
+
+**Beta Feature**: Event-based automations are in beta for **OpenHands Cloud** and **OpenHands Enterprise** users.
+
+
+Event-based automations run when something happens—a PR is opened, an issue is commented on, or a webhook fires—instead of on a schedule. This is ideal for responsive workflows like auto-reviewing PRs, triaging issues, or reacting to external service events.
+
+## Built-In vs. Custom Integrations
+
+| Type | Setup | Best For |
+|------|-------|----------|
+| **Built-in (GitHub)** | None—just create the automation | PR reviews, issue triage, push-triggered tasks |
+| **Custom Webhooks** | Register webhook first, then create automation | Linear, Stripe, Slack, and other services |
+
+## GitHub Events (Built-In)
+
+GitHub is a built-in integration. Create automations that respond to GitHub events without any webhook setup.
+
+### Example: Auto-Review PRs with a Specific Label
+
+When a PR is labeled with `openhands`, automatically review it:
+
+```
+Create an event-based automation called "Auto Review PRs" that triggers
+when a pull request is labeled with "openhands" in any of my repos.
+
+It should review the PR for code quality and best practices, then post
+the review as a comment.
+```
+
+The agent will create an automation with:
+- **Trigger type**: `event`
+- **Source**: `github`
+- **Event**: `pull_request.labeled`
+- **Filter**: Matches PRs labeled `openhands`
+
+### Example: Respond to @openhands Mentions
+
+```
+Create an automation that responds when someone mentions @openhands
+in an issue comment. It should analyze the issue context and provide
+a helpful response.
+```
+
+### Available GitHub Events
+
+| Event | Common Actions | Use Case |
+|-------|---------------|----------|
+| `pull_request` | `opened`, `labeled`, `synchronize`, `ready_for_review` | PR automation |
+| `issues` | `opened`, `labeled`, `assigned` | Issue triage |
+| `issue_comment` | `created` | Mention responses |
+| `push` | — | Branch-based triggers |
+| `release` | `published` | Release workflows |
+
+Use wildcards like `pull_request.*` to match all actions for an event type.
+
+### Filtering Events
+
+Filters let you narrow which events trigger your automation. They use [JMESPath expressions](https://jmespath.org/) to match fields in the event payload—so you can trigger only on specific labels, users, branches, or other conditions.
+
+**Common filter patterns:**
+
+```javascript
+// Match a specific label
+contains(pull_request.labels[].name, 'openhands')
+
+// Case-insensitive mention in comment
+icontains(comment.body, '@openhands')
+
+// Match repos in your org
+glob(repository.full_name, 'myorg/*')
+
+// Push to main branch only
+ref == 'refs/heads/main'
+
+// Combine conditions
+glob(repository.full_name, 'myorg/*') && contains(pull_request.labels[].name, 'bug')
+```
+
+---
+
+## Custom Webhooks
+
+For services beyond GitHub—like Linear, Stripe, or Slack—register a custom webhook first, then create automations that use it.
+
+
+**Two-phase workflow for custom webhooks:**
+
+1. **Webhook registration (one-time setup)**: You execute the curl command yourself to register the webhook. This keeps your signing secrets secure—the agent provides the command but never handles your credentials directly.
+
+2. **Automation creation (repeatable)**: Once the webhook is registered, the agent can create, update, and manage automations for that webhook source conversationally—no manual curl commands needed.
+
+
+### Walkthrough: Linear Integration
+
+This example walks through setting up a Linear webhook to auto-triage new issues.
+
+#### Step 1: Get Your Webhook Secret from Linear
+
+Linear provides the webhook signing secret—you cannot configure your own.
+
+1. Go to **Linear Settings → API → Webhooks**
+2. Click **New webhook**
+3. Copy the **signing secret** that Linear displays (you'll need this in the next step)
+4. Leave the webhook URL blank for now—you'll get it from OpenHands
+
+#### Step 2: Register the Webhook with OpenHands
+
+First, set up your environment variables:
+
+1. Create an OpenHands API key at [app.all-hands.dev/settings/api-keys](https://app.all-hands.dev/settings/api-keys)
+2. Export the API key and the webhook secret from Step 1:
+
+```bash
+export OPENHANDS_API_KEY="your-openhands-api-key"
+export LINEAR_WEBHOOK_SECRET="your-linear-signing-secret-from-step-1"
+```
+
+Then run the following command to register the webhook:
+
+```bash
+curl -X POST "https://app.all-hands.dev/api/automation/v1/webhooks" \
+ -H "Authorization: Bearer ${OPENHANDS_API_KEY}" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "name": "Linear Issues",
+ "source": "linear",
+ "event_key_expr": "type",
+ "signature_header": "Linear-Signature",
+ "webhook_secret": "'"${LINEAR_WEBHOOK_SECRET}"'"
+ }'
+```
+
+The response includes a `webhook_url` that you'll configure in Linear.
+
+
+The `event_key_expr` is a JMESPath expression that extracts the event type from incoming webhook payloads. This extracted value is what you match against in the automation's `on` field.
+
+For example, Linear sends payloads like:
+```json
+{"type": "Issue", "action": "create", "data": {...}}
+```
+
+With `event_key_expr: "type"`, the system extracts `"Issue"` as the event type. Then in your automation, you set `on: "Issue"` to match it.
+
+
+
+If you're integrating a service that lets you configure the signing secret (unlike Linear), you can omit `webhook_secret` from the request. The automation service will generate one and return it in the response—store it securely, as it's shown only once.
+
+
+#### Step 3: Complete the Linear Webhook Configuration
+
+1. Return to the Linear webhook you started in Step 1
+2. Paste the `webhook_url` from the previous step
+3. Select which events to send (e.g., Issues, Comments)
+4. Save the webhook
+
+#### Step 4: Create the Automation
+
+Now the webhook is registered, the agent can create automations for you end-to-end. Just describe what you want:
+
+```
+Create an event-based automation called "Triage Linear Issues" that triggers
+when a new issue is created in Linear.
+
+It should analyze the issue title and description, suggest appropriate labels,
+and add a comment with initial triage notes.
+```
+
+The agent creates the automation with:
+- **Source**: `linear` (your registered webhook)
+- **Event**: `Issue` (Linear's event type)
+- **Filter**: `action == 'create'`
+
+### Custom Webhook Parameters
+
+When registering any custom webhook, these parameters define how OpenHands processes incoming events:
+
+| Parameter | Required | Description |
+|-----------|----------|-------------|
+| `name` | Yes | Human-readable name |
+| `source` | Yes | Unique identifier (lowercase, alphanumeric with hyphens) |
+| `event_key_expr` | No | JMESPath to extract event type (default: `type`) |
+| `signature_header` | No | Header containing HMAC signature (default: `X-Signature-256`) |
+| `webhook_secret` | No | Signing secret—provide yours or let the system generate one |
+
+### Common Services
+
+These are example configurations for popular services. **Always verify with each service's webhook documentation**, as signature headers and payload formats may change.
+
+| Service | Signature Header | Event Key |
+|---------|-----------------|-----------|
+| Linear | `Linear-Signature` | `type` |
+| Stripe | `Stripe-Signature` | `type` |
+| Slack | `X-Slack-Signature` | `type` |
+| Twilio | `X-Twilio-Signature` | `type` |
+
+---
+
+## Next Steps
+
+New to automations? Start with the [Automations Overview](/openhands/usage/automations/overview) for the bigger picture, including cron-based scheduling and general concepts.
+
+- [Automations Overview](/openhands/usage/automations/overview) — Cron-based automations and general concepts
+- [Managing Automations](/openhands/usage/automations/managing-automations) — Update, disable, or delete automations
+
### Managing Automations
Source: https://docs.openhands.dev/openhands/usage/automations/managing-automations.md
@@ -34144,7 +34526,13 @@ The PR review workflow uses the OpenHands Software Agent SDK to analyze your cod
## Composite Action
-The workflow uses a reusable composite action from the Software Agent SDK that handles all the setup automatically:
+
+**Action Path Updated:** The PR review action has moved to the extensions repository. If your workflow still references the old path, update it:
+- **Old:** `OpenHands/software-agent-sdk/.github/actions/pr-review@main`
+- **New:** `OpenHands/extensions/plugins/pr-review@main`
+
+
+The workflow uses a reusable composite action that handles all the setup automatically:
- Checking out the extensions repository at the specified version
- Setting up Python and dependencies
@@ -34155,18 +34543,115 @@ The workflow uses a reusable composite action from the Software Agent SDK that h
| Input | Description | Required | Default |
|-------|-------------|----------|---------|
-| `llm-model` | LLM model to use | Yes | - |
+| `agent-kind` | Review backend: `openhands` for the standard SDK agent or `acp` for an ACP-compatible agent server | No | `openhands` |
+| `llm-model` | LLM model(s). Comma-separated to run multiple reviews and compare results (A/B testing). In ACP mode this is passed to the ACP server when supported. | No | `anthropic/claude-sonnet-4-5-20250929` |
+| `acp-command` | Command used to start the ACP server. Required when `agent-kind` is `acp`. Examples: `npx -y @zed-industries/codex-acp@0.12.0`, `codex-acp`, `claude-agent-acp`, `npx -y @agentclientprotocol/claude-agent-acp` | Yes for ACP mode | `''` |
+| `acp-prompt-timeout` | Timeout in seconds for one ACP prompt turn | No | `1800` |
| `llm-base-url` | LLM base URL (for custom endpoints) | No | `''` |
| `review-style` | **[DEPRECATED]** Previously chose between `standard` and `roasted`. Now ignored — the styles have been merged. | No | `roasted` |
-| `extensions-version` | Git ref for extensions (tag, branch, or commit SHA) | No | `main` |
+| `require-evidence` | Require the reviewer to enforce an `Evidence` section in the PR description with end-to-end proof | No | `'false'` |
+| `use-sub-agents` | Enable sub-agent delegation for file-level reviews in `openhands` mode. Ignored in ACP mode. | No | `'false'` |
| `extensions-repo` | Extensions repository (owner/repo) | No | `OpenHands/extensions` |
-| `llm-api-key` | LLM API key | Yes | - |
+| `extensions-version` | Git ref for extensions (tag, branch, or commit SHA) | No | `main` |
+| `openhands-sdk-package` | Package spec passed to `uv --with`; override only when pinning a specific SDK build for testing or rollout control | No | `openhands-sdk` |
+| `llm-api-key` | LLM API key. Required when `agent-kind` is `openhands`; ignored in ACP mode. | Yes for OpenHands mode | - |
| `github-token` | GitHub token for API access | Yes | - |
+| `lmnr-api-key` | Laminar API key for observability | No | `''` |
+| `enable-uv-cache` | Enable setup-uv's GitHub Actions cache for Python deps. Default `false` for security. | No | `'false'` |
Use `extensions-version` to pin to a specific version tag (e.g., `v1.0.0`) for production stability, or use `main` to always get the latest features. The extensions repository contains the PR review plugin scripts.
+## Experimental: ACP Review Backend
+
+The PR review action can run through an ACP-compatible agent server by setting
+`agent-kind: acp`. In this mode, OpenHands still loads the review skills
+and plugin prompt context, but the ACP server owns model access,
+authentication, and tool execution.
+
+Use ACP mode when your runner already has an authenticated ACP CLI available.
+The action does not install ACP CLIs for you; install and authenticate the ACP
+server in workflow steps before invoking the PR review action.
+
+
+ACP mode is experimental. Use it on trusted self-hosted runners where you
+control the installed ACP command and the authentication material. Do not expose
+subscription credentials to workflows that run untrusted pull request code.
+
+
+### Codex ACP Example
+
+To use Codex ACP, first install the Codex CLI and complete device-code login on
+a trusted machine:
+
+```bash
+codex login --device-auth
+codex login status
+```
+
+Then create a base64-encoded secret from the generated auth file:
+
+```bash
+# Linux
+base64 -w 0 "$HOME/.codex/auth.json"
+
+# macOS
+base64 < "$HOME/.codex/auth.json" | tr -d '\n'
+```
+
+Store the printed value as a repository or organization secret named
+`CODEX_AUTH_JSON_B64`. The workflow can then restore that file on a
+self-hosted runner, start Codex ACP with `npx`, and run the review:
+
+```yaml
+name: PR Review by OpenHands
+
+on:
+ pull_request:
+ types: [labeled, review_requested]
+
+permissions:
+ contents: read
+ pull-requests: write
+ issues: write
+
+jobs:
+ pr-review:
+ if: |
+ github.event.label.name == 'review-this' ||
+ github.event.requested_reviewer.login == 'openhands-agent'
+ runs-on: [self-hosted]
+ timeout-minutes: 30
+ steps:
+ - name: Restore Codex auth
+ env:
+ CODEX_AUTH_JSON_B64: ${{ secrets.CODEX_AUTH_JSON_B64 }}
+ run: |
+ if [ -z "$CODEX_AUTH_JSON_B64" ]; then
+ echo "Error: CODEX_AUTH_JSON_B64 is required for Codex ACP review."
+ exit 1
+ fi
+ mkdir -p "$HOME/.codex"
+ if ! printf '%s' "$CODEX_AUTH_JSON_B64" | base64 -d > "$HOME/.codex/auth.json"; then
+ echo "Error: Failed to decode CODEX_AUTH_JSON_B64 — check the base64 value."
+ exit 1
+ fi
+ chmod 600 "$HOME/.codex/auth.json"
+
+ - name: Run PR Review
+ uses: OpenHands/extensions/plugins/pr-review@main
+ with:
+ agent-kind: acp
+ acp-command: npx -y @zed-industries/codex-acp@0.12.0
+ llm-model: o3
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Cleanup Codex auth
+ if: always()
+ run: rm -f "$HOME/.codex/auth.json"
+```
+
## Customization
### Repository-Specific Review Guidelines
@@ -38895,80 +39380,11 @@ configure a GitHub App.
#### Create a GitHub App
-1. Go to [github.com/settings/apps](https://github.com/settings/apps) and click **New GitHub App**.
-
-2. Set a unique **GitHub App name** (e.g., `ACME Corp OpenHands`).
-
-3. Set the **Homepage URL** to `https://app.`.
-
-4. Under **Identifying and authorizing users**:
- - Set the **Callback URL** to:
- ```
- https://auth.app./realms/allhands/broker/github/endpoint
- ```
- - Check the box for **Request user authorization (OAuth) during installation**
-
-5. Under **Webhook**:
- - Set the **Webhook URL** to:
- ```
- https://app./integration/github/events
- ```
- - Generate a webhook secret:
- ```bash
- export WEBHOOK_SECRET=$(openssl rand -base64 32 | tr -dc A-Za-z0-9 | head -c 32)
- echo $WEBHOOK_SECRET
- ```
- - Paste the generated value into the **Secret** field
-
-
- Save the webhook secret value -- you will need to enter it in the Admin Console configuration.
-
-
-6. Under **Permissions**, configure the following:
-
- **Repository permissions:**
-
- | Permission | Access |
- |-----------|--------|
- | Actions | Read and write |
- | Commit statuses | Read and write |
- | Contents | Read and write |
- | Issues | Read and write |
- | Pull requests | Read and write |
- | Webhooks | Read and write |
- | Workflows | Read and write |
-
- **Organization permissions:**
-
- | Permission | Access |
- |-----------|--------|
- | Events | Read-only |
-
- **Account permissions:**
-
- | Permission | Access |
- |-----------|--------|
- | Email addresses | Read-only |
-
-7. Click **Create GitHub App**.
-
-8. On the GitHub App page, under **Client secrets**, click **Generate a new client secret**.
- Save this value.
-
-9. Under **Private keys**, click **Generate a private key**. The `.pem` file downloads
- automatically -- note its location.
+Run our [script](https://github.com/All-Hands-AI/OpenHands-Cloud/tree/main/scripts/create_github_app) to create a GitHub App configured for your install.
#### Map GitHub App values to Admin Console
-Go back to the Installer Admin Console in your browser and enter the following values:
-
-| GitHub App Value | Admin Console Field |
-|-----------------|-------------------|
-| Client ID (shown on app page) | GitHub OAuth Client ID |
-| Client secret (from step 8) | GitHub OAuth Client Secret |
-| App ID (shown on app page) | GitHub App ID |
-| Webhook secret (from step 5) | GitHub App Webhook Secret |
-| Private key file (from step 9) | GitHub App Private Key (file upload) |
+Go back to the Installer Admin Console in your browser and enter the values from the Create GitHub App script output. For the private key, upload the file from the `keys` directory of the script location.
After filling in all fields, click **Continue** at the bottom of the page.
diff --git a/llms.txt b/llms.txt
index 0dc30574..1a94acc6 100644
--- a/llms.txt
+++ b/llms.txt
@@ -126,6 +126,7 @@ from the OpenHands Software Agent SDK.
- [Docker Sandbox](https://docs.openhands.dev/openhands/usage/sandboxes/docker.md): The recommended sandbox provider for running OpenHands locally.
- [Environment Variables Reference](https://docs.openhands.dev/openhands/usage/environment-variables.md): Complete reference of all environment variables supported by OpenHands
- [Evaluation Harness](https://docs.openhands.dev/openhands/usage/developers/evaluation-harness.md)
+- [Event-Based Automations](https://docs.openhands.dev/openhands/usage/automations/event-automations.md): Trigger automations from GitHub events or custom webhooks instead of cron schedules.
- [Good vs. Bad Instructions](https://docs.openhands.dev/openhands/usage/essential-guidelines/good-vs-bad-instructions.md): Learn how to write effective instructions for OpenHands
- [Google Gemini/Vertex](https://docs.openhands.dev/openhands/usage/llms/google-llms.md): OpenHands uses LiteLLM to make calls to Google's chat models. You can find their documentation on using Google as a provider -> [Gemini - Google AI Studio](https://docs.litellm.ai/docs/providers/gemini), [VertexAI - Google Cloud Platform](https://docs.litellm.ai/docs/providers/vertex)
- [Groq](https://docs.openhands.dev/openhands/usage/llms/groq.md): OpenHands uses LiteLLM to make calls to chat models on Groq. You can find their documentation on using Groq as a provider [here](https://docs.litellm.ai/docs/providers/groq).
diff --git a/openhands/usage/use-cases/code-review.mdx b/openhands/usage/use-cases/code-review.mdx
index 774b91e3..5251a977 100644
--- a/openhands/usage/use-cases/code-review.mdx
+++ b/openhands/usage/use-cases/code-review.mdx
@@ -103,7 +103,13 @@ The PR review workflow uses the OpenHands Software Agent SDK to analyze your cod
## Composite Action
-The workflow uses a reusable composite action from the Software Agent SDK that handles all the setup automatically:
+
+**Action Path Updated:** The PR review action has moved to the extensions repository. If your workflow still references the old path, update it:
+- **Old:** `OpenHands/software-agent-sdk/.github/actions/pr-review@main`
+- **New:** `OpenHands/extensions/plugins/pr-review@main`
+
+
+The workflow uses a reusable composite action that handles all the setup automatically:
- Checking out the extensions repository at the specified version
- Setting up Python and dependencies
@@ -114,18 +120,115 @@ The workflow uses a reusable composite action from the Software Agent SDK that h
| Input | Description | Required | Default |
|-------|-------------|----------|---------|
-| `llm-model` | LLM model to use | Yes | - |
+| `agent-kind` | Review backend: `openhands` for the standard SDK agent or `acp` for an ACP-compatible agent server | No | `openhands` |
+| `llm-model` | LLM model(s). Comma-separated to run multiple reviews and compare results (A/B testing). In ACP mode this is passed to the ACP server when supported. | No | `anthropic/claude-sonnet-4-5-20250929` |
+| `acp-command` | Command used to start the ACP server. Required when `agent-kind` is `acp`. Examples: `npx -y @zed-industries/codex-acp@0.12.0`, `codex-acp`, `claude-agent-acp`, `npx -y @agentclientprotocol/claude-agent-acp` | Yes for ACP mode | `''` |
+| `acp-prompt-timeout` | Timeout in seconds for one ACP prompt turn | No | `1800` |
| `llm-base-url` | LLM base URL (for custom endpoints) | No | `''` |
| `review-style` | **[DEPRECATED]** Previously chose between `standard` and `roasted`. Now ignored — the styles have been merged. | No | `roasted` |
-| `extensions-version` | Git ref for extensions (tag, branch, or commit SHA) | No | `main` |
+| `require-evidence` | Require the reviewer to enforce an `Evidence` section in the PR description with end-to-end proof | No | `'false'` |
+| `use-sub-agents` | Enable sub-agent delegation for file-level reviews in `openhands` mode. Ignored in ACP mode. | No | `'false'` |
| `extensions-repo` | Extensions repository (owner/repo) | No | `OpenHands/extensions` |
-| `llm-api-key` | LLM API key | Yes | - |
+| `extensions-version` | Git ref for extensions (tag, branch, or commit SHA) | No | `main` |
+| `openhands-sdk-package` | Package spec passed to `uv --with`; override only when pinning a specific SDK build for testing or rollout control | No | `openhands-sdk` |
+| `llm-api-key` | LLM API key. Required when `agent-kind` is `openhands`; ignored in ACP mode. | Yes for OpenHands mode | - |
| `github-token` | GitHub token for API access | Yes | - |
+| `lmnr-api-key` | Laminar API key for observability | No | `''` |
+| `enable-uv-cache` | Enable setup-uv's GitHub Actions cache for Python deps. Default `false` for security. | No | `'false'` |
Use `extensions-version` to pin to a specific version tag (e.g., `v1.0.0`) for production stability, or use `main` to always get the latest features. The extensions repository contains the PR review plugin scripts.
+## Experimental: ACP Review Backend
+
+The PR review action can run through an ACP-compatible agent server by setting
+`agent-kind: acp`. In this mode, OpenHands still loads the review skills
+and plugin prompt context, but the ACP server owns model access,
+authentication, and tool execution.
+
+Use ACP mode when your runner already has an authenticated ACP CLI available.
+The action does not install ACP CLIs for you; install and authenticate the ACP
+server in workflow steps before invoking the PR review action.
+
+
+ACP mode is experimental. Use it on trusted self-hosted runners where you
+control the installed ACP command and the authentication material. Do not expose
+subscription credentials to workflows that run untrusted pull request code.
+
+
+### Codex ACP Example
+
+To use Codex ACP, first install the Codex CLI and complete device-code login on
+a trusted machine:
+
+```bash
+codex login --device-auth
+codex login status
+```
+
+Then create a base64-encoded secret from the generated auth file:
+
+```bash
+# Linux
+base64 -w 0 "$HOME/.codex/auth.json"
+
+# macOS
+base64 < "$HOME/.codex/auth.json" | tr -d '\n'
+```
+
+Store the printed value as a repository or organization secret named
+`CODEX_AUTH_JSON_B64`. The workflow can then restore that file on a
+self-hosted runner, start Codex ACP with `npx`, and run the review:
+
+```yaml
+name: PR Review by OpenHands
+
+on:
+ pull_request:
+ types: [labeled, review_requested]
+
+permissions:
+ contents: read
+ pull-requests: write
+ issues: write
+
+jobs:
+ pr-review:
+ if: |
+ github.event.label.name == 'review-this' ||
+ github.event.requested_reviewer.login == 'openhands-agent'
+ runs-on: [self-hosted]
+ timeout-minutes: 30
+ steps:
+ - name: Restore Codex auth
+ env:
+ CODEX_AUTH_JSON_B64: ${{ secrets.CODEX_AUTH_JSON_B64 }}
+ run: |
+ if [ -z "$CODEX_AUTH_JSON_B64" ]; then
+ echo "Error: CODEX_AUTH_JSON_B64 is required for Codex ACP review."
+ exit 1
+ fi
+ mkdir -p "$HOME/.codex"
+ if ! printf '%s' "$CODEX_AUTH_JSON_B64" | base64 -d > "$HOME/.codex/auth.json"; then
+ echo "Error: Failed to decode CODEX_AUTH_JSON_B64 — check the base64 value."
+ exit 1
+ fi
+ chmod 600 "$HOME/.codex/auth.json"
+
+ - name: Run PR Review
+ uses: OpenHands/extensions/plugins/pr-review@main
+ with:
+ agent-kind: acp
+ acp-command: npx -y @zed-industries/codex-acp@0.12.0
+ llm-model: o3
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Cleanup Codex auth
+ if: always()
+ run: rm -f "$HOME/.codex/auth.json"
+```
+
## Customization
### Repository-Specific Review Guidelines