@demigodmode/pi-web-agent 1.11.0 → 1.13.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 +29 -0
- package/README.md +2 -0
- package/dist/backends/config.d.ts +13 -0
- package/dist/backends/config.js +44 -1
- package/dist/backends/doctor.js +27 -29
- package/dist/backends/factory.d.ts +18 -7
- package/dist/backends/factory.js +126 -99
- package/dist/backends/failure.d.ts +11 -0
- package/dist/backends/failure.js +34 -0
- package/dist/backends/fallback-policy.d.ts +31 -0
- package/dist/backends/fallback-policy.js +239 -0
- package/dist/backends/provider-failure.d.ts +21 -0
- package/dist/backends/provider-failure.js +110 -0
- package/dist/backends/provider-health.d.ts +29 -0
- package/dist/backends/provider-health.js +49 -0
- package/dist/commands/web-agent-config.d.ts +14 -1
- package/dist/commands/web-agent-config.js +75 -3
- package/dist/extension.js +47 -3
- package/dist/extract/bot-check.d.ts +1 -0
- package/dist/extract/bot-check.js +10 -0
- package/dist/extract/readability.d.ts +4 -0
- package/dist/extract/readability.js +67 -3
- package/dist/extract/section-selector.d.ts +14 -0
- package/dist/extract/section-selector.js +233 -0
- package/dist/fetch/destination-policy.d.ts +32 -0
- package/dist/fetch/destination-policy.js +24 -0
- package/dist/fetch/firecrawl-fetch.d.ts +1 -1
- package/dist/fetch/firecrawl-fetch.js +74 -46
- package/dist/fetch/guard-proxy-fetch.d.ts +17 -0
- package/dist/fetch/guard-proxy-fetch.js +82 -0
- package/dist/fetch/guard-proxy.d.ts +58 -0
- package/dist/fetch/guard-proxy.js +420 -0
- package/dist/fetch/guarded-fetch.d.ts +7 -0
- package/dist/fetch/guarded-fetch.js +75 -0
- package/dist/fetch/headless-fetch.d.ts +11 -2
- package/dist/fetch/headless-fetch.js +190 -13
- package/dist/fetch/http-fetch.d.ts +1 -1
- package/dist/fetch/http-fetch.js +29 -8
- package/dist/fetch/network-guard.d.ts +82 -0
- package/dist/fetch/network-guard.js +275 -0
- package/dist/orchestration/answer-synthesizer.js +2 -0
- package/dist/orchestration/evidence-quality.d.ts +3 -2
- package/dist/orchestration/evidence-quality.js +2 -1
- package/dist/orchestration/index.d.ts +26 -7
- package/dist/orchestration/index.js +9 -2
- package/dist/orchestration/research-orchestrator.d.ts +23 -7
- package/dist/orchestration/research-orchestrator.js +60 -26
- package/dist/orchestration/research-types.d.ts +13 -1
- package/dist/orchestration/research-worker.d.ts +2 -4
- package/dist/orchestration/research-worker.js +51 -15
- package/dist/orchestration/stop-decider.js +3 -1
- package/dist/presentation/config-store.js +6 -0
- package/dist/presentation/explore-presentation.js +3 -1
- package/dist/presentation/fetch-presentation.js +16 -9
- package/dist/presentation/search-presentation.d.ts +2 -1
- package/dist/presentation/search-presentation.js +13 -1
- package/dist/readers/resolver.d.ts +3 -7
- package/dist/search/brave.d.ts +1 -2
- package/dist/search/brave.js +23 -80
- package/dist/search/duckduckgo.d.ts +7 -3
- package/dist/search/duckduckgo.js +17 -18
- package/dist/search/exa.d.ts +1 -2
- package/dist/search/exa.js +15 -76
- package/dist/search/fanout.d.ts +12 -0
- package/dist/search/fanout.js +86 -47
- package/dist/search/json-provider.d.ts +32 -0
- package/dist/search/json-provider.js +76 -0
- package/dist/search/searxng.d.ts +1 -2
- package/dist/search/searxng.js +15 -57
- package/dist/search/tavily.d.ts +1 -2
- package/dist/search/tavily.js +17 -74
- package/dist/search/youcom.d.ts +4 -2
- package/dist/search/youcom.js +49 -75
- package/dist/tools/web-explore.d.ts +9 -0
- package/dist/tools/web-explore.js +16 -2
- package/dist/tools/web-fetch-headless.d.ts +3 -5
- package/dist/tools/web-fetch-headless.js +3 -3
- package/dist/tools/web-fetch.d.ts +3 -5
- package/dist/tools/web-fetch.js +3 -3
- package/dist/tools/web-search.js +41 -103
- package/dist/types.d.ts +48 -0
- package/package.json +3 -3
|
@@ -11,6 +11,10 @@ export declare function createWebExploreTool({ explore }?: {
|
|
|
11
11
|
evidence: ResearchEvidence[];
|
|
12
12
|
workerPass: unknown;
|
|
13
13
|
metadata?: WebExploreResponse['metadata'];
|
|
14
|
+
terminalFailure?: {
|
|
15
|
+
code: string;
|
|
16
|
+
message: string;
|
|
17
|
+
};
|
|
14
18
|
}>;
|
|
15
19
|
} | ((input: {
|
|
16
20
|
query: string;
|
|
@@ -21,6 +25,10 @@ export declare function createWebExploreTool({ explore }?: {
|
|
|
21
25
|
evidence: ResearchEvidence[];
|
|
22
26
|
workerPass: unknown;
|
|
23
27
|
metadata?: WebExploreResponse['metadata'];
|
|
28
|
+
terminalFailure?: {
|
|
29
|
+
code: string;
|
|
30
|
+
message: string;
|
|
31
|
+
};
|
|
24
32
|
}>);
|
|
25
33
|
}): ({ query }: {
|
|
26
34
|
query: string;
|
|
@@ -42,6 +50,7 @@ export declare function createWebExploreTool({ explore }?: {
|
|
|
42
50
|
caveatReasons?: string[];
|
|
43
51
|
fanoutProviders?: import("../types.js").SearchProviderName[];
|
|
44
52
|
fanoutSkipped?: import("../types.js").SearchProviderName[];
|
|
53
|
+
attempts?: import("../types.js").Attempt[];
|
|
45
54
|
};
|
|
46
55
|
error?: import("../types.js").ToolError;
|
|
47
56
|
}>;
|
|
@@ -18,15 +18,29 @@ export function createWebExploreTool({ explore = createResearchWorkflow() } = {}
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
const result = await runExplore({ query: normalizedQuery });
|
|
21
|
+
if (result.terminalFailure) {
|
|
22
|
+
const failed = {
|
|
23
|
+
status: 'error',
|
|
24
|
+
findings: [],
|
|
25
|
+
sources: [],
|
|
26
|
+
error: result.terminalFailure,
|
|
27
|
+
metadata: result.metadata
|
|
28
|
+
};
|
|
29
|
+
return { ...failed, presentation: buildExplorePresentation(failed) };
|
|
30
|
+
}
|
|
21
31
|
const sources = result.evidence.slice(0, 4).map((item) => ({
|
|
22
32
|
title: item.title,
|
|
23
33
|
url: item.url,
|
|
24
34
|
method: item.method
|
|
25
35
|
}));
|
|
36
|
+
const reasons = (result.metadata?.caveatReasons ?? []);
|
|
37
|
+
const decisionPartial = result.decision.action !== 'answer';
|
|
38
|
+
const coveragePartial = reasons.includes('partial-search-coverage');
|
|
26
39
|
const synthesized = synthesizeAnswer({
|
|
27
40
|
evidence: result.evidence,
|
|
28
|
-
partial:
|
|
29
|
-
|
|
41
|
+
partial: decisionPartial || coveragePartial,
|
|
42
|
+
// A confident answer with partial coverage mentions only the coverage, not unrelated quality notes.
|
|
43
|
+
caveatReasons: decisionPartial ? reasons : ['partial-search-coverage']
|
|
30
44
|
});
|
|
31
45
|
const shaped = {
|
|
32
46
|
status: 'ok',
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import type { WebFetchHeadlessResponse } from '../types.js';
|
|
1
|
+
import type { ResearchFetchInput, WebFetchHeadlessResponse } from '../types.js';
|
|
2
2
|
export declare function createWebFetchHeadlessTool({ fetchPage }?: {
|
|
3
|
-
fetchPage?: (
|
|
4
|
-
}): ({ url }:
|
|
5
|
-
url: string;
|
|
6
|
-
}) => Promise<WebFetchHeadlessResponse>;
|
|
3
|
+
fetchPage?: (input: ResearchFetchInput) => Promise<WebFetchHeadlessResponse>;
|
|
4
|
+
}): ({ url, query }: ResearchFetchInput) => Promise<WebFetchHeadlessResponse>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { headlessFetch } from '../fetch/headless-fetch.js';
|
|
2
2
|
import { buildFetchPresentation } from '../presentation/fetch-presentation.js';
|
|
3
|
-
export function createWebFetchHeadlessTool({ fetchPage = headlessFetch } = {}) {
|
|
4
|
-
return async function webFetchHeadless({ url }) {
|
|
3
|
+
export function createWebFetchHeadlessTool({ fetchPage = ({ url, query }) => headlessFetch(url, { query }) } = {}) {
|
|
4
|
+
return async function webFetchHeadless({ url, query }) {
|
|
5
5
|
if (!/^https?:\/\//.test(url)) {
|
|
6
6
|
const result = {
|
|
7
7
|
status: 'unsupported',
|
|
@@ -14,7 +14,7 @@ export function createWebFetchHeadlessTool({ fetchPage = headlessFetch } = {}) {
|
|
|
14
14
|
presentation: buildFetchPresentation(result)
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
|
-
const result = await fetchPage(url);
|
|
17
|
+
const result = await fetchPage({ url, ...(query ? { query } : {}) });
|
|
18
18
|
return {
|
|
19
19
|
...result,
|
|
20
20
|
presentation: buildFetchPresentation(result)
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import type { WebFetchResponse } from '../types.js';
|
|
1
|
+
import type { ResearchFetchInput, WebFetchResponse } from '../types.js';
|
|
2
2
|
export declare function createWebFetchTool({ fetchPage }?: {
|
|
3
|
-
fetchPage?: (
|
|
4
|
-
}): ({ url }:
|
|
5
|
-
url: string;
|
|
6
|
-
}) => Promise<WebFetchResponse>;
|
|
3
|
+
fetchPage?: (input: ResearchFetchInput) => Promise<WebFetchResponse>;
|
|
4
|
+
}): ({ url, query }: ResearchFetchInput) => Promise<WebFetchResponse>;
|
package/dist/tools/web-fetch.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHttpFetcher } from '../fetch/http-fetch.js';
|
|
2
2
|
import { buildFetchPresentation } from '../presentation/fetch-presentation.js';
|
|
3
|
-
export function createWebFetchTool({ fetchPage = createHttpFetcher() } = {}) {
|
|
4
|
-
return async function webFetch({ url }) {
|
|
3
|
+
export function createWebFetchTool({ fetchPage = ({ url, query }) => createHttpFetcher()(url, query) } = {}) {
|
|
4
|
+
return async function webFetch({ url, query }) {
|
|
5
5
|
if (!/^https?:\/\//.test(url)) {
|
|
6
6
|
const result = {
|
|
7
7
|
status: 'unsupported',
|
|
@@ -14,7 +14,7 @@ export function createWebFetchTool({ fetchPage = createHttpFetcher() } = {}) {
|
|
|
14
14
|
presentation: buildFetchPresentation(result)
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
|
-
const result = await fetchPage(url);
|
|
17
|
+
const result = await fetchPage({ url, ...(query ? { query } : {}) });
|
|
18
18
|
return {
|
|
19
19
|
...result,
|
|
20
20
|
presentation: buildFetchPresentation(result)
|
package/dist/tools/web-search.js
CHANGED
|
@@ -1,25 +1,9 @@
|
|
|
1
1
|
import { createCacheKey, createTtlCache } from '../cache/ttl-cache.js';
|
|
2
2
|
import { buildSearchPresentation } from '../presentation/search-presentation.js';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
if (normalized.includes('blocked') ||
|
|
8
|
-
normalized.includes('rate limit') ||
|
|
9
|
-
normalized.includes('rate-limit') ||
|
|
10
|
-
normalized.includes('403') ||
|
|
11
|
-
normalized.includes('429') ||
|
|
12
|
-
normalized.includes('captcha') ||
|
|
13
|
-
normalized.includes('challenge')) {
|
|
14
|
-
return {
|
|
15
|
-
code: 'BLOCKED',
|
|
16
|
-
message: 'DuckDuckGo search appears to be blocked or rate limited.'
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
return {
|
|
20
|
-
code: 'FETCH_FAILED',
|
|
21
|
-
message: `DuckDuckGo search request failed: ${rawMessage}`
|
|
22
|
-
};
|
|
3
|
+
import { classifyHttpFailure } from '../backends/provider-failure.js';
|
|
4
|
+
import { DuckDuckGoHttpError, fetchDuckDuckGoHtml, parseDuckDuckGoResults } from '../search/duckduckgo.js';
|
|
5
|
+
function respond(result) {
|
|
6
|
+
return { ...result, presentation: buildSearchPresentation(result) };
|
|
23
7
|
}
|
|
24
8
|
function htmlLooksBlocked(html) {
|
|
25
9
|
const normalized = html.toLowerCase();
|
|
@@ -37,107 +21,61 @@ export function createWebSearchTool({ searchHtml = fetchDuckDuckGoHtml, cache =
|
|
|
37
21
|
return async function webSearch({ query }) {
|
|
38
22
|
const normalizedQuery = query.trim();
|
|
39
23
|
if (!normalizedQuery) {
|
|
40
|
-
|
|
24
|
+
return respond({
|
|
41
25
|
status: 'error',
|
|
42
26
|
results: [],
|
|
43
27
|
metadata: { backend: 'duckduckgo', cacheHit: false },
|
|
44
|
-
error: { code: 'INVALID_QUERY', message: 'Query must not be empty.' }
|
|
45
|
-
};
|
|
46
|
-
return {
|
|
47
|
-
...result,
|
|
48
|
-
presentation: buildSearchPresentation(result)
|
|
49
|
-
};
|
|
28
|
+
error: { code: 'INVALID_QUERY', message: 'Query must not be empty.', failure: { kind: 'bad_request' } }
|
|
29
|
+
});
|
|
50
30
|
}
|
|
51
31
|
const cacheKey = createCacheKey(['web_search', normalizedQuery]);
|
|
52
32
|
const cached = cache.get(cacheKey);
|
|
53
33
|
if (cached) {
|
|
54
|
-
|
|
55
|
-
...cached,
|
|
56
|
-
metadata: { ...cached.metadata, cacheHit: true }
|
|
57
|
-
};
|
|
58
|
-
return {
|
|
59
|
-
...result,
|
|
60
|
-
presentation: buildSearchPresentation(result)
|
|
61
|
-
};
|
|
34
|
+
return respond({ ...cached, metadata: { ...cached.metadata, cacheHit: true } });
|
|
62
35
|
}
|
|
36
|
+
let html;
|
|
63
37
|
try {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (parsed.results.length > 0) {
|
|
73
|
-
const result = {
|
|
74
|
-
status: 'ok',
|
|
75
|
-
results: parsed.results,
|
|
76
|
-
metadata: { backend: 'duckduckgo', cacheHit: false }
|
|
77
|
-
};
|
|
78
|
-
cache.set(cacheKey, result);
|
|
79
|
-
return {
|
|
80
|
-
...result,
|
|
81
|
-
presentation: buildSearchPresentation(result)
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
// Check for a bot-wall before "no results": a page can carry both markers, and BLOCKED is
|
|
85
|
-
// the honest call since it routes to the fallback instead of a dead end.
|
|
86
|
-
if (htmlLooksBlocked(html)) {
|
|
87
|
-
const result = {
|
|
88
|
-
status: 'error',
|
|
89
|
-
results: [],
|
|
90
|
-
metadata: { backend: 'duckduckgo', cacheHit: false },
|
|
91
|
-
error: {
|
|
92
|
-
code: 'BLOCKED',
|
|
93
|
-
message: 'DuckDuckGo search appears to be blocked or rate limited.'
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
return {
|
|
97
|
-
...result,
|
|
98
|
-
presentation: buildSearchPresentation(result)
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
if (parsed.noResults) {
|
|
102
|
-
const result = {
|
|
103
|
-
status: 'error',
|
|
104
|
-
results: [],
|
|
105
|
-
metadata: { backend: 'duckduckgo', cacheHit: false },
|
|
106
|
-
error: {
|
|
107
|
-
code: 'NO_RESULTS',
|
|
108
|
-
message: 'DuckDuckGo returned no usable results for this query.'
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
return {
|
|
112
|
-
...result,
|
|
113
|
-
presentation: buildSearchPresentation(result)
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
const result = {
|
|
38
|
+
html = await searchHtml(normalizedQuery);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
const failure = error instanceof DuckDuckGoHttpError
|
|
42
|
+
? classifyHttpFailure('duckduckgo', { status: error.status, headers: error.headers })
|
|
43
|
+
: { kind: 'transient' };
|
|
44
|
+
const blockedLike = failure.kind === 'blocked' || failure.kind === 'rate_limited';
|
|
45
|
+
return respond({
|
|
117
46
|
status: 'error',
|
|
118
47
|
results: [],
|
|
119
48
|
metadata: { backend: 'duckduckgo', cacheHit: false },
|
|
120
49
|
error: {
|
|
121
|
-
code: '
|
|
122
|
-
message:
|
|
50
|
+
code: blockedLike ? 'BLOCKED' : 'FETCH_FAILED',
|
|
51
|
+
message: blockedLike
|
|
52
|
+
? 'DuckDuckGo search appears to be blocked or rate limited.'
|
|
53
|
+
: `DuckDuckGo search request failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
54
|
+
failure
|
|
123
55
|
}
|
|
124
|
-
};
|
|
125
|
-
return {
|
|
126
|
-
...result,
|
|
127
|
-
presentation: buildSearchPresentation(result)
|
|
128
|
-
};
|
|
56
|
+
});
|
|
129
57
|
}
|
|
130
|
-
|
|
131
|
-
|
|
58
|
+
const parsed = parseDuckDuckGoResults(html);
|
|
59
|
+
if (parsed.results.length > 0) {
|
|
60
|
+
const result = { status: 'ok', results: parsed.results, metadata: { backend: 'duckduckgo', cacheHit: false } };
|
|
61
|
+
cache.set(cacheKey, result);
|
|
62
|
+
return respond(result);
|
|
63
|
+
}
|
|
64
|
+
// Bot-wall check first: a page can carry both markers, and blocked routes to fallback.
|
|
65
|
+
const walled = htmlLooksBlocked(html) || (!parsed.hasResultContainers && !parsed.noResults);
|
|
66
|
+
if (walled) {
|
|
67
|
+
return respond({
|
|
132
68
|
status: 'error',
|
|
133
69
|
results: [],
|
|
134
70
|
metadata: { backend: 'duckduckgo', cacheHit: false },
|
|
135
|
-
error:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
};
|
|
71
|
+
error: {
|
|
72
|
+
code: 'BLOCKED',
|
|
73
|
+
message: 'DuckDuckGo search appears to be blocked or rate limited.',
|
|
74
|
+
failure: { kind: 'blocked' }
|
|
75
|
+
}
|
|
76
|
+
});
|
|
141
77
|
}
|
|
78
|
+
// DuckDuckGo said there are no results, or every result was filtered out: a valid empty search.
|
|
79
|
+
return respond({ status: 'ok', results: [], metadata: { backend: 'duckduckgo', cacheHit: false } });
|
|
142
80
|
};
|
|
143
81
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -12,10 +12,44 @@ export type FanoutMetadata = {
|
|
|
12
12
|
mode: Exclude<FanoutMode, 'off'>;
|
|
13
13
|
providers: SearchProviderName[];
|
|
14
14
|
skipped?: SearchProviderName[];
|
|
15
|
+
outcomes?: FanoutOutcome[];
|
|
16
|
+
};
|
|
17
|
+
export type FailureKind = 'rate_limited' | 'quota_exhausted' | 'auth_failed' | 'not_configured' | 'transient' | 'blocked' | 'bad_response' | 'bad_request' | 'config_global' | 'guard_refused';
|
|
18
|
+
export type FailureInfo = {
|
|
19
|
+
kind: FailureKind;
|
|
20
|
+
httpStatus?: number;
|
|
21
|
+
/** Documented provider body code or tag, e.g. Exa `NO_MORE_CREDITS`. */
|
|
22
|
+
providerCode?: string;
|
|
23
|
+
/** Exactly what the provider reported, uncapped. */
|
|
24
|
+
providerRetryAfterMs?: number;
|
|
25
|
+
};
|
|
26
|
+
export type Attempt = {
|
|
27
|
+
backend: string;
|
|
28
|
+
outcome: 'results' | 'empty' | 'failed' | 'skipped' | 'retried';
|
|
29
|
+
failure?: FailureInfo;
|
|
30
|
+
skipReason?: 'cooling_down' | 'disabled';
|
|
31
|
+
cooldownUntil?: number;
|
|
32
|
+
/** The provider's own message for a user-fixable failure, e.g. a missing key or base URL. */
|
|
33
|
+
detail?: string;
|
|
34
|
+
};
|
|
35
|
+
export type SearchCoverage = {
|
|
36
|
+
partial: true;
|
|
37
|
+
unavailable: Array<{
|
|
38
|
+
provider: string;
|
|
39
|
+
kind: FailureKind;
|
|
40
|
+
}>;
|
|
41
|
+
};
|
|
42
|
+
export type FanoutOutcome = {
|
|
43
|
+
provider: SearchProviderName;
|
|
44
|
+
outcome: 'results' | 'empty' | 'failed' | 'skipped';
|
|
45
|
+
count?: number;
|
|
46
|
+
failure?: FailureInfo;
|
|
47
|
+
skipReason?: 'cooling_down' | 'disabled';
|
|
15
48
|
};
|
|
16
49
|
export type ToolError = {
|
|
17
50
|
code: string;
|
|
18
51
|
message: string;
|
|
52
|
+
failure?: FailureInfo;
|
|
19
53
|
};
|
|
20
54
|
export type SearchMetadata = {
|
|
21
55
|
backend: 'duckduckgo' | 'searxng' | 'brave' | 'youcom' | 'exa' | 'tavily';
|
|
@@ -23,8 +57,14 @@ export type SearchMetadata = {
|
|
|
23
57
|
fallbackFrom?: 'searxng' | 'brave' | 'youcom' | 'exa' | 'tavily' | 'duckduckgo';
|
|
24
58
|
fallbackReason?: string;
|
|
25
59
|
fanout?: FanoutMetadata;
|
|
60
|
+
attempts?: Attempt[];
|
|
61
|
+
coverage?: SearchCoverage;
|
|
26
62
|
};
|
|
27
63
|
export type FetchMethod = 'http' | 'headless' | 'firecrawl' | 'github' | 'pdf' | 'youtube';
|
|
64
|
+
export type ResearchFetchInput = {
|
|
65
|
+
url: string;
|
|
66
|
+
query?: string;
|
|
67
|
+
};
|
|
28
68
|
export type FetchMetadata = {
|
|
29
69
|
method: FetchMethod;
|
|
30
70
|
cacheHit: boolean;
|
|
@@ -34,11 +74,18 @@ export type FetchMetadata = {
|
|
|
34
74
|
truncated?: boolean;
|
|
35
75
|
browser?: 'configured' | 'chrome' | 'edge' | 'brave' | 'chromium';
|
|
36
76
|
navigationMs?: number;
|
|
77
|
+
/** Headless only: browser requests refused by the private-address guard (#53). */
|
|
78
|
+
blockedSubresources?: number;
|
|
79
|
+
attempts?: Attempt[];
|
|
37
80
|
};
|
|
38
81
|
export type ExtractedContent = {
|
|
39
82
|
title?: string;
|
|
40
83
|
byline?: string;
|
|
41
84
|
text: string;
|
|
85
|
+
/** A bot or security verification marker appeared in the source before query selection. */
|
|
86
|
+
botCheck?: boolean;
|
|
87
|
+
/** Anchor of the section chosen for research; direct fetches do not set this. */
|
|
88
|
+
sectionAnchor?: string;
|
|
42
89
|
};
|
|
43
90
|
export type WebSearchResponse = {
|
|
44
91
|
status: 'ok' | 'error';
|
|
@@ -80,6 +127,7 @@ export type WebExploreResponse = {
|
|
|
80
127
|
caveatReasons?: string[];
|
|
81
128
|
fanoutProviders?: SearchProviderName[];
|
|
82
129
|
fanoutSkipped?: SearchProviderName[];
|
|
130
|
+
attempts?: Attempt[];
|
|
83
131
|
};
|
|
84
132
|
presentation?: PresentationEnvelope;
|
|
85
133
|
error?: ToolError;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@demigodmode/pi-web-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.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",
|
|
@@ -77,10 +77,10 @@
|
|
|
77
77
|
"@earendil-works/pi-tui": "^0.80.10",
|
|
78
78
|
"@types/jsdom": "^21.1.7",
|
|
79
79
|
"@types/node": "^24.0.0",
|
|
80
|
-
"@vitest/coverage-v8": "^
|
|
80
|
+
"@vitest/coverage-v8": "^4.1.11",
|
|
81
81
|
"typescript": "^5.8.0",
|
|
82
82
|
"vitepress": "^1.6.4",
|
|
83
|
-
"vitest": "^
|
|
83
|
+
"vitest": "^4.1.11"
|
|
84
84
|
},
|
|
85
85
|
"peerDependencies": {
|
|
86
86
|
"@earendil-works/pi-coding-agent": "*",
|