Skip to content

Conversation

@jordan-umusu
Copy link
Collaborator

@jordan-umusu jordan-umusu commented Jan 21, 2026

Summary by cubic

Adds end-to-end agent interrupt support so users can stop a running session from the chat. Sessions now track lifecycle status and streams close cleanly on interrupt.

  • New Features

    • Stop generation in chat with a new interrupt action that aborts the client stream and requests a backend interrupt.
    • New POST /agent/sessions/{session_id}/interrupt endpoint and AgentSessionStatus enum (idle, running, interrupted, completed, failed) returned in session reads.
    • Executor polls for interrupts, kills the runtime, emits stream.done, and updates status; workflows set status on success, failure, and interrupt. Frontend reflects interrupted runs via AgentOutput.interrupted and toasts.
  • Migration

    • Run DB migration to add agent_session.status (alembic upgrade).

Written for commit b195983. Summary will update on new commits.

@jordan-umusu
Copy link
Collaborator Author

@cubic review

@cubic-dev-ai
Copy link
Contributor

cubic-dev-ai bot commented Jan 21, 2026

@cubic review

@jordan-umusu I have started the AI code review. It will take a few minutes to complete.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 19 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="tracecat/agent/executor/loopback.py">

<violation number="1" location="tracecat/agent/executor/loopback.py:242">
P2: Using `asyncio.wait_for` around the full `read_message` can cancel a read mid‑payload, which can desynchronize the framed protocol and corrupt subsequent messages. Consider implementing timeout polling without cancelling an in‑flight read (e.g., keep a `read_message` task across polls or only apply the timeout while waiting for the header).</violation>
</file>

<file name="tracecat/agent/session/activities.py">

<violation number="1" location="tracecat/agent/session/activities.py:185">
P2: Avoid catching all exceptions here; it swallows unexpected failures and prevents the workflow/activity from failing fast. Either let exceptions propagate or only catch specific, expected errors.

(Based on your team's feedback about avoiding broad `except Exception` blocks.) [FEEDBACK_USED]</violation>
</file>

<file name="frontend/src/components/chat/chat-session-pane.tsx">

<violation number="1" location="frontend/src/components/chat/chat-session-pane.tsx:462">
P2: The updated disabled condition enables the button during non-streaming in-flight states (e.g., status="submitted"), so it becomes a submit button while a response is pending. This can trigger overlapping sendMessage calls and out-of-order responses. Keep the button disabled for non-streaming statuses while still allowing interrupts during streaming.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

try:
_msg_type, payload_bytes = await read_message(
reader, expected_type=MessageType.EVENT
_, payload_bytes = await asyncio.wait_for(
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Using asyncio.wait_for around the full read_message can cancel a read mid‑payload, which can desynchronize the framed protocol and corrupt subsequent messages. Consider implementing timeout polling without cancelling an in‑flight read (e.g., keep a read_message task across polls or only apply the timeout while waiting for the header).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tracecat/agent/executor/loopback.py, line 242:

<comment>Using `asyncio.wait_for` around the full `read_message` can cancel a read mid‑payload, which can desynchronize the framed protocol and corrupt subsequent messages. Consider implementing timeout polling without cancelling an in‑flight read (e.g., keep a `read_message` task across polls or only apply the timeout while waiting for the header).</comment>

<file context>
@@ -210,16 +231,31 @@ async def _process_runtime_events(self, reader: asyncio.StreamReader) -> None:
             try:
-                _msg_type, payload_bytes = await read_message(
-                    reader, expected_type=MessageType.EVENT
+                _, payload_bytes = await asyncio.wait_for(
+                    read_message(reader, expected_type=MessageType.EVENT),
+                    timeout=INTERRUPT_POLL_INTERVAL_SEC,
</file context>
Fix with Cubic

session_id=input.session_id,
status=input.status,
)
except Exception as e:
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Avoid catching all exceptions here; it swallows unexpected failures and prevents the workflow/activity from failing fast. Either let exceptions propagate or only catch specific, expected errors.

(Based on your team's feedback about avoiding broad except Exception blocks.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tracecat/agent/session/activities.py, line 185:

<comment>Avoid catching all exceptions here; it swallows unexpected failures and prevents the workflow/activity from failing fast. Either let exceptions propagate or only catch specific, expected errors.

(Based on your team's feedback about avoiding broad `except Exception` blocks.) </comment>

<file context>
@@ -158,9 +158,43 @@ async def load_session_activity(input: LoadSessionInput) -> LoadSessionResult:
+                session_id=input.session_id,
+                status=input.status,
+            )
+    except Exception as e:
+        logger.error(
+            "Failed to update session status",
</file context>
Fix with Cubic

)}
<PromptInputSubmit
disabled={isReadonly || !input || !!status}
disabled={isReadonly || (!input && !status)}
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The updated disabled condition enables the button during non-streaming in-flight states (e.g., status="submitted"), so it becomes a submit button while a response is pending. This can trigger overlapping sendMessage calls and out-of-order responses. Keep the button disabled for non-streaming statuses while still allowing interrupts during streaming.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/components/chat/chat-session-pane.tsx, line 462:

<comment>The updated disabled condition enables the button during non-streaming in-flight states (e.g., status="submitted"), so it becomes a submit button while a response is pending. This can trigger overlapping sendMessage calls and out-of-order responses. Keep the button disabled for non-streaming statuses while still allowing interrupts during streaming.</comment>

<file context>
@@ -451,8 +459,9 @@ export function ChatSessionPane({
             )}
             <PromptInputSubmit
-              disabled={isReadonly || !input || !!status}
+              disabled={isReadonly || (!input && !status)}
               status={status}
+              onInterrupt={interrupt}
</file context>
Suggested change
disabled={isReadonly || (!input && !status)}
disabled={isReadonly || (status && status !== "streaming") || (!input && !status)}
Fix with Cubic

@blacksmith-sh
Copy link
Contributor

blacksmith-sh bot commented Jan 21, 2026

Found 14 test failures on Blacksmith runners:

Failures

Test View Logs
test_durable_agent_workflow/test_agent_workflow_simple_execution View Logs
test_durable_agent_workflow/test_agent_workflow_uses_agent_config_model_settings View Logs
test_expressions/
test_extract_expressions_errors[Multiple inline substitutions ${{ ACTIONS.my_action.res
ult }} and ${{ ACTIONS.my_action.url }} no errors ${{ 'hello' }} another
View Logs
TestAgentWorkerLifecycle/test_session_activities_registered View Logs
TestAgentWorkerMultiTenant/test_concurrent_execution_different_workspaces View Logs
TestAgentWorkerMultiTenant/test_multiple_concurrent_same_workspace View Logs
TestAgentWorkerMultiTenant/test_single_workspace_execution View Logs
TestAgentWorkerSingleTenant/test_executes_simple_agent_turn View Logs
TestAgentWorkerSingleTenant/test_handles_agent_error View Logs
TestAgentWorkerSingleTenant/test_streams_events_to_redis View Logs
TestAgentWorkerThrashing/test_burst_then_switch_pattern View Logs
TestAgentWorkerThrashing/test_interleaved_concurrent_bursts View Logs
TestAgentWorkerThrashing/test_rapid_alternation_between_workspaces View Logs
TestSessionActivities/test_get_session_activities_returns_list View Logs

Fix in Cursor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant