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

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

Ломающее обновление

Удалены или изменены публичные API — прикладной код может перестать работать.

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

Что сломается

Переведите внешнего AI-агента на новые методы

REST V3-контроллеры внешнего AI-агента в bizprocdesigner переписали на типизированные запросы и DTO и заодно переименовали методы:

Было Стало
bizprocdesigner.agent.connect без изменений
bizprocdesigner.catalog.list bizprocdesigner.catalog.block.list
bizprocdesigner.catalog.list с blockType bizprocdesigner.catalog.block.get
bizprocdesigner.document.fields bizprocdesigner.document.field.list
bizprocdesigner.template.draft.push bizprocdesigner.template.draft.add
bizprocdesigner.template.get без изменений
bizprocdesigner.template.list без изменений
bizprocdesigner.template.validate без изменений

Путь /rest/api/<userId>/<secret>/ остался прежним, ссылка подключения вела на него и в 26.700, из неё убрали только ?templateId=. Сменилась передача параметров. Раньше инструкция для агента (AgentConnectInstructionService) передавала их в query string (catalog.list?templateId=…&blockType=…), теперь они принимаются только в JSON-теле POST-запроса. Классический /rest/... на эти методы отвечает 404 ERROR_METHOD_NOT_FOUND.

        curl -s -X POST "https://portal.example.ru/rest/api/1/<secret>/bizprocdesigner.document.field.list" \
  -H "Content-Type: application/json" -d '{}'

    

Экшены теперь принимают типизированные V3-запросы (GetRequest, ListRequest, AddRequest, свои TemplateValidateRequest, BlockGetRequest, DocumentFieldListRequest) и отдают GetResponse / ListResponse с DTO по #[DtoType] (TemplateDto, AgentBlockDto, BlockTypeDto, DocumentFieldDto и другие). Раньше ответ был ArrayResponse, так что разбор ответа в агенте придётся проверить. С шаблоном экшены работают так:

  • template.list возвращает только шаблон, к которому привязан токен;
  • agent.connect и остальные экшены без templateId берут шаблон из привязки токена;
  • template.get требует, чтобы id был строкой из цифр.

В PHP удалены Rest\Controller\Catalog, Rest\Controller\Document и Rest\Exception\AgentRestException. Вместо общего AgentRestException с кодами вроде VALIDATION_FAILED ошибки теперь приходят отдельными исключениями: GraphValidationException (наследник ValidationException), DraftSaveFailedException и ClientDraftSaveFailedException, BlockTypeNotFoundException, BlockCatalogUnavailableException. Ещё удалён install/updater.php, который регистрировал обработчик rest:OnRestServiceBuildDescription.

Прогоните свои действия из /local/activities

Код дочерних действий переехал из CBPCompositeActivity в CBPActivity. Методы initialize(), finalize(), setWorkflow(), unsetWorkflow(), collectNestedActivities(), pullProperties(), reInitialize() и fixUpParentChildRelationship() теперь общие и живут в CBPActivity, а контейнер детей включается флагом. Кроме того, CBPActivity подмешивает трейт Public\Activity\Mixins\ChildFlowTraversal и объявляет несколько методов сам:

  • из трейта приходят close(): void, executeActivity(), executeByNames(), getInputNames(), getOutputNames(), getAuxNames(), getStartActivityNames(): array, onDeadEndReached(), статические createInputName() и createOutputName();
  • в самом классе появились protected function OnEvent(CBPActivity $sender), protected static function getActivityCode(): string, public function getRawPropertyValue($name) и public function markSkippedByEngine(): void.

Любое ваше действие наследует CBPActivity. Если в нём уже есть метод с таким именем и несовместимой сигнатурой, класс перестанет загружаться. Так будет, например, с OnEvent, у которого обязательный второй параметр, или с нестатическим getActivityCode(). Ядро наткнулось на тот же конфликт у себя: CBPActivity подключает трейт с алиасом onEvent as protected runChildFlowClosedEvent, потому что штатные CBPIfElseActivity и CBPApproveActivity объявляют OnEvent защищённым и с одним аргументом.

Со свойствами то же самое. Трейт приносит типизированные protected array $activityQueue, protected array $pendingQueue и protected bool $childFlowStarted, и одноимённое свойство без типа в вашем действии даст фатал.

Несовместимую сигнатуру PHP отвергает при объявлении класса фатальной ошибкой, поэтому рефлексией по вашим классам конфликт не найти. Скрипт ниже читает файлы действий токенайзером и для каждого совпадения по имени выводит обе сигнатуры, вашу и из ядра. Запускать на копии сайта, уже обновлённой до 26.1400.0:

        <?php declare(strict_types=1);

// /local/tools/check-activities.php, запуск из корня сайта: php local/tools/check-activities.php
$_SERVER['DOCUMENT_ROOT'] = dirname(__DIR__, 2);
define('NO_KEEP_STATISTIC', true);
define('NOT_CHECK_PERMISSIONS', true);
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';

\Bitrix\Main\Loader::requireModule('bizproc');

// имена, которые в 26.1400.0 появились или сменили сигнатуру в CBPActivity и его трейте
$watch = [
    'onevent', 'getactivitycode', 'getrawpropertyvalue', 'markskippedbyengine',
    'close', 'executeactivity', 'executebynames', 'getinputnames', 'getoutputnames',
    'getauxnames', 'getstartactivitynames', 'ondeadendreached',
    'createinputname', 'createoutputname',
    'startchildflow', 'extractactivitynamefromlink', 'extractactivitynamesfromlinks',
    'runchildflowclosedevent', 'getchildcontainertracekey', 'getchildcontainertracemessage',
    'getchildcontainertracereadonlydata', 'isservedbyunifiedpanel', 'createinstance',
];

$coreSignature = static function (string $method): string {
    $m = new ReflectionMethod(CBPActivity::class, $method);
    $params = array_map(
        static fn (ReflectionParameter $p): string => ltrim($p->getType() . ' $' . $p->getName())
            . ($p->isOptional() ? ' = …' : ''),
        $m->getParameters(),
    );

    return implode(' ', Reflection::getModifierNames($m->getModifiers()))
        . ' ' . $m->getName() . '(' . implode(', ', $params) . ')'
        . ($m->hasReturnType() ? ': ' . $m->getReturnType() : '');
};

$root = $_SERVER['DOCUMENT_ROOT'] . '/local/activities';
if (!is_dir($root)) {
    exit("Нет каталога {$root}\n");
}

$files = new RegexIterator(
    new RecursiveIteratorIterator(new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS)),
    '/\.php$/',
);

foreach ($files as $file) {
    // файлы действий не подключаем: класс с конфликтом упал бы при объявлении
    $source = (string)file_get_contents($file->getPathname());
    $lines = explode("\n", $source);
    $tokens = PhpToken::tokenize($source);

    foreach ($tokens as $i => $token) {
        if (!$token->is(T_FUNCTION)) {
            continue;
        }
        $next = $i + 1;
        while (isset($tokens[$next]) && $tokens[$next]->isIgnorable()) {
            $next++;
        }
        $name = $tokens[$next] ?? null;
        if ($name === null || !$name->is(T_STRING) || !in_array(strtolower($name->text), $watch, true)) {
            continue;
        }

        printf(
            "%s:%d\n  у вас: %s\n  ядро:  %s\n",
            $file->getPathname(),
            $token->line,
            trim($lines[$token->line - 1]),
            $coreSignature($name->text),
        );
    }
}

    

Имена сравниваются без учёта регистра, как их сравнивает PHP, поэтому ваш onEvent совпадёт с OnEvent ядра. В списке все публичные и защищённые методы, которые в этом релизе появились или изменились у CBPActivity и ChildFlowTraversal. Приватные методы трейта PHP при наследовании не сверяет.

Вместе с переездом удалено dev-действие unifiednode. В палитру оно попадало только при определённой константе Bitrix\Bizproc\Dev\ENV, а его механика теперь есть у всех действий.

Переопределите методы в наследниках FlowDirectedActivity

У FlowDirectedActivity методы getStartActivityNames() и onDeadEndReached() больше не абстрактные. Их конкретные версии приносит трейт, а PHP не даёт объявить abstract метод, который трейт уже реализовал. Поэтому конструктор проверяет через ReflectionMethod, что наследник переопределил оба метода, и бросает NotImplementedException, если нет. Результат проверки кешируется по классу.

Забытый метод раньше ронял загрузку класса. Теперь исключение вылетит при создании экземпляра, уже в рантайме. В списке изменений API методы FlowDirectedActivity и BaseComplexActivity::initializeFilterResultProperties() значатся удалёнными, но они только переехали в трейты ChildFlowTraversal и NodeFilterResultProperties и у классов остались.

Передавайте политику констант в WorkflowTemplateService по имени

В конструктор Api\Service\WorkflowTemplateService добавили шесть необязательных зависимостей: WorkflowTemplateHistoryService, SettingsFreezeGate, StartFormParameters, PilotVisibilityProvider, CommonRevisionWriter и TemplatePersistAccessService. Они встали перед ?TemplateConstantsAccessPolicyInterface $templateConstantsAccessPolicy, и политика переехала с четвёртой позиции, где она стояла в 26.1300.0, на десятую. Позиционный вызов с политикой четвёртым аргументом упадёт с TypeError. Ядро само передаёт её именованным аргументом, так же стоит делать и в своём коде:

        <?php declare(strict_types=1);

use Bitrix\Bizproc\Api\Service\WorkflowTemplateService;

// было: new WorkflowTemplateService(null, null, null, $policy)
$service = new WorkflowTemplateService(templateConstantsAccessPolicy: $policy);

    

У saveTemplate() появился последний параметр bool $withoutCommonVersion = false.

Перенесите пары триггеров в getTriggerUpgradeMap()

Starter\ModuleSettings::getInterchangeableTriggerPairs() появился в 26.1200.0 и уже удалён. Вместо него getTriggerUpgradeMap(): array<string, TriggerUpgradeDto>, одностороннее обновление устаревшего класса триггера до актуального с набором свойств. Обратного направления нет. Модуль, который переопределял старый метод, не упадёт, но его пары больше никто не читает:

        <?php declare(strict_types=1);

namespace Vendor\Module\Integration\Bizproc;

use Bitrix\Bizproc\Starter\Dto\TriggerUpgradeDto;

final class ModuleSettings extends \Bitrix\Bizproc\Starter\ModuleSettings
{
    // isAutomationFeatureEnabled(), isScriptFeatureEnabled(), isAutomationLimited(), isAutomationOverLimited() остаются как были

    public function getTriggerUpgradeMap(): array
    {
        return [
            // устаревший тип триггера => актуальный тип и свойства, которые узел получит сверху
            'VendorOldDealTrigger' => new TriggerUpgradeDto('VendorDealTrigger', ['Mode' => 'update']),
        ];
    }
}

    

В дизайнере на этой карте работает Public\Service\Activity\TriggerUpgradeResolver. SaveCommandHandler при сохранении настроек заменяет устаревший тип узла актуальным и добавляет свойства из апгрейда. Controller\Activity::getSettingsControlsAction() сразу строит форму для нового класса и в шаблон ничего не пишет. Селектор режима реакции из bizprocdesigner 26.700 (reaction-mode-selector.js, reaction-mode-fields.js) удалён.

Научите отчёты по журналу типу 14

Для узла, который движок не выполнил, потому что тот выключен или его условие ложно, CBPWorkflow теперь пишет в b_bp_tracking запись CBPTrackingType::SkipActivity = 14. Причина уходит в ACTION_NOTE, а CloseActivity для такого узла не пишется. Запись подчиняется той же настройке журналирования, что и ExecuteActivity. Очистка журнала раньше удаляла TYPE IN (0,1,2,3,4,5,7,8,9), теперь берёт список из константы CLEAR_LOG_TYPES, и тип 14 в него входит.

Если ваш отчёт собирает историю процесса из b_bp_tracking, у пропущенных узлов он найдёт одну запись нового типа:

        <?php declare(strict_types=1);

function trackingLabel(array $row): string
{
    return match ((int)$row['TYPE']) {
        \CBPTrackingType::ExecuteActivity => 'запущен',
        \CBPTrackingType::CloseActivity => 'завершён',
        \CBPTrackingType::SkipActivity => 'пропущен: ' . $row['ACTION_NOTE'],
        default => 'тип ' . $row['TYPE'],
    };
}

    

Включайте AI-разделы флагами main

Три опции bizproc заменены фича-флагами main (механизм разобран в main 26.700.0):

Была опция Теперь флаг Что включает
bizproc:feature_ai_agents Public\Feature\AiAgent\AiAgentSectionFlag видимость SetupTemplateActivity
bizproc:is_rag_available Public\Feature\Rag\KnowledgeBaseFlag RagService
bizproc:bitrix_ai_coach_available Public\Feature\AiAgent\CoachAgentFlag установщик агента-тренера

Все три флага наследуют Bitrix\Main\Config\Feature\AbstractFlag и своего кода не содержат. Опции, выставленные вручную, после обновления никто не читает.

Проверьте именованные аргументы и переопределённые методы

  • У Workflow\Template\Converter\TemplateToNodes::__construct() параметр relationsRuntimeAvailable переименован в complexNodeConnectionsAvailable. Вызов с именованным аргументом по старому имени упадёт.
  • Integration\UI\EntitySelector\StartWorkflowTemplateProvider больше не переопределяет getDefaultTemplateFilter(), метод наследуется от TemplateProvider и Nodes-шаблоны не исключает, их отбирает новый filterTemplates(). Наследник, который брал из parent::getDefaultTemplateFilter() фильтр без Nodes-шаблонов, начнёт их получать.
  • Новые необязательные параметры получили CBPActivity::createInstance($code, $name, ?array $activityData = null), CBPWorkflowTemplateLoader::update(..., bool $deferCacheInvalidation = false, bool $publishesCommonVersion = false), validateTemplate(..., ?array $documentType = null), getDocumentTypeStates(..., ?int $visibleForUserId = null) и Calc\Parser::__construct(\CBPActivity $activity, ?array $functions = null). ?ManualStartSurface $manualStartSurface добавлен в Starter\Context, Starter\Dto\ContextDto и PrepareStartParametersRequest, ?int $visibleForUserId в Starter\Template\Start\CollectRequest. Вызовы это не ломает, но методы, переопределённые в наследниках, придётся дополнить.
  • В bizprocdesigner конструктор WorkflowTemplateIdentifier принимает ещё ?string $name и ?DateTime $modified, ValidateSingleRuleCommand принимает ?array $activityProperties, SaveWorkflowValidator принимает ?GraphLimitsValidator. У ConditionValueResolver::resolveConditionValue() появился ?array $conditionDocumentType = null, у NodeCatalogItemDto появился bool $servedByUnifiedPanel = false.

Новое

Включите историю версий шаблонов

bizproc научился хранить версии шаблонов. Сервис Api\Service\WorkflowTemplateHistoryService отдаёт список и отдельную версию (getVersions(), getVersion()), восстанавливает её (restoreVersion()), отменяет восстановление (undoRestore()) и удаляет резервную копию (discardRestoreBackup()). Журнал лежит в новой таблице b_bp_workflow_template_change (ORM Workflow\Template\WorkflowTemplateChangeTable). По docblock сервиса механизм работает так:

  • в снимок попадают TEMPLATE, PARAMETERS, VARIABLES, CONSTANTS, NAME и DESCRIPTION;
  • версия пишется в транзакции публикации, и если записать её не удалось, публикация отменяется с ошибкой TEMPLATE_HISTORY_UNAVAILABLE;
  • восстановление создаёт новый черновик, прежний черновик остаётся резервной копией, живой шаблон не меняется;
  • тело хранится у последних template_version_limit версий (по умолчанию 10), более старые остаются в журнале событиями;
  • журнал ведётся только для шаблонов типа Nodes и только для несистемных;
  • импорт и установка пакета идут мимо журнала, после них ни одна версия не считается текущей.

Включается история двумя опциями двух модулей, и обе по умолчанию N. bizproc:template_version_history_enabled включает запись версий, bizprocdesigner:version_history_available показывает историю в редакторе. По docblock Feature::isVersionHistoryAvailable() запись включают раньше интерфейса («is switched on earlier»).

        <?php declare(strict_types=1);

use Bitrix\Bizproc\Api\Request\WorkflowTemplateHistoryService\GetVersionsRequest;
use Bitrix\Bizproc\Api\Service\WorkflowTemplateHistoryService;
use Bitrix\Main\Config\Option;
use Bitrix\Main\Loader;

Loader::requireModule('bizproc');

Option::set('bizproc', 'template_version_history_enabled', 'Y'); // запись версий
Option::set('bizprocdesigner', 'version_history_available', 'Y'); // история в редакторе
Option::set('bizproc', 'template_version_limit', '20'); // сколько снимков хранить, по умолчанию 10

$response = (new WorkflowTemplateHistoryService())->getVersions(
    new GetVersionsRequest(templateId: $templateId, userId: $userId),
);

if (!$response->isSuccess()) {
    // например, TEMPLATE_HISTORY_UNAVAILABLE
    throw new RuntimeException(implode('; ', $response->getErrorMessages()));
}

foreach ($response->getVersions() as $version) {
    printf(
        "v%d %s%s\n",
        $version->versionNumber,
        $version->createdTimestamp !== null ? date('Y-m-d H:i', $version->createdTimestamp) : '—',
        $version->isCurrent ? ' (текущая)' : '',
    );
}

    

Редактор ходит в сервис через экшены Controller\Diagram::getVersionsAction(), getVersionAction(), restoreVersionAction(), undoRestoreAction() и discardRestoreBackupAction(), права проверяет сам сервис. Просмотр версии прогоняет снимок через тот же конвертер, что и обычная загрузка шаблона. Для ошибок появились коды ErrorMessage::TEMPLATE_VERSION_NOT_FOUND, TEMPLATE_HISTORY_UNAVAILABLE, TEMPLATE_VERSION_CONFLICT и TEMPLATE_RESTORE_UNDO_UNAVAILABLE, у черновиков появились статусы STATUS_RESTORED = 2 и STATUS_RESTORED_WITHOUT_BACKUP = 3.

Включайте системного AI-агента из своего модуля

Public\Command\AiAgent\EnableSystemAiAgentCommand и DisableSystemAiAgentCommand включают и выключают системного AI-агента для внешнего объекта. Экземпляр определяет пара из системного кода и контекста SystemAiAgentContext(namespace, type, id), в docblock для примера взяты socialnetwork и project. Произвольный templateId команды не принимают. Значения для агента передаются в SystemAiAgentActivationParameters двумя секциями, parameters и constants, и неизвестное имя команда отклонит.

Повторное включение идемпотентно и возвращает already_enabled. Та же пара с другой конфигурацией даёт ошибку AI_AGENT_CONFIGURATION_CONFLICT. Результат SystemAiAgentLifecycleResult сообщает исход через getOutcome() (enabled, already_enabled, disabled, already_disabled, cleanup_pending) и причину отказа через getErrorCode(), это один из 15 стабильных кодов SystemAiAgentErrorCode.

Docblock команд ставит два условия. Вызывать их нужно вне транзакции вызывающего кода, потому что включение само коммитит запуск процесса, создание ботов и расписаний. И проверять права вы должны сами: команда рассчитана на доверенный серверный вызов, собственной проверки прав у неё нет, в REST и контроллеры она напрямую не опубликована.

Так может выглядеть сервис модуля, который включает и выключает агента для проекта. ProjectAccessChecker в нём ваш собственный интерфейс проверки прав:

        <?php declare(strict_types=1);

namespace Vendor\Module\Application\Service;

use Bitrix\Bizproc\Public\Command\AiAgent\DisableSystemAiAgentCommand;
use Bitrix\Bizproc\Public\Command\AiAgent\Dto\SystemAiAgentActivationParameters;
use Bitrix\Bizproc\Public\Command\AiAgent\Dto\SystemAiAgentContext;
use Bitrix\Bizproc\Public\Command\AiAgent\EnableSystemAiAgentCommand;
use Bitrix\Bizproc\Public\Command\AiAgent\Enum\SystemAiAgentErrorCode;
use Bitrix\Bizproc\Public\Command\AiAgent\Enum\SystemAiAgentLifecycleOutcome;
use Bitrix\Bizproc\Public\Command\AiAgent\Result\SystemAiAgentLifecycleResult;
use Bitrix\Main\Error;
use Bitrix\Main\Loader;
use Bitrix\Main\Result;
use Vendor\Module\Domain\Access\ProjectAccessChecker;

final class ProjectAgentSwitch
{
    // пример системного кода из docblock команды
    private const SYSTEM_CODE = 'bitrix_ai_project_pulse';

    public function __construct(
        private readonly ProjectAccessChecker $access,
    ) {}

    /** @param array<string, mixed> $constants значения констант шаблона агента */
    public function enable(int $projectId, int $userId, array $constants = []): Result
    {
        if (!$this->access->canManage($projectId, $userId)) {
            return $this->denied();
        }

        Loader::requireModule('bizproc');

        // вне транзакции: команда сама коммитит запуск процесса, ботов и расписания
        $result = (new EnableSystemAiAgentCommand(
            systemCode: self::SYSTEM_CODE,
            context: $this->context($projectId),
            userId: $userId,
            activation: new SystemAiAgentActivationParameters(constants: $constants),
        ))->run();

        return $this->explain($result);
    }

    public function disable(int $projectId, int $userId): Result
    {
        if (!$this->access->canManage($projectId, $userId)) {
            return $this->denied();
        }

        Loader::requireModule('bizproc');

        $result = (new DisableSystemAiAgentCommand(self::SYSTEM_CODE, $this->context($projectId)))->run();

        return $this->explain($result, disabling: true);
    }

    private function context(int $projectId): SystemAiAgentContext
    {
        return new SystemAiAgentContext('vendor.module', 'project', (string)$projectId);
    }

    private function denied(): Result
    {
        // команда права не проверяет, это делает вызывающий код
        return (new Result())->addError(new Error('Нет права настраивать проект', 'ACCESS_DENIED'));
    }

    private function explain(Result $result, bool $disabling = false): Result
    {
        if (!$result instanceof SystemAiAgentLifecycleResult) {
            return $result;
        }

        $outcome = $result->getOutcome();

        // already_enabled и already_disabled тоже успех: повтор второй копии не создаёт.
        // cleanup_pending при выключении: удаление сохранено, остаток доделает агент модуля
        if ($result->isSuccess() || ($disabling && $outcome === SystemAiAgentLifecycleOutcome::CleanupPending)) {
            return (new Result())->setData(['outcome' => $outcome?->value]);
        }

        $error = match ($result->getErrorCode()) {
            SystemAiAgentErrorCode::ConfigurationConflict
                => new Error('В проекте агент уже включён с другими настройками', 'AGENT_CONFLICT'),
            SystemAiAgentErrorCode::AgentNotAvailable
                => new Error('Агент на портале недоступен', 'AGENT_NOT_AVAILABLE'),
            default => $result->getError() ?? new Error('Команда не выполнена'),
        };

        return (new Result())->addError($error);
    }
}

    

cleanup_pending приходит в неуспешном результате с кодом AI_AGENT_CLEANUP_PENDING, поэтому пример проверяет его отдельно. При включении тот же исход означает отказ, потому что прежняя копия агента ещё удаляется. Выключение удаляет копию агента целиком: её расписания, работающие процессы, ботов и область хранилища. Один вызов делает ограниченный проход, остаток доделывает агент Infrastructure\Agent\ManagedSystemAiAgentCleanupAgent::runAgent. Он запускается раз в 300 секунд и за проход обрабатывает до 20 экземпляров, тратя на это не больше 30 секунд.

Шаблон выбирает Internal\AiAgent\Lifecycle\Service\SystemAiAgentResolver, и подойти должен ровно один шаблон Nodes из раздела AI_AGENT с этим системным кодом. Какие ресурсы создать, резолвер выводит из графа шаблона (ScheduledTrigger, ImBotCreateBotActivity, ImOpenLinesBotSettingsActivity, CreateStorageNode, WriteDataStorageActivity), коды конкретных агентов в коде не зашиты. Из других модулей эти команды пока никто не вызывает.

Публикуйте шаблон для части сотрудников (за опцией)

Вендор в описании обновления пилот не упоминает. В коде он есть и выключен опцией bizproc:pilot_publication_available (по умолчанию N). Пилот публикует версию шаблона для выбранных сотрудников и отделов, остальные работают по общей версии. В lang дизайнера это сформулировано так: «Остальные сотрудники продолжат работать по старой схеме». Код лежит в Public\Service\WorkflowTemplate\PilotPublicationService, PilotVersionResolver, PilotEditorStateService, Public\Provider\PilotVisibilityProvider и Internal\Service\Pilot\*.

При старте CBPRuntime выбирает версию для инициатора (chooseTemplateVersion()). Автоматические запуски, то есть события документа, роботы, триггеры и дочерние процессы, идут по общей версии. Списки ручного запуска в CBPDocument, admin/bizproc_start.php, lib/restservice.php и lib/controller/workflow/starter.php показывают сотруднику только видимые ему шаблоны.

Место ручного запуска описывает перечисление Starter\Enum\ManualStartSurface: bizproc.start_form, bizproc.trigger_button, bizproc.rest, bizproc.admin_start, iblock.element_form, iblock.element_admin, webdav.element, disk.file. Оно передаётся параметром CBPDocument::PARAM_MANUAL_START_SURFACE = 'ManualStartSurface' и влияет только на выбор версии и диагностику.

Если версия шаблона для сотрудника сменилась, пока он заполнял форму запуска, старт это заметит. В параметры кладётся VersionChoice::PARAMETER_SCHEMA_TOKEN, и при несовпадении CBPRuntime::revalidateChangedVersionParameters() перепроверяет параметры и бросает CBPWorkflowTemplateValidationException. Параметры запуска теперь могут приходить и в виде [$templateId => [...]], рантайм их разворачивает.

В дизайнере под пилот есть getPilotAudienceAction(), changePilotAudienceAction(), stopPilotAction(int $templateId, int $pilotId, array $confirmations = []) и режим публикации «для выбранных» в publicateAction(). Feature::isPilotPublicationAvailable() дизайнера следует за флагом bizproc.

Запускайте Nodes-шаблоны роботом и ставьте условие на триггер

Internal\Service\WorkflowTemplate\ManualStartTemplateAvailabilityService::getAvailableTemplateIds() отдаёт шаблоны нового дизайнера с триггером ручного запуска. Через него робот «Запустить бизнес-процесс» (StartWorkflowActivity) и селектор StartWorkflowTemplateProvider принимают Nodes-шаблоны.

У триггеров и базовых узлов появилась единая панель настроек, её описывает Internal\Service\Activity\UnifiedPanelDescriptorProvider. В панели есть свойство условия NodeCondition, граф детей (Links, InputNames, OutputNames) и списки триггеров без документа и без предстартовой проверки. ApplyRulesChecker::checkNodeCondition() проверяет условие триггера после штатной проверки, и если вычислителя условий нет, запуск запрещается. В дизайнере условие по полям документа триггера собирают ConditionConstructionConverter::convertChain() и ConditionValueResolver::resolveConditionDocumentType().

Остальное новое

  • Любой установленный модуль может объявить в .settings.php секцию bizproc.field-types, где типу поля сопоставлено JS-расширение. Читает её Public\Fields\Registry\FieldTypeRegistry. Новое расширение bizproc.fields (FieldRegistry.register(), BaseField) реализует типы string, text, int, bool, double, date, datetime и select. Оно подключает расширения через post_rel, но ни одно расширение или компонент его пока не использует.
  • Константы в мастере AI-агента бывают теперь «да/нет», «дата» и «дата/время». В template.json штатных агентов у констант появился ключ "Source": "SetupTemplateActivity".
  • Значение колонки таблицы данных теперь может вычисляться формулой (Internal\Service\DataView\ColumnFormula). Это выражение bizproc со ссылками {=Column:CODE} и модификатором вывода. Недетерминированные функции (rand, randstring, shuffle) и функции, привязанные к процессу (getdocumenturl, touserdate, workdateadd и другие), в формуле запрещены. CombineEngine::combine() получил bool $withComputedColumns = true, ColumnResolver::resolve() и DataViewValidator::assertValid() принимают ?array $previousDefinition, FieldValueFormatter::format() принимает string $format = 'printable'.
  • В настройках прав шаблоны грузятся постранично (Internal\Integration\UI\AccessRights\V2\LazyTemplatesOption, опция bizproc:access_rights_lazy_templates, по умолчанию Y), поиск ограничен по числу слов и длине запроса. Для выбора шаблонов добавлен провайдер AccessTemplateProvider (entity bizproc-access-template).
  • Public\Service\SetupTemplate\SetupTemplateService открыт как публичный наследник внутреннего сервиса. SystemTemplateActivationService::startTemplate() принимает array $startParameters = [].
  • Добавлены Activity\BaseTrigger::getEventDocumentComplexType(), CBPActivity::isServedByUnifiedPanel(), Activity\ReturnPropertiesResolver и CBPRuntime::isTriggerActivity(string $activityCode). По docblock последнего суффикс Trigger в имени действия контрактом не считается.
  • В редакторе bizprocdesigner появились автораскладка схемы, вставка узла в существующую связь, подсветка блоков, которые изменил внешний агент, правка заголовка узла и диалог модификаторов значения в редакторе таблиц данных.

Права в дизайнере и REST закрыли плотнее

  • Controller\Diagram: для шаблонов типа Nodes publicateAction(), publicateDraftAction() и getLastRunValuesAction() проверяют ACL шаблона (TemplateAccessService::canPublish() / canEdit()). Раньше проверялось CreateWorkflow на тип документа, для прочих шаблонов так и осталось. Публикация и черновик без templateId требуют ещё canCreate(): по комментарию в коде, проверка области для несуществующего id отвечает «да».
  • updateTemplateAction() открыт держателю права публикации (раньше только админу) и берёт из $data только NAME и DESCRIPTION. По комментарию в коде, иначе держатель PUBLISH мог бы через TYPE, SYSTEM_CODE или DOCUMENT_TYPE вывести шаблон из-под ACL Nodes.
  • Сложный узел проверяет CreateWorkflow на тип документа, который пришёл в свойствах от клиента (Complex::validateResolvedDocumentTypeAccess() в loadSettingsAction(), getCapabilityCatalogAction() и saveSettingsAction()). По docblock, прямой запрос мог назвать любой тип, получить каталог действий в нём и сохранить разрешённые там дочерние действия. Сбой провайдера прав считается отказом.
  • В REST внешнего агента AgentTokenFilter стоит перед IdempotencyFilter. На повтор с тем же ключом идемпотентности IdempotencyFilter отдаёт сохранённый ответ, и при обратном порядке отозванный токен прошёл бы этим путём. Отказ всегда AccessDeniedException, чтобы по статусу ответа нельзя было узнать состояние токена.
  • GraphLimitsValidator ограничивает граф от внешнего агента: MAX_SETTINGS = 8192, MAX_SETTING_VALUE_LENGTH = 65536, MAX_GRAPH_BYTES 2 МБ. Пределы откалиброваны на тестовом портале, где самый большой граф весил 329 КБ, а самое длинное значение заняло 23 611 символов (системный промпт AI-действия). Действуют пределы только для внешнего агента. Для Марты их не включили, по комментарию в коде это «a product decision with a calibration of its own».
  • bizproc: WorkflowTemplateService::prepareStartParameters() спрашивает видимость шаблона до чтения строки, и невидимый шаблон отвечает так же, как несуществующий («same code, same message, same delay»). Кеш коллектора шаблонов по сотруднику не сужается, сужается результат, иначе набор одного сотрудника достался бы следующему.

БД

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

Таблица / индекс Что хранит
b_bp_workflow_template_change Журнал версий: TEMPLATE_ID, EVENT_TYPE, USER_ID, CREATED, VERSION_NUMBER, PUBLICATION_TYPE, SNAPSHOT (mediumblob), TEMPLATE_MODIFIED. Уникальный индекс (TEMPLATE_ID, VERSION_NUMBER), индексы (TEMPLATE_ID, EVENT_TYPE, PUBLICATION_TYPE) и (TEMPLATE_ID, CREATED)
b_bp_workflow_template_pilot Пилотная версия: тип документа, TEMPLATE_DATA, REVISION, автор, дата. Уникальный индекс по TEMPLATE_ID, единственное, что держит правило «не больше одного пилота на шаблон»
b_bp_workflow_template_pilot_access Аудитория пилота (PILOT_ID, ACCESS_CODE) с зеркальной парой индексов
b_bp_managed_agent_instance Управляемые копии системных AI-агентов: IDENTITY_HASH, SYSTEM_CODE, CONTEXT_NAMESPACE/CONTEXT_TYPE/CONTEXT_ID, USER_ID, TEMPLATE_ID, STATE, CONFIG_FINGERPRINT, RETRY_COUNT, NEXT_RETRY_AT, LAST_ERROR_CODE
b_bp_managed_agent_resource Ресурсы копий: INSTANCE_ID, TYPE, RESOURCE_ID, DATA
ix_bp_storage_record_data_template Индекс (TEMPLATE_ID, ID) на b_bp_storage_record_data для пакетного удаления данных копии агента

Обе таблицы реестра агентов обходятся без внешних ключей. После их создания миграция ставит bizproc:ai_agent_registry_schema_ready = Y. Реестр опрашивается при создании и завершении каждого процесса портала, и наличие таблиц на лету не проверяется.

В install/migrations/agents.php добавлен ManagedSystemAiAgentCleanupAgent::runAgent с интервалом 300 секунд. В install/migrations/events.php на bizproc:OnCreateWorkflow, bizproc:OnWorkflowComplete (compatible) и bizproc:onAfterWorkflowKill подписан Internal\AiAgent\Lifecycle\EventHandler\WorkflowLifecycleEventHandler.

Два степпера:

  • Infrastructure\Stepper\TriggerApplyRulesResyncStepper пересобирает APPLY_RULES устаревших триггеров. По docblock, у них правила были пустыми, пустой набор читается как «подходит любой документ», и фильтр по типу сущности и категории не работал;
  • Worker\Template\MarkStubsWithNoCommonVersionStepper помечает созданные редактором заглушки шаблонов как никогда не опубликованные для всех.

Удаление шаблона теперь чистит его журнал версий и черновики (WorkflowTemplateChangeTable::deleteByFilter, WorkflowTemplateDraftTable::deleteByFilter).

Новые опции в миграциях не записываются:

Опция По умолчанию
bizproc:template_version_history_enabled N
bizproc:template_version_limit 10
bizproc:pilot_publication_available N
bizproc:access_rights_lazy_templates Y
bizprocdesigner:version_history_available N

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

  • Rest\Exception\RussianMessageFallback спасает от 500. Ядро REST V3 берёт текст ошибки на языке портала или заголовка X-Bitrix-Rest-Response-Language, а Loc подставляет русскую фразу только для kz, by и uz. На en, de или ua пустая фраза против типа string вызвала бы TypeError, и 400 или 404 превратился бы в 500. Пока перевода нет, трейт откатывается на русский.
  • ClientDraftSaveFailedException реализует SkipWriteToLogException. Отказ по правам (ACCESS_DENIED) или из-за отсутствия шаблона (TEMPLATE_NOT_FOUND) при сохранении черновика не засоряет журнал исключений, а агент получает тот же статус и код, что и при сбое записи.
  • ManagedAgentResourceRegistry зарегистрирован в .settings.php с пометкой «Keep this a singleton». Второй экземпляр отклонил бы старт управляемой копии и не увидел бы операцию оркестратора.
  • CreateStorageNode берёт автора хранилища управляемой копии агента из инициатора процесса. В фоне текущего пользователя нет, и хранилище создалось бы от пользователя 0.
  • Бот bizproc передаёт в ImBotNewMessageTrigger автора сообщения как userId и пропускает сообщения без автора. lib/service/user.php для узла оргструктуры берёт всех сотрудников через getAllEmployees() вместо getPagedEmployees().
  • updateTemplateAction() следит, чтобы новое MODIFIED было строго больше прежнего. Если секунды совпали, время сдвигается на секунду вперёд.
  • Перечисления TemplateChangeEventType (Publication = 1, Creation = 2) и TemplatePublicationType (Common = 1) помечены «persisted in b_bp_workflow_template_change and cannot be renumbered».
  • Под PHP 8.2 "${key}" заменили на "{$key}" в lib/Api/Data/WorkflowTemplateService/WorkflowTemplate.php и lib/basetype/select.php.
  • ClearStuckPauseWorkflowAgent в install/migrations/agents.php попал в дифф только контекстом. Автоочистка зависших процессов пришла в 26.1100.0, в 26.1400.0 её не меняли.

Что делать

  1. Если к дизайнеру подключён внешний AI-агент, переведите его на новые имена методов и JSON-тело запроса без templateId.
  2. Прогоните скрипт по /local/activities на копии с новой версией и поправьте сигнатуры методов, которые совпали по имени с новыми методами CBPActivity.
  3. Поищите в своём коде позиционные вызовы new WorkflowTemplateService(, а также getInterchangeableTriggerPairs, relationsRuntimeAvailable и наследников StartWorkflowTemplateProvider.
  4. Если вы выставляли опции feature_ai_agents, is_rag_available или bitrix_ai_coach_available, включите вместо них соответствующие флаги.
  5. Проверьте отчёты по b_bp_tracking на записи с TYPE = 14.
  6. Историю версий и пилотную публикацию включайте опциями, когда будете к ним готовы.

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

Символ Статус Чем заменять
bizprocdesigner.catalog.list Удалено bizprocdesigner.catalog.block.list, bizprocdesigner.catalog.block.get
Bitrix\Bizproc\Starter\ModuleSettings::getInterchangeableTriggerPairs() Удалено Bitrix\Bizproc\Starter\ModuleSettings::getTriggerUpgradeMap()
bizprocdesigner.document.fields Удалено bizprocdesigner.document.field.list
bizprocdesigner.template.draft.push Удалено bizprocdesigner.template.draft.add

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

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

Подписаться

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

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.1100.0 Безопасность Свежее

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

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

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

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

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

5 мин
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 Домовому.

Войти