Skip to content

hotfix(backend/copilot): refactor transcript to SDK-based atomic full-context model - #12318

Merged
majdyz merged 8 commits into
masterfrom
hotfix/transcript-sdk-refactor
Mar 6, 2026
Merged

hotfix(backend/copilot): refactor transcript to SDK-based atomic full-context model#12318
majdyz merged 8 commits into
masterfrom
hotfix/transcript-sdk-refactor

Conversation

@majdyz

@majdyz majdyz commented Mar 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Major refactor to eliminate CLI transcript race conditions and simplify the codebase by building transcripts directly from SDK messages instead of reading CLI files.

Problem

The previous approach had race conditions:

  • SDK reads CLI transcript file during stop hook
  • CLI may not have finished writing → incomplete transcript
  • Complex merge logic to detect and fix incomplete writes
  • ~200 lines of synthetic entry detection and merge code

Solution

Atomic Full-Context Transcript Model:

  • Build transcript from SDK messages during streaming (TranscriptBuilder)
  • Each upload REPLACES the previous transcript entirely (atomic)
  • No CLI file reading → no race conditions
  • Eliminates all merge complexity

Key Changes

Core Refactor

  • NEW: transcript_builder.py - Build JSONL from SDK messages during streaming
  • SIMPLIFIED: transcript.py - Removed merge logic, simplified upload/download
  • SIMPLIFIED: service.py - Use TranscriptBuilder, removed stop hook callback
  • CLEANED: security_hooks.py - Removed on_stop parameter

Performance & Code Quality

  • orjson migration: Use backend.util.json (2-3x faster than stdlib)
  • Added fallback parameter to json.loads() for cleaner error handling
  • Moved SDK imports to top-level per code style guidelines

Bug Fixes

  • Fixed garbage collection bug in background task handling
  • Fixed double upload bug in timeout handling
  • Downgraded PII-risk logging from WARNING to DEBUG
  • Added 30s timeout to prevent session lock hang

Code Removed (~200 lines)

  • merge_with_previous_transcript() - No longer needed
  • read_transcript_file() - No longer needed
  • CapturedTranscript dataclass - No longer needed
  • _on_stop() callback - No longer needed
  • Synthetic entry detection logic - No longer needed
  • Manual append/merge logic in finally block - No longer needed

Testing

  • ✅ All transcript tests passing (24/24)
  • ✅ Verified with real session logs showing proper transcript growth
  • ✅ Verified with Langfuse traces showing proper turn tracking (1-8)

Transcript Growth Pattern

From session logs:

  • Turn 1: 2 entries (initial)
  • Turn 2: 5 entries (+3), 2257B uploaded
  • Turn N: ~2N entries (linear growth)

Each upload is the complete atomic state - always REPLACES, never incremental.

Files Changed

backend/copilot/sdk/transcript_builder.py (NEW)   | +140 lines
backend/copilot/sdk/transcript.py                  | -198, +125 lines  
backend/copilot/sdk/service.py                     | -214, +160 lines
backend/copilot/sdk/security_hooks.py              | -33, +10 lines
backend/copilot/sdk/transcript_test.py             | -85, +36 lines
backend/util/json.py                               | +45 lines

Net result: -200 lines, more reliable, faster JSON operations.

Migration Notes

This is a breaking change for any code that:

  • Directly calls merge_with_previous_transcript() or read_transcript_file()
  • Relies on incremental transcript uploads
  • Expects stop hook callbacks

All internal usage has been updated.


@ntindle - Tagging for autogpt-reviewer

…-context model

Major refactor to eliminate CLI transcript race conditions and simplify code:

**Core Changes:**
- Build transcript from SDK messages during streaming (TranscriptBuilder)
- Atomic full-context model: each upload REPLACES previous transcript entirely
- Eliminated CLI file reading race conditions and merge complexity
- Removed ~200 lines of transcript merge/synthetic detection code
- Use orjson via backend.util.json with fallback for 2-3x faster JSON ops

**Key Improvements:**
- No more race conditions between CLI writes and SDK reads
- Simpler, more reliable transcript handling
- Added fallback parameter to json.loads() for cleaner error handling
- Fixed garbage collection bug in background task handling
- Fixed double upload bug in timeout handling
- Moved SDK imports to top-level per code style
- Downgraded PII-risk logging from WARNING to DEBUG
- Added 30s timeout to prevent session lock hang

**Files Changed:**
- backend/copilot/sdk/transcript_builder.py (NEW): SDK message → JSONL builder
- backend/copilot/sdk/transcript.py: Simplified upload/download
- backend/copilot/sdk/service.py: Use TranscriptBuilder, remove stop hook
- backend/copilot/sdk/security_hooks.py: Remove on_stop parameter
- backend/util/json.py: Add fallback support for graceful error handling
- backend/copilot/sdk/transcript_test.py: Updated tests (24/24 passing)

**Testing:**
- All transcript tests passing (24/24)
- Verified with real session logs showing proper transcript growth
- Verified with Langfuse traces showing proper turn tracking (1-8)

Closes race condition issues and simplifies maintenance going forward.
@majdyz
majdyz requested a review from a team as a code owner March 6, 2026 12:45
@majdyz
majdyz requested review from Pwuts and Swiftyos and removed request for a team March 6, 2026 12:45
@github-project-automation github-project-automation Bot moved this to 🆕 Needs initial review in AutoGPT development kanban Mar 6, 2026
@github-actions github-actions Bot added platform/backend AutoGPT Platform - Back end size/xl labels Mar 6, 2026
@majdyz
majdyz requested a review from ntindle March 6, 2026 12:46
Comment thread autogpt_platform/backend/backend/util/json.py Fixed
Comment thread autogpt_platform/backend/backend/util/json.py Fixed
- Replace ellipsis defaults with None in overload signatures
- Replace ellipsis function bodies with pass statements
- Addresses CodeQL 'statement has no effect' warnings
- No runtime behavior change (overloads are type-hints only)

Fixes: #12318 (comment 2895610595, 2895610603)
@majdyz

majdyz commented Mar 6, 2026

Copy link
Copy Markdown
Contributor Author

Fixed both CodeQL warnings in b9f0cba:

✅ Replaced fallback: T | None = ... with fallback: T | None = None
✅ Replaced ... function bodies with pass in both overloads

All tests still passing (24/24). Thanks for catching these!

@coderabbitai

coderabbitai Bot commented Mar 6, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Removes the Stop hook and on_stop callback, adds a TranscriptBuilder to manage full JSONL transcripts, centralizes transcript finalization/upload in a finally path, tightens transcript I/O/validation and logging, and updates util.json to accept a fallback for loads.

Changes

Cohort / File(s) Summary
Security Hooks Simplification
autogpt_platform/backend/backend/copilot/sdk/security_hooks.py
Removed Stop hook and the on_stop parameter from create_security_hooks; updated signature and docstrings.
Transcript Builder
autogpt_platform/backend/backend/copilot/sdk/transcript_builder.py
Added TranscriptBuilder and TranscriptEntry to maintain/export a full JSONL transcript; APIs to load previous, append user/assistant entries, and serialize.
Service Integration & Streaming
autogpt_platform/backend/backend/copilot/sdk/service.py
Replaced stop-hook-based capture with TranscriptBuilder usage and _format_sdk_content_blocks; centralized transcript finalization/upload in finally block; removed CapturedTranscript and related helpers; added structured log_prefix and turn numbering.
Transcript I/O & Validation
autogpt_platform/backend/backend/copilot/sdk/transcript.py, autogpt_platform/backend/backend/util/json.py
Added log_prefix to upload/download functions; relaxed validation (assistant required), refined strip/reparent logic, removed read_transcript_file; added _NO_FALLBACK and fallback behavior to util.json.loads with overloads.
Tests
autogpt_platform/backend/backend/copilot/sdk/transcript_test.py
Removed tests for deleted APIs and expanded tests for new validation, reparenting/resume behavior, and backend.util.json fallback usage.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant SDK_Service as SDK_Service
  participant Builder as TranscriptBuilder
  participant Storage

  Client->>SDK_Service: start streaming session / send messages
  SDK_Service->>Builder: load_previous(downloaded_transcript)
  Client->>SDK_Service: user message
  SDK_Service->>Builder: add_user_message(user content)
  SDK_Service->>SDK_Service: process / stream assistant response
  SDK_Service->>Builder: add_assistant_message(formatted content blocks)
  SDK_Service->>SDK_Service: stream end or error (finally)
  SDK_Service->>Builder: to_jsonl()
  SDK_Service->>Storage: upload_transcript(content, message_count, log_prefix)
  Storage-->>SDK_Service: upload result
  SDK_Service-->>Client: return finalization/result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

Review effort 4/5

Suggested reviewers

  • ntindle
  • Swiftyos
  • Pwuts

Poem

🐰 I hopped through hooks and stitched each line,
Builder holds the threads of chat and time,
No Stop to catch — the final save sings,
Prefixes glow as the upload springs,
Hop, transcript done — carrots for all! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main objective: refactoring the transcript system to use an SDK-based atomic full-context model instead of CLI-based incremental uploads.
Description check ✅ Passed The description comprehensively explains the problem, solution, key changes, bug fixes, and testing verification, all directly related to the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 85.19% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hotfix/transcript-sdk-refactor

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.

majdyz added 2 commits March 6, 2026 19:52
- Remove extra user_id and only_if_empty parameters
- Function signature is update_session_title(session_id, title)
- Fixes pyright error: Expected 2 positional arguments
- Log warning when encountering unrecognized block types
- Helps detect new SDK versions with additional content blocks
- Keeps code simple - just warn and skip unknown blocks
@majdyz
majdyz enabled auto-merge (squash) March 6, 2026 13:05
Comment thread autogpt_platform/backend/backend/util/json.py
Pwuts
Pwuts previously approved these changes Mar 6, 2026
@github-project-automation github-project-automation Bot moved this from 🆕 Needs initial review to 👍🏼 Mergeable in AutoGPT development kanban Mar 6, 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.

Actionable comments posted: 5

🤖 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/sdk/service.py`:
- Around line 1212-1219: The transcript builder only records AssistantMessage
right now; extend the SDK message handling loop to also persist the
user/tool-result half of turns by detecting ToolResultBlock and user message
types, calling _format_sdk_content_blocks(sdk_msg.content) and then appending
the formatted blocks to the transcript_builder with the appropriate API (e.g.,
transcript_builder.add_user_message or
transcript_builder.add_tool_result_message—use the builder method that matches
the message type). Ensure the same serialization used for AssistantMessage (via
_format_sdk_content_blocks) is applied so session.messages entries added after
tool execution are also represented in the uploaded transcript.
- Around line 1567-1579: The background upload_task can outlive the session lock
and later overwrite a newer transcript; wrap or replace the scheduled
upload_task (the one added to _background_tasks on TimeoutError) so it never
writes directly to the deterministic session transcript path after being
backgrounded—either await it before releasing the session lock or change the
upload coroutine to write to a temporary/unique path and perform an atomic
rename only if the session id/turn still matches the current session state at
rename time; update the code handling upload_task, _background_tasks, and the
finally block that releases the session lock so the backgrounded task cannot
clobber newer transcripts.

In `@autogpt_platform/backend/backend/copilot/sdk/transcript_builder.py`:
- Around line 59-62: The warning in transcript_builder.py currently logs
transcript content via logger.warning("Failed to parse transcript line: %s",
line[:100]) which can leak user/assistant text; change the warning to a
content-free message like logger.warning("Failed to parse transcript line") and
move any non-sensitive preview to a DEBUG log (e.g., logger.debug) using a
hashed or truncated preview of the line (use a stable hash such as
hashlib.sha256(line.encode()).hexdigest() or a short safe preview) so parsing
failures are visible without exposing raw transcript content; update the code
around the json.loads(line, fallback=None) handling and replace the existing
warning call accordingly.
- Around line 64-67: load_previous() currently discards any entries whose
data["type"] is not "user" or "assistant", which drops non-strippable nodes
(e.g., "system") that strip_progress_entries() preserves and leads to dangling
parentUuid/resume problems; update load_previous() in transcript_builder.py to
reuse the same preservation logic as strip_progress_entries() (or call
strip_progress_entries() first) so that entries marked non-strippable (such as
"system" or other preserved types) are kept instead of filtered out, ensuring
parentUuid links remain valid when reconstructing transcripts.

In `@autogpt_platform/backend/backend/copilot/sdk/transcript.py`:
- Around line 223-230: The loop in transcript validation uses json.loads(...,
fallback=None) but doesn't verify the parsed value is a mapping before calling
entry.get(), causing AttributeError on JSON scalars; update the loop in the
function that contains the shown block to explicitly check that entry is an
instance of dict (or has mapping behavior) after json.loads and return False if
not, then only call entry.get("type") when that shape check passes; apply the
same shape guard in strip_progress_entries() before using .get() so non-object
JSON lines (e.g., strings, numbers, arrays) are treated as invalid and produce a
clean False/skip rather than raising.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bdf91d1f-e519-46d7-9afc-4f96f8f214bb

📥 Commits

Reviewing files that changed from the base of the PR and between 3e108a8 and 37325ac.

📒 Files selected for processing (6)
  • autogpt_platform/backend/backend/copilot/sdk/security_hooks.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_builder.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_test.py
  • autogpt_platform/backend/backend/util/json.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). (4)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.13)
  • 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/util/json.py
  • autogpt_platform/backend/backend/copilot/sdk/security_hooks.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_test.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_builder.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript.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/util/json.py
  • autogpt_platform/backend/backend/copilot/sdk/security_hooks.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_test.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_builder.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript.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/util/json.py
  • autogpt_platform/backend/backend/copilot/sdk/security_hooks.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_test.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_builder.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript.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/util/json.py
  • autogpt_platform/backend/backend/copilot/sdk/security_hooks.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_test.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_builder.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript.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/transcript_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/transcript_test.py
🧠 Learnings (4)
📓 Common learnings
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:11897-11900
Timestamp: 2026-03-04T23:58:09.319Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12284`
Backend/frontend OpenAPI codegen convention: In backend/api/features/store/model.py, the StoreSubmission and StoreSubmissionAdminView models define submitted_at: datetime | None, changes_summary: str | None, and instructions: str | None with no default. This is intentional to produce “required but nullable” fields in OpenAPI (properties appear in required[] and use anyOf [type, null]). This matches Prisma’s submittedAt DateTime? and changesSummary String?. Do not flag this as a required/nullable mismatch.
📚 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/util/json.py
  • autogpt_platform/backend/backend/copilot/sdk/security_hooks.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_test.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_builder.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
📚 Learning: 2026-03-05T15:42:08.207Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.

Applied to files:

  • autogpt_platform/backend/backend/util/json.py
  • autogpt_platform/backend/backend/copilot/sdk/security_hooks.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_test.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_builder.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
📚 Learning: 2026-03-04T08:04:35.881Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12273
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:216-220
Timestamp: 2026-03-04T08:04:35.881Z
Learning: In the AutoGPT Copilot backend, ensure that SVG images are not treated as vision image types by excluding 'image/svg+xml' from INLINEABLE_MIME_TYPES and MULTIMODAL_TYPES in tool_adapter.py; the Claude API supports PNG, JPEG, GIF, and WebP for vision. SVGs (XML text) should be handled via the text path instead, not the vision path.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/security_hooks.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_test.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_builder.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
🔇 Additional comments (2)
autogpt_platform/backend/backend/copilot/sdk/security_hooks.py (2)

125-147: LGTM! Clean removal of Stop hook support aligns with the SDK-based transcript refactor.

The function signature correctly removes the on_stop parameter, and the docstring updates reflect the new approach where transcript handling is managed atomically via the SDK rather than through stop-hook callbacks. Security hooks remain intact:

  • Task concurrency limiting with task_tool_use_ids
  • Workspace path validation
  • User isolation enforcement
  • Dangerous pattern blocking

The on_compact callback is appropriately retained for context compaction observability.


310-317: Hooks configuration correctly excludes Stop hook.

The hooks dictionary now contains only the four necessary event types (PreToolUse, PostToolUse, PostToolUseFailure, PreCompact), consistent with the removal of stop-hook based transcript capture in favor of the new TranscriptBuilder approach mentioned in the PR objectives.

Comment thread autogpt_platform/backend/backend/copilot/sdk/service.py Outdated
Comment thread autogpt_platform/backend/backend/copilot/sdk/service.py Outdated
Comment thread autogpt_platform/backend/backend/copilot/sdk/transcript_builder.py
Comment thread autogpt_platform/backend/backend/copilot/sdk/transcript_builder.py Outdated
Comment thread autogpt_platform/backend/backend/copilot/sdk/transcript.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 #12318 — hotfix(backend/copilot): refactor transcript to SDK-based atomic full-context model
Author: majdyz | Reviewer: automated-squad (8 specialists) | Files: 6 (+534/-382, net -200)

🎯 Verdict: APPROVE_WITH_CONDITIONS

What This PR Does

Eliminates CLI transcript race conditions by replacing the file-reading approach with an in-memory TranscriptBuilder that builds transcripts from SDK messages during streaming. Each upload atomically REPLACES the full previous transcript — no more merge logic, no more incomplete writes. Also migrates to orjson, fixes a GC bug in background tasks, fixes a double-upload bug, and adds a 30s upload timeout with background continuation.

Specialist Findings

🛡️ Security ✅ — No blocking issues. Path sanitization intact, auth scoping preserved. Removing read_transcript_file() and stop hook reduces attack surface. PII in DEBUG-level log (acceptable). orjson fallback sentinel pattern is correct.

🏗️ Architecture ⚠️ — Strong architectural improvement (eliminates CLI file dependency, ~200 lines removed). One concern: load_previous() drops summary entries produced by SDK compaction — could lose compacted context on resume. Also: _format_sdk_content_blocks belongs in transcript_builder.py not service.py.

Performance ⚠️ — No blockers. Atomic full-context model trades O(N) bandwidth per turn for correctness — acceptable for hotfix. Triple-parse overhead (load→serialize→strip) should be optimized in follow-up. orjson migration is a net positive. 30s timeout is appropriate.

🧪 Testing ⚠️TranscriptBuilder (140 new lines, core class) has zero unit tests. _format_sdk_content_blocks (46 new lines) has zero tests. json.loads fallback parameter (43 lines changed) has zero tests. Existing validate_transcript and strip_progress_entries tests are well-covered. Author reports 24/24 tests passing but coverage gaps are significant for new core code.

📖 Quality ✅ — Clean, well-structured. Excellent structured logging ([SDK][session][Turn]). Good dead code removal. Minor: stale module docstring in transcript.py, _format_sdk_content_blocks placement.

📦 Product ✅ — High positive user impact. Fixes real race conditions causing incomplete transcripts. Breaking changes are internal-only (all callers updated). PII logging downgrade (WARNING→DEBUG) is appropriate.

📬 Discussion ⚠️ — Pwuts APPROVED (scoped to json.py only). CodeRabbit posted 5 new findings at 13:10 UTC (unaddressed): (1) tool-result messages not captured in builder, (2) timeout upload race could re-introduce overwrite, (3) PII in WARNING log, (4) load_previous drops non-user/assistant entries, (5) non-object JSON lines cause AttributeError. CI tests (3.11/3.12/3.13) were still pending at review time.

🔎 QA ✅ — Full end-to-end pass. Multi-turn copilot with tool use works correctly. Atomic transcript model confirmed in backend logs: Turn 1 uploaded 5 entries (4264B), Turn 2 downloaded 5, appended, uploaded 18 entries (10498B). Chat history persists across page navigation. No console errors. 7 screenshots captured.

Conditions (Must Address Before/Shortly After Merge)

  1. Add unit tests for TranscriptBuilder — 140 lines of new core code with zero tests. At minimum: load_previous round-trip, add_user_message/add_assistant_message chaining, to_jsonl output format, edge cases (empty content, malformed JSON, missing uuid).

  2. Verify summary entry handling in load_previous() — Currently filters to only user/assistant types (transcript_builder.py:67). If SDK compaction produces summary entries, they'll be silently dropped on resume, potentially losing compacted context. Either add "summary" to accepted types or document why it's safe to drop.

  3. Address CodeRabbit finding: non-object JSON linesjson.loads with fallback=None succeeds for JSON arrays/scalars, then .get() raises AttributeError. Add isinstance(data, dict) guard in both load_previous() and strip_progress_entries().

Should Fix (Follow-up OK)

  1. _format_sdk_content_blocks placement — Move from service.py to transcript_builder.py where it belongs (transcript concern, not service concern)
  2. Add unit tests for _format_sdk_content_blocks — 46-line function handling 4 SDK block types + unknown, zero tests
  3. Add tests for json.loads fallback parameter — New feature used throughout transcript code, zero tests
  4. Update stale docstring in transcript.py — Still references "when the SDK's Stop hook fires we read this file"
  5. Add error-logging callback for background upload tasks — Currently failures are silently swallowed after timeout
  6. Investigate CodeRabbit timeout-race finding — After 30s timeout, background upload continues while session lock is released. Verify whether a new turn could start and produce a newer upload that gets overwritten by the stale background task
  7. Triple-parse optimization — Previous entries are parsed 3× (load→Pydantic serialize→strip). Store raw JSONL lines for unchanged entries
  8. Skip strip_progress_entries for builder-generated transcripts — SDK-built transcripts never contain progress entries

QA Evidence

Step Screenshot
Landing page landing
Copilot home copilot-home
Turn 1 response response
Turn 2 tool calls followup
Turn 2 complete done
Chat history reload history

Risk Assessment

Merge risk: LOW-MEDIUM | Rollback: EASY (revert one commit, old transcript code was self-contained)

The atomic model is architecturally correct and the hotfix solves a real, active bug (incomplete transcripts from race conditions). QA confirms end-to-end functionality works. The conditions above are important but none block the immediate hotfix value — the current race condition in production is worse than any of these edge cases.

@ntindle @Swiftyos @Pwuts — Hotfix looks good overall. Core transcript race condition is fixed, QA confirms multi-turn copilot works end-to-end with atomic uploads. Main gap is test coverage for the new TranscriptBuilder class (140 lines, 0 tests) — recommend adding tests before or immediately after merge. Also verify the summary entry handling if SDK compaction is in use.

@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

♻️ Duplicate comments (2)
autogpt_platform/backend/backend/copilot/sdk/service.py (2)

1356-1368: ⚠️ Potential issue | 🔴 Critical

Tool results still never reach the transcript builder.

This path appends tool outputs to session.messages, but nothing equivalent is written to transcript_builder, so the uploaded JSONL can still contain tool_use blocks without the matching tool_result half of the turn. That breaks the “full-context replacement” model on the next resume.

🤖 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 1356 -
1368, The StreamToolOutputAvailable branch appends tool output to
session.messages but never records a matching tool_result in transcript_builder,
causing mismatched tool_use/tool_result pairs; update the branch handling
StreamToolOutputAvailable to also call the same transcript_builder method used
in other tool-output branches (e.g., transcript_builder.add_tool_result or
whatever function records tool results) with the response.toolCallId and
stringified response.output so the uploaded JSONL contains the corresponding
tool_result entry alongside the session.messages append.

1573-1585: ⚠️ Potential issue | 🔴 Critical

A timed-out upload can still overwrite a newer transcript.

After the timeout, the same upload_task keeps writing in the background, but the stream lock is still released later in this finally. If the next turn starts before that task finishes, the older upload can complete after newer state exists and clobber it. The backgrounded path needs a freshness guard or it needs to finish before the lock is released.

🤖 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 1573 -
1585, The backgrounded upload_task can finish after a newer transcript is
written and clobber state; modify the background path so it cannot overwrite
fresher data: when catching TimeoutError in the block around upload_task, either
(A) ensure the lock held for the stream is not released until the upload_task
completes (await upload_task or await its completion before releasing the stream
lock), or (B) attach a freshness guard to the upload routine that checks a
transcript version/timestamp before performing the write (e.g., pass the
expected_version/timestamp into the upload function and have upload_task verify
current_version matches before writing), and keep the existing
_background_tasks.add(upload_task) but ensure upload_task's done callback
enforces discarding stale writes; reference upload_task, _background_tasks, and
the finally that releases the stream lock when applying the change.
🤖 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/sdk/service.py`:
- Around line 1135-1140: The transcript is storing the prompt-engineered string
`query_message` instead of the actual user turn; change the code so
`client.query(query_message, session_id=session_id)` still sends the engineered
query but `transcript_builder.add_user_message(...)` uses the real user content
(e.g., `content_blocks` or the original user input variable) rather than
`query_message`, and if prior context is missing, fetch/backfill the session's
stored conversation history into `transcript_builder` before adding the user
message; update the logic around `client.query`,
`transcript_builder.add_user_message`, `query_message`, `content_blocks`, and
`session_id` to reflect this separation.

---

Duplicate comments:
In `@autogpt_platform/backend/backend/copilot/sdk/service.py`:
- Around line 1356-1368: The StreamToolOutputAvailable branch appends tool
output to session.messages but never records a matching tool_result in
transcript_builder, causing mismatched tool_use/tool_result pairs; update the
branch handling StreamToolOutputAvailable to also call the same
transcript_builder method used in other tool-output branches (e.g.,
transcript_builder.add_tool_result or whatever function records tool results)
with the response.toolCallId and stringified response.output so the uploaded
JSONL contains the corresponding tool_result entry alongside the
session.messages append.
- Around line 1573-1585: The backgrounded upload_task can finish after a newer
transcript is written and clobber state; modify the background path so it cannot
overwrite fresher data: when catching TimeoutError in the block around
upload_task, either (A) ensure the lock held for the stream is not released
until the upload_task completes (await upload_task or await its completion
before releasing the stream lock), or (B) attach a freshness guard to the upload
routine that checks a transcript version/timestamp before performing the write
(e.g., pass the expected_version/timestamp into the upload function and have
upload_task verify current_version matches before writing), and keep the
existing _background_tasks.add(upload_task) but ensure upload_task's done
callback enforces discarding stale writes; reference upload_task,
_background_tasks, and the finally that releases the stream lock when applying
the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 14821ecf-9aec-4fb5-baca-8f492821dbca

📥 Commits

Reviewing files that changed from the base of the PR and between 37325ac and 9b78d2c.

📒 Files selected for processing (1)
  • autogpt_platform/backend/backend/copilot/sdk/service.py
📜 Review details
🧰 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/**/*.{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/**/*.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/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/copilot/sdk/service.py
🧠 Learnings (4)
📓 Common learnings
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:11897-11900
Timestamp: 2026-03-04T23:58:09.319Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12284`
Backend/frontend OpenAPI codegen convention: In backend/api/features/store/model.py, the StoreSubmission and StoreSubmissionAdminView models define submitted_at: datetime | None, changes_summary: str | None, and instructions: str | None with no default. This is intentional to produce “required but nullable” fields in OpenAPI (properties appear in required[] and use anyOf [type, null]). This matches Prisma’s submittedAt DateTime? and changesSummary String?. Do not flag this as a required/nullable mismatch.
📚 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
📚 Learning: 2026-03-04T08:04:35.881Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12273
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:216-220
Timestamp: 2026-03-04T08:04:35.881Z
Learning: In the AutoGPT Copilot backend, ensure that SVG images are not treated as vision image types by excluding 'image/svg+xml' from INLINEABLE_MIME_TYPES and MULTIMODAL_TYPES in tool_adapter.py; the Claude API supports PNG, JPEG, GIF, and WebP for vision. SVGs (XML text) should be handled via the text path instead, not the vision path.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service.py
📚 Learning: 2026-03-05T15:42:08.207Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.

Applied to files:

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

Comment thread autogpt_platform/backend/backend/copilot/sdk/service.py Outdated
Fixes 5 issues flagged in PR review:

1. **Critical: Capture ResultMessage in transcript** (2895700075)
   - Added ResultMessage capture as user messages in transcript_builder
   - Ensures tool results are preserved in uploaded transcripts

2. **Critical: Remove upload timeout to prevent race conditions** (2895700079)
   - Removed 30s timeout on transcript upload
   - Use shield without timeout to prevent old uploads overwriting new
   - Simpler approach: let uploads complete, avoid backgrounding

3. **Major: Remove PII from parse failure warnings** (2895700083)
   - Changed to log SHA256 hash instead of content preview at WARNING
   - Content preview moved to DEBUG level only
   - Prevents user conversation leaking into production logs

4. **Major: Preserve system entries when loading transcript** (2895700084)
   - load_previous() now preserves all non-strippable types (system, etc.)
   - Matches strip_progress_entries() behavior for consistency
   - Prevents dangling parentUuid refs and broken --resume chains

5. **Major: Guard against non-dict JSON in validation** (2895700085)
   - Added isinstance(entry, dict) checks before .get() calls
   - Applied to validate_transcript() and strip_progress_entries()
   - Prevents AttributeError on malformed transcript lines

All tests passing (24/24)
@majdyz

majdyz commented Mar 6, 2026

Copy link
Copy Markdown
Contributor Author

All CodeRabbit issues fixed ✅

Fixed all 5 critical/major issues in commit 6e1d00f:

🔴 Critical Issues

  1. Tool results missing (2895700075)

    • ✅ Added ResultMessage capture to transcript_builder
    • Tool results now preserved in uploaded transcripts
  2. Upload race condition (2895700079)

    • ✅ Removed 30s timeout completely
    • Now just await asyncio.shield(upload_transcript(...))
    • Simpler solution: no backgrounding = no race condition

🟠 Major Issues

  1. PII in warning logs (2895700083)

    • ✅ Changed to SHA256 hash at WARNING level
    • Content preview only at DEBUG level
  2. System entries dropped (2895700084)

    • load_previous() now preserves all non-strippable types
    • Matches strip_progress_entries() behavior
  3. Non-dict JSON crash (2895700085)

    • ✅ Added isinstance(entry, dict) checks
    • Applied to both validate_transcript() and strip_progress_entries()

All tests passing (24/24) ✅

majdyz added 2 commits March 6, 2026 20:25
…message

- Move hashlib to top-level import
- Simplify parse failure logging: show line number, not content
- Fix transcript storing engineered query instead of actual user message
  - Now stores current_message (actual user input)
  - Not query_message (engineered with context wrappers)
- Addresses comment 2895733844
- Pass log_prefix through to load_previous() method
- Makes logs more traceable with session context
- Consistent with other transcript logging
@majdyz
majdyz requested a review from Pwuts March 6, 2026 13:28
Comment thread autogpt_platform/backend/backend/copilot/sdk/service.py Outdated
ResultMessage represents session completion status (success/error), not tool
execution results. Adding it to the transcript corrupts conversation history
during --resume by injecting error messages as user messages.

Fixes CodeRabbit discussion r2895784911

@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.

♻️ Duplicate comments (3)
autogpt_platform/backend/backend/copilot/sdk/service.py (2)

1088-1094: ⚠️ Potential issue | 🔴 Critical

Backfill stale transcript gaps before writing the new watermark.

When _build_query_message() compresses session.messages[transcript_msg_count:-1] into query_message, the builder still records only current_message here. Line 1572 then stores message_count=len(session.messages) anyway, so a successful replace can permanently drop the gap while marking the transcript as fully current. Rehydrate the missing turns into transcript_builder first, or keep the stored watermark at the last transcript-covered message.

Also applies to: 1138-1141, 1544-1574


1119-1136: ⚠️ Potential issue | 🟠 Major

Use the raw user text in the multimodal transcript branch.

The text block at Line 1123 is still built from query_message, so any <conversation_history> wrapper or attachment hint gets persisted as if the user authored it. The plain-text path already avoids this at Lines 1138-1141; the image path should build transcript blocks from current_message plus the actual image blocks instead.

🤖 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 1119 -
1136, The multimodal branch currently injects query_message into the text block,
which persists wrapper/attachment hints; change it to use the raw user text from
current_message (or current_message.text) when building content_blocks and when
calling transcript_builder.add_user_message so the text block reflects the
user's original input (keep attachments.image_blocks merged with the single text
block, and leave the rest of the user_msg structure and transport write
unchanged).
autogpt_platform/backend/backend/copilot/sdk/transcript.py (1)

307-311: ⚠️ Potential issue | 🟡 Minor

Guard the invalid-transcript logging path against JSON scalars.

fallback={"type": "INVALID_JSON"} only covers decode failures. A valid JSON line like [] or "x" still comes back as a non-dict, so entry.get(...) raises here and turns the intended "skip upload" branch into an exception from upload_transcript().

🛠️ Suggested guard
         for line in stripped.strip().split("\n"):
             entry = json.loads(line, fallback={"type": "INVALID_JSON"})
-            entry_types.append(entry.get("type", "?"))
+            if isinstance(entry, dict):
+                entry_types.append(entry.get("type", "?"))
+            else:
+                entry_types.append(type(entry).__name__)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/copilot/sdk/transcript.py` around lines 307
- 311, The loop that builds entry_types (variables entry_types and entry)
assumes json.loads(line, fallback=...) returns a dict and calls entry.get(...),
but valid JSON scalars (e.g., [] or "x") produce non-dict objects and cause .get
to raise; update the loop in upload_transcript (or the function containing
entry_types) to guard the result of json.loads: after parsing (json.loads(...)),
check if isinstance(entry, dict) and if so append entry.get("type", "?"),
otherwise append a fallback like "INVALID_JSON" or "?" so non-dict JSON values
do not raise.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@autogpt_platform/backend/backend/copilot/sdk/service.py`:
- Around line 1119-1136: The multimodal branch currently injects query_message
into the text block, which persists wrapper/attachment hints; change it to use
the raw user text from current_message (or current_message.text) when building
content_blocks and when calling transcript_builder.add_user_message so the text
block reflects the user's original input (keep attachments.image_blocks merged
with the single text block, and leave the rest of the user_msg structure and
transport write unchanged).

In `@autogpt_platform/backend/backend/copilot/sdk/transcript.py`:
- Around line 307-311: The loop that builds entry_types (variables entry_types
and entry) assumes json.loads(line, fallback=...) returns a dict and calls
entry.get(...), but valid JSON scalars (e.g., [] or "x") produce non-dict
objects and cause .get to raise; update the loop in upload_transcript (or the
function containing entry_types) to guard the result of json.loads: after
parsing (json.loads(...)), check if isinstance(entry, dict) and if so append
entry.get("type", "?"), otherwise append a fallback like "INVALID_JSON" or "?"
so non-dict JSON values do not raise.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 85ca6618-8943-4f5b-9246-157f95601ee8

📥 Commits

Reviewing files that changed from the base of the PR and between 9b78d2c and f5b79ec.

📒 Files selected for processing (3)
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_builder.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). (9)
  • 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: Analyze (python)
  • GitHub Check: Check PR Status
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (python)
🧰 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/sdk/transcript_builder.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript.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/sdk/transcript_builder.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript.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/sdk/transcript_builder.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript.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/sdk/transcript_builder.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript.py
🧠 Learnings (5)
📓 Common learnings
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:11897-11900
Timestamp: 2026-03-04T23:58:09.319Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12284`
Backend/frontend OpenAPI codegen convention: In backend/api/features/store/model.py, the StoreSubmission and StoreSubmissionAdminView models define submitted_at: datetime | None, changes_summary: str | None, and instructions: str | None with no default. This is intentional to produce “required but nullable” fields in OpenAPI (properties appear in required[] and use anyOf [type, null]). This matches Prisma’s submittedAt DateTime? and changesSummary String?. Do not flag this as a required/nullable mismatch.
📚 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/sdk/transcript_builder.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript.py
📚 Learning: 2026-03-04T08:04:35.881Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12273
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:216-220
Timestamp: 2026-03-04T08:04:35.881Z
Learning: In the AutoGPT Copilot backend, ensure that SVG images are not treated as vision image types by excluding 'image/svg+xml' from INLINEABLE_MIME_TYPES and MULTIMODAL_TYPES in tool_adapter.py; the Claude API supports PNG, JPEG, GIF, and WebP for vision. SVGs (XML text) should be handled via the text path instead, not the vision path.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_builder.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript.py
📚 Learning: 2026-03-05T15:42:08.207Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript_builder.py
  • autogpt_platform/backend/backend/copilot/sdk/transcript.py
📚 Learning: 2026-03-04T23:58:09.319Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:11897-11900
Timestamp: 2026-03-04T23:58:09.319Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12284`
Backend/frontend OpenAPI codegen convention: In backend/api/features/store/model.py, the StoreSubmission and StoreSubmissionAdminView models define submitted_at: datetime | None, changes_summary: str | None, and instructions: str | None with no default. This is intentional to produce “required but nullable” fields in OpenAPI (properties appear in required[] and use anyOf [type, null]). This matches Prisma’s submittedAt DateTime? and changesSummary String?. Do not flag this as a required/nullable mismatch.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/transcript.py

@majdyz
majdyz merged commit d564528 into master Mar 6, 2026
25 checks passed
@majdyz
majdyz deleted the hotfix/transcript-sdk-refactor branch March 6, 2026 14:03
@github-project-automation github-project-automation Bot moved this from 👍🏼 Mergeable to ✅ Done in AutoGPT development kanban Mar 6, 2026
majdyz added a commit that referenced this pull request Mar 6, 2026
## Summary
- Fixes tool results not being captured in the CoPilot transcript during
SDK-based streaming
- Adds `transcript_builder.add_user_message()` call with `tool_result`
content block when a `StreamToolOutputAvailable` event is received
- Ensures transcript accurately reflects the full conversation including
tool outputs, which is critical for Langfuse tracing and debugging

## Context
After the transcript refactor in #12318, tool call results from the SDK
streaming loop were not being recorded in the transcript. This meant
Langfuse traces were missing tool outputs, making it hard to debug agent
behavior.

## Test plan
- [ ] Verify CoPilot conversation with tool calls captures tool results
in Langfuse traces
- [ ] Verify transcript includes tool_result content blocks after tool
execution
majdyz added a commit that referenced this pull request Mar 8, 2026
…-context model (#12318)

Major refactor to eliminate CLI transcript race conditions and simplify
the codebase by building transcripts directly from SDK messages instead
of reading CLI files.

The previous approach had race conditions:
- SDK reads CLI transcript file during stop hook
- CLI may not have finished writing → incomplete transcript
- Complex merge logic to detect and fix incomplete writes
- ~200 lines of synthetic entry detection and merge code

**Atomic Full-Context Transcript Model:**
- Build transcript from SDK messages during streaming
(`TranscriptBuilder`)
- Each upload REPLACES the previous transcript entirely (atomic)
- No CLI file reading → no race conditions
- Eliminates all merge complexity

- **NEW**: `transcript_builder.py` - Build JSONL from SDK messages
during streaming
- **SIMPLIFIED**: `transcript.py` - Removed merge logic, simplified
upload/download
- **SIMPLIFIED**: `service.py` - Use TranscriptBuilder, removed stop
hook callback
- **CLEANED**: `security_hooks.py` - Removed `on_stop` parameter

- **orjson migration**: Use `backend.util.json` (2-3x faster than
stdlib)
- Added `fallback` parameter to `json.loads()` for cleaner error
handling
- Moved SDK imports to top-level per code style guidelines

- Fixed garbage collection bug in background task handling
- Fixed double upload bug in timeout handling
- Downgraded PII-risk logging from WARNING to DEBUG
- Added 30s timeout to prevent session lock hang

- `merge_with_previous_transcript()` - No longer needed
- `read_transcript_file()` - No longer needed
- `CapturedTranscript` dataclass - No longer needed
- `_on_stop()` callback - No longer needed
- Synthetic entry detection logic - No longer needed
- Manual append/merge logic in finally block - No longer needed

- ✅ All transcript tests passing (24/24)
- ✅ Verified with real session logs showing proper transcript growth
- ✅ Verified with Langfuse traces showing proper turn tracking (1-8)

From session logs:
- **Turn 1**: 2 entries (initial)
- **Turn 2**: 5 entries (+3), 2257B uploaded
- **Turn N**: ~2N entries (linear growth)

Each upload is the **complete atomic state** - always REPLACES, never
incremental.

```
backend/copilot/sdk/transcript_builder.py (NEW)   | +140 lines
backend/copilot/sdk/transcript.py                  | -198, +125 lines
backend/copilot/sdk/service.py                     | -214, +160 lines
backend/copilot/sdk/security_hooks.py              | -33, +10 lines
backend/copilot/sdk/transcript_test.py             | -85, +36 lines
backend/util/json.py                               | +45 lines
```

**Net result**: -200 lines, more reliable, faster JSON operations.

This is a **breaking change** for any code that:
- Directly calls `merge_with_previous_transcript()` or
`read_transcript_file()`
- Relies on incremental transcript uploads
- Expects stop hook callbacks

All internal usage has been updated.

---

@ntindle - Tagging for autogpt-reviewer
majdyz added a commit that referenced this pull request Mar 8, 2026
## Summary
- Fixes tool results not being captured in the CoPilot transcript during
SDK-based streaming
- Adds `transcript_builder.add_user_message()` call with `tool_result`
content block when a `StreamToolOutputAvailable` event is received
- Ensures transcript accurately reflects the full conversation including
tool outputs, which is critical for Langfuse tracing and debugging

## Context
After the transcript refactor in #12318, tool call results from the SDK
streaming loop were not being recorded in the transcript. This meant
Langfuse traces were missing tool outputs, making it hard to debug agent
behavior.

## Test plan
- [ ] Verify CoPilot conversation with tool calls captures tool results
in Langfuse traces
- [ ] Verify transcript includes tool_result content blocks after tool
execution
@sentry

sentry Bot commented Mar 13, 2026

Copy link
Copy Markdown

Issues attributed to commits in this pull request

This pull request was merged and Sentry observed the following issues:

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 size/xl

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

4 participants