-
Notifications
You must be signed in to change notification settings - Fork 6k
Add context control for subagents #4119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: production
Are you sure you want to change the base?
Add context control for subagents #4119
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements context filtering for subagents to control what parent session context they receive. This enables better performance for lightweight local models by limiting the amount of context passed to them.
- Introduces a
ContextFiltermodule with multiple filtering modes (none, summary, filtered, full) - Adds context configuration to agent definitions with token/message limits
- Updates the task tool to gather and filter parent session context before passing it to subagents
Reviewed Changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/opencode/src/session/context-filter.ts | Core filtering logic with four modes for context control |
| packages/opencode/src/session/context-filter.test.ts | Comprehensive test suite covering all filtering modes |
| packages/opencode/src/config/config.ts | Schema definition for context filter configuration |
| packages/opencode/src/agent/agent.ts | Adds context property to agent schema and parsing |
| packages/opencode/src/tool/task.ts | Integrates context filtering into task execution with debug logging |
| AGENTS.md | Documentation for context control feature |
| .opencode/agent/*.md | Example agent configurations demonstrating context control |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
packages/opencode/src/tool/task.ts
Outdated
| console.log('\n🔍 [CONTEXT FILTER DEBUG]') | ||
| console.log(' Agent:', agent.name) | ||
| console.log(' Mode: none (or no config)') | ||
| console.log(' Context parts: 0') | ||
| console.log('') |
Copilot
AI
Nov 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Debug console.log statements should be removed or replaced with a proper logging mechanism. The codebase uses a Log utility (imported in other files like session/compaction.ts) for structured logging. Consider using that instead or removing these debug statements before merging to production.
| }) | ||
|
|
||
| // Gather parent session context if agent has context config | ||
| const contextParts: any[] = [] |
Copilot
AI
Nov 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using any[] for contextParts bypasses TypeScript's type safety. Since the parts are later filtered to only include specific types (text, file, agent) and should match MessageV2.Part[], declare this as const contextParts: MessageV2.Part[] = [] for better type safety.
| const contextParts: any[] = [] | |
| const contextParts: MessageV2.Part[] = [] |
packages/opencode/src/tool/task.ts
Outdated
| // DEBUG: Log context filtering results | ||
| const estimatedTokens = Math.round( | ||
| allowedParts.reduce((sum, part) => { | ||
| if (part.type === 'text') return sum + ((part as any).text.length / 4) |
Copilot
AI
Nov 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using (part as any) bypasses type safety. Since the type has already been checked as 'text', use a proper type assertion: (part as MessageV2.TextPart).text.length / 4. This provides compile-time type checking and better IDE support.
| if (part.type === 'text') return sum + ((part as any).text.length / 4) | |
| if (part.type === 'text') return sum + ((part as MessageV2.TextPart).text.length / 4) |
|
|
||
| for (const part of msg.parts) { | ||
| if (part.type === "tool" && part.state.status === "completed") { | ||
| const toolName = (part as MessageV2.ToolPart).tool |
Copilot
AI
Nov 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The type assertion (part as MessageV2.ToolPart) is used after checking part.type === 'tool', which is correct. However, TypeScript should be able to narrow the type automatically. Consider using a type guard or restructuring the code to leverage TypeScript's discriminated union narrowing for better type safety without manual assertions.
| const toolName = (part as MessageV2.ToolPart).tool | |
| const toolName = part.tool |
| if (remaining > 100) { | ||
| const truncatedPart: MessageV2.TextPart = { | ||
| ...part, | ||
| text: (part as MessageV2.TextPart).text.substring(0, remaining) + "\n...(truncated)", |
Copilot
AI
Nov 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] While the type assertion is safe here due to the preceding type check, consider using TypeScript's type narrowing. You could assign the part to a typed variable after the check (e.g., const textPart: MessageV2.TextPart = part) to avoid repetitive type assertions and improve code clarity.
| */ | ||
| function estimatePartSize(part: MessageV2.Part): number { | ||
| if (part.type === "text") { | ||
| return (part as MessageV2.TextPart).text.length |
Copilot
AI
Nov 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Similar to other type assertions in this file, consider using a type guard or local variable after the type check to avoid type assertions and leverage TypeScript's type narrowing capabilities.
| return (part as MessageV2.TextPart).text.length | |
| const textPart = part; | |
| return textPart.text.length; |
Implements granular control over what context from parent sessions is passed to subagents, enabling significant performance improvements for small local models. Features: - Four context modes: none, summary, filtered, full - Configurable token and message limits - Selective filtering by message types and tool results - Backward compatible (defaults to 'none' mode) - Comprehensive unit tests - Example configurations and documentation This addresses issue anomalyco#4096 by providing native context control without requiring external proxies. Performance impact: - 99% reduction in token usage for small models - 8x faster inference times with llama3.2:1b - Enables practical use of lightweight local models as subagents
- Document context control feature in AGENTS.md with modes and examples - Move example agents to .opencode/agent/ (code-reviewer, doc-reader, log-analyzer, quick-search) - Follow standard OpenCode documentation structure
- Update types to use MessageV2.WithParts instead of MessageV2.Info - Change ToolPart.name to ToolPart.tool (API change) - Add null checks for config parameter - Filter context parts to only allowed types (text, file, agent) for prompt - Fix test helper to use correct callID format - All tests passing
36e46de to
b03e4e4
Compare
Summary
Implementation
This PR introduces native context filtering for subagents, allowing fine-grained control over what parent session context is passed to subagents.
Key Features
Context Modes:
none(default) - No parent context, maintains current behaviorsummary- Compact session summary (~100-500 tokens)filtered- Selective inclusion by message type and tool resultsfull- All context with configurable limitsConfiguration:
Performance Impact
Before: 45,000 tokens → 8 seconds (llama3.2:1b)
After: 200 tokens → 1 second (99% reduction, 8× faster)
Changes
New files:
packages/opencode/src/session/context-filter.ts- Core filtering logic (246 lines)packages/opencode/src/session/context-filter.test.ts- Comprehensive tests (237 lines).opencode/agent/- Example agent configurationsModified files:
packages/opencode/src/config/config.ts- Context filter schemapackages/opencode/src/agent/agent.ts- Agent context supportpackages/opencode/src/tool/task.ts- Apply filtering before subagent invocationAGENTS.md- DocumentationBackward Compatibility
none(current behavior)Testing
Test Results: ✅ 11 tests passing, 32 assertions
Addresses #4096