pi-agent-browser-native 0.8.3 → 0.9.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/CHANGELOG.md +36 -0
- package/README.md +12 -11
- package/dist/extensions/agent-browser/index.js +96 -320
- package/dist/extensions/agent-browser/lib/argv-descriptor.js +1 -1
- package/dist/extensions/agent-browser/lib/argv-grammar.js +3 -3
- package/dist/extensions/agent-browser/lib/browser-journal.js +33 -12
- package/dist/extensions/agent-browser/lib/browser-transcript.js +1 -1
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +0 -3
- package/dist/extensions/agent-browser/lib/config-policy.js +0 -13
- package/dist/extensions/agent-browser/lib/config.js +3 -48
- package/dist/extensions/agent-browser/lib/electron/launch.js +4 -4
- package/dist/extensions/agent-browser/lib/execution-cwd.js +0 -16
- package/dist/extensions/agent-browser/lib/input-modes/params.js +2 -3
- package/dist/extensions/agent-browser/lib/input-modes/script.js +28 -6
- package/dist/extensions/agent-browser/lib/input-modes/types.js +2 -2
- package/dist/extensions/agent-browser/lib/managed-session-restore.js +1 -4
- package/dist/extensions/agent-browser/lib/native-output.js +46 -0
- package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +0 -9
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-merge.js +22 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +2 -2
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/execution-queue.js +86 -0
- package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +31 -17
- package/dist/extensions/agent-browser/lib/process.js +0 -3
- package/dist/extensions/agent-browser/lib/prompt-policy.js +16 -12
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +2 -2
- package/dist/extensions/agent-browser/lib/results/network-routes.js +1 -1
- package/dist/extensions/agent-browser/lib/results/network.js +1 -1
- package/dist/extensions/agent-browser/lib/results/presentation/batch.js +1 -1
- package/dist/extensions/agent-browser/lib/results/presentation/content.js +4 -0
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +1 -1
- package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +15 -8
- package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +2 -2
- package/dist/extensions/agent-browser/lib/results/selector-recovery.js +1 -1
- package/dist/extensions/agent-browser/lib/runtime.js +4 -2
- package/dist/extensions/agent-browser/lib/session-page-state.js +1 -1
- package/dist/extensions/agent-browser/lib/tool-surface.js +39 -11
- package/dist/extensions/agent-browser/lib/web-search.js +100 -79
- package/docs/ARCHITECTURE.md +17 -19
- package/docs/COMMAND_REFERENCE.md +6 -2
- package/docs/RELEASE.md +26 -20
- package/docs/REQUIREMENTS.md +5 -5
- package/docs/SESSION_STORAGE.md +4 -4
- package/docs/SUPPORT_MATRIX.md +15 -13
- package/docs/TOOL_CONTRACT.md +12 -8
- package/package.json +6 -6
- package/scripts/doctor.mjs +12 -4
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import { JsonSchema } from "./json-schema.js";
|
|
2
|
+
import { isRecord } from "./parsing.js";
|
|
3
|
+
import { AGENT_BROWSER_NAMESPACE, AGENT_BROWSER_OUTPUT_SCHEMA, finalizeAgentBrowserNativeResult } from "./native-output.js";
|
|
4
|
+
import { redactSensitiveText } from "./runtime.js";
|
|
2
5
|
import { StringEnum as localStringEnum } from "./string-enum-schema.js";
|
|
3
6
|
import { DEFAULT_WEB_SEARCH_PROVIDER, EXA_SEARCH_TYPES, WEB_SEARCH_PROVIDERS, resolvePreferredWebSearchCredential, } from "./config.js";
|
|
4
7
|
export const AGENT_BROWSER_WEB_SEARCH_TOOL_NAME = "agent_browser_web_search";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
const BRAVE_SEARCH_ENDPOINT = "https://api.search.brave.com/res/v1/web/search";
|
|
9
|
+
const EXA_SEARCH_ENDPOINT = "https://api.exa.ai/search";
|
|
10
|
+
const DEFAULT_SEARCH_RESULT_COUNT = 5;
|
|
11
|
+
const MAX_SEARCH_RESULT_COUNT = 10;
|
|
12
|
+
const SEARCH_REQUEST_TIMEOUT_MS = 15_000;
|
|
13
|
+
const EXA_DEEP_LITE_SEARCH_REQUEST_TIMEOUT_MS = 45_000;
|
|
14
|
+
const EXA_DEEP_SEARCH_REQUEST_TIMEOUT_MS = 60_000;
|
|
15
|
+
const EXA_DEEP_REASONING_SEARCH_REQUEST_TIMEOUT_MS = 90_000;
|
|
16
|
+
const EXA_DYNAMIC_HIGHLIGHTS_BETA = "dynamic-highlights-2026-08-28";
|
|
14
17
|
export const WEB_SEARCH_MIN_REQUEST_INTERVAL_MS = 1_100;
|
|
15
18
|
export const EXA_SEARCH_SYSTEM_PROMPT = "Prefer primary, official sources. Respect any requested version or date. Avoid duplicate or equivalent results.";
|
|
16
19
|
export { EXA_SEARCH_TYPES };
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
const WEB_SEARCH_PROVIDER_PARAM_VALUES = ["auto", ...WEB_SEARCH_PROVIDERS];
|
|
21
|
+
const EXA_SEARCH_CATEGORIES = ["company", "people", "publication", "news", "personal site", "financial report"];
|
|
19
22
|
const MAX_EXA_DOMAIN_FILTERS = 20;
|
|
20
23
|
const MAX_EXA_ADDITIONAL_QUERIES = 10;
|
|
21
24
|
export function createAgentBrowserWebSearchParamsSchema(Type = JsonSchema, StringEnum = localStringEnum) {
|
|
@@ -190,7 +193,7 @@ export function cleanSearchText(value, maxLength = 500) {
|
|
|
190
193
|
return cleaned;
|
|
191
194
|
return `${cleaned.slice(0, Math.max(0, maxLength - 1)).trimEnd()}…`;
|
|
192
195
|
}
|
|
193
|
-
|
|
196
|
+
function normalizeSearchUrl(value) {
|
|
194
197
|
if (typeof value !== "string")
|
|
195
198
|
return undefined;
|
|
196
199
|
try {
|
|
@@ -255,7 +258,7 @@ export function normalizeExaSearchResult(result) {
|
|
|
255
258
|
function getProviderLabel(provider) {
|
|
256
259
|
return provider === "exa" ? "Exa" : "Brave";
|
|
257
260
|
}
|
|
258
|
-
|
|
261
|
+
function formatSearchResults(provider, query, results) {
|
|
259
262
|
const providerLabel = getProviderLabel(provider);
|
|
260
263
|
if (results.length === 0) {
|
|
261
264
|
return `No ${providerLabel} web results found for: ${query}`;
|
|
@@ -311,10 +314,10 @@ function getStartPublishedDate(freshness, now) {
|
|
|
311
314
|
export function buildExaSearchRequestBody(params, now = () => new Date()) {
|
|
312
315
|
const searchType = params.searchType ?? "auto";
|
|
313
316
|
if (params.additionalQueries?.length && !searchType.startsWith("deep")) {
|
|
314
|
-
throw new
|
|
317
|
+
throw new WebSearchLocalError(`additionalQueries requires deep-lite, deep, or deep-reasoning; received ${searchType}.`);
|
|
315
318
|
}
|
|
316
319
|
if ((params.category === "company" || params.category === "people") && (params.freshness || params.excludeDomains?.length)) {
|
|
317
|
-
throw new
|
|
320
|
+
throw new WebSearchLocalError(`category ${params.category} cannot be combined with freshness or excludeDomains.`);
|
|
318
321
|
}
|
|
319
322
|
const body = {
|
|
320
323
|
query: params.query,
|
|
@@ -535,14 +538,14 @@ const EXA_WEB_SEARCH_ADAPTER = {
|
|
|
535
538
|
};
|
|
536
539
|
},
|
|
537
540
|
};
|
|
538
|
-
|
|
541
|
+
const WEB_SEARCH_PROVIDER_ADAPTERS = {
|
|
539
542
|
exa: EXA_WEB_SEARCH_ADAPTER,
|
|
540
543
|
brave: BRAVE_WEB_SEARCH_ADAPTER,
|
|
541
544
|
};
|
|
542
545
|
export function getWebSearchProviderAdapter(provider) {
|
|
543
546
|
return WEB_SEARCH_PROVIDER_ADAPTERS[provider];
|
|
544
547
|
}
|
|
545
|
-
|
|
548
|
+
function dedupeSearchResults(results) {
|
|
546
549
|
const seen = new Set();
|
|
547
550
|
return results.filter((result) => {
|
|
548
551
|
if (seen.has(result.url))
|
|
@@ -558,79 +561,97 @@ function buildMissingCredentialError(provider) {
|
|
|
558
561
|
return "agent_browser_web_search provider exa was requested but no EXA_API_KEY/config credential resolved.";
|
|
559
562
|
return "No Exa or Brave web search credential resolved. Configure webSearch.exaApiKey or webSearch.braveApiKey, or load EXA_API_KEY/BRAVE_API_KEY in the runtime environment.";
|
|
560
563
|
}
|
|
564
|
+
/** Local validation/config failures, not provider outages; reported as `validation-error`. */
|
|
565
|
+
class WebSearchLocalError extends Error {
|
|
566
|
+
}
|
|
561
567
|
export function createAgentBrowserWebSearchTool(configState, options = {}) {
|
|
562
568
|
const requestGate = new WebSearchRequestGate();
|
|
563
569
|
return {
|
|
564
570
|
name: AGENT_BROWSER_WEB_SEARCH_TOOL_NAME,
|
|
571
|
+
namespace: AGENT_BROWSER_NAMESPACE,
|
|
572
|
+
outputSchema: AGENT_BROWSER_OUTPUT_SCHEMA,
|
|
565
573
|
label: "Agent Browser Web Search",
|
|
566
574
|
description: `Search the live web with Exa or Brave for current or external information. For Exa research tasks, use searchType deep-lite or deeper. Returns up to ${MAX_SEARCH_RESULT_COUNT} concise web results.`,
|
|
567
575
|
promptSnippet: "Search the live web with Exa or Brave for current or external information.",
|
|
568
576
|
parameters: AgentBrowserWebSearchParams,
|
|
569
577
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
if (!runtimeConfigState.webSearchEnabled) {
|
|
575
|
-
throw new Error("agent_browser_web_search is disabled by pi-agent-browser-native config.");
|
|
576
|
-
}
|
|
577
|
-
const requestedProvider = params.provider ?? "auto";
|
|
578
|
-
const resolved = await resolvePreferredWebSearchCredential(runtimeConfigState, { provider: requestedProvider, signal });
|
|
579
|
-
if (!resolved)
|
|
580
|
-
throw new Error(buildMissingCredentialError(requestedProvider));
|
|
581
|
-
if (resolved.provider === "brave") {
|
|
582
|
-
const exaOnlyFields = [
|
|
583
|
-
params.includeDomains ? "includeDomains" : undefined,
|
|
584
|
-
params.excludeDomains ? "excludeDomains" : undefined,
|
|
585
|
-
params.category ? "category" : undefined,
|
|
586
|
-
params.additionalQueries ? "additionalQueries" : undefined,
|
|
587
|
-
params.highlightsDynamic ? "highlightsDynamic" : undefined,
|
|
588
|
-
].filter((field) => Boolean(field));
|
|
589
|
-
if (exaOnlyFields.length > 0) {
|
|
590
|
-
throw new Error(`${exaOnlyFields.join(", ")} ${exaOnlyFields.length === 1 ? "requires" : "require"} provider exa; resolved provider was brave.`);
|
|
578
|
+
try {
|
|
579
|
+
const runtimeConfigState = ctx ? options.loadConfigState?.(ctx) ?? configState : configState;
|
|
580
|
+
if (runtimeConfigState.errors.length > 0) {
|
|
581
|
+
throw new WebSearchLocalError(`agent_browser_web_search config is invalid: ${runtimeConfigState.errors.join("; ")}`);
|
|
591
582
|
}
|
|
583
|
+
if (!runtimeConfigState.webSearchEnabled) {
|
|
584
|
+
throw new WebSearchLocalError("agent_browser_web_search is disabled by pi-agent-browser-native config.");
|
|
585
|
+
}
|
|
586
|
+
const requestedProvider = params.provider ?? "auto";
|
|
587
|
+
const resolved = await resolvePreferredWebSearchCredential(runtimeConfigState, { provider: requestedProvider, signal });
|
|
588
|
+
if (!resolved)
|
|
589
|
+
throw new WebSearchLocalError(buildMissingCredentialError(requestedProvider));
|
|
590
|
+
if (resolved.provider === "brave") {
|
|
591
|
+
const exaOnlyFields = [
|
|
592
|
+
params.includeDomains ? "includeDomains" : undefined,
|
|
593
|
+
params.excludeDomains ? "excludeDomains" : undefined,
|
|
594
|
+
params.category ? "category" : undefined,
|
|
595
|
+
params.additionalQueries ? "additionalQueries" : undefined,
|
|
596
|
+
params.highlightsDynamic ? "highlightsDynamic" : undefined,
|
|
597
|
+
].filter((field) => Boolean(field));
|
|
598
|
+
if (exaOnlyFields.length > 0) {
|
|
599
|
+
throw new WebSearchLocalError(`${exaOnlyFields.join(", ")} ${exaOnlyFields.length === 1 ? "requires" : "require"} provider exa; resolved provider was brave.`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
const query = params.query.trim();
|
|
603
|
+
if (!query)
|
|
604
|
+
throw new WebSearchLocalError("query must not be blank");
|
|
605
|
+
const count = Math.min(Math.max(params.count ?? DEFAULT_SEARCH_RESULT_COUNT, 1), MAX_SEARCH_RESULT_COUNT);
|
|
606
|
+
const offset = Math.max(params.offset ?? 0, 0);
|
|
607
|
+
const adapter = getWebSearchProviderAdapter(resolved.provider);
|
|
608
|
+
const executionParams = {
|
|
609
|
+
additionalQueries: params.additionalQueries,
|
|
610
|
+
category: params.category,
|
|
611
|
+
country: params.country,
|
|
612
|
+
count,
|
|
613
|
+
excludeDomains: params.excludeDomains,
|
|
614
|
+
freshness: params.freshness,
|
|
615
|
+
highlightsDynamic: params.highlightsDynamic,
|
|
616
|
+
includeDomains: params.includeDomains,
|
|
617
|
+
offset,
|
|
618
|
+
query,
|
|
619
|
+
safesearch: params.safesearch,
|
|
620
|
+
searchLang: params.searchLang,
|
|
621
|
+
searchType: params.searchType ?? runtimeConfigState.config.webSearch?.defaultSearchType ?? "auto",
|
|
622
|
+
};
|
|
623
|
+
const request = adapter.buildRequest(executionParams);
|
|
624
|
+
const data = await requestGate.run(signal, () => adapter.fetchJson(request, resolved.credential.value, signal));
|
|
625
|
+
const normalized = adapter.normalizeResponse(data, executionParams);
|
|
626
|
+
const results = dedupeSearchResults(normalized.results);
|
|
627
|
+
const duplicatesRemoved = normalized.results.length - results.length;
|
|
628
|
+
const details = {
|
|
629
|
+
provider: adapter.provider,
|
|
630
|
+
query,
|
|
631
|
+
returnedQuery: normalized.returnedQuery,
|
|
632
|
+
count,
|
|
633
|
+
offset,
|
|
634
|
+
...normalized.extraDetails,
|
|
635
|
+
fetchedAt: new Date().toISOString(),
|
|
636
|
+
results,
|
|
637
|
+
duplicatesRemoved: duplicatesRemoved || undefined,
|
|
638
|
+
};
|
|
639
|
+
const result = await finalizeAgentBrowserNativeResult({
|
|
640
|
+
content: [{ type: "text", text: `${formatSearchResults(adapter.provider, normalized.returnedQuery, results)}${duplicatesRemoved ? `\n\nDuplicate URLs removed: ${duplicatesRemoved}.` : ""}` }],
|
|
641
|
+
details: { data: details },
|
|
642
|
+
}, params);
|
|
643
|
+
// Keep the finalized spill's invocation artifact receipt while flattening the documented search details shape.
|
|
644
|
+
const finalizedManifest = isRecord(result.details) ? result.details.artifactManifest : undefined;
|
|
645
|
+
return finalizedManifest === undefined ? { ...result, details } : { ...result, details: { ...details, artifactManifest: finalizedManifest } };
|
|
646
|
+
}
|
|
647
|
+
catch (error) {
|
|
648
|
+
const message = redactSensitiveText(error instanceof Error ? error.message : String(error)).slice(0, 1_000);
|
|
649
|
+
return finalizeAgentBrowserNativeResult({
|
|
650
|
+
content: [{ type: "text", text: message }],
|
|
651
|
+
details: { error: message, resultCategory: "failure", failureCategory: signal?.aborted ? "aborted" : error instanceof WebSearchLocalError ? "validation-error" : "upstream-error" },
|
|
652
|
+
isError: true,
|
|
653
|
+
}, params);
|
|
592
654
|
}
|
|
593
|
-
const query = params.query.trim();
|
|
594
|
-
if (!query)
|
|
595
|
-
throw new Error("query must not be blank");
|
|
596
|
-
const count = Math.min(Math.max(params.count ?? DEFAULT_SEARCH_RESULT_COUNT, 1), MAX_SEARCH_RESULT_COUNT);
|
|
597
|
-
const offset = Math.max(params.offset ?? 0, 0);
|
|
598
|
-
const adapter = getWebSearchProviderAdapter(resolved.provider);
|
|
599
|
-
const executionParams = {
|
|
600
|
-
additionalQueries: params.additionalQueries,
|
|
601
|
-
category: params.category,
|
|
602
|
-
country: params.country,
|
|
603
|
-
count,
|
|
604
|
-
excludeDomains: params.excludeDomains,
|
|
605
|
-
freshness: params.freshness,
|
|
606
|
-
highlightsDynamic: params.highlightsDynamic,
|
|
607
|
-
includeDomains: params.includeDomains,
|
|
608
|
-
offset,
|
|
609
|
-
query,
|
|
610
|
-
safesearch: params.safesearch,
|
|
611
|
-
searchLang: params.searchLang,
|
|
612
|
-
searchType: params.searchType ?? runtimeConfigState.config.webSearch?.defaultSearchType ?? "auto",
|
|
613
|
-
};
|
|
614
|
-
const request = adapter.buildRequest(executionParams);
|
|
615
|
-
const data = await requestGate.run(signal, () => adapter.fetchJson(request, resolved.credential.value, signal));
|
|
616
|
-
const normalized = adapter.normalizeResponse(data, executionParams);
|
|
617
|
-
const results = dedupeSearchResults(normalized.results);
|
|
618
|
-
const duplicatesRemoved = normalized.results.length - results.length;
|
|
619
|
-
const details = {
|
|
620
|
-
provider: adapter.provider,
|
|
621
|
-
query,
|
|
622
|
-
returnedQuery: normalized.returnedQuery,
|
|
623
|
-
count,
|
|
624
|
-
offset,
|
|
625
|
-
...normalized.extraDetails,
|
|
626
|
-
fetchedAt: new Date().toISOString(),
|
|
627
|
-
results,
|
|
628
|
-
duplicatesRemoved: duplicatesRemoved || undefined,
|
|
629
|
-
};
|
|
630
|
-
return {
|
|
631
|
-
content: [{ type: "text", text: `${formatSearchResults(adapter.provider, normalized.returnedQuery, results)}${duplicatesRemoved ? `\n\nDuplicate URLs removed: ${duplicatesRemoved}.` : ""}` }],
|
|
632
|
-
details,
|
|
633
|
-
};
|
|
634
655
|
},
|
|
635
656
|
};
|
|
636
657
|
}
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -19,7 +19,7 @@ The package install path is the primary product path. Local checkout development
|
|
|
19
19
|
|
|
20
20
|
`agent_browser` accepts native argv/stdin, result output path, timeout, and session mode. `agent_browser_code` accepts strict JSON source plus optional parent-selected session/namespace, timeout, and output path. Both use one ordinary CLI executor. Use direct calls for one action, native batch for fixed sequences, and code for branches, loops, or aggregation; return to the model at judgment boundaries.
|
|
21
21
|
|
|
22
|
-
`agent_browser_tools` inventories/enables five flat specialized tools: action, QA, Electron, UI source, and network source. `lib/tool-surface.ts` registers them once and adds them through Pi's native active-tool API, preserving unrelated tools, explicit host selections, and native selected-tool history. It owns no parallel activation journal. QA verdicts, unique accessible select, host Electron lifecycle, and bounded source scans remain specialized capabilities rather than being reduced to raw-command approximations. The former public job DSL and isolated script route are removed.
|
|
22
|
+
`agent_browser_tools` inventories/enables five flat specialized tools: action, QA, Electron, UI source, and network source. `lib/tool-surface.ts` registers them once and adds them through Pi's native active-tool API, preserving unrelated tools, explicit host selections, and native selected-tool history. The five advanced direct tools use native `defaultActive: false`, not deferred registration. A narrow additive fallback restores transcript-enabled advanced tools on official Pi 1.0 initial resume, whose SDK bypasses native restoration by supplying initial tool names. It does not deactivate explicit/defaultTools selections or widen filtered catalogs/CLI allowlists; loader, tree and reload retain native selection semantics. It owns no parallel activation journal. QA verdicts, unique accessible select, host Electron lifecycle, and bounded source scans remain specialized capabilities rather than being reduced to raw-command approximations. The former public job DSL and isolated script route are removed.
|
|
23
23
|
|
|
24
24
|
Optional `agent_browser_web_search` remains separately credential/config gated and independent of browser state.
|
|
25
25
|
|
|
@@ -46,22 +46,24 @@ The extension should:
|
|
|
46
46
|
|
|
47
47
|
The child has an empty environment, Node permission mode without host capability allowlists, a 64 MiB heap ceiling, and a VM with string/WebAssembly code generation disabled. Null-prototype `browser`, `emit`, and `emitImage` functions use JSON-only IPC; parent promises/objects/functions are not exposed. There are no imports or host filesystem/network/process/timer APIs. Node's VM alone is not a security boundary. The retained child isolation is distinct from the selected browser's persistent authentication and native capabilities; no QuickJS, broker, second driver, or provider payload injection is involved.
|
|
48
48
|
|
|
49
|
-
Limits are 25 attempted calls, 64 KiB source/final JSON, 1 MiB per IPC message, 8 MiB cumulative IPC, and a 120-second default/300-second maximum deadline. `browser()` returns a canonical observation. `emit` selects JSON; `emitImage` selects a cell-local verified image handle, with file identity rechecks and normal inline limits. `lib/orchestration/script-mode.ts` aggregates selected output, failures, and artifact receipts; retired isolated-session leases are normalized by the offline converter into UUID-owned cleanup facts.
|
|
49
|
+
Limits are 25 attempted calls, 64 KiB source/final JSON, 1 MiB per IPC message, 8 MiB cumulative IPC, and a 120-second default/300-second maximum deadline. `lib/input-modes/types.ts` owns the maximum shared by the public code schema and runner. `browser()` returns a canonical observation. `emit` selects JSON; `emitImage` selects a cell-local verified image handle, with file identity rechecks and normal inline limits. `lib/orchestration/script-mode.ts` aggregates selected output, failures, and artifact receipts; retired isolated-session leases are normalized by the offline converter into UUID-owned cleanup facts.
|
|
50
50
|
|
|
51
51
|
### Ordered transitions and shared execution
|
|
52
52
|
|
|
53
|
-
`lib/managed-session-policy-lock.ts` supplies cooperating-process execution coordination keyed by the **actual socket context**, canonical namespace, and session, separate from existing managed-daemon policy checks. The lock spans direct helper/action/state work and an entire code cell. Fresh replacement and multi-identity cleanup acquire sorted identity sets; namespace-wide direct close uses an exclusive namespace barrier.
|
|
53
|
+
`lib/managed-session-policy-lock.ts` supplies cooperating-process execution coordination keyed by the **actual socket context**, canonical namespace, and session, separate from existing managed-daemon policy checks. The lock spans direct helper/action/state work and an entire code cell. Fresh replacement and multi-identity cleanup acquire sorted identity sets; namespace-wide direct close uses an exclusive namespace barrier. `lib/orchestration/execution-queue.ts` owns the existing local managed/artifact queues and keyed namespace barriers; branch-generation checks still protect in-process ownership/replay. Independent identities remain concurrent. Direct calls bound coordination waits using the existing configured/command-aware timeout, then retain their native subprocess and Electron action watchdogs. Code owns a whole-cell deadline, including local queue waits. Independently bounded cleanup can borrow an already-held covered claim after cancellation, but cannot acquire a new identity or release the outer claim.
|
|
54
54
|
|
|
55
55
|
All participating Pi processes must restart with the updated package. The lock is not a browser transaction, rollback, or a guarantee against humans, third-party native clients, older extensions, or separately named sessions attached to one external Chrome. Separate tool calls still need workflow coordination.
|
|
56
56
|
|
|
57
|
-
`lib/browser-transcript.ts` defines one canonical `data.event` format and optional once-stored `data.snapshot`. Direct, advanced, code and Electron host operations record begin/finish receipts before dispatch and after observed effects, using outer tool-call ID plus inner index and a unique operation ID. The shared page reducer retains an inaccessible candidate during unresolved work and requires explicit reuse/replace/invalidate/unknown disposition. Ordinary results omit unchanged maps and unrelated artifact receipts; journal events carry redacted artifact-row changes in commit order. Final rendering queues actual spill writes and manifest publication, allowing unrelated small observations to finish while another namespace holds its artifact/lifecycle operation. Native lifecycle evidence is retained before presentation/export. Failed begin blocks dispatch; failed finish stops dependent work and requires inspection without mutation replay. A named persistent journal requires actual publication even before Pi's first conversation. Disabled/no-file sessions use the native current in-memory ancestry for direct browser work without claiming durability; code still requires a published intent journal. Observation-only artifact receipts are optional without a published journal, so sessionless local results retain complete redacted private spills and selected files. Code lifecycle joins include outer finalization, and the shared generation guard keeps prior-branch observation spills process-private and prevents their artifact publication onto the new branch. JavaScript variables are never persisted.
|
|
57
|
+
`lib/browser-transcript.ts` defines one canonical `data.event` format and optional once-stored `data.snapshot`. Direct, advanced, code and Electron host operations record begin/finish receipts before dispatch and after observed effects, using outer tool-call ID plus inner index and a unique operation ID. The shared page reducer retains an inaccessible candidate during unresolved work and requires explicit reuse/replace/invalidate/unknown disposition. Ordinary results omit unchanged maps and unrelated artifact receipts; journal events carry redacted artifact-row changes in commit order. Final rendering queues actual spill writes and manifest publication, allowing unrelated small observations to finish while another namespace holds its artifact/lifecycle operation. `lib/orchestration/browser-run/artifact-merge.ts` preserves concurrent three-way manifest updates and recording lifecycle order. Native lifecycle evidence is retained before presentation/export. Failed begin blocks dispatch; failed finish stops dependent work and requires inspection without mutation replay. A named persistent journal requires actual publication even before Pi's first conversation. Disabled/no-file sessions use the native current in-memory ancestry for direct browser work without claiming durability; code still requires a published intent journal. Observation-only artifact receipts are optional without a published journal, so sessionless local results retain complete redacted private spills and selected files. Code lifecycle joins include outer finalization, and the shared generation guard keeps prior-branch observation spills process-private and prevents their artifact publication onto the new branch. JavaScript variables are never persisted.
|
|
58
58
|
|
|
59
|
-
`lib/browser-journal.ts` token-projects published envelopes and ancestry through public Pi file/header/leaf APIs, loading only winning snapshot definitions. It captures the selected leaf before asynchronous reads and validates that same branch through
|
|
59
|
+
`lib/browser-journal.ts` token-projects published envelopes and ancestry through public Pi file/header/leaf APIs, loading only winning snapshot definitions. It captures the selected leaf before asynchronous reads and validates that same branch through public `getEntry()` ancestry on both hosts. Capture-local admission reuses a validated append-only prefix while checking endpoint identity/parent and new suffixes; publication/durability checks remain independent. Startup/tree restoration uses one queued captured replay for branch state and cleanup leases, with one per-session launch-configuration fold. Stock official 1.0.0 still eagerly loads native history before extension startup. Legacy adapters live only in `lib/browser-session-conversion.ts`, which publishes a validated stopped separate copy while retaining the source. Native generation checks use PID/start and socket identity, never saved URL or delta revision alone. Filtered/helper captures share this check. Small owner-qualified launch receipts retain observed restore-key provenance; restart verifies the current native generation and key before treating that provenance as live. Full storage, migration and consumer-memory boundaries are documented in [`SESSION_STORAGE.md`](SESSION_STORAGE.md).
|
|
60
60
|
|
|
61
61
|
### Canonical observations and image geometry
|
|
62
62
|
|
|
63
63
|
`results/presentation/content.ts` projects decision-relevant fields once; `large-output.ts` renders model-visible JSON/prose after recovery, diagnostics, and requested output-file writes are assembled. Failed file exports retain partial code data/images and report a failed observation. `details` remains audit/replay metadata. Final text has a 16,000-character bound; a complete redacted observation spill retains data and exact recovery payloads that cannot fit inline. Native help/version stays text. Inner `modelVisible: false` calls keep data without full prose/spill/image encoding; selected JSON and images are emitted at the outer boundary. Handled failures remain visible with counts and recovery, even if source finishes successfully.
|
|
64
64
|
|
|
65
|
+
Native tool declarations share Browser namespace metadata and `outputSchema`; `structuredContent` reuses the bounded redacted observation or its existing compact/spill object. Loader inventory and normalized provider search keep their existing shapes. Internal ownership/replay details are not exported wholesale. This interoperates with host-selected native codemode without auto-enabling it or replacing the Browser VM. A path/handle is not native image data; verified content image blocks remain separate. Final results return native `isError` and retain prose failure notices while explicit JSON stays parseable and unchanged. Per-row TUI memoization skips unchanged formatting/Text updates without caching browser state or observations across calls.
|
|
66
|
+
|
|
65
67
|
`orchestration/browser-run/screenshot-observation.ts` brackets standalone screenshots with CSS viewport/document/scroll/DPR/frame samples. Existing Pi image-dimension primitives measure saved pixels. Matching samples and dimensions can establish a crop mapping; changed, unmeasured batch, scrolled/ambiguous element, zoom, or frame-uncertain captures remain unknown. These samples are not an atomic capture guarantee. Native mouse input uses current viewport CSS pixels, not image pixels; full-page/element origin and any Pi resize must be accounted for separately. Unchanged conditional screenshots attach nothing. See [image observations](TOOL_CONTRACT.md#image-observations).
|
|
66
68
|
|
|
67
69
|
### Agent-first UX
|
|
@@ -83,7 +85,7 @@ Pi docs use `settings.json` for package/resource loading and filtering, not arbi
|
|
|
83
85
|
- project-local: `.pi/config/pi-agent-browser-native/config.json`
|
|
84
86
|
- explicit override: `PI_AGENT_BROWSER_CONFIG=/path/to/config.json`
|
|
85
87
|
|
|
86
|
-
Config layers merge in that order: global, project, override. The shared policy module (`extensions/agent-browser/lib/config-policy.js`) owns provider descriptors, environment variable names, config keys, credential source parsing, developer-trusted project layer inclusion, layer validation/merge, redacted status projection, and credential summaries for both runtime config loading and the package config helper.
|
|
88
|
+
Config layers merge in that order: global, project, override. The shared policy module (`extensions/agent-browser/lib/config-policy.js`) owns provider descriptors, environment variable names, config keys, credential source parsing, developer-trusted project layer inclusion, layer validation/merge, redacted status projection, and credential summaries for both runtime config loading and the package config helper. Globally installed or CLI-loaded copies read `.pi/config/pi-agent-browser-native/config.json` when Pi's required native `isProjectTrusted()` decision permits it, and skip that project layer when the project is untrusted or Pi is launched with `--no-approve`. Global config and explicit `PI_AGENT_BROWSER_CONFIG` overrides remain available either way. The config reader accepts v1 fields for `webSearch.enabled`, `webSearch.preferredProvider`, `webSearch.defaultSearchType`, `webSearch.exaApiKey`, `webSearch.braveApiKey`, and conservative browser defaults such as `browser.defaultProfile` and `browser.executablePath`. Web-search key fields follow Pi model/provider-style value resolution from any loaded layer: literal values, `$ENV_VAR` / `${ENV_VAR}` interpolation, escapes (`$$`, `$!`), and leading `!command` resolved at request time. `EXA_API_KEY` and `BRAVE_API_KEY` remain environment fallbacks when no config credential source exists for that provider. Browser default values keep their source scope; prompt guidance is emitted from the highest-priority loaded layer, including project config when Pi trust/loading allows it.
|
|
87
89
|
|
|
88
90
|
`agent_browser_web_search` availability is conditional. Startup registration uses global, override, and environment fallback config without reading project-local config before Pi trust context exists; trusted project config can register the companion tool on `session_start`, and every execution reloads the final session config so `webSearch.enabled: false` still prevents a request even if a startup credential made the tool visible. A global disable is the normal user default and can still be overridden by project config or `PI_AGENT_BROWSER_CONFIG`; a project disable applies to one repo; an explicit `PI_AGENT_BROWSER_CONFIG` file with `webSearch.enabled: false` is the highest-priority hard disable for that run. Literal and env-backed sources must resolve before they make the tool available; command-backed sources are considered configured without running the command until tool execution, so secret managers do not slow startup or prompt unexpectedly. The tool resolves the selected key lazily, chooses Exa or Brave from available credentials (preferring Exa by default unless `webSearch.preferredProvider` says otherwise), then follows one provider-agnostic execution path through provider adapters for request building, HTTP JSON fetch, response normalization, and provider-specific detail fields. Exa search-type precedence is per-call value, merged config default, then `auto`; bounded Exa filters stay on the Exa adapter and explicit Exa-only options fail before a Brave request. The tool calls Exa `/search` with highlights plus a fixed primary-source/version/date/distinct-result system prompt, or Brave Search, then removes later exact normalized-URL duplicates while retaining provider order. It does not guess across distinct paths/query URLs or overfetch replacements; provider page dates remain clues rather than claimed crawl/version evidence. Compact result details report any `duplicatesRemoved` without exposing keys.
|
|
89
91
|
|
|
@@ -91,7 +93,7 @@ Global/override package profile names with `policy: "always"` and executable def
|
|
|
91
93
|
|
|
92
94
|
### Prompt guidance budget
|
|
93
95
|
|
|
94
|
-
Use Pi-native `promptGuidelines`, tool declarations, and named `systemPromptOptions.sections` contributions. Do not return a replacement full system prompt from `before_agent_start`: it breaks additive declaration anchoring. Official Pi 0
|
|
96
|
+
Use Pi-native `promptGuidelines`, tool declarations, and named `systemPromptOptions.sections` contributions. Do not return a replacement full system prompt from `before_agent_start`: it breaks additive declaration anchoring. Official Pi 1.0 and the fork use this same composition; hosted programmatic tool calling, async provider lifecycle, image-detail overrides, or model-specific payload edits are not package dependencies. Prefix stability is not proof of a cache hit.
|
|
95
97
|
|
|
96
98
|
Browser instructions come from one full source assembled from `lib/playbook.ts` and current trusted config. Shared rules and all advanced tool guidelines are delivered together, rather than maintaining a short eager summary beside a hidden manual. Managed hosts defer this full group; stock hosts receive it eagerly. Tool registrations retain concise descriptions and schemas without duplicate `promptGuidelines`.
|
|
97
99
|
|
|
@@ -112,7 +114,7 @@ The published package should load from the `pi` manifest in `package.json`.
|
|
|
112
114
|
Local checkout validation has two intentional modes:
|
|
113
115
|
|
|
114
116
|
- **Checkout-only extension mode:** use explicit CLI loading such as `pi --approve --no-extensions -e .` from the intentionally trusted repository root. This disables automatic extension loading and avoids duplicate `agent_browser` registrations, but settings, configured package resolution, and other resource types remain active. Temporary `HOME` and `PI_CODING_AGENT_DIR` directories isolate test settings; `PI_OFFLINE=1` disables automatic startup network/update operations. Omit `--approve` only when testing Project Trust.
|
|
115
|
-
- **Configured-source lifecycle mode:** configure exactly one active checkout or package source in Pi settings and launch plain `pi` for manual validation, or run the automated harness that launches with `--approve`. This is the right mode for validating `/reload` and exact-session relaunch because those lifecycle checks exercise discovered/configured resources. Focused extension harness tests validate branch-backed `session_tree` rehydration and cleanup ownership. Before shipping, maintainers also run `npm run verify -- lifecycle` (same semantics under automation, using Pi 0.
|
|
117
|
+
- **Configured-source lifecycle mode:** configure exactly one active checkout or package source in Pi settings and launch plain `pi` for manual validation, or run the automated harness that launches with `--approve`. This is the right mode for validating `/reload` and exact-session relaunch because those lifecycle checks exercise discovered/configured resources. Focused extension harness tests validate branch-backed `session_tree` rehydration and cleanup ownership. Before shipping, maintainers also run `npm run verify -- lifecycle` (same semantics under automation, using Pi 1.0.0+ `--approve --session-id` to reopen the exact JSONL session) plus the live-site checks in [`RELEASE.md`](RELEASE.md#pre-release-checks); `npm publish` enforces `npm run verify -- release` via `prepublishOnly` unless scripts are skipped.
|
|
116
118
|
|
|
117
119
|
The repo should not add a repo-local `.pi/extensions/` autoload shim as the documented checkout path.
|
|
118
120
|
|
|
@@ -173,7 +175,7 @@ Ownership rule:
|
|
|
173
175
|
- extension-managed sessions should be reusable during an active `pi` session and across `/reload`, exact-session relaunch, `/resume`, and Pi branch-tree transitions, while still being cleaned up predictably
|
|
174
176
|
|
|
175
177
|
Practical policy:
|
|
176
|
-
- preserve the current branch-visible extension-managed session across `/reload`, exact-session relaunch, `/resume`, and Pi 0.
|
|
178
|
+
- preserve the current branch-visible extension-managed session across `/reload`, exact-session relaunch, `/resume`, and Pi 1.0.0+ `session_tree` branch transitions so persisted sessions can keep following the live browser after lifecycle changes
|
|
177
179
|
- close the active extension-managed session when the originating `pi` process quits, while leaving explicit caller-provided sessions alone
|
|
178
180
|
- after branch restore, use the existing locked daemon inspection to distinguish a confirmed inactive wrapper-owned daemon from a live, unknown, or unavailable one. For compatible automatic managed restore only, keep the pending reopen in ordered session page state and persist it as `sessionTabReopenPending`. Non-page calls such as `tab list` can start a daemon without fulfilling it; explicit HTTP reads leave the managed browser and pending reopen untouched, including across branch/reload replay. Before the first current-page operation (`get url`, history commands and relative `pushstate` included), reopen the complete recorded URL with native `open`, invalidate old refs with the existing `page-transition` state, and verify the actual tab. Explicit URL reads and all-read batches skip managed ownership changes and browser helpers entirely; native owns their HTTP fetch and validation. Native `open` resets frame scope. Consume the obligation on that attempt or an executed explicit context/navigation/close command, not an unreached batch row; a failed open does not permit repeated navigation of a now-live browser. After the reopen CLI starts, cancellation returns a structured `aborted` result through the ordinary result path with the exact namespace/session, consumed marker and ref invalidation; it does not throw away replay state or run later browser helpers. Cancellation before the CLI starts does not consume the pending reopen. Internal URLs retain their fragments while tab/ref comparisons remain fragment-insensitive and presentation keeps normal redaction. Older transcripts cannot recover a fragment they did not record. Caller-owned/attached browsers and restore-disabled sessions do not take this path; live wrong-tab recovery still only selects an existing target. Reopening reloads the URL, not unsaved forms, JavaScript memory, or history. There is no second restore store or lifecycle lock.
|
|
179
181
|
- set an idle timeout on extension-managed sessions as a backstop for abnormal exits or cleanup failures, and apply that same `AGENT_BROWSER_IDLE_TIMEOUT_MS` value to every subprocess targeting that owned session (including wrapper helper snapshots, tab lists, and navigation-summary reads); caller-owned sessions instead retain native config/environment idle policy because changing the launch environment between calls can make upstream restart the background browser, discard the active tab, and invalidate fresh refs
|
|
@@ -198,7 +200,7 @@ Practical policy:
|
|
|
198
200
|
- native 0.37 owns first-document setup inheritance for new tabs; the wrapper does not copy headers, user agents or other browser setup itself. The page-summary formatter displays native positive WebMCP availability while retaining raw details; it adds no discovery probe
|
|
199
201
|
- when a direct or batched WebMCP call returns `status: "pending"`, or `result` / `cancel` fails while that target is unknown, keep its tab target unknown and discard same-call snapshot evidence instead of treating an immediate post-dispatch URL probe as stable; `webmcp result` / `cancel`, `get url`, and explicit navigation remain available while unknown; replace the generic blocked snapshot action with `verify-page-target-after-pending-webmcp` (`get url`), and let a completed `batch --bail` use that verification before `snapshot -i` to re-establish both target and refs
|
|
200
202
|
- for top-level non-Electron direct `click` commands with an eligible target, install a bounded in-page target-specific event probe before upstream runs; if upstream reports success but no trusted pointer/mouse/click event reached the resolved target, fail the tool and report `details.clickDispatch` with explicit retry/inspect next actions (the wrapper does not replay clicks in-page). The probe covers `xpath=` targets and current `@e…` / `ref=` refs whose latest stored `refSnapshot.refs` role is `button`, `checkbox`, `menuitem`, `radio`, `switch`, or `tab`; it requires a unique role/name in the saved snapshot and the live candidates instead of taking a fresh pre-click snapshot that could recycle upstream refs. Ref and XPath candidates must additionally prove identity through a per-probe temporary DOM attribute read back by native `get attr <original-selector> <marker>`. Missing or mismatched identity, including XPath/frame scope mismatches, cleans up the probe and lets native click run without dispatch verification; candidate naming heuristics alone are never proof. Duplicate-name refs pass through without a probe: their old ordinal is not target identity. The probe is intentionally skipped for CSS selector clicks, unresolved `find … click` locators, and `batch`/`qa` click steps
|
|
201
|
-
-
|
|
203
|
+
- skip unrelated tools and harmless Bash inspections before building prompt policy. Seed the latest raw user message on start/tree, update it on user `message_end`, and re-read its current content before parsing policy so native content replacement cannot leave stale intent. Capture that policy at queued admission without a whole-branch scan on every event. Derive narrow prompt guards only for concrete evidence invariants: explicitly requested screenshot/recording output paths block browser close until the artifact manifest verifies those paths, while bare inbound attachment paths remain inputs. The wrapper intentionally does not infer broad business/user intent from prompt text such as order/payment/post boundaries; agents must follow those instructions themselves. The artifact guard is bounded preflight policy (`details.promptGuard`, `failureCategory: "policy-blocked"`), not a reusable browser recipe layer
|
|
202
204
|
- keep the existing page/selector scroll API; explicit CSS-container movement uses `Element.scrollBy` with `behavior: "instant"` before measuring offsets so smooth-scroll CSS cannot cause a premature no-movement failure
|
|
203
205
|
- reject direct and effective batch `scrollintoview text=...` / `scrollinto text=...` before dispatch because current upstream can falsely report success without movement, while leaving help forms untouched; return only native recovery (`find text ... hover` or fresh snapshot/ref), leaving CSS, XPath, and current-ref behavior upstream-owned
|
|
204
206
|
- after successful `get text` on a qualifying non-ref CSS selector, optionally issue one read-only `eval --stdin` probe per selector when multiple DOM matches or a hidden first match with visible peers could misread tabbed or off-screen content; simple id selectors and sensitive-looking literals skip this probe. Merge `details.selectorTextVisibility` / `selectorTextVisibilityAll`, visible warning lines, and `inspect-visible-text-candidates*` next actions as documented in [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details) and `RQ-0074` in [`SUPPORT_MATRIX.md`](SUPPORT_MATRIX.md)
|
|
@@ -207,15 +209,11 @@ Practical policy:
|
|
|
207
209
|
|
|
208
210
|
This is primarily about ownership clarity and avoiding surprise, not adding a heavy safety wrapper. If the extension invented the session, the extension should own its lifecycle without breaking reload, resume, or branch-tree semantics. If the caller explicitly chose the upstream session model, the extension should stay out of the way.
|
|
209
211
|
|
|
210
|
-
###
|
|
211
|
-
|
|
212
|
-
The additive native `session_checkpoint` hook qualifies only the no-active-resource path. Pi already awaits tools, commands and lifecycle callbacks, including their returned execution queues. The hook checks the active code controllers/completion promises, recording reservations and dirty retry state, cleanup leases, Electron records/children, attachments, trace owners and routes. It adds no work queue or state journal, and never invokes shutdown, stops recording, or closes a browser during capture.
|
|
213
|
-
|
|
214
|
-
Known native identities come from existing ownership and all native tool-result entries, including off-branch caller-owned/root sessions and the unnamed native default (launch flags can open it even for an explicit URL read). Read-only `session info` must prove each daemon inactive; active, missing-binary and unknown results remain blockers. These probes reuse the existing owned-session context for runtime-owned identities and transcript-proven wrapper-created identities, including off-branch browsers after an abnormal restart, so an ambient `AGENT_BROWSER_SOCKET_DIR` cannot redirect their inspection. Historical routing uses the existing managed-resource events and namespace/session identity, not a name pattern alone, and does not acquire cleanup ownership. For owned or historically owned targets, inspection also checks native ambient routing: the same namespace/name can identify different daemons in different socket roots. A later transcript close does not suppress the historical route's read-only probe, since it may have closed only the ambient caller. Explicit caller choices (even the exact historical name with `sessionMode: "fresh"`), unrelated namespaces and root identities retain native ambient routing. An unused extension needs no browser executable or profile scan. Independent hosts/group members must still coordinate their own writers and resources; a Pi receipt is not a cross-process browser lock.
|
|
212
|
+
### Lifecycle boundary
|
|
215
213
|
|
|
216
|
-
|
|
214
|
+
Native Pi lifecycle callbacks and existing queues retain ownership-aware shutdown/reload behavior. Shared root and caller-owned browsers are not closed on Pi quit; wrapper-owned managed/fresh and Electron resources keep UUID/branch-qualified cleanup. An ordinary session fork does not inherit its parent's cleanup rights. Close or clean up owned resources before disabling/removing the extension: native reload after removal has no browser handover guarantee. Forced concurrent SDK reload and recovery after failed off-branch cleanup are not fully qualified lifecycle guarantees.
|
|
217
215
|
|
|
218
|
-
|
|
216
|
+
Native close/save and explicit URL reopen remain the persistence path, not a live browser snapshot. Page JavaScript, unsaved forms, history, IndexedDB and live recordings are not serialized by this extension. Neither supported Pi 1.0 target uses Browser checkpoint or fork metadata/revision hooks; retained legacy journal decoding remains confined to stopped-copy conversion.
|
|
219
217
|
|
|
220
218
|
### Launch flags
|
|
221
219
|
|
|
@@ -268,8 +266,8 @@ Upstream restore-state persistence remains upstream-owned. The wrapper passes an
|
|
|
268
266
|
- subprocess execution and JSON parsing through `buildAgentBrowserProcessEnv` in `extensions/agent-browser/lib/process.ts`: copies the parent process environment so user-approved provider credentials and other runtime variables reach upstream, then applies wrapper overrides such as the managed socket directory and clamped default operation timeout
|
|
269
267
|
- clear missing-binary errors
|
|
270
268
|
- filtered `network requests --current-page` / `--current-origin` / `--current-url` preparation results use the same `redactPresentationData` credential redactor as ordinary diagnostics before populating `details.data` or writing `outputPath`
|
|
271
|
-
- compact result summaries, including presentation-time redaction: stateful browser-context commands (`auth`, `cookies`, `storage`, `dialog`, `frame`, `state`) use field-aware value redaction and compact formatters, while other structured upstream JSON (for example `network`, `diff`, `trace` / `profiler` / `record`, `console` / `errors` / `highlight` / `inspect` / `clipboard`, `stream`, `dashboard`, and `chat`) is passed through `redactPresentationData` in `extensions/agent-browser/lib/results/presentation.ts` so model-facing `details.data` and batch roll-ups stay compact and do not echo bearer tokens, proxy passwords, or similar fields verbatim; `redactInvocationArgs` in `extensions/agent-browser/lib/runtime.ts` masks trailing values for sensitive global flags such as `--body`, `--headers`, `--password`, and `--proxy`, preserves positional rules for `cookies set` and `storage local|session set
|
|
272
|
-
- bounded machine-readable outcome metadata on tool `details` (`resultCategory`, `successCategory`, `failureCategory`, optional `nextActions`, optional `pageChangeSummary` with observed-vs-dispatched evidence and per-step summaries on `batch`, optional `artifactVerification` with the same shape on each successful `batchSteps[]` row) so agents can branch without parsing prose; enums, classifier precedence, and generic follow-up payloads are implemented under `extensions/agent-browser/lib/results/` in focused modules (`contracts.ts` for shared types, `categories.ts` for `classifyAgentBrowserSuccessCategory` / `classifyAgentBrowserFailureCategory` / `buildAgentBrowserResultCategoryDetails`, `action-recommendations.ts` for `buildAgentBrowserNextActions`, `next-actions.ts` for the `AgentBrowserNextAction` shape and merge helpers, `recovery-actions.ts` for recovery id registries and `buildRecoveryNextActions`, `network.ts` for `classifyNetworkRequestFailure` / `summarizeNetworkFailures`, and related helpers). Per-session tab target, `refSnapshot` alignment, invalidation, and tab pinning observations flow through `extensions/agent-browser/lib/session-page-state.ts` from `extensions/agent-browser/index.ts`. Compact page-change summaries and artifact verification rollups are built in `extensions/agent-browser/lib/results/presentation.ts` (`buildPageChangeSummary`, `buildArtifactVerificationSummary`), and the human contract lives in [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details).
|
|
269
|
+
- compact result summaries, including presentation-time redaction: stateful browser-context commands (`auth`, `cookies`, `storage`, `dialog`, `frame`, `state`) use field-aware value redaction and compact formatters, while other structured upstream JSON (for example `network`, `diff`, `trace` / `profiler` / `record`, `console` / `errors` / `highlight` / `inspect` / `clipboard`, `stream`, `dashboard`, and `chat`) is passed through `redactPresentationData` in `extensions/agent-browser/lib/results/presentation.ts` so model-facing `details.data` and batch roll-ups stay compact and do not echo bearer tokens, proxy passwords, or similar fields verbatim; `redactInvocationArgs` in `extensions/agent-browser/lib/runtime.ts` masks trailing values for sensitive global flags such as `--body`, `--headers`, `--password`, and `--proxy`, preserves positional rules for `cookies set` and `storage local|session set` while distinguishing native `--curl` file-import operands from inline cookie values (including failed batch diagnostics), and nested `batch` steps use the same argv and error-body scrubbing before echoing commands or errors. Shared URL redaction covers SAMLRequest/SAMLResponse/RelayState and auth-context-only state/nonce at model/persisted-artifact boundaries while retaining exact internal page-target URLs; snapshot spills are redacted before disk writes
|
|
270
|
+
- bounded machine-readable outcome metadata on tool `details` (`resultCategory`, `successCategory`, `failureCategory`, optional `nextActions`, optional `pageChangeSummary` with observed-vs-dispatched evidence and per-step summaries on `batch`, optional `artifactVerification` with the same shape on each successful `batchSteps[]` row) so agents can branch without parsing prose; enums, classifier precedence, and generic follow-up payloads are implemented under `extensions/agent-browser/lib/results/` in focused modules (`contracts.ts` for shared types, `categories.ts` for `classifyAgentBrowserSuccessCategory` / `classifyAgentBrowserFailureCategory` / `buildAgentBrowserResultCategoryDetails`, `action-recommendations.ts` for `buildAgentBrowserNextActions`, `next-actions.ts` for the `AgentBrowserNextAction` shape and merge helpers, `recovery-actions.ts` for recovery id registries and `buildRecoveryNextActions`, `network.ts` for `classifyNetworkRequestFailure` / `summarizeNetworkFailures`, and related helpers). Per-session tab target, `refSnapshot` alignment, invalidation, and tab pinning observations flow through `extensions/agent-browser/lib/session-page-state.ts` from `extensions/agent-browser/index.ts`. Compact page-change summaries and artifact verification rollups are built in `extensions/agent-browser/lib/results/presentation.ts` (`buildPageChangeSummary`, `buildArtifactVerificationSummary`), and the human contract lives in [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details). Final public results return native `isError` after all wrapper reclassifications, without a `tool_result` repair hook. Prose retains the visible category notice; explicit parseable JSON stays unchanged. `lib/native-output.ts` and `finalizeAgentBrowserFailure` in `lib/pi-tool-rendering.ts` keep structured observations, transcript status and TUI affordances aligned (transcript semantics in the same contract doc)
|
|
273
271
|
- inline screenshots/images for the plain `screenshot` command; other image-like saves (for example `diff screenshot`) still appear in `details.artifacts` and summaries but are not auto-inlined as Pi image attachments (see [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details))
|
|
274
272
|
- lightweight session convenience
|
|
275
273
|
- docs, including a repo-readable command reference that mirrors the blocked direct-binary help path closely enough for normal agent work
|
|
@@ -10,7 +10,7 @@ Related docs:
|
|
|
10
10
|
|
|
11
11
|
## Purpose
|
|
12
12
|
|
|
13
|
-
Requires Pi 0.
|
|
13
|
+
Requires Pi 1.0.0 or newer; official and fork hosts are qualified independently.
|
|
14
14
|
|
|
15
15
|
Provide a local, repo-readable command reference for the native `agent_browser` tool.
|
|
16
16
|
|
|
@@ -18,6 +18,8 @@ This project intentionally blocks normal `agent-browser` bash usage in most agen
|
|
|
18
18
|
|
|
19
19
|
After updating `pi-agent-browser-native`, fully quit and restart Pi before using the updated tools. `/reload` can retain previously loaded compiled JavaScript even after `dist/` is rebuilt, so it is not a reliable way to pick up package updates.
|
|
20
20
|
|
|
21
|
+
Tool declarations use the native `browser` namespace and output schemas. Native `structuredContent` carries the same bounded redacted observation as direct/code output; loader inventory and optional search retain their existing data shapes. It does not expose internal replay details or turn image paths/handles into native `image()` input. The five advanced direct tools default inactive; enable them through `agent_browser_tools` unless already selected by the host. See [canonical observations](TOOL_CONTRACT.md#canonical-observation).
|
|
22
|
+
|
|
21
23
|
SDK hosts can supply an awaited [`beforeExecute` callback](TOOL_CONTRACT.md#host-execution-hook) to save host state before ordinary or code-inner browser dispatch. This is a factory option, not a tool argument; normal installations do not need it.
|
|
22
24
|
|
|
23
25
|
## Upstream baseline
|
|
@@ -757,6 +759,8 @@ These calls return plain text and stay stateless: the extension does not inject
|
|
|
757
759
|
| `cookies [get|set|clear]` | Manage cookies. Full set form: `cookies set <name> <value> --url <url> --domain <domain> --path <path> --httpOnly --secure --sameSite <Strict|Lax|None> --expires <timestamp>`; also supports `cookies set --curl <file>` for JSON, cURL, or bare Cookie-header bulk imports. |
|
|
758
760
|
| `storage <local|session>` | Manage web storage. |
|
|
759
761
|
|
|
762
|
+
`cookies set --curl <file>` selects native file-import mode even when `--curl` follows other set operands. Ordinary import paths remain visible in invocation echoes and failed batch diagnostics; inline cookie values, sensitive flags, and secret-bearing path text still redact. File contents are not echoed by this exception.
|
|
763
|
+
|
|
760
764
|
Privacy note: `cookies get` can expose real profile cookies. Do not run it against `--profile Default` or other authenticated profiles unless the user explicitly needs cookie inspection; prefer task-specific page actions and storage checks.
|
|
761
765
|
|
|
762
766
|
### WebMCP page tools
|
|
@@ -910,7 +914,7 @@ When these commands are invoked through the native `agent_browser` tool, structu
|
|
|
910
914
|
- project-local: `.pi/config/pi-agent-browser-native/config.json`
|
|
911
915
|
- explicit override: `PI_AGENT_BROWSER_CONFIG=/path/to/config.json`
|
|
912
916
|
|
|
913
|
-
Get an Exa API key from the [Exa dashboard](https://dashboard.exa.ai/api-keys) or a Brave Search API key from the [Brave Search API dashboard](https://api-dashboard.search.brave.com/). If both keys are available, `agent_browser_web_search` prefers Exa by default because its `/search` endpoint returns token-efficient highlights and agent-oriented search modes; set `webSearch.preferredProvider` to `"brave"` when Brave Search is preferred. You can also disable this package's search tool with `webSearch.enabled: false` when another search tool should win. Config merges global → project → `PI_AGENT_BROWSER_CONFIG` override, so `enabled` is read from the final loaded config: a global disable can be re-enabled by project or override config, while an override file with `enabled: false` is the highest-priority hard disable for that run.
|
|
917
|
+
Get an Exa API key from the [Exa dashboard](https://dashboard.exa.ai/api-keys) or a Brave Search API key from the [Brave Search API dashboard](https://api-dashboard.search.brave.com/). If both keys are available, `agent_browser_web_search` prefers Exa by default because its `/search` endpoint returns token-efficient highlights and agent-oriented search modes; set `webSearch.preferredProvider` to `"brave"` when Brave Search is preferred. You can also disable this package's search tool with `webSearch.enabled: false` when another search tool should win. Config merges global → project → `PI_AGENT_BROWSER_CONFIG` override, so `enabled` is read from the final loaded config: a global disable can be re-enabled by project or override config, while an override file with `enabled: false` is the highest-priority hard disable for that run. Globally installed or CLI-loaded copies read project-local config under `.pi/config/...` when Pi's native trust decision permits it, and skip that layer when the project is untrusted or Pi is launched with `--no-approve`.
|
|
914
918
|
|
|
915
919
|
`pi install npm:pi-agent-browser-native` loads the extension, but it does **not** usually put the package helper on your shell `PATH`. The clearest setup is to write the config file directly and keep actual keys in the environment that launches `pi`:
|
|
916
920
|
|