@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.
Files changed (105) hide show
  1. package/README.md +157 -5
  2. package/dist/agent-ui/AgentTarget.cjs +11 -0
  3. package/dist/agent-ui/AgentTarget.cjs.map +1 -0
  4. package/dist/agent-ui/AgentTarget.d.ts +17 -0
  5. package/dist/agent-ui/AgentTarget.js +11 -0
  6. package/dist/agent-ui/AgentTarget.js.map +1 -0
  7. package/dist/agent-ui/AgentUiLayer.cjs +387 -0
  8. package/dist/agent-ui/AgentUiLayer.cjs.map +1 -0
  9. package/dist/agent-ui/AgentUiLayer.d.ts +40 -0
  10. package/dist/agent-ui/AgentUiLayer.js +387 -0
  11. package/dist/agent-ui/AgentUiLayer.js.map +1 -0
  12. package/dist/agent-ui/contract.cjs +104 -0
  13. package/dist/agent-ui/contract.cjs.map +1 -0
  14. package/dist/agent-ui/contract.d.ts +113 -0
  15. package/dist/agent-ui/contract.js +104 -0
  16. package/dist/agent-ui/contract.js.map +1 -0
  17. package/dist/agent-ui/driver.cjs +335 -0
  18. package/dist/agent-ui/driver.cjs.map +1 -0
  19. package/dist/agent-ui/driver.d.ts +66 -0
  20. package/dist/agent-ui/driver.js +335 -0
  21. package/dist/agent-ui/driver.js.map +1 -0
  22. package/dist/agent-ui/index.cjs +33 -0
  23. package/dist/agent-ui/index.cjs.map +1 -0
  24. package/dist/agent-ui/index.d.ts +19 -0
  25. package/dist/agent-ui/index.js +33 -0
  26. package/dist/agent-ui/index.js.map +1 -0
  27. package/dist/agent-ui/pace.cjs +121 -0
  28. package/dist/agent-ui/pace.cjs.map +1 -0
  29. package/dist/agent-ui/pace.d.ts +66 -0
  30. package/dist/agent-ui/pace.js +121 -0
  31. package/dist/agent-ui/pace.js.map +1 -0
  32. package/dist/agent-ui/screen.cjs +471 -0
  33. package/dist/agent-ui/screen.cjs.map +1 -0
  34. package/dist/agent-ui/screen.d.ts +97 -0
  35. package/dist/agent-ui/screen.js +471 -0
  36. package/dist/agent-ui/screen.js.map +1 -0
  37. package/dist/agent-ui/secret-fields.cjs +50 -0
  38. package/dist/agent-ui/secret-fields.cjs.map +1 -0
  39. package/dist/agent-ui/secret-fields.d.ts +15 -0
  40. package/dist/agent-ui/secret-fields.js +50 -0
  41. package/dist/agent-ui/secret-fields.js.map +1 -0
  42. package/dist/agent-ui/stage.cjs +196 -0
  43. package/dist/agent-ui/stage.cjs.map +1 -0
  44. package/dist/agent-ui/stage.d.ts +110 -0
  45. package/dist/agent-ui/stage.js +196 -0
  46. package/dist/agent-ui/stage.js.map +1 -0
  47. package/dist/agent-ui/tools.cjs +448 -0
  48. package/dist/agent-ui/tools.cjs.map +1 -0
  49. package/dist/agent-ui/tools.d.ts +103 -0
  50. package/dist/agent-ui/tools.js +448 -0
  51. package/dist/agent-ui/tools.js.map +1 -0
  52. package/dist/agent-ui/use-agent-target.cjs +63 -0
  53. package/dist/agent-ui/use-agent-target.cjs.map +1 -0
  54. package/dist/agent-ui/use-agent-target.d.ts +27 -0
  55. package/dist/agent-ui/use-agent-target.js +63 -0
  56. package/dist/agent-ui/use-agent-target.js.map +1 -0
  57. package/dist/agent-ui/use-agent-ui.cjs +15 -0
  58. package/dist/agent-ui/use-agent-ui.cjs.map +1 -0
  59. package/dist/agent-ui/use-agent-ui.d.ts +24 -0
  60. package/dist/agent-ui/use-agent-ui.js +15 -0
  61. package/dist/agent-ui/use-agent-ui.js.map +1 -0
  62. package/dist/chat/AthenaChat.cjs +17 -4
  63. package/dist/chat/AthenaChat.cjs.map +1 -1
  64. package/dist/chat/AthenaChat.d.ts +13 -0
  65. package/dist/chat/AthenaChat.js +17 -4
  66. package/dist/chat/AthenaChat.js.map +1 -1
  67. package/dist/chat/StatewireClientToolBridge.cjs +17 -12
  68. package/dist/chat/StatewireClientToolBridge.cjs.map +1 -1
  69. package/dist/chat/StatewireClientToolBridge.d.ts +4 -2
  70. package/dist/chat/StatewireClientToolBridge.js +17 -12
  71. package/dist/chat/StatewireClientToolBridge.js.map +1 -1
  72. package/dist/chat/StatewireClientToolConsentCard.cjs +3 -2
  73. package/dist/chat/StatewireClientToolConsentCard.cjs.map +1 -1
  74. package/dist/chat/StatewireClientToolConsentCard.d.ts +2 -1
  75. package/dist/chat/StatewireClientToolConsentCard.js +3 -2
  76. package/dist/chat/StatewireClientToolConsentCard.js.map +1 -1
  77. package/dist/chat/chat-voice-controls.cjs +5 -2
  78. package/dist/chat/chat-voice-controls.cjs.map +1 -1
  79. package/dist/chat/chat-voice-controls.d.ts +6 -1
  80. package/dist/chat/chat-voice-controls.js +6 -3
  81. package/dist/chat/chat-voice-controls.js.map +1 -1
  82. package/dist/chat/client-tool-write-consent.cjs +16 -5
  83. package/dist/chat/client-tool-write-consent.cjs.map +1 -1
  84. package/dist/chat/client-tool-write-consent.d.ts +12 -4
  85. package/dist/chat/client-tool-write-consent.js +16 -5
  86. package/dist/chat/client-tool-write-consent.js.map +1 -1
  87. package/dist/chat-ui/messages/message-components.cjs +1 -1
  88. package/dist/chat-ui/messages/message-components.cjs.map +1 -1
  89. package/dist/chat-ui/messages/message-components.d.ts +2 -2
  90. package/dist/chat-ui/messages/message-components.js +1 -1
  91. package/dist/chat-ui/messages/message-components.js.map +1 -1
  92. package/dist/index.cjs +7 -0
  93. package/dist/index.cjs.map +1 -1
  94. package/dist/index.d.ts +6 -0
  95. package/dist/index.js +7 -0
  96. package/dist/index.js.map +1 -1
  97. package/dist/provider/AthenaProvider.cjs +37 -8
  98. package/dist/provider/AthenaProvider.cjs.map +1 -1
  99. package/dist/provider/AthenaProvider.d.ts +18 -1
  100. package/dist/provider/AthenaProvider.js +33 -4
  101. package/dist/provider/AthenaProvider.js.map +1 -1
  102. package/dist/react/package.json.cjs +1 -1
  103. package/dist/react/package.json.js +1 -1
  104. package/dist/styles.css +1 -1
  105. package/package.json +6 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.cjs","sources":["../../src/agent-ui/contract.ts"],"sourcesContent":["/*\n * Agent targets: the elements of an app the agent may see, point at and\n * operate on the screen the person has open.\n *\n * The contract is a handful of `data-agent-*` attributes on the element\n * itself. The DOM is the registry: what is in the document is what is on\n * screen, list rows and third-party components need nothing special, and a\n * plain web component can take part without React. `agentTarget()` writes the\n * attributes with types; `useAgentTarget()` also registers handlers for a\n * control that ordinary clicks and typing cannot drive.\n *\n * The app decides what the agent can touch: an element without\n * `data-agent-target` does not exist for the agent, a target lists the actions\n * it allows, and one marked dangerous or sensitive is treated as such by every\n * tool. Nothing here is a permission boundary. The tools drive the same\n * handlers a click would, as the signed-in person.\n */\n\n/** What the agent may do to a target, beyond pointing at it. */\nexport const AGENT_TARGET_ACTIONS = ['click', 'type', 'select', 'toggle', 'clear'] as const;\nexport type AgentTargetAction = (typeof AGENT_TARGET_ACTIONS)[number];\n\nexport function isAgentTargetAction(value: unknown): value is AgentTargetAction {\n return AGENT_TARGET_ACTIONS.some((action) => action === value);\n}\n\n/** The attribute names of the contract. */\nexport const AGENT_ATTR = {\n /** The target's id, stable across renders (`investors.search`). */\n target: 'data-agent-target',\n /** Which one of several targets sharing an id (a row's key). */\n key: 'data-agent-key',\n /** The name the person would call it. Falls back to the element's own accessible name. */\n label: 'data-agent-label',\n /** One line for the agent on what it is for. */\n description: 'data-agent-description',\n /** Space-separated actions the agent may take, or `none`. Derived from the element when absent. */\n actions: 'data-agent-actions',\n /** Present: acting on it changes or destroys something, so the person confirms every time. */\n danger: 'data-agent-danger',\n /** Present: its value is never read and never typed. */\n sensitive: 'data-agent-sensitive',\n /** On a container: a named part of the screen its targets belong to. */\n region: 'data-agent-region',\n /** Present on the overlay's own root: never a target, and input inside it is the overlay's. */\n layer: 'data-agent-ui-layer',\n} as const;\n\n/** What an app says about one target. */\nexport interface AgentTargetOptions {\n /**\n * The target's id: lowercase words joined by `.`, `-` or `_`\n * (`investors.search`, `filters.stage`). Stable across renders and visits,\n * since the agent names targets by it.\n */\n id: string;\n /** Which one, when several targets share the id (a table row's key). */\n key?: string;\n /** The name a person would call it (\"Search investors\"). */\n label?: string;\n /** One line for the agent: what it is for, or what using it does. */\n description?: string;\n /**\n * What the agent may do to it. Absent: what the element allows by its\n * nature (a button is clicked, a text field typed into). An empty list: the\n * agent may only point at it.\n */\n actions?: readonly AgentTargetAction[];\n /** Acting on it changes or destroys something: the person confirms every time. */\n danger?: boolean;\n /** Its value is never read back to the agent and never typed by it. */\n sensitive?: boolean;\n}\n\n/** The props an element takes to become a target. */\nexport type AgentTargetProps = Readonly<Record<string, string>>;\n\nconst TARGET_ID_PATTERN = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;\nconst NO_PROPS: AgentTargetProps = {};\n\n/** Whether `id` is a well-formed target id. */\nexport function isAgentTargetId(id: string): boolean {\n return id.length <= 80 && TARGET_ID_PATTERN.test(id);\n}\n\n/**\n * The props that make an element an agent target. Spread them on the element\n * (any component that passes `data-*` props to its root works):\n *\n * ```tsx\n * <input {...agentTarget({ id: 'investors.search', label: 'Search investors' })} />\n * {rows.map((row) => (\n * <tr key={row.id} {...agentTarget({ id: 'investors.row', key: row.id, label: row.name })} />\n * ))}\n * ```\n */\nexport function agentTarget(options: AgentTargetOptions): AgentTargetProps {\n if (!isAgentTargetId(options.id)) {\n // An id built from data must never take the page down with it: the\n // element is simply not a target, and the console says why.\n console.error(\n `agentTarget: \"${options.id}\" is not a target id, so the element is not a target. Use lowercase words joined by \".\", \"-\" or \"_\" (e.g. \"investors.search\").`,\n );\n return NO_PROPS;\n }\n const props: Record<string, string> = { [AGENT_ATTR.target]: options.id };\n if (options.key !== undefined && options.key !== '') props[AGENT_ATTR.key] = options.key;\n if (options.label) props[AGENT_ATTR.label] = options.label;\n if (options.description) props[AGENT_ATTR.description] = options.description;\n if (options.actions) {\n props[AGENT_ATTR.actions] = options.actions.length > 0 ? options.actions.join(' ') : 'none';\n }\n if (options.danger) props[AGENT_ATTR.danger] = '';\n if (options.sensitive) props[AGENT_ATTR.sensitive] = '';\n return props;\n}\n\n/** The props that name a part of the screen; targets inside it are grouped under it. */\nexport function agentRegion({ id, label }: { id: string; label?: string }): AgentTargetProps {\n if (!isAgentTargetId(id)) {\n console.error(`agentRegion: \"${id}\" is not a region id (e.g. \"filters\"), so the element names no region.`);\n return NO_PROPS;\n }\n return label ? { [AGENT_ATTR.region]: id, [AGENT_ATTR.label]: label } : { [AGENT_ATTR.region]: id };\n}\n\n/** How the agent names one target: its id, with its key in brackets when it has one. */\nexport function agentTargetRef({ id, key }: { id: string; key?: string | null }): string {\n return key ? `${id}[${key}]` : id;\n}\n\n/** A reference split into id and key; `null` when it is not one. */\nexport function parseAgentTargetRef(ref: string): { id: string; key: string | null } | null {\n const text = ref.trim();\n const match = /^([^[\\]]+)(?:\\[(.+)\\])?$/.exec(text);\n if (!match?.[1]) return null;\n return { id: match[1].trim(), key: match[2] ?? null };\n}\n\n/**\n * What a target does when ordinary input cannot drive it (a custom select, a\n * date picker, a canvas). A handler here replaces the tool's own click or\n * keystrokes for that action; it runs the app's own code, so validation and\n * undo apply as for a person.\n */\nexport interface AgentTargetHandlers {\n click?: () => void | Promise<void>;\n /** Called with the text so far as it is typed, then once more with `done: true`. */\n type?: (args: { text: string; done: boolean }) => void | Promise<void>;\n select?: (args: { option: string }) => void | Promise<void>;\n toggle?: (args: { checked: boolean | null }) => void | Promise<void>;\n clear?: () => void | Promise<void>;\n /** The options a `select` may be given, when the element is not a native `<select>`. */\n options?: () => readonly string[];\n /** The target's current value, as the agent should read it. */\n value?: () => string | null;\n}\n\nconst HANDLER_REGISTRY_KEY = Symbol.for('athena.agentUi.targetHandlers');\n\ntype HandlerRegistry = Map<string, AgentTargetHandlers>;\n\n/**\n * One registry per page, on `globalThis`: a host can carry more than one copy\n * of this module (the SDK bundles its own), and a handler registered through\n * one must be found by the tools of another.\n */\nfunction handlerRegistry(): HandlerRegistry {\n const holder = globalThis as typeof globalThis & { [HANDLER_REGISTRY_KEY]?: HandlerRegistry };\n let registry = holder[HANDLER_REGISTRY_KEY];\n if (!registry) {\n registry = new Map();\n holder[HANDLER_REGISTRY_KEY] = registry;\n }\n return registry;\n}\n\n/** Register a target's handlers; returns the unregister function. */\nexport function registerAgentTargetHandlers({\n ref,\n handlers,\n}: {\n ref: string;\n handlers: AgentTargetHandlers;\n}): () => void {\n const registry = handlerRegistry();\n if (registry.has(ref)) {\n // Handlers are found by reference: with two mounted targets sharing one,\n // the agent would be shown one and run the other's.\n console.error(\n `useAgentTarget: two mounted targets share \"${ref}\" and both register handlers. Give each its own key.`,\n );\n }\n registry.set(ref, handlers);\n return () => {\n if (registry.get(ref) === handlers) registry.delete(ref);\n };\n}\n\n/** The handlers registered for a target reference, if any. */\nexport function agentTargetHandlers(ref: string): AgentTargetHandlers | undefined {\n return handlerRegistry().get(ref);\n}\n"],"names":[],"mappings":";;AAmBO,MAAM,uBAAuB,CAAC,SAAS,QAAQ,UAAU,UAAU,OAAO;AAG1E,SAAS,oBAAoB,OAA4C;AAC9E,SAAO,qBAAqB,KAAK,CAAC,WAAW,WAAW,KAAK;AAC/D;AAGO,MAAM,aAAa;AAAA;AAAA,EAExB,QAAQ;AAAA;AAAA,EAER,KAAK;AAAA;AAAA,EAEL,OAAO;AAAA;AAAA,EAEP,aAAa;AAAA;AAAA,EAEb,SAAS;AAAA;AAAA,EAET,QAAQ;AAAA;AAAA,EAER,WAAW;AAAA;AAAA,EAEX,QAAQ;AAAA;AAAA,EAER,OAAO;AACT;AA+BA,MAAM,oBAAoB;AAC1B,MAAM,WAA6B,CAAA;AAG5B,SAAS,gBAAgB,IAAqB;AACnD,SAAO,GAAG,UAAU,MAAM,kBAAkB,KAAK,EAAE;AACrD;AAaO,SAAS,YAAY,SAA+C;AACzE,MAAI,CAAC,gBAAgB,QAAQ,EAAE,GAAG;AAGhC,YAAQ;AAAA,MACN,iBAAiB,QAAQ,EAAE;AAAA,IAAA;AAE7B,WAAO;AAAA,EACT;AACA,QAAM,QAAgC,EAAE,CAAC,WAAW,MAAM,GAAG,QAAQ,GAAA;AACrE,MAAI,QAAQ,QAAQ,UAAa,QAAQ,QAAQ,GAAI,OAAM,WAAW,GAAG,IAAI,QAAQ;AACrF,MAAI,QAAQ,MAAO,OAAM,WAAW,KAAK,IAAI,QAAQ;AACrD,MAAI,QAAQ,YAAa,OAAM,WAAW,WAAW,IAAI,QAAQ;AACjE,MAAI,QAAQ,SAAS;AACnB,UAAM,WAAW,OAAO,IAAI,QAAQ,QAAQ,SAAS,IAAI,QAAQ,QAAQ,KAAK,GAAG,IAAI;AAAA,EACvF;AACA,MAAI,QAAQ,OAAQ,OAAM,WAAW,MAAM,IAAI;AAC/C,MAAI,QAAQ,UAAW,OAAM,WAAW,SAAS,IAAI;AACrD,SAAO;AACT;AAGO,SAAS,YAAY,EAAE,IAAI,SAA2D;AAC3F,MAAI,CAAC,gBAAgB,EAAE,GAAG;AACxB,YAAQ,MAAM,iBAAiB,EAAE,wEAAwE;AACzG,WAAO;AAAA,EACT;AACA,SAAO,QAAQ,EAAE,CAAC,WAAW,MAAM,GAAG,IAAI,CAAC,WAAW,KAAK,GAAG,UAAU,EAAE,CAAC,WAAW,MAAM,GAAG,GAAA;AACjG;AAGO,SAAS,eAAe,EAAE,IAAI,OAAoD;AACvF,SAAO,MAAM,GAAG,EAAE,IAAI,GAAG,MAAM;AACjC;AAGO,SAAS,oBAAoB,KAAwD;AAC1F,QAAM,OAAO,IAAI,KAAA;AACjB,QAAM,QAAQ,2BAA2B,KAAK,IAAI;AAClD,MAAI,EAAC,+BAAQ,IAAI,QAAO;AACxB,SAAO,EAAE,IAAI,MAAM,CAAC,EAAE,QAAQ,KAAK,MAAM,CAAC,KAAK,KAAA;AACjD;AAqBA,MAAM,uBAAuB,OAAO,IAAI,+BAA+B;AASvE,SAAS,kBAAmC;AAC1C,QAAM,SAAS;AACf,MAAI,WAAW,OAAO,oBAAoB;AAC1C,MAAI,CAAC,UAAU;AACb,mCAAe,IAAA;AACf,WAAO,oBAAoB,IAAI;AAAA,EACjC;AACA,SAAO;AACT;AAGO,SAAS,4BAA4B;AAAA,EAC1C;AAAA,EACA;AACF,GAGe;AACb,QAAM,WAAW,gBAAA;AACjB,MAAI,SAAS,IAAI,GAAG,GAAG;AAGrB,YAAQ;AAAA,MACN,8CAA8C,GAAG;AAAA,IAAA;AAAA,EAErD;AACA,WAAS,IAAI,KAAK,QAAQ;AAC1B,SAAO,MAAM;AACX,QAAI,SAAS,IAAI,GAAG,MAAM,SAAU,UAAS,OAAO,GAAG;AAAA,EACzD;AACF;AAGO,SAAS,oBAAoB,KAA8C;AAChF,SAAO,gBAAA,EAAkB,IAAI,GAAG;AAClC;;;;;;;;;;;"}
@@ -0,0 +1,113 @@
1
+ /** What the agent may do to a target, beyond pointing at it. */
2
+ export declare const AGENT_TARGET_ACTIONS: readonly ['click', 'type', 'select', 'toggle', 'clear'];
3
+ export type AgentTargetAction = (typeof AGENT_TARGET_ACTIONS)[number];
4
+ export declare function isAgentTargetAction(value: unknown): value is AgentTargetAction;
5
+ /** The attribute names of the contract. */
6
+ export declare const AGENT_ATTR: {
7
+ /** The target's id, stable across renders (`investors.search`). */
8
+ readonly target: 'data-agent-target';
9
+ /** Which one of several targets sharing an id (a row's key). */
10
+ readonly key: 'data-agent-key';
11
+ /** The name the person would call it. Falls back to the element's own accessible name. */
12
+ readonly label: 'data-agent-label';
13
+ /** One line for the agent on what it is for. */
14
+ readonly description: 'data-agent-description';
15
+ /** Space-separated actions the agent may take, or `none`. Derived from the element when absent. */
16
+ readonly actions: 'data-agent-actions';
17
+ /** Present: acting on it changes or destroys something, so the person confirms every time. */
18
+ readonly danger: 'data-agent-danger';
19
+ /** Present: its value is never read and never typed. */
20
+ readonly sensitive: 'data-agent-sensitive';
21
+ /** On a container: a named part of the screen its targets belong to. */
22
+ readonly region: 'data-agent-region';
23
+ /** Present on the overlay's own root: never a target, and input inside it is the overlay's. */
24
+ readonly layer: 'data-agent-ui-layer';
25
+ };
26
+ /** What an app says about one target. */
27
+ export interface AgentTargetOptions {
28
+ /**
29
+ * The target's id: lowercase words joined by `.`, `-` or `_`
30
+ * (`investors.search`, `filters.stage`). Stable across renders and visits,
31
+ * since the agent names targets by it.
32
+ */
33
+ id: string;
34
+ /** Which one, when several targets share the id (a table row's key). */
35
+ key?: string;
36
+ /** The name a person would call it ("Search investors"). */
37
+ label?: string;
38
+ /** One line for the agent: what it is for, or what using it does. */
39
+ description?: string;
40
+ /**
41
+ * What the agent may do to it. Absent: what the element allows by its
42
+ * nature (a button is clicked, a text field typed into). An empty list: the
43
+ * agent may only point at it.
44
+ */
45
+ actions?: readonly AgentTargetAction[];
46
+ /** Acting on it changes or destroys something: the person confirms every time. */
47
+ danger?: boolean;
48
+ /** Its value is never read back to the agent and never typed by it. */
49
+ sensitive?: boolean;
50
+ }
51
+ /** The props an element takes to become a target. */
52
+ export type AgentTargetProps = Readonly<Record<string, string>>;
53
+ /** Whether `id` is a well-formed target id. */
54
+ export declare function isAgentTargetId(id: string): boolean;
55
+ /**
56
+ * The props that make an element an agent target. Spread them on the element
57
+ * (any component that passes `data-*` props to its root works):
58
+ *
59
+ * ```tsx
60
+ * <input {...agentTarget({ id: 'investors.search', label: 'Search investors' })} />
61
+ * {rows.map((row) => (
62
+ * <tr key={row.id} {...agentTarget({ id: 'investors.row', key: row.id, label: row.name })} />
63
+ * ))}
64
+ * ```
65
+ */
66
+ export declare function agentTarget(options: AgentTargetOptions): AgentTargetProps;
67
+ /** The props that name a part of the screen; targets inside it are grouped under it. */
68
+ export declare function agentRegion({ id, label }: {
69
+ id: string;
70
+ label?: string;
71
+ }): AgentTargetProps;
72
+ /** How the agent names one target: its id, with its key in brackets when it has one. */
73
+ export declare function agentTargetRef({ id, key }: {
74
+ id: string;
75
+ key?: string | null;
76
+ }): string;
77
+ /** A reference split into id and key; `null` when it is not one. */
78
+ export declare function parseAgentTargetRef(ref: string): {
79
+ id: string;
80
+ key: string | null;
81
+ } | null;
82
+ /**
83
+ * What a target does when ordinary input cannot drive it (a custom select, a
84
+ * date picker, a canvas). A handler here replaces the tool's own click or
85
+ * keystrokes for that action; it runs the app's own code, so validation and
86
+ * undo apply as for a person.
87
+ */
88
+ export interface AgentTargetHandlers {
89
+ click?: () => void | Promise<void>;
90
+ /** Called with the text so far as it is typed, then once more with `done: true`. */
91
+ type?: (args: {
92
+ text: string;
93
+ done: boolean;
94
+ }) => void | Promise<void>;
95
+ select?: (args: {
96
+ option: string;
97
+ }) => void | Promise<void>;
98
+ toggle?: (args: {
99
+ checked: boolean | null;
100
+ }) => void | Promise<void>;
101
+ clear?: () => void | Promise<void>;
102
+ /** The options a `select` may be given, when the element is not a native `<select>`. */
103
+ options?: () => readonly string[];
104
+ /** The target's current value, as the agent should read it. */
105
+ value?: () => string | null;
106
+ }
107
+ /** Register a target's handlers; returns the unregister function. */
108
+ export declare function registerAgentTargetHandlers({ ref, handlers, }: {
109
+ ref: string;
110
+ handlers: AgentTargetHandlers;
111
+ }): () => void;
112
+ /** The handlers registered for a target reference, if any. */
113
+ export declare function agentTargetHandlers(ref: string): AgentTargetHandlers | undefined;
@@ -0,0 +1,104 @@
1
+ const AGENT_TARGET_ACTIONS = ["click", "type", "select", "toggle", "clear"];
2
+ function isAgentTargetAction(value) {
3
+ return AGENT_TARGET_ACTIONS.some((action) => action === value);
4
+ }
5
+ const AGENT_ATTR = {
6
+ /** The target's id, stable across renders (`investors.search`). */
7
+ target: "data-agent-target",
8
+ /** Which one of several targets sharing an id (a row's key). */
9
+ key: "data-agent-key",
10
+ /** The name the person would call it. Falls back to the element's own accessible name. */
11
+ label: "data-agent-label",
12
+ /** One line for the agent on what it is for. */
13
+ description: "data-agent-description",
14
+ /** Space-separated actions the agent may take, or `none`. Derived from the element when absent. */
15
+ actions: "data-agent-actions",
16
+ /** Present: acting on it changes or destroys something, so the person confirms every time. */
17
+ danger: "data-agent-danger",
18
+ /** Present: its value is never read and never typed. */
19
+ sensitive: "data-agent-sensitive",
20
+ /** On a container: a named part of the screen its targets belong to. */
21
+ region: "data-agent-region",
22
+ /** Present on the overlay's own root: never a target, and input inside it is the overlay's. */
23
+ layer: "data-agent-ui-layer"
24
+ };
25
+ const TARGET_ID_PATTERN = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;
26
+ const NO_PROPS = {};
27
+ function isAgentTargetId(id) {
28
+ return id.length <= 80 && TARGET_ID_PATTERN.test(id);
29
+ }
30
+ function agentTarget(options) {
31
+ if (!isAgentTargetId(options.id)) {
32
+ console.error(
33
+ `agentTarget: "${options.id}" is not a target id, so the element is not a target. Use lowercase words joined by ".", "-" or "_" (e.g. "investors.search").`
34
+ );
35
+ return NO_PROPS;
36
+ }
37
+ const props = { [AGENT_ATTR.target]: options.id };
38
+ if (options.key !== void 0 && options.key !== "") props[AGENT_ATTR.key] = options.key;
39
+ if (options.label) props[AGENT_ATTR.label] = options.label;
40
+ if (options.description) props[AGENT_ATTR.description] = options.description;
41
+ if (options.actions) {
42
+ props[AGENT_ATTR.actions] = options.actions.length > 0 ? options.actions.join(" ") : "none";
43
+ }
44
+ if (options.danger) props[AGENT_ATTR.danger] = "";
45
+ if (options.sensitive) props[AGENT_ATTR.sensitive] = "";
46
+ return props;
47
+ }
48
+ function agentRegion({ id, label }) {
49
+ if (!isAgentTargetId(id)) {
50
+ console.error(`agentRegion: "${id}" is not a region id (e.g. "filters"), so the element names no region.`);
51
+ return NO_PROPS;
52
+ }
53
+ return label ? { [AGENT_ATTR.region]: id, [AGENT_ATTR.label]: label } : { [AGENT_ATTR.region]: id };
54
+ }
55
+ function agentTargetRef({ id, key }) {
56
+ return key ? `${id}[${key}]` : id;
57
+ }
58
+ function parseAgentTargetRef(ref) {
59
+ const text = ref.trim();
60
+ const match = /^([^[\]]+)(?:\[(.+)\])?$/.exec(text);
61
+ if (!(match == null ? void 0 : match[1])) return null;
62
+ return { id: match[1].trim(), key: match[2] ?? null };
63
+ }
64
+ const HANDLER_REGISTRY_KEY = Symbol.for("athena.agentUi.targetHandlers");
65
+ function handlerRegistry() {
66
+ const holder = globalThis;
67
+ let registry = holder[HANDLER_REGISTRY_KEY];
68
+ if (!registry) {
69
+ registry = /* @__PURE__ */ new Map();
70
+ holder[HANDLER_REGISTRY_KEY] = registry;
71
+ }
72
+ return registry;
73
+ }
74
+ function registerAgentTargetHandlers({
75
+ ref,
76
+ handlers
77
+ }) {
78
+ const registry = handlerRegistry();
79
+ if (registry.has(ref)) {
80
+ console.error(
81
+ `useAgentTarget: two mounted targets share "${ref}" and both register handlers. Give each its own key.`
82
+ );
83
+ }
84
+ registry.set(ref, handlers);
85
+ return () => {
86
+ if (registry.get(ref) === handlers) registry.delete(ref);
87
+ };
88
+ }
89
+ function agentTargetHandlers(ref) {
90
+ return handlerRegistry().get(ref);
91
+ }
92
+ export {
93
+ AGENT_ATTR,
94
+ AGENT_TARGET_ACTIONS,
95
+ agentRegion,
96
+ agentTarget,
97
+ agentTargetHandlers,
98
+ agentTargetRef,
99
+ isAgentTargetAction,
100
+ isAgentTargetId,
101
+ parseAgentTargetRef,
102
+ registerAgentTargetHandlers
103
+ };
104
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sources":["../../src/agent-ui/contract.ts"],"sourcesContent":["/*\n * Agent targets: the elements of an app the agent may see, point at and\n * operate on the screen the person has open.\n *\n * The contract is a handful of `data-agent-*` attributes on the element\n * itself. The DOM is the registry: what is in the document is what is on\n * screen, list rows and third-party components need nothing special, and a\n * plain web component can take part without React. `agentTarget()` writes the\n * attributes with types; `useAgentTarget()` also registers handlers for a\n * control that ordinary clicks and typing cannot drive.\n *\n * The app decides what the agent can touch: an element without\n * `data-agent-target` does not exist for the agent, a target lists the actions\n * it allows, and one marked dangerous or sensitive is treated as such by every\n * tool. Nothing here is a permission boundary. The tools drive the same\n * handlers a click would, as the signed-in person.\n */\n\n/** What the agent may do to a target, beyond pointing at it. */\nexport const AGENT_TARGET_ACTIONS = ['click', 'type', 'select', 'toggle', 'clear'] as const;\nexport type AgentTargetAction = (typeof AGENT_TARGET_ACTIONS)[number];\n\nexport function isAgentTargetAction(value: unknown): value is AgentTargetAction {\n return AGENT_TARGET_ACTIONS.some((action) => action === value);\n}\n\n/** The attribute names of the contract. */\nexport const AGENT_ATTR = {\n /** The target's id, stable across renders (`investors.search`). */\n target: 'data-agent-target',\n /** Which one of several targets sharing an id (a row's key). */\n key: 'data-agent-key',\n /** The name the person would call it. Falls back to the element's own accessible name. */\n label: 'data-agent-label',\n /** One line for the agent on what it is for. */\n description: 'data-agent-description',\n /** Space-separated actions the agent may take, or `none`. Derived from the element when absent. */\n actions: 'data-agent-actions',\n /** Present: acting on it changes or destroys something, so the person confirms every time. */\n danger: 'data-agent-danger',\n /** Present: its value is never read and never typed. */\n sensitive: 'data-agent-sensitive',\n /** On a container: a named part of the screen its targets belong to. */\n region: 'data-agent-region',\n /** Present on the overlay's own root: never a target, and input inside it is the overlay's. */\n layer: 'data-agent-ui-layer',\n} as const;\n\n/** What an app says about one target. */\nexport interface AgentTargetOptions {\n /**\n * The target's id: lowercase words joined by `.`, `-` or `_`\n * (`investors.search`, `filters.stage`). Stable across renders and visits,\n * since the agent names targets by it.\n */\n id: string;\n /** Which one, when several targets share the id (a table row's key). */\n key?: string;\n /** The name a person would call it (\"Search investors\"). */\n label?: string;\n /** One line for the agent: what it is for, or what using it does. */\n description?: string;\n /**\n * What the agent may do to it. Absent: what the element allows by its\n * nature (a button is clicked, a text field typed into). An empty list: the\n * agent may only point at it.\n */\n actions?: readonly AgentTargetAction[];\n /** Acting on it changes or destroys something: the person confirms every time. */\n danger?: boolean;\n /** Its value is never read back to the agent and never typed by it. */\n sensitive?: boolean;\n}\n\n/** The props an element takes to become a target. */\nexport type AgentTargetProps = Readonly<Record<string, string>>;\n\nconst TARGET_ID_PATTERN = /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;\nconst NO_PROPS: AgentTargetProps = {};\n\n/** Whether `id` is a well-formed target id. */\nexport function isAgentTargetId(id: string): boolean {\n return id.length <= 80 && TARGET_ID_PATTERN.test(id);\n}\n\n/**\n * The props that make an element an agent target. Spread them on the element\n * (any component that passes `data-*` props to its root works):\n *\n * ```tsx\n * <input {...agentTarget({ id: 'investors.search', label: 'Search investors' })} />\n * {rows.map((row) => (\n * <tr key={row.id} {...agentTarget({ id: 'investors.row', key: row.id, label: row.name })} />\n * ))}\n * ```\n */\nexport function agentTarget(options: AgentTargetOptions): AgentTargetProps {\n if (!isAgentTargetId(options.id)) {\n // An id built from data must never take the page down with it: the\n // element is simply not a target, and the console says why.\n console.error(\n `agentTarget: \"${options.id}\" is not a target id, so the element is not a target. Use lowercase words joined by \".\", \"-\" or \"_\" (e.g. \"investors.search\").`,\n );\n return NO_PROPS;\n }\n const props: Record<string, string> = { [AGENT_ATTR.target]: options.id };\n if (options.key !== undefined && options.key !== '') props[AGENT_ATTR.key] = options.key;\n if (options.label) props[AGENT_ATTR.label] = options.label;\n if (options.description) props[AGENT_ATTR.description] = options.description;\n if (options.actions) {\n props[AGENT_ATTR.actions] = options.actions.length > 0 ? options.actions.join(' ') : 'none';\n }\n if (options.danger) props[AGENT_ATTR.danger] = '';\n if (options.sensitive) props[AGENT_ATTR.sensitive] = '';\n return props;\n}\n\n/** The props that name a part of the screen; targets inside it are grouped under it. */\nexport function agentRegion({ id, label }: { id: string; label?: string }): AgentTargetProps {\n if (!isAgentTargetId(id)) {\n console.error(`agentRegion: \"${id}\" is not a region id (e.g. \"filters\"), so the element names no region.`);\n return NO_PROPS;\n }\n return label ? { [AGENT_ATTR.region]: id, [AGENT_ATTR.label]: label } : { [AGENT_ATTR.region]: id };\n}\n\n/** How the agent names one target: its id, with its key in brackets when it has one. */\nexport function agentTargetRef({ id, key }: { id: string; key?: string | null }): string {\n return key ? `${id}[${key}]` : id;\n}\n\n/** A reference split into id and key; `null` when it is not one. */\nexport function parseAgentTargetRef(ref: string): { id: string; key: string | null } | null {\n const text = ref.trim();\n const match = /^([^[\\]]+)(?:\\[(.+)\\])?$/.exec(text);\n if (!match?.[1]) return null;\n return { id: match[1].trim(), key: match[2] ?? null };\n}\n\n/**\n * What a target does when ordinary input cannot drive it (a custom select, a\n * date picker, a canvas). A handler here replaces the tool's own click or\n * keystrokes for that action; it runs the app's own code, so validation and\n * undo apply as for a person.\n */\nexport interface AgentTargetHandlers {\n click?: () => void | Promise<void>;\n /** Called with the text so far as it is typed, then once more with `done: true`. */\n type?: (args: { text: string; done: boolean }) => void | Promise<void>;\n select?: (args: { option: string }) => void | Promise<void>;\n toggle?: (args: { checked: boolean | null }) => void | Promise<void>;\n clear?: () => void | Promise<void>;\n /** The options a `select` may be given, when the element is not a native `<select>`. */\n options?: () => readonly string[];\n /** The target's current value, as the agent should read it. */\n value?: () => string | null;\n}\n\nconst HANDLER_REGISTRY_KEY = Symbol.for('athena.agentUi.targetHandlers');\n\ntype HandlerRegistry = Map<string, AgentTargetHandlers>;\n\n/**\n * One registry per page, on `globalThis`: a host can carry more than one copy\n * of this module (the SDK bundles its own), and a handler registered through\n * one must be found by the tools of another.\n */\nfunction handlerRegistry(): HandlerRegistry {\n const holder = globalThis as typeof globalThis & { [HANDLER_REGISTRY_KEY]?: HandlerRegistry };\n let registry = holder[HANDLER_REGISTRY_KEY];\n if (!registry) {\n registry = new Map();\n holder[HANDLER_REGISTRY_KEY] = registry;\n }\n return registry;\n}\n\n/** Register a target's handlers; returns the unregister function. */\nexport function registerAgentTargetHandlers({\n ref,\n handlers,\n}: {\n ref: string;\n handlers: AgentTargetHandlers;\n}): () => void {\n const registry = handlerRegistry();\n if (registry.has(ref)) {\n // Handlers are found by reference: with two mounted targets sharing one,\n // the agent would be shown one and run the other's.\n console.error(\n `useAgentTarget: two mounted targets share \"${ref}\" and both register handlers. Give each its own key.`,\n );\n }\n registry.set(ref, handlers);\n return () => {\n if (registry.get(ref) === handlers) registry.delete(ref);\n };\n}\n\n/** The handlers registered for a target reference, if any. */\nexport function agentTargetHandlers(ref: string): AgentTargetHandlers | undefined {\n return handlerRegistry().get(ref);\n}\n"],"names":[],"mappings":"AAmBO,MAAM,uBAAuB,CAAC,SAAS,QAAQ,UAAU,UAAU,OAAO;AAG1E,SAAS,oBAAoB,OAA4C;AAC9E,SAAO,qBAAqB,KAAK,CAAC,WAAW,WAAW,KAAK;AAC/D;AAGO,MAAM,aAAa;AAAA;AAAA,EAExB,QAAQ;AAAA;AAAA,EAER,KAAK;AAAA;AAAA,EAEL,OAAO;AAAA;AAAA,EAEP,aAAa;AAAA;AAAA,EAEb,SAAS;AAAA;AAAA,EAET,QAAQ;AAAA;AAAA,EAER,WAAW;AAAA;AAAA,EAEX,QAAQ;AAAA;AAAA,EAER,OAAO;AACT;AA+BA,MAAM,oBAAoB;AAC1B,MAAM,WAA6B,CAAA;AAG5B,SAAS,gBAAgB,IAAqB;AACnD,SAAO,GAAG,UAAU,MAAM,kBAAkB,KAAK,EAAE;AACrD;AAaO,SAAS,YAAY,SAA+C;AACzE,MAAI,CAAC,gBAAgB,QAAQ,EAAE,GAAG;AAGhC,YAAQ;AAAA,MACN,iBAAiB,QAAQ,EAAE;AAAA,IAAA;AAE7B,WAAO;AAAA,EACT;AACA,QAAM,QAAgC,EAAE,CAAC,WAAW,MAAM,GAAG,QAAQ,GAAA;AACrE,MAAI,QAAQ,QAAQ,UAAa,QAAQ,QAAQ,GAAI,OAAM,WAAW,GAAG,IAAI,QAAQ;AACrF,MAAI,QAAQ,MAAO,OAAM,WAAW,KAAK,IAAI,QAAQ;AACrD,MAAI,QAAQ,YAAa,OAAM,WAAW,WAAW,IAAI,QAAQ;AACjE,MAAI,QAAQ,SAAS;AACnB,UAAM,WAAW,OAAO,IAAI,QAAQ,QAAQ,SAAS,IAAI,QAAQ,QAAQ,KAAK,GAAG,IAAI;AAAA,EACvF;AACA,MAAI,QAAQ,OAAQ,OAAM,WAAW,MAAM,IAAI;AAC/C,MAAI,QAAQ,UAAW,OAAM,WAAW,SAAS,IAAI;AACrD,SAAO;AACT;AAGO,SAAS,YAAY,EAAE,IAAI,SAA2D;AAC3F,MAAI,CAAC,gBAAgB,EAAE,GAAG;AACxB,YAAQ,MAAM,iBAAiB,EAAE,wEAAwE;AACzG,WAAO;AAAA,EACT;AACA,SAAO,QAAQ,EAAE,CAAC,WAAW,MAAM,GAAG,IAAI,CAAC,WAAW,KAAK,GAAG,UAAU,EAAE,CAAC,WAAW,MAAM,GAAG,GAAA;AACjG;AAGO,SAAS,eAAe,EAAE,IAAI,OAAoD;AACvF,SAAO,MAAM,GAAG,EAAE,IAAI,GAAG,MAAM;AACjC;AAGO,SAAS,oBAAoB,KAAwD;AAC1F,QAAM,OAAO,IAAI,KAAA;AACjB,QAAM,QAAQ,2BAA2B,KAAK,IAAI;AAClD,MAAI,EAAC,+BAAQ,IAAI,QAAO;AACxB,SAAO,EAAE,IAAI,MAAM,CAAC,EAAE,QAAQ,KAAK,MAAM,CAAC,KAAK,KAAA;AACjD;AAqBA,MAAM,uBAAuB,OAAO,IAAI,+BAA+B;AASvE,SAAS,kBAAmC;AAC1C,QAAM,SAAS;AACf,MAAI,WAAW,OAAO,oBAAoB;AAC1C,MAAI,CAAC,UAAU;AACb,mCAAe,IAAA;AACf,WAAO,oBAAoB,IAAI;AAAA,EACjC;AACA,SAAO;AACT;AAGO,SAAS,4BAA4B;AAAA,EAC1C;AAAA,EACA;AACF,GAGe;AACb,QAAM,WAAW,gBAAA;AACjB,MAAI,SAAS,IAAI,GAAG,GAAG;AAGrB,YAAQ;AAAA,MACN,8CAA8C,GAAG;AAAA,IAAA;AAAA,EAErD;AACA,WAAS,IAAI,KAAK,QAAQ;AAC1B,SAAO,MAAM;AACX,QAAI,SAAS,IAAI,GAAG,MAAM,SAAU,UAAS,OAAO,GAAG;AAAA,EACzD;AACF;AAGO,SAAS,oBAAoB,KAA8C;AAChF,SAAO,gBAAA,EAAkB,IAAI,GAAG;AAClC;"}
@@ -0,0 +1,335 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const pace = require("./pace.cjs");
4
+ const screen = require("./screen.cjs");
5
+ const tagOf = (element) => element.tagName.toUpperCase();
6
+ function isFocusable(element) {
7
+ return typeof element.focus === "function";
8
+ }
9
+ async function revealElement({
10
+ element,
11
+ pace: pace$1,
12
+ signal
13
+ }) {
14
+ pace.throwIfInterrupted(signal);
15
+ if (screen.isInViewport(element) && isFullyInViewport(element)) return;
16
+ element.scrollIntoView({
17
+ behavior: pace$1.smoothScroll ? "smooth" : "auto",
18
+ block: "center",
19
+ inline: "nearest"
20
+ });
21
+ if (pace$1.scrollSettleMaxMs <= 0) return;
22
+ const deadline = Date.now() + pace$1.scrollSettleMaxMs;
23
+ let last = element.getBoundingClientRect();
24
+ let still = 0;
25
+ while (Date.now() < deadline && still < 3) {
26
+ await pace.pause(50, signal);
27
+ const next = element.getBoundingClientRect();
28
+ still = Math.abs(next.top - last.top) < 0.5 && Math.abs(next.left - last.left) < 0.5 ? still + 1 : 0;
29
+ last = next;
30
+ }
31
+ }
32
+ function isFullyInViewport(element) {
33
+ const view = element.ownerDocument.defaultView;
34
+ if (!view) return false;
35
+ const rect = element.getBoundingClientRect();
36
+ return rect.top >= 0 && rect.left >= 0 && rect.bottom <= view.innerHeight && rect.right <= view.innerWidth;
37
+ }
38
+ function setNativeValue(control, value) {
39
+ var _a, _b, _c, _d;
40
+ const view = control.ownerDocument.defaultView;
41
+ const prototype = tagOf(control) === "TEXTAREA" ? (_a = view == null ? void 0 : view.HTMLTextAreaElement) == null ? void 0 : _a.prototype : tagOf(control) === "SELECT" ? (_b = view == null ? void 0 : view.HTMLSelectElement) == null ? void 0 : _b.prototype : (_c = view == null ? void 0 : view.HTMLInputElement) == null ? void 0 : _c.prototype;
42
+ const setter = prototype ? (_d = Object.getOwnPropertyDescriptor(prototype, "value")) == null ? void 0 : _d.set : void 0;
43
+ if (setter) setter.call(control, value);
44
+ else control.value = value;
45
+ }
46
+ function fire(element, type, init = {}) {
47
+ const view = element.ownerDocument.defaultView;
48
+ const EventClass = (view == null ? void 0 : view.Event) ?? Event;
49
+ const event = new EventClass(type, { bubbles: true, cancelable: true, ...init });
50
+ return element.dispatchEvent(event);
51
+ }
52
+ function fireInput(control, data) {
53
+ const view = control.ownerDocument.defaultView;
54
+ const InputEventClass = view == null ? void 0 : view.InputEvent;
55
+ if (InputEventClass) {
56
+ control.dispatchEvent(
57
+ new InputEventClass("input", {
58
+ bubbles: true,
59
+ cancelable: false,
60
+ data,
61
+ inputType: data === null ? "deleteContentBackward" : "insertText"
62
+ })
63
+ );
64
+ } else {
65
+ fire(control, "input");
66
+ }
67
+ }
68
+ function isEditable(control) {
69
+ const editable = control.getAttribute("contenteditable");
70
+ return editable === "" || editable === "true";
71
+ }
72
+ function writeValue(control, value, typed) {
73
+ if (isEditable(control)) control.textContent = value;
74
+ else setNativeValue(control, value);
75
+ fireInput(control, typed);
76
+ }
77
+ function typingSteps({ text, pace: pace2 }) {
78
+ const characters = Array.from(text);
79
+ if (!pace2.typeByCharacter || characters.length === 0) return [text];
80
+ const most = pace2.typeCharMs > 0 ? Math.max(1, Math.floor(pace2.typeMaxMs / pace2.typeCharMs)) : 1;
81
+ const size = Math.max(1, Math.ceil(characters.length / most));
82
+ const steps = [];
83
+ for (let index = size; index < characters.length; index += size) {
84
+ steps.push(characters.slice(0, index).join(""));
85
+ }
86
+ steps.push(text);
87
+ return steps;
88
+ }
89
+ function pressEnter(control) {
90
+ const view = control.ownerDocument.defaultView;
91
+ const KeyboardEventClass = (view == null ? void 0 : view.KeyboardEvent) ?? KeyboardEvent;
92
+ const init = { key: "Enter", code: "Enter", keyCode: 13, which: 13, bubbles: true, cancelable: true };
93
+ const proceeded = control.dispatchEvent(new KeyboardEventClass("keydown", init));
94
+ const form = control.form;
95
+ if (proceeded && form && typeof form.requestSubmit === "function") form.requestSubmit();
96
+ control.dispatchEvent(new KeyboardEventClass("keyup", init));
97
+ }
98
+ function clickElement(element) {
99
+ const view = element.ownerDocument.defaultView;
100
+ const rect = element.getBoundingClientRect();
101
+ const point = {
102
+ bubbles: true,
103
+ cancelable: true,
104
+ composed: true,
105
+ button: 0,
106
+ clientX: rect.left + rect.width / 2,
107
+ clientY: rect.top + rect.height / 2,
108
+ view
109
+ };
110
+ const PointerEventClass = view == null ? void 0 : view.PointerEvent;
111
+ const MouseEventClass = (view == null ? void 0 : view.MouseEvent) ?? MouseEvent;
112
+ const pointer = { ...point, pointerId: 1, pointerType: "mouse", isPrimary: true };
113
+ if (PointerEventClass) element.dispatchEvent(new PointerEventClass("pointerdown", { ...pointer, buttons: 1 }));
114
+ element.dispatchEvent(new MouseEventClass("mousedown", { ...point, buttons: 1 }));
115
+ if (isFocusable(element)) element.focus({ preventScroll: true });
116
+ if (PointerEventClass) element.dispatchEvent(new PointerEventClass("pointerup", pointer));
117
+ element.dispatchEvent(new MouseEventClass("mouseup", point));
118
+ if (typeof element.click === "function") element.click();
119
+ else element.dispatchEvent(new MouseEventClass("click", point));
120
+ }
121
+ const sameWords = (text) => text.replace(/\s+/g, " ").trim().toLowerCase();
122
+ function matchOption(options, wanted) {
123
+ const text = sameWords(wanted).replace(/…$/, "").trimEnd();
124
+ const exact = options.filter(
125
+ (option) => sameWords(option.label) === text || option.value.toLowerCase() === text
126
+ );
127
+ if (exact[0]) return exact[0];
128
+ const starts = options.filter((option) => sameWords(option.label).startsWith(text));
129
+ if (starts.length === 1) return starts[0];
130
+ const holds = options.filter((option) => sameWords(option.label).includes(text));
131
+ return holds.length === 1 ? holds[0] : void 0;
132
+ }
133
+ function assertCanAct({
134
+ target,
135
+ action
136
+ }) {
137
+ if (!target.actions.includes(action)) {
138
+ const allowed = target.actions.length > 0 ? target.actions.join(", ") : "nothing: it can only be pointed at";
139
+ throw new screen.AgentUiError(
140
+ `"${target.ref}" (${target.label}) does not allow "${action}". It allows: ${allowed}.`
141
+ );
142
+ }
143
+ if (target.disabled) {
144
+ throw new screen.AgentUiError(`"${target.ref}" (${target.label}) is disabled right now.`);
145
+ }
146
+ }
147
+ async function performAct({
148
+ target,
149
+ action,
150
+ text,
151
+ submit,
152
+ option,
153
+ checked,
154
+ pace: pace$1,
155
+ signal,
156
+ onPress,
157
+ beforeTouch
158
+ }) {
159
+ var _a;
160
+ assertCanAct({ target, action });
161
+ const { handlers } = target;
162
+ const control = target.control;
163
+ switch (action) {
164
+ case "click": {
165
+ onPress == null ? void 0 : onPress();
166
+ await pace.pause(pace$1.pressMs, signal);
167
+ beforeTouch == null ? void 0 : beforeTouch();
168
+ if (handlers == null ? void 0 : handlers.click) await handlers.click();
169
+ else clickElement(target.element);
170
+ return { detail: `Clicked ${target.label}.` };
171
+ }
172
+ case "toggle": {
173
+ const current = target.checked;
174
+ const wanted = checked ?? (current === null ? null : !current);
175
+ if (wanted !== null && current === wanted) {
176
+ return { unchanged: true, detail: `${target.label} is already ${wanted ? "on" : "off"}.` };
177
+ }
178
+ if (target.role === "radio" && wanted === false) {
179
+ throw new screen.AgentUiError(`"${target.ref}" is a radio button: choose another option instead of turning it off.`);
180
+ }
181
+ onPress == null ? void 0 : onPress();
182
+ await pace.pause(pace$1.pressMs, signal);
183
+ beforeTouch == null ? void 0 : beforeTouch();
184
+ if (handlers == null ? void 0 : handlers.toggle) await handlers.toggle({ checked: wanted });
185
+ else clickElement(control ?? target.element);
186
+ return { detail: `Turned ${target.label} ${wanted === false ? "off" : "on"}.` };
187
+ }
188
+ case "select": {
189
+ if (typeof option !== "string" || !option.trim()) {
190
+ throw new screen.AgentUiError(`Give the option to choose: act_on({ target: "${target.ref}", action: "select", option: "…" }).`);
191
+ }
192
+ if (handlers == null ? void 0 : handlers.select) {
193
+ const known = (_a = handlers.options) == null ? void 0 : _a.call(handlers);
194
+ const chosen2 = known ? matchOption(known.map((label) => ({ label, value: label })), option) : void 0;
195
+ if (known && !chosen2) {
196
+ throw new screen.AgentUiError(`"${option}" is not an option of ${target.label}. Options: ${known.join(", ")}.`);
197
+ }
198
+ onPress == null ? void 0 : onPress();
199
+ await pace.pause(pace$1.pressMs, signal);
200
+ beforeTouch == null ? void 0 : beforeTouch();
201
+ beforeTouch == null ? void 0 : beforeTouch();
202
+ await handlers.select({ option: (chosen2 == null ? void 0 : chosen2.label) ?? option.trim() });
203
+ return { detail: `Chose ${(chosen2 == null ? void 0 : chosen2.label) ?? option.trim()} in ${target.label}.` };
204
+ }
205
+ if (!control || tagOf(control) !== "SELECT") {
206
+ throw new screen.AgentUiError(`"${target.ref}" has no list of options to choose from.`);
207
+ }
208
+ const select = control;
209
+ const options = Array.from(select.options).filter((entry) => !entry.disabled).map((entry) => ({ label: (entry.label || entry.text).trim(), value: entry.value, entry }));
210
+ const chosen = matchOption(options, option);
211
+ if (!chosen) {
212
+ const listed = options.slice(0, 30).map((entry) => entry.label);
213
+ throw new screen.AgentUiError(
214
+ `"${option}" is not an option of ${target.label}. Options: ${listed.join(", ")}${options.length > listed.length ? ", …" : ""}.`
215
+ );
216
+ }
217
+ const already = select.multiple ? chosen.entry.selected : select.value === chosen.value;
218
+ if (already) {
219
+ return { unchanged: true, detail: `${target.label} is already ${chosen.label}.` };
220
+ }
221
+ onPress == null ? void 0 : onPress();
222
+ await pace.pause(pace$1.pressMs, signal);
223
+ beforeTouch == null ? void 0 : beforeTouch();
224
+ if (isFocusable(control)) control.focus({ preventScroll: true });
225
+ if (select.multiple) chosen.entry.selected = true;
226
+ else setNativeValue(control, chosen.value);
227
+ fire(control, "input");
228
+ fire(control, "change");
229
+ return { detail: `Chose ${chosen.label} in ${target.label}.` };
230
+ }
231
+ case "clear":
232
+ case "type": {
233
+ const value = action === "clear" ? "" : text ?? "";
234
+ if (action === "type" && typeof text !== "string") {
235
+ throw new screen.AgentUiError(`Give the text to type: act_on({ target: "${target.ref}", action: "type", text: "…" }).`);
236
+ }
237
+ if (target.sensitive && action === "type") {
238
+ throw new screen.AgentUiError(`"${target.ref}" holds a secret: only the person types into it.`);
239
+ }
240
+ if (action === "clear" && (handlers == null ? void 0 : handlers.clear)) {
241
+ beforeTouch == null ? void 0 : beforeTouch();
242
+ await handlers.clear();
243
+ return { detail: `Cleared ${target.label}.` };
244
+ }
245
+ if (handlers == null ? void 0 : handlers.type) {
246
+ for (const step of typingSteps({ text: value, pace: pace$1 })) {
247
+ pace.throwIfInterrupted(signal);
248
+ beforeTouch == null ? void 0 : beforeTouch();
249
+ await handlers.type({ text: step, done: false });
250
+ await pace.pause(pace$1.typeCharMs, signal);
251
+ }
252
+ await handlers.type({ text: value, done: true });
253
+ return { detail: action === "clear" ? `Cleared ${target.label}.` : `Typed into ${target.label}.` };
254
+ }
255
+ if (!control) {
256
+ throw new screen.AgentUiError(`"${target.ref}" has no field to type into.`);
257
+ }
258
+ if (target.readOnly) {
259
+ throw new screen.AgentUiError(`"${target.ref}" (${target.label}) is read-only: nobody can type into it.`);
260
+ }
261
+ const longest = Number(control.maxLength);
262
+ if (Number.isFinite(longest) && longest >= 0 && Array.from(value).length > longest) {
263
+ throw new screen.AgentUiError(`${target.label} takes at most ${longest} characters, and this text has ${Array.from(value).length}.`);
264
+ }
265
+ beforeTouch == null ? void 0 : beforeTouch();
266
+ if (isFocusable(control)) control.focus({ preventScroll: true });
267
+ if (value === "") {
268
+ writeValue(control, "", null);
269
+ } else {
270
+ let previous = "";
271
+ for (const step of typingSteps({ text: value, pace: pace$1 })) {
272
+ pace.throwIfInterrupted(signal);
273
+ beforeTouch == null ? void 0 : beforeTouch();
274
+ writeValue(control, step, step.slice(previous.length));
275
+ previous = step;
276
+ await pace.pause(pace$1.typeCharMs, signal);
277
+ }
278
+ }
279
+ fire(control, "change");
280
+ if (submit) pressEnter(control);
281
+ return {
282
+ detail: action === "clear" ? `Cleared ${target.label}.` : `Typed into ${target.label}${submit ? " and pressed Enter" : ""}.`
283
+ };
284
+ }
285
+ }
286
+ }
287
+ async function waitForPageToSettle({
288
+ doc,
289
+ pace: pace$1,
290
+ signal
291
+ }) {
292
+ var _a;
293
+ await pace.pause(pace$1.afterActMs, signal);
294
+ const Observer = (_a = doc.defaultView) == null ? void 0 : _a.MutationObserver;
295
+ if (pace$1.settleMaxMs <= 0 || !Observer || !doc.body) {
296
+ await new Promise((resolve) => setTimeout(resolve, 0));
297
+ return;
298
+ }
299
+ let lastChange = Date.now();
300
+ const observer = new Observer(() => {
301
+ lastChange = Date.now();
302
+ });
303
+ observer.observe(doc.body, { subtree: true, childList: true, attributes: true, characterData: true });
304
+ const deadline = Date.now() + pace$1.settleMaxMs;
305
+ try {
306
+ while (Date.now() < deadline && Date.now() - lastChange < pace$1.settleQuietMs) {
307
+ await pace.pause(40, signal);
308
+ }
309
+ } finally {
310
+ observer.disconnect();
311
+ }
312
+ }
313
+ function watchForPerson({
314
+ doc,
315
+ isLayerEvent,
316
+ isPersonEvent = (event) => event.isTrusted,
317
+ onTakeover
318
+ }) {
319
+ const types = ["pointerdown", "keydown", "wheel", "touchstart"];
320
+ const listener = (event) => {
321
+ if (!isPersonEvent(event) || isLayerEvent(event)) return;
322
+ onTakeover("person");
323
+ };
324
+ for (const type of types) doc.addEventListener(type, listener, { capture: true, passive: true });
325
+ return () => {
326
+ for (const type of types) doc.removeEventListener(type, listener, { capture: true });
327
+ };
328
+ }
329
+ exports.assertCanAct = assertCanAct;
330
+ exports.performAct = performAct;
331
+ exports.revealElement = revealElement;
332
+ exports.typingSteps = typingSteps;
333
+ exports.waitForPageToSettle = waitForPageToSettle;
334
+ exports.watchForPerson = watchForPerson;
335
+ //# sourceMappingURL=driver.cjs.map