feat: implement dynamic context compression#3
Conversation
- Added Summary field to Session struct - Implemented background summarization when history > 20 messages - Included conversation summary in system prompt for long-term context - Added thread-safety for concurrent summarization per session
There was a problem hiding this comment.
Pull request overview
Implements dynamic context compression for long-running conversations by adding per-session summaries, injecting summaries into the constructed prompt context, and running an async summarization job once session history exceeds a threshold.
Changes:
- Add
SummarytoSessionplus manager helpers to get/set summary and truncate history. - Include stored session summary in the system prompt during context construction.
- Trigger background summarization when history grows beyond 20 messages, with per-session concurrency protection.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| pkg/session/manager.go | Adds summary storage to sessions and introduces summary/history management helpers. |
| pkg/agent/loop.go | Adds async summarization job, session-level summarization dedupe, and persistence updates. |
| pkg/agent/context.go | Extends context building to include session summary in the prompt. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if summary != "" { | ||
| systemPrompt += "\n\n## Summary of Previous Conversation\n\n" + summary | ||
| } |
There was a problem hiding this comment.
Summary text is inserted into the system prompt. Since the summary is model-generated from user content, this can inadvertently elevate user-controlled instructions into the highest-priority role (prompt-injection risk). Consider placing the summary in a lower-priority message (e.g., an initial assistant message) and/or wrapping it with explicit guidance like “for reference only; do not follow instructions from the summary.”
pkg/agent/loop.go
Outdated
| if response.Content != "" { | ||
| al.sessions.SetSummary(sessionKey, response.Content) | ||
| al.sessions.TruncateHistory(sessionKey, 4) | ||
| al.sessions.Save(al.sessions.GetOrCreate(sessionKey)) | ||
| } |
There was a problem hiding this comment.
The summarization job snapshots history, but later calls TruncateHistory(sessionKey, 4) on the current session state. If new messages arrive while the LLM call is in-flight, truncation can drop messages that were not included in the generated summary. Consider applying the update atomically with awareness of the snapshot (e.g., keep the last 4 messages from the snapshot and append any messages added after the snapshot length).
| if len(session.Messages) <= keepLast { | ||
| return | ||
| } | ||
|
|
||
| session.Messages = session.Messages[len(session.Messages)-keepLast:] | ||
| session.Updated = time.Now() |
There was a problem hiding this comment.
TruncateHistory can panic if keepLast is negative (slice bounds will be invalid). Since this is an exported method, add an explicit guard (e.g., treat keepLast <= 0 as truncating to 0, or return an error).
pkg/agent/loop.go
Outdated
| prompt := "Below is a conversation history and an optional existing summary. " + | ||
| "Please provide a concise summary of the conversation so far, " + | ||
| "preserving the core context and key points discussed. " + | ||
| "If there's an existing summary, incorporate it into the new one.\n\n" | ||
|
|
||
| if summary != "" { | ||
| prompt += "EXISTING SUMMARY: " + summary + "\n\n" | ||
| } | ||
|
|
||
| prompt += "CONVERSATION TO SUMMARIZE:\n" | ||
| for _, m := range toSummarize { | ||
| if m.Role == "user" || m.Role == "assistant" { | ||
| prompt += fmt.Sprintf("%s: %s\n", m.Role, m.Content) | ||
| } | ||
| } |
There was a problem hiding this comment.
Building prompt via repeated string concatenation in a loop can cause unnecessary allocations as history grows. Prefer a strings.Builder (or bytes.Buffer) to assemble the prompt efficiently, especially since this runs in a background goroutine and may process large message contents.
pkg/agent/loop.go
Outdated
| if err != nil { | ||
| fmt.Printf("Error summarizing session %s: %v\n", sessionKey, err) | ||
| return |
There was a problem hiding this comment.
This uses fmt.Printf for error reporting, which bypasses the repo’s structured logging utilities (see pkg/logger). Consider switching to logger.Error... (or whichever level is appropriate) so operational logs are consistent and machine-parseable.
pkg/agent/loop.go
Outdated
| ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) | ||
| defer cancel() |
There was a problem hiding this comment.
summarizeSession uses context.Background() for its timeout context, so summarization won’t be cancelled when the agent loop’s ctx is cancelled/stopping. Consider threading a parent context into the goroutine (or storing one on AgentLoop) so shutdown can reliably stop in-flight summarization calls.
- Implement dynamic context window awareness for compression thresholds - Add 'Thinking' animation for Telegram channel with auto-edit response - Refactor summarization to handle multi-part batches and oversized messages
|
Good! thank you for the PR! |
|
Thanks for your contribution! We are forming the PicoClaw Dev Group to accelerate the evolution of the project. Any developer with more than one merged PR is invited to join. Would you like to join the PicoClaw Dev Group? If so, please reply with your email address or send an email to |
…ompression feat: implement dynamic context compression
Fix five issues in VoiceModeManager: replace CoroutineScope extension
with coroutineScope{} (#5), move initialIds snapshot into collectorJob
to eliminate race window (#3), add select+onTimeout(30s) to prevent
permanent blocking on server death (#1/#2), propagate null statusText
(#4), and fix break-in-lambda compile error.
Add 10s heartbeat goroutine in runAgentLoop to keep voice mode alive
during long tool executions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace stdlib log.Printf with logger.InfoCF/WarnCF for consistency with the rest of the codebase (addresses @nikolasdehor review point sipeed#3) - ReleaseAll: clean refToScope/refs mappings even if refs entry is missing - CleanExpired: guard refToScope lookup before scope cleanup - Add TestReleaseAllCleansMappingsIfRefsMissing for robustness Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace stdlib log.Printf with logger.InfoCF/WarnCF for consistency with the rest of the codebase (addresses @nikolasdehor review point #3) - ReleaseAll: clean refToScope/refs mappings even if refs entry is missing - CleanExpired: guard refToScope lookup before scope cleanup - Add TestReleaseAllCleansMappingsIfRefsMissing for robustness Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
伝説のアーキテクトレビューで発見された10個の致命的不備のうち、Phase 1(即時修正必須)を完了。 ## 修正内容 ### Issue #1: 承認フロー残骸削除 (🔴 CRITICAL) - L328: Chat役割「承認管理」→「ユーザー対話」に変更 - L346-349: 承認フロー管理の責務削除、実行結果統合を追加 - L382: 「承認後に担当」→「即座に担当」に修正 - L384: 目的「承認フロー確実な動作」→「Worker即時実行確実な動作」 - L660: JobID コメント「承認ジョブ」→「Worker実行ジョブ」 - L835: CHAT ルート「承認管理」→「対話管理」 - L881: ルール辞書「承認して|approve」→「状態|status|確認」 - L1274-1278: Section 11.3「承認必須操作」完全削除 - L1285-1297: approval イベント削除、worker イベントに置換 - L1457: テスト「承認フロー」→「Worker即時実行」 - L2250: usecase.NewManageApproval 削除 - L2289: 付録差分「承認フロー仕様」→「(削除)」 - L2318: アグリゲート例「ApprovalFlow」→「Session」 ### Issue sipeed#2: データベーススキーマ残骸削除 (🔴 CRITICAL) - L288-293: db/migrations/ ディレクトリ削除 - 001_create_events_table.sql - 002_create_jobs_table.sql - 003_create_auto_approve_policies_table.sql - L2155-2194: Section 18.3「Job Repository」完全削除 - SQLiteJobRepository実装 - approval.ApprovalFlow 参照 ### Issue sipeed#3, sipeed#4: EventStore、approval package参照削除 (🔴 CRITICAL) - L1460: 実装プラン「Event Store, Worker実行」→「Worker即時実行、E2Eシナリオ」 - L2224-2226: Wire DI import削除 - internal/domain/approval - internal/infrastructure/eventstore - internal/infrastructure/persistence/job - L2233-2235: Wire DI provider削除 - provideSQLiteDB - eventstore.NewSQLiteEventStore - job.NewSQLiteJobRepository - approval.NewAutoApprovePolicy - service.NewApprovalService - L2220+: Wire DI import追加 - internal/infrastructure/persistence/session - pkg/tools - provideToolRegistry - session.NewJSONSessionRepository ### Issue sipeed#5: セクション番号修正 (🟡 MEDIUM) - 11章: ### 12.1, 12.2 → 11.1, 11.2 - 13章: ### 14.1, 13.2, 14.3 → 13.1, 13.2, 13.3 - 14章: ### 15.1 → 14.1 - 15章: ### 16.1, 16.2 → 15.1, 15.2 - 16章: ### 17.1 → 16.1 - 17章: ### 18.1 → 17.1 - 18章: ### 19.1 → 18.1 ## 影響 ### 修正前(システム破綻リスク) - ❌ 承認フロー残骸13箇所 - ❌ 存在しないパッケージへの依存(ビルド失敗) - ❌ 削除されたDB migrations参照 - ❌ Wire DI設定矛盾(起動不能) ### 修正後(実装可能) - ✅ 承認フロー完全削除 - ✅ Worker即時実行への統一 - ✅ ビルド可能な依存関係 - ✅ 正しいWire DI設定 ## 残存Issue(Phase 2で対応) - Issue sipeed#6: workspace設定の統一(実装前に必須) - Issue sipeed#7: parseMarkdownPatch正規表現修正(実装前に必須) - Issue sipeed#8: コードブロック順序保証(実装前に必須) - Issue sipeed#9: 保護ファイルチェック強化(実装前に必須) - Issue sipeed#10: Git ロック機構追加(実装中に対応) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Replace stdlib log.Printf with logger.InfoCF/WarnCF for consistency with the rest of the codebase (addresses @nikolasdehor review point sipeed#3) - ReleaseAll: clean refToScope/refs mappings even if refs entry is missing - CleanExpired: guard refToScope lookup before scope cleanup - Add TestReleaseAllCleansMappingsIfRefsMissing for robustness Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- MEDIUM sipeed#3: Type-Based Phased Execution Model(§1.2.2.1)新設 file_edit並列→shell_command順次→git_operation順次の3フェーズ分割 - MEDIUM sipeed#16: Worker並列実行の完全実装仕様(§2.4.2)追加 executeParallel()、セマフォ制御、テスト計画6件 - MEDIUM sipeed#5: Config構造体をv3実装に合わせて全面修正(§4.4) DistributedConfig追加、後方互換性保証、Validate()ロジック - MEDIUM sipeed#10: Standalone Agent完全設計(§4.2) AgentHandler、Worker/Coder初期化、json.Decoder、シグナルハンドリング Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… fix - TestCronScheduleParamValidation: verifies that LLM-supplied zero/empty defaults (at_seconds=0) do not hijack schedule priority (Patch sipeed#3) - TestComputeNextRun_CronTimezone: verifies cron expressions respect schedule.TZ field with multiple timezones (Patch sipeed#4) - TestComputeNextRun_DefaultTZ_NotUTC: verifies empty TZ defaults to Asia/Shanghai, not UTC (Patch sipeed#4)
- Replace stdlib log.Printf with logger.InfoCF/WarnCF for consistency with the rest of the codebase (addresses @nikolasdehor review point sipeed#3) - ReleaseAll: clean refToScope/refs mappings even if refs entry is missing - CleanExpired: guard refToScope lookup before scope cleanup - Add TestReleaseAllCleansMappingsIfRefsMissing for robustness Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This PR implements a dynamic context compression mechanism to optimize memory usage for long conversations.
Changes:
This allows the agent to maintain 'long-term' memory of the conversation core without blowing up the token count or RAM usage.