Compare commits

...

5 commits

Author SHA1 Message Date
dependabot[bot]
69ccfce0f4
chore(deps): bump vite-tsconfig-paths from 5.1.4 to 6.1.1 in /ui
Bumps [vite-tsconfig-paths](https://github.com/aleclarson/vite-tsconfig-paths) from 5.1.4 to 6.1.1.
- [Release notes](https://github.com/aleclarson/vite-tsconfig-paths/releases)
- [Commits](https://github.com/aleclarson/vite-tsconfig-paths/compare/v5.1.4...v6.1.1)

---
updated-dependencies:
- dependency-name: vite-tsconfig-paths
  dependency-version: 6.1.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-02 22:45:23 +00:00
Adam Moussa
2a226e2cd1
fix(ui): pin nitro to patched 3.0.260603-beta (#116)
Some checks failed
CI / Lint (push) Has been cancelled
CI / Format check (push) Has been cancelled
CI / Unit tests (push) Has been cancelled
CI / Playwright E2E (push) Has been cancelled
CI / Docker build smoke (push) Has been cancelled
Resolves Dependabot GHSA-9phm-9p8f-hw5m (open redirect via
protocol-relative URL in wildcard route rules) and GHSA-5w89-w975-hf9q
(proxy scope bypass via percent-encoded path traversal in routeRules).

nitro was pinned to "latest", which Dependabot can't resolve to a fixed
version, so the alerts stayed open even though the lockfile already
resolved 3.0.260603-beta (newer than the 3.0.260429-beta patch line).
Pin it to an exact version — nitro ships a date-stamped beta channel
where caret ranges behave unpredictably — so installs are reproducible
and both alerts close.

bun.lock also reconciles @pierre/trees beta.4 -> beta.5, which the
manifest already declared but the committed lockfile was stale on.
2026-07-02 18:41:14 -04:00
Adam Moussa
b65c3a07db
feat: distill Sea Haven conventions into agent prompt, reviewer, and fork docs (#113)
* Add Dependabot ignore for @types/node semver-major bumps

Prevent Dependabot from proposing wrong-direction @types/node major
bumps (e.g. 24 -> 26). /ui runs on Node 24 on Vercel; a too-new types
major still compiles but describes APIs absent at runtime.

Refs: #110

* feat(agent): seed all-repos custom instructions in default_prompt.md

Distill the universally-applicable Sea Haven authoring conventions into the
team-default Custom Instructions the main agent gets on every repo: secrets/
config placement, keep-docs-in-sync, verify-before-push, re-run-real-gates
after delegating, confirm-a-convention-before-adopting, and house writing
style. Toolchain references are generalized (not tied to a specific stack).

* feat(reviewer): seed Sea Haven review baseline as org-guidelines default

Bake DEFAULT_ORG_REVIEW_GUIDELINES (severity model, secrets, security surface,
tests, naming, deferred-work-needs-an-issue) and default org_guidelines to it
in _default_settings(). The reviewer now applies the Sea Haven baseline on
every repo until a workspace admin overrides it with a non-empty value via
the dashboard. Stack-agnostic and well under the 10k-char cap.

* refactor(prompt): consolidate duplicated COMMIT_PR_SECTION + add fork-sync runbook

COMMIT_PR_SECTION had two overlapping passes with a contradictory PR-title
rule (a fixed 'type: description' form vs the repo-aware detection). Collapse
into one numbered sequence (lint -> commit -> push/PR -> notify), keep the
authoritative repo-aware title rule, and drop the duplicate notify step. All
IMPORTANT directives (force-push ban, workflow-approval, autonomy, 403
handling) are preserved verbatim.

Add a fork-maintenance runbook to CLAUDE.md distilling the durable
upstream-sync methodology (conflict triage, deferred-refactor resolution rule,
the silent re-import/wiring hazards, test-impl-same-side, layered CI).

* refactor(prompt): adopt conventional-commit style

Flip the Sea Haven authoring convention baked into the agent prompt from
imperative/no-prefix to conventional-commit style:
- Commit subjects and the no-gate PR-title default now use
  type(scope): description with the allowed type set (feat, fix, docs,
  style, refactor, perf, test, build, ci, chore, revert, release).
- Branch prefixes expanded to feature/, fix/, hotfix/, chore/, docs/,
  refactor/, release/ (kebab-case description).
- The repo-aware gate detection is preserved: a repo's own title gate
  still wins and may narrow the allowed types/scopes.

Updated test_github_comment_prompts.py to assert the new convention.
2026-07-02 18:29:15 -04:00
seahaven-openswe[bot]
34c5da7328
Add Dependabot ignore for @types/node semver-major bumps (#112)
Prevent Dependabot from proposing wrong-direction @types/node major
bumps (e.g. 24 -> 26). /ui runs on Node 24 on Vercel; a too-new types
major still compiles but describes APIs absent at runtime.

Refs: #110

Co-authored-by: amoussa1229 <166072409+amoussa1229@users.noreply.github.com>
2026-07-02 18:15:22 -04:00
Adam Moussa
f43606309e
fix(ui): clear eslint errors from dep bumps + track .env.example (#111)
Some checks are pending
CI / Lint (push) Waiting to run
CI / Format check (push) Waiting to run
CI / Unit tests (push) Waiting to run
CI / Playwright E2E (push) Waiting to run
CI / Docker build smoke (push) Waiting to run
* fix(ui): resolve eslint errors surfaced by dep bumps

Clear the 6 eslint errors introduced by the typescript-eslint / TS 6.0
dependency bumps:
- sidebarPrefs.ts, vite.config.ts: inline type imports -> top-level
  type-only imports; sort import members (autofix)
- cloud-agents.tsx: drop optional chain on session.data (already
  narrowed non-null by the earlier login guard)
- vite.config.ts: drop optional chain on proxyHead (typed Buffer from
  the upgrade event, always present)

No behavior change. eslint and tsc --noEmit both clean.

* docs(env): track .env.example and document deployment vars

- Un-ignore .env.example (!.env.example) so the template stays tracked;
  .env / .env.* secret files remain ignored.
- Add LLM_MODEL_ID, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY (used by the
  Bedrock/managed deployment but previously undocumented).
- Mark LANGCHAIN_TRACING_V2 and LANGCHAIN_PROJECT as reserved on
  LangGraph Platform (the platform sets them; deploys are rejected if
  provided).
2026-07-02 17:31:07 -04:00
14 changed files with 232 additions and 46 deletions

123
.env.example Normal file
View file

@ -0,0 +1,123 @@
# === 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

View file

@ -22,6 +22,15 @@ 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
View file

@ -39,6 +39,7 @@ yarn-error.log*
.env*.local
.env
.env.*
!.env.example
# vercel
.vercel

View file

@ -116,3 +116,17 @@ 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.

View file

@ -32,6 +32,22 @@ 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
@ -152,7 +168,7 @@ def _default_settings() -> dict[str, Any]:
"pr_summaries": True,
"review_trace_links": True,
"review_tracing_project": None,
"org_guidelines": None,
"org_guidelines": DEFAULT_ORG_REVIEW_GUIDELINES,
"default_agent_model": fallback_model,
"default_agent_reasoning_effort": fallback_effort,
"default_agent_subagent_model": fallback_model,

View file

@ -163,8 +163,12 @@ 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
- `bug/` — a defect caught before it reaches production
- `fix/` — 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.
@ -233,26 +237,17 @@ 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`; 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.
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
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.
Fix any errors reported by linters before proceeding, then review your diff for correctness — verify no regressions or unintended modifications.
**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.
2. **Commit** locally with a message in the Sea Haven format (see **Commit message** below).
**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.
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.
- **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.
@ -262,13 +257,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. 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 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 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:
*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:
```
[<KEY>] Add retry logic for transient upstream failures
feat: add retry logic for transient upstream failures [<KEY>]
```
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.
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.
**PR Body** — use this structure. Omit a section only when it would be empty:
```
@ -292,18 +287,14 @@ 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.
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").
**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").
- 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 subjects ("Fix stuff", "Update code", "WIP", "Address review comments") and no self-referential phrasing ("This commit…", "This PR…", "I refactored…").
- No generic descriptions ("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 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.
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.
**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.**
@ -325,6 +316,8 @@ 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>

View file

@ -1,3 +1,17 @@
# 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.

View file

@ -229,8 +229,10 @@ 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
assert "Do NOT use a conventional-commit `type:` prefix" in prompt
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 "## Summary" in prompt and "## Validation" in prompt
assert "## Release Note" not in prompt
@ -254,8 +256,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
# ...without dropping the no-prefix default for repos that don't enforce one.
assert "Do NOT use a conventional-commit `type:` prefix" 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
def test_construct_system_prompt_shell_escapes_user_name() -> None:

View file

@ -6,9 +6,11 @@ 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,
@ -33,6 +35,14 @@ 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

View file

@ -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.4",
"@pierre/trees": "1.0.0-beta.5",
"@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": "latest",
"nitro": "3.0.260603-beta",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-icons": "^5.6.0",
@ -494,7 +494,9 @@
"@pierre/theme": ["@pierre/theme@1.0.3", "", {}, "sha512-sWHv11TMoqKxKDgTIk5VbhQjdPhs8DCcBxbjh3mRlS3YOM/OcrWoGX6MM8eBGn9cUu3M46Py0JnxsG2nJaFTuA=="],
"@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=="],
"@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=="],
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.1.4", "", { "os": "android", "cpu": "arm64" }, "sha512-EZLpf/8y7GXkkra90ML47kzik/GMP3EMcE9bPyHmRfxLC6z9+aW5A8poCsoxjrT5GfEcNAAvWwUHjvP1pUQkfw=="],

View file

@ -34,7 +34,7 @@
"clsx": "^2.1.1",
"lucide-react": "^1.16.0",
"monaco-editor": "^0.55.1",
"nitro": "latest",
"nitro": "3.0.260603-beta",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-icons": "^5.6.0",
@ -44,7 +44,7 @@
"tailwind-merge": "^3.6.0",
"tailwindcss": "^4.2.1",
"tw-animate-css": "^1.4.0",
"vite-tsconfig-paths": "^5.1.4"
"vite-tsconfig-paths": "^6.1.1"
},
"devDependencies": {
"@tanstack/devtools-vite": "^0.8.1",

View file

@ -1,10 +1,11 @@
import { useCallback, useEffect, useState } from "react"
import {
DEFAULT_SIDEBAR_FILTERS,
type SidebarFilters,
type SidebarGroupMode,
DEFAULT_SIDEBAR_FILTERS
} from "./sidebarFilter"
import type {SidebarFilters, SidebarGroupMode} from "./sidebarFilter";
const STORAGE_KEY = "open-swe.agents.sidebar-prefs"

View file

@ -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"

View file

@ -1,5 +1,5 @@
import http from "node:http"
import { defineConfig, type Plugin } from "vite"
import { defineConfig } from "vite"
import { devtools } from "@tanstack/devtools-vite"
import { tanstackStart } from "@tanstack/react-start/plugin/vite"
import viteReact from "@vitejs/plugin-react"
@ -7,6 +7,7 @@ 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
@ -60,7 +61,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())