bizproc · Бизнес-процессы 26.1100.0 Безопасность

Bizproc 26.1100.0: агент удаляет процессы, зависшие на паузе, а импорт смарт-скриптов проверяет действия

2 мин чтения Устаревших API: 1
Узнавайте о ломающих изменениях Битрикса раньше, чем они доедут до прода: свежие разборы — в канале @bxmax_updates

Обновление безопасности

Закрыта уязвимость или ослабленная проверка прав. Ставить в первую очередь.

В bizproc 26.1100.0 импорт смарт-скриптов через REST-конфигурацию начал проверять действия шаблона от имени импортирующего пользователя. До этого шаблон сохранялся без ValidateProperties, и через импорт в него могло попасть действие CBPCodeActivity, которое эта проверка разрешает только администратору. Всех, у кого работают бизнес-процессы, касается ещё и новый агент, который раз в сутки удаляет процессы, больше недели стоящие на паузе без шансов проснуться.

Решите, нужно ли вам автоудаление зависших процессов

Агент Bitrix\Bizproc\Infrastructure\Agent\ClearStuckPauseWorkflowAgent::run() ищет процессы, которые стоят в DelayActivity, RobotDelayActivity или WaitWorkDayActivity дольше clear_stuck_pause_grace_days дней и у которых нет живого механизма пробуждения. По умолчанию срок 7 дней, это StuckPauseWorkflowDetector::DEFAULT_GRACE_DAYS. Процесс, который ждёт своего таймера через месяц, агент не тронет, потому что его есть чем разбудить. Чтобы не удалить процесс, который как раз оживает, агент работает в два прохода. Первый отмечает кандидата, второй проверяет его ещё раз под блокировкой bizproc_<workflowId> и только потом удаляет. Удаление состоит из CBPDocument::killWorkflow($workflowId, false), SchedulerEventTable::deleteByWorkflow() и чистки resume-сообщений процесса.

На новой установке агент включается сразу, через install/migrations/agents.php с clear_stuck_pause_agent_active = 'Y'. На обновлении его зарегистрирует степпер ResumeMessageItemIdStepper, но только когда допишет ITEM_ID всем старым resume-сообщениям, и до этого момента автоудаление не работает. Выключить агента или поменять срок можно в настройках модуля. Там появились чекбокс «Автоматически удалять зависшие на паузе бизнес-процессы» и поле «Время ожидания...» (от 1 до 365 дней). Срок и выключатель хранятся в опциях clear_stuck_pause_grace_days и clear_stuck_pause_agent_active, и их можно выставить из кода:

        <?php declare(strict_types=1);

use Bitrix\Main\Config\Option;

// ждать месяц вместо недели
Option::set('bizproc', 'clear_stuck_pause_grace_days', '30');

// или выключить автоудаление совсем
Option::set('bizproc', 'clear_stuck_pause_agent_active', 'N');

    

Агент, увидев 'N', удаляет сам себя, так что обратно его вернёт чекбокс (он вызывает ClearStuckPauseWorkflowAgent::register(), этот метод можно вызвать и из кода). Пока степпер не дописал ITEM_ID, чекбокс заблокирован, и выключить автоудаление заранее можно только этой опцией. Степпер значение 'N' не перезаписывает.

Каждое удаление агент пишет в именованный логгер bizproc.workflow.stuckPause. Чтобы потом было видно, какие процессы он удалил, опишите логгер в секции loggers и включите его в реестре логгеров, иначе LoggerFactory::createById() отдаст NullLogger:

        <?php declare(strict_types=1);

// фрагмент bitrix/.settings_extra.php
return [
    'loggers' => [
        'value' => [
            'bizproc.workflow.stuckPause' => [
                'constructor' => static fn (): \Bitrix\Main\Diag\FileLogger => new \Bitrix\Main\Diag\FileLogger(
                    '/var/log/bitrix/bizproc-stuck-pause.log',
                ),
                'level' => \Psr\Log\LogLevel::INFO,
            ],
        ],
        'readonly' => true,
    ],
];

    

Включается логгер одной строкой:

        <?php declare(strict_types=1);

(new \Bitrix\Main\Diag\LoggerRegistry())->enable('bizproc.workflow.stuckPause');

    

Допишите новые очереди в команду воркера

На порталах, где у bizproc включено scheduler_use_messenger_transport = Y, паузы больше не пишут продолжение в общую resume_workflow_queue. У каждой теперь своя очередь:

  • DelayActivity пишет в resume_workflow_delay_queue;
  • RobotDelayActivity в resume_workflow_robot_delay_queue;
  • WaitWorkDayActivity в resume_workflow_wait_workday_queue.

У всех трёх limit => 5 и total_processing_limit => 5. Старая resume_workflow_queue никуда не делась. В enum Bitrix\Bizproc\Activity\Enum\ResumeWorkflowQueue она значится как Legacy с пометкой @deprecated, рядом лежат новые Delay, RobotDelay и WaitWorkDay.

Воркер, который запускает messenger:consume со списком очередей, новые очереди сам не подхватит, и процессы после паузы будут стоять, пока их сообщения никто не разбирает. Добавьте очереди к тем, что уже перечислены в вашей команде:

        cd /var/www/site && php bitrix/bitrix.php messenger:consume \
    resume_workflow_queue \
    resume_workflow_delay_queue \
    resume_workflow_robot_delay_queue \
    resume_workflow_wait_workday_queue \
    --sleep=1 --time-limit=270 --no-interaction

    

resume_workflow_queue из списка не убирайте. Туда по-прежнему пишут подписки, созданные до обновления, и вызовы sendResumeWorkflowMessage() без явной очереди: параметр очереди у него по умолчанию равен ResumeWorkflowQueue::Legacy.

Ещё enqueueResumeWorkflow() теперь добавляет в сообщение ItemIdParam($workflowId). По этому ITEM_ID детектор ищет у процесса живое resume-сообщение, а агент потом удаляет сообщения удалённого процесса. Сообщение без ITEM_ID детектор не видит, и живой процесс выглядел бы брошенным, поэтому на обновлении агент ждёт степпер.

Верните массив из onDeadEndReached()

У абстрактного Bitrix\Bizproc\Public\Activity\Structure\FlowDirectedActivity::onDeadEndReached(CBPActivity $lastActivity) возвращаемый тип сменился с void на array. Ядро зовёт метод, когда у завершившегося дочернего действия нет выходов, и запускает действия по ссылкам, которые он вернёт (вида 'A1111_2222_3333_4444:i0'). Наследник с : void после обновления падает с фаталом. Штатные наследники уже переписаны: BaseComplexActivity возвращает [], а CBPNodeWorkflowActivity отдаёт следующее состояние через новый FlowStateRouterTrait. Если ваш узел наследует BaseComplexActivity и переопределяет этот метод, его тоже придётся поправить.

Так выглядит своё действие, которое из тупика уходит в запасную ветку:

        <?php declare(strict_types=1);

// local/activities/custom/vendorrouteractivity/vendorrouteractivity.php
use Bitrix\Bizproc\Public\Activity\Structure\FlowDirectedActivity;

/**
 * @property-read string $FallbackActivity
 */
final class CBPVendorRouterActivity extends FlowDirectedActivity
{
    public function __construct($name)
    {
        parent::__construct($name);
        $this->arProperties['FallbackActivity'] = '';
    }

    protected function getStartActivityNames(): array
    {
        $first = $this->arActivities[0] ?? null;

        return $first !== null ? [$first->getName()] : [];
    }

    // до 26.1100.0 здесь был ': void', с ним класс теперь не загрузится
    protected function onDeadEndReached(\CBPActivity $lastActivity): array
    {
        $fallback = (string)$this->FallbackActivity;

        // пустой список: ветка кончилась, узел закроется, когда опустеет очередь
        if ($fallback === '' || $lastActivity->getName() === $fallback) {
            return [];
        }

        return [static::createInputName($fallback, 0)];
    }
}

    

Если такое действие уезжает на разные порталы, закройте его от старых ядер ключом MIN_API_VERSION в описании. Ядро сверяет его с CBPRuntime::ACTIVITY_API_VERSION, а тот в этом релизе поднялся с 3 до 4. Набросок показывает только этот ключ и минимум полей, настройки узла для дизайнера в нём опущены:

        <?php declare(strict_types=1);

// local/activities/custom/vendorrouteractivity/.description.php
use Bitrix\Main\Localization\Loc;

$arActivityDescription = [
    'NAME' => Loc::getMessage('VENDOR_ROUTER_ACTIVITY_NAME'),
    'TYPE' => 'activity',
    'CLASS' => 'VendorRouterActivity',
    'JSCLASS' => 'BizProcActivity',
    'CATEGORY' => ['ID' => 'other'],
    // при ACTIVITY_API_VERSION ниже 4 ядро исключит действие из списка
    'FILTER' => ['MIN_API_VERSION' => 4],
];

    

Откройте getPropertiesMap() в наследниках CBPMailActivity

CBPMailActivity теперь реализует IBPConfigurableActivity, и его getPropertiesMap() стал public static вместо protected static. Наследник, который переопределяет метод как protected, не загрузится. В самой карте свойств у MailUserTo появился Setter, у MailCharset значение по умолчанию 'windows-1251', у DirrectMail значение 'Y'.

        <?php declare(strict_types=1);

// local/activities/custom/vendormailactivity/vendormailactivity.php
\CBPRuntime::getRuntime()->includeActivityFile('MailActivity');

class CBPVendorMailActivity extends \CBPMailActivity
{
    // с protected здесь теперь фатал: у родителя метод public
    public static function getPropertiesMap(array $documentType, array $context = []): array
    {
        $map = parent::getPropertiesMap($documentType, $context);
        // родитель теперь подставляет windows-1251
        $map['MailCharset']['Default'] = 'UTF-8';

        return $map;
    }
}

    

Пройдитесь по остальным изменениям поведения

  • Если HandleFault одного действия падает 3 раза подряд (MAX_FAULT_HANDLING_ATTEMPTS), CBPWorkflow завершает процесс через terminate(). Раньше очередь в такой ситуации могла крутиться бесконечно. Ошибку записи в журнал отслеживания при обработке сбоя новый приватный writeFaultTracking() отправляет в файловый лог, и второй раз процесс из-за неё не падает. Такую же правку получил CBPActivity.
  • ActivityDescription::toArray() отдаёт COMPLEX_ACTIVITY_SETTINGS массивом, а не объектом Complex\Settings. setComplexActivitySettings() принимает Complex\Settings|array|null. Код, который вызывал методы у этого ключа, придётся перевести на массив.
  • Условия in и !in в фильтрах действий (lib/activity/mixins/entityfilter.php) разбирают строку, которая начинается с [, как JSON-список значений. В списке может быть не больше 500 значений по 255 символов, весь JSON ограничен 65535 символами. Значение, которое не укладывается в лимиты, даёт заведомо пустой ORM-фильтр, и выборка сужается до нуля.
  • Из конвертеров шаблонов (SequentialToNodeWorkflow, StateChildToNodeWorkflow, StateMachineToNodeWorkflow) удалены protected-методы раскладки setPosition(), movePosition*(), copyPosition(), getRow(), getColumn(), setChildPosition*(). У convertChild() и convertChildren() новая сигнатура с Layout\GridPoint, StateChildToNodeWorkflow::createTriggers() заменён на createTrigger(string $type), раскладку строят новые классы Converter\Canvas\* и Converter\Layout\*. Заденет это только тех, кто наследовал SequentialToNodeWorkflow.
  • RagService берёт список допустимых расширений файлов базы знаний из Bitrix\Rag\Public\Service\FileKnowledgeBasePublicService::getFileUploadConfig()->extensions. Опции rag_tables_enabled, rag_pptx_enabled и rag_images_enabled модуль больше не читает.

Обновитесь, если смарт-скрипты импортируют не администраторы

Импорт смарт-скрипта из REST-конфигурации идёт через Script\Manager::importScript() и importWorkflowTemplate() в lib/script/manager.php. Перед сохранением шаблона его действия теперь проверяет новый CBPWorkflowTemplateLoader::checkImportedTemplateActivities(array $template, CBPWorkflowTemplateUser $user), и проверка идёт от имени импортирующего. Скрипт с CBPCodeActivity теперь импортирует только администратор, потому что CBPCodeActivity::validateProperties() требует администратора. Если в вашем модуле есть свой импорт шаблонов, вызывайте этот метод до сохранения, ошибки проверки он вернёт в Main\Result.

Остальные правки безопасности:

  • сортировка списка шаблонов проходит через белый список полей (CBPWorkflowTemplateLoader::prepareTemplatesOrder()), и неизвестные by/order из URL больше не вызывают ArgumentException;
  • upgradeAction для AI-агентов доступен только по POST и только администратору;
  • у условий in/!in появились лимиты и пустая выборка на невалидное значение, подробности выше;
  • AiAgentBotAvatar и buildTemplateFromSource() проверяют код на path traversal.

Проверьте, как AI-ассистент описывает ваше действие

Новый ActivityAiAutoDescriber строит AI-схему настроек действия из getPropertiesMap(), а если такого метода нет, из getPropertiesDialogMap(). Поля Title и EditorComment в схему не попадают. Для REST-действий схема строится по RestActivityTable.PROPERTIES в строгом режиме. Ручное описание в .ai.php (класс CBPAI<Code> с интерфейсом IBPActivityAiDescription) по-прежнему работает, и ActivityAiDescriptionMerger объединяет его с автосхемой. Читает результат BlockDescriptionService из bizprocdesigner.

Чтобы исключить действие из автоописания, поставьте в его .description.php ключ 'AI_AUTO_DESCRIPTION' => 'N'. В PHP за ним стоят ActivityDescription::setAiAutoDescription(string|bool) и isAiAutoDescriptionAllowed(). Схема из карты свойств тогда строиться не будет, а ручной .ai.php продолжит работать. В ActivityAiDescriptionResult появились поля source (ActivityAiDescriptionSource::Manual или Auto) и skippedSettings. AiDescription::getActivityDescription() кеширует результат, изолирует исключения отдельного действия и пишет их в логгер bizproc.aiassistant.activity_description.

Загляните в остальные новые API

  • Трейты Public\Activity\Structure\FlowListenerTrait (subscribeOutputFlow(), onFlowEvent()) и FlowStateRouterTrait (setNextState(), getNextStateName()). FlowListenerTrait подключён в CBPListenActivity и CBPStateNode, FlowStateRouterTrait в CBPNodeWorkflowActivity.
  • Действия StateNode (узел состояния с портами «Вход», «Выход» и «Ожидание») и SetStateNode в install/activities/bitrix/. Через StateNode конвертер StateMachineToNodeWorkflow переводит БП со статусами в узловую схему. Комплексный UnifiedNode появляется, только если определена константа Bitrix\Bizproc\Dev\ENV.
  • В Public\Activity\ReturnDocumentTrait появились методы execute(), buildDocumentPropertyMap() и validateDocumentProperty().
  • Каталог возможностей узла: DTO AvailableBlock, BlockAvailability, NodeActionCatalogEntry и NodeCapabilityCatalog в Activity\Dto\Complex, Settings::fromArray() с полем availableBlocks, enum ActionGroup и NodeBlockType, сервис bizproc.service.activity.capabilityCatalog. Вариативные параметры узла вроде frameColorName у EmptyBlockActivity хранит Activity\Dto\NodeSettingsParam.
  • AI-агента можно обновить до новой версии системного шаблона экшеном Infrastructure\Controller\Integration\AiAgent\Template::upgradeAction(int $templateId, ?array $constantValues = null) или действием грида UpgradeAction. Версия копии хранится в настройке ORIGIN_SYSTEM_VERSION (WorkflowTemplateSettingsTable) и считается как sha256 от нормализованного TEMPLATE, где ключи отсортированы, а CONSTANTS не учитываются.
  • Public\Provider\WorkflowTemplate\AiAgentProvider::checkLaunchAccess(?int) возвращает стабильный код ошибки AI_AGENT_START_ACCESS_DENIED. Сам bizproc этот метод не вызывает.
  • Public\Service\Trigger\TriggerService::hasStartTrigger(string $triggerType, array $complexType) поверх Internal\Repository\Trigger\TriggerRepository.
  • CBPWorkflowPersister бросает Bitrix\Bizproc\Exception\EmptyWorkflowInstanceException там, где раньше был Exception("WorkflowNotFound") или Exception("EmptyWorkflowInstance"). Класс наследует \Exception, так что старые catch (\Exception) продолжают ловить.
  • Api\Enum\ErrorMessage::getCodedError() отдаёт ошибку с кодом, равным значению enum.
  • NodesInstaller::getMessageReplacements() и prepareMessage() подставляют значения во фразы узлов при установке. Так bitrix_ai_day_planner получает #COPILOT_NAME# из CopilotNameService, по умолчанию 'BitrixGPT'. NodesInstallerService::buildTemplateFromSource() собирает шаблон из файлов узла без записи в БД.
  • Команда bizproc:ai-agent-generate после генерации запускает phpunit-тесты агента из bizproc/tests/nodes/AI_AGENT/<name> и при падении откатывает template.json и installer.php. Опция --skip-tests тесты пропускает.
  • Для секции loggers появились именованные логгеры bizproc.workflow.stuckPause, bizproc.aiassistant.activity_description, bizproc.runtime.activity_searcher и bizproc.imbot, и каждый включается через LoggerRegistry::enable().

Дайте степперу дописать ITEM_ID

Схему таблиц bizproc описывает install/migrations/tables.php, изменения пришли туда и в install/migrations/agents.php:

  • в b_bp_workflow_instance два индекса для выборки кандидатов на очистку, ix_bp_wi_status_modified (STATUS, MODIFIED) и ix_bp_wi_modified_id_owned_until (MODIFIED, ID, OWNED_UNTIL);
  • в b_bp_messenger_workflow_resume_message индекс IX_QUEUE_ID_ITEM_ID, а WorkflowResumeMessageTable получил DeleteByFilterTrait;
  • агент ClearStuckPauseWorkflowAgent::run(); с интервалом 86400 секунд и опции clear_stuck_pause_agent_active, clear_stuck_pause_grace_days, clear_stuck_pause_workflow_offset, clear_stuck_pause_sweep_from и clear_stuck_pause_swept_until.

ResumeMessageItemIdStepper заполняет ITEM_ID у старых resume-сообщений порциями по 500 через updateMulti(..., ignoreEvents: true). События выключены намеренно, чтобы onBeforeUpdate не сдвигал UPDATED_AT. Если вы вешали свои обработчики на WorkflowResumeMessageTable, на этих обновлениях они не сработают.

Мелочи и находки

  • CBPGetUserActivity при выборе руководителя с пропуском неработающих проверял $this->isUserWorking($skipTimeman), то есть булев флаг вместо ID. Теперь проверяется $headId. Заодно тип пользователя можно задать выражением (user_type_text), а user_type в диалоге выводится через CUtil::JSEscape.
  • Хранилище или его поле нельзя удалить, пока StorageItemDeleteStepper удаляет записи. Проверку делает новый hasAgentsForStorage(), а пользователь видит сообщения ..._DELETION_IN_PROGRESS. Сам степпер при ошибке выборки больше не падает, он завершается и будит процесс.
  • WorkflowTemplateService::saveTemplate() отклоняет запись в несуществующий шаблон и черновик удалённого шаблона с кодом TEMPLATE_NOT_FOUND, а шаблон и настройки обновляет в одной транзакции. То же сделали в UpdateWorkflowTemplateCommandHandler.
  • RestService::addActivityInternal() (через него идут bizproc.activity.add и bizproc.robot.add) ловил SqlQueryException, который в пространстве имён Bitrix\Bizproc не импортирован, поэтому catch ничего не ловил. Теперь там Main\DB\DuplicateEntryException. Похоже на исправление bizproc.robot.add, о котором пишет вендор.
  • Приватный CBPDocument::unSignArray(?string) на null и пустую строку возвращает [].
  • Порты StateNode в .description.php подписаны по-русски прямо в коде, без локализации: 'Вход', 'Выход', 'Ожидание'.
  • В комментариях контроллера AiAgent\Template (upgradeAction() и relaunchAfterUpgrade()) остались ссылки на внутреннюю спецификацию: «ADR § 10.3», «API-01», «AC-017/AC-018», «ERR-002/003/005/006». Ещё одна, «P3.T2», есть в CBPSetupTemplateActivity. В SequentialToNodeWorkflow::migrateTemplate() висит TODO active-migration про двойной старт при миграции на месте.
  • CHTTP из модуля ушёл целиком. В admin/task.php, компоненте bizproc.task.list и шаблоне bizproc.document его заменил Main\Web\Uri.
  • BaseReceiver при INSTANCE_LOCKED откладывает повтор на 60 секунд (INSTANCE_LOCKED_RETRY_DELAY), раньше задержки не было.
  • В шаблоне bitrix_ai_day_planner сообщения и бот адресуются руководителю ({=A7001_0001_0001_0001:MANAGER}), а у сообщения появилась кнопка с командой discussReport. Обработчик этой скрытой команды пришёл в BizprocBot и вызывает ReportDiscussionService::discuss() из timeman. В «Управлении сайтом» модулей timeman и imbot нет, и там этот путь не исполняется.
  • VERSION_DATE в install/version.php старше, чем у предыдущей 26.1075.0.

Что делать

  1. Решите, нужны ли вам процессы, которые больше недели стоят на паузе. Если нужны, выключите clear_stuck_pause_agent_active или увеличьте clear_stuck_pause_grace_days. Логгер bizproc.workflow.stuckPause в любом случае опишите в loggers и включите через LoggerRegistry::enable().
  2. При scheduler_use_messenger_transport = Y допишите три новые очереди в команду messenger:consume, а resume_workflow_queue оставьте.
  3. Найдите в своём коде onDeadEndReached и замените : void на : array.
  4. Найдите наследников CBPMailActivity и сделайте их getPropertiesMap() публичным.
  5. Обновитесь, если смарт-скрипты на портале импортируют пользователи без прав администратора.

Устаревшие и удалённые API в этой версии

Символ Статус Чем заменять
Bitrix\Bizproc\Activity\Enum\ResumeWorkflowQueue::Legacy Deprecated Bitrix\Bizproc\Activity\Enum\ResumeWorkflowQueue::Delay / Bitrix\Bizproc\Activity\Enum\ResumeWorkflowQueue::RobotDelay / Bitrix\Bizproc\Activity\Enum\ResumeWorkflowQueue::WaitWorkDay

Новая версия вышла — разбор уже в Telegram

В канале @bxmax_updates публикуем каждый свежий разбор: что сломается, что устарело и что появилось в API. Узнаете до того, как обновите прод.

Подписаться

Читайте дальше

bizproc 26.1400.0 Ломающее Свежее

bizproc 26.1400.0 + bizprocdesigner 26.800.0: новые REST-методы внешнего AI-агента, дочерние действия в CBPActivity, история версий шаблонов

bizproc 26.1400.0 и bizprocdesigner 26.800.0 ставятся одним шагом. Внешнему AI-агенту дизайнера придётся сменить имена трёх методов и формат запросов, потому что его V3-контроллеры перевели с `ArrayResponse` на типизированные запросы и DTO. Свои действия из `/local/activities` стоит проверить на обн…

4 мин
bizproc 26.1200.0 Безопасность Свежее

bizproc 26.1200.0: псевдодокумент WORKFLOW закрыт для не-админов, у узловых шаблонов появились роли

До bizproc 26.1200.0 псевдодокумент `WORKFLOW` разрешал `CreateWorkflow` и `WriteDocument` любому пользователю, и запись узловых шаблонов ничем не ограничивалась. Теперь эти операции доступны только администратору, а для узловых шаблонов появились роли на `Main\Access`, которые после обновления ни к…

5 мин
bizproc 26.1300.0 Безопасность Свежее

bizproc 26.1300.0 + bizprocdesigner 26.700.0: дизайнер проверяет права на присланный documentType, внешнего AI-агента вернули владельцу его копии

bizprocdesigner 26.700.0 ставится одним шагом с bizproc 26.1300.0 и закрывает экшен, который без проверки прав отдавал категории и стадии любого присланного клиентом типа документа. Внешнего AI-агента на псевдотипе `Workflow`, которого с bizproc 26.1200 подключал только администратор, снова может по…

2 мин
bizproc 26.1075.0 Рутинное Свежее

Bizproc 26.1075.0: строже ответ AI-коуча, кеш хранилищ без нулей

Почти весь релиз про встроенного AI-агента `bitrix_ai_coach`, который проводит тесты для сотрудников. В JSON-схеме ответа модели появились обязательные `questions_review`, `correct_count` и `total_count`, а промпт велит сверять их между собой перед выводом итога. Промпты при этом разошлись. Английск…

1 мин
Мы используем файлы cookie для улучшения работы сайта. Продолжая использовать сайт, вы соглашаетесь с нашей политикой конфиденциальности.
AI Домовой

AI Домовой История

на связи

пишет…

Можно закрыть чат и перейти на другую страницу. Когда ответ будет готов, на кнопке Домового появится отметка.

Нет истории чатов
AI Домовой

Нужна авторизация

Войдите, чтобы задавать вопросы AI Домовому.

Войти