@yuzc-001/grasp 0.6.6

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.
Files changed (78) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +327 -0
  3. package/README.zh-CN.md +324 -0
  4. package/examples/README.md +31 -0
  5. package/examples/claude-desktop.json +8 -0
  6. package/examples/codex-config.toml +4 -0
  7. package/grasp.skill +0 -0
  8. package/index.js +87 -0
  9. package/package.json +48 -0
  10. package/scripts/grasp_openclaw_ctl.sh +122 -0
  11. package/scripts/run-search-benchmark.mjs +287 -0
  12. package/scripts/update-star-history.mjs +274 -0
  13. package/skill/SKILL.md +61 -0
  14. package/skill/references/tools.md +306 -0
  15. package/src/cli/auto-configure.js +116 -0
  16. package/src/cli/cmd-connect.js +148 -0
  17. package/src/cli/cmd-explain.js +42 -0
  18. package/src/cli/cmd-logs.js +55 -0
  19. package/src/cli/cmd-status.js +119 -0
  20. package/src/cli/config.js +27 -0
  21. package/src/cli/detect-chrome.js +58 -0
  22. package/src/grasp/handoff/events.js +67 -0
  23. package/src/grasp/handoff/persist.js +48 -0
  24. package/src/grasp/handoff/state.js +28 -0
  25. package/src/grasp/page/capture.js +34 -0
  26. package/src/grasp/page/state.js +273 -0
  27. package/src/grasp/verify/evidence.js +40 -0
  28. package/src/grasp/verify/pipeline.js +52 -0
  29. package/src/layer1-bridge/chrome.js +416 -0
  30. package/src/layer1-bridge/webmcp.js +143 -0
  31. package/src/layer2-perception/hints.js +284 -0
  32. package/src/layer3-action/actions.js +400 -0
  33. package/src/runtime/browser-instance.js +65 -0
  34. package/src/runtime/truth/model.js +94 -0
  35. package/src/runtime/truth/snapshot.js +51 -0
  36. package/src/server/affordances.js +47 -0
  37. package/src/server/audit.js +122 -0
  38. package/src/server/boss-fast-path.js +164 -0
  39. package/src/server/boundary-guard.js +53 -0
  40. package/src/server/content.js +97 -0
  41. package/src/server/continuity.js +256 -0
  42. package/src/server/engine-selection.js +29 -0
  43. package/src/server/entry-orchestrator.js +115 -0
  44. package/src/server/error-codes.js +7 -0
  45. package/src/server/explain-share-card.js +113 -0
  46. package/src/server/fast-path-router.js +134 -0
  47. package/src/server/form-runtime.js +602 -0
  48. package/src/server/form-tasks.js +254 -0
  49. package/src/server/gateway-response.js +62 -0
  50. package/src/server/index.js +22 -0
  51. package/src/server/observe.js +52 -0
  52. package/src/server/page-projection.js +31 -0
  53. package/src/server/page-state.js +27 -0
  54. package/src/server/postconditions.js +128 -0
  55. package/src/server/prompt-assembly.js +148 -0
  56. package/src/server/responses.js +44 -0
  57. package/src/server/route-boundary.js +174 -0
  58. package/src/server/route-policy.js +168 -0
  59. package/src/server/runtime-confirmation.js +87 -0
  60. package/src/server/runtime-status.js +7 -0
  61. package/src/server/share-artifacts.js +284 -0
  62. package/src/server/state.js +132 -0
  63. package/src/server/structured-extraction.js +131 -0
  64. package/src/server/surface-prompts.js +166 -0
  65. package/src/server/task-frame.js +11 -0
  66. package/src/server/tasks/search-task.js +321 -0
  67. package/src/server/tools.actions.js +1361 -0
  68. package/src/server/tools.form.js +526 -0
  69. package/src/server/tools.gateway.js +757 -0
  70. package/src/server/tools.handoff.js +210 -0
  71. package/src/server/tools.js +20 -0
  72. package/src/server/tools.legacy.js +983 -0
  73. package/src/server/tools.strategy.js +250 -0
  74. package/src/server/tools.task-surface.js +66 -0
  75. package/src/server/tools.workspace.js +873 -0
  76. package/src/server/workspace-runtime.js +1138 -0
  77. package/src/server/workspace-tasks.js +735 -0
  78. package/start-chrome.bat +84 -0
@@ -0,0 +1,274 @@
1
+ import { writeFile } from 'node:fs/promises';
2
+ import { fileURLToPath } from 'node:url';
3
+
4
+ const repository = process.env.STAR_HISTORY_REPO || process.env.GITHUB_REPOSITORY || 'Yuzc-001/grasp';
5
+ const [owner, repo] = repository.split('/');
6
+
7
+ if (!owner || !repo) {
8
+ throw new Error(`Invalid repository name: ${repository}`);
9
+ }
10
+
11
+ const outputPath = new URL('../star-history.svg', import.meta.url);
12
+ const token = process.env.GITHUB_TOKEN;
13
+
14
+ async function main() {
15
+ const starredAtValues = await fetchStargazers();
16
+ const series = buildSeries(starredAtValues);
17
+ const svg = buildSvg(series);
18
+
19
+ await writeFile(outputPath, svg, 'utf8');
20
+ console.log(`Updated ${fileURLToPath(outputPath)} with ${series.at(-1)?.count ?? 0} stars`);
21
+ }
22
+
23
+ async function fetchStargazers() {
24
+ try {
25
+ return await fetchStargazersWithToken(token);
26
+ } catch (error) {
27
+ if (token && error?.status === 401) {
28
+ console.warn('GITHUB_TOKEN is invalid locally, retrying without authentication');
29
+ return fetchStargazersWithToken();
30
+ }
31
+
32
+ throw error;
33
+ }
34
+ }
35
+
36
+ async function fetchStargazersWithToken(authToken) {
37
+ const headers = {
38
+ Accept: 'application/vnd.github.v3.star+json',
39
+ 'User-Agent': 'grasp-star-history-updater',
40
+ 'X-GitHub-Api-Version': '2022-11-28',
41
+ };
42
+
43
+ if (authToken) {
44
+ headers.Authorization = `Bearer ${authToken}`;
45
+ }
46
+
47
+ const starredAtValues = [];
48
+
49
+ for (let page = 1; ; page += 1) {
50
+ const response = await fetch(
51
+ `https://api.github.com/repos/${owner}/${repo}/stargazers?per_page=100&page=${page}`,
52
+ { headers },
53
+ );
54
+
55
+ if (!response.ok) {
56
+ const body = await response.text();
57
+ const error = new Error(
58
+ `GitHub API request failed on page ${page}: ${response.status} ${response.statusText}\n${body}`,
59
+ );
60
+ error.status = response.status;
61
+ throw error;
62
+ }
63
+
64
+ const entries = await response.json();
65
+
66
+ if (!Array.isArray(entries) || entries.length === 0) {
67
+ break;
68
+ }
69
+
70
+ for (const entry of entries) {
71
+ if (entry?.starred_at) {
72
+ starredAtValues.push(entry.starred_at);
73
+ }
74
+ }
75
+
76
+ if (entries.length < 100) {
77
+ break;
78
+ }
79
+ }
80
+
81
+ return starredAtValues.sort((left, right) => Date.parse(left) - Date.parse(right));
82
+ }
83
+
84
+ function buildSeries(starredAtValues) {
85
+ const today = toUtcDay(new Date());
86
+
87
+ if (starredAtValues.length === 0) {
88
+ return [{ date: today, count: 0 }];
89
+ }
90
+
91
+ const dailyAdds = new Map();
92
+
93
+ for (const value of starredAtValues) {
94
+ const day = toUtcDay(value);
95
+ dailyAdds.set(day, (dailyAdds.get(day) || 0) + 1);
96
+ }
97
+
98
+ const startDay = [...dailyAdds.keys()].sort()[0];
99
+ const series = [];
100
+ let running = 0;
101
+
102
+ for (
103
+ let cursor = new Date(`${startDay}T00:00:00Z`);
104
+ cursor <= new Date(`${today}T00:00:00Z`);
105
+ cursor.setUTCDate(cursor.getUTCDate() + 1)
106
+ ) {
107
+ const day = toUtcDay(cursor);
108
+ running += dailyAdds.get(day) || 0;
109
+ series.push({ date: day, count: running });
110
+ }
111
+
112
+ return series;
113
+ }
114
+
115
+ function buildSvg(series) {
116
+ const width = 960;
117
+ const height = 540;
118
+ const padding = { top: 76, right: 34, bottom: 52, left: 76 };
119
+ const plotWidth = width - padding.left - padding.right;
120
+ const plotHeight = height - padding.top - padding.bottom;
121
+ const lastPoint = series.at(-1);
122
+ const updatedDay = toUtcDay(new Date());
123
+ const xDivisor = Math.max(series.length - 1, 1);
124
+ const { ceiling, ticks } = buildYTicks(lastPoint.count);
125
+
126
+ const scaleX = (index) => padding.left + (plotWidth * index) / xDivisor;
127
+ const scaleY = (value) => padding.top + plotHeight - (plotHeight * value) / ceiling;
128
+
129
+ const points = series.map((point, index) => ({
130
+ x: Number(scaleX(index).toFixed(2)),
131
+ y: Number(scaleY(point.count).toFixed(2)),
132
+ date: point.date,
133
+ count: point.count,
134
+ }));
135
+
136
+ const polylinePoints = points.map((point) => `${point.x},${point.y}`).join(' ');
137
+ const areaPoints = [
138
+ `${padding.left},${padding.top + plotHeight}`,
139
+ ...points.map((point) => `${point.x},${point.y}`),
140
+ `${padding.left + plotWidth},${padding.top + plotHeight}`,
141
+ ].join(' ');
142
+
143
+ const xTickIndices = buildTickIndices(series.length, 5);
144
+ const yGrid = ticks
145
+ .map((tick) => {
146
+ const y = scaleY(tick);
147
+ return [
148
+ `<line x1="${padding.left}" y1="${y}" x2="${padding.left + plotWidth}" y2="${y}" stroke="#D7E3F4" stroke-width="1" />`,
149
+ `<text x="${padding.left - 12}" y="${y + 4}" text-anchor="end" font-size="12" fill="#48617D">${formatNumber(tick)}</text>`,
150
+ ].join('');
151
+ })
152
+ .join('');
153
+
154
+ const xLabels = xTickIndices
155
+ .map((index) => {
156
+ const point = points[index];
157
+ return [
158
+ `<line x1="${point.x}" y1="${padding.top + plotHeight}" x2="${point.x}" y2="${padding.top + plotHeight + 6}" stroke="#B6C8DE" stroke-width="1" />`,
159
+ `<text x="${point.x}" y="${padding.top + plotHeight + 24}" text-anchor="middle" font-size="12" fill="#48617D">${escapeXml(formatDate(point.date))}</text>`,
160
+ ].join('');
161
+ })
162
+ .join('');
163
+
164
+ return `<?xml version="1.0" encoding="UTF-8"?>
165
+ <svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" role="img" aria-labelledby="title desc">
166
+ <title id="title">${escapeXml(repository)} star history</title>
167
+ <desc id="desc">${escapeXml(`Cumulative GitHub stars for ${repository} from ${series[0].date} to ${lastPoint.date}.`)}</desc>
168
+ <defs>
169
+ <linearGradient id="areaFill" x1="0" y1="0" x2="0" y2="1">
170
+ <stop offset="0%" stop-color="#38BDF8" stop-opacity="0.28" />
171
+ <stop offset="100%" stop-color="#38BDF8" stop-opacity="0.04" />
172
+ </linearGradient>
173
+ <linearGradient id="lineStroke" x1="0" y1="0" x2="1" y2="0">
174
+ <stop offset="0%" stop-color="#0EA5E9" />
175
+ <stop offset="100%" stop-color="#2563EB" />
176
+ </linearGradient>
177
+ </defs>
178
+
179
+ <rect x="0" y="0" width="${width}" height="${height}" rx="22" fill="#F8FBFF" />
180
+ <rect x="1.5" y="1.5" width="${width - 3}" height="${height - 3}" rx="20.5" fill="none" stroke="#D7E3F4" stroke-width="3" />
181
+
182
+ <text x="${padding.left}" y="42" font-size="26" font-weight="700" fill="#0F172A">Star History</text>
183
+ <text x="${padding.left}" y="64" font-size="13" fill="#48617D">${escapeXml(repository)}</text>
184
+ <text x="${width - padding.right}" y="42" text-anchor="end" font-size="24" font-weight="700" fill="#0F172A">${formatNumber(lastPoint.count)}</text>
185
+ <text x="${width - padding.right}" y="64" text-anchor="end" font-size="13" fill="#48617D">Updated ${escapeXml(updatedDay)} UTC</text>
186
+
187
+ ${yGrid}
188
+ <line x1="${padding.left}" y1="${padding.top + plotHeight}" x2="${padding.left + plotWidth}" y2="${padding.top + plotHeight}" stroke="#94AFCB" stroke-width="1.5" />
189
+ ${xLabels}
190
+
191
+ <polygon points="${areaPoints}" fill="url(#areaFill)" />
192
+ <polyline points="${polylinePoints}" fill="none" stroke="url(#lineStroke)" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" />
193
+ <circle cx="${points.at(-1).x}" cy="${points.at(-1).y}" r="6.5" fill="#2563EB" />
194
+ <circle cx="${points.at(-1).x}" cy="${points.at(-1).y}" r="11" fill="#2563EB" fill-opacity="0.16" />
195
+ </svg>
196
+ `;
197
+ }
198
+
199
+ function buildYTicks(maxValue) {
200
+ const step = niceStep(Math.max(maxValue, 1) / 4);
201
+ const ceiling = Math.max(step, Math.ceil(Math.max(maxValue, 1) / step) * step);
202
+ const ticks = [];
203
+
204
+ for (let value = 0; value <= ceiling; value += step) {
205
+ ticks.push(value);
206
+ }
207
+
208
+ return { ceiling, ticks };
209
+ }
210
+
211
+ function buildTickIndices(length, desiredCount) {
212
+ if (length === 1) {
213
+ return [0];
214
+ }
215
+
216
+ const count = Math.min(length, desiredCount);
217
+ const indices = new Set();
218
+
219
+ for (let index = 0; index < count; index += 1) {
220
+ indices.add(Math.round(((length - 1) * index) / (count - 1)));
221
+ }
222
+
223
+ return [...indices].sort((left, right) => left - right);
224
+ }
225
+
226
+ function niceStep(target) {
227
+ const safeTarget = Math.max(target, 1);
228
+ const power = 10 ** Math.floor(Math.log10(safeTarget));
229
+ const scaled = safeTarget / power;
230
+
231
+ if (scaled <= 1) {
232
+ return power;
233
+ }
234
+
235
+ if (scaled <= 2) {
236
+ return 2 * power;
237
+ }
238
+
239
+ if (scaled <= 5) {
240
+ return 5 * power;
241
+ }
242
+
243
+ return 10 * power;
244
+ }
245
+
246
+ function formatDate(day) {
247
+ return new Intl.DateTimeFormat('en-US', {
248
+ month: 'short',
249
+ year: 'numeric',
250
+ timeZone: 'UTC',
251
+ }).format(new Date(`${day}T00:00:00Z`));
252
+ }
253
+
254
+ function formatNumber(value) {
255
+ return new Intl.NumberFormat('en-US').format(value);
256
+ }
257
+
258
+ function toUtcDay(value) {
259
+ return new Date(value).toISOString().slice(0, 10);
260
+ }
261
+
262
+ function escapeXml(value) {
263
+ return String(value)
264
+ .replaceAll('&', '&amp;')
265
+ .replaceAll('<', '&lt;')
266
+ .replaceAll('>', '&gt;')
267
+ .replaceAll('"', '&quot;')
268
+ .replaceAll("'", '&apos;');
269
+ }
270
+
271
+ main().catch((error) => {
272
+ console.error(error);
273
+ process.exitCode = 1;
274
+ });
package/skill/SKILL.md ADDED
@@ -0,0 +1,61 @@
1
+ ---
2
+ name: grasp
3
+ description: Use when an agent needs the visible local Grasp browser runtime for multi-step web tasks or public-web extraction through one interface: confirm the runtime instance, enter URLs, inspect/extract/share page content, switch visible tabs, interact with live pages, fill forms, operate authenticated workspaces, capture screenshots, and recover through handoff after login or CAPTCHA checkpoints.
4
+ ---
5
+
6
+ # Grasp
7
+
8
+ ## When to use
9
+
10
+ - The task needs a real browser session, not a one-shot headless script
11
+ - The work depends on persistent login state, a visible browser window, or human handoff/recovery
12
+ - The agent needs one interface for page entry, extraction, share/export, forms, workspace actions, screenshots, and low-level browser control
13
+ - The agent must know which runtime instance it is acting on, or it needs to switch between user-visible tabs safely
14
+
15
+ ## Safe defaults
16
+
17
+ - Treat Grasp as the browser runtime surface. Use MCP tools for page actions instead of recreating interactions in shell scripts.
18
+ - Start with `get_status`. Before page-changing actions, prefer `confirm_runtime_instance(display="windowed")` or confirm the mode you actually expect.
19
+ - If a tool returns `INSTANCE_CONFIRMATION_REQUIRED`, confirm the instance and retry the same action.
20
+ - For first arrival to a URL, prefer `entry(url, intent)`. Use `navigate(url)` only when you intentionally want to move the current page directly.
21
+ - Prefer the high-level surfaces first: runtime loop, form tools, and workspace tools. Drop to hint map, tabs, cookies, dialogs, or `evaluate` only when the higher-level path is not enough.
22
+ - Prefer `list_visible_tabs` / `select_visible_tab` before raw tab primitives such as `get_tabs` or `switch_tab`.
23
+ - Refresh `get_hint_map` after navigation, a page-changing click, a visible DOM change, or scroll-loaded content. Old hint IDs are not safe to reuse after the page changes.
24
+ - Use the handoff flow when the task is blocked by login, CAPTCHA, checkpoints, or other human-only steps.
25
+ - If local CDP is unreachable, Grasp may auto-launch local Chrome or Edge now. If that still does not recover the runtime, ask the user to run `npx -y @yuzc-001/grasp`, `grasp connect`, or `start-chrome.bat` on Windows.
26
+
27
+ ## Recommended workflow
28
+
29
+ 1. `get_status`
30
+ 2. `confirm_runtime_instance` when the task needs a confirmed live instance
31
+ 3. `entry(url, intent)`
32
+ 4. `inspect`
33
+ 5. Follow the task surface that matches the job:
34
+ - `extract` / `extract_structured` / `extract_batch` / `share_page` / `continue`
35
+ - `form_inspect`
36
+ - `workspace_inspect`
37
+ 6. Use `explain_route` when the route choice needs explanation
38
+ 7. If blocked, use handoff and then resume with `continue`
39
+
40
+ ## Task surfaces
41
+
42
+ ### Public web and extraction
43
+
44
+ Use `entry(..., intent="extract" | "read")`, then stay on `inspect`, `extract`, `extract_structured`, `extract_batch`, `share_page`, `explain_share_card`, and `continue` as long as the runtime surface is enough.
45
+
46
+ ### Forms
47
+
48
+ Use `form_inspect -> fill_form / set_option / set_date -> verify_form -> safe_submit`.
49
+
50
+ ### Authenticated workspaces
51
+
52
+ Use `workspace_inspect -> select_live_item -> draft_action -> execute_action -> verify_outcome`.
53
+
54
+ ### Lower-level control
55
+
56
+ Only when the higher-level surface is not enough, use `get_hint_map`, `click`, `type`, `hover`, `scroll`, `scroll_into_view`, `screenshot`, `wait_for`, `go_back`, `go_forward`, `reload`, raw tab tools, cookies, dialogs, file upload, drag-and-drop, keyboard controls, and `evaluate`.
57
+
58
+ ## Additional resources
59
+
60
+ - Detailed tool selection, examples, troubleshooting, and compatibility notes: [references/tools.md](references/tools.md)
61
+ - Product framing for the runtime model: [Browser Runtime for Agents](../docs/product/browser-runtime-for-agents.md)
@@ -0,0 +1,306 @@
1
+ # Grasp Reference
2
+
3
+ ## 1. Bootstrap and instance confirmation
4
+
5
+ Recommended default order:
6
+
7
+ 1. `get_status`
8
+ 2. Check whether the runtime instance matches the browser session you expect
9
+ 3. Before page-changing actions, call `confirm_runtime_instance`
10
+
11
+ Typical confirmation flow:
12
+
13
+ ```text
14
+ get_status()
15
+ confirm_runtime_instance(display="windowed")
16
+ ```
17
+
18
+ If `get_status` or another tool reports that CDP is unreachable:
19
+
20
+ - Grasp may now auto-launch local Chrome or Edge when the endpoint is local
21
+ - If the runtime still does not recover, ask the user to run:
22
+ - `npx -y @yuzc-001/grasp`
23
+ - `grasp connect`
24
+ - `start-chrome.bat` on Windows
25
+
26
+ If tab identity is ambiguous:
27
+
28
+ - Use `list_visible_tabs`
29
+ - Then use `select_visible_tab`
30
+
31
+ Only drop to raw tab indices when you truly need them:
32
+
33
+ - `get_tabs`
34
+ - `switch_tab`
35
+ - `new_tab`
36
+ - `close_tab`
37
+
38
+ ## 2. Intent selection
39
+
40
+ | intent | When to use it |
41
+ |---|---|
42
+ | `read` | Read-only browsing without page-changing actions |
43
+ | `extract` | Public-web reading, summary, or structured extraction |
44
+ | `act` | General interaction such as clicking, typing, switching, or selecting |
45
+ | `submit` | Filling and submitting forms |
46
+ | `workspace` | Operating authenticated dashboards, inboxes, chat panes, or back-office surfaces |
47
+ | `collect` | Visiting multiple URLs and extracting the same fields repeatedly |
48
+
49
+ ## 3. Default workflows
50
+
51
+ ### 3.1 Runtime loop
52
+
53
+ ```text
54
+ get_status
55
+ confirm_runtime_instance (when live actions need confirmation)
56
+ entry(url, intent)
57
+ inspect
58
+ extract / extract_structured / extract_batch / share_page / continue
59
+ explain_route (when route choice matters)
60
+ ```
61
+
62
+ When a high-level tool responds, read the `Boundary: ...` block before picking the next action. The same response also carries structured `agent_boundary` metadata plus `agent_prompt`, which includes the assembled `system_prompt`, prompt segments, and chosen boundary/surface prompt packs for the current step. If a form or workspace tool is used on the wrong surface, expect a `BOUNDARY_MISMATCH` response and follow its recovery step instead of retrying blindly.
63
+
64
+ ### 3.2 Structured extraction
65
+
66
+ When the task is "turn this page into named fields", prefer:
67
+
68
+ ```text
69
+ entry(url, intent="extract")
70
+ inspect
71
+ extract_structured(fields=[...])
72
+ ```
73
+
74
+ For repeated extraction across multiple URLs:
75
+
76
+ ```text
77
+ extract_batch(urls=[...], fields=[...])
78
+ ```
79
+
80
+ ### 3.3 Share and export
81
+
82
+ When the result should be handed to a human as an artifact rather than just returning raw page text:
83
+
84
+ ```text
85
+ share_page(format="markdown" | "screenshot" | "pdf")
86
+ explain_share_card(width=640)
87
+ ```
88
+
89
+ ### 3.4 Forms
90
+
91
+ ```text
92
+ form_inspect
93
+ fill_form(values={...})
94
+ set_option(field="Country", value="Germany")
95
+ set_date(field="Start date", value="2026-04-01")
96
+ verify_form
97
+ safe_submit(mode="preview")
98
+ safe_submit(mode="confirm", confirmation="SUBMIT")
99
+ ```
100
+
101
+ ### 3.5 Workspaces
102
+
103
+ ```text
104
+ workspace_inspect
105
+ select_live_item(item="Conversation A")
106
+ draft_action(text="Hello, I have a question...")
107
+ execute_action(mode="preview")
108
+ execute_action(mode="confirm", confirmation="EXECUTE")
109
+ verify_outcome
110
+ ```
111
+
112
+ ### 3.6 Handoff and recovery
113
+
114
+ ```text
115
+ inspect or continue
116
+ request_handoff(reason="captcha_required", note="Need human verification")
117
+ mark_handoff_done()
118
+ resume_after_handoff(verify=true)
119
+ continue()
120
+ ```
121
+
122
+ ## 4. Tool selection guide
123
+
124
+ | Need | Preferred tools | Notes |
125
+ |---|---|---|
126
+ | First arrival to a URL | `entry(url, intent)` | Default entry point; do not start with `navigate` unless you intentionally want a direct page jump |
127
+ | Directly change the current page URL | `navigate(url)` | Lower-level navigation path |
128
+ | Inspect current runtime status | `inspect` | Returns route, page state, and continuation hints |
129
+ | Quick current-page summary | `get_page_summary` | Lightweight read |
130
+ | Extract a concise page result | `extract` | Stay on the runtime path |
131
+ | Extract named fields from one page | `extract_structured` | Returns structured output and exports |
132
+ | Extract the same fields from many URLs | `extract_batch` | Writes CSV and JSON artifacts, plus optional Markdown |
133
+ | Export something shareable | `share_page` | `markdown`, `screenshot`, or `pdf` |
134
+ | Explain how a shared artifact would look | `explain_share_card` | Useful before exporting |
135
+ | Explain why the runtime chose a route | `explain_route` | Good for gated or surprising route choices |
136
+ | Decide what should happen next without acting | `continue` | Best after inspection or recovery |
137
+ | Read visible tabs from the active runtime | `list_visible_tabs` / `select_visible_tab` | Prefer these over raw indices |
138
+ | Raw tab control | `get_tabs` / `switch_tab` / `new_tab` / `close_tab` | Use when index-level control is required |
139
+ | Inspect a form | `form_inspect` | Starting point for the form surface |
140
+ | Fill text-like form fields | `fill_form` | Uses a label-to-value map |
141
+ | Set selects, radios, and other option controls | `set_option` | Use after `form_inspect` when control type matters |
142
+ | Set date controls | `set_date` | Use ISO-like values |
143
+ | Re-check missing or risky form fields | `verify_form` | Best before submit |
144
+ | Preview or confirm form submission | `safe_submit` | `preview` first, `confirm` only when ready |
145
+ | Inspect an authenticated workspace | `workspace_inspect` | Starting point for the workspace surface |
146
+ | Switch active workspace item | `select_live_item` | Select by visible label |
147
+ | Draft text into the composer | `draft_action` | Writes without sending |
148
+ | Preview or execute a workspace send | `execute_action` | Use `preview` before `confirm` |
149
+ | Verify the workspace result after send | `verify_outcome` | Confirms what changed and what is next |
150
+ | Handle login or CAPTCHA checkpoints | `request_handoff` / `mark_handoff_done` / `resume_after_handoff` / `continue` | Keep continuity instead of restarting |
151
+ | Find interactable targets | `get_hint_map` | Refresh after page changes |
152
+ | Click, type, or hover | `click` / `type` / `hover` | Preferred low-level interactions |
153
+ | Scroll a known target into view | `scroll_into_view` | Better than guessing pixels |
154
+ | Scroll the page or a nested container | `scroll` | Supports `up`, `down`, `left`, `right`, and `hint_id` targeting |
155
+ | Move browser history or refresh the page | `go_back` / `go_forward` / `reload` | Useful after navigation detours or retry flows |
156
+ | Wait for text, hidden text, or URL changes | `wait_for` | Uses one condition at a time |
157
+ | Wait for DOM selector changes | `watch_element` | Use when you know a CSS selector |
158
+ | Visual verification | `screenshot` | Full page, element clip, or annotated viewport |
159
+ | Keyboard actions | `press_key`, `key_down`, `key_up` | Use `key_down` / `key_up` for combos |
160
+ | Checkbox or radio state | `check` | Idempotent checked/unchecked control |
161
+ | Double-click | `double_click` | Lower-level pointer action |
162
+ | Handle browser dialogs | `handle_dialog` | For alert, confirm, or prompt |
163
+ | Upload files | `upload_file` | Uses an absolute file path array |
164
+ | Drag and drop | `drag` | Lower-level pointer flow |
165
+ | Read browser console output | `get_console_logs` | Great for debugging live pages |
166
+ | Run custom page JavaScript | `evaluate` | Escape hatch; prefer specialized tools first |
167
+ | Read or mutate cookies | `get_cookies` / `set_cookie` / `clear_cookies` | Useful for debugging session state |
168
+
169
+ ## 5. Hint Map rules
170
+
171
+ - `B*`: buttons
172
+ - `I*`: inputs
173
+ - `L*`: links
174
+ - `S*`: selects
175
+
176
+ Old hint IDs may be invalid after:
177
+
178
+ - navigation
179
+ - a click that changes the page
180
+ - a visible DOM update
181
+ - a tab switch
182
+ - scroll-loaded content
183
+ - form submission or workspace state changes
184
+
185
+ Practical rule:
186
+
187
+ - Get a hint map before interaction
188
+ - Rebuild the hint map after any visible page change
189
+ - If you only need visual confirmation, prefer `screenshot` instead of forcing a new hint map
190
+
191
+ ## 6. Common scenarios
192
+
193
+ ### 6.1 Read a public page
194
+
195
+ ```text
196
+ entry("https://example.com/article", "extract")
197
+ inspect()
198
+ extract()
199
+ ```
200
+
201
+ ### 6.2 Act inside a logged-in page
202
+
203
+ ```text
204
+ get_status()
205
+ confirm_runtime_instance(display="windowed")
206
+ entry("https://app.example.com/dashboard", "act")
207
+ inspect()
208
+ screenshot(annotate=true)
209
+ click(hint_id="B3")
210
+ get_hint_map()
211
+ ```
212
+
213
+ ### 6.3 Fill a form
214
+
215
+ ```text
216
+ entry("https://example.com/apply", "submit")
217
+ form_inspect()
218
+ fill_form(values={"Full name":"Taylor Doe","Email":"taylor@example.com"})
219
+ set_option(field="Country", value="Germany")
220
+ set_date(field="Start date", value="2026-04-01")
221
+ verify_form()
222
+ safe_submit(mode="preview")
223
+ ```
224
+
225
+ ### 6.4 Operate a workspace
226
+
227
+ ```text
228
+ entry("https://app.example.com/inbox", "workspace")
229
+ workspace_inspect()
230
+ select_live_item(item="Priority thread")
231
+ draft_action(text="Hello, following up on this request...")
232
+ execute_action(mode="preview")
233
+ verify_outcome()
234
+ ```
235
+
236
+ ### 6.5 Recover after login or CAPTCHA
237
+
238
+ ```text
239
+ entry("https://protected-site.example", "act")
240
+ inspect()
241
+ request_handoff(reason="captcha_required", note="Need human verification")
242
+ mark_handoff_done()
243
+ resume_after_handoff(verify=true)
244
+ continue()
245
+ ```
246
+
247
+ ### 6.6 Work across multiple tabs
248
+
249
+ ```text
250
+ list_visible_tabs()
251
+ select_visible_tab(query="docs")
252
+ extract()
253
+ new_tab(url="https://docs.example.com")
254
+ get_tabs()
255
+ switch_tab(index=0)
256
+ close_tab(index=1)
257
+ ```
258
+
259
+ ### 6.7 Scroll inside a nested container
260
+
261
+ ```text
262
+ get_hint_map()
263
+ scroll_into_view(hint_id="L15")
264
+
265
+ # or step through the scrollable ancestor
266
+ scroll(direction="down", hint_id="L15", amount=200)
267
+ get_hint_map()
268
+ ```
269
+
270
+ ### 6.8 Recover after a navigation detour
271
+
272
+ ```text
273
+ go_back()
274
+ go_forward()
275
+ reload()
276
+ inspect()
277
+ ```
278
+
279
+ ## 7. Compatibility and legacy tools
280
+
281
+ These tools still exist for older flows or edge cases, but they are not the preferred path for new tasks:
282
+
283
+ | Tool | Use it only when |
284
+ |---|---|
285
+ | `confirm_click` | You are intentionally using the older safe-mode click flow and need to bypass its confirmation gate |
286
+ | `get_form_fields` | You are working with an older hint-first form workflow rather than the newer form surface |
287
+ | `get_logs` | You need the Grasp audit log rather than browser console output |
288
+ | `call_webmcp_tool` | The current page exposes a native WebMCP API and that lower-level integration is truly needed |
289
+
290
+ If you are not sure, prefer the route-aware runtime surface plus the current form/workspace tools described above.
291
+
292
+ ## 8. Troubleshooting
293
+
294
+ | Problem | What to do |
295
+ |---|---|
296
+ | `connected: false` or `CDP_UNREACHABLE` | Retry `get_status`; Grasp may auto-launch local Chrome or Edge; if not, ask the user to run `npx -y @yuzc-001/grasp`, `grasp connect`, or `start-chrome.bat` |
297
+ | `INSTANCE_CONFIRMATION_REQUIRED` | Call `get_status`, then `confirm_runtime_instance(display="windowed")` or confirm the actual expected mode |
298
+ | `INSTANCE_CONFIRMATION_MISMATCH` | Stop and switch to the correct runtime instance before acting |
299
+ | `TAB_AMBIGUOUS` or `TAB_NOT_FOUND` | Use `list_visible_tabs` and refine `select_visible_tab(query=..., title_contains=..., url_contains=...)` |
300
+ | `get_hint_map` returns nothing useful | The page may still be loading, blocked, or not yet in an interaction-ready state; use `inspect` or `get_page_summary` |
301
+ | Click target disappeared after action | The page changed; call `get_hint_map` again |
302
+ | The target is inside a scrollable pane | Prefer `scroll_into_view`, then `scroll(..., hint_id=...)` if needed |
303
+ | A dialog blocks progress | Use `handle_dialog` |
304
+ | `wait_for` times out | Check the condition text or URL fragment and retry with a larger timeout only if the condition is really expected |
305
+ | `evaluate` returns `undefined` | The expression produced no return value; rewrite it as an expression that returns something |
306
+ | Cookie changes do not stick | Check `domain`, `path`, and whether they match the current page context |