ak-claude 0.0.2 → 0.0.5
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/GUIDE.md +16 -0
- package/README.md +1 -0
- package/index.cjs +108 -49
- package/message.js +1 -1
- package/package.json +1 -1
- package/tool-agent.js +123 -50
- package/types.d.ts +3 -0
package/GUIDE.md
CHANGED
|
@@ -430,6 +430,22 @@ const agent = new ToolAgent({
|
|
|
430
430
|
});
|
|
431
431
|
```
|
|
432
432
|
|
|
433
|
+
### Parallel Tool Execution
|
|
434
|
+
|
|
435
|
+
Control whether tool calls within a round execute in parallel or sequentially:
|
|
436
|
+
|
|
437
|
+
```javascript
|
|
438
|
+
const agent = new ToolAgent({
|
|
439
|
+
tools: [...],
|
|
440
|
+
toolExecutor: myExecutor,
|
|
441
|
+
parallelToolCalls: true, // default: unlimited parallel execution
|
|
442
|
+
// parallelToolCalls: false, // sequential execution
|
|
443
|
+
// parallelToolCalls: 3, // max 3 concurrent tool executions
|
|
444
|
+
});
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
When the model returns multiple tool calls in a single response, parallel execution runs them concurrently — significantly faster for I/O-bound tools (HTTP requests, database queries, etc.).
|
|
448
|
+
|
|
433
449
|
### Streaming
|
|
434
450
|
|
|
435
451
|
Stream the agent's output in real-time --- useful for showing progress in a UI:
|
package/README.md
CHANGED
|
@@ -561,6 +561,7 @@ All classes (except AgentQuery) accept `BaseClaudeOptions`:
|
|
|
561
561
|
| `onBeforeExecution` | function | — | `async (toolName, args) => boolean` — gate execution |
|
|
562
562
|
| `toolChoice` | object | — | Tool choice config (`auto`, `any`, `tool`, `none`) |
|
|
563
563
|
| `disableParallelToolUse` | boolean | `false` | Force sequential tool calls |
|
|
564
|
+
| `parallelToolCalls` | boolean \| number | `true` | Parallel tool execution: `false` = sequential, `true` = unlimited, number = concurrency limit |
|
|
564
565
|
|
|
565
566
|
### CodeAgent-Specific
|
|
566
567
|
|
package/index.cjs
CHANGED
|
@@ -1280,7 +1280,7 @@ No markdown code blocks, no preamble text.`;
|
|
|
1280
1280
|
};
|
|
1281
1281
|
if (this._isStructured) {
|
|
1282
1282
|
try {
|
|
1283
|
-
if (this._responseSchema) {
|
|
1283
|
+
if (this._responseSchema && !this.vertexai) {
|
|
1284
1284
|
result.data = JSON.parse(text);
|
|
1285
1285
|
} else {
|
|
1286
1286
|
result.data = extractJSON(text);
|
|
@@ -1309,6 +1309,24 @@ No markdown code blocks, no preamble text.`;
|
|
|
1309
1309
|
var message_default = Message;
|
|
1310
1310
|
|
|
1311
1311
|
// tool-agent.js
|
|
1312
|
+
async function runWithConcurrency(tasks, concurrency) {
|
|
1313
|
+
if (concurrency === Infinity) return Promise.all(tasks.map((t) => t()));
|
|
1314
|
+
if (concurrency === 1) {
|
|
1315
|
+
const results2 = [];
|
|
1316
|
+
for (const t of tasks) results2.push(await t());
|
|
1317
|
+
return results2;
|
|
1318
|
+
}
|
|
1319
|
+
const results = new Array(tasks.length);
|
|
1320
|
+
let next = 0;
|
|
1321
|
+
async function worker() {
|
|
1322
|
+
while (next < tasks.length) {
|
|
1323
|
+
const i = next++;
|
|
1324
|
+
results[i] = await tasks[i]();
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, tasks.length) }, () => worker()));
|
|
1328
|
+
return results;
|
|
1329
|
+
}
|
|
1312
1330
|
var ToolAgent = class extends base_default {
|
|
1313
1331
|
/**
|
|
1314
1332
|
* @param {ToolAgentOptions} [options={}]
|
|
@@ -1332,6 +1350,8 @@ var ToolAgent = class extends base_default {
|
|
|
1332
1350
|
}
|
|
1333
1351
|
this.toolChoice = options.toolChoice ?? void 0;
|
|
1334
1352
|
this.disableParallelToolUse = options.disableParallelToolUse ?? false;
|
|
1353
|
+
this.parallelToolCalls = options.parallelToolCalls ?? true;
|
|
1354
|
+
this._concurrency = this.parallelToolCalls === true ? Infinity : this.parallelToolCalls === false ? 1 : this.parallelToolCalls;
|
|
1335
1355
|
this.maxToolRounds = options.maxToolRounds || 10;
|
|
1336
1356
|
this.onToolCall = options.onToolCall || null;
|
|
1337
1357
|
this.onBeforeExecution = options.onBeforeExecution || null;
|
|
@@ -1373,8 +1393,7 @@ var ToolAgent = class extends base_default {
|
|
|
1373
1393
|
if (response.stop_reason !== "tool_use") break;
|
|
1374
1394
|
const toolUseBlocks = response.content.filter((b) => b.type === "tool_use");
|
|
1375
1395
|
if (toolUseBlocks.length === 0) break;
|
|
1376
|
-
const
|
|
1377
|
-
for (const block of toolUseBlocks) {
|
|
1396
|
+
const tasks = toolUseBlocks.map((block) => async () => {
|
|
1378
1397
|
if (this.onToolCall) {
|
|
1379
1398
|
try {
|
|
1380
1399
|
this.onToolCall(block.name, block.input);
|
|
@@ -1387,13 +1406,7 @@ var ToolAgent = class extends base_default {
|
|
|
1387
1406
|
const allowed = await this.onBeforeExecution(block.name, block.input);
|
|
1388
1407
|
if (allowed === false) {
|
|
1389
1408
|
const result2 = { error: "Execution denied by onBeforeExecution callback" };
|
|
1390
|
-
|
|
1391
|
-
toolResults.push({
|
|
1392
|
-
type: "tool_result",
|
|
1393
|
-
tool_use_id: block.id,
|
|
1394
|
-
content: JSON.stringify(result2)
|
|
1395
|
-
});
|
|
1396
|
-
continue;
|
|
1409
|
+
return { toolCall: { name: block.name, args: block.input, result: result2 }, toolResult: { type: "tool_result", tool_use_id: block.id, content: JSON.stringify(result2) } };
|
|
1397
1410
|
}
|
|
1398
1411
|
} catch (e) {
|
|
1399
1412
|
logger_default.warn(`onBeforeExecution callback error: ${e.message}`);
|
|
@@ -1406,13 +1419,14 @@ var ToolAgent = class extends base_default {
|
|
|
1406
1419
|
logger_default.warn(`Tool ${block.name} failed: ${err.message}`);
|
|
1407
1420
|
result = { error: err.message };
|
|
1408
1421
|
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
type: "tool_result",
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1422
|
+
return {
|
|
1423
|
+
toolCall: { name: block.name, args: block.input, result },
|
|
1424
|
+
toolResult: { type: "tool_result", tool_use_id: block.id, content: typeof result === "string" ? result : JSON.stringify(result) }
|
|
1425
|
+
};
|
|
1426
|
+
});
|
|
1427
|
+
const results = await runWithConcurrency(tasks, this._concurrency);
|
|
1428
|
+
const toolResults = results.map((r) => r.toolResult);
|
|
1429
|
+
for (const r of results) allToolCalls.push(r.toolCall);
|
|
1416
1430
|
response = await this._sendMessage(toolResults, { tools: this.tools, ...toolChoice && { tool_choice: toolChoice } });
|
|
1417
1431
|
}
|
|
1418
1432
|
this._cumulativeUsage = {
|
|
@@ -1472,43 +1486,88 @@ var ToolAgent = class extends base_default {
|
|
|
1472
1486
|
return;
|
|
1473
1487
|
}
|
|
1474
1488
|
const toolResults = [];
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1489
|
+
if (this._concurrency === 1) {
|
|
1490
|
+
for (const block of toolUseBlocks) {
|
|
1491
|
+
if (this._stopped) break;
|
|
1492
|
+
yield { type: "tool_call", toolName: block.name, args: block.input };
|
|
1493
|
+
if (this.onToolCall) {
|
|
1494
|
+
try {
|
|
1495
|
+
this.onToolCall(block.name, block.input);
|
|
1496
|
+
} catch (e) {
|
|
1497
|
+
logger_default.warn(`onToolCall callback error: ${e.message}`);
|
|
1498
|
+
}
|
|
1483
1499
|
}
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1500
|
+
let denied = false;
|
|
1501
|
+
if (this.onBeforeExecution) {
|
|
1502
|
+
try {
|
|
1503
|
+
const allowed = await this.onBeforeExecution(block.name, block.input);
|
|
1504
|
+
if (allowed === false) denied = true;
|
|
1505
|
+
} catch (e) {
|
|
1506
|
+
logger_default.warn(`onBeforeExecution callback error: ${e.message}`);
|
|
1507
|
+
}
|
|
1492
1508
|
}
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1509
|
+
let result;
|
|
1510
|
+
if (denied) {
|
|
1511
|
+
result = { error: "Execution denied by onBeforeExecution callback" };
|
|
1512
|
+
} else {
|
|
1513
|
+
try {
|
|
1514
|
+
result = await this.toolExecutor(block.name, block.input);
|
|
1515
|
+
} catch (err) {
|
|
1516
|
+
logger_default.warn(`Tool ${block.name} failed: ${err.message}`);
|
|
1517
|
+
result = { error: err.message };
|
|
1518
|
+
}
|
|
1503
1519
|
}
|
|
1520
|
+
allToolCalls.push({ name: block.name, args: block.input, result });
|
|
1521
|
+
yield { type: "tool_result", toolName: block.name, result };
|
|
1522
|
+
toolResults.push({
|
|
1523
|
+
type: "tool_result",
|
|
1524
|
+
tool_use_id: block.id,
|
|
1525
|
+
content: typeof result === "string" ? result : JSON.stringify(result)
|
|
1526
|
+
});
|
|
1504
1527
|
}
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1528
|
+
} else {
|
|
1529
|
+
for (const block of toolUseBlocks) {
|
|
1530
|
+
yield { type: "tool_call", toolName: block.name, args: block.input };
|
|
1531
|
+
}
|
|
1532
|
+
const tasks = toolUseBlocks.map((block) => async () => {
|
|
1533
|
+
if (this.onToolCall) {
|
|
1534
|
+
try {
|
|
1535
|
+
this.onToolCall(block.name, block.input);
|
|
1536
|
+
} catch (e) {
|
|
1537
|
+
logger_default.warn(`onToolCall callback error: ${e.message}`);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
let denied = false;
|
|
1541
|
+
if (this.onBeforeExecution) {
|
|
1542
|
+
try {
|
|
1543
|
+
const allowed = await this.onBeforeExecution(block.name, block.input);
|
|
1544
|
+
if (allowed === false) denied = true;
|
|
1545
|
+
} catch (e) {
|
|
1546
|
+
logger_default.warn(`onBeforeExecution callback error: ${e.message}`);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
let result;
|
|
1550
|
+
if (denied) {
|
|
1551
|
+
result = { error: "Execution denied by onBeforeExecution callback" };
|
|
1552
|
+
} else {
|
|
1553
|
+
try {
|
|
1554
|
+
result = await this.toolExecutor(block.name, block.input);
|
|
1555
|
+
} catch (err) {
|
|
1556
|
+
logger_default.warn(`Tool ${block.name} failed: ${err.message}`);
|
|
1557
|
+
result = { error: err.message };
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
return {
|
|
1561
|
+
toolCall: { name: block.name, args: block.input, result },
|
|
1562
|
+
toolResult: { type: "tool_result", tool_use_id: block.id, content: typeof result === "string" ? result : JSON.stringify(result) }
|
|
1563
|
+
};
|
|
1511
1564
|
});
|
|
1565
|
+
const results = await runWithConcurrency(tasks, this._concurrency);
|
|
1566
|
+
for (const r of results) {
|
|
1567
|
+
allToolCalls.push(r.toolCall);
|
|
1568
|
+
yield { type: "tool_result", toolName: r.toolCall.name, result: r.toolCall.result };
|
|
1569
|
+
toolResults.push(r.toolResult);
|
|
1570
|
+
}
|
|
1512
1571
|
}
|
|
1513
1572
|
stream = await this._streamMessage(toolResults, { tools: this.tools, ...toolChoice && { tool_choice: toolChoice } });
|
|
1514
1573
|
}
|
package/message.js
CHANGED
|
@@ -183,7 +183,7 @@ class Message extends BaseClaude {
|
|
|
183
183
|
// Parse structured data if configured
|
|
184
184
|
if (this._isStructured) {
|
|
185
185
|
try {
|
|
186
|
-
if (this._responseSchema) {
|
|
186
|
+
if (this._responseSchema && !this.vertexai) {
|
|
187
187
|
// Native structured output — guaranteed valid JSON
|
|
188
188
|
result.data = JSON.parse(text);
|
|
189
189
|
} else {
|
package/package.json
CHANGED
package/tool-agent.js
CHANGED
|
@@ -19,6 +19,31 @@ import log from './logger.js';
|
|
|
19
19
|
* @typedef {import('./types').AgentStreamEvent} AgentStreamEvent
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Execute async task factories with a concurrency limit.
|
|
24
|
+
* @param {Array<() => Promise<any>>} tasks
|
|
25
|
+
* @param {number} concurrency - Infinity for unlimited, 1 for sequential
|
|
26
|
+
* @returns {Promise<any[]>} Results in same order as tasks
|
|
27
|
+
*/
|
|
28
|
+
async function runWithConcurrency(tasks, concurrency) {
|
|
29
|
+
if (concurrency === Infinity) return Promise.all(tasks.map(t => t()));
|
|
30
|
+
if (concurrency === 1) {
|
|
31
|
+
const results = [];
|
|
32
|
+
for (const t of tasks) results.push(await t());
|
|
33
|
+
return results;
|
|
34
|
+
}
|
|
35
|
+
const results = new Array(tasks.length);
|
|
36
|
+
let next = 0;
|
|
37
|
+
async function worker() {
|
|
38
|
+
while (next < tasks.length) {
|
|
39
|
+
const i = next++;
|
|
40
|
+
results[i] = await tasks[i]();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, tasks.length) }, () => worker()));
|
|
44
|
+
return results;
|
|
45
|
+
}
|
|
46
|
+
|
|
22
47
|
/**
|
|
23
48
|
* AI agent that uses user-provided tools to accomplish tasks.
|
|
24
49
|
* Automatically manages the tool-use loop: when Claude decides to call
|
|
@@ -90,6 +115,13 @@ class ToolAgent extends BaseClaude {
|
|
|
90
115
|
this.toolChoice = options.toolChoice ?? undefined;
|
|
91
116
|
this.disableParallelToolUse = options.disableParallelToolUse ?? false;
|
|
92
117
|
|
|
118
|
+
// ── Parallel execution ──
|
|
119
|
+
this.parallelToolCalls = options.parallelToolCalls ?? true;
|
|
120
|
+
/** @private */
|
|
121
|
+
this._concurrency = this.parallelToolCalls === true ? Infinity
|
|
122
|
+
: this.parallelToolCalls === false ? 1
|
|
123
|
+
: this.parallelToolCalls;
|
|
124
|
+
|
|
93
125
|
// ── Tool loop config ──
|
|
94
126
|
this.maxToolRounds = options.maxToolRounds || 10;
|
|
95
127
|
this.onToolCall = options.onToolCall || null;
|
|
@@ -147,9 +179,8 @@ class ToolAgent extends BaseClaude {
|
|
|
147
179
|
const toolUseBlocks = response.content.filter(b => b.type === 'tool_use');
|
|
148
180
|
if (toolUseBlocks.length === 0) break;
|
|
149
181
|
|
|
150
|
-
// Execute tools
|
|
151
|
-
const
|
|
152
|
-
for (const block of toolUseBlocks) {
|
|
182
|
+
// Execute tools (parallel or sequential based on _concurrency)
|
|
183
|
+
const tasks = toolUseBlocks.map(block => async () => {
|
|
153
184
|
// Fire onToolCall callback
|
|
154
185
|
if (this.onToolCall) {
|
|
155
186
|
try { this.onToolCall(block.name, block.input); }
|
|
@@ -162,13 +193,7 @@ class ToolAgent extends BaseClaude {
|
|
|
162
193
|
const allowed = await this.onBeforeExecution(block.name, block.input);
|
|
163
194
|
if (allowed === false) {
|
|
164
195
|
const result = { error: 'Execution denied by onBeforeExecution callback' };
|
|
165
|
-
|
|
166
|
-
toolResults.push({
|
|
167
|
-
type: 'tool_result',
|
|
168
|
-
tool_use_id: block.id,
|
|
169
|
-
content: JSON.stringify(result)
|
|
170
|
-
});
|
|
171
|
-
continue;
|
|
196
|
+
return { toolCall: { name: block.name, args: block.input, result }, toolResult: { type: 'tool_result', tool_use_id: block.id, content: JSON.stringify(result) } };
|
|
172
197
|
}
|
|
173
198
|
} catch (e) {
|
|
174
199
|
log.warn(`onBeforeExecution callback error: ${e.message}`);
|
|
@@ -183,14 +208,15 @@ class ToolAgent extends BaseClaude {
|
|
|
183
208
|
result = { error: err.message };
|
|
184
209
|
}
|
|
185
210
|
|
|
186
|
-
|
|
211
|
+
return {
|
|
212
|
+
toolCall: { name: block.name, args: block.input, result },
|
|
213
|
+
toolResult: { type: 'tool_result', tool_use_id: block.id, content: typeof result === 'string' ? result : JSON.stringify(result) }
|
|
214
|
+
};
|
|
215
|
+
});
|
|
187
216
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
content: typeof result === 'string' ? result : JSON.stringify(result)
|
|
192
|
-
});
|
|
193
|
-
}
|
|
217
|
+
const results = await runWithConcurrency(tasks, this._concurrency);
|
|
218
|
+
const toolResults = results.map(r => r.toolResult);
|
|
219
|
+
for (const r of results) allToolCalls.push(r.toolCall);
|
|
194
220
|
|
|
195
221
|
// Send tool results back to Claude as user message
|
|
196
222
|
response = await this._sendMessage(toolResults, { tools: this.tools, ...(toolChoice && { tool_choice: toolChoice }) });
|
|
@@ -270,50 +296,97 @@ class ToolAgent extends BaseClaude {
|
|
|
270
296
|
return;
|
|
271
297
|
}
|
|
272
298
|
|
|
273
|
-
// Execute tools
|
|
299
|
+
// Execute tools and yield events
|
|
274
300
|
const toolResults = [];
|
|
275
|
-
|
|
276
|
-
|
|
301
|
+
if (this._concurrency === 1) {
|
|
302
|
+
// Sequential: yield tool_call, execute, yield tool_result for each
|
|
303
|
+
for (const block of toolUseBlocks) {
|
|
304
|
+
if (this._stopped) break;
|
|
277
305
|
|
|
278
|
-
|
|
306
|
+
yield { type: 'tool_call', toolName: block.name, args: block.input };
|
|
279
307
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
308
|
+
if (this.onToolCall) {
|
|
309
|
+
try { this.onToolCall(block.name, block.input); }
|
|
310
|
+
catch (e) { log.warn(`onToolCall callback error: ${e.message}`); }
|
|
311
|
+
}
|
|
285
312
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
313
|
+
let denied = false;
|
|
314
|
+
if (this.onBeforeExecution) {
|
|
315
|
+
try {
|
|
316
|
+
const allowed = await this.onBeforeExecution(block.name, block.input);
|
|
317
|
+
if (allowed === false) denied = true;
|
|
318
|
+
} catch (e) {
|
|
319
|
+
log.warn(`onBeforeExecution callback error: ${e.message}`);
|
|
320
|
+
}
|
|
294
321
|
}
|
|
295
|
-
}
|
|
296
322
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
323
|
+
let result;
|
|
324
|
+
if (denied) {
|
|
325
|
+
result = { error: 'Execution denied by onBeforeExecution callback' };
|
|
326
|
+
} else {
|
|
327
|
+
try {
|
|
328
|
+
result = await this.toolExecutor(block.name, block.input);
|
|
329
|
+
} catch (err) {
|
|
330
|
+
log.warn(`Tool ${block.name} failed: ${err.message}`);
|
|
331
|
+
result = { error: err.message };
|
|
332
|
+
}
|
|
306
333
|
}
|
|
334
|
+
|
|
335
|
+
allToolCalls.push({ name: block.name, args: block.input, result });
|
|
336
|
+
yield { type: 'tool_result', toolName: block.name, result };
|
|
337
|
+
|
|
338
|
+
toolResults.push({
|
|
339
|
+
type: 'tool_result',
|
|
340
|
+
tool_use_id: block.id,
|
|
341
|
+
content: typeof result === 'string' ? result : JSON.stringify(result)
|
|
342
|
+
});
|
|
307
343
|
}
|
|
344
|
+
} else {
|
|
345
|
+
// Parallel: yield all tool_call events, execute all, yield all tool_result events
|
|
346
|
+
for (const block of toolUseBlocks) {
|
|
347
|
+
yield { type: 'tool_call', toolName: block.name, args: block.input };
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const tasks = toolUseBlocks.map(block => async () => {
|
|
351
|
+
if (this.onToolCall) {
|
|
352
|
+
try { this.onToolCall(block.name, block.input); }
|
|
353
|
+
catch (e) { log.warn(`onToolCall callback error: ${e.message}`); }
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
let denied = false;
|
|
357
|
+
if (this.onBeforeExecution) {
|
|
358
|
+
try {
|
|
359
|
+
const allowed = await this.onBeforeExecution(block.name, block.input);
|
|
360
|
+
if (allowed === false) denied = true;
|
|
361
|
+
} catch (e) {
|
|
362
|
+
log.warn(`onBeforeExecution callback error: ${e.message}`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
308
365
|
|
|
309
|
-
|
|
310
|
-
|
|
366
|
+
let result;
|
|
367
|
+
if (denied) {
|
|
368
|
+
result = { error: 'Execution denied by onBeforeExecution callback' };
|
|
369
|
+
} else {
|
|
370
|
+
try {
|
|
371
|
+
result = await this.toolExecutor(block.name, block.input);
|
|
372
|
+
} catch (err) {
|
|
373
|
+
log.warn(`Tool ${block.name} failed: ${err.message}`);
|
|
374
|
+
result = { error: err.message };
|
|
375
|
+
}
|
|
376
|
+
}
|
|
311
377
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
378
|
+
return {
|
|
379
|
+
toolCall: { name: block.name, args: block.input, result },
|
|
380
|
+
toolResult: { type: 'tool_result', tool_use_id: block.id, content: typeof result === 'string' ? result : JSON.stringify(result) }
|
|
381
|
+
};
|
|
316
382
|
});
|
|
383
|
+
|
|
384
|
+
const results = await runWithConcurrency(tasks, this._concurrency);
|
|
385
|
+
for (const r of results) {
|
|
386
|
+
allToolCalls.push(r.toolCall);
|
|
387
|
+
yield { type: 'tool_result', toolName: r.toolCall.name, result: r.toolCall.result };
|
|
388
|
+
toolResults.push(r.toolResult);
|
|
389
|
+
}
|
|
317
390
|
}
|
|
318
391
|
|
|
319
392
|
// Send tool results back and get next stream
|
package/types.d.ts
CHANGED
|
@@ -203,6 +203,8 @@ export interface ToolAgentOptions extends BaseClaudeOptions {
|
|
|
203
203
|
toolChoice?: ToolChoice;
|
|
204
204
|
/** Disable parallel tool use — forces sequential tool calls (default: false) */
|
|
205
205
|
disableParallelToolUse?: boolean;
|
|
206
|
+
/** Parallel tool execution: false = sequential, true = unlimited parallel, number = concurrency limit (default: true) */
|
|
207
|
+
parallelToolCalls?: boolean | number;
|
|
206
208
|
}
|
|
207
209
|
|
|
208
210
|
export interface LocalDataEntry {
|
|
@@ -483,6 +485,7 @@ export declare class ToolAgent extends BaseClaude {
|
|
|
483
485
|
onBeforeExecution: ((toolName: string, args: Record<string, any>) => Promise<boolean>) | null;
|
|
484
486
|
toolChoice: ToolChoice | undefined;
|
|
485
487
|
disableParallelToolUse: boolean;
|
|
488
|
+
parallelToolCalls: boolean | number;
|
|
486
489
|
|
|
487
490
|
chat(message: string, opts?: Record<string, any>): Promise<AgentResponse>;
|
|
488
491
|
stream(message: string, opts?: Record<string, any>): AsyncGenerator<AgentStreamEvent, void, unknown>;
|