@athenaintel/react 0.60.1 → 0.61.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 +157 -5
- package/dist/agent-ui/AgentTarget.cjs +11 -0
- package/dist/agent-ui/AgentTarget.cjs.map +1 -0
- package/dist/agent-ui/AgentTarget.d.ts +17 -0
- package/dist/agent-ui/AgentTarget.js +11 -0
- package/dist/agent-ui/AgentTarget.js.map +1 -0
- package/dist/agent-ui/AgentUiLayer.cjs +387 -0
- package/dist/agent-ui/AgentUiLayer.cjs.map +1 -0
- package/dist/agent-ui/AgentUiLayer.d.ts +40 -0
- package/dist/agent-ui/AgentUiLayer.js +387 -0
- package/dist/agent-ui/AgentUiLayer.js.map +1 -0
- package/dist/agent-ui/contract.cjs +104 -0
- package/dist/agent-ui/contract.cjs.map +1 -0
- package/dist/agent-ui/contract.d.ts +113 -0
- package/dist/agent-ui/contract.js +104 -0
- package/dist/agent-ui/contract.js.map +1 -0
- package/dist/agent-ui/driver.cjs +335 -0
- package/dist/agent-ui/driver.cjs.map +1 -0
- package/dist/agent-ui/driver.d.ts +66 -0
- package/dist/agent-ui/driver.js +335 -0
- package/dist/agent-ui/driver.js.map +1 -0
- package/dist/agent-ui/index.cjs +33 -0
- package/dist/agent-ui/index.cjs.map +1 -0
- package/dist/agent-ui/index.d.ts +19 -0
- package/dist/agent-ui/index.js +33 -0
- package/dist/agent-ui/index.js.map +1 -0
- package/dist/agent-ui/pace.cjs +121 -0
- package/dist/agent-ui/pace.cjs.map +1 -0
- package/dist/agent-ui/pace.d.ts +66 -0
- package/dist/agent-ui/pace.js +121 -0
- package/dist/agent-ui/pace.js.map +1 -0
- package/dist/agent-ui/screen.cjs +471 -0
- package/dist/agent-ui/screen.cjs.map +1 -0
- package/dist/agent-ui/screen.d.ts +97 -0
- package/dist/agent-ui/screen.js +471 -0
- package/dist/agent-ui/screen.js.map +1 -0
- package/dist/agent-ui/secret-fields.cjs +50 -0
- package/dist/agent-ui/secret-fields.cjs.map +1 -0
- package/dist/agent-ui/secret-fields.d.ts +15 -0
- package/dist/agent-ui/secret-fields.js +50 -0
- package/dist/agent-ui/secret-fields.js.map +1 -0
- package/dist/agent-ui/stage.cjs +196 -0
- package/dist/agent-ui/stage.cjs.map +1 -0
- package/dist/agent-ui/stage.d.ts +110 -0
- package/dist/agent-ui/stage.js +196 -0
- package/dist/agent-ui/stage.js.map +1 -0
- package/dist/agent-ui/tools.cjs +448 -0
- package/dist/agent-ui/tools.cjs.map +1 -0
- package/dist/agent-ui/tools.d.ts +103 -0
- package/dist/agent-ui/tools.js +448 -0
- package/dist/agent-ui/tools.js.map +1 -0
- package/dist/agent-ui/use-agent-target.cjs +63 -0
- package/dist/agent-ui/use-agent-target.cjs.map +1 -0
- package/dist/agent-ui/use-agent-target.d.ts +27 -0
- package/dist/agent-ui/use-agent-target.js +63 -0
- package/dist/agent-ui/use-agent-target.js.map +1 -0
- package/dist/agent-ui/use-agent-ui.cjs +15 -0
- package/dist/agent-ui/use-agent-ui.cjs.map +1 -0
- package/dist/agent-ui/use-agent-ui.d.ts +24 -0
- package/dist/agent-ui/use-agent-ui.js +15 -0
- package/dist/agent-ui/use-agent-ui.js.map +1 -0
- package/dist/chat/AthenaChat.cjs +17 -4
- package/dist/chat/AthenaChat.cjs.map +1 -1
- package/dist/chat/AthenaChat.d.ts +13 -0
- package/dist/chat/AthenaChat.js +17 -4
- package/dist/chat/AthenaChat.js.map +1 -1
- package/dist/chat/StatewireClientToolBridge.cjs +17 -12
- package/dist/chat/StatewireClientToolBridge.cjs.map +1 -1
- package/dist/chat/StatewireClientToolBridge.d.ts +4 -2
- package/dist/chat/StatewireClientToolBridge.js +17 -12
- package/dist/chat/StatewireClientToolBridge.js.map +1 -1
- package/dist/chat/StatewireClientToolConsentCard.cjs +3 -2
- package/dist/chat/StatewireClientToolConsentCard.cjs.map +1 -1
- package/dist/chat/StatewireClientToolConsentCard.d.ts +2 -1
- package/dist/chat/StatewireClientToolConsentCard.js +3 -2
- package/dist/chat/StatewireClientToolConsentCard.js.map +1 -1
- package/dist/chat/chat-voice-controls.cjs +5 -2
- package/dist/chat/chat-voice-controls.cjs.map +1 -1
- package/dist/chat/chat-voice-controls.d.ts +6 -1
- package/dist/chat/chat-voice-controls.js +6 -3
- package/dist/chat/chat-voice-controls.js.map +1 -1
- package/dist/chat/client-tool-write-consent.cjs +16 -5
- package/dist/chat/client-tool-write-consent.cjs.map +1 -1
- package/dist/chat/client-tool-write-consent.d.ts +12 -4
- package/dist/chat/client-tool-write-consent.js +16 -5
- package/dist/chat/client-tool-write-consent.js.map +1 -1
- package/dist/chat-ui/messages/message-components.cjs +1 -1
- package/dist/chat-ui/messages/message-components.cjs.map +1 -1
- package/dist/chat-ui/messages/message-components.d.ts +2 -2
- package/dist/chat-ui/messages/message-components.js +1 -1
- package/dist/chat-ui/messages/message-components.js.map +1 -1
- package/dist/index.cjs +7 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/provider/AthenaProvider.cjs +37 -8
- package/dist/provider/AthenaProvider.cjs.map +1 -1
- package/dist/provider/AthenaProvider.d.ts +18 -1
- package/dist/provider/AthenaProvider.js +33 -4
- package/dist/provider/AthenaProvider.js.map +1 -1
- package/dist/react/package.json.cjs +1 -1
- package/dist/react/package.json.js +1 -1
- package/dist/styles.css +1 -1
- package/package.json +6 -1
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const contract = require("./contract.cjs");
|
|
4
|
+
const secretFields = require("./secret-fields.cjs");
|
|
5
|
+
class AgentUiError extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "AgentUiError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const CONTROL_SELECTOR = 'input:not([type="hidden"]), textarea, select, [contenteditable=""], [contenteditable="true"]';
|
|
12
|
+
const MAX_LABEL_CHARS = 80;
|
|
13
|
+
const MAX_VALUE_CHARS = 200;
|
|
14
|
+
const MAX_DESCRIPTION_CHARS = 240;
|
|
15
|
+
const MAX_OPTIONS = 30;
|
|
16
|
+
const MAX_OPTION_CHARS = 60;
|
|
17
|
+
const MAX_REF_CHARS = 160;
|
|
18
|
+
const MAX_REGIONS = 40;
|
|
19
|
+
const tagOf = (element) => element.tagName.toUpperCase();
|
|
20
|
+
function clip(text, max) {
|
|
21
|
+
const collapsed = text.replace(/\s+/g, " ").trim();
|
|
22
|
+
return collapsed.length > max ? `${collapsed.slice(0, max - 1)}…` : collapsed;
|
|
23
|
+
}
|
|
24
|
+
function inputType(element) {
|
|
25
|
+
return (element.getAttribute("type") ?? "text").toLowerCase();
|
|
26
|
+
}
|
|
27
|
+
function isEditable(element) {
|
|
28
|
+
const editable = element.getAttribute("contenteditable");
|
|
29
|
+
return editable === "" || editable === "true";
|
|
30
|
+
}
|
|
31
|
+
function controlOf(element) {
|
|
32
|
+
if (element.matches(CONTROL_SELECTOR)) return element;
|
|
33
|
+
const tag = tagOf(element);
|
|
34
|
+
if (tag === "BUTTON" || tag === "A") return null;
|
|
35
|
+
return element.querySelector(CONTROL_SELECTOR);
|
|
36
|
+
}
|
|
37
|
+
function roleOf(element, control) {
|
|
38
|
+
var _a, _b;
|
|
39
|
+
const explicit = (_a = element.getAttribute("role")) == null ? void 0 : _a.trim();
|
|
40
|
+
if (explicit) return explicit;
|
|
41
|
+
const subject = control ?? element;
|
|
42
|
+
const tag = tagOf(subject);
|
|
43
|
+
if (tag === "BUTTON" || tag === "SUMMARY") return "button";
|
|
44
|
+
if (tag === "A") return subject.hasAttribute("href") ? "link" : "element";
|
|
45
|
+
if (tag === "SELECT") return "combobox";
|
|
46
|
+
if (tag === "TEXTAREA") return "textbox";
|
|
47
|
+
if (tag === "INPUT") {
|
|
48
|
+
const type = inputType(subject);
|
|
49
|
+
if (type === "checkbox") return "checkbox";
|
|
50
|
+
if (type === "radio") return "radio";
|
|
51
|
+
if (type === "range") return "slider";
|
|
52
|
+
if (type === "button" || type === "submit" || type === "reset" || type === "image") return "button";
|
|
53
|
+
if (type === "search") return "searchbox";
|
|
54
|
+
return "textbox";
|
|
55
|
+
}
|
|
56
|
+
if (isEditable(subject)) return "textbox";
|
|
57
|
+
return ((_b = subject.getAttribute("role")) == null ? void 0 : _b.trim()) || "element";
|
|
58
|
+
}
|
|
59
|
+
function referencedText(element, attribute) {
|
|
60
|
+
const ids = (element.getAttribute(attribute) ?? "").split(/\s+/).filter(Boolean);
|
|
61
|
+
return ids.map((id) => {
|
|
62
|
+
const named = element.ownerDocument.getElementById(id);
|
|
63
|
+
return named ? shownText(named) : "";
|
|
64
|
+
}).join(" ").trim();
|
|
65
|
+
}
|
|
66
|
+
function labelsText(control) {
|
|
67
|
+
if (!control) return "";
|
|
68
|
+
const labels = control.labels;
|
|
69
|
+
if (!labels) return "";
|
|
70
|
+
return Array.from(labels, shownText).join(" ").trim();
|
|
71
|
+
}
|
|
72
|
+
function shownText(element) {
|
|
73
|
+
let text = "";
|
|
74
|
+
for (const node of Array.from(element.childNodes)) {
|
|
75
|
+
if (node.nodeType === 3) text += node.textContent ?? "";
|
|
76
|
+
else if (node.nodeType === 1) {
|
|
77
|
+
const child = node;
|
|
78
|
+
if (child.matches(CONTROL_SELECTOR) || tagOf(child) === "OPTION") continue;
|
|
79
|
+
if (child.hasAttribute(contract.AGENT_ATTR.sensitive)) continue;
|
|
80
|
+
text += ` ${shownText(child)}`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return text;
|
|
84
|
+
}
|
|
85
|
+
function labelOf({
|
|
86
|
+
element,
|
|
87
|
+
control,
|
|
88
|
+
sensitive,
|
|
89
|
+
fallback
|
|
90
|
+
}) {
|
|
91
|
+
const subject = control ?? element;
|
|
92
|
+
const candidates = [
|
|
93
|
+
element.getAttribute(contract.AGENT_ATTR.label),
|
|
94
|
+
element.getAttribute("aria-label"),
|
|
95
|
+
subject.getAttribute("aria-label"),
|
|
96
|
+
referencedText(element, "aria-labelledby"),
|
|
97
|
+
referencedText(subject, "aria-labelledby"),
|
|
98
|
+
labelsText(control),
|
|
99
|
+
subject.getAttribute("placeholder"),
|
|
100
|
+
element.getAttribute("title"),
|
|
101
|
+
subject.getAttribute("alt"),
|
|
102
|
+
// A button drawn by an `<input>` shows its `value` as its face.
|
|
103
|
+
!sensitive && tagOf(subject) === "INPUT" && roleOf(element, control) === "button" ? subject.getAttribute("value") : null,
|
|
104
|
+
// A control's own text is its value, never its name.
|
|
105
|
+
sensitive || control !== null && control === element ? null : shownText(element)
|
|
106
|
+
];
|
|
107
|
+
for (const candidate of candidates) {
|
|
108
|
+
const text = candidate ? clip(candidate, MAX_LABEL_CHARS) : "";
|
|
109
|
+
if (text) return text;
|
|
110
|
+
}
|
|
111
|
+
return fallback;
|
|
112
|
+
}
|
|
113
|
+
function ariaFlag(element, attribute) {
|
|
114
|
+
const value = element.getAttribute(attribute);
|
|
115
|
+
if (value === "true") return true;
|
|
116
|
+
if (value === "false") return false;
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
function isDisabled(element, control) {
|
|
120
|
+
const subject = control ?? element;
|
|
121
|
+
if (element.getAttribute("aria-disabled") === "true") return true;
|
|
122
|
+
if (subject.getAttribute("aria-disabled") === "true") return true;
|
|
123
|
+
try {
|
|
124
|
+
return subject.matches(":disabled") || element.matches(":disabled");
|
|
125
|
+
} catch {
|
|
126
|
+
return subject.disabled === true;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function styleHides(element) {
|
|
130
|
+
const view = element.ownerDocument.defaultView;
|
|
131
|
+
for (let node = element; node; node = node.parentElement) {
|
|
132
|
+
if (node.hasAttribute("hidden") || node.hasAttribute("inert")) return true;
|
|
133
|
+
if (node.getAttribute("aria-hidden") === "true") return true;
|
|
134
|
+
const style = view == null ? void 0 : view.getComputedStyle(node);
|
|
135
|
+
if (style && (style.display === "none" || style.visibility === "hidden")) return true;
|
|
136
|
+
}
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
function isRendered(element) {
|
|
140
|
+
if (!element.isConnected) return false;
|
|
141
|
+
const check = element.checkVisibility;
|
|
142
|
+
if (typeof check === "function") {
|
|
143
|
+
try {
|
|
144
|
+
if (!check.call(element, { visibilityProperty: true })) return false;
|
|
145
|
+
} catch {
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return !styleHides(element);
|
|
149
|
+
}
|
|
150
|
+
function isInViewport(element) {
|
|
151
|
+
const view = element.ownerDocument.defaultView;
|
|
152
|
+
if (!view) return false;
|
|
153
|
+
const rect = element.getBoundingClientRect();
|
|
154
|
+
if (rect.width <= 0 && rect.height <= 0) return false;
|
|
155
|
+
return rect.bottom > 0 && rect.right > 0 && rect.top < view.innerHeight && rect.left < view.innerWidth;
|
|
156
|
+
}
|
|
157
|
+
function selectOptions(control) {
|
|
158
|
+
const options = control.options;
|
|
159
|
+
if (!options) return [];
|
|
160
|
+
return Array.from(options, (option) => option.label || option.text || "").filter(Boolean);
|
|
161
|
+
}
|
|
162
|
+
function guarded(read, fallback) {
|
|
163
|
+
try {
|
|
164
|
+
return read();
|
|
165
|
+
} catch {
|
|
166
|
+
return fallback;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function controlValue(control) {
|
|
170
|
+
if (tagOf(control) === "SELECT") {
|
|
171
|
+
const select = control;
|
|
172
|
+
const chosen = Array.from(select.selectedOptions ?? [], (option) => option.label || option.text || "");
|
|
173
|
+
return chosen.length > 0 ? chosen.join(", ") : select.value ?? "";
|
|
174
|
+
}
|
|
175
|
+
if (isEditable(control)) return control.textContent ?? "";
|
|
176
|
+
return control.value ?? "";
|
|
177
|
+
}
|
|
178
|
+
function isReadOnly(control) {
|
|
179
|
+
if (!control) return false;
|
|
180
|
+
return control.hasAttribute("readonly") || control.getAttribute("aria-readonly") === "true";
|
|
181
|
+
}
|
|
182
|
+
function isFileInput(control) {
|
|
183
|
+
return control !== null && tagOf(control) === "INPUT" && inputType(control) === "file";
|
|
184
|
+
}
|
|
185
|
+
function derivedActions({
|
|
186
|
+
role,
|
|
187
|
+
control,
|
|
188
|
+
handlers
|
|
189
|
+
}) {
|
|
190
|
+
const actions = /* @__PURE__ */ new Set();
|
|
191
|
+
switch (isFileInput(control) ? "file" : role) {
|
|
192
|
+
case "button":
|
|
193
|
+
case "link":
|
|
194
|
+
case "tab":
|
|
195
|
+
case "menuitem":
|
|
196
|
+
case "option":
|
|
197
|
+
case "treeitem":
|
|
198
|
+
actions.add("click");
|
|
199
|
+
break;
|
|
200
|
+
case "textbox":
|
|
201
|
+
case "searchbox":
|
|
202
|
+
case "spinbutton":
|
|
203
|
+
if (!isReadOnly(control)) {
|
|
204
|
+
actions.add("type");
|
|
205
|
+
actions.add("clear");
|
|
206
|
+
}
|
|
207
|
+
break;
|
|
208
|
+
case "combobox":
|
|
209
|
+
case "listbox":
|
|
210
|
+
if (control && tagOf(control) === "SELECT") actions.add("select");
|
|
211
|
+
else if (control) {
|
|
212
|
+
actions.add("type");
|
|
213
|
+
actions.add("clear");
|
|
214
|
+
}
|
|
215
|
+
break;
|
|
216
|
+
case "checkbox":
|
|
217
|
+
case "radio":
|
|
218
|
+
case "switch":
|
|
219
|
+
actions.add("toggle");
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
if (handlers == null ? void 0 : handlers.click) actions.add("click");
|
|
223
|
+
if (handlers == null ? void 0 : handlers.type) actions.add("type");
|
|
224
|
+
if (handlers == null ? void 0 : handlers.select) actions.add("select");
|
|
225
|
+
if (handlers == null ? void 0 : handlers.toggle) actions.add("toggle");
|
|
226
|
+
if (handlers == null ? void 0 : handlers.clear) actions.add("clear");
|
|
227
|
+
return Array.from(actions);
|
|
228
|
+
}
|
|
229
|
+
function declaredActions(element) {
|
|
230
|
+
const declared = element.getAttribute(contract.AGENT_ATTR.actions);
|
|
231
|
+
if (declared === null) return null;
|
|
232
|
+
return declared.split(/\s+/).filter(contract.isAgentTargetAction);
|
|
233
|
+
}
|
|
234
|
+
function snapshotTarget(element) {
|
|
235
|
+
var _a, _b;
|
|
236
|
+
const id = element.getAttribute(contract.AGENT_ATTR.target) ?? "";
|
|
237
|
+
const key = element.getAttribute(contract.AGENT_ATTR.key) || null;
|
|
238
|
+
const ref = contract.agentTargetRef({ id, key });
|
|
239
|
+
const handlers = contract.agentTargetHandlers(ref) ?? (key ? contract.agentTargetHandlers(id) : void 0);
|
|
240
|
+
const control = controlOf(element);
|
|
241
|
+
const role = roleOf(element, control);
|
|
242
|
+
const danger = element.hasAttribute(contract.AGENT_ATTR.danger);
|
|
243
|
+
const sensitive = element.hasAttribute(contract.AGENT_ATTR.sensitive) || control !== null && secretFields.isSecretField(control);
|
|
244
|
+
const declared = declaredActions(element);
|
|
245
|
+
const allowed = declared ?? (danger ? [] : derivedActions({ role, control, handlers }));
|
|
246
|
+
const actions = sensitive ? allowed.filter((action) => action !== "type") : allowed;
|
|
247
|
+
const raw = (handlers == null ? void 0 : handlers.value) ? guarded(() => {
|
|
248
|
+
var _a2;
|
|
249
|
+
return ((_a2 = handlers.value) == null ? void 0 : _a2.call(handlers)) ?? null;
|
|
250
|
+
}, null) : control ? controlValue(control) : null;
|
|
251
|
+
const isCheckable = role === "checkbox" || role === "radio" || role === "switch";
|
|
252
|
+
const checkedProperty = control && tagOf(control) === "INPUT" && (inputType(control) === "checkbox" || inputType(control) === "radio") ? control.checked === true : null;
|
|
253
|
+
const everyOption = (handlers == null ? void 0 : handlers.options) ? guarded(() => {
|
|
254
|
+
var _a2;
|
|
255
|
+
return Array.from(((_a2 = handlers.options) == null ? void 0 : _a2.call(handlers)) ?? [], String);
|
|
256
|
+
}, []) : control && tagOf(control) === "SELECT" ? selectOptions(control) : null;
|
|
257
|
+
const options = everyOption ? everyOption.map((option) => clip(option, MAX_OPTION_CHARS)).filter(Boolean).slice(0, MAX_OPTIONS) : null;
|
|
258
|
+
return {
|
|
259
|
+
ref,
|
|
260
|
+
id,
|
|
261
|
+
key,
|
|
262
|
+
element,
|
|
263
|
+
control,
|
|
264
|
+
role,
|
|
265
|
+
label: labelOf({ element, control, sensitive, fallback: ref }),
|
|
266
|
+
description: clip(element.getAttribute(contract.AGENT_ATTR.description) ?? "", MAX_DESCRIPTION_CHARS) || null,
|
|
267
|
+
region: ((_b = (_a = element.parentElement) == null ? void 0 : _a.closest(`[${contract.AGENT_ATTR.region}]`)) == null ? void 0 : _b.getAttribute(contract.AGENT_ATTR.region)) ?? null,
|
|
268
|
+
actions,
|
|
269
|
+
danger,
|
|
270
|
+
sensitive,
|
|
271
|
+
disabled: isDisabled(element, control),
|
|
272
|
+
visible: isRendered(element),
|
|
273
|
+
onScreen: isInViewport(element),
|
|
274
|
+
value: sensitive || isCheckable || raw === null ? null : clip(raw, MAX_VALUE_CHARS),
|
|
275
|
+
filled: sensitive && raw !== null ? raw !== "" : null,
|
|
276
|
+
checked: checkedProperty ?? ariaFlag(element, "aria-checked") ?? ariaFlag(element, "aria-pressed"),
|
|
277
|
+
selected: ariaFlag(element, "aria-selected") ?? (element.hasAttribute("aria-current") ? element.getAttribute("aria-current") !== "false" : null),
|
|
278
|
+
expanded: ariaFlag(element, "aria-expanded"),
|
|
279
|
+
options: options && options.length > 0 ? options : null,
|
|
280
|
+
optionsOmitted: everyOption && options ? Math.max(0, everyOption.length - options.length) : 0,
|
|
281
|
+
readOnly: isReadOnly(control),
|
|
282
|
+
handlers
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
function findTargetElements(doc) {
|
|
286
|
+
return Array.from(doc.querySelectorAll(`[${contract.AGENT_ATTR.target}]`)).filter(
|
|
287
|
+
(element) => element.getAttribute(contract.AGENT_ATTR.target) !== "" && !element.closest(`[${contract.AGENT_ATTR.layer}]`)
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
function snapshotTargets(doc) {
|
|
291
|
+
return findTargetElements(doc).map(snapshotTarget).filter((target) => target.visible && target.ref.length <= MAX_REF_CHARS);
|
|
292
|
+
}
|
|
293
|
+
function suggestions(targets, wanted) {
|
|
294
|
+
const words = wanted.toLowerCase().split(/[^a-z0-9]+/).filter((word) => word.length > 2);
|
|
295
|
+
const refs = Array.from(new Set(targets.map((target) => target.ref)));
|
|
296
|
+
const close = refs.filter((ref) => words.some((word) => ref.toLowerCase().includes(word)));
|
|
297
|
+
const listed = (close.length > 0 ? close : refs).slice(0, 8);
|
|
298
|
+
if (listed.length === 0) return "This screen has no targets.";
|
|
299
|
+
return `${close.length > 0 ? "Closest here" : "Targets here"}: ${listed.join(", ")}${refs.length > listed.length ? ", …" : ""}.`;
|
|
300
|
+
}
|
|
301
|
+
function resolveTarget({
|
|
302
|
+
doc,
|
|
303
|
+
ref,
|
|
304
|
+
allowLabel = false,
|
|
305
|
+
exact = false
|
|
306
|
+
}) {
|
|
307
|
+
const parsed = contract.parseAgentTargetRef(ref);
|
|
308
|
+
if (!parsed) throw new AgentUiError(`"${ref}" is not a target. Give a target from read_screen.`);
|
|
309
|
+
const all = snapshotTargets(doc);
|
|
310
|
+
const byId = all.filter(
|
|
311
|
+
(target) => target.id === parsed.id && (parsed.key === null || target.key === parsed.key)
|
|
312
|
+
);
|
|
313
|
+
let matches = byId;
|
|
314
|
+
if (matches.length === 0 && allowLabel) {
|
|
315
|
+
const wanted = ref.trim().toLowerCase();
|
|
316
|
+
const exact2 = all.filter((target) => target.label.toLowerCase() === wanted);
|
|
317
|
+
const partial = all.filter((target) => target.label.toLowerCase().includes(wanted));
|
|
318
|
+
matches = exact2.length > 0 ? exact2 : partial.length === 1 ? partial : [];
|
|
319
|
+
}
|
|
320
|
+
const found = matches.find((target) => target.onScreen) ?? matches[0];
|
|
321
|
+
if (!found) {
|
|
322
|
+
throw new AgentUiError(
|
|
323
|
+
`No target "${ref}" is on the screen now. ${suggestions(all, ref)} Call read_screen for what is here; a target on another page appears once that page is open.`
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
if (exact && matches.length > 1) {
|
|
327
|
+
const keyed = Array.from(new Set(matches.map((target) => target.ref))).filter((other) => other !== ref);
|
|
328
|
+
throw new AgentUiError(
|
|
329
|
+
keyed.length > 0 ? `${matches.length} targets share "${ref}". Name the one you mean by its key, e.g. ${keyed.slice(0, 5).join(", ")}.` : `${matches.length} elements on the screen are all "${ref}", so it cannot be operated by name. Point at it instead, and let the person do it.`
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
return found;
|
|
333
|
+
}
|
|
334
|
+
async function waitForTarget({
|
|
335
|
+
doc,
|
|
336
|
+
ref,
|
|
337
|
+
timeoutMs,
|
|
338
|
+
signal,
|
|
339
|
+
allowLabel = false
|
|
340
|
+
}) {
|
|
341
|
+
const deadline = Date.now() + timeoutMs;
|
|
342
|
+
for (; ; ) {
|
|
343
|
+
try {
|
|
344
|
+
return resolveTarget({ doc, ref, allowLabel });
|
|
345
|
+
} catch (error) {
|
|
346
|
+
if (!(error instanceof AgentUiError)) throw error;
|
|
347
|
+
}
|
|
348
|
+
if ((signal == null ? void 0 : signal.aborted) || Date.now() >= deadline) return null;
|
|
349
|
+
await new Promise((resolve) => setTimeout(resolve, 120));
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function describeTarget(target) {
|
|
353
|
+
return {
|
|
354
|
+
target: target.ref,
|
|
355
|
+
role: target.role,
|
|
356
|
+
label: target.label,
|
|
357
|
+
...target.description ? { does: target.description } : {},
|
|
358
|
+
...target.value !== null ? { value: target.value } : {},
|
|
359
|
+
...target.filled !== null ? { filled: target.filled } : {},
|
|
360
|
+
...target.checked !== null ? { checked: target.checked } : {},
|
|
361
|
+
...target.selected ? { selected: true } : {},
|
|
362
|
+
...target.expanded !== null ? { expanded: target.expanded } : {},
|
|
363
|
+
...target.options ? { options: target.options } : {},
|
|
364
|
+
...target.optionsOmitted > 0 ? { more_options: target.optionsOmitted } : {},
|
|
365
|
+
...target.readOnly ? { read_only: true } : {},
|
|
366
|
+
can: target.actions.length > 0 ? target.actions : "point only",
|
|
367
|
+
...target.region ? { region: target.region } : {},
|
|
368
|
+
...target.disabled ? { disabled: true } : {},
|
|
369
|
+
...target.danger ? { danger: "the person confirms every action on it" } : {},
|
|
370
|
+
...target.sensitive ? { sensitive: "its value is never read or typed" } : {},
|
|
371
|
+
...target.onScreen ? {} : { scrolled_out_of_view: true }
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
const READ_SCREEN_MAX_TARGETS = 120;
|
|
375
|
+
const READ_SCREEN_MAX_PER_ID = 25;
|
|
376
|
+
const READ_SCREEN_MAX_CHARS = 24e3;
|
|
377
|
+
function readScreen({
|
|
378
|
+
doc,
|
|
379
|
+
region,
|
|
380
|
+
query,
|
|
381
|
+
maxChars = READ_SCREEN_MAX_CHARS
|
|
382
|
+
}) {
|
|
383
|
+
var _a;
|
|
384
|
+
const all = snapshotTargets(doc);
|
|
385
|
+
const wantedRegion = region == null ? void 0 : region.trim();
|
|
386
|
+
const wantedText = query == null ? void 0 : query.trim().toLowerCase();
|
|
387
|
+
const matching = all.filter((target) => {
|
|
388
|
+
if (wantedRegion && target.region !== wantedRegion) return false;
|
|
389
|
+
if (!wantedText) return true;
|
|
390
|
+
return [target.ref, target.label, target.description ?? "", target.value ?? ""].some(
|
|
391
|
+
(text) => text.toLowerCase().includes(wantedText)
|
|
392
|
+
);
|
|
393
|
+
});
|
|
394
|
+
const perId = /* @__PURE__ */ new Map();
|
|
395
|
+
const overPerId = /* @__PURE__ */ new Map();
|
|
396
|
+
const withinPerId = matching.filter((target) => {
|
|
397
|
+
const seen = (perId.get(target.id) ?? 0) + 1;
|
|
398
|
+
perId.set(target.id, seen);
|
|
399
|
+
if (seen <= READ_SCREEN_MAX_PER_ID) return true;
|
|
400
|
+
overPerId.set(target.id, (overPerId.get(target.id) ?? 0) + 1);
|
|
401
|
+
return false;
|
|
402
|
+
});
|
|
403
|
+
let kept = withinPerId;
|
|
404
|
+
if (kept.length > READ_SCREEN_MAX_TARGETS) {
|
|
405
|
+
const inView = kept.filter((target) => target.onScreen).slice(0, READ_SCREEN_MAX_TARGETS);
|
|
406
|
+
const room = READ_SCREEN_MAX_TARGETS - inView.length;
|
|
407
|
+
const rest = new Set(kept.filter((target) => !target.onScreen).slice(0, room));
|
|
408
|
+
const chosen = new Set(inView);
|
|
409
|
+
kept = kept.filter((target) => chosen.has(target) || rest.has(target));
|
|
410
|
+
}
|
|
411
|
+
const regions = /* @__PURE__ */ new Map();
|
|
412
|
+
for (const target of all) {
|
|
413
|
+
if (!target.region) continue;
|
|
414
|
+
const entry = regions.get(target.region);
|
|
415
|
+
if (entry) entry.targets += 1;
|
|
416
|
+
else {
|
|
417
|
+
const element = (_a = target.element.parentElement) == null ? void 0 : _a.closest(`[${contract.AGENT_ATTR.region}]`);
|
|
418
|
+
const label = (element == null ? void 0 : element.getAttribute(contract.AGENT_ATTR.label)) ?? (element == null ? void 0 : element.getAttribute("aria-label"));
|
|
419
|
+
regions.set(target.region, { label: label ? clip(label, MAX_LABEL_CHARS) : null, targets: 1 });
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
const view = doc.defaultView;
|
|
423
|
+
const listed = kept.map(describeTarget);
|
|
424
|
+
const result = (targets) => {
|
|
425
|
+
const omitted = matching.length - targets.length;
|
|
426
|
+
return {
|
|
427
|
+
page: {
|
|
428
|
+
title: clip(doc.title ?? "", MAX_LABEL_CHARS),
|
|
429
|
+
...view ? { path: `${view.location.pathname}${view.location.search}` } : {}
|
|
430
|
+
},
|
|
431
|
+
...regions.size > 0 ? {
|
|
432
|
+
regions: Array.from(regions, ([id, entry]) => ({
|
|
433
|
+
region: id,
|
|
434
|
+
...entry.label ? { label: entry.label } : {},
|
|
435
|
+
targets: entry.targets
|
|
436
|
+
})).slice(0, MAX_REGIONS),
|
|
437
|
+
...regions.size > MAX_REGIONS ? { more_regions: regions.size - MAX_REGIONS } : {}
|
|
438
|
+
} : {},
|
|
439
|
+
targets,
|
|
440
|
+
...overPerId.size > 0 ? {
|
|
441
|
+
more_sharing_an_id: Object.fromEntries(overPerId)
|
|
442
|
+
} : {},
|
|
443
|
+
...omitted > 0 ? {
|
|
444
|
+
omitted,
|
|
445
|
+
note: "Not every target fits one read: narrow it with `region` or `query`."
|
|
446
|
+
} : {},
|
|
447
|
+
...all.length === 0 ? { note: "This screen has no agent targets: nothing here can be pointed at or operated." } : matching.length === 0 ? { note: "No target matches. Read again without `region` and `query`." } : {}
|
|
448
|
+
};
|
|
449
|
+
};
|
|
450
|
+
let answer = result(listed);
|
|
451
|
+
while (listed.length > 0 && JSON.stringify(answer).length > maxChars) {
|
|
452
|
+
listed.pop();
|
|
453
|
+
answer = result(listed);
|
|
454
|
+
}
|
|
455
|
+
return answer;
|
|
456
|
+
}
|
|
457
|
+
exports.AgentUiError = AgentUiError;
|
|
458
|
+
exports.READ_SCREEN_MAX_CHARS = READ_SCREEN_MAX_CHARS;
|
|
459
|
+
exports.READ_SCREEN_MAX_PER_ID = READ_SCREEN_MAX_PER_ID;
|
|
460
|
+
exports.READ_SCREEN_MAX_TARGETS = READ_SCREEN_MAX_TARGETS;
|
|
461
|
+
exports.controlOf = controlOf;
|
|
462
|
+
exports.describeTarget = describeTarget;
|
|
463
|
+
exports.findTargetElements = findTargetElements;
|
|
464
|
+
exports.isInViewport = isInViewport;
|
|
465
|
+
exports.isRendered = isRendered;
|
|
466
|
+
exports.readScreen = readScreen;
|
|
467
|
+
exports.resolveTarget = resolveTarget;
|
|
468
|
+
exports.snapshotTarget = snapshotTarget;
|
|
469
|
+
exports.snapshotTargets = snapshotTargets;
|
|
470
|
+
exports.waitForTarget = waitForTarget;
|
|
471
|
+
//# sourceMappingURL=screen.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screen.cjs","sources":["../../src/agent-ui/screen.ts"],"sourcesContent":["/*\n * Reading the screen: the agent targets in a document, each with the name,\n * state and allowed actions the agent is told, and the lookup the tools use to\n * turn the agent's reference back into an element.\n *\n * Read at call time, never cached: the document is the registry, so a target\n * that is not in it is not on the screen.\n */\n\nimport {\n AGENT_ATTR,\n type AgentTargetAction,\n type AgentTargetHandlers,\n agentTargetHandlers,\n agentTargetRef,\n isAgentTargetAction,\n parseAgentTargetRef,\n} from './contract';\nimport { isSecretField } from './secret-fields';\n\n/** A refusal or a miss the agent is told about, in words it can act on. */\nexport class AgentUiError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'AgentUiError';\n }\n}\n\n/** One target as it is on screen now. */\nexport interface AgentTargetSnapshot {\n /** How the agent names it: `id`, or `id[key]`. */\n ref: string;\n id: string;\n key: string | null;\n element: Element;\n /** The form control the target stands for: itself, or the one inside it. */\n control: Element | null;\n role: string;\n label: string;\n description: string | null;\n region: string | null;\n /** What the agent may do to it. Empty: it may only be pointed at. */\n actions: readonly AgentTargetAction[];\n danger: boolean;\n sensitive: boolean;\n disabled: boolean;\n /** Rendered (not hidden by the page). */\n visible: boolean;\n /** Inside the viewport now. */\n onScreen: boolean;\n /** Its value, absent for a sensitive target and for one that has none. */\n value: string | null;\n /** For a sensitive field: whether it holds anything. */\n filled: boolean | null;\n checked: boolean | null;\n selected: boolean | null;\n expanded: boolean | null;\n /** What a `select` may be given. */\n options: readonly string[] | null;\n /** How many more options there are than `options` lists. */\n optionsOmitted: number;\n /** A field the page shows but does not let anyone edit. */\n readOnly: boolean;\n handlers: AgentTargetHandlers | undefined;\n}\n\nconst CONTROL_SELECTOR =\n 'input:not([type=\"hidden\"]), textarea, select, [contenteditable=\"\"], [contenteditable=\"true\"]';\n\nconst MAX_LABEL_CHARS = 80;\nconst MAX_VALUE_CHARS = 200;\nconst MAX_DESCRIPTION_CHARS = 240;\nconst MAX_OPTIONS = 30;\nconst MAX_OPTION_CHARS = 60;\n/** A reference longer than this is not one the agent could pass back reliably. */\nconst MAX_REF_CHARS = 160;\n/** The most regions one read names. */\nconst MAX_REGIONS = 40;\n\nconst tagOf = (element: Element): string => element.tagName.toUpperCase();\n\nfunction clip(text: string, max: number): string {\n const collapsed = text.replace(/\\s+/g, ' ').trim();\n return collapsed.length > max ? `${collapsed.slice(0, max - 1)}…` : collapsed;\n}\n\nfunction inputType(element: Element): string {\n return (element.getAttribute('type') ?? 'text').toLowerCase();\n}\n\nfunction isEditable(element: Element): boolean {\n const editable = element.getAttribute('contenteditable');\n return editable === '' || editable === 'true';\n}\n\n/** The form control a target stands for: itself, or the first one inside it. */\nexport function controlOf(element: Element): Element | null {\n if (element.matches(CONTROL_SELECTOR)) return element;\n const tag = tagOf(element);\n if (tag === 'BUTTON' || tag === 'A') return null;\n return element.querySelector(CONTROL_SELECTOR);\n}\n\nfunction roleOf(element: Element, control: Element | null): string {\n const explicit = element.getAttribute('role')?.trim();\n if (explicit) return explicit;\n const subject = control ?? element;\n const tag = tagOf(subject);\n if (tag === 'BUTTON' || tag === 'SUMMARY') return 'button';\n if (tag === 'A') return subject.hasAttribute('href') ? 'link' : 'element';\n if (tag === 'SELECT') return 'combobox';\n if (tag === 'TEXTAREA') return 'textbox';\n if (tag === 'INPUT') {\n const type = inputType(subject);\n if (type === 'checkbox') return 'checkbox';\n if (type === 'radio') return 'radio';\n if (type === 'range') return 'slider';\n if (type === 'button' || type === 'submit' || type === 'reset' || type === 'image') return 'button';\n if (type === 'search') return 'searchbox';\n return 'textbox';\n }\n if (isEditable(subject)) return 'textbox';\n return subject.getAttribute('role')?.trim() || 'element';\n}\n\nfunction referencedText(element: Element, attribute: string): string {\n const ids = (element.getAttribute(attribute) ?? '').split(/\\s+/).filter(Boolean);\n return ids\n .map((id) => {\n const named = element.ownerDocument.getElementById(id);\n return named ? shownText(named) : '';\n })\n .join(' ')\n .trim();\n}\n\nfunction labelsText(control: Element | null): string {\n if (!control) return '';\n const labels = (control as { labels?: ArrayLike<Element> | null }).labels;\n if (!labels) return '';\n // A `<label>` wrapped around its field holds the field's contents too.\n return Array.from(labels, shownText).join(' ').trim();\n}\n\n/**\n * The words an element shows, without what any field inside it holds: a\n * field's contents are its value, never part of a name.\n */\nfunction shownText(element: Element): string {\n let text = '';\n for (const node of Array.from(element.childNodes)) {\n if (node.nodeType === 3) text += node.textContent ?? '';\n else if (node.nodeType === 1) {\n const child = node as Element;\n if (child.matches(CONTROL_SELECTOR) || tagOf(child) === 'OPTION') continue;\n // Nor what the app marked sensitive inside it: a secret shown as text\n // must not ride out in the name of the element around it.\n if (child.hasAttribute(AGENT_ATTR.sensitive)) continue;\n text += ` ${shownText(child)}`;\n }\n }\n return text;\n}\n\nfunction labelOf({\n element,\n control,\n sensitive,\n fallback,\n}: {\n element: Element;\n control: Element | null;\n /** The target's own text is then a secret, so only what names it is used. */\n sensitive: boolean;\n fallback: string;\n}): string {\n const subject = control ?? element;\n const candidates = [\n element.getAttribute(AGENT_ATTR.label),\n element.getAttribute('aria-label'),\n subject.getAttribute('aria-label'),\n referencedText(element, 'aria-labelledby'),\n referencedText(subject, 'aria-labelledby'),\n labelsText(control),\n subject.getAttribute('placeholder'),\n element.getAttribute('title'),\n subject.getAttribute('alt'),\n // A button drawn by an `<input>` shows its `value` as its face.\n !sensitive && tagOf(subject) === 'INPUT' && roleOf(element, control) === 'button'\n ? subject.getAttribute('value')\n : null,\n // A control's own text is its value, never its name.\n sensitive || (control !== null && control === element) ? null : shownText(element),\n ];\n for (const candidate of candidates) {\n const text = candidate ? clip(candidate, MAX_LABEL_CHARS) : '';\n if (text) return text;\n }\n return fallback;\n}\n\nfunction ariaFlag(element: Element, attribute: string): boolean | null {\n const value = element.getAttribute(attribute);\n if (value === 'true') return true;\n if (value === 'false') return false;\n return null;\n}\n\nfunction isDisabled(element: Element, control: Element | null): boolean {\n const subject = control ?? element;\n if (element.getAttribute('aria-disabled') === 'true') return true;\n if (subject.getAttribute('aria-disabled') === 'true') return true;\n try {\n return subject.matches(':disabled') || element.matches(':disabled');\n } catch {\n return (subject as { disabled?: boolean }).disabled === true;\n }\n}\n\nfunction styleHides(element: Element): boolean {\n const view = element.ownerDocument.defaultView;\n for (let node: Element | null = element; node; node = node.parentElement) {\n if (node.hasAttribute('hidden') || node.hasAttribute('inert')) return true;\n if (node.getAttribute('aria-hidden') === 'true') return true;\n const style = view?.getComputedStyle(node);\n if (style && (style.display === 'none' || style.visibility === 'hidden')) return true;\n }\n return false;\n}\n\n/** Whether the page renders the element at all. */\nexport function isRendered(element: Element): boolean {\n if (!element.isConnected) return false;\n const check = (element as { checkVisibility?: (options?: object) => boolean }).checkVisibility;\n if (typeof check === 'function') {\n try {\n if (!check.call(element, { visibilityProperty: true })) return false;\n } catch {\n /* fall through to the style walk */\n }\n }\n return !styleHides(element);\n}\n\n/** Whether any of the element is inside the viewport now. */\nexport function isInViewport(element: Element): boolean {\n const view = element.ownerDocument.defaultView;\n if (!view) return false;\n const rect = element.getBoundingClientRect();\n if (rect.width <= 0 && rect.height <= 0) return false;\n return (\n rect.bottom > 0 && rect.right > 0 && rect.top < view.innerHeight && rect.left < view.innerWidth\n );\n}\n\nfunction selectOptions(control: Element): string[] {\n const options = (control as { options?: ArrayLike<{ label?: string; text?: string }> }).options;\n if (!options) return [];\n return Array.from(options, (option) => option.label || option.text || '').filter(Boolean);\n}\n\n/** `read()`, or `fallback` when it throws. */\nfunction guarded<T>(read: () => T, fallback: T): T {\n try {\n return read();\n } catch {\n return fallback;\n }\n}\n\nfunction controlValue(control: Element): string {\n if (tagOf(control) === 'SELECT') {\n const select = control as unknown as {\n selectedOptions?: ArrayLike<{ label?: string; text?: string }>;\n value?: string;\n };\n const chosen = Array.from(select.selectedOptions ?? [], (option) => option.label || option.text || '');\n return chosen.length > 0 ? chosen.join(', ') : (select.value ?? '');\n }\n if (isEditable(control)) return control.textContent ?? '';\n return (control as { value?: string }).value ?? '';\n}\n\nfunction isReadOnly(control: Element | null): boolean {\n if (!control) return false;\n return control.hasAttribute('readonly') || control.getAttribute('aria-readonly') === 'true';\n}\n\n/** A file input opens the system's file picker, which only the person can use. */\nfunction isFileInput(control: Element | null): boolean {\n return control !== null && tagOf(control) === 'INPUT' && inputType(control) === 'file';\n}\n\nfunction derivedActions({\n role,\n control,\n handlers,\n}: {\n role: string;\n control: Element | null;\n handlers: AgentTargetHandlers | undefined;\n}): AgentTargetAction[] {\n const actions = new Set<AgentTargetAction>();\n switch (isFileInput(control) ? 'file' : role) {\n case 'button':\n case 'link':\n case 'tab':\n case 'menuitem':\n case 'option':\n case 'treeitem':\n actions.add('click');\n break;\n case 'textbox':\n case 'searchbox':\n case 'spinbutton':\n if (!isReadOnly(control)) {\n actions.add('type');\n actions.add('clear');\n }\n break;\n case 'combobox':\n case 'listbox':\n if (control && tagOf(control) === 'SELECT') actions.add('select');\n else if (control) {\n actions.add('type');\n actions.add('clear');\n }\n break;\n case 'checkbox':\n case 'radio':\n case 'switch':\n actions.add('toggle');\n break;\n default:\n break;\n }\n if (handlers?.click) actions.add('click');\n if (handlers?.type) actions.add('type');\n if (handlers?.select) actions.add('select');\n if (handlers?.toggle) actions.add('toggle');\n if (handlers?.clear) actions.add('clear');\n return Array.from(actions);\n}\n\nfunction declaredActions(element: Element): AgentTargetAction[] | null {\n const declared = element.getAttribute(AGENT_ATTR.actions);\n if (declared === null) return null;\n return declared.split(/\\s+/).filter(isAgentTargetAction);\n}\n\n/** One target element as the agent is told about it. */\nexport function snapshotTarget(element: Element): AgentTargetSnapshot {\n const id = element.getAttribute(AGENT_ATTR.target) ?? '';\n const key = element.getAttribute(AGENT_ATTR.key) || null;\n const ref = agentTargetRef({ id, key });\n const handlers = agentTargetHandlers(ref) ?? (key ? agentTargetHandlers(id) : undefined);\n const control = controlOf(element);\n const role = roleOf(element, control);\n const danger = element.hasAttribute(AGENT_ATTR.danger);\n const sensitive =\n element.hasAttribute(AGENT_ATTR.sensitive) || (control !== null && isSecretField(control));\n const declared = declaredActions(element);\n // A dangerous target allows only what the app spelled out for it.\n const allowed = declared ?? (danger ? [] : derivedActions({ role, control, handlers }));\n const actions = sensitive ? allowed.filter((action) => action !== 'type') : allowed;\n\n // A target's own handlers are the app's code: one that throws costs that\n // target its value or options, never the whole read. Where a handler answers\n // for the value, its answer stands (`null`: it has none to show).\n const raw = handlers?.value\n ? guarded(() => handlers.value?.() ?? null, null)\n : control\n ? controlValue(control)\n : null;\n const isCheckable = role === 'checkbox' || role === 'radio' || role === 'switch';\n const checkedProperty =\n control && tagOf(control) === 'INPUT' && (inputType(control) === 'checkbox' || inputType(control) === 'radio')\n ? (control as { checked?: boolean }).checked === true\n : null;\n\n const everyOption = handlers?.options\n ? guarded(() => Array.from(handlers.options?.() ?? [], String), [])\n : control && tagOf(control) === 'SELECT'\n ? selectOptions(control)\n : null;\n const options = everyOption\n ? everyOption\n .map((option) => clip(option, MAX_OPTION_CHARS))\n .filter(Boolean)\n .slice(0, MAX_OPTIONS)\n : null;\n\n return {\n ref,\n id,\n key,\n element,\n control,\n role,\n label: labelOf({ element, control, sensitive, fallback: ref }),\n description: clip(element.getAttribute(AGENT_ATTR.description) ?? '', MAX_DESCRIPTION_CHARS) || null,\n region: element.parentElement?.closest(`[${AGENT_ATTR.region}]`)?.getAttribute(AGENT_ATTR.region) ?? null,\n actions,\n danger,\n sensitive,\n disabled: isDisabled(element, control),\n visible: isRendered(element),\n onScreen: isInViewport(element),\n value: sensitive || isCheckable || raw === null ? null : clip(raw, MAX_VALUE_CHARS),\n filled: sensitive && raw !== null ? raw !== '' : null,\n checked: checkedProperty ?? ariaFlag(element, 'aria-checked') ?? ariaFlag(element, 'aria-pressed'),\n selected:\n ariaFlag(element, 'aria-selected') ??\n (element.hasAttribute('aria-current') ? element.getAttribute('aria-current') !== 'false' : null),\n expanded: ariaFlag(element, 'aria-expanded'),\n options: options && options.length > 0 ? options : null,\n optionsOmitted: everyOption && options ? Math.max(0, everyOption.length - options.length) : 0,\n readOnly: isReadOnly(control),\n handlers,\n };\n}\n\n/** Every target element in the document, in document order. The overlay's own are never targets. */\nexport function findTargetElements(doc: Document): Element[] {\n return Array.from(doc.querySelectorAll(`[${AGENT_ATTR.target}]`)).filter(\n (element) =>\n element.getAttribute(AGENT_ATTR.target) !== '' && !element.closest(`[${AGENT_ATTR.layer}]`),\n );\n}\n\n/** Every rendered target, in document order. */\nexport function snapshotTargets(doc: Document): AgentTargetSnapshot[] {\n return findTargetElements(doc)\n .map(snapshotTarget)\n .filter((target) => target.visible && target.ref.length <= MAX_REF_CHARS);\n}\n\nfunction suggestions(targets: readonly AgentTargetSnapshot[], wanted: string): string {\n const words = wanted.toLowerCase().split(/[^a-z0-9]+/).filter((word) => word.length > 2);\n const refs = Array.from(new Set(targets.map((target) => target.ref)));\n const close = refs.filter((ref) => words.some((word) => ref.toLowerCase().includes(word)));\n const listed = (close.length > 0 ? close : refs).slice(0, 8);\n if (listed.length === 0) return 'This screen has no targets.';\n return `${close.length > 0 ? 'Closest here' : 'Targets here'}: ${listed.join(', ')}${refs.length > listed.length ? ', …' : ''}.`;\n}\n\n/**\n * The target the agent named. By id (and key); a pointing tool may also name\n * it by its label. Throws an {@link AgentUiError} that says what is on screen\n * instead, so the agent can correct itself in one step.\n */\nexport function resolveTarget({\n doc,\n ref,\n allowLabel = false,\n exact = false,\n}: {\n doc: Document;\n ref: string;\n allowLabel?: boolean;\n /**\n * The reference must name one target and no other (a tool about to act):\n * several targets sharing the id is then a question for the agent, not a\n * choice made for it.\n */\n exact?: boolean;\n}): AgentTargetSnapshot {\n const parsed = parseAgentTargetRef(ref);\n if (!parsed) throw new AgentUiError(`\"${ref}\" is not a target. Give a target from read_screen.`);\n const all = snapshotTargets(doc);\n const byId = all.filter(\n (target) => target.id === parsed.id && (parsed.key === null || target.key === parsed.key),\n );\n let matches = byId;\n if (matches.length === 0 && allowLabel) {\n const wanted = ref.trim().toLowerCase();\n const exact = all.filter((target) => target.label.toLowerCase() === wanted);\n const partial = all.filter((target) => target.label.toLowerCase().includes(wanted));\n matches = exact.length > 0 ? exact : partial.length === 1 ? partial : [];\n }\n const found = matches.find((target) => target.onScreen) ?? matches[0];\n if (!found) {\n throw new AgentUiError(\n `No target \"${ref}\" is on the screen now. ${suggestions(all, ref)} Call read_screen for what is here; a target on another page appears once that page is open.`,\n );\n }\n if (exact && matches.length > 1) {\n const keyed = Array.from(new Set(matches.map((target) => target.ref))).filter((other) => other !== ref);\n throw new AgentUiError(\n keyed.length > 0\n ? `${matches.length} targets share \"${ref}\". Name the one you mean by its key, e.g. ${keyed.slice(0, 5).join(', ')}.`\n : `${matches.length} elements on the screen are all \"${ref}\", so it cannot be operated by name. Point at it instead, and let the person do it.`,\n );\n }\n return found;\n}\n\n/** Wait for a target to be on the page (a page that is still loading), or give up. */\nexport async function waitForTarget({\n doc,\n ref,\n timeoutMs,\n signal,\n allowLabel = false,\n}: {\n doc: Document;\n ref: string;\n timeoutMs: number;\n signal?: AbortSignal;\n allowLabel?: boolean;\n}): Promise<AgentTargetSnapshot | null> {\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n try {\n return resolveTarget({ doc, ref, allowLabel });\n } catch (error) {\n if (!(error instanceof AgentUiError)) throw error;\n }\n if (signal?.aborted || Date.now() >= deadline) return null;\n await new Promise((resolve) => setTimeout(resolve, 120));\n }\n}\n\n/** A target as the agent reads it: only what is set, in few words. */\nexport function describeTarget(target: AgentTargetSnapshot): Record<string, unknown> {\n return {\n target: target.ref,\n role: target.role,\n label: target.label,\n ...(target.description ? { does: target.description } : {}),\n ...(target.value !== null ? { value: target.value } : {}),\n ...(target.filled !== null ? { filled: target.filled } : {}),\n ...(target.checked !== null ? { checked: target.checked } : {}),\n ...(target.selected ? { selected: true } : {}),\n ...(target.expanded !== null ? { expanded: target.expanded } : {}),\n ...(target.options ? { options: target.options } : {}),\n ...(target.optionsOmitted > 0 ? { more_options: target.optionsOmitted } : {}),\n ...(target.readOnly ? { read_only: true } : {}),\n can: target.actions.length > 0 ? target.actions : 'point only',\n ...(target.region ? { region: target.region } : {}),\n ...(target.disabled ? { disabled: true } : {}),\n ...(target.danger ? { danger: 'the person confirms every action on it' } : {}),\n ...(target.sensitive ? { sensitive: 'its value is never read or typed' } : {}),\n ...(target.onScreen ? {} : { scrolled_out_of_view: true }),\n };\n}\n\n/** The most targets one read lists. */\nexport const READ_SCREEN_MAX_TARGETS = 120;\n/** The most targets sharing one id (a table's rows) one read lists. */\nexport const READ_SCREEN_MAX_PER_ID = 25;\n/** The most characters one read answers with, under the runtime's cap on a client tool's result. */\nexport const READ_SCREEN_MAX_CHARS = 24_000;\n\nexport interface ReadScreenArgs {\n doc: Document;\n /** Only the targets of this region. */\n region?: string;\n /** Only targets whose reference, label, description or value holds this text. */\n query?: string;\n /** The most characters the answer may take, when the caller adds to it. Defaults to the whole cap. */\n maxChars?: number;\n}\n\n/** What `read_screen` answers: the page, its regions and its targets, bounded. */\nexport function readScreen({\n doc,\n region,\n query,\n maxChars = READ_SCREEN_MAX_CHARS,\n}: ReadScreenArgs): Record<string, unknown> {\n const all = snapshotTargets(doc);\n const wantedRegion = region?.trim();\n const wantedText = query?.trim().toLowerCase();\n const matching = all.filter((target) => {\n if (wantedRegion && target.region !== wantedRegion) return false;\n if (!wantedText) return true;\n return [target.ref, target.label, target.description ?? '', target.value ?? ''].some((text) =>\n text.toLowerCase().includes(wantedText),\n );\n });\n\n // Rows of one table share an id: list the first of them and count the rest.\n const perId = new Map<string, number>();\n const overPerId = new Map<string, number>();\n const withinPerId = matching.filter((target) => {\n const seen = (perId.get(target.id) ?? 0) + 1;\n perId.set(target.id, seen);\n if (seen <= READ_SCREEN_MAX_PER_ID) return true;\n overPerId.set(target.id, (overPerId.get(target.id) ?? 0) + 1);\n return false;\n });\n\n // Over the cap, what is in view is kept first; the order stays the page's.\n let kept = withinPerId;\n if (kept.length > READ_SCREEN_MAX_TARGETS) {\n const inView = kept.filter((target) => target.onScreen).slice(0, READ_SCREEN_MAX_TARGETS);\n const room = READ_SCREEN_MAX_TARGETS - inView.length;\n const rest = new Set(kept.filter((target) => !target.onScreen).slice(0, room));\n const chosen = new Set(inView);\n kept = kept.filter((target) => chosen.has(target) || rest.has(target));\n }\n\n const regions = new Map<string, { label: string | null; targets: number }>();\n for (const target of all) {\n if (!target.region) continue;\n const entry = regions.get(target.region);\n if (entry) entry.targets += 1;\n else {\n const element = target.element.parentElement?.closest(`[${AGENT_ATTR.region}]`);\n const label = element?.getAttribute(AGENT_ATTR.label) ?? element?.getAttribute('aria-label');\n regions.set(target.region, { label: label ? clip(label, MAX_LABEL_CHARS) : null, targets: 1 });\n }\n }\n\n const view = doc.defaultView;\n const listed = kept.map(describeTarget);\n const result = (targets: readonly Record<string, unknown>[]): Record<string, unknown> => {\n const omitted = matching.length - targets.length;\n return {\n page: {\n title: clip(doc.title ?? '', MAX_LABEL_CHARS),\n ...(view ? { path: `${view.location.pathname}${view.location.search}` } : {}),\n },\n ...(regions.size > 0\n ? {\n regions: Array.from(regions, ([id, entry]) => ({\n region: id,\n ...(entry.label ? { label: entry.label } : {}),\n targets: entry.targets,\n })).slice(0, MAX_REGIONS),\n ...(regions.size > MAX_REGIONS ? { more_regions: regions.size - MAX_REGIONS } : {}),\n }\n : {}),\n targets,\n ...(overPerId.size > 0\n ? {\n more_sharing_an_id: Object.fromEntries(overPerId),\n }\n : {}),\n ...(omitted > 0\n ? {\n omitted,\n note: 'Not every target fits one read: narrow it with `region` or `query`.',\n }\n : {}),\n ...(all.length === 0\n ? { note: 'This screen has no agent targets: nothing here can be pointed at or operated.' }\n : matching.length === 0\n ? { note: 'No target matches. Read again without `region` and `query`.' }\n : {}),\n };\n };\n\n // Every field of a target is clipped, so targets are dropped from the end\n // until the answer fits; with none left it still says how many there are.\n let answer = result(listed);\n while (listed.length > 0 && JSON.stringify(answer).length > maxChars) {\n listed.pop();\n answer = result(listed);\n }\n return answer;\n}\n"],"names":["AGENT_ATTR","isAgentTargetAction","agentTargetRef","agentTargetHandlers","isSecretField","_a","parseAgentTargetRef","exact"],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { type AgentTargetAction, type AgentTargetHandlers } from './contract';
|
|
2
|
+
/** A refusal or a miss the agent is told about, in words it can act on. */
|
|
3
|
+
export declare class AgentUiError extends Error {
|
|
4
|
+
constructor(message: string);
|
|
5
|
+
}
|
|
6
|
+
/** One target as it is on screen now. */
|
|
7
|
+
export interface AgentTargetSnapshot {
|
|
8
|
+
/** How the agent names it: `id`, or `id[key]`. */
|
|
9
|
+
ref: string;
|
|
10
|
+
id: string;
|
|
11
|
+
key: string | null;
|
|
12
|
+
element: Element;
|
|
13
|
+
/** The form control the target stands for: itself, or the one inside it. */
|
|
14
|
+
control: Element | null;
|
|
15
|
+
role: string;
|
|
16
|
+
label: string;
|
|
17
|
+
description: string | null;
|
|
18
|
+
region: string | null;
|
|
19
|
+
/** What the agent may do to it. Empty: it may only be pointed at. */
|
|
20
|
+
actions: readonly AgentTargetAction[];
|
|
21
|
+
danger: boolean;
|
|
22
|
+
sensitive: boolean;
|
|
23
|
+
disabled: boolean;
|
|
24
|
+
/** Rendered (not hidden by the page). */
|
|
25
|
+
visible: boolean;
|
|
26
|
+
/** Inside the viewport now. */
|
|
27
|
+
onScreen: boolean;
|
|
28
|
+
/** Its value, absent for a sensitive target and for one that has none. */
|
|
29
|
+
value: string | null;
|
|
30
|
+
/** For a sensitive field: whether it holds anything. */
|
|
31
|
+
filled: boolean | null;
|
|
32
|
+
checked: boolean | null;
|
|
33
|
+
selected: boolean | null;
|
|
34
|
+
expanded: boolean | null;
|
|
35
|
+
/** What a `select` may be given. */
|
|
36
|
+
options: readonly string[] | null;
|
|
37
|
+
/** How many more options there are than `options` lists. */
|
|
38
|
+
optionsOmitted: number;
|
|
39
|
+
/** A field the page shows but does not let anyone edit. */
|
|
40
|
+
readOnly: boolean;
|
|
41
|
+
handlers: AgentTargetHandlers | undefined;
|
|
42
|
+
}
|
|
43
|
+
/** The form control a target stands for: itself, or the first one inside it. */
|
|
44
|
+
export declare function controlOf(element: Element): Element | null;
|
|
45
|
+
/** Whether the page renders the element at all. */
|
|
46
|
+
export declare function isRendered(element: Element): boolean;
|
|
47
|
+
/** Whether any of the element is inside the viewport now. */
|
|
48
|
+
export declare function isInViewport(element: Element): boolean;
|
|
49
|
+
/** One target element as the agent is told about it. */
|
|
50
|
+
export declare function snapshotTarget(element: Element): AgentTargetSnapshot;
|
|
51
|
+
/** Every target element in the document, in document order. The overlay's own are never targets. */
|
|
52
|
+
export declare function findTargetElements(doc: Document): Element[];
|
|
53
|
+
/** Every rendered target, in document order. */
|
|
54
|
+
export declare function snapshotTargets(doc: Document): AgentTargetSnapshot[];
|
|
55
|
+
/**
|
|
56
|
+
* The target the agent named. By id (and key); a pointing tool may also name
|
|
57
|
+
* it by its label. Throws an {@link AgentUiError} that says what is on screen
|
|
58
|
+
* instead, so the agent can correct itself in one step.
|
|
59
|
+
*/
|
|
60
|
+
export declare function resolveTarget({ doc, ref, allowLabel, exact, }: {
|
|
61
|
+
doc: Document;
|
|
62
|
+
ref: string;
|
|
63
|
+
allowLabel?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* The reference must name one target and no other (a tool about to act):
|
|
66
|
+
* several targets sharing the id is then a question for the agent, not a
|
|
67
|
+
* choice made for it.
|
|
68
|
+
*/
|
|
69
|
+
exact?: boolean;
|
|
70
|
+
}): AgentTargetSnapshot;
|
|
71
|
+
/** Wait for a target to be on the page (a page that is still loading), or give up. */
|
|
72
|
+
export declare function waitForTarget({ doc, ref, timeoutMs, signal, allowLabel, }: {
|
|
73
|
+
doc: Document;
|
|
74
|
+
ref: string;
|
|
75
|
+
timeoutMs: number;
|
|
76
|
+
signal?: AbortSignal;
|
|
77
|
+
allowLabel?: boolean;
|
|
78
|
+
}): Promise<AgentTargetSnapshot | null>;
|
|
79
|
+
/** A target as the agent reads it: only what is set, in few words. */
|
|
80
|
+
export declare function describeTarget(target: AgentTargetSnapshot): Record<string, unknown>;
|
|
81
|
+
/** The most targets one read lists. */
|
|
82
|
+
export declare const READ_SCREEN_MAX_TARGETS = 120;
|
|
83
|
+
/** The most targets sharing one id (a table's rows) one read lists. */
|
|
84
|
+
export declare const READ_SCREEN_MAX_PER_ID = 25;
|
|
85
|
+
/** The most characters one read answers with, under the runtime's cap on a client tool's result. */
|
|
86
|
+
export declare const READ_SCREEN_MAX_CHARS = 24000;
|
|
87
|
+
export interface ReadScreenArgs {
|
|
88
|
+
doc: Document;
|
|
89
|
+
/** Only the targets of this region. */
|
|
90
|
+
region?: string;
|
|
91
|
+
/** Only targets whose reference, label, description or value holds this text. */
|
|
92
|
+
query?: string;
|
|
93
|
+
/** The most characters the answer may take, when the caller adds to it. Defaults to the whole cap. */
|
|
94
|
+
maxChars?: number;
|
|
95
|
+
}
|
|
96
|
+
/** What `read_screen` answers: the page, its regions and its targets, bounded. */
|
|
97
|
+
export declare function readScreen({ doc, region, query, maxChars, }: ReadScreenArgs): Record<string, unknown>;
|