Перейти к содержанию

Runtime SDK

Используйте Runtime SDK в backend-коде, worker, автоматизации или доверенном локальном скрипте.

Готовые UI-компоненты и рецепты помогают собрать приложение под ваш запрос. Они дают редактируемые исходники и готовое управление формами, данными, доступом и ошибками. Можно сочетать блоки разных рецептов, менять компоновку и добавлять собственные экраны с теми же проверенными механизмами. Финальная структура определяется задачей, а установка рецепта требует последующей проверки нужных вам сценариев. Когда в проекте доступна сборка через Agent, он выбирает компоненты и пишет итоговый код; выбирать шаблон вам не требуется.

Для библиотеки обучения подготовлены материалы SDK: диалоги Telegram, AI-помощник, операции, оплата и возврат, каталог с сообществом и подписанные события. Они открывают эту справку вручную и не запускают сборку или действия приложения. Новые материалы ещё ожидают публикации и финальной проверки.

Что это

Runtime SDK дает коду приложения безопасный доступ к возможностям текущего проекта: данным, App Auth guardrails и автоматизациям. Он предназначен для backend-кода, worker, автоматизации или доверенного локального скрипта.

Новый API-сервис получает совместимую копию SDK вместе с исходниками приложения. Отдельно устанавливать или заменять её для первой сборки не требуется. При локальной разработке сохраните эту зависимость и выполните обычную установку зависимостей проекта. Самостоятельно созданные сервисы могут устанавливать SDK из npm; перед обновлением проверьте совместимость нужных методов.

Текущие разделы SDK: auth, context, data, storage, messaging, workflows и workflowRuns. В storage доступны server-side загрузка, проверка статуса, короткая ссылка на скачивание, удаление и безопасная привязка anonymous upload session к business object. Разделов events, jobs и gateway в публичном Runtime SDK нет; не копируйте примеры, которые используют такие вызовы.

messaging.max(ref) выполняет операции бота через сохранённое подключение. Для этого разработчик явно выдаёт права сервису при публикации. Подробнее: MAX из Runtime SDK. Новые исходники добавляют чтение подключений, принятых событий, существующих App User и прежних квитанций MAX. Они не регистрируют подписку и не отправляют сообщение повторно; публикация SDK и native-проверка пока ожидаются.

Служебные коллекции платформы закрыты для приложения. Ограничения прямых запросов описаны в Безопасных запросах к данным.

Для Telegram подготовлен серверный runtime.messaging.telegram(ref). Через сохранённое подключение можно отправлять и изменять сообщения, отвечать кнопкам, использовать native file ID/URL, читать бот/чат и настраивать меню команд. Разработчик явно выдаёт права текущей ревизии сервиса: чтение, отправка, файлы или команды. Без выдачи этих прав доступ отсутствует; токен бота остаётся на сервере.

Для изменения передавайте исходный ключ намерения. Подтверждённый результат повтора читается из журнала; неизвестный результат требует сверки и не допускает автоматической повторной отправки. Подтверждённое ограничение частоты позволяет явно повторить прежний ключ после указанной задержки. Не заменяйте неизвестное действие новым ключом. Права приложения и допустимые действия внутри чата проверяются отдельно. Исходники ещё требуют публикации/миграций и окончательной проверки настоящего бота; бинарный перенос файлов и полноценный диалог — следующий этап.

Для приватных действий бота resolveUser(telegramUserId) читает уже существующую связь Telegram с App User в этом realm, окружении и боте. Метод не создаёт аккаунт и не присваивает права по имени, телефону или содержимому сообщения. Если связи нет, предложите пользователю войти через настроенное mini app. После входа проверяйте текущие права App User и принадлежность к данным перед каждым действием. Поиск связи ещё требует окончательной проверки Runtime API и публикации SDK.

getEvent(updateId) читает сохранённое событие из подтверждённого входящего журнала этого подключения. Передавайте в обработчик только ссылку на событие: пользователь, чат и содержимое берутся с сервера. Метод требует права чтения Telegram; ID события не даёт прав App User. События хранятся 30 дней, ожидающие доставки защищены от очистки. recoverAction(originalKey) читает результат исходной операции для того же сервиса и подключения, без новой отправки. Для чтения результата нужны право чтения и право на исходную операцию.

Для анкеты бота подготовлено редактируемое ядро диалога. Агент сам задаёт поля, вопросы, варианты ответа и итоговый сценарий под вашу задачу. Общая основа проверяет ответы на текущем шаге, возвращает назад с очисткой последующих ответов, отменяет диалог и предлагает проверить анкету перед отправкой. При повторе того же события шаг не выполняется второй раз.

Нажатие «Отправить» сначала сохраняет ожидающее действие. Успех показывается после подтверждения записи или другого бизнес-результата; неизвестное исполнение требует сверки. Подготовленный серверный модуль связывает подтверждённые события, текущие права и приватное хранилище диалогов. Агент заполняет botPolicies и сам пишет сообщения, кнопки и сценарий; пустая настройка не принимает события. Текстовый ответ привязывается к сообщению с вопросом, кнопка — к версии диалога. Старый ответ не должен переходить на следующий вопрос. Сервер сохраняет шаг и очередь сообщений одной условной записью, worker читает квитанцию отправки по исходному ключу. getConnection() проверяет, что имя подключения по-прежнему указывает на исходного бота; метод не вызывает Telegram.

При обновлении анкеты сохраняйте её прежнюю версию для уже начатых диалогов. Новые диалоги могут получать дополнительные вопросы на той же команде бота, а старые продолжают исходную анкету и обработку заявки. Текущие права проверяются при продолжении и повторной доставке событий. Изменение или удаление прежней версии останавливает её продолжение до явного решения разработчика.

Для итоговой заявки разработчик настраивает её обработчик: автоматизацию и опубликованную версию, допустимые поля, права и проверку созданной записи. Отправка анкеты сохраняет это решение и данные вместе со шагом диалога. Worker запускает обработчик по прежнему ключу и читает его результат при потере ответа. Перед запуском и сверкой проверяются текущие права и принадлежность данных. Успех автоматизации сам по себе не показывает «Заявка создана» — сначала нужно подтвердить запись. Неизвестная попытка остаётся на сверку и не создаёт вторую заявку. Место для итогового ответа резервируется до запуска. Для доставки в сервис настраивается внутренний адрес, отдельный секрет и сохранённое HTTP-подключение автоматизации. Передаётся только ID уже принятого события; токен Telegram и данные входа пользователя не пересылаются в запросе. Ответ «Принято» означает сохранение события в приложении.

Настроенный сервис в роли worker обрабатывает заявки и сообщения небольшими порциями. При сохранении события worker в этом сервисе пробуждается сразу; периодический проход подбирает работу после восстановления. Ожидающая заявка не задерживает ответы другим диалогам. Подтверждение отправленного вопроса сохраняется в том же проходе, чтобы ответ пользователя можно было сразу связать с ним. При остановке новые проходы прекращаются, а сохранённые попытки продолжаются по прежним ключам после восстановления. Исходники подключены к сервису, но ещё требуют окончательной проверки настоящего бота и публикации соответствующих API/SDK; готовым ботом их считать пока нельзя.

Разработчик может включить приватный архив завершённых диалогов. Он освобождает место для новых заявок и сохраняет историю вместе с исходными квитанциями. Повтор уже принятого события после переноса не отправляет сообщение и не создаёт заявку ещё раз; текущие права всё равно проверяются. Неопределённые отправки и действия остаются на сверку. Для неиспользованных вложений разработчик может отдельно разрешить очистку. Проверяются права текущего пользователя, принадлежность файла и отсутствие его в сохранённых ответах и действиях. Очистка запускается только после отдельного серверного решения; ошибка или отмена анкеты сама по себе ничего не удаляет. Потеря ответа на удаление оставляет файл на сверке по исходному подтверждению. Подтверждение закрывает доступ к файлу, а физическое удаление выполняется отдельно по правилам хранилища. Если перенос ещё не подтверждён, обработка новых событий временно ожидает завершения архива. В базовом исходнике архив ограничен 10 000 исходных событий; для большего объёма разработчик переносит историю с сохранением защиты от повторных действий. Архивирование входит в написанные исходники сервиса и также ожидает финальной проверки и публикации.

При ошибочном ответе бот объясняет, что исправить, и остаётся на текущем вопросе. Исправленный ответ можно дать прямо на его новое сообщение. Старую подсказку не отправляют после перехода на следующий шаг. Нажатие кнопки подтверждается отдельно: это снимает ожидание в Telegram и не означает создания заявки. Повтор доставки ошибки или кнопки использует прежнюю квитанцию и ключ.

Та же серверная Telegram-основа подготовлена для Python и Go. В Python используйте client.messaging.telegram(ref) и методы get_connection, get_event, resolve_user, recover_action; в Go — client.Messaging.Telegram(ref) и GetConnection, GetEvent, ResolveUser, RecoverAction. Операции используют сохранённое подключение и права Runtime, токен бота в код приложения не передаётся. Для подготовленного запуска автоматизации сохраняйте исходный ключ и ожидаемую опубликованную версию; потерянный ответ восстанавливайте чтением исходного запуска. Источник SDK дополнен, но версии пакетов и окончательная проверка ещё впереди.

Для бинарных файлов Telegram подготовлен серверный обмен через Files. Импорт использует обычную проверку типа файла, сканер и хранилище; повтор сверяет исходную загрузку и байты. Нужна ещё окончательная проверка настоящей передачи и публикация API/SDK; готовность обмена по исходникам не подтверждается.

Серверные API и исходники методов файлового обмена теперь подключены. Для импорта используйте importFile в JS, import_file в Python или ImportFile в Go: передайте ID подтверждённого события, фото/документ, бакет и сохранённый ключ. Файл определяется по событию, а его тип — по байтам. Токен Telegram и адрес скачивания не передаются в приложение или браузер.

Для отправки используйте sendStoredFile, send_stored_file или SendStoredFile: укажите приватный файл Files, чат, тип отправки, подпись/кнопки и прежний ключ. Сервер сверяет владельца, состояние и все байты; приватная ссылка не нужна. Повтор неизвестной отправки требует чтения её исходного результата, а не нового ключа. Доступ к файлу проверяется и при восстановлении.

Разработчик отдельно проверяет текущего App User, его права, принадлежность файла к записи и tenant, а также право отправки в выбранный чат. Права Runtime-сервиса эти проверки не заменяют. Files и нужные Runtime-права должны быть включены. Публикация API/SDK, сценарий вложений диалога и настоящая проверка обмена ещё впереди; наличие исходников не подтверждает работающую передачу.

Для вопроса анкеты теперь подготовлен тип «Файл»: разработчик задаёт бакет, документ или фото, размер и допустимые форматы. Пользователь отвечает вложением на сообщение с вопросом. Бот сначала сохраняет намерение и проверяет файл в Files; следующий вопрос появляется после подтверждения. Если пользователь уже отменил анкету или изменил шаг, завершившаяся загрузка не перезапишет новый ответ.

Файл с неподходящим форматом или размером не становится ответом. Возврат назад удаляет его из дальнейших ответов, а физическое удаление требует отдельного разрешённого действия. Перед отправкой заявки текущие права и состояние вложений проверяются снова. Настоящая проверка бота, восстановление и публикация ещё впереди.

Для сверки импорта есть отдельное чтение по его исходному ключу: recoverFileImport, recover_file_import или RecoverFileImport. Оно не начинает новую загрузку. После отмены диалога бот сверяет только прежнее исполнение; неизвестный результат не создаёт файл для отменённого вопроса.

Вход в mini app через Telegram

Для mini app подготовлен отдельный browser adapter @nsp-labs/agnostic-app-kit/telegram. createTelegramMiniApp() читает подписанный getInitData() и доступные функции клиента: кнопку «Назад», тему, высоту окна и безопасные отступы. Отображаемые имя и язык являются только подсказками; права берутся из подтверждённой сессии приложения.

Разработчик на сервере включает Telegram в App Auth realm и выбирает сохранённое подключение отдельно для каждого окружения. Пользователь mini app не выбирает бота, realm или технические настройки. В браузере используйте явное действие session.loginWithTelegram(mini.getInitData()) общего контроллера сессии. BFF передаст proof серверу, проверит полученную сессию и установит защищённые cookies. Токены Telegram и Runtime SDK в браузер не передаются.

Подписанный запуск используется один раз. После неопределённого ответа сначала прочитайте текущую сессию; после истечения или повторного использования proof откройте mini app заново через Telegram. Не повторяйте вход автоматически и не используйте initDataUnsafe вместо проверки. Выход очищает приватные данные и не позволяет позднему ответу восстановить интерфейс вошедшего пользователя.

Исходники и тестовые сценарии подготовлены; подключение, миграции, публикация пакета и проверка настоящего клиента ещё должны быть завершены перед запуском. Приём событий бота, диалоги, файлы и доставка сообщений входят в отдельный этап.

Приём событий Telegram через автоматизацию

Для узла «Событие Telegram» подготовлена управляемая привязка опубликованного сценария к сохранённому боту и окружению. Worker регистрирует собственный webhook и проверяет его состояние. Один бот использует один webhook: чужой адрес не перезаписывается автоматически. Новый токен подключения должен принадлежать тому же боту.

Событие сначала сохраняется вместе с получателями и версией сценария, затем подтверждается его приём. Повтор одного update_id не создаёт новое выполнение; при временной недоступности очереди восстанавливается исходный запуск. Подтверждение приёма не означает, что сценарий завершился или сообщение отправлено. Идентификатор пользователя Telegram не заменяет права App User.

При удалении подключения снимается только собственный webhook; данные уже принятых событий сохраняются до срока очистки. Регистрация, миграции и проверки этого механизма ещё не пройдены. Полный диалог, управление файлами и подтверждённая доставка сообщений требуют следующих адаптеров и отдельной приёмки.

Разработчик может прочитать состояние приёма через scoped API подключения telegram-ingress. «Не привязано» означает, что токен может быть действительным, но вход событий ещё не подготовлен. «Ожидает» показывает незавершённую сверку публикации и webhook; «Активно» относится к подтверждённой регистрации нужных событий. Этот статус не подтверждает выполнение каждого сценария или доставку исходящего сообщения. Ошибка показывает безопасный код и время следующей сверки.

Заказы, магазин и остатки

Подключите createInventoryAdapter из App Kit к API приложения и используйте рецепты «Заказы и ресурсы» и «Каталог и покупки». На сервере заранее подготовьте каталог, приватную запись склада и права пользователей. Права изменения каталога и остатков, работы с заказами и выдачи ресурсов назначаются отдельно.

  1. Создайте черновик заказа и добавьте позиции из каталога или соберите корзину.
  2. Проверьте цену и количество. «Зарезервировать ресурсы» или «Оформить заказ»

удерживает доступный остаток для всех позиций одной операцией.

  1. «Освободить резерв» возвращает заказ в черновик; отмена освобождает позиции.
  2. Кладовщик выбирает «Выдать ресурсы». После подтверждения физический остаток

уменьшается на выданное количество. Повтор того же запроса не выдаёт товар снова.

В разделе «Ресурсы и остатки» можно добавить товар, изменить цену и доступность, оформить приход или расход с причиной. Расход не может затронуть уже подтверждённый резерв. Цены хранятся в целых копейках; браузер не определяет итоговую сумму. Зарезервированный заказ сохраняет принятую цену.

Если каталог изменился, обновите корзину, сравните сумму и повторно подтвердите оформление. При конфликте в форме склада введённые данные сохраняются: прочитайте текущий ресурс и явно примените изменение к новой версии. После потери ответа повторяйте прежнее действие из открытой формы. Когда действие принято, повторное обновление выполняет только чтение.

Оформление создаёт заказ с резервом. Оплата показана как «Не подключена», пока нет отдельно проверенного платёжного сервиса. Доставка, скидки, возврат оплаты и операции между несколькими складами требуют отдельных адаптеров.

Базовый склад ограничен 100 товарами, 200 заказами, 500 квитанциями действий и 256 КиБ состояния. При достижении лимита требуется архив с сохранением квитанций; история не удаляется автоматически. Перед включением проверьте права, одновременный резерв последней единицы и восстановление после потери связи.

Запись и бронирование времени

Подключите createBookingAdapter из App Kit к API приложения и рецепт «Запись и бронирование». На сервере подготовьте ресурсы, интервалы времени, вместимость, длительность резерва и правила отмены. Права записи и просмотра своих бронирований назначаются отдельно от прав сотрудника на чужие записи. Время хранится в UTC, а календарь показывает выбранный часовой пояс со смещением относительно UTC.

  1. Выберите доступное время и нажмите «Зарезервировать». Сервер удержит место

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

  1. Введите имя и контактный email, затем подтвердите запись до окончания

резерва. Email сохраняется как контакт; письмо автоматически не отправляется.

  1. Чтобы перенести запись, загрузите доступное время и подтвердите новый

интервал. Если он занят, прежняя запись сохранится. Перенос резерва не продлевает его срок.

  1. Отмените запись, пока это разрешают правила сервиса. После отмены место

становится доступным.

Срок проверяет сервер. Подтверждённая запись продолжает занимать место после окончания первоначального резерва; просроченный неподтверждённый резерв подтвердить нельзя. При изменении записи сравните время и явно выберите «Принять обновлённую запись»: введённый контакт сохранится. Если действие принято, а обновление страницы не удалось, повторите только чтение результата.

Базовый календарь поддерживает до 50 ресурсов, 200 интервалов, 200 записей, 500 квитанций действий и 256 КиБ состояния. История не удаляется автоматически. Один пользователь может иметь до 10 активных записей, включая резервы и подтверждённые бронирования. Резерв настраивается на сервере от 15 секунд до 15 минут. Перед включением проверьте права, конкурирующие записи, истечение срока, перенос и восстановление после потери ответа. Оплата, напоминания и рассылка требуют отдельных подключений.

Задачи, исполнители и календарь

Подключите createPlanningAdapter из App Kit к API приложения и рецепт «Планирование». На сервере подготовьте поля задачи, допустимый порядок статусов, участников и права. Список исполнителей формируется по действующей авторизации и принадлежности к команде; пользователь не может назначить произвольный аккаунт.

В Agnostic откройте «Помощь» → «Материалы обучения» → «Добавить задачи и планирование». Кнопка «Подключить задачи и планирование» открывает это руководство.

  1. Создайте задачу с названием, описанием и явно выбранной датой.
  2. Назначьте исполнителя, если это разрешено вашей роли. Для внутренних задач

можно передавать ему владение: тогда участник видит свои задачи, а руководитель — задачи команды. Снятая задача возвращается текущему руководителю.

  1. Перемещайте задачу по доступным этапам на канбане или через поле статуса

в карточке. Управление клавиатурой и перетаскивание используют те же правила.

  1. Измените календарную дату отдельным действием. Это задача на весь день;

часовой пояс не превращает выбранный день в другое число.

Если задача изменилась, сравните поля с черновиком и явно выберите продолжение или загрузку версии сервера. Изменение одного поля сохраняет другие введённые значения. Когда действие принято, но данные не обновились, «Повторить обновление» повторяет только чтение, сохраняя результат действия и черновик.

Базовый список ограничен 64 участниками. История задач имеет заданный на сервере лимит и не удаляет квитанции автоматически. Перед включением проверьте права, передачу задачи, одновременное изменение, повтор запроса и потерю ответа. Расписание на точное время, вместимость, напоминания и календарные интеграции подключаются отдельно.

База знаний и публикация статей

Чтобы собрать приложение, в режиме «Собрать» опишите нужную базу знаний и права читателей, редакторов и тех, кто публикует статьи. Добавление отдельной статьи или изменение данных существующего приложения остаётся обычной операцией.

Подключите createContentAdapter из App Kit 0.11 к API приложения. Используйте готовый редактируемый экран Content или соберите свои список, статью и редактор. Серверу нужны отдельные права на чтение, редактирование и публикацию, проверенная принадлежность к команде и приватное хранилище базы знаний.

  1. Создайте статью с заголовком и разделом. Родительская статья задаётся при

создании; заголовок и положение в дереве в базовом адаптере сохраняются.

  1. Сохраните текст как черновик. Он доступен редакторам; читатели его не получают.
  2. Опубликуйте сохранённую редакцию с правом публикации. Дочернюю статью можно

опубликовать после родительской.

  1. При следующих правках читатели продолжают видеть опубликованный текст.

История позволяет восстановить прежний текст как новый черновик; публикация после восстановления выполняется отдельным действием.

Текст отображается как обычный текст: HTML и скрипты не выполняются. Черновики, история и время частных изменений не попадают в ответ читателю. Перед показом экрана получите свежий контекст адаптера; его identity разделяет запросы и черновики при смене пользователя или прав. При конфликте сравните версии и явно выберите дальнейшее действие. После принятого сохранения и ошибки чтения повторяйте только обновление данных.

Список можно отфильтровать одновременно по разделу и состоянию публикации. У читателя в результат входят опубликованные статьи; у редактора доступны также черновики и статьи с новой неопубликованной редакцией. Фильтры применяются ко всему списку перед подсчётом и разбиением на страницы. При смене фильтра начните с первой страницы; пустой фильтр раздела показывает все разделы.

Базовая база знаний ограничена 50 статьями, 200 редакциями суммарно, 500 квитанциями действий, 20000 символами текста одной редакции, глубиной дерева 10 и общим размером 256 КиБ. История и квитанции не удаляются автоматически. Для большего объёма нужен отдельный архивный адаптер. Форматирование текста, медиа, полнотекстовый поиск и изменение дерева подключаются отдельно. Перед включением проверьте публикацию, права читателя, конфликт сохранения, восстановление и повтор после потери ответа через реальный API приложения.

Обращения и переписка

Для собственной службы поддержки используйте createSupportAdapter из App Kit 0.10 с API приложения. Экран можно собрать из отдельных компонентов: очередь, карточка клиента, переписка и форма ответа. Готовый рецепт помогает начать; состав экранов и нужные действия определяет задача приложения.

На сервере подготовьте обращения, участников команды, права клиента и сотрудника, состояния и срок ответа. Первое сообщение создаётся вместе с обращением; имя и email клиента берутся из подтверждённой авторизации. Ответ сотрудника переводит обращение в ожидание клиента. Следующее сообщение клиента возвращает его в очередь с новым сроком ответа. Сотрудник может назначить исполнителя, отложить обращение или закрыть его, если это разрешено. Назначение сотрудника сохраняет доступ исходного клиента к своему обращению. Изменяйте обращение через действия ответа, заметки, назначения, отложения и смены состояния. Общее обновление полей обращения через API заблокировано, чтобы каждое изменение проходило предусмотренные проверки и сохраняло квитанцию.

Внутренние заметки доступны только сотрудникам с отдельными правами на их чтение и запись. Клиент получает публичную переписку без текста этих заметок. Просрочку определяет сервер; часы на устройстве пользователя не меняют срок. При конфликте обновите карточку и сравните изменения перед новым действием. Подтверждённое сообщение не отправляйте снова из-за ошибки последующего обновления: повторите только чтение результата.

Для подключения готового редактируемого экрана из App Recipes 0.9 передайте проверенный серверный контекст и ответ адаптера как replyRecord. При смене пользователя или его прав перечитайте контекст: его identity разделяет кэш, формы и черновики. Сам экран также можно собрать из отдельных панели свойств и переписки. Поле отложения задаётся явно в UTC; допустимый срок проверяет сервер. Выбор «Продолжить с черновиком» после сравнения обновляет версию и сохраняет несохранённые поля. Загрузка версии сервера заменяет черновики только после подтверждения. После успешной записи экран сохраняет возможность повторить обновление, даже если новое состояние уже запрещает это действие.

Этот адаптер сохраняет переписку внутри приложения. Отправку по email, доставку в мессенджер, уведомления и обновления в реальном времени подключайте отдельно и проверяйте для выбранного канала. Перед включением службы поддержки проверьте права, сохранение черновиков, конкурирующие ответы, повтор запроса и восстановление после потери ответа. История ограничена настройкой сервера и 256 КиБ; квитанции действий не удаляются автоматически.

Комментарии и согласование заявки

Готовый рецепт согласования показывает очередь, карточку, комментарии и историю. Подключите createRecordActionsAdapter из App Kit к серверному API приложения. До запуска подготовьте поля заявки и истории, начальный статус «Ожидает решения» и права пользователей приложения. Публичная форма редактирует только разрешённые поля; статус меняется отдельным решением согласующего.

  1. Создайте заявку и откройте её карточку.
  2. Оставьте комментарий, если у вашей роли есть это право.
  3. Согласующий выбирает «Согласовать» или «Отклонить» и указывает причину.
  4. Проверьте статус и историю после повторного открытия карточки.

Если другой сотрудник уже изменил заявку, сравните актуальную карточку со своим черновиком. Когда решение уже принято, второе противоположное решение не сохранится. После потери связи повторяйте то же действие из открытой формы: сохранённое решение не должно создавать ещё один комментарий. По умолчанию пользователь не согласует собственную заявку.

Одношаговое согласование поддерживает одно решение по заявке. Перед включением проверьте доступ двух ролей, одновременные решения и повтор после потери ответа.

Последовательные этапы обработки

Для нескольких этапов подключите createRecordActionsAdapter.transition и настройте разрешённые переходы и права на сервере приложения. Карточка получает availableActions: только действия, доступные вашей роли и текущему статусу. Таким же способом можно настроить этапы задач, обращений и публикации материалов. Переход меняет статус и сохраняет комментарий с историей вместе. Обычное редактирование и перенос карточки не обходят правила перехода.

  1. Откройте карточку и выберите доступное действие.
  2. Укажите комментарий, если он обязателен, и подтвердите действие.
  3. Следующий участник открывает обновлённую карточку и выполняет свой этап.
  4. Проверьте статус и историю после повторного открытия.

При необходимости сервер требует разных участников для последовательных согласований. Одни и те же права у сотрудника не позволяют согласовать оба этапа вместо другого участника. Также можно запретить согласование собственной заявки.

При конфликте нажмите «Сравнить с сервером». Черновик комментария сохраняется. Если действие ещё доступно, выберите «Применить актуальную версию» и подтвердите его снова. Если другой участник уже завершил этап, закройте форму и прочитайте историю. После потери связи повторите прежнее подтверждение из открытой формы. Если действие принято, но карточка не обновилась, «Обновить карточку» повторяет только чтение.

Перед включением проверьте последовательность этапов, разные роли, одновременные решения, отзыв прав и потерю ответа. Ограниченная история сохраняет квитанции; при достижении лимита новые действия требуют подготовки архива. Автоматические эскалации по времени и операции над несколькими записями подключаются отдельно.

Когда использовать

  • Backend должен читать или записывать данные проекта.
  • Нужно запустить автоматизацию из кода.
  • Backend должен привязать файлы публичной формы к созданному заказу.
  • Backend должен загрузить, проверить, скачать или удалить файл бакета.
  • Нужно написать локальный скрипт для доверенного окружения.

Перед началом

  • Установите пакет в backend-проект или доверенный локальный скрипт:

npm install @nsp-labs/agnostic-sdk.

  • В опубликованном backend-сервисе Runtime SDK context добавляет платформа при публикации.
  • Для доверенного локального скрипта передайте Runtime SDK context через безопасную локальную настройку.
  • Используйте SDK только там, где runtime-токен не попадет к пользователю.
  • Не добавляйте runtime-токен в публичный frontend, мобильное приложение, статические файлы, скриншоты или логи.
  • Для пользовательских браузерных сценариев используйте обычный user-session API или App Auth.

Вход и профиль в приложении

В Web-каталоге есть готовый shared/AppAuthView.tsx: регистрация, подтверждение почты кодом, вход, восстановление пароля и личный профиль. Подключите createSessionController и createAppAuthController из App Kit к вашему серверному BFF. Для существующего приложения сначала посмотрите preview обновления каталога, чтобы сохранить ручные изменения.

До запуска нужны активный App Auth проекта, разрешённый адрес приложения, работающая отправка почты и серверный Runtime context с правом проверки сессии. Email приходит с кодом: его вводят в форму подтверждения или сброса. После сброса пароля прежние сессии отзываются; пользователь входит с новым паролем.

В API-шаблоне app-auth-bff.ts токены сессии и обновления остаются в HttpOnly cookies. Браузер получает профиль и права из проверенной сессии. В профиле можно изменить имя и адрес изображения; новый email нужно отдельно подтвердить кодом на новой почте. Назначение ролей и прав выполняется администратором приложения, а не через форму личного профиля.

AppAuthGate открывает приватную часть после проверки входа. Действия кабинета отображайте по hasAppScopes / AppScopeGate, а canWrite рецепта задавайте из этой сессии. Сервер всё равно проверяет каждый запрос, владельца записи и окружение. При смене пользователя или прав App Kit очищает прежние данные и зарегистрированные черновики. Пароли и почтовые коды не сохраняйте в drafts.

Проверьте весь путь: регистрация → реальное письмо → подтверждение → вход → изменение профиля → перезагрузка → выход. Отдельно проверьте сброс пароля, отзыв старых сессий, чужой одноразовый код, пользователя без прав и мобильную ширину. Работающая форма без серверной проверки доступа ещё не подтверждает готовность.

Как сделать

  1. Откройте страницу ресурса или раздел кода, где есть пример SDK.
  2. Выберите язык.
  3. Скопируйте установку и пример.
  4. Проверьте, что код выполняется в backend или доверенном окружении.
  5. Опубликуйте backend-сервис или передайте runtime context в доверенный локальный запуск.

Если код создает новый продукт с помощью Agent, сначала подтвердите создание нужных таблиц, полей, App Auth или автоматизаций. SDK работает с уже созданными ресурсами во время выполнения приложения и не заменяет эти действия настройки.

При каждой новой генерации Agent сверяет код с описанием SDK из установленной версии пакета. Поэтому после обновления SDK он использует актуальные разделы и методы, а не старый пример из предыдущего диалога. Если описание SDK недоступно или нужного метода в нем нет, Agent должен показать ограничение и не подставлять несуществующий вызов.

После подтверждённого создания или изменения Data локальный агент получает карточку таблицы с её текущим техническим именем и примерами именно из установленной версии SDK. Для таблицы leads базовые операции выглядят так:

const leads = agnostic.data.table('leads').records;

const page = await leads.list();
const totals = await leads.aggregate({
  groupBy: ['status'],
  metrics: [
    { operation: 'count', as: 'leads' },
    { operation: 'avg', field: 'score', as: 'averageScore' },
  ],
  orderBy: [{ by: 'leads', direction: 'desc' }],
  limit: 20,
});
const created = await leads.create({ email: 'user@example.com' });
await leads.update(created.id, { email: 'new@example.com' });
await leads.delete(created.id);

Подключение формы к реальным данным

В шаблоне API-сервиса есть серверный обработчик src/app-data-bff.ts и настройки ресурсов src/app-resources.ts. Перед подключением формы укажите техническое имя существующей таблицы, разрешённые поля, права чтения и записи. Пример orders требует поля id, ownerId, creationReceipt, title и status. Разрешения App User назначаются администратором; самостоятельно зарегистрированный пользователь не получает доступ к данным.

Браузер обращается к BFF через App Auth session и CSRF. Сервер проверяет сессию, применяет права и фильтр владельца и только затем вызывает Runtime SDK. Runtime token остаётся в сервисе. Если приложение разделяет данные организаций, настройте серверную проверку членства в организации: ID из формы не является подтверждением доступа.

Обработчик возвращает плоскую карточку для App Kit: разрешённые поля, id и строку version. App Kit 0.3.1 и App Recipes 0.2.2 передают эту строку при сохранении без преобразования в число. Табличный список, поиск и сортировка используют только разрешённые поля; одна страница содержит не более 100 записей.

Для создания передавайте idempotencyKey: повтор того же запроса вернёт сохранённую карточку, а другой запрос с тем же ключом получит конфликт. При редактировании передавайте expectedVersion из полной карточки. Удаление, переходы заказа, оплата, файлы и составные операции требуют отдельного прикладного обработчика с проверкой прав и результата.

Проверьте сценарий двумя пользователями: создайте запись, перечитайте её после перезагрузки, убедитесь, что второй пользователь не получает чужую карточку, и попробуйте сохранить две правки с одной версией. Сервер должен принять одну правку и показать конфликт для второй; готовый UI без такой проверки ещё не подтверждает рабочее приложение.

Сохранение при одновременном редактировании

В SDK 2.1.0 и новее, с обновлённым backend платформы, полное чтение карточки возвращает revision. Сохраните эту версию вместе с изменениями:

const { records } = await leads.query({ filter: { id: leadId }, limit: 1 });
const record = records[0];
if (!record?.revision) {
  throw new Error('Не удалось получить версию карточки для сохранения');
}
await leads.update(record.id, {
  values: { email: 'new@example.com' },
  expectedRevision: record.revision,
});

Если содержимое изменилось, сервер вернёт DATA_RECORD_STALE (HTTP 409). Перечитайте карточку, покажите новые данные пользователю и сохраните выбранные изменения с новой версией. Не повторяйте старое сохранение автоматически. После успешного сохранения ответ содержит версию сохранённого содержимого. При сетевом сбое сначала перечитайте карточку и проверьте результат.

Версия выдаётся при чтении без fields и относится к конкретной карточке, таблице, проекту и окружению. Частичная выборка не возвращает версию. Старые вызовы без expectedRevision продолжают работать. В Python параметр называется expected_revision; в Go используйте UpdateRecordInput.ExpectedRevision.

Проверку роли и владельца карточки выполняет backend вашего приложения перед вызовом SDK. Версия отражает содержимое одной карточки: для бронирования, складских остатков и оплаты нужны отдельные правила целостности всей операции.

Используйте имена полей из актуальной карточки таблицы. Эти методы работают с записями уже созданного ресурса; изменение схемы по-прежнему требует отдельного действия и подтверждения в Agnostic.

aggregate считает показатели без загрузки всех записей в backend-код. Можно использовать count, sum, avg, min и max, сгруппировать результат максимум по двум полям и получить до 100 групп. Для sum и avg выбирайте числовое поле. Проект и окружение метод берет только из текущего Runtime context.

Чтобы читать только нужные поля, используйте records.query в SDK 1.5.0 или новее после обновления backend платформы:

const page = await agnostic.data.table('leads').records.query({
  fields: ['email'],
  filter: { id: { in: leadIds } },
  limit: 50,
});

id записи возвращается всегда. Без fields возвращаются все поля. Фильтр и сортировка могут использовать поля, которые не включены в ответ. Для следующей страницы передайте nextCursor с теми же полями и условиями; при смене условий начните загрузку заново. В in допускается до 50 значений, на странице — до 500 записей. Большие списки идентификаторов разбивайте на части.

Для нескольких независимых выборок используйте agnostic.data.batchQuery:

const { results } = await agnostic.data.batchQuery([
  { tableName: 'leads', query: { fields: ['email'], limit: 50 } },
  { tableName: 'orders', query: { fields: ['status'], limit: 50 } },
]);

Ответы идут в порядке запросов. Каждая выборка имеет собственный курсор. Допустимо до 20 выборок и суммарно до 1000 запрашиваемых записей; без limit выборка учитывается как 50 записей. Если одна выборка завершится ошибкой, весь пакет вернёт ошибку. Это не транзакционный снимок нескольких таблиц. Методы только читают данные и не добавляют кэш. Они не заменяют проверку прав пользователя в вашем backend и не предназначены для вызова из браузера.

Для records.create и records.update допустим JSON-запрос до 2 МиБ (2 097 152 байта), включая имена полей и служебную оболочку запроса. Это размер JSON в UTF-8, а не число символов. Для aggregate и остальных обычных API-запросов сохраняется лимит 512 КиБ. Превышение возвращает ошибку PAYLOAD_TOO_LARGE (HTTP 413); повтор без уменьшения запроса не поможет. Ограничения типов полей, хранилища и права доступа продолжают действовать. Для новых крупных вложений используйте файловое хранилище и сохраняйте ссылку в записи.

При одновременной записи данных запрос может ожидать завершения другой операции, чтобы сохранить корректность связей. Если ожидание превышает 15 секунд, сервер возвращает DATA_RELATION_WRITE_BUSY (HTTP 409). Уменьшите число параллельных записей. Не считайте любой HTTP 409 признаком того, что запись уже существует: проверяйте код ошибки. При сетевом тайм-ауте сначала проверьте состояние записи; повторяйте изменение только если оно безопасно при повторном выполнении.

После подтверждённой публикации автоматизации локальный агент получает её техническое имя и примеры запуска из установленной версии SDK. Например:

const run = await agnostic.workflows.start('lead-enrichment', {
  leadId: 'lead-42',
});

const finished = await agnostic.workflowRuns.wait(run.id);
const steps = await agnostic.workflowRuns.listSteps(run.id);

start запускает опубликованную версию в текущем runtime-окружении. wait дожидается конечного статуса, а listSteps позволяет разобрать результат по шагам. Используйте поля входных данных из актуальной карточки автоматизации и не передавайте в payload ключи или пароли.

Для запуска, который нужно восстановить после потери ответа, сохраните один ключ и передайте его в заголовке Idempotency-Key:

const requestKey = crypto.randomUUID();
const run = await agnostic.workflows.start(
  'lead-enrichment',
  { leadId: 'lead-123', source: 'landing' },
  { idempotencyKey: requestKey },
);

Ключ должен содержать от 8 до 128 букв латинского алфавита, цифр, двоеточий, подчёркиваний или дефисов. Одновременные одинаковые запросы возвращают один запуск. Повтор с изменёнными данными или контекстом участника возвращает конфликт: для нового действия нужен новый ключ. Без ключа каждый запрос создаёт самостоятельный запуск.

После потери ответа доверенный сервер может прочитать принятый запуск через agnostic.workflows.recoverStart('lead-enrichment', requestKey). В SDK 2.2 этот метод передаёт исходный ключ и действующую Runtime-авторизацию сервера. Такое чтение не создаёт новый запуск. Запуск доступен исходному серверному участнику в той же среде, в том числе после замены его токена. Повтор сохраняет первоначальную версию автоматизации; новый ключ использует актуальную опубликованную версию.

Если ваш сервер сохраняет историю операций, сохраните также callerBindingSha256 из await agnostic.context.get(). Обновлённый Runtime API возвращает этот идентификатор для действующего серверного участника и среды. Он сохраняется при замене токена того же участника. После потери ответа сначала восстановите исходный запуск чтением; изменение серверного участника не должно привести к повторному запуску старого запроса. Идентификатор не даёт прав: текущие права пользователя приложения проверяйте отдельно. Сам Runtime-токен в истории операций не сохраняйте.

Отмена доступна через agnostic.workflowRuns.cancel(run.id) с отдельным разрешением workflow.run.cancel. Проверьте права пользователя приложения на сервере перед отменой. Повтор отмены сохраняет её результат; завершённый с успехом или ошибкой запуск отменить нельзя. Отмена не возвращает назад уже выполненный запрос во внешнем сервисе. Runtime-токен храните только на сервере, а не в браузере. Эти методы требуют SDK 2.2 и соответствующего обновления Runtime API. В SDK предыдущей версии можно передавать ключ через заголовок Idempotency-Key и использовать Runtime HTTP API для восстановления и отмены. Не используйте несуществующий раздел jobs.

В стартовом API проекта также есть серверный адаптер истории операций. Настройте src/app-operation-resources.ts: разрешённые автоматизации, входные поля, подписи шагов, приватное хранилище истории и отдельные права чтения, запуска и отмены. Начальная конфигурация пустая. Адаптер сохраняет исходный запрос до запуска и восстанавливает его по прежнему ключу; повтор после ошибки или отмены создаёт отдельную связанную операцию. В интерфейс передаются только разрешённые статусы и события. Перед включением проверьте действия двух пользователей, отзыв прав, потерю ответа и фактическое исполнение автоматизации. Интерфейс приложения собирайте под свой сценарий.

Для каталога услуг или поставщиков в стартовом API есть отдельная конфигурация src/app-catalog-resources.ts. Настройте приватное хранилище и текущие права автора, читателя и модератора. Автор сохраняет карточку и отправляет редакцию на проверку; читатели видят только одобренную публикацию. Поздний черновик не меняет опубликованные данные. Заявка на контакт хранится отдельно и доступна отправителю, получателю и разрешённому модератору. Отправку сообщения во внешний канал подключайте отдельно. Перед использованием требуется прикладная приёмка.

Браузерный createCatalogAdapter сначала получает getContext(), затем создаётся с подтверждённым контекстом. Его resource читает карточки, а inquiries — доступные заявки. Обычные update/delete заменены отдельными действиями: сохранение, отправка на проверку, решение модератора, снятие публикации, заявка на контакт и её закрытие. Сохраняйте исходный ключ запроса и версию прочитанной записи. После изменения пользователя или прав пересоздайте адаптер; форматирование и итоговые экраны собирайте под свою задачу.

Для клуба или сообщества настройте src/app-community-resources.ts: приватное хранилище профилей и событий, принадлежность к организации и отдельные права участника, организатора и модератора. В каталоге участников публикуется только одобренный профиль; следующий черновик остаётся приватным. Запись на событие занимает место сразу, в том числе пока ожидает подтверждения организатора. Сервер проверяет свободные места и сроки записи и отмены. После первой записи время события и правила подтверждения фиксируются. При отмене события будущие активные записи отменяются вместе; приостановка участника освобождает его будущие места. Восстановление профиля требует новой проверки и не возвращает отменённые записи автоматически.

Используйте createCommunityAdapter: сначала получите getContext() и пересоздайте адаптер с этим контекстом. members, events, registrations и feedback читают доступные записи; изменение выполняется отдельными действиями с ключом запроса и прочитанной версией. Отзыв доступен подтвердившему участие пользователю после завершения события, а публикация требует решения модератора. Уведомления, платежи и повторяющиеся встречи подключаются отдельно. Начальная конфигурация пустая; перед использованием проверьте права, одновременную запись на последнее место, потерю ответа и ограничения своего приложения. Итоговые экраны и взаимодействия собирайте под задачу пользователя.

Для подписанных событий от внутренних сервисов настройте src/app-webhook-resources.ts и прочитайте app-webhook-ingress.md в стартовом API. Укажите отдельное приватное хранилище событий, разрешённые поля, автоматизацию и серверный источник ключей подписи. Подпись связывает событие с проектом, средой и адресом. Повторная доставка сохраняет идентификатор и данные, но получает свежую подпись времени. Сервер отклоняет неподписанные и слишком большие запросы.

При добавлении необязательного поля сохраните точные прежние правила полей в серверном previousFields. Тогда старое событие с исходным идентификатором и свежей подписью останется повтором, а незавершённый запуск можно будет восстановить. Данные и квитанция прежнего события сохраняются. Изменение его данных под прежним идентификатором вызывает конфликт. Тип, обязательность и ограничения старых полей нельзя менять под видом совместимого расширения; прежние правила не принимаются из входящего события.

Для кабинета оператора настройте webhookMonitorPolicies и подключите createAppWebhookMonitor. Просмотр требует текущего права AppUser на чтение и показывает только идентификатор доставки, время и подтверждение приёма или запуска. Payload и серверные ключи остаются приватными. Восстановление требует одновременно прав на чтение и восстановление, действующей сессии, CSRF и limit от 1 до 10. Отзыв права применяется перед чтением приватного хранилища или запуском автоматизации.

Ответ 202 stored означает, что событие сохранено, а 202 dispatched — что запуск автоматизации подтверждён. Фактическое выполнение проверяйте отдельно. Worker приложения может восстанавливать ожидающие события через recoverWebhookInbox, сохраняя исходный ключ. Этот адаптер не подтверждает оплату и не заменяет проверки подлинности Telegram, MAX или платёжного провайдера. Начальный обработчик бота сообщает, что канал не настроен; включайте его после подключения и проверки действительного канала.

Для AI-разговоров в стартовом API подготовлены src/app-ai-resources.ts, настройка src/app-ai-worker-resources.ts и руководство app-ai-runtime.md. Подключите настоящий серверный провайдер, подтверждаемый учёт расхода, приватную историю и worker с нужными правами. Модель, цену и лимиты задаёт сервер; пользователи приложения их не выбирают. Настроенный worker подбирает уже сохранённые запросы и подтверждённые действия, восстанавливает их по исходным ключам и продолжает сверку расхода. Сам он не создаёт новый разговор и не подтверждает действие за пользователя. Запрос пробуждает worker после сохранения; при остановке текущая обработка завершается в пределах настроенного срока. Эти исходники ещё ожидают финальной проверки провайдера, учёта расхода и настоящего приложения. Маршрут MiMo Pro/GLM5.2 для сборки к AI-разговорам приложения не подключается. SDK не содержит разделов ai и billing; эти интеграции настраиваются отдельно.

Серверному токену Runtime нужны права runtime.read, data.read, data.write и app_auth.session.verify. Последнее позволяет проверить текущую сессию и прочитать актуальные права её App User перед обработкой. Пользовательские права на разговор, отправку и подтверждение действий задаются отдельно в AI-политике.

Запрос сначала сохраняется. Резерв подтверждается до обращения к провайдеру; частичный ответ остаётся в истории при потере соединения. Восстановление читает исходный запрос и не запускает модель повторно. Неизвестный расход остаётся на сверке и не считается нулевым. Остановка сохраняет намерение; фактический исход и расход подтверждаются отдельно. Следующее сообщение ждёт завершения этой сверки. Для помощника по знаниям можно подключить опубликованные материалы: черновики исключаются, права и публикация проверяются на сервере.

Предложенные AI действия готовит сервер и подтверждает текущий пользователь. Само разрешение ещё не означает, что действие исполнено. Для действий настройте src/app-ai-tool-resources.ts: точную автоматизацию, версию правила, проверку полей, текущие права и чтение фактического результата. createAiToolPreparer готовит предложение, а processAiTool исполняет разрешённое действие через настроенный worker. Повторная доставка читает исходный запуск; потеря ответа не создаёт новый. Просроченное подтверждение не допускает нового запуска. Статус «передано на исполнение» отличается от подтверждённого применения. Ошибка или отмена автоматизации не возвращает уже выполненные изменения назад.

При подготовке действия закрепите на сервере точный workflowVersionId опубликованной автоматизации. Запуск передаёт его через expectedWorkflowVersionId: если публикация успела смениться, новое действие отклоняется. Подготовьте новое предложение и подтвердите его отдельно. Уже принятый запуск восстанавливается в исходной версии после новой публикации. Старую историю без закреплённой версии можно читать и восстанавливать, но она не разрешает новый запуск. Модель и пользователь не выбирают версию исполнения.

Для обновления записи после согласования сохраните её исходную revision на сервере при подготовке предложения. Парные настройки prepareInput и validatePreparedInput добавляют эту версию к проверенным полям модели и проверяют её при восстановлении. В узле db_upsert с operation: update передайте сохранённую версию через expectedRevision. Если запись успела измениться или исчезла, Data отклонит обновление. Прочитайте новые данные и подготовьте новое предложение для согласования; восстановление старого запроса сохраняет исходную версию.

Для серверного подключения Yandex подготовлен createYandexAiProvider. Он сохраняет выбранную модель и каталог, читает credential только на сервере и получает фактический отчёт расхода. createAiProviderReceiptStore сохраняет финальный ответ и структурированные предложения для восстановления без нового запроса. После сбоя сервер добавляет только недостающие предложения и заново проверяет права и исходную версию действия. Уже принятое решение и срок подтверждения сохраняются. Остановка, отзыв прав или смена правила блокируют новые предложения; подтверждённый расход учитывается отдельно. Восстановление само не подтверждает и не выполняет действия. Подключите настоящее серверное списание по своему тарифу отдельно. При включённых инструментах нужен проверенный счётчик полного запроса: текстовый токенизатор их не учитывает. Набор инструментов и их версии закрепите в серверном маршруте до отправки. Временная недоступность проверки бизнес-прав оставляет ответ на восстановлении: сервер повторяет проверку исходных предложений, а расход модели учитывает один раз. Следующее сообщение в этом разговоре дождётся восстановления результата. Пользовательский JWT платформы в приложение не передавайте; Runtime-доступ не даёт прав управлять платформенным кошельком. Реальные подключения и прикладная приёмка этой основы ещё требуются перед использованием в приложении.

Браузерный createAiAdapter сначала получает getContext() и создаётся с этим контекстом. История читается отдельными ресурсами, отправка и остановка — именованными действиями с версией и ключом. createAiStreamObserver читает сохранённые части ответа по курсору. При потере сети он сохраняет уже полученный текст; reconnect выполняет только чтение. Новый наблюдатель после перезагрузки перечитывает полный сохранённый ответ. Изменение доступа очищает его состояние и требует обновления контекста. В AI-рецепте можно подключить этот наблюдатель: состояния остановки, неподтверждённого результата и сверки расхода показываются отдельно. При сверке следующее сообщение заблокировано. Автоматического повторного вызова модели нет.

Для оплаты заказа подготовлены src/app-payment-resources.ts и серверная проверка CloudPayments. Подключите реального продавца в нужном тестовом или рабочем режиме, приватную историю оплаты и текущие права покупателей. Сумма берётся из зарезервированных позиций; браузер не может назначить сумму или статус «оплачено». Событие провайдера проверяется по подписи и данным транзакции. Авторизация средств ещё не является списанием. Поздняя или несовпавшая оплата требует проверки и не запускает выдачу товара автоматически.

События оплаты в JSON должны содержать уникальные имена полей, даже если часть имени записана кодами Unicode. Повторяющееся имя отклоняется до сверки транзакции и изменения заказа. Если событие не принимается, проверьте его исходное тело на стороне интеграции; неоднозначный ответ платёжного сервиса также не подтверждает оплату.

Если в настройке склада указан платёжный раздел, оплачиваемый заказ нельзя изменить обычными действиями, а выдача требует подтверждённой оплаты. Для браузера загрузите getContext() через createPaymentsAdapter и создайте адаптер с подтверждённым контекстом. Действие createIntent передаёт только заказ, его версию и исходный ключ. Подключите официальное окно CloudPayments через createCloudPaymentsLauncher и откройте сохранённый платёж действием createPaymentCheckoutController.open(). Повторные одновременные нажатия используют одно открытие. После закрытия окна контроллер читает статус на сервере; сообщение об успехе от окна само по себе не означает «Оплачено».

Обновление статуса выполняет только чтение. При смене пользователя уничтожьте контроллер и загрузите новый контекст; ошибка доступа очищает сохранённые данные. Агент собирает экран оплаты под задачу приложения. cancel(payment, actionContext) закрывает собственное намерение, пока провайдер ещё не начал оплату. Сервер проверяет текущие права, версию и исходный ключ. После подтверждённой отмены можно отдельно освободить резерв или отменить заказ обычным действием склада. Если платёж уже обрабатывается, отмена намерения недоступна: сначала обновите статус. Поздняя авторизация или списание требуют проверки продавцом.

При отдельном праве на возврат requestRefund передаёт платёж, точную сумму в копейках, причину, прочитанную версию и исходный ключ. Можно вернуть часть суммы или весь подтверждённый платёж. Сервер сначала сохраняет запрос; принятие запроса не означает, что деньги уже возвращены. getRefund и listRefunds читают его статус. «Выполняется» означает ожидание подтверждения, «Требуется сверка» — неопределённый результат. Такая сумма остаётся занята исходным запросом: не создавайте новый возврат вместо обновления статуса.

После подтверждения отдельно показываются частичный и полный возврат. Остатки не восстанавливаются автоматически; для полного возврата незавершённого заказа можно отдельно освободить резерв действием склада. Серверное исполнение возвратов, фискальные данные конкретного продавца и проверка реального магазина должны быть подключены и проверены перед использованием. Возвраты через личный кабинет провайдера требуют отдельной сверки с приложением.

Для браузера используйте createOperationsAdapter из App Kit 0.12. Сначала получите getContext(), затем создайте адаптер с этим контекстом и подключите его ресурс чтения к своему экрану или TechnicalRecipe. Подготовьте поля конкретной операции в приложении; start передаёт её имя и значения на ваш сервер. Runtime-токен в браузер не передаётся.

Храните исходный ключ до подтверждения результата. recoverStart только читает его состояние. «Запуск не подтверждён» означает сохранённое намерение, а не исполнение; продолжение выполняется отдельным действием. Отмена подтверждается ответом сервера и не откатывает уже выполненные шаги. Повтор после ошибки создаёт связанную запись. При изменении текущего пользователя или прав обновите контекст и адаптер: прежняя история и кнопки не должны переноситься в новый доступ.

Для обычной server-side загрузки используйте логическое имя бакета и один стабильный idempotencyKey на файл:

const bucket = agnostic.storage.bucket('uploads');
const upload = await bucket.createUpload({
  fileName: 'invoice.pdf',
  contentType: 'application/pdf',
  size: contents.byteLength,
  checksumSha256,
  access: 'private',
  idempotencyKey: `invoice:${invoice.id}:v1`,
});

await fetch(upload.uploadUrl, {
  method: 'PUT',
  headers: upload.requiredHeaders,
  body: contents,
});

const completed = await bucket.completeUpload(upload.id);
const file = await bucket.get(completed.file.id);

При повторе используйте прежние idempotencyKey, upload ID и file ID. В browser после своих проверок можно вернуть только короткую upload/download URL; импорт SDK и runtime token остаются на backend.

Для createUpload и completeUpload нужна возможность files.write, для get — files.read. Вновь опубликованный управляемый API-сервис получает обе. Если операция вернула runtime_capability_missing, проверьте поле required в ответе и возможности того же токена через agnostic.context.get(). Изменение возможностей управляемого сервиса требует новой публикации API unit. Поле actor указывает, от чьего имени выполнен запрос для аудита, и не расширяет возможности Runtime token.

После успешного PUT устраните причину отказа completeUpload и повторите завершение с тем же upload ID, не отправляя байты повторно. Пока session действует, повторный createUpload с прежними параметрами и idempotencyKey вернёт тот же file ID и upload ID. При files_upload_expired (HTTP 410) старая session не восстанавливается: создайте новую с новым ключом и загрузите байты заново. Помечайте собственную запись готовой только после результата completeUpload со статусом ready, совпадающими размером и SHA-256.

createDownloadUrl дополнительно требует files.links.create, а delete — files.delete. Эти возможности не входят в стандартный токен управляемого API-сервиса.

После создания заказа backend может идемпотентно привязать к нему anonymous upload session:

const attachments = await agnostic.storage
  .bucket('order-files')
  .anonymousUploads.claim({
    token: attachmentSessionToken,
    externalRef: `order:${order.id}`,
    idempotencyKey: `order:${order.id}`,
  });

Повторяйте вызов с теми же значениями при retry. Не принимайте отдельные file ID из браузера: используйте только handles из claim result. Claim token, как и Runtime token, остаётся только на backend.

Проверить SDK внутри опубликованного сервиса

В актуальном шаблоне backend-сервиса есть встроенная проверка Runtime SDK. Она использует контекст самого сервиса и выполняет только чтение служебного контекста. Из локального workspace выполните:

agnostic service runtime-check --service <service-id> --environment <environment-id> --revision <revision-id> --json

Выберите активную ревизию нужного сервиса и окружения. Результат ready подтверждает работу SDK именно в ней. Если во время проверки появилась новая ревизия, выберите её и повторите команду.

Для ранее созданного сервиса сначала добавьте проверку из актуального шаблона и опубликуйте новую версию обычным способом. При отсутствии проверки команда покажет, что она недоступна. Обновление платформы само по себе не переписывает код существующего приложения.

Эта команда не исполняет произвольный проверочный скрипт. Для собственного сценария добавьте его в код сервиса и вызовите через защищённый backend-сценарий, используя тот же SDK-клиент. Секреты остаются внутри сервиса.

Что дальше

Код сможет работать только в пределах текущего проекта и среды. SDK не предназначен для управления организацией, тарифом, проектами или инфраструктурными настройками. Создание, изменение и публикация автоматизации остаются действиями настройки и требуют подтверждения в Agnostic.

Частые проблемы

ПроблемаЧто сделать
SDK не видит runtime contextОпубликуйте backend-сервис заново или проверьте безопасную локальную настройку.
Ошибка доступаПроверьте возможности runtime identity и выбранную среду.
Нужно вызвать SDK из браузераНе делайте этого; вынесите вызов в backend или используйте безопасный пользовательский API.
В примере есть events, jobs или gatewayТакой раздел не входит в текущий SDK. Используйте доступное действие продукта или поддерживаемый namespace.
Agent ссылается на старый метод SDKНачните новую генерацию после обновления backend/worker-зависимости; Agent перечитает contract установленной версии.
Автоматизация не запускаетсяПроверьте, что она опубликована в том же окружении, где выполняется код.
Входные данные отклоненыСверьте payload с актуальной карточкой автоматизации и уберите защищённые значения.
Aggregate-запрос отклонёнПроверьте имя поля, его тип, уникальность alias и ограничения: до 2 группировок и до 100 групп.
Anonymous claim отклонёнПроверьте готовность upload, capability files.write и точное повторение externalRef с idempotencyKey.
Upload или delete отклонёнПроверьте capability Files, логический бакет, текущее окружение и что файл принадлежит этому runtime.

Связанные статьи

Приватные вложения в карточке приложения

Для CRM, кабинета или базы документов используйте готовый серверный адаптер вложений вместе с Data BFF. Бакет, таблицу, окружение и поля задаёт разработчик на сервере. Пользователь приложения загружает файл в доступную карточку.

  1. Проверьте приватный бакет Files и добавьте отдельное JSON-поле для связей

вложений. Укажите его в resourcePolicies.files вместе с лимитами размера, количества и разрешёнными MIME-типами. Это поле не входит в редактируемые поля формы. Когда в проекте доступна сборка приложений через агента, он может проверить существующее хранилище или предложить создать пустое приватное. Для создания нужна роль Developer или выше. Подтверждение показывает точное окружение, разрешённые форматы и размер файла. Пользователь приложения не выбирает хранилище и не меняет его настройки.

  1. Выдайте сервису runtime-права files.read, files.write, files.delete и

files.links.create. App User по-прежнему нужны права на саму карточку; runtime-права сервиса их не заменяют.

  1. Подключите createRecordFilesAdapter из App Kit к callback-методам рецепта.

Он передаёт файл через короткое разрешение без runtime-токена. Создавайте адаптер в текущем пользовательском scope и завершайте его при выходе.

  1. Дождитесь статуса «Готов». Передача всех байтов и вызов завершения загрузки

ещё не означают готовность: платформа проверяет размер, checksum, содержимое и безопасность файла. Для обработки предусмотрена ограниченная проверка статуса; отклонение и карантин остаются видимыми.

  1. Для скачивания backend заново проверяет сессию, права, владельца карточки

и связь с вложением, затем выдаёт короткую ссылку на готовый приватный файл.

  1. Удаление требует актуальной версии карточки. Если она изменилась, обновите

данные перед новым решением. При сбое продолжайте точный прежний запрос: checkpoint закрывает новые ссылки до подтверждения удаления в Files. Отклонённый файл можно убрать из карточки; файл в обработке должен дождаться результата проверки.

Приёмка приложения включает два разных App User, повторное открытие карточки, реальную передачу и скачивание байтов, отказ для чужой карточки, отклонённого файла и устаревшей версии. Публичные загрузки, anonymous-формы и multipart не включаются этим адаптером.

Связь между Data и Files не является общей транзакцией. Состояние удаления сохраняется в карточке, чтобы продолжить операцию после потери ответа. Подробнее о хранении и существующих ссылках: Файлы проекта и логические бакеты.

Созданное хранилище само по себе не означает, что вложения уже работают. Агент повторно проверяет его настройки; затем приложение должно пройти проверки загрузки, готовности, скачивания, удаления и доступа к чужой карточке. При повторе одного подтверждённого создания сохраняется то же хранилище. Изменение его настроек останавливает продолжение до новой проверки. Загрузка, хранение и скачивание учитываются отдельно по тарифу организации.

Для MAX подготовлены общее ядро диалога и импорт входящих вложений. Текущие права, исходное сообщение и готовность Files проверяются перед продолжением. Эти исходники ожидают финальной проверки настоящего бота и публикации SDK.