@cuylabs/agent-core 0.9.0 → 0.11.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/README.md +33 -17
- package/dist/chunk-2O4MCSQS.js +780 -0
- package/dist/chunk-2TTOLHBT.js +198 -0
- package/dist/chunk-5FMSGQVX.js +281 -0
- package/dist/chunk-5NVVNXPQ.js +288 -0
- package/dist/{chunk-EKR6PKXU.js → chunk-6HZBHFOL.js} +3 -3
- package/dist/chunk-CJI7PVS2.js +58 -0
- package/dist/{chunk-WKHDSSXG.js → chunk-CMYN2RCB.js} +146 -46
- package/dist/chunk-FII65CN7.js +117 -0
- package/dist/{chunk-UHCJEM2E.js → chunk-ICZ66572.js} +13 -6
- package/dist/chunk-KYLPMBHD.js +316 -0
- package/dist/chunk-MXAP4UG6.js +2956 -0
- package/dist/{chunk-4QFNWPIF.js → chunk-N3VX7FEE.js} +35 -2
- package/dist/{chunk-MAZ5DY5B.js → chunk-NDZWXCBZ.js} +213 -78
- package/dist/{chunk-MHKK374K.js → chunk-Q742PSH3.js} +11 -27
- package/dist/{chunk-WGZAPU6N.js → chunk-QAL3OMI3.js} +15 -1
- package/dist/{chunk-UDCZ673N.js → chunk-RN6WZEUF.js} +27 -23
- package/dist/{chunk-ZXAKHMWH.js → chunk-ROTGCYDW.js} +22 -84
- package/dist/chunk-SPBFQXOT.js +0 -0
- package/dist/chunk-SSFBF3US.js +602 -0
- package/dist/chunk-SZ2XBPTW.js +8 -0
- package/dist/chunk-T4UIX5D7.js +115 -0
- package/dist/{chunk-IYWQOJMQ.js → chunk-TIHPYVAJ.js} +34 -34
- package/dist/{chunk-RKEW5WXI.js → chunk-TOTDGK3P.js} +1 -1
- package/dist/chunk-V4RFNEET.js +563 -0
- package/dist/chunk-VOUEJSW6.js +0 -0
- package/dist/{chunk-J4QDGZIA.js → chunk-WBPOZ7CL.js} +659 -275
- package/dist/chunk-X4VN4GIJ.js +185 -0
- package/dist/dispatch/index.d.ts +93 -0
- package/dist/dispatch/index.js +37 -0
- package/dist/events/index.d.ts +93 -0
- package/dist/events/index.js +6 -0
- package/dist/{runtime → execution}/index.d.ts +120 -35
- package/dist/{runtime → execution}/index.js +17 -11
- package/dist/index.d.ts +489 -115
- package/dist/index.js +1665 -462
- package/dist/inference/errors/index.js +1 -1
- package/dist/inference/index.d.ts +13 -21
- package/dist/inference/index.js +15 -12
- package/dist/instance-DzPiv6EK.d.ts +5723 -0
- package/dist/logger/index.d.ts +50 -0
- package/dist/logger/index.js +11 -0
- package/dist/mcp/index.d.ts +5 -9
- package/dist/mcp/index.js +2 -3
- package/dist/middleware/index.d.ts +10 -150
- package/dist/middleware/index.js +10 -2
- package/dist/model-messages-CJfwfzGe.d.ts +13 -0
- package/dist/models/index.d.ts +5 -2
- package/dist/models/index.js +2 -1
- package/dist/models/reasoning/index.js +2 -1
- package/dist/plugin/index.d.ts +55 -11
- package/dist/plugin/index.js +1 -1
- package/dist/profiles/index.d.ts +55 -0
- package/dist/{presets → profiles}/index.js +10 -10
- package/dist/prompt/index.d.ts +8 -13
- package/dist/safety/index.d.ts +109 -14
- package/dist/safety/index.js +59 -3
- package/dist/sandbox/index.d.ts +81 -0
- package/dist/sandbox/index.js +1 -0
- package/dist/skill/index.d.ts +10 -8
- package/dist/skill/index.js +2 -2
- package/dist/storage/index.d.ts +12 -4
- package/dist/storage/index.js +1 -1
- package/dist/subagents/index.d.ts +177 -0
- package/dist/subagents/index.js +78 -0
- package/dist/team/index.d.ts +544 -0
- package/dist/team/index.js +41 -0
- package/dist/tool/host/index.d.ts +41 -0
- package/dist/tool/host/index.js +10 -0
- package/dist/tool/index.d.ts +111 -21
- package/dist/tool/index.js +20 -13
- package/dist/{types-VQgymC1N.d.ts → types-Bj_J8u_W.d.ts} +44 -64
- package/dist/{types-CHiPh8U2.d.ts → types-C_LCeYNg.d.ts} +7 -7
- package/dist/types-RSCv7nQ4.d.ts +59 -0
- package/package.json +46 -47
- package/dist/builder-BgZ_j4Vs.d.ts +0 -35
- package/dist/chunk-5ARZJWD2.js +0 -259
- package/dist/chunk-DXFBQMXP.js +0 -53
- package/dist/chunk-H3FUYU52.js +0 -81
- package/dist/chunk-JLXG2SH7.js +0 -905
- package/dist/chunk-N7P4PN3O.js +0 -84
- package/dist/chunk-OFDKHNCX.js +0 -727
- package/dist/chunk-SDSBEQXG.js +0 -157
- package/dist/chunk-VEKUXUVF.js +0 -41
- package/dist/chunk-VNQBHPCT.js +0 -398
- package/dist/chunk-WWYYNWEW.js +0 -259
- package/dist/context/index.d.ts +0 -259
- package/dist/context/index.js +0 -26
- package/dist/events-CE72w8W4.d.ts +0 -149
- package/dist/host/index.d.ts +0 -45
- package/dist/host/index.js +0 -8
- package/dist/index-DQuTZ8xL.d.ts +0 -1335
- package/dist/messages-BYWGn8TY.d.ts +0 -110
- package/dist/presets/index.d.ts +0 -53
- package/dist/registry-DwYqsQkX.d.ts +0 -164
- package/dist/runner-CI-XeR16.d.ts +0 -91
- package/dist/scope/index.d.ts +0 -10
- package/dist/scope/index.js +0 -14
- package/dist/session-manager-KbYt2WUh.d.ts +0 -282
- package/dist/signal/index.d.ts +0 -28
- package/dist/signal/index.js +0 -6
- package/dist/sub-agent/index.d.ts +0 -24
- package/dist/sub-agent/index.js +0 -32
- package/dist/tool-CZWN3KbO.d.ts +0 -141
- package/dist/tool-DkhSCV2Y.d.ts +0 -145
- package/dist/tracker-DClqYqTj.d.ts +0 -96
- package/dist/tracking/index.d.ts +0 -111
- package/dist/tracking/index.js +0 -20
- package/dist/types-BfNpU8NS.d.ts +0 -270
- package/dist/types-BlOKk-Bb.d.ts +0 -330
- package/dist/types-BlZwmnuW.d.ts +0 -50
- package/dist/types-CQL-SvTn.d.ts +0 -29
- package/dist/types-CWm-7rvB.d.ts +0 -55
- package/dist/types-DTSkxakL.d.ts +0 -651
- package/dist/types-DmDwi2zI.d.ts +0 -339
- package/dist/types-YuWV4ag7.d.ts +0 -72
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// src/safety/patterns.ts
|
|
2
|
+
function matchApprovalPattern(pattern, value) {
|
|
3
|
+
if (pattern.length > 500) return false;
|
|
4
|
+
const p = pattern.toLowerCase();
|
|
5
|
+
const v = value.toLowerCase();
|
|
6
|
+
let pi = 0;
|
|
7
|
+
let vi = 0;
|
|
8
|
+
let starPi = -1;
|
|
9
|
+
let starVi = -1;
|
|
10
|
+
while (vi < v.length) {
|
|
11
|
+
if (pi < p.length && (p[pi] === "?" || p[pi] === v[vi])) {
|
|
12
|
+
pi++;
|
|
13
|
+
vi++;
|
|
14
|
+
} else if (pi < p.length && p[pi] === "*") {
|
|
15
|
+
starPi = pi;
|
|
16
|
+
starVi = vi;
|
|
17
|
+
pi++;
|
|
18
|
+
} else if (starPi !== -1) {
|
|
19
|
+
pi = starPi + 1;
|
|
20
|
+
starVi++;
|
|
21
|
+
vi = starVi;
|
|
22
|
+
} else {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
while (pi < p.length && p[pi] === "*") {
|
|
27
|
+
pi++;
|
|
28
|
+
}
|
|
29
|
+
return pi === p.length;
|
|
30
|
+
}
|
|
31
|
+
function extractApprovalPatterns(tool, args) {
|
|
32
|
+
if (!args || typeof args !== "object") {
|
|
33
|
+
return [tool];
|
|
34
|
+
}
|
|
35
|
+
const record = args;
|
|
36
|
+
if ("path" in record && typeof record.path === "string" || "filePath" in record && typeof record.filePath === "string") {
|
|
37
|
+
const path = record.path ?? record.filePath;
|
|
38
|
+
const dir = path.substring(0, path.lastIndexOf("/") + 1);
|
|
39
|
+
return [dir ? `${dir}*` : path];
|
|
40
|
+
}
|
|
41
|
+
if ("command" in record && typeof record.command === "string") {
|
|
42
|
+
const command = record.command.split(/\s+/)[0];
|
|
43
|
+
return [command];
|
|
44
|
+
}
|
|
45
|
+
if ("pattern" in record && typeof record.pattern === "string") {
|
|
46
|
+
return [record.pattern];
|
|
47
|
+
}
|
|
48
|
+
return [tool];
|
|
49
|
+
}
|
|
50
|
+
function describeApprovalOperation(tool, args) {
|
|
51
|
+
if (!args || typeof args !== "object") {
|
|
52
|
+
return `Execute ${tool}`;
|
|
53
|
+
}
|
|
54
|
+
const record = args;
|
|
55
|
+
switch (tool) {
|
|
56
|
+
case "read":
|
|
57
|
+
case "read_file":
|
|
58
|
+
return `Read file: ${record.path ?? record.filePath}`;
|
|
59
|
+
case "write":
|
|
60
|
+
case "write_file":
|
|
61
|
+
case "create_file":
|
|
62
|
+
return `Write file: ${record.path ?? record.filePath}`;
|
|
63
|
+
case "edit":
|
|
64
|
+
case "edit_file":
|
|
65
|
+
return `Edit file: ${record.path ?? record.filePath}`;
|
|
66
|
+
case "delete_file":
|
|
67
|
+
case "remove":
|
|
68
|
+
return `Delete: ${record.path}`;
|
|
69
|
+
case "bash":
|
|
70
|
+
case "shell": {
|
|
71
|
+
const command = String(record.command);
|
|
72
|
+
return `Run command: ${command.slice(0, 100)}${command.length > 100 ? "..." : ""}`;
|
|
73
|
+
}
|
|
74
|
+
case "grep":
|
|
75
|
+
return `Search for: ${record.pattern}`;
|
|
76
|
+
case "glob":
|
|
77
|
+
return `Find files: ${record.pattern}`;
|
|
78
|
+
default: {
|
|
79
|
+
const json = JSON.stringify(args);
|
|
80
|
+
const truncated = json.length > 50 ? `${json.slice(0, 50)}...` : json;
|
|
81
|
+
return `${tool}(${truncated})`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/types/tool.ts
|
|
87
|
+
function requiresToolHost(requirements) {
|
|
88
|
+
if (!requirements) return false;
|
|
89
|
+
if (requirements.required !== void 0) {
|
|
90
|
+
return requirements.required;
|
|
91
|
+
}
|
|
92
|
+
return requirements.filesystem !== void 0 && requirements.filesystem !== "none" ? true : requirements.shell === true;
|
|
93
|
+
}
|
|
94
|
+
function getRequiredToolHost(ctx, options = {}) {
|
|
95
|
+
if (ctx.host) {
|
|
96
|
+
return ctx.host;
|
|
97
|
+
}
|
|
98
|
+
const target = options.toolName ? `Tool "${options.toolName}"` : "This tool";
|
|
99
|
+
const usageSuffix = options.usage ? ` (${options.usage})` : "";
|
|
100
|
+
throw new Error(
|
|
101
|
+
`${target} requires an execution host${usageSuffix}, but none was configured.`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
function resolveCapability(cap, input, fallback) {
|
|
105
|
+
if (cap === void 0) return fallback;
|
|
106
|
+
if (typeof cap === "function") return cap(input);
|
|
107
|
+
return cap;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export {
|
|
111
|
+
matchApprovalPattern,
|
|
112
|
+
extractApprovalPatterns,
|
|
113
|
+
describeApprovalOperation,
|
|
114
|
+
requiresToolHost,
|
|
115
|
+
getRequiredToolHost,
|
|
116
|
+
resolveCapability
|
|
117
|
+
};
|
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
var STORAGE_VERSION = 1;
|
|
3
3
|
|
|
4
4
|
// src/storage/utils.ts
|
|
5
|
-
function logStorageWarning(message) {
|
|
6
|
-
console.warn(message);
|
|
7
|
-
}
|
|
8
5
|
function generateEntryId(existingIds) {
|
|
9
6
|
for (let i = 0; i < 100; i++) {
|
|
10
7
|
const id = crypto.randomUUID().slice(0, 8);
|
|
@@ -12,7 +9,7 @@ function generateEntryId(existingIds) {
|
|
|
12
9
|
}
|
|
13
10
|
return crypto.randomUUID();
|
|
14
11
|
}
|
|
15
|
-
function parseJSONL(content) {
|
|
12
|
+
function parseJSONL(content, logger) {
|
|
16
13
|
const entries = [];
|
|
17
14
|
const lines = content.split("\n");
|
|
18
15
|
for (const line of lines) {
|
|
@@ -21,7 +18,7 @@ function parseJSONL(content) {
|
|
|
21
18
|
try {
|
|
22
19
|
entries.push(JSON.parse(trimmed));
|
|
23
20
|
} catch {
|
|
24
|
-
|
|
21
|
+
logger?.warn("Skipping malformed JSONL line");
|
|
25
22
|
}
|
|
26
23
|
}
|
|
27
24
|
return entries;
|
|
@@ -207,7 +204,7 @@ function buildMessagesFromEntries(entries, leafId) {
|
|
|
207
204
|
for (const entry of path) {
|
|
208
205
|
if (entry.type === "compaction") {
|
|
209
206
|
messages.length = 0;
|
|
210
|
-
skipUntilId = entry.firstKeptEntryId;
|
|
207
|
+
skipUntilId = entry.firstKeptEntryId !== entry.id ? entry.firstKeptEntryId : void 0;
|
|
211
208
|
messages.push({
|
|
212
209
|
id: entry.id,
|
|
213
210
|
role: "system",
|
|
@@ -294,6 +291,16 @@ async function ensureStorageDirectory(directory) {
|
|
|
294
291
|
}
|
|
295
292
|
function getSessionFilePath(directory, extension, sessionId) {
|
|
296
293
|
const safeId = sessionId.replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
294
|
+
const needsHash = safeId !== sessionId;
|
|
295
|
+
if (needsHash) {
|
|
296
|
+
let hash = 2166136261;
|
|
297
|
+
for (let i = 0; i < sessionId.length; i++) {
|
|
298
|
+
hash ^= sessionId.charCodeAt(i);
|
|
299
|
+
hash = hash * 16777619 >>> 0;
|
|
300
|
+
}
|
|
301
|
+
const suffix = hash.toString(16).padStart(8, "0");
|
|
302
|
+
return join(directory, `${safeId}_${suffix}${extension}`);
|
|
303
|
+
}
|
|
297
304
|
return join(directory, `${safeId}${extension}`);
|
|
298
305
|
}
|
|
299
306
|
async function loadEntriesFromDisk(filePath, sessionId) {
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Tool
|
|
3
|
+
} from "./chunk-Q742PSH3.js";
|
|
4
|
+
|
|
5
|
+
// src/tool/registry.ts
|
|
6
|
+
function escapeRegExp(s) {
|
|
7
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8
|
+
}
|
|
9
|
+
var ToolRegistry = class {
|
|
10
|
+
tools = /* @__PURE__ */ new Map();
|
|
11
|
+
groups = /* @__PURE__ */ new Map();
|
|
12
|
+
// --------------------------------------------------------------------------
|
|
13
|
+
// Tool registration
|
|
14
|
+
// --------------------------------------------------------------------------
|
|
15
|
+
/**
|
|
16
|
+
* Register a tool. Throws if a tool with the same ID is already registered.
|
|
17
|
+
* Use `set()` for upsert semantics.
|
|
18
|
+
*/
|
|
19
|
+
register(tool) {
|
|
20
|
+
if (this.tools.has(tool.id)) {
|
|
21
|
+
throw new Error(`Tool '${tool.id}' is already registered`);
|
|
22
|
+
}
|
|
23
|
+
this.tools.set(tool.id, tool);
|
|
24
|
+
}
|
|
25
|
+
/** Register multiple tools (throws on duplicates). */
|
|
26
|
+
registerAll(tools) {
|
|
27
|
+
for (const tool of tools) {
|
|
28
|
+
this.register(tool);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Register or replace a tool (upsert). */
|
|
32
|
+
set(tool) {
|
|
33
|
+
this.tools.set(tool.id, tool);
|
|
34
|
+
}
|
|
35
|
+
/** Unregister a tool by ID. Returns `true` if it existed. */
|
|
36
|
+
unregister(id) {
|
|
37
|
+
return this.tools.delete(id);
|
|
38
|
+
}
|
|
39
|
+
/** Get a tool by ID. */
|
|
40
|
+
get(id) {
|
|
41
|
+
return this.tools.get(id);
|
|
42
|
+
}
|
|
43
|
+
/** Check if a tool is registered. */
|
|
44
|
+
has(id) {
|
|
45
|
+
return this.tools.has(id);
|
|
46
|
+
}
|
|
47
|
+
/** Get all tool IDs. */
|
|
48
|
+
ids() {
|
|
49
|
+
return Array.from(this.tools.keys());
|
|
50
|
+
}
|
|
51
|
+
/** Get all tools. */
|
|
52
|
+
all() {
|
|
53
|
+
return Array.from(this.tools.values());
|
|
54
|
+
}
|
|
55
|
+
/** Clear all tools and groups. */
|
|
56
|
+
clear() {
|
|
57
|
+
this.tools.clear();
|
|
58
|
+
this.groups.clear();
|
|
59
|
+
}
|
|
60
|
+
/** Number of registered tools. */
|
|
61
|
+
get size() {
|
|
62
|
+
return this.tools.size;
|
|
63
|
+
}
|
|
64
|
+
// --------------------------------------------------------------------------
|
|
65
|
+
// Group management
|
|
66
|
+
// --------------------------------------------------------------------------
|
|
67
|
+
/**
|
|
68
|
+
* Register a named group of tool IDs.
|
|
69
|
+
* The group name can be used in `resolve()` specs.
|
|
70
|
+
* Tool IDs don't need to be registered yet — resolution is lazy.
|
|
71
|
+
*/
|
|
72
|
+
registerGroup(name, toolIds) {
|
|
73
|
+
this.groups.set(name, toolIds);
|
|
74
|
+
}
|
|
75
|
+
/** Get tools in a group (only returns registered tools). */
|
|
76
|
+
getGroup(name) {
|
|
77
|
+
const ids = this.groups.get(name);
|
|
78
|
+
if (!ids) return void 0;
|
|
79
|
+
return ids.map((id) => this.tools.get(id)).filter((t) => t !== void 0);
|
|
80
|
+
}
|
|
81
|
+
/** Check if a group is registered. */
|
|
82
|
+
hasGroup(name) {
|
|
83
|
+
return this.groups.has(name);
|
|
84
|
+
}
|
|
85
|
+
/** List all group names. */
|
|
86
|
+
listGroups() {
|
|
87
|
+
return Array.from(this.groups.keys());
|
|
88
|
+
}
|
|
89
|
+
// --------------------------------------------------------------------------
|
|
90
|
+
// Deferred loading
|
|
91
|
+
// --------------------------------------------------------------------------
|
|
92
|
+
/**
|
|
93
|
+
* Partition registered tools into eager and deferred sets.
|
|
94
|
+
*
|
|
95
|
+
* Eager tools have their full schemas sent to the LLM on turn 1.
|
|
96
|
+
* Deferred tools are held back — the model discovers them via a
|
|
97
|
+
* search mechanism when needed, saving context window space.
|
|
98
|
+
*
|
|
99
|
+
* @returns `{ eager, deferred }` arrays of tool infos.
|
|
100
|
+
*/
|
|
101
|
+
partition() {
|
|
102
|
+
const eager = [];
|
|
103
|
+
const deferred = [];
|
|
104
|
+
for (const tool of this.tools.values()) {
|
|
105
|
+
if (tool.deferred) {
|
|
106
|
+
deferred.push(tool);
|
|
107
|
+
} else {
|
|
108
|
+
eager.push(tool);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return { eager, deferred };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Search deferred tools by keyword with multi-signal weighted scoring.
|
|
115
|
+
*
|
|
116
|
+
* Scoring per term:
|
|
117
|
+
* - Exact tool ID match: +10
|
|
118
|
+
* - Partial tool ID match (substring): +5
|
|
119
|
+
* - Keyword word-boundary match: +4
|
|
120
|
+
* - Keyword substring match: +2
|
|
121
|
+
*
|
|
122
|
+
* @param query - Space-separated search terms
|
|
123
|
+
* @param maxResults - Maximum results to return (default: 5)
|
|
124
|
+
* @returns Matching deferred tools sorted by relevance (highest score first)
|
|
125
|
+
*/
|
|
126
|
+
searchDeferred(query, maxResults = 5) {
|
|
127
|
+
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
128
|
+
if (terms.length === 0) return [];
|
|
129
|
+
const { deferred } = this.partition();
|
|
130
|
+
const scored = deferred.map((tool) => {
|
|
131
|
+
let score = 0;
|
|
132
|
+
const id = tool.id.toLowerCase();
|
|
133
|
+
const keywords = (tool.searchKeywords ?? []).join(" ").toLowerCase();
|
|
134
|
+
for (const term of terms) {
|
|
135
|
+
if (id === term) {
|
|
136
|
+
score += 10;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (id.includes(term)) {
|
|
140
|
+
score += 5;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const wordBoundary = new RegExp(`\\b${escapeRegExp(term)}\\b`);
|
|
144
|
+
if (wordBoundary.test(keywords)) {
|
|
145
|
+
score += 4;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (keywords.includes(term)) {
|
|
149
|
+
score += 2;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return { tool, score };
|
|
153
|
+
}).filter(({ score }) => score > 0).sort((a, b) => b.score - a.score);
|
|
154
|
+
return scored.slice(0, maxResults).map(({ tool }) => tool);
|
|
155
|
+
}
|
|
156
|
+
// --------------------------------------------------------------------------
|
|
157
|
+
// Resolution
|
|
158
|
+
// --------------------------------------------------------------------------
|
|
159
|
+
/**
|
|
160
|
+
* Resolve a `ToolSpec` to an array of tools.
|
|
161
|
+
*
|
|
162
|
+
* Supports group names, individual tool IDs, comma-separated strings,
|
|
163
|
+
* exclusions with `-` prefix, booleans, and pass-through arrays.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```typescript
|
|
167
|
+
* registry.resolve("read-only"); // group name
|
|
168
|
+
* registry.resolve("read,grep,glob"); // comma-separated IDs
|
|
169
|
+
* registry.resolve("all,-bash"); // all except bash
|
|
170
|
+
* registry.resolve(["read", "grep"]); // array of IDs
|
|
171
|
+
* registry.resolve(true); // all registered tools
|
|
172
|
+
* registry.resolve(false); // empty array
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
resolve(spec) {
|
|
176
|
+
if (spec === true) return this.all();
|
|
177
|
+
if (spec === false) return [];
|
|
178
|
+
if (Array.isArray(spec) && spec.length > 0 && typeof spec[0] !== "string") {
|
|
179
|
+
return spec;
|
|
180
|
+
}
|
|
181
|
+
const tokens = Array.isArray(spec) ? spec : spec.split(",").map((s) => s.trim()).filter(Boolean);
|
|
182
|
+
const includes = [];
|
|
183
|
+
const excludes = [];
|
|
184
|
+
for (const token of tokens) {
|
|
185
|
+
if (token.startsWith("-")) {
|
|
186
|
+
excludes.push(token.slice(1));
|
|
187
|
+
} else {
|
|
188
|
+
includes.push(token);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const result = /* @__PURE__ */ new Map();
|
|
192
|
+
if (includes.length === 0 && excludes.length > 0) {
|
|
193
|
+
for (const [id, tool] of this.tools) {
|
|
194
|
+
result.set(id, tool);
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
for (const token of includes) {
|
|
198
|
+
if (token === "all") {
|
|
199
|
+
for (const [id, tool] of this.tools) {
|
|
200
|
+
result.set(id, tool);
|
|
201
|
+
}
|
|
202
|
+
} else if (this.groups.has(token)) {
|
|
203
|
+
const ids = this.groups.get(token);
|
|
204
|
+
for (const id of ids) {
|
|
205
|
+
const tool = this.tools.get(id);
|
|
206
|
+
if (tool) result.set(id, tool);
|
|
207
|
+
}
|
|
208
|
+
} else if (this.tools.has(token)) {
|
|
209
|
+
result.set(token, this.tools.get(token));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
for (const id of excludes) {
|
|
214
|
+
if (this.groups.has(id)) {
|
|
215
|
+
const ids = this.groups.get(id);
|
|
216
|
+
for (const gid of ids) {
|
|
217
|
+
result.delete(gid);
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
result.delete(id);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return Array.from(result.values());
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
var defaultRegistry = new ToolRegistry();
|
|
227
|
+
|
|
228
|
+
// src/tool/tool-search.ts
|
|
229
|
+
import { z } from "zod";
|
|
230
|
+
function createToolSearchTool(options) {
|
|
231
|
+
const { registry, maxResults = 5, onMatch } = options;
|
|
232
|
+
return Tool.define("tool_search", {
|
|
233
|
+
description: "Search for additional tools by keyword. Some tools are not loaded by default to save context. Call this when you need a capability that isn't available in your current tool set. Returns matching tool names and descriptions.",
|
|
234
|
+
parameters: z.object({
|
|
235
|
+
query: z.string().describe(
|
|
236
|
+
"Space-separated keywords describing the tool you need (e.g. 'jupyter notebook cells' or 'docker container'). Use 'select:tool_name' to load a specific tool by name, or 'select:foo,bar' for multiple."
|
|
237
|
+
)
|
|
238
|
+
}),
|
|
239
|
+
capabilities: {
|
|
240
|
+
parallelSafe: true,
|
|
241
|
+
readOnly: true,
|
|
242
|
+
riskLevel: "safe"
|
|
243
|
+
},
|
|
244
|
+
execute: async ({ query }, ctx) => {
|
|
245
|
+
if (query.startsWith("select:")) {
|
|
246
|
+
const ids = query.slice(7).split(",").map((s) => s.trim()).filter(Boolean);
|
|
247
|
+
const found = [];
|
|
248
|
+
const missing = [];
|
|
249
|
+
for (const id of ids) {
|
|
250
|
+
const tool = registry.get(id);
|
|
251
|
+
if (tool) {
|
|
252
|
+
found.push(tool);
|
|
253
|
+
} else {
|
|
254
|
+
missing.push(id);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (found.length > 0) onMatch(found);
|
|
258
|
+
const summaries2 = [];
|
|
259
|
+
for (const t of found) {
|
|
260
|
+
try {
|
|
261
|
+
const init = await t.init({ cwd: ctx.cwd });
|
|
262
|
+
summaries2.push(`- **${t.id}**: ${init.description}`);
|
|
263
|
+
} catch {
|
|
264
|
+
summaries2.push(`- **${t.id}**: (description unavailable)`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (missing.length > 0) {
|
|
268
|
+
summaries2.push(`
|
|
269
|
+
Not found: ${missing.join(", ")}`);
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
title: `Loaded ${found.length} tool(s)`,
|
|
273
|
+
output: found.length > 0 ? summaries2.join("\n") + "\n\nThese tools are now available for use." : `No tools found: ${missing.join(", ")}`,
|
|
274
|
+
metadata: {
|
|
275
|
+
matchCount: found.length,
|
|
276
|
+
toolIds: found.map((t) => t.id)
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
const matches = registry.searchDeferred(query, maxResults);
|
|
281
|
+
if (matches.length === 0) {
|
|
282
|
+
return {
|
|
283
|
+
title: `No tools found for "${query}"`,
|
|
284
|
+
output: `No matching tools found for query: "${query}". Try different keywords.`,
|
|
285
|
+
metadata: { matchCount: 0, toolIds: [] }
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
const summaries = [];
|
|
289
|
+
for (const tool of matches) {
|
|
290
|
+
try {
|
|
291
|
+
const init = await tool.init({ cwd: ctx.cwd });
|
|
292
|
+
summaries.push(`- **${tool.id}**: ${init.description}`);
|
|
293
|
+
} catch {
|
|
294
|
+
summaries.push(`- **${tool.id}**: (description unavailable)`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
onMatch(matches);
|
|
298
|
+
return {
|
|
299
|
+
title: `Found ${matches.length} tool(s)`,
|
|
300
|
+
output: `Found ${matches.length} tool(s) matching "${query}":
|
|
301
|
+
|
|
302
|
+
` + summaries.join("\n") + "\n\nThese tools are now available for use.",
|
|
303
|
+
metadata: {
|
|
304
|
+
matchCount: matches.length,
|
|
305
|
+
toolIds: matches.map((t) => t.id)
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export {
|
|
313
|
+
ToolRegistry,
|
|
314
|
+
defaultRegistry,
|
|
315
|
+
createToolSearchTool
|
|
316
|
+
};
|