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,415 @@
|
|
|
1
|
+
# Proposal: Parallel Tool Execution
|
|
2
|
+
|
|
3
|
+
- **Proposal ID**: 0018
|
|
4
|
+
- **Author**: mycode team
|
|
5
|
+
- **Status**: Draft
|
|
6
|
+
- **Created**: 2025-01-15
|
|
7
|
+
- **Updated**: 2025-01-15
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
Implement parallel tool execution to allow multiple independent tool calls to run concurrently within a single agent turn. This reduces latency and improves efficiency when the agent needs to perform multiple non-dependent operations.
|
|
12
|
+
|
|
13
|
+
## Motivation
|
|
14
|
+
|
|
15
|
+
Currently, mycode executes tools sequentially, one after another:
|
|
16
|
+
|
|
17
|
+
1. **High latency**: 3 file reads take 3x the time
|
|
18
|
+
2. **Wasted time**: Network requests wait for each other
|
|
19
|
+
3. **Poor utilization**: CPU/IO idle between calls
|
|
20
|
+
4. **Slow exploration**: Searching multiple directories is slow
|
|
21
|
+
5. **Suboptimal UX**: User waits longer than necessary
|
|
22
|
+
|
|
23
|
+
Parallel execution enables concurrent tool operations for faster results.
|
|
24
|
+
|
|
25
|
+
## Claude Code Reference
|
|
26
|
+
|
|
27
|
+
Claude Code supports parallel tool calls natively:
|
|
28
|
+
|
|
29
|
+
### From System Prompt
|
|
30
|
+
```
|
|
31
|
+
You can call multiple tools in a single response. When multiple independent
|
|
32
|
+
pieces of information are requested and all commands are likely to succeed,
|
|
33
|
+
run multiple tool calls in parallel for optimal performance.
|
|
34
|
+
|
|
35
|
+
However, if some tool calls depend on previous calls to inform dependent
|
|
36
|
+
values, do NOT call these tools in parallel and instead call them sequentially.
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Parallel Call Example
|
|
40
|
+
Multiple tool calls in single response:
|
|
41
|
+
```
|
|
42
|
+
Agent: I'll read both configuration files to understand the setup.
|
|
43
|
+
[Read: src/config.ts]
|
|
44
|
+
[Read: src/settings.json]
|
|
45
|
+
[Glob: **/*.test.ts]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
All three execute concurrently, results return together.
|
|
49
|
+
|
|
50
|
+
### Key Rules
|
|
51
|
+
- Independent calls: Run in parallel
|
|
52
|
+
- Dependent calls: Run sequentially
|
|
53
|
+
- Never use placeholders for dependent values
|
|
54
|
+
- Agent decides based on data dependencies
|
|
55
|
+
|
|
56
|
+
## Detailed Design
|
|
57
|
+
|
|
58
|
+
### API Design
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
// src/agent/parallel-executor.ts
|
|
62
|
+
interface ToolCall {
|
|
63
|
+
id: string;
|
|
64
|
+
name: string;
|
|
65
|
+
input: unknown;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface ToolResult {
|
|
69
|
+
id: string;
|
|
70
|
+
name: string;
|
|
71
|
+
success: boolean;
|
|
72
|
+
output: string;
|
|
73
|
+
error?: string;
|
|
74
|
+
duration_ms: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface ParallelExecutionConfig {
|
|
78
|
+
max_concurrent: number; // Max concurrent executions
|
|
79
|
+
timeout_per_tool: number; // Per-tool timeout
|
|
80
|
+
fail_fast: boolean; // Stop on first error
|
|
81
|
+
retry_failed: boolean; // Retry failed tools once
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const DEFAULT_CONFIG: ParallelExecutionConfig = {
|
|
85
|
+
max_concurrent: 10,
|
|
86
|
+
timeout_per_tool: 30000,
|
|
87
|
+
fail_fast: false,
|
|
88
|
+
retry_failed: false
|
|
89
|
+
};
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Implementation Approach
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// src/agent/parallel-executor.ts
|
|
96
|
+
class ParallelToolExecutor {
|
|
97
|
+
private registry: ToolRegistry;
|
|
98
|
+
private config: ParallelExecutionConfig;
|
|
99
|
+
private semaphore: Semaphore;
|
|
100
|
+
|
|
101
|
+
constructor(registry: ToolRegistry, config?: Partial<ParallelExecutionConfig>) {
|
|
102
|
+
this.registry = registry;
|
|
103
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
104
|
+
this.semaphore = new Semaphore(this.config.max_concurrent);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async executeParallel(
|
|
108
|
+
calls: ToolCall[],
|
|
109
|
+
context: ToolContext
|
|
110
|
+
): Promise<ToolResult[]> {
|
|
111
|
+
const results: ToolResult[] = [];
|
|
112
|
+
|
|
113
|
+
// Execute all calls concurrently with semaphore
|
|
114
|
+
const promises = calls.map(call =>
|
|
115
|
+
this.executeWithSemaphore(call, context)
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// Wait for all (unless fail_fast)
|
|
119
|
+
if (this.config.fail_fast) {
|
|
120
|
+
const settled = await Promise.allSettled(promises);
|
|
121
|
+
for (const result of settled) {
|
|
122
|
+
if (result.status === 'fulfilled') {
|
|
123
|
+
results.push(result.value);
|
|
124
|
+
if (!result.value.success) break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
const allResults = await Promise.all(promises);
|
|
129
|
+
results.push(...allResults);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return results;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private async executeWithSemaphore(
|
|
136
|
+
call: ToolCall,
|
|
137
|
+
context: ToolContext
|
|
138
|
+
): Promise<ToolResult> {
|
|
139
|
+
await this.semaphore.acquire();
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
return await this.executeSingle(call, context);
|
|
143
|
+
} finally {
|
|
144
|
+
this.semaphore.release();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private async executeSingle(
|
|
149
|
+
call: ToolCall,
|
|
150
|
+
context: ToolContext
|
|
151
|
+
): Promise<ToolResult> {
|
|
152
|
+
const start = Date.now();
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
// Wrap with timeout
|
|
156
|
+
const result = await Promise.race([
|
|
157
|
+
this.registry.execute(call.name, call.input, context),
|
|
158
|
+
this.timeout(this.config.timeout_per_tool)
|
|
159
|
+
]);
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
id: call.id,
|
|
163
|
+
name: call.name,
|
|
164
|
+
success: result.success,
|
|
165
|
+
output: result.output || '',
|
|
166
|
+
error: result.error,
|
|
167
|
+
duration_ms: Date.now() - start
|
|
168
|
+
};
|
|
169
|
+
} catch (error) {
|
|
170
|
+
return {
|
|
171
|
+
id: call.id,
|
|
172
|
+
name: call.name,
|
|
173
|
+
success: false,
|
|
174
|
+
output: '',
|
|
175
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
176
|
+
duration_ms: Date.now() - start
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private timeout(ms: number): Promise<never> {
|
|
182
|
+
return new Promise((_, reject) =>
|
|
183
|
+
setTimeout(() => reject(new Error('Tool execution timeout')), ms)
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Agent Loop Integration
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
// Updated src/agent/agent.ts
|
|
193
|
+
class Agent {
|
|
194
|
+
private parallelExecutor: ParallelToolExecutor;
|
|
195
|
+
|
|
196
|
+
async *run(messages: Message[]): AsyncGenerator<AgentEvent> {
|
|
197
|
+
while (true) {
|
|
198
|
+
const response = await this.provider.complete({
|
|
199
|
+
messages,
|
|
200
|
+
tools: this.registry.getToolDefinitions()
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Yield text content
|
|
204
|
+
for (const block of response.content) {
|
|
205
|
+
if (block.type === 'text') {
|
|
206
|
+
yield { type: 'text', content: block.text };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Check for tool calls
|
|
211
|
+
const toolCalls = response.content.filter(b => b.type === 'tool_use');
|
|
212
|
+
|
|
213
|
+
if (toolCalls.length === 0) {
|
|
214
|
+
yield { type: 'done', reason: response.stopReason };
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Execute tools in parallel
|
|
219
|
+
yield { type: 'tools_start', count: toolCalls.length };
|
|
220
|
+
|
|
221
|
+
const results = await this.parallelExecutor.executeParallel(
|
|
222
|
+
toolCalls.map(tc => ({
|
|
223
|
+
id: tc.id,
|
|
224
|
+
name: tc.name,
|
|
225
|
+
input: tc.input
|
|
226
|
+
})),
|
|
227
|
+
{ cwd: this.cwd, signal: this.signal }
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
// Yield individual results
|
|
231
|
+
for (const result of results) {
|
|
232
|
+
yield {
|
|
233
|
+
type: 'tool_result',
|
|
234
|
+
id: result.id,
|
|
235
|
+
name: result.name,
|
|
236
|
+
success: result.success,
|
|
237
|
+
output: result.output,
|
|
238
|
+
duration_ms: result.duration_ms
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Add results to messages
|
|
243
|
+
messages.push({
|
|
244
|
+
role: 'assistant',
|
|
245
|
+
content: response.content
|
|
246
|
+
});
|
|
247
|
+
messages.push({
|
|
248
|
+
role: 'user',
|
|
249
|
+
content: results.map(r => ({
|
|
250
|
+
type: 'tool_result',
|
|
251
|
+
tool_use_id: r.id,
|
|
252
|
+
content: r.success ? r.output : `Error: ${r.error}`
|
|
253
|
+
}))
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Semaphore Implementation
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
// src/utils/semaphore.ts
|
|
264
|
+
class Semaphore {
|
|
265
|
+
private permits: number;
|
|
266
|
+
private waiting: (() => void)[] = [];
|
|
267
|
+
|
|
268
|
+
constructor(permits: number) {
|
|
269
|
+
this.permits = permits;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
async acquire(): Promise<void> {
|
|
273
|
+
if (this.permits > 0) {
|
|
274
|
+
this.permits--;
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return new Promise(resolve => {
|
|
279
|
+
this.waiting.push(resolve);
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
release(): void {
|
|
284
|
+
if (this.waiting.length > 0) {
|
|
285
|
+
const next = this.waiting.shift()!;
|
|
286
|
+
next();
|
|
287
|
+
} else {
|
|
288
|
+
this.permits++;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### File Changes
|
|
295
|
+
|
|
296
|
+
| File | Action | Description |
|
|
297
|
+
|------|--------|-------------|
|
|
298
|
+
| `src/agent/parallel-executor.ts` | Create | Parallel execution engine |
|
|
299
|
+
| `src/agent/agent.ts` | Modify | Integrate parallel execution |
|
|
300
|
+
| `src/utils/semaphore.ts` | Create | Concurrency control |
|
|
301
|
+
| `src/agent/types.ts` | Modify | Add parallel-related events |
|
|
302
|
+
|
|
303
|
+
## User Experience
|
|
304
|
+
|
|
305
|
+
### Parallel Execution Display
|
|
306
|
+
```
|
|
307
|
+
Agent: I'll check multiple files to understand the project structure.
|
|
308
|
+
|
|
309
|
+
┌─ Parallel Tool Execution (4 calls) ───────────────┐
|
|
310
|
+
│ [1/4] Read: src/index.ts ✓ (45ms) │
|
|
311
|
+
│ [2/4] Read: package.json ✓ (32ms) │
|
|
312
|
+
│ [3/4] Glob: src/**/*.ts ✓ (78ms) │
|
|
313
|
+
│ [4/4] Grep: "TODO" ✓ (156ms) │
|
|
314
|
+
├───────────────────────────────────────────────────┤
|
|
315
|
+
│ Total: 4/4 succeeded in 156ms (parallel) │
|
|
316
|
+
│ Sequential would have taken: 311ms │
|
|
317
|
+
└───────────────────────────────────────────────────┘
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Mixed Results
|
|
321
|
+
```
|
|
322
|
+
┌─ Parallel Tool Execution (3 calls) ───────────────┐
|
|
323
|
+
│ [1/3] Read: src/config.ts ✓ (38ms) │
|
|
324
|
+
│ [2/3] Read: missing.ts ✗ (12ms) │
|
|
325
|
+
│ Error: File not found │
|
|
326
|
+
│ [3/3] Bash: npm --version ✓ (234ms) │
|
|
327
|
+
├───────────────────────────────────────────────────┤
|
|
328
|
+
│ Total: 2/3 succeeded, 1 failed │
|
|
329
|
+
└───────────────────────────────────────────────────┘
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Sequential Fallback
|
|
333
|
+
```
|
|
334
|
+
Agent: These operations depend on each other, so I'll run them sequentially.
|
|
335
|
+
|
|
336
|
+
[Read: package.json]
|
|
337
|
+
→ Found dependencies list
|
|
338
|
+
|
|
339
|
+
[Bash: npm install lodash]
|
|
340
|
+
→ Using package info from previous read
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## Alternatives Considered
|
|
344
|
+
|
|
345
|
+
### Alternative 1: Always Sequential
|
|
346
|
+
Keep current sequential behavior.
|
|
347
|
+
|
|
348
|
+
**Pros**: Simpler, predictable
|
|
349
|
+
**Cons**: Slow, wastes time
|
|
350
|
+
**Decision**: Rejected - Performance is important
|
|
351
|
+
|
|
352
|
+
### Alternative 2: Unlimited Parallelism
|
|
353
|
+
No concurrency limits.
|
|
354
|
+
|
|
355
|
+
**Pros**: Maximum speed
|
|
356
|
+
**Cons**: Resource exhaustion, rate limiting
|
|
357
|
+
**Decision**: Rejected - Need semaphore control
|
|
358
|
+
|
|
359
|
+
### Alternative 3: Provider-Side Parallelism
|
|
360
|
+
Let LLM provider handle parallel calls.
|
|
361
|
+
|
|
362
|
+
**Pros**: Simpler client
|
|
363
|
+
**Cons**: Not all providers support it
|
|
364
|
+
**Decision**: Rejected - Need client-side control
|
|
365
|
+
|
|
366
|
+
## Security Considerations
|
|
367
|
+
|
|
368
|
+
1. **Resource Limits**: Cap concurrent operations
|
|
369
|
+
2. **Timeout Enforcement**: Per-tool and total timeouts
|
|
370
|
+
3. **Memory Management**: Stream large outputs
|
|
371
|
+
4. **Rate Limiting**: Respect external API limits
|
|
372
|
+
5. **Cancellation**: Support AbortSignal propagation
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
const SAFETY_LIMITS = {
|
|
376
|
+
maxConcurrent: 10,
|
|
377
|
+
maxTotalCalls: 50,
|
|
378
|
+
perToolTimeout: 30000,
|
|
379
|
+
totalTimeout: 120000
|
|
380
|
+
};
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## Testing Strategy
|
|
384
|
+
|
|
385
|
+
1. **Unit Tests**:
|
|
386
|
+
- Semaphore behavior
|
|
387
|
+
- Parallel vs sequential timing
|
|
388
|
+
- Error handling
|
|
389
|
+
- Timeout enforcement
|
|
390
|
+
|
|
391
|
+
2. **Integration Tests**:
|
|
392
|
+
- Multiple tool types
|
|
393
|
+
- Mixed success/failure
|
|
394
|
+
- Resource cleanup
|
|
395
|
+
|
|
396
|
+
3. **Performance Tests**:
|
|
397
|
+
- Speedup measurement
|
|
398
|
+
- Memory usage
|
|
399
|
+
- Concurrent limits
|
|
400
|
+
|
|
401
|
+
## Migration Path
|
|
402
|
+
|
|
403
|
+
1. **Phase 1**: ParallelToolExecutor with semaphore
|
|
404
|
+
2. **Phase 2**: Agent loop integration
|
|
405
|
+
3. **Phase 3**: CLI display updates
|
|
406
|
+
4. **Phase 4**: Configuration options
|
|
407
|
+
5. **Phase 5**: Performance telemetry
|
|
408
|
+
|
|
409
|
+
No breaking changes - sequential behavior preserved as default.
|
|
410
|
+
|
|
411
|
+
## References
|
|
412
|
+
|
|
413
|
+
- [Promise.all() - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all)
|
|
414
|
+
- [Semaphore Pattern](https://en.wikipedia.org/wiki/Semaphore_(programming))
|
|
415
|
+
- [Claude Code Tool Calling](https://code.claude.com/docs/en/tools)
|