@underactive/pi-topping-web-tools 0.2.8 → 0.2.9
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 +18 -0
- package/README.md +54 -2
- package/package.json +4 -4
- package/src/fetch-markdown.ts +3 -1
- package/src/pdf-extract.ts +3 -1
- package/src/tool-exposure.ts +137 -0
- package/src/web-browser/index.ts +9 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.2.9] - 2026-10-16
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Optional `toolExposure` setting (`direct` default, or `codemode` / `deferred`) in `~/.pi/agent/pi-topping-web-tools/settings.json`. All three tools share the `pi-topping-web-tools` namespace. At each session start, a non-`direct` mode activates the loader it needs with `pi.setActiveTools()`: `codemode` for `codemode`, and `tool_search` (or `codemode` when `tool_search` is not registered) for `deferred`. `defaultTools` needs no `+codemode` / `+tool_search` entry. When `--tools` or `--exclude-tools` leaves the loader out, the extension notifies once instead.
|
|
10
|
+
- README settings profiles for default direct tools, codemode-minimal (`inlineBudget: 0`), and deferred + `tool_search`. Clarified that a large `inlineBudget` moves declarations into the `codemode` description instead of shrinking them.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Tested against pi 1.2.0. No break: the 1.2.0 breaking changes (`before_agent_start` for `triggerTurn` runs, busy session during run start, theme schema validation, `pi mcp list --json` shape) touch APIs and resources this package does not use.
|
|
15
|
+
- Bumped the pinned `@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent`, and `@earendil-works/pi-tui` devDependencies from 1.1.0 to 1.2.0.
|
|
16
|
+
- Tested against pi 1.3.0. No break: the extension-facing declaration surface (`ExtensionAPI`, `ToolDefinition`, `ExtensionContext`, `ExtensionUIContext`) is unchanged, pi-ai only adds optional `ToolCall.argumentsError`, and pi-tui is unchanged. Provider, `/share`, and model-switching changes do not reach these tools.
|
|
17
|
+
- Bumped the pinned `@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent`, and `@earendil-works/pi-tui` devDependencies from 1.2.0 to 1.3.0.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- `web_browser`: the `mode`, `state`, `cookies[].sameSite`, and `dialogAction` parameter schemas now pass `as const` arrays to `StringEnum`, so their inferred types are the literal unions instead of `string`. This fixes four `tsc` errors; runtime validation is unchanged.
|
|
22
|
+
|
|
5
23
|
## [0.2.8] - 2026-10-12
|
|
6
24
|
|
|
7
25
|
### Added
|
package/README.md
CHANGED
|
@@ -54,16 +54,68 @@ Start with `fetch_markdown` for efficient static content; use `web_browser` when
|
|
|
54
54
|
| `pdf_extract` | Extract the text layer from a remote or local PDF, with page markers |
|
|
55
55
|
| `web_browser` | Headless Chromium automation — navigate, click, type, screenshot, evaluate, logs, cookies |
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
By default all three tools use `direct` exposure. Pi activates those on registration (`defaultActive` defaults to true), so a default install needs no extra `defaultTools` entry.
|
|
58
|
+
|
|
59
|
+
### Tool exposure (optional)
|
|
60
|
+
|
|
61
|
+
A single setting controls how the model reaches all three tools. It does not change host allowlists, permission prompts, or `--exclude-tools` / `defaultTools` filtering. Set it in `~/.pi/agent/pi-topping-web-tools/settings.json` (no TUI control):
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"toolExposure": "direct"
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
| `toolExposure` | Tool `exposure` | Loader the extension activates |
|
|
70
|
+
|----------------|-----------------|--------------------------------|
|
|
71
|
+
| `direct` (default) | `direct`, active on register | None |
|
|
72
|
+
| `codemode` | `codemode` | `codemode` |
|
|
73
|
+
| `deferred` | `deferred` | `tool_search`, or `codemode` when `tool_search` is not available |
|
|
74
|
+
|
|
75
|
+
All three tools share the `pi-topping-web-tools` namespace so `searchTools()` and `describeNamespace("pi-topping-web-tools")` can find them when they are not listed inline.
|
|
76
|
+
|
|
77
|
+
Needs pi ≥0.99.0 for `codemode` / `tool_search` / `exposure`. Screenshot inspection from a script needs pi ≥1.0.4 (`toFile=true`, then `tools.read` and `image()`).
|
|
78
|
+
|
|
79
|
+
At every session start (startup, reload, new, resume, and fork), the extension activates the loader with `pi.setActiveTools()`, as Pi's MCP support does for MCP servers. You do not need `+codemode` or `+tool_search` in `defaultTools`. The loader is a session-wide Pi tool, so the model can also use it for the other tools in the session, not only for these three. `--tools` and `--exclude-tools` unregister a loader they leave out. The extension does not activate that loader. It notifies once at session start instead, because the model cannot reach the tools.
|
|
80
|
+
|
|
81
|
+
`codemode.mode: "only"` is a session-wide Pi setting. While it is on, the model must call bash/read/edit/write through `codemode` as well, not only these web tools. `--tools` still applies, for example `pi --tools +codemode,-web_browser`.
|
|
58
82
|
|
|
59
83
|
### Enabling and disabling tools
|
|
60
84
|
|
|
61
85
|
- To hide a tool for a session, name it in `--exclude-tools` (or start with `--no-tools` and re-add what you want with `--tools`).
|
|
62
86
|
- To change the startup set in your `settings.json`, use `defaultTools`. Bare names **replace** pi's defaults, so write `"+fetch_markdown"` to add a tool without dropping `read`, `bash`, `edit`, and `write`. On pi 0.99.2+, `/reload` activates tools **newly added** to `defaultTools` without a restart. The reverse does not follow: a tool **removed** from `defaultTools` stays active until you restart, and a tool you turned off during the session stays off. `--tools`, `--no-tools`, and `--no-builtin-tools` still override the setting, on reload as well.
|
|
63
87
|
|
|
88
|
+
### Settings profiles for smaller prompts
|
|
89
|
+
|
|
90
|
+
These snippets go in `~/.pi/agent/settings.json`. They are optional. Default installs can omit them.
|
|
91
|
+
|
|
92
|
+
**Default (unchanged).** Direct tools. No codemode. Full tool schemas stay in the prompt.
|
|
93
|
+
|
|
94
|
+
**Codemode-minimal.** Hide direct declarations and keep the `codemode` description small. `inlineBudget: 0` lists namespaces only. Scripts find tools with `searchTools()` / `describeTool()` / `describeNamespace()`. `inlineBudget: 4096` (or more) instead copies the full seven-tool catalog into the `codemode` description. That **moves** the schemas; it does not shrink them. Use `0` (or a small budget such as `512` for `fetch_markdown` / `pdf_extract` only) when the goal is fewer prompt tokens.
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"defaultTools": ["+codemode"],
|
|
99
|
+
"codemode.mode": "only",
|
|
100
|
+
"codemode.inlineBudget": 0
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
With `toolExposure` still `direct`, `mode: "only"` hides the active declarations while they stay callable from scripts. Set `"toolExposure": "codemode"` in the extension settings file if you also want these three tools off the active set unless a loader activates them.
|
|
105
|
+
|
|
106
|
+
**Deferred + tool_search.** Register the tools as `deferred` and let `tool_search` load a schema on the next turn. The extension activates `tool_search`, so `~/.pi/agent/settings.json` needs no change.
|
|
107
|
+
|
|
108
|
+
`~/.pi/agent/pi-topping-web-tools/settings.json`:
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"toolExposure": "deferred"
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
64
116
|
### Codemode tool listing
|
|
65
117
|
|
|
66
|
-
With `codemode.mode: "only"`, pi hides the direct declarations of active tools, so the model calls them from `codemode` scripts, and the `codemode` description lists each callable tool instead, with its prompt guidelines on pi 1.0.4+. Listed tools share `codemode.inlineBudget`, which defaults to 3000 estimated tokens (characters / 4); tools that do not fit are left out and are reached with `searchTools()` or `describeTool()`. On pi 1.0.4+, the seven-tool catalog `fetch_markdown`, `pdf_extract`, `web_browser`, plus built-in `read`, `bash`, `edit`, and `write` needs about 3,700 tokens as of pi 1.1.0 — `web_browser` alone is about 1,600, the largest section — so the default budget drops `web_browser` from that list. Set the budget to 4096 or more if you use `only`
|
|
118
|
+
With `codemode.mode: "only"`, pi hides the direct declarations of active tools, so the model calls them from `codemode` scripts, and the `codemode` description lists each callable tool instead, with its prompt guidelines on pi 1.0.4+. Listed tools share `codemode.inlineBudget`, which defaults to 3000 estimated tokens (characters / 4); tools that do not fit are left out and are reached with `searchTools()` or `describeTool()`. `0` lists only namespaces. On pi 1.0.4+, the seven-tool catalog `fetch_markdown`, `pdf_extract`, `web_browser`, plus built-in `read`, `bash`, `edit`, and `write` needs about 3,700 tokens as of pi 1.1.0 — `web_browser` alone is about 1,600, the largest section — so the default budget drops `web_browser` from that list. Set the budget to 4096 or more if you use `only` and want every tool inline; real sessions that load more extensions need more. Use `0` when you want the smallest `codemode` description.
|
|
67
119
|
|
|
68
120
|
```json
|
|
69
121
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@underactive/pi-topping-web-tools",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "Pi extensions for the web: fetch_markdown (URL to markdown, cached, keyless), pdf_extract (PDF text extraction) and web_browser (headless Playwright automation), sharing a preapproved-host allowlist.",
|
|
5
5
|
"keywords": ["pi-package", "pi-extension"],
|
|
6
6
|
"license": "MIT",
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
"typebox": "*"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@earendil-works/pi-ai": "1.
|
|
57
|
-
"@earendil-works/pi-coding-agent": "1.
|
|
58
|
-
"@earendil-works/pi-tui": "1.
|
|
56
|
+
"@earendil-works/pi-ai": "1.3.0",
|
|
57
|
+
"@earendil-works/pi-coding-agent": "1.3.0",
|
|
58
|
+
"@earendil-works/pi-tui": "1.3.0",
|
|
59
59
|
"@types/node": "^24.0.0",
|
|
60
60
|
"@types/turndown": "^5.0.5",
|
|
61
61
|
"typebox": "^1.3.3",
|
package/src/fetch-markdown.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { getBrowserHeaders } from "./browser-headers.ts";
|
|
|
13
13
|
import { isPreapprovedHost, permissionKey } from "./permissions.ts";
|
|
14
14
|
import { requestHostPermission } from "./permission-prompt.ts";
|
|
15
15
|
import { BoundedPreview, clipField, resultText } from "./render-preview.ts";
|
|
16
|
+
import { attachExposureLoader, registerWebTool } from "./tool-exposure.ts";
|
|
16
17
|
import { MAX_URL_LENGTH, isLocalOrPrivateHost, upgradeHttpToHttps } from "./web-browser/permissions.ts";
|
|
17
18
|
|
|
18
19
|
const MAX_HTTP_CONTENT_LENGTH = 25 * 1024 * 1024;
|
|
@@ -582,7 +583,8 @@ To complete your request, I need to fetch content from the redirected URL. Pleas
|
|
|
582
583
|
export default function (pi: ExtensionAPI) {
|
|
583
584
|
const sessionPermissions = new Map<string, "allow" | "deny">();
|
|
584
585
|
|
|
585
|
-
pi
|
|
586
|
+
registerWebTool(pi, fetchMarkdownTool);
|
|
587
|
+
attachExposureLoader(pi);
|
|
586
588
|
|
|
587
589
|
pi.on("tool_call", async (event, ctx) => {
|
|
588
590
|
if (event.toolName !== "fetch_markdown") return undefined;
|
package/src/pdf-extract.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { combineSignals } from "./abort-utils.ts";
|
|
|
12
12
|
import { isInsideCwd, isPreapprovedHost, permissionKey } from "./permissions.ts";
|
|
13
13
|
import { requestHostPermission } from "./permission-prompt.ts";
|
|
14
14
|
import { BoundedPreview, clipField, resultText } from "./render-preview.ts";
|
|
15
|
+
import { attachExposureLoader, registerWebTool } from "./tool-exposure.ts";
|
|
15
16
|
import { getWithPermittedRedirects, isPermittedRedirect, sliceContent, validateURL } from "./fetch-markdown.ts";
|
|
16
17
|
import { upgradeHttpToHttps } from "./web-browser/permissions.ts";
|
|
17
18
|
|
|
@@ -428,7 +429,8 @@ Usage notes:
|
|
|
428
429
|
export default function (pi: ExtensionAPI) {
|
|
429
430
|
const sessionPermissions = new Map<string, "allow" | "deny">();
|
|
430
431
|
|
|
431
|
-
pi
|
|
432
|
+
registerWebTool(pi, pdfExtractTool);
|
|
433
|
+
attachExposureLoader(pi);
|
|
432
434
|
|
|
433
435
|
pi.on("tool_call", async (event, ctx) => {
|
|
434
436
|
if (event.toolName !== "pdf_extract") return undefined;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared ToolExposure + namespace for fetch_markdown, pdf_extract, and web_browser.
|
|
3
|
+
* Default remains Pi's direct registration. Other modes are opt-in via settings.json.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { getAgentDir, type ExtensionAPI, type ToolDefinition, type ToolExposure, type ToolNamespace } from "@earendil-works/pi-coding-agent";
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
|
|
10
|
+
export type WebToolsExposureMode = "direct" | "codemode" | "deferred";
|
|
11
|
+
|
|
12
|
+
export const WEB_TOOLS_NAMESPACE: ToolNamespace = {
|
|
13
|
+
name: "pi-topping-web-tools",
|
|
14
|
+
description: "Keyless fetch_markdown, pdf_extract, and headless web_browser.",
|
|
15
|
+
instructions: [
|
|
16
|
+
"Use fetch_markdown for public HTML documentation, READMEs, and articles when you want turndown markdown and a 15-minute cache.",
|
|
17
|
+
"Use pdf_extract for a remote or local PDF. fetch_markdown and web_fetch cannot read PDF text.",
|
|
18
|
+
"Use web_browser for JavaScript-rendered pages, local servers, screenshots, and interaction.",
|
|
19
|
+
"Host allowlist and permission prompts still apply. Exposure only changes how the model sees the tools, not what they may execute.",
|
|
20
|
+
"See the package README sections for fetch_markdown, pdf_extract, and web_browser.",
|
|
21
|
+
].join("\n"),
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const MAX_SETTINGS_BYTES = 64 * 1024;
|
|
25
|
+
|
|
26
|
+
export function settingsPath(): string {
|
|
27
|
+
return process.env.PI_WEB_TOOLS_SETTINGS_FILE ?? join(getAgentDir(), "pi-topping-web-tools", "settings.json");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function parseToolExposureMode(value: unknown): WebToolsExposureMode {
|
|
31
|
+
if (value === "codemode" || value === "deferred" || value === "direct") {
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
return "direct";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function loadToolExposureMode(path: string = settingsPath()): WebToolsExposureMode {
|
|
38
|
+
let raw: string;
|
|
39
|
+
try {
|
|
40
|
+
raw = readFileSync(path, "utf-8");
|
|
41
|
+
} catch {
|
|
42
|
+
return "direct";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (Buffer.byteLength(raw) > MAX_SETTINGS_BYTES) {
|
|
46
|
+
return "direct";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let parsed: unknown;
|
|
50
|
+
try {
|
|
51
|
+
parsed = JSON.parse(raw);
|
|
52
|
+
} catch {
|
|
53
|
+
return "direct";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
57
|
+
return "direct";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return parseToolExposureMode((parsed as { toolExposure?: unknown }).toolExposure);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function webToolPresentation(mode: WebToolsExposureMode = loadToolExposureMode()): {
|
|
64
|
+
exposure: ToolExposure;
|
|
65
|
+
namespace: ToolNamespace;
|
|
66
|
+
} {
|
|
67
|
+
return {
|
|
68
|
+
exposure: mode,
|
|
69
|
+
namespace: WEB_TOOLS_NAMESPACE,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function registerWebTool(pi: ExtensionAPI, tool: ToolDefinition): void {
|
|
74
|
+
pi.registerTool({ ...tool, ...webToolPresentation() });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let didNotifyPrerequisites = false;
|
|
78
|
+
|
|
79
|
+
export function _resetExposureNoticeForTests(): void {
|
|
80
|
+
didNotifyPrerequisites = false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Loader tools that reach tools with this exposure, in the order to activate them. */
|
|
84
|
+
function exposureLoaders(mode: WebToolsExposureMode): readonly string[] {
|
|
85
|
+
if (mode === "codemode") return ["codemode"];
|
|
86
|
+
if (mode === "deferred") return ["tool_search", "codemode"];
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The registered loader tool to activate so the model can reach the web tools, or undefined when a
|
|
92
|
+
* loader is already active or none is registered. `--tools` and `--exclude-tools` unregister tools,
|
|
93
|
+
* so an excluded loader is never activated.
|
|
94
|
+
*/
|
|
95
|
+
export function exposureLoaderToActivate(
|
|
96
|
+
mode: WebToolsExposureMode,
|
|
97
|
+
activeTools: readonly string[],
|
|
98
|
+
registeredTools: readonly string[],
|
|
99
|
+
): string | undefined {
|
|
100
|
+
const loaders = exposureLoaders(mode);
|
|
101
|
+
if (loaders.some((name) => activeTools.includes(name))) return undefined;
|
|
102
|
+
return loaders.find((name) => registeredTools.includes(name));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function exposurePrerequisiteNotice(
|
|
106
|
+
mode: WebToolsExposureMode,
|
|
107
|
+
activeTools: readonly string[],
|
|
108
|
+
): string | undefined {
|
|
109
|
+
if (mode === "direct") return undefined;
|
|
110
|
+
if (exposureLoaders(mode).some((name) => activeTools.includes(name))) return undefined;
|
|
111
|
+
if (mode === "codemode") {
|
|
112
|
+
return 'pi-topping-web-tools toolExposure is "codemode", but the codemode tool is not available, so the model cannot reach the tools. Check --tools and --exclude-tools, or set toolExposure to "direct".';
|
|
113
|
+
}
|
|
114
|
+
return 'pi-topping-web-tools toolExposure is "deferred", but neither tool_search nor codemode is available, so the model cannot reach the tools. Check --tools and --exclude-tools, or set toolExposure to "direct".';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* On every session start, activate the loader tool the configured exposure needs, as Pi's MCP support
|
|
119
|
+
* does for MCP servers. Notify once when no loader can be activated.
|
|
120
|
+
*/
|
|
121
|
+
export function attachExposureLoader(pi: ExtensionAPI): void {
|
|
122
|
+
pi.on("session_start", (_event, ctx) => {
|
|
123
|
+
const mode = loadToolExposureMode();
|
|
124
|
+
if (mode === "direct") return;
|
|
125
|
+
if (typeof pi.getActiveTools !== "function") return;
|
|
126
|
+
const active = pi.getActiveTools();
|
|
127
|
+
const loader = exposureLoaderToActivate(mode, active, pi.getAllTools().map((tool) => tool.name));
|
|
128
|
+
if (loader) pi.setActiveTools([...active, loader]);
|
|
129
|
+
if (didNotifyPrerequisites) return;
|
|
130
|
+
const message = exposurePrerequisiteNotice(mode, pi.getActiveTools());
|
|
131
|
+
if (!message) return;
|
|
132
|
+
didNotifyPrerequisites = true;
|
|
133
|
+
if (ctx.hasUI) {
|
|
134
|
+
ctx.ui.notify(message, "warning");
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
package/src/web-browser/index.ts
CHANGED
|
@@ -34,6 +34,7 @@ import { listGrants } from "../permission-store.ts";
|
|
|
34
34
|
import { isInsideCwd, permissionKey } from "../permissions.ts";
|
|
35
35
|
import { suppressNotifications } from "../env.ts";
|
|
36
36
|
import { publishBrowserState, registerBrowserFeeds } from "./feeds.ts";
|
|
37
|
+
import { attachExposureLoader, webToolPresentation } from "../tool-exposure.ts";
|
|
37
38
|
|
|
38
39
|
const ACTIONS = [
|
|
39
40
|
"navigate",
|
|
@@ -228,6 +229,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
228
229
|
return manager;
|
|
229
230
|
};
|
|
230
231
|
registerBrowserFeeds(pi);
|
|
232
|
+
attachExposureLoader(pi);
|
|
231
233
|
|
|
232
234
|
pi.on("session_start", async (_event, ctx) => {
|
|
233
235
|
const info = await getConfiguredBrowserManager().getPageInfoAsync();
|
|
@@ -322,9 +324,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
322
324
|
},
|
|
323
325
|
});
|
|
324
326
|
|
|
327
|
+
const presentation = webToolPresentation();
|
|
325
328
|
pi.registerTool({
|
|
326
329
|
name: "web_browser",
|
|
327
330
|
label: "Web Browser",
|
|
331
|
+
exposure: presentation.exposure,
|
|
332
|
+
namespace: presentation.namespace,
|
|
328
333
|
promptSnippet:
|
|
329
334
|
"Automate a headless browser to navigate sites, click/type, capture screenshots, evaluate JS, and read console logs.",
|
|
330
335
|
promptGuidelines: [
|
|
@@ -389,7 +394,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
389
394
|
Type.Number({ description: "Tab index to switch to, from list_tabs (action: switch_tab)" }),
|
|
390
395
|
),
|
|
391
396
|
dialogAction: Type.Optional(
|
|
392
|
-
StringEnum(["accept", "dismiss"], {
|
|
397
|
+
StringEnum(["accept", "dismiss"] as const, {
|
|
393
398
|
description:
|
|
394
399
|
"How to resolve future JS dialogs (alert/confirm/prompt) for the rest of the session (action: set_dialog_behavior, default dismiss)",
|
|
395
400
|
}),
|
|
@@ -418,13 +423,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
418
423
|
Type.Number({ description: "Viewport height in pixels (action: set_viewport)" }),
|
|
419
424
|
),
|
|
420
425
|
mode: Type.Optional(
|
|
421
|
-
StringEnum(["fill", "press"], {
|
|
426
|
+
StringEnum(["fill", "press"] as const, {
|
|
422
427
|
description:
|
|
423
428
|
"Typing mode (action: type, default fill). fill clears the field first; press types key-by-key and appends to existing input.",
|
|
424
429
|
}),
|
|
425
430
|
),
|
|
426
431
|
state: Type.Optional(
|
|
427
|
-
StringEnum(["visible", "hidden", "attached", "detached"], {
|
|
432
|
+
StringEnum(["visible", "hidden", "attached", "detached"] as const, {
|
|
428
433
|
description: "Wait state (action: wait_for, default visible)",
|
|
429
434
|
}),
|
|
430
435
|
),
|
|
@@ -453,7 +458,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
453
458
|
path: Type.Optional(Type.String()),
|
|
454
459
|
httpOnly: Type.Optional(Type.Boolean()),
|
|
455
460
|
secure: Type.Optional(Type.Boolean()),
|
|
456
|
-
sameSite: Type.Optional(StringEnum(["Strict", "Lax", "None"])),
|
|
461
|
+
sameSite: Type.Optional(StringEnum(["Strict", "Lax", "None"] as const)),
|
|
457
462
|
}),
|
|
458
463
|
{ description: "Cookies to set (action: set_cookies); each needs url or domain+path" },
|
|
459
464
|
),
|