Commit graph

63 commits

Author SHA1 Message Date
open-swe[bot]
eb92947806
fix: propagate Slack reply errors (#1358)
* fix: propagate Slack reply errors

Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>

* fix: classify Slack 429 as rate_limited with retry-after

Slack chat.postMessage rate limiting returns HTTP 429, which raise_for_status
turned into a generic http_error and told the agent to retry immediately,
ignoring Slack's retry window. Special-case 429 (threading Retry-After) and
normalize the ratelimited body code before the generic HTTP path so the
existing rate_limited hint actually fires.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
2026-05-28 23:11:42 +00:00
open-swe[bot]
a361ee8f2e
feat: let reviewer set comment titles (#1356)
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>
2026-05-28 16:04:38 -07:00
open-swe[bot]
9998119921
feat: generate reviewer finding titles (#1355)
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>
2026-05-28 15:28:55 -07:00
open-swe[bot]
d8d3794649
fix: include reviewer trace links (#1351)
* fix: include reviewer trace links

Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>

* feat: move reviewer trace-link toggle to dashboard

Replace the OPEN_SWE_REVIEW_TRACE_LINK_ENABLED env var with a team-level
'Trace Links' toggle in the Open SWE Review dashboard tab. The toggle is
read per-publish via get_team_review_trace_links_enabled(); the per-run
review_trace_link_enabled config override still forces it off.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
2026-05-28 13:48:14 -07:00
Johannes du Plessis
fee209601b
feat: structured reviewer comments + auto resolution comments (#1352)
* feat: structured reviewer comments + auto resolution comments

Restructure inline review comment bodies (severity emoji, bold title from
the first line, line reference, feedback footer) without duplicating the
first description line, and post an automatic resolution/dismissal comment
to the GitHub thread when a finding is resolved or dismissed.

- Centralize render_resolution_comment in reviewer_publish; fix a crash when
  last_reconciliation_note is None and drop the misleading generic fallback.
- Post the resolution comment in resolve_finding_thread (the normal
  update_finding path), not only in publish_review, so it actually fires on
  re-review. Dedupe via github_posted_resolution_comment_ids.
- Add pytest coverage for rendering and the resolution-comment flow.

* fix: post resolution comment to every closed thread in resolve_finding_thread

Per-thread iteration (matching _resolve_threads_for_resolved_findings) so
duplicate threads after the first also receive the resolved/dismissed
explanation before being closed.
2026-05-28 12:44:00 -07:00
Johannes du Plessis
68069ea058
fix: reviewer finding thread resolution state (#1348)
* fix: keep reviewer findings open until threads resolve

* fix: allow hidden reviewer findings to resolve locally
2026-05-28 09:16:30 -07:00
Johannes du Plessis
197d339df4
fix: Reconcile reviewer findings with PR threads (#1346)
* feat: reconcile reviewer findings with PR threads

* fix: harden reviewer finding reply handling

* fix: queue reviewer finding reply body

Ensure review-comment replies that arrive during an active reviewer run include the sanitized reply body in the queued reassessment prompt.

* fix: apply reviewer reply formatting

Apply the repository formatter so the reviewer reply handling fix passes CI format checks.
2026-05-27 17:26:08 -07:00
Johannes du Plessis
0d4d1c5a3b
fix: dedupe reviewer comments from PR state (#1341)
* fix: dedupe reviewer comments from PR state

Use GitHub review-thread markers to repair reviewer publication state before posting or resolving findings, so re-reviews do not duplicate comments and resolved findings close all matching PR threads.

* fix: require all duplicate reviewer threads resolved

Avoid treating a marker-backed finding as resolved when only one duplicate thread is outdated while another matching thread remains open.
2026-05-27 10:52:30 -07:00
open-swe[bot]
dd4ca9181e
fix: route PR review requests through agent tool (#1340)
* fix: route PR review requests through agent tool

Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>

* fix: align public repo gate test with agent routing

Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>

* fix: allow app-token PR review requests

Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>
2026-05-27 10:29:43 -07:00
Johannes du Plessis
fa147ce256
fix: fetch PR diff via GitHub API to re-enable add_finding validation (#1339)
* reviewer: fetch PR diff via GitHub API to re-enable add_finding validation

The previous hotfix in reviewer.py set diff_line_set=None because the
sandbox-based diff prep was sometimes producing empty diffs. That made
every bad anchor a publish-time 422 instead of a creation-time
rejection — the agent burned tokens producing unanchorable findings,
and we had to add a publish-time retry safety net (#1338) to clean up.

Fetch the PR's unified diff via the GitHub REST API at reviewer
startup and populate diff_text + diff_line_set so add_finding can
reject bad anchors immediately. The API path is reliable and is the
same diff GitHub validates against when posting inline review
comments. If the fetch fails, fall back to the previous behavior
(validation disabled, publish-time retry handles it).

Also extract the PR-diff fetch into reviewer_diff.fetch_pr_diff so
both reviewer.py and publish_review.py share one implementation
instead of two copies.

* reviewer: make diff_line_set validation side-aware

compute_diff_line_set previously returned only new-side line numbers,
so re-enabling add_finding's validation would wrongly reject findings
with side=LEFT (deleted-line bugs whose only anchor is an old-side
line). Return {file: {"RIGHT": {new_lines}, "LEFT": {old_lines}}}
instead, and have is_range_in_diff select the matching side from the
finding's recorded side. add_finding and publish_review's retry
filter both pass the finding's side through.
2026-05-27 17:03:33 +00:00
langsmith-engine[bot]
58b1d52fee
fix: publish_review HTTP 422 "Path/Line could not be resolved" — agent retries with identical args instead of dropping unresolvable findings (#1338)
* publish_review: drop unresolvable findings and retry once on GitHub 422

GitHub returns 422 with 'Path could not be resolved' or 'Line could not be
resolved' when an inline comment anchors to a file/line not in the PR diff.
Previously the agent retried publish_review with byte-identical args
multiple times before draining to skipped_empty_re_review=true, silently
losing findings.

- reviewer_publish.post_pull_request_review: parse 422 body and tag with
  _error_kind='unresolved_anchor' plus _raw_errors so callers can act.
- tools/publish_review._publish_review_async: when that signal fires,
  cross-check each finding's range against the run config's diff_line_set,
  drop the bad ones, and re-POST once with only the valid findings. Return
  unresolvable_findings + hint so the agent calls update_finding instead of
  retrying the same payload.
- reviewer.py: one-line prompt addendum telling the agent that
  unresolvable_findings means update_finding, not retry.
- tests: cover 422 tagging (path + line), the drop-and-retry success path,
  the retry-still-fails path, and the don't-blind-retry path when no
  diff_line_set is available.

* publish_review: fetch PR diff on demand for 422 retry filter

Reviewer runs clear configurable['diff_line_set'] before the agent
starts, so the unresolved-anchor retry path had no diff data to filter
against — in the reachable production case it dropped nothing and
returned success=False with empty unresolvable_findings, losing the
otherwise-valid comments.

Fall back to fetching the PR's unified diff via the GitHub REST API
and recomputing the line set on the fly when no cached set is
available. The cached set is still preferred when present.

---------

Co-authored-by: issues-agent <issues-agent@langchain.dev>
Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
2026-05-26 18:37:56 -07:00
Johannes du Plessis
5702a9d452
feat: reconcile reviewer comment lifecycle (#1332)
* feat: reconcile reviewer comment lifecycle

Track GitHub review threads for reviewer findings so re-reviews can resolve or reply to existing comments, and collect thumbs feedback on new review comments in LangSmith.

* fix: clarify reviewer comment lifecycle

* chore: apply reviewer formatting

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-26 16:24:34 -07:00
Johannes du Plessis
ce466889ff
fix: remove line collapsing (#1330)
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-23 00:43:48 +00:00
Johannes du Plessis
b2a0ac3b79
feat: auto-review PRs on opened / ready-for-review (#1325)
* feat: auto-review PRs on opened / ready-for-review

Trigger Open SWE Review on `pull_request` actions `opened` and
`ready_for_review` against the canonical reviewer thread (no need to
request open-swe[bot] as a reviewer). `converted_to_draft` now also
flips watch=False on the existing reviewer thread.

Draft PRs are gated by a tri-state user setting on the profile:
inherit team default, always on, or always off. The team-wide
`review_draft_prs` setting is the org-wide default; each user can
override it in My Settings.

External contributors with no Open SWE profile fall back to the team
default.

* fix: PR review comments — auth source + draft-aware watch toggle

- `process_github_pr_ready` now dispatches with `source="github"` so the
  auth resolver finds the bot token persisted on the thread. The previous
  `source="github_auto"` fell through to the email-based path in non
  bot-token-only deployments and failed with a missing-user-email error.

- `converted_to_draft` no longer unconditionally clears `watch`. When the
  PR author's effective `review_draft_prs` setting is on, watch stays on
  so subsequent pushes still trigger re-reviews while the PR is in draft.

* feat(reviewer): skip "no issues found" comment on empty re-reviews

A re-review run with no new findings to surface no longer posts another
"Open SWE Review: No issues found" comment on the PR. The "no issues"
summary now only appears on the first review of a PR — matching Devin's
behavior, where subsequent reviews are silent unless there's something
new to flag.

Resolved-thread reconciliation and ``last_reviewed_sha`` persistence
still happen on the skipped path, so findings the user just fixed still
get their GitHub threads marked resolved, and the next push event sees
an up-to-date dedup SHA.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-22 13:36:14 -07:00
Johannes du Plessis
82852f9eda
feat: tune reviewer for precision — web/wiki tools + recalibrated prompt (#1312)
* feat: tune reviewer for precision — web/wiki tools + recalibrated prompt

Reviewer agent now has web_search, fetch_url, and http_request alongside the
finding tools, so it can verify library semantics and consult the DeepWiki
auto-generated wiki for public repos (https://deepwiki.com/<owner>/<repo>)
before flagging cross-file or architectural concerns.

Prompt rewritten to push precision over recall:
- explicit severity ladder pushing reviews toward bimodal high/low instead of
  defaulting to medium
- ≤200-char description target (gold set averages ~186 chars; we were at ~436)
- mandatory docs / wiki / code lookup before flagging concurrency, security,
  or perf — the three categories that dominated false positives
- "do not flag" list covering compiler/linter-catchable nits, speculative
  claims without a concrete attacker/interleaving/scale, style preferences
  the codebase doesn't share, and test-quality nits on non-test diffs
- smart file-selection guidance for large PRs (deprioritize generated /
  vendored / pure-rename hunks)

Eval config switched to openai:gpt-5.5 + high reasoning effort for the next
benchmark run.

* trim prompt

* subagent prompting

* confidence ratings

* added medium

* enforce confidence threshold

* .

* reviewer: precision-tuned prompt + drop confidence gate

Rewrites the reviewer system prompt around a defensibility bar (anchor +
failure mode + maintainer wouldn't say "not a bug"), an explicit do-not-file
list (style nits, speculation, scope-policing, same-bug fan-out), and a
checklist of 10 bug archetypes drawn from a per-PR audit of the eval golden
set. The audit showed 145 FPs in the last eval split ~28% speculative, ~26%
style-nit, ~31% real-but-unscored (mostly same-archetype fan-out); the new
prompt targets each class directly.

Confidence is still recorded on every finding for post-hoc calibration but
no longer gates publication — the audit showed the gate was a no-op (agent
self-rated 65% of findings "high" regardless), and the prompt's defensibility
bar is the actual discipline. Drops CONFIDENCE_ORDER, CONFIDENCE_THRESHOLD,
the confidence_threshold kwarg on filter_findings_for_publish, the
confidence_filtered score_mode, and the min_confidence kwarg on the eval
target's _extract_comments — all dead once the gate is gone.

Also removes the "informational" severity tier from the Severity enum,
SEVERITY_ORDER, and all validators / tests / docstrings. It was reserved for
FYI observations the dataset never rewards.

* benchmax

* adding google provider

* slight steering

* tuning

* more tuning

* fix

* cleanup

* reducing overfitting

* Add per-repo review style profiles and inject them into the reviewer.

Dashboard users can analyze historical PR review feedback per repository,
edit the resulting style guide, and have it loaded from LangGraph Store at
reviewer runtime (including Martian eval runs) keyed by owner/name.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix review style job errors leaking exception details to clients.

Return generic dashboard messages while logging full stack traces server-side.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 18:35:00 +00:00
Johannes du Plessis
834efbc33c
feat: Adds ability to run evals against deployment (#1311)
* feat: tighten reviewer eval workflow

Require the reviewer to verify and dedupe findings before recording them, and make benchmark runs safe to execute against deployed reviewer graphs without posting GitHub reviews.

* chore: move reviewer eval settings to config

Load reviewer benchmark settings from the default eval config file so deployed eval runs do not require a wide CLI surface.

* feat: allow reviewer eval model overrides

Pass reviewer model and reasoning effort from the eval config into reviewer runs so isolated benchmark deployments can test Opus 4.7 high thinking.

* fix: use adaptive thinking for Opus 4.7

Switch Opus 4.7 model overrides to Anthropic adaptive thinking with effort instead of the deprecated budgeted thinking payload rejected by the API.

* refactor: use latest Anthropic effort API

Remove legacy Anthropic budget-token thinking support and route Anthropic efforts through adaptive thinking plus effort.

* revert prompting
2026-05-18 15:47:13 -07:00
langsmith-forge[bot]
74f5df5df8
fix: publish_review tool returns generic "Failed to POST PR review" without GitHub API status/body, agent retries with no signal (#1299)
* fix(reviewer): surface HTTP status and body excerpt for non-dict GitHub PR review responses

When post_pull_request_review received a non-dict body, it returned
None and publish_review surfaced a generic 'Failed to POST PR review'
string with no signal for the agent to adapt — leading to blind
retries with permuted cap/severity_threshold args.

Now the non-dict-body path mirrors the existing HTTPStatusError /
HTTPError paths: it returns {'_error': 'HTTP <status>: non-dict
response body: <excerpt>'} so the user-facing tool can include the
underlying detail. The bare-None branch in publish_review.py is kept
as a defensive guard with a clearer message.

* ci: apply ruff format to reviewer_publish.py

Collapse the multi-line return dict into a single line so it matches the
output of `ruff format`, unblocking the Agent lint / format-check CI jobs.

Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>

---------

Co-authored-by: LangSmith Issues Agent <issues-agent@langsmith.dev>
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>
2026-05-12 13:55:39 -07:00
open-swe[bot]
85343fab63
feat: TTL and revocation handling for cached GitHub OAuth tokens [closes AB-2322] (#1280)
* feat: TTL and revocation handling for cached GitHub OAuth tokens [closes AB-2322]

Persist github_token_expires_at alongside github_token_encrypted, treat
expired cache entries as missing so we re-resolve before kicking off
runs, and invalidate the cached ciphertext on a downstream 401 so the
next invocation gets a fresh token instead of replaying a revoked one.

Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>

* webapp: forward installation-token expiry to reviewer cache writes

The three reviewer-thread persist sites in webapp.py were calling
get_github_app_installation_token() (no expiry) and persist_encrypted_github_token
without expires_at, so cached App tokens were treated as never-expiring even
though they actually expire in ~1 hour.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
2026-05-08 22:57:01 +00:00
Johannes du Plessis
3f319b7d8a
reviewer: surface GitHub error details when publish_review fails (#1282)
post_pull_request_review used to swallow the HTTPError and return None,
so the tool result was just "Failed to POST PR review" with no status
or body — the actual cause (e.g. 422 invalid inline comment, 404 app
not installed) only lived in logs. Capture status + body and propagate
into the tool result so the agent (and traces) can see why.
2026-05-08 15:40:05 -07:00
open-swe[bot]
a9331e78e6
fix: harden http_request SSRF guard against DNS rebinding [closes AB-2321] (#1277)
* fix: harden http_request SSRF guard against DNS rebinding [closes AB-2321]

Pin DNS resolution per request hop so urllib3's connection-time lookup
cannot rebind to a private IP after _is_url_safe validated a public one.

Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>

* fix: scope DNS pin to urllib3 with reference-counted install

Address review feedback: the previous version permanently overwrote the
process-global socket.getaddrinfo on first use. Now the patch targets
urllib3.util.connection.create_connection (much narrower blast radius),
and is installed/uninstalled via reference count so no global mutation
persists once no http_request calls are in flight.

Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>

* fix: forward timeout and socket_options through pinned create_connection

urllib3 calls create_connection with timeout positional and socket_options
as a keyword. The previous wrapper only read kwargs, silently dropping the
caller's connect timeout (so a slow validated IP could hang) and TCP
options like TCP_NODELAY. Accept both positionally and forward them to
the underlying socket.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
2026-05-08 22:06:25 +00:00
Johannes du Plessis
3f9dbb6597
feat: add Slack reaction feedback to LangSmith (#1231)
* feat: add Slack reaction feedback to LangSmith

Record Slack reaction feedback against explicitly mapped LangGraph runs so user ratings are idempotent and tied to the message they reacted to.

* Address review feedback on Slack reaction → LangSmith feedback

- langsmith.py: drop lru_cache on _build_langsmith_feedback_clients so
  rotated keys / late env hydration are picked up; dedupe by (key, url)
  tuple instead of key alone so the same key pointing at different
  endpoints (cloud + self-hosted) builds both clients.
- langsmith.py: treat LangSmithNotFoundError on delete_feedback as
  success — out-of-order or redelivered reaction_removed events would
  otherwise loop forever on Slack's retry policy.
- slack_feedback.py: include channel_id in _feedback_key so the same
  message_ts in two channels can't collide on the same feedback id.
- slack_feedback.py: treat conflicting +/- reactions from one user as
  ambiguous (clear feedback) instead of averaging to a misleading 0.5.
- slack_feedback.py + slack.py + webapp.py: gate reaction handling to
  the user who triggered the run (stored in the slack_run_map mapping
  alongside run_id). Prevents bystanders in shared channels from
  polluting eval feedback.
2026-05-08 14:24:12 -07:00
Johannes du Plessis
bdea1fb6da
feat(open-swe): always anchor findings to a single line (#1264)
* feat(open-swe): always anchor findings to a single line

GitHub renders multi-line review comment ranges as walls of context above
the comment, which buries the point. Match Devin Review's behavior and
always collapse `end_line` to `start_line` so each finding is anchored to
the single most relevant line.

Removes the (now unused) `MAX_FINDING_RANGE_LINES` cap and
`clip_finding_range` helper.

* fix(open-swe): collapse end_line before diff-range check
2026-05-07 20:26:59 -07:00
Johannes du Plessis
089db06135
feat(open-swe): collapse oversized finding ranges to start line (#1263)
Anchoring a finding to a 25-line range (e.g. an entire function) makes
GitHub render the whole block as context above the comment, burying the
review text. Cap ranges at 10 lines and collapse to the start line when
exceeded; small ranges (≤10 lines) still render as a multi-line anchor.
Updated the reviewer prompt to anchor tightly rather than range-select
the whole function.
2026-05-07 19:59:25 -07:00
Johannes du Plessis
0212a60f10
feat(open-swe): cap reviewer suggestions at 4 lines (#1262)
* feat(open-swe): cap reviewer suggestions at 4 lines

Long suggestion blocks read as the reviewer rewriting the code rather
than flagging an issue, which clutters PR comments. Steer the reviewer
toward description-only findings for non-trivial fixes, and enforce the
cap in `add_finding` / `update_finding` so suggestions over 4 lines are
dropped (the finding itself still publishes).

* fix(open-swe): don't clobber prior suggestion on over-cap update

`update_finding` was setting `suggestion=None` whenever clip_suggestion
dropped the input, which silently wiped any existing suggestion on the
finding. Distinguish the three cases: empty string clears, valid value
sets, over-cap value is rejected without touching the stored field.
2026-05-07 19:27:16 -07:00
Johannes du Plessis
892347041f
feat(open-swe): post review summary to Slack on first review (#1258)
When a Slack user kicks off a PR review with `@open-swe review <pr-url>`,
the reviewer agent now posts a one-line summary back to the Slack thread
when it finishes — either "No issues found" or "found N potential
issue(s)" with a link to the GitHub review.

The reviewer agent has no Slack tools by design, so the summary is sent
host-side from `publish_review` after the GitHub review POST succeeds.
The Slack channel/thread_ts is persisted on reviewer thread metadata at
trigger time and read back on publish. Re-reviews triggered by push
events stay silent in Slack to avoid noise on the original thread.
2026-05-07 16:11:36 -07:00
Johannes du Plessis
88a55057ea
fix(open-swe): host-format the review summary, drop agent prose (#1257)
The reviewer agent was writing a 1–2 sentence "top-level take" as the
review body, which produced noisy paragraph-style summaries on PRs
("Reviewed the PR. The new ALLOWED_GITHUB_REPOS allowlist…"). Devin's
review comment is just a one-liner (`✅ No Issues Found` or
`**Devin Review** found N potential issue.`), and was preferred in the
internal A/B vs Graphite.

Drop the `summary` parameter from `publish_review`; render a fixed,
host-formatted body in `render_review_body` instead. Update the reviewer
prompt to forbid prose summaries.
2026-05-07 15:41:40 -07:00
Johannes du Plessis
03d9f23645
fix(open-swe): always post a review summary, even with no findings (#1256)
* fix(reviewer): log every push/close early-return so 'silent ignore' is debuggable

Pushes to PRs that haven't had a first review fall through the watch
handler because the reviewer thread doesn't have kind=reviewer set.
Without log lines on the early-return paths, this scenario was
indistinguishable from 'webhook reached the handler at all' in the
hosted log stream.

Now every early-return logs at info or debug:
- info when a real PR exists but the reviewer thread isn't set up
  (with a hint pointing at the trigger paths the user can use)
- info when the repo isn't in the reviewer allowlist
- debug for benign skips (non-branch refs, branch deletions,
  already-reviewed head_sha)

* fix(reviewer): always post a summary review, even with no findings

The publish_review tool gated POSTing on `inline_comments or summary`,
so when the agent called publish_review() with no args on a clean PR
the result returned `success: true` but no GitHub review was posted —
the user got silence instead of a "no issues found" comment.

- Drop the gate so publish_review always POSTs.
- Friendlier no-findings render: `**No issues found.**` when the
  findings list is empty, vs. `**No issues at or above \`<sev>\`
  severity.**` with hidden count when only sub-threshold findings
  exist. Agent summary renders below.
- Prompt now requires the agent to always pass a `summary` so the
  body is meaningful; calls out specifically not to skip on a clean PR.
2026-05-07 22:16:20 +00:00
Johannes du Plessis
378b95266e
feat: implement reviewer findings, publish_review, and watch mode (#1253)
* feat: implement reviewer findings, publish_review, and watch mode

Build out the reviewer agent end-to-end against the design in
REVIEWER_DESIGN.md:

- Findings as first-class state on the reviewer thread metadata
  (`agent/reviewer_findings.py`): Finding TypedDict with start_line/end_line
  ranges, suggestion text for ```suggestion blocks, github_review_comment_id
  for cross-run reconciliation, diff_hunk for UI rendering. Thread-level
  metadata gets `kind=reviewer`, `pr`, `last_reviewed_sha`, `watch` so a
  future frontend can list reviewer threads via the langgraph SDK.
- Diff utilities (`agent/reviewer_diff.py`): parse_unified_diff,
  compute_diff_line_set for in-diff validation, extract_diff_hunk for
  caching the hunk on a Finding, compute_diff_in_sandbox for SHA-to-SHA
  diffs against the prepped repo.
- Tools: `add_finding` (validates against the diff line set so out-of-diff
  ranges fail at creation, not at GitHub-publish), `update_finding`,
  `list_findings`, `publish_review`. The reviewer agent's tool list is
  swapped from `[]` (direct shell `gh api` calls) to these four.
- Publish path (`agent/reviewer_publish.py` + `agent/tools/publish_review.py`):
  one POST /reviews call with body + inline comments + ```suggestion blocks,
  per-comment IDs stored back on findings, GraphQL `resolveReviewThread`
  fired for findings transitioning open->resolved on a re-review.
- Reviewer graph: deterministic clone-or-fetch + checkout in the factory
  before the agent's first model call (warm- and cold-path symmetric);
  computed diff and in-diff line set passed via runnable config; system
  prompt rewritten for the single-evolving-findings model, severity ladder,
  in-diff-only discipline, and watch-mode reconciliation flow.
- Watch mode in webapp.py: `push` event + `pull_request` closed/reopened
  added to supported events. New `process_github_push_event` resolves the
  open PR for the pushed branch, gates on the reviewer thread's `watch`
  flag, builds a re-review configurable, and triggers a run on the same
  canonical thread. `process_github_pr_close` toggles watch on
  closed/reopened. `set_reviewer_thread_metadata` is called on first
  review to install `kind=reviewer` + PR identity + watch=True.
- Eval harness: target.py now extracts `add_finding` calls (mapped to the
  legacy {file, line, body, severity} shape the judge expects) and passes
  the right configurable so the prep step has base/head SHAs.
- Tests: new unit suites for findings helpers, diff parsing, finding tools,
  publish rendering + GraphQL resolve, and watch-mode webhook handlers
  (push triggers re-review only when watching, idempotent on unchanged
  head SHA, PR close disables watch). Updated existing reviewer-webhook
  tests to mock `set_reviewer_thread_metadata`.
- REVIEWER_EVAL_PLAN.md removed per user request; folded relevant context
  into REVIEWER_DESIGN.md.

* fix(reviewer): correct git diff flags, scope, dedup, and review-comments URL

Address PR #1253 review findings:

- compute_diff_in_sandbox dropped the invalid `--no-prefix=false` flag
  (`option no-prefix takes no value` — every prep run was failing
  silently and the agent saw an empty diff).
- compute_diff_in_sandbox grew a `merge_base` flag. First-review path
  now uses three-dot `base...head` (the merge-base diff GitHub renders
  on Files-changed) so we don't pick up changes that landed on the base
  branch after the PR diverged. Re-review delta keeps two-dot
  `last_reviewed_sha..head` since that's exactly the new commits.
- publish_review skips findings that already carry
  `github_review_comment_id`. Without this, watched re-reviews
  re-posted every previously surfaced finding, and only the most-recent
  duplicate's id would later resolve when the issue got addressed.
- fetch_review_comments URL now includes `{pull_number}` —
  `/repos/{owner}/{repo}/pulls/{pr_number}/reviews/{review_id}/comments`
  is the canonical endpoint; the old form 404s, so comment ids were
  never stored and watch-mode resolution couldn't run.

Three new tests cover: three-dot vs two-dot wiring, no `--no-prefix`
flag in the executed command, and that publish_review does not re-post
findings whose `github_review_comment_id` is set.

* fix(reviewer): default publish cap from 15 to 4

A clean PR with one critical issue padded out by three lower-severity
findings is fine; fifteen is review spam. The agent can override per
call when a PR genuinely warrants more.
2026-05-07 14:48:43 -07:00
open-swe[bot]
5a01aff1c7
feat: only post Slack 'Working on it!' on first thread mention (#1250)
* feat: only post Slack 'Working on it!' on first thread mention

* feat: randomize Slack trace reply phrase

Pick from a small list of friendly phrases instead of always saying
'Working on it!' so the bot feels less robotic. Explicit messages (e.g.
'Taking a look...' from PR review path) are unaffected.

* adjust phrases

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
2026-05-07 12:37:46 -07:00
Johannes du Plessis
1319347dd9
feat: route Slack PR review requests (#1245)
* feat: route Slack PR review requests

Add a lightweight Slack review command path that starts the reviewer graph directly and gives the core agent a handoff tool when review requests are misrouted.

* fix: harden Slack PR review routing

* fix: validate Slack PR review URLs

* fix: preserve malformed GitHub review routing
2026-05-06 17:14:43 -07:00
Johannes du Plessis
65f6b4636b
feat(open-swe): remove github_comment and enable reviewer trigger (#1244)
* Use gh for reviewer inline comments

* Trigger reviewer on PR review requests

* Format reviewer webhook test

* Add GitHub repo allowlist

* Separate reviewer repo allowlist

* only implement allowlist for reviewer
2026-05-06 16:14:38 -07:00
Johannes du Plessis
ace71b0fd0
feat: add reviewer graph + eval target wiring (#1241)
* feat: add reviewer graph + eval target wiring

- New `reviewer` graph (`agent/reviewer.py`) registered in langgraph.json
  alongside the main `agent` graph. Reuses the same sandbox lifecycle,
  GH proxy auth, and middleware primitives from `agent.server`, but with
  a narrower tool set, a reviewer-specific system prompt, no
  commit/push, and the `task` (subagent) tool stripped via
  `_ToolExclusionMiddleware` so review stays in one context.

- New `github_comment` tool: agents call it once per issue with
  `(file, line, body, severity)` and the eval scores those calls
  against golden comments.

- `ensure_no_empty_msg` middleware (the no_op nudge) is intentionally
  *not* on the reviewer's stack — that middleware exists to enforce the
  main agent's "always finalize via Slack/Linear/PR" contract, which
  the reviewer doesn't have. The main agent's behavior is unchanged.

- `evals/reviewer/target.py`: send PR info as a user message, extract
  every `github_comment` tool call (multiple expected per review) into
  the run output.

- `evals/reviewer/judge.py`: per-example evaluator now returns a list
  of metrics under `{"results": [...]}` so LangSmith averages each
  numeric key (f1/precision/recall/tp/fp/fn) across the experiment in
  the UI. Dropped the broken `aggregate_pr` summary evaluator that
  reached for an attribute that doesn't exist on `RunTree`.

- `evals/reviewer/run_eval.py`: `--limit` now slices the dataset via
  `client.list_examples(limit=N)` since `aevaluate` doesn't accept
  `max_examples`.

- Makefile: `dev` and `run` targets now use `uv run` so they work
  without an activated venv.

* resolve comments

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-06 10:15:58 -07:00
Johannes du Plessis
96774f20ae
feat: move github workflows to gh cli (#1238)
* feat: move github workflows to gh cli

Use LangSmith proxy auth to support gh-driven GitHub workflows while removing custom GitHub wrapper tools.

* docker ignore + snapshot and docker image updates

* updated image and instructions

* removing open_pr if needed after agent call
2026-05-04 18:03:53 -07:00
Johannes du Plessis
13f5d8a1c9
fix: preserve existing PR descriptions (#1237)
* fix: preserve existing PR descriptions

* test: update existing PR label expectations
2026-05-04 11:43:32 -07:00
Brace Sproul
3405d145ac
feat: add edit_pull_request tool for editing PR titles/descriptions (#1063)
* feat: add edit_pull_request tool for editing PR titles and descriptions

* fix: patch auth flow in open PR middleware tests

* fix: support app token for editing PRs

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
2026-05-01 18:12:41 -07:00
open-swe[bot]
b829cef97a
feat: add release note section to PR body template (#1202)
* feat: add release note section to PR body template

The langchainplus repo PR template includes a ## Release Note section,
but the agent hardcoded PR body template only had ## Description and
## Test Plan. This caused the release note section to be dropped from
agent-created PRs.

* docs: align PR body argument docs

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
2026-05-01 15:32:30 -07:00
langsmith-forge[bot]
bd97678a5e
fix: prevent futile retry loop when commit_and_open_pr fails (#1210)
* fix: prevent futile retry loop when commit_and_open_pr fails with git/API errors

- Root cause: when git checkout or GitHub PR API fails, the tool returned a generic {"success": false} error with no signal that retrying is futile, causing the agent to loop 9-13+ times until hitting the 1000-step recursion limit
- Change: (1) git_checkout_branch now returns (bool, str) so the actual git error output is surfaced in the tool response; (2) checkout and PR creation failures now include "fatal": true and an explicit "Do not retry" message; (3) prompt.py COMMIT_PR_SECTION adds an explicit instruction to stop on fatal errors
- Verified: 109 unit tests pass, no regressions

* fix: skip PR safety net on fatal commit failures

* style(open_pr): ruff-format fatal retry skip condition

---------

Co-authored-by: LangSmith Forge <forge-agent@langsmith.ai>
Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
2026-05-01 22:05:22 +00:00
langsmith-forge[bot]
0464126c83
fix: warn against using http_request for GitHub PR creation (#1203)
* fix: warn against using http_request for GitHub PR creation

- Root cause: http_request tool description and system prompt lacked explicit guidance not to use it for GitHub PR operations, causing the agent to fall back to it and receive 401 Unauthorized responses
- Change: added clear warnings to both the http_request docstring and the TOOL_USAGE_SECTION in prompt.py directing agents to use commit_and_open_pr instead
- Verified: docstring and prompt changes are minimal and scoped

* fix: clarify http_request PR guidance

---------

Co-authored-by: LangSmith Forge <forge-agent@langsmith.ai>
Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
2026-05-01 14:14:44 -07:00
langsmith-forge[bot]
15a7268d30
fix: return actionable error when push fails with workflows scope error (#1153)
* fix: return actionable error when git push fails due to workflows scope

- Root cause: push failure with "workflows scope" error returned a generic
  "Git push failed: ..." message, causing the agent to retry 10+ times
- Change: detect "workflows" + "scope" in push output and return a clear
  message instructing the agent to remove .github/workflows/ file changes
- Verified: unit tests cover both the workflow-scope path and the
  non-workflow path to prevent regressions

* fix: detect github workflow permission push failures

* style: ruff-format checkout line in commit_and_open_pr

---------

Co-authored-by: LangSmith Forge <forge-agent@langsmith.ai>
Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
2026-05-01 13:09:54 -07:00
langsmith-forge-dev[bot]
c61d8b0376
fix: stop agent retrying commit_and_open_pr on 403 permission denied (#1123)
* fix: stop agent retrying commit_and_open_pr on 403 permission denied

Detect 403/permission-denied push failures in commit_and_open_pr and
return a PERMANENT_FAILURE message so the LLM stops retrying. Also add
prompt-level guidance to the COMMIT_PR_SECTION reinforcing this. Add
unit tests covering both the 403 and non-403 push failure paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: stop safety net retrying permanent push failures

Skip the after-agent PR fallback when commit_and_open_pr reports a permanent GitHub push authorization failure, while preserving fallback behavior for recoverable failures.

---------

Co-authored-by: Claude Agent <agent@anthropic.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
2026-05-01 19:37:53 +00:00
langsmith-forge-dev[bot]
4060933ce4
feat: add github CI check run tools for shepherding CI (#1121)
* feat: add github CI check run tools for shepherding CI

Add get_pr_check_runs and rerun_failed_check_runs tools that authenticate
using the GitHub App installation token so the agent can query and retry
CI status on private repos without relying on GH_TOKEN or unauthenticated
http_request calls.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: handle paginated GitHub CI results

* refactor(github_ci): address review feedback

- Rename rerun_failed_check_runs -> rerun_failed_workflow_runs and clarify
  in docstrings that the tool only retries GitHub Actions workflow runs
  (not third-party CI checks surfaced by get_pr_check_runs).
- Skip action_required workflow runs when rerunning; those need manual
  approval, not a rerun.
- Run rerun-failed-jobs requests concurrently via asyncio.gather instead
  of sequentially.
- Fix latent pagination bug in _fetch_paginated_items where caller-supplied
  params could overwrite per_page/page and break the end-of-pagination
  check; reserved keys now always win and the threshold uses a PER_PAGE
  constant.
- Set an explicit 30s httpx timeout so a hung GitHub call cannot stall
  the agent loop.
- Restore alphabetical ordering of tools in agent/tools/__init__.py.
- Add tests for: a 500 surfaced on a later pagination page, and
  action_required runs being filtered out of rerun candidates.

---------

Co-authored-by: Claude Agent <agent@anthropic.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
2026-05-01 12:19:04 -07:00
langsmith-forge-dev[bot]
d63780a77f
fix: add get_pr_review_comments tool to fetch PR review comments with auth (#1043)
* feat: add get_pr_review_comments tool for authenticated GitHub API access

The agent was asking users to paste PR review comments because it had no
tool to fetch them with auth. This adds get_pr_review_comments, which uses
the GitHub App installation token to fetch all three comment types (thread
comments, inline review comments, review submissions) from private repos.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* u

* u

---------

Co-authored-by: Forge Agent <agent@forge.ai>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Palash Shah <palash@langchain.dev>
Co-authored-by: Palash Shah <35114859+Palashio@users.noreply.github.com>
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-04-30 17:01:13 -07:00
Aran Yogesh
b6ea229a46
feat: give agent ability to read cross-posted Slack message links [closes OPE-37] (#1200)
* feat: give agent ability to read cross-posted Slack message links [close OPE-37]

* refactor: clean up Slack link resolution code

* linting

* refactor: address PR review feedback for Slack link resolution

* linting

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-04-29 17:42:27 -07:00
Aran Yogesh
920a8a7624
feat: open PRs under user's name and add OpenSWE label (#1215)
* feat: open PRs under user's name and add OpenSWE label

* feat: use user token for PR authorship, add OpenSWE label, and consolidate fallback logic

* linting

* fix: address review nits for PR authorship and labeling

Fix docstring casing, add debug logging for 422 existing-PR search
fallback, tighten test type annotations, and add missing HTTPError
fallback test.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-04-29 17:34:00 -07:00
Aran Yogesh
a3a40a1bec
Revert "fix: Auto assign PRs to creator (#1211)" (#1214)
This reverts commit e9b94ac8ba.
2026-04-22 13:46:44 -07:00
Brace Sproul
e9b94ac8ba
fix: Auto assign PRs to creator (#1211)
* fix: Auto assign PRs to creator

* cr
2026-04-21 14:13:55 -07:00
Aran Yogesh
2039fe6660
feat: stop auto-cloning and let agent manage repo setup [closes OPE-21] (#1159)
* feat: authenticate git operations via sandbox proxy instead of credential files

* feat: authenticate git operations via sandbox proxy instead of credential files

* feat: authenticate git operations via sandbox proxy instead of credential files

* removing logger.info

* formatting and linting

* fix: resolve lint errors in server.py (imports, unused vars, undefined names)

* feat: use opaque proxy headers for GitHub auth in sandbox

* linting formatting and test changes

* linting

* Delete .claude directory

* Delete tests/evals directory

* fix: address PR review — guard missing tokens, quote shell paths, add proxy auth tests

* fix: restore authorship, branch_name support, and installation token for PR creation

* linitng

* fix: move installation token fetch before commit, clean up dead proxy validation code

* feat: stop auto-cloning and let agent manage repo setup [closes OPE-21]

* feat: stop auto-cloning and let agent manage repo setup [closes OPE-21]

* fix: address review feedback — restore agents_md, add git user config, lint fixes

* fix: drop github_token arg from sandbox creation, use generic create_sandbox factory with langsmith-only proxy config

* fix: use _get_langsmith_api_key() for prod key fallback, warn when API key missing for proxy config

* linting

* linting

* feat: add installation token auth to list_repos GitHub API call

* agents.md update

* linting

* fix: address PR review feedback — shell precedence bug in prompt, remove dead code

* linting

* Apply suggestion from @bracesproul

Co-authored-by: Brace Sproul <braceasproul@gmail.com>

* Apply suggestion from @bracesproul

Co-authored-by: Brace Sproul <braceasproul@gmail.com>

* fix: address PR review feedback — restore {working_dir} in prompt, remove clone code block

* fix:Extract check_or_recreate_sandbox utility from inline sandbox health check

* fix: address PR review feedback — async list_repos, restore template name, fix prompt colon

* fix: resolve merge conflicts with main, adopt deepagents v0.5.0a4 LangSmithSandbox

* linting

* yogesh/ope-21-stop-auto-cloning

* Update agent/tools/list_repos.py

Co-authored-by: Brace Sproul <braceasproul@gmail.com>

* Update agent/prompt.py

Co-authored-by: Brace Sproul <braceasproul@gmail.com>

* feat: address PR review — list_repos uses GitHub API only, PR trigger includes org/repo

* linting

* feat: address PR review feedback — list_repos pagination, simpler return, sandbox health check

* feat: support listing repos for personal user accounts via is_organization flag

---------

Co-authored-by: Brace Sproul <braceasproul@gmail.com>
2026-04-10 17:04:55 -07:00
Aran Yogesh
67c782c295
fix: block open-swe from approving PRs (#1177)
* fix: block open-swe from approving PRs

* linting

* fix: add case-insensitive APPROVE guard and unit tests
2026-04-09 11:45:08 -07:00
Aran Yogesh
4d4f5fbfc7
fix: proxy config restored the branch yogesh/GitHub auth proxy (#1173)
* feat: authenticate git operations via sandbox proxy instead of credential files

* feat: authenticate git operations via sandbox proxy instead of credential files

* feat: authenticate git operations via sandbox proxy instead of credential files

* removing logger.info

* formatting and linting

* fix: resolve lint errors in server.py (imports, unused vars, undefined names)

* feat: use opaque proxy headers for GitHub auth in sandbox

* linting formatting and test changes

* linting

* Delete .claude directory

* Delete tests/evals directory

* fix: address PR review — guard missing tokens, quote shell paths, add proxy auth tests

* fix: restore authorship, branch_name support, and installation token for PR creation

* linitng

* fix: move installation token fetch before commit, clean up dead proxy validation code

* fix: drop github_token arg from sandbox creation, use generic create_sandbox factory with langsmith-only proxy config

* fix: use _get_langsmith_api_key() for prod key fallback, warn when API key missing for proxy config

* linting

* linting

* fix: resolve merge conflicts with main, adopt deepagents v0.5.0a4 LangSmithSandbox
2026-04-08 15:02:52 -07:00
Aran Yogesh
9aba4d0545
Revert "feat: authenticate git operations via sandbox proxy instead of creden…" (#1170)
This reverts commit 6305e13dc6.
2026-04-07 18:45:33 -07:00