2026-02-09 15:34:18 -08:00
|
|
|
"""Before-model middleware that injects queued messages into state.
|
|
|
|
|
|
|
|
|
|
Checks the LangGraph store for pending messages (e.g. follow-up Linear
|
|
|
|
|
comments that arrived while the agent was busy) and injects them as new
|
|
|
|
|
human messages before the next model call.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
from typing import Any
|
|
|
|
|
|
2026-02-19 14:06:06 -08:00
|
|
|
import httpx
|
2026-02-09 15:34:18 -08:00
|
|
|
from langchain.agents.middleware import AgentState, before_model
|
|
|
|
|
from langgraph.config import get_config, get_store
|
|
|
|
|
from langgraph.runtime import Runtime
|
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
|
|
|
from langgraph.store.base import BaseStore
|
2026-06-17 09:12:52 -07:00
|
|
|
from langgraph_sdk import get_client
|
2026-02-09 15:34:18 -08:00
|
|
|
|
2026-06-17 09:12:52 -07:00
|
|
|
from ..dashboard.options import model_supports_images
|
|
|
|
|
from ..utils.multimodal import fetch_image_block, vision_not_supported_warning
|
2026-02-19 14:06:06 -08:00
|
|
|
|
2026-02-09 15:34:18 -08:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
2026-06-04 10:01:10 -07:00
|
|
|
DASHBOARD_HANDOFF_MARKER = "[Open SWE Web handoff]"
|
|
|
|
|
DASHBOARD_HANDOFF_INSTRUCTION = (
|
|
|
|
|
f"{DASHBOARD_HANDOFF_MARKER} This follow-up was sent from Web. "
|
|
|
|
|
"The conversation has moved to Web, so answer in the dashboard stream with a normal "
|
|
|
|
|
"assistant message. Do not call slack_thread_reply unless a later Slack message explicitly "
|
|
|
|
|
"moves the conversation back to Slack."
|
|
|
|
|
)
|
|
|
|
|
|
2026-02-09 15:34:18 -08:00
|
|
|
|
|
|
|
|
class LinearNotifyState(AgentState):
|
|
|
|
|
"""Extended agent state for tracking Linear notifications."""
|
|
|
|
|
|
|
|
|
|
linear_messages_sent_count: int
|
|
|
|
|
|
|
|
|
|
|
2026-06-17 09:12:52 -07:00
|
|
|
async def _resolve_thread_model_id(thread_id: str) -> str | None:
|
|
|
|
|
"""Read the resolved model from thread metadata (set by ``get_agent``)."""
|
|
|
|
|
try:
|
|
|
|
|
client = get_client()
|
|
|
|
|
thread = await client.threads.get(thread_id)
|
|
|
|
|
metadata = thread.get("metadata") if isinstance(thread, dict) else None
|
|
|
|
|
if not isinstance(metadata, dict):
|
|
|
|
|
return None
|
|
|
|
|
model = metadata.get("model")
|
|
|
|
|
return model if isinstance(model, str) and model else None
|
|
|
|
|
except Exception:
|
|
|
|
|
logger.debug("Could not read thread metadata for model resolution", exc_info=True)
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
2026-02-22 18:57:49 -08:00
|
|
|
async def _build_blocks_from_payload(
|
|
|
|
|
payload: dict[str, Any],
|
2026-06-17 09:12:52 -07:00
|
|
|
*,
|
|
|
|
|
model_id: str | None = None,
|
2026-02-22 18:57:49 -08:00
|
|
|
) -> list[dict[str, Any]]:
|
|
|
|
|
text = payload.get("text", "")
|
|
|
|
|
image_urls = payload.get("image_urls", []) or []
|
2026-06-06 11:50:24 -07:00
|
|
|
images = payload.get("images", []) or []
|
2026-02-22 18:57:49 -08:00
|
|
|
blocks: list[dict[str, Any]] = []
|
|
|
|
|
if text:
|
|
|
|
|
blocks.append({"type": "text", "text": text})
|
2026-06-06 11:50:24 -07:00
|
|
|
if isinstance(images, list):
|
|
|
|
|
blocks.extend(image for image in images if isinstance(image, dict))
|
2026-02-22 18:57:49 -08:00
|
|
|
|
|
|
|
|
if not image_urls:
|
|
|
|
|
return blocks
|
2026-06-17 09:12:52 -07:00
|
|
|
if model_id and not model_supports_images(model_id):
|
|
|
|
|
logger.warning(
|
|
|
|
|
"Skipping %d queued image(s): model %s does not support images",
|
|
|
|
|
len(image_urls),
|
|
|
|
|
model_id,
|
|
|
|
|
)
|
|
|
|
|
if text:
|
|
|
|
|
blocks[0] = {
|
|
|
|
|
"type": "text",
|
|
|
|
|
"text": text + vision_not_supported_warning(model_id, len(image_urls)),
|
|
|
|
|
}
|
|
|
|
|
return blocks
|
2026-02-22 18:57:49 -08:00
|
|
|
async with httpx.AsyncClient() as client:
|
|
|
|
|
for image_url in image_urls:
|
2026-02-24 12:11:24 -08:00
|
|
|
image_block = await fetch_image_block(image_url, client)
|
2026-02-22 18:57:49 -08:00
|
|
|
if image_block:
|
|
|
|
|
blocks.append(image_block)
|
|
|
|
|
return blocks
|
|
|
|
|
|
|
|
|
|
|
2026-06-04 10:01:10 -07:00
|
|
|
def _is_dashboard_queued_message(content: object) -> bool:
|
|
|
|
|
return isinstance(content, dict) and content.get("source") == "dashboard"
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
def _message_update(content_blocks: list[dict[str, Any]], thread_id: str) -> dict[str, Any] | None:
|
|
|
|
|
if not content_blocks:
|
|
|
|
|
return None
|
|
|
|
|
logger.info(
|
|
|
|
|
"Injected %d queued message block(s) into state for thread %s",
|
|
|
|
|
len(content_blocks),
|
|
|
|
|
thread_id,
|
|
|
|
|
)
|
|
|
|
|
return {"messages": [{"role": "user", "content": content_blocks}]}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def _consume_pending_autofix_event(store: BaseStore, thread_id: str) -> str | None:
|
|
|
|
|
"""Pull and clear a batched PR-babysitting event from the store (no thread fetch)."""
|
|
|
|
|
namespace = ("autofix", thread_id)
|
|
|
|
|
try:
|
|
|
|
|
item = await store.aget(namespace, "pending_event")
|
|
|
|
|
except Exception: # noqa: BLE001
|
|
|
|
|
logger.debug(
|
|
|
|
|
"Could not read pending auto-fix event for thread %s", thread_id, exc_info=True
|
|
|
|
|
)
|
|
|
|
|
return None
|
|
|
|
|
if item is None or not item.value.get("reason"):
|
|
|
|
|
return None
|
|
|
|
|
try:
|
|
|
|
|
await store.adelete(namespace, "pending_event")
|
|
|
|
|
except Exception: # noqa: BLE001
|
|
|
|
|
logger.debug(
|
|
|
|
|
"Could not clear pending auto-fix event for thread %s", thread_id, exc_info=True
|
|
|
|
|
)
|
|
|
|
|
message = (
|
|
|
|
|
"A PR babysitting event arrived while you were already working on this PR. "
|
|
|
|
|
"Do not start a separate run for that event. Before finishing, re-check the "
|
|
|
|
|
"PR's latest CI status and review comments, then address any newly failed "
|
|
|
|
|
"checks or actionable comments that are clear and deterministic."
|
|
|
|
|
)
|
|
|
|
|
details = item.value.get("details")
|
|
|
|
|
if isinstance(details, list):
|
|
|
|
|
joined = "\n\n".join(d for d in details if isinstance(d, str) and d)
|
|
|
|
|
if joined:
|
|
|
|
|
message += "\n\nNewly arrived feedback to address:\n" + joined
|
|
|
|
|
return message
|
|
|
|
|
|
|
|
|
|
|
2026-02-09 15:34:18 -08:00
|
|
|
@before_model(state_schema=LinearNotifyState)
|
|
|
|
|
async def check_message_queue_before_model( # noqa: PLR0911
|
|
|
|
|
state: LinearNotifyState, # noqa: ARG001
|
|
|
|
|
runtime: Runtime, # noqa: ARG001
|
|
|
|
|
) -> dict[str, Any] | None:
|
|
|
|
|
"""Middleware that checks for queued messages before each model call.
|
|
|
|
|
|
|
|
|
|
If messages are found in the queue for this thread, it extracts all messages,
|
|
|
|
|
adds them to the conversation state as new human messages, and clears the queue.
|
|
|
|
|
Messages are processed in FIFO order (oldest first).
|
|
|
|
|
|
|
|
|
|
This enables handling of follow-up comments that arrive while the agent is busy.
|
|
|
|
|
The agent will see the new messages and can incorporate them into its response.
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
config = get_config()
|
|
|
|
|
configurable = config.get("configurable", {})
|
|
|
|
|
thread_id = configurable.get("thread_id")
|
|
|
|
|
|
|
|
|
|
if not thread_id:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
store = get_store()
|
|
|
|
|
except Exception as e: # noqa: BLE001
|
|
|
|
|
logger.debug("Could not get store from context: %s", e)
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
if store is None:
|
|
|
|
|
return None
|
|
|
|
|
|
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
|
|
|
content_blocks: list[dict[str, Any]] = []
|
|
|
|
|
pending_autofix = await _consume_pending_autofix_event(store, thread_id)
|
|
|
|
|
if pending_autofix:
|
|
|
|
|
content_blocks.append({"type": "text", "text": pending_autofix})
|
|
|
|
|
|
2026-02-09 15:34:18 -08:00
|
|
|
namespace = ("queue", thread_id)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
queued_item = await store.aget(namespace, "pending_messages")
|
|
|
|
|
except Exception as e: # noqa: BLE001
|
|
|
|
|
logger.warning("Failed to get queued item: %s", e)
|
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
|
|
|
return _message_update(content_blocks, thread_id)
|
2026-02-09 15:34:18 -08:00
|
|
|
|
|
|
|
|
if queued_item is None:
|
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
|
|
|
return _message_update(content_blocks, thread_id)
|
2026-02-09 15:34:18 -08:00
|
|
|
|
|
|
|
|
queued_value = queued_item.value
|
|
|
|
|
queued_messages = queued_value.get("messages", [])
|
|
|
|
|
|
|
|
|
|
# Delete early to prevent duplicate processing if middleware runs again
|
|
|
|
|
await store.adelete(namespace, "pending_messages")
|
|
|
|
|
|
|
|
|
|
if not queued_messages:
|
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
|
|
|
return _message_update(content_blocks, thread_id)
|
2026-02-09 15:34:18 -08:00
|
|
|
|
|
|
|
|
logger.info(
|
|
|
|
|
"Found %d queued message(s) for thread %s, injecting into state",
|
|
|
|
|
len(queued_messages),
|
|
|
|
|
thread_id,
|
|
|
|
|
)
|
|
|
|
|
|
2026-06-17 09:12:52 -07:00
|
|
|
has_images = any(
|
|
|
|
|
isinstance(msg.get("content"), dict)
|
|
|
|
|
and (msg["content"].get("image_urls") or msg["content"].get("images"))
|
|
|
|
|
for msg in queued_messages
|
|
|
|
|
)
|
|
|
|
|
resolved_model_id: str | None = None
|
|
|
|
|
if has_images:
|
|
|
|
|
resolved_model_id = await _resolve_thread_model_id(thread_id)
|
|
|
|
|
|
2026-02-19 14:06:06 -08:00
|
|
|
for msg in queued_messages:
|
|
|
|
|
content = msg.get("content")
|
2026-06-04 10:01:10 -07:00
|
|
|
if _is_dashboard_queued_message(content):
|
|
|
|
|
content_blocks.append({"type": "text", "text": DASHBOARD_HANDOFF_INSTRUCTION})
|
2026-06-06 11:50:24 -07:00
|
|
|
if isinstance(content, dict) and (
|
|
|
|
|
"text" in content or "image_urls" in content or "images" in content
|
|
|
|
|
):
|
2026-02-19 14:06:06 -08:00
|
|
|
logger.debug("Queued message contains text + image URLs")
|
2026-06-17 09:12:52 -07:00
|
|
|
blocks = await _build_blocks_from_payload(content, model_id=resolved_model_id)
|
2026-02-19 14:06:06 -08:00
|
|
|
content_blocks.extend(blocks)
|
|
|
|
|
continue
|
|
|
|
|
if isinstance(content, list):
|
2026-02-24 12:11:24 -08:00
|
|
|
logger.debug("Queued message contains %d content block(s)", len(content))
|
2026-02-19 14:06:06 -08:00
|
|
|
content_blocks.extend(content)
|
|
|
|
|
continue
|
|
|
|
|
if isinstance(content, str) and content:
|
|
|
|
|
logger.debug("Queued message contains text content")
|
|
|
|
|
content_blocks.append({"type": "text", "text": content})
|
2026-02-09 15:34:18 -08:00
|
|
|
|
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
|
|
|
return _message_update(content_blocks, thread_id) # noqa: TRY300
|
2026-02-09 15:34:18 -08:00
|
|
|
except Exception:
|
|
|
|
|
logger.exception("Error in check_message_queue_before_model")
|
|
|
|
|
return None
|