24 KiB
24 KiB
Milestone: 공통 Agent Task Runtime과 Desktop Agent
위치
- Roadmap: ROADMAP.md
- Phase: PHASE.md
목표
현재 Python 기반 스킬이 모델 호출로 수행하는 Agent Task 감시·선택·실행·복구 루프를 프로덕션 Go runtime으로 이전하고, Node와 독립 Desktop Agent가 동일한 공통 CLI Provider 및 AgentTaskManager 구현을 사용하게 한다. 기능을 축소한 초기판이 아니라 현재 마일스톤, 동작 중인 Python dispatcher, Node CLI runtime에서 확인되는 실행·quota·오류·관측 동작을 동등성 기준으로 삼으며, Desktop Agent는 Edge 없이 app-owned 설정과 프로젝트별 override로 이미 agent-ops 작업 체계를 가진 여러 workspace의 마일스톤을 순차 실행한다.
상태
[계획]
승격 조건
- 없음
구현 잠금
- 상태: 잠금
- SDD: 필요
- SDD 문서: SDD.md
- SDD 사유: 공통 runtime 추출, Node/Desktop host 경계, config schema, provider process lifecycle, 상태 복구, 자동 실행, 실제 로그인 환경 smoke를 함께 변경한다.
- 잠금 해제 조건: 아래 체크리스트
- SDD 잠금이 해제되어 있다.
- SDD 사용자 리뷰가 없거나 승인·해결되었다.
- Acceptance Scenario가 Milestone 기능 Task와 연결되어 있다.
- Evidence Map이 완료 시
Roadmap Completion과 최종 검증 evidence로 검증 가능하게 연결되어 있다.
- 결정 필요: 아래 체크리스트
- SDD USER_REVIEW D01에서 macOS 창 닫기·명시 종료·로그인 시 시작의 Desktop background lifecycle을 결정한다.
범위
packages/go아래 공통 runtime이 CLI Provider 실행, emitter/stream 해석, session/resume, quota 상태, 오류 분류, process 관측과 AgentTaskManager를 단일 구현으로 소유하고 Node와 Desktop Agent가 host adapter로 소비한다.- Node 내부의 기존 CLI provider 구현을 공통 runtime으로 이동해 Node에는 현재
RunRequest/RunEvent, config, command/status 경계를 연결하는 얇은 bridge만 남긴다. Node와 Desktop에 provider를 중복 선언하거나 복사하지 않는다. - Python dispatcher와 selector는 실행 의존성이 아니라 동작·정책·관측 오류의 참조 evidence로만 사용한다. 최종 runtime은 Python process나 monitoring skill 없이 동작한다.
- 등록 project의 agent-ops가 Milestone·Plan·Code Review skill/rule과 작업 파일 의미를 소유한다. 공통 runtime은 이를 복사·내장하거나 IOP 소유 skill로 재정의하지 않고, 이미 생성된 project-local 작업 상태와 선택된 작업 지시를 실행 입력으로 소비한다. 실행 감시, heartbeat, 오류 판정과 다음 agent 호출은 AgentTaskManager runtime이 소유한다.
- 일반 사용자 요청을 direct/Plan/Milestone으로 분류하고 IOP가 소유한 작업 의미로 사용자 agent에 합성 tool call을 주입해 작업 파일을 만드는 기능은 에이전트 작업 루프 오케스트레이션 MVP의 별도 책임이다. 이 runtime은 그 오케스트레이션의 공통 실행 기반이 될 수 있지만 진입 요청 라우터나 Plan/Milestone skill 소유자가 되지 않는다.
- 동등성 기준은 구현 계획 시점의 Agent Task 동적 실행 Target Selector, 관련 active
WORK_LOG.md, Python dispatcher/selector, Node CLI runtime과 Go usage checker를 함께 대조해 고정한다. 충돌 시 Milestone/SDD, 현재 agent-contract, 참조 구현 순으로 우선한다. - 사용자 환경에 선언된 provider가 실제 실행 대상이다. runtime은 현재 Node와 선행 Milestone이 지원하는 one-shot/persistent CLI, Codex, Claude, Antigravity/Agy, OpenCode, Pi 등 provider profile과 emitter family를 공통 catalog에서 해석하며 임의의 축소된 고정 목록만 지원하지 않는다.
- 외부 표기는
codex/gpt-5.6-sol-xhigh처럼 provider/model/profile을 사용자가 이해할 수 있는 공식 계열 이름으로 표현한다. Desktop config/event/UI는 genericcliadapter를 주 식별자로 노출하지 않고, 내부 Node bridge만 기존adapter + target과 안정된 provider/profile id를 유지한다. - provider 인증과 credential은 각 CLI가 소유한다. 앱은 binary/version/authenticated readiness를 조회·검증할 뿐 로그인, token 저장, 계정 전환을 관리하지 않는다.
- app-owned YAML config tree가 provider catalog, 기본 정책과 명시 등록 project별 custom override를 모두 소유한다. workspace 안의 YAML은 runtime 설정 권위가 아니며, project override는 app registry id 아래에서 scalar/map을 key 기준으로 덮어쓰고 순서가 의미인 selection rule array는 전체 교체한다.
- 선택 엔진은 기본 provider와 조건부 rule array를 평가해 정확히 하나의 provider/model을 반환한다. 시간, quota/token 잔량, agent/stage/lane/grade, capability, 알려진 실패 상태를 조건으로 사용할 수 있고 겹치는 조건은 선언 순서가 우선한다. Node와 Desktop은 공통 evaluator를 사용하되 서로 다른 정책 문서를 주입할 수 있다.
- config file watcher가 revision을 검증하며, 실행 중인 agent는 시작 시점 snapshot으로 끝까지 수행한다. 유효한 변경은 다음 agent 호출부터 적용하고 잘못된 변경은 명시적 config error로 표면화하며 영향을 받는 새 dispatch를 중단한다.
- quota/usage는 현재 Go usage checker와 Python의 검증된 해석을 기준으로
available | exhausted | unknown | not_applicable과 관측 근거를 제공한다. 같은 provider credential/profile의 quota는 app 전체 project가 공유하고 동일 admission batch는 같은 immutable snapshot을 재사용한다. 조회 오류는 해당 provider key에서unknown으로 격리해 다른 provider/project key로 확산시키지 않는다. - 앱은 workspace의 소유자가 아니라 관리 주체다. 프로젝트는 명시 등록하며 app-owned provider/global 설정과 project override를 관리하고,
agent-task,agent-roadmap,priority-queue.md,WORK_LOG.md와 project-local log가 작업 상태의 source of truth가 된다. - 자동 실행은 기본 on이며 사용자가 언제든 중단할 수 있다. 등록 프로젝트의 기존 agent-ops 작업 상태에 남은 agent-task가 있으면 먼저 이어서 처리하고, 없으면 기존 agent-roadmap/priority-queue.md의 최상위 ready Milestone을 선택해 project-owned plan/review/완료 지시를 순차 실행한다. 일반 사용자 요청에서 새 Milestone/Plan을 분류·생성하는 진입 동작은 수행하지 않는다.
- 서로 다른 등록 project/workspace instance는 병렬 실행할 수 있다. 같은 저장소를 clone/worktree/branch로 여러 경로에 둔 경우도 canonical workspace instance를 분리해 동시에 관리하며, 한 instance 안에서는 dependency와 work-unit route pin을 따른다.
- Gemini를 실행하는 Agy adapter를 포함한 각 provider codec은 raw stream/status에서 provider별 강한 quota evidence를 판정해 normalized failure를 내보낸다. AgentTaskManager는 provider 문자열을 다시 해석하지 않고 이 typed result만 작업 전이 입력으로 사용하며 generic·불완전 evidence는 unknown/generic 오류로 표면화한다.
- 실행 중 provider-confirmed quota exhaustion은 admission snapshot을 복제·변조하지 않고 base snapshot을 참조하는 별도 runtime quota observation으로 기록한다. 현재 work unit은 이 observation을 즉시 사용하고 app quota service는 같은 credential/profile key를 무효화·갱신해 후속 admission에 반영하되, 이미 admission된 sibling route는 바꾸지 않는다.
- AgentTaskManager는 같은 work-unit id, persisted candidate 순서와 사용 이력을 보존해 다음 unused eligible target으로 failover하고 session locator와 logical context를 이전한다. 대체 target이 없으면 해당 work unit만 typed blocker로 두며, 사용자 재시도 또는 auto-run policy가 허용한 quota 상태 갱신에서도 새 initial route를 계산하지 않고 persisted route 안에서만 복구한다.
- 현재 Python에서 검증된 raw/normalized stream, heartbeat, PID/process-group, session locator, silence inspection, cancellation, route pin, logical context transfer, failure budget, dependency drain, review-control 위반 감지와 알려진 provider 오류 예외를 공통 runtime 동등성 matrix에 흡수한다. Python의 함수명·marker·state key는 계약으로 복사하지 않고 관측된 상태 전이와 불변 조건만 Go 타입·테스트로 옮긴다.
- 오류는 모두 runtime event와 project log에 표면화한다. 알려진 오류만 선언된 policy에 따라 retry/failover하고, unknown 오류는 추정 복구하지 않고 해당 work unit을 명시적으로 중단한다.
- 프로젝트별 최소 관측 로그는 각 프로젝트의
agent-log계열 경로에 보존하고 현재 수준보다 축소하지 않는다. provider/model 선택, quota snapshot, config revision, process/session locator, stream/heartbeat, failure evidence, retry/failover, stop/completion을 동일 execution identity로 추적한다. - Desktop Agent는 Go runtime host, YAML 운영 entry, app-owned registry/state와 Flutter macOS shell을 포함한다. UI 설정 화면은 scaffold만 두지만 설치 산출물에는 runtime binary, 기본 YAML과 Flutter macOS app wrapper가 함께 있어야 한다.
- Desktop Agent는 기본적으로 provider별 approval bypass를 사용하며 사용자 승인 flow를 추가하지 않는다. 자동 연결·실행은 설정으로 끌 수 있게 하되 최초 기본값은 켜짐이다.
- 단위·통합 fake provider 검증과 별도로 실제 로그인된 CLI 환경에서 provider discovery, 짧은 실행, stream, quota/status, cancel, 재호출을 확인하는 macOS smoke를 완료 근거로 남긴다.
기능
Epic: [shared-core] 공통 Runtime과 Host 경계
Node와 Desktop Agent가 하나의 실행 구현을 공유하고 각 제품에는 host 책임만 남기는 기반을 만든다.
- [provider-runtime] 공통 Go runtime이 CLI process 실행, profile/renderer/emitter, stream/session, quota/status와 cancellation을 소유하고 현재 지원 provider family를 단일 catalog로 제공한다. 검증: Node와 Desktop target이 같은 provider conformance suite를 통과하고 host별 provider 구현 복제가 없다.
- [agent-task-manager] 공통 AgentTaskManager가 project filesystem 상태, work-unit identity, route pin, plan/review loop, sequential Milestone 실행과 stop/resume을 모델 감시 없이 수행한다. 검증: supervisor 모델 호출 없이 남은 task 우선과 priority queue 다음 작업 선택이 결정적으로 재현된다.
- [workflow-ownership-boundary] AgentTaskManager는 project-owned agent-ops 작업 파일과 이미 선택된 작업 지시만 실행하며 일반 요청의 direct/Plan/Milestone 분류, IOP 소유 skill, 합성 tool call 기반 작업 파일 생성을 구현하지 않는다. 검증: 기존 작업 파일 dispatch는 동작하고 일반 요청 입력만으로는 runtime이 Milestone/Plan을 분류하거나 생성하지 않으며 별도 오케스트레이션 경계가 유지된다.
- [host-boundary] Node bridge와 Desktop host가 동일 runtime API를 사용하되 Node는 기존 wire/config/command mapping을, Desktop은 app lifecycle/registry/local events를 소유한다. 검증: Edge를 포함하지 않은 Desktop 실행과 기존 Node run/session/status 경로가 모두 같은 core를 통과한다.
- [parity-cutover] 현재 Milestone·SDD, active work log, Python 참조 동작과 Node 구현을 대조한 동등성 matrix를 작성하고 공통 runtime으로 cutover한 뒤 Python 실행 의존성과 Node provider 중복을 제거한다. 검증: matrix의 모든 필수 행에 test 또는 field-smoke evidence가 연결된다.
Epic: [provider-policy] Provider Catalog와 선택 정책
사용자별로 다른 선언 provider와 app/project 정책을 한 번의 결정적 선택으로 연결한다.
- [provider-catalog] YAML 선언과 read-only discovery가 binary, version, authenticated readiness, model/profile capability를 공통 provider catalog로 정규화하고 공식 계열 이름과 내부 identity를 매핑한다. 검증: 누락·미인증·미지원 model/profile은 추정 fallback 없이 명시 오류가 된다.
- [config-ownership] app-owned YAML 안의 기본값과 project registry별 override, file watcher, revision snapshot과 next-invocation 적용 규칙을 구현한다. 검증: 실행 중 변경은 현재 agent에 영향을 주지 않고 다음 호출부터 적용되며 invalid revision은 새 dispatch를 config error로 중단한다.
- [selector-policy] 공통 selector가 default와 ordered conditional rules를 평가해 정확히 하나의 provider/model을 반환하고 Node/Desktop별 정책 차이를 허용한다. 검증: 겹친 조건은 array 순서가 우선하며 결과와 reason/config revision이 audit log에 남는다.
- [quota-admission] 기존 Go usage checker와 검증된 Python 해석을 공통 quota/status 입력으로 흡수하고 app-global 공유 quota, immutable admission batch snapshot과 unknown 격리를 제공한다. 검증: 같은 provider credential/profile을 쓰는 project가 동일 quota 상태를 공유하고 parser 오류가 다른 provider key를 차단하지 않으며 generic stderr만으로 quota exhausted를 추정하지 않는다.
- [runtime-quota-failover] provider adapter의 confirmed quota failure를 base admission snapshot과 분리된 runtime observation으로 받아 같은 persisted candidate 순서·사용 이력의 다음 unused eligible target으로 failover한다. 검증: Gemini/Agy positive·negative fixture, locator/context와 work-unit 보존, app-global key 갱신, 원본 batch·in-flight sibling 불변 및 alternate 부재 시 task-local blocker를 확인한다.
Epic: [execution-lifecycle] 프로젝트 실행과 복구 Lifecycle
여러 workspace를 app이 관리하면서 각 project의 작업 순서와 실행 identity를 보존한다.
- [workspace-registry] 명시 등록 project와 clone/worktree/branch별 canonical workspace instance를 app registry에서 관리하고 project filesystem source of truth와 연결한다. 검증: 같은 저장소의 서로 다른 경로가 충돌 없이 별도 queue/log/session identity를 가진다.
- [task-order] 자동 실행이 등록 project의 기존 agent-ops 작업 상태에서 남은 agent-task를 우선하고 없으면 기존 priority-queue.md의 최상위 ready Milestone부터 project-owned plan/review/완료 지시를 수행한다. 검증: task 잔여·빈 task·blocked dependency·사용자 stop matrix에서 선택과 종료가 일관되고 일반 요청에서 새 작업 파일을 생성하지 않는다.
- [concurrency-stop] 서로 다른 workspace instance는 병렬로 실행하고 같은 instance는 dependency/route pin을 지키며, 사용자 stop은 process group과 session을 취소해 후속 자동 호출을 막는다. 검증: 병렬 project, 동일 repo clone, graceful stop과 강제 종료 smoke가 중복 실행을 남기지 않는다.
- [session-recovery] config revision, work-unit route, process/session locator, logical context와 failure budget을 보존해 crash/restart 뒤 현재 filesystem 상태에서 재개한다. 검증: native resume 가능 provider와 logical transfer provider가 각각 중복 실행 없이 현재 stage를 복원한다.
- [blocked-failover-recovery] typed blocker와 persisted decision identity가 일치할 때만 복구 intent를 만들고 사용자 재시도 또는 policy-authorized status refresh에서 기존 route의 unused alternate와 현재 blocked target에 허용된 same-target retry만 재평가한다. 검증: 현재 시각 정책으로 새 candidate를 만들거나 이미 떠난 target으로 bounce하지 않고 ordinary resume·sibling은 불변이며, 준비 또는 commit 실패에는 기존 decision/history/intent가 보존되고 성공 commit 뒤 intent가 한 번만 소비되어 저장 locator로 이어진다.
Epic: [failure-observe] 오류·관측·로그 동등성
모델 감시 없이도 현재 Python dispatcher가 제공하는 최소 운영 가시성과 예외 처리를 runtime으로 흡수한다.
- [stream-observer] stdout/stderr 분리, raw/normalized stream, heartbeat, silence inspection, PID/start token/process group, session locator와 completion 단일화를 공통 runtime event로 제공한다. 검증: fake emitter와 실제 CLI smoke에서 delta 순서, 단일 terminal, 취소와 orphan 부재를 확인한다.
- [failure-taxonomy] provider codec이 context limit, provider quota, model unavailable, connection/stream disconnect, generic error와 process termination을 provider별 fixture로 typed failure에 정규화하고 AgentTaskManager가 review-control violation 및 work-unit blocker와 결합한다. 검증: Gemini/Agy의 structured quota/429/rate-limit positive fixture와 generic·불완전·target 불일치 negative fixture가 올바른 class/source를 반환하며 manager가 raw 문자열을 재분류하지 않는다.
- [project-logs] provider/model decision, admission snapshot id, runtime quota observation id, app-global quota refresh, config revision, process/session locator, typed blocker, recovery/failover transition과 completion을 project
agent-log에서 execution/work-unit identity로 추적한다. 검증: provider failure와 manager transition의 연결 근거가 누락되지 않고 secret/raw credential이 기록되지 않는다. - [surface-errors] invalid config/provider/auth/model/capability와 unknown runtime 오류를 Desktop event/UI scaffold 및 Node event에 모두 표면화하고 unknown은 자동 추정 복구하지 않는다. 검증: 오류 matrix가 silent fallback이나 무한 retry 없이 terminal 상태와 로그를 남긴다.
Epic: [desktop-delivery] Desktop Agent와 실제 환경 검증
Edge 없이 실행되는 macOS 제품 껍데기와 설치·운영 기준을 제공한다.
- [desktop-host] Desktop Go host가 app-owned project registry/config/state, file watcher, auto-run toggle과 common runtime lifecycle을 제공한다. 검증: Edge나 Python process 없이 여러 등록 workspace를 시작·중단·재시작할 수 있다.
- [flutter-shell] Flutter macOS shell이 Desktop runtime binary와 기본 YAML을 포함한 설치 가능한 app bundle로 패키징되고 최소 status/error/stop surface를 가진다. 검증: clean macOS 사용자 경로에서 설치·실행할 수 있고 D01에서 확정한 창 닫기·명시 종료·로그인 시작 lifecycle과 child process ownership을 지킨다.
- [yaml-operations] UI 설정 화면 없이도 YAML 생성·조회 기반 보조 설정, validation, reload와 project override를 운영할 수 있고 이후 UI가 같은 config service를 사용할 scaffold가 있다. 검증: YAML roundtrip과 read-only discovery 결과가 동일 schema로 조회된다.
- [logged-in-smoke] 실제 로그인된 지원 CLI 환경에서 discovery, 짧은 agent 실행, stream, quota/status, cancel, next invocation config 적용과 project log를 검증한다. 검증: smoke manifest가 실행 provider/model, 환경, 결과와 evidence 경로를 기록한다.
완료 리뷰
- 상태: 없음
- 요청일: 없음
- 완료 근거: 기능 Task가 아직 충족되지 않았고 SDD 사용자 리뷰와 구현 잠금이 남아 있다.
- 검토 항목:
- 모든 기능 Task와 Task 안의 검증이 충족되었다.
- 동등성 matrix에 Python 참조, Node 기존 동작, 선행 selector/provider/routing Milestone 결과가 반영되었다.
- runtime Gemini quota failover와 blocker 복구가 provider/manager 책임 분리, admission snapshot 불변성, app-global 후속 갱신, persisted route, locator/context 및 exactly-once intent 소비까지 검증되었다.
- Node와 Desktop이 동일 provider/runtime conformance suite를 통과하고 중복 provider 구현이 없다.
- 실제 로그인된 macOS smoke와 project-local log evidence가 남아 있다.
- agent-ui 상태 반영: 해당 없음
- 리뷰 코멘트: 없음
범위 제외
- Desktop Agent에 Edge, Control Plane 또는 기존 Personal Edge runtime을 포함하는 구성
- Python dispatcher/selector 코드를 production runtime에서 import, 실행, 번역 호출하거나 진행 중 Python process 상태를 승계하는 migration
- CLI 로그인, credential/token 저장, 계정 회전과 billing 구매 자동화
- provider/model/선택/프로젝트 설정 전체를 편집하는 완성형 Flutter UI. 이번 범위는 향후 같은 config service를 감싸는 scaffold까지다.
- Windows와 Linux 패키지, macOS code signing/notarization과 외부 배포 채널
- 사용자 승인 prompt, interactive approval gate와 per-action 권한 정책
- 외부 webhook/메신저 알림, Control Plane dashboard와 중앙 로그 집계
- 원격 terminal tunnel, Edge를 통한 원격 workspace 제어, oto scheduler/CI-CD
- agent-ops를 사용하지 않는 일반 사용자 요청의 direct/Plan/Milestone 분류, IOP 소유 Milestone/Plan skill 실행과 사용자 agent에 대한 합성 tool call 주입. 이는 에이전트 작업 루프 오케스트레이션 MVP의 범위다.
- agent-ops 공통 스킬 자체를 runtime monitoring loop로 사용하거나 공통 skill/rule 원문을 변경하는 작업
작업 컨텍스트
- 관련 경로:
packages/go/agentruntime,apps/node/internal/adapters/cli,apps/node/internal/runtime,apps/desktop-agent,apps/desktop-agent-ui,packages/go/config,agent-ops/skills/project/orchestrate-agent-task-loop,agent-task,agent-roadmap - 표준선(선택): 공통 구현은
packages/go/agentruntime에 두고 Node와 Desktop host가 의존한다. provider-specific codec은 core 내부 확장점일 수 있지만 host app에 복제하지 않는다. - 표준선(선택): Python은 동작과 오류 사례의 참조이며 production dependency가 아니다. 아직 Python에 없는 선택 엔진은 Agent Task 동적 실행 Target Selector, CLI Agent Group Grade Routing과 승인된 SDD를 기준으로 Go에서 구현한다.
- 표준선(선택): 구현 계획 직전에 Agent Task 동적 실행 Target Selector의 최종 PASS·
complete.logevidence를 다시 freeze한다. 미종결 active plan/review는 결함·검증 후보로만 참고하고 Python 내부 함수명이나 persisted key 자체를 Go parity 요구로 승격하지 않는다. - 표준선(선택): app config는 provider/global/project override의 운영 소유권을 가지고, project filesystem은 작업 상태의 source of truth다. workspace 안의 runtime config를 권위로 사용하거나 app store가
agent-task/roadmap 원문을 중앙 복제하지 않는다. - 표준선(선택): Desktop은 Flutter가 관리하는 local Go sidecar process를 기본 topology로 삼고, Node는 동일 library를 in-process로 사용한다. 정확한 IPC와 lifecycle 계약은 SDD 잠금에서 고정한다.
- 표준선(선택): 설정 merge는 app-owned defaults 뒤 app registry의 project override를 적용하며 ordered rule array는 전체 교체한다. 현재 실행은 immutable revision을 사용하고 hot reload는 다음 agent invocation 경계에서만 활성화한다.
- 표준선(선택): 자동 실행과 approval bypass는 기본 on이다. auth는 CLI가 소유하고 app은 이미 인증된 실행만 사용한다.
- 큐 배치: CLI Agent Group Grade Routing 뒤, Provider 사용량 알림과 운영 표면 앞의 기존 작업 루프 위치를 유지한다.
- 선행 작업: Agent Task 동적 실행 Target Selector, Pi CLI Provider Integration, CLI Agent Group Grade Routing
- 후속 작업: 에이전트 작업 루프 오케스트레이션 MVP, 완성형 Flutter 설정 UI, Windows/Linux packaging, 외부 알림·운영 dashboard, signing/notarization과 배포 채널
- 확인 필요: USER_REVIEW.md의 Desktop background lifecycle 결정