@demigodmode/pi-web-agent 1.5.0 → 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 CHANGED
@@ -7,13 +7,41 @@ The format is intentionally simple and release-oriented.
7
7
  ## Unreleased
8
8
 
9
9
  ### Added
10
- - Nothing yet.
10
+ - None.
11
+
12
+ ### Changed
13
+ - None.
14
+
15
+ ### Fixed
16
+ - None.
17
+
18
+ ### Breaking
19
+ - None.
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
+
36
+ ## [1.5.1] - 2026-06-22
37
+ ### Added
38
+ - None.
11
39
 
12
40
  ### Changed
13
- - Nothing yet.
41
+ - None.
14
42
 
15
43
  ### Fixed
16
- - Nothing yet.
44
+ - Updated vulnerable dependency paths reported by `npm audit`, including Vitest, Pi dev tooling, `undici`, `ws`, `protobufjs`, `postcss`, and the Vite docs toolchain override.
17
45
 
18
46
  ### Breaking
19
47
  - None.
@@ -30,7 +58,7 @@ The format is intentionally simple and release-oriented.
30
58
  - Backend config validation and fallback metadata now understand Brave, including clear `fallbackFrom: 'brave'` reporting when DuckDuckGo fallback is used.
31
59
 
32
60
  ### Fixed
33
- - Nothing yet.
61
+ - None.
34
62
 
35
63
  ### Breaking
36
64
  - None.
@@ -44,7 +72,7 @@ The format is intentionally simple and release-oriented.
44
72
  - Partial research caveats are now more specific when evidence is community-only, low-diversity, blocked, or cautionary.
45
73
 
46
74
  ### Fixed
47
- - Nothing yet.
75
+ - None.
48
76
 
49
77
  ### Breaking
50
78
  - None.
@@ -71,10 +99,10 @@ The format is intentionally simple and release-oriented.
71
99
  - Added interactive URL prompts for SearXNG and Firecrawl backend setup.
72
100
 
73
101
  ### Changed
74
- - Nothing yet.
102
+ - None.
75
103
 
76
104
  ### Fixed
77
- - Nothing yet.
105
+ - None.
78
106
 
79
107
  ### Breaking
80
108
  - None.
@@ -89,7 +117,7 @@ The format is intentionally simple and release-oriented.
89
117
  - `/web-agent show` and `/web-agent doctor` now report configured backend fallback/options.
90
118
 
91
119
  ### Fixed
92
- - Nothing yet.
120
+ - None.
93
121
 
94
122
  ### Breaking
95
123
  - None.
@@ -102,7 +130,7 @@ The format is intentionally simple and release-oriented.
102
130
  - Migrated Pi package imports to `@earendil-works/*` after the upstream Pi scope move.
103
131
 
104
132
  ### Fixed
105
- - Nothing yet.
133
+ - None.
106
134
 
107
135
  ### Breaking
108
136
  - This release requires Pi 0.74+. Users on older Pi versions should stay on `@demigodmode/pi-web-agent@0.6.x` until they update Pi.
@@ -126,10 +154,10 @@ The format is intentionally simple and release-oriented.
126
154
 
127
155
  ## [0.5.1] - 2026-05-04
128
156
  ### Added
129
- - Nothing yet.
157
+ - None.
130
158
 
131
159
  ### Changed
132
- - Nothing yet.
160
+ - None.
133
161
 
134
162
  ### Fixed
135
163
  - Fixed the Windows browser-resolution test so it is deterministic on Linux CI.
@@ -174,7 +202,7 @@ The format is intentionally simple and release-oriented.
174
202
 
175
203
  ## [0.3.1] - 2026-04-22
176
204
  ### Added
177
- - Nothing yet.
205
+ - None.
178
206
 
179
207
  ### Changed
180
208
  - Stopped self-upgrading npm inside the publish workflow before install and publish steps.
@@ -222,10 +250,10 @@ The format is intentionally simple and release-oriented.
222
250
 
223
251
  ## [0.2.1] - 2026-04-20
224
252
  ### Added
225
- - Nothing yet.
253
+ - None.
226
254
 
227
255
  ### Changed
228
- - Nothing yet.
256
+ - None.
229
257
 
230
258
  ### Fixed
231
259
  - Added the missing npm `--provenance` flag to the publish workflow so Trusted Publishing can exchange the GitHub OIDC token correctly.
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, Brave can be selected as a hosted search backend while `web_explore` still handles page reading, ranking, and caveats itself.
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
- If you already run SearXNG or Firecrawl, see the self-hosted backend guide:
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;
@@ -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 brave');
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');
@@ -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;
@@ -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
- : createDuckDuckGoSearch();
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,7 @@
1
+ import type { WebSearchResponse } from '../types.js';
2
+ export declare function createYouComSearchTool({ apiKey, fetchImpl }: {
3
+ apiKey?: string;
4
+ fetchImpl?: typeof fetch;
5
+ }): ({ query }: {
6
+ query: string;
7
+ }) => Promise<WebSearchResponse>;
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@demigodmode/pi-web-agent",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Pi package for reliable web access with explicit search, fetch, and headless boundaries.",
5
5
  "type": "module",
6
6
  "main": "./dist/extension.js",
@@ -61,17 +61,20 @@
61
61
  "typebox": "^1.1.37"
62
62
  },
63
63
  "devDependencies": {
64
- "@earendil-works/pi-coding-agent": "^0.74.0",
65
- "@earendil-works/pi-tui": "^0.74.0",
64
+ "@earendil-works/pi-coding-agent": "^0.79.10",
65
+ "@earendil-works/pi-tui": "^0.79.10",
66
66
  "@types/jsdom": "^21.1.7",
67
67
  "@types/node": "^24.0.0",
68
- "@vitest/coverage-v8": "^3.2.4",
68
+ "@vitest/coverage-v8": "^3.2.6",
69
69
  "typescript": "^5.8.0",
70
70
  "vitepress": "^1.6.4",
71
- "vitest": "^3.2.0"
71
+ "vitest": "^3.2.6"
72
72
  },
73
73
  "peerDependencies": {
74
74
  "@earendil-works/pi-coding-agent": "*",
75
75
  "@earendil-works/pi-tui": "*"
76
+ },
77
+ "overrides": {
78
+ "vite": "6.4.3"
76
79
  }
77
80
  }