@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.
Files changed (34) hide show
  1. package/THIRD-PARTY-NOTICES.md +13 -5
  2. package/dist/chunks/bash-description-0ah30s12.js +33 -0
  3. package/dist/chunks/bash-description-4cdbmar0.js +124 -0
  4. package/dist/chunks/bash-description-4h0hnsv4.js +68 -0
  5. package/dist/chunks/bash-description-5dpf5hpt.js +724 -0
  6. package/dist/chunks/bash-description-9gw9ets8.js +162 -0
  7. package/dist/chunks/bash-description-b78pre8c.js +147 -0
  8. package/dist/chunks/{bash-description-eqp7ap2w.js → bash-description-vg0xecwm.js} +16 -5
  9. package/dist/src/acp/connection.js +1 -1
  10. package/dist/src/acp/index.js +1 -1
  11. package/dist/src/claude-code-sdk/index.js +1 -0
  12. package/dist/src/codemode.js +148 -41
  13. package/dist/src/core.d.ts +1 -1
  14. package/dist/src/rename.d.ts +7 -22
  15. package/dist/src/rename.js +115 -48
  16. package/dist/src/subagents.d.ts +30 -0
  17. package/dist/src/subagents.js +323 -0
  18. package/dist/src/tool-search.d.ts +49 -0
  19. package/dist/src/tool-search.js +14 -0
  20. package/dist/src/web/body.d.ts +3 -0
  21. package/dist/src/web/body.js +6 -0
  22. package/dist/src/web/fetch.d.ts +16 -0
  23. package/dist/src/web/fetch.js +8 -0
  24. package/dist/src/web/html-markdown.d.ts +9 -0
  25. package/dist/src/web/html-markdown.js +10 -0
  26. package/dist/src/web/index.d.ts +24 -0
  27. package/dist/src/web/index.js +93 -0
  28. package/dist/src/web/providers.d.ts +60 -0
  29. package/dist/src/web/providers.js +11 -0
  30. package/dist/src/web/search.d.ts +21 -0
  31. package/dist/src/web/search.js +6 -0
  32. package/package.json +16 -7
  33. package/dist/src/web-search.d.ts +0 -78
  34. package/dist/src/web-search.js +0 -219
@@ -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-search
316
- plugin set (`packages/plugin/src/web-search.ts`) ports its Exa, Firecrawl,
317
- Parallel, and Tavily adapters, its stateless MCP `tools/call` transport and
318
- response bounds, its result and error vocabulary, and its arrangement of one
319
- plugin per provider from OpenCode v2.
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 };