@nyte-ai/plugin 0.0.25-beta.1 → 0.0.25-nightly.20261025.2
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/THIRD-PARTY-NOTICES.md +13 -5
- package/dist/chunks/bash-description-0ah30s12.js +33 -0
- package/dist/chunks/bash-description-4cdbmar0.js +124 -0
- package/dist/chunks/bash-description-4h0hnsv4.js +68 -0
- package/dist/chunks/bash-description-5dpf5hpt.js +724 -0
- package/dist/chunks/bash-description-9gw9ets8.js +162 -0
- package/dist/chunks/bash-description-b78pre8c.js +147 -0
- package/dist/chunks/{bash-description-eqp7ap2w.js → bash-description-vg0xecwm.js} +16 -5
- package/dist/src/acp/connection.js +1 -1
- package/dist/src/acp/index.js +1 -1
- package/dist/src/claude-code-sdk/index.js +1 -0
- package/dist/src/codemode.js +148 -41
- package/dist/src/core.d.ts +1 -1
- package/dist/src/rename.d.ts +7 -22
- package/dist/src/rename.js +115 -48
- package/dist/src/subagents.d.ts +30 -0
- package/dist/src/subagents.js +323 -0
- package/dist/src/tool-search.d.ts +49 -0
- package/dist/src/tool-search.js +14 -0
- package/dist/src/web/body.d.ts +3 -0
- package/dist/src/web/body.js +6 -0
- package/dist/src/web/fetch.d.ts +16 -0
- package/dist/src/web/fetch.js +8 -0
- package/dist/src/web/html-markdown.d.ts +9 -0
- package/dist/src/web/html-markdown.js +10 -0
- package/dist/src/web/index.d.ts +24 -0
- package/dist/src/web/index.js +93 -0
- package/dist/src/web/providers.d.ts +60 -0
- package/dist/src/web/providers.js +11 -0
- package/dist/src/web/search.d.ts +21 -0
- package/dist/src/web/search.js +6 -0
- package/package.json +16 -7
- package/dist/src/web-search.d.ts +0 -78
- package/dist/src/web-search.js +0 -219
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readCapped2
|
|
3
|
+
} from "./bash-description-0ah30s12.js";
|
|
4
|
+
|
|
5
|
+
// src/web/providers.ts
|
|
6
|
+
import { Type } from "typebox";
|
|
7
|
+
import { Value } from "typebox/value";
|
|
8
|
+
var PROVIDERS2 = [
|
|
9
|
+
{ id: "exa", name: "Exa" },
|
|
10
|
+
{ id: "tavily", name: "Tavily" },
|
|
11
|
+
{ id: "firecrawl", name: "Firecrawl" },
|
|
12
|
+
{ id: "parallel", name: "Parallel" }
|
|
13
|
+
];
|
|
14
|
+
var MAX_RESPONSE_BYTES = 256 * 1024;
|
|
15
|
+
var textBlocks = Type.Object({
|
|
16
|
+
content: Type.Array(Type.Object({ type: Type.String(), text: Type.Optional(Type.String()) }))
|
|
17
|
+
});
|
|
18
|
+
var tavilyResponse = Type.Object({
|
|
19
|
+
results: Type.Array(Type.Object({ url: Type.String(), title: Type.String(), content: Type.String() }))
|
|
20
|
+
});
|
|
21
|
+
var firecrawlResponse = Type.Object({
|
|
22
|
+
data: Type.Object({
|
|
23
|
+
web: Type.Array(Type.Object({
|
|
24
|
+
url: Type.String(),
|
|
25
|
+
title: Type.Optional(Type.Union([Type.String(), Type.Null()])),
|
|
26
|
+
description: Type.Optional(Type.Union([Type.String(), Type.Null()]))
|
|
27
|
+
}))
|
|
28
|
+
})
|
|
29
|
+
});
|
|
30
|
+
var parallelResponse = Type.Object({
|
|
31
|
+
structuredContent: Type.Object({
|
|
32
|
+
results: Type.Array(Type.Object({
|
|
33
|
+
url: Type.String(),
|
|
34
|
+
title: Type.Optional(Type.Union([Type.String(), Type.Null()])),
|
|
35
|
+
publish_date: Type.Optional(Type.Union([Type.String(), Type.Null()])),
|
|
36
|
+
excerpts: Type.Array(Type.String())
|
|
37
|
+
}))
|
|
38
|
+
})
|
|
39
|
+
});
|
|
40
|
+
var adapters2 = {
|
|
41
|
+
async tavily({ query, key, fetch, signal }) {
|
|
42
|
+
const response = await fetch("https://api.tavily.com/search", {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: {
|
|
45
|
+
"Content-Type": "application/json",
|
|
46
|
+
...key === undefined ? { "X-Tavily-Access-Mode": "keyless" } : { Authorization: `Bearer ${key}` }
|
|
47
|
+
},
|
|
48
|
+
body: JSON.stringify({ query, search_depth: "basic", max_results: 8 }),
|
|
49
|
+
signal
|
|
50
|
+
});
|
|
51
|
+
const body = parse(await answered(response, "tavily"));
|
|
52
|
+
return Value.Check(tavilyResponse, body) ? body.results.map((hit) => ({ url: hit.url, title: hit.title, content: hit.content })) : [];
|
|
53
|
+
},
|
|
54
|
+
async exa({ query, key, fetch, signal }) {
|
|
55
|
+
const endpoint = new URL("https://mcp.exa.ai/mcp");
|
|
56
|
+
if (key !== undefined)
|
|
57
|
+
endpoint.searchParams.set("exaApiKey", key);
|
|
58
|
+
const result = await callTool(fetch, endpoint, "exa", undefined, "web_search_exa", {
|
|
59
|
+
query,
|
|
60
|
+
numResults: 8
|
|
61
|
+
}, signal);
|
|
62
|
+
const text = Value.Check(textBlocks, result) ? result.content.find((block) => block.text !== undefined)?.text ?? "" : "";
|
|
63
|
+
return text.split(/\n\n---\n\n/).flatMap((block) => {
|
|
64
|
+
const url = field(block, "URL");
|
|
65
|
+
const published = field(block, "Published");
|
|
66
|
+
return url === undefined ? [] : [
|
|
67
|
+
{
|
|
68
|
+
url,
|
|
69
|
+
title: field(block, "Title"),
|
|
70
|
+
content: block.match(/^(?:Highlights|Text):\s*\n?([\s\S]*)$/m)?.[1]?.trim(),
|
|
71
|
+
published: published === undefined ? undefined : Date.parse(published)
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
async firecrawl({ query, key, fetch, signal }) {
|
|
77
|
+
const result = await callTool(fetch, new URL("https://mcp.firecrawl.dev/v2/mcp"), "firecrawl", key, "firecrawl_search", { query, limit: 8 }, signal);
|
|
78
|
+
const text = Value.Check(textBlocks, result) ? result.content.find((block) => block.text !== undefined)?.text ?? "" : "";
|
|
79
|
+
const body = parse(text);
|
|
80
|
+
return Value.Check(firecrawlResponse, body) ? body.data.web.map((hit) => ({
|
|
81
|
+
url: hit.url,
|
|
82
|
+
title: hit.title ?? undefined,
|
|
83
|
+
content: hit.description ?? undefined
|
|
84
|
+
})) : [];
|
|
85
|
+
},
|
|
86
|
+
async parallel({ query, key, fetch, signal }) {
|
|
87
|
+
const result = await callTool(fetch, new URL("https://search.parallel.ai/mcp"), "parallel", key, "web_search", { objective: query, search_queries: [query] }, signal);
|
|
88
|
+
return Value.Check(parallelResponse, result) ? result.structuredContent.results.map((hit) => ({
|
|
89
|
+
url: hit.url,
|
|
90
|
+
title: hit.title ?? undefined,
|
|
91
|
+
content: hit.excerpts.length === 0 ? undefined : hit.excerpts.join(`
|
|
92
|
+
|
|
93
|
+
`),
|
|
94
|
+
published: hit.publish_date === undefined || hit.publish_date === null ? undefined : Date.parse(hit.publish_date)
|
|
95
|
+
})) : [];
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
function shippedProviders2(fetch, key) {
|
|
99
|
+
return PROVIDERS2.map((provider) => ({
|
|
100
|
+
id: provider.id,
|
|
101
|
+
name: provider.name,
|
|
102
|
+
search: async (input) => adapters2[provider.id]({
|
|
103
|
+
query: input.query,
|
|
104
|
+
key: await key(provider.id),
|
|
105
|
+
fetch,
|
|
106
|
+
signal: input.signal
|
|
107
|
+
})
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
async function callTool(fetch, url, provider, key, tool, args, signal) {
|
|
111
|
+
const response = await fetch(url, {
|
|
112
|
+
method: "POST",
|
|
113
|
+
headers: {
|
|
114
|
+
Accept: "application/json, text/event-stream",
|
|
115
|
+
"Content-Type": "application/json",
|
|
116
|
+
...key === undefined ? {} : { Authorization: `Bearer ${key}` }
|
|
117
|
+
},
|
|
118
|
+
body: JSON.stringify({
|
|
119
|
+
jsonrpc: "2.0",
|
|
120
|
+
id: 1,
|
|
121
|
+
method: "tools/call",
|
|
122
|
+
params: { name: tool, arguments: args }
|
|
123
|
+
}),
|
|
124
|
+
signal
|
|
125
|
+
});
|
|
126
|
+
const body = await answered(response, provider);
|
|
127
|
+
const frames = [
|
|
128
|
+
body,
|
|
129
|
+
...body.split(`
|
|
130
|
+
`).flatMap((line) => line.startsWith("data: ") ? [line.slice(6)] : [])
|
|
131
|
+
];
|
|
132
|
+
for (const frame of frames) {
|
|
133
|
+
const message = parse(frame);
|
|
134
|
+
if (typeof message === "object" && message !== null && "result" in message)
|
|
135
|
+
return message.result;
|
|
136
|
+
}
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
async function answered(response, provider) {
|
|
140
|
+
const body = new TextDecoder().decode(await readCapped2(response, MAX_RESPONSE_BYTES));
|
|
141
|
+
const name = PROVIDERS2.find((known) => known.id === provider)?.name ?? provider;
|
|
142
|
+
if (response.status === 401)
|
|
143
|
+
throw new Error(`${name} refused the search (HTTP 401). Set a key with /websearch-key ${provider} <key>.`);
|
|
144
|
+
if (response.status === 429)
|
|
145
|
+
throw new Error(`${name} is rate limiting searches (HTTP 429). Try again later, or set a key with /websearch-key ${provider} <key>.`);
|
|
146
|
+
if (!response.ok)
|
|
147
|
+
throw new Error(`HTTP ${response.status}: ${body.trim().slice(0, 200) || response.statusText}`);
|
|
148
|
+
return body;
|
|
149
|
+
}
|
|
150
|
+
function parse(text) {
|
|
151
|
+
try {
|
|
152
|
+
return JSON.parse(text);
|
|
153
|
+
} catch {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function field(block, name) {
|
|
158
|
+
const value = block.match(new RegExp(`^${name}:\\s*(.+)$`, "m"))?.[1]?.trim();
|
|
159
|
+
return value === undefined || value === "" || value === "N/A" ? undefined : value;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export { PROVIDERS2, adapters2, shippedProviders2 };
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// src/tool-search.ts
|
|
2
|
+
import { isJsonObject, standard } from "@nyte-ai/schema";
|
|
3
|
+
import { Type } from "typebox";
|
|
4
|
+
var DEFAULT_SEARCH_LIMIT2 = 8;
|
|
5
|
+
var STOP_WORDS = new Set([
|
|
6
|
+
"a",
|
|
7
|
+
"an",
|
|
8
|
+
"and",
|
|
9
|
+
"are",
|
|
10
|
+
"as",
|
|
11
|
+
"at",
|
|
12
|
+
"be",
|
|
13
|
+
"by",
|
|
14
|
+
"for",
|
|
15
|
+
"from",
|
|
16
|
+
"in",
|
|
17
|
+
"is",
|
|
18
|
+
"it",
|
|
19
|
+
"of",
|
|
20
|
+
"on",
|
|
21
|
+
"or",
|
|
22
|
+
"that",
|
|
23
|
+
"the",
|
|
24
|
+
"this",
|
|
25
|
+
"to",
|
|
26
|
+
"with"
|
|
27
|
+
]);
|
|
28
|
+
var K1 = 1.2;
|
|
29
|
+
var B = 0.75;
|
|
30
|
+
var DESCRIPTION = `Searches over deferred tool metadata with BM25 and exposes matching tools for the next model call.
|
|
31
|
+
|
|
32
|
+
Some of the tools, such as tools of MCP servers, may not have been provided to you upfront, and you should use this tool (\`tool_search\`) to search for the required tools. For MCP tool discovery, always use \`tool_search\`.`;
|
|
33
|
+
var parameters = standard(Type.Object({
|
|
34
|
+
query: Type.String({ description: "Search query for deferred tools." }),
|
|
35
|
+
limit: Type.Optional(Type.Integer({
|
|
36
|
+
minimum: 1,
|
|
37
|
+
description: `Maximum number of tools to return. Defaults to ${DEFAULT_SEARCH_LIMIT2}.`
|
|
38
|
+
}))
|
|
39
|
+
}));
|
|
40
|
+
var output = standard(Type.Object({ loaded: Type.Array(Type.String()) }));
|
|
41
|
+
var tool_search_default = {
|
|
42
|
+
id: "tool-search",
|
|
43
|
+
activate(api) {
|
|
44
|
+
const namespaces = () => new Map(api.contribute.tools.list().flatMap((definition) => definition.namespace === undefined ? [] : [[definition.name, definition.namespace]]));
|
|
45
|
+
const search = {
|
|
46
|
+
name: "tool_search",
|
|
47
|
+
description: DESCRIPTION,
|
|
48
|
+
parameters,
|
|
49
|
+
output,
|
|
50
|
+
exposure: "model-only",
|
|
51
|
+
class: () => ({ kind: "custom", label: "Searching tools" }),
|
|
52
|
+
effects: "none",
|
|
53
|
+
approval: "never",
|
|
54
|
+
replay: "safe",
|
|
55
|
+
loads: (result) => result.loaded,
|
|
56
|
+
async execute(args, invocation) {
|
|
57
|
+
if (args.query.trim() === "")
|
|
58
|
+
throw new Error("query must not be empty");
|
|
59
|
+
const loaded = new Set(await invocation.tools.loaded());
|
|
60
|
+
const found = namespaces();
|
|
61
|
+
const candidates = invocation.tools.list().filter((info) => (info.exposure === "code" || info.exposure === "deferred") && !loaded.has(info.name));
|
|
62
|
+
const names = rankTools2(args.query, candidates.map((info) => createToolSearchDocument2(info, found.get(info.name))), args.limit ?? DEFAULT_SEARCH_LIMIT2);
|
|
63
|
+
const lines = names.map((name) => {
|
|
64
|
+
const [line = ""] = (candidates.find((info) => info.name === name)?.description ?? "").trim().split(/\r?\n/);
|
|
65
|
+
return `- ${name}: ${line}`;
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
content: [
|
|
69
|
+
{
|
|
70
|
+
kind: "text",
|
|
71
|
+
text: names.length === 0 ? "No matching tools found." : `Loaded ${names.length} tool${names.length === 1 ? "" : "s"}. They are available from your next call:
|
|
72
|
+
${lines.join(`
|
|
73
|
+
`)}`
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
output: { loaded: [...names] }
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
api.contribute.tools.add(search);
|
|
81
|
+
api.hook("transform_transcript", (transcript) => {
|
|
82
|
+
const declared = new Set(transcript.tools.map((info) => info.name));
|
|
83
|
+
const pending = api.contribute.tools.list().some((definition) => definition.exposure === "deferred" && !declared.has(definition.name));
|
|
84
|
+
return pending ? transcript : { ...transcript, tools: transcript.tools.filter((info) => info.name !== search.name) };
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
function tokenize2(text) {
|
|
89
|
+
return text.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter((term) => term.length > 0 && !STOP_WORDS.has(term)).map(stem);
|
|
90
|
+
}
|
|
91
|
+
function stem(term) {
|
|
92
|
+
if (term.length > 4 && term.endsWith("ies"))
|
|
93
|
+
return `${term.slice(0, -3)}y`;
|
|
94
|
+
if (term.length > 4 && /(ches|shes|sses|xes|zes)$/.test(term))
|
|
95
|
+
return term.slice(0, -2);
|
|
96
|
+
if (term.length > 3 && term.endsWith("s") && !term.endsWith("ss"))
|
|
97
|
+
return term.slice(0, -1);
|
|
98
|
+
return term;
|
|
99
|
+
}
|
|
100
|
+
function createToolSearchDocument2(tool, namespace) {
|
|
101
|
+
const parts = [
|
|
102
|
+
tool.name,
|
|
103
|
+
tool.name.replaceAll("_", " "),
|
|
104
|
+
tool.description,
|
|
105
|
+
...describeSchema(tool.parameters),
|
|
106
|
+
...namespace === undefined ? [] : [namespace.name, namespace.description ?? "", namespace.instructions ?? ""]
|
|
107
|
+
];
|
|
108
|
+
return { name: tool.name, text: parts.filter((part) => part.trim() !== "").join(" ") };
|
|
109
|
+
}
|
|
110
|
+
function describeSchema(schema) {
|
|
111
|
+
if (schema === undefined || !isJsonObject(schema))
|
|
112
|
+
return [];
|
|
113
|
+
const properties = isJsonObject(schema.properties) ? Object.entries(schema.properties) : [];
|
|
114
|
+
return [
|
|
115
|
+
...typeof schema.description === "string" ? [schema.description] : [],
|
|
116
|
+
...properties.flatMap(([name, property]) => [name, ...describeSchema(property)]),
|
|
117
|
+
...describeSchema(schema.items),
|
|
118
|
+
...["anyOf", "oneOf", "allOf"].flatMap((key) => {
|
|
119
|
+
const variants = schema[key];
|
|
120
|
+
return Array.isArray(variants) ? variants.flatMap(describeSchema) : [];
|
|
121
|
+
})
|
|
122
|
+
];
|
|
123
|
+
}
|
|
124
|
+
function rankTools2(query, documents, limit) {
|
|
125
|
+
const terms = [...new Set(tokenize2(query))];
|
|
126
|
+
if (terms.length === 0 || documents.length === 0)
|
|
127
|
+
return [];
|
|
128
|
+
const indexed = documents.map((document) => {
|
|
129
|
+
const tokens = tokenize2(document.text);
|
|
130
|
+
return { name: document.name, length: tokens.length, counts: Map.groupBy(tokens, (t) => t) };
|
|
131
|
+
});
|
|
132
|
+
const average = indexed.reduce((sum, entry) => sum + entry.length, 0) / indexed.length || 1;
|
|
133
|
+
const idf = new Map(terms.map((term) => {
|
|
134
|
+
const frequency = indexed.filter((entry) => entry.counts.has(term)).length;
|
|
135
|
+
return [term, Math.log(1 + (indexed.length - frequency + 0.5) / (frequency + 0.5))];
|
|
136
|
+
}));
|
|
137
|
+
return indexed.map((entry) => ({
|
|
138
|
+
name: entry.name,
|
|
139
|
+
score: terms.reduce((score, term) => {
|
|
140
|
+
const count = entry.counts.get(term)?.length ?? 0;
|
|
141
|
+
const norm = K1 * (1 - B + B * entry.length / average);
|
|
142
|
+
return score + (idf.get(term) ?? 0) * (count * (K1 + 1)) / (count + norm);
|
|
143
|
+
}, 0)
|
|
144
|
+
})).filter((match) => match.score > 0).toSorted((left, right) => right.score - left.score).slice(0, limit).map((match) => match.name);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export { DEFAULT_SEARCH_LIMIT2, tool_search_default, tokenize2, createToolSearchDocument2, rankTools2 };
|
|
@@ -15,7 +15,7 @@ import { processEnvironment } from "@nyte-ai/core";
|
|
|
15
15
|
// package.json
|
|
16
16
|
var package_default = {
|
|
17
17
|
name: "@nyte-ai/plugin",
|
|
18
|
-
version: "0.0.25-
|
|
18
|
+
version: "0.0.25-nightly.20261025.2",
|
|
19
19
|
private: false,
|
|
20
20
|
license: "MIT",
|
|
21
21
|
repository: {
|
|
@@ -46,8 +46,10 @@ var package_default = {
|
|
|
46
46
|
"./core/openai-compaction": "./src/openai-compaction.ts",
|
|
47
47
|
"./core/question": "./src/question/question.ts",
|
|
48
48
|
"./core/rename": "./src/rename.ts",
|
|
49
|
+
"./core/subagents": "./src/subagents.ts",
|
|
49
50
|
"./core/telemetry": "./src/telemetry.ts",
|
|
50
|
-
"./core/
|
|
51
|
+
"./core/tool-search": "./src/tool-search.ts",
|
|
52
|
+
"./core/web": "./src/web/index.ts",
|
|
51
53
|
"./client": "./src/client.ts",
|
|
52
54
|
"./client/mount": "./src/client/mount.ts",
|
|
53
55
|
"./client/notifications": "./src/client/notifications.ts",
|
|
@@ -127,13 +129,21 @@ var package_default = {
|
|
|
127
129
|
types: "./dist/src/rename.d.ts",
|
|
128
130
|
import: "./dist/src/rename.js"
|
|
129
131
|
},
|
|
132
|
+
"./core/subagents": {
|
|
133
|
+
types: "./dist/src/subagents.d.ts",
|
|
134
|
+
import: "./dist/src/subagents.js"
|
|
135
|
+
},
|
|
130
136
|
"./core/telemetry": {
|
|
131
137
|
types: "./dist/src/telemetry.d.ts",
|
|
132
138
|
import: "./dist/src/telemetry.js"
|
|
133
139
|
},
|
|
134
|
-
"./core/
|
|
135
|
-
types: "./dist/src/
|
|
136
|
-
import: "./dist/src/
|
|
140
|
+
"./core/tool-search": {
|
|
141
|
+
types: "./dist/src/tool-search.d.ts",
|
|
142
|
+
import: "./dist/src/tool-search.js"
|
|
143
|
+
},
|
|
144
|
+
"./core/web": {
|
|
145
|
+
types: "./dist/src/web/index.d.ts",
|
|
146
|
+
import: "./dist/src/web/index.js"
|
|
137
147
|
},
|
|
138
148
|
"./client": {
|
|
139
149
|
types: "./dist/src/client.d.ts",
|
|
@@ -206,6 +216,7 @@ var package_default = {
|
|
|
206
216
|
"@opentelemetry/sdk-trace": "^3.0.0",
|
|
207
217
|
"@opentelemetry/semantic-conventions": "^1.43.0",
|
|
208
218
|
"es-module-lexer": "^3.0.4",
|
|
219
|
+
htmlparser2: "^8.0.2",
|
|
209
220
|
sucrase: "^3.35.1",
|
|
210
221
|
typebox: "^1.4.2",
|
|
211
222
|
zod: "^4.6.7"
|
package/dist/src/acp/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import"../../chunks/bash-description-svrhh5a5.js";
|
|
9
9
|
import {
|
|
10
10
|
AcpProcess2
|
|
11
|
-
} from "../../chunks/bash-description-
|
|
11
|
+
} from "../../chunks/bash-description-vg0xecwm.js";
|
|
12
12
|
import"../../chunks/bash-description-4vz9pxyx.js";
|
|
13
13
|
import {
|
|
14
14
|
registryEntries2,
|
package/dist/src/codemode.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_SEARCH_LIMIT2,
|
|
3
|
+
createToolSearchDocument2,
|
|
4
|
+
rankTools2
|
|
5
|
+
} from "../chunks/bash-description-b78pre8c.js";
|
|
6
|
+
|
|
1
7
|
// src/codemode.ts
|
|
2
8
|
import { CodemodeSandbox } from "@earendil-works/pi-codemode";
|
|
3
|
-
import { renderToolSample } from "@earendil-works/pi-codemode/declarations";
|
|
9
|
+
import { renderToolSample, toCodemodeIdentifier } from "@earendil-works/pi-codemode/declarations";
|
|
4
10
|
import { CODEMODE_SOURCE_GRAMMAR, parseCodemodeSource } from "@earendil-works/pi-codemode/source";
|
|
11
|
+
import { estimateTextTokens } from "@nyte-ai/ai/utils/estimate";
|
|
5
12
|
import { dev } from "@nyte-ai/core";
|
|
6
13
|
import { isJson, isJsonObject, SdkError, standard } from "@nyte-ai/schema";
|
|
7
14
|
import { Type } from "typebox";
|
|
@@ -9,8 +16,8 @@ var DESCRIPTION = `Run JavaScript that calls other tools. The input is raw JavaS
|
|
|
9
16
|
- \`await tools.<name>({ ...args })\` resolves to the tool's output, or its text, and rejects with an Error on failure.
|
|
10
17
|
- Optional first line: \`// @options: {"timeout_ms": 60000}\`
|
|
11
18
|
- \`text(value)\`, \`console.log(...)\` and \`return\` add output; \`store(key, value)\` and \`load(key)\` keep JSON values across codemode calls in this session.
|
|
12
|
-
- \`ALL_TOOLS\`, \`searchTools(query, { limit?, namespace? })\` and \`
|
|
13
|
-
var
|
|
19
|
+
- \`ALL_TOOLS\`, \`await searchTools(query, { limit?, namespace? })\`, \`await describeTool(name)\` and \`await describeNamespace(name)\` find unlisted tools, such as MCP tools, and their declarations.`;
|
|
20
|
+
var LISTING_TOKENS = 3000;
|
|
14
21
|
var parameters = standard(Type.Object({ code: Type.String({ description: "Raw JavaScript source." }) }));
|
|
15
22
|
function codemodePlugin(runtime) {
|
|
16
23
|
return {
|
|
@@ -18,32 +25,94 @@ function codemodePlugin(runtime) {
|
|
|
18
25
|
activate(api) {
|
|
19
26
|
api.contribute.tools.add(codemode(api, runtime));
|
|
20
27
|
api.contribute.prompt.add((draft, siblings) => {
|
|
21
|
-
const
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
draft.set("codemode", {
|
|
25
|
-
id: "codemode",
|
|
26
|
-
order: 96,
|
|
27
|
-
text: [
|
|
28
|
-
"Tools you reach only from a codemode script, as `await tools.<name>(args)`; `describeTool(name)` inside a script returns a tool's arguments:",
|
|
29
|
-
...reachable
|
|
30
|
-
].join(`
|
|
31
|
-
`)
|
|
32
|
-
});
|
|
28
|
+
const text = listTools(siblings.tools.list());
|
|
29
|
+
if (text !== null)
|
|
30
|
+
draft.set("codemode", { id: "codemode", order: 96, text });
|
|
33
31
|
});
|
|
34
32
|
}
|
|
35
33
|
};
|
|
36
34
|
}
|
|
37
|
-
function
|
|
38
|
-
const
|
|
35
|
+
function listTools(definitions) {
|
|
36
|
+
const sections = definitions.flatMap((definition) => {
|
|
37
|
+
if (definition.exposure !== "code")
|
|
38
|
+
return [];
|
|
39
|
+
const inputSchema = toJsonSchema(definition.parameters, "input");
|
|
40
|
+
const outputSchema = definition.output === undefined ? { type: "string" } : toJsonSchema(definition.output, "output");
|
|
41
|
+
if (inputSchema === null || outputSchema === null)
|
|
42
|
+
return [];
|
|
43
|
+
const id = toCodemodeIdentifier(definition.name);
|
|
44
|
+
const sample = renderToolSample({
|
|
45
|
+
name: definition.name,
|
|
46
|
+
description: definition.description,
|
|
47
|
+
inputSchema,
|
|
48
|
+
outputSchema
|
|
49
|
+
}).trim();
|
|
50
|
+
const section = `### \`${id}\`${id === definition.name ? "" : ` (\`${definition.name}\`)`}
|
|
51
|
+
${sample}`;
|
|
52
|
+
return [
|
|
53
|
+
{
|
|
54
|
+
namespace: definition.namespace,
|
|
55
|
+
name: definition.name,
|
|
56
|
+
section,
|
|
57
|
+
cost: estimateTextTokens(section)
|
|
58
|
+
}
|
|
59
|
+
];
|
|
60
|
+
});
|
|
61
|
+
if (sections.length === 0)
|
|
62
|
+
return null;
|
|
63
|
+
const groups = [
|
|
64
|
+
...Map.groupBy(sections, (entry) => entry.namespace?.name ?? "").entries()
|
|
65
|
+
].toSorted((left, right) => left[0].localeCompare(right[0])).map((pair) => ({ namespace: pair[1][0]?.namespace, entries: pair[1] }));
|
|
66
|
+
const shown = selectListed(groups, LISTING_TOKENS);
|
|
67
|
+
return [
|
|
68
|
+
"Tools you reach only from a codemode script, as `await tools.<name>(args)`:",
|
|
69
|
+
...groups.flatMap((group) => {
|
|
70
|
+
const visible = group.entries.filter((entry) => shown.has(entry.name));
|
|
71
|
+
if (group.namespace === undefined)
|
|
72
|
+
return visible.map((entry) => entry.section);
|
|
73
|
+
const listing = visible.length === group.entries.length ? "" : visible.length === 0 ? " (tools not listed)" : " (some tools not listed)";
|
|
74
|
+
const description = group.namespace.description?.trim() ?? "";
|
|
75
|
+
return [
|
|
76
|
+
`## ${group.namespace.name}${listing}${description === "" ? "" : `
|
|
77
|
+
${description}`}`,
|
|
78
|
+
...visible.map((entry) => entry.section)
|
|
79
|
+
];
|
|
80
|
+
})
|
|
81
|
+
].join(`
|
|
82
|
+
|
|
39
83
|
`);
|
|
40
|
-
|
|
84
|
+
}
|
|
85
|
+
function selectListed(groups, budget) {
|
|
86
|
+
const queues = groups.map((group) => group.entries.toSorted((a, b) => a.cost - b.cost));
|
|
87
|
+
const shown = new Set;
|
|
88
|
+
let remaining = budget;
|
|
89
|
+
let active = queues.filter((queue) => queue.length > 0);
|
|
90
|
+
while (active.length > 0)
|
|
91
|
+
active = active.filter((queue) => {
|
|
92
|
+
const next = queue[0];
|
|
93
|
+
if (next === undefined || next.cost > remaining)
|
|
94
|
+
return false;
|
|
95
|
+
remaining -= next.cost;
|
|
96
|
+
shown.add(next.name);
|
|
97
|
+
queue.shift();
|
|
98
|
+
return queue.length > 0;
|
|
99
|
+
});
|
|
100
|
+
return shown;
|
|
101
|
+
}
|
|
102
|
+
function toJsonSchema(schema, direction) {
|
|
103
|
+
try {
|
|
104
|
+
const converted = schema["~standard"].jsonSchema[direction]({ target: "draft-2020-12" });
|
|
105
|
+
return isJson(converted) && isJsonObject(converted) ? converted : null;
|
|
106
|
+
} catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
41
109
|
}
|
|
42
110
|
function codemode(api, runtime) {
|
|
43
111
|
return {
|
|
44
112
|
name: "codemode",
|
|
45
113
|
description: DESCRIPTION,
|
|
46
114
|
parameters,
|
|
115
|
+
exposure: "model-only",
|
|
47
116
|
grammar: { kind: "lark", source: CODEMODE_SOURCE_GRAMMAR },
|
|
48
117
|
class: () => ({ kind: "custom", label: "Running a script" }),
|
|
49
118
|
effects: "none",
|
|
@@ -51,14 +120,24 @@ function codemode(api, runtime) {
|
|
|
51
120
|
replay: "never",
|
|
52
121
|
async execute(args, invocation) {
|
|
53
122
|
const { code, options } = parseCodemodeSource(args.code);
|
|
54
|
-
const callable = invocation.tools.list().filter((info) => info.
|
|
123
|
+
const callable = invocation.tools.list().filter((info) => info.exposure !== "model-only" && info.exposure !== "hidden");
|
|
55
124
|
const stored = await api.storage.get("store");
|
|
56
125
|
if (stored !== undefined && !isJsonObject(stored))
|
|
57
126
|
throw new SdkError("internal", "Codemode's stored values are not a JSON object.");
|
|
127
|
+
const namespaces = new Map(api.contribute.tools.list().flatMap((definition) => definition.namespace === undefined ? [] : [[definition.name, definition.namespace]]));
|
|
128
|
+
const samples = new Map(callable.map((info) => [
|
|
129
|
+
info.name,
|
|
130
|
+
renderToolSample({
|
|
131
|
+
name: info.name,
|
|
132
|
+
description: info.description,
|
|
133
|
+
inputSchema: info.parameters,
|
|
134
|
+
outputSchema: info.output ?? { type: "string" }
|
|
135
|
+
})
|
|
136
|
+
]));
|
|
58
137
|
const sandbox = new CodemodeSandbox({
|
|
59
138
|
...runtime,
|
|
60
|
-
tools: callable.map((info) => bridge(info, invocation)),
|
|
61
|
-
globals: discovery(callable),
|
|
139
|
+
tools: callable.map((info) => bridge(info, samples.get(info.name) ?? "", invocation)),
|
|
140
|
+
globals: discovery(callable, samples, namespaces),
|
|
62
141
|
timeoutMs: options.timeoutMs ?? 300000,
|
|
63
142
|
memoryLimitBytes: 256 * 1024 * 1024
|
|
64
143
|
});
|
|
@@ -102,11 +181,11 @@ ${calls(result)}`);
|
|
|
102
181
|
}
|
|
103
182
|
};
|
|
104
183
|
}
|
|
105
|
-
function bridge(info, invocation) {
|
|
184
|
+
function bridge(info, sample, invocation) {
|
|
106
185
|
let sequence = 0;
|
|
107
186
|
return {
|
|
108
187
|
name: info.name,
|
|
109
|
-
description:
|
|
188
|
+
description: sample,
|
|
110
189
|
inputSchema: info.parameters,
|
|
111
190
|
outputSchema: info.output ?? { type: "string" },
|
|
112
191
|
async execute(args, { signal }) {
|
|
@@ -121,42 +200,70 @@ function unwrapToolOutcome(outcome) {
|
|
|
121
200
|
`);
|
|
122
201
|
throw new Error(outcome.kind === "failed" ? outcome.message : outcome.kind === "rejected" ? `Rejected: ${outcome.reason}` : `The call was ${outcome.kind}.`);
|
|
123
202
|
}
|
|
124
|
-
function discovery(tools) {
|
|
203
|
+
function discovery(tools, samples, namespaces) {
|
|
125
204
|
return [
|
|
126
205
|
{
|
|
127
206
|
name: "searchTools",
|
|
128
207
|
spread: true,
|
|
129
208
|
execute(args) {
|
|
130
209
|
const [query, options] = Array.isArray(args) ? args : [];
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
210
|
+
if (typeof query !== "string")
|
|
211
|
+
throw new Error("searchTools() expects a query string");
|
|
212
|
+
const limit = isJsonObject(options) ? options.limit ?? null : null;
|
|
213
|
+
const namespace = isJsonObject(options) ? options.namespace ?? null : null;
|
|
214
|
+
if (limit !== null && (typeof limit !== "number" || !Number.isInteger(limit) || limit <= 0))
|
|
215
|
+
throw new Error("searchTools() limit must be a positive integer");
|
|
216
|
+
if (namespace !== null && typeof namespace !== "string")
|
|
217
|
+
throw new Error("searchTools() namespace must be a string");
|
|
218
|
+
const documents = tools.flatMap((info) => {
|
|
219
|
+
const found = namespaces.get(info.name);
|
|
220
|
+
if (namespace !== null && (found === undefined || !matchesNamespace(found, namespace)))
|
|
221
|
+
return [];
|
|
222
|
+
return [createToolSearchDocument2(info, found)];
|
|
223
|
+
});
|
|
224
|
+
return rankTools2(query, documents, limit ?? DEFAULT_SEARCH_LIMIT2).map((name) => ({
|
|
225
|
+
name: toCodemodeIdentifier(name),
|
|
226
|
+
description: samples.get(name) ?? ""
|
|
141
227
|
}));
|
|
142
228
|
}
|
|
143
229
|
},
|
|
144
230
|
{
|
|
145
231
|
name: "describeTool",
|
|
146
232
|
execute(name) {
|
|
147
|
-
|
|
233
|
+
if (typeof name !== "string")
|
|
234
|
+
throw new Error("describeTool() expects a tool name");
|
|
235
|
+
const info = tools.find((candidate) => candidate.name === name || toCodemodeIdentifier(candidate.name) === name);
|
|
148
236
|
if (info === undefined)
|
|
149
|
-
throw new Error(`No tool is named ${
|
|
150
|
-
return
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
237
|
+
throw new Error(`No tool is named ${name}.`);
|
|
238
|
+
return samples.get(info.name) ?? "";
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
name: "describeNamespace",
|
|
243
|
+
execute(name) {
|
|
244
|
+
if (typeof name !== "string")
|
|
245
|
+
throw new Error("describeNamespace() expects a namespace name");
|
|
246
|
+
const members = tools.filter((info) => {
|
|
247
|
+
const found = namespaces.get(info.name);
|
|
248
|
+
return found !== undefined && matchesNamespace(found, name);
|
|
155
249
|
});
|
|
250
|
+
const namespace = members[0] === undefined ? undefined : namespaces.get(members[0].name);
|
|
251
|
+
if (namespace === undefined)
|
|
252
|
+
throw new Error(`No namespace is named ${name}.`);
|
|
253
|
+
return {
|
|
254
|
+
name: namespace.name,
|
|
255
|
+
...namespace.description === undefined ? {} : { description: namespace.description },
|
|
256
|
+
...namespace.instructions === undefined ? {} : { instructions: namespace.instructions },
|
|
257
|
+
tools: members.map((info) => toCodemodeIdentifier(info.name))
|
|
258
|
+
};
|
|
156
259
|
}
|
|
157
260
|
}
|
|
158
261
|
];
|
|
159
262
|
}
|
|
263
|
+
function matchesNamespace(namespace, query) {
|
|
264
|
+
const id = toCodemodeIdentifier(query);
|
|
265
|
+
return [namespace.name, ...namespace.aliases ?? []].some((name) => name === query || toCodemodeIdentifier(name) === id);
|
|
266
|
+
}
|
|
160
267
|
function calls(result) {
|
|
161
268
|
return result.calls.length === 0 ? "No tool calls were made." : `Tool calls made before the failure (not undone): ${result.calls.map((call) => `${call.name} (${call.status})`).join(", ")}`;
|
|
162
269
|
}
|
package/dist/src/core.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { HookEntry as SideHookEntry, HookHandler as SideHookHandler, HookName as SideHookName, Plugin as CorePlugin, PluginApi as CorePluginApi } from "@nyte-ai/core";
|
|
2
2
|
export { branchSummary, compaction, definePart, isPart, text, thinking, tool } from "@nyte-ai/core";
|
|
3
|
-
export type { ActionContext, Agent, Command, Durable, HookTable, ModelContextPolicy, Part, PartDefinition, PartState, PromptSection, Setting, SettingDefinition, Skill, ToolDefinition, ToolInvocation, } from "@nyte-ai/core";
|
|
3
|
+
export type { ActionContext, Agent, Command, Durable, HookTable, ModelContextPolicy, Part, PartDefinition, PartState, PromptSection, SearchProvider, SearchResult, Setting, SettingDefinition, Skill, ToolDefinition, ToolInvocation, } from "@nyte-ai/core";
|
|
4
4
|
export type { CompactionPlan, Decision, ModelRequest, Submission, ToolCall, Transcript, } from "@nyte-ai/schema";
|
|
5
5
|
/**
|
|
6
6
|
* A core plugin: per session on the host, with tools, prompt sections,
|