bashkit 0.1.3 → 0.2.1
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/AGENTS.md +108 -0
- package/README.md +148 -1
- package/dist/cache/cached.d.ts +37 -0
- package/dist/cache/index.d.ts +6 -0
- package/dist/cache/lru.d.ts +25 -0
- package/dist/cache/redis.d.ts +41 -0
- package/dist/cache/types.d.ts +53 -0
- package/dist/index.d.ts +7 -5
- package/dist/index.js +294 -37
- package/dist/middleware/anthropic-cache.d.ts +20 -1
- package/dist/middleware/index.d.ts +1 -1
- package/dist/skills/index.d.ts +1 -1
- package/dist/skills/loader.d.ts +36 -0
- package/dist/tools/exit-plan-mode.d.ts +1 -2
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/task.d.ts +11 -1
- package/dist/tools/todo-write.d.ts +1 -2
- package/dist/tools/web-constants.d.ts +5 -0
- package/dist/tools/web-fetch.d.ts +2 -6
- package/dist/tools/web-search.d.ts +2 -4
- package/dist/types.d.ts +54 -4
- package/dist/utils/http-constants.d.ts +5 -0
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -18,10 +18,8 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
18
18
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
19
|
|
|
20
20
|
// src/middleware/anthropic-cache.ts
|
|
21
|
-
function
|
|
22
|
-
if (!message)
|
|
23
|
-
return;
|
|
24
|
-
if (!("content" in message))
|
|
21
|
+
function addCacheMarker(message) {
|
|
22
|
+
if (!message || !("content" in message))
|
|
25
23
|
return;
|
|
26
24
|
const content = message.content;
|
|
27
25
|
if (!content || !Array.isArray(content))
|
|
@@ -36,21 +34,20 @@ function ensureCacheMarker(message) {
|
|
|
36
34
|
}
|
|
37
35
|
};
|
|
38
36
|
}
|
|
37
|
+
function applyCacheMarkers(params) {
|
|
38
|
+
const messages = params.prompt;
|
|
39
|
+
if (!messages || messages.length === 0)
|
|
40
|
+
return params;
|
|
41
|
+
addCacheMarker(messages.at(-1));
|
|
42
|
+
addCacheMarker(messages.slice(0, -1).findLast((m) => m.role !== "assistant"));
|
|
43
|
+
return params;
|
|
44
|
+
}
|
|
45
|
+
var anthropicPromptCacheMiddlewareV2 = {
|
|
46
|
+
transformParams: async ({ params }) => applyCacheMarkers(params)
|
|
47
|
+
};
|
|
39
48
|
var anthropicPromptCacheMiddleware = {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}) => {
|
|
43
|
-
const messages = params.prompt;
|
|
44
|
-
if (!messages || messages.length === 0) {
|
|
45
|
-
return params;
|
|
46
|
-
}
|
|
47
|
-
ensureCacheMarker(messages.at(-1));
|
|
48
|
-
ensureCacheMarker(messages.slice(0, -1).findLast((m) => m.role !== "assistant"));
|
|
49
|
-
return {
|
|
50
|
-
...params,
|
|
51
|
-
prompt: messages
|
|
52
|
-
};
|
|
53
|
-
}
|
|
49
|
+
specificationVersion: "v3",
|
|
50
|
+
transformParams: async ({ params }) => applyCacheMarkers(params)
|
|
54
51
|
};
|
|
55
52
|
// src/sandbox/e2b.ts
|
|
56
53
|
import { Sandbox as E2BSandboxSDK } from "@e2b/code-interpreter";
|
|
@@ -349,6 +346,132 @@ function createVercelSandbox(config = {}) {
|
|
|
349
346
|
}
|
|
350
347
|
};
|
|
351
348
|
}
|
|
349
|
+
// src/cache/lru.ts
|
|
350
|
+
class LRUCacheStore {
|
|
351
|
+
cache = new Map;
|
|
352
|
+
maxSize;
|
|
353
|
+
constructor(maxSize = 1000) {
|
|
354
|
+
this.maxSize = maxSize;
|
|
355
|
+
}
|
|
356
|
+
get(key) {
|
|
357
|
+
const entry = this.cache.get(key);
|
|
358
|
+
if (entry) {
|
|
359
|
+
this.cache.delete(key);
|
|
360
|
+
this.cache.set(key, entry);
|
|
361
|
+
}
|
|
362
|
+
return entry;
|
|
363
|
+
}
|
|
364
|
+
set(key, entry) {
|
|
365
|
+
if (this.cache.has(key)) {
|
|
366
|
+
this.cache.delete(key);
|
|
367
|
+
} else if (this.cache.size >= this.maxSize) {
|
|
368
|
+
const firstKey = this.cache.keys().next().value;
|
|
369
|
+
if (firstKey !== undefined) {
|
|
370
|
+
this.cache.delete(firstKey);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
this.cache.set(key, entry);
|
|
374
|
+
}
|
|
375
|
+
delete(key) {
|
|
376
|
+
this.cache.delete(key);
|
|
377
|
+
}
|
|
378
|
+
clear() {
|
|
379
|
+
this.cache.clear();
|
|
380
|
+
}
|
|
381
|
+
size() {
|
|
382
|
+
return this.cache.size;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// src/cache/cached.ts
|
|
386
|
+
import { createHash } from "crypto";
|
|
387
|
+
function defaultKeyGenerator(toolName, params) {
|
|
388
|
+
const sortedKeys = params && typeof params === "object" ? Object.keys(params).sort() : undefined;
|
|
389
|
+
const serialized = JSON.stringify(params, sortedKeys);
|
|
390
|
+
const hash = createHash("sha256").update(serialized).digest("hex").slice(0, 16);
|
|
391
|
+
return `${toolName}:${hash}`;
|
|
392
|
+
}
|
|
393
|
+
function cached(tool, toolName, options = {}) {
|
|
394
|
+
const {
|
|
395
|
+
ttl = 5 * 60 * 1000,
|
|
396
|
+
store = new LRUCacheStore,
|
|
397
|
+
keyGenerator = defaultKeyGenerator,
|
|
398
|
+
debug = false,
|
|
399
|
+
onHit,
|
|
400
|
+
onMiss
|
|
401
|
+
} = options;
|
|
402
|
+
let hits = 0;
|
|
403
|
+
let misses = 0;
|
|
404
|
+
const log = debug ? console.log.bind(console) : () => {};
|
|
405
|
+
const cachedTool = {
|
|
406
|
+
...tool,
|
|
407
|
+
execute: async (params, execOptions) => {
|
|
408
|
+
const key = keyGenerator(toolName, params);
|
|
409
|
+
const now = Date.now();
|
|
410
|
+
const entry = await store.get(key);
|
|
411
|
+
if (entry && now - entry.timestamp < ttl) {
|
|
412
|
+
hits++;
|
|
413
|
+
log(`[Cache] HIT ${toolName}:${key.slice(-8)}`);
|
|
414
|
+
onHit?.(toolName, key);
|
|
415
|
+
return entry.result;
|
|
416
|
+
}
|
|
417
|
+
misses++;
|
|
418
|
+
log(`[Cache] MISS ${toolName}:${key.slice(-8)}`);
|
|
419
|
+
onMiss?.(toolName, key);
|
|
420
|
+
if (!tool.execute) {
|
|
421
|
+
throw new Error(`Tool ${toolName} has no execute function`);
|
|
422
|
+
}
|
|
423
|
+
const result = await tool.execute(params, execOptions);
|
|
424
|
+
if (result && typeof result === "object" && !("error" in result)) {
|
|
425
|
+
await store.set(key, { result, timestamp: now });
|
|
426
|
+
log(`[Cache] STORED ${toolName}:${key.slice(-8)}`);
|
|
427
|
+
}
|
|
428
|
+
return result;
|
|
429
|
+
},
|
|
430
|
+
async getStats() {
|
|
431
|
+
const total = hits + misses;
|
|
432
|
+
const size = await store.size?.() ?? 0;
|
|
433
|
+
return {
|
|
434
|
+
hits,
|
|
435
|
+
misses,
|
|
436
|
+
hitRate: total > 0 ? hits / total : 0,
|
|
437
|
+
size
|
|
438
|
+
};
|
|
439
|
+
},
|
|
440
|
+
async clearCache(key) {
|
|
441
|
+
if (key) {
|
|
442
|
+
await store.delete(key);
|
|
443
|
+
} else {
|
|
444
|
+
await store.clear();
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
return cachedTool;
|
|
449
|
+
}
|
|
450
|
+
// src/cache/redis.ts
|
|
451
|
+
function createRedisCacheStore(client, options = {}) {
|
|
452
|
+
const { prefix = "bashkit:" } = options;
|
|
453
|
+
return {
|
|
454
|
+
async get(key) {
|
|
455
|
+
const data = await client.get(`${prefix}${key}`);
|
|
456
|
+
return data ? JSON.parse(data) : undefined;
|
|
457
|
+
},
|
|
458
|
+
async set(key, entry) {
|
|
459
|
+
await client.set(`${prefix}${key}`, JSON.stringify(entry));
|
|
460
|
+
},
|
|
461
|
+
async delete(key) {
|
|
462
|
+
await client.del(`${prefix}${key}`);
|
|
463
|
+
},
|
|
464
|
+
async clear() {
|
|
465
|
+
const keys = await client.keys(`${prefix}*`);
|
|
466
|
+
if (keys.length)
|
|
467
|
+
await client.del(keys);
|
|
468
|
+
},
|
|
469
|
+
async size() {
|
|
470
|
+
const keys = await client.keys(`${prefix}*`);
|
|
471
|
+
return keys.length;
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
}
|
|
352
475
|
// src/types.ts
|
|
353
476
|
var DEFAULT_CONFIG = {
|
|
354
477
|
defaultTimeout: 120000,
|
|
@@ -500,6 +623,9 @@ function createBashTool(sandbox, config) {
|
|
|
500
623
|
return tool2({
|
|
501
624
|
description: BASH_DESCRIPTION,
|
|
502
625
|
inputSchema: zodSchema2(bashInputSchema),
|
|
626
|
+
strict: config?.strict,
|
|
627
|
+
needsApproval: config?.needsApproval,
|
|
628
|
+
providerOptions: config?.providerOptions,
|
|
503
629
|
execute: async ({
|
|
504
630
|
command,
|
|
505
631
|
timeout,
|
|
@@ -576,6 +702,9 @@ function createEditTool(sandbox, config) {
|
|
|
576
702
|
return tool3({
|
|
577
703
|
description: EDIT_DESCRIPTION,
|
|
578
704
|
inputSchema: zodSchema3(editInputSchema),
|
|
705
|
+
strict: config?.strict,
|
|
706
|
+
needsApproval: config?.needsApproval,
|
|
707
|
+
providerOptions: config?.providerOptions,
|
|
579
708
|
execute: async ({
|
|
580
709
|
file_path,
|
|
581
710
|
old_string,
|
|
@@ -750,7 +879,7 @@ Before using this tool, ensure your plan is clear and unambiguous. If there are
|
|
|
750
879
|
1. "Search for and understand the implementation of X" - Do NOT use this tool (research task)
|
|
751
880
|
2. "Help me implement feature Y" - Use this tool after planning the implementation steps
|
|
752
881
|
3. "Add user authentication" - If unsure about approach (OAuth vs JWT), clarify first, then use this tool`;
|
|
753
|
-
function createExitPlanModeTool(
|
|
882
|
+
function createExitPlanModeTool(onPlanSubmit) {
|
|
754
883
|
return tool5({
|
|
755
884
|
description: EXIT_PLAN_MODE_DESCRIPTION,
|
|
756
885
|
inputSchema: zodSchema5(exitPlanModeInputSchema),
|
|
@@ -794,6 +923,9 @@ function createGlobTool(sandbox, config) {
|
|
|
794
923
|
return tool6({
|
|
795
924
|
description: GLOB_DESCRIPTION,
|
|
796
925
|
inputSchema: zodSchema6(globInputSchema),
|
|
926
|
+
strict: config?.strict,
|
|
927
|
+
needsApproval: config?.needsApproval,
|
|
928
|
+
providerOptions: config?.providerOptions,
|
|
797
929
|
execute: async ({
|
|
798
930
|
pattern,
|
|
799
931
|
path
|
|
@@ -871,6 +1003,9 @@ function createGrepTool(sandbox, config) {
|
|
|
871
1003
|
return tool7({
|
|
872
1004
|
description: GREP_DESCRIPTION,
|
|
873
1005
|
inputSchema: zodSchema7(grepInputSchema),
|
|
1006
|
+
strict: config?.strict,
|
|
1007
|
+
needsApproval: config?.needsApproval,
|
|
1008
|
+
providerOptions: config?.providerOptions,
|
|
874
1009
|
execute: async (input) => {
|
|
875
1010
|
const {
|
|
876
1011
|
pattern,
|
|
@@ -1080,6 +1215,9 @@ function createReadTool(sandbox, config) {
|
|
|
1080
1215
|
return tool8({
|
|
1081
1216
|
description: READ_DESCRIPTION,
|
|
1082
1217
|
inputSchema: zodSchema8(readInputSchema),
|
|
1218
|
+
strict: config?.strict,
|
|
1219
|
+
needsApproval: config?.needsApproval,
|
|
1220
|
+
providerOptions: config?.providerOptions,
|
|
1083
1221
|
execute: async ({
|
|
1084
1222
|
file_path,
|
|
1085
1223
|
offset,
|
|
@@ -1236,11 +1374,15 @@ function createSkillTool(config) {
|
|
|
1236
1374
|
import { generateText, tool as tool10, zodSchema as zodSchema10 } from "ai";
|
|
1237
1375
|
import Parallel from "parallel-web";
|
|
1238
1376
|
import { z as z10 } from "zod";
|
|
1377
|
+
|
|
1378
|
+
// src/utils/http-constants.ts
|
|
1379
|
+
var RETRYABLE_STATUS_CODES = [408, 429, 500, 502, 503];
|
|
1380
|
+
|
|
1381
|
+
// src/tools/web-fetch.ts
|
|
1239
1382
|
var webFetchInputSchema = z10.object({
|
|
1240
1383
|
url: z10.string().describe("The URL to fetch content from"),
|
|
1241
1384
|
prompt: z10.string().describe("The prompt to run on the fetched content")
|
|
1242
1385
|
});
|
|
1243
|
-
var RETRYABLE_CODES = [408, 429, 500, 502, 503];
|
|
1244
1386
|
var WEB_FETCH_DESCRIPTION = `
|
|
1245
1387
|
- Fetches content from a specified URL and processes it using an AI model
|
|
1246
1388
|
- Takes a URL and a prompt as input
|
|
@@ -1258,10 +1400,13 @@ Usage notes:
|
|
|
1258
1400
|
- When a URL redirects to a different host, the tool will inform you and provide the redirect URL. You should then make a new WebFetch request with the redirect URL to fetch the content.
|
|
1259
1401
|
`;
|
|
1260
1402
|
function createWebFetchTool(config) {
|
|
1261
|
-
const { apiKey, model } = config;
|
|
1403
|
+
const { apiKey, model, strict, needsApproval, providerOptions } = config;
|
|
1262
1404
|
return tool10({
|
|
1263
1405
|
description: WEB_FETCH_DESCRIPTION,
|
|
1264
1406
|
inputSchema: zodSchema10(webFetchInputSchema),
|
|
1407
|
+
strict,
|
|
1408
|
+
needsApproval,
|
|
1409
|
+
providerOptions,
|
|
1265
1410
|
execute: async (input) => {
|
|
1266
1411
|
const { url, prompt } = input;
|
|
1267
1412
|
try {
|
|
@@ -1309,7 +1454,7 @@ ${content}`
|
|
|
1309
1454
|
return {
|
|
1310
1455
|
error: message,
|
|
1311
1456
|
status_code: statusCode,
|
|
1312
|
-
retryable:
|
|
1457
|
+
retryable: RETRYABLE_STATUS_CODES.includes(statusCode)
|
|
1313
1458
|
};
|
|
1314
1459
|
}
|
|
1315
1460
|
return {
|
|
@@ -1329,7 +1474,6 @@ var webSearchInputSchema = z11.object({
|
|
|
1329
1474
|
allowed_domains: z11.array(z11.string()).optional().describe("Only include results from these domains"),
|
|
1330
1475
|
blocked_domains: z11.array(z11.string()).optional().describe("Never include results from these domains")
|
|
1331
1476
|
});
|
|
1332
|
-
var RETRYABLE_CODES2 = [408, 429, 500, 502, 503];
|
|
1333
1477
|
var WEB_SEARCH_DESCRIPTION = `Searches the web and returns results with links. Use this for accessing up-to-date information beyond your knowledge cutoff.
|
|
1334
1478
|
|
|
1335
1479
|
**Capabilities:**
|
|
@@ -1351,10 +1495,13 @@ When searching for recent information, documentation, or current events, use the
|
|
|
1351
1495
|
- allowed_domains: Only include results from these domains
|
|
1352
1496
|
- blocked_domains: Never include results from these domains`;
|
|
1353
1497
|
function createWebSearchTool(config) {
|
|
1354
|
-
const { apiKey } = config;
|
|
1498
|
+
const { apiKey, strict, needsApproval, providerOptions } = config;
|
|
1355
1499
|
return tool11({
|
|
1356
1500
|
description: WEB_SEARCH_DESCRIPTION,
|
|
1357
1501
|
inputSchema: zodSchema11(webSearchInputSchema),
|
|
1502
|
+
strict,
|
|
1503
|
+
needsApproval,
|
|
1504
|
+
providerOptions,
|
|
1358
1505
|
execute: async (input) => {
|
|
1359
1506
|
const { query, allowed_domains, blocked_domains } = input;
|
|
1360
1507
|
try {
|
|
@@ -1388,7 +1535,7 @@ function createWebSearchTool(config) {
|
|
|
1388
1535
|
return {
|
|
1389
1536
|
error: message,
|
|
1390
1537
|
status_code: statusCode,
|
|
1391
|
-
retryable:
|
|
1538
|
+
retryable: RETRYABLE_STATUS_CODES.includes(statusCode)
|
|
1392
1539
|
};
|
|
1393
1540
|
}
|
|
1394
1541
|
return {
|
|
@@ -1422,6 +1569,9 @@ function createWriteTool(sandbox, config) {
|
|
|
1422
1569
|
return tool12({
|
|
1423
1570
|
description: WRITE_DESCRIPTION,
|
|
1424
1571
|
inputSchema: zodSchema12(writeInputSchema),
|
|
1572
|
+
strict: config?.strict,
|
|
1573
|
+
needsApproval: config?.needsApproval,
|
|
1574
|
+
providerOptions: config?.providerOptions,
|
|
1425
1575
|
execute: async ({
|
|
1426
1576
|
file_path,
|
|
1427
1577
|
content
|
|
@@ -1465,13 +1615,17 @@ import { z as z13 } from "zod";
|
|
|
1465
1615
|
var taskInputSchema = z13.object({
|
|
1466
1616
|
description: z13.string().describe("A short (3-5 word) description of the task"),
|
|
1467
1617
|
prompt: z13.string().describe("The task for the agent to perform"),
|
|
1468
|
-
subagent_type: z13.string().describe("The type of specialized agent to use for this task")
|
|
1618
|
+
subagent_type: z13.string().describe("The type of specialized agent to use for this task"),
|
|
1619
|
+
system_prompt: z13.string().optional().describe("Optional custom system prompt for this agent. If provided, overrides the default system prompt for the subagent type. Use this to create dynamic, specialized agents on the fly."),
|
|
1620
|
+
tools: z13.array(z13.string()).optional().describe("Optional list of tool names this agent can use (e.g., ['Read', 'Grep', 'WebSearch']). If provided, overrides the default tools for the subagent type. Use this to restrict or expand the agent's capabilities.")
|
|
1469
1621
|
});
|
|
1470
1622
|
var TASK_DESCRIPTION = `Launch a new agent to handle complex, multi-step tasks autonomously.
|
|
1471
1623
|
|
|
1472
1624
|
The Task tool launches specialized agents (subprocesses) that autonomously handle complex tasks. Each agent type has specific capabilities and tools available to it.
|
|
1473
1625
|
|
|
1474
|
-
|
|
1626
|
+
**Subagent types:**
|
|
1627
|
+
- Use predefined subagent_type values for common task patterns
|
|
1628
|
+
- For dynamic agents: provide custom system_prompt and/or tools to create a specialized agent on the fly
|
|
1475
1629
|
|
|
1476
1630
|
**When NOT to use the Task tool:**
|
|
1477
1631
|
- If you want to read a specific file path, use the Read or Glob tool instead, to find the match more quickly
|
|
@@ -1517,14 +1671,16 @@ function createTaskTool(config) {
|
|
|
1517
1671
|
execute: async ({
|
|
1518
1672
|
description,
|
|
1519
1673
|
prompt,
|
|
1520
|
-
subagent_type
|
|
1674
|
+
subagent_type,
|
|
1675
|
+
system_prompt,
|
|
1676
|
+
tools: customTools
|
|
1521
1677
|
}) => {
|
|
1522
1678
|
const startTime = performance.now();
|
|
1523
1679
|
try {
|
|
1524
1680
|
const typeConfig = subagentTypes[subagent_type] || {};
|
|
1525
1681
|
const model = typeConfig.model || defaultModel;
|
|
1526
|
-
const tools = filterTools(allTools, typeConfig.tools);
|
|
1527
|
-
const systemPrompt = typeConfig.systemPrompt;
|
|
1682
|
+
const tools = filterTools(allTools, customTools ?? typeConfig.tools);
|
|
1683
|
+
const systemPrompt = system_prompt ?? typeConfig.systemPrompt;
|
|
1528
1684
|
const commonOptions = {
|
|
1529
1685
|
model,
|
|
1530
1686
|
tools,
|
|
@@ -1677,7 +1833,7 @@ var TODO_WRITE_DESCRIPTION = `Use this tool to create and manage a structured ta
|
|
|
1677
1833
|
- Keep exactly ONE task in_progress at any time
|
|
1678
1834
|
- ONLY mark completed when FULLY accomplished
|
|
1679
1835
|
- If blocked/errors, keep in_progress and create new task for the blocker`;
|
|
1680
|
-
function createTodoWriteTool(state,
|
|
1836
|
+
function createTodoWriteTool(state, onUpdate) {
|
|
1681
1837
|
return tool14({
|
|
1682
1838
|
description: TODO_WRITE_DESCRIPTION,
|
|
1683
1839
|
inputSchema: zodSchema14(todoWriteInputSchema),
|
|
@@ -1709,6 +1865,58 @@ function createTodoWriteTool(state, config, onUpdate) {
|
|
|
1709
1865
|
}
|
|
1710
1866
|
|
|
1711
1867
|
// src/tools/index.ts
|
|
1868
|
+
var DEFAULT_CACHEABLE = [
|
|
1869
|
+
"Read",
|
|
1870
|
+
"Glob",
|
|
1871
|
+
"Grep",
|
|
1872
|
+
"WebFetch",
|
|
1873
|
+
"WebSearch"
|
|
1874
|
+
];
|
|
1875
|
+
function resolveCache(config) {
|
|
1876
|
+
if (!config) {
|
|
1877
|
+
return { store: null, ttl: 0, debug: false, enabled: new Set };
|
|
1878
|
+
}
|
|
1879
|
+
if (config === true) {
|
|
1880
|
+
return {
|
|
1881
|
+
store: new LRUCacheStore,
|
|
1882
|
+
ttl: 5 * 60 * 1000,
|
|
1883
|
+
debug: false,
|
|
1884
|
+
enabled: new Set(DEFAULT_CACHEABLE)
|
|
1885
|
+
};
|
|
1886
|
+
}
|
|
1887
|
+
if (typeof config === "object" && typeof config.get === "function" && typeof config.set === "function" && typeof config.delete === "function" && typeof config.clear === "function") {
|
|
1888
|
+
return {
|
|
1889
|
+
store: config,
|
|
1890
|
+
ttl: 5 * 60 * 1000,
|
|
1891
|
+
debug: false,
|
|
1892
|
+
enabled: new Set(DEFAULT_CACHEABLE)
|
|
1893
|
+
};
|
|
1894
|
+
}
|
|
1895
|
+
const enabled = new Set;
|
|
1896
|
+
for (const tool15 of DEFAULT_CACHEABLE) {
|
|
1897
|
+
if (config[tool15] !== false) {
|
|
1898
|
+
enabled.add(tool15);
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
for (const [key, value] of Object.entries(config)) {
|
|
1902
|
+
if (["store", "ttl", "debug", "onHit", "onMiss", "keyGenerator"].includes(key))
|
|
1903
|
+
continue;
|
|
1904
|
+
if (value === true)
|
|
1905
|
+
enabled.add(key);
|
|
1906
|
+
if (value === false)
|
|
1907
|
+
enabled.delete(key);
|
|
1908
|
+
}
|
|
1909
|
+
const cfg = config;
|
|
1910
|
+
return {
|
|
1911
|
+
store: cfg.store ?? new LRUCacheStore,
|
|
1912
|
+
ttl: cfg.ttl ?? 5 * 60 * 1000,
|
|
1913
|
+
debug: cfg.debug ?? false,
|
|
1914
|
+
onHit: cfg.onHit,
|
|
1915
|
+
onMiss: cfg.onMiss,
|
|
1916
|
+
keyGenerator: cfg.keyGenerator,
|
|
1917
|
+
enabled
|
|
1918
|
+
};
|
|
1919
|
+
}
|
|
1712
1920
|
function createAgentTools(sandbox, config) {
|
|
1713
1921
|
const toolsConfig = {
|
|
1714
1922
|
...DEFAULT_CONFIG.tools,
|
|
@@ -1744,6 +1952,21 @@ function createAgentTools(sandbox, config) {
|
|
|
1744
1952
|
if (config?.webFetch) {
|
|
1745
1953
|
tools.WebFetch = createWebFetchTool(config.webFetch);
|
|
1746
1954
|
}
|
|
1955
|
+
const cacheConfig = resolveCache(config?.cache);
|
|
1956
|
+
if (cacheConfig.store) {
|
|
1957
|
+
for (const [name, tool15] of Object.entries(tools)) {
|
|
1958
|
+
if (cacheConfig.enabled.has(name)) {
|
|
1959
|
+
tools[name] = cached(tool15, name, {
|
|
1960
|
+
store: cacheConfig.store,
|
|
1961
|
+
ttl: cacheConfig.ttl,
|
|
1962
|
+
debug: cacheConfig.debug,
|
|
1963
|
+
onHit: cacheConfig.onHit,
|
|
1964
|
+
onMiss: cacheConfig.onMiss,
|
|
1965
|
+
keyGenerator: cacheConfig.keyGenerator
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1747
1970
|
return { tools, planModeState };
|
|
1748
1971
|
}
|
|
1749
1972
|
// src/utils/compact-conversation.ts
|
|
@@ -2097,11 +2320,13 @@ function contextNeedsCompaction(status) {
|
|
|
2097
2320
|
return status.status === "critical";
|
|
2098
2321
|
}
|
|
2099
2322
|
// src/skills/discovery.ts
|
|
2100
|
-
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2323
|
+
import { readdir as readdir2, readFile as readFile2, stat } from "node:fs/promises";
|
|
2101
2324
|
import { homedir } from "node:os";
|
|
2102
|
-
import { join, resolve } from "node:path";
|
|
2325
|
+
import { join as join2, resolve } from "node:path";
|
|
2103
2326
|
|
|
2104
2327
|
// src/skills/loader.ts
|
|
2328
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2329
|
+
import { basename, dirname, join } from "node:path";
|
|
2105
2330
|
function parseSkillMetadata(content, skillPath) {
|
|
2106
2331
|
const frontmatter = extractFrontmatter(content);
|
|
2107
2332
|
if (!frontmatter) {
|
|
@@ -2190,6 +2415,32 @@ function parseYaml(yaml) {
|
|
|
2190
2415
|
}
|
|
2191
2416
|
return result;
|
|
2192
2417
|
}
|
|
2418
|
+
async function loadSkillBundle(skillDir) {
|
|
2419
|
+
const files = {};
|
|
2420
|
+
const name = basename(skillDir);
|
|
2421
|
+
async function readDirRecursive(dir, prefix = "") {
|
|
2422
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
2423
|
+
for (const entry of entries) {
|
|
2424
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
2425
|
+
const fullPath = join(dir, entry.name);
|
|
2426
|
+
if (entry.isDirectory()) {
|
|
2427
|
+
await readDirRecursive(fullPath, relativePath);
|
|
2428
|
+
} else {
|
|
2429
|
+
files[relativePath] = await readFile(fullPath, "utf-8");
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
await readDirRecursive(skillDir);
|
|
2434
|
+
return { name, files };
|
|
2435
|
+
}
|
|
2436
|
+
async function loadSkillBundles(skills) {
|
|
2437
|
+
const bundles = {};
|
|
2438
|
+
for (const skill of skills) {
|
|
2439
|
+
const skillDir = dirname(skill.path);
|
|
2440
|
+
bundles[skill.name] = await loadSkillBundle(skillDir);
|
|
2441
|
+
}
|
|
2442
|
+
return bundles;
|
|
2443
|
+
}
|
|
2193
2444
|
|
|
2194
2445
|
// src/skills/discovery.ts
|
|
2195
2446
|
var DEFAULT_SKILL_PATHS = [".skills", "~/.bashkit/skills"];
|
|
@@ -2212,7 +2463,7 @@ async function discoverSkills(options) {
|
|
|
2212
2463
|
}
|
|
2213
2464
|
function resolvePath(path, cwd) {
|
|
2214
2465
|
if (path.startsWith("~/")) {
|
|
2215
|
-
return
|
|
2466
|
+
return join2(homedir(), path.slice(2));
|
|
2216
2467
|
}
|
|
2217
2468
|
if (path.startsWith("/")) {
|
|
2218
2469
|
return path;
|
|
@@ -2222,18 +2473,18 @@ function resolvePath(path, cwd) {
|
|
|
2222
2473
|
async function scanDirectory(dirPath) {
|
|
2223
2474
|
const skills = [];
|
|
2224
2475
|
try {
|
|
2225
|
-
const entries = await
|
|
2476
|
+
const entries = await readdir2(dirPath, { withFileTypes: true });
|
|
2226
2477
|
for (const entry of entries) {
|
|
2227
2478
|
if (!entry.isDirectory()) {
|
|
2228
2479
|
continue;
|
|
2229
2480
|
}
|
|
2230
|
-
const skillPath =
|
|
2481
|
+
const skillPath = join2(dirPath, entry.name, "SKILL.md");
|
|
2231
2482
|
try {
|
|
2232
2483
|
const skillStat = await stat(skillPath);
|
|
2233
2484
|
if (!skillStat.isFile()) {
|
|
2234
2485
|
continue;
|
|
2235
2486
|
}
|
|
2236
|
-
const content = await
|
|
2487
|
+
const content = await readFile2(skillPath, "utf-8");
|
|
2237
2488
|
const metadata = parseSkillMetadata(content, skillPath);
|
|
2238
2489
|
if (metadata.name !== entry.name) {
|
|
2239
2490
|
console.warn(`Skill name "${metadata.name}" does not match folder name "${entry.name}" in ${skillPath}`);
|
|
@@ -2406,6 +2657,8 @@ export {
|
|
|
2406
2657
|
setupAgentEnvironment,
|
|
2407
2658
|
pruneMessagesByTokens,
|
|
2408
2659
|
parseSkillMetadata,
|
|
2660
|
+
loadSkillBundles,
|
|
2661
|
+
loadSkillBundle,
|
|
2409
2662
|
getContextStatus,
|
|
2410
2663
|
fetchSkills,
|
|
2411
2664
|
fetchSkill,
|
|
@@ -2420,6 +2673,7 @@ export {
|
|
|
2420
2673
|
createTodoWriteTool,
|
|
2421
2674
|
createTaskTool,
|
|
2422
2675
|
createSkillTool,
|
|
2676
|
+
createRedisCacheStore,
|
|
2423
2677
|
createReadTool,
|
|
2424
2678
|
createLocalSandbox,
|
|
2425
2679
|
createGrepTool,
|
|
@@ -2435,7 +2689,10 @@ export {
|
|
|
2435
2689
|
contextNeedsCompaction,
|
|
2436
2690
|
contextNeedsAttention,
|
|
2437
2691
|
compactConversation,
|
|
2692
|
+
cached,
|
|
2693
|
+
anthropicPromptCacheMiddlewareV2,
|
|
2438
2694
|
anthropicPromptCacheMiddleware,
|
|
2439
2695
|
MODEL_CONTEXT_LIMITS,
|
|
2696
|
+
LRUCacheStore,
|
|
2440
2697
|
DEFAULT_CONFIG
|
|
2441
2698
|
};
|
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
import type { LanguageModelV2Middleware } from "@ai-sdk/provider";
|
|
2
|
+
import type { LanguageModelMiddleware } from "ai";
|
|
2
3
|
/**
|
|
3
4
|
* Middleware that enables Anthropic's prompt caching feature.
|
|
5
|
+
* For AI SDK v5 (LanguageModelV2).
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { wrapLanguageModel } from 'ai';
|
|
10
|
+
* import { anthropic } from '@ai-sdk/anthropic';
|
|
11
|
+
* import { anthropicPromptCacheMiddlewareV2 } from 'bashkit';
|
|
12
|
+
*
|
|
13
|
+
* const model = wrapLanguageModel({
|
|
14
|
+
* model: anthropic('claude-sonnet-4-20250514'),
|
|
15
|
+
* middleware: anthropicPromptCacheMiddlewareV2,
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare const anthropicPromptCacheMiddlewareV2: LanguageModelV2Middleware;
|
|
20
|
+
/**
|
|
21
|
+
* Middleware that enables Anthropic's prompt caching feature.
|
|
22
|
+
* For AI SDK v6+ (LanguageModelV3).
|
|
4
23
|
*
|
|
5
24
|
* @example
|
|
6
25
|
* ```typescript
|
|
@@ -14,4 +33,4 @@ import type { LanguageModelV2Middleware } from "@ai-sdk/provider";
|
|
|
14
33
|
* });
|
|
15
34
|
* ```
|
|
16
35
|
*/
|
|
17
|
-
export declare const anthropicPromptCacheMiddleware:
|
|
36
|
+
export declare const anthropicPromptCacheMiddleware: LanguageModelMiddleware;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { anthropicPromptCacheMiddleware } from "./anthropic-cache";
|
|
1
|
+
export { anthropicPromptCacheMiddleware, anthropicPromptCacheMiddlewareV2, } from "./anthropic-cache";
|
package/dist/skills/index.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ export type { DiscoverSkillsOptions, SkillMetadata } from "./types";
|
|
|
2
2
|
export type { SkillBundle } from "./fetch";
|
|
3
3
|
export { discoverSkills } from "./discovery";
|
|
4
4
|
export { fetchSkill, fetchSkills } from "./fetch";
|
|
5
|
-
export { parseSkillMetadata } from "./loader";
|
|
5
|
+
export { parseSkillMetadata, loadSkillBundle, loadSkillBundles, } from "./loader";
|
|
6
6
|
export { skillsToXml } from "./xml";
|
package/dist/skills/loader.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SkillBundle } from "./fetch";
|
|
1
2
|
import type { SkillMetadata } from "./types";
|
|
2
3
|
/**
|
|
3
4
|
* Parses YAML frontmatter from a SKILL.md file content.
|
|
@@ -9,3 +10,38 @@ import type { SkillMetadata } from "./types";
|
|
|
9
10
|
* @throws Error if required fields (name, description) are missing
|
|
10
11
|
*/
|
|
11
12
|
export declare function parseSkillMetadata(content: string, skillPath: string): SkillMetadata;
|
|
13
|
+
/**
|
|
14
|
+
* Loads all files from a local skill directory as a SkillBundle.
|
|
15
|
+
* Recursively reads all files, preserving directory structure.
|
|
16
|
+
*
|
|
17
|
+
* @param skillDir - Absolute path to skill directory (containing SKILL.md)
|
|
18
|
+
* @returns SkillBundle with all files
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const bundle = await loadSkillBundle('/path/to/.skills/pdf-processing');
|
|
23
|
+
* // bundle.files = {
|
|
24
|
+
* // 'SKILL.md': '---\nname: pdf-processing\n...',
|
|
25
|
+
* // 'templates/report.md': '# Report Template...',
|
|
26
|
+
* // 'scripts/extract.sh': '#!/bin/bash...',
|
|
27
|
+
* // }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export declare function loadSkillBundle(skillDir: string): Promise<SkillBundle>;
|
|
31
|
+
/**
|
|
32
|
+
* Loads multiple discovered skills as bundles.
|
|
33
|
+
* Takes the output of discoverSkills() and loads all files for each skill.
|
|
34
|
+
*
|
|
35
|
+
* @param skills - Array of skill metadata from discoverSkills()
|
|
36
|
+
* @returns Record mapping skill names to their bundles
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const discovered = await discoverSkills();
|
|
41
|
+
* const bundles = await loadSkillBundles(discovered);
|
|
42
|
+
*
|
|
43
|
+
* // Use with setupAgentEnvironment
|
|
44
|
+
* await setupAgentEnvironment(sandbox, { skills: bundles });
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare function loadSkillBundles(skills: SkillMetadata[]): Promise<Record<string, SkillBundle>>;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { ToolConfig } from "../types";
|
|
2
1
|
export interface ExitPlanModeOutput {
|
|
3
2
|
message: string;
|
|
4
3
|
approved?: boolean;
|
|
@@ -6,6 +5,6 @@ export interface ExitPlanModeOutput {
|
|
|
6
5
|
export interface ExitPlanModeError {
|
|
7
6
|
error: string;
|
|
8
7
|
}
|
|
9
|
-
export declare function createExitPlanModeTool(
|
|
8
|
+
export declare function createExitPlanModeTool(onPlanSubmit?: (plan: string) => Promise<boolean> | boolean): import("ai").Tool<{
|
|
10
9
|
plan: string;
|
|
11
10
|
}, ExitPlanModeOutput | ExitPlanModeError>;
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -69,9 +69,9 @@ export type { SubagentEventData, SubagentStepEvent, SubagentTypeConfig, TaskErro
|
|
|
69
69
|
export { createTaskTool } from "./task";
|
|
70
70
|
export type { TodoItem, TodoState, TodoWriteError, TodoWriteOutput, } from "./todo-write";
|
|
71
71
|
export { createTodoWriteTool } from "./todo-write";
|
|
72
|
-
export type { WebFetchError, WebFetchOutput
|
|
72
|
+
export type { WebFetchError, WebFetchOutput } from "./web-fetch";
|
|
73
73
|
export { createWebFetchTool } from "./web-fetch";
|
|
74
|
-
export type { WebSearchError, WebSearchOutput, WebSearchResult,
|
|
74
|
+
export type { WebSearchError, WebSearchOutput, WebSearchResult, } from "./web-search";
|
|
75
75
|
export { createWebSearchTool } from "./web-search";
|
|
76
76
|
export type { WriteError, WriteOutput } from "./write";
|
|
77
77
|
export { createWriteTool } from "./write";
|
package/dist/tools/task.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ModelMessage, type LanguageModel, type PrepareStepFunction, type StepResult, type StopCondition, type UIMessageStreamWriter, type Tool, type ToolSet } from "ai";
|
|
2
|
+
import { z } from "zod";
|
|
2
3
|
export interface TaskOutput {
|
|
3
4
|
result: string;
|
|
4
5
|
usage?: {
|
|
@@ -12,6 +13,14 @@ export interface TaskOutput {
|
|
|
12
13
|
export interface TaskError {
|
|
13
14
|
error: string;
|
|
14
15
|
}
|
|
16
|
+
declare const taskInputSchema: z.ZodObject<{
|
|
17
|
+
description: z.ZodString;
|
|
18
|
+
prompt: z.ZodString;
|
|
19
|
+
subagent_type: z.ZodString;
|
|
20
|
+
system_prompt: z.ZodOptional<z.ZodString>;
|
|
21
|
+
tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
22
|
+
}, z.core.$strip>;
|
|
23
|
+
type TaskInput = z.infer<typeof taskInputSchema>;
|
|
15
24
|
/** Event emitted for each step a subagent takes */
|
|
16
25
|
export interface SubagentStepEvent {
|
|
17
26
|
subagentType: string;
|
|
@@ -55,4 +64,5 @@ export interface TaskToolConfig {
|
|
|
55
64
|
/** Optional stream writer for real-time subagent activity (uses streamText instead of generateText) */
|
|
56
65
|
streamWriter?: UIMessageStreamWriter;
|
|
57
66
|
}
|
|
58
|
-
export declare function createTaskTool(config: TaskToolConfig): Tool
|
|
67
|
+
export declare function createTaskTool(config: TaskToolConfig): Tool<TaskInput, TaskOutput | TaskError>;
|
|
68
|
+
export {};
|