@opengeni/contracts 0.44.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (382) hide show
  1. package/dist/agent-authored-durable-text.d.ts +70 -0
  2. package/dist/agent-config.d.ts +869 -0
  3. package/dist/agent-instruction-changes.d.ts +110 -0
  4. package/dist/agent-learning.d.ts +243 -0
  5. package/dist/agent-topology.d.ts +292 -0
  6. package/dist/allowance-refusal.d.ts +9 -0
  7. package/dist/allowance-refusal.js +50 -0
  8. package/dist/allowance-refusal.js.map +1 -0
  9. package/dist/analytics-consent-report.d.ts +19 -0
  10. package/dist/analytics-consent-report.js +10 -0
  11. package/dist/analytics-consent-report.js.map +1 -0
  12. package/dist/api-key-presets.d.ts +19 -0
  13. package/dist/artifact-catalog.d.ts +161 -0
  14. package/dist/artifacts.d.ts +135 -11
  15. package/dist/atlassian-native-retirement.d.ts +16 -0
  16. package/dist/atlassian-native-retirement.js +30 -0
  17. package/dist/atlassian-native-retirement.js.map +1 -0
  18. package/dist/atlassian.d.ts +2 -0
  19. package/dist/atlassian.js +27 -11
  20. package/dist/browser-storage.d.ts +5 -0
  21. package/dist/browser-storage.js +9 -0
  22. package/dist/browser-storage.js.map +1 -0
  23. package/dist/bundled-skills.d.ts +40 -0
  24. package/dist/canonical-human-identities.d.ts +157 -0
  25. package/dist/canonical-human-identities.js +23 -0
  26. package/dist/canonical-human-identities.js.map +1 -0
  27. package/dist/chunk-2ACYHZFB.js +292 -0
  28. package/dist/chunk-2ACYHZFB.js.map +1 -0
  29. package/dist/chunk-2VORLN2R.js +353 -0
  30. package/dist/chunk-2VORLN2R.js.map +1 -0
  31. package/dist/{chunk-Y3MLSLB4.js → chunk-3SIZGL3O.js} +42 -30
  32. package/dist/chunk-3SIZGL3O.js.map +1 -0
  33. package/dist/chunk-46KLD2UH.js +186 -0
  34. package/dist/chunk-46KLD2UH.js.map +1 -0
  35. package/dist/chunk-4JGHSSB7.js +11 -0
  36. package/dist/chunk-4JGHSSB7.js.map +1 -0
  37. package/dist/{chunk-52SGB2QO.js → chunk-5IXYF6XN.js} +50 -10
  38. package/dist/chunk-5IXYF6XN.js.map +1 -0
  39. package/dist/chunk-BCS7WHCK.js +139 -0
  40. package/dist/chunk-BCS7WHCK.js.map +1 -0
  41. package/dist/chunk-EK7NDGFR.js +128 -0
  42. package/dist/chunk-EK7NDGFR.js.map +1 -0
  43. package/dist/{chunk-5UDNP7AO.js → chunk-FZ6BXQHL.js} +78 -38
  44. package/dist/chunk-FZ6BXQHL.js.map +1 -0
  45. package/dist/chunk-GRG7PHWQ.js +25 -0
  46. package/dist/chunk-GRG7PHWQ.js.map +1 -0
  47. package/dist/chunk-JUFK3T4Q.js +80 -0
  48. package/dist/chunk-JUFK3T4Q.js.map +1 -0
  49. package/dist/chunk-KHHZTBP7.js +11 -0
  50. package/dist/chunk-KHHZTBP7.js.map +1 -0
  51. package/dist/chunk-KR2V42UZ.js +187 -0
  52. package/dist/chunk-KR2V42UZ.js.map +1 -0
  53. package/dist/chunk-MZLBPKFE.js +168 -0
  54. package/dist/chunk-MZLBPKFE.js.map +1 -0
  55. package/dist/{chunk-H5BVCZKF.js → chunk-NRMCVHAU.js} +6 -5
  56. package/dist/chunk-NRMCVHAU.js.map +1 -0
  57. package/dist/{chunk-TZ6YEYN4.js → chunk-NVJMJWLL.js} +3 -1
  58. package/dist/chunk-NVJMJWLL.js.map +1 -0
  59. package/dist/chunk-O77VSFYZ.js +70 -0
  60. package/dist/chunk-O77VSFYZ.js.map +1 -0
  61. package/dist/chunk-PX3VCKAU.js +29 -0
  62. package/dist/chunk-PX3VCKAU.js.map +1 -0
  63. package/dist/chunk-QR5D2IQC.js +61 -0
  64. package/dist/chunk-QR5D2IQC.js.map +1 -0
  65. package/dist/chunk-RJ76DRPO.js +117 -0
  66. package/dist/chunk-RJ76DRPO.js.map +1 -0
  67. package/dist/chunk-RKX3NQYS.js +28 -0
  68. package/dist/chunk-RKX3NQYS.js.map +1 -0
  69. package/dist/{chunk-FJBG5E4O.js → chunk-SIOU2K4U.js} +45 -22
  70. package/dist/chunk-SIOU2K4U.js.map +1 -0
  71. package/dist/chunk-TSCDAGET.js +75 -0
  72. package/dist/chunk-TSCDAGET.js.map +1 -0
  73. package/dist/{chunk-LSB7GRLG.js → chunk-TXTQIYCV.js} +15 -11
  74. package/dist/chunk-TXTQIYCV.js.map +1 -0
  75. package/dist/chunk-X7ETVO4P.js +26544 -0
  76. package/dist/chunk-X7ETVO4P.js.map +1 -0
  77. package/dist/{chunk-CADSX6UN.js → chunk-Y3KNXVTD.js} +2 -2
  78. package/dist/chunk-YBW5JSA3.js +251 -0
  79. package/dist/chunk-YBW5JSA3.js.map +1 -0
  80. package/dist/chunk-YRXAWWCU.js +45 -0
  81. package/dist/chunk-YRXAWWCU.js.map +1 -0
  82. package/dist/claude-provider-account-authority.d.ts +18 -0
  83. package/dist/claude-subscription-oauth.d.ts +42 -0
  84. package/dist/claude-subscription-usage.d.ts +159 -0
  85. package/dist/client-error-report.d.ts +109 -0
  86. package/dist/client-error-report.js +87 -0
  87. package/dist/client-error-report.js.map +1 -0
  88. package/dist/code-search.d.ts +52 -0
  89. package/dist/code-search.js +40 -0
  90. package/dist/code-search.js.map +1 -0
  91. package/dist/codex-provider-account-authority.d.ts +23 -0
  92. package/dist/codex-provider-account-authority.js +23 -0
  93. package/dist/codex-provider-account-authority.js.map +1 -0
  94. package/dist/company-brain-governed-writes.d.ts +475 -0
  95. package/dist/company-brain.d.ts +336 -0
  96. package/dist/company-profile.d.ts +566 -0
  97. package/dist/connect.d.ts +325 -0
  98. package/dist/connect.js +191 -0
  99. package/dist/connect.js.map +1 -0
  100. package/dist/connection-account-label.d.ts +2 -0
  101. package/dist/connection-account-label.js +35 -0
  102. package/dist/connection-account-label.js.map +1 -0
  103. package/dist/connection-authority.d.ts +193 -0
  104. package/dist/connection-authority.js +50 -0
  105. package/dist/connection-authority.js.map +1 -0
  106. package/dist/connector-attachments.d.ts +95 -0
  107. package/dist/connector-attachments.js +51 -0
  108. package/dist/connector-attachments.js.map +1 -0
  109. package/dist/connector-tool-permissions.d.ts +73 -0
  110. package/dist/direct-model-provider.d.ts +36 -0
  111. package/dist/editable-artifact-codec-registry.d.ts +16 -7
  112. package/dist/editable-artifact-codec-registry.js +19 -9
  113. package/dist/editable-artifact-committed-transaction.d.ts +2 -2
  114. package/dist/editable-artifact-committed-transaction.js +2 -2
  115. package/dist/editable-artifact-live.d.ts +11 -12
  116. package/dist/editable-artifact-live.js +110 -45
  117. package/dist/editable-artifact-live.js.map +1 -1
  118. package/dist/editable-artifact-serialized-commit.d.ts +2 -1
  119. package/dist/editable-artifact-serialized-commit.js +8 -7
  120. package/dist/editable-artifact-versions.d.ts +20 -5
  121. package/dist/editable-artifact-versions.js +23 -5
  122. package/dist/editable-artifacts.d.ts +2 -0
  123. package/dist/editable-artifacts.js +48 -36
  124. package/dist/external-identities.d.ts +784 -0
  125. package/dist/external-identities.js +44 -0
  126. package/dist/external-identities.js.map +1 -0
  127. package/dist/feedback.d.ts +51 -0
  128. package/dist/github-repository-contracts.d.ts +36 -0
  129. package/dist/github-repository-contracts.js +59 -0
  130. package/dist/github-repository-contracts.js.map +1 -0
  131. package/dist/github-repository.d.ts +14 -0
  132. package/dist/github-repository.js +36 -0
  133. package/dist/github-repository.js.map +1 -0
  134. package/dist/google-drive.d.ts +90 -1
  135. package/dist/google-drive.js +91 -15
  136. package/dist/google-drive.js.map +1 -1
  137. package/dist/governed-learning-activation.d.ts +154 -0
  138. package/dist/governed-learning-evaluator.d.ts +152 -0
  139. package/dist/host-mcp-bindings.d.ts +326 -0
  140. package/dist/host-mcp-bindings.js +252 -0
  141. package/dist/host-mcp-bindings.js.map +1 -0
  142. package/dist/image-generation.d.ts +1 -0
  143. package/dist/index.d.ts +27574 -5374
  144. package/dist/index.js +2531 -177
  145. package/dist/insights-usage.d.ts +877 -0
  146. package/dist/insights-usage.js +509 -0
  147. package/dist/insights-usage.js.map +1 -0
  148. package/dist/interaction.d.ts +1752 -13
  149. package/dist/knowledge-entries.d.ts +982 -0
  150. package/dist/knowledge-preparation.d.ts +237 -0
  151. package/dist/knowledge.d.ts +740 -0
  152. package/dist/managed-auth-session-sets.d.ts +241 -0
  153. package/dist/managed-auth-session-sets.js +191 -0
  154. package/dist/managed-auth-session-sets.js.map +1 -0
  155. package/dist/managed-sign-in-methods.d.ts +56 -0
  156. package/dist/managed-sign-in-methods.js +46 -0
  157. package/dist/managed-sign-in-methods.js.map +1 -0
  158. package/dist/mcp-catalog-limits.d.ts +2 -0
  159. package/dist/mcp-endpoint.d.ts +7 -0
  160. package/dist/mcp-endpoint.js +9 -0
  161. package/dist/mcp-endpoint.js.map +1 -0
  162. package/dist/mcp-oauth.d.ts +71 -0
  163. package/dist/modal-native-proof-v2.d.ts +587 -0
  164. package/dist/modal-native-proof-v2.js +37 -0
  165. package/dist/modal-native-proof-v2.js.map +1 -0
  166. package/dist/model-availability.d.ts +15 -0
  167. package/dist/model-connection-access.d.ts +24 -0
  168. package/dist/model-context-inspector.d.ts +329 -0
  169. package/dist/model-display.d.ts +38 -0
  170. package/dist/model-display.js +134 -0
  171. package/dist/model-display.js.map +1 -0
  172. package/dist/model-picker-order.d.ts +25 -0
  173. package/dist/model-picker-order.js +42 -0
  174. package/dist/model-picker-order.js.map +1 -0
  175. package/dist/organization-access.d.ts +231 -0
  176. package/dist/organization-access.js +34 -0
  177. package/dist/organization-access.js.map +1 -0
  178. package/dist/organization-integration-policy.d.ts +27 -0
  179. package/dist/organization-membership-lifecycle.d.ts +1022 -0
  180. package/dist/organization-model-usage.d.ts +146 -0
  181. package/dist/organization-model-usage.js +67 -0
  182. package/dist/organization-model-usage.js.map +1 -0
  183. package/dist/organization-recovery.d.ts +337 -0
  184. package/dist/organization-recovery.js +37 -0
  185. package/dist/organization-recovery.js.map +1 -0
  186. package/dist/organization-usage.d.ts +138 -0
  187. package/dist/permissions.d.ts +54 -0
  188. package/dist/personal-github.d.ts +320 -0
  189. package/dist/personal-github.js +222 -0
  190. package/dist/personal-github.js.map +1 -0
  191. package/dist/plugin-discovery.d.ts +35 -0
  192. package/dist/plugin-discovery.js +7 -0
  193. package/dist/plugin-discovery.js.map +1 -0
  194. package/dist/preference-registry.d.ts +44 -1
  195. package/dist/product-analytics.d.ts +113 -0
  196. package/dist/product-lifecycle-facts.d.ts +73 -0
  197. package/dist/remember.d.ts +350 -0
  198. package/dist/retained-output.d.ts +8 -0
  199. package/dist/sandbox-file-artifacts.d.ts +57 -0
  200. package/dist/sandbox-provider-command.d.ts +113 -0
  201. package/dist/sandbox-recovery.d.ts +146 -0
  202. package/dist/sandbox-snapshots.d.ts +39 -0
  203. package/dist/session-final-reply.d.ts +6 -0
  204. package/dist/session-final-reply.js +9 -0
  205. package/dist/session-final-reply.js.map +1 -0
  206. package/dist/session-goal-reports.d.ts +21 -0
  207. package/dist/session-history-import.d.ts +947 -0
  208. package/dist/session-list-entries.d.ts +4 -0
  209. package/dist/session-list-entries.js +51 -0
  210. package/dist/session-list-entries.js.map +1 -0
  211. package/dist/session-mcp-projections.d.ts +215 -0
  212. package/dist/session-message-search.d.ts +68 -0
  213. package/dist/session-titles.d.ts +68 -0
  214. package/dist/session-titles.js +25 -0
  215. package/dist/session-titles.js.map +1 -0
  216. package/dist/session-topology-primitives.d.ts +22 -0
  217. package/dist/signup-attribution.d.ts +39 -0
  218. package/dist/site-request-headers.test.d.ts +1 -0
  219. package/dist/site-session-http.d.ts +11 -0
  220. package/dist/site-session-http.js +11 -0
  221. package/dist/site-session-http.js.map +1 -0
  222. package/dist/skill-catalog-context.d.ts +7 -0
  223. package/dist/skill-files.d.ts +12 -0
  224. package/dist/skill-metadata.d.ts +9 -0
  225. package/dist/skill-use.d.ts +61 -0
  226. package/dist/skills.d.ts +185 -0
  227. package/dist/slack-bot-scopes.d.ts +46 -21
  228. package/dist/slack-bot-scopes.js +14 -3
  229. package/dist/slack-rest-mcp.d.ts +31 -0
  230. package/dist/slack-rest-mcp.js +19 -0
  231. package/dist/slack-rest-mcp.js.map +1 -0
  232. package/dist/slack-task-policy.d.ts +294 -0
  233. package/dist/spreadsheet-artifact-commands.d.ts +2 -3
  234. package/dist/spreadsheet-artifact-commands.js +4 -2
  235. package/dist/spreadsheet-artifact-query.js +3 -2
  236. package/dist/subscription-account-pools.d.ts +292 -0
  237. package/dist/task-notes.d.ts +169 -0
  238. package/dist/tool-catalog.d.ts +286 -0
  239. package/dist/tool-result-spill.d.ts +16 -0
  240. package/dist/usage-allowances.d.ts +285 -0
  241. package/dist/usage-allowances.js +49 -0
  242. package/dist/usage-allowances.js.map +1 -0
  243. package/dist/video-generation.d.ts +51 -0
  244. package/dist/video-generation.js +9 -1
  245. package/dist/work-claims.d.ts +411 -0
  246. package/dist/workspace-instruction-policies.d.ts +94 -0
  247. package/dist/workspace-integration-wire.d.ts +36 -0
  248. package/dist/workspace-integration-wire.js +23 -0
  249. package/dist/workspace-integration-wire.js.map +1 -0
  250. package/dist/workspace-integrations.d.ts +948 -0
  251. package/dist/workspace-learning-administration.d.ts +289 -0
  252. package/dist/workspace-learning-policy.d.ts +1 -1
  253. package/dist/workspace-state.d.ts +85 -416
  254. package/dist/xai-provider-account-authority.d.ts +26 -0
  255. package/dist/xai-provider-account-authority.js +9 -0
  256. package/dist/xai-provider-account-authority.js.map +1 -0
  257. package/package.json +146 -1
  258. package/src/agent-authored-durable-text.ts +116 -0
  259. package/src/agent-config.ts +1527 -0
  260. package/src/agent-instruction-changes.ts +98 -0
  261. package/src/agent-learning.ts +161 -0
  262. package/src/agent-topology.ts +69 -0
  263. package/src/allowance-refusal.ts +81 -0
  264. package/src/analytics-consent-report.ts +20 -0
  265. package/src/api-key-presets.ts +25 -0
  266. package/src/artifact-catalog.ts +60 -0
  267. package/src/artifacts.ts +146 -18
  268. package/src/atlassian-native-retirement.ts +33 -0
  269. package/src/browser-storage.ts +10 -0
  270. package/src/bundled-skills.ts +73 -0
  271. package/src/canonical-human-identities.ts +93 -0
  272. package/src/claude-provider-account-authority.ts +12 -0
  273. package/src/claude-subscription-oauth.ts +62 -0
  274. package/src/claude-subscription-usage.ts +59 -0
  275. package/src/client-error-report.ts +184 -0
  276. package/src/code-search.ts +105 -0
  277. package/src/codex-provider-account-authority.ts +37 -0
  278. package/src/company-brain-governed-writes.ts +356 -0
  279. package/src/company-brain.ts +201 -0
  280. package/src/company-profile.ts +167 -1
  281. package/src/connect.ts +202 -0
  282. package/src/connection-account-label.ts +46 -0
  283. package/src/connection-authority.ts +213 -0
  284. package/src/connector-attachments.ts +327 -0
  285. package/src/connector-tool-permissions.ts +60 -0
  286. package/src/direct-model-provider.ts +98 -0
  287. package/src/editable-artifact-codec-registry.ts +55 -17
  288. package/src/editable-artifact-committed-transaction.ts +44 -31
  289. package/src/editable-artifact-live.ts +123 -57
  290. package/src/editable-artifact-serialized-commit.ts +2 -1
  291. package/src/editable-artifact-versions.ts +26 -5
  292. package/src/editable-artifacts.ts +2 -0
  293. package/src/external-identities.ts +285 -0
  294. package/src/feedback.ts +39 -0
  295. package/src/github-repository-contracts.ts +85 -0
  296. package/src/github-repository.ts +59 -0
  297. package/src/google-drive.ts +76 -4
  298. package/src/governed-learning-activation.ts +191 -0
  299. package/src/governed-learning-evaluator.ts +176 -0
  300. package/src/host-mcp-bindings.ts +298 -0
  301. package/src/image-generation.ts +8 -2
  302. package/src/index.ts +8019 -1799
  303. package/src/insights-usage.ts +679 -0
  304. package/src/interaction.ts +1102 -6
  305. package/src/knowledge-entries.ts +375 -0
  306. package/src/knowledge-preparation.ts +50 -0
  307. package/src/knowledge.ts +335 -0
  308. package/src/managed-auth-session-sets.ts +253 -0
  309. package/src/managed-sign-in-methods.ts +37 -0
  310. package/src/mcp-catalog-limits.ts +2 -0
  311. package/src/mcp-endpoint.ts +30 -0
  312. package/src/mcp-oauth.ts +66 -0
  313. package/src/modal-native-proof-v2.ts +398 -0
  314. package/src/model-availability.ts +22 -0
  315. package/src/model-connection-access.ts +17 -0
  316. package/src/model-context-inspector.ts +139 -0
  317. package/src/model-display.ts +195 -0
  318. package/src/model-picker-order.ts +97 -0
  319. package/src/organization-access.ts +176 -0
  320. package/src/organization-integration-policy.ts +54 -0
  321. package/src/organization-membership-lifecycle.ts +559 -0
  322. package/src/organization-model-usage.ts +72 -0
  323. package/src/organization-recovery.ts +186 -0
  324. package/src/organization-usage.ts +79 -0
  325. package/src/permissions.ts +58 -0
  326. package/src/personal-github.ts +267 -0
  327. package/src/plugin-discovery.ts +33 -0
  328. package/src/preference-registry.ts +20 -1
  329. package/src/product-analytics.ts +253 -0
  330. package/src/product-lifecycle-facts.ts +121 -0
  331. package/src/remember.ts +243 -0
  332. package/src/retained-output.ts +20 -3
  333. package/src/sandbox-file-artifacts.ts +73 -0
  334. package/src/sandbox-provider-command.ts +82 -0
  335. package/src/sandbox-recovery.ts +116 -0
  336. package/src/sandbox-snapshots.ts +142 -0
  337. package/src/session-final-reply.ts +16 -0
  338. package/src/session-goal-reports.ts +55 -0
  339. package/src/session-history-import.ts +159 -0
  340. package/src/session-list-entries.ts +91 -0
  341. package/src/session-mcp-projections.ts +273 -0
  342. package/src/session-message-search.ts +54 -0
  343. package/src/session-titles.ts +467 -0
  344. package/src/session-topology-primitives.ts +21 -0
  345. package/src/signup-attribution.ts +81 -0
  346. package/src/site-request-headers.test.ts +28 -0
  347. package/src/site-session-http.ts +63 -0
  348. package/src/skill-catalog-context.ts +50 -0
  349. package/src/skill-files.ts +52 -0
  350. package/src/skill-metadata.ts +40 -0
  351. package/src/skill-use.ts +77 -0
  352. package/src/skills.ts +136 -0
  353. package/src/slack-bot-scopes.ts +106 -42
  354. package/src/slack-rest-mcp.ts +194 -0
  355. package/src/slack-task-policy.ts +215 -0
  356. package/src/spreadsheet-artifact-commands.ts +12 -13
  357. package/src/subscription-account-pools.ts +44 -0
  358. package/src/task-notes.ts +105 -0
  359. package/src/tool-catalog.ts +155 -0
  360. package/src/tool-result-spill.ts +35 -0
  361. package/src/usage-allowances.ts +187 -0
  362. package/src/video-generation.ts +43 -6
  363. package/src/work-claims.ts +266 -0
  364. package/src/workspace-instruction-policies.ts +31 -0
  365. package/src/workspace-integration-wire.ts +99 -0
  366. package/src/workspace-integrations.ts +640 -0
  367. package/src/workspace-learning-administration.ts +88 -0
  368. package/src/workspace-learning-policy.ts +4 -3
  369. package/src/workspace-state.ts +16 -126
  370. package/src/xai-provider-account-authority.ts +38 -0
  371. package/dist/chunk-52SGB2QO.js.map +0 -1
  372. package/dist/chunk-5UDNP7AO.js.map +0 -1
  373. package/dist/chunk-BVGIYW7D.js +0 -11
  374. package/dist/chunk-BVGIYW7D.js.map +0 -1
  375. package/dist/chunk-FJBG5E4O.js.map +0 -1
  376. package/dist/chunk-H5BVCZKF.js.map +0 -1
  377. package/dist/chunk-LSB7GRLG.js.map +0 -1
  378. package/dist/chunk-TZ6YEYN4.js.map +0 -1
  379. package/dist/chunk-VA22KRHS.js +0 -15177
  380. package/dist/chunk-VA22KRHS.js.map +0 -1
  381. package/dist/chunk-Y3MLSLB4.js.map +0 -1
  382. /package/dist/{chunk-CADSX6UN.js.map → chunk-Y3KNXVTD.js.map} +0 -0
@@ -5,6 +5,7 @@ export const INTERACTION_PROTOCOL_VERSION = 1 as const;
5
5
  export const BROWSER_CONTROL_PROTOCOL_VERSION = INTERACTION_PROTOCOL_VERSION;
6
6
  export const BROWSER_CONTROL_WEBSOCKET_PROTOCOL = "opengeni.browser.v1" as const;
7
7
  export const COMPUTER_CONTROL_WEBSOCKET_PROTOCOL = "opengeni.computer.v1" as const;
8
+ export const COMPUTER_RFB_WEBSOCKET_PROTOCOL = "opengeni.computer.rfb.v1" as const;
8
9
  export const BROWSER_CONTROL_WEBSOCKET_BEARER_PREFIX = "opengeni.auth." as const;
9
10
  export const BROWSER_CONTROL_MAX_JSON_BYTES = 40 * 1024 * 1024;
10
11
  export const BROWSER_CONTROL_MAX_FRAME_HEADER_BYTES = 64 * 1024;
@@ -16,12 +17,41 @@ export const INTERACTION_MAX_SEMANTIC_NODES = 10_000;
16
17
  export const INTERACTION_MAX_CHANGED_NODES = 2_000;
17
18
  export const INTERACTION_MAX_DIAGNOSTIC_ENTRIES = 1_000;
18
19
  export const INTERACTION_MAX_ACTIONS_PER_BATCH = 32;
20
+ export const INTERACTION_MAX_WORKSPACE_FILES_PER_COMMAND = 100;
21
+ export const INTERACTION_MAX_CLIPBOARD_BYTES = 1024 * 1024;
22
+
23
+ /**
24
+ * Latest-wins workspace invalidation for Browser/Computer resources. This is
25
+ * deliberately a cursor snapshot rather than a durable event-log entry:
26
+ * consumers refetch authoritative resource lists whenever `revision` advances.
27
+ */
28
+ export const WorkspaceInteractionRevisionEvent = z
29
+ .object({
30
+ workspaceId: z.string().uuid(),
31
+ sequence: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
32
+ revision: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
33
+ type: z.literal("workspace.interaction.changed"),
34
+ occurredAt: z.string().datetime({ offset: true }),
35
+ })
36
+ .strict()
37
+ .refine((event) => event.sequence === event.revision, {
38
+ message: "interaction event sequence must equal revision",
39
+ path: ["sequence"],
40
+ });
41
+ export type WorkspaceInteractionRevisionEvent = z.infer<typeof WorkspaceInteractionRevisionEvent>;
19
42
 
20
43
  const opaqueGeneration = z
21
44
  .string()
22
45
  .min(1)
23
46
  .max(256)
24
47
  .regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/u);
48
+ // Native bridge generations use unpadded base64url; both - and _ can lead.
49
+ // Keep the bytes unchanged: generation equality fences old connections.
50
+ const attachedBridgeGeneration = z
51
+ .string()
52
+ .min(1)
53
+ .max(256)
54
+ .regex(/^[A-Za-z0-9_-][A-Za-z0-9._:-]*$/u);
25
55
  const boundedOpaqueId = z
26
56
  .string()
27
57
  .min(1)
@@ -168,6 +198,7 @@ export const BrowserSessionCapabilities = z
168
198
  downloads: z.boolean(),
169
199
  uploads: z.boolean(),
170
200
  clipboard: z.boolean(),
201
+ permissions: z.boolean(),
171
202
  diagnostics: z.boolean(),
172
203
  rawCdp: z.boolean(),
173
204
  linkedComputer: z.boolean(),
@@ -178,6 +209,11 @@ export const BrowserSessionCapabilities = z
178
209
  .strict();
179
210
  export type BrowserSessionCapabilities = z.infer<typeof BrowserSessionCapabilities>;
180
211
 
212
+ /** Versioned persistent discriminator for experimental disposable contexts. */
213
+ export { EPHEMERAL_CHROMIUM_DRIVER_ID, browserSessionStorageMode } from "./browser-storage";
214
+ export const BrowserStorageMode = z.enum(["private_profile", "ephemeral_context"]);
215
+ export type BrowserStorageMode = z.infer<typeof BrowserStorageMode>;
216
+
181
217
  export const BrowserSession = z
182
218
  .object({
183
219
  id: z.string().uuid(),
@@ -205,6 +241,48 @@ export const BrowserSession = z
205
241
  .strict();
206
242
  export type BrowserSession = z.infer<typeof BrowserSession>;
207
243
 
244
+ /** Controller-private, BrowserSession-scoped clipboard. This is deliberately
245
+ * distinct from a ComputerSession/host OS clipboard so concurrent browsers do
246
+ * not exchange ambient machine state. */
247
+ export const BrowserClipboard = z
248
+ .object({
249
+ browserSessionId: z.string().uuid(),
250
+ controllerGeneration: opaqueGeneration,
251
+ revision: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
252
+ text: z
253
+ .string()
254
+ .max(INTERACTION_MAX_CLIPBOARD_BYTES)
255
+ .refine(
256
+ (value) => new TextEncoder().encode(value).byteLength <= INTERACTION_MAX_CLIPBOARD_BYTES,
257
+ { message: "browser clipboard text exceeds its UTF-8 byte envelope" },
258
+ ),
259
+ source: z.enum(["empty", "write", "clear", "copy", "paste"]),
260
+ sourceTargetId: boundedOpaqueId.nullable(),
261
+ updatedAt: z.string().datetime({ offset: true }).nullable(),
262
+ })
263
+ .strict()
264
+ .superRefine((clipboard, context) => {
265
+ if (clipboard.revision === 0) {
266
+ if (
267
+ clipboard.text !== "" ||
268
+ clipboard.source !== "empty" ||
269
+ clipboard.sourceTargetId !== null ||
270
+ clipboard.updatedAt !== null
271
+ ) {
272
+ context.addIssue({
273
+ code: "custom",
274
+ message: "initial browser clipboard state must be empty",
275
+ });
276
+ }
277
+ } else if (clipboard.source === "empty" || clipboard.updatedAt === null) {
278
+ context.addIssue({
279
+ code: "custom",
280
+ message: "updated browser clipboard state requires a source and timestamp",
281
+ });
282
+ }
283
+ });
284
+ export type BrowserClipboard = z.infer<typeof BrowserClipboard>;
285
+
208
286
  /** One live Chrome-profile bridge installed on an enrolled machine. This is a
209
287
  * transport endpoint, not saved browser/login state: BrowserIdentity remains
210
288
  * the immutable, reusable state abstraction. */
@@ -243,7 +321,7 @@ export type AttachedBrowserDeviceAnnouncement = z.infer<typeof AttachedBrowserDe
243
321
 
244
322
  export const AttachedBrowserInventorySnapshot = z
245
323
  .object({
246
- bridgeGeneration: opaqueGeneration,
324
+ bridgeGeneration: attachedBridgeGeneration,
247
325
  revision: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
248
326
  devices: z.array(AttachedBrowserDeviceAnnouncement).max(10_000),
249
327
  })
@@ -298,9 +376,25 @@ export const AttachedBrowserDevice = z
298
376
  });
299
377
  export type AttachedBrowserDevice = z.infer<typeof AttachedBrowserDevice>;
300
378
 
379
+ /** One enrolled machine agent currently reporting its browser-bridge inventory.
380
+ * A bridge with zero devices is operational but has no Chrome profile connected
381
+ * through the OpenGeni extension yet. */
382
+ export const AttachedBrowserBridge = z
383
+ .object({
384
+ enrollmentId: z.string().uuid(),
385
+ state: z.enum(["online", "offline"]),
386
+ bridgeGeneration: attachedBridgeGeneration,
387
+ inventoryRevision: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
388
+ connectedProfileCount: z.number().int().nonnegative().max(10_000),
389
+ lastSeenAt: z.string().datetime({ offset: true }),
390
+ })
391
+ .strict();
392
+ export type AttachedBrowserBridge = z.infer<typeof AttachedBrowserBridge>;
393
+
301
394
  export const AttachedBrowserDeviceListResponse = z
302
395
  .object({
303
396
  revision: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
397
+ bridges: z.array(AttachedBrowserBridge).max(10_000),
304
398
  devices: z.array(AttachedBrowserDevice).max(10_000),
305
399
  })
306
400
  .strict();
@@ -444,6 +538,7 @@ export const BrowserIdentity = z
444
538
  workspaceId: z.string().uuid(),
445
539
  name: z.string().trim().min(1).max(200),
446
540
  status: BrowserIdentityStatus,
541
+ version: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
447
542
  defaultRevisionId: z.string().uuid().nullable(),
448
543
  headGeneration: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
449
544
  revisionCount: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
@@ -521,6 +616,93 @@ export const NetworkRouteConsistency = z
521
616
  .strict();
522
617
  export type NetworkRouteConsistency = z.infer<typeof NetworkRouteConsistency>;
523
618
 
619
+ export function interactionPlacementsEqual(
620
+ left: InteractionPlacement,
621
+ right: InteractionPlacement,
622
+ ): boolean {
623
+ if (left.kind !== right.kind) return false;
624
+ switch (left.kind) {
625
+ case "sandbox_group":
626
+ return right.kind === "sandbox_group" && left.sandboxGroupId === right.sandboxGroupId;
627
+ case "connected_machine":
628
+ return right.kind === "connected_machine" && left.sandboxId === right.sandboxId;
629
+ case "attached_device":
630
+ return right.kind === "attached_device" && left.deviceId === right.deviceId;
631
+ case "external_provider":
632
+ return (
633
+ right.kind === "external_provider" &&
634
+ left.providerId === right.providerId &&
635
+ left.placementId === right.placementId
636
+ );
637
+ }
638
+ }
639
+
640
+ /** One secret-free compatibility rule shared by configuration, persistence,
641
+ * and launch. `null` placement means the ordinary managed sandbox default. */
642
+ export function networkRoutePlacementCompatibilityIssue(
643
+ configuration: NetworkRouteConfiguration,
644
+ consistency: NetworkRouteConsistency,
645
+ placement: InteractionPlacement | null,
646
+ ): string | null {
647
+ if (configuration.kind === "tunnel") {
648
+ if (!placement || !interactionPlacementsEqual(configuration.placement, placement)) {
649
+ return "Tunnel route is bound to another placement";
650
+ }
651
+ }
652
+ if (configuration.kind === "managed") {
653
+ if (placement?.kind !== "external_provider") {
654
+ return "Managed NetworkRoutes require an external browser provider placement";
655
+ }
656
+ if (configuration.providerId !== placement.providerId) {
657
+ return "Managed NetworkRoute belongs to another external browser provider";
658
+ }
659
+ if (configuration.credential !== null) {
660
+ return "Managed provider routes cannot use a separate proxy credential";
661
+ }
662
+ if (consistency.dns !== "provider") {
663
+ return "Managed provider routes require provider DNS";
664
+ }
665
+ if (configuration.providerId === "browserbase") {
666
+ if (configuration.routeId !== "default" || configuration.egressClass !== "residential") {
667
+ return "Browserbase supports only its default managed residential route";
668
+ }
669
+ if (configuration.region !== null && !/^[A-Za-z]{2}$/u.test(configuration.region)) {
670
+ return "Browserbase managed route region must be a two-letter country code";
671
+ }
672
+ if (consistency.stability !== "session") {
673
+ return "Browserbase managed routing cannot promise a stable IP across sessions";
674
+ }
675
+ } else if (configuration.providerId !== "kernel") {
676
+ return `Managed NetworkRoute provider ${configuration.providerId} is unsupported`;
677
+ }
678
+ return null;
679
+ }
680
+ if (placement?.kind === "external_provider") {
681
+ return "External browser providers require a provider-managed NetworkRoute";
682
+ }
683
+ const expectedDns = configuration.kind === "proxy" ? "proxy" : "placement";
684
+ if (consistency.dns !== expectedDns) {
685
+ return `Network route ${configuration.kind} cannot provide ${consistency.dns} DNS`;
686
+ }
687
+ if (consistency.webRtc === "proxy_only" && configuration.kind !== "proxy") {
688
+ return "WebRTC proxy-only routing requires a proxy network route";
689
+ }
690
+ if (placement?.kind === "attached_device") {
691
+ if (configuration.kind === "proxy") {
692
+ return "Attached Chrome cannot change its process-scoped proxy configuration";
693
+ }
694
+ if (
695
+ consistency.locale !== null ||
696
+ consistency.timezone !== null ||
697
+ consistency.geolocation !== null ||
698
+ consistency.webRtc !== "default"
699
+ ) {
700
+ return "Attached Chrome cannot change process-scoped route emulation";
701
+ }
702
+ }
703
+ return null;
704
+ }
705
+
524
706
  export const NetworkRoute = z
525
707
  .object({
526
708
  id: z.string().uuid(),
@@ -637,6 +819,9 @@ export const SiteAuthAuthority = z.discriminatedUnion("kind", [
637
819
  kind: z.literal("external_provider"),
638
820
  label: z.string().trim().min(1).max(200),
639
821
  adapterId: boundedOpaqueId,
822
+ /** Opaque provider-owned auth-connection reference. It is meaningful
823
+ * only to the named adapter and never treated as credential material. */
824
+ connectionId: boundedOpaqueId,
640
825
  credential: InteractionCredentialAuthorityRef.nullable(),
641
826
  })
642
827
  .strict(),
@@ -673,6 +858,22 @@ export type SiteAuthHealthPolicy = z.infer<typeof SiteAuthHealthPolicy>;
673
858
  export const SiteAuthVerificationState = z.enum(["unknown", "verified", "needs_repair", "failed"]);
674
859
  export type SiteAuthVerificationState = z.infer<typeof SiteAuthVerificationState>;
675
860
 
861
+ export const SiteAuthMaintenance = z
862
+ .object({
863
+ action: z.enum(["health_check", "repair"]),
864
+ /** Hidden until the durable session start has been confirmed. */
865
+ sessionId: z.string().uuid().nullable(),
866
+ dueAt: z.string().datetime({ offset: true }),
867
+ startedAt: z.string().datetime({ offset: true }).nullable(),
868
+ })
869
+ .strict();
870
+ export type SiteAuthMaintenance = z.infer<typeof SiteAuthMaintenance>;
871
+
872
+ /** Trusted service provenance for scheduler-authored maintenance sessions. */
873
+ export const SITE_AUTH_MAINTENANCE_OPERATION_CONTEXT_KEY =
874
+ "opengeniSiteAuthMaintenanceOperationId" as const;
875
+ export const SITE_AUTH_MAINTENANCE_CONNECTION_CONTEXT_KEY = "opengeniSiteAuthConnectionId" as const;
876
+
676
877
  const SiteAuthConnectionConfiguration = z
677
878
  .object({
678
879
  name: z.string().trim().min(1).max(200),
@@ -693,6 +894,17 @@ function validateSiteAuthConfiguration(
693
894
  connection: z.infer<typeof SiteAuthConnectionConfiguration>,
694
895
  context: z.RefinementCtx,
695
896
  ): void {
897
+ if (
898
+ connection.preferredIdentityId &&
899
+ (connection.preferredPlacement?.kind === "attached_device" ||
900
+ connection.preferredPlacement?.kind === "external_provider")
901
+ ) {
902
+ context.addIssue({
903
+ code: "custom",
904
+ path: ["preferredIdentityId"],
905
+ message: "the preferred placement owns its live profile identity",
906
+ });
907
+ }
696
908
  if (new Set(connection.origins).size !== connection.origins.length) {
697
909
  context.addIssue({ code: "custom", path: ["origins"], message: "origins repeat" });
698
910
  }
@@ -767,6 +979,9 @@ export const SiteAuthConnection = SiteAuthConnectionConfiguration.extend({
767
979
  verificationState: SiteAuthVerificationState,
768
980
  lastVerifiedAt: z.string().datetime({ offset: true }).nullable(),
769
981
  lastVerifiedUrl: boundedUrl.nullable(),
982
+ lastCheckedAt: z.string().datetime({ offset: true }).nullable(),
983
+ nextCheckAt: z.string().datetime({ offset: true }).nullable(),
984
+ maintenance: SiteAuthMaintenance.nullable(),
770
985
  repairCode: boundedOpaqueId.nullable(),
771
986
  version: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
772
987
  createdBySubjectId: z.string().min(1).max(1_024),
@@ -862,6 +1077,7 @@ export const AuthRun = z
862
1077
  controllerGeneration: opaqueGeneration,
863
1078
  targetGeneration: opaqueGeneration,
864
1079
  documentGeneration: opaqueGeneration.nullable(),
1080
+ purpose: z.enum(["authenticate", "health_check", "repair"]),
865
1081
  methodId: boundedOpaqueId.nullable(),
866
1082
  authorityId: boundedOpaqueId.nullable(),
867
1083
  state: AuthRunState,
@@ -945,15 +1161,15 @@ export const StartAuthRunRequest = z
945
1161
  targetId: boundedOpaqueId,
946
1162
  expectedTargetGeneration: opaqueGeneration,
947
1163
  expectedDocumentGeneration: opaqueGeneration.nullable(),
1164
+ purpose: z.enum(["authenticate", "health_check", "repair"]).optional(),
948
1165
  methodId: boundedOpaqueId.optional(),
949
1166
  authorityId: boundedOpaqueId.optional(),
950
1167
  })
951
1168
  .strict();
952
1169
  export type StartAuthRunRequest = z.infer<typeof StartAuthRunRequest>;
953
1170
 
954
- export const ReportAuthRunRequest = z
1171
+ export const ReportAuthRunPayload = z
955
1172
  .object({
956
- operationId: z.string().uuid(),
957
1173
  expectedVersion: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
958
1174
  methodId: boundedOpaqueId.optional(),
959
1175
  authorityId: boundedOpaqueId.optional(),
@@ -1001,6 +1217,11 @@ export const ReportAuthRunRequest = z
1001
1217
  });
1002
1218
  }
1003
1219
  });
1220
+ export type ReportAuthRunPayload = z.infer<typeof ReportAuthRunPayload>;
1221
+
1222
+ export const ReportAuthRunRequest = ReportAuthRunPayload.safeExtend({
1223
+ operationId: z.string().uuid(),
1224
+ });
1004
1225
  export type ReportAuthRunRequest = z.infer<typeof ReportAuthRunRequest>;
1005
1226
 
1006
1227
  export const ProtectedAuthField = z
@@ -1045,6 +1266,101 @@ export const ProtectedAuthFillResponse = z
1045
1266
  .strict();
1046
1267
  export type ProtectedAuthFillResponse = z.infer<typeof ProtectedAuthFillResponse>;
1047
1268
 
1269
+ /** Public request for advancing a provider-managed AuthRun. Provider secrets,
1270
+ * profile names, browser ids, and hosted-login URLs stay behind browserd. */
1271
+ export const ExternalAuthRunRequest = z
1272
+ .object({
1273
+ operationId: z.string().uuid(),
1274
+ expectedVersion: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
1275
+ action: z.enum(["start", "poll"]),
1276
+ })
1277
+ .strict();
1278
+ export type ExternalAuthRunRequest = z.infer<typeof ExternalAuthRunRequest>;
1279
+
1280
+ export const ExternalAuthRunResponse = z
1281
+ .object({
1282
+ run: AuthRun,
1283
+ status: z.enum(["working", "needs_human", "ready_to_verify", "failed"]),
1284
+ operationId: z.string().uuid(),
1285
+ replayed: z.boolean(),
1286
+ })
1287
+ .strict();
1288
+ export type ExternalAuthRunResponse = z.infer<typeof ExternalAuthRunResponse>;
1289
+
1290
+ /** Human-only request for opening the provider's ephemeral hosted login UI. */
1291
+ export const ExternalAuthInteractiveRequest = z
1292
+ .object({
1293
+ operationId: z.string().uuid(),
1294
+ expectedVersion: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
1295
+ })
1296
+ .strict();
1297
+ export type ExternalAuthInteractiveRequest = z.infer<typeof ExternalAuthInteractiveRequest>;
1298
+
1299
+ export const ExternalAuthInteractiveResponse = z
1300
+ .object({
1301
+ authRunId: z.string().uuid(),
1302
+ url: boundedHttpUrl,
1303
+ expiresAt: z.string().datetime({ offset: true }).nullable(),
1304
+ })
1305
+ .strict();
1306
+ export type ExternalAuthInteractiveResponse = z.infer<typeof ExternalAuthInteractiveResponse>;
1307
+
1308
+ /** Controller-private Kernel/host-adapter command. This crosses only the
1309
+ * authenticated API -> browserd control channel; it is not a model tool. */
1310
+ export const BrowserExternalAuthCommand = z
1311
+ .object({
1312
+ browserSessionId: z.string().uuid(),
1313
+ controllerGeneration: opaqueGeneration,
1314
+ operationId: z.string().uuid(),
1315
+ authRunId: z.string().uuid(),
1316
+ adapterId: boundedOpaqueId,
1317
+ connectionId: boundedOpaqueId,
1318
+ action: z.enum(["start", "poll", "interactive"]),
1319
+ })
1320
+ .strict();
1321
+ export type BrowserExternalAuthCommand = z.infer<typeof BrowserExternalAuthCommand>;
1322
+
1323
+ export const BrowserExternalAuthResult = z
1324
+ .object({
1325
+ state: z.enum(["authenticated", "in_progress", "needs_human", "failed"]),
1326
+ externalAction: AuthRunExternalAction.nullable(),
1327
+ interactiveUrl: boundedHttpUrl.nullable(),
1328
+ failureCode: boundedOpaqueId.nullable(),
1329
+ profileLoaded: z.boolean(),
1330
+ })
1331
+ .strict()
1332
+ .superRefine((result, context) => {
1333
+ if ((result.state === "needs_human") !== (result.externalAction !== null)) {
1334
+ context.addIssue({
1335
+ code: "custom",
1336
+ path: ["externalAction"],
1337
+ message: "human external-auth state requires an action",
1338
+ });
1339
+ }
1340
+ if ((result.state === "failed") !== (result.failureCode !== null)) {
1341
+ context.addIssue({
1342
+ code: "custom",
1343
+ path: ["failureCode"],
1344
+ message: "failed external-auth state requires a failure code",
1345
+ });
1346
+ }
1347
+ if (result.interactiveUrl !== null && result.state !== "needs_human") {
1348
+ context.addIssue({
1349
+ code: "custom",
1350
+ path: ["interactiveUrl"],
1351
+ message: "interactive URL requires a human external-auth state",
1352
+ });
1353
+ }
1354
+ if (result.profileLoaded && result.state !== "authenticated") {
1355
+ context.addIssue({
1356
+ code: "custom",
1357
+ path: ["profileLoaded"],
1358
+ message: "only authenticated external auth can load a profile",
1359
+ });
1360
+ }
1361
+ });
1362
+ export type BrowserExternalAuthResult = z.infer<typeof BrowserExternalAuthResult>;
1363
+
1048
1364
  export const VerifyAuthRunRequest = z
1049
1365
  .object({
1050
1366
  operationId: z.string().uuid(),
@@ -1140,6 +1456,38 @@ export type InteractionInterventionMutationResponse = z.infer<
1140
1456
  typeof InteractionInterventionMutationResponse
1141
1457
  >;
1142
1458
 
1459
+ /** Canonical model/Codemode input for one typed browser/computer human wait. */
1460
+ export const INTERACTION_REQUEST_HUMAN_MODEL_TOOL_NAME = "interaction__interaction_request_human";
1461
+
1462
+ export const RequestHumanInteractionToolInput = z.discriminatedUnion("operation", [
1463
+ z.object({ operation: z.literal("wait"), interventionId: z.string().uuid() }).strict(),
1464
+ z
1465
+ .object({
1466
+ operation: z.literal("request"),
1467
+ resourceKind: z.enum(["browser_session", "computer_session"]),
1468
+ resourceId: z.string().uuid(),
1469
+ targetId: boundedOpaqueId,
1470
+ expectedControllerGeneration: opaqueGeneration,
1471
+ expectedTargetGeneration: opaqueGeneration,
1472
+ expectedDocumentGeneration: opaqueGeneration.nullable(),
1473
+ kind: z.enum(["manual_login", "mfa", "external_action", "confirmation", "other"]),
1474
+ reason: z.string().trim().min(1).max(2_048),
1475
+ authRunId: z.string().uuid().optional(),
1476
+ expiresInSeconds: z.number().int().min(30).max(86_400).default(900),
1477
+ })
1478
+ .strict(),
1479
+ ]);
1480
+ export type RequestHumanInteractionToolInput = z.infer<typeof RequestHumanInteractionToolInput>;
1481
+
1482
+ export const RequestHumanInteractionToolOutput = z
1483
+ .object({
1484
+ intervention: InteractionIntervention,
1485
+ observation: z.lazy(() => z.union([BrowserObservation, ComputerObservation])).nullable(),
1486
+ observationErrorCode: z.string().min(1).max(128).nullable(),
1487
+ })
1488
+ .strict();
1489
+ export type RequestHumanInteractionToolOutput = z.infer<typeof RequestHumanInteractionToolOutput>;
1490
+
1143
1491
  export const BrowserIdentityListResponse = z
1144
1492
  .object({
1145
1493
  revision: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
@@ -1164,6 +1512,24 @@ export const CreateBrowserIdentityRequest = z
1164
1512
  .strict();
1165
1513
  export type CreateBrowserIdentityRequest = z.infer<typeof CreateBrowserIdentityRequest>;
1166
1514
 
1515
+ export const UpdateBrowserIdentityRequest = z
1516
+ .object({
1517
+ operationId: z.string().uuid(),
1518
+ expectedVersion: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
1519
+ name: z.string().trim().min(1).max(200).optional(),
1520
+ status: BrowserIdentityStatus.optional(),
1521
+ defaultRevisionId: z.string().uuid().optional(),
1522
+ })
1523
+ .strict()
1524
+ .refine(
1525
+ (value) =>
1526
+ value.name !== undefined ||
1527
+ value.status !== undefined ||
1528
+ value.defaultRevisionId !== undefined,
1529
+ { message: "browser identity update is empty" },
1530
+ );
1531
+ export type UpdateBrowserIdentityRequest = z.infer<typeof UpdateBrowserIdentityRequest>;
1532
+
1167
1533
  export const BrowserIdentityMutationResponse = z
1168
1534
  .object({
1169
1535
  identity: BrowserIdentity,
@@ -1212,6 +1578,155 @@ export const BrowserTarget = z
1212
1578
  .strict();
1213
1579
  export type BrowserTarget = z.infer<typeof BrowserTarget>;
1214
1580
 
1581
+ export const BrowserDownloadStatus = z.enum([
1582
+ "in_progress",
1583
+ "completed",
1584
+ "cancelled",
1585
+ "failed",
1586
+ "unavailable",
1587
+ ]);
1588
+ export type BrowserDownloadStatus = z.infer<typeof BrowserDownloadStatus>;
1589
+
1590
+ /** One browser-produced file. Its bytes remain private to the exact controller
1591
+ * until an explicit save publishes and materializes them. No placement path or
1592
+ * source URL crosses this contract. */
1593
+ export const BrowserDownload = z
1594
+ .object({
1595
+ id: z.string().uuid(),
1596
+ browserSessionId: z.string().uuid(),
1597
+ controllerGeneration: opaqueGeneration,
1598
+ targetId: boundedOpaqueId.nullable(),
1599
+ filename: z
1600
+ .string()
1601
+ .min(1)
1602
+ .max(4_096)
1603
+ .regex(/^[^\u0000-\u001f\u007f]+$/u),
1604
+ status: BrowserDownloadStatus,
1605
+ receivedBytes: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
1606
+ totalBytes: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).nullable(),
1607
+ sha256: sha256Hex.nullable(),
1608
+ version: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
1609
+ startedAt: z.string().datetime({ offset: true }),
1610
+ settledAt: z.string().datetime({ offset: true }).nullable(),
1611
+ failureCode: boundedOpaqueId.nullable(),
1612
+ })
1613
+ .strict()
1614
+ .superRefine((download, context) => {
1615
+ const terminal = download.status !== "in_progress";
1616
+ if (terminal !== (download.settledAt !== null)) {
1617
+ context.addIssue({
1618
+ code: "custom",
1619
+ path: ["settledAt"],
1620
+ message: "download settlement must match its terminal status",
1621
+ });
1622
+ }
1623
+ if (
1624
+ (download.status === "failed" || download.status === "unavailable") !==
1625
+ (download.failureCode !== null)
1626
+ ) {
1627
+ context.addIssue({
1628
+ code: "custom",
1629
+ path: ["failureCode"],
1630
+ message: "failed or unavailable downloads require one failure code",
1631
+ });
1632
+ }
1633
+ if (download.status === "completed") {
1634
+ if (download.sha256 === null || download.totalBytes !== download.receivedBytes) {
1635
+ context.addIssue({
1636
+ code: "custom",
1637
+ path: ["sha256"],
1638
+ message: "completed downloads require exact bytes and SHA-256",
1639
+ });
1640
+ }
1641
+ } else if (download.sha256 !== null) {
1642
+ context.addIssue({
1643
+ code: "custom",
1644
+ path: ["sha256"],
1645
+ message: "only completed downloads carry a SHA-256",
1646
+ });
1647
+ }
1648
+ });
1649
+ export type BrowserDownload = z.infer<typeof BrowserDownload>;
1650
+
1651
+ export const BrowserDownloadListResponse = z
1652
+ .object({
1653
+ browserSessionId: z.string().uuid(),
1654
+ controllerGeneration: opaqueGeneration,
1655
+ downloads: z.array(BrowserDownload).max(10_000),
1656
+ })
1657
+ .strict();
1658
+ export type BrowserDownloadListResponse = z.infer<typeof BrowserDownloadListResponse>;
1659
+
1660
+ const workspaceRelativeFilePath = z
1661
+ .string()
1662
+ .min(1)
1663
+ .max(4_096)
1664
+ .refine((value) => {
1665
+ if (value !== value.trim() || value.startsWith("/") || /^[A-Za-z]:[\\/]/u.test(value)) {
1666
+ return false;
1667
+ }
1668
+ if (value.includes("\\") || value.includes("\0")) return false;
1669
+ const segments = value.split("/");
1670
+ return segments.every(
1671
+ (segment) =>
1672
+ segment.length > 0 &&
1673
+ segment !== "." &&
1674
+ segment !== ".." &&
1675
+ !/[<>:"|?*\u0000-\u001f\u007f]/u.test(segment) &&
1676
+ !/[ .]$/u.test(segment) &&
1677
+ !/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/iu.test(segment),
1678
+ );
1679
+ }, "destinationPath must be a portable workspace-relative file path");
1680
+
1681
+ export const BrowserDownloadSaveRequest = z
1682
+ .object({
1683
+ operationId: z.string().uuid(),
1684
+ destinationPath: workspaceRelativeFilePath,
1685
+ overwrite: z.boolean().default(false),
1686
+ })
1687
+ .strict();
1688
+ export type BrowserDownloadSaveRequest = z.infer<typeof BrowserDownloadSaveRequest>;
1689
+
1690
+ export const BrowserDownloadSaveResponse = z
1691
+ .object({
1692
+ download: BrowserDownload,
1693
+ destinationPath: workspaceRelativeFilePath,
1694
+ fileId: z.string().uuid(),
1695
+ operationId: z.string().uuid(),
1696
+ replayed: z.boolean(),
1697
+ })
1698
+ .strict();
1699
+ export type BrowserDownloadSaveResponse = z.infer<typeof BrowserDownloadSaveResponse>;
1700
+
1701
+ /** Controller-private, narrow object authority for publishing one exact
1702
+ * completed download. Signed URLs and headers never appear in public SDK or
1703
+ * durable controller receipts. */
1704
+ export const BrowserDownloadExportRequest = z
1705
+ .object({
1706
+ operationId: z.string().uuid(),
1707
+ downloadId: z.string().uuid(),
1708
+ upload: z
1709
+ .object({
1710
+ url: boundedHttpUrl,
1711
+ requiredHeaders: z.record(z.string().min(1).max(256), z.string().max(8_192)),
1712
+ expiresAt: z.string().datetime({ offset: true }),
1713
+ })
1714
+ .strict(),
1715
+ })
1716
+ .strict();
1717
+ export type BrowserDownloadExportRequest = z.infer<typeof BrowserDownloadExportRequest>;
1718
+
1719
+ export const BrowserDownloadExportReceipt = z
1720
+ .object({
1721
+ operationId: z.string().uuid(),
1722
+ downloadId: z.string().uuid(),
1723
+ sizeBytes: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
1724
+ sha256: sha256Hex,
1725
+ replayed: z.boolean(),
1726
+ })
1727
+ .strict();
1728
+ export type BrowserDownloadExportReceipt = z.infer<typeof BrowserDownloadExportReceipt>;
1729
+
1215
1730
  export const InteractionRect = z
1216
1731
  .object({
1217
1732
  x: z.number().finite(),
@@ -1313,6 +1828,18 @@ export const BrowserObservation = z
1313
1828
  frameId: opaqueGeneration.nullable(),
1314
1829
  semantic: z.union([InteractionSemanticSnapshot, InteractionSemanticDiff]).nullable(),
1315
1830
  screenshot: RetainedArtifactReferenceSchema.nullable(),
1831
+ viewport: z
1832
+ .object({
1833
+ width: z.number().int().positive(),
1834
+ height: z.number().int().positive(),
1835
+ visualWidth: z.number().positive(),
1836
+ visualHeight: z.number().positive(),
1837
+ deviceScaleFactor: z.number().positive(),
1838
+ maxTouchPoints: z.number().int().nonnegative(),
1839
+ })
1840
+ .strict()
1841
+ .nullable()
1842
+ .optional(),
1316
1843
  focusedRef: boundedOpaqueId.nullable(),
1317
1844
  changedRegions: z.array(InteractionRect).max(INTERACTION_MAX_CHANGED_NODES),
1318
1845
  diagnostics: InteractionDiagnosticSummary,
@@ -1381,6 +1908,90 @@ export const BrowserLocator = z.discriminatedUnion("kind", [
1381
1908
  ]);
1382
1909
  export type BrowserLocator = z.infer<typeof BrowserLocator>;
1383
1910
 
1911
+ /** Cheap causal state for a target. This is intentionally independent of an
1912
+ * accessibility observation, which can require a full tree for every frame. */
1913
+ export const BrowserTargetState = z
1914
+ .object({
1915
+ browserSessionId: z.string().uuid(),
1916
+ controllerGeneration: opaqueGeneration,
1917
+ targetId: boundedOpaqueId,
1918
+ targetGeneration: opaqueGeneration,
1919
+ documentGeneration: opaqueGeneration.nullable(),
1920
+ frameId: opaqueGeneration.nullable(),
1921
+ })
1922
+ .strict();
1923
+ export type BrowserTargetState = z.infer<typeof BrowserTargetState>;
1924
+
1925
+ export const BrowserDomSafeAttribute = z.enum([
1926
+ "href",
1927
+ "src",
1928
+ "alt",
1929
+ "title",
1930
+ "role",
1931
+ "aria-label",
1932
+ "aria-expanded",
1933
+ "aria-checked",
1934
+ "aria-selected",
1935
+ "placeholder",
1936
+ "type",
1937
+ "name",
1938
+ "data-testid",
1939
+ ]);
1940
+ export type BrowserDomSafeAttribute = z.infer<typeof BrowserDomSafeAttribute>;
1941
+
1942
+ export const BrowserDomReadSelector = z
1943
+ .string()
1944
+ .min(1)
1945
+ .max(8_192)
1946
+ .regex(/^[A-Za-z0-9_#.,\s>*-]+$/u);
1947
+
1948
+ /** DOM extraction avoids content-matching locators, which can probe redacted text. */
1949
+ export const BrowserDomReadLocator = z.discriminatedUnion("kind", [
1950
+ z.object({ kind: z.literal("ref"), ref: boundedOpaqueId }).strict(),
1951
+ z.object({ kind: z.literal("placeholder"), text: z.string().min(1).max(8_192) }).strict(),
1952
+ z.object({ kind: z.literal("test_id"), value: z.string().min(1).max(2_048) }).strict(),
1953
+ z.object({ kind: z.literal("css"), selector: BrowserDomReadSelector }).strict(),
1954
+ ]);
1955
+ export type BrowserDomReadLocator = z.infer<typeof BrowserDomReadLocator>;
1956
+
1957
+ const BrowserDomReadFence = z.object({
1958
+ expectedTargetGeneration: opaqueGeneration,
1959
+ expectedDocumentGeneration: opaqueGeneration,
1960
+ expectedFrameId: opaqueGeneration,
1961
+ });
1962
+
1963
+ /** A fixed, read-only query. The caller never supplies executable JavaScript.
1964
+ * CSS reads accept only simple tag, class, id, descendant, and child selectors
1965
+ * so predicate queries cannot probe protected attribute values. CSS count is
1966
+ * scoped to the target's main document; child frames and shadow roots are not
1967
+ * part of its count. */
1968
+ export const BrowserDomReadRequest = z.discriminatedUnion("kind", [
1969
+ BrowserDomReadFence.extend({
1970
+ kind: z.literal("element"),
1971
+ locator: BrowserDomReadLocator,
1972
+ attributes: z.array(BrowserDomSafeAttribute).max(13).optional(),
1973
+ maxChars: z.number().int().min(1).max(32_768).optional(),
1974
+ }).strict(),
1975
+ BrowserDomReadFence.extend({
1976
+ kind: z.literal("count"),
1977
+ selector: BrowserDomReadSelector,
1978
+ }).strict(),
1979
+ ]);
1980
+ export type BrowserDomReadRequest = z.infer<typeof BrowserDomReadRequest>;
1981
+
1982
+ export const BrowserDomReadResponse = BrowserTargetState.extend({
1983
+ kind: z.enum(["element", "count"]),
1984
+ count: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
1985
+ text: z.string().max(32_768).nullable().optional(),
1986
+ value: z.string().max(32_768).nullable().optional(),
1987
+ attributes: z
1988
+ .partialRecord(BrowserDomSafeAttribute, z.string().max(32_768).nullable())
1989
+ .optional(),
1990
+ redacted: z.enum(["password", "payment", "private", "policy"]).nullable().optional(),
1991
+ truncated: z.boolean(),
1992
+ }).strict();
1993
+ export type BrowserDomReadResponse = z.infer<typeof BrowserDomReadResponse>;
1994
+
1384
1995
  /** Provider-neutral locator for native accessibility trees. Native automation
1385
1996
  * identifiers are intentionally distinct from DOM selectors/test ids: AX,
1386
1997
  * AT-SPI, and UIA adapters resolve them inside the exact observed target. */
@@ -1417,8 +2028,39 @@ export const ComputerLocator = z.discriminatedUnion("kind", [
1417
2028
  ]);
1418
2029
  export type ComputerLocator = z.infer<typeof ComputerLocator>;
1419
2030
 
2031
+ /** Web-platform permissions which a managed browser can set for the exact
2032
+ * top-level origin currently fenced by a BrowserActionCommand. Names remain
2033
+ * provider-neutral; drivers map them to their native permission descriptors. */
2034
+ export const BrowserPermission = z.enum([
2035
+ "geolocation",
2036
+ "notifications",
2037
+ "camera",
2038
+ "microphone",
2039
+ "midi",
2040
+ "midi_sysex",
2041
+ "sensors",
2042
+ "idle_detection",
2043
+ "local_fonts",
2044
+ "window_management",
2045
+ ]);
2046
+ export type BrowserPermission = z.infer<typeof BrowserPermission>;
2047
+
2048
+ export const BrowserPermissionSetting = z.enum(["granted", "denied", "prompt"]);
2049
+ export type BrowserPermissionSetting = z.infer<typeof BrowserPermissionSetting>;
2050
+
1420
2051
  const browserActionVariants = [
1421
2052
  z.object({ type: z.literal("navigate"), url: boundedUrl }).strict(),
2053
+ z.object({ type: z.literal("history"), direction: z.enum(["back", "forward"]) }).strict(),
2054
+ z.object({ type: z.literal("activate") }).strict(),
2055
+ z
2056
+ .object({
2057
+ type: z.literal("viewport"),
2058
+ width: z.number().int().min(240).max(4_096),
2059
+ height: z.number().int().min(240).max(4_096),
2060
+ mobile: z.boolean(),
2061
+ deviceScaleFactor: z.number().min(0.5).max(4).optional(),
2062
+ })
2063
+ .strict(),
1422
2064
  z
1423
2065
  .object({
1424
2066
  type: z.literal("click"),
@@ -1489,6 +2131,7 @@ const browserActionVariants = [
1489
2131
  deltaX: z.number().finite().min(-1_000_000).max(1_000_000).optional(),
1490
2132
  deltaY: z.number().finite().min(-1_000_000).max(1_000_000).optional(),
1491
2133
  button: z.enum(["left", "right", "middle"]).optional(),
2134
+ clickCount: z.union([z.literal(1), z.literal(2)]).optional(),
1492
2135
  })
1493
2136
  .strict()
1494
2137
  .superRefine((action, context) => {
@@ -1522,6 +2165,12 @@ const browserActionVariants = [
1522
2165
  message: "pointer deltas require scroll",
1523
2166
  });
1524
2167
  }
2168
+ if (action.action !== "click" && action.clickCount !== undefined) {
2169
+ context.addIssue({
2170
+ code: "custom",
2171
+ message: "pointer clickCount requires click",
2172
+ });
2173
+ }
1525
2174
  }),
1526
2175
  z
1527
2176
  .object({
@@ -1537,6 +2186,84 @@ const browserActionVariants = [
1537
2186
  workspaceFileIds: z.array(z.string().uuid()).min(1).max(100),
1538
2187
  })
1539
2188
  .strict(),
2189
+ z
2190
+ .object({
2191
+ type: z.literal("clipboard"),
2192
+ operation: z.enum(["write", "clear", "copy", "paste"]),
2193
+ text: z
2194
+ .string()
2195
+ .max(INTERACTION_MAX_CLIPBOARD_BYTES)
2196
+ .refine(
2197
+ (value) => new TextEncoder().encode(value).byteLength <= INTERACTION_MAX_CLIPBOARD_BYTES,
2198
+ { message: "browser clipboard text exceeds its UTF-8 byte envelope" },
2199
+ )
2200
+ .optional(),
2201
+ locator: BrowserLocator.optional(),
2202
+ content: z.enum(["selection", "value", "text"]).optional(),
2203
+ })
2204
+ .strict()
2205
+ .superRefine((action, context) => {
2206
+ if (action.operation === "write") {
2207
+ if (action.text === undefined) {
2208
+ context.addIssue({
2209
+ code: "custom",
2210
+ path: ["text"],
2211
+ message: "clipboard write requires text",
2212
+ });
2213
+ }
2214
+ if (action.locator !== undefined || action.content !== undefined) {
2215
+ context.addIssue({
2216
+ code: "custom",
2217
+ message: "clipboard write accepts only text",
2218
+ });
2219
+ }
2220
+ return;
2221
+ }
2222
+ if (action.operation === "clear") {
2223
+ if (
2224
+ action.text !== undefined ||
2225
+ action.locator !== undefined ||
2226
+ action.content !== undefined
2227
+ ) {
2228
+ context.addIssue({
2229
+ code: "custom",
2230
+ message: "clipboard clear accepts no payload",
2231
+ });
2232
+ }
2233
+ return;
2234
+ }
2235
+ if (action.operation === "copy") {
2236
+ if (action.text !== undefined) {
2237
+ context.addIssue({
2238
+ code: "custom",
2239
+ path: ["text"],
2240
+ message: "clipboard copy does not accept text",
2241
+ });
2242
+ }
2243
+ if (action.content !== undefined && action.content !== "selection" && !action.locator) {
2244
+ context.addIssue({
2245
+ code: "custom",
2246
+ path: ["locator"],
2247
+ message: "copying element value or text requires a locator",
2248
+ });
2249
+ }
2250
+ return;
2251
+ }
2252
+ if (action.content !== undefined) {
2253
+ context.addIssue({
2254
+ code: "custom",
2255
+ path: ["content"],
2256
+ message: "clipboard paste does not accept content",
2257
+ });
2258
+ }
2259
+ }),
2260
+ z
2261
+ .object({
2262
+ type: z.literal("permission"),
2263
+ permission: BrowserPermission,
2264
+ setting: BrowserPermissionSetting,
2265
+ })
2266
+ .strict(),
1540
2267
  z
1541
2268
  .object({
1542
2269
  type: z.literal("wait"),
@@ -1554,10 +2281,59 @@ export const BrowserActionBatch = z
1554
2281
  .object({
1555
2282
  type: z.literal("batch"),
1556
2283
  actions: z.array(BrowserAction).min(1).max(INTERACTION_MAX_ACTIONS_PER_BATCH),
2284
+ /** Revalidate the original document before each action; never follow navigation. */
2285
+ fenceEachAction: z.literal(true).optional(),
1557
2286
  })
1558
- .strict();
2287
+ .strict()
2288
+ .superRefine((batch, context) => {
2289
+ const fileIds = new Set(
2290
+ batch.actions.flatMap((action) => (action.type === "upload" ? action.workspaceFileIds : [])),
2291
+ );
2292
+ if (fileIds.size > INTERACTION_MAX_WORKSPACE_FILES_PER_COMMAND) {
2293
+ context.addIssue({
2294
+ code: "custom",
2295
+ path: ["actions"],
2296
+ message: "browser action references too many workspace files",
2297
+ });
2298
+ }
2299
+ });
1559
2300
  export type BrowserActionBatch = z.infer<typeof BrowserActionBatch>;
1560
2301
 
2302
+ /** Secret-safe projection of a connected-machine control failure carried in a
2303
+ * standard API error envelope. The outer envelope owns HTTP status, retryability,
2304
+ * and public request correlation; this preserves the exact failing control class
2305
+ * and optional inner request id without exposing subjects, local paths, or tokens. */
2306
+ export const InteractionControlFailureCode = z.enum([
2307
+ "unknown",
2308
+ "unsupported",
2309
+ "os",
2310
+ "not_found",
2311
+ "consent_required",
2312
+ "timeout",
2313
+ "draining",
2314
+ "protocol",
2315
+ "stream",
2316
+ "agent_offline",
2317
+ "fenced",
2318
+ "payload_too_large",
2319
+ ]);
2320
+ export type InteractionControlFailureCode = z.infer<typeof InteractionControlFailureCode>;
2321
+
2322
+ export const InteractionControlFailureDetails = z
2323
+ .object({
2324
+ interactionLayer: z.literal("connected_machine"),
2325
+ interactionSurface: z.enum(["browser", "computer"]),
2326
+ controlFailureCode: InteractionControlFailureCode,
2327
+ controlRequestId: z
2328
+ .string()
2329
+ .min(1)
2330
+ .max(128)
2331
+ .regex(/^[A-Za-z0-9._:-]+$/u)
2332
+ .optional(),
2333
+ })
2334
+ .strict();
2335
+ export type InteractionControlFailureDetails = z.infer<typeof InteractionControlFailureDetails>;
2336
+
1561
2337
  export const InteractionError = z
1562
2338
  .object({
1563
2339
  code: z.enum([
@@ -1600,11 +2376,72 @@ export const BrowserActionCommand = z
1600
2376
  expectedDocumentGeneration: opaqueGeneration.nullable(),
1601
2377
  expectedFrameId: opaqueGeneration.nullable(),
1602
2378
  actor: InteractionActor,
2379
+ /** Human live-control surfaces already receive the resulting pixels. They
2380
+ * may omit the expensive semantic snapshot; agent actions keep it. The
2381
+ * negotiated input mode only observes a focused native dropdown after a
2382
+ * pointer click, and otherwise returns no observation. */
2383
+ observationMode: z.enum(["full", "none", "input"]).optional(),
1603
2384
  action: z.union([BrowserAction, BrowserActionBatch]),
1604
2385
  })
1605
2386
  .strict();
1606
2387
  export type BrowserActionCommand = z.infer<typeof BrowserActionCommand>;
1607
2388
 
2389
+ /** Controller-private authority used to materialize immutable workspace files
2390
+ * beside a BrowserSession before an upload action dispatches. Signed URLs and
2391
+ * placement paths never appear in the public BrowserAction or its receipt. */
2392
+ export const BrowserWorkspaceFileAuthority = z
2393
+ .object({
2394
+ fileId: z.string().uuid(),
2395
+ safeFilename: z
2396
+ .string()
2397
+ .min(1)
2398
+ .max(240)
2399
+ .regex(/^[A-Za-z0-9._ -]+$/u)
2400
+ .refine((value) => value !== "." && value !== "..", {
2401
+ message: "safe filename must be one path segment",
2402
+ }),
2403
+ sizeBytes: z.number().int().nonnegative().max(5_000_000_000),
2404
+ sha256: sha256Hex.nullable(),
2405
+ download: z
2406
+ .object({
2407
+ url: boundedHttpUrl,
2408
+ expiresAt: z.string().datetime({ offset: true }),
2409
+ })
2410
+ .strict(),
2411
+ })
2412
+ .strict();
2413
+ export type BrowserWorkspaceFileAuthority = z.infer<typeof BrowserWorkspaceFileAuthority>;
2414
+
2415
+ export const BrowserWorkspaceFileStageRequest = z
2416
+ .object({
2417
+ operationId: z.string().uuid(),
2418
+ files: z.array(BrowserWorkspaceFileAuthority).min(1).max(100),
2419
+ })
2420
+ .strict()
2421
+ .superRefine((request, context) => {
2422
+ const seen = new Set<string>();
2423
+ for (const [index, file] of request.files.entries()) {
2424
+ if (seen.has(file.fileId)) {
2425
+ context.addIssue({
2426
+ code: "custom",
2427
+ path: ["files", index, "fileId"],
2428
+ message: "workspace file authority is duplicated",
2429
+ });
2430
+ }
2431
+ seen.add(file.fileId);
2432
+ }
2433
+ });
2434
+ export type BrowserWorkspaceFileStageRequest = z.infer<typeof BrowserWorkspaceFileStageRequest>;
2435
+
2436
+ export const BrowserWorkspaceFileStageResponse = z
2437
+ .object({
2438
+ operationId: z.string().uuid(),
2439
+ fileIds: z.array(z.string().uuid()).min(1).max(100),
2440
+ replayed: z.boolean(),
2441
+ })
2442
+ .strict();
2443
+ export type BrowserWorkspaceFileStageResponse = z.infer<typeof BrowserWorkspaceFileStageResponse>;
2444
+
1608
2445
  export const InteractionOperationState = z.enum([
1609
2446
  "prepared",
1610
2447
  "dispatched",
@@ -1671,6 +2508,10 @@ export const CreateBrowserSessionRequest = z
1671
2508
  name: z.string().trim().min(1).max(200).optional(),
1672
2509
  initialUrl: boundedUrl.optional(),
1673
2510
  headless: z.boolean().default(true),
2511
+ storageMode: BrowserStorageMode.default("private_profile"),
2512
+ /** Managed engine choice. Attached Chrome continues to derive its engine
2513
+ * from the selected device rather than accepting an impersonated value. */
2514
+ engine: z.enum(["chromium", "lightpanda"]).default("chromium"),
1674
2515
  placement: InteractionPlacement.optional(),
1675
2516
  identityId: z.string().uuid().optional(),
1676
2517
  baseRevisionId: z.string().uuid().optional(),
@@ -1679,6 +2520,23 @@ export const CreateBrowserSessionRequest = z
1679
2520
  })
1680
2521
  .strict()
1681
2522
  .superRefine((value, context) => {
2523
+ if (
2524
+ value.storageMode === "ephemeral_context" &&
2525
+ (!value.headless ||
2526
+ value.engine !== "chromium" ||
2527
+ value.identityId ||
2528
+ value.baseRevisionId ||
2529
+ value.networkRouteId ||
2530
+ value.linkedComputerSessionId ||
2531
+ (value.placement && value.placement.kind !== "sandbox_group"))
2532
+ ) {
2533
+ context.addIssue({
2534
+ code: "custom",
2535
+ path: ["storageMode"],
2536
+ message:
2537
+ "ephemeral contexts require headless managed sandbox Chromium without identity, revision, route or Computer",
2538
+ });
2539
+ }
1682
2540
  if (value.baseRevisionId && !value.identityId) {
1683
2541
  context.addIssue({
1684
2542
  code: "custom",
@@ -1693,12 +2551,42 @@ export const CreateBrowserSessionRequest = z
1693
2551
  message: "a linked computer requires a headed browser",
1694
2552
  });
1695
2553
  }
1696
- if (value.placement?.kind === "attached_device") {
2554
+ if (value.engine === "lightpanda") {
2555
+ if (!value.headless) {
2556
+ context.addIssue({
2557
+ code: "custom",
2558
+ path: ["headless"],
2559
+ message: "Lightpanda is a headless semantic browser",
2560
+ });
2561
+ }
2562
+ if (value.identityId || value.baseRevisionId) {
2563
+ context.addIssue({
2564
+ code: "custom",
2565
+ path: ["identityId"],
2566
+ message: "Lightpanda does not support Chromium browser identities",
2567
+ });
2568
+ }
2569
+ if (value.networkRouteId) {
2570
+ context.addIssue({
2571
+ code: "custom",
2572
+ path: ["networkRouteId"],
2573
+ message: "Lightpanda network routes are not supported yet",
2574
+ });
2575
+ }
1697
2576
  if (value.linkedComputerSessionId) {
1698
2577
  context.addIssue({
1699
2578
  code: "custom",
1700
2579
  path: ["linkedComputerSessionId"],
1701
- message: "attached Chrome does not expose an exact linked computer yet",
2580
+ message: "Lightpanda has no linked desktop window",
2581
+ });
2582
+ }
2583
+ }
2584
+ if (value.placement?.kind === "attached_device") {
2585
+ if (value.engine !== "chromium") {
2586
+ context.addIssue({
2587
+ code: "custom",
2588
+ path: ["engine"],
2589
+ message: "attached browser placement uses the selected Chrome engine",
1702
2590
  });
1703
2591
  }
1704
2592
  if (value.headless) {
@@ -1716,6 +2604,29 @@ export const CreateBrowserSessionRequest = z
1716
2604
  });
1717
2605
  }
1718
2606
  }
2607
+ if (value.placement?.kind === "external_provider") {
2608
+ if (value.engine !== "chromium") {
2609
+ context.addIssue({
2610
+ code: "custom",
2611
+ path: ["engine"],
2612
+ message: "external browser providers use their Chromium-compatible engine",
2613
+ });
2614
+ }
2615
+ if (value.identityId || value.baseRevisionId) {
2616
+ context.addIssue({
2617
+ code: "custom",
2618
+ path: ["identityId"],
2619
+ message: "external browser providers do not yet support portable BrowserIdentity state",
2620
+ });
2621
+ }
2622
+ if (value.linkedComputerSessionId) {
2623
+ context.addIssue({
2624
+ code: "custom",
2625
+ path: ["linkedComputerSessionId"],
2626
+ message: "external browser providers cannot link a placement desktop",
2627
+ });
2628
+ }
2629
+ }
1719
2630
  });
1720
2631
  export type CreateBrowserSessionRequest = z.infer<typeof CreateBrowserSessionRequest>;
1721
2632
 
@@ -1783,6 +2694,19 @@ const DirectInteractionFrameStreamAttachment = z
1783
2694
  })
1784
2695
  .strict();
1785
2696
 
2697
+ /** Native tunnels offer `binary` + RFB + bearer (3). Docker and OpenSandbox
2698
+ * hide those on the API interaction frame-proxy, so the viewer sees `binary` +
2699
+ * the proxy grant (2). */
2700
+ const DirectComputerRfbAttachment = z
2701
+ .object({
2702
+ kind: z.literal("direct_rfb"),
2703
+ url: boundedUrl,
2704
+ protocols: z.array(z.string().min(1).max(2_048)).min(2).max(3),
2705
+ /** Absent on an older API means pixel viewing only. */
2706
+ inputAllowed: z.boolean().default(false),
2707
+ })
2708
+ .strict();
2709
+
1786
2710
  function relayInteractionFrameStreamAttachment<const Kind extends 3 | 4>(kind: Kind) {
1787
2711
  return z
1788
2712
  .object({
@@ -1810,6 +2734,7 @@ export type BrowserFrameStreamAttachment = z.infer<typeof BrowserFrameStreamAtta
1810
2734
 
1811
2735
  export const ComputerFrameStreamAttachment = z.discriminatedUnion("kind", [
1812
2736
  DirectInteractionFrameStreamAttachment,
2737
+ DirectComputerRfbAttachment,
1813
2738
  relayInteractionFrameStreamAttachment(4),
1814
2739
  ]);
1815
2740
  export type ComputerFrameStreamAttachment = z.infer<typeof ComputerFrameStreamAttachment>;
@@ -1826,6 +2751,8 @@ export const BrowserSessionAttachment = z
1826
2751
  controllerGeneration: opaqueGeneration,
1827
2752
  targetId: boundedOpaqueId,
1828
2753
  stream: BrowserFrameStreamAttachment,
2754
+ fencedInputBatches: z.literal(true).optional(),
2755
+ focusedInputObservations: z.literal(true).optional(),
1829
2756
  expiresAt: z.string().datetime({ offset: true }),
1830
2757
  })
1831
2758
  .strict();
@@ -1850,6 +2777,7 @@ export const BrowserActionRequest = z
1850
2777
  expectedTargetGeneration: opaqueGeneration,
1851
2778
  expectedDocumentGeneration: opaqueGeneration.nullable(),
1852
2779
  expectedFrameId: opaqueGeneration.nullable(),
2780
+ observationMode: z.enum(["full", "none", "input"]).default("full"),
1853
2781
  action: z.union([BrowserAction, BrowserActionBatch]),
1854
2782
  })
1855
2783
  .strict();
@@ -1903,6 +2831,104 @@ export const BrowserActionReceipt = z
1903
2831
  });
1904
2832
  export type BrowserActionReceipt = z.infer<typeof BrowserActionReceipt>;
1905
2833
 
2834
+ /** Controller-private protected-fill wire contract. Secret values cross only
2835
+ * the credential broker -> exact placement controller boundary; this command
2836
+ * is never projected into model MCP, Codemode, public action history, or UI. */
2837
+ export const BrowserProtectedAuthFieldValue = ProtectedAuthField.extend({
2838
+ purpose: SiteAuthFieldPurpose,
2839
+ value: z.string().min(1).max(65_536),
2840
+ }).strict();
2841
+ export type BrowserProtectedAuthFieldValue = z.infer<typeof BrowserProtectedAuthFieldValue>;
2842
+
2843
+ export const BrowserProtectedAuthFillCommand = z
2844
+ .object({
2845
+ protocolVersion: z.literal(INTERACTION_PROTOCOL_VERSION),
2846
+ operationId: z.string().uuid(),
2847
+ browserSessionId: z.string().uuid(),
2848
+ controllerGeneration: opaqueGeneration,
2849
+ targetId: boundedOpaqueId,
2850
+ expectedTargetGeneration: opaqueGeneration,
2851
+ expectedDocumentGeneration: opaqueGeneration,
2852
+ expectedFrameId: opaqueGeneration,
2853
+ actor: InteractionActor,
2854
+ authorityId: boundedOpaqueId,
2855
+ credentialVersion: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
2856
+ allowedOrigins: z.array(canonicalWebOrigin).min(1).max(64),
2857
+ fields: z.array(BrowserProtectedAuthFieldValue).min(1).max(32),
2858
+ submit: ProtectedAuthSubmit,
2859
+ })
2860
+ .strict()
2861
+ .superRefine((command, context) => {
2862
+ if (new Set(command.allowedOrigins).size !== command.allowedOrigins.length) {
2863
+ context.addIssue({
2864
+ code: "custom",
2865
+ path: ["allowedOrigins"],
2866
+ message: "protected-fill origins repeat",
2867
+ });
2868
+ }
2869
+ const fieldIds = command.fields.map((field) => field.fieldId);
2870
+ if (new Set(fieldIds).size !== fieldIds.length) {
2871
+ context.addIssue({
2872
+ code: "custom",
2873
+ path: ["fields"],
2874
+ message: "protected-fill field ids repeat",
2875
+ });
2876
+ }
2877
+ });
2878
+ export type BrowserProtectedAuthFillCommand = z.infer<typeof BrowserProtectedAuthFillCommand>;
2879
+
2880
+ export const BrowserProtectedAuthObservation = z
2881
+ .object({
2882
+ target: BrowserTarget,
2883
+ status: z.enum(["submitted", "working"]),
2884
+ })
2885
+ .strict();
2886
+ export type BrowserProtectedAuthObservation = z.infer<typeof BrowserProtectedAuthObservation>;
2887
+
2888
+ /** Secret-free placement receipt. It can be journaled, replayed, and returned
2889
+ * to the broker without retaining any field value or model-visible page tree. */
2890
+ export const BrowserProtectedAuthFillReceipt = z
2891
+ .object({
2892
+ protocolVersion: z.literal(INTERACTION_PROTOCOL_VERSION),
2893
+ operationId: z.string().uuid(),
2894
+ browserSessionId: z.string().uuid(),
2895
+ controllerGeneration: opaqueGeneration,
2896
+ targetId: boundedOpaqueId,
2897
+ state: InteractionOperationState,
2898
+ dispatchedAt: z.string().datetime({ offset: true }).nullable(),
2899
+ settledAt: z.string().datetime({ offset: true }).nullable(),
2900
+ observation: BrowserProtectedAuthObservation.nullable(),
2901
+ error: InteractionError.nullable(),
2902
+ })
2903
+ .strict()
2904
+ .superRefine((receipt, context) => {
2905
+ if (
2906
+ receipt.state === "completed" &&
2907
+ (receipt.error !== null || receipt.observation === null || receipt.settledAt === null)
2908
+ ) {
2909
+ context.addIssue({
2910
+ code: "custom",
2911
+ message: "completed protected-fill receipt requires a result and no error",
2912
+ });
2913
+ }
2914
+ if (
2915
+ (receipt.state === "failed" || receipt.state === "outcome_unknown") &&
2916
+ (receipt.error === null || receipt.observation !== null || receipt.settledAt === null)
2917
+ ) {
2918
+ context.addIssue({
2919
+ code: "custom",
2920
+ message: "terminal protected-fill error requires error, no result, and time",
2921
+ });
2922
+ }
2923
+ if (receipt.state === "dispatched" && receipt.dispatchedAt === null) {
2924
+ context.addIssue({
2925
+ code: "custom",
2926
+ message: "dispatched protected-fill receipt requires dispatch time",
2927
+ });
2928
+ }
2929
+ });
2930
+ export type BrowserProtectedAuthFillReceipt = z.infer<typeof BrowserProtectedAuthFillReceipt>;
2931
+
1906
2932
  export const ComputerSessionCapabilities = z
1907
2933
  .object({
1908
2934
  semanticObservation: z.boolean(),
@@ -1913,12 +2939,45 @@ export const ComputerSessionCapabilities = z
1913
2939
  semanticActions: z.boolean(),
1914
2940
  pointerInput: z.boolean(),
1915
2941
  keyboardInput: z.boolean(),
2942
+ clipboard: z.boolean(),
1916
2943
  backgroundActions: z.boolean(),
2944
+ /** Raw window input stays in the background. Absent means foreground-only. */
2945
+ backgroundInput: z.boolean().optional(),
1917
2946
  parallelApps: z.boolean(),
1918
2947
  })
1919
2948
  .strict();
1920
2949
  export type ComputerSessionCapabilities = z.infer<typeof ComputerSessionCapabilities>;
1921
2950
 
2951
+ /** A fresh bounded read of the native clipboard for the ComputerSession's
2952
+ * graphical seat. Physical ComputerSessions on the same login seat intentionally
2953
+ * observe the same OS clipboard; BrowserSession private clipboards never do. */
2954
+ export const ComputerClipboard = z
2955
+ .object({
2956
+ computerSessionId: z.string().uuid(),
2957
+ controllerGeneration: opaqueGeneration,
2958
+ text: z
2959
+ .string()
2960
+ .max(INTERACTION_MAX_CLIPBOARD_BYTES)
2961
+ .refine(
2962
+ (value) => new TextEncoder().encode(value).byteLength <= INTERACTION_MAX_CLIPBOARD_BYTES,
2963
+ { message: "computer clipboard text exceeds its UTF-8 byte envelope" },
2964
+ )
2965
+ .nullable(),
2966
+ truncated: z.boolean(),
2967
+ observedAt: z.string().datetime({ offset: true }),
2968
+ })
2969
+ .strict()
2970
+ .superRefine((clipboard, context) => {
2971
+ if (clipboard.text === null && clipboard.truncated) {
2972
+ context.addIssue({
2973
+ code: "custom",
2974
+ path: ["truncated"],
2975
+ message: "an unavailable computer clipboard value cannot be truncated",
2976
+ });
2977
+ }
2978
+ });
2979
+ export type ComputerClipboard = z.infer<typeof ComputerClipboard>;
2980
+
1922
2981
  export const ComputerSession = z
1923
2982
  .object({
1924
2983
  id: z.string().uuid(),
@@ -2079,6 +3138,29 @@ export const ComputerAction = z.discriminatedUnion("type", [
2079
3138
  value: z.string().max(1_000_000),
2080
3139
  })
2081
3140
  .strict(),
3141
+ z
3142
+ .object({
3143
+ type: z.literal("clipboard"),
3144
+ operation: z.enum(["write", "clear", "copy", "paste"]),
3145
+ text: z
3146
+ .string()
3147
+ .max(INTERACTION_MAX_CLIPBOARD_BYTES)
3148
+ .refine(
3149
+ (value) => new TextEncoder().encode(value).byteLength <= INTERACTION_MAX_CLIPBOARD_BYTES,
3150
+ { message: "computer clipboard text exceeds its UTF-8 byte envelope" },
3151
+ )
3152
+ .optional(),
3153
+ })
3154
+ .strict()
3155
+ .superRefine((action, context) => {
3156
+ if ((action.operation === "write") !== (action.text !== undefined)) {
3157
+ context.addIssue({
3158
+ code: "custom",
3159
+ path: ["text"],
3160
+ message: "computer clipboard text is required exactly for write",
3161
+ });
3162
+ }
3163
+ }),
2082
3164
  z.object({ type: z.literal("focus"), targetId: boundedOpaqueId }).strict(),
2083
3165
  z
2084
3166
  .object({
@@ -2225,12 +3307,26 @@ export const ComputerSessionAttachment = z
2225
3307
  computerSessionId: z.string().uuid(),
2226
3308
  controllerGeneration: opaqueGeneration,
2227
3309
  targetId: boundedOpaqueId,
3310
+ // Viewer posture only. Canonical actions still require live source authority.
3311
+ // Older API responses omit it; their existing action authorization remains.
3312
+ inputAllowed: z.boolean().optional(),
2228
3313
  stream: ComputerFrameStreamAttachment,
2229
3314
  expiresAt: z.string().datetime({ offset: true }),
2230
3315
  })
2231
3316
  .strict();
2232
3317
  export type ComputerSessionAttachment = z.infer<typeof ComputerSessionAttachment>;
2233
3318
 
3319
+ /** Caller-specific human input posture, independent of media/native capability.
3320
+ * It is a current-controller hint; every action still reauthorizes. */
3321
+ export const ComputerSessionInputPosture = z
3322
+ .object({
3323
+ computerSessionId: z.string().uuid(),
3324
+ controllerGeneration: opaqueGeneration,
3325
+ inputAllowed: z.boolean(),
3326
+ })
3327
+ .strict();
3328
+ export type ComputerSessionInputPosture = z.infer<typeof ComputerSessionInputPosture>;
3329
+
2234
3330
  export const ComputerSessionAttachmentRequest = z
2235
3331
  .object({
2236
3332
  targetId: boundedOpaqueId,