@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.
- package/LICENSE +21 -0
- package/README.md +327 -0
- package/README.zh-CN.md +324 -0
- package/examples/README.md +31 -0
- package/examples/claude-desktop.json +8 -0
- package/examples/codex-config.toml +4 -0
- package/grasp.skill +0 -0
- package/index.js +87 -0
- package/package.json +48 -0
- package/scripts/grasp_openclaw_ctl.sh +122 -0
- package/scripts/run-search-benchmark.mjs +287 -0
- package/scripts/update-star-history.mjs +274 -0
- package/skill/SKILL.md +61 -0
- package/skill/references/tools.md +306 -0
- package/src/cli/auto-configure.js +116 -0
- package/src/cli/cmd-connect.js +148 -0
- package/src/cli/cmd-explain.js +42 -0
- package/src/cli/cmd-logs.js +55 -0
- package/src/cli/cmd-status.js +119 -0
- package/src/cli/config.js +27 -0
- package/src/cli/detect-chrome.js +58 -0
- package/src/grasp/handoff/events.js +67 -0
- package/src/grasp/handoff/persist.js +48 -0
- package/src/grasp/handoff/state.js +28 -0
- package/src/grasp/page/capture.js +34 -0
- package/src/grasp/page/state.js +273 -0
- package/src/grasp/verify/evidence.js +40 -0
- package/src/grasp/verify/pipeline.js +52 -0
- package/src/layer1-bridge/chrome.js +416 -0
- package/src/layer1-bridge/webmcp.js +143 -0
- package/src/layer2-perception/hints.js +284 -0
- package/src/layer3-action/actions.js +400 -0
- package/src/runtime/browser-instance.js +65 -0
- package/src/runtime/truth/model.js +94 -0
- package/src/runtime/truth/snapshot.js +51 -0
- package/src/server/affordances.js +47 -0
- package/src/server/audit.js +122 -0
- package/src/server/boss-fast-path.js +164 -0
- package/src/server/boundary-guard.js +53 -0
- package/src/server/content.js +97 -0
- package/src/server/continuity.js +256 -0
- package/src/server/engine-selection.js +29 -0
- package/src/server/entry-orchestrator.js +115 -0
- package/src/server/error-codes.js +7 -0
- package/src/server/explain-share-card.js +113 -0
- package/src/server/fast-path-router.js +134 -0
- package/src/server/form-runtime.js +602 -0
- package/src/server/form-tasks.js +254 -0
- package/src/server/gateway-response.js +62 -0
- package/src/server/index.js +22 -0
- package/src/server/observe.js +52 -0
- package/src/server/page-projection.js +31 -0
- package/src/server/page-state.js +27 -0
- package/src/server/postconditions.js +128 -0
- package/src/server/prompt-assembly.js +148 -0
- package/src/server/responses.js +44 -0
- package/src/server/route-boundary.js +174 -0
- package/src/server/route-policy.js +168 -0
- package/src/server/runtime-confirmation.js +87 -0
- package/src/server/runtime-status.js +7 -0
- package/src/server/share-artifacts.js +284 -0
- package/src/server/state.js +132 -0
- package/src/server/structured-extraction.js +131 -0
- package/src/server/surface-prompts.js +166 -0
- package/src/server/task-frame.js +11 -0
- package/src/server/tasks/search-task.js +321 -0
- package/src/server/tools.actions.js +1361 -0
- package/src/server/tools.form.js +526 -0
- package/src/server/tools.gateway.js +757 -0
- package/src/server/tools.handoff.js +210 -0
- package/src/server/tools.js +20 -0
- package/src/server/tools.legacy.js +983 -0
- package/src/server/tools.strategy.js +250 -0
- package/src/server/tools.task-surface.js +66 -0
- package/src/server/tools.workspace.js +873 -0
- package/src/server/workspace-runtime.js +1138 -0
- package/src/server/workspace-tasks.js +735 -0
- package/start-chrome.bat +84 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
import { getActivePage } from '../layer1-bridge/chrome.js';
|
|
4
|
+
import { textResponse } from './responses.js';
|
|
5
|
+
import { syncPageState } from './state.js';
|
|
6
|
+
import { audit } from './audit.js';
|
|
7
|
+
import {
|
|
8
|
+
requestHandoff,
|
|
9
|
+
markHandoffInProgress,
|
|
10
|
+
markAwaitingReacquisition,
|
|
11
|
+
markResumedUnverified,
|
|
12
|
+
markResumeVerified,
|
|
13
|
+
clearHandoff,
|
|
14
|
+
} from '../grasp/handoff/events.js';
|
|
15
|
+
import { attachHandoffTaskMetadata, readHandoffState, writeHandoffState } from '../grasp/handoff/persist.js';
|
|
16
|
+
import { capturePageEvidence } from '../grasp/verify/evidence.js';
|
|
17
|
+
import { assessResumeContinuation } from './continuity.js';
|
|
18
|
+
|
|
19
|
+
export function getHandoffContinuationAnchors(handoff = {}) {
|
|
20
|
+
return {
|
|
21
|
+
expected_url_contains: handoff.expected_url_contains ?? null,
|
|
22
|
+
expected_page_role: handoff.expected_page_role ?? null,
|
|
23
|
+
expected_selector: handoff.expected_selector ?? null,
|
|
24
|
+
continuation_goal: handoff.continuation_goal ?? null,
|
|
25
|
+
expected_hint_label: handoff.expected_hint_label ?? null,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function shouldMarkResumeVerified({
|
|
30
|
+
verify = true,
|
|
31
|
+
checkpointStillPresent = false,
|
|
32
|
+
pageState = {},
|
|
33
|
+
continuation = {},
|
|
34
|
+
}) {
|
|
35
|
+
if (!verify || checkpointStillPresent) return false;
|
|
36
|
+
|
|
37
|
+
const pageReacquired = !!pageState?.reacquired;
|
|
38
|
+
const taskVerified = continuation?.task_continuation_ok;
|
|
39
|
+
const continuationReady = continuation?.continuation_ready === true;
|
|
40
|
+
|
|
41
|
+
if (taskVerified === false) return false;
|
|
42
|
+
|
|
43
|
+
return pageReacquired || continuationReady;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function registerHandoffTools(server, state, deps = {}) {
|
|
47
|
+
const getPage = deps.getActivePage ?? getActivePage;
|
|
48
|
+
|
|
49
|
+
server.registerTool(
|
|
50
|
+
'request_handoff',
|
|
51
|
+
{
|
|
52
|
+
description: 'Mark that the current task/page requires a human step before the agent can continue.',
|
|
53
|
+
inputSchema: {
|
|
54
|
+
reason: z.string().describe('Why human help is required, e.g. login_required, captcha_required'),
|
|
55
|
+
note: z.string().optional().describe('Optional note for the human/operator'),
|
|
56
|
+
expected_url_contains: z.string().optional().describe('Optional task anchor persisted into handoff state'),
|
|
57
|
+
expected_page_role: z.string().optional().describe('Optional task anchor persisted into handoff state'),
|
|
58
|
+
expected_selector: z.string().optional().describe('Optional task anchor persisted into handoff state'),
|
|
59
|
+
continuation_goal: z.string().optional().describe('Human-readable description of the task that should become continue-ready after resume'),
|
|
60
|
+
expected_hint_label: z.string().optional().describe('Expected next affordance label that should reappear after resume'),
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
async ({ reason, note, expected_url_contains, expected_page_role, expected_selector, continuation_goal, expected_hint_label }) => {
|
|
64
|
+
state.handoff = attachHandoffTaskMetadata(
|
|
65
|
+
requestHandoff(await readHandoffState(), reason, note ?? null, {
|
|
66
|
+
expected_url_contains,
|
|
67
|
+
expected_page_role,
|
|
68
|
+
expected_selector,
|
|
69
|
+
continuation_goal,
|
|
70
|
+
expected_hint_label,
|
|
71
|
+
}),
|
|
72
|
+
state
|
|
73
|
+
);
|
|
74
|
+
await writeHandoffState(state.handoff);
|
|
75
|
+
await audit('handoff_request', `${reason}${note ? ` :: ${note}` : ''}`);
|
|
76
|
+
return textResponse([
|
|
77
|
+
`Handoff requested: ${reason}`,
|
|
78
|
+
...(note ? [`Note: ${note}`] : []),
|
|
79
|
+
'State: handoff_required',
|
|
80
|
+
], { handoff: state.handoff });
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
server.registerTool(
|
|
85
|
+
'mark_handoff_in_progress',
|
|
86
|
+
{
|
|
87
|
+
description: 'Mark that a human is currently performing the required browser step.',
|
|
88
|
+
inputSchema: {
|
|
89
|
+
note: z.string().optional().describe('Optional note about the in-progress human step'),
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
async ({ note } = {}) => {
|
|
93
|
+
state.handoff = attachHandoffTaskMetadata(markHandoffInProgress(await readHandoffState(), note ?? null), state);
|
|
94
|
+
await writeHandoffState(state.handoff);
|
|
95
|
+
await audit('handoff_progress', note ?? 'in progress');
|
|
96
|
+
return textResponse([
|
|
97
|
+
'Handoff is now in progress.',
|
|
98
|
+
...(note ? [`Note: ${note}`] : []),
|
|
99
|
+
'State: handoff_in_progress',
|
|
100
|
+
], { handoff: state.handoff });
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
server.registerTool(
|
|
105
|
+
'mark_handoff_done',
|
|
106
|
+
{
|
|
107
|
+
description: 'Mark that the human step is done and Grasp should now reacquire the page state.',
|
|
108
|
+
inputSchema: {
|
|
109
|
+
note: z.string().optional().describe('Optional note left by the human/operator'),
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
async ({ note } = {}) => {
|
|
113
|
+
state.handoff = attachHandoffTaskMetadata(markAwaitingReacquisition(await readHandoffState(), note ?? null), state);
|
|
114
|
+
await writeHandoffState(state.handoff);
|
|
115
|
+
await audit('handoff_done', note ?? 'awaiting reacquisition');
|
|
116
|
+
return textResponse([
|
|
117
|
+
'Human step marked done.',
|
|
118
|
+
...(note ? [`Note: ${note}`] : []),
|
|
119
|
+
'State: awaiting_reacquisition',
|
|
120
|
+
'Next: call resume_after_handoff to reacquire page state.',
|
|
121
|
+
], { handoff: state.handoff });
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
server.registerTool(
|
|
126
|
+
'resume_after_handoff',
|
|
127
|
+
{
|
|
128
|
+
description: 'Reacquire page state after a human step, then mark the handoff as resumed.',
|
|
129
|
+
inputSchema: {
|
|
130
|
+
verify: z.boolean().optional().describe('Require visible reacquisition evidence before marking verified'),
|
|
131
|
+
note: z.string().optional().describe('Optional note about the resumed state'),
|
|
132
|
+
expected_url_contains: z.string().optional().describe('Optional task anchor: URL should contain this substring after resume'),
|
|
133
|
+
expected_page_role: z.string().optional().describe('Optional task anchor: page role should match after resume'),
|
|
134
|
+
expected_selector: z.string().optional().describe('Optional task anchor: selector should be present after resume'),
|
|
135
|
+
continuation_goal: z.string().optional().describe('Optional continuation goal for resumed task'),
|
|
136
|
+
expected_hint_label: z.string().optional().describe('Optional expected next affordance label after resume'),
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
async ({ verify = true, note, expected_url_contains, expected_page_role, expected_selector, continuation_goal, expected_hint_label } = {}) => {
|
|
140
|
+
const page = await getPage({ state });
|
|
141
|
+
await syncPageState(page, state, { force: true });
|
|
142
|
+
const currentHandoff = await readHandoffState();
|
|
143
|
+
const anchors = {
|
|
144
|
+
...getHandoffContinuationAnchors(currentHandoff),
|
|
145
|
+
expected_url_contains: expected_url_contains ?? currentHandoff.expected_url_contains,
|
|
146
|
+
expected_page_role: expected_page_role ?? currentHandoff.expected_page_role,
|
|
147
|
+
expected_selector: expected_selector ?? currentHandoff.expected_selector,
|
|
148
|
+
continuation_goal: continuation_goal ?? currentHandoff.continuation_goal,
|
|
149
|
+
expected_hint_label: expected_hint_label ?? currentHandoff.expected_hint_label,
|
|
150
|
+
};
|
|
151
|
+
const continuation = await assessResumeContinuation(page, state, anchors);
|
|
152
|
+
const checkpointStillPresent = state.pageState?.currentRole === 'checkpoint' || state.pageState?.riskGateDetected === true;
|
|
153
|
+
const effectiveContinuation = checkpointStillPresent
|
|
154
|
+
? {
|
|
155
|
+
...continuation,
|
|
156
|
+
continuation_ready: false,
|
|
157
|
+
suggested_next_action: state.pageState?.suggestedNextAction ?? 'handoff_required',
|
|
158
|
+
}
|
|
159
|
+
: continuation;
|
|
160
|
+
const evidence = await capturePageEvidence(page, state, {
|
|
161
|
+
action: 'resume_after_handoff',
|
|
162
|
+
details: {
|
|
163
|
+
pageIdentity: state.pageState?.pageIdentity ?? null,
|
|
164
|
+
continuation: effectiveContinuation,
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const taskVerified = continuation.task_continuation_ok;
|
|
169
|
+
const shouldVerify = shouldMarkResumeVerified({
|
|
170
|
+
verify,
|
|
171
|
+
checkpointStillPresent,
|
|
172
|
+
pageState: state.pageState,
|
|
173
|
+
continuation: effectiveContinuation,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
if (shouldVerify) {
|
|
177
|
+
state.handoff = attachHandoffTaskMetadata(markResumeVerified(currentHandoff, evidence, note ?? null), state);
|
|
178
|
+
} else {
|
|
179
|
+
state.handoff = attachHandoffTaskMetadata(markResumedUnverified(currentHandoff, evidence, note ?? null), state);
|
|
180
|
+
}
|
|
181
|
+
await writeHandoffState(state.handoff);
|
|
182
|
+
await audit('handoff_resume', `${state.handoff.state}${note ? ` :: ${note}` : ''}`);
|
|
183
|
+
|
|
184
|
+
return textResponse([
|
|
185
|
+
`Resume state: ${state.handoff.state}`,
|
|
186
|
+
`Page role: ${state.pageState?.currentRole ?? 'unknown'}`,
|
|
187
|
+
`Grasp confidence: ${state.pageState?.graspConfidence ?? 'unknown'}`,
|
|
188
|
+
`Reacquired: ${state.pageState?.reacquired ? 'yes' : 'no'}`,
|
|
189
|
+
`Task continuation: ${taskVerified === null ? 'not checked' : taskVerified ? 'ok' : 'failed'}`,
|
|
190
|
+
`Checkpoint still present: ${checkpointStillPresent ? 'yes' : 'no'}`,
|
|
191
|
+
`Continuation ready: ${effectiveContinuation.continuation_ready ? 'yes' : 'no'}`,
|
|
192
|
+
`Suggested next action: ${effectiveContinuation.suggested_next_action}`,
|
|
193
|
+
], { handoff: state.handoff, evidence, continuation: effectiveContinuation, checkpointStillPresent });
|
|
194
|
+
}
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
server.registerTool(
|
|
198
|
+
'clear_handoff',
|
|
199
|
+
{
|
|
200
|
+
description: 'Clear the current handoff state and return to idle.',
|
|
201
|
+
inputSchema: {},
|
|
202
|
+
},
|
|
203
|
+
async () => {
|
|
204
|
+
state.handoff = clearHandoff(await readHandoffState());
|
|
205
|
+
await writeHandoffState(state.handoff);
|
|
206
|
+
await audit('handoff_clear', 'idle');
|
|
207
|
+
return textResponse('Handoff cleared. State: idle', { handoff: state.handoff });
|
|
208
|
+
}
|
|
209
|
+
);
|
|
210
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { registerGatewayTools } from './tools.gateway.js';
|
|
2
|
+
import { registerFormTools } from './tools.form.js';
|
|
3
|
+
import { registerWorkspaceTools } from './tools.workspace.js';
|
|
4
|
+
import { registerStrategyTools } from './tools.strategy.js';
|
|
5
|
+
import { registerHandoffTools } from './tools.handoff.js';
|
|
6
|
+
import { registerActionTools } from './tools.actions.js';
|
|
7
|
+
import { registerTaskTools } from './tools.task-surface.js';
|
|
8
|
+
|
|
9
|
+
export function registerTools(server, state) {
|
|
10
|
+
registerGatewayTools(server, state);
|
|
11
|
+
registerFormTools(server, state);
|
|
12
|
+
registerWorkspaceTools(server, state);
|
|
13
|
+
registerStrategyTools(server, state);
|
|
14
|
+
registerHandoffTools(server, state);
|
|
15
|
+
registerActionTools(server, state);
|
|
16
|
+
registerTaskTools(server, state);
|
|
17
|
+
|
|
18
|
+
return server;
|
|
19
|
+
}
|
|
20
|
+
|