libre-webui 0.27.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.
- package/README.md +57 -29
- package/backend/dist/persistence/postgresAutomationWorkTargetMigration.d.ts +9 -0
- package/backend/dist/persistence/postgresAutomationWorkTargetMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresAutomationWorkTargetMigration.js +44 -0
- package/backend/dist/persistence/postgresAutomationWorkTargetMigration.js.map +1 -0
- package/backend/dist/persistence/postgresMfaPushRecoveryMigration.d.ts.map +1 -1
- package/backend/dist/persistence/postgresMfaPushRecoveryMigration.js +4 -4
- package/backend/dist/persistence/postgresMfaPushRecoveryMigration.js.map +1 -1
- package/backend/dist/persistence/postgresMigrationRegistry.d.ts.map +1 -1
- package/backend/dist/persistence/postgresMigrationRegistry.js +6 -0
- package/backend/dist/persistence/postgresMigrationRegistry.js.map +1 -1
- package/backend/dist/persistence/postgresResourceRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/postgresResourceRepositories.js +17 -4
- package/backend/dist/persistence/postgresResourceRepositories.js.map +1 -1
- package/backend/dist/persistence/postgresSecurityRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/postgresSecurityRepositories.js +4 -0
- package/backend/dist/persistence/postgresSecurityRepositories.js.map +1 -1
- package/backend/dist/persistence/postgresWorkComputerMigration.d.ts +8 -0
- package/backend/dist/persistence/postgresWorkComputerMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresWorkComputerMigration.js +37 -0
- package/backend/dist/persistence/postgresWorkComputerMigration.js.map +1 -0
- package/backend/dist/persistence/postgresWorkTakeoverMigration.d.ts +9 -0
- package/backend/dist/persistence/postgresWorkTakeoverMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresWorkTakeoverMigration.js +38 -0
- package/backend/dist/persistence/postgresWorkTakeoverMigration.js.map +1 -0
- package/backend/dist/persistence/resourceTypes.d.ts +5 -0
- package/backend/dist/persistence/resourceTypes.d.ts.map +1 -1
- package/backend/dist/persistence/resourceTypes.js.map +1 -1
- package/backend/dist/persistence/securityTypes.d.ts +1 -0
- package/backend/dist/persistence/securityTypes.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteMigrations.d.ts +3 -0
- package/backend/dist/persistence/sqliteMigrations.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteMigrations.js +73 -0
- package/backend/dist/persistence/sqliteMigrations.js.map +1 -1
- package/backend/dist/persistence/sqliteResourceRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteResourceRepositories.js +17 -7
- package/backend/dist/persistence/sqliteResourceRepositories.js.map +1 -1
- package/backend/dist/persistence/sqliteSecurityRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteSecurityRepositories.js +6 -0
- package/backend/dist/persistence/sqliteSecurityRepositories.js.map +1 -1
- package/backend/dist/platform/jobs/domainJobHandlers.d.ts.map +1 -1
- package/backend/dist/platform/jobs/domainJobHandlers.js +87 -0
- package/backend/dist/platform/jobs/domainJobHandlers.js.map +1 -1
- package/backend/dist/platform/workPersistence/postgresWorkPersistence.d.ts.map +1 -1
- package/backend/dist/platform/workPersistence/postgresWorkPersistence.js +7 -3
- package/backend/dist/platform/workPersistence/postgresWorkPersistence.js.map +1 -1
- package/backend/dist/platform/workPersistence/sqliteWorkPersistence.d.ts.map +1 -1
- package/backend/dist/platform/workPersistence/sqliteWorkPersistence.js +8 -4
- package/backend/dist/platform/workPersistence/sqliteWorkPersistence.js.map +1 -1
- package/backend/dist/platform/workPersistence/types.d.ts +2 -0
- package/backend/dist/platform/workPersistence/types.d.ts.map +1 -1
- package/backend/dist/platform/workPersistence/types.js.map +1 -1
- package/backend/dist/routes/auth.d.ts.map +1 -1
- package/backend/dist/routes/auth.js +8 -4
- package/backend/dist/routes/auth.js.map +1 -1
- package/backend/dist/routes/automations.d.ts.map +1 -1
- package/backend/dist/routes/automations.js +5 -0
- package/backend/dist/routes/automations.js.map +1 -1
- package/backend/dist/routes/work.d.ts.map +1 -1
- package/backend/dist/routes/work.js +205 -1
- package/backend/dist/routes/work.js.map +1 -1
- package/backend/dist/services/authService.d.ts +2 -0
- package/backend/dist/services/authService.d.ts.map +1 -1
- package/backend/dist/services/authService.js +20 -0
- package/backend/dist/services/authService.js.map +1 -1
- package/backend/dist/services/automationSchedulerService.d.ts.map +1 -1
- package/backend/dist/services/automationSchedulerService.js +24 -0
- package/backend/dist/services/automationSchedulerService.js.map +1 -1
- package/backend/dist/services/automationService.d.ts +4 -1
- package/backend/dist/services/automationService.d.ts.map +1 -1
- package/backend/dist/services/automationService.js +27 -0
- package/backend/dist/services/automationService.js.map +1 -1
- package/backend/dist/services/mfaService.d.ts.map +1 -1
- package/backend/dist/services/mfaService.js +2 -3
- package/backend/dist/services/mfaService.js.map +1 -1
- package/backend/dist/services/webauthnService.d.ts.map +1 -1
- package/backend/dist/services/webauthnService.js +1 -0
- package/backend/dist/services/webauthnService.js.map +1 -1
- package/backend/dist/services/websocketTicketService.d.ts +1 -1
- package/backend/dist/services/websocketTicketService.d.ts.map +1 -1
- package/backend/dist/services/websocketTicketService.js +3 -1
- package/backend/dist/services/websocketTicketService.js.map +1 -1
- package/backend/dist/services/workAgentGuidance.d.ts +14 -0
- package/backend/dist/services/workAgentGuidance.d.ts.map +1 -1
- package/backend/dist/services/workAgentGuidance.js +38 -2
- package/backend/dist/services/workAgentGuidance.js.map +1 -1
- package/backend/dist/services/workAgentService.d.ts +9 -0
- package/backend/dist/services/workAgentService.d.ts.map +1 -1
- package/backend/dist/services/workAgentService.js +503 -13
- package/backend/dist/services/workAgentService.js.map +1 -1
- package/backend/dist/services/workComputerSetupService.d.ts +33 -0
- package/backend/dist/services/workComputerSetupService.d.ts.map +1 -0
- package/backend/dist/services/workComputerSetupService.js +164 -0
- package/backend/dist/services/workComputerSetupService.js.map +1 -0
- package/backend/dist/services/workComputerTeachService.d.ts +105 -0
- package/backend/dist/services/workComputerTeachService.d.ts.map +1 -0
- package/backend/dist/services/workComputerTeachService.js +513 -0
- package/backend/dist/services/workComputerTeachService.js.map +1 -0
- package/backend/dist/services/workKubernetesDriver.d.ts +8 -0
- package/backend/dist/services/workKubernetesDriver.d.ts.map +1 -1
- package/backend/dist/services/workKubernetesDriver.js +22 -0
- package/backend/dist/services/workKubernetesDriver.js.map +1 -1
- package/backend/dist/services/workModelProviderService.d.ts.map +1 -1
- package/backend/dist/services/workModelProviderService.js +102 -8
- package/backend/dist/services/workModelProviderService.js.map +1 -1
- package/backend/dist/services/workPolicyService.d.ts +8 -0
- package/backend/dist/services/workPolicyService.d.ts.map +1 -1
- package/backend/dist/services/workPolicyService.js +25 -0
- package/backend/dist/services/workPolicyService.js.map +1 -1
- package/backend/dist/services/workRuntimeDriver.d.ts +32 -0
- package/backend/dist/services/workRuntimeDriver.d.ts.map +1 -1
- package/backend/dist/services/workRuntimeDriver.js +54 -8
- package/backend/dist/services/workRuntimeDriver.js.map +1 -1
- package/backend/dist/services/workRuntimeService.d.ts +212 -1
- package/backend/dist/services/workRuntimeService.d.ts.map +1 -1
- package/backend/dist/services/workRuntimeService.js +1184 -9
- package/backend/dist/services/workRuntimeService.js.map +1 -1
- package/backend/dist/services/workRuntimeShared.d.ts +11 -1
- package/backend/dist/services/workRuntimeShared.d.ts.map +1 -1
- package/backend/dist/services/workRuntimeShared.js +24 -1
- package/backend/dist/services/workRuntimeShared.js.map +1 -1
- package/backend/dist/services/workScreenControlService.d.ts +60 -0
- package/backend/dist/services/workScreenControlService.d.ts.map +1 -0
- package/backend/dist/services/workScreenControlService.js +201 -0
- package/backend/dist/services/workScreenControlService.js.map +1 -0
- package/backend/dist/services/workTaskService.d.ts +11 -0
- package/backend/dist/services/workTaskService.d.ts.map +1 -1
- package/backend/dist/services/workTaskService.js +31 -0
- package/backend/dist/services/workTaskService.js.map +1 -1
- package/backend/dist/types/index.d.ts +7 -0
- package/backend/dist/types/index.d.ts.map +1 -1
- package/backend/dist/types/index.js.map +1 -1
- package/backend/dist/types/work.d.ts +13 -0
- package/backend/dist/types/work.d.ts.map +1 -1
- package/backend/dist/utils/webauthn.d.ts +6 -2
- package/backend/dist/utils/webauthn.d.ts.map +1 -1
- package/backend/dist/utils/webauthn.js +32 -3
- package/backend/dist/utils/webauthn.js.map +1 -1
- package/backend/dist/websocketServer.d.ts.map +1 -1
- package/backend/dist/websocketServer.js +9 -0
- package/backend/dist/websocketServer.js.map +1 -1
- package/backend/dist/workScreenServer.d.ts +12 -0
- package/backend/dist/workScreenServer.d.ts.map +1 -0
- package/backend/dist/workScreenServer.js +291 -0
- package/backend/dist/workScreenServer.js.map +1 -0
- package/backend/package.json +1 -1
- package/deploy/work-computer/Dockerfile +48 -0
- package/deploy/work-computer/README.md +93 -0
- package/deploy/work-computer/fonts/fonts.css +335 -0
- package/deploy/work-computer/fonts/v16-aFTR7PB1QTsUX8KYvumzEY2tbYf-Vlh3uA.woff2 +0 -0
- package/deploy/work-computer/fonts/v16-aFTR7PB1QTsUX8KYvumzEYOtbYf-Vlg.woff2 +0 -0
- package/deploy/work-computer/fonts/v16-aFTU7PB1QTsUX8KYthSQBK6PYK3EXw.woff2 +0 -0
- package/deploy/work-computer/fonts/v16-aFTU7PB1QTsUX8KYthqQBK6PYK0.woff2 +0 -0
- package/deploy/work-computer/fonts/v20-xn7gYHE41ni1AdIRggOxSvfedN62Zw.woff2 +0 -0
- package/deploy/work-computer/fonts/v20-xn7gYHE41ni1AdIRggSxSvfedN62Zw.woff2 +0 -0
- package/deploy/work-computer/fonts/v20-xn7gYHE41ni1AdIRggexSvfedN4.woff2 +0 -0
- package/deploy/work-computer/fonts/v20-xn7gYHE41ni1AdIRggixSvfedN62Zw.woff2 +0 -0
- package/deploy/work-computer/fonts/v20-xn7gYHE41ni1AdIRggmxSvfedN62Zw.woff2 +0 -0
- package/deploy/work-computer/fonts/v20-xn7gYHE41ni1AdIRggqxSvfedN62Zw.woff2 +0 -0
- package/deploy/work-computer/fonts/v22-V8mDoQDjQSkFtoMM3T6r8E7mPb54C_k3HqUtEw.woff2 +0 -0
- package/deploy/work-computer/fonts/v22-V8mDoQDjQSkFtoMM3T6r8E7mPb94C_k3HqUtEw.woff2 +0 -0
- package/deploy/work-computer/fonts/v22-V8mDoQDjQSkFtoMM3T6r8E7mPbF4C_k3HqU.woff2 +0 -0
- package/deploy/work-computer/start-computer.sh +155 -0
- package/deploy/work-computer/start-page.html +251 -0
- package/deploy/work-computer/tint2rc +76 -0
- package/deploy/work-computer/wallpaper.png +0 -0
- package/frontend/dist/artifact-runtime/.build-fingerprint +1 -1
- package/frontend/dist/assets/index-DGy6mo0B.js +33 -0
- package/frontend/dist/assets/index-DqLJKWF-.css +1 -0
- package/frontend/dist/index.html +31 -16
- package/frontend/dist/js/{ArtifactContainer-C9zNtCay.js → ArtifactContainer-DvIMxleC.js} +1 -1
- package/frontend/dist/js/{ArtifactDemoPage-C-EBFBCt.js → ArtifactDemoPage-BqYskFqc.js} +1 -1
- package/frontend/dist/js/{ArtifactSandboxFrame-CTA1j_d1.js → ArtifactSandboxFrame-9wuufHn6.js} +1 -1
- package/frontend/dist/js/ArtifactSlideOutPanel-DXESMtPh.js +1 -0
- package/frontend/dist/js/AutomationsPage-DByxhIKj.js +1 -0
- package/frontend/dist/js/AvatarUpload-DnCGQ46o.js +1 -0
- package/frontend/dist/js/{Button-C0r3J9G_.js → Button-CpobxJ2P.js} +1 -1
- package/frontend/dist/js/CalendarPage-JywVP-9y.js +1 -0
- package/frontend/dist/js/ChannelsPage-B31rcARd.js +1 -0
- package/frontend/dist/js/ChatPage-CHXJY8m3.js +14 -0
- package/frontend/dist/js/{EvaluationsPage-FWIJu2fI.js → EvaluationsPage-CcmB5wdF.js} +1 -1
- package/frontend/dist/js/FirstTimeSetup-DDU7SowM.js +1 -0
- package/frontend/dist/js/GalleryPage-XfX_7iCf.js +1 -0
- package/frontend/dist/js/HomePage-DAZAkoAV.js +1 -0
- package/frontend/dist/js/LibreClawPage-De0gZmbF.js +1 -0
- package/frontend/dist/js/Logo-yKM43xGa.js +1 -0
- package/frontend/dist/js/LogoMark-PmpKQOre.js +1 -0
- package/frontend/dist/js/{ModelManagerModals-BY5TK38_.js → ModelManagerModals-hz2cZZ4a.js} +1 -1
- package/frontend/dist/js/ModelSelector-BoFehQy-.js +1 -0
- package/frontend/dist/js/NotesPage-BGPI3gG6.js +3 -0
- package/frontend/dist/js/OptimizedSyntaxHighlighter-DrejdNUm.js +1 -0
- package/frontend/dist/js/OptimizedSyntaxHighlighter-hNqZPLiB.js +1 -0
- package/frontend/dist/js/PasswordStrengthMeter-RwModpZ1.js +1 -0
- package/frontend/dist/js/PersonasPage-BkNRD2wO.js +13 -0
- package/frontend/dist/js/PluginUsagePage-Cqt7V2SQ.js +1 -0
- package/frontend/dist/js/RichMessageContent-B2QSyX-l.js +1 -0
- package/frontend/dist/js/RichMessageContent-CYEhT6uU.js +3 -0
- package/frontend/dist/js/SettingsModal-IbIvVyss.js +19 -0
- package/frontend/dist/js/{SettingsToggle-BIVyfGjl.js → SettingsToggle-3GxA96E0.js} +1 -1
- package/frontend/dist/js/ShareDialog-BoE4OVhV.js +1 -0
- package/frontend/dist/js/{SystemPage-BT07dTMT.js → SystemPage-B5ecz5H3.js} +1 -1
- package/frontend/dist/js/TurnstileWidget-DHU9IUM1.js +1 -0
- package/frontend/dist/js/UserManagementPage-C0VUfbMk.js +1 -0
- package/frontend/dist/js/WorkPage-EweYI5Dx.js +74 -0
- package/frontend/dist/js/api-Bbh2AHHN.js +1 -0
- package/frontend/dist/js/api-F0CnlVH8.js +14 -0
- package/frontend/dist/js/appNavigation-DqV69ohR.js +1 -0
- package/frontend/dist/js/appStore-SLLBamo2.js +2 -0
- package/frontend/dist/js/appVersion-46bF9UkF.js +1 -0
- package/frontend/dist/js/ar-C4yo7x5J.js +2 -0
- package/frontend/dist/js/authStore-CcfL8T6T.js +1 -0
- package/frontend/dist/js/authStore-DlSQ1TcP.js +4 -0
- package/frontend/dist/js/axios-_ZpUYd3e.js +1 -0
- package/frontend/dist/js/bn-BeZ6d2qF.js +2 -0
- package/frontend/dist/js/chatStore-DI79tEJ-.js +3 -0
- package/frontend/dist/js/classPrivateMethodInitSpec-B31Q4d2M.js +1 -0
- package/frontend/dist/js/{config-BqZ5304Q.js → config-0QgGZCrI.js} +1 -1
- package/frontend/dist/js/cs-9K5LILI5.js +2 -0
- package/frontend/dist/js/da-BWbBhLK8.js +2 -0
- package/frontend/dist/js/de-BXD9t2uY.js +2 -0
- package/frontend/dist/js/es-Dv4wZKlU.js +2 -0
- package/frontend/dist/js/{estree-DPkpBuOU.js → estree-WDwviHUZ.js} +1 -1
- package/frontend/dist/js/evaluationsApi-wjf0nh5J.js +1 -0
- package/frontend/dist/js/fr-BkXGyEei.js +2 -0
- package/frontend/dist/js/hi-BS1ldbWk.js +2 -0
- package/frontend/dist/js/{html-BpGUXG8E.js → html-Cj0HNcRt.js} +1 -1
- package/frontend/dist/js/id-Cr1njx5M.js +2 -0
- package/frontend/dist/js/is-BqpNCtDd.js +2 -0
- package/frontend/dist/js/{it-AxEMG8Jg.js → it-Dp-iwH27.js} +1 -1
- package/frontend/dist/js/ja-_e4v_6Mn.js +2 -0
- package/frontend/dist/js/ko-CjXf2pIX.js +2 -0
- package/frontend/dist/js/{logger-DSXjF4ye.js → logger-CpCQz3Vi.js} +1 -1
- package/frontend/dist/js/{markdown-DCBRWJSK.js → markdown-DTahdtRF.js} +1 -1
- package/frontend/dist/js/{messageCodeStyles-BQwf7sSz.js → messageCodeStyles-COJFoq4P.js} +2 -2
- package/frontend/dist/js/{ms-BhEwb4b7.js → ms-xpn-zACj.js} +1 -1
- package/frontend/dist/js/nl-Baf0a2X0.js +2 -0
- package/frontend/dist/js/pl-BEOTrcUv.js +2 -0
- package/frontend/dist/js/pt-B5FUaihM.js +2 -0
- package/frontend/dist/js/rfb-bJaPz33m.js +5 -0
- package/frontend/dist/js/ru-BjzgMmkJ.js +2 -0
- package/frontend/dist/js/{standalone-BFrK0R2A.js → standalone-C04jT26m.js} +1 -1
- package/frontend/dist/js/sv-DDX_gP1z.js +2 -0
- package/frontend/dist/js/teamEventStream-CgUkgjFC.js +8 -0
- package/frontend/dist/js/{th-DVnW2yyh.js → th-BXr3KovE.js} +1 -1
- package/frontend/dist/js/tr-CdqxHEM8.js +2 -0
- package/frontend/dist/js/ttsBatching-BfhNT3c5.js +2 -0
- package/frontend/dist/js/{typescript-DYVyvmm_.js → typescript-FtGI7Y5P.js} +1 -1
- package/frontend/dist/js/ui-DFa2W4QC.js +2 -0
- package/frontend/dist/js/ui-vendor-CRZXLK0Q.js +177 -0
- package/frontend/dist/js/{uk-BNPKIZga.js → uk-CotKDP2Q.js} +1 -1
- package/frontend/dist/js/useChat-BawGm7ua.js +10 -0
- package/frontend/dist/js/useQuery-BboI5V5G.js +1 -0
- package/frontend/dist/js/{useTranslation-5b5UhAc9.js → useTranslation-D7uLySn1.js} +1 -1
- package/frontend/dist/js/{utils-BGrdX9ie.js → utils-sD-qFh8D.js} +1 -1
- package/frontend/dist/js/utils-vendor-DTLgmsuc.js +9 -0
- package/frontend/dist/js/vi-F2NC1HaJ.js +2 -0
- package/frontend/dist/js/webauthnClient-CPSq528Q.js +1 -0
- package/frontend/dist/js/workApi-8mBuh_-J.js +6 -0
- package/frontend/dist/js/workApi-DJtLOslf.js +1 -0
- package/frontend/dist/js/workStatus-CcO9Fg41.js +1 -0
- package/frontend/dist/js/zh-DZ-kpEEA.js +2 -0
- package/package.json +4 -2
- package/frontend/dist/assets/index-DBAf_VaS.js +0 -47
- package/frontend/dist/assets/index-kn_Axjts.css +0 -1
- package/frontend/dist/js/ArtifactSlideOutPanel-BOENbOiv.js +0 -1
- package/frontend/dist/js/AutomationsPage-C58MQ4te.js +0 -1
- package/frontend/dist/js/CalendarPage-BcdAZ292.js +0 -1
- package/frontend/dist/js/ChannelsPage-DPr9Yajs.js +0 -1
- package/frontend/dist/js/ChatPage-DsbB748s.js +0 -14
- package/frontend/dist/js/FirstTimeSetup-Cy-w6xko.js +0 -1
- package/frontend/dist/js/GalleryPage-DDAtxM-x.js +0 -1
- package/frontend/dist/js/HomePage-DLKwmE7n.js +0 -1
- package/frontend/dist/js/LibreClawPage-vKOj-9w9.js +0 -1
- package/frontend/dist/js/ModelSelector-CoCS6MZ9.js +0 -1
- package/frontend/dist/js/NotesPage-BElIpXgA.js +0 -3
- package/frontend/dist/js/OptimizedSyntaxHighlighter-xgyE3dRA.js +0 -1
- package/frontend/dist/js/PersonasPage-P40jVbfU.js +0 -13
- package/frontend/dist/js/PluginUsagePage-CKUEPCJN.js +0 -1
- package/frontend/dist/js/RichMessageContent-Bm2WaEGi.js +0 -3
- package/frontend/dist/js/SettingsModal-DxSGzx9A.js +0 -19
- package/frontend/dist/js/UserManagementPage-B9Vz5cm3.js +0 -1
- package/frontend/dist/js/WorkPage-qoN_e_jK.js +0 -6
- package/frontend/dist/js/api-D7Ff5M2Q.js +0 -14
- package/frontend/dist/js/appStore-DmV5wqDz.js +0 -2
- package/frontend/dist/js/ar-YHHTVP8f.js +0 -2
- package/frontend/dist/js/authStore-BYW8-kP6.js +0 -4
- package/frontend/dist/js/bn-C8VTxBVl.js +0 -2
- package/frontend/dist/js/chatStore-D-NTlArO.js +0 -3
- package/frontend/dist/js/cs-CCngB5fC.js +0 -2
- package/frontend/dist/js/da-CM2PP2ma.js +0 -2
- package/frontend/dist/js/de-BqVkD-dw.js +0 -2
- package/frontend/dist/js/es-BtWsWBjZ.js +0 -2
- package/frontend/dist/js/evaluationsApi-Dnf2dTd-.js +0 -1
- package/frontend/dist/js/fr-C68w9abT.js +0 -2
- package/frontend/dist/js/hi-DtBlcyXZ.js +0 -2
- package/frontend/dist/js/id-BWzIu8tn.js +0 -2
- package/frontend/dist/js/is-yeFwu4dJ.js +0 -2
- package/frontend/dist/js/ja-B-VyokB8.js +0 -2
- package/frontend/dist/js/ko-CvwzYr9k.js +0 -2
- package/frontend/dist/js/nl-Dq-QRx1L.js +0 -2
- package/frontend/dist/js/pl-BNEy8mQm.js +0 -2
- package/frontend/dist/js/pt-_3Jl3FdY.js +0 -2
- package/frontend/dist/js/ru-IhQiJ8ta.js +0 -2
- package/frontend/dist/js/sv-DWunXUNz.js +0 -2
- package/frontend/dist/js/tr-Do8CFmhR.js +0 -2
- package/frontend/dist/js/ui-db9meqAB.js +0 -2
- package/frontend/dist/js/ui-vendor-C0DZ8-Cm.js +0 -177
- package/frontend/dist/js/utils-vendor-9bQ62WeD.js +0 -9
- package/frontend/dist/js/vi-CZMFo9je.js +0 -2
- package/frontend/dist/js/workApi-CcprMH_1.js +0 -6
- package/frontend/dist/js/zh-Pg0881v0.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,
|
|
20
|
-
import
|
|
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
|
-
|
|
178
|
+
const schemas = [...WORK_TOOL_SCHEMAS];
|
|
179
|
+
if (task.networkEnabled &&
|
|
87
180
|
(await isWebSearchAvailable()) &&
|
|
88
|
-
(await userCanUseWebSearch(await userModel.getUserById(task.userId)))
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
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 =
|
|
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()}\
|
|
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)
|