pi-agent-browser-native 0.8.2 → 0.9.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 +35 -0
- package/README.md +24 -15
- package/dist/extensions/agent-browser/index.js +496 -343
- package/dist/extensions/agent-browser/lib/browser-journal.js +439 -0
- package/dist/extensions/agent-browser/lib/browser-session-conversion.js +381 -0
- package/dist/extensions/agent-browser/lib/browser-transcript.js +119 -19
- package/dist/extensions/agent-browser/lib/execution-cwd.js +0 -16
- package/dist/extensions/agent-browser/lib/input-modes/job.js +49 -2
- package/dist/extensions/agent-browser/lib/input-modes/params.js +32 -35
- package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +25 -5
- package/dist/extensions/agent-browser/lib/managed-session-restore.js +20 -5
- package/dist/extensions/agent-browser/lib/native-output.js +46 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +97 -78
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +23 -2
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +52 -17
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/script-mode.js +13 -8
- package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +30 -16
- package/dist/extensions/agent-browser/lib/playbook.js +9 -2
- package/dist/extensions/agent-browser/lib/prompt-policy.js +16 -26
- package/dist/extensions/agent-browser/lib/results/presentation/content.js +4 -0
- package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +17 -9
- package/dist/extensions/agent-browser/lib/session-page-state.js +95 -157
- package/dist/extensions/agent-browser/lib/temp.js +14 -6
- package/dist/extensions/agent-browser/lib/tool-surface.js +50 -33
- package/dist/extensions/agent-browser/lib/web-search.js +85 -72
- package/docs/ARCHITECTURE.md +25 -25
- package/docs/COMMAND_REFERENCE.md +13 -9
- package/docs/RELEASE.md +25 -19
- package/docs/REQUIREMENTS.md +6 -6
- package/docs/SESSION_STORAGE.md +60 -0
- package/docs/SUPPORT_MATRIX.md +16 -11
- package/docs/TOOL_CONTRACT.md +26 -19
- package/package.json +9 -5
- package/scripts/build.mjs +4 -3
- package/scripts/convert-browser-session.mjs +22 -0
- package/scripts/doctor.mjs +2 -2
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { JsonSchema } from "./json-schema.js";
|
|
2
|
-
import {
|
|
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";
|
|
3
5
|
import { StringEnum as localStringEnum } from "./string-enum-schema.js";
|
|
4
6
|
import { DEFAULT_WEB_SEARCH_PROVIDER, EXA_SEARCH_TYPES, WEB_SEARCH_PROVIDERS, resolvePreferredWebSearchCredential, } from "./config.js";
|
|
5
7
|
export const AGENT_BROWSER_WEB_SEARCH_TOOL_NAME = "agent_browser_web_search";
|
|
@@ -312,10 +314,10 @@ function getStartPublishedDate(freshness, now) {
|
|
|
312
314
|
export function buildExaSearchRequestBody(params, now = () => new Date()) {
|
|
313
315
|
const searchType = params.searchType ?? "auto";
|
|
314
316
|
if (params.additionalQueries?.length && !searchType.startsWith("deep")) {
|
|
315
|
-
throw new
|
|
317
|
+
throw new WebSearchLocalError(`additionalQueries requires deep-lite, deep, or deep-reasoning; received ${searchType}.`);
|
|
316
318
|
}
|
|
317
319
|
if ((params.category === "company" || params.category === "people") && (params.freshness || params.excludeDomains?.length)) {
|
|
318
|
-
throw new
|
|
320
|
+
throw new WebSearchLocalError(`category ${params.category} cannot be combined with freshness or excludeDomains.`);
|
|
319
321
|
}
|
|
320
322
|
const body = {
|
|
321
323
|
query: params.query,
|
|
@@ -559,86 +561,97 @@ function buildMissingCredentialError(provider) {
|
|
|
559
561
|
return "agent_browser_web_search provider exa was requested but no EXA_API_KEY/config credential resolved.";
|
|
560
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.";
|
|
561
563
|
}
|
|
564
|
+
/** Local validation/config failures, not provider outages; reported as `validation-error`. */
|
|
565
|
+
class WebSearchLocalError extends Error {
|
|
566
|
+
}
|
|
562
567
|
export function createAgentBrowserWebSearchTool(configState, options = {}) {
|
|
563
568
|
const requestGate = new WebSearchRequestGate();
|
|
564
569
|
return {
|
|
565
570
|
name: AGENT_BROWSER_WEB_SEARCH_TOOL_NAME,
|
|
571
|
+
namespace: AGENT_BROWSER_NAMESPACE,
|
|
572
|
+
outputSchema: AGENT_BROWSER_OUTPUT_SCHEMA,
|
|
566
573
|
label: "Agent Browser Web Search",
|
|
567
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.`,
|
|
568
575
|
promptSnippet: "Search the live web with Exa or Brave for current or external information.",
|
|
569
|
-
promptGuidelines: [
|
|
570
|
-
WEB_SEARCH_PROMPT_GUIDELINE,
|
|
571
|
-
"agent_browser_web_search chooses Exa or Brave from configured keys; when both are available, Exa is preferred by default unless webSearch.preferredProvider says otherwise. Use provider only when the user/config calls for a specific provider.",
|
|
572
|
-
"Use Exa deep only when deep-lite may miss angles, and deep-reasoning only for exhaustive or still-thin research. Do not run parallel agent_browser_web_search calls; make one high-signal query, inspect its results, then at most one follow-up.",
|
|
573
|
-
"If agent_browser_web_search returns HTTP 429, stop searching and tell the user the API plan/rate limit needs time or a plan change.",
|
|
574
|
-
"After using agent_browser_web_search, cite result URLs in the final answer when web evidence informed the answer.",
|
|
575
|
-
],
|
|
576
576
|
parameters: AgentBrowserWebSearchParams,
|
|
577
577
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
if (!runtimeConfigState.webSearchEnabled) {
|
|
583
|
-
throw new Error("agent_browser_web_search is disabled by pi-agent-browser-native config.");
|
|
584
|
-
}
|
|
585
|
-
const requestedProvider = params.provider ?? "auto";
|
|
586
|
-
const resolved = await resolvePreferredWebSearchCredential(runtimeConfigState, { provider: requestedProvider, signal });
|
|
587
|
-
if (!resolved)
|
|
588
|
-
throw new Error(buildMissingCredentialError(requestedProvider));
|
|
589
|
-
if (resolved.provider === "brave") {
|
|
590
|
-
const exaOnlyFields = [
|
|
591
|
-
params.includeDomains ? "includeDomains" : undefined,
|
|
592
|
-
params.excludeDomains ? "excludeDomains" : undefined,
|
|
593
|
-
params.category ? "category" : undefined,
|
|
594
|
-
params.additionalQueries ? "additionalQueries" : undefined,
|
|
595
|
-
params.highlightsDynamic ? "highlightsDynamic" : undefined,
|
|
596
|
-
].filter((field) => Boolean(field));
|
|
597
|
-
if (exaOnlyFields.length > 0) {
|
|
598
|
-
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("; ")}`);
|
|
599
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);
|
|
600
654
|
}
|
|
601
|
-
const query = params.query.trim();
|
|
602
|
-
if (!query)
|
|
603
|
-
throw new Error("query must not be blank");
|
|
604
|
-
const count = Math.min(Math.max(params.count ?? DEFAULT_SEARCH_RESULT_COUNT, 1), MAX_SEARCH_RESULT_COUNT);
|
|
605
|
-
const offset = Math.max(params.offset ?? 0, 0);
|
|
606
|
-
const adapter = getWebSearchProviderAdapter(resolved.provider);
|
|
607
|
-
const executionParams = {
|
|
608
|
-
additionalQueries: params.additionalQueries,
|
|
609
|
-
category: params.category,
|
|
610
|
-
country: params.country,
|
|
611
|
-
count,
|
|
612
|
-
excludeDomains: params.excludeDomains,
|
|
613
|
-
freshness: params.freshness,
|
|
614
|
-
highlightsDynamic: params.highlightsDynamic,
|
|
615
|
-
includeDomains: params.includeDomains,
|
|
616
|
-
offset,
|
|
617
|
-
query,
|
|
618
|
-
safesearch: params.safesearch,
|
|
619
|
-
searchLang: params.searchLang,
|
|
620
|
-
searchType: params.searchType ?? runtimeConfigState.config.webSearch?.defaultSearchType ?? "auto",
|
|
621
|
-
};
|
|
622
|
-
const request = adapter.buildRequest(executionParams);
|
|
623
|
-
const data = await requestGate.run(signal, () => adapter.fetchJson(request, resolved.credential.value, signal));
|
|
624
|
-
const normalized = adapter.normalizeResponse(data, executionParams);
|
|
625
|
-
const results = dedupeSearchResults(normalized.results);
|
|
626
|
-
const duplicatesRemoved = normalized.results.length - results.length;
|
|
627
|
-
const details = {
|
|
628
|
-
provider: adapter.provider,
|
|
629
|
-
query,
|
|
630
|
-
returnedQuery: normalized.returnedQuery,
|
|
631
|
-
count,
|
|
632
|
-
offset,
|
|
633
|
-
...normalized.extraDetails,
|
|
634
|
-
fetchedAt: new Date().toISOString(),
|
|
635
|
-
results,
|
|
636
|
-
duplicatesRemoved: duplicatesRemoved || undefined,
|
|
637
|
-
};
|
|
638
|
-
return {
|
|
639
|
-
content: [{ type: "text", text: `${formatSearchResults(adapter.provider, normalized.returnedQuery, results)}${duplicatesRemoved ? `\n\nDuplicate URLs removed: ${duplicatesRemoved}.` : ""}` }],
|
|
640
|
-
details,
|
|
641
|
-
};
|
|
642
655
|
},
|
|
643
656
|
};
|
|
644
657
|
}
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -19,13 +19,13 @@ 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
|
|
|
26
|
-
|
|
26
|
+
The extension factory subscribes synchronously to the public `pi:instruction-groups` bus and registers `browser` with its direct/code/loader, advanced, and conditional web-search names. `fullInstructions(ctx)` is the sole delivery source: project rules, trusted runtime config, full shared playbook, tab recovery, every advanced guideline, and conditional web-search guidance. The callback reads current config rather than retaining startup-only guidance.
|
|
27
27
|
|
|
28
|
-
`
|
|
28
|
+
The collector's dynamic `isManaged()` decides ownership. Managed Pi delivers full text on group enable and repairs it after compaction; `before_agent_start` adds no duplicate browser section. Without management, including stock Pi or disabled discovery, that hook eagerly supplies the same full text in the named `agent_browser` section on every prompt. No `ToolDefinition.discovery`, `sectionTools` detection, keyword gate, or full system-prompt replacement remains. Registration and group enablement do not change callable permissions, tool names/namespaces, startup selection, or the separate advanced loader.
|
|
29
29
|
|
|
30
30
|
### Direct subprocess execution
|
|
31
31
|
|
|
@@ -46,7 +46,7 @@ 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;
|
|
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.
|
|
50
50
|
|
|
51
51
|
### Ordered transitions and shared execution
|
|
52
52
|
|
|
@@ -54,12 +54,16 @@ Limits are 25 attempted calls, 64 KiB source/final JSON, 1 MiB per IPC message,
|
|
|
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`
|
|
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.
|
|
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 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).
|
|
58
60
|
|
|
59
61
|
### Canonical observations and image geometry
|
|
60
62
|
|
|
61
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.
|
|
62
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
|
+
|
|
63
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).
|
|
64
68
|
|
|
65
69
|
### Agent-first UX
|
|
@@ -81,7 +85,7 @@ Pi docs use `settings.json` for package/resource loading and filtering, not arbi
|
|
|
81
85
|
- project-local: `.pi/config/pi-agent-browser-native/config.json`
|
|
82
86
|
- explicit override: `PI_AGENT_BROWSER_CONFIG=/path/to/config.json`
|
|
83
87
|
|
|
84
|
-
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.
|
|
85
89
|
|
|
86
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.
|
|
87
91
|
|
|
@@ -89,11 +93,11 @@ Global/override package profile names with `policy: "always"` and executable def
|
|
|
89
93
|
|
|
90
94
|
### Prompt guidance budget
|
|
91
95
|
|
|
92
|
-
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.
|
|
93
97
|
|
|
94
|
-
|
|
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`.
|
|
95
99
|
|
|
96
|
-
|
|
100
|
+
Keep operating rules in the canonical playbook arrays and regenerate their documentation mirrors. Absolute installed-package documentation paths remain available for detailed examples and command inventories; they do not substitute for delivering the full browser guidance.
|
|
97
101
|
|
|
98
102
|
### No reusable recipe layer yet
|
|
99
103
|
|
|
@@ -110,7 +114,7 @@ The published package should load from the `pi` manifest in `package.json`.
|
|
|
110
114
|
Local checkout validation has two intentional modes:
|
|
111
115
|
|
|
112
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.
|
|
113
|
-
- **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.
|
|
114
118
|
|
|
115
119
|
The repo should not add a repo-local `.pi/extensions/` autoload shim as the documented checkout path.
|
|
116
120
|
|
|
@@ -171,16 +175,16 @@ Ownership rule:
|
|
|
171
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
|
|
172
176
|
|
|
173
177
|
Practical policy:
|
|
174
|
-
- 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
|
|
175
179
|
- close the active extension-managed session when the originating `pi` process quits, while leaving explicit caller-provided sessions alone
|
|
176
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.
|
|
177
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
|
|
178
|
-
- for wrapper-owned implicit sessions only, set a transcript- and checkout-scoped `AGENT_BROWSER_RESTORE` key on compatible calls so cookies and web storage can survive idle shutdown, reload, and resume. Explicit caller sessions, restore/state choices, profiles, upstream config, file access, launch arguments, environment variables, local pages, output paths, and close arguments remain upstream-owned and pass through unchanged. `piab-*` names are not reserved; session/state lists and restore identifiers are not filtered or redacted. The wrapper validates only its automatic restore checkout/storage identity and coordinates same-daemon reuse so its own restore pools cannot mix. Ambiguous page-target transitions still require live `get url` verification before content calls. The current
|
|
182
|
+
- for wrapper-owned implicit sessions only, set a transcript- and checkout-scoped `AGENT_BROWSER_RESTORE` key on compatible calls so cookies and web storage can survive idle shutdown, reload, and resume. Explicit caller sessions, restore/state choices, profiles, upstream config, file access, launch arguments, environment variables, local pages, output paths, and close arguments remain upstream-owned and pass through unchanged. `piab-*` names are not reserved; session/state lists and restore identifiers are not filtered or redacted. The wrapper validates only its automatic restore checkout/storage identity and coordinates same-daemon reuse so its own restore pools cannot mix. Ambiguous page-target transitions still require live `get url` verification before content calls. The current v4 set-valued ticket-claim lock is the only managed-daemon coordination protocol; no earlier lock bridge or compatibility path remains. Successful lock acquisition opportunistically inspects at most eight current-protocol candidates/removal artifacts/claims under the existing acquisition deadline (single-attempt `timeoutMs: 0` probes retain their existing no-deadline behavior). Published claims must be at least 30 minutes old, valid, and owned by a proven-dead process before rename/revalidation removal; contention still immediately reclaims proven-dead overlapping claims. Legacy or ambiguous claims remain untouched. Secure-temp allocation similarly inspects at most eight roots per call, retaining the 24-hour lease floor, dead-owner proof and protected Electron children. A randomized first window avoids a fixed retained prefix across short-lived processes; subsequent calls rotate in process. Cross-process progress is probabilistic, not a guaranteed number of calls. Listing/sorting still covers the whole directory; these are work-count bounds, not hard wall-clock deadlines.
|
|
179
183
|
- redact snapshot spill payloads before writing them, clean up process-private temp spill artifacts on shutdown, and keep persisted-session spill files in a private session-scoped artifact directory for `details.fullOutputPath` after reload/resume, with a 32 MiB per-session budget by default; `PI_AGENT_BROWSER_SESSION_ARTIFACT_MAX_BYTES=0` skips persistent-spill enumeration and eviction without changing temporary spill cleanup
|
|
180
|
-
- keep explicit screenshots, downloads, PDFs, traces, HAR captures, and recordings written to caller-chosen paths on disk after a successful upstream close command (`close`, `quit`, or `exit`); before artifact-producing commands run, create missing parent directories for requested host paths, while native download always owns the click and download, including loopback links, generated Blob exports, and redirects; never substitute an anchor-href fetch. When the
|
|
181
|
-
- reconstruct the current branch-visible extension-managed session, every transcript-proven still-active wrapper-owned managed identity, page-scoped refs,
|
|
184
|
+
- keep explicit screenshots, downloads, PDFs, traces, HAR captures, and recordings written to caller-chosen paths on disk after a successful upstream close command (`close`, `quit`, or `exit`); before artifact-producing commands run, create missing parent directories for requested host paths, while native download always owns the click and download, including loopback links, generated Blob exports, and redirects; never substitute an anchor-href fetch. When the internal recent artifact view has entries, successful close commands also surface `details.artifactCleanup` and a compact `Artifact lifecycle` note pointing to structured explicit paths so operators remove files with normal host tools—the native tool does not delete arbitrary user paths (`extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts`, `getArtifactCleanupGuidance`); contract in [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details), checklist `RQ-0079` in [`SUPPORT_MATRIX.md`](SUPPORT_MATRIX.md)
|
|
185
|
+
- reconstruct the current branch-visible extension-managed session, every transcript-proven still-active wrapper-owned managed identity, page-scoped refs, journal-ordered artifact-row changes, unbounded active-recording reservation events, and Electron launch records from the active transcript branch on `session_start` and `session_tree` so later default and explicit off-current calls keep following owned browsers after resume/reload or branch switching; restore also honors successful explicit `--session <wrapper-owned> close` rows, terminal nested-batch close outcomes even when aggregate artifact verification failed, and `electron.cleanup` managed-session steps so closed wrapper-owned sessions are not resurrected; a nested close invalidates the pre-close page target so a lifecycle-proven relaunch at `about:blank` is not treated as stale focus drift; explicit lifecycle evidence that a later diagnostic did not launch a browser preserves the terminal close, while any later row—including a failed row—whose lifecycle reports a browser launch keeps active/attached provenance; failed-step presentation persists only that bounded launch boolean so transcript replay reaches the same decision, missing lifecycle evidence remains conservatively active even on the first managed call, successful closes clear wrapper trace/profiler ownership before ordered later successful rows can rebuild it, namespace-scoped `close --all` clears all matching managed/attached/page/ref/route/trace/recording ownership, and recording starts after close are rejected before spawn
|
|
182
186
|
- keep active recording destination reservations separate from the bounded metadata-only artifact manifest. The same journal retains native recording IDs and start windows; native receipts, including failures, flow through existing artifact metadata and verification. A stop timeout/no-recording result gets one two-second native `session info` query matching namespace/session, ID/path or effective batch start window. Recovery needs terminal native encoder success plus a matching verified file; filesystem presence alone is insufficient, old same-path receipts cannot verify a new take, and freshness starts at capture rather than status-query time. Original attempts and unrelated batch failures remain visible. No second ledger, recorder, sidecar or runtime ffprobe is added. The process-wide map is keyed by canonical namespace/session identity, rebuilt from append-only branch events, and retained for still-live process-owned recordings across branch switches. Shutdown/reload appends both terminal tombstones and still-live reservations onto the current branch so restart cannot resurrect a cross-branch close or lose a live-daemon reservation. One artifact lifecycle/output queue makes global destination preflight and reservation updates atomic across otherwise-concurrent caller-owned session queues. Every successful direct, ordered nested-batch, managed replacement, code, Electron, or shutdown close retires its exact identity at that lifecycle point; only the newest pending recording path remains authoritative across current transition replay (including same-timestamp restart rows), and recording starts after a nested close are rejected because upstream can falsely report success. Existing and dangling symlink ancestry, hardlink inode identity, full Unicode/platform case folding, and same-call `outputPath` comparison prevent alias reuse. One shared command-token projection mirrors upstream's full-argv global cleanup before artifact, recording, and presentation parsing; wait-download detection removes only the first timeout pair, follows upstream long/short mode precedence, and accepts both `--download` and `-d` wherever download mode wins; screenshot destinations use upstream's exact-flag, selector-prefix, case-sensitive extension, slash-path, and second-positional rules, while retaining the wrapper's intentional slash-bearing hidden-workspace path normalization. Recording path/URL consumers share a command-local reader that skips complete numeric `--fps` pairs, cursor/contact-sheet switches, and contact-sheet threshold values without rewriting argv or treating them as outer globals; native owns rate, format and extra-argument validation. Current recording transitions are replayed directly; artifact manifests are not treated as reservation events
|
|
183
|
-
- keep process-owned cleanup registries for extension-managed sessions and wrapper-launched Electron records separate from the current branch-visible view; `session_tree` restore and wrapper-owned browser commands are serialized with managed-session work, while caller-owned explicit-session commands use local queues and shared execution locks keyed to actual socket context and effective canonical namespace/session across prepare helpers (explicit namespace argv overrides inherited `AGENT_BROWSER_NAMESPACE`, including an explicit empty default) and main execution. macOS and Windows additionally normalize and case-fold namespace and session components to match case-insensitive daemon identity. Different caller-owned identities remain concurrent, except namespace-scoped `close --all` drains and exclusively barriers managed plus matching caller-owned work before clearing global namespace state; nested helpers never re-enter the outer queue, policy/route/artifact deltas merge across unrelated managed-state commits, and a separate branch-restore generation guard prevents stale completions from overwriting newer branch-visible state;
|
|
187
|
+
- keep process-owned cleanup registries for extension-managed sessions and wrapper-launched Electron records separate from the current branch-visible view; `session_tree` restore and wrapper-owned browser commands are serialized with managed-session work, while caller-owned explicit-session commands use local queues and shared execution locks keyed to actual socket context and effective canonical namespace/session across prepare helpers (explicit namespace argv overrides inherited `AGENT_BROWSER_NAMESPACE`, including an explicit empty default) and main execution. macOS and Windows additionally normalize and case-fold namespace and session components to match case-insensitive daemon identity. Different caller-owned identities remain concurrent, except namespace-scoped `close --all` drains and exclusively barriers managed plus matching caller-owned work before clearing global namespace state; nested helpers never re-enter the outer queue, policy/route/artifact deltas merge across unrelated managed-state commits, and a separate branch-restore generation guard prevents stale completions from overwriting newer branch-visible state; artifact-row changes use the existing artifact queue and journal order so replay retains concurrently completed entries. Branch switches still must not drop resources the current Pi process owns and must keep fresh-session allocation monotonic
|
|
184
188
|
- record successful `connect`, `--cdp`, enabled `--auto-connect`, environment-configured CDP/auto-connect, and wrapper Electron attachment identities in branch-visible state. First-use and later content-bearing calls live-check `get url` because attached targets can drift outside Pi. Caller config, file access, launch arguments, and environment pass through unchanged; only wrapper-injected compatibility launch arguments are omitted on active attachments. A terminal successful close removes the marker; a close followed by a later step whose lifecycle reports a browser launch preserves it, while a non-launching diagnostic such as `stream status` leaves the close terminal
|
|
185
189
|
- when a successful close targets the current extension-managed session, including an explicit `--session <current> close` or an `electron.cleanup` managed-session step, clear page/ref state, mark that session inactive, untrack cleanup ownership, and rotate the next default auto call to a fresh wrapper-generated session name rather than reusing the closed name
|
|
186
190
|
- on non-quit shutdown such as `/reload`, close off-branch owned managed sessions and off-branch owned Electron launches before clearing process-local ownership, but preserve the current branch-visible active managed session and Electron launch plus that launch's isolated `userDataDir` so reload continuity still works from the active transcript branch. First reuse of a restored Electron attachment checks the recorded namespace/session, live PID/profile presence, and exact browser WebSocket identity against current CDP metadata, then uses upstream `get cdp-url` to verify that the named daemon still targets that browser or one of its current pages. The metadata probe cannot commit daemon provenance merely by spawning; only a matching result permits the existing policy to record it. Ordinary calls, status, and probe share this path under the existing lock. Generic restore-disabled sessions, mismatched connections, quit, and off-branch cleanup are unchanged
|
|
@@ -192,11 +196,11 @@ Practical policy:
|
|
|
192
196
|
- once the wrapper observes tab-drift risk for a session (profile restore correction, overlapping stale opens, or restored session state), later active-tab commands verify the intended tab under the existing session queue before semantic/ref helpers and user commands. Targets retain native CDP `targetId` across transcript replay; exact identity distinguishes duplicate URL/title tabs, while older targets without an ID keep URL-based matching. A cold managed reopen replaces the old ID. Native selection runs only when the intended tab is not already active, because upstream selection clears refs and frame scope even on same-tab reselection. Missing targets, failed selection, and post-selection target mismatches fail before user commands. Caller argv/stdin and native `--pin-tab` / `--no-pin-tab` preferences remain unchanged. Local commands, live `get url`, explicit HTTP `read <url>` (including its flags), URL `a11y`/`vitals`/`web-vitals`, `diff url`, `window new`, URL-bearing recording commands, and explicit tab/navigation/`connect`/`state load` recovery do not require the old target; history back/forward/reload, `pushstate`, and page-content operations still do. The same classifier scans effective batch rows past non-page prefixes until a page dependency or explicit context change, without rewriting user rows or changing bail behavior. For `window new` and `diff url`, observe the resulting URL instead of retaining the old target or treating the requested second URL as redirect evidence. Fold only reached native batch rows when available, discard targets before failed navigation/context transitions and live-verify the final page, discard observations and refs from before window/diff transitions, and let later successful snapshots rebuild refs even when another batch row fails. Retain an observed blank destination after either command instead of recovering the old page; if no final target is observed, use the existing unknown-target state. Caller batch arguments, stdin and bail behavior stay unchanged. Successful batches containing navigation-observable actions use the same final live page/identity observation as direct actions, so same-address popup tabs replace the pre-batch target before later pinning. Terminal batch closes do not trigger a page probe, and pending WebMCP targets remain unknown. Routine same-session commands avoid `tab list` preflights
|
|
193
197
|
- for sessions with observed tab-drift risk, after a successful command on a known tab target, the wrapper may best-effort restore that same target again if restored/background tabs steal focus after the command returns; routine same-session commands skip focus correction, while observed pages without a returned native ID use `tab list` to establish current identity rather than inheriting an earlier tab's ID
|
|
194
198
|
- after successful tab creation, selection, or close, including dispatched native batch rows, read the now-active URL and fresh non-blank title—even when two tabs share a URL—before updating per-session page state; retain the deliberate blank target instead of treating it as accidental drift
|
|
195
|
-
- keep a per-session `refSnapshot` aligned with the last successful `snapshot` (including refs merged from a successful `batch` by taking the last successful `snapshot` step in batch result order): restore
|
|
199
|
+
- keep a per-session `refSnapshot` aligned with the last successful `snapshot` (including refs merged from a successful `batch` by taking the last successful `snapshot` step in batch result order): restore its once-stored ancestral definition when reloading, resuming, or moving to a different Pi session-tree branch, retain full membership and accessible metadata from that capture for wrapper-side diagnostics, drop it on successful close commands (`close`, `quit`, or `exit`), replace it with a persisted `page-transition` invalidation after any upstream-executed `record start` attempt (direct or batch; older supported natives can swap the page before their already-active check, so failed starts count; the warning and persisted summary explicitly describe conservative ref invalidation, not an observed page change), a `record restart` with a URL operand, `window new`, `diff url`, or WebMCP `invoke` / `result` / `cancel` (these page-provided tools can mutate, rerender, or navigate; when a spawned `batch` yields no parseable result rows, for example after a wrapper timeout, planned transition steps still record the invalidation), or after a failed transition command (`eval`, `back`, `forward`, `reload`, `connect`, `state load`, `tab` selection), including reached batch transitions and failed batch `open` rows whose live URL re-verification probe observed the page (a failed transition can still have mutated or replaced the document before throwing, so keeping the verified URL must not keep the prior refs; transcript replay preserves the persisted invalidation summary), and refuse page-scoped `@e…` argv before spawn when the active tab URL no longer matches the snapshot URL, when a ref id was never in that snapshot, when the snapshot state is invalidated, or when a `batch` step would reuse `@e…` on a guarded getter or mutation step after an earlier invalidating step (including `record start`, URL-bearing `record restart`, and WebMCP `invoke` / `result` / `cancel`) without a later `snapshot` step in the same plan; batch steps come from the source upstream actually executes (raw batch argument strings exclusively when any exist — filtering only the exact `--bail` token like upstream — stdin only otherwise, via `getUpstreamEffectiveBatchSteps` in `extensions/agent-browser/lib/orchestration/batch-stdin.ts`); tab recovery (which leaves user argv/stdin and continue-on-error control flow unchanged), artifact/recording preflight, batch screenshot path preparation (parent directories are created for effective raw rows too, without rewriting raw strings), and stale-ref echo args use that same selection so pinning and preflights cannot act on upstream-ignored stdin, while the pre-spawn state-policy validator deliberately keeps scanning parseable stdin alongside argv as a fail-closed content superset and treats stdin parse failures as fatal only when upstream would actually read stdin (its raw-token filter also uses the exact `--bail` token only). Same-snapshot `fill @e…` rows are guarded but do not themselves set that invalidation latch, so ordinary form fills can precede a click/submit row in one batch—see [`TOOL_CONTRACT.md`](TOOL_CONTRACT.md#details) for the agent-visible contract and failure text; typed per-session tab/ref/pinning state lives in `extensions/agent-browser/lib/session-page-state.ts` and is updated from `extensions/agent-browser/index.ts` after each tool result
|
|
196
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
|
|
197
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
|
|
198
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
|
|
199
|
-
-
|
|
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
|
|
200
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
|
|
201
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
|
|
202
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)
|
|
@@ -205,15 +209,11 @@ Practical policy:
|
|
|
205
209
|
|
|
206
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.
|
|
207
211
|
|
|
208
|
-
###
|
|
209
|
-
|
|
210
|
-
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.
|
|
211
|
-
|
|
212
|
-
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
|
|
213
213
|
|
|
214
|
-
|
|
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.
|
|
215
215
|
|
|
216
|
-
|
|
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.
|
|
217
217
|
|
|
218
218
|
### Launch flags
|
|
219
219
|
|
|
@@ -267,7 +267,7 @@ Upstream restore-state persistence remains upstream-owned. The wrapper passes an
|
|
|
267
267
|
- clear missing-binary errors
|
|
268
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`
|
|
269
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`, 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).
|
|
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)
|
|
271
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))
|
|
272
272
|
- lightweight session convenience
|
|
273
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
|
|
@@ -230,14 +232,14 @@ Call `agent_browser_tools {}` for inventory, then enable required capabilities:
|
|
|
230
232
|
{ "enable": ["action", "qa", "electron", "source", "network"] }
|
|
231
233
|
```
|
|
232
234
|
|
|
233
|
-
On hosts supporting
|
|
235
|
+
On hosts supporting instruction groups, first enable `browser` with `discover_tools` to receive full browser and advanced guidance and reveal selected declarations. The extension registers its own group through `pi:instruction-groups`; this step changes neither callable permissions nor advanced-tool selection. Official Pi receives the same full guidance eagerly without this discovery step. The separate advanced loader below retains its activation semantics.
|
|
234
236
|
|
|
235
237
|
Activation is additive and uses native Pi selected-tool history. Omit CLI `--tools` for normal lazy activation, or include desired advanced tools in that explicit selection; excluded tools remain unavailable. The tools take flat input:
|
|
236
238
|
|
|
237
239
|
| Tool | Example |
|
|
238
240
|
| --- | --- |
|
|
239
241
|
| `agent_browser_action` | `{ "action": "select", "locator": "role", "role": "combobox", "name": "Flavor", "value": "chocolate" }` |
|
|
240
|
-
| `agent_browser_qa` | `{ "url": "https://example.com", "expectedText": "
|
|
242
|
+
| `agent_browser_qa` | `{ "url": "https://example.com", "expectedText": "Learn more" }` |
|
|
241
243
|
| `agent_browser_electron` | `{ "action": "list", "query": "code" }` |
|
|
242
244
|
| `agent_browser_source` | `{ "selector": "#save", "componentName": "SaveButton" }` |
|
|
243
245
|
| `agent_browser_network_source` | `{ "requestId": "req-1", "url": "/api/fail" }` |
|
|
@@ -402,10 +404,12 @@ For longer text, code can construct the rows and call native batch; keep the for
|
|
|
402
404
|
|
|
403
405
|
For quick smoke/QA checks, enable `qa` and call `agent_browser_qa` with flat fields. It clears enabled network/console buffers and snapshots any page-error residue after the unreliable upstream clear, then opens the target URL and gives immediate post-load console/page-error callbacks a bounded 150 ms settle, waits for page readiness, checks expected text/selector, then inspects fresh network requests, console messages, and page errors only if preceding assertions pass, and can capture an evidence screenshot. Successful reset rows are labeled as reset-scoped diagnostic output. If final page-error rows match a nonempty post-clear baseline, QA is non-pass with “page-error check could not be verified”: identical new errors can replace old rows in the native FIFO buffer without changing the result. Those matches are not proof of new application errors. Definitely new rows, including errors after an empty baseline, count separately as page errors; a clean final buffer can pass. Explicit `checkErrors: false` omits this check for a more limited smoke test. The preset compiles to `batch --bail` so a missing text/selector assertion fails crisply instead of letting slower diagnostics burn the wrapper watchdog. Expected text compiles to bounded visible-text `wait --fn … --timeout 5000` predicates after load so dense pages can pass on visible headings/copy without dumping `body` text; missing text reports a crisp QA failure. The readiness wait defaults to `loadState: "domcontentloaded"`; set `loadState` to `"load"` or `"networkidle"` only when that stricter state is useful and the site is not expected to keep background requests alive. QA network diagnostics classify failed requests by likely impact and list failed rows first in the network preview: actionable document/script/API-style failures fail the preset, while common low-impact browser icon misses such as `favicon.ico` are surfaced as warnings (`qaPreset.warnings`) so they do not fail an otherwise healthy page. Successful QA with no failed checks returns compact model-visible prose (page URL/title when known, checks run, optional screenshot verification) while keeping the full step matrix in `details.qaPreset` and `details.batchSteps`. Failed QA presets report `details.resultCategory: "failure"`, `failureCategory: "qa-failure"`, show compact failure reasons with the full matrix in `details.batchSteps`, and real Pi sessions treat the diagnostic as a failed tool result. Prose output also gets a model-visible result-category line including `Pi tool isError: true`; caller-requested `--json` output keeps the JSON string parseable and relies on the patched `isError` plus `details` fields.
|
|
404
406
|
|
|
407
|
+
For failed QA, inspect `details.qaPreset.failedChecks` separately from `notRunChecks`: a reported fail-fast error leaves later checks not run, rather than proving their assertions failed. Failure prose leads with the redacted cause and reports received execution coverage; missing timeout receipts mean an unknown executed count. Incomplete receipts without a reported failure are unverified, never proof of a pass or of unreached checks.
|
|
408
|
+
|
|
405
409
|
The same classification drives plain `network requests` presentation: when any row counts as failed (HTTP status ≥ 400, `failed: true`, or a string `error`), model-facing text starts with a line like `Network failure summary: 0 actionable, 1 benign low-impact (1 total).`, and each preview line can end with an impact tag such as `[benign: low-impact browser icon asset]` or `[actionable: document, script, API, or non-benign request failure]`. When safe request IDs are present, `details.nextActions` adds bounded read-only follow-ups such as `network request <id>`, `agent_browser_network_source` for actionable failed rows, `network requests --filter <path>`, `network requests --clear` before a repro, and `network har start`; prefer those payloads over rebuilding request-id commands from prose. For aggregate buffers, the wrapper accepts `network requests --current-page` / `--current-origin` to render only rows matching the active page origin, or `--current-url` for exact active document URL matching; it strips those wrapper-only flags before upstream spawn and reports counts in `details.networkRequestsPageFilter`. Filtered data receives the same credential redaction as ordinary network diagnostics before entering `details.data` or an `outputPath` export. If the wrapper has seen a prior `network route` in the same session, matching failed, pending, or CORS-looking fetch/XHR rows add `details.networkRouteDiagnostics` plus executable route-mock follow-ups (`inspect-routed-network-request` and `start-network-har-capture-for-route-mock`) so agents do not mistake an unfulfilled mock for a fulfilled mock; same-origin/CORS fixture retry guidance stays in visible prose. `network requests` also hides `data:image` screenshot/artifact noise from the compact preview by default while preserving raw rows in `details.data.requests`. Rules live in `classifyNetworkRequestFailure` / `summarizeNetworkFailures` in `extensions/agent-browser/lib/results/network.ts`; QA aggregation is `analyzeQaPresetResults` in `extensions/agent-browser/lib/input-modes/job.ts`.
|
|
406
410
|
|
|
407
411
|
```json
|
|
408
|
-
{ "url": "https://example.com", "expectedText": "
|
|
412
|
+
{ "url": "https://example.com", "expectedText": "Learn more", "screenshotPath": ".dogfood/qa-example.png" }
|
|
409
413
|
```
|
|
410
414
|
|
|
411
415
|
Optional `loadState`, `checkNetwork`, `checkConsole`, and `checkErrors` default to `"domcontentloaded"`, `true`, `true`, and `true` for URL-opening QA; set a check to `false` to skip that diagnostic. For `qa.attached`, the diagnostic checks default to `false` because upstream buffers may predate the current check; opt in with `checkNetwork`, `checkConsole`, or `checkErrors` when preserved-buffer failures are desired. Omit `expectedText` and `expectedSelector` when you only need load plus diagnostics.
|
|
@@ -457,7 +461,7 @@ Manual path for externally launched apps: if you started the Electron app yourse
|
|
|
457
461
|
|
|
458
462
|
A successful raw `connect` means the debug endpoint accepted the session, not that the app has an active ready page. Prefer `details.nextActions` when present: `verify-connected-session-url` performs the only page read allowed while the attached target is unverified, and `list-connected-session-tabs` runs session-scoped tab inspection. A verified HTTP(S)/app target clears the guard; otherwise navigate explicitly to a safe URL. After the read-only tab list, select or confirm the stable `t<N>` target, verify it with `get url`, and run `snapshot -i` explicitly before trusting refs. If a `snapshot -i` says `No active page`, the wrapper clears any prior refs for that session; follow `list-tabs-after-no-active-page`, select the stable `t<N>` surface, then use a condition wait or retry `snapshot -i` before trusting refs.
|
|
459
463
|
|
|
460
|
-
For current-session smoke checks after either path, use `qa.attached`; for compact state instead of separate title/url/focus/tab/snapshot calls, use `electron.probe`. `electron.probe.timeoutMs` bounds each underlying read subprocess; `electron.probe.launchId` ties the probe to a wrapper launch and can surface session or target mismatch guidance before you trust page refs. Electron status target reads and probe reads use the same daemon-policy lock and owned restore decision as ordinary managed commands. A probe reads and validates the live URL before title, focus, tab, or snapshot helpers. Electron `launch` snapshot/tabs handoff likewise validates the URL before tab/snapshot reads; handoff failure or cancellation closes the new managed session and host process/profile. Current-managed probe
|
|
464
|
+
For current-session smoke checks after either path, use `qa.attached`; for compact state instead of separate title/url/focus/tab/snapshot calls, use `electron.probe`. `electron.probe.timeoutMs` bounds each underlying read subprocess; `electron.probe.launchId` ties the probe to a wrapper launch and can surface session or target mismatch guidance before you trust page refs. Electron status target reads and probe reads use the same daemon-policy lock and owned restore decision as ordinary managed commands. A probe reads and validates the live URL before title, focus, tab, or snapshot helpers. Electron `launch` snapshot/tabs handoff likewise validates the URL before tab/snapshot reads; handoff failure or cancellation closes the new managed session and host process/profile. Current-managed probe events persist namespace, tab target and a once-stored ref definition so Pi reload/branch replay restores the same page identity after native-generation verification. For VS Code-style quick inputs, treat a successful `fill` as tentative: the wrapper may append `details.fillVerification` if `get value` still reads empty or different, and Electron `@e…` mutations can append `refresh-electron-refs-after-rerender` because same-URL UI rerenders commonly churn refs.
|
|
461
465
|
|
|
462
466
|
For local app debugging, enable `source`; `agent_browser_source` can gather candidate component/file locations for a visible element from selector DOM hints, React DevTools inspection, and a bounded workspace component-name search rooted at the captured execution directory (`maxWorkspaceFiles` defaults to 2000 and cannot exceed 5000; the scan records at most ten `workspace-search` candidates). With a `selector`, the wrapper runs `is visible` and, unless `includeDomHints` is `false`, `get html` so DOM data attributes and embedded source-like paths can become `dom-attribute` candidates. It reports evidence and confidence in `details.sourceLookup` instead of claiming a guaranteed source file. React hints require a session opened with `--enable react-devtools`. The `details.sourceLookup.status` field reads `unsupported` only when no candidates were collected **and** a `react` batch step failed (inspect errors, missing renderer, and similar); it reads `no-candidates` when the batch succeeded but nothing matched. If selector or workspace hints still yield candidates, `status` remains `candidates-found` even when React inspection failed. Unlike `qa`, the wrapper does not downgrade a **fully successful** upstream batch to `isError` solely because those statuses appear—though failed batch steps still produce normal tool errors. For wrapper-tracked packaged Electron sessions with no candidates, `details.sourceLookup.workspaceRoot` and optional `details.sourceLookup.electronContext` explain that the scan only covered the captured execution directory; installed app resources or `app.asar` bundles are outside that scan and are not unpacked. Those results may add `snapshot-electron-session`, `probe-electron-launch`, and `list-electron-tabs` next actions so you can inspect the live packaged app before deciding whether to change the workspace or app bundle.
|
|
463
467
|
|
|
@@ -547,11 +551,11 @@ Detailed receipt/live-browser fields and browser-independent native read/confirm
|
|
|
547
551
|
|
|
548
552
|
#### Artifact retention and dogfood-heavy QA runs
|
|
549
553
|
|
|
550
|
-
The wrapper
|
|
554
|
+
The wrapper's internal recent artifact view defaults to 100 metadata rows, configurable with `PI_AGENT_BROWSER_SESSION_ARTIFACT_MANIFEST_MAX_ENTRIES=<count>`. That cap bounds the current view, not append-only history. Journal events store row changes; `details.artifactManifest` contains changed or referenced receipts for this invocation rather than the unchanged history. Complete reference captures are likewise defined once, with small reuse/invalidation events. See [browser session storage and stopped-copy conversion](SESSION_STORAGE.md).
|
|
551
555
|
|
|
552
|
-
This
|
|
556
|
+
This cap controls the internal recent view and summaries such as `Session artifacts: 42 live, 0 evicted (42/100 recent)`. It does not delete explicit files that upstream saved to paths you chose, such as screenshots, PDFs, downloads, traces, HAR files, or WebM recordings.
|
|
553
557
|
|
|
554
|
-
Browser close commands (`close`, `quit`, or `exit`) are also not file cleanup.
|
|
558
|
+
Browser close commands (`close`, `quit`, or `exit`) are also not file cleanup. When the internal recent view contains explicit artifacts, a successful close command appends a compact `Artifact lifecycle` note and reports `details.artifactCleanup` with the current retention summary and the same host-owned cleanup `note` as the contract (`extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts`, `getArtifactCleanupGuidance`). Up to ten distinct user-chosen paths that still exist on disk appear in `explicitArtifactPaths` when matching `explicit-path` manifest rows exist in the recent window; deleted/stale paths are skipped. Otherwise that array is empty and the visible text stays compact while the structured detail still reminds you that close commands do not delete saved files. Delete any paths you care about with host file tools after inspection; the native browser tool intentionally does not remove arbitrary user-chosen filesystem paths.
|
|
555
559
|
|
|
556
560
|
Oversized snapshots and oversized generic outputs are different: when a persisted pi session is available, their wrapper-managed spill files are stored under the private session artifact directory and are governed by the byte budget `PI_AGENT_BROWSER_SESSION_ARTIFACT_MAX_BYTES` (default 32 MiB). Raise that byte budget for longer retention, or set `PI_AGENT_BROWSER_SESSION_ARTIFACT_MAX_BYTES=0` to disable automatic eviction of persistent spill files. Zero leaves existing files in place as new spills are written; it does not recover files already evicted or change temporary subprocess spill cleanup.
|
|
557
561
|
|
|
@@ -908,7 +912,7 @@ When these commands are invoked through the native `agent_browser` tool, structu
|
|
|
908
912
|
- project-local: `.pi/config/pi-agent-browser-native/config.json`
|
|
909
913
|
- explicit override: `PI_AGENT_BROWSER_CONFIG=/path/to/config.json`
|
|
910
914
|
|
|
911
|
-
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.
|
|
915
|
+
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`.
|
|
912
916
|
|
|
913
917
|
`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`:
|
|
914
918
|
|