Commit graph

50 commits

Author SHA1 Message Date
Johannes du Plessis
39a26e16b5
fix: optimize agent thread lists (#1570)
* fix: optimize agent thread lists

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: refresh missing thread run status

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-18 10:09:22 -07:00
Johannes du Plessis
8e39f62122
feat: activate PR babysitting UI toggles for autofix and trigger mode (#1561)
* feat: activate PR babysitting UI toggles for autofix and trigger mode

Remove the "coming soon" gating on the Autofix Mode, Autofix Severity
Threshold, and Trigger Mode controls in the review settings page so
admins can enable CI auto-fix and review-comment resolution on PRs
that Open SWE opens. The backend (ci_autofix.py, webapp.py webhook
routing) was already fully wired — only the UI was disabled.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* feat: simplify autofix to on/off toggle, remove severity threshold

Replace the four-level AutofixMode (off/low/medium/high) and the
autofix_severity_threshold setting with a single boolean
autofix_enabled toggle. The severity threshold was leftover from the
reviewer finding-severity model and does not apply to CI autofix;
the agent should fix any failing CI and resolve any comments on PRs
it opens.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* feat: move autofix toggle to per-user profile, remove team-level setting

The autofix toggle is now per-user (auto_fix_ci in the user profile)
instead of team-level (admin-only). This uses the existing auto_fix_ci
field that was already in ProfileUpdate but never wired up.

Changes:
- ci_autofix.py: check per-user auto_fix_ci profile flag after
  resolving the agent thread's github_login, instead of checking
  team-level autofix_enabled before knowing the PR
- webapp.py: removed early is_autofix_enabled() webhook gates; the
  per-user check now happens in ci_autofix.py once the thread is found
- team_settings.py: removed autofix_enabled field, is_autofix_enabled()
- cloud-agents.tsx: enabled the auto_fix_ci toggle (was comingSoon)
- review.tsx: removed the admin-level autofix switch
- Updated tests and AGENTS.md

The agent graph (not the reviewer) is what gets dispatched - this was
already correct in ci_autofix.py line 223: client.runs.create(
thread_id, "agent", ...).

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* feat: batch PR babysitting events

Remove the leftover trigger-mode gate from PR babysitting and batch new CI/review events while an agent run is already active so the running agent can handle the latest PR state before finishing. Also moves review-feedback permission checks behind the per-user opt-out and applies the auto-fix profile gate to merge-conflict babysitting.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: consume batched babysitting events

Teach the agent queue middleware to turn pending PR babysitting metadata into an injected instruction for the active run, so batched CI/review events are not dropped while still avoiding duplicate run creation.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: address review findings in PR babysitting batching

- Route batched events through the LangGraph store (read in-process by the
  message-queue middleware) instead of a per-model-call threads.get on every
  agent thread.
- Only record an attempt / mark the head SHA handled on a real dispatch, not
  on a batch, so an event isn't permanently dropped if the in-flight run ends
  before consuming it.
- Carry the reviewer's comment through batched review feedback instead of
  replacing it with a generic re-check nudge.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-17 14:12:04 -07:00
Johannes du Plessis
60b7f4677a
feat: add user-scoped Currents.dev API key for e2e test investigation (#1566)
* feat: add user-scoped Currents.dev API key for e2e test investigation

Allow each user to configure their own Currents.dev API key on the
Profile Settings page. The key is encrypted at rest in a per-user
LangGraph Store namespace and feeds server-side read-only tools that
query the Currents REST API (runs, instances, projects, test results)
so agent runs can inspect e2e test failures including screenshots and
DOM snapshots.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: add pagination cursors to currents_list_project_runs

Address review feedback: forward starting_after/ending_before cursor
parameters to /projects/{projectId}/runs so the agent can paginate
beyond the first 50 results.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-17 13:59:55 -07:00
Johannes du Plessis
efe07486e2
feat: weight merged PRs above LOC in usage leaderboard sorting (#1563)
Move merged_prs to the primary sort key in the agent usage leaderboard,
ahead of agent_loc, prs_opened, and agent_runs. Update the UI description
to reflect the new ranking order.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-17 10:59:42 -07:00
Johannes du Plessis
d2c232cc54
feat: add browser notifications for agent run completion (#1558)
* feat: add browser notifications for agent run completion

Request notification permission when a user starts their first agent
run from the home page. A toggle in Profile Settings lets users
enable/disable desktop notifications. When a run transitions from
running to a terminal state (finished/error/interrupted), a browser
notification is fired — suppressed for the thread the user is
currently viewing.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: notify for active thread when page is in background tab

Only suppress the notification for the active thread when the page is
actually visible. If the user switched to another browser tab, the
notification fires even for the thread they have open.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-17 09:24:44 -07:00
Johannes du Plessis
0927f2dd9c
feat: Run reviewer eval in a GitHub Action; dashboard becomes read-only (#1556)
* Run reviewer eval in a GitHub Action; make dashboard a read-only progress view

The dashboard launched the eval as a subprocess inside the serving deployment
worker, so a container recycle killed long runs and discarded results that had
already completed server-side. Move the harness to a workflow_dispatch Action
(run on prod). run_eval now publishes status/progress/log-tail to the LangGraph
store record the dashboard reads, so /admin/evals stays a live view; a killed
Action surfaces as failed via the stale-heartbeat reconcile.

* reviewer_eval workflow: pass inputs via env, no shell interpolation

Addresses the reviewer finding: workflow_dispatch string inputs were
interpolated into the run: block (limit unquoted), allowing shell injection in
a job holding LANGSMITH/ANTHROPIC keys. Pass inputs through env and reference
quoted "$VARS"; validate limit is numeric and build its flag in bash.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-16 19:38:36 -07:00
Johannes du Plessis
e58b609b2f
fix: Simplify review explanation: full-width, plain prose, no diff links (#1547)
* Simplify review explanation: full-width, plain prose, no diff links

* Update _build_prompt test for plain diff fences

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-16 15:32:24 -07:00
Johannes du Plessis
876836bdfe
Normalize frontend typography + enable font smoothing (#1546)
Lighter, more consistent type across the dashboard and agent UI:
unify title sizes, drop semibold/bold in chrome to medium, harmonize
stray text-sm to text-xs, normalize chat markdown headings, and enable
antialiased font smoothing (fixes heavy text on dark backgrounds).

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-16 14:56:11 -07:00
Johannes du Plessis
801f93b4de
feat: AI-sorted PR review view with diff grouping (#1544)
* feat: AI-sorted PR review view with diff grouping

Group a PR's changed files into a logical top-to-bottom walkthrough via a best-effort structured-output LLM pass, kicked off concurrently with the reviewer run (~0 added latency) and persisted on reviewer thread metadata. Results render in the review UI behind an AI sorted / file tree toggle that persists across PRs; the view falls back to the file tree when groups are absent or stale.

Adds a grouping-model team default (inherits the Reviewer subagent model when unset) and the admin RolePicker for it.

* feat(reviews): richer AI-sorted explanations + sidebar polish

Sidebar group rows get Devin-style spacing (dividers, padding), a per-group file list (click a file to jump to its diff), inline-code chips in titles, and an accent Read explanation link.

Group explanations are now rich markdown: the grouping prompt feeds per-hunk line ranges and asks for inline code, a short code block, and [path:line](#loc=...) references. The Markdown renderer turns those #loc= links into in-page buttons that scroll the diff to the hunk and highlight the range, reusing the existing selectedLines path.

* fix(reviews): drop stale diff groups from the AI-sorted view

When groups were generated for a previous head, a persisted "ai" view in localStorage still rendered the outdated walkthrough. groupedView now returns null on diff_groups_stale, so the file-tree fallback is used and the view toggle hides until fresh groups arrive.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-16 13:59:37 -07:00
Johannes du Plessis
e3025dee77
Reviewer eval admin: configurable runs + stacked form layout (#1540)
Drive dashboard-triggered reviewer eval runs with per-run model, effort,
score mode, severity threshold, cap, limit, and concurrency overrides, plus
per-example start/finish/error logging in the eval target.

Rework the admin eval form from the label-left/control-right SettingsRow
(which crushed the description column when packing 3-4 wide inputs) into
stacked field groups with captioned inputs in a responsive grid.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-16 10:12:35 -07:00
Johannes du Plessis
0108d764d9
fix(ui): resolve eslint errors in dashboard components (#1538)
Clears ~83 pre-existing eslint errors in the UI: auto-fixable rules
(array-type, import/order, sort-imports, type-only imports, redundant
assertions/conditions) plus manual fixes for unnecessary conditions and
banned @ts-nocheck directives. The 8 ported components excluded from
tsconfig are now also ignored by eslint so type-aware linting no longer
fails to parse them.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-15 17:29:38 -07:00
Johannes du Plessis
911c835c2a
feat: chat with your PR on the review page (#1534)
* feat: chat with your PR on the review page

Add a sandbox-less `chat` graph that answers questions about a single PR
from its diff, the published review findings, and read-only GitHub access.

- agent/chat.py: deepagents graph, no sandbox (default StateBackend, file
  mutation + execute tools excluded). PR context is seeded as virtual files
  under /pr/; a repo-scoped App token is resolved in-graph.
- tools: read_repo_file, search_repo_code, list_review_findings.
- dashboard/review_chat_api.py + routes: per-user chat thread, LangGraph
  stream/commands/state/history proxy pinned to the chat assistant, seeds
  diff/findings/overview on first run. Gated by repo access.
- UI: Chat tab wired to a chat-scoped StreamProvider (replaces Coming Soon).

* feat: admin setting for review-chat default model

Add a 'Open SWE Review Chat' default to team settings (default_chat_model /
default_chat_reasoning_effort). get_team_default_model("chat") inherits the
Agent default when unset; the chat graph resolves through it. Admin RolePicker
gains an 'Agent default' inherit option that clears the override.

* feat: multi-conversation review chat (tabs, new chat, history)

Replace the single per-PR chat thread with multiple per-user conversations:
- threads minted client-side; first message persists with a title derived
  from the prompt.
- list + delete endpoints; chat panel gets a tab strip (history), new-chat
  (+), close (x), refresh, an intro greeting, and suggested prompts.
- get_review_chat now returns availability only (ids are client-minted).

* ui fixes

* ui: review-chat history dropdown, full-width AI replies, resizable side panel

* fix(review-chat): enforce per-user thread ownership on proxy endpoints; reseed PR context on head change

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-15 17:17:30 -07:00
Johannes du Plessis
be2fe7131b
feat: live reviewer eval logs on a dedicated admin page (#1527)
* feat: stream reviewer eval logs on a dedicated admin page

Stream the eval subprocess output into a rolling log_tail and persist it
during the run (was only captured at exit), so the live output is visible
while the eval runs. Move the eval runner off the admin page onto its own
/admin/evals page (linked like Review Style Prompts) with a live log viewer.

* chore: drop unrelated SSR-register drift from generated route tree

* fix(ui): pre-bundle workbox-window to stop dev re-optimize reload

The PWA service worker (devOptions.enabled) pulls workbox-window, which
Vite discovers after first render and re-optimizes, forcing a reload that
cancels in-flight code-split route imports (Failed to fetch dynamically
imported module). Pre-bundling it via optimizeDeps.include avoids the
mid-session reload.

* fix(ui): suppress html hydration warning for pre-hydration theme script

The inline theme script sets class="dark"/color-scheme on <html> before
React hydrates, so the prerendered HTML never matches. suppressHydrationWarning
on <html> silences the (expected) one-level attribute mismatch.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-15 11:29:16 -07:00
Johannes du Plessis
eb18b07b20
feat: trigger reviewer evals from the admin page (#1524)
* feat: trigger reviewer evals from the admin page

Add an admin-only "Reviewer eval" section + endpoints that launch the
reviewer benchmark as an isolated subprocess against the running
deployment, with live status and the LangSmith experiment link. Route
eval traces to a dedicated open-swe-evals project so they stay out of
the production tracing project.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: reconcile reviewer eval status via heartbeat, not local process

The persisted record is shared across workers but _PROCS is process-local.
The owning worker now refreshes a heartbeat while the subprocess runs, and
status is only reconciled to failed once the heartbeat is stale, so a poll on
a worker without the local handle no longer kills a live run (and a duplicate
start is rejected across workers).

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-15 09:48:12 -07:00
Johannes du Plessis
67239bb2c3
perf: speed up Reviews list (My PRs + All PRs) (#1518)
* perf: speed up Reviews list (My PRs + All PRs)

Push the "My PRs" author filter into the threads.search metadata
(pr.author containment) instead of scanning up to 1000 reviewer threads
in Python, and replace the per-repo GitHub access check (an N+1 of
sequential GET /repos calls) with a single per-login accessible-repo set,
cached for 60s. Detail endpoints still re-validate access live.

Frontend: prefetch the inactive tab and adjacent page on hover/focus so
tab switches and pagination are instant.

* fix: don't cache repo access for the reviews list

The /reviews list is an authorization boundary for private PR metadata
(repo/PR titles, branches, authors, finding counts). A cross-request TTL
cache on the accessible-repo set could surface that metadata for up to
60s after a user lost repo access. Resolve the set fresh per request
instead — still a fixed, repo-count-independent burst of GitHub calls
(no per-repo N+1), with no staleness.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-12 14:14:50 -07:00
Caroline di Vittorio
e8bb6b497b
feat: mark threads as resolved to hide from sidebar (#1500)
* feat: mark threads resolved to hide from sidebar [closes resolve-threads]

Add a resolved flag (thread metadata) so users can clear finished
threads from the sidebar without deleting them. Resolved threads move to
a collapsible "Resolved" group (capped at 20 with a "Show all" link) and
are fully searchable/filterable + paginated on a new /agents/threads
page driven by URL query params. Sending a new message auto-unresolves.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: refresh paginated agent thread lists

Invalidate all agent thread list queries when thread lifecycle events change sidebar data, keeping the new paginated sidebar cache in sync.

---------

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-06-12 12:01:42 -07:00
Johannes du Plessis
09d5d00e59
feat: render Reviews page diffs with pierre MultiFileDiff (#1517)
* feat: render Reviews page diffs with pierre MultiFileDiff

The Reviews detail page used a hand-rolled hunk renderer with no syntax
highlighting. Switch it to the same pierre MultiFileDiff + theming the agent
chat git panel uses.

- review-diff API now returns full original/modified file contents instead of
  hunks, via a shared build_pr_diff_files helper extracted from thread_api
- findings render as right-anchored markers (pierre line annotations); focus
  highlight uses selectedLines; floating finding card still anchors to the marker

* feat: auto-collapse a review diff card when marked as viewed

* fix: URL-encode file path in Contents API fetch

Filenames containing reserved URL characters (#, ?) were truncated, so those
files rendered as empty/unrenderable. quote(path, safe='/') preserves the path
separators while escaping the rest.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-12 11:03:51 -07:00
Johannes du Plessis
3e105f5027
fix: Reviews tab — anchored finding card, paginated list, file tree truncation (#1507)
* fix: Reviews tab — anchored finding card, paginated list, file tree truncation

- Finding card now tracks the diff anchor while scrolling instead of staying
  frozen in the viewport; auto-hides when its diff card collapses (including
  collapse via mark-as-viewed) and on click outside
- Checks section capped with max height + scroll
- /reviews paginated (page size 20) with has_more, filtered to the current
  user's PRs by default with an All toggle; PR author login now stored in
  reviewer thread metadata
- File tree truncation marker overlapped filenames because the sidebar bg
  was transparent; use the opaque sidebar color

* fix: finding card tracks anchor 1:1 while scrolling

Drop the vertical viewport clamp — it pinned the card at the clamp
boundary while the highlighted lines kept scrolling, breaking the
attachment.

* fix: anchor finding card with Base UI popover

Replace manual fixed-position tracking (laggy: setState per scroll
frame) with a Popover anchored to the finding's diff row. Floating UI
tracks the anchor outside React renders, so the card moves 1:1 with
the content and scrolls out of view with it. Unanchored findings keep
the fixed top-right card.

* fix: lock finding card to diff scroll

Replace the Base UI popover (async repositioning, paints a frame behind
native scroll) with a card absolutely positioned inside the scroll
container, so it scrolls with the diff in the same compositor frame.
Scroll moves to the ReviewBody root, side panel becomes sticky.
Position recomputes only on layout shifts via ResizeObserver.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-11 17:52:20 -07:00
Johannes du Plessis
bfcb714c5c
feat: PR review page (#1495)
* feat: PR review page

* feat: manual review trigger card on admin page

* feat: finding focus mode with floating card and dimmed backdrop

* fix: dim only info panel, subtle block highlight for focused finding

* refactor: move PR reviews into agents page with file-tree sidebar

* fix: address review feedback

- paginate list_reviews until enough accessible records collected
- include rename/metadata-only files in diff API with empty hunks
- remount ReviewBody on head_sha change so viewed-files state resets

* fix: review tree background + finding card anchoring

* fix: remove no-op PR size chip and dead check links

* fix: parallel diff fetch, complete reReview type, shared github_headers

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-11 16:11:10 -07:00
Christian Bromann
abf354bb05
feat(open-swe): stream agent chat via @langchain/react v2 protocol (#1475)
* feat(dashboard): stream agent chat via @langchain/react v2 protocol

Replace the bespoke SSE + React Query polling path with LangGraph’s
v2 event stream through credentialed dashboard proxies. Run starts go
through stream commands; mid-run follow-ups still queue via /messages.

* fix import path

* fix tests after rebase

* format

* PR feedback

* improved model fallback

* fix image handling

* embrace sdk

* cleanup

* cr

* more cleanup

* fix cors

* harden security

---------

Co-authored-by: open-swe[bot] <215916821+open-swe[bot]@users.noreply.github.com>
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-11 09:54:35 -07:00
Johannes du Plessis
f539962c73
feat: server-side Datadog/LangSmith observability tools + team creds [closes OPE-54] (#1476)
* feat: server-side Datadog/LangSmith observability tools + team creds

Add team-wide observability credential settings (Datadog DD_SITE/API/APP
keys, LangSmith API key) stored encrypted server-side, with an admin
dashboard section to connect/disconnect each provider. When connected,
get_agent loads read-only observability tools server-side: Datadog via its
hosted MCP server (langchain-mcp-adapters, toolsets=core) and LangSmith
read tools (langsmith_get_trace, langsmith_list_runs). Credentials live in
the LangGraph server process and are never exposed to the sandbox.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: address review on observability tools

Address PR review feedback:
- Authorize observability tools per triggering user (admins + the
  OBSERVABILITY_AUTHORIZED_EMAILS allowlist) so prompt-injected runs from
  untrusted contributors can't reach team Datadog/LangSmith data.
- Use the documented Datadog MCP auth headers DD_API_KEY / DD_APPLICATION_KEY.
- Store each provider's credentials under its own store key to avoid a
  read-modify-write race dropping the other provider on concurrent saves.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: async email resolution in observability authorization gate

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-10 11:07:42 -07:00
Johannes du Plessis
da8f003933
feat: per-repo custom instructions for the coding agent (#1460)
* feat: per-repo custom instructions for the coding agent

Adds per-repository custom instructions for the main coding agent,
mirroring the reviewer's per-repo style prompts. Instructions are stored
in the LangGraph Store, managed via dashboard API + UI (Monaco editor),
and appended to the agent's system prompt for runs targeting that repo.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* chore: wire agent instructions route into generated route tree

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: enforce repo access on instruction routes

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-09 15:46:29 -07:00
Caroline di Vittorio
c9960a7253
fix: let usage table expand to fill available width (#1465)
* fix: let usage table expand to fill available width

The usage page was constrained to max-w-3xl while the leaderboard table
needs a 760px minimum for its 7 columns, so it always overflowed and
forced horizontal scrolling. Add an optional maxWidthClassName prop to
AppShell and widen the usage page to max-w-5xl so the table expands when
space allows, keeping overflow-x-auto as a narrow-screen fallback.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* refactor: use cn-merged className override on AppShell

Replace the single-purpose maxWidthClassName prop with a general
className prop merged via cn (twMerge), matching the shadcn pattern. This
lets callers override any of the content-container classes ad hoc rather
than adding a new prop per override. Usage page passes className=max-w-5xl.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
2026-06-09 11:14:18 -07:00
Johannes du Plessis
c7b32e34e6
Revert "fix: precompute usage tab caches (#1434)" (#1463)
This reverts commit 10dfc6d2b0.
2026-06-09 09:21:23 -07:00
Ramon Nogueira
9350361f0d
perf: preload agent thread details (#1448)
* perf: preload agent thread details

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* perf: persist agent layout across thread switches

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

---------

Co-authored-by: Ramon Nogueira <270434257+ramon-langchain@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-06-08 14:07:55 -07:00
Johannes du Plessis
30d5d55f0f
feat: add dark mode support to web UI (#1436)
* feat: add dark mode support to web UI

Add a persisted, system-aware theme with a Light/Dark/System toggle in
the sidebar user menu. An inline head script applies the stored theme
before paint to avoid a flash, and the agents-ui surface gets dark
overrides for its --ui-* variables.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: use static theme init script to satisfy CodeQL

Build the no-FOUC theme script from a fully static string literal
instead of interpolating THEME_STORAGE_KEY, so no value flows into
code construction (CodeQL "Improper code sanitization").

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: avoid theme flip on hydration for persisted preferences

Only apply a theme after the stored preference is read, instead of
eagerly applying the default "system" theme on first effect. This
prevented a brief flip to the OS theme (then back) on load for users
with a saved preference that differs from their OS setting.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-06 12:06:31 -07:00
Johannes du Plessis
10dfc6d2b0
fix: precompute usage tab caches (#1434)
* fix: precompute usage tab caches

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: dedupe usage cache refreshes

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-06 08:11:53 -07:00
Johannes du Plessis
e128f2d6dd
feat: cache usage stats and add reviewer metrics (#1432)
* feat: cache usage dashboard stats

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* chore: adjust usage nav placement

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: paginate reviewer usage stats

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-05 14:33:22 -07:00
Johannes du Plessis
f3512841fc
feat: inject org-wide guidelines into reviewer prompt (#1431)
Adds an admin-managed, org-wide review guidelines field to team settings
that the reviewer injects into every PR review across all repos, alongside
the existing per-repo style prompt and AGENTS.md context. Repo-specific
rules take precedence when they conflict.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-05 13:49:30 -07:00
Johannes du Plessis
449cb5d1a8
fix: make default repository configurable (#1429)
* fix: make default repository configurable

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: preserve dashboard repo-less runs

* fix: distinguish explicit repo-less dashboard runs

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-05 13:48:47 -07:00
Johannes du Plessis
6895ddcedc
feat: add scheduled web agents (#1422)
* feat: add scheduled web agents

Co-authored-by: open-swe[bot] <215916821+open-swe[bot]@users.noreply.github.com>

* fix: secure scheduled agent repositories

Co-authored-by: open-swe[bot] <215916821+open-swe[bot]@users.noreply.github.com>

* feat: rebuild scheduled agents as Automations tab

Scrap the inline ScheduledAgentsPanel and replace it with a dedicated
Automations tab: sidebar nav entry, list view with stat cards + empty
state, and a full editor (name, Active toggle, repo, scheduled trigger
picker, agent instructions + model).

* fix: clear collapsed-sidebar button on mobile in Automations

* fix: allow clearing automation repo on update

---------

Co-authored-by: open-swe[bot] <215916821+open-swe[bot]@users.noreply.github.com>
2026-06-05 02:20:24 +00:00
Johannes du Plessis
6f330f777d
fix: tidy agents home + mobile logo + PWA dev/icon issues (#1419)
* fix: tidy agents home, mobile logo, and PWA dev/icon issues

- Remove recent-runs cards from the new agent page
- Scale the ASCII logo to fit narrow viewports instead of overflowing
- Serve manifest in dev and skip SW registration in dev to clear console errors
- Use a flat, opaque apple-touch-icon so iOS stops adding a black border
- Ignore generated ui/dev-dist

* feat: add send button and prevent iOS focus-zoom on chat input

- Add a circular send button (accent, spinner while sending) to the prompt bar
- Bump textarea to 16px on mobile so iOS doesn't zoom the viewport on focus

* fix: polish prompt bar and center logo

- Center the ASCII logo at all widths
- Prevent iOS focus-zoom via viewport maximum-scale instead of bumping the
  input to 16px, so mobile text stays the intended size
- Give the model picker a chip/chevron treatment to anchor it next to the send button

* fix: simplify model picker to plain text + chevron

* fix: tighten prompt bar padding and shrink send button

---------

Co-authored-by: open-swe[bot] <215916821+open-swe[bot]@users.noreply.github.com>
2026-06-04 16:47:35 -07:00
Johannes du Plessis
a511add856
feat: add agent usage leaderboard (#1418)
* feat: add agent usage leaderboard

Co-authored-by: open-swe[bot] <215916821+open-swe[bot]@users.noreply.github.com>

* fix: bound leaderboard refresh and hide emails

Co-authored-by: open-swe[bot] <215916821+open-swe[bot]@users.noreply.github.com>

---------

Co-authored-by: open-swe[bot] <215916821+open-swe[bot]@users.noreply.github.com>
2026-06-04 23:33:31 +00:00
Johannes du Plessis
02cfdbda5b
feat: make UI installable as a PWA (#1417)
* feat: make UI installable as a PWA

Co-authored-by: open-swe[bot] <215916821+open-swe[bot]@users.noreply.github.com>

* fix(pwa): address review feedback

- Regenerate bun.lock for new PWA deps (Vercel builds with bun).
- Switch SW to prompt registration so deploys don't reload tabs mid-run.
- Remove orphaned public/manifest.json; app links the generated /manifest.webmanifest.
- Drop json from workbox globPatterns to avoid precaching stray JSON.

Verified bun build emits sw.js, manifest.webmanifest, and precaches _shell.html.

* fix(pwa): bun.lock, prompt SW registration, tighten globPatterns

- Regenerate bun.lock for new PWA deps (Vercel builds with bun).
- Switch SW to prompt registration so deploys don't reload tabs mid-run.
- Drop json from workbox globPatterns to avoid precaching stray JSON.

Verified bun build emits sw.js, manifest.webmanifest, and precaches _shell.html.

---------

Co-authored-by: open-swe[bot] <215916821+open-swe[bot]@users.noreply.github.com>
2026-06-04 15:52:06 -07:00
Johannes du Plessis
a75e9b027b
fix: stabilize agent selector defaults (#1414)
* fix: stabilize agent defaults and repo selector

Co-authored-by: open-swe[bot] <215916821+open-swe[bot]@users.noreply.github.com>

* fix: align default repo selector styling, restore text fallback

Match the repo selector to sibling settings controls (h-7, bg-input/20,
text-xs). Fall back to a text input when the repo list is empty so a
default repo can still be entered.

* fix: make repo selector dropdown more compact

---------

Co-authored-by: open-swe[bot] <215916821+open-swe[bot]@users.noreply.github.com>
2026-06-04 21:06:25 +00:00
Johannes du Plessis
58e0f84470
fix: use Slack OIDC mappings for Slack thread ownership (#1410)
* fix: tag Slack threads with stored identity so they surface in web

process_slack_mention gated the run on mapped_login (resolved from the stable
Slack user id), but upsert_agent_thread_owner_metadata independently re-resolved
the GitHub login from the Slack profile email. When that email differs from the
user's mapping email (e.g. a personal vs work address), the lookup returned None,
so github_login was never stamped on the thread and the thread never surfaced in
the web Agents UI (which searches by github_login / triggering_user_email).

Resolve the GitHub user from the store via the Slack id, pass that login through
to the owner metadata, and use the mapping's stored work email (falling back to
the Slack profile email for unmapped users) for both the run config and the
thread tagging, so Slack-started threads reliably appear in web.

* fix: stamp github_login on Slack threads so they surface in web

process_slack_mention gated the run on mapped_login (resolved from the stable
Slack user id) but upsert_agent_thread_owner_metadata re-resolved the login from
the Slack profile email; when that email isn't the user's mapping email the
lookup returns None and github_login is never stamped, so the thread is invisible
in the web Agents UI (which searches by github_login / triggering_user_email).

Pass the already-resolved mapped_login through to the owner metadata. The
dashboard match keys on the stable GitHub login, so this is sufficient; the
triggering email stays the live Slack profile value.

* fix: preserve Slack email during account mapping

* fix: require Slack OIDC for email mappings

* chore: format Slack OIDC mapping cleanup
2026-06-04 19:58:30 +00:00
Johannes du Plessis
c8a997c125
fix: reliable, safe Slack account-connect prompt + first-login Slack dialog (#1383)
* fix: deliver Slack account-link prompt as a visible threaded reply

Blocked Slack users got no prompt at all. Prod logs show chat.postEphemeral
returns ok, but ephemeral messages are silently dropped in Slack's assistant
threads (where Open SWE runs), so the user sees nothing. Post the prompt as a
normal threaded reply instead — the same channel the agent uses to reply.

* fix: deliver Slack auth-failure prompt as a visible threaded reply

leave_failure_comment() tried an ephemeral message first and only fell back
to a thread reply on failure. Ephemeral messages succeed (ok) but are dropped
in Slack's assistant threads, so the fallback never fired and the user saw no
auth-failure prompt. Post the visible threaded reply directly, matching the
account-link prompt fix.

* fix: prompt blocked Slack users with a generic, token-free dashboard link

Addresses the review findings that posting the per-user account-link token /
auth URL in a visible thread lets any channel member bind their GitHub account
to the triggering user's Slack identity.

Drop the per-user signed link entirely. Both the account-link prompt
(_post_account_link_prompt) and the runtime auth-failure prompt
(leave_failure_comment) now post a plain dashboard settings link
(build_settings_url) as a visible threaded reply. The user signs in with GitHub
from their own session and connects Slack via verified OIDC on the settings
page — no secret in the thread, nothing to hijack, and no DM machinery.

* feat: nudge first-time users to connect Slack from the dashboard home

Show a Connect Slack banner on the agents landing page whenever Slack OAuth is
enabled and the user hasn't linked Slack yet. A first-time user (no Slack
mapping) sees it immediately after signing in; it disappears once connected.

* feat: prompt first-time users to connect Slack via a dialog

Replace the inline Connect Slack card on the agents home with a modal dialog
(Base UI). It opens automatically once the mapping query resolves to
"not connected" and closes itself once Slack is linked; "Maybe later" dismisses
it for the session. No new dependency — uses the design system's Base UI.

* copy: frame Slack connect as resolving the user's GitHub account

Drop 'act/reply on your behalf' wording across the connect-Slack dialog, the
Slack thread prompts (blocked + auth-failure), and the settings description.
Connecting Slack lets Open SWE resolve the user's GitHub account when they tag
it in Slack.
2026-06-02 20:55:07 -07:00
open-swe[bot]
5dda8acb7a
fix: use brand icons in settings (#1377)
Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>
Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-02 15:53:02 -07:00
Johannes du Plessis
cb4c643e43
feat: open Slack-triggered PRs as the triggering user (#1375)
* feat: open Slack-triggered PRs as the triggering user

Route the Slack per-user GitHub token through the dashboard OAuth store
(the backend the self-service link prompt populates) and block runs that
lack a valid user token, prompting the user to (re-)link. Per-user OAuth
now wins over bot-token-only mode for mapped Slack/dashboard users.

Flip commit/PR authorship across all sources: the triggering user is the
commit author (via repo-local git identity using their resolvable GitHub
noreply email) and open-swe[bot] is the Co-authored-by collaborator.

* fix: address PR review — shell-escape commit identity, fix token cache impersonation

- Shell-escape the triggering user's name/email with shlex.quote before
  embedding them in the repo-setup `git config` command, so a name like
  O'Connor (or a crafted one) can't break or inject into the command.
- Stop consulting the shared thread-metadata token cache in
  _resolve_dashboard_user_token. Slack thread ids are shared across the
  conversation, so a cached token from a prior triggering user could be
  returned for the current github_login. Always resolve by login from the
  dashboard OAuth store instead.

* feat: dashboard self-service user mapping + UI cleanup

- Add session-scoped GET/PUT /dashboard/api/my-mapping so users can set their
  own work email / Slack member ID (keyed by their GitHub login, source=self).
- Slack account-link prompt now redirects to Profile Settings after auth.
- Rename "My Settings" -> "Profile Settings" and "Cloud Agents" -> "Open SWE
  Agent"; remove the Integrations tab/section (folded out, low value for now)
  and redirect /integrations to Profile Settings.
- Add a "User mapping" section to Profile Settings (work email used by Slack
  and Linear, optional Slack member ID).
- Make dashboard auth cookies scheme-aware: Secure;SameSite=None over HTTPS,
  non-Secure;SameSite=Lax over http://localhost so local login works.

* feat: self-service Slack account linking via Sign in with Slack (OIDC)

Replace the spoofable manual work-email/Slack-ID form with a verified
"Sign in with Slack" flow so a logged-in GitHub user can only ever link
their own Slack identity.

- New agent/dashboard/slack_oauth.py: OIDC authorize URL, code exchange,
  userInfo identity parse, optional workspace gate, configured check.
- routes.py: session-gated GET /slack/login and /slack/callback that upsert
  the mapping from Slack-verified user_id + email (source=slack_oauth).
  Remove the spoofable PUT /my-mapping; expose slack_oauth_enabled on /me.
- UI: drop the editable inputs; add a Connect Slack button + status to the
  User mapping section.

Admin-managed mappings are unaffected and still resolve at trigger time.
2026-06-02 15:04:20 -07:00
Johannes du Plessis
c41185a3ca
chore: remove legacy mapping + admin profiles section, page user mappings (#1371)
* chore: remove legacy mapping + admin profiles section, page user mappings

- Delete the hardcoded github_user_email_map.py and the one-time
  POST /admin/user-mappings/import endpoint + Import legacy mapping UI
  button (the Store is now the sole source of truth post-import).
- Remove the per-user profiles admin section and its GET/PUT
  /admin/profiles endpoints + ProfileForm component; users still manage
  their own profile via My Settings.
- Page the user mappings list: /admin/user-mappings now takes
  page/page_size and returns {items,total,page,page_size}; the admin UI
  shows 20 rows per page with Previous/Next controls.

* Address review: fix stale import-button doc + clamp mappings page on shrink
2026-06-01 22:10:55 +00:00
Johannes du Plessis
427bfe4f56
feat: Store-backed GitHub/Slack user mapping (self-service + admin) (#1369)
* Replace hardcoded GitHub-email map with Store-backed user mapping

Move the static GITHUB_USER_EMAIL_MAP to a Store-backed bidirectional
mapping (GitHub login <-> work email <-> optional Slack ID) with an
in-process cache, self-service onboarding, and admin management.

- agent/dashboard/user_mappings.py: Store CRUD + login/email/slack-id
  indexes, sync cache readers for hot paths, async fallthrough, and a
  bulk_import that preserves existing richer records.
- Migrate all read sites (auth.py, agent_overrides.py, authorship.py,
  github_comments.py, webapp.py x2) off the dict.
- Unmapped Slack tags now run on the GitHub App installation token
  (use_installation_token_fallback) and get an ephemeral "link your
  GitHub account" prompt carrying the Slack id + email via a signed
  account-link token threaded through the OAuth state.
- OAuth callback completes a self-service (org-gated) mapping from that
  token, falling back to the verified GitHub email.
- Admin CRUD endpoints + one-time legacy import; dashboard UI section.
- Legacy dict retained only as the import payload (no longer read).

Tests: mapping store, account-link round-trip + completion, mapped vs
unmapped Slack flows; existing trust-gate tests updated to prime cache.

* Address review: cold-cache email resolution + stale alias de-indexing

- agent_overrides: add resolve_login_from_email_async that falls through to
  the Store on a cold cache; use it at the async repo-resolution call sites
  (Slack repo config, Linear comment, owner-metadata) so a mapped user still
  resolves to their GitHub login + dashboard default_repo on a fresh worker.
- user_mappings.upsert_mapping: de-index the existing login before re-indexing
  so a changed email/Slack id no longer leaves stale aliases resolving to the
  login in-process.
- Tests for both fixes; update Slack repo-config test to patch the async resolver.
2026-06-01 14:37:19 -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
aeaa92eb8a
feat: Configure subagent model defaults (#1342) 2026-05-27 12:34:52 -07:00
open-swe[bot]
e347aed851
feat: make PR creation policy opt-in (#1334)
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-26 13:30:18 -07: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
e87085139b
feat: add Agents chat UI for cloud threads (#1323)
* feat(ui): add Agents chat UI ported from open-swe-app

Introduce a Cursor-style Agents surface separate from the dashboard, with ported chat/diff components and mock thread data until LangGraph APIs land.

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

* feat(dashboard): wire Agents UI to LangGraph thread APIs

Add dashboard thread list/detail/run/message/stream endpoints with a LangGraph message adapter, dashboard OAuth auth for runs, and TanStack Query hooks replacing mock data.

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

* fix(dashboard): single agent reply per turn in Agents UI

Use UUID thread IDs LangGraph accepts, skip confirming_completion for
dashboard threads, and merge adapter agent messages so duplicate bubbles
do not render.

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

* feat(ui): polish Agents UI with floating prompt and layout cleanup

Remove no-op chrome (git panel, headers, sidebar search), port CloudPromptBar
from open-swe-app, and refine chat layout so messages scroll behind the input.

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

* fix(agent): patch deepagents reducer for None messages on checkpoint replay

LangGraph thread state could 500 when cancelled runs left messages as None.
Apply the reducer guard before graph import, fall back to metadata in the
dashboard API, and adjust Agents prompt bar layout.

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

* feat(ui): unify sidebar user menu and clean up Agents UI navigation

Extract SidebarUserMenu so the dashboard and Agents sidebars render the
same profile button, drop the redundant Agents nav row in favor of the
existing Back to Agents link, add the open-swe logo header to the Agents
sidebar, flatten the New Agent button, and cap the home screen run list
to keep the prompt input in view.

* feat(ui): resizable/collapsible sidebar shared across dashboard and Agents

Add a useSidebarLayout hook + SidebarFrame wrapper so both sidebars
share a persisted width (default 260px, drag to resize, 200-420 range)
and a collapse toggle that hides the panel and surfaces a floating
reopen button. Also adds a DELETE /threads/{id} endpoint and an X-on-
hover thread delete control in the Agents sidebar.

* feat(ui): instant user message and busy indicator on Agents transition

Stash submitted prompts in sessionStorage, pre-populate the new thread
detail cache, and merge pending prompts into the rendered message list
so the Agents page renders the user bubble plus the existing thinking
spinner immediately instead of flashing a skeleton and "Agent is
starting" while the run boots.

* feat(ui): token-stream agent replies in the Agents thread view

Opt the LangGraph runs into messages-tuple streaming and forward those
events through the existing SSE channel. The frontend now applies
AIMessageChunk deltas directly to the cached thread (cancelling any
in-flight refetch first so optimistic tokens are not clobbered) and
keeps positional pending prompts so the user bubble stays in the right
place while the agent streams its reply.

* fix(dashboard): await threads.join_stream before iterating

threads.join_stream is async def returning an AsyncIterator, so it must
be awaited before async for. The SSE endpoint was raising
TypeError: 'async for' requires an object with __aiter__ method, got
coroutine on every connection.

* fix(dashboard): drop messages-tuple stream_mode that broke thinking-mode tool turns

Setting stream_mode=["values","messages-tuple","updates"] on
runs.create forces langchain_anthropic into streaming, and on the
second model call (after tool execution) its serialized thinking
blocks come back malformed, so Anthropic rejects the request with
'messages.1.content.0.thinking.thinking: Field required'. Revert to
the default stream_mode so claude-opus thinking + tool use runs to
completion. The frontend keeps the messages-event handler in place
as a no-op fallback for when streaming is re-enabled.

* feat(agents): per-thread model picker wired through to the run

Add optional model_id/effort to the create-thread and send-message
request bodies, forward them as agent_model_id/agent_effort in the
LangGraph run configurable, and record the resolved choice in thread
metadata so the UI can show the model the run is actually using.
get_agent now picks the per-thread override last (highest priority over
team default + profile override) and falls back gracefully when it is
absent or unsupported.

The frontend prompt bar becomes a controlled component fed by a
shared useModelOptions hook (options + profile -> defaultSelection).
AgentsHome seeds the picker from the user's profile default; the
thread view seeds from the thread's recorded model/effort and lets
each follow-up retarget the run.

* refactor(ui): align Agents prompt bar layout with open-swe-app PromptBar

Drop the absolute-positioned send button, restore the original
px-4 py-3.5 min-h-[106px] flex-col container, and move the model
picker into a mt-auto pt-2 footer row so the placeholder text and
the model selector share the same horizontal padding.

* chore: fix lint/format CI failures

Remove unused imports and reformat two files flagged by ruff.

* fix(tests): stop messages-reducer patch tests from polluting the suite

Restore agent modules after reducer patch tests and import LangSmithSandbox
from agent.server in proxy refresh tests so isinstance checks stay valid.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 18:15:59 +00:00
Johannes du Plessis
32ec9b485f
feat: restructure Open SWE Review tab + wire create_prs (#1319)
* feat(dashboard): restructure Open SWE Review tab + wire create_prs

Restructures the dashboard around two related changes the reviewer settings
have been asking for:

- Wire profile.create_prs. Defaults to true (opt-out); when off the system
  prompt gets a `Pull Request Policy Override` section telling the agent
  to push the branch and notify with the branch URL instead of opening a
  PR. Removes the noop Slack Notifications / Allow Artifacts / First Name
  / Last Name controls and their schema fields.
- Repositories opt-in for Open SWE Review. New per-team enabled list
  stored in the LangGraph Store (`["enabled_review_repos"]`). Every
  reviewer webhook chokepoint now goes through `_is_repo_enabled_for_review`
  which AND-combines the existing env allowlist with the dashboard list.
  Default is empty (opt-in) — admins enable repos per-installation from
  the new Repositories page nested under Open SWE Review.
- Open SWE Review tab now mirrors the Cursor "rules" pattern: main page
  shows installation rows + a Rules entry; both drill into nested pages
  (/review/repositories/$owner and /review/styles) with a back link.
- Adds the new logo/favicon assets shipped from sidebar + html head.

Tests pass with a new autouse fixture (`tests/conftest.py`) that defaults
`is_review_repo_enabled` to True for existing allowlist tests.

* fix(dashboard): make main content scroll independently of the sidebar

Outer flex container was min-h-svh, so it grew with main's content and the
whole page scrolled — sidebar moved with it. Pin to h-svh + overflow-hidden
so the sidebar stays put and only <main> scrolls.

* fix(dashboard): make disabled repo toggles obviously disabled

Switch's disabled state used opacity-50 against a muted background, so
the not-admin state looked nearly identical to the off state. Bump to
opacity-40 + grayscale, and wrap each repo toggle in a span carrying a
native hover tooltip explaining why it's disabled.

* fix(switch): handle base-ui's data-disabled state

base-ui's Switch.Root sets data-disabled (not the HTML disabled attribute)
when disabled, so Tailwind's disabled: variant never matches and the
button keeps its cursor-pointer + clickable look. Mirror the styling
under the data-[disabled] variant and add pointer-events-none so the
disabled state is both visible and actually unclickable.

* feat(dashboard): paginate per-installation repository list

20 repos per page with Prev / page X of Y / Next controls at the bottom.
Pager only renders when there are more than 20 repos. Page resets to 0
when navigating between installations.

* feat(dashboard): global default model selectors for Agent + Reviewer

Adds team-wide default model + reasoning effort for both agents in the
Admin tab so operators can switch models without redeploying.

Resolution chain:
  Agent:    hardcoded -> LLM_MODEL_ID env -> team default -> user profile
  Reviewer: hardcoded -> LLM_MODEL_ID env -> team default -> per-call configurable

Team defaults live in team_settings and are validated against the
SUPPORTED_MODELS allowlist + the model's supported reasoning efforts.
'Inherit from env' clears the override and falls back to LLM_MODEL_ID.

* refactor(models): drop LLM_MODEL_ID env in favour of the team default

The team default is now the single source of truth for the runtime model
choice; per-user (agent) and per-call configurable (reviewer) selections
still win on top. When no admin has touched the team default, it surfaces
the hardcoded fallback (DEFAULT_MODEL_ID + its default effort), so the
admin UI's dropdown is always pre-populated with a sensible value.

The Admin UI loses the 'Inherit from env' option since there is no longer
an env layer to inherit from.

* chore(models): set hardcoded fallback to gpt-5.5 medium

Decouple the team-default boot value (gpt-5.5 / medium) from each model's
ProfileForm-suggested default_effort so we can change one without nudging
the other. The Opus xhigh default for new user profiles is unchanged.

* feat(dashboard): trigger-mode copy, Coming Soon badges, logout in My Settings

- Rename trigger mode 'ready_for_review' -> 'once_per_pr' with new
  description copy that matches the screenshot. Legacy stored values
  fall back to 'every_push' on read so the UI never shows an unknown
  selection.
- Add a 'Coming soon' badge + greyed-out + disabled state on the
  controls that don't have runtime consumers yet: Trigger Mode,
  Autofix Mode, Autofix Severity Threshold, and Automatically fix CI
  failures. SettingsRow grew a comingSoon prop to keep this consistent.
- My Settings drops the noop PR Preferences section and adds a Sign
  Out button. preferred_pr_destination is removed from the profile
  schema; old records get the field popped on next write.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-21 09:17:07 -07:00
Johannes du Plessis
1ea9c0d880
feat: refactor UI into sidebar layout (#1318)
* feat(dashboard): refactor UI into Cursor-style sidebar layout

Replace the top-bar AppHeader with a left sidebar that holds the four
primary sections (My Settings, Cloud Agents, Open SWE Review,
Integrations) and the user account menu at the bottom. Admin remains a
hidden route reachable from the sidebar only when is_admin.

UI:
- AppShell + AppSidebar with bottom-anchored avatar/sign-out menu.
- My Settings: profile (first/last name) + PR destination preference.
- Cloud Agents: model/effort, default repo, base branch, branch prefix,
  PR toggles (auto-fix CI, create PRs, allow artifacts), Slack
  notifications.
- Open SWE Review: trigger mode, draft reviews, PR summaries, autofix
  mode + severity threshold, plus the existing per-repo review-style
  prompts (was /review-styles).
- Integrations: GitHub install status + Slack/Linear status rows.
- New Switch and Menu primitives.

Backend:
- Extend ProfileUpdate with first_name, last_name, base_branch,
  branch_prefix, auto_fix_ci, create_prs, allow_artifacts,
  slack_notifications, preferred_pr_destination.
- Add team_settings module + /team-settings GET (any session) / PUT
  (admin only) for the reviewer configuration.

* fix(dashboard): replace base-ui Menu with plain dropdown, stabilise Selects

- base-ui's Menu hit an "Invalid hook call / Cannot read properties of
  null (reading 'useRef')" crash inside fastComponent under Vite's dep
  optimisation. The sidebar account menu is the only consumer, so swap
  it for a useState-driven dropdown with click-outside + Esc handling
  and drop the unused Menu wrapper.
- Initialise the Open SWE Review settings form with the same defaults
  the server returns instead of `null`, so the Selects don't switch
  from uncontrolled (`undefined`) to controlled on first data load.

* fix(dashboard): address review feedback on admin overwrite + form reset

- admin PUT /admin/profiles/{login} only sent model/effort/repo from the
  admin form. ProfileUpdate's Pydantic defaults then wrote auto_fix_ci /
  create_prs / slack_notifications etc back onto the target user's
  profile, clobbering whatever they had configured. Switch to
  model_dump(exclude_unset=True) and overlay the incoming fields on the
  user's existing stored profile so only sent fields change.

- my-settings.tsx and cloud-agents.tsx initialised local state from
  profile.data on every change. Because useSaveProfile updates the
  cached profile on success, toggling any control would overwrite the
  user's typed-but-unsaved input in another field. Guard the
  initialisation effect with a ref so it runs once on first load.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-20 14:13:23 -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
88856a04fa
feat: open-swe dashboard for per-user profile config (#1302)
* feat: dashboard backend — GitHub OAuth, profile CRUD, admin endpoints

Adds agent/dashboard/ FastAPI router mounted at /dashboard/api covering:
- GitHub App OAuth login → JWT cookie session (cross-domain ready)
- profile CRUD against LangGraph Store with model+effort validation
- admin gate via CONFIGURED_ADMINS
- /repos via /user/installations using the user's encrypted OAuth token

CORS allowlist on webapp.py is opt-in via DASHBOARD_ALLOWED_ORIGINS so the
Vercel-hosted frontend can call the LangSmith deployment with credentials.

* feat: apply dashboard profile model/effort overrides in get_agent

Look up the triggering user's GitHub login from config (direct field or
GITHUB_USER_EMAIL_MAP reverse lookup), read their profile from the Store,
and apply default_model + reasoning_effort to make_model when both are
valid. Effort 'max' is captured on the profile but not yet wired through —
the OpenAI Reasoning Literal doesn't accept it.

* feat: ui/ TanStack Start dashboard for profile config

Scaffolded with the shadcn b7CScJIjA preset (TanStack Start template,
base-ui primitives, Tailwind v4). Three routes:

- /login   — Sign in with GitHub (links to /dashboard/api/auth/login)
- /profile — Edit default model, reasoning effort, default repo
- /admin   — Admin-only: list users and edit other profiles

API client (src/lib/api.ts) uses credentials: include so the osw_session
cookie set by the OAuth callback rides cross-origin. VITE_DASHBOARD_API_BASE_URL
points at the LangSmith deployment.

Effort options re-render when the model changes; 'max' on Opus 4.7 is
captured on the profile but ignored downstream until anthropic reasoning
is wired through make_model.

* feat: searchable Combobox for default repo picker

Replaces the Select with a base-ui Combobox so users can filter by typing,
the popup is wider than the trigger so full owner/repo names are readable,
and the list caps at max-h-80 to stay on screen.

* fix: address review comments + wire default_repo and Anthropic thinking

Security/correctness fixes from PR review:

* Open redirect: validate `redirect_to` in `/auth/login` against
  `DASHBOARD_BASE_URL` + `DASHBOARD_ALLOWED_ORIGINS` before signing it
  into the state JWT. Anything off-allowlist falls back to the dashboard
  base URL. (PR #1302 r3250054386)

* Login CSRF: bind the OAuth `state` to the requesting browser. At
  `/auth/login` we generate a fresh nonce, set it as a short-lived
  HttpOnly SameSite=Lax cookie scoped to `/dashboard/api/auth`, and
  embed `hash_state_nonce(nonce)` in the state JWT. At `/auth/callback`
  we require the cookie nonce to hash-match the state JWT's nonce_hash
  (constant-time compare). (PR #1302 r3250054395)

* RMW race in profile vs token writes: split storage into two
  namespaces — `["profiles"]` for user-editable settings and
  `["oauth_tokens"]` for the encrypted GitHub token. Each upsert now
  only writes its own namespace so an in-flight profile save can no
  longer clobber a fresh token from a concurrent re-login (and vice
  versa). (PR #1302 r3250054393)

* /repos pagination: follow `Link: rel="next"` for both
  `/user/installations` and per-installation `/repositories` with
  per_page=100, capped at 1000 items. (PR #1302 r3250054401)

Feature wires:

* default_repo: applied as a fallback in `get_slack_repo_config` (after
  explicit-repo / thread metadata, before the env defaults) and in the
  Linear webhook (after comment-body extraction, before team mapping).
  Both paths resolve the triggering user's GitHub login via
  GITHUB_USER_EMAIL_MAP and read the profile's default_repo.

* Anthropic "thinking" effort: `make_model` now accepts a `thinking`
  kwarg; `get_agent` maps profile effort {low,medium,high,xhigh,max}
  to budget_tokens {1k,4k,12k,32k,60k} when the chosen model is
  anthropic. OpenAI path still ignores "max" since the Literal doesn't
  accept it.
2026-05-15 11:23:53 -07:00