@blario/react-native 0.0.0 → 0.1.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 (313) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +378 -0
  3. package/dist/BlarProvider.d.ts +74 -0
  4. package/dist/actions/batched.d.ts +1 -0
  5. package/dist/actions/click.d.ts +4 -0
  6. package/dist/actions/commit-watch.d.ts +15 -0
  7. package/dist/actions/events.d.ts +35 -0
  8. package/dist/actions/executor.d.ts +29 -0
  9. package/dist/actions/highlight.d.ts +4 -0
  10. package/dist/actions/interaction-guards.d.ts +9 -0
  11. package/dist/actions/landing.d.ts +11 -0
  12. package/dist/actions/navigation-driver.d.ts +22 -0
  13. package/dist/actions/parse.d.ts +4 -0
  14. package/dist/actions/result.d.ts +4 -0
  15. package/dist/actions/scrolling.d.ts +12 -0
  16. package/dist/actions/services.d.ts +20 -0
  17. package/dist/actions/settle.d.ts +13 -0
  18. package/dist/actions/switch.d.ts +3 -0
  19. package/dist/actions/target.d.ts +25 -0
  20. package/dist/actions/text-input.d.ts +9 -0
  21. package/dist/actions/types.d.ts +59 -0
  22. package/dist/agent/batch-commands.d.ts +17 -0
  23. package/dist/agent/compose.d.ts +23 -0
  24. package/dist/agent/controller.d.ts +28 -0
  25. package/dist/agent/frame-runner.d.ts +9 -0
  26. package/dist/agent/frames.d.ts +180 -0
  27. package/dist/agent/handlers/agent-command.d.ts +5 -0
  28. package/dist/agent/handlers/batch.d.ts +5 -0
  29. package/dist/agent/handlers/consent-screenshot.d.ts +4 -0
  30. package/dist/agent/handlers/context.d.ts +16 -0
  31. package/dist/agent/handlers/dom-state.d.ts +4 -0
  32. package/dist/agent/handlers/element-context.d.ts +2 -0
  33. package/dist/agent/handlers/screenshot.d.ts +3 -0
  34. package/dist/agent/handlers/text-dom.d.ts +5 -0
  35. package/dist/agent/stage.d.ts +13 -0
  36. package/dist/agent-mode/action-description.d.ts +3 -0
  37. package/dist/agent-mode/bubbles.d.ts +10 -0
  38. package/dist/agent-mode/colors.d.ts +12 -0
  39. package/dist/agent-mode/controller.d.ts +92 -0
  40. package/dist/agent-mode/cursor-follow.d.ts +12 -0
  41. package/dist/agent-mode/cursor.d.ts +37 -0
  42. package/dist/agent-mode/frames.d.ts +36 -0
  43. package/dist/agent-mode/haptics.d.ts +6 -0
  44. package/dist/agent-mode/keep-awake.d.ts +19 -0
  45. package/dist/agent-mode/layer-stacking.d.ts +7 -0
  46. package/dist/agent-mode/markdown.d.ts +19 -0
  47. package/dist/agent-mode/panel-geometry.d.ts +6 -0
  48. package/dist/agent-mode/resume.d.ts +13 -0
  49. package/dist/agent-mode/run-memory.d.ts +19 -0
  50. package/dist/agent-mode/spotlight-controller.d.ts +68 -0
  51. package/dist/agent-mode/spotlight.d.ts +6 -0
  52. package/dist/agent-mode/state.d.ts +9 -0
  53. package/dist/agent-mode/status.d.ts +11 -0
  54. package/dist/agent-mode/timers.d.ts +6 -0
  55. package/dist/agent-mode/ui/AgentDock.d.ts +37 -0
  56. package/dist/agent-mode/ui/AgentLayer.d.ts +19 -0
  57. package/dist/agent-mode/ui/AgentLayerHost.d.ts +8 -0
  58. package/dist/agent-mode/ui/AgentPanel.d.ts +19 -0
  59. package/dist/agent-mode/ui/AgentTap.d.ts +10 -0
  60. package/dist/agent-mode/ui/MarkdownText.d.ts +8 -0
  61. package/dist/agent-mode/ui/MessageBubbles.d.ts +9 -0
  62. package/dist/agent-mode/ui/PendingOverlay.d.ts +5 -0
  63. package/dist/agent-mode/ui/SpotlightLayer.d.ts +9 -0
  64. package/dist/agent-mode/ui/SpotlightOverlay.d.ts +7 -0
  65. package/dist/agent-mode/ui/StatusCard.d.ts +14 -0
  66. package/dist/agent-mode/ui/TargetPulse.d.ts +9 -0
  67. package/dist/agent-mode/ui/TouchShield.d.ts +5 -0
  68. package/dist/agent-mode/ui/WarningIcon.d.ts +6 -0
  69. package/dist/agent-mode/ui/full-window-overlay.d.ts +5 -0
  70. package/dist/agent-mode/ui/layer-host.d.ts +2 -0
  71. package/dist/backend/api-client.d.ts +38 -0
  72. package/dist/backend/api-types.d.ts +25 -0
  73. package/dist/backend/app-info.d.ts +14 -0
  74. package/dist/backend/backend-transport.d.ts +70 -0
  75. package/dist/backend/backoff.d.ts +9 -0
  76. package/dist/backend/base64.d.ts +1 -0
  77. package/dist/backend/errors.d.ts +17 -0
  78. package/dist/backend/foreground.d.ts +1 -0
  79. package/dist/backend/health.d.ts +17 -0
  80. package/dist/backend/identity-proof.d.ts +23 -0
  81. package/dist/backend/identity.d.ts +27 -0
  82. package/dist/backend/message-metadata.d.ts +19 -0
  83. package/dist/backend/presence.d.ts +20 -0
  84. package/dist/backend/response-cache.d.ts +9 -0
  85. package/dist/backend/session-change.d.ts +2 -0
  86. package/dist/backend/session-socket.d.ts +49 -0
  87. package/dist/backend/session.d.ts +39 -0
  88. package/dist/backend/socket-url.d.ts +9 -0
  89. package/dist/backend/storage.d.ts +13 -0
  90. package/dist/backend/tab-id.d.ts +2 -0
  91. package/dist/blar.d.ts +37 -0
  92. package/dist/chat/ChatLayer.d.ts +29 -0
  93. package/dist/chat/ChatSheet.d.ts +11 -0
  94. package/dist/chat/ChatView.d.ts +19 -0
  95. package/dist/chat/Launcher.d.ts +14 -0
  96. package/dist/chat/bridge.d.ts +22 -0
  97. package/dist/chat/chat-coordinator.d.ts +57 -0
  98. package/dist/chat/chat-state.d.ts +20 -0
  99. package/dist/chat/navigation.d.ts +5 -0
  100. package/dist/chat/options.d.ts +22 -0
  101. package/dist/chat/webview.d.ts +10 -0
  102. package/dist/chat/widget-bundle.generated.d.ts +2 -0
  103. package/dist/chat/widget-shell.d.ts +24 -0
  104. package/dist/confirmation/port.d.ts +12 -0
  105. package/dist/fiber/locate.d.ts +7 -0
  106. package/dist/fiber/narrow.d.ts +5 -0
  107. package/dist/fiber/reader.d.ts +10 -0
  108. package/dist/fiber/text.d.ts +4 -0
  109. package/dist/fiber/types.d.ts +21 -0
  110. package/dist/host/border-radius.d.ts +1 -0
  111. package/dist/host/instance.d.ts +9 -0
  112. package/dist/host/measure.d.ts +3 -0
  113. package/dist/host/rect.d.ts +22 -0
  114. package/dist/host/visible-area.d.ts +2 -0
  115. package/dist/index.d.ts +10 -0
  116. package/dist/nudge/NudgeBubble.d.ts +15 -0
  117. package/dist/nudge/nudge.d.ts +23 -0
  118. package/dist/optional/expo-haptics.d.ts +1 -0
  119. package/dist/optional/expo-keep-awake.d.ts +1 -0
  120. package/dist/optional/react-native-haptic-feedback.d.ts +1 -0
  121. package/dist/optional/react-native-keep-awake.d.ts +1 -0
  122. package/dist/overlay/AgentOverlay.d.ts +5 -0
  123. package/dist/screenshot/capture-backend.d.ts +6 -0
  124. package/dist/screenshot/screenshotter.d.ts +18 -0
  125. package/dist/screenshot/skia.d.ts +35 -0
  126. package/dist/screenshot/view-shot.d.ts +2 -0
  127. package/dist/snapshot/capture.d.ts +36 -0
  128. package/dist/snapshot/classify.d.ts +11 -0
  129. package/dist/snapshot/collect.d.ts +27 -0
  130. package/dist/snapshot/element-context.d.ts +9 -0
  131. package/dist/snapshot/icon.d.ts +2 -0
  132. package/dist/snapshot/kinds.d.ts +3 -0
  133. package/dist/snapshot/modals.d.ts +2 -0
  134. package/dist/snapshot/navigation.d.ts +14 -0
  135. package/dist/snapshot/ordering.d.ts +2 -0
  136. package/dist/snapshot/properties.d.ts +14 -0
  137. package/dist/snapshot/registry.d.ts +16 -0
  138. package/dist/snapshot/render.d.ts +10 -0
  139. package/dist/snapshot/route-summary.d.ts +3 -0
  140. package/dist/snapshot/scroll.d.ts +15 -0
  141. package/dist/snapshot/text-layout.d.ts +8 -0
  142. package/dist/snapshot/view.d.ts +30 -0
  143. package/dist/snapshot/visibility.d.ts +17 -0
  144. package/dist/snapshot/wrappers.d.ts +11 -0
  145. package/dist/telemetry/SdkErrorBoundary.d.ts +15 -0
  146. package/dist/telemetry/build-info.generated.d.ts +2 -0
  147. package/dist/telemetry/envelope.d.ts +53 -0
  148. package/dist/telemetry/reporter.d.ts +33 -0
  149. package/dist/telemetry/scrub.d.ts +1 -0
  150. package/dist/telemetry/sdk-reporter.d.ts +5 -0
  151. package/dist/transport/connection-options.d.ts +18 -0
  152. package/dist/transport/relay-transport.d.ts +17 -0
  153. package/dist/transport/transport.d.ts +7 -0
  154. package/dist/ui/insets.d.ts +8 -0
  155. package/dist/ui/strings.d.ts +33 -0
  156. package/dist/ui/useInsets.d.ts +2 -0
  157. package/dist/version.d.ts +1 -0
  158. package/package.json +84 -3
  159. package/src/BlarProvider.tsx +462 -0
  160. package/src/actions/batched.ts +16 -0
  161. package/src/actions/click.ts +54 -0
  162. package/src/actions/commit-watch.ts +86 -0
  163. package/src/actions/events.ts +58 -0
  164. package/src/actions/executor.ts +147 -0
  165. package/src/actions/highlight.ts +13 -0
  166. package/src/actions/interaction-guards.ts +44 -0
  167. package/src/actions/landing.ts +26 -0
  168. package/src/actions/navigation-driver.ts +131 -0
  169. package/src/actions/parse.ts +82 -0
  170. package/src/actions/result.ts +14 -0
  171. package/src/actions/scrolling.ts +112 -0
  172. package/src/actions/services.ts +24 -0
  173. package/src/actions/settle.ts +40 -0
  174. package/src/actions/switch.ts +31 -0
  175. package/src/actions/target.ts +77 -0
  176. package/src/actions/text-input.ts +82 -0
  177. package/src/actions/types.ts +52 -0
  178. package/src/agent/batch-commands.ts +33 -0
  179. package/src/agent/compose.ts +67 -0
  180. package/src/agent/controller.ts +106 -0
  181. package/src/agent/frame-runner.ts +27 -0
  182. package/src/agent/frames.ts +193 -0
  183. package/src/agent/handlers/agent-command.ts +74 -0
  184. package/src/agent/handlers/batch.ts +72 -0
  185. package/src/agent/handlers/consent-screenshot.ts +44 -0
  186. package/src/agent/handlers/context.ts +17 -0
  187. package/src/agent/handlers/dom-state.ts +19 -0
  188. package/src/agent/handlers/element-context.ts +9 -0
  189. package/src/agent/handlers/screenshot.ts +24 -0
  190. package/src/agent/handlers/text-dom.ts +17 -0
  191. package/src/agent/stage.ts +22 -0
  192. package/src/agent-mode/action-description.ts +25 -0
  193. package/src/agent-mode/bubbles.ts +31 -0
  194. package/src/agent-mode/colors.ts +40 -0
  195. package/src/agent-mode/controller.ts +459 -0
  196. package/src/agent-mode/cursor-follow.ts +43 -0
  197. package/src/agent-mode/cursor.ts +118 -0
  198. package/src/agent-mode/frames.ts +70 -0
  199. package/src/agent-mode/haptics.ts +49 -0
  200. package/src/agent-mode/keep-awake.ts +107 -0
  201. package/src/agent-mode/layer-stacking.ts +16 -0
  202. package/src/agent-mode/markdown.ts +84 -0
  203. package/src/agent-mode/panel-geometry.ts +27 -0
  204. package/src/agent-mode/resume.ts +16 -0
  205. package/src/agent-mode/run-memory.ts +65 -0
  206. package/src/agent-mode/spotlight-controller.ts +255 -0
  207. package/src/agent-mode/spotlight.ts +14 -0
  208. package/src/agent-mode/state.ts +36 -0
  209. package/src/agent-mode/status.ts +13 -0
  210. package/src/agent-mode/timers.ts +11 -0
  211. package/src/agent-mode/ui/AgentDock.tsx +285 -0
  212. package/src/agent-mode/ui/AgentLayer.tsx +123 -0
  213. package/src/agent-mode/ui/AgentLayerHost.tsx +37 -0
  214. package/src/agent-mode/ui/AgentPanel.tsx +187 -0
  215. package/src/agent-mode/ui/AgentTap.tsx +190 -0
  216. package/src/agent-mode/ui/MarkdownText.tsx +55 -0
  217. package/src/agent-mode/ui/MessageBubbles.tsx +58 -0
  218. package/src/agent-mode/ui/PendingOverlay.tsx +32 -0
  219. package/src/agent-mode/ui/SpotlightLayer.tsx +60 -0
  220. package/src/agent-mode/ui/SpotlightOverlay.tsx +35 -0
  221. package/src/agent-mode/ui/StatusCard.tsx +137 -0
  222. package/src/agent-mode/ui/TargetPulse.tsx +74 -0
  223. package/src/agent-mode/ui/TouchShield.tsx +22 -0
  224. package/src/agent-mode/ui/WarningIcon.tsx +32 -0
  225. package/src/agent-mode/ui/full-window-overlay.ts +16 -0
  226. package/src/agent-mode/ui/layer-host.ts +11 -0
  227. package/src/backend/api-client.ts +210 -0
  228. package/src/backend/api-types.ts +56 -0
  229. package/src/backend/app-info.ts +40 -0
  230. package/src/backend/backend-transport.ts +338 -0
  231. package/src/backend/backoff.ts +16 -0
  232. package/src/backend/base64.ts +25 -0
  233. package/src/backend/errors.ts +32 -0
  234. package/src/backend/foreground.ts +10 -0
  235. package/src/backend/health.ts +64 -0
  236. package/src/backend/identity-proof.ts +151 -0
  237. package/src/backend/identity.ts +31 -0
  238. package/src/backend/message-metadata.ts +28 -0
  239. package/src/backend/presence.ts +95 -0
  240. package/src/backend/response-cache.ts +45 -0
  241. package/src/backend/session-change.ts +14 -0
  242. package/src/backend/session-socket.ts +292 -0
  243. package/src/backend/session.ts +188 -0
  244. package/src/backend/socket-url.ts +25 -0
  245. package/src/backend/storage.ts +49 -0
  246. package/src/backend/tab-id.ts +24 -0
  247. package/src/blar.ts +100 -0
  248. package/src/chat/ChatLayer.tsx +109 -0
  249. package/src/chat/ChatSheet.tsx +82 -0
  250. package/src/chat/ChatView.tsx +127 -0
  251. package/src/chat/Launcher.tsx +72 -0
  252. package/src/chat/bridge.ts +56 -0
  253. package/src/chat/chat-coordinator.ts +189 -0
  254. package/src/chat/chat-state.ts +35 -0
  255. package/src/chat/navigation.ts +15 -0
  256. package/src/chat/options.ts +39 -0
  257. package/src/chat/webview.ts +23 -0
  258. package/src/chat/widget-bundle.generated.ts +2 -0
  259. package/src/chat/widget-shell.ts +170 -0
  260. package/src/confirmation/port.ts +15 -0
  261. package/src/fiber/locate.ts +41 -0
  262. package/src/fiber/narrow.ts +15 -0
  263. package/src/fiber/reader.ts +98 -0
  264. package/src/fiber/text.ts +52 -0
  265. package/src/fiber/types.ts +24 -0
  266. package/src/host/border-radius.ts +11 -0
  267. package/src/host/instance.ts +30 -0
  268. package/src/host/measure.ts +25 -0
  269. package/src/host/rect.ts +45 -0
  270. package/src/host/visible-area.ts +5 -0
  271. package/src/index.ts +10 -0
  272. package/src/nudge/NudgeBubble.tsx +117 -0
  273. package/src/nudge/nudge.ts +100 -0
  274. package/src/optional/expo-haptics.ts +11 -0
  275. package/src/optional/expo-keep-awake.ts +11 -0
  276. package/src/optional/react-native-haptic-feedback.ts +11 -0
  277. package/src/optional/react-native-keep-awake.ts +11 -0
  278. package/src/overlay/AgentOverlay.tsx +16 -0
  279. package/src/screenshot/capture-backend.ts +7 -0
  280. package/src/screenshot/screenshotter.ts +39 -0
  281. package/src/screenshot/skia.ts +107 -0
  282. package/src/screenshot/view-shot.ts +41 -0
  283. package/src/snapshot/capture.ts +163 -0
  284. package/src/snapshot/classify.ts +40 -0
  285. package/src/snapshot/collect.ts +136 -0
  286. package/src/snapshot/element-context.ts +66 -0
  287. package/src/snapshot/icon.ts +21 -0
  288. package/src/snapshot/kinds.ts +10 -0
  289. package/src/snapshot/modals.ts +21 -0
  290. package/src/snapshot/navigation.ts +47 -0
  291. package/src/snapshot/ordering.ts +26 -0
  292. package/src/snapshot/properties.ts +71 -0
  293. package/src/snapshot/registry.ts +33 -0
  294. package/src/snapshot/render.ts +140 -0
  295. package/src/snapshot/route-summary.ts +15 -0
  296. package/src/snapshot/scroll.ts +90 -0
  297. package/src/snapshot/text-layout.ts +59 -0
  298. package/src/snapshot/view.ts +87 -0
  299. package/src/snapshot/visibility.ts +63 -0
  300. package/src/snapshot/wrappers.ts +69 -0
  301. package/src/telemetry/SdkErrorBoundary.tsx +28 -0
  302. package/src/telemetry/build-info.generated.ts +2 -0
  303. package/src/telemetry/envelope.ts +83 -0
  304. package/src/telemetry/reporter.ts +170 -0
  305. package/src/telemetry/scrub.ts +10 -0
  306. package/src/telemetry/sdk-reporter.ts +37 -0
  307. package/src/transport/connection-options.ts +65 -0
  308. package/src/transport/relay-transport.ts +82 -0
  309. package/src/transport/transport.ts +8 -0
  310. package/src/ui/insets.ts +31 -0
  311. package/src/ui/strings.ts +102 -0
  312. package/src/ui/useInsets.ts +10 -0
  313. package/src/version.ts +1 -0
@@ -0,0 +1,193 @@
1
+ import { parsePageContentRequest } from '../actions/parse.ts';
2
+ import type { ActionErrorCode, ActionOutcome, PageContentRequest } from '../actions/types.ts';
3
+ import { isRecord, nonEmptyString } from '../fiber/narrow.ts';
4
+
5
+ export interface ScreenshotOptions {
6
+ quality: number;
7
+ maxWidth: number;
8
+ }
9
+
10
+ export type InboundFrame =
11
+ | { kind: 'ping' }
12
+ | { kind: 'agent_command'; commandId: string; data: unknown }
13
+ | { kind: 'agent_command_batch'; commandId: string; data: unknown }
14
+ | { kind: 'request_dom_state'; commandId: string }
15
+ | { kind: 'request_text_dom'; commandId: string; options: PageContentRequest }
16
+ | { kind: 'request_screenshot'; commandId: string; options: ScreenshotOptions }
17
+ | { kind: 'request_element_context'; commandId: string; agentIds: string[] }
18
+ | { kind: 'unknown'; name: string };
19
+
20
+ export type RoutedFrame = Exclude<InboundFrame, { kind: 'ping' | 'unknown' }>;
21
+
22
+ export interface DomStateResponseData {
23
+ schema_version: 3;
24
+ platform: 'react-native';
25
+ url: string;
26
+ title: string;
27
+ screen: string;
28
+ routes: string[];
29
+ html_snapshot: string;
30
+ }
31
+
32
+ export interface TextDomResponseData {
33
+ url: string;
34
+ title: string;
35
+ screen: string;
36
+ text_tree: string;
37
+ refs: never[];
38
+ }
39
+
40
+ export interface BoundingBox {
41
+ x: number;
42
+ y: number;
43
+ width: number;
44
+ height: number;
45
+ }
46
+
47
+ export interface ElementContextItem {
48
+ agent_id: string;
49
+ found: boolean;
50
+ tag_name?: string;
51
+ visible?: boolean;
52
+ line?: string;
53
+ bounding_box?: BoundingBox;
54
+ text_content?: string;
55
+ placeholder?: string;
56
+ nearest_label?: string;
57
+ sibling_text?: string[];
58
+ }
59
+
60
+ export interface ElementContextResponseData {
61
+ elements: ElementContextItem[];
62
+ }
63
+
64
+ export type PageContentSkipReason = 'no_op';
65
+
66
+ export interface AgentCommandResponseData extends ActionOutcome {
67
+ page_content?: TextDomResponseData;
68
+ page_content_skipped?: PageContentSkipReason;
69
+ }
70
+
71
+ export interface BatchActionResult {
72
+ command_id: string;
73
+ success: boolean;
74
+ message: string;
75
+ error?: ActionErrorCode;
76
+ no_op?: boolean;
77
+ total_duration_ms: number;
78
+ }
79
+
80
+ export interface BatchDomStateResult {
81
+ command_id: string;
82
+ type: 'dom_state';
83
+ dom_state: DomStateResponseData;
84
+ total_duration_ms: number;
85
+ }
86
+
87
+ export interface BatchTextDomResult {
88
+ command_id: string;
89
+ type: 'text_dom';
90
+ text_dom: TextDomResponseData;
91
+ total_duration_ms: number;
92
+ }
93
+
94
+ export type BatchCommandResult = BatchActionResult | BatchDomStateResult | BatchTextDomResult;
95
+
96
+ export interface BatchResponseData {
97
+ results: BatchCommandResult[];
98
+ stopped_at: number | null;
99
+ stopped_reason?: 'cancelled' | 'check_unreached';
100
+ }
101
+
102
+ export interface ScreenshotResponseData {
103
+ success: true;
104
+ image: string;
105
+ format: 'jpeg';
106
+ screen: string;
107
+ timestamp: number;
108
+ partial?: true;
109
+ omitted?: Array<'modal'>;
110
+ }
111
+
112
+ export interface ConsentScreenshotMetadata {
113
+ action_type: string;
114
+ element_description: string;
115
+ url: string;
116
+ }
117
+
118
+ export type ConsentScreenshotData =
119
+ | { success: true; image: string; screenshot_metadata: ConsentScreenshotMetadata; capture_duration_ms: number; sent_at: number }
120
+ | { success: false; screenshot_error: 'CAPTURE_FAILED'; sent_at: number };
121
+
122
+ export type OutboundFrame =
123
+ | { type: 'command_ack'; command_id: string }
124
+ | { type: 'pong' }
125
+ | { type: 'sdk_response'; command_id: string; response_type: 'dom_state_response'; data: DomStateResponseData }
126
+ | { type: 'sdk_response'; command_id: string; response_type: 'text_dom_response'; data: TextDomResponseData }
127
+ | { type: 'sdk_response'; command_id: string; response_type: 'element_context_response'; data: ElementContextResponseData }
128
+ | { type: 'sdk_response'; command_id: string; response_type: 'agent_command_response'; data: AgentCommandResponseData }
129
+ | { type: 'sdk_response'; command_id: string; response_type: 'agent_command_response'; data: ConsentScreenshotData }
130
+ | { type: 'sdk_response'; command_id: string; response_type: 'agent_command_batch_response'; data: BatchResponseData }
131
+ | { type: 'sdk_response'; command_id: string; response_type: 'screenshot_response'; data: ScreenshotResponseData }
132
+ | { type: 'sdk_error'; command_id: string; error: string; code?: string }
133
+ | { type: 'agent_cancel'; reason: string };
134
+
135
+ const DEFAULT_SCREENSHOT: ScreenshotOptions = { quality: 0.7, maxWidth: 720 };
136
+ const MAX_SCREENSHOT_WIDTH = 1280;
137
+
138
+ function parseScreenshotOptions(data: unknown): ScreenshotOptions {
139
+ const options = isRecord(data) && isRecord(data.options) ? data.options : null;
140
+ if (options === null) {
141
+ return DEFAULT_SCREENSHOT;
142
+ }
143
+ return {
144
+ quality: typeof options.quality === 'number' ? options.quality : DEFAULT_SCREENSHOT.quality,
145
+ maxWidth: typeof options.maxWidth === 'number' ? Math.min(options.maxWidth, MAX_SCREENSHOT_WIDTH) : DEFAULT_SCREENSHOT.maxWidth,
146
+ };
147
+ }
148
+
149
+ function parseAgentIds(data: unknown): string[] {
150
+ const ids = isRecord(data) && Array.isArray(data.agent_ids) ? data.agent_ids : [];
151
+ return ids.filter((id): id is string => typeof id === 'string');
152
+ }
153
+
154
+ function parseJson(raw: string): unknown {
155
+ try {
156
+ return JSON.parse(raw);
157
+ } catch {
158
+ return null;
159
+ }
160
+ }
161
+
162
+ export function parseInboundFrame(raw: string): InboundFrame | null {
163
+ const parsed = parseJson(raw);
164
+ if (!isRecord(parsed)) {
165
+ return null;
166
+ }
167
+ if (parsed.type === 'ping') {
168
+ return { kind: 'ping' };
169
+ }
170
+ const name = nonEmptyString(parsed.event);
171
+ if (name === null) {
172
+ return null;
173
+ }
174
+ const data = isRecord(parsed.data) ? parsed.data : {};
175
+ const commandId = nonEmptyString(data.batch_id) ?? nonEmptyString(parsed.command_id) ?? nonEmptyString(data.command_id) ?? '';
176
+ switch (name) {
177
+ case 'agent_command':
178
+ return { kind: 'agent_command', commandId, data };
179
+ case 'agent_command_batch':
180
+ return { kind: 'agent_command_batch', commandId, data };
181
+ case 'request_dom_state':
182
+ case 'dom_state_request':
183
+ return { kind: 'request_dom_state', commandId };
184
+ case 'request_text_dom':
185
+ return { kind: 'request_text_dom', commandId, options: parsePageContentRequest(data.options) };
186
+ case 'request_screenshot':
187
+ return { kind: 'request_screenshot', commandId, options: parseScreenshotOptions(data) };
188
+ case 'request_element_context':
189
+ return { kind: 'request_element_context', commandId, agentIds: parseAgentIds(data) };
190
+ default:
191
+ return { kind: 'unknown', name };
192
+ }
193
+ }
@@ -0,0 +1,74 @@
1
+ import { describeUnparsedAction, parseAgentCommand } from '../../actions/parse.ts';
2
+ import { viewOf } from '../../actions/target.ts';
3
+ import type { ActionOutcome, AgentCommandData } from '../../actions/types.ts';
4
+ import type { ConfirmationOutcome } from '../../confirmation/port.ts';
5
+ import type { AgentCommandResponseData } from '../frames.ts';
6
+ import { sendConsentScreenshot } from './consent-screenshot.ts';
7
+ import type { HandlerContext } from './context.ts';
8
+ import { readTextDom } from './text-dom.ts';
9
+
10
+ const CONFIRMATION_RISKS: readonly string[] = ['irreversible', 'destructive'];
11
+ const UNCONFIRMED_ACTIONS: readonly string[] = ['highlight', 'scroll_to'];
12
+
13
+ function needsConfirmation(command: AgentCommandData): boolean {
14
+ return !UNCONFIRMED_ACTIONS.includes(command.action.type) && (command.confirmation_required || CONFIRMATION_RISKS.includes(command.risk_level));
15
+ }
16
+
17
+ function declined(outcome: Exclude<ConfirmationOutcome, 'confirmed'>): ActionOutcome {
18
+ return {
19
+ success: false,
20
+ no_op: false,
21
+ error: 'user_cancelled',
22
+ message: outcome === 'timed_out' ? 'User did not respond to the confirmation prompt' : 'User declined the action',
23
+ };
24
+ }
25
+
26
+ async function attachPageContent(
27
+ context: HandlerContext,
28
+ command: AgentCommandData,
29
+ response: AgentCommandResponseData,
30
+ ): Promise<AgentCommandResponseData> {
31
+ if (!command.include_page_content || !response.success) {
32
+ return response;
33
+ }
34
+ if (response.no_op) {
35
+ return { ...response, page_content_skipped: 'no_op' };
36
+ }
37
+ return { ...response, page_content: await readTextDom(context, command.page_content_options) };
38
+ }
39
+
40
+ export async function runAgentCommand(context: HandlerContext, command: AgentCommandData): Promise<AgentCommandResponseData> {
41
+ const prepared = await context.executor.prepare(command);
42
+ if (!prepared.ok) {
43
+ return prepared.outcome;
44
+ }
45
+ if (needsConfirmation(command)) {
46
+ const outcome = await context.confirmation.confirm({
47
+ riskLevel: command.risk_level,
48
+ action: command.action,
49
+ explanation: command.teaching_explanation,
50
+ target: prepared.target === null ? null : viewOf(prepared.target),
51
+ });
52
+ if (outcome !== 'confirmed') {
53
+ return declined(outcome);
54
+ }
55
+ await sendConsentScreenshot(context, command, prepared.target);
56
+ }
57
+ context.stage.beforeAction(command);
58
+ const outcome = await context.executor.execute(command, prepared.target, { isCancelled: () => context.stage.isCancelled() });
59
+ return attachPageContent(context, command, outcome);
60
+ }
61
+
62
+ export async function handleAgentCommand(context: HandlerContext, commandId: string, data: unknown): Promise<void> {
63
+ const command = parseAgentCommand(data);
64
+ if (command === null) {
65
+ context.send({ type: 'sdk_error', command_id: commandId, error: describeUnparsedAction(data), code: 'unsupported_action' });
66
+ return;
67
+ }
68
+ context.send({
69
+ type: 'sdk_response',
70
+ command_id: commandId,
71
+ response_type: 'agent_command_response',
72
+ data: await runAgentCommand(context, command),
73
+ });
74
+ }
@@ -0,0 +1,72 @@
1
+ import type { AgentCommandData } from '../../actions/types.ts';
2
+ import { SYSTEM_CLOCK } from '../../actions/settle.ts';
3
+ import { parseBatchEntries, type BatchEntry } from '../batch-commands.ts';
4
+ import type { BatchActionResult, BatchCommandResult, BatchResponseData } from '../frames.ts';
5
+ import { runAgentCommand } from './agent-command.ts';
6
+ import type { HandlerContext } from './context.ts';
7
+ import { readDomState } from './dom-state.ts';
8
+ import { readTextDom } from './text-dom.ts';
9
+
10
+ async function runAction(context: HandlerContext, command: AgentCommandData): Promise<BatchActionResult> {
11
+ const startedAt = Date.now();
12
+ const response = await runAgentCommand(context, command);
13
+ return {
14
+ command_id: command.command_id,
15
+ success: response.success,
16
+ message: response.message ?? (response.success ? `${command.action.type} completed` : 'Action failed'),
17
+ ...(response.error === undefined ? {} : { error: response.error }),
18
+ no_op: response.no_op,
19
+ total_duration_ms: Date.now() - startedAt,
20
+ };
21
+ }
22
+
23
+ async function runEntry(context: HandlerContext, entry: BatchEntry): Promise<BatchCommandResult> {
24
+ const startedAt = Date.now();
25
+ switch (entry.kind) {
26
+ case 'action':
27
+ return runAction(context, entry.command);
28
+ case 'dom_state':
29
+ return { command_id: entry.commandId, type: 'dom_state', dom_state: await readDomState(context), total_duration_ms: Date.now() - startedAt };
30
+ case 'text_dom':
31
+ return {
32
+ command_id: entry.commandId,
33
+ type: 'text_dom',
34
+ text_dom: await readTextDom(context, entry.options),
35
+ total_duration_ms: Date.now() - startedAt,
36
+ };
37
+ case 'invalid':
38
+ return { command_id: entry.commandId, success: false, message: entry.reason, error: 'unsupported_action', total_duration_ms: 0 };
39
+ }
40
+ }
41
+
42
+ function isFailure(result: BatchCommandResult): result is BatchActionResult {
43
+ return 'success' in result && !result.success;
44
+ }
45
+
46
+ export async function runBatch(context: HandlerContext, entries: BatchEntry[]): Promise<BatchResponseData> {
47
+ const results: BatchCommandResult[] = [];
48
+ for (const [index, entry] of entries.entries()) {
49
+ if (context.stage.isCancelled()) {
50
+ return { results, stopped_at: index, stopped_reason: 'cancelled' };
51
+ }
52
+ const result = await runEntry(context, entry);
53
+ results.push(result);
54
+ if (isFailure(result)) {
55
+ const cancelled = result.error === 'user_cancelled';
56
+ return { results, stopped_at: cancelled ? index + 1 : index, ...(cancelled ? { stopped_reason: 'cancelled' as const } : {}) };
57
+ }
58
+ if (entry.kind === 'action' && entry.command.dom_settle_ms > 0) {
59
+ await SYSTEM_CLOCK.sleep(entry.command.dom_settle_ms);
60
+ }
61
+ }
62
+ return { results, stopped_at: null };
63
+ }
64
+
65
+ export async function handleBatch(context: HandlerContext, batchId: string, data: unknown): Promise<void> {
66
+ context.send({
67
+ type: 'sdk_response',
68
+ command_id: batchId,
69
+ response_type: 'agent_command_batch_response',
70
+ data: await runBatch(context, parseBatchEntries(data)),
71
+ });
72
+ }
@@ -0,0 +1,44 @@
1
+ import { nameOf, type ResolvedTarget } from '../../actions/target.ts';
2
+ import type { AgentCommandData } from '../../actions/types.ts';
3
+ import type { ScreenshotOptions } from '../frames.ts';
4
+ import type { HandlerContext } from './context.ts';
5
+
6
+ const CONSENT_CAPTURE: ScreenshotOptions = { quality: 0.6, maxWidth: 1280 };
7
+
8
+ export async function sendConsentScreenshot(context: HandlerContext, command: AgentCommandData, target: ResolvedTarget | null): Promise<void> {
9
+ if (!context.screenshotter.isAvailable() || context.stage.isCancelled()) {
10
+ return;
11
+ }
12
+ const commandId = `${command.command_id}:screenshot`;
13
+ const startedAt = Date.now();
14
+ const result = await context.screenshotter.capture(CONSENT_CAPTURE);
15
+ if (context.stage.isCancelled()) {
16
+ return;
17
+ }
18
+ if (!result.ok) {
19
+ context.send({
20
+ type: 'sdk_response',
21
+ command_id: commandId,
22
+ response_type: 'agent_command_response',
23
+ data: { success: false, screenshot_error: 'CAPTURE_FAILED', sent_at: Date.now() },
24
+ });
25
+ return;
26
+ }
27
+ const { screen } = await context.capturer.capture('peek');
28
+ context.send({
29
+ type: 'sdk_response',
30
+ command_id: commandId,
31
+ response_type: 'agent_command_response',
32
+ data: {
33
+ success: true,
34
+ image: result.dataUrl,
35
+ screenshot_metadata: {
36
+ action_type: command.action.type,
37
+ element_description: (target === null ? null : nameOf(target)) ?? command.action.type,
38
+ url: screen,
39
+ },
40
+ capture_duration_ms: Date.now() - startedAt,
41
+ sent_at: Date.now(),
42
+ },
43
+ });
44
+ }
@@ -0,0 +1,17 @@
1
+ import type { ActionExecutor } from '../../actions/executor.ts';
2
+ import type { ConfirmationPort } from '../../confirmation/port.ts';
3
+ import type { Screenshotter } from '../../screenshot/screenshotter.ts';
4
+ import type { SnapshotCapturer } from '../../snapshot/capture.ts';
5
+ import type { ElementContextReader } from '../../snapshot/element-context.ts';
6
+ import type { OutboundFrame } from '../frames.ts';
7
+ import type { AgentStage } from '../stage.ts';
8
+
9
+ export interface HandlerContext {
10
+ capturer: SnapshotCapturer;
11
+ executor: ActionExecutor;
12
+ screenshotter: Screenshotter;
13
+ elementContext: ElementContextReader;
14
+ confirmation: ConfirmationPort;
15
+ stage: AgentStage;
16
+ send: (frame: OutboundFrame) => void;
17
+ }
@@ -0,0 +1,19 @@
1
+ import type { DomStateResponseData } from '../frames.ts';
2
+ import type { HandlerContext } from './context.ts';
3
+
4
+ export async function readDomState(context: HandlerContext): Promise<DomStateResponseData> {
5
+ const snapshot = await context.capturer.capture('register');
6
+ return {
7
+ schema_version: 3,
8
+ platform: 'react-native',
9
+ url: snapshot.screen,
10
+ title: '',
11
+ screen: snapshot.screen,
12
+ routes: snapshot.routes,
13
+ html_snapshot: snapshot.text,
14
+ };
15
+ }
16
+
17
+ export async function handleDomState(context: HandlerContext, commandId: string): Promise<void> {
18
+ context.send({ type: 'sdk_response', command_id: commandId, response_type: 'dom_state_response', data: await readDomState(context) });
19
+ }
@@ -0,0 +1,9 @@
1
+ import type { HandlerContext } from './context.ts';
2
+
3
+ export async function handleElementContext(context: HandlerContext, commandId: string, agentIds: string[]): Promise<void> {
4
+ const elements = [];
5
+ for (const agentId of agentIds) {
6
+ elements.push(await context.elementContext.read(agentId));
7
+ }
8
+ context.send({ type: 'sdk_response', command_id: commandId, response_type: 'element_context_response', data: { elements } });
9
+ }
@@ -0,0 +1,24 @@
1
+ import type { ScreenshotOptions } from '../frames.ts';
2
+ import type { HandlerContext } from './context.ts';
3
+
4
+ export async function handleScreenshot(context: HandlerContext, commandId: string, options: ScreenshotOptions): Promise<void> {
5
+ const result = await context.screenshotter.capture(options);
6
+ if (!result.ok) {
7
+ context.send({ type: 'sdk_error', command_id: commandId, error: result.reason, code: 'CAPTURE_FAILED' });
8
+ return;
9
+ }
10
+ const snapshot = await context.capturer.capture('peek');
11
+ context.send({
12
+ type: 'sdk_response',
13
+ command_id: commandId,
14
+ response_type: 'screenshot_response',
15
+ data: {
16
+ success: true,
17
+ image: result.dataUrl,
18
+ format: 'jpeg',
19
+ screen: snapshot.screen,
20
+ timestamp: Date.now(),
21
+ ...(snapshot.modalOpen && !result.capturesModals ? { partial: true as const, omitted: ['modal' as const] } : {}),
22
+ },
23
+ });
24
+ }
@@ -0,0 +1,17 @@
1
+ import type { PageContentRequest } from '../../actions/types.ts';
2
+ import type { TextDomResponseData } from '../frames.ts';
3
+ import type { HandlerContext } from './context.ts';
4
+
5
+ export async function readTextDom(context: HandlerContext, options: PageContentRequest): Promise<TextDomResponseData> {
6
+ const tree = await context.capturer.captureText(options);
7
+ return { url: tree.screen, title: '', screen: tree.screen, text_tree: tree.text, refs: [] };
8
+ }
9
+
10
+ export async function handleTextDom(context: HandlerContext, commandId: string, options: PageContentRequest): Promise<void> {
11
+ context.send({
12
+ type: 'sdk_response',
13
+ command_id: commandId,
14
+ response_type: 'text_dom_response',
15
+ data: await readTextDom(context, options),
16
+ });
17
+ }
@@ -0,0 +1,22 @@
1
+ import type { AgentCommandData } from '../actions/types.ts';
2
+
3
+ export interface Admission {
4
+ kind: 'command' | 'batch';
5
+ keepText: string | null;
6
+ }
7
+
8
+ export interface AgentStage {
9
+ received: () => void;
10
+ admit: (admission: Admission) => Promise<boolean>;
11
+ beforeAction: (command: AgentCommandData) => void;
12
+ settled: () => void;
13
+ isCancelled: () => boolean;
14
+ }
15
+
16
+ export const OPEN_STAGE: AgentStage = {
17
+ received: () => undefined,
18
+ admit: () => Promise.resolve(true),
19
+ beforeAction: () => undefined,
20
+ settled: () => undefined,
21
+ isCancelled: () => false,
22
+ };
@@ -0,0 +1,25 @@
1
+ import type { AgentAction } from '../actions/types.ts';
2
+ import type { UiStrings } from '../ui/strings.ts';
3
+
4
+ const TYPED_VALUE_LIMIT = 40;
5
+
6
+ function fill(template: string, value: string): string {
7
+ return template.replace('{value}', value);
8
+ }
9
+
10
+ export function describeAction(action: AgentAction, strings: UiStrings): string {
11
+ switch (action.type) {
12
+ case 'click':
13
+ return strings.actionClick;
14
+ case 'type':
15
+ return fill(strings.actionType, action.value.length > TYPED_VALUE_LIMIT ? `${action.value.slice(0, TYPED_VALUE_LIMIT)}...` : action.value);
16
+ case 'check':
17
+ return action.checked === true ? strings.actionCheck : strings.actionUncheck;
18
+ case 'scroll_to':
19
+ return strings.actionScrollTo;
20
+ case 'press_key':
21
+ return fill(strings.actionPressKey, action.key);
22
+ default:
23
+ return strings.actionFallback;
24
+ }
25
+ }
@@ -0,0 +1,31 @@
1
+ export interface AgentBubble {
2
+ id: string;
3
+ text: string;
4
+ leaving: boolean;
5
+ }
6
+
7
+ const READING_WPM = 120;
8
+ const MIN_READING_MS = 6_000;
9
+ const MAX_READING_MS = 18_000;
10
+
11
+ export function readingTimeMs(text: string): number {
12
+ const words = text.trim().split(/\s+/).length;
13
+ const milliseconds = (words / READING_WPM) * 60 * 1000;
14
+ return Math.max(MIN_READING_MS, Math.min(MAX_READING_MS, milliseconds));
15
+ }
16
+
17
+ export function isShowing(bubbles: readonly AgentBubble[], text: string): boolean {
18
+ return bubbles.some((bubble) => bubble.text === text);
19
+ }
20
+
21
+ export function keepOnly(bubbles: readonly AgentBubble[], keepText: string | null): AgentBubble[] {
22
+ return keepText === null ? [] : bubbles.filter((bubble) => bubble.text === keepText);
23
+ }
24
+
25
+ export function markLeaving(bubbles: readonly AgentBubble[], id: string): AgentBubble[] {
26
+ return bubbles.map((bubble) => (bubble.id === id ? { ...bubble, leaving: true } : bubble));
27
+ }
28
+
29
+ export function without(bubbles: readonly AgentBubble[], id: string): AgentBubble[] {
30
+ return bubbles.filter((bubble) => bubble.id !== id);
31
+ }
@@ -0,0 +1,40 @@
1
+ const HEX = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i;
2
+
3
+ export interface Rgb {
4
+ r: number;
5
+ g: number;
6
+ b: number;
7
+ }
8
+
9
+ export function parseHex(color: string): Rgb | null {
10
+ const match = HEX.exec(color.trim());
11
+ const digits = match?.[1];
12
+ if (digits === undefined) {
13
+ return null;
14
+ }
15
+ const full = digits.length === 3 ? digits.replace(/./g, (digit) => digit + digit) : digits;
16
+ return { r: parseInt(full.slice(0, 2), 16), g: parseInt(full.slice(2, 4), 16), b: parseInt(full.slice(4, 6), 16) };
17
+ }
18
+
19
+ function channel(value: number): number {
20
+ const linear = value / 255;
21
+ return linear <= 0.03928 ? linear / 12.92 : ((linear + 0.055) / 1.055) ** 2.4;
22
+ }
23
+
24
+ export function luminance(color: string): number | null {
25
+ const rgb = parseHex(color);
26
+ return rgb === null ? null : 0.2126 * channel(rgb.r) + 0.7152 * channel(rgb.g) + 0.0722 * channel(rgb.b);
27
+ }
28
+
29
+ export const ON_LIGHT = '#14161a';
30
+ export const ON_DARK = '#ffffff';
31
+
32
+ /** Text that reads on a brand-coloured surface: near-black on a light brand, white otherwise. */
33
+ export function onColor(color: string): string {
34
+ return (luminance(color) ?? 0) > 0.45 ? ON_LIGHT : ON_DARK;
35
+ }
36
+
37
+ export function withAlpha(color: string, alpha: number): string {
38
+ const rgb = parseHex(color);
39
+ return rgb === null ? color : `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha})`;
40
+ }