iop/agent-contract/inner/agent-runtime.md

16 KiB

Agent Runtime Contract

계약 메타

  • id: iop.agent-runtime
  • boundary: inner
  • status: active
  • 원본 경로:
    • packages/go/agentruntime/types.go
    • packages/go/agentruntime/failure.go
    • packages/go/agentruntime/emitter.go
    • packages/go/agentruntime/session.go
    • packages/go/agentruntime/status.go
    • packages/go/agentruntime/registry.go
    • packages/go/agentconfig/
    • packages/go/agentprovider/cli/
    • packages/go/agentprovider/catalog/
    • packages/go/agentguard/
    • packages/go/agenttask/
    • configs/iop-agent.providers.yaml
    • apps/node/internal/node/runtime_bridge.go

읽는 조건

  • Node와 독립 host가 공통 provider run/stream/resume/cancel/status 계약을 소비할 때
  • Provider, ExecutionSpec, RuntimeEvent, SessionMode, Failure, Registry를 변경할 때
  • CLI provider process, logical session, emitter, terminal, status/quota 파서를 변경할 때
  • agent provider catalog YAML, provider/model/profile ID, discovery/readiness와 profile factory를 변경할 때
  • unattended AgentTask의 canonical workspace grant, task isolation descriptor, admission permit과 provider invocation gate를 변경할 때
  • AgentTaskManager, manual start/auto-resume, explicit dependency, isolated dispatch, official review와 serial integration orchestration을 변경할 때
  • Node의 protobuf 요청/이벤트와 공통 runtime 사이 변환을 변경할 때

범위와 비범위

이 계약은 Node와 독립 agent host가 공유하는 host-neutral provider 실행 및 Agent Task orchestration 경계다. 공통 package는 provider lifecycle, 실행 요청, stream event, logical session, cancel, status/quota projection, typed failure와 registry lifecycle을 소유한다. agent 전용 catalog는 외부 CLI provider/model/profile의 공식 ID와 비밀정보 없는 실행·probe 선언, readiness와 공통 provider factory를 소유한다. agentguard는 unattended AgentTask provider 호출 직전의 canonical workspace와 capability admission을 소유한다. agenttask.Manager는 durable manual start intent부터 dependency-ready dispatch, submission/review, follow-up과 ordinal integration까지의 상태 전이를 단일 구현으로 소유한다.

Edge-Node protobuf field와 ordering 원문은 iop.edge-node-runtime-wire가 소유한다. 기존 Edge resource provider pool과 models[]iop.edge-config-runtime-refresh가 소유하며 agent catalog와 이름이 비슷해도 schema와 의미를 섞지 않는다. 실제 workspace overlay 생성·change-set apply/rollback backend와 standalone iop-agent process lifecycle은 이 계약의 비범위다. AgentTaskManager는 이 backend들의 strict port와 호출 순서만 소유한다. Admission은 이미 생성된 isolation descriptor를 검증할 뿐 overlay/worktree/clone을 만들지 않는다.

최소 호출과 이벤트 형태

  • host는 Provider.Capabilities(ctx)로 target과 concurrency capability를 읽고 Provider.Execute(ctx, ExecutionSpec, EventSink)로 실행한다.
  • ExecutionSpecrun_id, adapter, target, session_id, session_mode, background, workspace, policy, input, timeout, metadata를 운반한다.
  • SessionModeCreateIfMissing은 새 logical session 생성을 허용하고 SessionModeRequireExisting은 기존 session이 없으면 실패해야 한다.
  • provider는 start, delta/reasoning_delta, complete/error/cancelled RuntimeEvent를 순서대로 보낸다. complete/error/cancelled 중 하나만 terminal이며 terminal 이후 event는 host에 노출하지 않는다.
  • run cancel은 실행 context 취소와 ErrRunCancelled로 수렴한다. logical session 종료는 optional SessionTerminator 경계로 분리한다.
  • 조회/제어는 실행 stream과 섞지 않고 optional CommandHandlerCommandRequest/CommandResponse로 처리한다. usage status는 AgentUsageStatus로 정규화한다.

AgentTaskManager 명령과 durable 상태

  • 공통 concrete 구현은 packages/go/agenttask.Manager 하나다. host는 AgentTaskManagerStartProject, Reconcile, StopProject lifecycle만 호출하고 Node나 독립 CLI에 state machine을 복제하지 않는다.
  • StartProjectcommand_id, project/workspace/Milestone identity와 workflow/config/grant revision을 atomic CAS state에 manual StartIntent로 기록한다. 같은 command와 같은 immutable 입력은 idempotent이고, 같은 command를 다른 입력으로 재사용하면 오류다.
  • ReconcileWorkflowAdapter.RegisteredProjects와 project별 snapshot을 관측하되 StartIntent가 없는 ready Milestone을 실행하지 않는다. 수동 시작된 project만 진행하며 시작 기록이 있는 interrupted state는 auto_resume_interrupted 생략 시 true, 명시 false이면 stopped로 유지한다.
  • durable identity는 project, workspace, Milestone, work unit, attempt, artifact, change set, workflow/config/grant/isolation revision과 dispatch/integration ordinal을 분리한다. corrupt 또는 drift한 identity를 빈 상태나 현재 설정으로 재선택하지 않고 typed task/project blocker로 남긴다.
  • StateStore는 revision compare-and-swap을 제공해야 한다. manager는 project invocation lease와 workspace integration lease를 durable state에 claim하고 live 다른 owner가 있으면 중복 호출하지 않는다.
  • work state는 observed → ready → preparing → dispatching → submitted → reviewing → pending_integration → integrating → completed를 기준으로 하며, blocked, stopped, terminal_deferred를 명시 terminal branch로 쓴다. 정의되지 않은 전이는 거부한다.
  • Event와 모든 external port idempotency key는 length-prefixed injective canonical tuple로 구성하여 raw delimiter 충돌을 방지하고, command/workflow revision/change-set ID·revision/integration attempt 등의 logical discriminator를 보존하여 replay 시 동일 event_id로 수렴해야 한다. sink는 같은 event_id replay를 idempotent하게 처리해야 한다.

Dependency, isolated dispatch와 review/integration

  • readiness gate는 workflow snapshot의 ExplicitPredecessors만 사용한다. task 번호, directory 순서, write-set 비중첩·중첩·unknown은 dependency를 만들지 않는다. predecessor reference가 없거나 둘 이상이면 각각 typed missing/ambiguous blocker다.
  • Selector는 immutable config revision의 provider/model/profile과 capacity를 반환한다. Scheduler는 provider/profile capacity와 work-attempt ticket을 결합하며 cancel/release가 capacity를 정확히 반환하도록 한다.
  • 실행 전에 IsolationBackend.Prepare가 task별 overlay | worktree | clone descriptor와 exact grant/profile revision을 반환해야 한다. backend 미설정, identity mismatch, admission 차단은 provider invocation 0회이며 canonical workspace direct-write fallback은 없다.
  • manager는 agentguard.Admit의 opaque Permit을 invocation 직전에 agentguard.Invoke로 재검증하고 그 canonical task view만 ProviderInvoker에 전달한다.
  • provider submission이 complete이고 project/work/attempt/artifact identity가 일치한 뒤에만 Reviewer를 호출한다. PASS는 exact artifact의 immutable change set을 integration queue에 넣고 WARN/FAIL rework는 같은 dispatch ordinal의 새 attempt로 진행하며 USER_REVIEW는 해당 task만 terminal-deferred로 둔다.
  • integration은 최초 dispatch ordinal 순서로 한 번에 하나씩 Integrator를 호출한다. 모든 external port call은 stable idempotency key를 받아 crash 후 replay가 같은 결과로 수렴해야 한다. conflict, unmanaged drift, validation/apply 오류는 partial completion 없이 retained change set과 blocker를 반환하며 뒤 independent ordinal은 계속 진행한다.
  • project-local workflow, admission, invocation, review와 integration blocker는 다른 project나 independent sibling 진행을 중단하지 않는다.

Workspace guardrail admission

  • WorkspaceGrant는 project/workspace identity, canonical base root, immutable grant revision과 worktree가 사용할 수 있는 exact external Git metadata root allowance를 가진다.
  • IsolationDescriptor는 immutable isolation/base revision, overlay | worktree | clone mode, canonical base/task/working root, task 내부 writable roots와 실제 writable-root confinement 여부를 가진다. task root와 canonical base가 같으면 admission을 거부한다.
  • ProviderProfile은 provider/model/profile identity와 immutable revision, unattended, approval_bypass, writable_root_confinement capability를 가진다. 세 capability 중 하나라도 없으면 provider process를 호출하지 않는다.
  • canonicalization은 absolute·clean·existing directory, symlink resolution, component-aware containment와 task root 및 effective working repository의 실제 .git/gitdir/commondir를 확인한다. task root 밖 Git metadata는 grant에 exact root로 등록된 경우만 허용한다.
  • 성공한 admission은 process-local opaque Permit에 grant/isolation/profile revision, pinned base revision, canonical roots와 filesystem identity를 봉인한다. invocation 직전에 현재 입력과 filesystem identity를 다시 검증하며 stale, forged, replacement identity는 provider invocation 0회로 차단한다.
  • unattended AgentTask caller는 catalog.NewAdmittedProfileProvider가 반환하는 facade의 Admit/Execute만 사용한다. facade는 caller가 제공한 raw ExecutionSpec.Workspace를 사용하지 않고 Permit의 canonical working directory로 덮어쓴다.
  • AdmissionResultpermitted | blocked, typed Blocker, raw path를 포함하지 않는 actionable Notification을 반환한다. 차단은 task/project-local result이며 다른 project provider를 stop하지 않는다. interactive approval fallback은 없다.
  • 기존 Node Edge-wire provider와 명시적인 authenticated smoke가 쓰는 ProfileProvider.Execute는 기존 실행 호환 경계다. AgentTask unattended 호출에서 이 compatibility 경로를 admission 우회로 사용하지 않는다.

Agent provider catalog와 readiness

  • configs/iop-agent.providers.yamlversion, providers[], models[], profiles[]의 비밀정보 없는 repo-owned 선언이다. 각 배열의 id는 배열 안에서 유일한 stable ID이며 profile은 정확히 하나의 provider와 그 provider가 소유한 model을 참조한다.
  • provider는 CLI command, bounded version/authentication probe, optional model target probe와 지원 capability를 선언한다. model probe를 생략하면 검증된 static model target 선언이 기준이며, probe를 선언하면 출력의 exact line과 target을 비교한다.
  • profile은 common CLI runtime args/resume args/mode/output format과 capability를 선언한다. {{model}}은 factory가 provider-native model target으로 치환하고 catalog 원본은 변경하지 않는다. writable_root_confinement는 task isolation owner와 결합해 provider process의 writable root를 제한할 수 있는 profile만 선언한다.
  • loader는 YAML unknown field, multiple document, duplicate ID, dangling/cross-provider reference, invalid capability/mode/regex/timeout과 secret-like environment key를 거부하고 provider/model/profile을 ID 순서로 정규화한다.
  • discovery는 PATH binary lookup, bounded version/authentication/model probe를 수행하고 공식 provider/model/profile ID와 함께 ready, missing_binary, unauthenticated, unsupported_model, probe_error 중 하나를 반환한다.
  • 실행 불가 readiness는 각각 ErrBinaryMissing, ErrAuthenticationRequired, ErrModelUnsupported, ErrProbeFailederrors.Is 가능한 ReadinessError를 반환한다. provider raw output, credential/token/header와 account identity는 redaction 후 bounded diagnostic에만 남긴다.
  • profile factory는 동일 ID의 ready 결과만 받아 하나의 공통 CLI provider를 생성한다. runtime target은 profile ID이며 run/resume/cancel/status event·result metadata에 provider_id, model_id, profile_id를 보존한다.
  • status는 predecessor 공통 CLI status API를 호출해 구조화 usage/quota를 얻고 discovery snapshot의 ID, readiness와 version을 AgentUsageStatus.Metadata에 병합한다. provider가 별도 status surface를 제공하지 못하면 직전에 검증한 readiness snapshot을 status_probe=readiness_fallback으로 명시해 반환하며 ready로 새로 추정하지 않는다.

Typed failure codec

  • Failure은 stable FailureCode, 사용자/운영 진단 message, retryable, 비민감 metadata를 가진다.
  • durable boundary는 EncodeFailure/DecodeFailure의 versioned JSON envelope를 사용한다.
  • 알 수 없는 미래 code는 실패를 버리지 않고 unknown으로 정규화하며 원래 code를 metadata에 보존한다.
  • ErrRunCancelledcontext.Canceledcancelled, context.DeadlineExceeded는 retryable deadline_exceeded다.
  • provider별 raw output, credential, token과 private endpoint를 failure metadata에 넣지 않는다.
  • readiness error는 실행 Failure codec과 별도 preflight 타입이다. readiness를 실행 실패처럼 codec에 강제로 넣지 않는다.

Node bridge 호환 규칙

  • Node만 protobuf를 import하고 runtime_bridge.go에서 RunRequest를 공통 RunRequest로, 공통 RuntimeEvent를 기존 RunEvent로 변환한다.
  • RunEvent.type, delta/message/error, usage, metadata, timestamp, session/background/node identity의 기존 wire 의미를 유지한다.
  • typed failure가 있어도 기존 Node wire error에는 사람 읽기 가능한 message를 유지한다. protobuf 확장 없이 codec payload를 기존 field에 강제로 넣지 않는다.
  • config refresh registry swap, in-flight snapshot, admission ticket release 뒤 terminal flush ordering은 공통 package 이동으로 바뀌지 않는다.

금지 사항

  • packages/go/agentruntimepackages/go/agentprovider에서 apps/*/internal 또는 protobuf package를 import하지 않는다.
  • Node와 독립 host에 CLI process/session/emitter/status/failure 구현을 복사하지 않는다.
  • unattended AgentTask에서 raw ProfileProvider.Execute를 직접 호출하거나 invalid/stale Permit을 interactive fallback으로 우회하지 않는다.
  • canonical base, task root 밖 writable root, grant에 없는 worktree Git metadata root를 Permit에 포함하지 않는다.
  • agent provider catalog를 기존 Edge provider-pool NodeProviderConf/ModelCatalogEntry schema와 합치거나 서로의 ID 의미로 해석하지 않는다.
  • tracked catalog에 raw token, credential, authorization header, password 또는 secret-bearing environment 값을 넣지 않는다.
  • discovery timeout/cancel을 ready로 간주하거나 unknown provider/model/profile을 fallback target으로 선택하지 않는다.
  • readiness ID와 factory profile ID가 다르거나 ready가 아닌 profile로 provider를 생성하지 않는다.
  • provider-specific session/conversation id를 공통 execution identity로 승격하지 않는다.
  • terminal event를 둘 이상 내보내거나 terminal 뒤 delta를 노출하지 않는다.
  • cancel과 terminate-session을 같은 lifecycle action으로 취급하지 않는다.
  • 기존 Edge-Node wire를 공통 runtime 타입과 같게 만들기 위해 proto 의미를 변경하지 않는다.
  • manual StartIntent가 없는 ready project를 daemon start나 filesystem scan만으로 dispatch하지 않는다.
  • explicit predecessor 외 번호, 경로, write-set overlap/unknown에서 암묵 dependency를 만들지 않는다.
  • IsolationBackend, ProviderInvoker, Reviewer, Integrator가 없거나 실패했을 때 canonical workspace 직접 실행, review 생략, blind integration으로 fallback하지 않는다.
  • artifact/change-set/revision identity mismatch를 성공으로 정규화하거나 새 identity로 조용히 재발급하지 않는다.
  • worker 완료 순서로 integration ordinal을 바꾸거나 terminal-deferred task 하나로 뒤 independent queue를 멈추지 않는다.

변경 시 확인할 코드/테스트

  • packages/go/agentruntime/*_test.go
  • packages/go/agentconfig/*_test.go
  • packages/go/agentprovider/catalog/*_test.go
  • packages/go/agentguard/*_test.go
  • packages/go/agenttask/*_test.go
  • packages/go/agentprovider/cli/*_test.go
  • packages/go/agentprovider/cli/status/*_test.go
  • apps/node/internal/node/*_test.go
  • apps/node/internal/adapters/config_set_test.go
  • apps/node/internal/router/router_test.go
  • apps/node/internal/bootstrap/module_test.go
  • cmd/iop-provider-smoke/main.go
  • configs/iop-agent.providers.yaml
  • agent-contract/inner/edge-node-runtime-wire.md