@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
package/THIRD-PARTY-NOTICES.md
CHANGED
|
@@ -55,6 +55,11 @@ SOFTWARE.
|
|
|
55
55
|
`@earendil-works/pi-codemode` 1.0.0 supply the MCP client and QuickJS
|
|
56
56
|
executor. Tool discovery and code-mode integration are adapted from commit
|
|
57
57
|
`86dfceec402ad77e563bf4feab5f26c42d5f5db6`.
|
|
58
|
+
- BM25 tool search (`tool_search`, `searchTools()`), tool namespaces with
|
|
59
|
+
`describeNamespace()`, the budgeted codemode listing and the one-line MCP
|
|
60
|
+
server summary (`packages/plugin/src/tool-search.ts`,
|
|
61
|
+
`packages/plugin/src/codemode.ts`, `packages/core/src/builtins/mcp.ts`) are
|
|
62
|
+
ported from commit `d5b3de22d0e24a3e9c38a9410d3329b099717283`.
|
|
58
63
|
- Transcript reconciliation, checkpoint instruction snapshots, provider system
|
|
59
64
|
updates, and cache warming are ported from the 1.0.0 release commit
|
|
60
65
|
`a13d35a742c6ef8462812a28fbe1d8c8b7431c32`.
|
|
@@ -312,11 +317,14 @@ Copyright 2025 OpenAI
|
|
|
312
317
|
front of the queue (`packages/tui/src/model/interactions.ts`) is its
|
|
313
318
|
`onEmptySubmit`. The rename
|
|
314
319
|
plugin's context construction, prompt, model fallback, and output cleanup
|
|
315
|
-
(`packages/plugin/src/rename.ts`) port OpenCode v2. The web
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
+
(`packages/plugin/src/rename.ts`) port OpenCode v2. The web plugin
|
|
321
|
+
(`packages/plugin/src/web/`) ports OpenCode v2's web search and fetch tools:
|
|
322
|
+
the search provider registry any plugin can add to (`searchProviders` in
|
|
323
|
+
`packages/core/src/contract/plugin.ts`), its Exa, Firecrawl, Parallel, and
|
|
324
|
+
Tavily adapters, their stateless MCP `tools/call` transport and response
|
|
325
|
+
bounds, the result and error vocabulary, `web_fetch`'s Accept headers,
|
|
326
|
+
Cloudflare-challenge retry and size limits, and the streaming HTML-to-Markdown
|
|
327
|
+
and HTML-to-text converters (`packages/plugin/src/web/html-markdown.ts`).
|
|
320
328
|
- Core's ripgrep resolver and search adapter (`packages/core/src/ripgrep.ts`,
|
|
321
329
|
`packages/core/src/ripgrep/binary.ts`) and filename search
|
|
322
330
|
(`packages/core/src/file-search.ts`) adapt OpenCode v2's core implementation.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// src/web/body.ts
|
|
2
|
+
async function readCapped2(response, limit) {
|
|
3
|
+
const declared = Number(response.headers.get("content-length") ?? Number.NaN);
|
|
4
|
+
if (Number.isSafeInteger(declared) && declared > limit) {
|
|
5
|
+
await response.body?.cancel();
|
|
6
|
+
throw tooLarge(limit);
|
|
7
|
+
}
|
|
8
|
+
if (response.body === null)
|
|
9
|
+
return new Uint8Array;
|
|
10
|
+
const reader = response.body.getReader();
|
|
11
|
+
const chunks = [];
|
|
12
|
+
let size = 0;
|
|
13
|
+
for (let next = await reader.read();!next.done; next = await reader.read()) {
|
|
14
|
+
size += next.value.byteLength;
|
|
15
|
+
if (size > limit) {
|
|
16
|
+
await reader.cancel();
|
|
17
|
+
throw tooLarge(limit);
|
|
18
|
+
}
|
|
19
|
+
chunks.push(next.value);
|
|
20
|
+
}
|
|
21
|
+
const body = new Uint8Array(size);
|
|
22
|
+
let offset = 0;
|
|
23
|
+
for (const chunk of chunks) {
|
|
24
|
+
body.set(chunk, offset);
|
|
25
|
+
offset += chunk.byteLength;
|
|
26
|
+
}
|
|
27
|
+
return body;
|
|
28
|
+
}
|
|
29
|
+
function tooLarge(limit) {
|
|
30
|
+
return new Error(`The response is larger than ${limit >= 1024 * 1024 ? `${limit / 1024 / 1024} MiB` : `${limit / 1024} KiB`}.`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { readCapped2 };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readCapped2
|
|
3
|
+
} from "./bash-description-0ah30s12.js";
|
|
4
|
+
import {
|
|
5
|
+
htmlToMarkdown2,
|
|
6
|
+
htmlToText2
|
|
7
|
+
} from "./bash-description-5dpf5hpt.js";
|
|
8
|
+
|
|
9
|
+
// src/web/fetch.ts
|
|
10
|
+
import { standard } from "@nyte-ai/schema";
|
|
11
|
+
import { Type } from "typebox";
|
|
12
|
+
var MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
|
|
13
|
+
var INLINE_CHARS = 50 * 1024;
|
|
14
|
+
var MAX_REDIRECTS = 10;
|
|
15
|
+
var DEFAULT_TIMEOUT_SECONDS = 30;
|
|
16
|
+
var MAX_TIMEOUT_SECONDS = 120;
|
|
17
|
+
var USER_AGENT = "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; Nyte/1.0; +https://nyte.sh";
|
|
18
|
+
var fetchParameters = Type.Object({
|
|
19
|
+
url: Type.String({ minLength: 1, description: "The http:// or https:// URL to fetch." }),
|
|
20
|
+
format: Type.Optional(Type.Union([Type.Literal("markdown"), Type.Literal("text"), Type.Literal("html")], {
|
|
21
|
+
description: "What to return the page as. Defaults to markdown."
|
|
22
|
+
})),
|
|
23
|
+
timeout: Type.Optional(Type.Number({
|
|
24
|
+
exclusiveMinimum: 0,
|
|
25
|
+
maximum: MAX_TIMEOUT_SECONDS,
|
|
26
|
+
description: `Seconds to wait, at most ${MAX_TIMEOUT_SECONDS}. Defaults to ${DEFAULT_TIMEOUT_SECONDS}.`
|
|
27
|
+
}))
|
|
28
|
+
});
|
|
29
|
+
var ACCEPT = {
|
|
30
|
+
markdown: "text/markdown;q=1.0, text/x-markdown;q=0.9, text/plain;q=0.8, text/html;q=0.7, */*;q=0.1",
|
|
31
|
+
text: "text/plain;q=1.0, text/markdown;q=0.9, text/html;q=0.8, */*;q=0.1",
|
|
32
|
+
html: "text/html;q=1.0, application/xhtml+xml;q=0.9, text/plain;q=0.8, text/markdown;q=0.7, */*;q=0.1"
|
|
33
|
+
};
|
|
34
|
+
function webFetch2(fetch) {
|
|
35
|
+
return {
|
|
36
|
+
name: "web_fetch",
|
|
37
|
+
description: "Fetch an http(s) URL and return it as markdown (the default), text or HTML; an image comes back as a file. Read-only. Prefer a more specific tool when one fits.",
|
|
38
|
+
parameters: standard(fetchParameters),
|
|
39
|
+
class: (args) => ({ kind: "custom", label: `Fetching ${args.url}` }),
|
|
40
|
+
effects: "none",
|
|
41
|
+
approval: "never",
|
|
42
|
+
replay: "safe",
|
|
43
|
+
async execute(args, invocation) {
|
|
44
|
+
const url = URL.parse(args.url);
|
|
45
|
+
if (url === null || url.protocol !== "http:" && url.protocol !== "https:")
|
|
46
|
+
throw new Error(`Fetch an http:// or https:// URL; ${args.url} isn't one.`);
|
|
47
|
+
const format = args.format ?? "markdown";
|
|
48
|
+
const seconds = args.timeout ?? DEFAULT_TIMEOUT_SECONDS;
|
|
49
|
+
const page = await invocation.memo("page", async () => {
|
|
50
|
+
const deadline = AbortSignal.timeout(seconds * 1000);
|
|
51
|
+
const signal = AbortSignal.any([invocation.signal, deadline]);
|
|
52
|
+
try {
|
|
53
|
+
const response = await follow(fetch, url, format, signal);
|
|
54
|
+
const mime = (response.headers.get("content-type") ?? "").split(";", 1)[0]?.trim().toLowerCase() ?? "";
|
|
55
|
+
if (mime.startsWith("image/") && mime !== "image/svg+xml" && mime !== "image/vnd.fastbidsheet") {
|
|
56
|
+
const bytes = await readCapped2(response, MAX_RESPONSE_BYTES);
|
|
57
|
+
const blob = await invocation.env.files.upload(async function* () {
|
|
58
|
+
yield bytes;
|
|
59
|
+
}());
|
|
60
|
+
return { kind: "image", blob, mime };
|
|
61
|
+
}
|
|
62
|
+
if (mime !== "" && !mime.startsWith("text/") && mime !== "application/json" && !mime.endsWith("+json") && mime !== "application/xml" && !mime.endsWith("+xml") && mime !== "application/javascript" && mime !== "application/x-javascript") {
|
|
63
|
+
await response.body?.cancel();
|
|
64
|
+
throw new Error(`${url.href} is ${mime}; web_fetch reads text, HTML and images.`);
|
|
65
|
+
}
|
|
66
|
+
const body = new TextDecoder().decode(await readCapped2(response, MAX_RESPONSE_BYTES));
|
|
67
|
+
const text = mime !== "text/html" || format === "html" ? body : format === "markdown" ? htmlToMarkdown2(body) : htmlToText2(body);
|
|
68
|
+
return {
|
|
69
|
+
kind: "text",
|
|
70
|
+
text: text.length > INLINE_CHARS ? `${text.slice(0, INLINE_CHARS)}
|
|
71
|
+
|
|
72
|
+
… Cut at ${INLINE_CHARS} of ${text.length} characters.` : text
|
|
73
|
+
};
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (deadline.aborted && !invocation.signal.aborted)
|
|
76
|
+
throw new Error(`${url.href} didn't answer within ${seconds} s.`, { cause: error });
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
if (page.kind === "image")
|
|
81
|
+
return { content: [{ kind: "file", blob: page.blob, mime: page.mime }] };
|
|
82
|
+
return {
|
|
83
|
+
content: [{ kind: "text", text: page.text === "" ? "The page is empty." : page.text }]
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
async function follow(fetch, start, format, signal) {
|
|
89
|
+
let url = start;
|
|
90
|
+
for (let hop = 0;hop <= MAX_REDIRECTS; hop += 1) {
|
|
91
|
+
const response = await get(fetch, url, format, signal);
|
|
92
|
+
const location = response.headers.get("location");
|
|
93
|
+
if (response.status < 300 || response.status >= 400 || location === null) {
|
|
94
|
+
if (response.ok)
|
|
95
|
+
return response;
|
|
96
|
+
await response.body?.cancel();
|
|
97
|
+
throw new Error(`${url.href} answered HTTP ${response.status}${response.statusText === "" ? "" : ` ${response.statusText}`}.`);
|
|
98
|
+
}
|
|
99
|
+
await response.body?.cancel();
|
|
100
|
+
const next = new URL(location, url);
|
|
101
|
+
if (next.protocol !== "http:" && next.protocol !== "https:")
|
|
102
|
+
throw new Error(`${url.href} redirects to ${next.href}, which isn't http(s).`);
|
|
103
|
+
url = next;
|
|
104
|
+
}
|
|
105
|
+
throw new Error(`${start.href} redirects more than ${MAX_REDIRECTS} times.`);
|
|
106
|
+
}
|
|
107
|
+
async function get(fetch, url, format, signal) {
|
|
108
|
+
const send = (agent) => fetch(url, {
|
|
109
|
+
headers: {
|
|
110
|
+
"User-Agent": agent,
|
|
111
|
+
Accept: ACCEPT[format],
|
|
112
|
+
"Accept-Language": "en-US,en;q=0.9"
|
|
113
|
+
},
|
|
114
|
+
redirect: "manual",
|
|
115
|
+
signal
|
|
116
|
+
});
|
|
117
|
+
const response = await send(USER_AGENT);
|
|
118
|
+
if (response.status !== 403 || response.headers.get("cf-mitigated") !== "challenge")
|
|
119
|
+
return response;
|
|
120
|
+
await response.body?.cancel();
|
|
121
|
+
return send("nyte");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export { webFetch2 };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// src/web/search.ts
|
|
2
|
+
import { dev } from "@nyte-ai/core";
|
|
3
|
+
import { standard } from "@nyte-ai/schema";
|
|
4
|
+
import { Type } from "typebox";
|
|
5
|
+
var TIMEOUT_MS = 25000;
|
|
6
|
+
var parameters = standard(Type.Object({ query: Type.String({ minLength: 1, description: "What to search the web for." }) }));
|
|
7
|
+
var searchOutput = Type.Object({
|
|
8
|
+
provider: Type.String(),
|
|
9
|
+
results: Type.Array(Type.Object({
|
|
10
|
+
url: Type.String(),
|
|
11
|
+
title: Type.Optional(Type.String()),
|
|
12
|
+
content: Type.Optional(Type.String()),
|
|
13
|
+
published: Type.Optional(Type.Number({ description: "Milliseconds since the Unix epoch." }))
|
|
14
|
+
}))
|
|
15
|
+
});
|
|
16
|
+
function webSearch2(api, setting) {
|
|
17
|
+
return {
|
|
18
|
+
name: "web_search",
|
|
19
|
+
description: `Search the web using the user's selected search provider. Use this for current information beyond your knowledge cutoff. The current year is ${new Date().getFullYear()}.`,
|
|
20
|
+
parameters,
|
|
21
|
+
output: standard(searchOutput),
|
|
22
|
+
class: (args) => ({ kind: "custom", label: `Searching ${args.query}` }),
|
|
23
|
+
effects: "none",
|
|
24
|
+
approval: "never",
|
|
25
|
+
replay: "safe",
|
|
26
|
+
async execute(args, invocation) {
|
|
27
|
+
const output = await invocation.memo("results", async () => {
|
|
28
|
+
const chosen = await api.contribute.settings.value(setting);
|
|
29
|
+
const providers = api.contribute.searchProviders.list();
|
|
30
|
+
const provider = providers.find((candidate) => candidate.id === chosen) ?? providers.find((candidate) => candidate.id === "exa") ?? providers[0];
|
|
31
|
+
if (provider === undefined)
|
|
32
|
+
throw new Error("No web search provider is installed.");
|
|
33
|
+
dev("web", "search", { call: invocation.call, provider: provider.id });
|
|
34
|
+
const deadline = AbortSignal.timeout(TIMEOUT_MS);
|
|
35
|
+
const hits = await provider.search({ query: args.query, signal: AbortSignal.any([invocation.signal, deadline]) }).catch((error) => {
|
|
36
|
+
if (deadline.aborted && !invocation.signal.aborted)
|
|
37
|
+
throw new Error(`${provider.name} didn't answer within ${TIMEOUT_MS / 1000} s.`, {
|
|
38
|
+
cause: error
|
|
39
|
+
});
|
|
40
|
+
throw error;
|
|
41
|
+
});
|
|
42
|
+
return { provider: provider.id, results: hits.map(clean) };
|
|
43
|
+
});
|
|
44
|
+
const text = output.results.length === 0 ? "No search results found. Try a different query." : output.results.map((hit) => {
|
|
45
|
+
const published = hit.published === undefined ? "" : `
|
|
46
|
+
Published: ${new Date(hit.published).toISOString()}`;
|
|
47
|
+
return `## [${hit.title ?? hit.url}](${hit.url})${published}${hit.content === undefined ? "" : `
|
|
48
|
+
|
|
49
|
+
${hit.content}`}`;
|
|
50
|
+
}).join(`
|
|
51
|
+
|
|
52
|
+
`);
|
|
53
|
+
return { content: [{ kind: "text", text }], output };
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function clean(hit) {
|
|
58
|
+
const kept = { url: hit.url };
|
|
59
|
+
if (hit.title !== undefined && hit.title !== "")
|
|
60
|
+
kept.title = hit.title;
|
|
61
|
+
if (hit.content !== undefined && hit.content !== "")
|
|
62
|
+
kept.content = hit.content;
|
|
63
|
+
if (hit.published !== undefined && Number.isFinite(hit.published))
|
|
64
|
+
kept.published = hit.published;
|
|
65
|
+
return kept;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export { webSearch2 };
|