@humain/terminal 0.0.3-beta.0 → 0.0.3-beta.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/CHANGELOG.md +13 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +4 -3
- package/dist/config.js.map +1 -1
- package/dist/humain/mcp-adapter/vendor/CHANGELOG.md +13 -0
- package/dist/humain/mcp-adapter/vendor/README.md +4 -1
- package/dist/humain/mcp-adapter/vendor/agent-dir.ts +12 -4
- package/dist/humain/mcp-adapter/vendor/cli.js +25 -4
- package/dist/humain/mcp-adapter/vendor/config.ts +4 -4
- package/dist/humain/mcp-adapter/vendor/direct-tools.ts +18 -15
- package/dist/humain/mcp-adapter/vendor/index.bundle.mjs +416 -60
- package/dist/humain/mcp-adapter/vendor/mcp-setup-panel.ts +2 -1
- package/dist/humain/mcp-adapter/vendor/metadata-cache.ts +18 -8
- package/dist/humain/mcp-adapter/vendor/package.json +177 -176
- package/dist/humain/mcp-adapter/vendor/request-headers-command.ts +336 -0
- package/dist/humain/mcp-adapter/vendor/server-manager.ts +5 -0
- package/dist/humain/mcp-adapter/vendor/tool-metadata.ts +38 -18
- package/dist/humain/mcp-adapter/vendor/types.ts +93 -10
- package/dist/humain/web-access/vendor/auth-fetch.d.ts +13 -0
- package/dist/humain/web-access/vendor/auth-fetch.d.ts.map +1 -0
- package/dist/humain/web-access/vendor/auth-fetch.js +134 -0
- package/dist/humain/web-access/vendor/auth-fetch.js.map +1 -0
- package/dist/humain/web-access/vendor/chrome-cookies.d.ts +13 -0
- package/dist/humain/web-access/vendor/chrome-cookies.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/chrome-cookies.js +106 -22
- package/dist/humain/web-access/vendor/chrome-cookies.js.map +1 -1
- package/dist/humain/web-access/vendor/curator-page.d.ts +1 -0
- package/dist/humain/web-access/vendor/curator-page.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/curator-page.js +3 -1
- package/dist/humain/web-access/vendor/curator-page.js.map +1 -1
- package/dist/humain/web-access/vendor/curator-server.d.ts +1 -0
- package/dist/humain/web-access/vendor/curator-server.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/curator-server.js +2 -0
- package/dist/humain/web-access/vendor/curator-server.js.map +1 -1
- package/dist/humain/web-access/vendor/extract.d.ts +2 -0
- package/dist/humain/web-access/vendor/extract.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/extract.js +79 -17
- package/dist/humain/web-access/vendor/extract.js.map +1 -1
- package/dist/humain/web-access/vendor/fetch-params.d.ts +2 -0
- package/dist/humain/web-access/vendor/fetch-params.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/fetch-params.js +17 -3
- package/dist/humain/web-access/vendor/fetch-params.js.map +1 -1
- package/dist/humain/web-access/vendor/firecrawl.d.ts +7 -0
- package/dist/humain/web-access/vendor/firecrawl.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/firecrawl.js +141 -10
- package/dist/humain/web-access/vendor/firecrawl.js.map +1 -1
- package/dist/humain/web-access/vendor/gemini-search.d.ts +2 -2
- package/dist/humain/web-access/vendor/gemini-search.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/gemini-search.js +22 -4
- package/dist/humain/web-access/vendor/gemini-search.js.map +1 -1
- package/dist/humain/web-access/vendor/index.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/index.js +122 -48
- package/dist/humain/web-access/vendor/index.js.map +1 -1
- package/dist/humain/web-access/vendor/summary-review.d.ts +3 -1
- package/dist/humain/web-access/vendor/summary-review.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/summary-review.js +6 -3
- package/dist/humain/web-access/vendor/summary-review.js.map +1 -1
- package/dist/humain/web-access/vendor/youtube-extract.d.ts.map +1 -1
- package/dist/humain/web-access/vendor/youtube-extract.js +2 -2
- package/dist/humain/web-access/vendor/youtube-extract.js.map +1 -1
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +2 -0
- package/dist/utils/clipboard-image.js.map +1 -1
- package/npm-shrinkwrap.json +7 -7
- package/package.json +6 -6
- package/src/humain/mcp-adapter/UPSTREAM.md +1 -1
- package/src/humain/mcp-adapter/upstream.json +3 -3
- package/src/humain/mcp-adapter/vendor/CHANGELOG.md +13 -0
- package/src/humain/web-access/UPSTREAM.md +1 -1
- package/src/humain/web-access/upstream.json +4 -4
|
@@ -10,6 +10,7 @@ import { Box, Text, truncateToWidth } from "@earendil-works/pi-tui";
|
|
|
10
10
|
import { Type } from "typebox";
|
|
11
11
|
import { StringEnum, complete } from "@earendil-works/pi-ai/compat";
|
|
12
12
|
import { normalizeFetchContentParams } from "./fetch-params.js";
|
|
13
|
+
import { resolveAuthFetchProfile } from "./auth-fetch.js";
|
|
13
14
|
import { findContent } from "./content-find.js";
|
|
14
15
|
import { answerFromPage } from "./page-query.js";
|
|
15
16
|
import { clearCloneCache } from "./github-extract.js";
|
|
@@ -38,6 +39,7 @@ import { isSearch1APIAvailable } from "./search1api.js";
|
|
|
38
39
|
import { isSearchinfinityAvailable } from "./searchinfinity.js";
|
|
39
40
|
import { isQueritAvailable } from "./querit.js";
|
|
40
41
|
import { isTavilyAvailable } from "./tavily.js";
|
|
42
|
+
import { isFirecrawlAvailable } from "./firecrawl.js";
|
|
41
43
|
import { isJinaSearchAvailable } from "./jina-search.js";
|
|
42
44
|
import { isSerpdiveAvailable } from "./serpdive.js";
|
|
43
45
|
import { isKagiAvailable } from "./kagi.js";
|
|
@@ -276,6 +278,7 @@ async function getProviderAvailability(ctx) {
|
|
|
276
278
|
searchinfinity: isSearchinfinityAvailable(),
|
|
277
279
|
querit: isQueritAvailable(),
|
|
278
280
|
tavily: isTavilyAvailable(),
|
|
281
|
+
firecrawl: isFirecrawlAvailable(),
|
|
279
282
|
jina: isJinaSearchAvailable(),
|
|
280
283
|
serpdive: isSerpdiveAvailable(),
|
|
281
284
|
kagi: isKagiAvailable(),
|
|
@@ -339,6 +342,8 @@ function firstAvailableProvider(available, preferOpenAI, fallback) {
|
|
|
339
342
|
return "querit";
|
|
340
343
|
if (available.tavily)
|
|
341
344
|
return "tavily";
|
|
345
|
+
if (available.firecrawl)
|
|
346
|
+
return "firecrawl";
|
|
342
347
|
if (available.jina)
|
|
343
348
|
return "jina";
|
|
344
349
|
if (available.serpdive)
|
|
@@ -397,6 +402,9 @@ function resolveProvider(provider, available, options) {
|
|
|
397
402
|
if (provider === "tavily" && !available.tavily) {
|
|
398
403
|
return firstAvailableProvider(available, preferOpenAI, "tavily");
|
|
399
404
|
}
|
|
405
|
+
if (provider === "firecrawl" && !available.firecrawl) {
|
|
406
|
+
return firstAvailableProvider(available, preferOpenAI, "firecrawl");
|
|
407
|
+
}
|
|
400
408
|
if (provider === "jina" && !available.jina) {
|
|
401
409
|
return firstAvailableProvider(available, preferOpenAI, "jina");
|
|
402
410
|
}
|
|
@@ -435,8 +443,8 @@ const activeCurators = new Map();
|
|
|
435
443
|
const glimpseWins = new Map();
|
|
436
444
|
const DEFAULT_MAX_INLINE_CONTENT_CHARS = 30_000;
|
|
437
445
|
const MAX_INLINE_CONTENT_CHARS = 200_000;
|
|
438
|
-
function getMaxInlineContentChars() {
|
|
439
|
-
const value =
|
|
446
|
+
function getMaxInlineContentChars(config = loadConfig()) {
|
|
447
|
+
const value = config.maxInlineContentChars;
|
|
440
448
|
if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
|
|
441
449
|
return DEFAULT_MAX_INLINE_CONTENT_CHARS;
|
|
442
450
|
}
|
|
@@ -445,6 +453,12 @@ function getMaxInlineContentChars() {
|
|
|
445
453
|
function stripThumbnails(results) {
|
|
446
454
|
return results.map(({ thumbnail, frames, ...rest }) => rest);
|
|
447
455
|
}
|
|
456
|
+
function storeFetchResult(pi, responseId, data, authProfile) {
|
|
457
|
+
if (authProfile?.cache === "off")
|
|
458
|
+
return false;
|
|
459
|
+
pi.appendEntry("web-search-results", storeFetchedContentResult(responseId, data));
|
|
460
|
+
return true;
|
|
461
|
+
}
|
|
448
462
|
function initialContentSlice(content, maxChars) {
|
|
449
463
|
let endOffset = Math.min(content.length, maxChars);
|
|
450
464
|
if (endOffset < content.length) {
|
|
@@ -468,6 +482,19 @@ function normalizeFindQueries(value) {
|
|
|
468
482
|
throw new Error("findText must contain at least one non-empty string");
|
|
469
483
|
return queries;
|
|
470
484
|
}
|
|
485
|
+
function formatInputValue(value) {
|
|
486
|
+
if (typeof value === "string")
|
|
487
|
+
return JSON.stringify(value);
|
|
488
|
+
if (typeof value === "number")
|
|
489
|
+
return Number.isNaN(value) ? "NaN" : String(value);
|
|
490
|
+
try {
|
|
491
|
+
const serialized = JSON.stringify(value);
|
|
492
|
+
return serialized === undefined ? String(value) : serialized;
|
|
493
|
+
}
|
|
494
|
+
catch {
|
|
495
|
+
return String(value);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
471
498
|
function formatSearchSummary(results, answer) {
|
|
472
499
|
if (results.length === 0) {
|
|
473
500
|
return answer ? `${answer}\n\n---\n\n**Sources:**\nNo sources returned.` : "No results found.";
|
|
@@ -1420,12 +1447,12 @@ export default function (pi) {
|
|
|
1420
1447
|
pi.registerTool({
|
|
1421
1448
|
name: toolNames.webSearch,
|
|
1422
1449
|
label: "Web Search",
|
|
1423
|
-
description: `Search the web using OpenAI, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Jina, SERPdive, Kagi, Bocha, Ollama, SearXNG, DuckDuckGo, Exa, Perplexity, Gemini, AnySearch, xAI, Bright Data, or SerpBase. Pass a provider array to search only those providers simultaneously, or use provider "all" to search every eligible provider except DuckDuckGo, AnySearch, xAI, Bright Data, and SerpBase. Returns an AI-synthesized answer with source citations. OpenAI search uses a Codex subscription or OpenAI API key; xAI search uses a SuperGrok/X Premium subscription or xAI API key. DuckDuckGo, AnySearch, xAI, Bright Data, and SerpBase are available only when explicitly selected. For comprehensive research, prefer queries (plural) with 2-4 varied angles over a single query — each query gets its own synthesized answer, so varying phrasing and scope gives much broader coverage. When includeContent is true, full page content is fetched in the background. Searches auto-open the interactive browser curator and stream results live; set workflow to "none" to skip curation or "auto-summary" for a model-generated summary without the browser curator. The configured provider is used when provider is omitted or set to auto; omit provider unless explicitly overriding it. Without a configured provider, auto-selects OpenAI when suitable and available, then Exa, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Jina, SERPdive, Kagi, Bocha, Ollama, Perplexity, Gemini API, or Gemini Web. When SearXNG is configured, it is preferred first for local/private search.`,
|
|
1450
|
+
description: `Search the web using OpenAI, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, SearXNG, DuckDuckGo, Exa, Perplexity, Gemini, AnySearch, xAI, Bright Data, or SerpBase. Pass a provider array to search only those providers simultaneously, or use provider "all" to search every eligible provider except DuckDuckGo, AnySearch, xAI, Bright Data, and SerpBase. Returns an AI-synthesized answer with source citations. OpenAI search uses a Codex subscription or OpenAI API key; xAI search uses a SuperGrok/X Premium subscription or xAI API key. DuckDuckGo, AnySearch, xAI, Bright Data, and SerpBase are available only when explicitly selected. For comprehensive research, prefer queries (plural) with 2-4 varied angles over a single query — each query gets its own synthesized answer, so varying phrasing and scope gives much broader coverage. When includeContent is true, full page content is fetched in the background. Searches auto-open the interactive browser curator and stream results live; set workflow to "none" to skip curation or "auto-summary" for a model-generated summary without the browser curator. The configured provider is used when provider is omitted or set to auto; omit provider unless explicitly overriding it. Without a configured provider, auto-selects OpenAI when suitable and available, then Exa, Brave, Parallel, TinyFish, Search1API, Searchinfinity, Querit, Tavily, Firecrawl, Jina, SERPdive, Kagi, Bocha, Ollama, Perplexity, Gemini API, or Gemini Web. When SearXNG is configured, it is preferred first for local/private search.`,
|
|
1424
1451
|
promptSnippet: "Use for web research questions. Prefer {queries:[...]} with 2-4 varied angles over a single query for broader coverage. Omit provider unless explicitly overriding the configured default.",
|
|
1425
1452
|
parameters: Type.Object({
|
|
1426
1453
|
query: Type.Optional(Type.String({ description: "Single search query. For research tasks, prefer 'queries' with multiple varied angles instead." })),
|
|
1427
1454
|
queries: Type.Optional(Type.Array(Type.String(), { description: "Multiple queries searched in sequence, each returning its own synthesized answer. Prefer this for research — vary phrasing, scope, and angle across 2-4 queries to maximize coverage. Good: ['React vs Vue performance benchmarks 2026', 'React vs Vue developer experience comparison', 'React ecosystem size vs Vue ecosystem']. Bad: ['React vs Vue', 'React vs Vue comparison', 'React vs Vue review'] (too similar, redundant results)." })),
|
|
1428
|
-
numResults: Type.Optional(Type.
|
|
1455
|
+
numResults: Type.Optional(Type.Integer({ minimum: 1, maximum: 20, description: "Results per query (default: 5, max: 20)" })),
|
|
1429
1456
|
includeContent: Type.Optional(Type.Boolean({ description: "Fetch full page content (async)" })),
|
|
1430
1457
|
recencyFilter: Type.Optional(StringEnum(["day", "week", "month", "year"], { description: "Filter by recency" })),
|
|
1431
1458
|
domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains (prefix with - to exclude)" })),
|
|
@@ -1928,7 +1955,7 @@ export default function (pi) {
|
|
|
1928
1955
|
parameters: Type.Object({
|
|
1929
1956
|
claim: Type.String({ description: "The assertion to check against web sources." }),
|
|
1930
1957
|
queries: Type.Optional(Type.Array(Type.String(), { description: "Search queries (default: the claim)." })),
|
|
1931
|
-
numResults: Type.Optional(Type.
|
|
1958
|
+
numResults: Type.Optional(Type.Integer({ minimum: 1, maximum: 20, description: "Results per query (default: 5, max: 20)." })),
|
|
1932
1959
|
fetchContent: Type.Optional(Type.Boolean({ description: "Fetch up to 5 result pages for exact passage extraction." })),
|
|
1933
1960
|
recencyFilter: Type.Optional(StringEnum(["day", "week", "month", "year"], { description: "Filter by recency." })),
|
|
1934
1961
|
domainFilter: Type.Optional(Type.Array(Type.String(), { description: "Limit to domains; prefix with - to exclude." })),
|
|
@@ -2051,6 +2078,9 @@ export default function (pi) {
|
|
|
2051
2078
|
model: Type.Optional(Type.String({
|
|
2052
2079
|
description: "Override the Gemini model for video/YouTube analysis (e.g. 'gemini-3.6-flash'). Defaults to config or gemini-3.6-flash.",
|
|
2053
2080
|
})),
|
|
2081
|
+
auth: Type.Optional(Type.Union([Type.String(), Type.Boolean()], {
|
|
2082
|
+
description: "Opt into an authFetch profile for local browser-cookie fetching. Use a profile name, or true only when exactly one profile exists.",
|
|
2083
|
+
})),
|
|
2054
2084
|
}),
|
|
2055
2085
|
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
2056
2086
|
let normalized;
|
|
@@ -2075,6 +2105,19 @@ export default function (pi) {
|
|
|
2075
2105
|
if (mode === "answer" && options.model) {
|
|
2076
2106
|
return { content: [{ type: "text", text: "Error: use answerModel, not model, with mode answer." }], details: { error: "model is incompatible with mode answer" } };
|
|
2077
2107
|
}
|
|
2108
|
+
if (mode === "answer" && options.auth !== undefined) {
|
|
2109
|
+
return { content: [{ type: "text", text: "Error: auth cannot be combined with mode answer." }], details: { error: "auth cannot be combined with mode answer" } };
|
|
2110
|
+
}
|
|
2111
|
+
let authFetchProfile;
|
|
2112
|
+
if (options.auth !== undefined) {
|
|
2113
|
+
try {
|
|
2114
|
+
authFetchProfile = resolveAuthFetchProfile(options.auth);
|
|
2115
|
+
}
|
|
2116
|
+
catch (err) {
|
|
2117
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
2118
|
+
return { content: [{ type: "text", text: `Error: ${error}` }], details: { error } };
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2078
2121
|
if (urlList.length === 0) {
|
|
2079
2122
|
return {
|
|
2080
2123
|
content: [{ type: "text", text: "Error: No URL provided." }],
|
|
@@ -2085,13 +2128,13 @@ export default function (pi) {
|
|
|
2085
2128
|
content: [{ type: "text", text: `Fetching ${urlList.length} URL(s)...` }],
|
|
2086
2129
|
details: { phase: "fetch", progress: 0 },
|
|
2087
2130
|
});
|
|
2088
|
-
const { answerModel: _answerModel, ...extractionOptions } = options;
|
|
2131
|
+
const { answerModel: _answerModel, auth: _auth, ...extractionOptions } = options;
|
|
2089
2132
|
const fetchOptions = mode === "answer"
|
|
2090
2133
|
? (() => {
|
|
2091
2134
|
const { prompt: _prompt, ...rest } = extractionOptions;
|
|
2092
|
-
return rest;
|
|
2135
|
+
return { ...rest, ...(authFetchProfile ? { authFetchProfile } : {}) };
|
|
2093
2136
|
})()
|
|
2094
|
-
: extractionOptions;
|
|
2137
|
+
: { ...extractionOptions, ...(authFetchProfile ? { authFetchProfile } : {}) };
|
|
2095
2138
|
const fetchResults = await fetchAllContent(urlList, signal, fetchOptions);
|
|
2096
2139
|
const presentedResults = mode === "answer"
|
|
2097
2140
|
? await Promise.all(fetchResults.map(async result => {
|
|
@@ -2116,7 +2159,6 @@ export default function (pi) {
|
|
|
2116
2159
|
: fetchResults;
|
|
2117
2160
|
const successful = presentedResults.filter((r) => !r.error).length;
|
|
2118
2161
|
const totalChars = presentedResults.reduce((sum, r) => sum + r.content.length, 0);
|
|
2119
|
-
// ALWAYS store results (even for single URL)
|
|
2120
2162
|
const responseId = generateId();
|
|
2121
2163
|
const data = {
|
|
2122
2164
|
id: responseId,
|
|
@@ -2124,14 +2166,14 @@ export default function (pi) {
|
|
|
2124
2166
|
timestamp: Date.now(),
|
|
2125
2167
|
urls: stripThumbnails(fetchResults),
|
|
2126
2168
|
};
|
|
2127
|
-
pi
|
|
2169
|
+
const storedContent = storeFetchResult(pi, responseId, data, authFetchProfile);
|
|
2128
2170
|
// Single URL: return content directly (possibly truncated) with responseId
|
|
2129
2171
|
if (urlList.length === 1) {
|
|
2130
2172
|
const result = presentedResults[0];
|
|
2131
2173
|
if (result.error) {
|
|
2132
2174
|
return {
|
|
2133
2175
|
content: [{ type: "text", text: `Error: ${result.error}` }],
|
|
2134
|
-
details: { urls: urlList, urlCount: 1, successful: 0, error: result.error, responseId, prompt: params.prompt, timestamp: params.timestamp, frames: params.frames },
|
|
2176
|
+
details: { urls: urlList, urlCount: 1, successful: 0, error: result.error, ...(storedContent ? { responseId } : {}), prompt: params.prompt, timestamp: params.timestamp, frames: params.frames },
|
|
2135
2177
|
};
|
|
2136
2178
|
}
|
|
2137
2179
|
const fullLength = result.content.length;
|
|
@@ -2140,9 +2182,11 @@ export default function (pi) {
|
|
|
2140
2182
|
let output = slice.text;
|
|
2141
2183
|
if (truncated) {
|
|
2142
2184
|
output += `\n\n---\nShowing ${slice.endOffset} of ${fullLength} chars, ${slice.shownBytes} of ${slice.totalBytes} bytes, and ${slice.shownLines} of ${slice.totalLines} lines. `;
|
|
2143
|
-
output +=
|
|
2144
|
-
?
|
|
2145
|
-
|
|
2185
|
+
output += storedContent
|
|
2186
|
+
? getSearchContentEnabled
|
|
2187
|
+
? `Use ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0, offset: ${slice.endOffset} }) for the next slice.`
|
|
2188
|
+
: "Content retrieval is not registered."
|
|
2189
|
+
: "Authenticated fetch cache is off; repeat the fetch to read more.";
|
|
2146
2190
|
}
|
|
2147
2191
|
const content = [];
|
|
2148
2192
|
if (result.frames?.length) {
|
|
@@ -2164,7 +2208,7 @@ export default function (pi) {
|
|
|
2164
2208
|
successful: 1,
|
|
2165
2209
|
totalChars: fullLength,
|
|
2166
2210
|
title: result.title,
|
|
2167
|
-
responseId,
|
|
2211
|
+
...(storedContent ? { responseId } : {}),
|
|
2168
2212
|
truncated,
|
|
2169
2213
|
hasImage: imageCount > 0,
|
|
2170
2214
|
imageCount,
|
|
@@ -2192,17 +2236,19 @@ export default function (pi) {
|
|
|
2192
2236
|
output += `- ${title || url} (${content.length} chars)\n`;
|
|
2193
2237
|
}
|
|
2194
2238
|
}
|
|
2195
|
-
output +=
|
|
2196
|
-
?
|
|
2197
|
-
|
|
2239
|
+
output += storedContent
|
|
2240
|
+
? getSearchContentEnabled
|
|
2241
|
+
? `\n---\nUse ${toolNames.getSearchContent}({ responseId: "${responseId}", urlIndex: 0 }) to retrieve bounded content slices.`
|
|
2242
|
+
: "\n---\nContent retrieval is not registered."
|
|
2243
|
+
: "\n---\nAuthenticated fetch cache is off; repeat the fetch to read content.";
|
|
2198
2244
|
return {
|
|
2199
2245
|
content: [{ type: "text", text: output }],
|
|
2200
|
-
details: { urls: urlList, urlCount: urlList.length, successful, totalChars, responseId },
|
|
2246
|
+
details: { urls: urlList, urlCount: urlList.length, successful, totalChars, ...(storedContent ? { responseId } : {}) },
|
|
2201
2247
|
};
|
|
2202
2248
|
},
|
|
2203
2249
|
renderCall(args, theme) {
|
|
2204
2250
|
const { urlList, options } = normalizeFetchContentParams(args);
|
|
2205
|
-
const { prompt, timestamp, frames, model, mode, answerModel } = options;
|
|
2251
|
+
const { prompt, timestamp, frames, model, mode, answerModel, auth } = options;
|
|
2206
2252
|
if (urlList.length === 0) {
|
|
2207
2253
|
return new Text(theme.fg("toolTitle", theme.bold("fetch ")) + theme.fg("error", "(no URL)"), 0, 0);
|
|
2208
2254
|
}
|
|
@@ -2240,6 +2286,9 @@ export default function (pi) {
|
|
|
2240
2286
|
if (answerModel) {
|
|
2241
2287
|
lines.push(theme.fg("dim", " answer model: ") + theme.fg("warning", answerModel));
|
|
2242
2288
|
}
|
|
2289
|
+
if (auth !== undefined) {
|
|
2290
|
+
lines.push(theme.fg("dim", " auth: ") + theme.fg("warning", auth === true ? "true" : auth));
|
|
2291
|
+
}
|
|
2243
2292
|
return new Text(lines.join("\n"), 0, 0);
|
|
2244
2293
|
},
|
|
2245
2294
|
renderResult(result, { expanded, isPartial }, theme) {
|
|
@@ -2313,7 +2362,8 @@ export default function (pi) {
|
|
|
2313
2362
|
return new Text(statusLine + "\n" + theme.fg("dim", preview), 0, 0);
|
|
2314
2363
|
},
|
|
2315
2364
|
});
|
|
2316
|
-
if (getSearchContentEnabled)
|
|
2365
|
+
if (getSearchContentEnabled) {
|
|
2366
|
+
const maxInlineContentChars = getMaxInlineContentChars(initConfig);
|
|
2317
2367
|
pi.registerTool({
|
|
2318
2368
|
name: toolNames.getSearchContent,
|
|
2319
2369
|
label: "Get Search Content",
|
|
@@ -2322,11 +2372,11 @@ export default function (pi) {
|
|
|
2322
2372
|
parameters: Type.Object({
|
|
2323
2373
|
responseId: Type.String({ description: `The responseId from ${storedContentSources}` }),
|
|
2324
2374
|
query: Type.Optional(Type.String({ description: searchQueryDescription })),
|
|
2325
|
-
queryIndex: Type.Optional(Type.
|
|
2375
|
+
queryIndex: Type.Optional(Type.Integer({ minimum: 0, description: "Get content for query at index" })),
|
|
2326
2376
|
url: Type.Optional(Type.String({ description: "Get content for this URL" })),
|
|
2327
|
-
urlIndex: Type.Optional(Type.
|
|
2328
|
-
offset: Type.Optional(Type.
|
|
2329
|
-
limit: Type.Optional(Type.
|
|
2377
|
+
urlIndex: Type.Optional(Type.Integer({ minimum: 0, description: "Get content for URL at index" })),
|
|
2378
|
+
offset: Type.Optional(Type.Integer({ minimum: 0, description: "Character offset for fetched URL content slices (default 0). Cannot be combined with findText." })),
|
|
2379
|
+
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: maxInlineContentChars, description: "Maximum characters to return for fetched URL content slices (default and max are set by maxInlineContentChars). Cannot be combined with findText." })),
|
|
2330
2380
|
findText: Type.Optional(Type.Union([
|
|
2331
2381
|
Type.String({ minLength: 1, maxLength: 500 }),
|
|
2332
2382
|
Type.Array(Type.String({ minLength: 1, maxLength: 500 }), { minItems: 1, maxItems: 10 }),
|
|
@@ -2335,15 +2385,23 @@ export default function (pi) {
|
|
|
2335
2385
|
}),
|
|
2336
2386
|
async execute(_toolCallId, params) {
|
|
2337
2387
|
if (params.findText !== undefined && (params.offset !== undefined || params.limit !== undefined)) {
|
|
2338
|
-
|
|
2388
|
+
const offset = formatInputValue(params.offset);
|
|
2389
|
+
const limit = formatInputValue(params.limit);
|
|
2390
|
+
return {
|
|
2391
|
+
content: [{ type: "text", text: `findText cannot be combined with offset or limit. Received offset=${offset}, limit=${limit}; omit offset and limit when using findText.` }],
|
|
2392
|
+
details: { error: "Incompatible find options" },
|
|
2393
|
+
};
|
|
2339
2394
|
}
|
|
2340
2395
|
if (params.findMode !== undefined && params.findText === undefined) {
|
|
2341
|
-
return {
|
|
2396
|
+
return {
|
|
2397
|
+
content: [{ type: "text", text: `findMode ${formatInputValue(params.findMode)} requires findText; provide findText or omit findMode.` }],
|
|
2398
|
+
details: { error: "findMode requires findText" },
|
|
2399
|
+
};
|
|
2342
2400
|
}
|
|
2343
2401
|
const data = getResult(params.responseId);
|
|
2344
2402
|
if (!data) {
|
|
2345
2403
|
return {
|
|
2346
|
-
content: [{ type: "text", text: `Error: No stored results for
|
|
2404
|
+
content: [{ type: "text", text: `Error: No stored results for responseId ${formatInputValue(params.responseId)}. Use a responseId returned by ${storedContentSources}.` }],
|
|
2347
2405
|
details: { error: "Not found", responseId: params.responseId },
|
|
2348
2406
|
};
|
|
2349
2407
|
}
|
|
@@ -2351,22 +2409,30 @@ export default function (pi) {
|
|
|
2351
2409
|
const artifact = getResearchArtifact(params.responseId);
|
|
2352
2410
|
if (!artifact) {
|
|
2353
2411
|
return {
|
|
2354
|
-
content: [{ type: "text", text: `Error: artifact ${params.responseId} not found
|
|
2412
|
+
content: [{ type: "text", text: `Error: stored research artifact for responseId ${formatInputValue(params.responseId)} was not found. Use a responseId returned by ${storedContentSources}.` }],
|
|
2355
2413
|
details: { error: "Artifact not found", responseId: params.responseId },
|
|
2356
2414
|
};
|
|
2357
2415
|
}
|
|
2358
2416
|
const serialized = JSON.stringify(artifact, null, 2);
|
|
2359
|
-
const maxInlineContentChars = getMaxInlineContentChars();
|
|
2360
2417
|
const offset = params.offset ?? 0;
|
|
2361
2418
|
const limit = params.limit ?? maxInlineContentChars;
|
|
2362
2419
|
if (!Number.isInteger(offset) || offset < 0) {
|
|
2363
|
-
return {
|
|
2420
|
+
return {
|
|
2421
|
+
content: [{ type: "text", text: `Invalid offset: received ${formatInputValue(offset)} for responseId ${formatInputValue(params.responseId)}; offset must be a non-negative integer. Use 0 or a larger integer.` }],
|
|
2422
|
+
details: { error: "Invalid offset", offset },
|
|
2423
|
+
};
|
|
2364
2424
|
}
|
|
2365
2425
|
if (!Number.isInteger(limit) || limit <= 0 || limit > maxInlineContentChars) {
|
|
2366
|
-
return {
|
|
2426
|
+
return {
|
|
2427
|
+
content: [{ type: "text", text: `Invalid limit: received ${formatInputValue(limit)} for responseId ${formatInputValue(params.responseId)}; limit must be an integer from 1 to ${maxInlineContentChars}. Use a value in that range.` }],
|
|
2428
|
+
details: { error: "Invalid limit", limit, maxLimit: maxInlineContentChars },
|
|
2429
|
+
};
|
|
2367
2430
|
}
|
|
2368
2431
|
if (offset > serialized.length) {
|
|
2369
|
-
return {
|
|
2432
|
+
return {
|
|
2433
|
+
content: [{ type: "text", text: `Offset ${offset} is out of range for responseId ${formatInputValue(params.responseId)}. Received offset ${offset}; valid range is 0-${serialized.length}. Use an offset within that range.` }],
|
|
2434
|
+
details: { error: "Offset out of range", offset, contentLength: serialized.length },
|
|
2435
|
+
};
|
|
2370
2436
|
}
|
|
2371
2437
|
const endOffset = Math.min(offset + limit, serialized.length);
|
|
2372
2438
|
const artifactSlice = serialized.slice(offset, endOffset);
|
|
@@ -2383,7 +2449,7 @@ export default function (pi) {
|
|
|
2383
2449
|
if (!queryData) {
|
|
2384
2450
|
const available = data.queries.map((q) => `"${q.query}"`).join(", ");
|
|
2385
2451
|
return {
|
|
2386
|
-
content: [{ type: "text", text: `Query
|
|
2452
|
+
content: [{ type: "text", text: `Query ${formatInputValue(params.query)} was not found for responseId ${formatInputValue(params.responseId)}. Received query=${formatInputValue(params.query)}. Available queries: ${available || "none"}. Use one of the available queries or queryIndex.` }],
|
|
2387
2453
|
details: { error: "Query not found" },
|
|
2388
2454
|
};
|
|
2389
2455
|
}
|
|
@@ -2391,8 +2457,9 @@ export default function (pi) {
|
|
|
2391
2457
|
else if (params.queryIndex !== undefined) {
|
|
2392
2458
|
queryData = data.queries[params.queryIndex];
|
|
2393
2459
|
if (!queryData) {
|
|
2460
|
+
const available = data.queries.map((q, i) => `${i}: "${q.query}"`).join(", ");
|
|
2394
2461
|
return {
|
|
2395
|
-
content: [{ type: "text", text: `
|
|
2462
|
+
content: [{ type: "text", text: `Query index ${formatInputValue(params.queryIndex)} is out of range for responseId ${formatInputValue(params.responseId)}. Received queryIndex=${formatInputValue(params.queryIndex)}; valid indexes are 0-${data.queries.length - 1}. Available queries: ${available || "none"}. Use one of the available indexes.` }],
|
|
2396
2463
|
details: { error: "Index out of range" },
|
|
2397
2464
|
};
|
|
2398
2465
|
}
|
|
@@ -2400,13 +2467,13 @@ export default function (pi) {
|
|
|
2400
2467
|
else {
|
|
2401
2468
|
const available = data.queries.map((q, i) => `${i}: "${q.query}"`).join(", ");
|
|
2402
2469
|
return {
|
|
2403
|
-
content: [{ type: "text", text: `Specify query or queryIndex. Available: ${available}
|
|
2470
|
+
content: [{ type: "text", text: `Specify query or queryIndex for responseId ${formatInputValue(params.responseId)}. Available queries: ${available || "none"}.` }],
|
|
2404
2471
|
details: { error: "No query specified" },
|
|
2405
2472
|
};
|
|
2406
2473
|
}
|
|
2407
2474
|
if (queryData.error) {
|
|
2408
2475
|
return {
|
|
2409
|
-
content: [{ type: "text", text: `Error
|
|
2476
|
+
content: [{ type: "text", text: `Error retrieving query ${formatInputValue(queryData.query)} from responseId ${formatInputValue(params.responseId)}: ${queryData.error}. Check the stored search result and retry with another query or queryIndex if needed.` }],
|
|
2410
2477
|
details: { error: queryData.error, query: queryData.query },
|
|
2411
2478
|
};
|
|
2412
2479
|
}
|
|
@@ -2422,7 +2489,10 @@ export default function (pi) {
|
|
|
2422
2489
|
}
|
|
2423
2490
|
catch (err) {
|
|
2424
2491
|
const error = err instanceof Error ? err.message : String(err);
|
|
2425
|
-
return {
|
|
2492
|
+
return {
|
|
2493
|
+
content: [{ type: "text", text: `Unable to find ${formatInputValue(params.findText)} in query ${formatInputValue(queryData.query)} for responseId ${formatInputValue(params.responseId)}: ${error}. Check findText and use a supported findMode.` }],
|
|
2494
|
+
details: { error, query: queryData.query },
|
|
2495
|
+
};
|
|
2426
2496
|
}
|
|
2427
2497
|
}
|
|
2428
2498
|
return {
|
|
@@ -2439,7 +2509,7 @@ export default function (pi) {
|
|
|
2439
2509
|
if (!urlData) {
|
|
2440
2510
|
const available = data.urls.map((u) => u.url).join("\n ");
|
|
2441
2511
|
return {
|
|
2442
|
-
content: [{ type: "text", text: `URL not found. Available:\n ${available}
|
|
2512
|
+
content: [{ type: "text", text: `URL ${formatInputValue(params.url)} was not found for responseId ${formatInputValue(params.responseId)}. Received url=${formatInputValue(params.url)}. Available URLs:\n ${available || " none"}\nUse one of the available URLs or urlIndex.` }],
|
|
2443
2513
|
details: { error: "URL not found" },
|
|
2444
2514
|
};
|
|
2445
2515
|
}
|
|
@@ -2448,8 +2518,9 @@ export default function (pi) {
|
|
|
2448
2518
|
selectedUrlIndex = params.urlIndex;
|
|
2449
2519
|
urlData = data.urls[selectedUrlIndex];
|
|
2450
2520
|
if (!urlData) {
|
|
2521
|
+
const available = data.urls.map((u, i) => `${i}: ${u.url}`).join("\n ");
|
|
2451
2522
|
return {
|
|
2452
|
-
content: [{ type: "text", text: `
|
|
2523
|
+
content: [{ type: "text", text: `URL index ${formatInputValue(params.urlIndex)} is out of range for responseId ${formatInputValue(params.responseId)}. Received urlIndex=${formatInputValue(params.urlIndex)}; valid indexes are 0-${data.urls.length - 1}. Available URLs:\n ${available || " none"}\nUse one of the available indexes.` }],
|
|
2453
2524
|
details: { error: "Index out of range" },
|
|
2454
2525
|
};
|
|
2455
2526
|
}
|
|
@@ -2457,13 +2528,13 @@ export default function (pi) {
|
|
|
2457
2528
|
else {
|
|
2458
2529
|
const available = data.urls.map((u, i) => `${i}: ${u.url}`).join("\n ");
|
|
2459
2530
|
return {
|
|
2460
|
-
content: [{ type: "text", text: `Specify url or urlIndex. Available:\n ${available}` }],
|
|
2531
|
+
content: [{ type: "text", text: `Specify url or urlIndex for responseId ${formatInputValue(params.responseId)}. Available URLs:\n ${available || " none"}` }],
|
|
2461
2532
|
details: { error: "No URL specified" },
|
|
2462
2533
|
};
|
|
2463
2534
|
}
|
|
2464
2535
|
if (urlData.error) {
|
|
2465
2536
|
return {
|
|
2466
|
-
content: [{ type: "text", text: `Error
|
|
2537
|
+
content: [{ type: "text", text: `Error retrieving URL ${formatInputValue(urlData.url)} from responseId ${formatInputValue(params.responseId)}: ${urlData.error}. Check the stored fetch result and retry with another URL or urlIndex if needed.` }],
|
|
2467
2538
|
details: { error: urlData.error, url: urlData.url },
|
|
2468
2539
|
};
|
|
2469
2540
|
}
|
|
@@ -2478,27 +2549,29 @@ export default function (pi) {
|
|
|
2478
2549
|
}
|
|
2479
2550
|
catch (err) {
|
|
2480
2551
|
const error = err instanceof Error ? err.message : String(err);
|
|
2481
|
-
return {
|
|
2552
|
+
return {
|
|
2553
|
+
content: [{ type: "text", text: `Unable to find ${formatInputValue(params.findText)} in URL ${formatInputValue(urlData.url)} for responseId ${formatInputValue(params.responseId)}: ${error}. Check findText and use a supported findMode.` }],
|
|
2554
|
+
details: { error, url: urlData.url },
|
|
2555
|
+
};
|
|
2482
2556
|
}
|
|
2483
2557
|
}
|
|
2484
|
-
const maxInlineContentChars = getMaxInlineContentChars();
|
|
2485
2558
|
const offset = params.offset ?? 0;
|
|
2486
2559
|
const limit = params.limit ?? maxInlineContentChars;
|
|
2487
2560
|
if (!Number.isInteger(offset) || offset < 0) {
|
|
2488
2561
|
return {
|
|
2489
|
-
content: [{ type: "text", text:
|
|
2562
|
+
content: [{ type: "text", text: `Invalid offset: received ${formatInputValue(offset)} for URL ${formatInputValue(urlData.url)}; offset must be a non-negative integer. Use 0 or a larger integer.` }],
|
|
2490
2563
|
details: { error: "Invalid offset", offset },
|
|
2491
2564
|
};
|
|
2492
2565
|
}
|
|
2493
2566
|
if (!Number.isInteger(limit) || limit <= 0 || limit > maxInlineContentChars) {
|
|
2494
2567
|
return {
|
|
2495
|
-
content: [{ type: "text", text: `limit must be an integer from 1 to ${maxInlineContentChars}
|
|
2568
|
+
content: [{ type: "text", text: `Invalid limit: received ${formatInputValue(limit)} for URL ${formatInputValue(urlData.url)}; limit must be an integer from 1 to ${maxInlineContentChars}. Use a value in that range.` }],
|
|
2496
2569
|
details: { error: "Invalid limit", limit, maxLimit: maxInlineContentChars },
|
|
2497
2570
|
};
|
|
2498
2571
|
}
|
|
2499
2572
|
if (offset > urlData.content.length) {
|
|
2500
2573
|
return {
|
|
2501
|
-
content: [{ type: "text", text: `
|
|
2574
|
+
content: [{ type: "text", text: `Offset ${offset} is out of range for URL ${formatInputValue(urlData.url)} in responseId ${formatInputValue(params.responseId)}. Received offset ${offset}; valid range is 0-${urlData.content.length}. Use an offset within that range.` }],
|
|
2502
2575
|
details: { error: "Offset out of range", offset, contentLength: urlData.content.length },
|
|
2503
2576
|
};
|
|
2504
2577
|
}
|
|
@@ -2527,7 +2600,7 @@ export default function (pi) {
|
|
|
2527
2600
|
};
|
|
2528
2601
|
}
|
|
2529
2602
|
return {
|
|
2530
|
-
content: [{ type: "text", text:
|
|
2603
|
+
content: [{ type: "text", text: `Invalid stored data for responseId ${formatInputValue(params.responseId)}: received type ${formatInputValue(data.type)}. Use a responseId returned by ${storedContentSources}.` }],
|
|
2531
2604
|
details: { error: "Invalid data" },
|
|
2532
2605
|
};
|
|
2533
2606
|
},
|
|
@@ -2589,6 +2662,7 @@ export default function (pi) {
|
|
|
2589
2662
|
return new Text(statusLine + "\n" + theme.fg("dim", preview), 0, 0);
|
|
2590
2663
|
},
|
|
2591
2664
|
});
|
|
2665
|
+
}
|
|
2592
2666
|
if (isCommandEnabled(initConfig, "websearch"))
|
|
2593
2667
|
pi.registerCommand("websearch", {
|
|
2594
2668
|
description: "Open web search curator",
|