@handstage/agent 1.0.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/README.md +3 -0
- package/dist/definitions.js +209 -0
- package/dist/handlerTypes.js +0 -0
- package/dist/handlers.js +104 -0
- package/dist/index.js +4 -0
- package/dist/result.js +21 -0
- package/dist/schemas.js +168 -0
- package/dist/types.js +0 -0
- package/package.json +32 -0
- package/src/definitions.ts +263 -0
- package/src/handlerTypes.ts +83 -0
- package/src/handlers.ts +163 -0
- package/src/index.ts +10 -0
- package/src/result.ts +31 -0
- package/src/schemas.ts +207 -0
- package/src/types.ts +108 -0
package/README.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { tool } from "ai";
|
|
2
|
+
import {
|
|
3
|
+
// ClickInputSchema,
|
|
4
|
+
ClickOnIdInputSchema,
|
|
5
|
+
// BringToFrontInputSchema,
|
|
6
|
+
// BringToFrontOutputSchema,
|
|
7
|
+
ClosePageInputSchema, ClosePageOutputSchema,
|
|
8
|
+
// ClickOnInputSchema,
|
|
9
|
+
ElementActionOutputSchema, FillOnIdInputSchema,
|
|
10
|
+
// FillOnInputSchema,
|
|
11
|
+
GoBackInputSchema, GoForwardInputSchema, GotoInputSchema, GotoOutputSchema, HistoryNavOutputSchema,
|
|
12
|
+
// HoverInputSchema,
|
|
13
|
+
HoverOnIdInputSchema,
|
|
14
|
+
// HoverOnInputSchema,
|
|
15
|
+
NewPageInputSchema, NewPageOutputSchema,
|
|
16
|
+
// PageInfoInputSchema,
|
|
17
|
+
// PageInfoOutputSchema,
|
|
18
|
+
PagesInputSchema, PagesOutputSchema,
|
|
19
|
+
// PointerOutputSchema,
|
|
20
|
+
ReloadInputSchema, ReloadOutputSchema,
|
|
21
|
+
// ScrollInputSchema,
|
|
22
|
+
SnapshotDomInputSchema, SnapshotDomOutputSchema,
|
|
23
|
+
// TypeInputSchema,
|
|
24
|
+
TypeOnIdInputSchema, } from "./schemas";
|
|
25
|
+
function pagesToXml(pages) {
|
|
26
|
+
return `<pages>\n${pages
|
|
27
|
+
.map((page) => `<page>\n<pageId>${page.pageId}</pageId>\n<url>${page.url}</url>\n<title>${page.title}</title>\n</page>`)
|
|
28
|
+
.join("\n")}\n</pages>`;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Same object as {@link handstageAgentTools}; kept for callers that only need a `ToolSet`.
|
|
32
|
+
*/
|
|
33
|
+
export function createHandstageAgentToolDefinitions(handler) {
|
|
34
|
+
const tools = {
|
|
35
|
+
pages: tool({
|
|
36
|
+
description: "List opend pages in current browser context. Each entry has pageId, url, and title. Pages are returned in the order they were opened; no tab is implicitly 'active' — pass the pageId you want to act on with every tool call.",
|
|
37
|
+
inputSchema: PagesInputSchema,
|
|
38
|
+
outputSchema: PagesOutputSchema,
|
|
39
|
+
toModelOutput: ({ output }) => {
|
|
40
|
+
return {
|
|
41
|
+
type: "text",
|
|
42
|
+
value: pagesToXml(output.pages),
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
execute: async (input) => {
|
|
46
|
+
return await handler.pages(input);
|
|
47
|
+
},
|
|
48
|
+
}),
|
|
49
|
+
newPage: tool({
|
|
50
|
+
description: "Open a new browser page. Returns the new page's pageId. Optional starting URL (defaults to about:blank).",
|
|
51
|
+
inputSchema: NewPageInputSchema,
|
|
52
|
+
outputSchema: NewPageOutputSchema,
|
|
53
|
+
execute: async (input) => {
|
|
54
|
+
return await handler.newPage(input);
|
|
55
|
+
},
|
|
56
|
+
}),
|
|
57
|
+
closePage: tool({
|
|
58
|
+
description: "Close a browser page by pageId.",
|
|
59
|
+
inputSchema: ClosePageInputSchema,
|
|
60
|
+
outputSchema: ClosePageOutputSchema,
|
|
61
|
+
execute: async (input) => {
|
|
62
|
+
return await handler.closePage(input);
|
|
63
|
+
},
|
|
64
|
+
}),
|
|
65
|
+
// bringToFront: tool({
|
|
66
|
+
// description:
|
|
67
|
+
// "Foreground a tab by pageId (Target.activateTarget). Necessary before input events can land on that tab in headful Chrome.",
|
|
68
|
+
// inputSchema: BringToFrontInputSchema,
|
|
69
|
+
// outputSchema: BringToFrontOutputSchema,
|
|
70
|
+
// }),
|
|
71
|
+
goto: tool({
|
|
72
|
+
description: "Navigate a page to a URL.",
|
|
73
|
+
inputSchema: GotoInputSchema,
|
|
74
|
+
outputSchema: GotoOutputSchema,
|
|
75
|
+
execute: async (input) => {
|
|
76
|
+
return await handler.goto(input);
|
|
77
|
+
},
|
|
78
|
+
}),
|
|
79
|
+
reload: tool({
|
|
80
|
+
description: "Reload the current document in a page.",
|
|
81
|
+
inputSchema: ReloadInputSchema,
|
|
82
|
+
outputSchema: ReloadOutputSchema,
|
|
83
|
+
execute: async (input) => {
|
|
84
|
+
return await handler.reload(input);
|
|
85
|
+
},
|
|
86
|
+
}),
|
|
87
|
+
goBack: tool({
|
|
88
|
+
description: "Go back in history for a page, if possible.",
|
|
89
|
+
inputSchema: GoBackInputSchema,
|
|
90
|
+
outputSchema: HistoryNavOutputSchema,
|
|
91
|
+
execute: async (input) => {
|
|
92
|
+
return await handler.goBack(input);
|
|
93
|
+
},
|
|
94
|
+
}),
|
|
95
|
+
goForward: tool({
|
|
96
|
+
description: "Go forward in history for a page, if possible.",
|
|
97
|
+
inputSchema: GoForwardInputSchema,
|
|
98
|
+
outputSchema: HistoryNavOutputSchema,
|
|
99
|
+
execute: async (input) => {
|
|
100
|
+
return await handler.goForward(input);
|
|
101
|
+
},
|
|
102
|
+
}),
|
|
103
|
+
snapshot_dom: tool({
|
|
104
|
+
description: "Accessibility tree for a page (pageId). Multiline outline with encoded node ids in brackets (e.g. [1-42]); use those ids with click_on_id, fill_on_id, type_on_id, or hover_on_id.",
|
|
105
|
+
inputSchema: SnapshotDomInputSchema,
|
|
106
|
+
outputSchema: SnapshotDomOutputSchema,
|
|
107
|
+
toModelOutput: ({ output }) => {
|
|
108
|
+
if (output.ok) {
|
|
109
|
+
return {
|
|
110
|
+
type: "text",
|
|
111
|
+
value: output.tree,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
type: "text",
|
|
116
|
+
value: output.error,
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
execute: async (input) => {
|
|
120
|
+
return await handler.snapshot_dom(input);
|
|
121
|
+
},
|
|
122
|
+
}),
|
|
123
|
+
// pageInfo: tool({
|
|
124
|
+
// description: "Current URL and document title for a page.",
|
|
125
|
+
// inputSchema: PageInfoInputSchema,
|
|
126
|
+
// outputSchema: PageInfoOutputSchema,
|
|
127
|
+
// }),
|
|
128
|
+
// click: tool({
|
|
129
|
+
// description:
|
|
130
|
+
// "Click at viewport coordinates (CSS pixels). Does not scroll; ensure the target is visible.",
|
|
131
|
+
// inputSchema: ClickInputSchema,
|
|
132
|
+
// outputSchema: PointerOutputSchema,
|
|
133
|
+
// }),
|
|
134
|
+
// hover: tool({
|
|
135
|
+
// description: "Move the pointer to viewport coordinates (CSS pixels).",
|
|
136
|
+
// inputSchema: HoverInputSchema,
|
|
137
|
+
// outputSchema: PointerOutputSchema,
|
|
138
|
+
// }),
|
|
139
|
+
// scroll: tool({
|
|
140
|
+
// description:
|
|
141
|
+
// "Dispatch a mouse wheel at viewport coordinates (deltaX/deltaY in pixels).",
|
|
142
|
+
// inputSchema: ScrollInputSchema,
|
|
143
|
+
// outputSchema: PointerOutputSchema,
|
|
144
|
+
// }),
|
|
145
|
+
// type: tool({
|
|
146
|
+
// description:
|
|
147
|
+
// "Type text using key events at the current focus. Focus an input first (e.g. click_on_id) or tab to it.",
|
|
148
|
+
// inputSchema: TypeInputSchema,
|
|
149
|
+
// outputSchema: TypeOutputSchema,
|
|
150
|
+
// }),
|
|
151
|
+
// click_on: tool({
|
|
152
|
+
// description:
|
|
153
|
+
// "Click the first element matching a CSS or XPath selector in the page's main frame.",
|
|
154
|
+
// inputSchema: ClickOnInputSchema,
|
|
155
|
+
// outputSchema: ElementActionOutputSchema,
|
|
156
|
+
// }),
|
|
157
|
+
// fill_on: tool({
|
|
158
|
+
// description:
|
|
159
|
+
// "Clear and fill an input element matched by a CSS or XPath selector (main frame).",
|
|
160
|
+
// inputSchema: FillOnInputSchema,
|
|
161
|
+
// outputSchema: ElementActionOutputSchema,
|
|
162
|
+
// }),
|
|
163
|
+
// type_on: tool({
|
|
164
|
+
// description:
|
|
165
|
+
// "Type into an element matched by a CSS or XPath selector (focuses the element first).",
|
|
166
|
+
// inputSchema: TypeOnInputSchema,
|
|
167
|
+
// outputSchema: ElementActionOutputSchema,
|
|
168
|
+
// }),
|
|
169
|
+
// hover_on: tool({
|
|
170
|
+
// description:
|
|
171
|
+
// "Hover the first element matching a CSS or XPath selector in the page's main frame.",
|
|
172
|
+
// inputSchema: HoverOnInputSchema,
|
|
173
|
+
// outputSchema: ElementActionOutputSchema,
|
|
174
|
+
// }),
|
|
175
|
+
click_on_id: tool({
|
|
176
|
+
description: "Click the element for an encoded accessibility tree node id from snapshot_dom.",
|
|
177
|
+
inputSchema: ClickOnIdInputSchema,
|
|
178
|
+
outputSchema: ElementActionOutputSchema,
|
|
179
|
+
execute: async (input) => {
|
|
180
|
+
return await handler.click_on_id(input);
|
|
181
|
+
},
|
|
182
|
+
}),
|
|
183
|
+
fill_on_id: tool({
|
|
184
|
+
description: "Clear and fill an input for an encoded accessibility tree node id from snapshot_dom.",
|
|
185
|
+
inputSchema: FillOnIdInputSchema,
|
|
186
|
+
outputSchema: ElementActionOutputSchema,
|
|
187
|
+
execute: async (input) => {
|
|
188
|
+
return await handler.fill_on_id(input);
|
|
189
|
+
},
|
|
190
|
+
}),
|
|
191
|
+
type_on_id: tool({
|
|
192
|
+
description: "Type into an element for an encoded accessibility tree node id from snapshot_dom.",
|
|
193
|
+
inputSchema: TypeOnIdInputSchema,
|
|
194
|
+
outputSchema: ElementActionOutputSchema,
|
|
195
|
+
execute: async (input) => {
|
|
196
|
+
return await handler.type_on_id(input);
|
|
197
|
+
},
|
|
198
|
+
}),
|
|
199
|
+
hover_on_id: tool({
|
|
200
|
+
description: "Hover the element for an encoded accessibility tree node id from snapshot_dom.",
|
|
201
|
+
inputSchema: HoverOnIdInputSchema,
|
|
202
|
+
outputSchema: ElementActionOutputSchema,
|
|
203
|
+
execute: async (input) => {
|
|
204
|
+
return await handler.hover_on_id(input);
|
|
205
|
+
},
|
|
206
|
+
}),
|
|
207
|
+
};
|
|
208
|
+
return tools;
|
|
209
|
+
}
|
|
File without changes
|
package/dist/handlers.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { errResult, tryAgentResult, withPage } from "./result";
|
|
2
|
+
export class HandstageContextAgentToolHandlers {
|
|
3
|
+
ctx;
|
|
4
|
+
constructor(ctx) {
|
|
5
|
+
this.ctx = ctx;
|
|
6
|
+
}
|
|
7
|
+
async pages(_input) {
|
|
8
|
+
const pages = await Promise.all(this.ctx.pages().map(async (page) => ({
|
|
9
|
+
pageId: page.pageId,
|
|
10
|
+
url: page.url(),
|
|
11
|
+
title: await page.title(),
|
|
12
|
+
})));
|
|
13
|
+
return { pages };
|
|
14
|
+
}
|
|
15
|
+
async newPage(input) {
|
|
16
|
+
const page = await this.ctx.newPage(input.url ?? "about:blank");
|
|
17
|
+
return { pageId: page.pageId };
|
|
18
|
+
}
|
|
19
|
+
async closePage(input) {
|
|
20
|
+
const page = this.ctx.resolvePageByTargetId(input.pageId);
|
|
21
|
+
if (!page)
|
|
22
|
+
return errResult(`Unknown pageId: ${input.pageId}`);
|
|
23
|
+
return tryAgentResult(async () => {
|
|
24
|
+
await page.close();
|
|
25
|
+
return {};
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async goto(input) {
|
|
29
|
+
return withPage(this.ctx, input.pageId, async (page) => {
|
|
30
|
+
await page.goto(input.url, {
|
|
31
|
+
waitUntil: input.waitUntil,
|
|
32
|
+
timeoutMs: input.timeoutMs,
|
|
33
|
+
});
|
|
34
|
+
return { url: page.url() };
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
async reload(input) {
|
|
38
|
+
return withPage(this.ctx, input.pageId, async (page) => {
|
|
39
|
+
await page.reload({
|
|
40
|
+
waitUntil: input.waitUntil,
|
|
41
|
+
timeoutMs: input.timeoutMs,
|
|
42
|
+
ignoreCache: input.ignoreCache,
|
|
43
|
+
});
|
|
44
|
+
return { url: page.url() };
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async goBack(input) {
|
|
48
|
+
return withPage(this.ctx, input.pageId, async (page) => {
|
|
49
|
+
const response = await page.goBack({
|
|
50
|
+
waitUntil: input.waitUntil,
|
|
51
|
+
timeoutMs: input.timeoutMs,
|
|
52
|
+
});
|
|
53
|
+
return { navigated: response !== null, url: page.url() };
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
async goForward(input) {
|
|
57
|
+
return withPage(this.ctx, input.pageId, async (page) => {
|
|
58
|
+
const response = await page.goForward({
|
|
59
|
+
waitUntil: input.waitUntil,
|
|
60
|
+
timeoutMs: input.timeoutMs,
|
|
61
|
+
});
|
|
62
|
+
return { navigated: response !== null, url: page.url() };
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
async snapshot_dom(input) {
|
|
66
|
+
return withPage(this.ctx, input.pageId, async (page) => {
|
|
67
|
+
const snapshot = await page.snapshot({
|
|
68
|
+
includeIframes: input.includeIframes,
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
tree: snapshot.formattedTree,
|
|
72
|
+
xpathMap: snapshot.xpathMap,
|
|
73
|
+
urlMap: snapshot.urlMap,
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
async click_on_id(input) {
|
|
78
|
+
return this.actOnEncodedId(input.pageId, input.id, (locator) => locator.click());
|
|
79
|
+
}
|
|
80
|
+
async fill_on_id(input) {
|
|
81
|
+
return this.actOnEncodedId(input.pageId, input.id, (locator) => locator.fill(input.value));
|
|
82
|
+
}
|
|
83
|
+
async type_on_id(input) {
|
|
84
|
+
return this.actOnEncodedId(input.pageId, input.id, (locator) => locator.type(input.text, { delay: input.delay }));
|
|
85
|
+
}
|
|
86
|
+
async hover_on_id(input) {
|
|
87
|
+
return this.actOnEncodedId(input.pageId, input.id, (locator) => locator.hover());
|
|
88
|
+
}
|
|
89
|
+
async actOnEncodedId(pageId, encodedId, action) {
|
|
90
|
+
return withPage(this.ctx, pageId, async (page) => {
|
|
91
|
+
const { xpathMap } = await page.snapshot();
|
|
92
|
+
const xpath = xpathMap[encodedId];
|
|
93
|
+
if (!xpath) {
|
|
94
|
+
throw new Error(`Unknown encoded id: ${encodedId}`);
|
|
95
|
+
}
|
|
96
|
+
await action(page.deepLocator(xpath));
|
|
97
|
+
await page.waitForLoadState("networkidle", 5000);
|
|
98
|
+
return {};
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export function createHandstageContextAgentToolHandlers(ctx) {
|
|
103
|
+
return new HandstageContextAgentToolHandlers(ctx);
|
|
104
|
+
}
|
package/dist/index.js
ADDED
package/dist/result.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function formatError(error) {
|
|
2
|
+
return error instanceof Error ? error.message : String(error);
|
|
3
|
+
}
|
|
4
|
+
export function errResult(error) {
|
|
5
|
+
return { ok: false, error };
|
|
6
|
+
}
|
|
7
|
+
export async function tryAgentResult(fn) {
|
|
8
|
+
try {
|
|
9
|
+
const data = await fn();
|
|
10
|
+
return { ok: true, ...data };
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
return errResult(formatError(error));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export async function withPage(ctx, pageId, fn) {
|
|
17
|
+
const page = ctx.resolvePageByTargetId(pageId);
|
|
18
|
+
if (!page)
|
|
19
|
+
return errResult(`Unknown pageId: ${pageId}`);
|
|
20
|
+
return tryAgentResult(() => fn(page));
|
|
21
|
+
}
|
package/dist/schemas.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const LoadStateSchema = z
|
|
3
|
+
.enum(["load", "domcontentloaded", "networkidle"])
|
|
4
|
+
.optional()
|
|
5
|
+
.describe("Wait until this lifecycle event (navigation / reload / history)");
|
|
6
|
+
export const PageIdSchema = z
|
|
7
|
+
.string()
|
|
8
|
+
.describe("Target id of the page tab (from pages or newPage)");
|
|
9
|
+
export const PagesInputSchema = z.object({});
|
|
10
|
+
export const NewPageInputSchema = z.object({
|
|
11
|
+
url: z.string().optional().describe('Initial URL (default "about:blank")'),
|
|
12
|
+
});
|
|
13
|
+
export const BringToFrontInputSchema = z.object({ pageId: PageIdSchema });
|
|
14
|
+
export const ClosePageInputSchema = z.object({ pageId: PageIdSchema });
|
|
15
|
+
export const GotoInputSchema = z.object({
|
|
16
|
+
pageId: PageIdSchema,
|
|
17
|
+
url: z.string().min(1),
|
|
18
|
+
waitUntil: LoadStateSchema,
|
|
19
|
+
timeoutMs: z.number().positive().optional(),
|
|
20
|
+
});
|
|
21
|
+
export const ReloadInputSchema = z.object({
|
|
22
|
+
pageId: PageIdSchema,
|
|
23
|
+
waitUntil: LoadStateSchema,
|
|
24
|
+
timeoutMs: z.number().positive().optional(),
|
|
25
|
+
ignoreCache: z.boolean().optional(),
|
|
26
|
+
});
|
|
27
|
+
export const GoBackInputSchema = z.object({
|
|
28
|
+
pageId: PageIdSchema,
|
|
29
|
+
waitUntil: LoadStateSchema,
|
|
30
|
+
timeoutMs: z.number().positive().optional(),
|
|
31
|
+
});
|
|
32
|
+
export const GoForwardInputSchema = z.object({
|
|
33
|
+
pageId: PageIdSchema,
|
|
34
|
+
waitUntil: LoadStateSchema,
|
|
35
|
+
timeoutMs: z.number().positive().optional(),
|
|
36
|
+
});
|
|
37
|
+
export const SnapshotDomInputSchema = z.object({
|
|
38
|
+
pageId: PageIdSchema,
|
|
39
|
+
includeIframes: z.boolean().optional(),
|
|
40
|
+
});
|
|
41
|
+
export const PageInfoInputSchema = z.object({ pageId: PageIdSchema });
|
|
42
|
+
export const ClickInputSchema = z.object({
|
|
43
|
+
pageId: PageIdSchema,
|
|
44
|
+
x: z.number(),
|
|
45
|
+
y: z.number(),
|
|
46
|
+
button: z.enum(["left", "right", "middle"]).optional(),
|
|
47
|
+
clickCount: z.number().int().positive().optional(),
|
|
48
|
+
});
|
|
49
|
+
export const HoverInputSchema = z.object({
|
|
50
|
+
pageId: PageIdSchema,
|
|
51
|
+
x: z.number(),
|
|
52
|
+
y: z.number(),
|
|
53
|
+
});
|
|
54
|
+
export const ScrollInputSchema = z.object({
|
|
55
|
+
pageId: PageIdSchema,
|
|
56
|
+
x: z.number(),
|
|
57
|
+
y: z.number(),
|
|
58
|
+
deltaX: z.number(),
|
|
59
|
+
deltaY: z.number(),
|
|
60
|
+
});
|
|
61
|
+
export const TypeInputSchema = z.object({
|
|
62
|
+
pageId: PageIdSchema,
|
|
63
|
+
text: z.string(),
|
|
64
|
+
delay: z.number().nonnegative().optional(),
|
|
65
|
+
withMistakes: z.boolean().optional(),
|
|
66
|
+
});
|
|
67
|
+
export const ClickOnInputSchema = z.object({
|
|
68
|
+
pageId: PageIdSchema,
|
|
69
|
+
select: z
|
|
70
|
+
.string()
|
|
71
|
+
.min(1)
|
|
72
|
+
.describe("CSS selector or XPath (e.g. //button[@id='x'])"),
|
|
73
|
+
});
|
|
74
|
+
export const FillOnInputSchema = z.object({
|
|
75
|
+
pageId: PageIdSchema,
|
|
76
|
+
select: z.string().min(1).describe("CSS selector or XPath"),
|
|
77
|
+
value: z.string(),
|
|
78
|
+
});
|
|
79
|
+
export const TypeOnInputSchema = z.object({
|
|
80
|
+
pageId: PageIdSchema,
|
|
81
|
+
select: z.string().min(1).describe("CSS selector or XPath"),
|
|
82
|
+
text: z.string(),
|
|
83
|
+
delay: z.number().nonnegative().optional(),
|
|
84
|
+
});
|
|
85
|
+
export const HoverOnInputSchema = z.object({
|
|
86
|
+
pageId: PageIdSchema,
|
|
87
|
+
select: z.string().min(1).describe("CSS selector or XPath"),
|
|
88
|
+
});
|
|
89
|
+
/** Encoded node id from snapshot_dom (e.g. `1-42` — frameOrdinal-backendNodeId). */
|
|
90
|
+
export const A11yEncodedIdSchema = z
|
|
91
|
+
.string()
|
|
92
|
+
.min(1)
|
|
93
|
+
.regex(/^\d+-\d+$/, "Expected frameOrdinal-backendNodeId from snapshot_dom")
|
|
94
|
+
.describe("Encoded node id from snapshot_dom (bracketed id in the a11y tree)");
|
|
95
|
+
export const ClickOnIdInputSchema = z.object({
|
|
96
|
+
pageId: PageIdSchema,
|
|
97
|
+
id: A11yEncodedIdSchema,
|
|
98
|
+
});
|
|
99
|
+
export const FillOnIdInputSchema = z.object({
|
|
100
|
+
pageId: PageIdSchema,
|
|
101
|
+
id: A11yEncodedIdSchema,
|
|
102
|
+
value: z.string(),
|
|
103
|
+
});
|
|
104
|
+
export const TypeOnIdInputSchema = z.object({
|
|
105
|
+
pageId: PageIdSchema,
|
|
106
|
+
id: A11yEncodedIdSchema,
|
|
107
|
+
text: z.string(),
|
|
108
|
+
delay: z.number().nonnegative().optional(),
|
|
109
|
+
});
|
|
110
|
+
export const HoverOnIdInputSchema = z.object({
|
|
111
|
+
pageId: PageIdSchema,
|
|
112
|
+
id: A11yEncodedIdSchema,
|
|
113
|
+
});
|
|
114
|
+
/** Shared `{ ok: true } | { ok: false; error }` tool result shape */
|
|
115
|
+
export const HandstageAgentOkOrErrOutputSchema = z.discriminatedUnion("ok", [
|
|
116
|
+
z.object({ ok: z.literal(true) }),
|
|
117
|
+
z.object({ ok: z.literal(false), error: z.string() }),
|
|
118
|
+
]);
|
|
119
|
+
export const HandstageAgentPageEntrySchema = z.object({
|
|
120
|
+
pageId: z.string(),
|
|
121
|
+
url: z.string(),
|
|
122
|
+
title: z.string(),
|
|
123
|
+
});
|
|
124
|
+
export const PagesOutputSchema = z.object({
|
|
125
|
+
pages: z.array(HandstageAgentPageEntrySchema),
|
|
126
|
+
});
|
|
127
|
+
export const NewPageOutputSchema = z.object({ pageId: z.string() });
|
|
128
|
+
export const BringToFrontOutputSchema = HandstageAgentOkOrErrOutputSchema;
|
|
129
|
+
export const ClosePageOutputSchema = HandstageAgentOkOrErrOutputSchema;
|
|
130
|
+
export const GotoOutputSchema = z.discriminatedUnion("ok", [
|
|
131
|
+
z.object({ ok: z.literal(true), url: z.string() }),
|
|
132
|
+
z.object({ ok: z.literal(false), error: z.string() }),
|
|
133
|
+
]);
|
|
134
|
+
export const ReloadOutputSchema = GotoOutputSchema;
|
|
135
|
+
export const HistoryNavOutputSchema = z.discriminatedUnion("ok", [
|
|
136
|
+
z.object({
|
|
137
|
+
ok: z.literal(true),
|
|
138
|
+
navigated: z.boolean(),
|
|
139
|
+
url: z.string(),
|
|
140
|
+
}),
|
|
141
|
+
z.object({ ok: z.literal(false), error: z.string() }),
|
|
142
|
+
]);
|
|
143
|
+
export const SnapshotDomOutputSchema = z.discriminatedUnion("ok", [
|
|
144
|
+
z.object({
|
|
145
|
+
ok: z.literal(true),
|
|
146
|
+
tree: z.string(),
|
|
147
|
+
xpathMap: z.record(z.string(), z.string()),
|
|
148
|
+
urlMap: z.record(z.string(), z.string()),
|
|
149
|
+
}),
|
|
150
|
+
z.object({ ok: z.literal(false), error: z.string() }),
|
|
151
|
+
]);
|
|
152
|
+
export const PageInfoOutputSchema = z.discriminatedUnion("ok", [
|
|
153
|
+
z.object({
|
|
154
|
+
ok: z.literal(true),
|
|
155
|
+
url: z.string(),
|
|
156
|
+
title: z.string(),
|
|
157
|
+
}),
|
|
158
|
+
z.object({ ok: z.literal(false), error: z.string() }),
|
|
159
|
+
]);
|
|
160
|
+
export const PointerOutputSchema = z.discriminatedUnion("ok", [
|
|
161
|
+
z.object({
|
|
162
|
+
ok: z.literal(true),
|
|
163
|
+
xpathAtPoint: z.string().optional(),
|
|
164
|
+
}),
|
|
165
|
+
z.object({ ok: z.literal(false), error: z.string() }),
|
|
166
|
+
]);
|
|
167
|
+
export const TypeOutputSchema = HandstageAgentOkOrErrOutputSchema;
|
|
168
|
+
export const ElementActionOutputSchema = HandstageAgentOkOrErrOutputSchema;
|
package/dist/types.js
ADDED
|
File without changes
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@handstage/agent",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./src/index.ts",
|
|
8
|
+
"import": "./dist/index.js"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"src"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "rm -rf dist && tsc"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@ai-sdk/devtools": "^0.0.18",
|
|
20
|
+
"@ai-sdk/openai-compatible": "^2.0.48",
|
|
21
|
+
"@types/bun": "1.3.14",
|
|
22
|
+
"@types/node": "^25.9.1"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"typescript": "^6.0.3"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@handstage/core": "1.0.0",
|
|
29
|
+
"ai": "^6.0.193",
|
|
30
|
+
"zod": "^4.4.3"
|
|
31
|
+
}
|
|
32
|
+
}
|