Skip to content

Commit dc3127b

Browse files
committed
Merge remote-tracking branch 'origin/main' into feature/issue-341-llm-content-extraction
2 parents 4fcc98e + 5794367 commit dc3127b

File tree

11 files changed

+266
-33
lines changed

11 files changed

+266
-33
lines changed

mycoder.config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ export default {
1919
// executablePath: null, // e.g., '/path/to/chrome'
2020
},
2121

22+
// Sub-agent workflow mode: 'disabled' (default), 'sync' (experimental), or 'async' (experimental)
23+
subAgentMode: 'disabled',
24+
2225
// Model settings
2326
//provider: 'anthropic',
2427
//model: 'claude-3-7-sonnet-20250219',

packages/agent/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
# [mycoder-agent-v1.7.0](https://github.com/drivecore/mycoder/compare/mycoder-agent-v1.6.0...mycoder-agent-v1.7.0) (2025-03-21)
2+
3+
### Bug Fixes
4+
5+
- Fix TypeScript errors and tests for message compaction feature ([d4f1fb5](https://github.com/drivecore/mycoder/commit/d4f1fb5d197e623bf98f2221352f9132dcb3e5de))
6+
7+
### Features
8+
9+
- Add automatic compaction of historical messages for agents ([a5caf46](https://github.com/drivecore/mycoder/commit/a5caf464a0a8dca925c7b46023ebde4727e211f8)), closes [#338](https://github.com/drivecore/mycoder/issues/338)
10+
- Improve message compaction with proactive suggestions ([6276bc0](https://github.com/drivecore/mycoder/commit/6276bc0bc5fa27c4f1e9be61ff4375690ad04c62))
11+
112
# [mycoder-agent-v1.6.0](https://github.com/drivecore/mycoder/compare/mycoder-agent-v1.5.0...mycoder-agent-v1.6.0) (2025-03-21)
213

314
### Features

packages/agent/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mycoder-agent",
3-
"version": "1.6.0",
3+
"version": "1.7.0",
44
"description": "Agent module for mycoder - an AI-powered software development assistant",
55
"type": "module",
66
"main": "dist/index.js",

packages/agent/src/core/llm/providers/anthropic.ts

Lines changed: 89 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,8 @@ import {
1212
ProviderOptions,
1313
} from '../types.js';
1414

15-
// Define model context window sizes for Anthropic models
16-
const ANTHROPIC_MODEL_LIMITS: Record<string, number> = {
17-
default: 200000,
18-
'claude-3-7-sonnet-20250219': 200000,
19-
'claude-3-7-sonnet-latest': 200000,
20-
'claude-3-5-sonnet-20241022': 200000,
21-
'claude-3-5-sonnet-latest': 200000,
22-
'claude-3-haiku-20240307': 200000,
23-
'claude-3-opus-20240229': 200000,
24-
'claude-3-sonnet-20240229': 200000,
25-
'claude-2.1': 100000,
26-
'claude-2.0': 100000,
27-
'claude-instant-1.2': 100000,
28-
};
15+
// Cache for model context window sizes
16+
const modelContextWindowCache: Record<string, number> = {};
2917

3018
/**
3119
* Anthropic-specific options
@@ -96,15 +84,27 @@ function addCacheControlToMessages(
9684
});
9785
}
9886

99-
function tokenUsageFromMessage(message: Anthropic.Message, model: string) {
87+
function tokenUsageFromMessage(
88+
message: Anthropic.Message,
89+
model: string,
90+
contextWindow?: number,
91+
) {
10092
const usage = new TokenUsage();
10193
usage.input = message.usage.input_tokens;
10294
usage.cacheWrites = message.usage.cache_creation_input_tokens ?? 0;
10395
usage.cacheReads = message.usage.cache_read_input_tokens ?? 0;
10496
usage.output = message.usage.output_tokens;
10597

10698
const totalTokens = usage.input + usage.output;
107-
const maxTokens = ANTHROPIC_MODEL_LIMITS[model] || 100000; // Default fallback
99+
100+
// Use provided context window or fallback to cached value
101+
const maxTokens = contextWindow || modelContextWindowCache[model];
102+
103+
if (!maxTokens) {
104+
throw new Error(
105+
`Context window size not available for model: ${model}. Make sure to initialize the model properly.`,
106+
);
107+
}
108108

109109
return {
110110
usage,
@@ -123,6 +123,7 @@ export class AnthropicProvider implements LLMProvider {
123123
private client: Anthropic;
124124
private apiKey: string;
125125
private baseUrl?: string;
126+
private modelContextWindow?: number;
126127

127128
constructor(model: string, options: AnthropicOptions = {}) {
128129
this.model = model;
@@ -138,6 +139,73 @@ export class AnthropicProvider implements LLMProvider {
138139
apiKey: this.apiKey,
139140
...(this.baseUrl && { baseURL: this.baseUrl }),
140141
});
142+
143+
// Initialize model context window detection
144+
// This is async but we don't need to await it here
145+
// If it fails, an error will be thrown when the model is used
146+
this.initializeModelContextWindow().catch((error) => {
147+
console.error(
148+
`Failed to initialize model context window: ${error.message}. The model will not work until context window information is available.`,
149+
);
150+
});
151+
}
152+
153+
/**
154+
* Fetches the model context window size from the Anthropic API
155+
*
156+
* @returns The context window size
157+
* @throws Error if the context window size cannot be determined
158+
*/
159+
private async initializeModelContextWindow(): Promise<number> {
160+
try {
161+
const response = await this.client.models.list();
162+
163+
if (!response?.data || !Array.isArray(response.data)) {
164+
throw new Error(
165+
`Invalid response from models.list() for ${this.model}`,
166+
);
167+
}
168+
169+
// Try to find the exact model
170+
let model = response.data.find((m) => m.id === this.model);
171+
172+
// If not found, try to find a model that starts with the same name
173+
// This helps with model aliases like 'claude-3-sonnet-latest'
174+
if (!model) {
175+
// Split by '-latest' or '-20' to get the base model name
176+
const parts = this.model.split('-latest');
177+
const modelPrefix =
178+
parts.length > 1 ? parts[0] : this.model.split('-20')[0];
179+
180+
if (modelPrefix) {
181+
model = response.data.find((m) => m.id.startsWith(modelPrefix));
182+
183+
if (model) {
184+
console.info(
185+
`Model ${this.model} not found, using ${model.id} for context window size`,
186+
);
187+
}
188+
}
189+
}
190+
191+
// Using type assertion to access context_window property
192+
// The Anthropic API returns context_window but it may not be in the TypeScript definitions
193+
if (model && 'context_window' in model) {
194+
const contextWindow = (model as any).context_window;
195+
this.modelContextWindow = contextWindow;
196+
// Cache the result for future use
197+
modelContextWindowCache[this.model] = contextWindow;
198+
return contextWindow;
199+
} else {
200+
throw new Error(
201+
`No context window information found for model: ${this.model}`,
202+
);
203+
}
204+
} catch (error) {
205+
throw new Error(
206+
`Failed to determine context window size for model ${this.model}: ${(error as Error).message}`,
207+
);
208+
}
141209
}
142210

143211
/**
@@ -198,7 +266,11 @@ export class AnthropicProvider implements LLMProvider {
198266
};
199267
});
200268

201-
const tokenInfo = tokenUsageFromMessage(response, this.model);
269+
const tokenInfo = tokenUsageFromMessage(
270+
response,
271+
this.model,
272+
this.modelContextWindow,
273+
);
202274

203275
return {
204276
text: content,

packages/agent/src/tools/getTools.ts

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Tool } from '../core/types.js';
33

44
// Import tools
55
import { agentDoneTool } from './agent/agentDone.js';
6+
import { agentExecuteTool } from './agent/agentExecute.js';
67
import { agentMessageTool } from './agent/agentMessage.js';
78
import { agentStartTool } from './agent/agentStart.js';
89
import { listAgentsTool } from './agent/listAgents.js';
@@ -21,38 +22,52 @@ import { textEditorTool } from './textEditor/textEditor.js';
2122

2223
// Import these separately to avoid circular dependencies
2324

25+
/**
26+
* Sub-agent workflow modes
27+
* - disabled: No sub-agent tools are available
28+
* - sync: Parent agent waits for sub-agent completion before continuing
29+
* - async: Sub-agents run in the background, parent can check status and provide guidance
30+
*/
31+
export type SubAgentMode = 'disabled' | 'sync' | 'async';
32+
2433
interface GetToolsOptions {
2534
userPrompt?: boolean;
2635
mcpConfig?: McpConfig;
36+
subAgentMode?: SubAgentMode;
2737
}
2838

2939
export function getTools(options?: GetToolsOptions): Tool[] {
3040
const userPrompt = options?.userPrompt !== false; // Default to true if not specified
3141
const mcpConfig = options?.mcpConfig || { servers: [], defaultResources: [] };
42+
const subAgentMode = options?.subAgentMode || 'disabled'; // Default to disabled mode
3243

3344
// Force cast to Tool type to avoid TypeScript issues
3445
const tools: Tool[] = [
3546
textEditorTool as unknown as Tool,
36-
37-
//agentExecuteTool as unknown as Tool,
38-
agentStartTool as unknown as Tool,
39-
agentMessageTool as unknown as Tool,
40-
listAgentsTool as unknown as Tool,
41-
agentDoneTool as unknown as Tool,
42-
4347
fetchTool as unknown as Tool,
44-
4548
shellStartTool as unknown as Tool,
4649
shellMessageTool as unknown as Tool,
4750
listShellsTool as unknown as Tool,
48-
4951
sessionStartTool as unknown as Tool,
5052
sessionMessageTool as unknown as Tool,
5153
listSessionsTool as unknown as Tool,
52-
5354
waitTool as unknown as Tool,
5455
];
5556

57+
// Add agent tools based on the configured mode
58+
if (subAgentMode === 'sync') {
59+
// For sync mode, include only agentExecute and agentDone
60+
tools.push(agentExecuteTool as unknown as Tool);
61+
tools.push(agentDoneTool as unknown as Tool);
62+
} else if (subAgentMode === 'async') {
63+
// For async mode, include all async agent tools
64+
tools.push(agentStartTool as unknown as Tool);
65+
tools.push(agentMessageTool as unknown as Tool);
66+
tools.push(listAgentsTool as unknown as Tool);
67+
tools.push(agentDoneTool as unknown as Tool);
68+
}
69+
// For 'disabled' mode, no agent tools are added
70+
5671
// Only include user interaction tools if enabled
5772
if (userPrompt) {
5873
tools.push(userPromptTool as unknown as Tool);

packages/cli/src/commands/$default.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ export async function executePrompt(
156156
const tools = getTools({
157157
userPrompt: config.userPrompt,
158158
mcpConfig: config.mcp,
159+
subAgentMode: config.subAgentMode,
159160
});
160161

161162
// Error handling

packages/cli/src/commands/tools.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export const command: CommandModule<object, ToolsArgs> = {
4141
describe: 'List all available tools and their capabilities',
4242
handler: () => {
4343
try {
44-
const tools = getTools();
44+
const tools = getTools({ subAgentMode: 'disabled' });
4545

4646
console.log('Available Tools:\n');
4747

packages/cli/src/options.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export type SharedOptions = {
1212
readonly profile?: boolean;
1313
readonly userPrompt?: boolean;
1414
readonly upgradeCheck?: boolean;
15+
readonly subAgentMode?: 'disabled' | 'sync' | 'async';
1516
};
1617

1718
export const sharedOptions = {
@@ -75,4 +76,10 @@ export const sharedOptions = {
7576
type: 'boolean',
7677
description: 'Disable version upgrade check (for automated/remote usage)',
7778
} as const,
79+
80+
subAgentMode: {
81+
type: 'string',
82+
description: 'Sub-agent workflow mode (disabled, sync, or async)',
83+
choices: ['disabled', 'sync', 'async'],
84+
} as const,
7885
};

packages/cli/src/settings/config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export type Config = {
1818
upgradeCheck: boolean;
1919
tokenUsage: boolean;
2020
interactive: boolean;
21+
subAgentMode?: 'disabled' | 'sync' | 'async';
2122

2223
baseUrl?: string;
2324

@@ -73,6 +74,7 @@ const defaultConfig: Config = {
7374
upgradeCheck: true,
7475
tokenUsage: false,
7576
interactive: false,
77+
subAgentMode: 'disabled',
7678

7779
// MCP configuration
7880
mcp: {
@@ -95,6 +97,7 @@ export const getConfigFromArgv = (argv: ArgumentsCamelCase<SharedOptions>) => {
9597
upgradeCheck: argv.upgradeCheck,
9698
tokenUsage: argv.tokenUsage,
9799
interactive: argv.interactive,
100+
subAgentMode: argv.subAgentMode,
98101
};
99102
};
100103

packages/docs/docs/usage/configuration.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,11 @@ export default {
114114

115115
### Behavior Customization
116116

117-
| Option | Description | Possible Values | Default |
118-
| -------------- | ------------------------------ | --------------- | ------- |
119-
| `customPrompt` | Custom instructions for the AI | Any string | `""` |
120-
| `githubMode` | Enable GitHub integration | `true`, `false` | `false` |
117+
| Option | Description | Possible Values | Default |
118+
| -------------- | ------------------------------ | --------------------------------------------------------------- | ------------ |
119+
| `customPrompt` | Custom instructions for the AI | Any string | `""` |
120+
| `githubMode` | Enable GitHub integration | `true`, `false` | `false` |
121+
| `subAgentMode` | Sub-agent workflow mode | `'disabled'`, `'sync'` (experimental), `'async'` (experimental) | `'disabled'` |
121122

122123
Example:
123124

@@ -200,5 +201,6 @@ export default {
200201
'Always prioritize readability and simplicity in your code. Prefer TypeScript over JavaScript when possible.',
201202
profile: true,
202203
tokenUsage: true,
204+
subAgentMode: 'disabled', // Options: 'disabled', 'sync' (experimental), 'async' (experimental)
203205
};
204206
```

0 commit comments

Comments
 (0)