gencode-ai 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +11 -0
- package/CLAUDE.md +70 -0
- package/LICENSE +21 -0
- package/README.md +117 -0
- package/dist/agent/agent.d.ts +84 -0
- package/dist/agent/agent.d.ts.map +1 -0
- package/dist/agent/agent.js +233 -0
- package/dist/agent/agent.js.map +1 -0
- package/dist/agent/index.d.ts +6 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +6 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/types.d.ts +47 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +5 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/cli/components/App.d.ts +14 -0
- package/dist/cli/components/App.d.ts.map +1 -0
- package/dist/cli/components/App.js +395 -0
- package/dist/cli/components/App.js.map +1 -0
- package/dist/cli/components/CommandSuggestions.d.ts +13 -0
- package/dist/cli/components/CommandSuggestions.d.ts.map +1 -0
- package/dist/cli/components/CommandSuggestions.js +32 -0
- package/dist/cli/components/CommandSuggestions.js.map +1 -0
- package/dist/cli/components/Header.d.ts +9 -0
- package/dist/cli/components/Header.d.ts.map +1 -0
- package/dist/cli/components/Header.js +13 -0
- package/dist/cli/components/Header.js.map +1 -0
- package/dist/cli/components/Input.d.ts +13 -0
- package/dist/cli/components/Input.d.ts.map +1 -0
- package/dist/cli/components/Input.js +27 -0
- package/dist/cli/components/Input.js.map +1 -0
- package/dist/cli/components/Logo.d.ts +2 -0
- package/dist/cli/components/Logo.d.ts.map +1 -0
- package/dist/cli/components/Logo.js +8 -0
- package/dist/cli/components/Logo.js.map +1 -0
- package/dist/cli/components/Messages.d.ts +37 -0
- package/dist/cli/components/Messages.d.ts.map +1 -0
- package/dist/cli/components/Messages.js +106 -0
- package/dist/cli/components/Messages.js.map +1 -0
- package/dist/cli/components/ModelSelector.d.ts +13 -0
- package/dist/cli/components/ModelSelector.d.ts.map +1 -0
- package/dist/cli/components/ModelSelector.js +72 -0
- package/dist/cli/components/ModelSelector.js.map +1 -0
- package/dist/cli/components/Spinner.d.ts +12 -0
- package/dist/cli/components/Spinner.d.ts.map +1 -0
- package/dist/cli/components/Spinner.js +45 -0
- package/dist/cli/components/Spinner.js.map +1 -0
- package/dist/cli/components/index.d.ts +12 -0
- package/dist/cli/components/index.d.ts.map +1 -0
- package/dist/cli/components/index.js +12 -0
- package/dist/cli/components/index.js.map +1 -0
- package/dist/cli/components/theme.d.ts +31 -0
- package/dist/cli/components/theme.d.ts.map +1 -0
- package/dist/cli/components/theme.js +36 -0
- package/dist/cli/components/theme.js.map +1 -0
- package/dist/cli/index-legacy.d.ts +7 -0
- package/dist/cli/index-legacy.d.ts.map +1 -0
- package/dist/cli/index-legacy.js +431 -0
- package/dist/cli/index-legacy.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +116 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/ink-cli.d.ts +7 -0
- package/dist/cli/ink-cli.d.ts.map +1 -0
- package/dist/cli/ink-cli.js +105 -0
- package/dist/cli/ink-cli.js.map +1 -0
- package/dist/cli/session-picker.d.ts +16 -0
- package/dist/cli/session-picker.d.ts.map +1 -0
- package/dist/cli/session-picker.js +280 -0
- package/dist/cli/session-picker.js.map +1 -0
- package/dist/cli/ui.d.ts +61 -0
- package/dist/cli/ui.d.ts.map +1 -0
- package/dist/cli/ui.js +364 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +6 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/manager.d.ts +31 -0
- package/dist/config/manager.d.ts.map +1 -0
- package/dist/config/manager.js +65 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/types.d.ts +22 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +6 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/memory/index.d.ts +10 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +9 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/init.d.ts +20 -0
- package/dist/memory/init.d.ts.map +1 -0
- package/dist/memory/init.js +332 -0
- package/dist/memory/init.js.map +1 -0
- package/dist/memory/manager.d.ts +85 -0
- package/dist/memory/manager.d.ts.map +1 -0
- package/dist/memory/manager.js +234 -0
- package/dist/memory/manager.js.map +1 -0
- package/dist/memory/types.d.ts +74 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +6 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/permissions/index.d.ts +7 -0
- package/dist/permissions/index.d.ts.map +1 -0
- package/dist/permissions/index.js +6 -0
- package/dist/permissions/index.js.map +1 -0
- package/dist/permissions/manager.d.ts +32 -0
- package/dist/permissions/manager.d.ts.map +1 -0
- package/dist/permissions/manager.js +79 -0
- package/dist/permissions/manager.js.map +1 -0
- package/dist/permissions/types.d.ts +14 -0
- package/dist/permissions/types.d.ts.map +1 -0
- package/dist/permissions/types.js +17 -0
- package/dist/permissions/types.js.map +1 -0
- package/dist/providers/anthropic.d.ts +20 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +185 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/gemini.d.ts +21 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +241 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/index.d.ts +34 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +72 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai.d.ts +19 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +221 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/types.d.ts +125 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +6 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/session/index.d.ts +6 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +6 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/manager.d.ts +101 -0
- package/dist/session/manager.d.ts.map +1 -0
- package/dist/session/manager.js +295 -0
- package/dist/session/manager.js.map +1 -0
- package/dist/session/types.d.ts +39 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +10 -0
- package/dist/session/types.js.map +1 -0
- package/dist/tools/builtin/bash.d.ts +7 -0
- package/dist/tools/builtin/bash.d.ts.map +1 -0
- package/dist/tools/builtin/bash.js +80 -0
- package/dist/tools/builtin/bash.js.map +1 -0
- package/dist/tools/builtin/edit.d.ts +7 -0
- package/dist/tools/builtin/edit.d.ts.map +1 -0
- package/dist/tools/builtin/edit.js +32 -0
- package/dist/tools/builtin/edit.js.map +1 -0
- package/dist/tools/builtin/glob.d.ts +7 -0
- package/dist/tools/builtin/glob.d.ts.map +1 -0
- package/dist/tools/builtin/glob.js +36 -0
- package/dist/tools/builtin/glob.js.map +1 -0
- package/dist/tools/builtin/grep.d.ts +7 -0
- package/dist/tools/builtin/grep.d.ts.map +1 -0
- package/dist/tools/builtin/grep.js +59 -0
- package/dist/tools/builtin/grep.js.map +1 -0
- package/dist/tools/builtin/read.d.ts +7 -0
- package/dist/tools/builtin/read.d.ts.map +1 -0
- package/dist/tools/builtin/read.js +29 -0
- package/dist/tools/builtin/read.js.map +1 -0
- package/dist/tools/builtin/write.d.ts +7 -0
- package/dist/tools/builtin/write.d.ts.map +1 -0
- package/dist/tools/builtin/write.js +24 -0
- package/dist/tools/builtin/write.js.map +1 -0
- package/dist/tools/index.d.ts +38 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +32 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/registry.d.ts +22 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +71 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/types.d.ts +62 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +126 -0
- package/dist/tools/types.js.map +1 -0
- package/docs/README.md +16 -0
- package/docs/proposals/0001-web-fetch-tool.md +293 -0
- package/docs/proposals/0002-web-search-tool.md +306 -0
- package/docs/proposals/0003-task-subagents.md +333 -0
- package/docs/proposals/0004-plan-mode.md +338 -0
- package/docs/proposals/0005-todo-system.md +299 -0
- package/docs/proposals/0006-memory-system.md +539 -0
- package/docs/proposals/0007-context-management.md +429 -0
- package/docs/proposals/0008-checkpointing.md +327 -0
- package/docs/proposals/0009-hooks-system.md +343 -0
- package/docs/proposals/0010-mcp-integration.md +382 -0
- package/docs/proposals/0011-custom-commands.md +374 -0
- package/docs/proposals/0012-ask-user-question.md +317 -0
- package/docs/proposals/0013-multi-edit-tool.md +345 -0
- package/docs/proposals/0014-lsp-tool.md +478 -0
- package/docs/proposals/0015-ls-tool.md +407 -0
- package/docs/proposals/0016-kill-shell-tool.md +455 -0
- package/docs/proposals/0017-background-tasks.md +489 -0
- package/docs/proposals/0018-parallel-tool-execution.md +415 -0
- package/docs/proposals/0019-session-enhancements.md +462 -0
- package/docs/proposals/0020-session-summarization.md +447 -0
- package/docs/proposals/0021-skills-system.md +409 -0
- package/docs/proposals/0022-plugin-system.md +467 -0
- package/docs/proposals/0023-permission-enhancements.md +470 -0
- package/docs/proposals/0024-keyboard-shortcuts.md +443 -0
- package/docs/proposals/0025-cost-tracking.md +447 -0
- package/docs/proposals/0026-git-integration.md +475 -0
- package/docs/proposals/0027-enhanced-read-tool.md +514 -0
- package/docs/proposals/0028-enhanced-bash-tool.md +511 -0
- package/docs/proposals/0029-notebook-edit-tool.md +413 -0
- package/docs/proposals/0030-plugin-marketplace.md +360 -0
- package/docs/proposals/0031-command-suggestions.md +295 -0
- package/docs/proposals/0032-ide-integrations.md +328 -0
- package/docs/proposals/0033-enterprise-deployment.md +221 -0
- package/docs/proposals/0034-sandboxing.md +273 -0
- package/docs/proposals/0035-auto-updater.md +311 -0
- package/docs/proposals/0036-enhanced-glob-tool.md +267 -0
- package/docs/proposals/0037-enhanced-grep-tool.md +360 -0
- package/docs/proposals/0038-interactive-cli-ui.md +373 -0
- package/docs/proposals/0039-streaming-enhancements.md +359 -0
- package/docs/proposals/0040-multi-provider-enhancements.md +369 -0
- package/docs/proposals/README.md +84 -0
- package/docs/proposals/TEMPLATE.md +57 -0
- package/docs/proposals/research/claude-code-research.md +307 -0
- package/examples/agent-demo.ts +115 -0
- package/examples/basic.ts +166 -0
- package/package.json +50 -0
- package/src/agent/agent.ts +276 -0
- package/src/agent/index.ts +6 -0
- package/src/agent/types.ts +62 -0
- package/src/cli/components/App.tsx +565 -0
- package/src/cli/components/CommandSuggestions.tsx +58 -0
- package/src/cli/components/Header.tsx +36 -0
- package/src/cli/components/Input.tsx +60 -0
- package/src/cli/components/Logo.tsx +16 -0
- package/src/cli/components/Messages.tsx +210 -0
- package/src/cli/components/ModelSelector.tsx +135 -0
- package/src/cli/components/Spinner.tsx +72 -0
- package/src/cli/components/index.ts +21 -0
- package/src/cli/components/theme.ts +36 -0
- package/src/cli/index.tsx +136 -0
- package/src/config/index.ts +7 -0
- package/src/config/manager.ts +77 -0
- package/src/config/types.ts +25 -0
- package/src/index.ts +86 -0
- package/src/permissions/index.ts +7 -0
- package/src/permissions/manager.ts +97 -0
- package/src/permissions/types.ts +29 -0
- package/src/providers/anthropic.ts +224 -0
- package/src/providers/gemini.ts +295 -0
- package/src/providers/index.ts +97 -0
- package/src/providers/openai.ts +261 -0
- package/src/providers/types.ts +181 -0
- package/src/session/index.ts +6 -0
- package/src/session/manager.ts +354 -0
- package/src/session/types.ts +49 -0
- package/src/tools/builtin/bash.ts +92 -0
- package/src/tools/builtin/edit.ts +37 -0
- package/src/tools/builtin/glob.ts +42 -0
- package/src/tools/builtin/grep.ts +67 -0
- package/src/tools/builtin/read.ts +34 -0
- package/src/tools/builtin/write.ts +27 -0
- package/src/tools/index.ts +36 -0
- package/src/tools/registry.ts +83 -0
- package/src/tools/types.ts +172 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
# Proposal: Context Management
|
|
2
|
+
|
|
3
|
+
- **Proposal ID**: 0007
|
|
4
|
+
- **Author**: mycode team
|
|
5
|
+
- **Status**: Draft
|
|
6
|
+
- **Created**: 2025-01-15
|
|
7
|
+
- **Updated**: 2025-01-15
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
Implement a comprehensive context management system that tracks token usage, manages context window limits, and automatically compacts conversations when approaching limits. This ensures the agent can handle long sessions without losing important context or crashing due to context overflow.
|
|
12
|
+
|
|
13
|
+
## Motivation
|
|
14
|
+
|
|
15
|
+
Currently, mycode has no awareness of context window limits. This leads to:
|
|
16
|
+
|
|
17
|
+
1. **Session crashes**: Long conversations exceed context limits silently
|
|
18
|
+
2. **Lost context**: Important early information gets truncated unexpectedly
|
|
19
|
+
3. **No visibility**: Users can't see how much context they're using
|
|
20
|
+
4. **Inefficient usage**: No automatic optimization of context space
|
|
21
|
+
5. **Provider differences**: Different LLMs have different context limits
|
|
22
|
+
|
|
23
|
+
A context management system solves these by tracking usage and proactively managing context.
|
|
24
|
+
|
|
25
|
+
## Claude Code Reference
|
|
26
|
+
|
|
27
|
+
Claude Code implements sophisticated context management:
|
|
28
|
+
|
|
29
|
+
1. **Token Tracking**: Counts tokens for each message and tool result
|
|
30
|
+
2. **Context Warnings**: Alerts when approaching limits
|
|
31
|
+
3. **Auto-Compaction**: Automatically summarizes old messages when needed
|
|
32
|
+
4. **`/compact` Command**: Manual context compaction
|
|
33
|
+
5. **Unlimited Context**: "The conversation has unlimited context through automatic summarization"
|
|
34
|
+
6. **Memory Tool (Beta)**: Persistent storage that survives context clears
|
|
35
|
+
|
|
36
|
+
Key behaviors:
|
|
37
|
+
- Displays context usage in status line
|
|
38
|
+
- Warns at 80% context usage
|
|
39
|
+
- Automatically compacts at 90% usage
|
|
40
|
+
- Preserves recent messages and important context
|
|
41
|
+
- Maintains conversation coherence after compaction
|
|
42
|
+
|
|
43
|
+
### Memory Tool API
|
|
44
|
+
|
|
45
|
+
Claude Code's Memory Tool (beta API `context-management-2025-06-27`) enables agents to persist information across context resets:
|
|
46
|
+
|
|
47
|
+
| Command | Description | Parameters |
|
|
48
|
+
|---------|-------------|------------|
|
|
49
|
+
| `view` | List directory or read file | `path`, `view_range?` |
|
|
50
|
+
| `create` | Create new file | `path`, `file_text` |
|
|
51
|
+
| `str_replace` | Replace text | `path`, `old_str`, `new_str` |
|
|
52
|
+
| `insert` | Insert at line | `path`, `insert_line`, `insert_text` |
|
|
53
|
+
| `delete` | Delete file/directory | `path` |
|
|
54
|
+
| `rename` | Rename/move | `old_path`, `new_path` |
|
|
55
|
+
|
|
56
|
+
### Context Editing Configuration
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
context_management: {
|
|
60
|
+
edits: [{
|
|
61
|
+
type: "clear_tool_uses_20250919",
|
|
62
|
+
trigger: { type: "input_tokens", value: 100000 },
|
|
63
|
+
keep: { type: "tool_uses", value: 3 },
|
|
64
|
+
exclude_tools: ["memory"] // Never clear memory operations
|
|
65
|
+
}]
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Detailed Design
|
|
70
|
+
|
|
71
|
+
### API Design
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// src/context/types.ts
|
|
75
|
+
interface TokenUsage {
|
|
76
|
+
prompt: number;
|
|
77
|
+
completion: number;
|
|
78
|
+
total: number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface ContextLimits {
|
|
82
|
+
maxContextTokens: number;
|
|
83
|
+
maxOutputTokens: number;
|
|
84
|
+
warningThreshold: number; // Default: 0.8 (80%)
|
|
85
|
+
compactionThreshold: number; // Default: 0.9 (90%)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
interface ContextStats {
|
|
89
|
+
currentUsage: number;
|
|
90
|
+
maxTokens: number;
|
|
91
|
+
usagePercent: number;
|
|
92
|
+
messageCount: number;
|
|
93
|
+
oldestMessageAge: Date;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
interface CompactionResult {
|
|
97
|
+
removedMessages: number;
|
|
98
|
+
savedTokens: number;
|
|
99
|
+
summary: string;
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// src/context/context-manager.ts
|
|
105
|
+
class ContextManager {
|
|
106
|
+
private usage: TokenUsage;
|
|
107
|
+
private limits: ContextLimits;
|
|
108
|
+
private tokenizer: Tokenizer;
|
|
109
|
+
|
|
110
|
+
constructor(provider: string, model: string);
|
|
111
|
+
|
|
112
|
+
// Count tokens for a message
|
|
113
|
+
countTokens(content: string | MessageContent[]): number;
|
|
114
|
+
|
|
115
|
+
// Get current context statistics
|
|
116
|
+
getStats(): ContextStats;
|
|
117
|
+
|
|
118
|
+
// Check if compaction is needed
|
|
119
|
+
needsCompaction(): boolean;
|
|
120
|
+
|
|
121
|
+
// Compact conversation history
|
|
122
|
+
async compact(messages: Message[], options?: CompactionOptions): Promise<CompactionResult>;
|
|
123
|
+
|
|
124
|
+
// Update usage after API call
|
|
125
|
+
updateUsage(usage: TokenUsage): void;
|
|
126
|
+
|
|
127
|
+
// Get provider-specific context limit
|
|
128
|
+
static getContextLimit(provider: string, model: string): number;
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// src/context/tokenizer.ts
|
|
134
|
+
interface Tokenizer {
|
|
135
|
+
encode(text: string): number[];
|
|
136
|
+
decode(tokens: number[]): string;
|
|
137
|
+
count(text: string): number;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Provider-specific tokenizer implementations
|
|
141
|
+
class OpenAITokenizer implements Tokenizer { ... }
|
|
142
|
+
class AnthropicTokenizer implements Tokenizer { ... }
|
|
143
|
+
class GeminiTokenizer implements Tokenizer { ... }
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Implementation Approach
|
|
147
|
+
|
|
148
|
+
1. **Token Counting**: Use provider-specific tokenizers (tiktoken for OpenAI, etc.)
|
|
149
|
+
2. **Limit Detection**: Map model names to context limits
|
|
150
|
+
3. **Usage Tracking**: Track cumulative usage across conversation
|
|
151
|
+
4. **Compaction Strategy**:
|
|
152
|
+
- Preserve system prompt and memory context
|
|
153
|
+
- Keep recent N messages (configurable)
|
|
154
|
+
- Summarize older messages into a concise summary
|
|
155
|
+
- Preserve tool results that are still relevant
|
|
156
|
+
5. **Auto-Compaction**: Trigger when usage exceeds threshold
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
// Context limits by provider/model
|
|
160
|
+
const CONTEXT_LIMITS: Record<string, number> = {
|
|
161
|
+
'gpt-4': 8192,
|
|
162
|
+
'gpt-4-turbo': 128000,
|
|
163
|
+
'gpt-4o': 128000,
|
|
164
|
+
'claude-3-opus': 200000,
|
|
165
|
+
'claude-3-sonnet': 200000,
|
|
166
|
+
'claude-3-haiku': 200000,
|
|
167
|
+
'gemini-1.5-pro': 2000000,
|
|
168
|
+
'gemini-1.5-flash': 1000000,
|
|
169
|
+
};
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Memory Tool Implementation
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// src/context/memory-tool.ts
|
|
176
|
+
import { Tool, ToolContext, ToolResult } from '../tools/types';
|
|
177
|
+
import { z } from 'zod';
|
|
178
|
+
import * as fs from 'fs';
|
|
179
|
+
import * as path from 'path';
|
|
180
|
+
|
|
181
|
+
const MemoryInputSchema = z.object({
|
|
182
|
+
command: z.enum(['view', 'create', 'str_replace', 'insert', 'delete', 'rename']),
|
|
183
|
+
path: z.string(),
|
|
184
|
+
file_text: z.string().optional(),
|
|
185
|
+
view_range: z.tuple([z.number(), z.number()]).optional(),
|
|
186
|
+
old_str: z.string().optional(),
|
|
187
|
+
new_str: z.string().optional(),
|
|
188
|
+
insert_line: z.number().optional(),
|
|
189
|
+
insert_text: z.string().optional(),
|
|
190
|
+
old_path: z.string().optional(),
|
|
191
|
+
new_path: z.string().optional(),
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
export class MemoryTool implements Tool<z.infer<typeof MemoryInputSchema>> {
|
|
195
|
+
name = 'memory';
|
|
196
|
+
description = `Store and retrieve information across context resets.
|
|
197
|
+
Commands: view, create, str_replace, insert, delete, rename.
|
|
198
|
+
Use /memories as the base path.`;
|
|
199
|
+
|
|
200
|
+
parameters = MemoryInputSchema;
|
|
201
|
+
private baseDir: string;
|
|
202
|
+
|
|
203
|
+
constructor(baseDir?: string) {
|
|
204
|
+
this.baseDir = baseDir || path.join(process.env.HOME || '~', '.mycode', 'memories');
|
|
205
|
+
if (!fs.existsSync(this.baseDir)) {
|
|
206
|
+
fs.mkdirSync(this.baseDir, { recursive: true });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async execute(input: z.infer<typeof MemoryInputSchema>): Promise<ToolResult> {
|
|
211
|
+
const sanitizedPath = this.sanitizePath(input.path);
|
|
212
|
+
if (!sanitizedPath) {
|
|
213
|
+
return { error: `Invalid path: ${input.path}` };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
switch (input.command) {
|
|
217
|
+
case 'view':
|
|
218
|
+
return this.handleView(sanitizedPath, input.view_range);
|
|
219
|
+
case 'create':
|
|
220
|
+
return this.handleCreate(sanitizedPath, input.file_text || '');
|
|
221
|
+
case 'str_replace':
|
|
222
|
+
return this.handleStrReplace(sanitizedPath, input.old_str!, input.new_str!);
|
|
223
|
+
case 'insert':
|
|
224
|
+
return this.handleInsert(sanitizedPath, input.insert_line!, input.insert_text!);
|
|
225
|
+
case 'delete':
|
|
226
|
+
return this.handleDelete(sanitizedPath);
|
|
227
|
+
case 'rename':
|
|
228
|
+
return this.handleRename(sanitizedPath, this.sanitizePath(input.new_path!)!);
|
|
229
|
+
default:
|
|
230
|
+
return { error: `Unknown command: ${input.command}` };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
private sanitizePath(inputPath: string): string | null {
|
|
235
|
+
const normalized = inputPath.replace(/^\/memories\/?/, '');
|
|
236
|
+
const fullPath = path.join(this.baseDir, normalized);
|
|
237
|
+
const resolved = path.resolve(fullPath);
|
|
238
|
+
if (!resolved.startsWith(this.baseDir)) return null;
|
|
239
|
+
return resolved;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
private handleView(filePath: string, range?: [number, number]): ToolResult {
|
|
243
|
+
if (!fs.existsSync(filePath)) {
|
|
244
|
+
return { error: `Path does not exist: ${filePath}` };
|
|
245
|
+
}
|
|
246
|
+
const stat = fs.statSync(filePath);
|
|
247
|
+
if (stat.isDirectory()) {
|
|
248
|
+
const entries = fs.readdirSync(filePath);
|
|
249
|
+
return { content: `Directory contents:\n${entries.join('\n')}` };
|
|
250
|
+
}
|
|
251
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
252
|
+
const lines = content.split('\n');
|
|
253
|
+
const start = range ? range[0] - 1 : 0;
|
|
254
|
+
const end = range ? range[1] : lines.length;
|
|
255
|
+
return {
|
|
256
|
+
content: lines.slice(start, end).map((l, i) =>
|
|
257
|
+
`${String(start + i + 1).padStart(6)} ${l}`
|
|
258
|
+
).join('\n')
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private handleCreate(filePath: string, content: string): ToolResult {
|
|
263
|
+
if (fs.existsSync(filePath)) {
|
|
264
|
+
return { error: `File already exists: ${filePath}` };
|
|
265
|
+
}
|
|
266
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
267
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
268
|
+
return { content: `File created: ${filePath}` };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
private handleStrReplace(filePath: string, oldStr: string, newStr: string): ToolResult {
|
|
272
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
273
|
+
if (!content.includes(oldStr)) {
|
|
274
|
+
return { error: `String not found: ${oldStr}` };
|
|
275
|
+
}
|
|
276
|
+
fs.writeFileSync(filePath, content.replace(oldStr, newStr), 'utf-8');
|
|
277
|
+
return { content: 'File updated successfully' };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
private handleInsert(filePath: string, line: number, text: string): ToolResult {
|
|
281
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
282
|
+
const lines = content.split('\n');
|
|
283
|
+
lines.splice(line, 0, text);
|
|
284
|
+
fs.writeFileSync(filePath, lines.join('\n'), 'utf-8');
|
|
285
|
+
return { content: `Inserted at line ${line}` };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private handleDelete(filePath: string): ToolResult {
|
|
289
|
+
fs.rmSync(filePath, { recursive: true });
|
|
290
|
+
return { content: `Deleted: ${filePath}` };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
private handleRename(oldPath: string, newPath: string): ToolResult {
|
|
294
|
+
fs.renameSync(oldPath, newPath);
|
|
295
|
+
return { content: `Renamed to: ${newPath}` };
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### File Changes
|
|
301
|
+
|
|
302
|
+
| File | Action | Description |
|
|
303
|
+
|------|--------|-------------|
|
|
304
|
+
| `src/context/types.ts` | Create | Context management types |
|
|
305
|
+
| `src/context/context-manager.ts` | Create | Core context manager |
|
|
306
|
+
| `src/context/memory-tool.ts` | Create | Memory tool implementation |
|
|
307
|
+
| `src/context/tokenizer.ts` | Create | Token counting implementations |
|
|
308
|
+
| `src/context/compactor.ts` | Create | Conversation compaction logic |
|
|
309
|
+
| `src/context/index.ts` | Create | Module exports |
|
|
310
|
+
| `src/agent/agent.ts` | Modify | Integrate context management |
|
|
311
|
+
| `src/session/session-manager.ts` | Modify | Store context stats in session |
|
|
312
|
+
| `src/providers/types.ts` | Modify | Add token usage to responses |
|
|
313
|
+
|
|
314
|
+
## User Experience
|
|
315
|
+
|
|
316
|
+
### Status Display
|
|
317
|
+
Show context usage in the CLI status line:
|
|
318
|
+
|
|
319
|
+
```
|
|
320
|
+
mycode v0.2.0 | gpt-4o | Context: 45% (58K/128K tokens)
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Warning Messages
|
|
324
|
+
Warn users when approaching limits:
|
|
325
|
+
|
|
326
|
+
```
|
|
327
|
+
⚠️ Context usage at 80% (102K/128K tokens)
|
|
328
|
+
Consider using /compact to summarize older messages
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Auto-Compaction
|
|
332
|
+
Automatically compact when needed:
|
|
333
|
+
|
|
334
|
+
```
|
|
335
|
+
📦 Auto-compacting conversation (90% context usage)
|
|
336
|
+
Summarized 45 messages, saved 52K tokens
|
|
337
|
+
Conversation coherence preserved
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Manual Compaction
|
|
341
|
+
Users can manually compact:
|
|
342
|
+
|
|
343
|
+
```
|
|
344
|
+
> /compact
|
|
345
|
+
Compacting conversation...
|
|
346
|
+
Removed: 32 messages
|
|
347
|
+
Saved: 41K tokens
|
|
348
|
+
Summary: "Discussion about implementing authentication..."
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Context Command
|
|
352
|
+
View detailed context information:
|
|
353
|
+
|
|
354
|
+
```
|
|
355
|
+
> /context
|
|
356
|
+
Context Usage:
|
|
357
|
+
Current: 58,432 tokens (45.6%)
|
|
358
|
+
Maximum: 128,000 tokens
|
|
359
|
+
Messages: 47
|
|
360
|
+
Oldest: 2 hours ago
|
|
361
|
+
|
|
362
|
+
Breakdown:
|
|
363
|
+
System prompt: 1,200 tokens
|
|
364
|
+
Memory context: 800 tokens
|
|
365
|
+
Conversation: 56,432 tokens
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## Alternatives Considered
|
|
369
|
+
|
|
370
|
+
### Alternative 1: Simple Truncation
|
|
371
|
+
Just remove oldest messages when limit reached.
|
|
372
|
+
|
|
373
|
+
**Pros**: Simple implementation
|
|
374
|
+
**Cons**: Loses important context, poor UX
|
|
375
|
+
**Decision**: Rejected - Summarization preserves more value
|
|
376
|
+
|
|
377
|
+
### Alternative 2: External Summarization API
|
|
378
|
+
Use a separate API call to summarize.
|
|
379
|
+
|
|
380
|
+
**Pros**: Better summaries
|
|
381
|
+
**Cons**: Additional cost, latency
|
|
382
|
+
**Decision**: Partially adopted - Use same provider for summarization
|
|
383
|
+
|
|
384
|
+
### Alternative 3: No Auto-Compaction
|
|
385
|
+
Only manual compaction via command.
|
|
386
|
+
|
|
387
|
+
**Pros**: User control
|
|
388
|
+
**Cons**: Sessions crash unexpectedly
|
|
389
|
+
**Decision**: Rejected - Auto-compaction is essential for UX
|
|
390
|
+
|
|
391
|
+
## Security Considerations
|
|
392
|
+
|
|
393
|
+
1. **Token Counting Accuracy**: Inaccurate counting could lead to context overflow
|
|
394
|
+
2. **Compaction Privacy**: Summaries should not leak sensitive information
|
|
395
|
+
3. **Rate Limiting**: Compaction uses API calls, respect rate limits
|
|
396
|
+
4. **Caching**: Don't cache sensitive token counts
|
|
397
|
+
|
|
398
|
+
## Testing Strategy
|
|
399
|
+
|
|
400
|
+
1. **Unit Tests**:
|
|
401
|
+
- Token counting accuracy for each provider
|
|
402
|
+
- Limit detection for all supported models
|
|
403
|
+
- Compaction logic with various message types
|
|
404
|
+
|
|
405
|
+
2. **Integration Tests**:
|
|
406
|
+
- End-to-end context tracking
|
|
407
|
+
- Auto-compaction triggering
|
|
408
|
+
- Session persistence of context stats
|
|
409
|
+
|
|
410
|
+
3. **Load Testing**:
|
|
411
|
+
- Very long conversations
|
|
412
|
+
- Large tool outputs
|
|
413
|
+
- Rapid message sequences
|
|
414
|
+
|
|
415
|
+
## Migration Path
|
|
416
|
+
|
|
417
|
+
1. **Phase 1**: Token counting and limit detection
|
|
418
|
+
2. **Phase 2**: Context stats display and warnings
|
|
419
|
+
3. **Phase 3**: Manual compaction command
|
|
420
|
+
4. **Phase 4**: Auto-compaction implementation
|
|
421
|
+
5. **Phase 5**: Optimized compaction strategies
|
|
422
|
+
|
|
423
|
+
Existing sessions will work without context stats; stats begin tracking on first message after upgrade.
|
|
424
|
+
|
|
425
|
+
## References
|
|
426
|
+
|
|
427
|
+
- [Claude Code Context Management](https://code.claude.com/docs/en/context)
|
|
428
|
+
- [OpenAI Tokenizer (tiktoken)](https://github.com/openai/tiktoken)
|
|
429
|
+
- [Anthropic Token Counting](https://docs.anthropic.com/en/docs/tokens)
|