@companion-ai/feynman 0.5.43 → 0.5.44
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/RELEASES.md +5 -0
- package/dist/cli.js +4 -7
- package/dist/pi/web-access.js +35 -29
- package/dist/search/commands.js +13 -20
- package/package.json +2 -2
package/RELEASES.md
CHANGED
|
@@ -6,6 +6,11 @@ GitHub release notes are generated from the matching `## vX.Y.Z` section in this
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## v0.5.44 - 2026-10-13
|
|
10
|
+
|
|
11
|
+
- **`feynman search set` works with every search provider pi-web-access supports.** It accepted only `auto`, `exa`, `perplexity` and `gemini`; it now takes all 39, including Brave, Tavily, Kagi, Serper, You.com, SearXNG and DuckDuckGo, and saves each key where pi-web-access reads it. `feynman search status` and `feynman doctor` name the provider you chose and list every saved key, and no longer describe an outdated automatic search order.
|
|
12
|
+
- **pi-web-access 0.40.0.** Asking about a YouTube or local video works again when a thumbnail comes back with the transcript, excerpts no longer cut an emoji in half, and a new opt-in setting (`fetchContent.redirects: "same-origin"`) refuses redirects to another site.
|
|
13
|
+
|
|
9
14
|
## v0.5.43 - 2026-10-13
|
|
10
15
|
|
|
11
16
|
- **Feynman reads and writes Pi's configuration only through Pi.** Settings now go through Pi's settings manager, which locks the file and changes only the keys Feynman sets, so a change Pi or another Feynman window makes at the same moment is no longer overwritten. Model checks, local-model detection and `feynman doctor` read your models through Pi, so comments in `models.json`, a `baseUrl` set on one model, and a key saved with `feynman model login` count the same way they do when Feynman runs.
|
package/dist/cli.js
CHANGED
|
@@ -24,7 +24,7 @@ import { BUNDLED_PI_PACKAGES, buildPiEnv, findWindowsBash, WINDOWS_BASH_MISSING_
|
|
|
24
24
|
import { getConfiguredServiceTier, normalizeServiceTier, setConfiguredServiceTier } from "./model/service-tier.js";
|
|
25
25
|
import { authenticateModelProvider, getCurrentModelSpec, isLocalModelProvider, loginModelProvider, logoutModelProvider, printModelList, selectDefaultModel, setDefaultModelSpec, } from "./model/commands.js";
|
|
26
26
|
import { buildModelStatusSnapshotFromRecords, getAuthenticatedModelRecords, isProClassModelSpec, getSupportedModelRecords, } from "./model/catalog.js";
|
|
27
|
-
import { clearSearchConfig, printSearchStatus, setSearchProvider } from "./search/commands.js";
|
|
27
|
+
import { clearSearchConfig, printSearchStatus, searchSetUsage, setSearchProvider } from "./search/commands.js";
|
|
28
28
|
import { fetchLatestFeynmanVersion, getFeynmanUpgradeLines, isNewerVersion } from "./system/self-update.js";
|
|
29
29
|
import { runDoctor, runStatus } from "./setup/doctor.js";
|
|
30
30
|
import { setupPreviewDependencies } from "./setup/preview.js";
|
|
@@ -307,12 +307,9 @@ function handleSearchCommand(subcommand, args) {
|
|
|
307
307
|
return;
|
|
308
308
|
}
|
|
309
309
|
if (subcommand === "set") {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
throw new Error("Usage: feynman search set <auto|perplexity|exa|gemini> [api-key]");
|
|
314
|
-
}
|
|
315
|
-
setSearchProvider(provider, args[1]);
|
|
310
|
+
if (!args[0])
|
|
311
|
+
throw new Error(searchSetUsage());
|
|
312
|
+
setSearchProvider(args[0], args[1]);
|
|
316
313
|
return;
|
|
317
314
|
}
|
|
318
315
|
if (subcommand === "clear") {
|
package/dist/pi/web-access.js
CHANGED
|
@@ -2,13 +2,33 @@ import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
3
|
import { readJsonFile } from "../config/json-file.js";
|
|
4
4
|
import { getFeynmanAgentDir, getFeynmanHome } from "../config/paths.js";
|
|
5
|
+
// pi-web-access's search providers (SEARCH_PROVIDERS in its gemini-search.ts;
|
|
6
|
+
// a test keeps this list in step with the installed package).
|
|
7
|
+
export const PI_WEB_SEARCH_PROVIDERS = [
|
|
8
|
+
"auto", "all", "openai", "brave", "parallel", "parallel-mcp", "tinyfish", "search1api", "searchinfinity", "querit",
|
|
9
|
+
"tavily", "you", "firecrawl", "jina", "searxng", "degoog", "duckduckgo", "perplexity", "gemini", "kimi", "exa",
|
|
10
|
+
"serpdive", "kagi", "ollama", "anysearch", "xai", "mistral", "brightdata", "serpbase", "serpapi", "serper", "serply",
|
|
11
|
+
"valyu", "bocha", "xcrawl", "baizhi", "zai", "keenable", "ceramic",
|
|
12
|
+
];
|
|
13
|
+
// The web-search.json field holding a provider's key (searxng takes its base URL);
|
|
14
|
+
// undefined for providers with nothing to set.
|
|
15
|
+
export function searchProviderKeyField(provider) {
|
|
16
|
+
if (provider === "auto" || provider === "all" || provider === "duckduckgo" || provider === "kimi")
|
|
17
|
+
return undefined;
|
|
18
|
+
if (provider === "parallel-mcp")
|
|
19
|
+
return "parallelApiKey";
|
|
20
|
+
if (provider === "searxng")
|
|
21
|
+
return "searxngBaseUrl";
|
|
22
|
+
return `${provider}ApiKey`;
|
|
23
|
+
}
|
|
24
|
+
const ROUTE_LABELS = { auto: "Auto", perplexity: "Perplexity", exa: "Exa", gemini: "Gemini" };
|
|
5
25
|
// pi-web-access reads web-search.json from PI_CODING_AGENT_DIR.
|
|
6
26
|
export function getPiWebSearchConfigPath(home) {
|
|
7
27
|
const feynmanHome = home ? resolve(home, ".feynman") : getFeynmanHome();
|
|
8
28
|
return resolve(getFeynmanAgentDir(feynmanHome), "web-search.json");
|
|
9
29
|
}
|
|
10
30
|
function normalizeProvider(value) {
|
|
11
|
-
return value
|
|
31
|
+
return PI_WEB_SEARCH_PROVIDERS.includes(value) ? value : undefined;
|
|
12
32
|
}
|
|
13
33
|
function normalizeWorkflow(value) {
|
|
14
34
|
return value === "none" || value === "summary-review" ? value : undefined;
|
|
@@ -48,29 +68,12 @@ export function savePiWebAccessConfig(updates, configPath = getPiWebSearchConfig
|
|
|
48
68
|
// ignore permission errors (best-effort)
|
|
49
69
|
}
|
|
50
70
|
}
|
|
51
|
-
function formatRouteLabel(provider) {
|
|
52
|
-
switch (provider) {
|
|
53
|
-
case "perplexity":
|
|
54
|
-
return "Perplexity";
|
|
55
|
-
case "exa":
|
|
56
|
-
return "Exa";
|
|
57
|
-
case "gemini":
|
|
58
|
-
return "Gemini";
|
|
59
|
-
default:
|
|
60
|
-
return "Auto";
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
71
|
function formatRouteNote(provider) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
case "gemini":
|
|
70
|
-
return "Pi web-access will use Gemini API. Browser-cookie fallback is opt-in.";
|
|
71
|
-
default:
|
|
72
|
-
return "Pi web-access will try Exa, then Perplexity, then Gemini API. Browser-cookie fallback is opt-in.";
|
|
73
|
-
}
|
|
72
|
+
if (provider === "auto")
|
|
73
|
+
return "Pi web-access picks from the search providers you have keys or sign-ins for. Browser-cookie fallback is opt-in.";
|
|
74
|
+
if (provider === "all")
|
|
75
|
+
return "Pi web-access searches every eligible provider at once.";
|
|
76
|
+
return `Pi web-access will use ${ROUTE_LABELS[provider] ?? provider} for search.`;
|
|
74
77
|
}
|
|
75
78
|
export function getPiWebAccessStatus(config = loadPiWebAccessConfig(), configPath = getPiWebSearchConfigPath()) {
|
|
76
79
|
const searchProvider = normalizeProvider(config.searchProvider) ?? normalizeProvider(config.route) ?? normalizeProvider(config.provider) ?? "auto";
|
|
@@ -82,6 +85,10 @@ export function getPiWebAccessStatus(config = loadPiWebAccessConfig(), configPat
|
|
|
82
85
|
const chromeProfile = normalizeNonEmptyString(config.browserCookies?.profile);
|
|
83
86
|
const geminiBrowserEnabled = config.allowBrowserCookies === true;
|
|
84
87
|
const effectiveProvider = searchProvider;
|
|
88
|
+
const configuredKeys = PI_WEB_SEARCH_PROVIDERS.filter((provider) => {
|
|
89
|
+
const field = searchProviderKeyField(provider);
|
|
90
|
+
return field !== undefined && field.endsWith("ApiKey") && Boolean(normalizeNonEmptyString(config[field]));
|
|
91
|
+
}).filter((provider) => provider !== "parallel-mcp");
|
|
85
92
|
return {
|
|
86
93
|
configPath,
|
|
87
94
|
configExists: existsSync(configPath),
|
|
@@ -93,7 +100,8 @@ export function getPiWebAccessStatus(config = loadPiWebAccessConfig(), configPat
|
|
|
93
100
|
geminiApiConfigured,
|
|
94
101
|
chromeProfile,
|
|
95
102
|
geminiBrowserEnabled,
|
|
96
|
-
|
|
103
|
+
configuredKeys,
|
|
104
|
+
routeLabel: ROUTE_LABELS[effectiveProvider] ?? effectiveProvider,
|
|
97
105
|
note: formatRouteNote(effectiveProvider),
|
|
98
106
|
};
|
|
99
107
|
}
|
|
@@ -104,18 +112,16 @@ export function formatPiWebAccessDoctorLines(status = getPiWebAccessStatus()) {
|
|
|
104
112
|
` search route: ${status.routeLabel}`,
|
|
105
113
|
` request route: ${status.requestProvider}`,
|
|
106
114
|
` search workflow: ${status.workflow}`,
|
|
107
|
-
`
|
|
108
|
-
` exa api: ${status.exaConfigured ? "configured" : "not configured"}`,
|
|
109
|
-
` gemini api: ${status.geminiApiConfigured ? "configured" : "not configured"}`,
|
|
115
|
+
` saved api keys: ${status.configuredKeys.join(", ") || "none"}`,
|
|
110
116
|
` gemini browser fallback: ${status.geminiBrowserEnabled ? "enabled" : "disabled"}`,
|
|
111
117
|
` config path: ${status.configPath}${configPathSuffix}`,
|
|
112
118
|
` note: ${status.note}`,
|
|
113
119
|
];
|
|
114
120
|
if (status.geminiBrowserEnabled && status.chromeProfile) {
|
|
115
|
-
lines.splice(
|
|
121
|
+
lines.splice(6, 0, ` gemini browser profile: ${status.chromeProfile}`);
|
|
116
122
|
}
|
|
117
123
|
if (!status.configExists) {
|
|
118
|
-
lines.push(" hint: run `feynman search set <
|
|
124
|
+
lines.push(" hint: run `feynman search set <provider> [api-key]` to configure web search (`feynman search set` lists providers)");
|
|
119
125
|
}
|
|
120
126
|
return lines;
|
|
121
127
|
}
|
package/dist/search/commands.js
CHANGED
|
@@ -1,20 +1,15 @@
|
|
|
1
|
-
import { getPiWebAccessStatus, savePiWebAccessConfig, } from "../pi/web-access.js";
|
|
1
|
+
import { getPiWebAccessStatus, PI_WEB_SEARCH_PROVIDERS, savePiWebAccessConfig, searchProviderKeyField, } from "../pi/web-access.js";
|
|
2
2
|
import { printInfo } from "../ui/terminal.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
exa: "exaApiKey",
|
|
7
|
-
gemini: "geminiApiKey",
|
|
8
|
-
};
|
|
3
|
+
export function searchSetUsage() {
|
|
4
|
+
return `Usage: feynman search set <provider> [api-key]\nProviders: ${PI_WEB_SEARCH_PROVIDERS.join(", ")}`;
|
|
5
|
+
}
|
|
9
6
|
export function printSearchStatus(status = getPiWebAccessStatus()) {
|
|
10
7
|
const configPathSuffix = status.configExists ? "" : " (not created yet)";
|
|
11
8
|
printInfo("Managed by: pi-web-access");
|
|
12
9
|
printInfo(`Search route: ${status.routeLabel}`);
|
|
13
10
|
printInfo(`Request route: ${status.requestProvider}`);
|
|
14
11
|
printInfo(`Search workflow: ${status.workflow}`);
|
|
15
|
-
printInfo(`
|
|
16
|
-
printInfo(`Exa API configured: ${status.exaConfigured ? "yes" : "no"}`);
|
|
17
|
-
printInfo(`Gemini API configured: ${status.geminiApiConfigured ? "yes" : "no"}`);
|
|
12
|
+
printInfo(`Saved API keys: ${status.configuredKeys.join(", ") || "none"}`);
|
|
18
13
|
printInfo(`Gemini browser fallback: ${status.geminiBrowserEnabled ? "enabled" : "disabled"}`);
|
|
19
14
|
if (status.geminiBrowserEnabled && status.chromeProfile) {
|
|
20
15
|
printInfo(`Gemini browser profile: ${status.chromeProfile}`);
|
|
@@ -23,17 +18,16 @@ export function printSearchStatus(status = getPiWebAccessStatus()) {
|
|
|
23
18
|
if (!status.configExists) {
|
|
24
19
|
printInfo("Not configured yet. Run one of:");
|
|
25
20
|
printInfo(" feynman search set auto");
|
|
26
|
-
printInfo(" feynman search set
|
|
27
|
-
printInfo(" feynman search set exa <api-key>");
|
|
28
|
-
printInfo(" feynman search set gemini <api-key>");
|
|
21
|
+
printInfo(" feynman search set <provider> <api-key> (for example exa, brave, tavily, perplexity)");
|
|
29
22
|
}
|
|
30
23
|
}
|
|
31
24
|
export function setSearchProvider(provider, apiKey) {
|
|
32
|
-
if (!
|
|
33
|
-
throw new Error(
|
|
25
|
+
if (!PI_WEB_SEARCH_PROVIDERS.includes(provider)) {
|
|
26
|
+
throw new Error(searchSetUsage());
|
|
34
27
|
}
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
const keyField = searchProviderKeyField(provider);
|
|
29
|
+
if (apiKey !== undefined && !keyField) {
|
|
30
|
+
throw new Error(`${provider} takes no API key. Usage: feynman search set ${provider}`);
|
|
37
31
|
}
|
|
38
32
|
const updates = {
|
|
39
33
|
provider,
|
|
@@ -42,9 +36,8 @@ export function setSearchProvider(provider, apiKey) {
|
|
|
42
36
|
allowBrowserCookies: false,
|
|
43
37
|
route: undefined,
|
|
44
38
|
};
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
updates[apiKeyField] = apiKey;
|
|
39
|
+
if (keyField && apiKey !== undefined) {
|
|
40
|
+
updates[keyField] = apiKey;
|
|
48
41
|
}
|
|
49
42
|
savePiWebAccessConfig(updates);
|
|
50
43
|
const status = getPiWebAccessStatus();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@companion-ai/feynman",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.44",
|
|
4
4
|
"description": "The open source AI research agent.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"pi-btw": "0.8.0",
|
|
87
87
|
"pi-docparser": "4.0.0",
|
|
88
88
|
"pi-subagents": "0.78.0",
|
|
89
|
-
"pi-web-access": "0.
|
|
89
|
+
"pi-web-access": "0.40.0",
|
|
90
90
|
"posthog-node": "^5.53.0"
|
|
91
91
|
},
|
|
92
92
|
"peerDependencies": {
|