bizproc 26.1400.0 + bizprocdesigner 26.800.0: новые REST-методы внешнего AI-агента, дочерние действия в CBPActivity, история версий шаблонов
Ломающее обновление
Удалены или изменены публичные 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(entitybizproc-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: для шаблонов типа NodespublicateAction(),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_BYTES2 МБ. Пределы откалиброваны на тестовом портале, где самый большой граф весил 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 её не меняли.
Что делать
- Если к дизайнеру подключён внешний AI-агент, переведите его на новые имена методов и JSON-тело запроса без
templateId. - Прогоните скрипт по
/local/activitiesна копии с новой версией и поправьте сигнатуры методов, которые совпали по имени с новыми методамиCBPActivity. - Поищите в своём коде позиционные вызовы
new WorkflowTemplateService(, а такжеgetInterchangeableTriggerPairs,relationsRuntimeAvailableи наследниковStartWorkflowTemplateProvider. - Если вы выставляли опции
feature_ai_agents,is_rag_availableилиbitrix_ai_coach_available, включите вместо них соответствующие флаги. - Проверьте отчёты по
b_bp_trackingна записи сTYPE = 14. - Историю версий и пилотную публикацию включайте опциями, когда будете к ним готовы.
Устаревшие и удалённые 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
|