Skip to content

Features/add auto compression#1376

Draft
iceljc wants to merge 3 commits into
SciSharp:masterfrom
iceljc:features/add-auto-compression
Draft

Features/add auto compression#1376
iceljc wants to merge 3 commits into
SciSharp:masterfrom
iceljc:features/add-auto-compression

Conversation

@iceljc

@iceljc iceljc commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

@iceljc iceljc marked this pull request as draft July 8, 2026 22:29
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Add automatic conversation compression with summarization and archival

✨ Enhancement 🕐 40+ Minutes

Grey Divider

AI Description

• Add auto-compression to summarize older turns and set a conversation breakpoint.
• Optionally compact storage by archiving old dialogs/state history and keeping a summary marker.
• Add cross-instance compression locking to prevent concurrent compactions and handle stale locks.
Diagram

graph TD
  A["SendMessage"] --> B["TriggerAutoCompression"] --> C["AutoCompressIfNeeded"]
  C --> D[("Conversation repo") ] --> E[("Dialogs + states") ]
  C --> F{{"LLM summary"}}
  C --> G["HookEmitter"]

  subgraph Legend
    direction LR
    _svc(["Service"]) ~~~ _db[("Storage")] ~~~ _ext{{"External"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Hosted background queue (IHostedService) instead of Task.Run
  • ➕ Bounded concurrency and backpressure under load
  • ➕ Better observability/retries (structured job records, metrics)
  • ➕ Cleaner shutdown semantics (drain/stop)
  • ➖ More infrastructure code (queue + worker)
  • ➖ Harder to keep "best effort" behavior without delaying turns
2. Scheduled compaction (cron/idle conversation sweep)
  • ➕ Moves LLM summarization cost off the critical interaction path entirely
  • ➕ Can batch work and prioritize oldest/largest conversations
  • ➖ Summaries may lag behind the latest context needs
  • ➖ More complexity in selecting candidates and avoiding races with active turns
3. Context-only compression (no storage compaction)
  • ➕ Lower data-integrity risk (no rewriting dialog/state records)
  • ➕ Easier rollback and fewer storage backend changes
  • ➖ Does not reduce hot storage size or state-history bloat
  • ➖ Still keeps large persisted dialog/state records over time

Recommendation: The PR’s approach (background fire-and-forget with local + persisted locking, plus optional storage compaction) is a pragmatic default and keeps user-facing latency low. If deployments commonly experience high throughput or need stronger operational guarantees, consider evolving TriggerAutoCompression to enqueue work to a hosted background worker for bounded concurrency and improved observability; the current repository-level atomic lock API is already a good foundation for that migration.

Files changed (16) +878 / -16

Enhancement (12) +782 / -16
ConversationHookBase.csAdd no-op hook for conversation compression events +3/-0

Add no-op hook for conversation compression events

• Introduces OnConversationCompressed as an overridable hook point. Enables downstream implementations to react after auto-compression runs.

src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs

MessageTypeName.csAdd Summary message type constant +1/-0

Add Summary message type constant

• Adds MessageTypeName.Summary to label persisted summary marker dialogs used during compaction.

src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/MessageTypeName.cs

IConversationHook.csAdd OnConversationCompressed hook to interface (with default impl) +10/-0

Add OnConversationCompressed hook to interface (with default impl)

• Extends IConversationHook with an OnConversationCompressed callback including breakpoint and archived count. Uses a default interface implementation to keep existing implementers compatible.

src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs

IConversationService.csAdd AutoCompressIfNeeded to conversation service contract +9/-0

Add AutoCompressIfNeeded to conversation service contract

• Introduces an optional AutoCompressIfNeeded API (default no-op) to trigger summarization/compaction when thresholds are reached.

src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationService.cs

Conversation.csAdd compression bookkeeping fields to Conversation model +23/-0

Add compression bookkeeping fields to Conversation model

• Adds LastCompactedMessageId and CompactedDialogCount for progress tracking, plus IsCompressing and CompressingStartedTime for persisted locking and stale-flag takeover.

src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs

IBotSharpRepository.csAdd repository APIs for compaction and compression locking +21/-0

Add repository APIs for compaction and compression locking

• Adds CompactConversationDialogs for archiving/replacing old dialogs (and trimming state history) and UpdateConversationCompressing for atomic acquire/release of a cross-instance compression lock.

src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs

ConversationService.Compression.csImplement auto-compression pipeline (summary + breakpoint + optional compaction) +213/-0

Implement auto-compression pipeline (summary + breakpoint + optional compaction)

• Adds AutoCompressIfNeeded with local and persisted guards, cut-point computation that avoids splitting user turns/tool-call pairs, summary generation via an agent-owned template, breakpoint updates, optional storage compaction, and hook emission.

src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Compression.cs

ConversationService.SendMessage.csTrigger background auto-compression after successful message send +34/-0

Trigger background auto-compression after successful message send

• Starts auto-compression asynchronously in a new DI scope to avoid request-scope disposal and user-facing latency. Errors are logged and do not affect the turn outcome.

src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs

FileRepository.Conversation.csAdd file-backed compaction, archiving, and stale-lock guard +289/-16

Add file-backed compaction, archiving, and stale-lock guard

• Hardens GetConversation for missing/corrupt files, adds UpdateConversationCompressing with stale takeover logic under the dialog lock, implements CompactConversationDialogs writing archived dialogs/states to a timestamped archive folder, and trims versioned state history while preserving latest values.

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs

ConversationArchiveDocument.csAdd Mongo archive document for compaction runs +15/-0

Add Mongo archive document for compaction runs

• Defines ConversationArchiveDocument to store archived dialogs and trimmed state versions per compaction run, paralleling the file-based archive folder approach.

src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationArchiveDocument.cs

ConversationDocument.csPersist compression fields in Mongo conversation document +4/-0

Persist compression fields in Mongo conversation document

• Adds compaction bookkeeping and compression lock fields to ConversationDocument so Mongo can participate in cross-instance guarding and progress tracking.

src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationDocument.cs

MongoRepository.Conversation.csImplement Mongo compression lock + dialog/state compaction with archival +160/-0

Implement Mongo compression lock + dialog/state compaction with archival

• Maps new compression fields into domain Conversation, implements atomic lock acquisition via UpdateConversationCompressing, compacts dialogs by replacing archived prefix with a summary marker, trims versioned states, and optionally writes archived data to ConversationArchives.

src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs

Documentation (1) +24 / -0
conversation.compression.liquidAdd LLM template for cumulative conversation compression +24/-0

Add LLM template for cumulative conversation compression

• Adds a prompt template instructing the model to produce a faithful, cumulative, structured summary suitable for context reconstruction.

src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/conversation.compression.liquid

Other (3) +72 / -0
ConversationSetting.csIntroduce AutoCompression settings block +48/-0

Introduce AutoCompression settings block

• Adds AutoCompressionSetting with enable/threshold/keep-recent knobs, summary agent/template selection, storage compaction toggle, and stale lock timeout.

src/Infrastructure/BotSharp.Abstraction/Conversations/Settings/ConversationSetting.cs

FileRepository.csAdd archive folder constant and EnsureDirectory helper +9/-0

Add archive folder constant and EnsureDirectory helper

• Introduces the archive folder name for compaction runs and a helper to create directories safely.

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs

MongoDbContext.csAdd ConversationArchives collection and index +15/-0

Add ConversationArchives collection and index

• Creates the ConversationArchives collection, adds an index on ConversationId, and exposes the collection via the context.

src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (2) 📜 Skill insights (0)

Grey Divider


Action required

1. Case-sensitive MessageId match 📘 Rule violation ≡ Correctness
Description
The new compaction code compares identifier-like MessageId strings using case-sensitive ==,
which can fail to locate the cut point when casing differs. This violates the required ordinal
case-insensitive semantics for identifiers and can cause compaction to no-op unexpectedly.
Code

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs[R901-905]

+            var foundIdx = dialogs.FindIndex(x => x.MetaData?.MessageId == cutMessageId);
+            if (foundIdx <= 0)
+            {
+                return 0;
+            }
Evidence
PR Compliance ID 3 requires ordinal case-insensitive semantics for identifier-like string
comparisons. The PR adds == comparisons for MessageId in both file and Mongo compaction paths,
which are case-sensitive by default.

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs[901-905]
src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs[770-775]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Identifier-like string comparisons for `MessageId` are performed with case-sensitive equality, which can break lookups when casing differs.

## Issue Context
Compaction depends on reliably finding the cut dialog by `cutMessageId`. Per compliance, identifier comparisons must use ordinal case-insensitive semantics.

## Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs[901-905]
- src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs[770-775]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. StartsWith lacks StringComparison 📘 Rule violation ⚙ Maintainability
Description
The new Mongo index-existence check uses StartsWith without specifying StringComparison, relying
on default comparison behavior. This violates the requirement to use ordinal case-insensitive
semantics for identifier-like comparisons and can behave inconsistently across cultures/casing.
Code

src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs[R150-152]

+        var curIndexes = collection.Indexes.List().ToList().Where(x => x.Contains("name")).Select(x => x["name"].AsString);
+        if (!curIndexes.Any(x => x.StartsWith("ConversationId")))
+        {
Evidence
PR Compliance ID 3 requires ordinal case-insensitive semantics for identifier-like comparisons. The
PR adds a StartsWith("ConversationId") check without a StringComparison, which defaults to
culture-sensitive behavior.

src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs[150-152]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Identifier-like string prefix checks are performed without an explicit `StringComparison`, which can lead to inconsistent behavior.

## Issue Context
Compliance requires ordinal case-insensitive semantics for identifier-like comparisons.

## Fix Focus Areas
- src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs[150-152]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Compression cut index crash 🐞 Bug ≡ Correctness
Description
ComputeCompressionCut can produce cutIndex == dialogs.Count when KeepRecentCount <= 0, causing an
IndexOutOfRangeException when accessing dialogs[cutIndex] and making auto-compression fail
repeatedly under misconfiguration.
Code

src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Compression.cs[R161-165]

+        var cutIndex = dialogs.Count - Math.Max(0, keepRecentCount);
+        while (cutIndex > 0 && dialogs[cutIndex].Role != AgentRole.User)
+        {
+            cutIndex--;
+        }
Evidence
ComputeCompressionCut indexes dialogs[cutIndex] after calculating cutIndex in a way that
yields dialogs.Count for keepRecentCount <= 0. KeepRecentCount is a configurable integer with
no runtime validation, so this edge case is reachable via configuration.

src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Compression.cs[148-168]
src/Infrastructure/BotSharp.Abstraction/Conversations/Settings/ConversationSetting.cs[44-48]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`ComputeCompressionCut` can compute an out-of-range index when `keepRecentCount` is `0` or negative, because it sets `cutIndex = dialogs.Count - Math.Max(0, keepRecentCount)` and then indexes `dialogs[cutIndex]`.

### Issue Context
`KeepRecentCount` is configuration-driven and currently has no validation, so `0`/negative values can enter at runtime.

### Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Compression.cs[154-168]

### Suggested fix
- Treat `keepRecentCount <= 0` as invalid and return `-1` (no compression), **or** clamp to a minimum of `1`.
- Ensure `cutIndex` is always within `[0, dialogs.Count - 1]` before indexing (e.g., early-return when `cutIndex >= dialogs.Count`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (2)
4. Compression guard leak 🐞 Bug ☼ Reliability
Description
AutoCompressIfNeeded releases the persisted "IsCompressing" flag before removing the local in-memory
guard; if the repository release call throws, the local guard removal is skipped and this instance
can permanently stop compressing that conversation until restart.
Code

src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Compression.cs[R139-145]

+        finally
+        {
+            // Release both guards so the next turn can compress again. Runs only because we acquired
+            // them above (a failed acquire returns before entering this try).
+            await db.UpdateConversationCompressing(conversationId, false);
+            _localCompressing.TryRemove(conversationId, out _);
+        }
Evidence
The cleanup sequence in AutoCompressIfNeeded awaits a repository release call before removing
_localCompressing; exceptions from repository implementations (file read/write or Mongo update)
can interrupt the finally block and skip local guard removal.

src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Compression.cs[139-145]
src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs[799-855]
src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs[717-753]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
In `AutoCompressIfNeeded`, the `finally` block does `await db.UpdateConversationCompressing(..., false)` and only then removes the local guard. If the awaited call throws (I/O/Mongo/network), `_localCompressing.TryRemove` never runs.

### Issue Context
This is a fire-and-forget background task, so failures are plausible (transient DB/file errors). Leaking `_localCompressing` for a conversation means all future attempts on the same instance return early forever.

### Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Compression.cs[139-145]

### Suggested fix
- In the `finally` block, remove the local guard in its own `finally` (or first), and wrap the repository release call in a `try/catch` so cleanup cannot prevent other cleanup.
- Example structure:
 - `try { await db.UpdateConversationCompressing(..., false); } catch (Exception ex) { _logger.LogWarning(ex, ...); } finally { _localCompressing.TryRemove(...); }`

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Archived data not deleted 🐞 Bug ⛨ Security
Description
MongoRepository.DeleteConversations does not delete ConversationArchives records (the deletion is
commented out), so archived raw dialogs and trimmed state versions can remain in storage even after
the conversation is deleted.
Code

src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs[R78-81]

+        //var filterArchive = Builders<ConversationArchiveDocument>.Filter.In(x => x.ConversationId, conversationIds);
        //var filterConvFile = Builders<ConversationFileDocument>.Filter.In(x => x.ConversationId, conversationIds);

        var promptLogDeleted = await _dc.LlmCompletionLogs.DeleteManyAsync(filterPromptLog);
Evidence
The archive collection is explicitly designed to store raw dialogs/state history from compaction
runs, but the deletion path currently comments out deleting those records, leaving orphaned archived
conversation data after deletion.

src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs[67-95]
src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationArchiveDocument.cs[3-15]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`DeleteConversations` removes conversations/dialogs/states/logs, but leaves `ConversationArchives` undeleted because the filter and delete calls are commented out.

### Issue Context
`ConversationArchives` holds archived raw dialogs and state versions for compaction runs. Leaving these behind defeats expectations for data deletion and can retain sensitive user content.

### Fix Focus Areas
- src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs[67-95]

### Suggested fix
- Re-enable archive deletion:
 - Create `filterArchive` and call `_dc.ConversationArchives.DeleteManyAsync(filterArchive)`.
- Ensure the method’s return value accounts for archive deletions if needed.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment on lines +901 to +905
var foundIdx = dialogs.FindIndex(x => x.MetaData?.MessageId == cutMessageId);
if (foundIdx <= 0)
{
return 0;
}

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.

Action required

1. Case-sensitive messageid match 📘 Rule violation ≡ Correctness

The new compaction code compares identifier-like MessageId strings using case-sensitive ==,
which can fail to locate the cut point when casing differs. This violates the required ordinal
case-insensitive semantics for identifiers and can cause compaction to no-op unexpectedly.
Agent Prompt
## Issue description
Identifier-like string comparisons for `MessageId` are performed with case-sensitive equality, which can break lookups when casing differs.

## Issue Context
Compaction depends on reliably finding the cut dialog by `cutMessageId`. Per compliance, identifier comparisons must use ordinal case-insensitive semantics.

## Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs[901-905]
- src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs[770-775]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +150 to +152
var curIndexes = collection.Indexes.List().ToList().Where(x => x.Contains("name")).Select(x => x["name"].AsString);
if (!curIndexes.Any(x => x.StartsWith("ConversationId")))
{

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.

Action required

2. startswith lacks stringcomparison 📘 Rule violation ⚙ Maintainability

The new Mongo index-existence check uses StartsWith without specifying StringComparison, relying
on default comparison behavior. This violates the requirement to use ordinal case-insensitive
semantics for identifier-like comparisons and can behave inconsistently across cultures/casing.
Agent Prompt
## Issue description
Identifier-like string prefix checks are performed without an explicit `StringComparison`, which can lead to inconsistent behavior.

## Issue Context
Compliance requires ordinal case-insensitive semantics for identifier-like comparisons.

## Fix Focus Areas
- src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs[150-152]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +161 to +165
var cutIndex = dialogs.Count - Math.Max(0, keepRecentCount);
while (cutIndex > 0 && dialogs[cutIndex].Role != AgentRole.User)
{
cutIndex--;
}

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.

Action required

3. Compression cut index crash 🐞 Bug ≡ Correctness

ComputeCompressionCut can produce cutIndex == dialogs.Count when KeepRecentCount <= 0, causing an
IndexOutOfRangeException when accessing dialogs[cutIndex] and making auto-compression fail
repeatedly under misconfiguration.
Agent Prompt
### Issue description
`ComputeCompressionCut` can compute an out-of-range index when `keepRecentCount` is `0` or negative, because it sets `cutIndex = dialogs.Count - Math.Max(0, keepRecentCount)` and then indexes `dialogs[cutIndex]`.

### Issue Context
`KeepRecentCount` is configuration-driven and currently has no validation, so `0`/negative values can enter at runtime.

### Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Compression.cs[154-168]

### Suggested fix
- Treat `keepRecentCount <= 0` as invalid and return `-1` (no compression), **or** clamp to a minimum of `1`.
- Ensure `cutIndex` is always within `[0, dialogs.Count - 1]` before indexing (e.g., early-return when `cutIndex >= dialogs.Count`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +139 to +145
finally
{
// Release both guards so the next turn can compress again. Runs only because we acquired
// them above (a failed acquire returns before entering this try).
await db.UpdateConversationCompressing(conversationId, false);
_localCompressing.TryRemove(conversationId, out _);
}

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.

Action required

4. Compression guard leak 🐞 Bug ☼ Reliability

AutoCompressIfNeeded releases the persisted "IsCompressing" flag before removing the local in-memory
guard; if the repository release call throws, the local guard removal is skipped and this instance
can permanently stop compressing that conversation until restart.
Agent Prompt
### Issue description
In `AutoCompressIfNeeded`, the `finally` block does `await db.UpdateConversationCompressing(..., false)` and only then removes the local guard. If the awaited call throws (I/O/Mongo/network), `_localCompressing.TryRemove` never runs.

### Issue Context
This is a fire-and-forget background task, so failures are plausible (transient DB/file errors). Leaking `_localCompressing` for a conversation means all future attempts on the same instance return early forever.

### Fix Focus Areas
- src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.Compression.cs[139-145]

### Suggested fix
- In the `finally` block, remove the local guard in its own `finally` (or first), and wrap the repository release call in a `try/catch` so cleanup cannot prevent other cleanup.
- Example structure:
  - `try { await db.UpdateConversationCompressing(..., false); } catch (Exception ex) { _logger.LogWarning(ex, ...); } finally { _localCompressing.TryRemove(...); }`

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +78 to 81
//var filterArchive = Builders<ConversationArchiveDocument>.Filter.In(x => x.ConversationId, conversationIds);
//var filterConvFile = Builders<ConversationFileDocument>.Filter.In(x => x.ConversationId, conversationIds);

var promptLogDeleted = await _dc.LlmCompletionLogs.DeleteManyAsync(filterPromptLog);

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.

Action required

5. Archived data not deleted 🐞 Bug ⛨ Security

MongoRepository.DeleteConversations does not delete ConversationArchives records (the deletion is
commented out), so archived raw dialogs and trimmed state versions can remain in storage even after
the conversation is deleted.
Agent Prompt
### Issue description
`DeleteConversations` removes conversations/dialogs/states/logs, but leaves `ConversationArchives` undeleted because the filter and delete calls are commented out.

### Issue Context
`ConversationArchives` holds archived raw dialogs and state versions for compaction runs. Leaving these behind defeats expectations for data deletion and can retain sensitive user content.

### Fix Focus Areas
- src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs[67-95]

### Suggested fix
- Re-enable archive deletion:
  - Create `filterArchive` and call `_dc.ConversationArchives.DeleteManyAsync(filterArchive)`.
- Ensure the method’s return value accounts for archive deletions if needed.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant