libre-webui 0.26.0 → 0.28.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 (395) hide show
  1. package/README.md +57 -29
  2. package/backend/dist/index.d.ts.map +1 -1
  3. package/backend/dist/index.js +7 -0
  4. package/backend/dist/index.js.map +1 -1
  5. package/backend/dist/persistence/postgresAutomationWorkTargetMigration.d.ts +9 -0
  6. package/backend/dist/persistence/postgresAutomationWorkTargetMigration.d.ts.map +1 -0
  7. package/backend/dist/persistence/postgresAutomationWorkTargetMigration.js +44 -0
  8. package/backend/dist/persistence/postgresAutomationWorkTargetMigration.js.map +1 -0
  9. package/backend/dist/persistence/postgresMfaPushRecoveryMigration.d.ts +9 -0
  10. package/backend/dist/persistence/postgresMfaPushRecoveryMigration.d.ts.map +1 -0
  11. package/backend/dist/persistence/postgresMfaPushRecoveryMigration.js +107 -0
  12. package/backend/dist/persistence/postgresMfaPushRecoveryMigration.js.map +1 -0
  13. package/backend/dist/persistence/postgresMigrationRegistry.d.ts.map +1 -1
  14. package/backend/dist/persistence/postgresMigrationRegistry.js +8 -0
  15. package/backend/dist/persistence/postgresMigrationRegistry.js.map +1 -1
  16. package/backend/dist/persistence/postgresResourceRepositories.d.ts.map +1 -1
  17. package/backend/dist/persistence/postgresResourceRepositories.js +179 -4
  18. package/backend/dist/persistence/postgresResourceRepositories.js.map +1 -1
  19. package/backend/dist/persistence/postgresSecurityRepositories.d.ts.map +1 -1
  20. package/backend/dist/persistence/postgresSecurityRepositories.js +154 -0
  21. package/backend/dist/persistence/postgresSecurityRepositories.js.map +1 -1
  22. package/backend/dist/persistence/postgresWorkComputerMigration.d.ts +8 -0
  23. package/backend/dist/persistence/postgresWorkComputerMigration.d.ts.map +1 -0
  24. package/backend/dist/persistence/postgresWorkComputerMigration.js +37 -0
  25. package/backend/dist/persistence/postgresWorkComputerMigration.js.map +1 -0
  26. package/backend/dist/persistence/postgresWorkTakeoverMigration.d.ts +9 -0
  27. package/backend/dist/persistence/postgresWorkTakeoverMigration.d.ts.map +1 -0
  28. package/backend/dist/persistence/postgresWorkTakeoverMigration.js +38 -0
  29. package/backend/dist/persistence/postgresWorkTakeoverMigration.js.map +1 -0
  30. package/backend/dist/persistence/resourceTypes.d.ts +64 -0
  31. package/backend/dist/persistence/resourceTypes.d.ts.map +1 -1
  32. package/backend/dist/persistence/resourceTypes.js.map +1 -1
  33. package/backend/dist/persistence/securityTypes.d.ts +60 -0
  34. package/backend/dist/persistence/securityTypes.d.ts.map +1 -1
  35. package/backend/dist/persistence/sqliteMigrations.d.ts +4 -0
  36. package/backend/dist/persistence/sqliteMigrations.d.ts.map +1 -1
  37. package/backend/dist/persistence/sqliteMigrations.js +267 -0
  38. package/backend/dist/persistence/sqliteMigrations.js.map +1 -1
  39. package/backend/dist/persistence/sqliteResourceRepositories.d.ts.map +1 -1
  40. package/backend/dist/persistence/sqliteResourceRepositories.js +151 -7
  41. package/backend/dist/persistence/sqliteResourceRepositories.js.map +1 -1
  42. package/backend/dist/persistence/sqliteSecurityRepositories.d.ts.map +1 -1
  43. package/backend/dist/persistence/sqliteSecurityRepositories.js +140 -0
  44. package/backend/dist/persistence/sqliteSecurityRepositories.js.map +1 -1
  45. package/backend/dist/persistence/sqliteToPostgresMigration.d.ts.map +1 -1
  46. package/backend/dist/persistence/sqliteToPostgresMigration.js +45 -0
  47. package/backend/dist/persistence/sqliteToPostgresMigration.js.map +1 -1
  48. package/backend/dist/platform/jobs/domainJobContracts.d.ts +2 -0
  49. package/backend/dist/platform/jobs/domainJobContracts.d.ts.map +1 -1
  50. package/backend/dist/platform/jobs/domainJobContracts.js +2 -0
  51. package/backend/dist/platform/jobs/domainJobContracts.js.map +1 -1
  52. package/backend/dist/platform/jobs/domainJobHandlers.d.ts.map +1 -1
  53. package/backend/dist/platform/jobs/domainJobHandlers.js +152 -2
  54. package/backend/dist/platform/jobs/domainJobHandlers.js.map +1 -1
  55. package/backend/dist/platform/workPersistence/postgresWorkPersistence.d.ts.map +1 -1
  56. package/backend/dist/platform/workPersistence/postgresWorkPersistence.js +7 -3
  57. package/backend/dist/platform/workPersistence/postgresWorkPersistence.js.map +1 -1
  58. package/backend/dist/platform/workPersistence/sqliteWorkPersistence.d.ts.map +1 -1
  59. package/backend/dist/platform/workPersistence/sqliteWorkPersistence.js +8 -4
  60. package/backend/dist/platform/workPersistence/sqliteWorkPersistence.js.map +1 -1
  61. package/backend/dist/platform/workPersistence/types.d.ts +2 -0
  62. package/backend/dist/platform/workPersistence/types.d.ts.map +1 -1
  63. package/backend/dist/platform/workPersistence/types.js.map +1 -1
  64. package/backend/dist/routes/auth.d.ts.map +1 -1
  65. package/backend/dist/routes/auth.js +427 -5
  66. package/backend/dist/routes/auth.js.map +1 -1
  67. package/backend/dist/routes/automations.d.ts.map +1 -1
  68. package/backend/dist/routes/automations.js +5 -0
  69. package/backend/dist/routes/automations.js.map +1 -1
  70. package/backend/dist/routes/documents.d.ts.map +1 -1
  71. package/backend/dist/routes/documents.js +4 -0
  72. package/backend/dist/routes/documents.js.map +1 -1
  73. package/backend/dist/routes/push.d.ts +3 -0
  74. package/backend/dist/routes/push.d.ts.map +1 -0
  75. package/backend/dist/routes/push.js +105 -0
  76. package/backend/dist/routes/push.js.map +1 -0
  77. package/backend/dist/routes/recovery.d.ts +3 -0
  78. package/backend/dist/routes/recovery.d.ts.map +1 -0
  79. package/backend/dist/routes/recovery.js +83 -0
  80. package/backend/dist/routes/recovery.js.map +1 -0
  81. package/backend/dist/routes/users.d.ts.map +1 -1
  82. package/backend/dist/routes/users.js +35 -0
  83. package/backend/dist/routes/users.js.map +1 -1
  84. package/backend/dist/routes/work.d.ts.map +1 -1
  85. package/backend/dist/routes/work.js +205 -1
  86. package/backend/dist/routes/work.js.map +1 -1
  87. package/backend/dist/services/authService.d.ts +8 -0
  88. package/backend/dist/services/authService.d.ts.map +1 -1
  89. package/backend/dist/services/authService.js +29 -0
  90. package/backend/dist/services/authService.js.map +1 -1
  91. package/backend/dist/services/automationSchedulerService.d.ts.map +1 -1
  92. package/backend/dist/services/automationSchedulerService.js +31 -0
  93. package/backend/dist/services/automationSchedulerService.js.map +1 -1
  94. package/backend/dist/services/automationService.d.ts +4 -1
  95. package/backend/dist/services/automationService.d.ts.map +1 -1
  96. package/backend/dist/services/automationService.js +27 -0
  97. package/backend/dist/services/automationService.js.map +1 -1
  98. package/backend/dist/services/dataArchiveService.d.ts.map +1 -1
  99. package/backend/dist/services/dataArchiveService.js +3 -0
  100. package/backend/dist/services/dataArchiveService.js.map +1 -1
  101. package/backend/dist/services/documentMediaExtractionService.d.ts +40 -0
  102. package/backend/dist/services/documentMediaExtractionService.d.ts.map +1 -0
  103. package/backend/dist/services/documentMediaExtractionService.js +157 -0
  104. package/backend/dist/services/documentMediaExtractionService.js.map +1 -0
  105. package/backend/dist/services/documentService.d.ts +6 -0
  106. package/backend/dist/services/documentService.d.ts.map +1 -1
  107. package/backend/dist/services/documentService.js +58 -4
  108. package/backend/dist/services/documentService.js.map +1 -1
  109. package/backend/dist/services/encryptionService.d.ts +6 -0
  110. package/backend/dist/services/encryptionService.d.ts.map +1 -1
  111. package/backend/dist/services/encryptionService.js +12 -0
  112. package/backend/dist/services/encryptionService.js.map +1 -1
  113. package/backend/dist/services/mfaService.d.ts +64 -0
  114. package/backend/dist/services/mfaService.d.ts.map +1 -0
  115. package/backend/dist/services/mfaService.js +262 -0
  116. package/backend/dist/services/mfaService.js.map +1 -0
  117. package/backend/dist/services/notificationService.d.ts +5 -0
  118. package/backend/dist/services/notificationService.d.ts.map +1 -1
  119. package/backend/dist/services/notificationService.js +33 -1
  120. package/backend/dist/services/notificationService.js.map +1 -1
  121. package/backend/dist/services/preferencesService.d.ts +1 -1
  122. package/backend/dist/services/preferencesService.d.ts.map +1 -1
  123. package/backend/dist/services/preferencesService.js +1 -0
  124. package/backend/dist/services/preferencesService.js.map +1 -1
  125. package/backend/dist/services/recoveryDrillService.d.ts +57 -0
  126. package/backend/dist/services/recoveryDrillService.d.ts.map +1 -0
  127. package/backend/dist/services/recoveryDrillService.js +363 -0
  128. package/backend/dist/services/recoveryDrillService.js.map +1 -0
  129. package/backend/dist/services/webPushService.d.ts +71 -0
  130. package/backend/dist/services/webPushService.d.ts.map +1 -0
  131. package/backend/dist/services/webPushService.js +328 -0
  132. package/backend/dist/services/webPushService.js.map +1 -0
  133. package/backend/dist/services/webauthnService.d.ts +58 -0
  134. package/backend/dist/services/webauthnService.d.ts.map +1 -0
  135. package/backend/dist/services/webauthnService.js +301 -0
  136. package/backend/dist/services/webauthnService.js.map +1 -0
  137. package/backend/dist/services/websocketTicketService.d.ts +1 -1
  138. package/backend/dist/services/websocketTicketService.d.ts.map +1 -1
  139. package/backend/dist/services/websocketTicketService.js +3 -1
  140. package/backend/dist/services/websocketTicketService.js.map +1 -1
  141. package/backend/dist/services/workAgentGuidance.d.ts +14 -0
  142. package/backend/dist/services/workAgentGuidance.d.ts.map +1 -1
  143. package/backend/dist/services/workAgentGuidance.js +38 -2
  144. package/backend/dist/services/workAgentGuidance.js.map +1 -1
  145. package/backend/dist/services/workAgentService.d.ts +9 -0
  146. package/backend/dist/services/workAgentService.d.ts.map +1 -1
  147. package/backend/dist/services/workAgentService.js +503 -13
  148. package/backend/dist/services/workAgentService.js.map +1 -1
  149. package/backend/dist/services/workComputerSetupService.d.ts +33 -0
  150. package/backend/dist/services/workComputerSetupService.d.ts.map +1 -0
  151. package/backend/dist/services/workComputerSetupService.js +164 -0
  152. package/backend/dist/services/workComputerSetupService.js.map +1 -0
  153. package/backend/dist/services/workComputerTeachService.d.ts +105 -0
  154. package/backend/dist/services/workComputerTeachService.d.ts.map +1 -0
  155. package/backend/dist/services/workComputerTeachService.js +513 -0
  156. package/backend/dist/services/workComputerTeachService.js.map +1 -0
  157. package/backend/dist/services/workKubernetesDriver.d.ts +8 -0
  158. package/backend/dist/services/workKubernetesDriver.d.ts.map +1 -1
  159. package/backend/dist/services/workKubernetesDriver.js +22 -0
  160. package/backend/dist/services/workKubernetesDriver.js.map +1 -1
  161. package/backend/dist/services/workModelProviderService.d.ts.map +1 -1
  162. package/backend/dist/services/workModelProviderService.js +102 -8
  163. package/backend/dist/services/workModelProviderService.js.map +1 -1
  164. package/backend/dist/services/workPolicyService.d.ts +8 -0
  165. package/backend/dist/services/workPolicyService.d.ts.map +1 -1
  166. package/backend/dist/services/workPolicyService.js +25 -0
  167. package/backend/dist/services/workPolicyService.js.map +1 -1
  168. package/backend/dist/services/workRuntimeDriver.d.ts +32 -0
  169. package/backend/dist/services/workRuntimeDriver.d.ts.map +1 -1
  170. package/backend/dist/services/workRuntimeDriver.js +54 -8
  171. package/backend/dist/services/workRuntimeDriver.js.map +1 -1
  172. package/backend/dist/services/workRuntimeService.d.ts +212 -1
  173. package/backend/dist/services/workRuntimeService.d.ts.map +1 -1
  174. package/backend/dist/services/workRuntimeService.js +1184 -9
  175. package/backend/dist/services/workRuntimeService.js.map +1 -1
  176. package/backend/dist/services/workRuntimeShared.d.ts +11 -1
  177. package/backend/dist/services/workRuntimeShared.d.ts.map +1 -1
  178. package/backend/dist/services/workRuntimeShared.js +24 -1
  179. package/backend/dist/services/workRuntimeShared.js.map +1 -1
  180. package/backend/dist/services/workScreenControlService.d.ts +60 -0
  181. package/backend/dist/services/workScreenControlService.d.ts.map +1 -0
  182. package/backend/dist/services/workScreenControlService.js +201 -0
  183. package/backend/dist/services/workScreenControlService.js.map +1 -0
  184. package/backend/dist/services/workTaskService.d.ts +11 -0
  185. package/backend/dist/services/workTaskService.d.ts.map +1 -1
  186. package/backend/dist/services/workTaskService.js +31 -0
  187. package/backend/dist/services/workTaskService.js.map +1 -1
  188. package/backend/dist/types/index.d.ts +10 -2
  189. package/backend/dist/types/index.d.ts.map +1 -1
  190. package/backend/dist/types/index.js.map +1 -1
  191. package/backend/dist/types/work.d.ts +13 -0
  192. package/backend/dist/types/work.d.ts.map +1 -1
  193. package/backend/dist/utils/documentExtraction.d.ts +31 -3
  194. package/backend/dist/utils/documentExtraction.d.ts.map +1 -1
  195. package/backend/dist/utils/documentExtraction.js +106 -0
  196. package/backend/dist/utils/documentExtraction.js.map +1 -1
  197. package/backend/dist/utils/toolEgress.d.ts +2 -1
  198. package/backend/dist/utils/toolEgress.d.ts.map +1 -1
  199. package/backend/dist/utils/toolEgress.js +14 -2
  200. package/backend/dist/utils/toolEgress.js.map +1 -1
  201. package/backend/dist/utils/totp.d.ts +19 -0
  202. package/backend/dist/utils/totp.d.ts.map +1 -0
  203. package/backend/dist/utils/totp.js +117 -0
  204. package/backend/dist/utils/totp.js.map +1 -0
  205. package/backend/dist/utils/webauthn.d.ts +47 -0
  206. package/backend/dist/utils/webauthn.d.ts.map +1 -0
  207. package/backend/dist/utils/webauthn.js +323 -0
  208. package/backend/dist/utils/webauthn.js.map +1 -0
  209. package/backend/dist/websocketServer.d.ts.map +1 -1
  210. package/backend/dist/websocketServer.js +9 -0
  211. package/backend/dist/websocketServer.js.map +1 -1
  212. package/backend/dist/workScreenServer.d.ts +12 -0
  213. package/backend/dist/workScreenServer.d.ts.map +1 -0
  214. package/backend/dist/workScreenServer.js +291 -0
  215. package/backend/dist/workScreenServer.js.map +1 -0
  216. package/backend/package.json +1 -1
  217. package/deploy/work-computer/Dockerfile +48 -0
  218. package/deploy/work-computer/README.md +93 -0
  219. package/deploy/work-computer/fonts/fonts.css +335 -0
  220. package/deploy/work-computer/fonts/v16-aFTR7PB1QTsUX8KYvumzEY2tbYf-Vlh3uA.woff2 +0 -0
  221. package/deploy/work-computer/fonts/v16-aFTR7PB1QTsUX8KYvumzEYOtbYf-Vlg.woff2 +0 -0
  222. package/deploy/work-computer/fonts/v16-aFTU7PB1QTsUX8KYthSQBK6PYK3EXw.woff2 +0 -0
  223. package/deploy/work-computer/fonts/v16-aFTU7PB1QTsUX8KYthqQBK6PYK0.woff2 +0 -0
  224. package/deploy/work-computer/fonts/v20-xn7gYHE41ni1AdIRggOxSvfedN62Zw.woff2 +0 -0
  225. package/deploy/work-computer/fonts/v20-xn7gYHE41ni1AdIRggSxSvfedN62Zw.woff2 +0 -0
  226. package/deploy/work-computer/fonts/v20-xn7gYHE41ni1AdIRggexSvfedN4.woff2 +0 -0
  227. package/deploy/work-computer/fonts/v20-xn7gYHE41ni1AdIRggixSvfedN62Zw.woff2 +0 -0
  228. package/deploy/work-computer/fonts/v20-xn7gYHE41ni1AdIRggmxSvfedN62Zw.woff2 +0 -0
  229. package/deploy/work-computer/fonts/v20-xn7gYHE41ni1AdIRggqxSvfedN62Zw.woff2 +0 -0
  230. package/deploy/work-computer/fonts/v22-V8mDoQDjQSkFtoMM3T6r8E7mPb54C_k3HqUtEw.woff2 +0 -0
  231. package/deploy/work-computer/fonts/v22-V8mDoQDjQSkFtoMM3T6r8E7mPb94C_k3HqUtEw.woff2 +0 -0
  232. package/deploy/work-computer/fonts/v22-V8mDoQDjQSkFtoMM3T6r8E7mPbF4C_k3HqU.woff2 +0 -0
  233. package/deploy/work-computer/start-computer.sh +155 -0
  234. package/deploy/work-computer/start-page.html +251 -0
  235. package/deploy/work-computer/tint2rc +76 -0
  236. package/deploy/work-computer/wallpaper.png +0 -0
  237. package/frontend/dist/artifact-runtime/.build-fingerprint +1 -1
  238. package/frontend/dist/assets/index-DGy6mo0B.js +33 -0
  239. package/frontend/dist/assets/index-DqLJKWF-.css +1 -0
  240. package/frontend/dist/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
  241. package/frontend/dist/fonts/jetbrains-mono-latin-700-normal.woff2 +0 -0
  242. package/frontend/dist/fonts/jetbrains-mono.css +24 -0
  243. package/frontend/dist/index.html +40 -18
  244. package/frontend/dist/js/ArtifactContainer-DvIMxleC.js +1 -0
  245. package/frontend/dist/js/{ArtifactDemoPage-D0JEIAbZ.js → ArtifactDemoPage-BqYskFqc.js} +1 -1
  246. package/frontend/dist/js/ArtifactSandboxFrame-9wuufHn6.js +111 -0
  247. package/frontend/dist/js/ArtifactSlideOutPanel-DXESMtPh.js +1 -0
  248. package/frontend/dist/js/AutomationsPage-DByxhIKj.js +1 -0
  249. package/frontend/dist/js/AvatarUpload-DnCGQ46o.js +1 -0
  250. package/frontend/dist/js/{Button-C0r3J9G_.js → Button-CpobxJ2P.js} +1 -1
  251. package/frontend/dist/js/CalendarPage-JywVP-9y.js +1 -0
  252. package/frontend/dist/js/ChannelsPage-B31rcARd.js +1 -0
  253. package/frontend/dist/js/ChatPage-CHXJY8m3.js +14 -0
  254. package/frontend/dist/js/{EvaluationsPage-Bm8FRoeX.js → EvaluationsPage-CcmB5wdF.js} +1 -1
  255. package/frontend/dist/js/FirstTimeSetup-DDU7SowM.js +1 -0
  256. package/frontend/dist/js/GalleryPage-XfX_7iCf.js +1 -0
  257. package/frontend/dist/js/HomePage-DAZAkoAV.js +1 -0
  258. package/frontend/dist/js/LibreClawPage-De0gZmbF.js +1 -0
  259. package/frontend/dist/js/Logo-yKM43xGa.js +1 -0
  260. package/frontend/dist/js/LogoMark-PmpKQOre.js +1 -0
  261. package/frontend/dist/js/{ModelManagerModals-DX9eT5r2.js → ModelManagerModals-hz2cZZ4a.js} +1 -1
  262. package/frontend/dist/js/ModelSelector-BoFehQy-.js +1 -0
  263. package/frontend/dist/js/NotesPage-BGPI3gG6.js +3 -0
  264. package/frontend/dist/js/OptimizedSyntaxHighlighter-DrejdNUm.js +1 -0
  265. package/frontend/dist/js/OptimizedSyntaxHighlighter-hNqZPLiB.js +1 -0
  266. package/frontend/dist/js/PasswordStrengthMeter-RwModpZ1.js +1 -0
  267. package/frontend/dist/js/PersonasPage-BkNRD2wO.js +13 -0
  268. package/frontend/dist/js/PluginUsagePage-Cqt7V2SQ.js +1 -0
  269. package/frontend/dist/js/RichMessageContent-B2QSyX-l.js +1 -0
  270. package/frontend/dist/js/RichMessageContent-CYEhT6uU.js +3 -0
  271. package/frontend/dist/js/SettingsModal-IbIvVyss.js +19 -0
  272. package/frontend/dist/js/{SettingsToggle-BIVyfGjl.js → SettingsToggle-3GxA96E0.js} +1 -1
  273. package/frontend/dist/js/ShareDialog-BoE4OVhV.js +1 -0
  274. package/frontend/dist/js/SystemPage-B5ecz5H3.js +1 -0
  275. package/frontend/dist/js/TurnstileWidget-DHU9IUM1.js +1 -0
  276. package/frontend/dist/js/UserManagementPage-C0VUfbMk.js +1 -0
  277. package/frontend/dist/js/WorkPage-EweYI5Dx.js +74 -0
  278. package/frontend/dist/js/api-Bbh2AHHN.js +1 -0
  279. package/frontend/dist/js/api-F0CnlVH8.js +14 -0
  280. package/frontend/dist/js/appNavigation-DqV69ohR.js +1 -0
  281. package/frontend/dist/js/appStore-SLLBamo2.js +2 -0
  282. package/frontend/dist/js/appVersion-46bF9UkF.js +1 -0
  283. package/frontend/dist/js/ar-C4yo7x5J.js +2 -0
  284. package/frontend/dist/js/artifactHtml-DDmfpcB0.js +41 -0
  285. package/frontend/dist/js/authStore-CcfL8T6T.js +1 -0
  286. package/frontend/dist/js/authStore-DlSQ1TcP.js +4 -0
  287. package/frontend/dist/js/axios-_ZpUYd3e.js +1 -0
  288. package/frontend/dist/js/bn-BeZ6d2qF.js +2 -0
  289. package/frontend/dist/js/chatStore-DI79tEJ-.js +3 -0
  290. package/frontend/dist/js/classPrivateMethodInitSpec-B31Q4d2M.js +1 -0
  291. package/frontend/dist/js/{config-8KdgjtEx.js → config-0QgGZCrI.js} +1 -1
  292. package/frontend/dist/js/cs-9K5LILI5.js +2 -0
  293. package/frontend/dist/js/da-BWbBhLK8.js +2 -0
  294. package/frontend/dist/js/de-BXD9t2uY.js +2 -0
  295. package/frontend/dist/js/es-Dv4wZKlU.js +2 -0
  296. package/frontend/dist/js/{estree-BU5-LV_1.js → estree-WDwviHUZ.js} +1 -1
  297. package/frontend/dist/js/evaluationsApi-wjf0nh5J.js +1 -0
  298. package/frontend/dist/js/fr-BkXGyEei.js +2 -0
  299. package/frontend/dist/js/hi-BS1ldbWk.js +2 -0
  300. package/frontend/dist/js/{html-DeoAy2bo.js → html-Cj0HNcRt.js} +1 -1
  301. package/frontend/dist/js/id-Cr1njx5M.js +2 -0
  302. package/frontend/dist/js/is-BqpNCtDd.js +2 -0
  303. package/frontend/dist/js/it-Dp-iwH27.js +2 -0
  304. package/frontend/dist/js/ja-_e4v_6Mn.js +2 -0
  305. package/frontend/dist/js/ko-CjXf2pIX.js +2 -0
  306. package/frontend/dist/js/{logger-DSXjF4ye.js → logger-CpCQz3Vi.js} +1 -1
  307. package/frontend/dist/js/{markdown-DOLsYkBY.js → markdown-DTahdtRF.js} +1 -1
  308. package/frontend/dist/js/{messageCodeStyles-DhVgizF-.js → messageCodeStyles-COJFoq4P.js} +2 -2
  309. package/frontend/dist/js/ms-xpn-zACj.js +2 -0
  310. package/frontend/dist/js/nl-Baf0a2X0.js +2 -0
  311. package/frontend/dist/js/pl-BEOTrcUv.js +2 -0
  312. package/frontend/dist/js/pt-B5FUaihM.js +2 -0
  313. package/frontend/dist/js/rfb-bJaPz33m.js +5 -0
  314. package/frontend/dist/js/ru-BjzgMmkJ.js +2 -0
  315. package/frontend/dist/js/{standalone-BO5pzI8_.js → standalone-C04jT26m.js} +1 -1
  316. package/frontend/dist/js/sv-DDX_gP1z.js +2 -0
  317. package/frontend/dist/js/{syntax-highlight-DcR9Vhb7.js → syntax-highlight-P2zo9o06.js} +1 -1
  318. package/frontend/dist/js/teamEventStream-CgUkgjFC.js +8 -0
  319. package/frontend/dist/js/th-BXr3KovE.js +2 -0
  320. package/frontend/dist/js/tr-CdqxHEM8.js +2 -0
  321. package/frontend/dist/js/ttsBatching-BfhNT3c5.js +2 -0
  322. package/frontend/dist/js/{typescript-6GJ8APmn.js → typescript-FtGI7Y5P.js} +1 -1
  323. package/frontend/dist/js/ui-DFa2W4QC.js +2 -0
  324. package/frontend/dist/js/ui-vendor-CRZXLK0Q.js +177 -0
  325. package/frontend/dist/js/uk-CotKDP2Q.js +2 -0
  326. package/frontend/dist/js/useChat-BawGm7ua.js +10 -0
  327. package/frontend/dist/js/useQuery-BboI5V5G.js +1 -0
  328. package/frontend/dist/js/{useTranslation-5b5UhAc9.js → useTranslation-D7uLySn1.js} +1 -1
  329. package/frontend/dist/js/{utils-BGrdX9ie.js → utils-sD-qFh8D.js} +1 -1
  330. package/frontend/dist/js/utils-vendor-DTLgmsuc.js +9 -0
  331. package/frontend/dist/js/vi-F2NC1HaJ.js +2 -0
  332. package/frontend/dist/js/webauthnClient-CPSq528Q.js +1 -0
  333. package/frontend/dist/js/workApi-8mBuh_-J.js +6 -0
  334. package/frontend/dist/js/workApi-DJtLOslf.js +1 -0
  335. package/frontend/dist/js/workStatus-CcO9Fg41.js +1 -0
  336. package/frontend/dist/js/zh-DZ-kpEEA.js +2 -0
  337. package/frontend/dist/manifest.webmanifest +32 -0
  338. package/frontend/dist/sw.js +125 -0
  339. package/package.json +4 -2
  340. package/frontend/dist/assets/index-BPsH9FII.js +0 -80
  341. package/frontend/dist/assets/index-BtXF0Oxa.css +0 -1
  342. package/frontend/dist/js/ArtifactContainer-CIoFoMv8.js +0 -1
  343. package/frontend/dist/js/ArtifactSandboxFrame-REazyIrx.js +0 -151
  344. package/frontend/dist/js/ArtifactSlideOutPanel-A-i94ghF.js +0 -1
  345. package/frontend/dist/js/AutomationsPage-Dow_lbIf.js +0 -1
  346. package/frontend/dist/js/CalendarPage-DLjasZOd.js +0 -1
  347. package/frontend/dist/js/ChannelsPage-C8hvTD7J.js +0 -1
  348. package/frontend/dist/js/ChatPage-BAgBHUES.js +0 -16
  349. package/frontend/dist/js/FirstTimeSetup-BxJapNul.js +0 -1
  350. package/frontend/dist/js/GalleryPage-DHz9DkK7.js +0 -1
  351. package/frontend/dist/js/HomePage-BqNy-FWX.js +0 -1
  352. package/frontend/dist/js/LibreClawPage-BDzDILjY.js +0 -1
  353. package/frontend/dist/js/ModelSelector-ol8mK2ja.js +0 -1
  354. package/frontend/dist/js/NotesPage-DzHEx2O4.js +0 -3
  355. package/frontend/dist/js/OptimizedSyntaxHighlighter-CZNPwN0i.js +0 -1
  356. package/frontend/dist/js/PersonasPage-M_WKH2a9.js +0 -13
  357. package/frontend/dist/js/PluginUsagePage-Ctoka6JZ.js +0 -1
  358. package/frontend/dist/js/RichMessageContent-BqgafH4p.js +0 -3
  359. package/frontend/dist/js/SettingsModal-R9EDadAV.js +0 -19
  360. package/frontend/dist/js/SystemPage-aOkrSuY7.js +0 -1
  361. package/frontend/dist/js/UserManagementPage-BOuaX3ay.js +0 -1
  362. package/frontend/dist/js/WorkPage-DxZISerQ.js +0 -6
  363. package/frontend/dist/js/api-6bSMtSNh.js +0 -14
  364. package/frontend/dist/js/appStore-CdObhQBb.js +0 -2
  365. package/frontend/dist/js/ar-DT_VLBO3.js +0 -2
  366. package/frontend/dist/js/authStore-DbqqrfZv.js +0 -4
  367. package/frontend/dist/js/bn-BrvEqhGA.js +0 -2
  368. package/frontend/dist/js/chatStore-DbZPJNsu.js +0 -3
  369. package/frontend/dist/js/cs-BthnM_YI.js +0 -2
  370. package/frontend/dist/js/da-DCth-6_j.js +0 -2
  371. package/frontend/dist/js/de-BBWt0gJ_.js +0 -2
  372. package/frontend/dist/js/es-DuslOPqM.js +0 -2
  373. package/frontend/dist/js/evaluationsApi-CwZJsgeG.js +0 -1
  374. package/frontend/dist/js/fr-DZAeg46S.js +0 -2
  375. package/frontend/dist/js/hi-ijDSiY77.js +0 -2
  376. package/frontend/dist/js/id-BS6qS0oq.js +0 -2
  377. package/frontend/dist/js/is-CO2PMDte.js +0 -2
  378. package/frontend/dist/js/it-ChhxT4hs.js +0 -2
  379. package/frontend/dist/js/ja-cyeqUeJN.js +0 -2
  380. package/frontend/dist/js/ko-CfqHTLXj.js +0 -2
  381. package/frontend/dist/js/ms-DZ5pACe1.js +0 -2
  382. package/frontend/dist/js/nl-BsRsnOVp.js +0 -2
  383. package/frontend/dist/js/pl-DXSTAW4M.js +0 -2
  384. package/frontend/dist/js/pt-DmDx0JxB.js +0 -2
  385. package/frontend/dist/js/ru-d2g8EWfY.js +0 -2
  386. package/frontend/dist/js/sv-CIhYUTVQ.js +0 -2
  387. package/frontend/dist/js/th-B3wSxanB.js +0 -2
  388. package/frontend/dist/js/tr-R4UGgPOY.js +0 -2
  389. package/frontend/dist/js/ui-CV0LYmkM.js +0 -2
  390. package/frontend/dist/js/ui-vendor-CWjIBW-z.js +0 -177
  391. package/frontend/dist/js/uk-BzUF4Vkt.js +0 -2
  392. package/frontend/dist/js/utils-vendor-9bQ62WeD.js +0 -9
  393. package/frontend/dist/js/vi-CKc6Fg4h.js +0 -2
  394. package/frontend/dist/js/workApi-BruWTlHM.js +0 -6
  395. package/frontend/dist/js/zh-CXL-kDe8.js +0 -2
@@ -16,8 +16,11 @@
16
16
  */
17
17
  import workModelProviderService, { WORK_TOOL_ARGUMENTS_ERROR_METADATA_KEY, WORK_TOOL_ARGUMENTS_ERROR_MESSAGE, } from './workModelProviderService.js';
18
18
  import workEventService from './workEventService.js';
19
- import { buildWorkAgentSystemPrompt, buildWorkBudgetExhaustionPrompt, buildWorkEmptyRoundNudgePrompt, WORK_AGENT_SKILLS, WORK_WRITE_FILE_RECOMMENDED_CHARS, workToolCallBudget, } from './workAgentGuidance.js';
20
- import workRuntimeService from './workRuntimeService.js';
19
+ import { buildWorkAgentSystemPrompt, buildWorkBudgetExhaustionPrompt, buildWorkComputerAmbiguityPrompt, buildWorkComputerStallPrompt, buildWorkEmptyRoundNudgePrompt, workAgentSkillsForContext, WORK_WRITE_FILE_RECOMMENDED_CHARS, workToolCallBudget, } from './workAgentGuidance.js';
20
+ import workPolicyService from './workPolicyService.js';
21
+ import workRuntimeService, { WORK_COMPUTER_ACTION_LIMIT, } from './workRuntimeService.js';
22
+ import workScreenControlService, { WORK_SCREEN_ASSIST_TIMEOUT_MS, } from './workScreenControlService.js';
23
+ import workComputerTeachService from './workComputerTeachService.js';
21
24
  import { isWebSearchAvailable, userCanUseWebSearch, webSearch, } from './webSearchService.js';
22
25
  import { userModel } from '../models/userModel.js';
23
26
  import workTaskService, { WORK_MESSAGE_METADATA_MAX_BYTES, WorkConflictError, WorkNotFoundError, } from './workTaskService.js';
@@ -82,12 +85,106 @@ export const WORK_WEB_SEARCH_TOOL_SCHEMA = functionTool('web_search', 'Search th
82
85
  description: "Optional result count. Defaults to 5, capped by the server's configured limit.",
83
86
  },
84
87
  }, ['query']);
88
+ // Offered only when the task's policy enables the Work Computer and the
89
+ // task has network access: the agent's eyes and hands on the GUI session.
90
+ export const WORK_COMPUTER_TOOL_SCHEMAS = [
91
+ functionTool('computer_observe', "Look at the task's virtual computer screen. Returns the current screenshot with the cursor position, active window, page URL, and which element holds keyboard focus.", {}),
92
+ functionTool('computer_act', 'Perform batched mouse and keyboard actions on the virtual computer, then return the screenshot after they settle. The batch stops early (and tells you) if the window, title, or window count changes mid-batch — later coordinates would target the previous screen — or if a "focus" assertion fails.', {
93
+ actions: {
94
+ type: 'array',
95
+ description: `Up to ${WORK_COMPUTER_ACTION_LIMIT} actions executed in order. Each action is an object with "type" plus type-specific fields: ` +
96
+ 'move {x,y}; click / double_click / right_click {x,y optional — clicks the current position without them}; ' +
97
+ 'type {text}; key {keys, an xdotool name or chord such as "Return" or "ctrl+l"}; ' +
98
+ 'scroll {direction: "up"|"down", amount optional, x,y optional}; ' +
99
+ 'scroll_until {direction, target: {text} or {edge: "top"|"bottom"}, maxAmount optional ≤30, x,y optional} — goal-directed scrolling that stops when the target text is visible and reports a visibility receipt, always preferable to blind scroll amounts; ' +
100
+ 'wait {ms, at most 5000}. ' +
101
+ 'type and key accept an optional "focus" string: the action runs only when the focused element, page URL, or window title contains it — use it before typing anything that must land in a specific field. ' +
102
+ 'Clicks at explicit coordinates return a receipt saying whether pixels near the click changed.',
103
+ items: {
104
+ type: 'object',
105
+ properties: {
106
+ type: {
107
+ type: 'string',
108
+ enum: [
109
+ 'move',
110
+ 'click',
111
+ 'double_click',
112
+ 'right_click',
113
+ 'type',
114
+ 'key',
115
+ 'scroll',
116
+ 'scroll_until',
117
+ 'wait',
118
+ ],
119
+ },
120
+ x: { type: 'integer' },
121
+ y: { type: 'integer' },
122
+ text: { type: 'string' },
123
+ keys: { type: 'string' },
124
+ focus: { type: 'string' },
125
+ direction: { type: 'string', enum: ['up', 'down'] },
126
+ amount: { type: 'integer' },
127
+ maxAmount: { type: 'integer' },
128
+ target: {
129
+ type: 'object',
130
+ properties: {
131
+ text: { type: 'string' },
132
+ edge: { type: 'string', enum: ['top', 'bottom'] },
133
+ },
134
+ },
135
+ ms: { type: 'integer' },
136
+ },
137
+ required: ['type'],
138
+ },
139
+ },
140
+ subgoal: {
141
+ type: 'string',
142
+ description: 'One short sentence naming what this batch is meant to achieve (e.g. "open the settings page"). Persisted with the result as a checkpoint, and echoed back if the loop needs to recover — declare it on every consequential batch.',
143
+ },
144
+ expect: {
145
+ type: 'object',
146
+ description: 'Optional expected outcome, verified by the runtime after the batch settles (polled up to withinMs, default 5000). ' +
147
+ 'Declare it for any consequential batch so you learn whether the intent succeeded, not merely that input was delivered. ' +
148
+ '"pending" in the result means not observed before the deadline — re-observe before assuming failure.',
149
+ properties: {
150
+ titleContains: {
151
+ type: 'string',
152
+ description: 'The active window title should contain this text.',
153
+ },
154
+ urlContains: {
155
+ type: 'string',
156
+ description: 'The browser URL should contain this text.',
157
+ },
158
+ regionChanged: {
159
+ type: 'object',
160
+ description: 'Pixels in this screen region should change relative to the start of the batch.',
161
+ properties: {
162
+ x: { type: 'integer' },
163
+ y: { type: 'integer' },
164
+ width: { type: 'integer' },
165
+ height: { type: 'integer' },
166
+ },
167
+ required: ['x', 'y', 'width', 'height'],
168
+ },
169
+ withinMs: { type: 'integer' },
170
+ },
171
+ },
172
+ }, ['actions']),
173
+ functionTool('request_takeover', 'Ask the user to take control of the virtual computer screen — for signing in, a CAPTCHA, 2FA, or any step you must not perform yourself. Waits until the user finishes and hands control back (or a timeout passes). While the user is driving, your computer tools are blocked.', {
174
+ reason: stringProperty('What you need the user to do on the screen, stated concretely.'),
175
+ }, ['reason']),
176
+ ];
85
177
  export async function workToolSchemasForTask(task) {
86
- return task.networkEnabled &&
178
+ const schemas = [...WORK_TOOL_SCHEMAS];
179
+ if (task.networkEnabled &&
87
180
  (await isWebSearchAvailable()) &&
88
- (await userCanUseWebSearch(await userModel.getUserById(task.userId)))
89
- ? [...WORK_TOOL_SCHEMAS, WORK_WEB_SEARCH_TOOL_SCHEMA]
90
- : WORK_TOOL_SCHEMAS;
181
+ (await userCanUseWebSearch(await userModel.getUserById(task.userId)))) {
182
+ schemas.push(WORK_WEB_SEARCH_TOOL_SCHEMA);
183
+ }
184
+ if (await workRuntimeService.computerToolsAvailable(task)) {
185
+ schemas.push(...WORK_COMPUTER_TOOL_SCHEMAS);
186
+ }
187
+ return schemas;
91
188
  }
92
189
  export class WorkAgentService {
93
190
  constructor() {
@@ -437,13 +534,31 @@ export class WorkAgentService {
437
534
  status: 'preparing',
438
535
  phase: 'preparing',
439
536
  }, 'transition:preparing', durableAttemptIdentity);
440
- for (const skill of WORK_AGENT_SKILLS) {
537
+ const computerAvailable = await workRuntimeService.computerToolsAvailable(task);
538
+ let taughtSkills = [];
539
+ if (computerAvailable) {
540
+ try {
541
+ taughtSkills =
542
+ await workComputerTeachService.taughtSkillsForUser(userId);
543
+ }
544
+ catch (error) {
545
+ logger.warn(`Could not load taught skills for run ${runId}:`, error);
546
+ }
547
+ }
548
+ for (const skill of workAgentSkillsForContext({ computerAvailable })) {
441
549
  await workEventService.publish(taskId, runId, 'skill_loaded', {
442
550
  id: skill.id,
443
551
  name: skill.title,
444
552
  description: skill.instructions[0],
445
553
  }, `skill:${skill.id}`, durableAttemptIdentity);
446
554
  }
555
+ for (const skill of taughtSkills) {
556
+ await workEventService.publish(taskId, runId, 'skill_loaded', {
557
+ id: `taught:${skill.slug}`,
558
+ name: skill.name,
559
+ description: 'Taught procedure demonstrated by the user.',
560
+ }, `skill:taught:${skill.slug}`, durableAttemptIdentity);
561
+ }
447
562
  releaseExecutionLease = await workRuntimeService.prepare(task, controller.signal);
448
563
  await this.throwIfCancelled(runId, controller);
449
564
  await workTaskService.updateRun(runId, 'running', { started: true });
@@ -456,7 +571,7 @@ export class WorkAgentService {
456
571
  };
457
572
  const providerRoutingFingerprint = await workModelProviderService.getRoutingFingerprint(run.model, providerSelection, userId);
458
573
  const providerStateScope = await workModelProviderService.getResponsesStateScope(run.model, providerSelection, userId);
459
- const messages = await this.contextMessages(task, roundLimit, providerStateScope, run);
574
+ const messages = await this.contextMessages(task, roundLimit, computerAvailable, taughtSkills, providerStateScope, run);
460
575
  let totalToolCalls = 0;
461
576
  let accumulatedInputTokens = 0;
462
577
  let accumulatedOutputTokens = 0;
@@ -464,13 +579,53 @@ export class WorkAgentService {
464
579
  let streamedReasoningTotal = '';
465
580
  let budgetReason = 'round';
466
581
  let emptyRoundNudges = 0;
582
+ // Computer-loop stall detection: an identical action against a screen
583
+ // whose pixels did not change is the signature of a grounding loop —
584
+ // a dead coordinate, a click that never lands. One recovery notice,
585
+ // then a handoff, instead of burning the remaining rounds.
586
+ let computerStallSignature = '';
587
+ let computerStallRepeats = 0;
588
+ let computerStallNudged = false;
589
+ // Ambiguity budget: consecutive unverified expectations mean the loop
590
+ // is acting on assumptions; one re-grounding notice before it
591
+ // compounds.
592
+ let consecutivePendingExpectations = 0;
593
+ let ambiguityNudged = false;
594
+ const computerContext = {};
595
+ const loopStats = {
596
+ rounds: 0,
597
+ toolCalls: 0,
598
+ screenshots: 0,
599
+ fences: 0,
600
+ expectationsPassed: 0,
601
+ expectationsPending: 0,
602
+ stallNudges: 0,
603
+ ambiguityNudges: 0,
604
+ };
467
605
  // Reasoning-channel models (DeepSeek and friends) sometimes place
468
606
  // their entire answer in reasoning and leave content empty. Keep the
469
607
  // last reasoning so a run can end with the model's actual findings
470
608
  // instead of a placeholder.
471
609
  let lastReasoningContent = '';
610
+ // Mid-run user messages join the model context at round boundaries,
611
+ // so the user can steer the agent without cancelling the run. Seed
612
+ // with what the restored context already contains so nothing repeats.
613
+ const injectedMidRunMessages = new Set((await workTaskService.getMessages(taskId))
614
+ .filter(message => message.metadata?.midRun === true)
615
+ .map(message => message.id));
472
616
  roundLoop: for (let round = 0; round < roundLimit; round++) {
473
617
  await this.throwIfCancelled(runId, controller);
618
+ loopStats.rounds = round + 1;
619
+ for (const message of await workTaskService.getMessages(taskId)) {
620
+ if (message.runId === runId &&
621
+ message.role === 'user' &&
622
+ message.kind === 'message' &&
623
+ message.metadata?.midRun === true &&
624
+ !injectedMidRunMessages.has(message.id)) {
625
+ injectedMidRunMessages.add(message.id);
626
+ messages.push({ role: 'user', content: message.content });
627
+ }
628
+ }
474
629
  await workEventService.publish(taskId, runId, 'run_state', {
475
630
  status: 'running',
476
631
  phase: 'thinking',
@@ -614,6 +769,7 @@ export class WorkAgentService {
614
769
  await workTaskService.updateTaskStatus(taskId, 'completed');
615
770
  await workEventService.publish(taskId, runId, 'done', {
616
771
  status: 'completed',
772
+ loopStats,
617
773
  }, 'terminal:completed');
618
774
  return;
619
775
  }
@@ -644,7 +800,10 @@ export class WorkAgentService {
644
800
  }
645
801
  for (const call of toolCalls) {
646
802
  await this.throwIfCancelled(runId, controller);
647
- const toolCallMetadata = summarizeToolCall(call);
803
+ const toolCallMetadata = {
804
+ ...summarizeToolCall(call),
805
+ round: round + 1,
806
+ };
648
807
  const toolCallMessage = await workTaskService.addMessage(taskId, runId, 'assistant', 'tool_call', `Calling ${call.function.name}`, toolCallMetadata);
649
808
  await workEventService.publish(taskId, runId, 'run_state', {
650
809
  status: 'running',
@@ -661,11 +820,13 @@ export class WorkAgentService {
661
820
  message: toolCallMessage,
662
821
  }, `message:${toolCallMessage.id}`);
663
822
  let toolOutput;
823
+ let toolImages;
664
824
  let toolMetadata = {
665
825
  name: call.function.name,
666
826
  toolCallId: call.id,
667
827
  toolName: call.function.name,
668
828
  };
829
+ const toolStartedAt = Date.now();
669
830
  try {
670
831
  const validationError = toolValidationErrors.get(call);
671
832
  if (validationError) {
@@ -674,8 +835,12 @@ export class WorkAgentService {
674
835
  if (hasInvalidToolArguments) {
675
836
  throw new WorkAgentHttpError('This tool call was not executed because another call in the same model response had incomplete or invalid arguments. Retry the batch with smaller payloads.', 400, 'WORK_TOOL_BATCH_NOT_EXECUTED');
676
837
  }
677
- const result = await this.executeTool(task, call);
838
+ const result = await this.executeTool(task, call, {
839
+ signal: controller.signal,
840
+ computer: computerContext,
841
+ });
678
842
  toolOutput = result.content;
843
+ toolImages = result.images;
679
844
  toolMetadata = { ...toolMetadata, ...result.metadata };
680
845
  }
681
846
  catch (error) {
@@ -685,6 +850,25 @@ export class WorkAgentService {
685
850
  : 'Tool error.';
686
851
  toolMetadata.error = true;
687
852
  }
853
+ toolMetadata.durationMs = Date.now() - toolStartedAt;
854
+ loopStats.toolCalls += 1;
855
+ if (toolMetadata.screenshot === true)
856
+ loopStats.screenshots += 1;
857
+ if (toolMetadata.fence)
858
+ loopStats.fences += 1;
859
+ const expectOutcome = toolMetadata.expect?.outcome;
860
+ if (expectOutcome === 'passed')
861
+ loopStats.expectationsPassed += 1;
862
+ if (expectOutcome === 'pending')
863
+ loopStats.expectationsPending += 1;
864
+ if (call.function.name === 'computer_act') {
865
+ if (expectOutcome === 'pending') {
866
+ consecutivePendingExpectations += 1;
867
+ }
868
+ else if (expectOutcome === 'passed') {
869
+ consecutivePendingExpectations = 0;
870
+ }
871
+ }
688
872
  const boundedOutput = boundPersistedToolOutput(toolOutput);
689
873
  if (boundedOutput !== toolOutput) {
690
874
  toolMetadata.outputTruncated = true;
@@ -704,6 +888,59 @@ export class WorkAgentService {
704
888
  content: toolOutput,
705
889
  tool_name: call.function.name,
706
890
  tool_call_id: call.id,
891
+ ...(toolImages?.length ? { images: toolImages } : {}),
892
+ });
893
+ // Screenshots are live-context-only: persisted messages keep the
894
+ // text observation, and only the most recent screenshots stay in
895
+ // model context so a long computer session cannot flood tokens.
896
+ retainRecentWorkImages(messages);
897
+ if ((call.function.name === 'computer_act' ||
898
+ call.function.name === 'computer_observe') &&
899
+ typeof toolMetadata.screenshotSha256 === 'string') {
900
+ let argsSignature = '';
901
+ try {
902
+ argsSignature = JSON.stringify(call.function.arguments ?? '').slice(0, 2000);
903
+ }
904
+ catch {
905
+ argsSignature = '';
906
+ }
907
+ const signature = [
908
+ toolMetadata.screenshotSha256,
909
+ call.function.name,
910
+ argsSignature,
911
+ ].join('|');
912
+ if (signature === computerStallSignature) {
913
+ computerStallRepeats += 1;
914
+ }
915
+ else {
916
+ computerStallSignature = signature;
917
+ computerStallRepeats = 0;
918
+ }
919
+ }
920
+ }
921
+ if (computerStallRepeats >= 2) {
922
+ computerStallRepeats = 0;
923
+ computerStallSignature = '';
924
+ if (!computerStallNudged) {
925
+ computerStallNudged = true;
926
+ loopStats.stallNudges += 1;
927
+ messages.push({
928
+ role: 'user',
929
+ content: buildWorkComputerStallPrompt(computerContext.lastSubgoal),
930
+ });
931
+ }
932
+ else {
933
+ budgetReason = 'computer-stall';
934
+ break roundLoop;
935
+ }
936
+ }
937
+ if (consecutivePendingExpectations >= 2 && !ambiguityNudged) {
938
+ ambiguityNudged = true;
939
+ consecutivePendingExpectations = 0;
940
+ loopStats.ambiguityNudges += 1;
941
+ messages.push({
942
+ role: 'user',
943
+ content: buildWorkComputerAmbiguityPrompt(computerContext.lastSubgoal),
707
944
  });
708
945
  }
709
946
  }
@@ -730,7 +967,9 @@ export class WorkAgentService {
730
967
  ...messages,
731
968
  {
732
969
  role: 'user',
733
- content: `${buildWorkBudgetExhaustionPrompt()}\nThe ${budgetReason} budget was reached.`,
970
+ content: `${buildWorkBudgetExhaustionPrompt()}\n${budgetReason === 'computer-stall'
971
+ ? 'The run was stopped because repeated computer actions kept producing an unchanged screen even after a recovery notice.'
972
+ : `The ${budgetReason} budget was reached.`}`,
734
973
  },
735
974
  ],
736
975
  tools: [],
@@ -789,6 +1028,7 @@ export class WorkAgentService {
789
1028
  await workTaskService.addMessage(taskId, runId, 'assistant', 'message', handoffContent, {
790
1029
  budgetHandoff: true,
791
1030
  budgetReason,
1031
+ loopStats,
792
1032
  ...handoffProviderStateMetadata,
793
1033
  });
794
1034
  await settleExecutionContainer();
@@ -800,6 +1040,7 @@ export class WorkAgentService {
800
1040
  await workEventService.publish(taskId, runId, 'done', {
801
1041
  status: 'needs_input',
802
1042
  budgetReason,
1043
+ loopStats,
803
1044
  }, 'terminal:needs_input');
804
1045
  }
805
1046
  catch (error) {
@@ -877,6 +1118,10 @@ export class WorkAgentService {
877
1118
  }
878
1119
  }
879
1120
  async cleanupExecutionContainer(task) {
1121
+ // A watched Work Computer screen outlives the run for the same reason a
1122
+ // preview does: someone is looking at it.
1123
+ if (workRuntimeService.screenSessionCount(task.id) > 0)
1124
+ return;
880
1125
  try {
881
1126
  // A preview is intentionally allowed to outlive the model run so the
882
1127
  // user can inspect it in the Work pane. Preserve only a verified ready
@@ -909,13 +1154,15 @@ export class WorkAgentService {
909
1154
  throw new WorkAgentHttpError('The provider routing changed while this Work run was active. Start a new run with the updated provider configuration.', 409, 'WORK_PROVIDER_ROUTING_CHANGED');
910
1155
  }
911
1156
  }
912
- async contextMessages(task, roundLimit, providerStateScope, provider = task) {
1157
+ async contextMessages(task, roundLimit, computerAvailable, taughtSkills, providerStateScope, provider = task) {
913
1158
  const persisted = restorePersistedWorkContext(await workTaskService.getRecentModelContextMessages(task.id, 30), provider, providerStateScope);
914
1159
  return [
915
1160
  {
916
1161
  role: 'system',
917
1162
  content: buildWorkAgentSystemPrompt({
918
1163
  networkEnabled: task.networkEnabled,
1164
+ computerAvailable,
1165
+ ...(taughtSkills.length > 0 ? { taughtSkills } : {}),
919
1166
  previewPort: workRuntimeService.previewPort,
920
1167
  roundBudget: roundLimit,
921
1168
  commandTimeoutMs: workRuntimeService.limits.commandTimeoutMs,
@@ -925,7 +1172,7 @@ export class WorkAgentService {
925
1172
  ...persisted,
926
1173
  ];
927
1174
  }
928
- async executeTool(task, call) {
1175
+ async executeTool(task, call, context = {}) {
929
1176
  const args = call.function.arguments;
930
1177
  switch (call.function.name) {
931
1178
  case 'list_files': {
@@ -1005,10 +1252,78 @@ export class WorkAgentService {
1005
1252
  onStopped: () => workTaskService.updatePreview(task.id, 'stopped'),
1006
1253
  });
1007
1254
  return { content: 'Preview stopped.' };
1255
+ case 'computer_observe': {
1256
+ await this.assertComputerToolsAvailable(task);
1257
+ await this.assertComputerNotHumanControlled(task);
1258
+ return computerToolResult(await workRuntimeService.computerObserve(task), undefined, context.computer);
1259
+ }
1260
+ case 'computer_act': {
1261
+ await this.assertComputerToolsAvailable(task);
1262
+ await this.assertComputerNotHumanControlled(task);
1263
+ const actions = Array.isArray(args.actions) ? args.actions : [];
1264
+ const subgoal = optionalString(args.subgoal)?.trim().slice(0, 300);
1265
+ const observation = await workRuntimeService.computerAct(task, actions, args.expect);
1266
+ const result = computerToolResult(observation, actions.length, context.computer);
1267
+ if (subgoal) {
1268
+ // The checkpoint rides the durable transcript: goal, actions, and
1269
+ // verified outcome persist together for resume and audit.
1270
+ result.metadata.subgoal = subgoal;
1271
+ result.content = `Subgoal: ${subgoal}\n${result.content}`;
1272
+ if (context.computer)
1273
+ context.computer.lastSubgoal = subgoal;
1274
+ }
1275
+ return result;
1276
+ }
1277
+ case 'request_takeover': {
1278
+ await this.assertComputerToolsAvailable(task);
1279
+ if ((await workPolicyService.resolve(task.policyId)).takeoverEnabled ===
1280
+ false) {
1281
+ return {
1282
+ content: "This task's Work policy does not allow human takeover of the screen. No one can be handed control; state the exact blocker in your response so the user can address it another way.",
1283
+ metadata: { outcome: 'disabled' },
1284
+ };
1285
+ }
1286
+ const reason = requiredString(args.reason, 'reason').slice(0, 1000);
1287
+ const outcome = await workScreenControlService.waitForAssist(task.id, reason, {
1288
+ timeoutMs: WORK_SCREEN_ASSIST_TIMEOUT_MS,
1289
+ signal: context.signal,
1290
+ });
1291
+ if (outcome === 'released') {
1292
+ return {
1293
+ content: 'The user took control of the computer and has handed it back. Use computer_observe to see the current screen before continuing.',
1294
+ metadata: { outcome },
1295
+ };
1296
+ }
1297
+ if (outcome === 'still_controlled') {
1298
+ return {
1299
+ content: 'The user is still controlling the computer. Your computer tools stay blocked until they finish; continue with other work or wait.',
1300
+ metadata: { outcome },
1301
+ };
1302
+ }
1303
+ return {
1304
+ content: 'No one took over the screen within the waiting period. State the exact blocker in your response so the user can handle it later.',
1305
+ metadata: { outcome },
1306
+ };
1307
+ }
1008
1308
  default:
1009
1309
  throw new WorkAgentHttpError(`Unknown tool: ${call.function.name}`, 400, 'WORK_UNKNOWN_TOOL');
1010
1310
  }
1011
1311
  }
1312
+ async assertComputerToolsAvailable(task) {
1313
+ if (!(await workRuntimeService.computerToolsAvailable(task))) {
1314
+ throw new WorkAgentHttpError('The Work Computer is not available for this task.', 403, 'WORK_COMPUTER_UNAVAILABLE');
1315
+ }
1316
+ }
1317
+ /**
1318
+ * While a human holds the control lease, the agent's eyes and hands are
1319
+ * both off: acting would fight the user's input, and observing could
1320
+ * capture usernames or one-time codes typed during a sign-in.
1321
+ */
1322
+ async assertComputerNotHumanControlled(task) {
1323
+ if (await workScreenControlService.current(task.id)) {
1324
+ throw new WorkAgentHttpError('A user is controlling the Work Computer right now. Wait for them to finish (request_takeover reports when control is handed back).', 409, 'WORK_COMPUTER_HUMAN_CONTROL');
1325
+ }
1326
+ }
1012
1327
  async throwIfCancelled(runId, controller) {
1013
1328
  if (controller.signal.aborted ||
1014
1329
  (await workTaskService.getRun(runId))?.status === 'cancelled') {
@@ -1510,7 +1825,12 @@ function validateToolCallArguments(call) {
1510
1825
  case 'list_files':
1511
1826
  case 'start_preview':
1512
1827
  case 'stop_preview':
1828
+ case 'computer_observe':
1513
1829
  return undefined;
1830
+ case 'computer_act':
1831
+ return Array.isArray(args.actions) && args.actions.length > 0
1832
+ ? undefined
1833
+ : `The model returned invalid arguments for computer_act: "actions" must be a non-empty array of action objects.`;
1514
1834
  case 'read_file':
1515
1835
  case 'delete_file':
1516
1836
  return invalidRequiredString('path');
@@ -1521,6 +1841,8 @@ function validateToolCallArguments(call) {
1521
1841
  case 'search_files':
1522
1842
  case 'web_search':
1523
1843
  return invalidRequiredString('query');
1844
+ case 'request_takeover':
1845
+ return invalidRequiredString('reason');
1524
1846
  case 'run_command':
1525
1847
  return invalidRequiredString('command');
1526
1848
  default:
@@ -1579,11 +1901,179 @@ function summarizeToolCall(call) {
1579
1901
  case 'start_preview':
1580
1902
  includeString('command', args.command, 2000);
1581
1903
  break;
1904
+ case 'request_takeover':
1905
+ includeString('reason', args.reason, 500);
1906
+ break;
1907
+ case 'computer_act':
1908
+ if (Array.isArray(args.actions)) {
1909
+ metadata.actionCount = args.actions.length;
1910
+ includeString('actions', args.actions
1911
+ .map(action => action && typeof action === 'object'
1912
+ ? String(action.type ?? '?')
1913
+ : '?')
1914
+ .join(','), 500);
1915
+ }
1916
+ break;
1582
1917
  default:
1583
1918
  break;
1584
1919
  }
1585
1920
  return metadata;
1586
1921
  }
1922
+ /**
1923
+ * Screenshots the live model context keeps (rakazo's trick): the newest
1924
+ * screenshot-bearing tool results retain their images, everything older
1925
+ * drops back to its text observation. Bounds tokens and sidesteps most of
1926
+ * the cross-run screenshot persistence problem — a resumed run re-observes.
1927
+ */
1928
+ const WORK_LIVE_SCREENSHOT_MESSAGE_LIMIT = 2;
1929
+ function retainRecentWorkImages(messages, limit = WORK_LIVE_SCREENSHOT_MESSAGE_LIMIT) {
1930
+ let retained = 0;
1931
+ for (let index = messages.length - 1; index >= 0; index--) {
1932
+ const message = messages[index];
1933
+ if (!message.images?.length)
1934
+ continue;
1935
+ retained += 1;
1936
+ if (retained > limit)
1937
+ delete message.images;
1938
+ }
1939
+ }
1940
+ function computerObservationDiff(observation, computerContext) {
1941
+ const previous = computerContext?.previous;
1942
+ if (computerContext) {
1943
+ computerContext.previous = {
1944
+ ...(observation.screenshotSha256
1945
+ ? { screenshotSha256: observation.screenshotSha256 }
1946
+ : {}),
1947
+ window: observation.window,
1948
+ ...(observation.url ? { url: observation.url } : {}),
1949
+ ...(observation.pageFocus !== undefined
1950
+ ? { pageFocus: observation.pageFocus }
1951
+ : {}),
1952
+ };
1953
+ }
1954
+ if (!previous?.screenshotSha256 || !observation.screenshotSha256)
1955
+ return [];
1956
+ if (previous.screenshotSha256 === observation.screenshotSha256) {
1957
+ return [
1958
+ 'Since the previous observation: the screen is IDENTICAL — nothing changed visually.',
1959
+ ];
1960
+ }
1961
+ const transitions = [
1962
+ ...(previous.window !== observation.window
1963
+ ? [`window "${previous.window}" → "${observation.window}"`]
1964
+ : []),
1965
+ ...(previous.url !== observation.url
1966
+ ? [`URL ${previous.url || '(none)'} → ${observation.url || '(none)'}`]
1967
+ : []),
1968
+ ...(previous.pageFocus !== observation.pageFocus
1969
+ ? [`page focus ${previous.pageFocus} → ${observation.pageFocus}`]
1970
+ : []),
1971
+ ];
1972
+ return [
1973
+ `Since the previous observation: the screen changed${transitions.length ? ` (${transitions.join('; ')})` : ' (same window, title, and URL)'}.`,
1974
+ ];
1975
+ }
1976
+ function computerToolResult(observation, actionCount, computerContext) {
1977
+ const ranCount = observation.fence
1978
+ ? observation.fence.afterAction
1979
+ : actionCount;
1980
+ const browserChrome = observation.pageFocus === false && /chromium/i.test(observation.window);
1981
+ const diffLines = computerObservationDiff(observation, computerContext);
1982
+ const receiptLines = [
1983
+ ...(observation.clickReceipts?.length
1984
+ ? [
1985
+ `Click receipts: ${observation.clickReceipts
1986
+ .map(receipt => `#${receipt.action} (${receipt.x},${receipt.y}) ${receipt.changed ? 'changed nearby pixels' : 'NO visible change nearby'}`)
1987
+ .join('; ')}.`,
1988
+ ]
1989
+ : []),
1990
+ ...(observation.scrollReceipts?.map(receipt => receipt.found === null
1991
+ ? `Scroll receipt: action #${receipt.action} scrolled ${receipt.scrolledUnits} unit(s) — no semantic signal, target visibility unknown.`
1992
+ : receipt.found && receipt.visible
1993
+ ? `Scroll receipt: action #${receipt.action} scrolled ${receipt.scrolledUnits} unit(s) — target is now visible.`
1994
+ : receipt.found
1995
+ ? `Scroll receipt: action #${receipt.action} scrolled ${receipt.scrolledUnits} unit(s) — target found but still not fully visible.`
1996
+ : `Scroll receipt: action #${receipt.action} scrolled ${receipt.scrolledUnits} unit(s) — target NOT found in the scrolled range.`) ?? []),
1997
+ ];
1998
+ const summary = [
1999
+ ...(observation.fence
2000
+ ? [`BATCH STOPPED EARLY: ${observation.fence.detail}`]
2001
+ : []),
2002
+ ...(actionCount !== undefined
2003
+ ? [
2004
+ `Applied ${ranCount} of ${actionCount} action${actionCount === 1 ? '' : 's'} and captured the settled screen.`,
2005
+ ]
2006
+ : []),
2007
+ `Screen ${observation.width}x${observation.height}, cursor at ${observation.cursorX},${observation.cursorY}.`,
2008
+ observation.window
2009
+ ? `Active window: ${observation.window}` +
2010
+ (observation.windowId !== undefined
2011
+ ? ` (#${observation.windowId}${observation.windowCount !== undefined ? `, ${observation.windowCount} visible` : ''})`
2012
+ : '')
2013
+ : 'No active window.',
2014
+ ...(observation.url ? [`Page URL: ${observation.url}`] : []),
2015
+ ...(browserChrome
2016
+ ? [
2017
+ 'Keyboard focus: the browser UI, NOT the page — typed text would go to the omnibox or a browser dialog.',
2018
+ ]
2019
+ : observation.focusedElement
2020
+ ? [`Focused element: ${observation.focusedElement}`]
2021
+ : []),
2022
+ ...(observation.expect
2023
+ ? [
2024
+ observation.expect.outcome === 'passed'
2025
+ ? 'Declared expectation: PASSED.'
2026
+ : `Declared expectation: NOT yet observed (pending${observation.expect.unmet?.length ? `: ${observation.expect.unmet.join(', ')}` : ''}) — re-observe before assuming failure.`,
2027
+ ]
2028
+ : []),
2029
+ ...receiptLines,
2030
+ ...diffLines,
2031
+ ...(observation.screenshotSha256
2032
+ ? [`Screenshot sha256: ${observation.screenshotSha256.slice(0, 16)}`]
2033
+ : []),
2034
+ 'The screenshot accompanies this result.',
2035
+ ].join('\n');
2036
+ return {
2037
+ content: summary,
2038
+ metadata: {
2039
+ screenshot: true,
2040
+ width: observation.width,
2041
+ height: observation.height,
2042
+ cursorX: observation.cursorX,
2043
+ cursorY: observation.cursorY,
2044
+ window: observation.window.slice(0, 300),
2045
+ ...(observation.windowId !== undefined
2046
+ ? { windowId: observation.windowId }
2047
+ : {}),
2048
+ ...(observation.windowCount !== undefined
2049
+ ? { windowCount: observation.windowCount }
2050
+ : {}),
2051
+ ...(observation.url ? { url: observation.url } : {}),
2052
+ ...(observation.pageFocus !== undefined
2053
+ ? { pageFocus: observation.pageFocus }
2054
+ : {}),
2055
+ ...(observation.focusedElement
2056
+ ? { focusedElement: observation.focusedElement }
2057
+ : {}),
2058
+ ...(observation.screenshotSha256
2059
+ ? { screenshotSha256: observation.screenshotSha256 }
2060
+ : {}),
2061
+ ...(observation.fence ? { fence: observation.fence } : {}),
2062
+ ...(observation.expect ? { expect: observation.expect } : {}),
2063
+ ...(observation.clickReceipts?.length
2064
+ ? { clickReceipts: observation.clickReceipts }
2065
+ : {}),
2066
+ ...(observation.scrollReceipts?.length
2067
+ ? { scrollReceipts: observation.scrollReceipts }
2068
+ : {}),
2069
+ ...(actionCount !== undefined ? { actionCount } : {}),
2070
+ ...(ranCount !== undefined && ranCount !== actionCount
2071
+ ? { ranCount }
2072
+ : {}),
2073
+ },
2074
+ images: [observation.screenshotBase64],
2075
+ };
2076
+ }
1587
2077
  function boundPersistedToolOutput(value) {
1588
2078
  const maxLength = 20000;
1589
2079
  if (value.length <= maxLength)