Возможности
-
Удалённый
browser_toolмостится в локальный браузер пользователя (Electron) — Агенты, работающие на удалённом workspace (безголовый сервер, docker, WebUI), теперь могут управлять локальным настольнымBrowserPaneManagerпользователя от начала до конца. Добавлена возможностьclient:browser:invokeчерез WebSocket (объявляется при рукопожатии, сервер вызывает клиент, ошибкаErrorс.codeсохраняется в обоих направлениях), зеркашущая работуshell.openExternalдляOPEN_URL. Транспорт получаетhasClientCapability/findClientsWithCapabilityдля маршрутизации; Electron получает новый диспетчер IPC__browser:invokeс авторизацией по ключу владельца для каждого метода, отключением повторного использования окон для удалённых вызывающих,listInstances, ограниченными сессиями, и преобразованиемBuffer↔Uint8Arrayдля скриншотов по проволоке.server-coreполучаетRemoteBrowserPaneManager(реализацияIBPM, привязанная к сессии) иSessionManager.getBrowserPaneManagerForSessionс осведомлённой о возможностях клиентской fallback и очисткой привязанного к сессии на отключение.uploadFileблокируется через мост;evaluateограничен локальной настройкойallowRemoteEvaluate. Среда выполнения Pi получает дружелюбные отображения ошибок дляBROWSER_NO_CAPABLE_CLIENT,CAPABILITY_UNAVAILABLE,CLIENT_DISCONNECTED,CLIENT_REQUEST_TIMEOUT,BROWSER_INSTANCE_NOT_OWNED,BROWSER_REMOTE_UPLOAD_NOT_SUPPORTED, иBROWSER_REMOTE_EVALUATE_BLOCKED, и теперь зеркашиваетgetBrowserToolEnabledиз Claude, чтобы Pi больше не рекламировалbrowser_tool, когда переключатель выключен. 27 новых тестов покрывают упаковку по проволоке, авторизацию по методам, интроспекцию возможностей, fallback между хостом и клиентом, скриншоты, сохранение кодов ошибок и контракт отображения ошибок Pi. (1d926c33) -
Вкладки браузера изолированы по workspace —
BrowserPaneManagerглобален в процессе, иSTATE_CHANGEDранее транслировался{ to: 'all' }, поэтому чат в workspace A видел вкладки и баннеры статуса, принадлежащие сессиям из workspace B. Теперь каждыйBrowserInstance(и DTOBrowserInstanceInfo) несёт nullableworkspaceId;STATE_CHANGEDмаршрутизируется на{ to: 'workspace', workspaceId }, когда установлен (с fallback на{ to: 'all' }для несвязанных ручных окон); обработчикbrowserPane.LISTфильтрует поctx.workspaceId; рендерер читает новуюbrowserInstancesForWorkspaceAtomFamily, ключированную поactiveWorkspaceId. Окна всё ещё работают параллельно как настоящиеBrowserWindow— это фильтр видимости UI, а не песочница.REMOVED/INTERACTEDостаются трансляциями во все (только id, безопасные no-op на workspace, которые никогда не видели запись).workspaceIdотправляется как опциональное на DTO, поэтому старые рендереры терпимо обращаются с отсутствующими значениями (считаетundefinedкакnull→ проходит фильтр, эквивалентно поведению сегодня). 17 новых тестов покрывают фильтр атома, маркировку BPM и маршрутизацию трансляций/LIST. (af817192)
Улучшения
-
Блок
markdown-previewзадокументирован в справочнике rich-output — Блокmarkdown-preview(выпущен в v0.9.6) теперь описан вapps/online-docs/go-further/rich-output.mdxрядом с существующими записямиhtml-preview/pdf-preview/image-preview, чтобы пользователи, находящие блок в чате, могли найти примеры использования и справочник полейsrc/itemsв документации. (2d9693b1,70c2955f) -
Мост браузера наблюдается из логов сервера — Три строки
sessionLog.infoвSessionManagerтеперь подтверждают, выполнился лиsetRpcServerпри загрузке и выполнился ли блок пересылки браузерной панели при инициализации агента. Позволяет диагностировать удалённые workspace, которые всё ещё сталкиваются с "Browser window controls are not available", только по логам сервера, без подключения отладчика. (ad26e61d)
Исправления
-
Рендерер принимает как локальные, так и удалённые идентификаторы workspace при фильтрации вкладок — При подключении к удалённому workspace рендерер имеет два важных идентификатора:
activeWorkspaceId(локальная идентичность окна AIKraft Agents, используется для локально открытых ручных вкладок) иactiveWorkspace.remoteServer.remoteWorkspaceId(идентификатор удалённого сервера, используется удалённым агентом при маркировке вкладок через мост WS). Первая версия фильтра изоляции по workspace соответствовала толькоactiveWorkspaceId, поэтому вкладки, помеченные удалённым id (что несёт каждая удалённая агент-открытая вкладка), были отфильтрованы — полоска вкладок TopBar и значок статуса в панели инструментов стали невидимыми для удалённых браузеров, а их скрытие делало их недоступными. ЗаменёнatomFamilyна простую функциюfilterInstancesForWorkspace(local, remote): вкладка соответствует, если любой id совпадает (или еслиworkspaceIdравен null/undefined для обратной совместимости). (bf8429fa) -
STATE_CHANGEDснова транслируется всем клиентам; фильтр видимости живёт в рендерере — Фильтр на стороне сервера (наSTATE_CHANGEDи обработчикеLIST) был неверным для зеркальных удалённых workspace: транспортныйworkspaceIdрендерера — это локальная идентичность окна, тогда как удалённо-замкнутые вкладки браузера носят идентификатор удалённого сервера. Они никогда не совпадают, поэтомуSTATE_CHANGED, направленный на удалённый id, отбрасывался слоем маршрутизации WS (никакой локальный рендерер не заявляет себя как принадлежащий удалённому workspace), иLISTвозвращал пустой результат по той же причине. Изоляция по workspace теперь живёт полностью в рендерере (filterInstancesForWorkspace), который знает оба id черезactiveWorkspace.remoteServer.remoteWorkspaceId. Обработчик возвращается к трансляциям{ to: 'all' }и полному ответуLIST. Конфиденциальность не изменена — каждый локально подключённый рендерер принадлежит одному пользователю — и удалённые вкладки наконец-то отображаются в TopBar workspace, которому они принадлежат. (f831bb42) -
Нет повторного использования окон на пути жизненного цикла удалённого моста (закрывает межрабочие перехваты) — Диспетчер возможностей корректно устанавливал
allowReuseManual=falseдляcreateForSession, ноgetOrCreateForSessionиfocusBoundForSessionшли через публичные помощники, которые по умолчанию используютallowReuseManual=true.browser_tool openудалённого агента маппится наfocusBoundForSession, поэтому он мог принять несвязанное окно, оставленное локальной сессией — именно межрабочий перехват, который фильтрworkspaceIdдолжен был блокировать. ФильтрworkspaceIdвсё ещё помогает, но это best-effort: окна, созданные до маркировки (или через пути, которые не устанавливаютworkspaceId), имеютworkspaceId=nullи остаются универсально пригодными для перехвата. Ремень и браслет: каждый удалённый вызов жизненного цикла теперь передаётallowReuseManual=false, поэтому удалённые сессии всегда создают новые окна, если они ещё не владеют одним. (ce3340a1) -
Повторное использование несвязанных окон ограничено принадлежащим workspace (больше нет "вкладка переехала из workspace A в B") — Когда сессия завершается,
unbindAllForSessionочищаетboundSessionIdи меняетownerTypeна'manual', чтобы следующий ход той же сессии мог повторно привязать окно;workspaceId, помеченный при создании, сохраняется.findReusableUnboundInstanceсоответствовал ЛЮБОМУ несвязанному окну 'manual' независимо от workspace, поэтому сессия в workspace B с радостью подхватывала оставшееся окно из workspace A —bindSessionзатем перезаписывалworkspaceIdна B, фактически "перемещая" окно из A в B и делая полосу вкладок workspace A потерявшей запись, тогда как B приобретала её. Повторное использование теперь разрешено только еслиworkspaceIdкандидата равен null (настоящее пользовательское ручное окно — пригодное для кого угодно) или совпадает сworkspaceIdвызывающего. Повторное использование на следующем ходу в том же workspace всё ещё работает. (ceb24603) -
Ручные окна, открытые через TopBar, наследуют workspace, в котором они были открыты — Обработчик
browserPane.CREATEсоздавал ручные окна сworkspaceId, по умолчанию равнымnull, что фильтр изоляции по workspace намеренно трактует как "видимое для всех workspace" — поэтому вкладка, открытая через TopBar, просачивалась в полосу вкладок каждого workspace. Рендерер, который инициируетCREATE, всегда имеетctx.workspaceIdустановленный, поэтому обработчик теперь передаёт его вcreateInstance/createForSession. Вызывающие из CLI / агент-харнесса без контекста workspace (ctx.workspaceId === null) всё ещё получают поведение трансляции во все как безопасный fallback. (7dfcaeac) -
BrowserInstanceпроецируется в простой снимок перед возвратом через IPC — ЛокальныйBrowserPaneManager.getInstance(id)возвращает живойBrowserInstance, который встраивает нативные ссылки Electron (window: BrowserWindow,pageView: BrowserView, ...). Когда диспетчер__browser:invokeвозвращал этот объект через IPC, сериализатор структурированного клонирования Electron выбрасывалAn object could not be cloned, и вызовgetInstanceAsyncна стороне удалённого агента, используемый для логирования, завершался ошибкой. ДобавленtoSnapshot(instance), который выдаёт только поля, объявленные вIBPM(ownerType,ownerSessionId,isVisible,title,currentUrl), и маршрут диспетчера для веткиgetInstanceперенаправлен через него. (8e2534b5) -
source_testкодирует basic-auth учётные данные в base64 —testApiConnectionWithAuthинтерполировал сырое значение из хранилища в заголовокAuthorization, поэтому basic-auth источники получалиBasic {"username":"...","password":"..."}и получали 401 от каждого провайдера. Хранилище сохраняет учётные данныеsource_basicкак JSON (записанныеsource_credential_prompt/ WebUI); путь выполнения вapi-tools.tsbuildHeadersуже разбирает и кодирует в base64 — путь валидатора был просто забыт. Теперь разбирает токен как JSON, если он содержитusername+password; сохраняет поведение pass-through для унаследованных / вручную отредактированных base64-записей и любой не-JSON-строки, зеркашиваяbuildHeaders. Три регрессионных теста покрывают JSON-форму, унаследованную уже-кодированную форму и не-JSON мусорный токен. Исправляет . (96dd7c0d)
Несовместимые изменения
- Нет. Поле
workspaceIdна DTOBrowserInstanceInfoопционально, поэтому более старые рендереры и агенты терпимо обращаются с отсутствующими значениями (считаетundefinedкакnull→ проходит фильтр видимости, эквивалентно поведению до версии 0.10.0).