mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-10-07 16:19:09 +00:00
Compare commits
1 commit
69ccfce0f4
...
168843926d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
168843926d |
14 changed files with 45 additions and 231 deletions
123
.env.example
123
.env.example
|
|
@ -1,123 +0,0 @@
|
|||
# === LangSmith ===
|
||||
LANGSMITH_API_KEY_PROD="" # From step 4a
|
||||
# NOTE: LANGCHAIN_TRACING_V2 and LANGCHAIN_PROJECT are RESERVED on LangGraph Platform
|
||||
# (LangSmith deployments) — the platform sets them itself and rejects the deploy if
|
||||
# you provide them. Only set them for local/self-hosted runs, not in a deployment's env.
|
||||
LANGSMITH_TENANT_ID_PROD=""
|
||||
LANGSMITH_TRACING_PROJECT_ID_PROD="" # Fallback project ID for "View trace" links; graphs trace into the open-swe-agent / open-swe-review projects by name
|
||||
LANGSMITH_URL_PROD="https://smith.langchain.com"
|
||||
|
||||
# === LLM ===
|
||||
LLM_MODEL_ID="" # Default model, e.g. "bedrock_converse:us.anthropic.claude-opus-4-8"
|
||||
ANTHROPIC_API_KEY="" # Anthropic API key
|
||||
OPENAI_API_KEY="" # OpenAI API key (when using openai: models)
|
||||
GOOGLE_API_KEY="" # Google AI API key (when using google_genai: models)
|
||||
FIREWORKS_API_KEY="" # Fireworks API key (when using fireworks: models)
|
||||
# AWS credentials for Bedrock (when using bedrock_converse: models). Region defaults to us-east-1.
|
||||
AWS_ACCESS_KEY_ID=""
|
||||
AWS_SECRET_ACCESS_KEY=""
|
||||
|
||||
# === GitHub App (required) ===
|
||||
GITHUB_APP_ID="" # From step 3c
|
||||
GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
|
||||
...
|
||||
-----END RSA PRIVATE KEY-----
|
||||
"
|
||||
GITHUB_APP_INSTALLATION_ID="" # From step 3d
|
||||
|
||||
# === GitHub Webhook (required) ===
|
||||
GITHUB_WEBHOOK_SECRET="" # The secret you generated in step 3b
|
||||
|
||||
# === Dashboard GitHub OAuth (required for the dashboard) ===
|
||||
# Direct GitHub OAuth used by the dashboard login flow (not via LangSmith).
|
||||
GITHUB_APP_CLIENT_ID="" # From step 3c
|
||||
GITHUB_APP_CLIENT_SECRET="" # From step 3c
|
||||
|
||||
# === Agent-runtime GitHub OAuth via LangSmith (optional) ===
|
||||
# Without these, all agent operations use the GitHub App's bot token.
|
||||
# With these, each agent run authenticates as the triggering user.
|
||||
GITHUB_OAUTH_PROVIDER_ID="" # The provider ID from steps 3a / 4b
|
||||
# Secret used to mint short-lived service JWTs that ask LangSmith to resolve a
|
||||
# specific user's GitHub token. Needed for per-user token resolution in deployed mode.
|
||||
X_SERVICE_AUTH_JWT_SECRET=""
|
||||
|
||||
# === Repo Allowlist (optional) ===
|
||||
# Comma-separated list of GitHub orgs the agent is allowed to operate on.
|
||||
# Also gates dashboard login to members of these orgs (requires the GitHub App's
|
||||
# Organization -> Members: Read-only permission; without it, all dashboard logins are rejected).
|
||||
# Leave empty to allow all orgs.
|
||||
ALLOWED_GITHUB_ORGS="" # e.g. "my-org,my-other-org"
|
||||
# Comma-separated list of specific owner/repo pairs the agent is allowed to operate on.
|
||||
# For GitHub/Linear webhooks, a repo is allowed if its org is in ALLOWED_GITHUB_ORGS OR its owner/repo is in ALLOWED_GITHUB_REPOS.
|
||||
# Slack mentions are not rejected from regex-inferred repository text; repository access is bounded by GitHub App installation permissions.
|
||||
# Leave both empty to allow all repos.
|
||||
ALLOWED_GITHUB_REPOS="" # e.g. "some-user/their-repo,another-org/specific-repo"
|
||||
|
||||
# === Default Repository ===
|
||||
# Used across all triggers when no repo is specified.
|
||||
DEFAULT_REPO_OWNER="" # Default GitHub org (e.g. "my-org")
|
||||
DEFAULT_REPO_NAME="" # Default GitHub repo (e.g. "my-repo")
|
||||
|
||||
# === Dashboard (required to run the web dashboard) ===
|
||||
# Public URL that browsers use for /dashboard/api/* and OAuth callbacks.
|
||||
# Use the FastAPI backend URL for local/cross-origin direct API calls.
|
||||
# Use the dashboard frontend URL when a same-origin frontend rewrite proxies /dashboard/api/*.
|
||||
# Its scheme drives cookie security: http:// => SameSite=Lax (local);
|
||||
# https:// => Secure + SameSite=None (production).
|
||||
DASHBOARD_API_BASE_URL="http://localhost:2024"
|
||||
# Public base URL of the dashboard frontend (the ui/ app). Default post-login redirect.
|
||||
DASHBOARD_BASE_URL="http://localhost:3000"
|
||||
# HMAC secret for dashboard JWTs (session cookie and OAuth state).
|
||||
DASHBOARD_JWT_SECRET="" # Generate with: openssl rand -hex 32
|
||||
# Comma-separated origins allowed for credentialed CORS and post-login redirects.
|
||||
# Required whenever the frontend and API are on different origins — including local
|
||||
# dev (UI :3000 -> API :2024 is cross-origin). CORS is only enabled when this is set.
|
||||
DASHBOARD_ALLOWED_ORIGINS="http://localhost:3000" # prod: your frontend origin(s)
|
||||
# Comma-separated GitHub login or email allowlist for admin dashboard endpoints.
|
||||
# Empty => nobody is an admin.
|
||||
CONFIGURED_ADMINS="" # e.g. "alice,bob@my-org.com"
|
||||
# URL of the LangGraph server the FastAPI side calls to trigger/stream runs.
|
||||
# Defaults to http://localhost:2024 locally; set to your deployment URL in prod.
|
||||
LANGGRAPH_URL="http://localhost:2024"
|
||||
|
||||
# === Linear (if using Linear trigger) ===
|
||||
LINEAR_API_KEY="" # From step 5
|
||||
LINEAR_WEBHOOK_SECRET="" # From step 5
|
||||
|
||||
# === Slack (if using Slack trigger) ===
|
||||
SLACK_BOT_TOKEN="" # From step 5
|
||||
SLACK_BOT_USER_ID=""
|
||||
SLACK_BOT_USERNAME=""
|
||||
SLACK_SIGNING_SECRET=""
|
||||
# Optional: Slack-specific default repo (falls back to DEFAULT_REPO_OWNER/NAME).
|
||||
SLACK_REPO_OWNER=""
|
||||
SLACK_REPO_NAME=""
|
||||
# Optional: "Sign in with Slack" account linking (GitHub <-> Slack). See step 5.
|
||||
SLACK_CLIENT_ID=""
|
||||
SLACK_CLIENT_SECRET=""
|
||||
SLACK_TEAM_ID="" # Optional; restrict linking to one workspace (T...)
|
||||
|
||||
# === Exa (optional — enables web search tool) ===
|
||||
EXA_API_KEY="" # From https://dashboard.exa.ai
|
||||
|
||||
# === Reviewer / Analyzer (optional) ===
|
||||
# LangSmith dataset where reviewer finding outcomes are recorded and read back by
|
||||
# the analyzer. Defaults to "openswe-reviewer-outcomes" if unset.
|
||||
REVIEWER_OUTCOMES_DATASET=""
|
||||
# Single GitHub org whose members may trigger the agent on *public* repos.
|
||||
# Empty => no public-repo gate (back-compat). Distinct from ALLOWED_GITHUB_ORGS.
|
||||
PUBLIC_REPO_ORG_GATE=""
|
||||
|
||||
# === Sandbox (optional) ===
|
||||
# Provider: langsmith (default), modal, daytona, runloop, or local. See CUSTOMIZATION.md.
|
||||
SANDBOX_TYPE="langsmith"
|
||||
DEFAULT_SANDBOX_SNAPSHOT_ID="" # Required when SANDBOX_TYPE=langsmith (see step 4c)
|
||||
DEFAULT_SANDBOX_SNAPSHOT_FS_CAPACITY_BYTES="" # Root FS size in bytes (default: 32 GiB)
|
||||
DEFAULT_SANDBOX_VCPUS="" # vCPUs per sandbox (default: 4)
|
||||
DEFAULT_SANDBOX_MEM_BYTES="" # Memory in bytes per sandbox (default: 15 GiB)
|
||||
DEFAULT_SANDBOX_IDLE_TTL_SECONDS="" # Auto-stop after N seconds idle (default: 7200; 0 disables)
|
||||
DEFAULT_SANDBOX_DELETE_AFTER_STOP_SECONDS="" # Delete N seconds after stop (default: 86400; 0 disables)
|
||||
|
||||
# === Token Encryption ===
|
||||
TOKEN_ENCRYPTION_KEY="" # Generate with: openssl rand -base64 32
|
||||
# Supports key rotation: see "Rotating TOKEN_ENCRYPTION_KEY" below
|
||||
9
.github/dependabot.yml
vendored
9
.github/dependabot.yml
vendored
|
|
@ -22,15 +22,6 @@ updates:
|
|||
groups:
|
||||
minor-and-patch:
|
||||
update-types: ["minor", "patch"]
|
||||
ignore:
|
||||
# @types/node must track the runtime Node major, not the latest release.
|
||||
# /ui is the web dashboard deployed on Vercel; pin @types/node to the Node
|
||||
# major Vercel builds/runs it on. Dependabot can't see that and a too-new
|
||||
# types major still compiles (passes CI, wrong at runtime). Sanctioned
|
||||
# exception to the no-blanket-ignore rule (engineering-handbook
|
||||
# github-standards Pinning Principle). Minor/patch within the major flow.
|
||||
- dependency-name: "@types/node"
|
||||
update-types: ["version-update:semver-major"]
|
||||
|
||||
# Docker — root Dockerfile
|
||||
- package-ecosystem: "docker"
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -39,7 +39,6 @@ yarn-error.log*
|
|||
.env*.local
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
|
|
|||
14
CLAUDE.md
14
CLAUDE.md
|
|
@ -116,17 +116,3 @@ Webhooks compute deterministic thread ids so the same Linear issue / Slack threa
|
|||
- New dashboard endpoints: add to `agent/dashboard/routes.py`. The router is auto-mounted on the FastAPI app.
|
||||
- New graphs: register the entrypoint in `langgraph.json` under `graphs`.
|
||||
- Minimal-to-no code comments — only when the *why* isn't obvious from the code.
|
||||
|
||||
## Fork maintenance — syncing `upstream/main`
|
||||
|
||||
This is a long-lived fork of `langchain-ai/open-swe` with Sea Haven customizations woven into upstream-owned files (notably `agent/prompt.py` prompt constants, `agent/webapp.py`, and the tool/middleware wiring). Merging upstream is a triage exercise, not a fast-forward. When you want upstream's clean changes but must **defer a large structural refactor** (and its entangled features), work in this order:
|
||||
|
||||
1. **Triage before resolving.** Merge-base is `git merge-base HEAD upstream/main`. The truthful conflict set is the combined merge, `git merge-tree --write-tree --name-only HEAD upstream/main` — a per-commit probe against each commit's parent *overstates* conflicts (a file a refactor merely added shows up as a phantom `modify/delete`). Decide keep-baseline vs adopt-refactor **before** resolving, and surface the choice to a human for any auth/webhook/IAM surface.
|
||||
2. **Chase the cascade, not just the textual conflicts.** The hard part is the non-conflicting files the refactor also touched. Get the refactor's file set (`git diff-tree --no-commit-id --name-status -r <refactor-sha>`) and cross-reference the files this fork modified (`git diff --name-only <fork-base> HEAD`). Files in both = hand-resolve; files only the refactor touched = mechanical.
|
||||
3. **Deferring a refactor:** default every refactor-touched file to **upstream**, except the deleted-module cluster, which stays at your **baseline (HEAD)** — and move its **paired tests to the same side**. A file goes to HEAD when its upstream version imports a module the refactor deleted, or kept code needs an old API. Bring back files the refactor deleted but you still use with `git checkout HEAD -- <file>`. Iterate `pytest --co -q` to chase import breaks one module at a time.
|
||||
4. **Two silent hazards.** (a) A thin upstream router ends with `from .webhooks.slack import process_slack_mention`; merged alongside your monolith's *local* `def process_slack_mention`, Python rebinds the name at import, so **upstream's handler runs and silently drops your fixes** — delete those re-import lines. (b) A new tool/middleware importing a deleted module crashes the whole graph at import — if you defer the feature, delete the tool file **and** all its wiring (`server.py` tool list, `tools/__init__.py`, prompt guidance, e2e harness, its test).
|
||||
5. **Keep test + impl on the same side** — a test at upstream and its impl at HEAD (or vice-versa) yields async-vs-sync or contract drift. Keep the whole vertical (backend + UI + e2e spec + fixtures) on one side.
|
||||
6. **Run CI in layers** — `ruff`/`tsc` (syntax/types) → `pytest --co` (import-time breaks) → unit tests (contract mismatches) → **E2E (Playwright + the real LangGraph dev server)**, which is the only layer that catches import-time crashes in tool/middleware *wiring* and frontend↔backend contract drift. "Unit green" is not "done" for a structural merge.
|
||||
7. **Tooling-switch fallout** — a package-manager/build-tool switch (upstream `pnpm`, this fork keeps `bun`) auto-merges into build scripts, CI, the `packageManager` field, and lockfiles even when you reject it for the product build. After merging, sweep those and never ship two lockfiles.
|
||||
|
||||
Validate on a throwaway branch with granular commits (one per cascade class) and let each CI layer prove out before promoting.
|
||||
|
|
|
|||
|
|
@ -32,22 +32,6 @@ TEAM_SETTINGS_KEY = "default"
|
|||
ORG_GUIDELINES_MAX_CHARS = 10_000
|
||||
REVIEW_TRACING_PROJECT_MAX_CHARS = 256
|
||||
|
||||
# Sea Haven review baseline seeded as the org-wide guidelines default. Surfaces
|
||||
# in the reviewer prompt for every repo until an admin overrides it with a
|
||||
# non-empty value via the dashboard (PUT /team-settings). Keep it stack-agnostic
|
||||
# and well under ORG_GUIDELINES_MAX_CHARS.
|
||||
DEFAULT_ORG_REVIEW_GUIDELINES = """\
|
||||
Sea Haven review baseline (applies to every repo unless a repo-specific guideline overrides it):
|
||||
|
||||
- Severity: map findings to critical / high / medium / low. Reserve critical and high for correctness bugs, security issues, data loss, or broken contracts — not style.
|
||||
- Secrets & config: flag any hardcoded secret, credential, or real `.env`/config value committed to source, and any sensitive value placed outside the platform's secrets manager.
|
||||
- Security surface (raise as high): changes to authentication/authorization or access checks; IAM/policy/permission or infrastructure-access changes; changes to the exported signature or contract of a public handler/endpoint; and untrusted-input handling (request parsing, deserialization, file uploads, SSRF-prone fetches, and template/SQL/command construction — flag unescaped interpolation of dynamic or user-controlled data).
|
||||
- Tests: flag new behavior that ships without a corresponding test, and "fixes" that only silence a check (added excludes, `noqa` / `# type: ignore`, skipped or `xfail`ed tests).
|
||||
- Naming & conventions: flag resources or code that break the repo's established naming and layout conventions.
|
||||
- Deferred work: a finding the author chooses to defer must be captured in a tracked issue, not dropped silently.
|
||||
|
||||
Only file a finding that anchors to a changed line and names a concrete failure mode. Do not police pre-existing issues outside the diff or raise pure style nits."""
|
||||
|
||||
|
||||
class TeamSettingsUpdate(BaseModel):
|
||||
review_draft_prs: bool = False
|
||||
|
|
@ -168,7 +152,7 @@ def _default_settings() -> dict[str, Any]:
|
|||
"pr_summaries": True,
|
||||
"review_trace_links": True,
|
||||
"review_tracing_project": None,
|
||||
"org_guidelines": DEFAULT_ORG_REVIEW_GUIDELINES,
|
||||
"org_guidelines": None,
|
||||
"default_agent_model": fallback_model,
|
||||
"default_agent_reasoning_effort": fallback_effort,
|
||||
"default_agent_subagent_model": fallback_model,
|
||||
|
|
|
|||
|
|
@ -163,12 +163,8 @@ Before any task that changes code, set up the repo in your sandbox, in order:
|
|||
This authors every commit. It is required for CI (e.g. Vercel preview deploys reject commits whose author email can't be resolved to a GitHub account; this email resolves). Do NOT set any other identity, pass `--author`, or export `GIT_AUTHOR_*` / `GIT_COMMITTER_*`.
|
||||
4. **Choose your branch** — Use a Sea Haven branch name: `<prefix>/<description>`, all kebab-case. Pick the prefix by the kind of work:
|
||||
- `feature/` — new functionality or an enhancement
|
||||
- `fix/` — a defect caught before it reaches production
|
||||
- `bug/` — a defect caught before it reaches production
|
||||
- `hotfix/` — a fix for a production-impacting issue
|
||||
- `chore/` — tooling, dependencies, config, or other maintenance
|
||||
- `docs/` — documentation-only changes
|
||||
- `refactor/` — internal restructuring with no behavior change
|
||||
- `release/` — release preparation
|
||||
|
||||
Keep `<description>` short and kebab-case (e.g. `feature/add-receipt-parser`). When a ticket key is resolvable from the run context, put it first: `feature/<KEY>-add-receipt-parser`; if no key is resolvable, omit it. Never commit directly to `main`. Keep the branch thread-stable: if a branch already exists for this thread, reuse it: fetch and check it out, starting from `origin/<branch>` (not the base branch) so prior commits are preserved for review — do not recreate it.
|
||||
5. **Read `AGENTS.md`** — IMMEDIATELY after cloning, you MUST check if `AGENTS.md` exists at the repository root (`{working_dir}/<repo>/AGENTS.md`). If it exists, you MUST read it IN FULL before doing ANY other work: its contents are **mandatory rules** that OVERRIDE your default behavior — treat them with the same authority as this system prompt. Violating AGENTS.md rules is a CRITICAL FAILURE. If `AGENTS.md` does not exist, skip this step.
|
||||
|
|
@ -237,17 +233,26 @@ This applies only after you've made code changes. By default, open or update a d
|
|||
|
||||
Steps, in order:
|
||||
|
||||
1. **Lint & format.** Run the repo's lint/format commands and fix errors before submitting:
|
||||
- Python: `make format` then `make lint`
|
||||
- Frontend / TypeScript / JavaScript (repo contains `package.json`): `yarn format` then `yarn lint`
|
||||
- Go (repo contains `.go` files): find the commands from `Makefile`/`go.mod`/CI and run them
|
||||
1. **Lint & format.** Run the repo's lint/format commands and fix errors before submitting (Python: `make format` then `make lint`; JS/TS with `package.json`: `yarn format` then `yarn lint`; Go: find the commands from `Makefile`/`go.mod`/CI). Then review your diff for correctness and unintended changes.
|
||||
|
||||
Fix any errors reported by linters before proceeding, then review your diff for correctness — verify no regressions or unintended modifications.
|
||||
2. **Push & open/update the PR.** Commit locally and `git push origin <branch>`.
|
||||
- **Open a new PR** with the `open_pull_request` tool (pass `owner`, `repo`, `head`=your branch, `base`, `title`, `body`; push BEFORE calling it) — NOT `gh pr create` — so it's attributed to the triggering user.
|
||||
- **Update an existing PR** (edit body, mark ready, etc.) with `GH_TOKEN=dummy gh pr edit`. If a PR already exists for the branch (including one the user pasted), don't open a duplicate — `open_pull_request` returns the existing URL, so switch to `gh pr edit` and add follow-up work as new commits.
|
||||
|
||||
2. **Commit** locally with a message in the Sea Haven format (see **Commit message** below).
|
||||
**PR Title** (<70 chars): `<type>: <concise description> [closes <TICKET>]` where type ∈ `fix`/`feat`/`chore`/`ci`. Append the resolvable ticket in brackets (e.g. `fix: handle null session [closes AB-000]`) — from the Linear-triggered run (`{linear_project_id}-{linear_issue_number}`) or a ticket referenced in the thread; omit the suffix entirely if none resolves.
|
||||
|
||||
3. **Push & open/update the PR.** `git push origin <branch>`, then open or update the PR when a PR is requested, necessary, or required by the Always Create PRs dashboard setting.
|
||||
- **Open a new PR** with the `open_pull_request` tool (pass `owner`, `repo`, `head` = your branch, `base`, `title`, `body`) — NOT `gh pr create`. By default the PR is authored by the app (`seahaven-openswe[bot]`), like GitHub-issue-triggered runs (a user can opt back into per-user attribution via the `author_prs_as_user` profile setting). Push the branch BEFORE calling it.
|
||||
**Frontend / TypeScript / JavaScript** (if repo contains `package.json`):
|
||||
- `yarn format` then `yarn lint`
|
||||
|
||||
**Go** (if repo contains `.go` files):
|
||||
- Figure out the lint/formatter commands (check `Makefile`, `go.mod`, or CI config) and run them
|
||||
|
||||
Fix any errors reported by linters before proceeding.
|
||||
|
||||
2. **Review your changes**: Review the diff to ensure correctness. Verify no regressions or unintended modifications.
|
||||
|
||||
3. **Submit**: Commit locally, push with `git push origin <branch>`, then open or update the PR when a PR is requested, necessary, or required by the Always Create PRs dashboard setting.
|
||||
- **Open a new PR** with the `open_pull_request` tool (pass `owner`, `repo`, `head` = your branch, `base`, `title`, `body`). By default the PR is authored by the app (`seahaven-openswe[bot]`), like GitHub-issue-triggered runs (a user can opt back into per-user attribution via the `author_prs_as_user` profile setting). Push the branch BEFORE calling it.
|
||||
- **Update an existing PR** (edit the body, mark ready for review, etc.) with `GH_TOKEN=dummy gh pr edit`. If a PR already exists for the branch (including one the user pasted in), do NOT open a duplicate — `open_pull_request` returns the existing PR's URL, so switch to `gh pr edit`. For follow-up changes, add a new commit on top of the existing branch history.
|
||||
|
||||
**PR Title** (under 70 characters): the title rule is **repo-aware** — first detect whether the target repo enforces a conventional-commit PR title, then pick the matching style. The repo is already cloned, so this check is cheap.
|
||||
|
|
@ -257,13 +262,13 @@ Steps, in order:
|
|||
- a `commitlint` config wired to PR titles (`commitlint.config.*`, `.commitlintrc*`, or a `commitlint` key in `package.json`);
|
||||
- `AGENTS.md` / `CONTRIBUTING.md` states a conventional-commit title requirement.
|
||||
|
||||
*If a gate is enforced* → emit a conventional-commit title `type(scope): description` and conform to the action's configuration (its allowed `types`/`scopes` may be narrower than the Sea Haven set below). Open the workflow (e.g. `.github/workflows/pr_lint.yml`) and read the allowed `types`/`scopes` so you stay inside them; if `requireScope` is false, a scope is optional. Map the work to a type: new functionality → `feat`, defect fix → `fix`, infra/CI → `ci`/`build`/`chore`, docs → `docs`, tests → `test`, refactor → `refactor`, perf → `perf`. Examples: `feat: add retry logic for transient upstream failures` or `fix(deps): pin langgraph-cli`. Do NOT rely on an escape-hatch label (e.g. `ignore-lint-pr-title`) to dodge the check — conform to the title instead. (Note: this repo's own `PR Title Lint` and upstream `langchain-ai/open-swe` both enforce this — emit a conforming `type:` title for them.)
|
||||
*If a gate is enforced* → emit a conventional-commit title `type(scope): description` and conform to the action's configuration. This **overrides** the Sea Haven no-`type:`-prefix default. Open the workflow (e.g. `.github/workflows/pr_lint.yml`) and read the allowed `types`/`scopes` so you stay inside them; if `requireScope` is false, a scope is optional. Map the work to a type: new functionality → `feat`, defect fix → `fix`, infra/CI → `ci`/`build`/`chore`, docs → `docs`, tests → `test`, refactor → `refactor`, perf → `perf`. Examples: `feat: add retry logic for transient upstream failures` or `fix(deps): pin langgraph-cli`. Do NOT rely on an escape-hatch label (e.g. `ignore-lint-pr-title`) to dodge the check — conform to the title instead. (Note: this repo's own `PR Title Lint` and upstream `langchain-ai/open-swe` both enforce this — emit a conforming `type:` title for them.)
|
||||
|
||||
*If no gate is enforced* → use the Sea Haven default, which is conventional-commit style: `type(scope): concise description`, where type ∈ `feat` / `fix` / `docs` / `style` / `refactor` / `perf` / `test` / `build` / `ci` / `chore` / `revert` / `release` (scope optional). Imperative mood after the type; describe the change, not the ticket. When a ticket key is resolvable from the run context, append it in square brackets; otherwise omit it:
|
||||
*If no gate is enforced* → use the Sea Haven imperative style: imperative mood, capitalized, describing the change — not the ticket. Do NOT use a conventional-commit `type:` prefix (no `feat:`/`fix:`/`chore:`). When a ticket key is resolvable from the run context, prefix it in square brackets; otherwise omit it entirely:
|
||||
```
|
||||
feat: add retry logic for transient upstream failures [<KEY>]
|
||||
[<KEY>] Add retry logic for transient upstream failures
|
||||
```
|
||||
With no resolvable key, drop the suffix: `feat: add retry logic for transient upstream failures`. Resolve the key from the Linear-triggered run when present (`{linear_project_id}-{linear_issue_number}`), or from a Linear ticket referenced in the Slack thread / task context.
|
||||
With no resolvable key, use just the imperative description: `Add retry logic for transient upstream failures`. Resolve the key from the Linear-triggered run when present (`{linear_project_id}-{linear_issue_number}`), or from a Linear ticket referenced in the Slack thread / task context.
|
||||
|
||||
**PR Body** — use this structure. Omit a section only when it would be empty:
|
||||
```
|
||||
|
|
@ -287,14 +292,18 @@ Steps, in order:
|
|||
- This is the GitHub-issue analog of the Linear `Refs: <KEY>` commit trailer — placed in the PR body where GitHub's auto-close looks.
|
||||
- **Default-branch caveat (don't mistake this for a bug):** GitHub only auto-closes the linked issue when the PR merges into the repo's **default branch**. In the Sea Haven flow the agent targets `dev`, not the default branch, so `Closes #<n>` will **not** close the issue at dev-merge time — it closes when `dev` is promoted to the default branch. The link still renders, and the issue closes on promotion; this is the correct, expected outcome. On repos where the agent targets the default branch directly, it closes on merge as usual.
|
||||
|
||||
**Commit message** — the message for the step-2 commit follows the Sea Haven conventional-commit format:
|
||||
- Subject `type(scope): concise description`, where type ∈ `feat` / `fix` / `docs` / `style` / `refactor` / `perf` / `test` / `build` / `ci` / `chore` / `revert` / `release` (scope optional). Imperative mood after the type (e.g. "add retry logic", not "added retry logic" or "adds retry logic").
|
||||
3. **Notify the source** right after pushing (and PR open/update) succeeds, with a brief summary plus the PR link (or branch URL if no PR): `linear_comment` (with an `@mention`) for Linear, `slack_thread_reply` for Slack, `GH_TOKEN=dummy gh issue comment`/`pr comment` for GitHub. Skip if there is no known source channel.
|
||||
|
||||
When the target repo is public, don't reference private repos or private PR/issue numbers in the description.
|
||||
|
||||
**Commit message** — follow the Sea Haven format:
|
||||
- Imperative mood, capitalized first letter (e.g. "Add retry logic", not "Added retry logic" or "adds retry logic").
|
||||
- Subject line ≤50 characters. If you need more, add a blank line and a body wrapped at 72 characters.
|
||||
- Explain *why*, not *what* — the diff already shows what changed.
|
||||
- No generic descriptions ("fix stuff", "update code", "WIP", "address review comments") and no self-referential phrasing ("This commit…", "This PR…", "I refactored…").
|
||||
- No generic subjects ("Fix stuff", "Update code", "WIP", "Address review comments") and no self-referential phrasing ("This commit…", "This PR…", "I refactored…").
|
||||
- When a ticket key is resolvable, add a `Refs: <KEY>` trailer (combine with `#<issue>` when both apply); otherwise omit the trailer.
|
||||
|
||||
This matches the repo-aware **PR title** rule above; on a squash-merge the commit subject and the PR title use the same conventional-commit form, so they stay consistent.
|
||||
This per-commit convention is independent of the repo-aware **PR title** rule above. On a repo that requires conventional PR titles **and** squash-merges, the squash commit subject becomes the PR title (e.g. `feat: …`) and so diverges from this imperative-no-prefix commit style — that's an acceptable tradeoff (the target repo's title lint wins), not a contradiction. Your own per-commit subjects still follow the Sea Haven format here.
|
||||
|
||||
**IMPORTANT: For code-change tasks, never ask the user for permission or confirmation before pushing commits or opening/updating a draft PR. Do not say "if you want, I can proceed" or "shall I open the PR?". When implementation is done and checks pass, push autonomously, and open/update a draft PR autonomously when requested, necessary, or required by the Always Create PRs dashboard setting.**
|
||||
|
||||
|
|
@ -316,8 +325,6 @@ Steps, in order:
|
|||
- GitHub-triggered: use `GH_TOKEN=dummy gh issue comment` or `GH_TOKEN=dummy gh pr comment`
|
||||
- If the task was not triggered from a known source channel (no Slack thread, no Linear ticket, no GitHub issue context), skip the notification step.
|
||||
|
||||
When the target repo is public, don't reference private repos or private PR/issue numbers in the summary.
|
||||
|
||||
Example:
|
||||
```
|
||||
@username, I've completed the implementation and opened a PR: <pr_url>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,3 @@
|
|||
# Default Prompt
|
||||
|
||||
When a repository is not explicitly mentioned, use the repository provided in the run metadata or dashboard settings. Do not assume a hardcoded repository name.
|
||||
|
||||
These apply to every repository unless the repo's own AGENTS.md / CONTRIBUTING.md overrides them.
|
||||
|
||||
**Secrets & config.** Never hardcode secrets or commit a real `.env`. Sensitive values (API keys, tokens, passwords, connection strings) belong in the platform's secrets manager; non-sensitive config in its parameter/config store — never baked into source or committed env files. Parameterize org- or company-specific values (names, IDs, hosts) instead of hardcoding them, especially in public repos.
|
||||
|
||||
**Keep docs in sync.** When you add, remove, or change functionality, update the README (and any other affected docs) in the same commit. An out-of-date README is a defect, not a follow-up.
|
||||
|
||||
**Verify before pushing.** Run the repo's configured checks — formatter, linter, type-checker, and test suite — and make them pass before you push. Discover the commands from the repo itself (`Makefile`, `package.json` scripts, CI config); don't assume a fixed toolchain.
|
||||
|
||||
**Trust only the real gates after delegating.** If you hand work to a subagent, re-run the actual checks yourself afterward and treat the task as unverified until you have seen them pass. Be suspicious of "fixes" that only silence a check — added test excludes, `noqa` / `# type: ignore`, skipped or `xfail`ed tests, or narrowed lint scope.
|
||||
|
||||
**Confirm a convention before adopting it.** A pattern in a single repo may be a one-off. Before treating something as house style, check that it holds across the repo's own established code or several sibling repos — match the surrounding code, not an imported assumption.
|
||||
|
||||
**Writing style.** Write PR descriptions, commit messages, and channel replies as a concise senior engineer would: plain and direct, no marketing tone, no emoji. Say what changed and why. Don't overclaim completeness — if something is untested or partial, state that plainly.
|
||||
|
|
|
|||
|
|
@ -229,10 +229,8 @@ def test_construct_system_prompt_uses_sea_haven_conventions() -> None:
|
|||
prompt = construct_system_prompt(working_dir="/workspace")
|
||||
|
||||
# Branch naming, PR structure, and commit format follow the handbook.
|
||||
assert "feature/" in prompt and "hotfix/" in prompt and "chore/" in prompt
|
||||
# Commits use the Sea Haven conventional-commit format with the allowed type list.
|
||||
assert "conventional-commit format" in prompt
|
||||
assert "revert" in prompt and "release" in prompt
|
||||
assert "feature/" in prompt and "hotfix/" in prompt
|
||||
assert "Do NOT use a conventional-commit `type:` prefix" in prompt
|
||||
assert "## Summary" in prompt and "## Validation" in prompt
|
||||
assert "## Release Note" not in prompt
|
||||
|
||||
|
|
@ -256,8 +254,8 @@ def test_construct_system_prompt_pr_title_rule_is_repo_aware() -> None:
|
|||
assert "amannn/action-semantic-pull-request" in prompt
|
||||
assert "repo-aware" in prompt
|
||||
assert "type(scope): description" in prompt or "type(scope): …" in prompt
|
||||
# ...and the no-gate default is itself conventional-commit style (Sea Haven standard).
|
||||
assert "the Sea Haven default, which is conventional-commit style" in prompt
|
||||
# ...without dropping the no-prefix default for repos that don't enforce one.
|
||||
assert "Do NOT use a conventional-commit `type:` prefix" in prompt
|
||||
|
||||
|
||||
def test_construct_system_prompt_shell_escapes_user_name() -> None:
|
||||
|
|
|
|||
|
|
@ -6,11 +6,9 @@ import pytest
|
|||
from pydantic import ValidationError
|
||||
|
||||
from agent.dashboard.team_settings import (
|
||||
DEFAULT_ORG_REVIEW_GUIDELINES,
|
||||
ORG_GUIDELINES_MAX_CHARS,
|
||||
REVIEW_TRACING_PROJECT_MAX_CHARS,
|
||||
TeamSettingsUpdate,
|
||||
_default_settings,
|
||||
get_org_review_guidelines,
|
||||
get_team_default_model,
|
||||
get_team_review_tracing_project,
|
||||
|
|
@ -35,14 +33,6 @@ def test_org_guidelines_rejects_oversized() -> None:
|
|||
TeamSettingsUpdate(org_guidelines="x" * (ORG_GUIDELINES_MAX_CHARS + 1))
|
||||
|
||||
|
||||
def test_default_settings_seed_sea_haven_org_guidelines() -> None:
|
||||
# Unset org guidelines default to the baked Sea Haven review baseline so the
|
||||
# reviewer applies it on every repo until an admin overrides it.
|
||||
assert _default_settings()["org_guidelines"] == DEFAULT_ORG_REVIEW_GUIDELINES
|
||||
assert "Sea Haven review baseline" in DEFAULT_ORG_REVIEW_GUIDELINES
|
||||
assert len(DEFAULT_ORG_REVIEW_GUIDELINES) <= ORG_GUIDELINES_MAX_CHARS
|
||||
|
||||
|
||||
def test_review_tracing_project_blank_normalizes_to_none() -> None:
|
||||
assert TeamSettingsUpdate(review_tracing_project=" ").review_tracing_project is None
|
||||
assert TeamSettingsUpdate(review_tracing_project=None).review_tracing_project is None
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
"@monaco-editor/react": "^4.7.0",
|
||||
"@phosphor-icons/react": "^2.1.10",
|
||||
"@pierre/diffs": "^1.2.1",
|
||||
"@pierre/trees": "1.0.0-beta.5",
|
||||
"@pierre/trees": "1.0.0-beta.4",
|
||||
"@tailwindcss/vite": "^4.2.1",
|
||||
"@tanstack/react-devtools": "^0.10.0",
|
||||
"@tanstack/react-query": "^5.100.10",
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^1.16.0",
|
||||
"monaco-editor": "^0.55.1",
|
||||
"nitro": "3.0.260603-beta",
|
||||
"nitro": "latest",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-icons": "^5.6.0",
|
||||
|
|
@ -494,9 +494,7 @@
|
|||
|
||||
"@pierre/theme": ["@pierre/theme@1.0.3", "", {}, "sha512-sWHv11TMoqKxKDgTIk5VbhQjdPhs8DCcBxbjh3mRlS3YOM/OcrWoGX6MM8eBGn9cUu3M46Py0JnxsG2nJaFTuA=="],
|
||||
|
||||
"@pierre/theming": ["@pierre/theming@0.0.2", "", { "peerDependencies": { "@pierre/theme": "^1.1.0", "@shikijs/themes": "^3.0.0 || ^4.0.0", "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0", "shiki": "^3.0.0 || ^4.0.0" }, "optionalPeers": ["@pierre/theme", "@shikijs/themes", "react", "react-dom", "shiki"] }, "sha512-QM1M4stXfnzfaE8I8YbjXSApV8c+2dBsXJj8eYg9WTpBR/cTmCZIcfGnN4p13iRrYu2Br/R/OJfEL7uR8Qjctw=="],
|
||||
|
||||
"@pierre/trees": ["@pierre/trees@1.0.0-beta.5", "", { "dependencies": { "@pierre/theming": "0.0.2", "preact": "11.0.0-beta.0", "preact-render-to-string": "6.6.5" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-IzxkB9qv6GLbeEXObhlAD205LfYHiLeRwJdnaIdX0f5keTZF4X9EfiuEQ3QiyxOxouVVmUX3rX7m6a8zNMo/wA=="],
|
||||
"@pierre/trees": ["@pierre/trees@1.0.0-beta.4", "", { "dependencies": { "preact": "11.0.0-beta.0", "preact-render-to-string": "6.6.5" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-OfT1yk9ne8Te5+GB5zUY8yqE6B8BqjBHQJleH4lu8ltwNpoocZl4vXt1AzlEExpxI/pp+AFX5QG+lR3JjtTEag=="],
|
||||
|
||||
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.1.4", "", { "os": "android", "cpu": "arm64" }, "sha512-EZLpf/8y7GXkkra90ML47kzik/GMP3EMcE9bPyHmRfxLC6z9+aW5A8poCsoxjrT5GfEcNAAvWwUHjvP1pUQkfw=="],
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^1.16.0",
|
||||
"monaco-editor": "^0.55.1",
|
||||
"nitro": "3.0.260603-beta",
|
||||
"nitro": "latest",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-icons": "^5.6.0",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { useCallback, useEffect, useState } from "react"
|
||||
|
||||
import {
|
||||
DEFAULT_SIDEBAR_FILTERS
|
||||
|
||||
|
||||
DEFAULT_SIDEBAR_FILTERS,
|
||||
type SidebarFilters,
|
||||
type SidebarGroupMode,
|
||||
} from "./sidebarFilter"
|
||||
import type {SidebarFilters, SidebarGroupMode} from "./sidebarFilter";
|
||||
|
||||
const STORAGE_KEY = "open-swe.agents.sidebar-prefs"
|
||||
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ function CloudAgentsPage() {
|
|||
</div>
|
||||
<CaretRightIcon className="size-3.5 shrink-0 text-muted-foreground" />
|
||||
</Link>
|
||||
{session.data.is_admin && (
|
||||
{session.data?.is_admin && (
|
||||
<Link
|
||||
to="/agents/snapshots"
|
||||
className="flex items-center justify-between gap-6 px-4 py-3 hover:bg-muted/40"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import http from "node:http"
|
||||
import { defineConfig } from "vite"
|
||||
import { defineConfig, type Plugin } from "vite"
|
||||
import { devtools } from "@tanstack/devtools-vite"
|
||||
import { tanstackStart } from "@tanstack/react-start/plugin/vite"
|
||||
import viteReact from "@vitejs/plugin-react"
|
||||
|
|
@ -7,7 +7,6 @@ import viteTsConfigPaths from "vite-tsconfig-paths"
|
|||
import tailwindcss from "@tailwindcss/vite"
|
||||
import { nitro } from "nitro/vite"
|
||||
import { VitePWA } from "vite-plugin-pwa"
|
||||
import type {Plugin} from "vite";
|
||||
|
||||
// Dev-only: when E2E_HARNESS is set (the `dev:mock` local harness) serve the app
|
||||
// and the harness from one origin by proxying the API routes + the Yjs collab
|
||||
|
|
@ -61,7 +60,7 @@ function mockHarnessProxy(): Plugin | null {
|
|||
socket.write(
|
||||
`HTTP/1.1 ${proxyRes.statusCode} ${proxyRes.statusMessage}\r\n${lines}\r\n\r\n`
|
||||
)
|
||||
if (proxyHead.length) socket.write(proxyHead)
|
||||
if (proxyHead?.length) socket.write(proxyHead)
|
||||
if (head?.length) proxySocket.write(head)
|
||||
proxySocket.on("error", () => socket.destroy())
|
||||
socket.on("error", () => proxySocket.destroy())
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue