Skip to content

feat(copilot): render context compaction as tool-call UI events - #12250

Merged
majdyz merged 20 commits into
devfrom
otto/secrt-2053-copilot-compaction-ui-event
Mar 4, 2026
Merged

feat(copilot): render context compaction as tool-call UI events#12250
majdyz merged 20 commits into
devfrom
otto/secrt-2053-copilot-compaction-ui-event

Conversation

@Otto-AGPT

@Otto-AGPT Otto-AGPT commented Mar 2, 2026

Copy link
Copy Markdown
Contributor

Requested by @majdyz

When CoPilot compacts (summarizes/truncates) conversation history to fit within context limits, the user now sees it rendered like a tool call — a spinner while compaction runs, then a completion notice.

Backend:

  • Added compaction_start_events(), compaction_end_events(), compaction_events() in response_model.py using the existing tool-call SSE protocol (tool-input-starttool-input-availabletool-output-available)
  • All three compaction paths (legacy service.py, SDK pre-query, SDK mid-stream) use the same pattern
  • Pre-query and SDK-internal compaction tracked independently so neither suppresses the other

Frontend:

  • Added compaction tool category to GenericTool with ArrowsClockwise icon
  • Shows "Summarizing earlier messages…" with spinner while running
  • Shows "Earlier messages were summarized" when done
  • No expandable accordion — just the status line

Cleanup:

  • Removed unused system_notice_start/end_events, COMPACTION_STARTED_MSG
  • Removed unused system_notice_events, system_error_events, _system_text_events

Closes SECRT-2053

@Otto-AGPT
Otto-AGPT requested a review from a team as a code owner March 2, 2026 01:46
@Otto-AGPT
Otto-AGPT requested review from 0ubbe and majdyz and removed request for a team March 2, 2026 01:46
@github-project-automation github-project-automation Bot moved this to 🆕 Needs initial review in AutoGPT development kanban Mar 2, 2026
@github-actions github-actions Bot added platform/backend AutoGPT Platform - Back end size/m labels Mar 2, 2026
@coderabbitai

coderabbitai Bot commented Mar 2, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Surfaces context-compaction events across CoPilot: query builder returns a compaction flag, security hooks accept an on_compact callback, and service/SDK streaming layers emit out-of-band stream steps (system-prefixed notices and new Stream* step types) when compaction occurs.

Changes

Cohort / File(s) Summary
Service layer updates
autogpt_platform/backend/backend/copilot/service.py
Passes through out-of-band stream chunks whose IDs differ from the active text block; yields StreamStart/Finish and text chunk events unchanged; emits a compaction notification sequence when compaction occurred; added uuid_module alias and imported COPILOT_SYSTEM_PREFIX.
SDK: compaction signaling & stream primitives
autogpt_platform/backend/backend/copilot/sdk/service.py
_build_query_message now returns (query_message, was_compacted); streaming and stop paths check was_compacted / sdk_compacted_event and emit a compaction notification sequence (StreamStartStep, StreamTextStart, StreamTextDelta(COPILOT_SYSTEM_PREFIX), StreamTextEnd, StreamFinishStep); added public stream step types and switched COPILOT_* prefixes to shared constants.
Security hooks API
autogpt_platform/backend/backend/copilot/sdk/security_hooks.py
create_security_hooks signature gains optional `on_compact: Callable[[], None]
Constants
autogpt_platform/backend/backend/copilot/constants.py
Added COPILOT_ERROR_PREFIX = "[COPILOT_ERROR]" and COPILOT_SYSTEM_PREFIX = "[COPILOT_SYSTEM]" for frontend-visible markers.
Tests: query builder
autogpt_platform/backend/backend/copilot/sdk/query_builder_test.py
Tests updated to unpack (result, was_compacted) from _build_query_message and assert was_compacted per scenario.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Service as CoPilot Service (streaming)
    participant SDK as SDK/_build_query_message
    participant Hooks as Security Hooks
    participant Events as Stream Event Emitter

    Client->>Service: request chat completion
    Service->>SDK: build query (may compact)
    SDK-->>Service: (query_message, was_compacted)

    alt was_compacted == true
        Service->>Hooks: call on_compact()
        Hooks-->>Service: ack
        Service->>Events: StreamStartStep
        Service->>Events: StreamTextStart(compaction_text_id)
        Service->>Events: StreamTextDelta("[COPILOT_SYSTEM] compaction notice")
        Service->>Events: StreamTextEnd(compaction_text_id)
        Service->>Events: StreamFinishStep
        Events-->>Client: compaction notification events
    end

    Service->>Events: continue normal streaming of tool/response events
    Events-->>Client: remaining stream events
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

Review effort 2/5

Suggested reviewers

  • 0ubbe
  • majdyz
  • ntindle

Poem

🐇
I nibbled context down to size,
A tiny tag now hops and cries,
Streams keep hopping, neat and bright,
A compact notice, soft and light.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 92.31% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The PR title accurately describes the main change: rendering context compaction as tool-call UI events, which is the core objective throughout the changeset.
Description check ✅ Passed The PR description is comprehensive and directly related to the changeset, detailing backend implementation of compaction events, frontend UI changes, and cleanup of unused code.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch otto/secrt-2053-copilot-compaction-ui-event

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Mar 2, 2026

Copy link
Copy Markdown
Contributor

🔍 PR Overlap Detection

This check compares your PR against all other open PRs targeting the same branch to detect potential merge conflicts early.

🔴 Merge Conflicts Detected

The following PRs have been tested and will have merge conflicts if merged after this PR. Consider coordinating with the authors.

  • feat(frontend/copilot): collapse repeated block executions into grouped summary rows #12259 (0ubbe · updated 1h ago)
    • 📁 autogpt_platform/
      • backend/backend/copilot/sdk/service.py (1 conflict, ~9 lines)
      • frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx (2 conflicts, ~39 lines)
      • frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/MessagePartRenderer.tsx (1 conflict, ~8 lines)
      • frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/ThinkingIndicator.tsx (3 conflicts, ~46 lines)
      • frontend/src/app/(platform)/copilot/useCopilotStream.ts (1 conflict, ~7 lines)
      • frontend/src/app/api/chat/sessions/[sessionId]/stream/route.ts (1 conflict, ~15 lines)

🟡 Medium Risk — Some Line Overlap

These PRs have some overlapping changes:

🟢 Low Risk — File Overlap Only

These PRs touch the same files but different sections (click to expand)

Summary: 1 conflict(s), 4 medium risk, 3 low risk (out of 8 PRs with file overlap)


Auto-generated on push. Ignores: openapi.json, lock files.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@autogpt_platform/backend/backend/copilot/service.py`:
- Around line 970-986: The compaction marker is emitted as normal StreamText*
events (StreamStartStep, StreamTextStart/Delta/End, StreamFinishStep using
compaction_text_id) which causes stream_chat_completion to treat it as assistant
text (flipping has_received_text/text_streaming_ended and appending into
assistant_response.content); change the compaction emission to be out-of-band so
the outer state machine ignores it: either emit dedicated out-of-band events
(e.g., OutOfBandStart/OutOfBandDelta/OutOfBandEnd or StreamMeta* events) or set
a clear out_of_band flag/metadata on the StreamText* events for
compaction_text_id, and update stream_chat_completion to detect that flag or
those event types and skip toggling has_received_text/text_streaming_ended and
skipping appending to assistant_response.content when handling compaction chunks
(reference compaction_text_id, StreamTextStart/StreamTextDelta/StreamTextEnd,
StreamStartStep/StreamFinishStep, and stream_chat_completion).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1c51dd1 and 91e9287.

📒 Files selected for processing (2)
  • autogpt_platform/backend/backend/copilot/response_model.py
  • autogpt_platform/backend/backend/copilot/service.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: types
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.12)
  • GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (4)
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

Files:

  • autogpt_platform/backend/backend/copilot/response_model.py
  • autogpt_platform/backend/backend/copilot/service.py
autogpt_platform/backend/**/*.{py,txt}

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use poetry run prefix for all Python commands, including testing, linting, formatting, and migrations

Files:

  • autogpt_platform/backend/backend/copilot/response_model.py
  • autogpt_platform/backend/backend/copilot/service.py
autogpt_platform/backend/backend/**/*.py

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings

Files:

  • autogpt_platform/backend/backend/copilot/response_model.py
  • autogpt_platform/backend/backend/copilot/service.py
autogpt_platform/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/copilot/response_model.py
  • autogpt_platform/backend/backend/copilot/service.py
🧠 Learnings (2)
📚 Learning: 2026-02-27T10:45:49.499Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx:23-24
Timestamp: 2026-02-27T10:45:49.499Z
Learning: As of PR `#12213`, MCP tool response types (MCPToolsDiscoveredResponse, MCPToolOutputResponse) are defined in openapi.json and frontend code in autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx uses the generated types from `@/app/api/__generated__/`. Other tools like RunBlock still use inline TypeScript interfaces (e.g., BlockDetailsResponse) for SSE stream payloads that are not included in openapi.json schemas. The pattern is tool-specific: use generated types when available in openapi.json, use inline types only when the payload schema is truly SSE-stream-only and not exposed via OpenAPI.

Applied to files:

  • autogpt_platform/backend/backend/copilot/response_model.py
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/backend/copilot/response_model.py
  • autogpt_platform/backend/backend/copilot/service.py
🧬 Code graph analysis (2)
autogpt_platform/backend/backend/copilot/response_model.py (1)
autogpt_platform/backend/backend/copilot/tools/models.py (1)
  • ResponseType (12-50)
autogpt_platform/backend/backend/copilot/service.py (1)
autogpt_platform/backend/backend/copilot/response_model.py (5)
  • StreamStartStep (87-94)
  • StreamTextStart (110-114)
  • StreamTextDelta (117-122)
  • StreamTextEnd (125-129)
  • StreamFinishStep (97-104)
🔇 Additional comments (1)
autogpt_platform/backend/backend/copilot/response_model.py (1)

45-45: Compaction response modeling is consistent with the existing stream schema.

Line 45 and Lines 230-240 cleanly extend the response model pattern without introducing protocol-shape inconsistencies in this file.

Also applies to: 230-240

Comment thread autogpt_platform/backend/backend/copilot/service.py Outdated
@github-actions github-actions Bot added the size/l label Mar 2, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
autogpt_platform/backend/backend/copilot/sdk/service.py (2)

758-773: Remove redundant import — uuid is already imported at module level.

Line 760 imports uuid as uuid_module inside the conditional block, but uuid is already imported at line 9. Use the existing module-level import instead.

♻️ Proposed fix
                # Notify the user if context was compacted before this query
                if was_compacted:
-                    import uuid as uuid_module
-
-                    compaction_text_id = str(uuid_module.uuid4())
+                    compaction_text_id = str(uuid.uuid4())
                    yield StreamStartStep()
                    yield StreamTextStart(id=compaction_text_id)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/copilot/sdk/service.py` around lines 758 -
773, The code redundantly imports uuid inside the was_compacted block; remove
the local import statement and use the module-level uuid instead (replace
uuid_module.uuid4() with uuid.uuid4()). Update the compaction_text_id assignment
in the was_compacted branch where StreamStartStep, StreamTextStart,
StreamTextDelta, StreamTextEnd, and StreamFinishStep are yielded to use
uuid.uuid4() and delete the inner "import uuid as uuid_module" line.

910-926: Remove redundant import and consolidate duplication.

Same issue as above: uuid is already imported at module level. Additionally, the notification emission logic (lines 763-773 and 916-926) is duplicated.

♻️ Proposed fix for import
                        # Check if the SDK triggered internal compaction
                        if sdk_compacted_event.is_set():
                            sdk_compacted_event.clear()
-                            import uuid as uuid_module
-
-                            cid = str(uuid_module.uuid4())
+                            cid = str(uuid.uuid4())
                            yield StreamStartStep()
                            yield StreamTextStart(id=cid)

Consider extracting a helper generator function to emit compaction notifications, reducing duplication between lines 763-773 and 916-926. This could be deferred to a follow-up.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/copilot/sdk/service.py` around lines 910 -
926, Remove the redundant local import "import uuid as uuid_module" and use the
module-level uuid already imported; then replace the duplicated
compaction-notification block inside the sdk_compacted_event branch (the code
that yields StreamStartStep, StreamTextStart, StreamTextDelta with the
summarization message, StreamTextEnd, StreamFinishStep) by calling a single
helper generator function (e.g., emit_compaction_notification or
_compaction_notification_generator) that accepts the event id (generate id with
the module-level uuid.uuid4()) and yields the same
StreamStartStep/StreamTextStart/StreamTextDelta/StreamTextEnd/StreamFinishStep
sequence; update both locations (the block guarded by sdk_compacted_event and
the other duplicate at lines ~763-773) to call this helper to eliminate
duplication while preserving behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@autogpt_platform/backend/backend/copilot/sdk/service.py`:
- Around line 758-773: The code redundantly imports uuid inside the
was_compacted block; remove the local import statement and use the module-level
uuid instead (replace uuid_module.uuid4() with uuid.uuid4()). Update the
compaction_text_id assignment in the was_compacted branch where StreamStartStep,
StreamTextStart, StreamTextDelta, StreamTextEnd, and StreamFinishStep are
yielded to use uuid.uuid4() and delete the inner "import uuid as uuid_module"
line.
- Around line 910-926: Remove the redundant local import "import uuid as
uuid_module" and use the module-level uuid already imported; then replace the
duplicated compaction-notification block inside the sdk_compacted_event branch
(the code that yields StreamStartStep, StreamTextStart, StreamTextDelta with the
summarization message, StreamTextEnd, StreamFinishStep) by calling a single
helper generator function (e.g., emit_compaction_notification or
_compaction_notification_generator) that accepts the event id (generate id with
the module-level uuid.uuid4()) and yields the same
StreamStartStep/StreamTextStart/StreamTextDelta/StreamTextEnd/StreamFinishStep
sequence; update both locations (the block guarded by sdk_compacted_event and
the other duplicate at lines ~763-773) to call this helper to eliminate
duplication while preserving behavior.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 91e9287 and e5e4d72.

📒 Files selected for processing (3)
  • autogpt_platform/backend/backend/copilot/sdk/query_builder_test.py
  • autogpt_platform/backend/backend/copilot/sdk/security_hooks.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: types
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.11)
  • GitHub Check: Seer Code Review
  • GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (6)
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

Files:

  • autogpt_platform/backend/backend/copilot/sdk/security_hooks.py
  • autogpt_platform/backend/backend/copilot/sdk/query_builder_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/backend/**/*.{py,txt}

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use poetry run prefix for all Python commands, including testing, linting, formatting, and migrations

Files:

  • autogpt_platform/backend/backend/copilot/sdk/security_hooks.py
  • autogpt_platform/backend/backend/copilot/sdk/query_builder_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/backend/backend/**/*.py

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings

Files:

  • autogpt_platform/backend/backend/copilot/sdk/security_hooks.py
  • autogpt_platform/backend/backend/copilot/sdk/query_builder_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/copilot/sdk/security_hooks.py
  • autogpt_platform/backend/backend/copilot/sdk/query_builder_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/backend/**/*_test.py

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

autogpt_platform/backend/**/*_test.py: Always review snapshot changes with git diff before committing when updating snapshots with poetry run pytest --snapshot-update
Use pytest with snapshot testing for API responses in test files
Colocate test files with source files using the *_test.py naming convention

Files:

  • autogpt_platform/backend/backend/copilot/sdk/query_builder_test.py
autogpt_platform/backend/**/*test*.py

📄 CodeRabbit inference engine (AGENTS.md)

Run poetry run test for backend testing (runs pytest with docker based postgres + prisma)

Files:

  • autogpt_platform/backend/backend/copilot/sdk/query_builder_test.py
🧠 Learnings (1)
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/security_hooks.py
  • autogpt_platform/backend/backend/copilot/sdk/query_builder_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
🧬 Code graph analysis (2)
autogpt_platform/backend/backend/copilot/sdk/query_builder_test.py (1)
autogpt_platform/backend/backend/copilot/sdk/service.py (1)
  • _build_query_message (445-489)
autogpt_platform/backend/backend/copilot/sdk/service.py (2)
autogpt_platform/backend/backend/copilot/response_model.py (7)
  • StreamFinishStep (97-104)
  • StreamHeartbeat (213-227)
  • StreamStart (62-78)
  • StreamStartStep (87-94)
  • StreamTextDelta (117-122)
  • StreamTextEnd (125-129)
  • StreamTextStart (110-114)
autogpt_platform/backend/backend/copilot/sdk/security_hooks.py (1)
  • create_security_hooks (145-376)
🔇 Additional comments (11)
autogpt_platform/backend/backend/copilot/sdk/security_hooks.py (2)

150-151: LGTM!

The on_compact callback parameter is cleanly integrated with proper type hints, documentation, and conditional invocation. The callback is appropriately optional and invoked only when provided.

Also applies to: 167-169


332-334: LGTM!

The conditional invocation is correct. Since the current caller passes asyncio.Event.set which is guaranteed not to raise, the lack of try/except is acceptable.

autogpt_platform/backend/backend/copilot/sdk/service.py (4)

34-34: LGTM!

The imports for stream event types are correctly added to support the new compaction notification functionality.

Also applies to: 37-40


445-489: LGTM!

The return type change to tuple[str, bool] is well-documented and correctly implemented. The was_compacted flag appropriately indicates when conversation history was compressed into the query context.


654-663: LGTM!

The asyncio.Event is correctly scoped to the stream function and safely wired to the security hooks callback. Using sdk_compacted_event.set as the callback is appropriate since Event.set() is synchronous and thread-safe.


743-756: LGTM!

The unpacking of the tuple return value and subsequent logging are correctly implemented.

autogpt_platform/backend/backend/copilot/sdk/query_builder_test.py (5)

121-130: LGTM!

Test correctly updated to unpack tuple and assert was_compacted=False when transcript is up-to-date.


145-156: LGTM!

Test correctly asserts was_compacted=True when stale transcript requires gap context compression.


169-177: LGTM!

Test correctly asserts was_compacted=False when transcript_msg_count is zero.


184-192: LGTM!

Test correctly asserts was_compacted=False for single-message sessions.


215-226: LGTM!

Test correctly asserts was_compacted=True when compression fallback is used for multi-message sessions without resume.

@autogpt-reviewer autogpt-reviewer left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📋 PR #12250 — feat(copilot): surface context compaction events to the UI

Author: Otto-AGPT | Requested by: 0ubbe, majdyz | Files: response_model.py (+14), sdk/service.py (+59/−5), service.py (+17), query_builder_test.py (+10/−5), security_hooks.py (+5)


🎯 Verdict: APPROVE WITH CONDITIONS

What This PR Does

When CoPilot's context window fills up and the SDK compacts (summarizes/truncates) conversation history, users currently have no visibility. This PR adds a backend-only system notification — emitted as [COPILOT_SYSTEM] stream events — so the frontend's existing parseSpecialMarkers renders a subtle gray info bar: "Earlier messages were summarized to fit within context limits." Three compaction paths are covered: pre-query history compression, SDK-internal compaction (via asyncio.Event bridge), and the legacy OpenAI streaming path.


Specialist Findings

🛡️ Security ✅ — No security concerns. All notification text is hardcoded (no injection vector), no new endpoints or auth changes, no secrets exposure. The on_compact callback is appropriately minimal (Callable[[], None]). asyncio.Event.set() from the hook is sync-safe in CPython's single-threaded event loop.

🏗️ Architecture ⚠️ — Notification logic is copy-pasted in 3 places (sdk/service.py ×2, service.py ×1) with no shared abstraction. StreamCompaction model is defined but never used (dead code). CodeRabbit's concern about compaction events contaminating stream_chat_completion's state machine is valid for the sdk/service.py mid-stream path (events emitted inside the message processing loop could flip has_received_text/text_streaming_ended). The service.py path is safe (emitted before the loop).

Performance ✅ — No performance concerns. All new code runs in cold/rare paths (compaction is infrequent). The asyncio.Event per session, 5 extra stream events per compaction (~500 bytes), and tuple return are all negligible. Lazy import uuid is technically unnecessary (already imported at module level) but ~200ns overhead is irrelevant.

🧪 Testing ⚠️_build_query_message tuple return is properly tested (all 5 existing tests updated, was_compacted asserted). However, the core feature — the 3-site notification emission — has zero test coverage. The on_compact callback and sdk_compacted_event flow are also untested. This is the primary gap.

📖 Quality ⚠️ — DRY violation with identical notification block ×3. Inconsistent variable naming (compaction_text_id vs cid). Magic string repeated 3× with inconsistent line-breaking. Redundant import uuid as uuid_module inside function bodies when uuid is already imported at module level. StreamCompaction model is unused dead code.

📦 Product ⚠️ — Feature solves a real user problem with minimal UX disruption. Concerns: (1) "summarized" may understate data loss — users could be surprised when AI can't recall specifics; (2) double-notification risk if both pre-query and SDK-internal compaction fire in the same turn; (3) mid-stream compaction notification could appear between tool calls, confusing users.

📬 Discussion ⚠️ — Zero human reviews from requested reviewers (0ubbe, majdyz). CodeRabbit's critical comment about state machine contamination is unresolved (no author reply). Medium merge-conflict risk with PR #12212 (majdyz) — overlaps on exact same lines in sdk/service.py. CI tests still pending at review time.

🔎 QA ✅ — Frontend loads correctly, copilot chat interface functional, zero console errors. parseSpecialMarkers correctly parses [COPILOT_SYSTEM] prefix and renders as subtle gray italic info bar. Cannot trigger actual compaction in QA (requires exceeding context window), but code path verified end-to-end.

QA Screenshots:


Should Fix (before merge)

  1. Extract notification helper to eliminate 3× duplicationsdk/service.py:760-773, sdk/service.py:913-926, service.py:973-986. Create a shared emit_compaction_notification() function (e.g., in response_model.py) that returns the 5 stream events. This also eliminates 3× redundant import uuid, 3× inconsistent magic strings, and the compaction_text_id vs cid naming inconsistency. The COPILOT_SYSTEM_PREFIX constant already exists at sdk/service.py:143 — use it.

  2. Remove or use StreamCompaction modelresponse_model.py:230-241 defines StreamCompaction with ResponseType.COMPACTION but it's never instantiated. Either delete it (since notifications use raw stream events) or make it the canonical factory for compaction events.

  3. Verify mid-stream compaction doesn't corrupt SDK stream statesdk/service.py:913-926: compaction events emitted inside the message-processing loop could interact with the stream adapter's state tracking (has_received_text, text_streaming_ended, assistant_response.content). Verify this path doesn't append the system marker text to the assistant response or confuse the state machine. The service.py path (pre-loop) is safe.

  4. Add deduplication for compaction notifications — Both was_compacted (pre-query) and sdk_compacted_event (mid-stream) can fire during the same query turn, causing the user to see two identical gray bars. Track whether a notification was already emitted and skip duplicates.

Nice to Have (follow-up OK)

  1. Add unit tests for notification emission — At minimum: test that _stream_chat_chunks yields the 5-event sequence when compaction occurs, and that stream_chat_completion_sdk does the same for both trigger paths. Test on_compact callback invocation.

  2. Improve notification copy — Consider "Earlier messages were condensed. The assistant may not recall specific details." to set clearer expectations about potential data loss.

  3. Move import uuid to module top-level — Already imported in both files; the inline re-imports are redundant.

  4. Coordinate with PR #12212 — Medium overlap risk on sdk/service.py at exactly the lines this PR modifies (L765-777, L916-921).


Risk Assessment

Merge risk: LOW | Rollback: EASY

The feature is purely additive — it emits extra SSE events that the frontend already knows how to render. No database changes, no new endpoints, no auth changes. If the notification causes issues, the 3 if was_compacted / if sdk_compacted_event.is_set() / if context_result.was_compacted guards can be trivially reverted. The duplication and missing tests are quality concerns, not correctness risks for the happy path.


Automated review by PR Review Squad — 8/8 specialists reported • [QA screenshots above]

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
autogpt_platform/backend/backend/copilot/service.py (2)

69-71: Consider extracting COPILOT_SYSTEM_PREFIX to a shared module.

The constant is duplicated from sdk/service.py with a comment noting the duplication. To avoid drift, consider moving it to a shared location (e.g., response_model.py or a new constants.py).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/copilot/service.py` around lines 69 - 71,
COPILOT_SYSTEM_PREFIX is duplicated here and in sdk/service.py; extract it into
a shared module (e.g., create or use response_model.py or a new constants.py)
and import it from there in autogpt_platform/backend/backend/copilot/service.py
and sdk/service.py to avoid drift; update both files to remove the local
definition of COPILOT_SYSTEM_PREFIX and replace with a single import of the
constant (refer to COPILOT_SYSTEM_PREFIX, service.py, and sdk/service.py to
locate usages), run tests/lint to ensure imports resolve.

4-4: Remove duplicate uuid import.

The module imports uuid as uuid_module at Line 4 (top-level), but Line 497 imports it again inside the function. The function-level import is redundant and should be removed.

♻️ Proposed fix
@@ -494,9 +494,6 @@
     tool_response_messages: list[ChatMessage] = []
     should_retry = False
 
-    # Generate unique IDs for AI SDK protocol
-    import uuid as uuid_module
-
     is_continuation = _continuation_message_id is not None
     message_id = _continuation_message_id or str(uuid_module.uuid4())
     text_block_id = str(uuid_module.uuid4())

Also applies to: 497-497

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/copilot/service.py` at line 4, Remove the
redundant inner import of uuid inside the function in
autogpt_platform/backend/backend/copilot/service.py and use the module-level
alias uuid_module (imported as "import uuid as uuid_module" at top) instead;
locate the function that contains a local "import uuid" at the end of the file,
delete that inner import statement, and update any local uses to call
uuid_module.uuid4()/other members as needed so all uuid references use the
single top-level uuid_module import.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@autogpt_platform/backend/backend/copilot/service.py`:
- Around line 69-71: COPILOT_SYSTEM_PREFIX is duplicated here and in
sdk/service.py; extract it into a shared module (e.g., create or use
response_model.py or a new constants.py) and import it from there in
autogpt_platform/backend/backend/copilot/service.py and sdk/service.py to avoid
drift; update both files to remove the local definition of COPILOT_SYSTEM_PREFIX
and replace with a single import of the constant (refer to
COPILOT_SYSTEM_PREFIX, service.py, and sdk/service.py to locate usages), run
tests/lint to ensure imports resolve.
- Line 4: Remove the redundant inner import of uuid inside the function in
autogpt_platform/backend/backend/copilot/service.py and use the module-level
alias uuid_module (imported as "import uuid as uuid_module" at top) instead;
locate the function that contains a local "import uuid" at the end of the file,
delete that inner import statement, and update any local uses to call
uuid_module.uuid4()/other members as needed so all uuid references use the
single top-level uuid_module import.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e5e4d72 and 4184b22.

📒 Files selected for processing (2)
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/service.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: types
  • GitHub Check: Seer Code Review
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.11)
  • GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (4)
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

Files:

  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/service.py
autogpt_platform/backend/**/*.{py,txt}

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use poetry run prefix for all Python commands, including testing, linting, formatting, and migrations

Files:

  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/service.py
autogpt_platform/backend/backend/**/*.py

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings

Files:

  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/service.py
autogpt_platform/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/service.py
🧠 Learnings (1)
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/service.py
🧬 Code graph analysis (2)
autogpt_platform/backend/backend/copilot/sdk/service.py (2)
autogpt_platform/backend/backend/copilot/response_model.py (7)
  • StreamFinishStep (96-103)
  • StreamHeartbeat (212-226)
  • StreamStart (61-77)
  • StreamStartStep (86-93)
  • StreamTextDelta (116-121)
  • StreamTextEnd (124-128)
  • StreamTextStart (109-113)
autogpt_platform/backend/backend/copilot/sdk/security_hooks.py (1)
  • create_security_hooks (145-376)
autogpt_platform/backend/backend/copilot/service.py (1)
autogpt_platform/backend/backend/copilot/response_model.py (5)
  • StreamTextStart (109-113)
  • StreamTextDelta (116-121)
  • StreamTextEnd (124-128)
  • StreamStartStep (86-93)
  • StreamFinishStep (96-103)
🔇 Additional comments (7)
autogpt_platform/backend/backend/copilot/sdk/service.py (5)

34-43: LGTM! Clean import additions for stream event types.

The new imports for StreamFinishStep, StreamStartStep, StreamTextDelta, StreamTextEnd, and StreamTextStart are correctly organized and necessary for the compaction notification feature.


451-489: LGTM! Well-documented signature change for _build_query_message.

The function now returns a tuple[str, bool] where the boolean indicates whether compaction occurred. The docstring is properly updated to document the return value, and all return paths correctly provide both values.


654-663: LGTM! Clean asyncio.Event integration for SDK compaction signaling.

Using asyncio.Event to signal compaction from the SDK's pre-compact hook is a good pattern. The event is passed as a callback via on_compact=sdk_compacted_event.set, which aligns with the security hooks signature documented in the relevant snippets.


757-771: LGTM! Pre-query compaction notification emits a self-contained step.

The compaction notice correctly uses a distinct compaction_text_id (separate from the main text stream) and emits a complete step sequence: StreamStartStepStreamTextStartStreamTextDeltaStreamTextEndStreamFinishStep. This ensures the frontend can render it as an out-of-band system message without contaminating the assistant response state.


908-923: LGTM! Mid-stream compaction notification with proper event clearing.

The sdk_compacted_event.is_set() check followed by clear() ensures each compaction event is surfaced exactly once. The notification sequence mirrors the pre-query emission, maintaining consistency.

autogpt_platform/backend/backend/copilot/service.py (2)

526-537: LGTM! Out-of-band event passthrough correctly addresses stream state contamination.

The logic properly detects events with a different id than text_block_id and yields them without updating has_received_text, text_streaming_ended, or appending to assistant_response.content. The StreamStartStep and StreamFinishStep events are also passed through directly. This prevents compaction notices from polluting the primary text stream state.


988-1002: LGTM! Compaction notification emits a self-contained step with distinct ID.

The implementation correctly:

  1. Uses a unique compaction_text_id separate from the main text_block_id
  2. Emits a complete step sequence that the outer stream_chat_completion will pass through as out-of-band
  3. Uses the COPILOT_SYSTEM_PREFIX marker for frontend parsing

This aligns with the SDK path implementation in sdk/service.py.

Comment thread autogpt_platform/backend/backend/copilot/service.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
autogpt_platform/backend/backend/copilot/sdk/service.py (1)

460-486: ⚠️ Potential issue | 🟠 Major

was_compacted is set True for non-compaction paths.

This currently triggers the compaction notice even when history was only reformatted or when compression did not compact anything, so users can get a misleading system message.

💡 Proposed fix
 async def _build_query_message(
@@
-            if gap_context:
+            if gap_context:
                 logger.info(
                     f"[SDK] Transcript stale: covers {transcript_msg_count} "
                     f"of {msg_count} messages, compressing {len(gap)} missed"
                 )
                 return (
                     f"{gap_context}\n\nNow, the user says:\n{current_message}",
-                    True,
+                    False,
                 )
@@
-        compressed = await _compress_conversation_history(session)
+        compressed, was_compacted = await _compress_conversation_history(session)
         history_context = _format_conversation_context(compressed)
         if history_context:
             return (
                 f"{history_context}\n\nNow, the user says:\n{current_message}",
-                True,
+                was_compacted,
             )
 
     return current_message, False
-async def _compress_conversation_history(
-    session: ChatSession,
-) -> list[ChatMessage]:
+async def _compress_conversation_history(
+    session: ChatSession,
+) -> tuple[list[ChatMessage], bool]:
@@
-    if len(messages) < 2:
-        return messages
+    if len(messages) < 2:
+        return messages, False
@@
-        return [
+        return ([
             ChatMessage(
                 role=m["role"],
                 content=m.get("content"),
                 tool_calls=m.get("tool_calls"),
                 tool_call_id=m.get("tool_call_id"),
             )
             for m in result.messages
-        ]
+        ], True)
 
-    return messages
+    return messages, False
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/copilot/sdk/service.py` around lines 460 -
486, The boolean "was_compacted" is being set True even when no compaction
occurred; update the returns in the branches inside the function (the block that
calls _compress_conversation_history(session) and the gap-handling block that
builds gap_context via _format_conversation_context) so that True is only
returned when actual compaction happened: for the compression fallback, call
_compress_conversation_history(session), then set was_compacted = True only if
len(compressed) < msg_count (or if _compress_conversation_history can return a
flag, use that); for the transcript gap path do not set was_compacted=True
unless you perform a real compaction (e.g., if you later compress gap and its
length is reduced). Ensure the final return signature remains (context_string,
was_compacted) and update any callers if the boolean semantics change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@autogpt_platform/backend/backend/copilot/sdk/service.py`:
- Around line 460-486: The boolean "was_compacted" is being set True even when
no compaction occurred; update the returns in the branches inside the function
(the block that calls _compress_conversation_history(session) and the
gap-handling block that builds gap_context via _format_conversation_context) so
that True is only returned when actual compaction happened: for the compression
fallback, call _compress_conversation_history(session), then set was_compacted =
True only if len(compressed) < msg_count (or if _compress_conversation_history
can return a flag, use that); for the transcript gap path do not set
was_compacted=True unless you perform a real compaction (e.g., if you later
compress gap and its length is reduced). Ensure the final return signature
remains (context_string, was_compacted) and update any callers if the boolean
semantics change.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4184b22 and 8bc2b98.

📒 Files selected for processing (3)
  • autogpt_platform/backend/backend/copilot/constants.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/service.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: types
  • GitHub Check: Seer Code Review
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.13)
  • GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (4)
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

Files:

  • autogpt_platform/backend/backend/copilot/constants.py
  • autogpt_platform/backend/backend/copilot/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/backend/**/*.{py,txt}

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use poetry run prefix for all Python commands, including testing, linting, formatting, and migrations

Files:

  • autogpt_platform/backend/backend/copilot/constants.py
  • autogpt_platform/backend/backend/copilot/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/backend/backend/**/*.py

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings

Files:

  • autogpt_platform/backend/backend/copilot/constants.py
  • autogpt_platform/backend/backend/copilot/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/copilot/constants.py
  • autogpt_platform/backend/backend/copilot/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
🧠 Learnings (2)
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/backend/copilot/constants.py
  • autogpt_platform/backend/backend/copilot/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
📚 Learning: 2026-02-20T03:28:06.619Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12173
File: autogpt_platform/backend/backend/copilot/sdk/tool_adapter.py:153-179
Timestamp: 2026-02-20T03:28:06.619Z
Learning: The copilot executor (`backend.copilot.executor`) runs in its own Docker container with Python 3.13, so Python 3.11+ features like `asyncio.timeout()` are available in copilot code even though the general backend pyproject.toml specifies `python = ">=3.10,<3.14"`.

Applied to files:

  • autogpt_platform/backend/backend/copilot/service.py
🧬 Code graph analysis (2)
autogpt_platform/backend/backend/copilot/service.py (1)
autogpt_platform/backend/backend/copilot/response_model.py (5)
  • StreamTextStart (109-113)
  • StreamTextDelta (116-121)
  • StreamTextEnd (124-128)
  • StreamStartStep (86-93)
  • StreamFinishStep (96-103)
autogpt_platform/backend/backend/copilot/sdk/service.py (2)
autogpt_platform/backend/backend/copilot/response_model.py (7)
  • StreamFinishStep (96-103)
  • StreamHeartbeat (212-226)
  • StreamStart (61-77)
  • StreamStartStep (86-93)
  • StreamTextDelta (116-121)
  • StreamTextEnd (124-128)
  • StreamTextStart (109-113)
autogpt_platform/backend/backend/copilot/sdk/security_hooks.py (1)
  • create_security_hooks (145-376)
🔇 Additional comments (3)
autogpt_platform/backend/backend/copilot/constants.py (1)

1-7: Good centralization of frontend marker constants.

This keeps marker definitions single-sourced and makes cross-module behavior more consistent.

autogpt_platform/backend/backend/copilot/service.py (1)

524-535: Out-of-band compaction handling looks correct.

The non-primary StreamText* pass-through plus self-contained compaction step prevents primary text-state contamination while still surfacing the UI notice.

Also applies to: 986-1000

autogpt_platform/backend/backend/copilot/sdk/service.py (1)

651-661: Nice wiring for SDK compaction hook → stream notification.

Using an asyncio.Event bridge from hooks to stream emission is a clean, low-coupling approach.

Also applies to: 905-921

Comment thread autogpt_platform/backend/backend/copilot/sdk/service.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
autogpt_platform/backend/backend/copilot/sdk/service.py (1)

740-790: ⚠️ Potential issue | 🟠 Major

Avoid preemptive “Summarizing…” events before compaction is confirmed.

Line 747 emits a compaction-in-progress notice based on may_compact heuristic, but Lines 776-787 only emit completion when was_compacted is true. On false positives, users still see “Summarizing earlier messages...” even though no compaction happened.

💡 Proposed fix
-                may_compact = (not use_resume and len(session.messages) > 1) or (
-                    use_resume
-                    and transcript_msg_count > 0
-                    and transcript_msg_count < len(session.messages) - 1
-                )
-                if may_compact:
-                    start_id = str(uuid.uuid4())
-                    yield StreamStartStep()
-                    yield StreamTextStart(id=start_id)
-                    yield StreamTextDelta(
-                        id=start_id,
-                        delta=(
-                            f"{COPILOT_SYSTEM_PREFIX} Summarizing earlier "
-                            "messages..."
-                        ),
-                    )
-                    yield StreamTextEnd(id=start_id)
-
                 query_message, was_compacted = await _build_query_message(
                     current_message,
                     session,
                     use_resume,
                     transcript_msg_count,
                     session_id,
                 )
@@
-                if was_compacted:
+                if was_compacted:
                     done_id = str(uuid.uuid4())
+                    yield StreamStartStep()
                     yield StreamTextStart(id=done_id)
                     yield StreamTextDelta(
                         id=done_id,
                         delta=(
                             f"{COPILOT_SYSTEM_PREFIX} Earlier messages were "
                             "summarized to fit within context limits."
                         ),
                     )
                     yield StreamTextEnd(id=done_id)
-
-                if may_compact:
                     yield StreamFinishStep()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/copilot/sdk/service.py` around lines 740 -
790, The code currently emits a "Summarizing earlier messages..." stream when
may_compact is true before calling _build_query_message, which can lead to a
false positive if _build_query_message returns was_compacted=False; change the
logic to only emit the summarizing start/delta/end (using start_id,
StreamTextStart, StreamTextDelta, StreamTextEnd) and the corresponding
StreamFinishStep after _build_query_message and only when was_compacted is true;
keep may_compact only as a pre-check if needed for behavior gating but do not
send any stream events until was_compacted is confirmed by _build_query_message.
🧹 Nitpick comments (1)
autogpt_platform/backend/backend/copilot/sdk/service.py (1)

740-759: Extract a helper for compaction notice emission.

These blocks duplicate near-identical step/text emission logic; a small helper would reduce drift risk between “start/done” variants and make future copy changes safer.

Also applies to: 775-787, 826-843, 945-966

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/copilot/sdk/service.py` around lines 740 -
759, The compaction-notice emission logic is duplicated across blocks (see uses
of may_compact and the sequence of StreamStartStep, StreamTextStart,
StreamTextDelta, StreamTextEnd); extract a small helper function (e.g.,
emit_compaction_notice or _emit_compaction_steps) that takes the generated id
and the notice text and yields the four steps so callers like the block
computing may_compact can replace their inlined yield sequence with a single
call to that helper; update all duplicate sites (including the other occurrences
around the file) to call the helper to keep start/done variants consistent and
reduce drift risk.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@autogpt_platform/backend/backend/copilot/sdk/service.py`:
- Around line 740-790: The code currently emits a "Summarizing earlier
messages..." stream when may_compact is true before calling
_build_query_message, which can lead to a false positive if _build_query_message
returns was_compacted=False; change the logic to only emit the summarizing
start/delta/end (using start_id, StreamTextStart, StreamTextDelta,
StreamTextEnd) and the corresponding StreamFinishStep after _build_query_message
and only when was_compacted is true; keep may_compact only as a pre-check if
needed for behavior gating but do not send any stream events until was_compacted
is confirmed by _build_query_message.

---

Nitpick comments:
In `@autogpt_platform/backend/backend/copilot/sdk/service.py`:
- Around line 740-759: The compaction-notice emission logic is duplicated across
blocks (see uses of may_compact and the sequence of StreamStartStep,
StreamTextStart, StreamTextDelta, StreamTextEnd); extract a small helper
function (e.g., emit_compaction_notice or _emit_compaction_steps) that takes the
generated id and the notice text and yields the four steps so callers like the
block computing may_compact can replace their inlined yield sequence with a
single call to that helper; update all duplicate sites (including the other
occurrences around the file) to call the helper to keep start/done variants
consistent and reduce drift risk.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8bc2b98 and 0bcdbce.

📒 Files selected for processing (2)
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/service.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: types
  • GitHub Check: Seer Code Review
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.12)
  • GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (4)
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

Files:

  • autogpt_platform/backend/backend/copilot/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/backend/**/*.{py,txt}

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use poetry run prefix for all Python commands, including testing, linting, formatting, and migrations

Files:

  • autogpt_platform/backend/backend/copilot/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/backend/backend/**/*.py

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings

Files:

  • autogpt_platform/backend/backend/copilot/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/copilot/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
🧠 Learnings (2)
📚 Learning: 2026-02-20T03:28:06.619Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12173
File: autogpt_platform/backend/backend/copilot/sdk/tool_adapter.py:153-179
Timestamp: 2026-02-20T03:28:06.619Z
Learning: The copilot executor (`backend.copilot.executor`) runs in its own Docker container with Python 3.13, so Python 3.11+ features like `asyncio.timeout()` are available in copilot code even though the general backend pyproject.toml specifies `python = ">=3.10,<3.14"`.

Applied to files:

  • autogpt_platform/backend/backend/copilot/service.py
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/backend/copilot/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
🧬 Code graph analysis (2)
autogpt_platform/backend/backend/copilot/service.py (1)
autogpt_platform/backend/backend/copilot/response_model.py (5)
  • StreamTextStart (109-113)
  • StreamTextDelta (116-121)
  • StreamTextEnd (124-128)
  • StreamStartStep (86-93)
  • StreamFinishStep (96-103)
autogpt_platform/backend/backend/copilot/sdk/service.py (2)
autogpt_platform/backend/backend/copilot/response_model.py (5)
  • StreamStart (61-77)
  • StreamStartStep (86-93)
  • StreamTextDelta (116-121)
  • StreamTextEnd (124-128)
  • StreamTextStart (109-113)
autogpt_platform/backend/backend/copilot/sdk/security_hooks.py (1)
  • create_security_hooks (145-376)
🔇 Additional comments (3)
autogpt_platform/backend/backend/copilot/sdk/service.py (1)

651-661: Good hook wiring for SDK-side compaction signaling.

Using sdk_compacted_event and passing on_compact=sdk_compacted_event.set cleanly bridges SDK hooks to the stream layer without coupling hook internals to SSE emission.

autogpt_platform/backend/backend/copilot/service.py (2)

524-535: Out-of-band stream chunks are now correctly isolated from primary text state.

The ID mismatch gate plus direct pass-through for StreamStartStep/StreamFinishStep prevents compaction/system chunks from polluting assistant_response.content and text boundary flags.


987-1000: Compaction event shape is well-formed and UI-friendly.

The StartStep → TextStart → TextDelta → TextEnd → FinishStep sequence with a dedicated text ID is consistent and keeps compaction messaging self-contained.

@autogpt-reviewer autogpt-reviewer left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📋 PR #12250 — feat(copilot): surface context compaction events to the UI (Re-review #2)

Author: Otto-AGPT | Requested by: 0ubbe, majdyz | HEAD: 0bcdbcef
Files: constants.py (+7), sdk/service.py (+102/−9), service.py (+32/−1), query_builder_test.py (+10/−5), security_hooks.py (+5)

New commits since last review (4184b220bcdbce):

  • 8bc2b983b — refactor: extract prefix constants to constants.py
  • 0bcdbcefb — feat: bracket compaction like tool calls (start/done)

🎯 Verdict: APPROVE WITH CONDITIONS

What This PR Does

Adds user-visible notifications when CoPilot compacts (summarizes) conversation history to fit within context limits. The backend emits [COPILOT_SYSTEM]-prefixed stream events that the frontend's existing parseSpecialMarkers renders as subtle gray info bars. New in this iteration: a "Summarizing earlier messages..." in-progress notice appears before compaction, followed by "Earlier messages were summarized to fit within context limits." when done — mirroring the tool-call UX pattern.

Previous Should-Fix Status

# Item Status
1 Extract constant duplication (COPILOT_SYSTEM_PREFIX) FIXEDconstants.py created, both services import from it
2 Remove/use StreamCompaction dead code FIXED — removed from codebase
3 Verify mid-stream safety (stream state contamination) FIXED — out-of-band passthrough at service.py:524-535 isolates compaction events from primary text state
4 Add notification dedup + tests ⚠️ PARTIALLY — tests updated for was_compacted return, but emission paths untested; dedup not addressed

Specialist Findings

🛡️ Security ✅ — No security issues. All notification text is hardcoded (no injection vector). on_compact callback is minimal (Callable[[], None]), invoked with no arguments — no data exfiltration possible. asyncio.Event bridge is safe (single-threaded event loop). No new endpoints, auth changes, or secrets. Pre-existing minor concern: LLM could output [COPILOT_SYSTEM] to spoof UI, but not introduced by this PR.

🏗️ Architecture ⚠️ — Constants extraction is clean. Bracket pattern (start/done) mirrors existing tool-call UX — correct design. However, notification emission is now duplicated 5× across 2 files (was 3× before, now worse). _build_query_messagetuple[str, bool] is clean. may_compact heuristic is correct but can false-positive (emits "Summarizing..." when no compaction occurs). Out-of-band passthrough in service.py works but is implicit (ID-based, not flagged).

Performance ✅ — No performance concerns. may_compact is O(1) integer comparison. SSE bracketing adds <1KB on infrequent compaction events. asyncio.Event.is_set() is a boolean check. Pass-through filter adds negligible isinstance + getattr per chunk.

🧪 Testing ⚠️_build_query_message tuple return is properly tested (all 5 tests updated, was_compacted asserted for each path). However, the core feature — notification emission from 5 sites — has zero test coverage. on_compact callback, sdk_compacted_event flow, may_compact heuristic, and out-of-band passthrough are all untested.

📖 Quality ⚠️ — DRY violation: identical 5-line emit block copy-pasted 5×. Inconsistent variable naming (start_id, done_id, s_id, compaction_text_id). Redundant inline import uuid as uuid_module remains in service.py:495. A simple _emit_system_notice(msg) helper would eliminate all 5 duplication sites.

📦 Product ⚠️ — Feature solves a real user problem. "Summarizing..." / "Summarized." copy is clear. Two concerns: (1) Double-notification risk persists — pre-query compaction AND SDK-internal compaction can both fire in one turn, showing two notices. (2) Heartbeat-path SDK compaction only shows "Summarizing..." with no "done" follow-up (step opens and closes immediately with in-progress text).

📬 Discussion ⚠️ — Still zero human reviews from requested reviewers (0ubbe, majdyz). CodeRabbit flagged a major issue: was_compacted returns True for non-compaction paths (gap-context, compression fallback) — potentially over-triggering notifications. CodeRabbit paused auto-reviews noting "branch under active development." Medium merge-conflict risk with PR #12212 on exact lines modified.

🔎 QA ✅ — Frontend loads correctly. Signup works. Copilot chat functional. Both compaction messages render correctly as gray italic info bars. "Summarizing..." appeared during processing, "Summarized..." appeared on completion. All 13 backend tests pass. No regressions. No console errors.

QA Screenshots:


Should Fix (before merge)

  1. Extract notification helper to eliminate 5× duplicationsdk/service.py (4 sites) + service.py (1 site). Create a shared generator/function like _emit_system_notice(msg: str) that yields the 5 stream events (StreamStartStep → StreamTextStart → StreamTextDelta → StreamTextEnd → StreamFinishStep). This was the primary should-fix from the previous review and duplication has increased from 3× to 5×.

  2. Fix may_compact false-positivesdk/service.py:742-746: when may_compact=True but was_compacted=False, the user sees "Summarizing earlier messages..." followed by the step closing with no confirmation. Either: (a) don't emit the "Summarizing..." start until after _build_query_message confirms compaction (emit start+done together), or (b) emit a "No summarization needed" close message. CodeRabbit also flagged was_compacted returning True for non-compaction paths.

  3. Add deduplication for compaction notifications — Pre-query (was_compacted) and SDK-internal (sdk_compacted_event) can both fire in the same turn, showing two identical notices. Track whether notification was already emitted per turn.

Nice to Have (follow-up OK)

  1. Add unit tests for notification emission — Test _stream_chat_chunks yields compaction events when context_result.was_compacted=True. Test on_compact callback invocation. Test sdk_compacted_event produces bracket events.

  2. Consistent variable naming — Pick one name for the text-block ID (text_id or notice_id) instead of 5 different names.

  3. Remove redundant inline import uuidservice.py:495 has an inline import uuid as uuid_module when it's already imported at line 4.

  4. Coordinate with PR #12212 — Medium merge-conflict risk on sdk/service.py at the exact lines this PR modifies.


Risk Assessment

Merge risk: LOW | Rollback: EASY

The feature is purely additive — extra SSE events the frontend already renders. No database changes, no new endpoints, no auth changes. The may_compact false-positive is cosmetic (user sees a briefly misleading "Summarizing..." that closes without confirmation). The dedup issue is also cosmetic (two notices instead of one). Both are UX polish, not correctness risks.

The constants extraction and stream state isolation from the previous review cycle are solid improvements. The bracket pattern (start/done) is architecturally sound. The main remaining concern is code hygiene (5× duplication) and the minor UX rough edges.


Automated re-review by PR Review Squad — 8/8 specialists reported • [QA screenshots above]

Comment thread autogpt_platform/backend/backend/copilot/sdk/service.py Outdated
@Otto-AGPT
Otto-AGPT force-pushed the otto/secrt-2053-copilot-compaction-ui-event branch from 0e92ddf to 3f33617 Compare March 2, 2026 03:03
Comment thread autogpt_platform/backend/backend/copilot/sdk/service.py Outdated
@Otto-AGPT
Otto-AGPT force-pushed the otto/secrt-2053-copilot-compaction-ui-event branch 3 times, most recently from ab8903a to 859129b Compare March 2, 2026 03:18
Comment thread autogpt_platform/backend/backend/copilot/sdk/service.py Outdated
@Otto-AGPT
Otto-AGPT force-pushed the otto/secrt-2053-copilot-compaction-ui-event branch 2 times, most recently from e21cd2c to e3822b0 Compare March 2, 2026 03:58
Comment thread autogpt_platform/backend/backend/copilot/service.py
majdyz and others added 9 commits March 3, 2026 13:52
…pact

When pre-query compression fired and the compressed context was still
large enough for the SDK to trigger its own compaction, the user saw
"Earlier messages were summarized" twice in the same turn.

- emit_pre_query now sets _done=True to suppress SDK-internal notification
- reset_for_query is called before emit_pre_query so the pre-query flag
  takes effect for the current turn
The size-based upload guard was skipping both content and metadata
updates when the transcript size was unchanged. This caused
message_count to stagnate, making the gap-fill logic re-compress
the same messages every turn.
compaction_events() generated a new random ID when building persistence
events, causing a mismatch with the ID already streamed to the frontend.
After page refresh the frontend couldn't match the tool call to its output.
Move COMPACTION_TOOL_NAME, compaction event builders (compaction_start_events,
compaction_end_events, compaction_events), filter_compaction_messages, and
is_compaction_tool_call into the single compaction module.

- response_model.py: removed all compaction code (was ~55 lines)
- sdk/service.py: removed _filter_compaction_messages, replaced inline
  COMPACTION_TOOL_NAME filtering in _format_conversation_context with
  filter_compaction_messages() call
- service.py (legacy): imports compaction_events from sdk/compaction
- sdk/compaction.py: single source of truth for all compaction logic
… API

- Remove noqa: ANN201 by adding proper Callable type annotation
- Make event builders private (_start_events, _end_events, _new_tool_call_id)
- Remove is_compaction_tool_call (inlined into filter_compaction_messages)
- Simplify persist_compaction to _persist(session, tool_call_id, message)
  instead of parsing events to extract IDs
- Add emit_compaction() convenience for the legacy path
- Remove COMPACTION_DONE_MSG import from service.py (no longer needed)
- Net -23 lines
- Add passthrough filter for compaction StreamToolInput*/StreamToolOutput*
  events in service.py to prevent contaminating accumulated_tool_calls
  and spurious LLM continuation calls (blocker from reviews #4-#6)
- Add comprehensive unit tests for sdk/compaction.py (22 tests covering
  CompactionTracker state machine, filter_compaction_messages, emit_compaction,
  event builders, and tool_call_id consistency)
- Assert was_compacted in all query_builder_test.py tests, add test for
  True path
- Simplify emit_pre_query to delegate to emit_compaction (DRY)
- Make compact_start attribute private (_compact_start)
…g COMPACTION_TOOL_NAME

service.py importing from sdk.compaction triggers circular import:
service.py → sdk.compaction → sdk/__init__.py → sdk/service.py → service.py

Use 'context_compaction' string directly in the passthrough filter.
Avoids circular import while keeping a single source of truth.
constants.py has no internal deps so it's safe to import from anywhere.
@Otto-AGPT
Otto-AGPT force-pushed the otto/secrt-2053-copilot-compaction-ui-event branch from c2fd871 to 823069a Compare March 3, 2026 13:53
@github-actions github-actions Bot removed the conflicts Automatically applied to PRs with merge conflicts label Mar 3, 2026
@github-actions

github-actions Bot commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

Conflicts have been resolved! 🎉 A maintainer will review the pull request shortly.

Comment thread autogpt_platform/backend/backend/copilot/sdk/service.py

@autogpt-reviewer autogpt-reviewer left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

PR #12250 — feat(copilot): render context compaction as tool-call UI events
Author: Otto-AGPT | Files: 17 changed (+909/−621) | HEAD: 089d0875

🎯 Verdict: APPROVE


What This PR Does

Surfaces context compaction (conversation summarization to fit within token limits) as a visible tool-call UI element. Users see a spinning ArrowsClockwise icon with "Summarizing earlier messages…" during compaction, then "Earlier messages were summarized" when done. Reuses the existing GenericTool infrastructure. The latest commit adds hex-suffixed marker prefixes ([__COPILOT_ERROR_f7a1__], [__COPILOT_SYSTEM_e3b0__]) to prevent LLM false-positive marker detection.

Previous Condition Status (from re-reviews #1#7)

# Condition Status
1 Non-SDK passthrough filter for tool-call events ✅ FIXED (re-review #7)
2 sdk/compaction.py needs unit tests ✅ FIXED — 291 lines covering all public APIs
3 Assert was_compacted in query builder tests ✅ FIXED — all tests assert + new True path test
4 isExplicitStopRef stop-button behavior ✅ DEFERRED to PR #12254

All previous blockers and conditions remain resolved.


Specialist Findings

🛡️ Security ✅ — No issues. Hex-suffixed markers are a security improvement — previous simple [COPILOT_ERROR] strings could plausibly appear in LLM output enabling fake error card injection. New _f7a1/_e3b0 suffixes make this statistically negligible. All notification text hardcoded. filter_compaction_messages uses safe dict .get() chains. asyncio.Event bridge safe within single-threaded model. No auth/endpoint changes.

🏗️ Architecture ✅ — constants.py is a proper leaf module with zero imports. Dependency graph clean: constants.pysdk/compaction.pysdk/service.py. CompactionTracker 3-state machine (idle → start_emitted → done) is sound with clean reset semantics. Minor note: lazy import of emit_compaction in legacy path crosses copilot → copilot.sdk boundary but is functionally clean. Heartbeat interval changed 3s → 10s (2s margin vs 12s frontend timeout — tight but valid).

Performance ✅ — No concerns. filter_compaction_messages is optimal O(n) with set-based lookups. Double-call in compress+format paths is harmless at realistic conversation sizes. Heartbeat increase 3s→10s is a net positive (fewer SSE events). Compaction emits exactly 5 events per occurrence, at most once per query. Transcript metadata always-update avoids redundant re-compression.

🧪 Testing ✅ — Excellent coverage. compaction_test.py (291 lines, 22 tests): CompactionTracker state machine, event builders, filtering, persistence. query_builder_test.py (14 tests): all assert was_compacted, new True path test. CI fully green: 3.11/3.12/3.13, lint, types, e2e, integration. Minor gap: no frontend component tests for compaction category, consistent with project patterns.

📖 Quality ✅ — Clean organization. Constants centralized with inline comments. Excellent module docstring in compaction.py. DRY throughout — compaction_events() / _start_events() / _end_events() factoring avoids repetition. Minor nits: _new_tool_call_id missing docstring, inline import in legacy path could use explaining comment. Nothing blocking.

📦 Product ✅ — UX well-considered. ArrowsClockwise icon intuitive for summarization. Copy is clear and non-technical ("Summarizing earlier messages…" / "Earlier messages were summarized"). Accordion correctly suppressed for compaction (no meaningful output to expand). Both pre-query and SDK compaction paths emit consistent UI events. Hex-suffix change invisible to users.

📬 Discussion ⚠️Zero human reviews across ALL 8 iterations. Requested reviewers 0ubbe and majdyz have not engaged. Author responsive through code (20+ commits) but zero written comments. New Sentry finding (post-c2fd8716): stuck UI spinner if SDK stream terminates after heartbeat triggers emit_start_if_ready() but before message arrives — finally block doesn't close spinner. Merge conflicts resolved. Overlap with PR #12259 (0ubbe) on sdk/service.py.

🔎 QA ✅ — Live testing passed. Frontend loads, signup works, copilot chat functional, library navigates correctly, no console errors. All 36 backend tests pass. ArrowsClockwise icon import verified. Tool category mapping context_compaction"compaction" correct. Cannot trigger actual compaction end-to-end (requires token overflow), but code paths well-tested. Screenshots captured.

QA Screenshots:


Should Fix (Follow-up OK)

  1. Stuck spinner on abnormal SDK stream termination — Sentry flagged: if SDK stream terminates after heartbeat triggers emit_start_if_ready() but before any message arrives, finally block doesn't close the compaction spinner. Edge case but worth a follow-up.
  2. Heartbeat margin_HEARTBEAT_INTERVAL at 10s with 12s frontend timeout leaves only 2s margin. Consider 7-8s for safety.
  3. Frontend tests for compaction category — No component tests exist for GenericTool categories (pre-existing gap).
  4. Coordinate with PR #12259 — Confirmed merge conflict overlap on sdk/service.py and frontend files.

Risk Assessment

Merge risk: LOW | Rollback: EASY

Feature is purely additive — extra SSE events the frontend already renders via established GenericTool patterns. No database changes, no new endpoints, no auth changes. Hex-suffixed markers are a security hardening improvement. 291+ lines of new tests provide solid coverage. All previous blockers from 7 iterations are resolved. CI fully green.

Iteration History

# Date HEAD Verdict
1–6 Mar 2–3 various APPROVE WITH CONDITIONS
7 Mar 3 c2fd871 ✅ APPROVE
8 Mar 3 089d087 ✅ APPROVE

Automated re-review #8 by PR Review Squad — 8/8 specialists reported

@ntindle All previous blockers resolved. Hex-suffixed markers improve security posture. CI green, QA passed. Zero human reviews after 8 iterations — please engage.

@github-actions github-actions Bot mentioned this pull request Mar 3, 2026
12 tasks
@github-project-automation github-project-automation Bot moved this from 🆕 Needs initial review to 👍🏼 Mergeable in AutoGPT development kanban Mar 3, 2026
@majdyz
majdyz added this pull request to the merge queue Mar 4, 2026
Merged via the queue into dev with commit a897f9e Mar 4, 2026
28 checks passed
@majdyz
majdyz deleted the otto/secrt-2053-copilot-compaction-ui-event branch March 4, 2026 05:50
@github-project-automation github-project-automation Bot moved this to Done in Frontend Mar 4, 2026
@github-project-automation github-project-automation Bot moved this from 👍🏼 Mergeable to ✅ Done in AutoGPT development kanban Mar 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform/backend AutoGPT Platform - Back end platform/frontend AutoGPT Platform - Front end size/l size/m size/xl

Projects

Status: ✅ Done
Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants