5.4 KiB
Runner Protocol v1
Новый контракт Invest Harness для заменяемых исполнителей. Стандартный движок подключается адаптером; он не обязан понимать этот API напрямую. ExecutionSpec одинаков для удалённой и локальной реализации.
Регистрация
Admin создаёт POST /v1/runner-pools/{poolId}/registration-grants с {expiresInSeconds:600}; ответ 201 {grant, expiresAt}. Grant одноразовый, не журналируется. Runner вызывает POST /v1/runners/register с {grant, name, protocolVersion:"1", capabilities}; ответ 201 {runnerId, poolId, credential, expiresAt}. Долгоживущая идентичность хранится в защищённом хранилище исполнителя; credential обновляется через POST /v1/runners/{id}/credentials/rotate с текущей идентичностью и отзывается через DELETE /v1/runners/{id} администратором.
Capabilities: os, architecture, executionKinds:["oci"], features:["cancel-v1"], maxConcurrency. checkpoint-v1 и input-v1 опциональны. Профиль пула задаёт доверенную изоляцию и квоты отдельно от заявления capabilities.
Выполнение
| Метод и путь | Запрос | Ответ |
|---|---|---|
| POST /v1/runner-jobs/claim | runnerId, availableSlots, capabilitiesRevision | 200 Assignment или 204 |
| POST /v1/attempts/{id}/heartbeat | leaseEpoch, sequence, progress? | 200 {leaseUntil, cancelRequested} |
| POST /v1/attempts/{id}/started | leaseEpoch, sequence | 200 Attempt |
| POST /v1/attempts/{id}/events | leaseEpoch, events:[RunEvent] | 200 {acceptedThrough} |
| POST /v1/attempts/{id}/uploads | leaseEpoch, purpose, mediaType, sizeBytes, digest | 201 UploadGrant |
| POST /v1/attempts/{id}/complete | leaseEpoch, completionId, outcome, resultManifest?, error? | 200 Attempt |
| POST /v1/attempts/{id}/suspend | leaseEpoch, checkpointArtifactId, inputRequest | 200 Run |
Assignment: attemptId, runId, leaseEpoch, leaseUntil, executionSpec, executionToken. Execution token scoped к Attempt. Запрос claim authenticated идентичностью Runner; последующие операции — execution token. Назначение создаётся сервером атомарно; один claim выдаёт максимум один Attempt, availableSlots не разрешает превысить maxConcurrency пула.
Outcome: succeeded/failed/cancelled. Success требует ResultManifest и сохранённых объектов, даже если artifacts пуст. Error: code, message, retryableHint; решение о retry принимает платформа. Complete повторяется с тем же completionId и телом; другой payload после terminal state → 409. Поздние heartbeat, события и complete с прежним epoch → 409 LEASE_EXPIRED. События могут доставляться повторно; уникальны по attemptId + eventId.
Suspend допустим только для checkpoint-v1 + input-v1. InputRequest: id, schema, prompt, expiresAt. Платформа принимает checkpoint, переводит Run в waiting_input, отзывает lease; новый Attempt получает checkpointRef и проверенный ответ. Без checkpoint протокол не обещает сохранение памяти процесса.
Локальный процесс
Адаптер materialize-ит immutable ExecutionSpec в /input/execution.json, входные файлы — read-only /input/files; вывод — /output. Разрешённые secrets передаются отдельным каналом и не сериализуются в spec. Entrypoint — массив аргументов, не shell-строка. События JSONL поступают через отдельный pipe или файл адаптера, stdout/stderr считаются логами. Адаптер читает /output/result.json и загружает разрешённые артефакты; ссылки вне /output и symlink traversal запрещены.
Соглашение путей относится к стандартному OCI-адаптеру. Другой Runner может реализовать иной транспорт при сохранении семантики ExecutionSpec и результатов. Локальный offline Runner не вызывает облачные claim/heartbeat, но ведёт эквивалентное локальное состояние.
Минимальная проверка совместимости
Runner обязан показать: корректный success; failure; отказ при несовместимом spec; отмену; тайм-аут; повтор complete без дублей; отказ с просроченным lease; изоляцию файлов между задачами; корректный upload digest; отсутствие secrets в событиях. Resume проверяется только при соответствующей capability. Managed и customer-managed пулы отображаются раздельно.