@demigodmode/pi-web-agent 1.5.1 → 1.6.0
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 +15 -0
- package/README.md +4 -4
- package/dist/backends/config.d.ts +1 -1
- package/dist/backends/config.js +4 -3
- package/dist/backends/doctor.js +39 -0
- package/dist/backends/factory.d.ts +2 -0
- package/dist/backends/factory.js +8 -1
- package/dist/commands/web-agent-config.js +11 -5
- package/dist/search/youcom.d.ts +7 -0
- package/dist/search/youcom.js +81 -0
- package/dist/types.d.ts +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -18,6 +18,21 @@ The format is intentionally simple and release-oriented.
|
|
|
18
18
|
### Breaking
|
|
19
19
|
- None.
|
|
20
20
|
|
|
21
|
+
## [1.6.0] - 2026-07-05
|
|
22
|
+
### Added
|
|
23
|
+
- You.com is now available as a hosted search backend for `web_explore`, selectable alongside DuckDuckGo, SearXNG, and Brave. Set `YDC_API_KEY` in the environment and choose `youcom` from **Settings → Backends**. You.com handles source discovery only; `web_explore` still fetches pages, ranks evidence, handles caveats, and synthesizes the answer itself.
|
|
24
|
+
- `/web-agent settings` now treats You.com as a first-class search backend (with optional DuckDuckGo fallback) while keeping API keys out of config files via `YDC_API_KEY`.
|
|
25
|
+
- `/web-agent doctor` now reports You.com setup status, warns when `YDC_API_KEY` is missing, and validates configured You.com access when a key is present.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- None.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
- None.
|
|
32
|
+
|
|
33
|
+
### Breaking
|
|
34
|
+
- None.
|
|
35
|
+
|
|
21
36
|
## [1.5.1] - 2026-06-22
|
|
22
37
|
### Added
|
|
23
38
|
- None.
|
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ Headless rendering first tries a detectable Chromium-family browser: Chrome, Chr
|
|
|
27
27
|
Later on, update installed packages with:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
pi update
|
|
30
|
+
pi update --extensions
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
## Docs
|
|
@@ -109,7 +109,7 @@ Example:
|
|
|
109
109
|
}
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
-
Backend config is also supported. Defaults remain DuckDuckGo search, plain HTTP fetch, and local-browser headless fallback with managed Chromium fallback configured. If you have a Brave Search API key,
|
|
112
|
+
Backend config is also supported. Defaults remain DuckDuckGo search, plain HTTP fetch, and local-browser headless fallback with managed Chromium fallback configured. If you have a Brave Search or You.com API key, either can be selected as a hosted search backend while `web_explore` still handles page reading, ranking, and caveats itself.
|
|
113
113
|
|
|
114
114
|
Backend settings can be changed from:
|
|
115
115
|
|
|
@@ -117,9 +117,9 @@ Backend settings can be changed from:
|
|
|
117
117
|
/web-agent settings
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
-
Choose **Backends** to edit search/fetch providers, fallback behavior, and SearXNG or Firecrawl base URLs interactively. Brave Search uses `PI_WEB_AGENT_BRAVE_API_KEY`. Firecrawl API keys should also stay in environment variables rather than being written into config files.
|
|
120
|
+
Choose **Backends** to edit search/fetch providers, fallback behavior, and SearXNG or Firecrawl base URLs interactively. Brave Search uses `PI_WEB_AGENT_BRAVE_API_KEY` and You.com uses `YDC_API_KEY`. Firecrawl API keys should also stay in environment variables rather than being written into config files.
|
|
121
121
|
|
|
122
|
-
For the full backend config shape, including SearXNG, Brave, Firecrawl, and fallback behavior, see:
|
|
122
|
+
For the full backend config shape, including SearXNG, Brave, You.com, Firecrawl, and fallback behavior, see:
|
|
123
123
|
|
|
124
124
|
- https://demigodmode.github.io/pi-web-agent/self-hosted-backends
|
|
125
125
|
|
|
@@ -8,7 +8,7 @@ export type FirecrawlOptions = {
|
|
|
8
8
|
onlyMainContent?: boolean;
|
|
9
9
|
};
|
|
10
10
|
export type SearchBackendConfig = {
|
|
11
|
-
provider: 'duckduckgo' | 'searxng' | 'brave';
|
|
11
|
+
provider: 'duckduckgo' | 'searxng' | 'brave' | 'youcom';
|
|
12
12
|
baseUrl?: string;
|
|
13
13
|
fallback?: 'duckduckgo';
|
|
14
14
|
options?: SearxngOptions;
|
package/dist/backends/config.js
CHANGED
|
@@ -40,7 +40,8 @@ export function extractBackendConfigOverride(file) {
|
|
|
40
40
|
const override = {};
|
|
41
41
|
if (backends?.search?.provider === 'duckduckgo' ||
|
|
42
42
|
backends?.search?.provider === 'searxng' ||
|
|
43
|
-
backends?.search?.provider === 'brave'
|
|
43
|
+
backends?.search?.provider === 'brave' ||
|
|
44
|
+
backends?.search?.provider === 'youcom') {
|
|
44
45
|
override.search = { provider: backends.search.provider };
|
|
45
46
|
if (backends.search.provider === 'searxng' && typeof backends.search.baseUrl === 'string') {
|
|
46
47
|
override.search.baseUrl = backends.search.baseUrl;
|
|
@@ -84,8 +85,8 @@ export function validateBackendConfig(config) {
|
|
|
84
85
|
if (config.fetch.provider === 'firecrawl' && !config.fetch.baseUrl) {
|
|
85
86
|
issues.push('fetch provider firecrawl requires backends.fetch.baseUrl');
|
|
86
87
|
}
|
|
87
|
-
if (config.search.fallback === 'duckduckgo' && config.search.provider !== 'searxng' && config.search.provider !== 'brave') {
|
|
88
|
-
issues.push('search fallback duckduckgo is only supported when search provider is searxng or
|
|
88
|
+
if (config.search.fallback === 'duckduckgo' && config.search.provider !== 'searxng' && config.search.provider !== 'brave' && config.search.provider !== 'youcom') {
|
|
89
|
+
issues.push('search fallback duckduckgo is only supported when search provider is searxng, brave, or youcom');
|
|
89
90
|
}
|
|
90
91
|
if (config.fetch.fallback === 'http' && config.fetch.provider !== 'firecrawl') {
|
|
91
92
|
issues.push('fetch fallback http is only supported when fetch provider is firecrawl');
|
package/dist/backends/doctor.js
CHANGED
|
@@ -12,6 +12,9 @@ function braveDoctorUrl() {
|
|
|
12
12
|
url.searchParams.set('count', '1');
|
|
13
13
|
return url.toString();
|
|
14
14
|
}
|
|
15
|
+
function youcomDoctorBody() {
|
|
16
|
+
return JSON.stringify({ query: 'pi-web-agent-doctor', max_results: 1 });
|
|
17
|
+
}
|
|
15
18
|
function searxngDoctorUrl(baseUrl, options = {}) {
|
|
16
19
|
const url = new URL('/search', baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`);
|
|
17
20
|
url.searchParams.set('q', 'pi-web-agent-doctor');
|
|
@@ -69,6 +72,42 @@ export async function checkBackendHealth(config, { fetchImpl = fetch, timeoutMs
|
|
|
69
72
|
}
|
|
70
73
|
}
|
|
71
74
|
}
|
|
75
|
+
else if (config.search.provider === 'youcom') {
|
|
76
|
+
const apiKey = process.env.YDC_API_KEY;
|
|
77
|
+
if (!apiKey?.trim()) {
|
|
78
|
+
lines.push('search backend: youcom warning (missing YDC_API_KEY)');
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
const timeout = withTimeout(timeoutMs);
|
|
82
|
+
try {
|
|
83
|
+
const response = await fetchImpl('https://api.you.com/v1/agents/search', {
|
|
84
|
+
method: 'POST',
|
|
85
|
+
headers: {
|
|
86
|
+
Accept: 'application/json',
|
|
87
|
+
'Content-Type': 'application/json',
|
|
88
|
+
'X-API-Key': apiKey
|
|
89
|
+
},
|
|
90
|
+
body: youcomDoctorBody(),
|
|
91
|
+
signal: timeout.signal
|
|
92
|
+
});
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
lines.push(`search backend: youcom warning (HTTP ${response.status})`);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const json = (await response.json());
|
|
98
|
+
lines.push(Array.isArray(json.results)
|
|
99
|
+
? 'search backend: youcom ok'
|
|
100
|
+
: 'search backend: youcom warning (unexpected response)');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
lines.push(`search backend: youcom warning (${message(error)})`);
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
timeout.done();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
72
111
|
else if (!config.search.baseUrl) {
|
|
73
112
|
lines.push('search backend: searxng warning (missing baseUrl)');
|
|
74
113
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createFirecrawlFetcher } from '../fetch/firecrawl-fetch.js';
|
|
2
2
|
import { createBraveSearchTool } from '../search/brave.js';
|
|
3
|
+
import { createYouComSearchTool } from '../search/youcom.js';
|
|
3
4
|
import { createSearxngSearchTool } from '../search/searxng.js';
|
|
4
5
|
import { createWebFetchHeadlessTool } from '../tools/web-fetch-headless.js';
|
|
5
6
|
import { createWebFetchTool } from '../tools/web-fetch.js';
|
|
@@ -21,6 +22,7 @@ export type BackendFactoryDeps = {
|
|
|
21
22
|
createDuckDuckGoSearch?: typeof createWebSearchTool;
|
|
22
23
|
createSearxngSearch?: typeof createSearxngSearchTool;
|
|
23
24
|
createBraveSearch?: typeof createBraveSearchTool;
|
|
25
|
+
createYouComSearch?: typeof createYouComSearchTool;
|
|
24
26
|
createHttpFetch?: typeof createWebFetchTool;
|
|
25
27
|
createFirecrawlFetch?: typeof createFirecrawlFetcher;
|
|
26
28
|
createHeadlessFetch?: typeof createWebFetchHeadlessTool;
|
package/dist/backends/factory.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createFirecrawlFetcher } from '../fetch/firecrawl-fetch.js';
|
|
2
2
|
import { createBraveSearchTool } from '../search/brave.js';
|
|
3
|
+
import { createYouComSearchTool } from '../search/youcom.js';
|
|
3
4
|
import { createSearxngSearchTool } from '../search/searxng.js';
|
|
4
5
|
import { buildFetchPresentation } from '../presentation/fetch-presentation.js';
|
|
5
6
|
import { buildSearchPresentation } from '../presentation/search-presentation.js';
|
|
@@ -73,6 +74,7 @@ export function createBackendSet(config = DEFAULT_BACKEND_CONFIG, deps = {}) {
|
|
|
73
74
|
const createDuckDuckGoSearch = deps.createDuckDuckGoSearch ?? createWebSearchTool;
|
|
74
75
|
const createSearxngSearch = deps.createSearxngSearch ?? createSearxngSearchTool;
|
|
75
76
|
const createBraveSearch = deps.createBraveSearch ?? createBraveSearchTool;
|
|
77
|
+
const createYouComSearch = deps.createYouComSearch ?? createYouComSearchTool;
|
|
76
78
|
const createHttpFetch = deps.createHttpFetch ?? createWebFetchTool;
|
|
77
79
|
const createFirecrawlFetch = deps.createFirecrawlFetch ?? createFirecrawlFetcher;
|
|
78
80
|
const createHeadlessFetch = deps.createHeadlessFetch ?? createWebFetchHeadlessTool;
|
|
@@ -82,13 +84,18 @@ export function createBackendSet(config = DEFAULT_BACKEND_CONFIG, deps = {}) {
|
|
|
82
84
|
: invalidSearxngSearch()
|
|
83
85
|
: config.search.provider === 'brave'
|
|
84
86
|
? createBraveSearch({ apiKey: process.env.PI_WEB_AGENT_BRAVE_API_KEY })
|
|
85
|
-
:
|
|
87
|
+
: config.search.provider === 'youcom'
|
|
88
|
+
? createYouComSearch({ apiKey: process.env.YDC_API_KEY })
|
|
89
|
+
: createDuckDuckGoSearch();
|
|
86
90
|
if (config.search.provider === 'searxng' && config.search.fallback === 'duckduckgo') {
|
|
87
91
|
search = withSearchFallback(search, createDuckDuckGoSearch(), 'searxng');
|
|
88
92
|
}
|
|
89
93
|
if (config.search.provider === 'brave' && config.search.fallback === 'duckduckgo') {
|
|
90
94
|
search = withSearchFallback(search, createDuckDuckGoSearch(), 'brave');
|
|
91
95
|
}
|
|
96
|
+
if (config.search.provider === 'youcom' && config.search.fallback === 'duckduckgo') {
|
|
97
|
+
search = withSearchFallback(search, createDuckDuckGoSearch(), 'youcom');
|
|
98
|
+
}
|
|
92
99
|
const httpFetch = createHttpFetch();
|
|
93
100
|
let fetchPage = config.fetch.provider === 'firecrawl'
|
|
94
101
|
? config.fetch.baseUrl
|
|
@@ -124,7 +124,7 @@ function buildBackendSettingsItems(scope, backends) {
|
|
|
124
124
|
id: 'backend:search:provider',
|
|
125
125
|
label: 'Search backend',
|
|
126
126
|
currentValue: backends.search.provider,
|
|
127
|
-
values: ['duckduckgo', 'searxng', 'brave']
|
|
127
|
+
values: ['duckduckgo', 'searxng', 'brave', 'youcom']
|
|
128
128
|
},
|
|
129
129
|
{
|
|
130
130
|
id: 'backend:search:baseUrl',
|
|
@@ -135,8 +135,8 @@ function buildBackendSettingsItems(scope, backends) {
|
|
|
135
135
|
{
|
|
136
136
|
id: 'backend:search:fallback',
|
|
137
137
|
label: 'Search fallback',
|
|
138
|
-
currentValue: backends.search.provider === 'searxng' || backends.search.provider === 'brave' ? backends.search.fallback ?? 'off' : 'off',
|
|
139
|
-
values: backends.search.provider === 'searxng' || backends.search.provider === 'brave' ? ['off', 'duckduckgo'] : ['off']
|
|
138
|
+
currentValue: backends.search.provider === 'searxng' || backends.search.provider === 'brave' || backends.search.provider === 'youcom' ? backends.search.fallback ?? 'off' : 'off',
|
|
139
|
+
values: backends.search.provider === 'searxng' || backends.search.provider === 'brave' || backends.search.provider === 'youcom' ? ['off', 'duckduckgo'] : ['off']
|
|
140
140
|
},
|
|
141
141
|
{
|
|
142
142
|
id: 'backend:secret:brave',
|
|
@@ -144,6 +144,12 @@ function buildBackendSettingsItems(scope, backends) {
|
|
|
144
144
|
currentValue: 'env var',
|
|
145
145
|
values: ['env var']
|
|
146
146
|
},
|
|
147
|
+
{
|
|
148
|
+
id: 'backend:secret:youcom',
|
|
149
|
+
label: 'You.com API key',
|
|
150
|
+
currentValue: 'env var',
|
|
151
|
+
values: ['env var']
|
|
152
|
+
},
|
|
147
153
|
{
|
|
148
154
|
id: 'backend:fetch:provider',
|
|
149
155
|
label: 'Fetch backend',
|
|
@@ -255,7 +261,7 @@ export function applySettingsValue(state, id, newValue) {
|
|
|
255
261
|
}
|
|
256
262
|
currentDraft.tools = nextTools;
|
|
257
263
|
}
|
|
258
|
-
if (id === 'backend:search:provider' && (newValue === 'duckduckgo' || newValue === 'searxng' || newValue === 'brave')) {
|
|
264
|
+
if (id === 'backend:search:provider' && (newValue === 'duckduckgo' || newValue === 'searxng' || newValue === 'brave' || newValue === 'youcom')) {
|
|
259
265
|
currentBackends.search.provider = newValue;
|
|
260
266
|
if (newValue !== 'searxng') {
|
|
261
267
|
delete currentBackends.search.baseUrl;
|
|
@@ -266,7 +272,7 @@ export function applySettingsValue(state, id, newValue) {
|
|
|
266
272
|
}
|
|
267
273
|
}
|
|
268
274
|
if (id === 'backend:search:fallback') {
|
|
269
|
-
if (newValue === 'duckduckgo' && (currentBackends.search.provider === 'searxng' || currentBackends.search.provider === 'brave')) {
|
|
275
|
+
if (newValue === 'duckduckgo' && (currentBackends.search.provider === 'searxng' || currentBackends.search.provider === 'brave' || currentBackends.search.provider === 'youcom')) {
|
|
270
276
|
currentBackends.search.fallback = 'duckduckgo';
|
|
271
277
|
}
|
|
272
278
|
else {
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { buildSearchPresentation } from '../presentation/search-presentation.js';
|
|
2
|
+
const YOUCOM_SEARCH_URL = 'https://api.you.com/v1/agents/search';
|
|
3
|
+
function resultWithPresentation(result) {
|
|
4
|
+
return { ...result, presentation: buildSearchPresentation(result) };
|
|
5
|
+
}
|
|
6
|
+
function normalizeResults(response) {
|
|
7
|
+
return (response.results ?? []).flatMap((item) => {
|
|
8
|
+
if (typeof item.title !== 'string' || typeof item.url !== 'string') {
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
11
|
+
return [
|
|
12
|
+
{
|
|
13
|
+
title: item.title,
|
|
14
|
+
url: item.url,
|
|
15
|
+
snippet: typeof item.snippet === 'string' ? item.snippet : ''
|
|
16
|
+
}
|
|
17
|
+
];
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
export function createYouComSearchTool({ apiKey, fetchImpl = fetch }) {
|
|
21
|
+
return async function youComSearch({ query }) {
|
|
22
|
+
const normalizedQuery = query.trim();
|
|
23
|
+
if (!normalizedQuery) {
|
|
24
|
+
return resultWithPresentation({
|
|
25
|
+
status: 'error',
|
|
26
|
+
results: [],
|
|
27
|
+
metadata: { backend: 'youcom', cacheHit: false },
|
|
28
|
+
error: { code: 'INVALID_QUERY', message: 'Query must not be empty.' }
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
if (!apiKey?.trim()) {
|
|
32
|
+
return resultWithPresentation({
|
|
33
|
+
status: 'error',
|
|
34
|
+
results: [],
|
|
35
|
+
metadata: { backend: 'youcom', cacheHit: false },
|
|
36
|
+
error: {
|
|
37
|
+
code: 'BACKEND_CONFIG_INVALID',
|
|
38
|
+
message: 'You.com search requires YDC_API_KEY.'
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const response = await fetchImpl(YOUCOM_SEARCH_URL, {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
headers: {
|
|
46
|
+
Accept: 'application/json',
|
|
47
|
+
'Content-Type': 'application/json',
|
|
48
|
+
'X-API-Key': apiKey
|
|
49
|
+
},
|
|
50
|
+
body: JSON.stringify({ query: normalizedQuery, max_results: 10 })
|
|
51
|
+
});
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
throw new Error(`HTTP ${response.status}`);
|
|
54
|
+
}
|
|
55
|
+
const parsed = (await response.json());
|
|
56
|
+
const results = normalizeResults(parsed);
|
|
57
|
+
if (results.length === 0) {
|
|
58
|
+
return resultWithPresentation({
|
|
59
|
+
status: 'error',
|
|
60
|
+
results: [],
|
|
61
|
+
metadata: { backend: 'youcom', cacheHit: false },
|
|
62
|
+
error: { code: 'NO_RESULTS', message: 'You.com returned no usable results for this query.' }
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return resultWithPresentation({
|
|
66
|
+
status: 'ok',
|
|
67
|
+
results,
|
|
68
|
+
metadata: { backend: 'youcom', cacheHit: false }
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
73
|
+
return resultWithPresentation({
|
|
74
|
+
status: 'error',
|
|
75
|
+
results: [],
|
|
76
|
+
metadata: { backend: 'youcom', cacheHit: false },
|
|
77
|
+
error: { code: 'FETCH_FAILED', message: `You.com search request failed: ${rawMessage}` }
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -11,9 +11,9 @@ export type ToolError = {
|
|
|
11
11
|
message: string;
|
|
12
12
|
};
|
|
13
13
|
export type SearchMetadata = {
|
|
14
|
-
backend: 'duckduckgo' | 'searxng' | 'brave';
|
|
14
|
+
backend: 'duckduckgo' | 'searxng' | 'brave' | 'youcom';
|
|
15
15
|
cacheHit: boolean;
|
|
16
|
-
fallbackFrom?: 'searxng' | 'brave';
|
|
16
|
+
fallbackFrom?: 'searxng' | 'brave' | 'youcom';
|
|
17
17
|
fallbackReason?: string;
|
|
18
18
|
};
|
|
19
19
|
export type FetchMetadata = {
|
package/package.json
CHANGED