Open SWE is designed to be forked and customized for your org. The core agent is assembled in a single function — `get_agent()` in `agent/server.py` — where you can swap out the sandbox, model, tools, and triggers.
By default, Open SWE runs each task in a [LangSmith cloud sandbox](https://docs.smith.langchain.com/) — an isolated Linux environment where the agent clones the repo and executes commands. Sandbox creation and connection is handled in `agent/integrations/langsmith.py`.
Set the `SANDBOX_TYPE` environment variable to switch providers. Each provider has a corresponding integration file in `agent/integrations/` and a factory function registered in `agent/utils/sandbox.py`:
If none of the built-in providers fit, you can build your own. The agent accepts any backend that implements `SandboxBackendProtocol` from `deepagents`. The protocol requires:
- **Identity**: `id` property returning a unique sandbox identifier
The easiest approach is to extend `BaseSandbox` from `deepagents.backends.sandbox` — it implements all file operations by delegating to `execute()`, so you only need to implement the shell execution layer:
```python
from deepagents.backends.sandbox import BaseSandbox
from deepagents.backends.protocol import ExecuteResponse
The model is configured in the `get_agent()` function in `agent/server.py`. By default it uses `openai:gpt-5.5` with medium reasoning effort, but you can override the model with the `LLM_MODEL_ID` environment variable:
If `LLM_MODEL_ID` is not set, the default model (`openai:gpt-5.5`) is used.
`max_tokens` is a maximum completion/output token budget, not the model's total context window. For OpenAI reasoning models, this budget can include both internal reasoning tokens and final response tokens.
The `make_model()` helper in `agent/utils/model.py` wraps `langchain.chat_models.init_chat_model`. For OpenAI models, it automatically enables the Responses API. For full control, pass a pre-configured model instance directly:
```python
from langchain_anthropic import ChatAnthropic
model = ChatAnthropic(model_name="claude-sonnet-4-6", temperature=0, max_tokens=16_000)
return create_deep_agent(
model=model,
...
)
```
### Using different models per context
You can route to different models based on task complexity, repo, or trigger source:
time_range: Time range to search (e.g. "1h", "24h", "7d")
Returns:
Dictionary with matching log entries
"""
# Your Datadog API integration here
...
```
Then register it in `agent/server.py`:
```python
from .tools import commit_and_open_pr, fetch_url, http_request, linear_comment, slack_thread_reply
from .tools.datadog_search import datadog_search
return create_deep_agent(
...
tools=[
http_request, fetch_url, commit_and_open_pr,
linear_comment, slack_thread_reply,
datadog_search, # new tool
],
...
)
```
The agent will automatically see the tool's name, docstring, and parameter types — the docstring serves as the tool description, so write it clearly.
### Removing tools
If you only use Linear (not Slack), remove `slack_thread_reply` from the tools list and vice versa. If you don't need web fetching, remove `fetch_url`. The only tool that's essential to the core workflow is `commit_and_open_pr`.
### Conditional tools
You can vary the toolset based on the trigger source:
Open SWE supports three invocation surfaces: Linear, Slack, and GitHub. Each is implemented as a webhook endpoint in `agent/webapp.py`. You can add, remove, or modify triggers independently.
### Removing a trigger
If you don't use Linear, simply don't configure the Linear webhook and remove the env vars. Same for Slack. The webhook endpoints still exist but won't receive events.
To fully remove a trigger's code, delete the corresponding endpoint from `agent/webapp.py`:
- **Linear**: `linear_webhook()` and `process_linear_issue()`
- **Slack**: `slack_webhook()` and `process_slack_mention()`
- A Slack message doesn't specify a repo (and no thread metadata exists)
- A Linear issue's team/project isn't in the `LINEAR_TEAM_TO_REPO` mapping
- A user writes `repo:name` without an org prefix — the org defaults to `DEFAULT_REPO_OWNER`
### Repository extraction from messages
Both Slack and Linear support specifying a target repo directly in the message or comment text. The shared utility `extract_repo_from_text()` in `agent/utils/repo.py` handles parsing these formats:
-`repo:owner/name` — explicit org and repo
-`repo owner/name` — space syntax (same result)
-`repo:name` — repo name only; the org defaults to `DEFAULT_REPO_OWNER`
Users can also override the team/project mapping on a per-comment basis by including `repo:owner/name` in their `@openswe` comment. This takes priority over the mapping — the mapping is used as a fallback when no repo is specified in the comment. If the team/project isn't found in the mapping either, `DEFAULT_REPO_OWNER`/`DEFAULT_REPO_NAME` is used.
Users can override per-message with `repo:owner/name` syntax in their Slack message. A shorthand `repo:name` (without the org) is also supported — the org defaults to `DEFAULT_REPO_OWNER`.
Open SWE supports a `default_prompt.md` file for org-level instructions that apply to **every** agent run, regardless of which repository is being worked on. This is the recommended way to set default repository preferences, org conventions, and shared guidelines.
The file is loaded at agent startup and injected into the system prompt between the task overview and repository setup sections.
**Location:** [`default_prompt.md`](./default_prompt.md) in the project root.
**Override:** Set the `DEFAULT_PROMPT_PATH` environment variable to use a different file:
```bash
DEFAULT_PROMPT_PATH="/path/to/my-org-prompt.md"
```
**Format:** Write plain markdown. The content is injected as-is under a `### Custom Instructions` heading in the system prompt. Example:
```markdown
# Default Prompt
## Default Repository
When no repository is specified, work on the **my-app** repository under **my-org**.
## Organization Conventions
- Use conventional commits: feat:, fix:, chore:
- Always tag the requesting user when work is complete
```
**Loading order:** Default prompt → System prompt sections → AGENTS.md (per-repo). If the file is missing or empty, it is silently skipped — no error is raised.
**When to use `default_prompt.md` vs `AGENTS.md`:**
| | `default_prompt.md` | `AGENTS.md` |
|---|---|---|
| Scope | All tasks, all repos | Single repository |
| Location | Open SWE project root | Target repo root |
| Use for | Default repo, org conventions | Repo-specific coding standards |
Drop an `AGENTS.md` file in the root of any repository to add repo-specific instructions. The agent reads it from the sandbox at startup and appends it to the system prompt. This is the easiest way to encode conventions per-repo without modifying Open SWE's code.
---
## 6. Middleware
Middleware hooks run around the agent loop. Open SWE includes four:
| `check_message_queue_before_model` | Before model | Injects follow-up messages that arrived mid-run |
| `ensure_no_empty_msg` | Before model | Prevents empty messages from reaching the model |
| `open_pr_if_needed` | After agent | Safety net — opens a PR if the agent didn't |
Add custom middleware by appending to the middleware list in `get_agent()`. See the [LangChain middleware docs](https://python.langchain.com/docs/concepts/agents/#middleware) for the `@before_model` and `@after_agent` decorators.
**Example — adding a CI check after agent completion:**
```python
from langchain.agents.middleware import AgentState, after_agent