mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-10-07 16:19:09 +00:00
Compare commits
4 commits
e0a6ed894e
...
a62226c9d7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a62226c9d7 | ||
|
|
4df6ea9241 | ||
|
|
52cc696431 | ||
|
|
f79f49c755 |
5 changed files with 53 additions and 12 deletions
|
|
@ -110,7 +110,7 @@ Each invocation creates a deterministic thread ID, so follow-up messages on the
|
|||
|
||||
**Trigger tags (Sea Haven fork):** a mention is a case-insensitive substring match on the comment body — `@openswe`, `@open-swe`, `@openswe-dev`, or `@seahaven-openswe` (the deployed App slug). GitHub won't linkify `@seahaven-openswe` (App `[bot]` accounts aren't user-mentionable), but the text still fires a run.
|
||||
|
||||
**Engineering conventions & attribution (Sea Haven fork):** the main agent's system prompt is tuned to the Sea Haven engineering handbook — branch names are `feature|bug|hotfix/<kebab-desc>` (optional resolvable `<KEY>-` prefix), PR titles are imperative with no conventional-commit `type:` prefix, PR bodies use `## Summary / Validation / Tests / Notes`, and commit messages follow the handbook format (≤50-char imperative subject, *why* over *what*). **No agent/AI attribution is added to any artifact** — no `Co-authored-by` bot trailer, no `Made by [Open SWE]` footer, no "generated by an agent" notes. Commits are currently authored as the **triggering user** (the upstream behavior, which keeps Vercel preview deploys resolvable); flipping authorship to the bot account is tracked separately in issue #11 pending the Vercel-resolvability decision.
|
||||
**Engineering conventions & attribution (Sea Haven fork):** the main agent's system prompt is tuned to the Sea Haven engineering handbook — branch names are `feature|bug|hotfix/<kebab-desc>` (optional resolvable `<KEY>-` prefix), PR bodies use `## Summary / Validation / Tests / Notes`, and commit messages follow the handbook format (≤50-char imperative subject, *why* over *what*). The **PR title rule is repo-aware**: when the target repo enforces a conventional-commit title (an `amannn/action-semantic-pull-request` workflow, a `commitlint` config, or a documented requirement in `AGENTS.md` / `CONTRIBUTING.md`), the agent emits a conforming `type(scope): …` title that reads the action's allowed types/scopes — this lets it pass gates like this repo's own `PR Title Lint` and upstream `langchain-ai/open-swe` without manual retitling; otherwise it falls back to the Sea Haven imperative style with no `type:` prefix. PRs that resolve a GitHub issue **auto-link it** in the body (`Closes #<n>` for full fixes, `Refs #<n>`/`Part of #<n>` for partial work, `Closes owner/repo#<n>` cross-repo); because the Sea Haven flow targets `dev` rather than the default branch, the issue closes when `dev` is promoted, not at dev-merge. **No agent/AI attribution is added to any artifact** — no `Co-authored-by` bot trailer, no `Made by [Open SWE]` footer, no "generated by an agent" notes. Commits are currently authored as the **triggering user** (the upstream behavior, which keeps Vercel preview deploys resolvable); flipping authorship to the bot account is tracked separately in issue #11 pending the Vercel-resolvability decision.
|
||||
|
||||
### 7. Validation — Prompt-Driven
|
||||
|
||||
|
|
|
|||
|
|
@ -394,7 +394,16 @@ When you have completed your implementation, follow these steps in order:
|
|||
- **Open a new PR** with the `open_pull_request` tool (pass `owner`, `repo`, `head` = your branch, `base`, `title`, `body`). This attributes the PR to the triggering user. 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): 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:
|
||||
**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.
|
||||
|
||||
*Detect a conventional-commit title gate* — the repo enforces one if ANY of these hold:
|
||||
- a workflow under `.github/workflows/` references `amannn/action-semantic-pull-request` (or any `semantic-pull-request` action);
|
||||
- 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 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:
|
||||
```
|
||||
[<KEY>] Add retry logic for transient upstream failures
|
||||
```
|
||||
|
|
@ -415,6 +424,13 @@ When you have completed your implementation, follow these steps in order:
|
|||
<Anything reviewers should know — migration steps, deploy order, follow-ups, breaking changes. Omit this section if empty.>
|
||||
```
|
||||
|
||||
**Link the GitHub issue the PR resolves** — when the run originates from (or fully fixes) a GitHub issue, add a closing keyword to the PR body so merging auto-closes the issue. The issue number is usually in-context: issue-triggered runs receive a `## GitHub Issue: #<n>` line; for Slack/Linear-triggered runs that fix a GitHub issue, pick `#<n>` up from the task text.
|
||||
- When the PR **fully resolves** a GitHub issue in the **same repo**, add a dedicated trailing line in `## Summary` (or its own line at the end of the body): `Closes #<n>`. GitHub recognizes `Closes`/`Fixes`/`Resolves #<n>` anywhere in the body.
|
||||
- When the PR only **partially** addresses an issue (more work remains), use a **non-closing** reference so the issue stays open: `Refs #<n>` or `Part of #<n>`.
|
||||
- **Cross-repo**: if the issue lives in a different repo, use the fully-qualified form: `Closes owner/repo#<n>` (or `Refs owner/repo#<n>` for partial).
|
||||
- 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.
|
||||
|
||||
You don't need to add links back to the originating Slack thread or Linear ticket — for private repos, `open_pull_request` appends a `## References` section automatically.
|
||||
|
||||
When the target repo is public, don't reference private repos or private PR/issue numbers in the description.
|
||||
|
|
@ -426,6 +442,8 @@ When you have completed your implementation, follow these steps in order:
|
|||
- 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 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.**
|
||||
|
||||
**IMPORTANT: If you made commits directly via `git commit` or `git revert` in the sandbox, you MUST push those commits to GitHub. Never report the work as done without pushing.**
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ dependencies = [
|
|||
"fastapi>=0.136.3",
|
||||
"uvicorn>=0.48.0",
|
||||
"httpx>=0.28.1",
|
||||
"PyJWT>=2.12.1",
|
||||
"PyJWT>=2.13.0",
|
||||
"cryptography>=48.0.1",
|
||||
"langgraph-sdk>=0.4.2",
|
||||
"langchain>=1.3.9",
|
||||
|
|
@ -22,7 +22,7 @@ dependencies = [
|
|||
"langchain-openai>=1.2.2",
|
||||
"langchain-fireworks>=1.4.2",
|
||||
# langchain-fireworks 1.4.2 pins a pre-release fireworks-ai; opt in explicitly so uv resolves it.
|
||||
"fireworks-ai>=1.2.0a71",
|
||||
"fireworks-ai>=1.2.0a85",
|
||||
"langchain-daytona>=0.0.5",
|
||||
"langchain-modal>=0.0.3",
|
||||
"langchain-runloop>=0.0.4",
|
||||
|
|
|
|||
|
|
@ -195,6 +195,29 @@ def test_construct_system_prompt_uses_sea_haven_conventions() -> None:
|
|||
assert "## Release Note" not in prompt
|
||||
|
||||
|
||||
def test_construct_system_prompt_links_resolved_github_issue() -> None:
|
||||
prompt = construct_system_prompt(working_dir="/workspace")
|
||||
|
||||
# Part A: PRs that resolve a GitHub issue auto-link it for auto-close.
|
||||
assert "Closes #<n>" in prompt
|
||||
assert "Refs #<n>" in prompt or "Part of #<n>" in prompt
|
||||
assert "Closes owner/repo#<n>" in prompt
|
||||
# The default-branch auto-close caveat must be stated so it isn't read as a bug.
|
||||
assert "default branch" in prompt
|
||||
assert "promoted" in prompt or "promotion" in prompt
|
||||
|
||||
|
||||
def test_construct_system_prompt_pr_title_rule_is_repo_aware() -> None:
|
||||
prompt = construct_system_prompt(working_dir="/workspace")
|
||||
|
||||
# Part B: detect a conventional-commit title gate and conform to it...
|
||||
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
|
||||
|
||||
|
||||
def test_construct_system_prompt_shell_escapes_user_name() -> None:
|
||||
import shlex
|
||||
|
||||
|
|
|
|||
16
uv.lock
generated
16
uv.lock
generated
|
|
@ -755,7 +755,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "fireworks-ai"
|
||||
version = "1.2.0a75"
|
||||
version = "1.2.0a85"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
|
|
@ -767,9 +767,9 @@ dependencies = [
|
|||
{ name = "sniffio" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f9/e8/3ce01c9802810f476a7d4273ff937584ba5941da9f06a0f3a4c10d0335c5/fireworks_ai-1.2.0a75.tar.gz", hash = "sha256:b7ccb914f2a64037fd8ab93b219565e94cd3a072fe60d0349707ab67210ba9aa", size = 416496, upload-time = "2026-06-03T18:45:34.846Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/65/94/fb75cf9c769a09de31e550ba99a279e5f83a05cfcf49cd0cbb8ad586ab35/fireworks_ai-1.2.0a85.tar.gz", hash = "sha256:33abbeddd9a6bd9993bca6cb9e16afe9799b2cd674d6b7c04a601cd4e412f2a7", size = 448217, upload-time = "2026-06-26T08:06:57.286Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/53/49/d799dcabfb65b50423dae7d2d4715058b848be7d4d9bb8248850b8aa1d18/fireworks_ai-1.2.0a75-py3-none-any.whl", hash = "sha256:6f2dfe15ceee2a700e21f775cad5776b2726ff475095e33a10ab8cd3c3d5b5aa", size = 495789, upload-time = "2026-06-03T18:45:33.328Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/5b/0b10d0afb633b45f234f6728cc680851f136b9f75903b4387049537a6cd6/fireworks_ai-1.2.0a85-py3-none-any.whl", hash = "sha256:80a4b13bda8e28ca9064c8339d0afe47cbbdb7c61a88eeebc173ccb58129f19a", size = 528844, upload-time = "2026-06-26T08:06:55.927Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2028,7 +2028,7 @@ requires-dist = [
|
|||
{ name = "deepagents", specifier = "==0.6.8" },
|
||||
{ name = "exa-py", specifier = ">=2.13.0" },
|
||||
{ name = "fastapi", specifier = ">=0.136.3" },
|
||||
{ name = "fireworks-ai", specifier = ">=1.2.0a71" },
|
||||
{ name = "fireworks-ai", specifier = ">=1.2.0a85" },
|
||||
{ name = "httpx", specifier = ">=0.28.1" },
|
||||
{ name = "langchain", specifier = ">=1.3.9" },
|
||||
{ name = "langchain-anthropic", specifier = ">=1.4.6" },
|
||||
|
|
@ -2045,7 +2045,7 @@ requires-dist = [
|
|||
{ name = "langsmith", specifier = "==0.8.18" },
|
||||
{ name = "markdownify", specifier = ">=1.2.2" },
|
||||
{ name = "pygments", marker = "extra == 'dev'", specifier = ">=2.20.0" },
|
||||
{ name = "pyjwt", specifier = ">=2.12.1" },
|
||||
{ name = "pyjwt", specifier = ">=2.13.0" },
|
||||
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=9.0.3" },
|
||||
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=1.4.0" },
|
||||
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.15.15" },
|
||||
|
|
@ -2618,11 +2618,11 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "pyjwt"
|
||||
version = "2.12.1"
|
||||
version = "2.13.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", size = 107515, upload-time = "2026-05-21T19:54:36.618Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", size = 31274, upload-time = "2026-05-21T19:54:35.362Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue