Metadata-Version: 2.4
Name: qanova-agent
Version: 0.3.9
Summary: Nova Agent - Browser automation agent for Nova QA platform
Project-URL: Homepage, https://github.com/nova-platform/nova-agent
Project-URL: Documentation, https://docs.nova-platform.com/agent
Project-URL: Repository, https://github.com/nova-platform/nova-agent
Author: Nova Team
License-Expression: MIT
Keywords: agent,automation,browser,playwright,qa
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.11
Requires-Dist: httpx>=0.26.0
Requires-Dist: orjson>=3.9.0
Requires-Dist: playwright>=1.40.0
Requires-Dist: pydantic-settings>=2.0.0
Requires-Dist: pydantic>=2.5.0
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: rich>=13.0.0
Requires-Dist: structlog>=24.1.0
Requires-Dist: typer>=0.9.0
Requires-Dist: websockets>=12.0
Provides-Extra: dev
Requires-Dist: mypy>=1.8.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
Requires-Dist: pytest>=7.4.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Description-Content-Type: text/markdown

# Nova Agent

Last analyzed: 2026-07-05

`qanova-agent`는 사용자의 로컬 환경에서 실행되는 QA Nova 브라우저 자동화 Agent CLI다. 사용자는 Agent를 등록해 토큰을 저장하고, `nova-agent start`로 Gateway에 연결한다. Runner는 Gateway를 통해 Agent에 DOM 추출, script 실행, browser primitive command를 보내고 Agent는 로컬 Playwright 브라우저에서 이를 수행한다.

## 기술 스택

| 영역 | 사용 기술 |
| --- | --- |
| Runtime | Python 3.11+ |
| CLI | Typer, Rich |
| Browser | Playwright |
| WebSocket | websockets |
| Models/settings | Pydantic, pydantic-settings, python-dotenv |
| Serialization | orjson |
| Logging | structlog |
| HTTP/pairing/update check | urllib, pip/pipx |
| Test | pytest, pytest-asyncio |

## 실행 책임

Agent는 로컬 브라우저 실행과 Gateway 메시지 응답만 담당한다. AI 호출과 DB 업데이트는 Runner가 담당한다.

- 사용자 브라우저에서 Agent 등록 페이지 열기
- 웹 pairing 승인 후 1회 발급된 token을 `~/.nova-agent/token`에 저장
- Gateway `/ws/agent`에 `Authorization: Bearer ...` 헤더로 연결
- Gateway registration 이후 system info 전송
- heartbeat 주기 전송
- job assignment 수락 또는 pool full 거절
- job별 browser session 생성/정리
- entry URL navigation
- DOM 추출과 screenshot 전송
- Runner가 보낸 Playwright script 실행
- Runner 공통 엔진이 보내는 browser primitive command 실행
- step/job 완료, 실패, 취소 message 전송
- reconnect 시 active job reset 알림

## CLI 명령

| 명령 | 설명 |
| --- | --- |
| `nova-agent --version` | 현재 Agent 버전 출력 |
| `nova-agent login` | pairing code로 FE Agent 등록 화면을 열고 승인된 token을 자동 저장 |
| `nova-agent start` | Gateway에 연결하고 job 대기 |
| `nova-agent doctor` | token, Gateway URL, 선택적 Playwright browser 준비 상태 진단 |
| `nova-agent test-connection` | Gateway 인증과 Agent 등록 handshake를 1회 테스트 |
| `nova-agent logs` | 최근 Agent 로그 출력 |
| `nova-agent status` | token 저장 여부와 경로 표시 |
| `nova-agent version-check` | 설치된 Agent와 PyPI 최신 버전 비교 |
| `nova-agent logout` | 저장된 token 삭제 |
| `nova-agent update` | pip 또는 pipx로 최신 `qanova-agent` 패키지 설치 |
| `nova-agent install-deps` | Linux에서 Playwright system dependency 설치 |

`start` 옵션:

| 옵션 | 설명 |
| --- | --- |
| `--headless/--no-headless` | 브라우저 headless 여부 |
| `--gateway-url`, `-g` | Gateway WebSocket URL override |
| `--pool-size`, `-p` | 동시 browser 수, 1-10 |
| `--verbose`, `-v` | DEBUG logging |

`login` 옵션:

| 옵션 | 설명 |
| --- | --- |
| `--url`, `-u` | Frontend URL override |
| `--api-url` | pairing session을 만들 API base URL override |
| `--pairing-timeout` | 브라우저 승인 대기 시간, 기본 600초 |
| `--manual` | 구버전 서버용 수동 token 복사/붙여넣기 fallback |

`doctor` 옵션:

| 옵션 | 설명 |
| --- | --- |
| `--gateway-url`, `-g` | 검증할 Gateway WebSocket URL override |
| `--check-browser` | Playwright Chromium 설치/실행 준비 상태 확인 |

`test-connection` 옵션:

| 옵션 | 설명 |
| --- | --- |
| `--gateway-url`, `-g` | 테스트할 Gateway WebSocket URL override |

`logs` 옵션:

| 옵션 | 설명 |
| --- | --- |
| `--lines`, `-n` | 출력할 최근 로그 라인 수, 기본 80 |
| `--path` | 로그 파일 경로만 출력 |

## 설정과 token 저장

기본 설정은 `src/nova_agent/settings.py`에 있다. 환경 변수 prefix는 `NOVA_`다. `NOVA_ENV=dev`이면 프로젝트 `.env`, 기본 prod이면 `.env.prod`를 읽는다.

| 설정 | 기본값/설명 |
| --- | --- |
| `NOVA_GATEWAY_URL` | 기본 `wss://agent-gw.qanova.kr/ws/agent` |
| `NOVA_API_URL` | 기본 `https://api.qanova.kr/api/v1` |
| `NOVA_FRONTEND_URL` | 기본 `https://qanova.kr` |
| `NOVA_AGENT_REGISTER_PATH` | 기본 `/agent/register` |
| `NOVA_HEADLESS` | 기본 `true` |
| `NOVA_VIEWPORT_WIDTH`, `NOVA_VIEWPORT_HEIGHT` | 기본 `1920x1080` |
| `NOVA_POOL_SIZE` | 기본 `1` |
| `NOVA_HEARTBEAT_INTERVAL` | 기본 30초 |
| `NOVA_CONNECTION_TIMEOUT` | 기본 10초 |
| `NOVA_RECONNECT_DELAY` | 기본 5초 |
| `NOVA_MAX_RECONNECT_ATTEMPTS` | 기본 0, 무제한 |
| `NOVA_JOB_TIMEOUT` | job idle timeout, 기본 300초 |
| `NOVA_SCRIPT_TIMEOUT` | script timeout, 기본 30초 |
| `NOVA_ACTION_TIMEOUT` | 단일 action timeout, 기본 10초 |
| `NOVA_MAX_SCREENSHOT_BYTES` | WebSocket으로 전송할 screenshot 원본 byte 상한, 기본 8MB, `0`이면 제한 없음 |
| `NOVA_MAX_DOM_ELEMENTS` | WebSocket으로 전송할 interactive DOM element 상한, 기본 1000 |
| `NOVA_MAX_DOM_TEXT_CHARS` | DOM element별 text/value 최대 문자 수, 기본 200 |
| `NOVA_MAX_DOM_HTML_CHARS` | fallback raw HTML 최대 문자 수, 기본 30000, `0`이면 raw HTML 생략 |
| `NOVA_LOCAL_AGENT_ALLOW_EXECUTE_SCRIPT` | Agent 기본 `execute_script` 허용 여부, 기본 `true` |
| `NOVA_LOCAL_AGENT_ALLOW_BROWSER_EVALUATE`, `NOVA_LOCAL_AGENT_ALLOW_BROWSER_CONTENT` | Agent 기본 browser `evaluate/content` 허용 여부, 기본 `true` |
| `NOVA_LOCAL_AGENT_ALLOW_STORAGE_STATE`, `NOVA_LOCAL_AGENT_ALLOW_ADD_COOKIES` | Agent 기본 storage/cookie primitive 허용 여부, 기본 `true` |
| `NOVA_LOCAL_AGENT_ALLOW_CDP` | Agent 기본 CDP primitive 허용 여부, 기본 `false` |
| `NOVA_LOCAL_AGENT_RESTRICT_NAVIGATION` | Agent 기본 navigation origin 제한 여부, 기본 `false` |

token은 `~/.nova-agent/token`에 저장되고 권한은 `0600`으로 설정된다. `~/.nova-agent` 디렉터리는 `0700`으로 수선되며, config directory 또는 token 파일이 symlink이면 읽기/쓰기를 거부한다. token path가 일반 파일이 아니어도 거부한다. token은 Gateway의 Agent JWT 검증에 사용된다.

Agent 로그는 `~/.nova-agent/agent.log`에 rotating file로 저장된다. 기본 보관 정책은 파일당 5MB, backup 3개다. `nova-agent logs`로 최근 로그를 확인할 수 있다.

Local Agent가 `execute_script` 또는 위험 browser primitive(`evaluate`, `content`, `storage_state`, `add_cookies`, `cdp_send`, `cdp_detach`)를 허용/차단할 때 job/step/command id와 policy key를 structured log로 남긴다. script 본문, cookie 값, storage state 같은 민감 payload는 로그에 남기지 않는다. Runner가 `job_assign.config.security_policy`를 보내면 그 값이 우선하며, 누락된 policy key는 위 `NOVA_LOCAL_AGENT_*` 기본값을 따른다.

Local Agent가 `dom_extracted`, `script_result`, primitive browser `screenshot` 결과에 screenshot을 첨부할 때 screenshot 원본 byte가 `NOVA_MAX_SCREENSHOT_BYTES`를 넘으면 base64 본문을 생략하고 `omitted=true`, `reason=too_large`, `original_bytes`, `max_bytes` metadata만 전송한다. Runner는 screenshot이 없는 결과도 처리할 수 있어 대형 payload로 인한 WebSocket 불안정을 줄인다.

DOM payload는 interactive element 중심이며 `NOVA_MAX_DOM_ELEMENTS`, `NOVA_MAX_DOM_TEXT_CHARS`, `NOVA_MAX_DOM_HTML_CHARS`로 크기를 제한한다. Agent는 `dom.payload_limits`에 적용된 제한과 `element_limit_reached`, `html_truncated`, `html_chars` metadata를 함께 전송한다.

## 시작 흐름

1. 사용자가 `nova-agent login`을 실행한다.
2. CLI가 API `POST /agent-pairings`로 10분짜리 pairing session을 만들고 pairing code를 표시한다.
3. CLI가 `FRONTEND_URL + /agent/register?pairingId=...&code=...`를 브라우저로 연다.
4. 사용자가 웹앱에서 프로젝트와 Agent 이름을 선택하고 pairing code를 승인한다.
5. CLI가 API `GET /agent-pairings/{pairingId}?code=...`를 poll하다가 완료 응답의 token을 1회 수신한다.
6. CLI가 token을 `~/.nova-agent/token`에 저장한다. token은 브라우저에 표시되지 않는다.
7. pairing API가 없거나 브라우저 handoff 전에 생성 응답이 유효하지 않으면 `nova-agent login`은 수동 token 입력 방식으로 fallback한다. 명시적으로 수동 방식을 쓰려면 `nova-agent login --manual`을 사용한다.
   API가 `429 Too Many Requests`를 반환하면 `Retry-After` 안내를 표시하고 종료한다. 이 경우 수동 token 입력으로 자동 fallback하지 않는다.
   승인 완료 응답에 token이 없으면 token을 저장하지 않고 명확한 오류로 종료한다.
8. 사용자가 `nova-agent start`를 실행한다.
9. CLI가 업데이트를 확인하고 Playwright Chromium 설치를 보장한다.
10. `BrowserPool`과 `JobManager`를 생성한다.
11. `AgentWebSocketClient`가 Agent token을 Authorization 헤더로 보내 Gateway에 연결한다.
12. Gateway의 `registered` 메시지를 받으면 OS/arch/version/python/browser 정보를 `register` 메시지로 보낸다.
13. heartbeat와 message loop가 시작되고 job을 기다린다.

## WebSocket protocol

Agent가 Gateway로 보내는 주요 메시지:

- `register`
- `heartbeat`
- `job_accepted`
- `job_assign_failed`
- `step_started`
- `dom_extracted`
- `script_result`
- `browser_command_result`
- `step_completed`
- `goal_achieved`
- `job_completed`
- `error`

Gateway/Runner에서 Agent로 들어오는 주요 메시지:

- `registered`
- `register_ack`
- `heartbeat_ack`
- `job_assign`
- `extract_dom`
- `execute_script`
- `browser_command`
- `next_step`
- `job_cancel`
- `goal_achieved`

`goal_achieved`는 Agent 내부에서 `check_goal` 결과처럼 처리되어 step 완료로 이어진다.

## 내부 구조

| 경로 | 설명 |
| --- | --- |
| `src/nova_agent/cli.py` | Typer CLI, login/start/status/logout/update/install-deps |
| `src/nova_agent/settings.py` | env 기반 runtime settings |
| `src/nova_agent/config.py` | token/config directory 관리 |
| `src/nova_agent/constants.py` | protocol constant, timeout constant |
| `src/nova_agent/websocket/client.py` | Gateway 연결, reconnect, heartbeat/message loop |
| `src/nova_agent/websocket/handlers.py` | message type별 handler |
| `src/nova_agent/websocket/heartbeat.py` | heartbeat loop |
| `src/nova_agent/executor/job_manager.py` | job session lifecycle, message execution, response 전송 |
| `src/nova_agent/executor/job_session.py` | job별 browser/session state |
| `src/nova_agent/executor/script_runner.py` | Runner가 보낸 script 실행 |
| `src/nova_agent/browser/pool.py` | 동시 browser pool |
| `src/nova_agent/browser/manager.py` | Playwright browser/context/page, cookie/localStorage/header/test access key 설정 |
| `src/nova_agent/browser/dom_extractor.py` | Cloud Runner DomParserService와 맞춘 DOM extraction |
| `src/nova_agent/models` | job/message/dom model |
| `src/nova_agent/utils` | system info, domain matcher, logging |

## Job lifecycle

1. `job_assign` 수신
2. Browser pool slot 확인
3. slot 없으면 `job_assign_failed` with `POOL_FULL`
4. slot 있으면 browser manager acquire
5. scenario, steps, config, environment variables로 `JobSession` 생성
6. `job_accepted` 전송
7. entry URL로 navigation
8. shared engine managed job이면 Runner가 `extract_dom`/`browser_command`로 주도
9. legacy flow면 Agent가 첫 step 시작
10. script/command 실행 중 새 tab/popup과 page close를 감지해 active page 복구
11. terminal 또는 cancel 시 session cleanup과 browser release

## BrowserManager 기능

BrowserManager는 Playwright Chromium/Firefox/WebKit 중 config browser type을 사용하고, 지원하지 않는 값은 Chromium으로 fallback한다.

지원 기능:

- viewport 설정
- headless, slow_mo, JS heap size, dev shm option
- storage_state, cookie, localStorage 적용
- extra HTTP headers
- test access key header를 route interception으로 주입
- permission grant
- network throttling
- popup/new page active page 전환
- dialog/download/network/console event capture
- DOM extraction용 page access
- browser primitive command 실행

Runner와 Cloud 모드의 동작 차이를 줄이기 위해 DOM extractor는 Cloud Runner의 selector/attribute/metadata extraction 정책을 따라간다.

## 로컬 개발

```bash
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
python -m playwright install chromium
nova-agent --version
```

dev Gateway를 사용할 때:

```bash
NOVA_ENV=dev nova-agent start --gateway-url ws://localhost:9100/ws/agent --no-headless --verbose
```

prod 기본 URL은 secure WebSocket이므로 local Gateway에는 반드시 override가 필요하다.

## 테스트

```bash
pytest
```

현재 테스트는 browser, CLI, WebSocket 동작을 중심으로 구성되어 있다. message protocol이나 job manager를 바꾸면 Gateway/Runner 쪽 테스트와 함께 검증한다.

## 변경 시 같이 봐야 하는 프로젝트

| Agent 변경 | 같이 확인 |
| --- | --- |
| message payload/type 변경 | `qanova-agent-gateway/gateway/models/message.py`, `qanova-runner/gateway_client` |
| token/JWT 처리 변경 | `qanova-api` AgentController/token 발급, Gateway jwt validator |
| browser config 변경 | Runner `AgentJobCoordinator._build_agent_browser_config` |
| DOM extraction 변경 | Runner `services/dom` 및 learning prompt/parser |
| pool/full/cancel 정책 변경 | Gateway runner handler, Runner AgentJobCoordinator |

## 운영/사용자 지원 메모

사용자가 인증 오류를 겪으면 token 만료/삭제/agent DB 비활성화/project mismatch를 먼저 확인한다. CLI는 401/403을 받으면 재연결하지 않고 종료하도록 되어 있다.

브라우저 설치 실패는 Playwright browser 또는 Linux system dependency 문제일 가능성이 높다. `nova-agent install-deps`와 `python -m playwright install chromium`을 확인한다.

Local Agent는 사용자의 로컬 네트워크와 브라우저 상태에 영향을 받는다. Cloud Runner에서 성공하는 시나리오가 Local Agent에서 실패하면 viewport, cookie/storage_state, test access key header, popup/tab 전환, 브라우저 권한을 우선 비교한다.
