@demigodmode/pi-web-agent 0.3.1 → 0.5.1
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/LICENSE +661 -661
- package/README.md +124 -119
- package/dist/commands/web-agent-config.d.ts +8 -0
- package/dist/commands/web-agent-config.js +90 -10
- package/dist/extension.js +10 -155
- package/dist/fetch/browser-resolution.d.ts +7 -2
- package/dist/fetch/browser-resolution.js +111 -17
- package/dist/orchestration/answer-synthesizer.d.ts +8 -0
- package/dist/orchestration/answer-synthesizer.js +17 -0
- package/dist/orchestration/candidate-selector.d.ts +6 -0
- package/dist/orchestration/candidate-selector.js +24 -0
- package/dist/orchestration/evidence-ranker.d.ts +4 -0
- package/dist/orchestration/evidence-ranker.js +36 -0
- package/dist/orchestration/index.d.ts +6 -21
- package/dist/orchestration/query-planner.d.ts +7 -0
- package/dist/orchestration/query-planner.js +37 -0
- package/dist/orchestration/research-orchestrator.d.ts +7 -22
- package/dist/orchestration/research-orchestrator.js +185 -73
- package/dist/orchestration/research-types.d.ts +6 -0
- package/dist/orchestration/research-worker.js +8 -1
- package/dist/orchestration/stop-decider.d.ts +19 -0
- package/dist/orchestration/stop-decider.js +14 -0
- package/dist/presentation/explore-presentation.js +26 -4
- package/dist/tools/web-explore.d.ts +10 -0
- package/dist/tools/web-explore.js +10 -20
- package/dist/types.d.ts +8 -1
- package/package.json +74 -75
|
@@ -25,6 +25,12 @@ export type ResearchWorkerResult = {
|
|
|
25
25
|
suggestedHeadlessUrl?: string;
|
|
26
26
|
exhaustedBudget: boolean;
|
|
27
27
|
};
|
|
28
|
+
export type ResearchRunMetadata = {
|
|
29
|
+
searchPasses: number;
|
|
30
|
+
fetchedPages: number;
|
|
31
|
+
headlessAttempts: number;
|
|
32
|
+
exhaustedBudget: boolean;
|
|
33
|
+
};
|
|
28
34
|
export type ResearchOrchestratorDecision = {
|
|
29
35
|
action: 'answer';
|
|
30
36
|
rationale: string;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { selectCandidates } from './candidate-selector.js';
|
|
1
2
|
function classifySource(url) {
|
|
2
3
|
if (url.includes('/docs/api/') || url.includes('/config/'))
|
|
3
4
|
return 'official-api';
|
|
4
5
|
if (url.includes('playwright.dev/docs') || url.includes('vitest.dev/guide/'))
|
|
5
6
|
return 'official-docs';
|
|
7
|
+
if (url.includes('github.com/vitest-dev/vitest') && url.includes('/docs/'))
|
|
8
|
+
return 'official-docs';
|
|
6
9
|
if (url.includes('learn.microsoft.com'))
|
|
7
10
|
return 'official-docs';
|
|
8
11
|
if (url.includes('github.com/') && url.includes('/issues/'))
|
|
@@ -91,7 +94,11 @@ export function createResearchWorker({ search, fetchPage }) {
|
|
|
91
94
|
exhaustedBudget: false
|
|
92
95
|
};
|
|
93
96
|
}
|
|
94
|
-
const candidates =
|
|
97
|
+
const candidates = selectCandidates({
|
|
98
|
+
results: searchResult.results,
|
|
99
|
+
seenUrls: new Set(evidence.map((item) => item.url)),
|
|
100
|
+
maxCandidates: maxFetches
|
|
101
|
+
});
|
|
95
102
|
for (const candidate of candidates) {
|
|
96
103
|
const fetched = await fetchPage({ url: candidate.url });
|
|
97
104
|
if (fetched.status === 'ok') {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ResearchEvidence } from './research-types.js';
|
|
2
|
+
export type ResearchStepDecision = {
|
|
3
|
+
action: 'answer';
|
|
4
|
+
} | {
|
|
5
|
+
action: 'answer-with-caveat';
|
|
6
|
+
} | {
|
|
7
|
+
action: 'search-again';
|
|
8
|
+
} | {
|
|
9
|
+
action: 'headless';
|
|
10
|
+
url: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function decideNextResearchStep({ evidence, suggestedHeadlessUrls, passIndex, maxPasses, headlessAttempts, maxHeadlessAttempts }: {
|
|
13
|
+
evidence: ResearchEvidence[];
|
|
14
|
+
suggestedHeadlessUrls: string[];
|
|
15
|
+
passIndex: number;
|
|
16
|
+
maxPasses: number;
|
|
17
|
+
headlessAttempts: number;
|
|
18
|
+
maxHeadlessAttempts: number;
|
|
19
|
+
}): ResearchStepDecision;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { hasOfficialEvidence, strongEvidenceCount } from './evidence-ranker.js';
|
|
2
|
+
export function decideNextResearchStep({ evidence, suggestedHeadlessUrls, passIndex, maxPasses, headlessAttempts, maxHeadlessAttempts }) {
|
|
3
|
+
if (strongEvidenceCount(evidence) >= 2 && hasOfficialEvidence(evidence)) {
|
|
4
|
+
return { action: 'answer' };
|
|
5
|
+
}
|
|
6
|
+
const headlessUrl = suggestedHeadlessUrls.find((url) => !url.includes('npmjs.com/package/'));
|
|
7
|
+
if (headlessUrl && headlessAttempts < maxHeadlessAttempts) {
|
|
8
|
+
return { action: 'headless', url: headlessUrl };
|
|
9
|
+
}
|
|
10
|
+
if (passIndex + 1 < maxPasses) {
|
|
11
|
+
return { action: 'search-again' };
|
|
12
|
+
}
|
|
13
|
+
return { action: 'answer-with-caveat' };
|
|
14
|
+
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
function internalReaderLabel(method) {
|
|
2
|
+
if (method === 'headless')
|
|
3
|
+
return 'web_fetch_headless';
|
|
4
|
+
if (method === 'http')
|
|
5
|
+
return 'web_fetch';
|
|
6
|
+
return 'web_explore';
|
|
7
|
+
}
|
|
1
8
|
export function buildExplorePresentation(result) {
|
|
2
9
|
if (result.status === 'error') {
|
|
3
10
|
return {
|
|
@@ -7,13 +14,26 @@ export function buildExplorePresentation(result) {
|
|
|
7
14
|
}
|
|
8
15
|
};
|
|
9
16
|
}
|
|
10
|
-
const
|
|
17
|
+
const internalSummary = result.metadata
|
|
18
|
+
? `Internal research: web_search ×${result.metadata.searchPasses}, web_fetch ×${result.metadata.fetchedPages}, web_fetch_headless ×${result.metadata.headlessAttempts}`
|
|
19
|
+
: undefined;
|
|
20
|
+
const hasEvidence = result.findings.length > 0 || result.sources.length > 0;
|
|
21
|
+
const evidenceLines = hasEvidence
|
|
22
|
+
? result.findings.map((finding, index) => `- [${internalReaderLabel(result.sources[index]?.method)}] ${finding}`)
|
|
23
|
+
: ['No usable evidence found.'];
|
|
24
|
+
const preview = [
|
|
25
|
+
...evidenceLines,
|
|
26
|
+
internalSummary ? `\n${internalSummary}` : undefined
|
|
27
|
+
]
|
|
28
|
+
.filter((line) => line !== undefined)
|
|
29
|
+
.join('\n');
|
|
11
30
|
const verbose = [
|
|
12
31
|
'Findings',
|
|
13
|
-
...
|
|
32
|
+
...evidenceLines,
|
|
14
33
|
'',
|
|
15
34
|
'Sources',
|
|
16
|
-
...result.sources.map((source) => `- ${source.title}: ${source.url}`),
|
|
35
|
+
...result.sources.map((source) => `- [${internalReaderLabel(source.method)}] ${source.title}: ${source.url}`),
|
|
36
|
+
internalSummary ? `\nInternal tools\n${internalSummary}` : undefined,
|
|
17
37
|
result.caveat ? `\nCaveat\n${result.caveat}` : undefined
|
|
18
38
|
]
|
|
19
39
|
.filter((line) => line !== undefined)
|
|
@@ -21,7 +41,9 @@ export function buildExplorePresentation(result) {
|
|
|
21
41
|
return {
|
|
22
42
|
mode: 'compact',
|
|
23
43
|
views: {
|
|
24
|
-
compact:
|
|
44
|
+
compact: hasEvidence
|
|
45
|
+
? `Reviewed ${result.sources.length} sources · synthesized answer with ${result.findings.length} findings`
|
|
46
|
+
: 'No usable evidence found',
|
|
25
47
|
preview,
|
|
26
48
|
verbose
|
|
27
49
|
},
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ResearchEvidence } from '../orchestration/research-types.js';
|
|
2
|
+
import type { WebExploreResponse } from '../types.js';
|
|
2
3
|
export declare function createWebExploreTool({ explore }?: {
|
|
3
4
|
explore?: {
|
|
4
5
|
run: (input: {
|
|
@@ -9,6 +10,7 @@ export declare function createWebExploreTool({ explore }?: {
|
|
|
9
10
|
};
|
|
10
11
|
evidence: ResearchEvidence[];
|
|
11
12
|
workerPass: unknown;
|
|
13
|
+
metadata?: WebExploreResponse['metadata'];
|
|
12
14
|
}>;
|
|
13
15
|
} | ((input: {
|
|
14
16
|
query: string;
|
|
@@ -18,6 +20,7 @@ export declare function createWebExploreTool({ explore }?: {
|
|
|
18
20
|
};
|
|
19
21
|
evidence: ResearchEvidence[];
|
|
20
22
|
workerPass: unknown;
|
|
23
|
+
metadata?: WebExploreResponse['metadata'];
|
|
21
24
|
}>);
|
|
22
25
|
}): ({ query }: {
|
|
23
26
|
query: string;
|
|
@@ -28,7 +31,14 @@ export declare function createWebExploreTool({ explore }?: {
|
|
|
28
31
|
sources: Array<{
|
|
29
32
|
title: string;
|
|
30
33
|
url: string;
|
|
34
|
+
method?: "http" | "headless";
|
|
31
35
|
}>;
|
|
32
36
|
caveat?: string;
|
|
37
|
+
metadata?: {
|
|
38
|
+
searchPasses: number;
|
|
39
|
+
fetchedPages: number;
|
|
40
|
+
headlessAttempts: number;
|
|
41
|
+
exhaustedBudget: boolean;
|
|
42
|
+
};
|
|
33
43
|
error?: import("../types.js").ToolError;
|
|
34
44
|
}>;
|
|
@@ -1,18 +1,6 @@
|
|
|
1
1
|
import { createResearchWorkflow } from '../orchestration/index.js';
|
|
2
|
+
import { synthesizeAnswer } from '../orchestration/answer-synthesizer.js';
|
|
2
3
|
import { buildExplorePresentation } from '../presentation/explore-presentation.js';
|
|
3
|
-
function findingFromEvidence(evidence, index) {
|
|
4
|
-
if (evidence.summary.includes('Use channel')) {
|
|
5
|
-
return 'Use channel for branded Chrome or Edge when possible.';
|
|
6
|
-
}
|
|
7
|
-
if (evidence.summary.includes('use at your own risk') || evidence.summary.includes('risky')) {
|
|
8
|
-
return 'Treat executablePath as a fallback because Playwright documents it as use-at-your-own-risk.';
|
|
9
|
-
}
|
|
10
|
-
if (evidence.summary.includes('coverage.provider to v8') ||
|
|
11
|
-
evidence.summary.includes('@vitest/coverage-v8')) {
|
|
12
|
-
return 'Vitest coverage docs say to set coverage.provider to v8 and install @vitest/coverage-v8.';
|
|
13
|
-
}
|
|
14
|
-
return evidence.summary || `Finding ${index + 1}`;
|
|
15
|
-
}
|
|
16
4
|
export function createWebExploreTool({ explore = createResearchWorkflow() } = {}) {
|
|
17
5
|
const runExplore = typeof explore === 'function' ? explore : explore.run.bind(explore);
|
|
18
6
|
return async function webExplore({ query }) {
|
|
@@ -30,19 +18,21 @@ export function createWebExploreTool({ explore = createResearchWorkflow() } = {}
|
|
|
30
18
|
};
|
|
31
19
|
}
|
|
32
20
|
const result = await runExplore({ query: normalizedQuery });
|
|
33
|
-
const findings = result.evidence.slice(0, 5).map(findingFromEvidence);
|
|
34
21
|
const sources = result.evidence.slice(0, 4).map((item) => ({
|
|
35
22
|
title: item.title,
|
|
36
|
-
url: item.url
|
|
23
|
+
url: item.url,
|
|
24
|
+
method: item.method
|
|
37
25
|
}));
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
:
|
|
26
|
+
const synthesized = synthesizeAnswer({
|
|
27
|
+
evidence: result.evidence,
|
|
28
|
+
partial: result.decision.action !== 'answer'
|
|
29
|
+
});
|
|
41
30
|
const shaped = {
|
|
42
31
|
status: 'ok',
|
|
43
|
-
findings,
|
|
32
|
+
findings: synthesized.findings,
|
|
44
33
|
sources,
|
|
45
|
-
caveat
|
|
34
|
+
caveat: synthesized.caveat,
|
|
35
|
+
metadata: result.metadata
|
|
46
36
|
};
|
|
47
37
|
return {
|
|
48
38
|
...shaped,
|
package/dist/types.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export type FetchMetadata = {
|
|
|
19
19
|
cacheHit: boolean;
|
|
20
20
|
contentType?: string;
|
|
21
21
|
truncated?: boolean;
|
|
22
|
-
browser?: 'configured' | 'chrome' | 'edge';
|
|
22
|
+
browser?: 'configured' | 'chrome' | 'edge' | 'brave' | 'chromium';
|
|
23
23
|
navigationMs?: number;
|
|
24
24
|
};
|
|
25
25
|
export type ExtractedContent = {
|
|
@@ -56,8 +56,15 @@ export type WebExploreResponse = {
|
|
|
56
56
|
sources: Array<{
|
|
57
57
|
title: string;
|
|
58
58
|
url: string;
|
|
59
|
+
method?: 'http' | 'headless';
|
|
59
60
|
}>;
|
|
60
61
|
caveat?: string;
|
|
62
|
+
metadata?: {
|
|
63
|
+
searchPasses: number;
|
|
64
|
+
fetchedPages: number;
|
|
65
|
+
headlessAttempts: number;
|
|
66
|
+
exhaustedBudget: boolean;
|
|
67
|
+
};
|
|
61
68
|
presentation?: PresentationEnvelope;
|
|
62
69
|
error?: ToolError;
|
|
63
70
|
};
|
package/package.json
CHANGED
|
@@ -1,75 +1,74 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@demigodmode/pi-web-agent",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Pi package for reliable web access with explicit search, fetch, and headless boundaries.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./dist/extension.js",
|
|
7
|
-
"types": "./dist/extension.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./dist/extension.d.ts",
|
|
11
|
-
"import": "./dist/extension.js"
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
"files": [
|
|
15
|
-
"dist",
|
|
16
|
-
"README.md"
|
|
17
|
-
],
|
|
18
|
-
"keywords": [
|
|
19
|
-
"pi-package",
|
|
20
|
-
"pi",
|
|
21
|
-
"extension",
|
|
22
|
-
"web-search",
|
|
23
|
-
"web-fetch"
|
|
24
|
-
],
|
|
25
|
-
"repository": {
|
|
26
|
-
"type": "git",
|
|
27
|
-
"url": "git+https://github.com/demigodmode/pi-web-agent.git"
|
|
28
|
-
},
|
|
29
|
-
"homepage": "https://github.com/demigodmode/pi-web-agent#readme",
|
|
30
|
-
"bugs": {
|
|
31
|
-
"url": "https://github.com/demigodmode/pi-web-agent/issues"
|
|
32
|
-
},
|
|
33
|
-
"license": "AGPL-3.0-only",
|
|
34
|
-
"publishConfig": {
|
|
35
|
-
"access": "public"
|
|
36
|
-
},
|
|
37
|
-
"scripts": {
|
|
38
|
-
"build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.build.json",
|
|
39
|
-
"build:dev": "tsc -p tsconfig.json",
|
|
40
|
-
"test": "vitest run --coverage",
|
|
41
|
-
"test:watch": "vitest",
|
|
42
|
-
"lint": "tsc -p tsconfig.json --noEmit",
|
|
43
|
-
"docs:dev": "vitepress dev docs",
|
|
44
|
-
"docs:build": "vitepress build docs",
|
|
45
|
-
"docs:preview": "vitepress preview docs",
|
|
46
|
-
"eval:live": "npm run build:dev && node dist/scripts/live-web-eval.js",
|
|
47
|
-
"release:dry-run": "node scripts/release.mjs --dry-run",
|
|
48
|
-
"release": "node scripts/release.mjs"
|
|
49
|
-
},
|
|
50
|
-
"pi": {
|
|
51
|
-
"extensions": [
|
|
52
|
-
"./dist/extension.js"
|
|
53
|
-
]
|
|
54
|
-
},
|
|
55
|
-
"dependencies": {
|
|
56
|
-
"@mozilla/readability": "^0.6.0",
|
|
57
|
-
"cheerio": "^1.1.0",
|
|
58
|
-
"jsdom": "^26.0.0",
|
|
59
|
-
"playwright-core": "^1.54.0"
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"@
|
|
64
|
-
"@types/jsdom": "^21.1.7",
|
|
65
|
-
"@types/node": "^24.0.0",
|
|
66
|
-
"@vitest/coverage-v8": "^3.2.4",
|
|
67
|
-
"typescript": "^5.8.0",
|
|
68
|
-
"vitepress": "^1.6.4",
|
|
69
|
-
"vitest": "^3.2.0"
|
|
70
|
-
},
|
|
71
|
-
"peerDependencies": {
|
|
72
|
-
"@mariozechner/pi-coding-agent": "*"
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@demigodmode/pi-web-agent",
|
|
3
|
+
"version": "0.5.1",
|
|
4
|
+
"description": "Pi package for reliable web access with explicit search, fetch, and headless boundaries.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/extension.js",
|
|
7
|
+
"types": "./dist/extension.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/extension.d.ts",
|
|
11
|
+
"import": "./dist/extension.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"keywords": [
|
|
19
|
+
"pi-package",
|
|
20
|
+
"pi",
|
|
21
|
+
"extension",
|
|
22
|
+
"web-search",
|
|
23
|
+
"web-fetch"
|
|
24
|
+
],
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/demigodmode/pi-web-agent.git"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/demigodmode/pi-web-agent#readme",
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/demigodmode/pi-web-agent/issues"
|
|
32
|
+
},
|
|
33
|
+
"license": "AGPL-3.0-only",
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.build.json",
|
|
39
|
+
"build:dev": "tsc -p tsconfig.json",
|
|
40
|
+
"test": "vitest run --coverage",
|
|
41
|
+
"test:watch": "vitest",
|
|
42
|
+
"lint": "tsc -p tsconfig.json --noEmit",
|
|
43
|
+
"docs:dev": "vitepress dev docs",
|
|
44
|
+
"docs:build": "vitepress build docs",
|
|
45
|
+
"docs:preview": "vitepress preview docs",
|
|
46
|
+
"eval:live": "npm run build:dev && node dist/scripts/live-web-eval.js",
|
|
47
|
+
"release:dry-run": "node scripts/release.mjs --dry-run",
|
|
48
|
+
"release": "node scripts/release.mjs"
|
|
49
|
+
},
|
|
50
|
+
"pi": {
|
|
51
|
+
"extensions": [
|
|
52
|
+
"./dist/extension.js"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@mozilla/readability": "^0.6.0",
|
|
57
|
+
"cheerio": "^1.1.0",
|
|
58
|
+
"jsdom": "^26.0.0",
|
|
59
|
+
"playwright-core": "^1.54.0",
|
|
60
|
+
"typebox": "^1.1.37"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@mariozechner/pi-coding-agent": "^0.69.0",
|
|
64
|
+
"@types/jsdom": "^21.1.7",
|
|
65
|
+
"@types/node": "^24.0.0",
|
|
66
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
67
|
+
"typescript": "^5.8.0",
|
|
68
|
+
"vitepress": "^1.6.4",
|
|
69
|
+
"vitest": "^3.2.0"
|
|
70
|
+
},
|
|
71
|
+
"peerDependencies": {
|
|
72
|
+
"@mariozechner/pi-coding-agent": "*"
|
|
73
|
+
}
|
|
74
|
+
}
|