Compare commits
250 Commits
14d2acf6c4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32cf69469c | ||
|
|
7c8891b99c | ||
|
|
3cc3442fda | ||
|
|
e95e7791f9 | ||
|
|
2bf1eb41d1 | ||
|
|
cf1352eefa | ||
|
|
6aea48e2e9 | ||
|
|
bd5a7ca8b9 | ||
|
|
8ada5f7daf | ||
|
|
4f2be831a1 | ||
|
|
cbfd137dcb | ||
|
|
a99a1e3f54 | ||
|
|
ad4ed623bd | ||
|
|
64800d3c20 | ||
|
|
70c83b4226 | ||
|
|
bb54802c06 | ||
|
|
bf53072f3c | ||
|
|
02b4b03699 | ||
|
|
db805c6fde | ||
|
|
7f33a20e43 | ||
|
|
ef788e6ecc | ||
|
|
cd00986274 | ||
|
|
12095f36a4 | ||
|
|
498683c977 | ||
|
|
1662ac4f6b | ||
|
|
d027562f17 | ||
|
|
cc261011d6 | ||
|
|
37fbb9657e | ||
|
|
965f619664 | ||
|
|
139ad3ee93 | ||
|
|
08fd08b9a6 | ||
|
|
326454be40 | ||
|
|
98b7585e3c | ||
|
|
c7f939ce85 | ||
|
|
7a1675fd5d | ||
|
|
6b9f1188c3 | ||
|
|
13e569f426 | ||
|
|
b2f17a086b | ||
|
|
7dbf73aa89 | ||
|
|
62ee081ffe | ||
|
|
60a2a97868 | ||
|
|
7ae43088e6 | ||
|
|
729875f3a6 | ||
|
|
a00d561e28 | ||
|
|
7ade31e4cf | ||
|
|
66233bd9cb | ||
|
|
ed90cbf874 | ||
|
|
2e32be96fe | ||
|
|
87c99c7243 | ||
|
|
01539e9bfb | ||
|
|
4684376c78 | ||
|
|
7ee5947b32 | ||
|
|
59ddcbb612 | ||
|
|
ed63f65975 | ||
|
|
02d9799f20 | ||
|
|
a9c64e6716 | ||
|
|
8e89209a27 | ||
|
|
a8d875167d | ||
|
|
2a1ebf1020 | ||
|
|
5a76e30993 | ||
|
|
d6ed8764b8 | ||
|
|
a214ab029f | ||
|
|
f45d2d970d | ||
|
|
6dc0854c47 | ||
|
|
0e03b3a300 | ||
|
|
353265bed8 | ||
|
|
eef59e6bb2 | ||
|
|
a4d7286bce | ||
|
|
70dc301dca | ||
|
|
7630bf1f8c | ||
|
|
ec7883755a | ||
|
|
072f83bf25 | ||
|
|
5e697cd919 | ||
|
|
b3825e1c8a | ||
|
|
a99c283656 | ||
|
|
58887f6933 | ||
|
|
488b36f192 | ||
|
|
300338d5d3 | ||
|
|
e745744636 | ||
|
|
b88e75b075 | ||
|
|
2ece05fc6f | ||
|
|
6bbc9ddd00 | ||
|
|
89c95de18c | ||
|
|
fadd39424b | ||
|
|
22e1799d66 | ||
|
|
e4f674ec9f | ||
|
|
47c0602427 | ||
|
|
75762964e3 | ||
|
|
d2023321bd | ||
|
|
2eb1fbb6b7 | ||
|
|
13f13ee243 | ||
|
|
2d5059d2d5 | ||
|
|
7bbd8749d7 | ||
|
|
d5fdc41f35 | ||
|
|
3ec45ac6b7 | ||
|
|
101ec20b21 | ||
|
|
86e5a24a75 | ||
|
|
7b6cd59801 | ||
|
|
f13bcc871c | ||
|
|
ecebec3906 | ||
|
|
e21f71baf8 | ||
|
|
b81135d855 | ||
|
|
a6aa643be9 | ||
|
|
6234301a47 | ||
|
|
a72c522ab5 | ||
|
|
f4ded343c7 | ||
|
|
5aad82c727 | ||
|
|
94cbda6f3d | ||
|
|
549af6dae2 | ||
|
|
e306fae130 | ||
|
|
bc9d0f2fbb | ||
|
|
17978a750c | ||
|
|
0f057c0c95 | ||
|
|
a41062b6ff | ||
|
|
029a246658 | ||
|
|
e7631177f8 | ||
|
|
4a5521dcc3 | ||
|
|
ab0c116c9e | ||
|
|
07bbb626a6 | ||
|
|
d55b6b97ad | ||
|
|
d8eac80b2f | ||
|
|
759dab55b6 | ||
|
|
bbfafdc5e4 | ||
|
|
ac803d436f | ||
|
|
ebf2228aa8 | ||
|
|
881a424b23 | ||
|
|
d06b1ea0db | ||
|
|
48ae19b3e1 | ||
|
|
9ccfa83439 | ||
|
|
0fae7e32aa | ||
|
|
47cc838d9d | ||
|
|
4e8ac8d6b7 | ||
|
|
0da6291d98 | ||
|
|
4bb400820c | ||
|
|
302d21d35c | ||
|
|
6640d42449 | ||
|
|
1ce8b7c707 | ||
|
|
2eea5fa638 | ||
|
|
adbed69237 | ||
|
|
442221e6a3 | ||
|
|
50efd52f41 | ||
|
|
f6181e552d | ||
|
|
1bb54eb820 | ||
|
|
9523d1328e | ||
|
|
96e9b8adce | ||
|
|
edd4943e2e | ||
|
|
6ea3211a58 | ||
|
|
b9b240de0b | ||
|
|
36b70505d7 | ||
|
|
5bdaba01bd | ||
|
|
28d399ba91 | ||
|
|
fadfd88f51 | ||
|
|
61bd4b1ffb | ||
|
|
5f795b9a91 | ||
|
|
a372bd8b2d | ||
|
|
e3f8fb93f7 | ||
|
|
7ca0bc0f1f | ||
|
|
7f079a56a0 | ||
|
|
fdc0084813 | ||
|
|
f309518e78 | ||
|
|
412c212c6e | ||
|
|
0035394b9c | ||
|
|
0fdf668abc | ||
|
|
5a1d4f0b0c | ||
|
|
82461bc3fc | ||
|
|
9ef2c3f07c | ||
|
|
12a1cf8692 | ||
|
|
f302984721 | ||
|
|
15f6a743a4 | ||
|
|
d521dd5fa3 | ||
|
|
078f721187 | ||
|
|
2d9fe964f6 | ||
|
|
37c0aae41c | ||
| 64f80212c3 | |||
| 9b93ee9776 | |||
| c9f44afcf1 | |||
| 429cae47b7 | |||
| 5e5f515db4 | |||
| 6739f8f30c | |||
| 75289b3ec5 | |||
| ae0509fbb5 | |||
| f96203646e | |||
| c910c7c386 | |||
| 10caae1506 | |||
| 28975f9c4b | |||
| 40e3cd550f | |||
|
|
1f96997831 | ||
|
|
d4a2016d06 | ||
|
|
1835166c5a | ||
|
|
e5a05e3ac4 | ||
|
|
9036f1cefc | ||
|
|
56de71470d | ||
|
|
5cdf7777a5 | ||
|
|
bcc29f9331 | ||
|
|
5a4ac1bf9b | ||
|
|
ae51d28857 | ||
|
|
71f2a269f0 | ||
|
|
6d8c6f182c | ||
|
|
bb6c03e957 | ||
|
|
a9feee6faa | ||
|
|
52c9526fdb | ||
|
|
feb8c05a73 | ||
|
|
3fcf4f7037 | ||
|
|
d7ed454332 | ||
|
|
1bf41ceee3 | ||
|
|
d2a477e12e | ||
|
|
58a421f5a6 | ||
|
|
7eca0763c9 | ||
|
|
3d75825bba | ||
|
|
7e36db5191 | ||
|
|
c60d14e408 | ||
|
|
95c2905e14 | ||
|
|
95da3e9307 | ||
|
|
6dbbb57fa7 | ||
|
|
c1303999cf | ||
|
|
1696a2976b | ||
|
|
71aa80d144 | ||
| ff559bc6ee | |||
| a0d46f1ff3 | |||
| 4d780ec5e7 | |||
| 6179c4d242 | |||
| 5a3217d31a | |||
| 08c5cb461b | |||
| ae91134ff2 | |||
| c9524fc8a8 | |||
| 11a4730873 | |||
| bd46beabb1 | |||
| 95d4f854f5 | |||
| 186875ad0b | |||
| 99f3f264ed | |||
| d1586c5e97 | |||
| 4dcb78c1ce | |||
| 0470c03ab3 | |||
| 26c19fb6be | |||
| c4dfbcad67 | |||
| 7982263fcd | |||
| 8fbf6bf6b7 | |||
| f8f9ce8f5f | |||
| 82b727a1e6 | |||
| c15b0f676f | |||
| 8a6428efa8 | |||
| b50012075e | |||
| 514c0f2738 | |||
| 17dd6654f1 | |||
| 048ffd90a3 | |||
| 93439d2f1c | |||
| a440868101 | |||
| 47dbd38c7c | |||
| e107b70510 | |||
| bec38f9a6a |
70
.agents/AGENT.md
Normal file
70
.agents/AGENT.md
Normal file
@@ -0,0 +1,70 @@
|
||||
---
|
||||
description: 모든 작업에 자동 적용되는 에이전트 행동 규칙. 새 대화 시작 시 반드시 이 파일을 먼저 읽습니다.
|
||||
---
|
||||
|
||||
# Agent Rules
|
||||
|
||||
## Identity
|
||||
|
||||
당신은 이 프로젝트의 시니어 개발자입니다. 지시를 정확히 따르고, 추측보다 근거를 우선합니다.
|
||||
|
||||
## NEVER (절대 금지)
|
||||
|
||||
1. NEVER start coding without reading relevant reference documents in `.agents/references/`
|
||||
2. NEVER guess when documentation exists — always check `.agents/references/` first
|
||||
3. NEVER repeat a failed approach — check `.agents/references/known-issues.md` first
|
||||
4. NEVER call APIs directly when helper scripts exist in `.agents/workflows/helpers/`
|
||||
5. NEVER skip the pre-task checklist defined in `.agents/workflows/pre-task.md`
|
||||
6. NEVER attempt the same failed approach more than 2 times
|
||||
7. NEVER truncate error messages — always show the full error output
|
||||
8. NEVER say "구현 완료" or "동작 확인" without ACTUAL end-to-end test — import/문법 통과는 검증이 아님
|
||||
9. NEVER confuse "코드가 논리적으로 맞음" with "실제로 동작함" — 실행 로그가 없으면 미검증
|
||||
10. NEVER fix or audit code by looking at only the immediate file:
|
||||
(a) Open the PRODUCER (who creates the data?) and CONSUMER (who reads/deletes?)
|
||||
(b) Search for defense mechanisms (try-catch, dedup, idempotency guards)
|
||||
(c) DISPROVE the bug before reporting — if a defense exists, it may be a false positive
|
||||
(d) Report only bugs with a proven end-to-end triggering path
|
||||
"I traced the flow" without opening actual files = violation.
|
||||
11. NEVER apply changes mechanically across files — every import, variable, function must have at least one callsite in the SAME file
|
||||
|
||||
## ALWAYS (필수)
|
||||
|
||||
1. ALWAYS run `.agents/workflows/pre-task.md` before any implementation task
|
||||
2. ALWAYS check `.agents/references/known-issues.md` before debugging
|
||||
3. ALWAYS cite which reference document you consulted and what you learned
|
||||
4. ALWAYS stop and ask the user if 2 consecutive attempts on the same approach fail
|
||||
5. ALWAYS use existing helper scripts instead of raw API calls
|
||||
6. ALWAYS read related existing code (minimum 3 files) before writing new code
|
||||
7. ALWAYS verify with real execution after implementation — trigger the actual flow, check logs (e.g. extension.log), confirm the expected result appeared
|
||||
8. ALWAYS distinguish "구현했다" vs "검증했다" when reporting to user — 테스트 안 했으면 명시
|
||||
9. ALWAYS cross-reference with project history (devlog, git log -5, Vikunja) when evaluating system state — code absence may mean "intentionally removed" or "deployed externally", not "unimplemented"
|
||||
|
||||
## Failure Protocol
|
||||
|
||||
```
|
||||
1st failure → Re-read reference docs → Try DIFFERENT approach
|
||||
2nd failure (same issue) → STOP → Report diagnosis to user with:
|
||||
- What was tried
|
||||
- What failed
|
||||
- Root cause hypothesis
|
||||
- Suggested next steps
|
||||
3rd attempt on same approach → FORBIDDEN
|
||||
```
|
||||
|
||||
## Reference Loading Order
|
||||
|
||||
1. `.agents/AGENT.md` (this file — behavior rules)
|
||||
2. `.agents/references/known-issues.md` (past failure patterns)
|
||||
3. `.agents/references/` (project-specific knowledge)
|
||||
4. `.agents/workflows/services.md` (service credentials & protocols)
|
||||
5. `.agents/workflows/` (action procedures)
|
||||
|
||||
## Bug Report Protocol
|
||||
|
||||
→ See `.agents/references/bug-report-protocol.md`
|
||||
|
||||
## PowerShell Notes
|
||||
|
||||
- `curl` → PowerShell에서 `Invoke-WebRequest` 별칭. **반드시 `curl.exe`** 사용
|
||||
- `npm` → 실행 정책 문제 시 `cmd /c npm` 사용
|
||||
- JSON 처리 시 `.py` 스크립트 권장 (PowerShell 이스케이핑 이슈 방지)
|
||||
163
.agents/GUIDE.md
Normal file
163
.agents/GUIDE.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# AI 에이전트 워크플로우 시스템 가이드
|
||||
|
||||
> 이 가이드는 AI 코딩 에이전트가 더 똑똑하게 동작하도록 설계된 범용 워크플로우 시스템의 사용법을 설명합니다.
|
||||
|
||||
---
|
||||
|
||||
## 왜 이 시스템이 필요한가?
|
||||
|
||||
AI 에이전트는 다음과 같은 문제를 자주 일으킵니다:
|
||||
|
||||
| 문제 | 원인 |
|
||||
|------|------|
|
||||
| 📋 워크플로우를 무시함 | 규칙이 강제가 아닌 권고 사항으로만 작성됨 |
|
||||
| 🔄 같은 실수를 반복함 | 과거 실패 기록을 저장/참조하는 메커니즘 없음 |
|
||||
| 📖 레퍼런스 문서를 안 읽음 | "읽어라"는 강제 지시가 없고, 어떤 문서를 확인할지 불명확 |
|
||||
| 🎲 추측으로 시행착오 | 작업 전 체크리스트(Pre-flight Checklist) 부재 |
|
||||
|
||||
이 시스템은 **13회 웹 검색**, **80+ 소스 분석**, **7개 주요 AI 플랫폼**(Claude, GPT, Gemini, Cursor, Cline, Roo, Windsurf) 연구를 기반으로 설계되었습니다.
|
||||
|
||||
---
|
||||
|
||||
## 파일 구조 개요
|
||||
|
||||
```
|
||||
.agents/
|
||||
├── AGENT.md ← 🧠 에이전트 헌법 (NEVER/ALWAYS 규칙)
|
||||
├── GUIDE.md ← 📖 이 가이드
|
||||
├── references/ ← 📚 프로젝트 지식 베이스
|
||||
│ ├── architecture.md ← 아키텍처 설명
|
||||
│ ├── tech-stack.md ← 기술 스택 & 버전
|
||||
│ ├── conventions.md ← 코딩 컨벤션
|
||||
│ └── known-issues.md ← 🔴 과거 실패 기록 (핵심!)
|
||||
└── workflows/ ← ⚙️ 행동 절차
|
||||
├── start.md ← 세션 시작 (룰 로딩 + devlog 복구)
|
||||
├── end.md ← 세션 종료 (devlog + known-issues + Vikunja + Git)
|
||||
├── pre-task.md ← 작업 전 필수 체크리스트
|
||||
├── debug.md ← 디버깅 전용 절차
|
||||
├── services.md ← 서비스 연동 정보 + AI 작업 프로토콜
|
||||
├── check-gitea.md ← Gitea 현황 조회
|
||||
├── check-vikunja.md ← Vikunja 태스크 조회
|
||||
└── helpers/
|
||||
├── vikunja_helper.py ← Vikunja API 안전 래퍼
|
||||
└── wiki_helper.py ← Gitea Wiki 래퍼
|
||||
```
|
||||
|
||||
**프로젝트 루트에 자동 생성되는 디렉토리:**
|
||||
```
|
||||
docs/devlog/ ← 📓 세션별 작업 기록
|
||||
├── YYYY-MM-DD.md ← Index (매일 1줄씩 누적)
|
||||
└── entries/
|
||||
└── YYYYMMDD-NNN.md ← Entry (설계 결정/미완료 시만)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 각 파일의 역할
|
||||
|
||||
### 🧠 `AGENT.md` — 에이전트 헌법
|
||||
|
||||
에이전트가 **모든 대화에서 따라야 하는 글로벌 규칙**입니다.
|
||||
|
||||
**핵심 메커니즘:**
|
||||
- **NEVER 규칙**: `"절대 ~하지 마라"` — 연구에 따르면 금지 규칙이 더 잘 지켜집니다
|
||||
- **Failure Protocol**: 동일 접근 2회 실패 시 자동 중단 → 유저에게 보고
|
||||
- **Reference Loading Order**: 어떤 문서를 먼저 읽을지 우선순위 명시
|
||||
|
||||
### 📋 `pre-task.md` — 사전 점검 체크리스트
|
||||
|
||||
모든 구현 작업 전에 실행하는 **4단계 체크리스트**:
|
||||
1. 요구사항 정리
|
||||
2. 레퍼런스 확인 (추측 금지)
|
||||
3. 계획 수립
|
||||
4. 유저 확인
|
||||
|
||||
### 🔴 `known-issues.md` — 과거 실패 기록
|
||||
|
||||
**가장 중요한 파일.** 에이전트가 같은 실수를 반복하는 근본 원인은 **실패를 기억하지 못하기 때문**입니다. 이 파일은:
|
||||
- 세션 종료 시 에이전트가 자동으로 새 이슈를 추가
|
||||
- 디버깅/구현 전에 에이전트가 반드시 확인
|
||||
- 시간이 지날수록 **축적 학습** 효과
|
||||
|
||||
### 🔧 `debug.md` — 디버깅 전용 워크플로우
|
||||
|
||||
**추측 기반 디버깅을 금지**하는 5단계 절차:
|
||||
1. 정보 수집 (에러 전문 확인)
|
||||
2. known-issues 확인
|
||||
3. 근본 원인 분석 (가설 → 검증)
|
||||
4. 수정 및 검증
|
||||
5. 기록 (known-issues에 추가)
|
||||
|
||||
### 📓 Devlog — 세션별 작업 기록 (start.md / end.md에서 관리)
|
||||
|
||||
known-issues가 **실패만** 기록한다면, devlog는 **전체 세션 이력**을 기록합니다:
|
||||
- **Index** (`docs/devlog/YYYY-MM-DD.md`): 매 작업마다 1줄 (필수)
|
||||
- **Entry** (`docs/devlog/entries/YYYYMMDD-NNN.md`): 설계 결정/미완료/삽질 시만 (선택)
|
||||
- **start.md**에서 자동으로 오늘/어제 devlog를 읽어 맥락 복구
|
||||
|
||||
### ▶️ `start.md` / ⏹️ `end.md` — 세션 관리
|
||||
|
||||
- **start**: 에이전트 룰 로딩 + devlog 맥락 복구 + Git 상태 + Vikunja TODO
|
||||
- **end**: known-issues 업데이트 + devlog 기록 + Vikunja 동기화 + Git commit/push
|
||||
|
||||
---
|
||||
|
||||
## 사용법
|
||||
|
||||
### 새 프로젝트에 적용하기
|
||||
|
||||
1. `.agents/` 디렉토리를 프로젝트에 복사
|
||||
2. `references/` 파일들을 프로젝트에 맞게 채우기:
|
||||
- `architecture.md` — 프로젝트 구조 설명
|
||||
- `tech-stack.md` — 사용 기술 및 버전
|
||||
- `conventions.md` — 코딩 스타일 규칙
|
||||
3. 프로젝트별 워크플로우가 있다면 `workflows/`에 추가
|
||||
|
||||
### 프로젝트별 워크플로우와 함께 사용하기
|
||||
|
||||
이 범용 워크플로우와 프로젝트별 워크플로우(예: Vikunja 동기화, Gitea 연동)는 **함께 사용**합니다:
|
||||
|
||||
```
|
||||
.agents/
|
||||
├── AGENT.md ← 범용 (공통)
|
||||
├── references/ ← 범용 + 프로젝트 특화
|
||||
│ ├── known-issues.md ← 범용 (공통)
|
||||
│ └── ... ← 프로젝트에 맞게 작성
|
||||
└── workflows/
|
||||
├── pre-task.md ← 범용 (공통)
|
||||
├── debug.md ← 범용 (공통)
|
||||
├── start.md ← 범용 기반 + 프로젝트 단계 추가
|
||||
├── end.md ← 범용 기반 + 프로젝트 단계 추가
|
||||
├── services.md ← ⭐ 프로젝트별
|
||||
├── check-vikunja.md ← ⭐ 프로젝트별
|
||||
├── check-gitea.md ← ⭐ 프로젝트별
|
||||
└── helpers/
|
||||
├── vikunja_helper.py ← ⭐ 프로젝트별
|
||||
└── wiki_helper.py ← ⭐ 프로젝트별
|
||||
```
|
||||
|
||||
### 다른 AI IDE에서도 사용하기
|
||||
|
||||
| 대상 플랫폼 | 방법 |
|
||||
|------------|------|
|
||||
| **Cursor** | `AGENT.md` → `.cursor/rules/agent.mdc` (alwaysApply) |
|
||||
| **Claude Code** | `AGENT.md` → `CLAUDE.md`, references를 `@import` |
|
||||
| **Windsurf** | `AGENT.md` → `.windsurfrules` 또는 `.windsurf/rules/agent.md` |
|
||||
| **Cline/Roo** | 루트에 `AGENTS.md`로 복사 |
|
||||
| **Gemini** | `AGENT.md` → `.gemini/GEMINI.md` |
|
||||
|
||||
---
|
||||
|
||||
## 연구 근거 요약
|
||||
|
||||
이 시스템의 각 설계 결정은 학술 연구와 실무 사례에 근거합니다:
|
||||
|
||||
| 설계 결정 | 근거 |
|
||||
|----------|------|
|
||||
| NEVER > ALWAYS (금지 규칙 우선) | Community 검증 — "NEVER use X" ≫ "always prefer Y" |
|
||||
| 2회 실패 시 자동 중단 | Streak Breaker / Sentinel Check 연구 |
|
||||
| 실패 기록 누적 | Reflexion Framework (텍스트 피드백 기반 자기 교정) |
|
||||
| 사전 체크리스트 강제 | Claude Skills 체크리스트 + GPT Chain-of-Thought |
|
||||
| Progressive Disclosure | Anthropic Context Engineering (2025) |
|
||||
| 300줄 이하 규칙 | Claude `CLAUDE.md` 공식 권장 (토큰 효율성) |
|
||||
| 코드 예시 > 설명 | GitHub Copilot Agents, AGENTS.md 공통 Best Practice |
|
||||
322
.agents/references/architecture.md
Normal file
322
.agents/references/architecture.md
Normal file
@@ -0,0 +1,322 @@
|
||||
# Architecture
|
||||
|
||||
> AI 에이전트는 구현 전 이 문서를 반드시 확인합니다.
|
||||
|
||||
## 1. 프로젝트 개요
|
||||
|
||||
**Gravity Control**은 Antigravity AI 코딩 에이전트와 Discord를 실시간으로 연결하는 브릿지 시스템이다.
|
||||
|
||||
### 핵심 목적
|
||||
- AI 에이전트의 **승인 요청**(코드 실행, 파일 수정 등)을 Discord로 전달하고 사용자 응답을 반환
|
||||
- AI 에이전트의 **작업 스냅샷**(대화 요약, 진행 상황)을 Discord에 실시간 표시
|
||||
- **코드 리뷰**(diff review) accept/reject을 Discord에서 처리
|
||||
- 사용자의 Discord **명령어**(!approve, !reject, !auto 등)를 AG Extension으로 전달
|
||||
- **Auto-approve 모드**로 무인 작업 지원
|
||||
|
||||
### 시스템 구성
|
||||
|
||||
```
|
||||
┌────────────────┐ WebSocket ┌──────────────┐ Discord API ┌─────────┐
|
||||
│ VS Code │◄──────────────────►│ Hub Server │◄───────────────────►│ Discord │
|
||||
│ AG Extension │ type:auth/chat │ (hub.py + │ discord.py bot │ 서버 │
|
||||
│ (TypeScript) │ /pending/resp │ gateway.py)│ │ │
|
||||
└────────────────┘ └──────────────┘ └─────────┘
|
||||
↕ AG SDK (RPC) ↕
|
||||
┌────────────────┐ ┌──────────────┐
|
||||
│ Antigravity │ │ 파일 bridge │ ← 레거시 fallback
|
||||
│ AI Engine │ │ (bridge.py) │ (WS 미사용 시)
|
||||
└────────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 디렉토리 구조
|
||||
|
||||
```
|
||||
gravity_control/
|
||||
├── main.py # 진입점: Bot + Hub + Watcher 통합 시작
|
||||
├── config.py # 환경변수 + .env 로드 (66줄)
|
||||
│
|
||||
├── ── 서버 측 (Python) ──
|
||||
├── bot.py # Discord 봇: 승인 UI, 채널 관리, Hub 핸들러 (1,286줄)
|
||||
├── hub.py # WebSocket Hub: 연결 관리, 메시지 라우팅 (580줄)
|
||||
├── auth.py # JWT 토큰 + registration code 인증 (127줄)
|
||||
├── gateway.py # HTTP REST API + /ws endpoint (168줄)
|
||||
├── bridge.py # 파일 기반 IPC (레거시 fallback) (270줄)
|
||||
├── watcher.py # Brain 디렉토리 변경 감시 (290줄)
|
||||
├── parser.py # Markdown → Discord 변환 (245줄)
|
||||
│
|
||||
├── ── Extension 측 (TypeScript) ──
|
||||
├── extension/src/
|
||||
│ ├── extension.ts # 메인: SDK init, activate, 오케스트레이션 (650줄)
|
||||
│ ├── step-probe.ts # 폴링 + 응답 처리 + 승인 전략 (1,479줄)
|
||||
│ │ # setupMonitor(), processResponseFile(),
|
||||
│ │ # writePendingApproval(), tryApprovalStrategies()
|
||||
│ ├── http-bridge.ts # HTTP 서버 (Renderer↔Extension Host 통신) (280줄)
|
||||
│ │ # startHttpBridge(), getDeterministicPort()
|
||||
│ ├── html-patcher.ts # AG HTML 패치 + product.json 체크섬 (280줄)
|
||||
│ │ # setupApprovalObserver(), updateProductChecksums()
|
||||
│ ├── command-handler.ts # Discord→AG 명령어 처리 (175줄)
|
||||
│ │ # watchCommandsDir(), handleWSCommand()
|
||||
│ ├── observer-script.ts # DOM Observer 스크립트 생성 (698줄)
|
||||
│ │ # generateApprovalObserverScript()
|
||||
│ ├── ws-client.ts # WSBridgeClient: Hub 연결, 재연결, 큐 (505줄)
|
||||
│ ├── step-utils.ts # Step 파싱 순수 함수 4개 (114줄)
|
||||
│ │ # extractPlannerText, filterEphemeral,
|
||||
│ │ # extractToolCommand, extractToolDescription
|
||||
│ └── sdk/ # Antigravity SDK 로컬 임베드
|
||||
│ ├── index.js # SDK 런타임 (4,014줄)
|
||||
│ └── index.d.ts # SDK 타입 정의 (2,297줄)
|
||||
│
|
||||
├── ── 테스트 ──
|
||||
├── tests/
|
||||
│ ├── test_ws_hub.py # Hub WS 연결 테스트
|
||||
│ └── test_syntax.py # Python 구문 검증
|
||||
│
|
||||
├── ── 문서 / 설정 ──
|
||||
├── .env # 환경변수 (git 제외)
|
||||
├── .agents/references/ # AI 에이전트 레퍼런스
|
||||
├── docs/devlog/ # 작업 로그
|
||||
└── start_bot.bat # 윈도우용 봇 시작 스크립트
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 핵심 모듈 상세
|
||||
|
||||
### 3.1 Hub (hub.py) — WebSocket 메시지 허브
|
||||
|
||||
**역할**: Extension ↔ Bot 간 실시간 양방향 통신 중계
|
||||
|
||||
| 기능 | 설명 |
|
||||
|------|------|
|
||||
| 연결 관리 | 프로젝트별 다중 인스턴스, 인스턴스 번호 자동 부여 |
|
||||
| JWT 인증 | registration_code → JWT 발급 → 이후 토큰 재인증 |
|
||||
| 메시지 라우팅 | pending, chat, register, auto_resolve, brain_event |
|
||||
| 응답 역라우팅 | request_id → pending_owners → 원본 Extension으로 전달 |
|
||||
| Rate limiting | per-connection 100msg/10s |
|
||||
| Dedup | msg_id 기반 60s TTL 중복 제거 |
|
||||
| Heartbeat | 30s 간격 ping/pong |
|
||||
|
||||
**프로토콜**:
|
||||
```
|
||||
1. Client → Server: {type:"auth", registration_code/token, project, pc}
|
||||
2. Server → Client: {type:"auth_ok", conn_id, instance_number, session_token}
|
||||
3. 양방향 메시지 교환:
|
||||
- Extension→Hub: pending, chat, register, auto_resolve, brain_event
|
||||
- Hub→Extension: response, command, instance_update, error
|
||||
```
|
||||
|
||||
### 3.2 Auth (auth.py) — 인증 관리
|
||||
|
||||
| 기능 | 설명 |
|
||||
|------|------|
|
||||
| Registration Code | 사전 공유 코드로 최초 인증 |
|
||||
| JWT 발급 | HMAC-SHA256, 24시간 유효 |
|
||||
| 토큰 검증 | 만료/위조 감지, 프로젝트+PC 메타데이터 포함 |
|
||||
|
||||
### 3.3 Bot (bot.py) — Discord 인터페이스
|
||||
|
||||
| 기능 | 설명 |
|
||||
|------|------|
|
||||
| 승인 UI | Approve/Reject 버튼, diff_review Accept/Reject |
|
||||
| Auto-approve | `!auto` 토글, 세션 간 초기화 |
|
||||
| 채널 관리 | `#ag-{project}` 자동 채널 매칭 |
|
||||
| 스냅샷 전달 | 2000자 초과 시 파일 첨부 |
|
||||
| 명령어 | !approve, !reject, !auto, !status, !send |
|
||||
| Hub 연동 | on_hub_pending, on_hub_chat, on_hub_register 핸들러 |
|
||||
| IDLE 알림 | AI step 종료 시 Discord 알림 |
|
||||
|
||||
### 3.4 Extension (extension.ts) — VS Code 확장 (오케스트레이터)
|
||||
|
||||
| 기능 | 설명 |
|
||||
|------|------|
|
||||
| AG SDK 연동 | getCascadeStatus, getAllTrajectories RPC |
|
||||
| 세션 감지 | activeSessionId 자동 추적 |
|
||||
| 프로젝트 자동 감지 | git remote URL 기반 |
|
||||
| 모듈 초기화 | HTTP bridge, observer, command handler 시작 |
|
||||
| WS bridge | WSBridgeClient 통한 Hub 연결 (우선) |
|
||||
| Status bar | SDK 상태 + 연결 상태 표시 |
|
||||
|
||||
### 3.4a HTTP Bridge (http-bridge.ts)
|
||||
|
||||
`HttpBridgeContext` 인터페이스로 extension.ts의 공유 상태 참조:
|
||||
|
||||
| 기능 | 설명 |
|
||||
|------|------|
|
||||
| POST /pending | Renderer가 발견한 승인 버튼 보고 |
|
||||
| GET /response/:rid | Renderer가 Discord 응답 폴링 |
|
||||
| GET /trigger-click | Extension→Renderer 클릭 트리거 |
|
||||
| GET/POST /deep-inspect* | DOM 심층 검사 |
|
||||
| getDeterministicPort | 프로젝트명 기반 결정적 포트 |
|
||||
|
||||
### 3.4b HTML Patcher (html-patcher.ts)
|
||||
|
||||
| 기능 | 설명 |
|
||||
|------|------|
|
||||
| setupApprovalObserver | AG Workbench HTML 파일에 observer 스크립트 인라인 삽입 |
|
||||
| updateProductChecksums | product.json SHA256 체크섬 업데이트 (vscode-file:// 프로토콜용) |
|
||||
| CSP 패치 | script-src에 'unsafe-inline' 추가 |
|
||||
| .orig 백업 | 최초 패치 전 원본 백업, 손상 시 자동 복구 |
|
||||
|
||||
### 3.4c Command Handler (command-handler.ts)
|
||||
|
||||
`CommandHandlerContext` 인터페이스로 extension.ts 상태 참조:
|
||||
|
||||
| 기능 | 설명 |
|
||||
|------|------|
|
||||
| watchCommandsDir | commands/ 디렉토리 fs.watch + 3s 폴링 |
|
||||
| handleWSCommand | WS Hub 경유 명령어 처리 |
|
||||
| !stop, !auto | AG 에이전트 제어 명령어 |
|
||||
| 텍스트 전달 | Discord → AG `sendPromptToAgentPanel` |
|
||||
|
||||
### 3.5 Step Probe (step-probe.ts) — 상태 폴링
|
||||
|
||||
`BridgeContext` 인터페이스로 extension.ts와 상태 공유:
|
||||
|
||||
| 기능 | 설명 |
|
||||
|------|------|
|
||||
| setupMonitor | 3초 간격 SDK 폴링, 세션/step 변화 감지 |
|
||||
| processResponseFile | Discord 응답 → AG RPC 실행 |
|
||||
| writePendingApproval | 승인 요청 파일/WS 전송 |
|
||||
| tryApprovalStrategies | 다단계 승인: DOM click → VS Code command → RPC |
|
||||
| setupResponseWatcher | response/ 디렉토리 파일 감시 |
|
||||
|
||||
**BridgeContext 필드** (14개):
|
||||
`bridgePath`, `projectName`, `sdk`, `wsBridge`, `logToFile`, `autoApproveEnabled`, `activeSessionId`, `setClickTrigger`, `recentDiscordSentTexts`, `writeChatSnapshot`, `writeChatSnapshotWithFiles`, `workspaceUri`, `diffReviewMetadata`, `sessionStalled`, `lastPendingStepIndex`, `stallProbed`, `sawRunningAfterPending`
|
||||
|
||||
### 3.6 WS Client (ws-client.ts) — Hub 클라이언트
|
||||
|
||||
| 기능 | 설명 |
|
||||
|------|------|
|
||||
| 연결 관리 | WebSocket + 자동 재연결 |
|
||||
| Backoff | 1s→60s 지수 백오프 + ±30% jitter |
|
||||
| 메시지 큐 | 200개 버퍼, 재연결 시 자동 flush |
|
||||
| Heartbeat | 25s 간격 ping |
|
||||
| 인증 | registration_code 또는 session_token |
|
||||
| API | sendPending, sendChat, sendRegister, sendAutoResolve |
|
||||
|
||||
### 3.7 Observer Script (observer-script.ts)
|
||||
|
||||
AG Webview의 DOM을 관찰하여 승인 버튼을 자동 감지/클릭:
|
||||
- MutationObserver로 `.actions-container` 감시
|
||||
- 버튼 텍스트 매칭으로 Approve/Reject 자동 실행
|
||||
- `postMessage`로 Extension과 통신
|
||||
|
||||
### 3.8 Step Utils (step-utils.ts)
|
||||
|
||||
순수 함수 4개:
|
||||
- `extractPlannerText(content)` — AI 응답 텍스트 추출
|
||||
- `filterEphemeral(text)` — 시스템 메시지 필터링
|
||||
- `extractToolCommand(content)` — 도구 명령어 추출
|
||||
- `extractToolDescription(content)` — 도구 설명 추출
|
||||
|
||||
---
|
||||
|
||||
## 4. 데이터 흐름
|
||||
|
||||
### 4.1 승인 요청 플로우
|
||||
|
||||
```
|
||||
AG Engine → SDK RPC → Extension(step-probe.ts)
|
||||
→ setupMonitor: WAITING step 감지
|
||||
→ writePendingApproval: pending 데이터 생성
|
||||
→ [WS] wsBridge.sendPending() → Hub → Bot → Discord (버튼 UI)
|
||||
→ [파일] bridge/pending/{id}.json (fallback)
|
||||
```
|
||||
|
||||
### 4.2 승인 응답 플로우
|
||||
|
||||
```
|
||||
Discord (사용자 버튼 클릭) → Bot
|
||||
→ [Hub connected] Hub.route_response() → WS → Extension
|
||||
→ [File fallback] bridge/response/{id}.json → setupResponseWatcher
|
||||
→ processResponseFile → tryApprovalStrategies
|
||||
1차: DOM observer script (webview inject)
|
||||
2차: VS Code command (cascade.approveCurrentStep)
|
||||
3차: Direct RPC (acknowledgeCodeActionStep)
|
||||
```
|
||||
|
||||
### 4.3 채팅 스냅샷 플로우
|
||||
|
||||
```
|
||||
Extension(step-probe.ts) → 새 step 텍스트 감지
|
||||
→ writeChatSnapshot(text) → truncation + dedup
|
||||
→ [WS] wsBridge.sendChat() → Hub → Bot → Discord (#ag-{project})
|
||||
→ [파일] bridge/pending/ snapshot 파일 (fallback)
|
||||
```
|
||||
|
||||
### 4.4 Diff Review 플로우
|
||||
|
||||
```
|
||||
AG Engine → 파일 수정 → Extension(step-probe.ts)
|
||||
→ edit_step_indices + modified_files 메타데이터 수집
|
||||
→ writePendingApproval (step_type="diff_review", 8초 지연)
|
||||
→ Discord (Accept all / Reject all 버튼)
|
||||
→ 응답 → handleDiffReviewResponse()
|
||||
→ openReviewChanges → 파일별 포커스 → agentAcceptAllInFile VS Code 커맨드
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> diff_review는 **VS Code 커맨드만 유효**합니다. RPC 방식(AcknowledgeCascadeCodeEdit 등)은 모두 실패 확정.
|
||||
> 상세 경위는 known-issues-archive.md의 "Diff Review 관련" 섹션을 참조하세요.
|
||||
|
||||
---
|
||||
|
||||
## 5. 통신 프로토콜
|
||||
|
||||
### 5.1 WebSocket 메시지 타입
|
||||
|
||||
**Extension → Hub (upstream)**:
|
||||
|
||||
| type | data 필드 | 설명 |
|
||||
|------|-----------|------|
|
||||
| `auth` | registration_code/token, project, pc | 최초 인증 |
|
||||
| `pending` | request_id, command, description, buttons | 승인 요청 |
|
||||
| `chat` | content, attached_files, conversation_id | 채팅 스냅샷 |
|
||||
| `register` | conversation_id, project_name | 세션 등록 |
|
||||
| `auto_resolve` | request_id | 자동 해결 알림 |
|
||||
| `brain_event` | (payload) | 브레인 이벤트 |
|
||||
| `heartbeat` | - | 연결 유지 |
|
||||
|
||||
**Hub → Extension (downstream)**:
|
||||
|
||||
| type | data 필드 | 설명 |
|
||||
|------|-----------|------|
|
||||
| `auth_ok` | conn_id, instance_number, session_token | 인증 성공 |
|
||||
| `auth_fail` | reason | 인증 실패 |
|
||||
| `response` | request_id, approved, button_index | 승인 응답 |
|
||||
| `command` | text, action | Discord 명령어 |
|
||||
| `instance_update` | active_count, instances[] | 인스턴스 변경 |
|
||||
| `error` | error | 에러 |
|
||||
|
||||
### 5.2 BOT_MODE 동작 차이
|
||||
|
||||
| 모드 | Watcher | Hub/Gateway | 용도 |
|
||||
|------|---------|-------------|------|
|
||||
| `local` | ✅ (brain 감시) | ❌ | 로컬 개발 (Extension과 같은 PC) |
|
||||
| `gateway` | ❌ | ✅ (port 8585) | 서버 배포 (WS Hub + Gateway) |
|
||||
| `remote` | ✅ | ❌ | ⚠️ **DEPRECATED** — 레거시 Collector (WS Hub 사용 권장) |
|
||||
|
||||
---
|
||||
|
||||
## 6. 보안
|
||||
|
||||
| 항목 | 구현 |
|
||||
|------|------|
|
||||
| WS 인증 | Registration Code → JWT (HMAC-SHA256, 24h) |
|
||||
| Gateway API | API Key 헤더 (`X-API-Key`) |
|
||||
| Rate limit | per-connection 100msg/10s |
|
||||
| 메시지 dedup | msg_id 기반 60s TTL |
|
||||
| Discord | Bot 토큰 + Guild ID 제한 |
|
||||
|
||||
---
|
||||
|
||||
## 7. Extension 설정 (VS Code)
|
||||
|
||||
| 설정 키 | 설명 | 기본값 |
|
||||
|---------|------|--------|
|
||||
| `gravityBridge.bridgePath` | Bridge 디렉토리 경로 | `~/.gemini/antigravity/bridge` |
|
||||
| `gravityBridge.projectName` | 프로젝트 이름 | git remote 자동 감지 |
|
||||
| `gravityBridge.hubUrl` | WebSocket Hub URL | (비어있으면 WS 비활성) |
|
||||
| `gravityBridge.registrationCode` | Hub 등록 코드 | (서버에서 발급) |
|
||||
41
.agents/references/bug-report-protocol.md
Normal file
41
.agents/references/bug-report-protocol.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# Bug Report Protocol
|
||||
|
||||
> 이 프로토콜은 코드 감사 또는 디버깅 시 false positive를 방지하기 위한 6단계 검증 절차입니다.
|
||||
> AGENT.md 규칙 #10의 세부 구현입니다.
|
||||
|
||||
## 절차
|
||||
|
||||
```
|
||||
1. Identify: 로컬 코드에서 잠재적 이슈 식별
|
||||
2. Trace: 해당 데이터의 전체 생명주기 추적
|
||||
- Producer (생성자) 파일을 열어 확인
|
||||
- Transport (전달 경로: file, HTTP, RPC) 확인
|
||||
- Consumer (소비자) 파일을 열어 확인
|
||||
3. Defend: 기존 방어 메커니즘 검색
|
||||
- try-catch, idempotency guard, dedup logic
|
||||
- upstream validation, downstream tolerance
|
||||
4. Disprove: 버그가 아닌 이유를 적극적으로 찾기
|
||||
- "이 코드가 안전한 이유는?"
|
||||
- 방어 메커니즘이 존재하면 → false positive 가능성 높음
|
||||
5. Prove: 여전히 버그라면 트리거 경로를 증명
|
||||
- 구체적 입력 → 구체적 경로 → 구체적 실패
|
||||
- "A가 B를 호출하면 C에서 D가 발생" 형태
|
||||
6. Report: 증명된 버그만 보고
|
||||
- 트리거 경로 + 심각도 + 영향 범위 포함
|
||||
- 증명 못 한 것은 보고하지 않음
|
||||
```
|
||||
|
||||
## 결정 기준
|
||||
|
||||
| 상황 | 판정 |
|
||||
|------|------|
|
||||
| 방어 메커니즘 존재 + 트리거 경로 없음 | ❌ 보고 안 함 |
|
||||
| 방어 메커니즘 없음 + 트리거 경로 증명됨 | ✅ 보고 |
|
||||
| 방어 메커니즘 존재 + 우회 경로 증명됨 | ✅ 보고 (우회 경로 명시) |
|
||||
| 잘 모르겠음 | 🔍 추가 조사 후 판단 (추측으로 보고 금지) |
|
||||
|
||||
## 근거
|
||||
|
||||
- Anthropic Code Review: "verification step attempts to disprove each finding"
|
||||
- LLM Self-Verification: 자기 결과를 검증하지 않으면 noise와 과신 리포트 양산
|
||||
- Systems Thinking: 개별 컴포넌트가 아닌 관계와 상호의존성에 집중
|
||||
125
.agents/references/conventions.md
Normal file
125
.agents/references/conventions.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# Coding Conventions
|
||||
|
||||
> AI 에이전트는 코드를 작성하기 전 이 컨벤션을 확인합니다.
|
||||
|
||||
## 네이밍
|
||||
|
||||
### Python (서버)
|
||||
|
||||
| 대상 | 규칙 | 예시 |
|
||||
|------|------|------|
|
||||
| 변수/함수 | snake_case | `write_pending_approval()` |
|
||||
| 클래스 | PascalCase | `GravityBot`, `WSHub`, `TokenManager` |
|
||||
| 상수 | UPPER_SNAKE_CASE | `MAX_MSG_SIZE`, `HEARTBEAT_INTERVAL` |
|
||||
| 파일명 | snake_case | `hub.py`, `ws_client.py` |
|
||||
| 로거명 | 모듈명 | `logging.getLogger("hub")` |
|
||||
|
||||
### TypeScript (Extension)
|
||||
|
||||
| 대상 | 규칙 | 예시 |
|
||||
|------|------|------|
|
||||
| 변수/함수 | camelCase | `writePendingApproval()`, `setupMonitor()` |
|
||||
| 클래스 | PascalCase | `WSBridgeClient` |
|
||||
| 인터페이스 | PascalCase | `BridgeContext`, `WSPendingData` |
|
||||
| 상수 | UPPER_SNAKE_CASE | `MAX_QUEUE_SIZE`, `AUTH_TIMEOUT` |
|
||||
| 파일명 | kebab-case | `ws-client.ts`, `step-probe.ts` |
|
||||
| export 함수 | camelCase | `initStepProbe()`, `generateApprovalObserverScript()` |
|
||||
|
||||
## 코드 스타일
|
||||
|
||||
| 항목 | Python | TypeScript |
|
||||
|------|--------|-----------|
|
||||
| 들여쓰기 | 4 spaces | 4 spaces |
|
||||
| 따옴표 | 쌍따옴표 `"` (f-string 포함) | 작은따옴표 `'` |
|
||||
| 세미콜론 | N/A | 사용 |
|
||||
| 줄바꿈 | LF (Unix) | CRLF (Windows, git 자동 변환) |
|
||||
| 최대 줄 길이 | 120자 권장 | 120자 권장 |
|
||||
| 타입 힌트 | 적극 사용 (`-> list[str]`) | strict (`BridgeContext` 인터페이스) |
|
||||
|
||||
## 커밋 메시지
|
||||
|
||||
```
|
||||
<type>(<scope>): <description>
|
||||
|
||||
type: feat|fix|refactor|test|docs|chore|ci|infra
|
||||
scope: server|extension|hub|bot|gateway|bridge (선택)
|
||||
```
|
||||
|
||||
**예시:**
|
||||
- `feat(hub): WebSocket Hub 구현 + JWT 인증`
|
||||
- `refactor(extension): 모듈 분리 (step-probe, observer-script)`
|
||||
- `fix(bot): auto-approve 세션 간 초기화`
|
||||
- `docs: architecture.md 전면 재작성`
|
||||
|
||||
관련 Vikunja 태스크가 있으면: `feat(hub): WS Hub 구현 #task-395`
|
||||
|
||||
## 주석
|
||||
|
||||
- 한국어/영어 혼용 가능
|
||||
- TODO 주석: `// TODO: 설명` 형식
|
||||
- 섹션 구분: `// ─── Section Name ───` (TypeScript), `# ─── Section ───` (Python)
|
||||
- 복잡한 로직에는 반드시 WHY(왜) 주석 추가
|
||||
- 함수 docstring: Python은 `"""..."""`, TypeScript는 `/** ... */`
|
||||
|
||||
## 모듈 분리 패턴
|
||||
|
||||
Extension 모듈 분리 시 사용하는 패턴:
|
||||
|
||||
| 패턴 | 용도 | 예시 |
|
||||
|------|------|------|
|
||||
| **순수 함수 추출** | 외부 상태 참조 없는 함수 | `step-utils.ts` |
|
||||
| **독립 스크립트** | 문자열 반환 함수 | `observer-script.ts` |
|
||||
| **Context 패턴** | 공유 상태가 많은 함수 그룹 | `step-probe.ts` (BridgeContext) |
|
||||
| **클래스 추출** | 자체 상태 + 메서드 | `ws-client.ts` (WSBridgeClient) |
|
||||
|
||||
## 테스트
|
||||
|
||||
| 항목 | 위치 | 도구 |
|
||||
|------|------|------|
|
||||
| Python 구문 검사 | `tests/test_syntax.py` | `ast.parse` |
|
||||
| WS Hub 연결 | `tests/test_ws_hub.py` | `websockets` |
|
||||
| TypeScript 컴파일 | `npx tsc --noEmit` | TypeScript compiler |
|
||||
| E2E | 수동 (Discord 버튼 클릭) | — |
|
||||
|
||||
## 로깅
|
||||
|
||||
| 측 | 방식 | 포맷 |
|
||||
|----|------|------|
|
||||
| Python | `logging.getLogger(name)` | `YYYY-MM-DD HH:MM:SS [name] LEVEL: message` |
|
||||
| Extension | `logToFile(msg)` → bridge/log/ | `[HH:MM:SS] message` + `[WS]` prefix |
|
||||
| Hub | `[HUB]` prefix | `[HUB] Auth OK: {conn_id} project={project}` |
|
||||
| Gateway | `[GATEWAY]` prefix | `[GATEWAY] HTTP API started on {host}:{port}` |
|
||||
|
||||
## WS / File Bridge 상호 배타 패턴
|
||||
|
||||
> [!IMPORTANT]
|
||||
> WS Hub과 파일 bridge는 **항상 상호 배타적**이어야 합니다.
|
||||
> 양쪽에 동시에 쓰면 이중 전달 버그가 발생합니다. (known-issues-archive 참조)
|
||||
|
||||
**Extension (TypeScript):**
|
||||
```typescript
|
||||
// ✅ 올바른 패턴
|
||||
if (ctx.wsBridge?.isConnected()) {
|
||||
ctx.wsBridge.sendPending(data);
|
||||
return; // ← 반드시 return으로 파일 쓰기 건너뛰기
|
||||
}
|
||||
// File fallback
|
||||
fs.writeFileSync(pendingPath, JSON.stringify(data));
|
||||
```
|
||||
|
||||
**Bot (Python):**
|
||||
```python
|
||||
# ✅ 올바른 패턴
|
||||
if self.hub:
|
||||
await self.hub.send_response(...)
|
||||
else:
|
||||
bridge.write_response(...)
|
||||
```
|
||||
|
||||
**금지 패턴:**
|
||||
```python
|
||||
# ❌ 이중 쓰기 — 절대 금지
|
||||
if self.hub:
|
||||
await self.hub.send_response(...)
|
||||
bridge.write_response(...) # ← Hub 성공해도 파일에도 씀 → 이중 처리
|
||||
```
|
||||
581
.agents/references/known-issues-archive.md
Normal file
581
.agents/references/known-issues-archive.md
Normal file
@@ -0,0 +1,581 @@
|
||||
# Known Issues — Archive
|
||||
|
||||
> **해결 완료된 이슈 아카이브입니다.**
|
||||
> 현재 활성 이슈는 [`known-issues.md`](file:///c:/Users/Variet-Worker/Desktop/gravity_control/.agents/references/known-issues.md)를 참조하세요.
|
||||
> 비슷한 문제가 재발할 때 이 문서에서 과거 해결 방법을 검색하세요.
|
||||
|
||||
---
|
||||
|
||||
## 포맷
|
||||
|
||||
```markdown
|
||||
### [날짜] [키워드] — 한줄 요약
|
||||
- **증상**: 무엇이 잘못되었는가
|
||||
- **원인**: 근본 원인
|
||||
- **해결**: 올바른 해결 방법
|
||||
- **주의**: 재발 방지를 위한 교훈
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 승인 전략 결정 체인 (2026-03-08~09 전체 요약)
|
||||
|
||||
> **이 섹션은 2026-03-08~09 전체 세션의 시행착오를 요약합니다.**
|
||||
> **이미 거부된 접근을 다시 시도하지 마세요.**
|
||||
|
||||
### ❌ 시도 후 거부된 접근 (재시도 금지)
|
||||
|
||||
| # | 접근 | 결과 | 거부 사유 |
|
||||
|---|------|------|-----------|
|
||||
| 1 | LS RPC `HandleCascadeUserInteraction` | `socket hang up` | AG 빌드에서 핸들러 미구현 |
|
||||
| 2 | LS RPC `ResolveOutstandingSteps` | CANCEL 동작 (승인이 아님) | 명칭과 달리 step을 취소함 |
|
||||
| 3 | VS Code 7개 승인 명령 (`terminalCommand.run` 등) | `command not found` | AG 런타임에 미등록 |
|
||||
| 4 | 키보드 시뮬레이션 (`type {Enter}`) | 빈 메시지 전송 | Chat input에 캡처됨 |
|
||||
| 5 | `sendChatActionMessage` / `executeCascadeAction` | 미등록 | 119개 명령에 없음 |
|
||||
| 6 | pywinauto (OS 레벨) | 사용자 결정 폐기 | 크로스플랫폼 불가, 창 겹침 문제 |
|
||||
| 7 | CDP (Chrome DevTools Protocol) | **사용자 명시적 거부** | 비표준, `--remote-debugging-port` 필요 |
|
||||
| 8 | Renderer v1 DOM Click (flat scan) | Run 버튼 미발견 | webview iframe 격리 |
|
||||
|
||||
### 🔑 핵심 전제
|
||||
- Run/Accept 버튼은 **`vscode-webview://` origin의 격리된 iframe** 안에 있음
|
||||
- 외부 workbench DOM에서 `querySelector`로 접근 **불가** (cross-origin)
|
||||
- `<webview>.executeJavaScript()`가 **유일한 표준 관통 경로** (Electron API)
|
||||
- AG 패치 후 **반드시 풀 프로세스 재시작** 필요 (Reload Window 불충분)
|
||||
- **양쪽 HTML (workbench.html + workbench-jetski-agent.html) 모두 inline 패치** 필수
|
||||
- HTML 패치 변경 시 **`%APPDATA%\Antigravity\CachedData` 삭제 필수** (V8 바이트코드 캐시 무효화)
|
||||
- **CSP `script-src`에 `'unsafe-inline'` 패치 필수** (없으면 인라인 스크립트 무조건 차단)
|
||||
|
||||
---
|
||||
|
||||
## Renderer / HTML 패치 관련 (2026-03-08~09)
|
||||
|
||||
### [2026-03-08] Antigravity Renderer Injection — Electron 캐시 차단
|
||||
- **증상**: workbench.html, workbench-jetski-agent.html에 `<script>` 태그 추가 후 리로드해도 실행되지 않음
|
||||
- **원인**: Electron의 V8 코드 캐시가 수정된 HTML을 무시하고 캐시된 버전을 서빙
|
||||
- **해결**: 렌더러 인젝션 방식 **포기**. Extension Host에서 RPC 폴링 방식으로 전환
|
||||
- **주의**: Antigravity는 `workbench-jetski-agent.html`을 사용 (Jetski = 내부 코드네임)
|
||||
|
||||
### [2026-03-08] Antigravity 승인 대기 = RUNNING (NOT IDLE)
|
||||
- **증상**: IDLE 기반 승인 감지가 실제 승인 대기를 놓침
|
||||
- **원인**: 승인 대기 시 세션 상태가 `CASCADE_RUN_STATUS_RUNNING` (IDLE 아님), `IDLE`은 대화 대기(notify_user 후)
|
||||
- **해결**: `RUNNING + delta=0` (stall) 기반 감지로 전환. 6 polls (30초) 이상 FROZEN 시 pending 생성
|
||||
- **주의**: Thinking/생성 중에도 `RUNNING + delta=0`이 발생 → `lastModifiedTime`으로 구분 시도했으나 불완전
|
||||
|
||||
### [2026-03-08] ResolveOutstandingSteps RPC — 승인이 아닌 취소!
|
||||
- **증상**: Discord 승인 → `ResolveOutstandingSteps` 호출 → step이 취소됨
|
||||
- **원인**: `ResolveOutstandingSteps`는 blocking steps를 "resolve" = **REJECT/CANCEL**, approve가 아님
|
||||
- **해결**: `ResolveOutstandingSteps` 제거. `HandleCascadeUserInteraction`은 `socket hang up`
|
||||
- **주의**: KI에 "more reliable"로 기록되어 있으나 실제 동작은 cancel임. KI 업데이트 필요
|
||||
|
||||
### [2026-03-08] VS Code Accept Commands — Silent Success 문제
|
||||
- **증상**: 4개 accept command 모두 OK(undefined) 반환하나 실제 승인 안 됨
|
||||
- **원인**: webview에 활성 포커스가 필요. `panel.focus()`로는 충분하지 않음
|
||||
- **해결**: **미해결**. Windows UI Automation 등 OS 레벨 접근 필요
|
||||
- **주의**: reject commands는 동작함. accept만 focus 의존성 있음
|
||||
|
||||
### [2026-03-08] Multi-Window 세션 등록 경쟁 조건
|
||||
- **증상**: 이 창(gravity_control)의 대화가 `#ag-variet_agent` 채널로 메시지 전달
|
||||
- **원인**: `writeRegistration()`이 폴링 루프에서 호출 → 먼저 폴링한 확장이 세션을 자기 프로젝트로 등록
|
||||
- **해결**: `writeRegistration`을 폴링에서 제거, `writeChatSnapshot`/`writePendingApproval`에서만 지연 호출
|
||||
- **주의**: `GetAllCascadeTrajectories`는 모든 창의 세션을 반환하므로 세션→창 매핑은 불가능. 활동 기반 등록만 신뢰 가능
|
||||
|
||||
### [2026-03-08] 공유 렌더러 스크립트 파일 덮어쓰기 문제
|
||||
- **증상**: DOM Observer 렌더러 스크립트가 잘못된 HTTP bridge 포트에 연결
|
||||
- **원인**: 두 확장이 동일한 `ag-sdk-variet-gravity-bridge.js` 파일에 각자 포트를 씀 → 마지막 확장 것만 남음
|
||||
- **해결**: `ag-bridge-ports.json`에 모든 확장의 port를 JSON으로 기록, 렌더러가 all ports를 순회하며 ping
|
||||
- **주의**: 렌더러 스크립트 파일 경로는 SDK patcher namespace에 의해 고정 — 변경 불가
|
||||
|
||||
### [2026-03-08] workbench.html vs workbench-jetski-agent.html
|
||||
- **증상**: 렌더러에서 `[GB Observer]` 로그가 전혀 안 나옴
|
||||
- **원인**: DevTools가 `workbench.html`을 로드 — 스크립트 태그는 `workbench-jetski-agent.html`에만 패치됨
|
||||
- **해결**: `workbench.html`에도 스크립트 태그 필요. Antigravity 재설치 후 SDK patcher가 올바르게 패치하도록 함
|
||||
- **주의**: SDK patcher는 `both` HTML 파일을 패치하지만, 수동 수정은 Antigravity integrity check에 의해 되돌려질 수 있음
|
||||
|
||||
### [2026-03-08] product.json 체크섬 불일치 → 렌더러 스크립트 미로딩
|
||||
- **증상**: `<script>` 태그가 HTML에 존재하고 .js 파일도 디스크에 있으나, 렌더러 콘솔에 스크립트 로그가 전혀 없음
|
||||
- **원인**: Antigravity 재설치 시 `product.json`의 SHA256 체크섬이 원본으로 리셋됨. Extension이 HTML을 패치하지만 `IntegrityManager.suppressCheck()`를 호출하지 않아 체크섬 불일치. `vscode-file://` 프로토콜이 체크섬 불일치 파일을 무시하고 **원본 캐시 HTML**을 서빙
|
||||
- **해결**: `product.json`의 `checksums` 항목에서 수정된 파일(workbench.html, workbench-jetski-agent.html)의 SHA256 해시를 실제 파일 기준으로 업데이트. SDK `IntegrityManager.suppressCheck()` 호출 또는 수동 스크립트로 해결
|
||||
- **주의**: Extension `setupApprovalObserver()`에 `suppressCheck()` 호출을 영구 추가해야 재설치마다 반복 안 됨. 해시 = `base64(sha256(file)).replace(/=+$/, '')`
|
||||
|
||||
### [2026-03-08] vscode-file:// 프로토콜 — 커스텀 .js 파일 서빙 불가
|
||||
- **증상**: `<script src="./ag-sdk-variet-gravity-bridge.js">` 태그가 HTML에 있으나 `net::ERR_FILE_NOT_FOUND` 발생, GB Observer 로그 전혀 없음
|
||||
- **원인**: `vscode-file://` 프로토콜은 원본 배포에 포함된 파일만 서빙. Extension이 디스크에 쓴 커스텀 `.js` 파일은 프로토콜 레벨에서 차단됨
|
||||
- **해결**: 외부 `<script src>` 참조 대신 **인라인 `<script>...코드...</script>`** 방식으로 HTML에 직접 삽입
|
||||
- **주의**: `ag-bridge-ports.json`도 같은 이유로 XHR 로딩 불가. 모든 렌더러 스크립트/데이터는 HTML 인라인으로 전달해야 함
|
||||
|
||||
### [2026-03-08] Renderer 포트 디스커버리 — ag-bridge-ports.json XHR 실패
|
||||
- **증상**: `[GB Observer] Port discovery timeout after 2min` — 렌더러가 bridge 포트를 찾지 못함
|
||||
- **원인**: 렌더러 스크립트가 `./ag-bridge-ports.json`을 동기 XHR로 읽으려 하나, `vscode-file://` 프로토콜이 `.json` 파일 서빙 거부
|
||||
- **해결**: (1) 프로젝트명 해시 기반 **결정론적 포트** 사용 (`gravity_control→34332`), (2) 스크립트 생성 시 포트를 `HARDCODED_PORT=${port}`로 직접 삽입
|
||||
- **주의**: `server.listen(0)` 랜덤 포트 → 매 재시작마다 변경되어 렌더러와 불일치. 결정론적 포트는 `EADDRINUSE` 시 랜덤 폴백 필요
|
||||
|
||||
### [2026-03-08] GetCascadeTrajectorySteps — cascadeId 파라미터 발견
|
||||
- **증상**: `GetCascadeTrajectorySteps`에 `trajectoryId` 파라미터 → 500 "trajectory not found"
|
||||
- **원인**: 파라미터명이 `trajectoryId`가 아니라 **`cascadeId`**. 값은 `GetAllCascadeTrajectories.trajectorySummaries`의 맵 키(세션 ID)
|
||||
- **해결**: `{ cascadeId: sessionId }`로 호출 → 전체 step 배열 반환 성공
|
||||
- **주의**: `latestToolCallStep` 필드는 `GetAllCascadeTrajectories` 응답에 **존재하지 않음** (KI 오류)
|
||||
|
||||
### [2026-03-08] Step 구조 — CORTEX_STEP_STATUS_WAITING 즉시 감지
|
||||
- **증상**: stall-based 감지(100초)가 너무 느림
|
||||
- **원인**: 이제 `GetCascadeTrajectorySteps`로 최신 step의 status를 직접 확인 가능
|
||||
- **해결**: stall 5초 후 step probe → `CORTEX_STEP_STATUS_WAITING` 확인 → 즉시 pending 생성
|
||||
- **Step 구조**: `{type: "CORTEX_STEP_TYPE_RUN_COMMAND", status: "CORTEX_STEP_STATUS_WAITING", metadata: {toolCall: {name, argumentsJson}}, runCommand, requestedInteraction}`
|
||||
- **주의**: 775-step 하드 리밋은 여전히 존재. 긴 세션에서는 fallback(40초) 사용
|
||||
|
||||
### [2026-03-08] Extension 재설치 안전성 — 자동 패치 메커니즘
|
||||
- **증상**: Antigravity 삭제 후 재설치 시 렌더러 스크립트가 동작 안 함
|
||||
- **원인**: 재설치 시 HTML/product.json이 원본으로 리셋됨
|
||||
- **해결**: Extension의 `setupApprovalObserver()`가 **자동으로** 모든 패치를 수행
|
||||
- **주의**: 패치 후 반드시 **Antigravity 풀 재시작** 필요 (Reload Window 불가)
|
||||
|
||||
### [2026-03-08] Response 파일 Race Condition — DOM Observer 승인 실패
|
||||
- **증상**: Discord에서 승인 → `[RESPONSE] renderer-handled approval` 로그 출력 → 실제 버튼 클릭 안 됨
|
||||
- **원인**: `processResponseFile` (파일 감시자)이 response 파일을 즉시 삭제 → renderer의 `pollResponse`가 HTTP `GET /response/:rid`로 조회 시 파일 이미 없음
|
||||
- **해결**: DOM observer 소스일 때는 response 파일을 삭제하지 않도록 수정. HTTP endpoint가 renderer에게 서빙한 후 삭제
|
||||
- **주의**: non-DOM (stall/step_probe relay)는 watcher에서 삭제해도 됨
|
||||
|
||||
### [2026-03-08] Renderer 스크립트 소스 혼동 — 3곳의 코드
|
||||
- **증상**: `extension.ts`에 BTN-DUMP 추가 → Reload 2번 → 콘솔에 안 나옴
|
||||
- **원인**: renderer 코드가 **3곳**에 존재: (1) `extension.ts`의 `generateApprovalObserverScript()` (소스), (2) `ag-sdk-variet-gravity-bridge.js` (배포됨, Reload시 소스에서 재생성), (3) `workbench-jetski-agent.html` inline (HTML, JS파일과 중복로드 방지됨)
|
||||
- **해결**: 항상 `extension.ts`의 `generateApprovalObserverScript()` 함수를 수정 → 컴파일 → 배포 → Reload
|
||||
- **주의**: HTML inline은 JS파일이 먼저 로드되어 `window.__agSDK` 가드에 의해 실행 안 됨. 실제 실행되는 것은 JS파일 경로의 스크립트
|
||||
|
||||
---
|
||||
|
||||
## 승인 / RPC 관련 (2026-03-09)
|
||||
|
||||
### [2026-03-09] VS Code Accept — SDK 승인 명령이 AG에 미등록
|
||||
- **증상**: Discord 승인 → `antigravity.terminalCommand.run` 등 7개 명령 → 모두 `command not found`
|
||||
- **원인**: SDK(command-bridge.ts)에 정의된 7개 승인 명령이 현재 AG 빌드에 **등록되어 있지 않음**
|
||||
- **해결**: Renderer DOM Click 구현 → v3 `deepFindButtons()` 업그레이드
|
||||
- **주의**: `agentPanel.focus`도 미등록, `agentSidePanel.focus`만 존재
|
||||
|
||||
### [2026-03-09] Renderer DOM — webview iframe 격리 확인 + v3 deep traversal
|
||||
- **증상**: Renderer trigger-click이 `document.querySelectorAll('button')`으로 버튼 검색 → Run 버튼 미발견
|
||||
- **원인**: Run/Accept 버튼은 AG 채팅 webview iframe (`vscode-webview://` origin) 안에 렌더링
|
||||
- **해결**: Renderer v3 `deepFindButtons()` 구현 (iframe contentDocument + webview.executeJavaScript + shadow DOM)
|
||||
- **주의**: CDP(Chrome DevTools Protocol)는 **사용자 결정에 의해 명시적으로 거부됨**
|
||||
|
||||
### [2026-03-09] Deep Inspect HTTP Endpoint — curl로 DOM 분석 트리거
|
||||
- **증상**: AG DevTools 콘솔에 붙여넣기 불가 (Chromium 보안 정책)
|
||||
- **원인**: Electron 렌더러 DevTools에서 `allow pasting`이 SyntaxError 발생
|
||||
- **해결**: Extension HTTP bridge에 `/deep-inspect` 엔드포인트 추가
|
||||
- **주의**: 시작 3초 후 자동 실행 + curl로 재트리거 가능
|
||||
|
||||
### [2026-03-09] workbench.html inline 스크립트 미삽입 — jetski만 패치한 버그
|
||||
- **증상**: AG 재시작 후 `/deep-inspect` timeout — renderer v3 스크립트 미로딩
|
||||
- **원인**: `setupApprovalObserver()`가 `workbench-jetski-agent.html`에만 inline 삽입
|
||||
- **해결**: HTML 패치 로직을 **양쪽 모두 inline** 삽입으로 변경
|
||||
- **주의**: **항상 양쪽 HTML을 동일하게 패치**
|
||||
|
||||
### [2026-03-09] V8 CachedData — 체크섬 정상이어도 스크립트 미실행
|
||||
- **증상**: HTML 패치 + product.json 체크섬 일치 → 그런데도 renderer 스크립트 미실행
|
||||
- **원인**: V8 바이트코드 캐시가 `vscode-file://` 프로토콜에도 적용
|
||||
- **해결**: `%APPDATA%\Antigravity\CachedData\*` 전체 삭제 후 AG 풀 재시작
|
||||
- **주의**: **HTML 패치 변경 시마다 CachedData 삭제 필수**
|
||||
|
||||
### [2026-03-09] CSP script-src — 인라인 스크립트 무조건 차단
|
||||
- **증상**: HTML 패치 ✅, 체크섬 ✅, CachedData 삭제 ✅ — 그런데도 renderer 미실행
|
||||
- **원인**: CSP `script-src`에 `'unsafe-inline'` 없음
|
||||
- **해결**: CSP `script-src`에 `'unsafe-inline'` 추가
|
||||
- **주의**: `style-src`에는 `'unsafe-inline'`이 있어 스타일은 동작 → 스크립트만 차단되는 것이 함정
|
||||
|
||||
### [2026-03-09] 중복 승인 요청 — DOM scan + Step probe 동시 발동
|
||||
- **증상**: Discord에 같은 명령에 대해 승인 요청이 2개 도착
|
||||
- **원인**: DOM Observer + Step probe가 동일 step에 대해 2개 파일 생성
|
||||
- **해결**: `writePendingApproval()`에 15초 dedup 윈도우 추가
|
||||
- **주의**: DOM scan은 제거 불가 — step probe가 감지 못하는 UI 버튼 존재
|
||||
|
||||
### [2026-03-09] Step probe reject → ResolveOutstandingSteps가 AI 작업 취소
|
||||
- **증상**: Discord에서 거부 클릭 → AI의 현재 step뿐 아니라 진행 중인 작업 전체가 중단
|
||||
- **원인**: step probe 경로의 `tryApprovalStrategies(approved=false)` → `ResolveOutstandingSteps` RPC 호출
|
||||
- **해결**: step probe 경로에서 reject 시 `tryApprovalStrategies` 호출 제거
|
||||
- **주의**: `ResolveOutstandingSteps`는 이름과 달리 "해결"이 아닌 "취소". 승인에 **절대 사용 금지**
|
||||
|
||||
### [2026-03-09] Pending 파일 무한 누적 — write_response 후 미삭제
|
||||
- **증상**: `bridge/pending/` 디렉토리에 79개 이상의 .json 파일 누적
|
||||
- **원인**: `write_response()`가 pending 파일을 삭제하지 않음
|
||||
- **해결**: pending 파일 삭제 + 5분 age filter + 시작 시 cleanup
|
||||
- **주의**: 봇 재시작 시 자동 정리
|
||||
|
||||
### [2026-03-09] Discord 승인 "Run" 표시 — DOM/step_probe 타이밍 불일치
|
||||
- **증상**: Discord에 상세 명령어 대신 "Run"만 표시
|
||||
- **원인**: DOM observer가 "Run" pending 생성 → 봇이 3초 후 전송 → step_probe MERGE가 10초 후 완료
|
||||
- **해결**: step_probe가 기존 DOM pending에 MERGE + 봇에서 짧은 명령어 대기
|
||||
- **주의**: MERGE 타이밍은 최소 10초
|
||||
|
||||
### [2026-03-09] DOM observer false positive — Proceed/Continue/Open 버튼 오감지
|
||||
- **증상**: 작업 전환 시 승인 요청 없는데도 Discord에 승인 요청 도착
|
||||
- **원인**: DOM observer가 AG UI의 PathsToReview 버튼을 승인 버튼으로 오인
|
||||
- **해결**: FALSE_POSITIVE_RE 필터 추가 + sessionStalled 조건
|
||||
- **주의**: 렌더러 인라인 스크립트는 VSIX 빌드 필요
|
||||
|
||||
### [2026-03-09] Discord ApprovalView timeout — 5분 후 버튼 무응답
|
||||
- **증상**: 시간이 지난 후 Discord 승인 버튼 클릭해도 반응 없음
|
||||
- **원인**: Discord.py View의 기본 timeout이 300초
|
||||
- **해결**: timeout을 1800초로 증가
|
||||
- **주의**: Discord View timeout은 서버 재시작 후만 적용
|
||||
|
||||
---
|
||||
|
||||
## 멀티 프로젝트 / Pending 관련 (2026-03-10)
|
||||
|
||||
### [2026-03-10] DOM Observer 승인 ENOENT — response 파일 Race Condition
|
||||
- **증상**: Discord에서 ✅승인 → `ENOENT: response/xxx.json` 에러
|
||||
- **원인**: `processResponseFile()`이 response 파일 즉시 삭제 → renderer 조회 실패
|
||||
- **해결**: DOM observer 경로에서 response 파일을 삭제하지 않고 HTTP handler가 서빙 후 삭제
|
||||
- **주의**: DOM observer와 step_probe 두 경로가 독립적
|
||||
|
||||
### [2026-03-10] Allow Once + Allow This Conversation — 별개 pending으로 분리되는 문제
|
||||
- **증상**: 파일 접근 시 Discord에 2개 별도 메시지로 도착
|
||||
- **원인**: renderer `scan()`이 한 사이클에 한 버튼만 처리
|
||||
- **해결**: `findButtonContainer()` + `collectSiblingButtons()`로 그룹화, `buttons` 배열 전송
|
||||
- **주의**: `buttons` 배열이 없는 legacy pending은 기존 2버튼(✅승인/❌거부)으로 표시
|
||||
|
||||
### [2026-03-10] step_probe verbosity — argumentsJson 미포함
|
||||
- **증상**: Discord 승인 메시지에 파라미터 이름만 표시, 값 없음
|
||||
- **원인**: `GetCascadeTrajectorySteps` 기본 verbosity에서 `argumentsJson` 빈 문자열
|
||||
- **해결**: `verbosity: 1` (DEBUG) 추가
|
||||
- **주의**: verbosity 0=NORMAL (키만), 1=DEBUG (값 포함)
|
||||
|
||||
### [2026-03-10] 파일 권한 응답 — "unexpected user interaction type: not file permission"
|
||||
- **증상**: `.agents` 디렉토리 접근 시 에러
|
||||
- **원인**: 봇이 file_permission pending에 잘못된 interaction type 전송
|
||||
- **해결**: step_type별 올바른 RPC 라우팅
|
||||
- **주의**: file_permission과 run_command가 동시에 대기 시 올바른 RPC 라우팅 필수
|
||||
|
||||
### [2026-03-10] active_project.lock — 멀티 프로젝트 동시 사용 차단
|
||||
- **증상**: 여러 AG 프로젝트 실행 시 첫 번째만 bridge 연결
|
||||
- **원인**: `active_project.lock` 파일이 단일 프로젝트만 허용
|
||||
- **해결**: lock 메커니즘 완전 제거, `project_name` 필터 기반으로 전환
|
||||
- **주의**: bridge 격리는 `project_name` 필드 기반 filtering으로 충분
|
||||
|
||||
### [2026-03-10] step_probe file_permission — 3-button 미주입
|
||||
- **증상**: Discord에 3개 선택지 대신 2개만 표시
|
||||
- **원인**: `writePendingApproval()`이 buttons 배열 미주입
|
||||
- **해결**: `step_type === 'file_permission'`일 때 자동 3-button 배열 주입
|
||||
- **주의**: DOM observer 경로는 기존 command 텍스트 기반 감지 유지
|
||||
|
||||
### [2026-03-10] GetAllCascadeTrajectories — 크로스 윈도우 세션 가로채기
|
||||
- **증상**: Deriva AG에서 대화 시작 → gravity_control 채널에 Deriva 내용이 릴레이
|
||||
- **원인**: `GetAllCascadeTrajectories`가 모든 인스턴스의 세션 반환
|
||||
- **해결**: `workspaces[0].workspaceFolderAbsoluteUri` 비교하여 자기 workspace 세션만 처리
|
||||
- **주의**: workspace URI normalize 필수 (protocol strip, %3A decode, 슬래시 통일, lowercase)
|
||||
|
||||
### [2026-03-10] 크로스 프로젝트 Response Watcher 우회
|
||||
- **증상**: Deriva 세션 승인 시도가 gravity_control에서 실패
|
||||
- **원인**: pending 파일 삭제 후 response watcher가 project_name 체크 건너뜀
|
||||
- **해결**: response JSON의 project_name으로 fallback 필터
|
||||
- **주의**: response 데이터 자체에 project_name 필수
|
||||
|
||||
### [2026-03-10] file_permission — write 도구 3-button 미주입
|
||||
- **증상**: `replace_file_content` 등 파일 수정 시 Discord에 2개만 표시
|
||||
- **원인**: step_probe의 file_permission 도구 리스트에 write 도구 누락
|
||||
- **해결**: write 도구를 file_permission 리스트에 추가
|
||||
- **주의**: AG가 파일 접근 권한을 요청하는 모든 도구는 이 리스트에 포함필요
|
||||
|
||||
### [2026-03-10] bestSession IDLE 고착 — RUNNING 세션 못 잡는 버그
|
||||
- **증상**: 새 대화 시작 → bridge가 구 IDLE 세션만 추적
|
||||
- **원인**: `bestSession` 선택이 `lastModifiedTime`만 비교
|
||||
- **해결**: RUNNING 세션이 IDLE보다 항상 우선
|
||||
- **주의**: Reload Window로도 해결되지만 근본적으로는 RUNNING 우선 로직 필요
|
||||
|
||||
### [2026-03-10] Bot IDLE 채널 자동 생성 — 불필요한 Discord 채널 증식
|
||||
- **증상**: 봇 시작 시 모든 등록된 프로젝트의 채널을 자동 생성
|
||||
- **원인**: `pending_approval_scanner`가 매 사이클마다 채널 생성
|
||||
- **해결**: 자동 채널 생성 루프 제거, on-demand 생성
|
||||
- **주의**: `_get_channel()`은 이미 on-demand 생성 로직 포함
|
||||
|
||||
### [2026-03-10] Reload Window 후 세션 stale
|
||||
- **증상**: Reload Window 후 세션이 IDLE/구 stepCount로 고정
|
||||
- **원인**: LS 프로세스는 유지되어 trajectory tracker 캐시 미갱신
|
||||
- **해결**: AG 완전 종료 → 재실행 (Full restart)
|
||||
- **주의**: Extension 코드 변경 후 배포 시 Full restart 권장
|
||||
|
||||
### [2026-03-10] start_bot.bat — Windows Store Python 스텁 우선 실행
|
||||
- **증상**: `start_bot.bat` 실행 시 스텁이 먼저 실행
|
||||
- **원인**: `where python`이 Windows Store의 Python 스텁을 먼저 찾음
|
||||
- **해결**: conda 경로를 우선 확인
|
||||
- **주의**: Windows 10/11에서 App Aliases의 python.exe가 PATH에 기본 포함
|
||||
|
||||
### [2026-03-10] VSIX 빌드 — SDK JS 파일 미포함 (require 실패)
|
||||
- **증상**: Extension 활성화 후 `SDK not initialized`
|
||||
- **원인**: TypeScript 컴파일러가 `.js` 파일을 `out/`에 복사하지 않음
|
||||
- **해결**: `compile` 스크립트에 복사 단계 추가 (`src/sdk/` → `out/sdk/`)
|
||||
- **주의**: VSIX 패키징은 `out/sdk/`를 포함함. 문제는 빌드 단계 복사 누락
|
||||
|
||||
### [2026-03-10] SDK _findLSProcess — 대소문자 구분 workspace hint 매칭 실패
|
||||
- **증상**: variet-agent AG에서 Discord에 신호 미도달
|
||||
- **원인**: SDK가 workspace hint를 대소문자 구분으로 비교
|
||||
- **해결**: `fixLSConnection()` 함수로 대소문자 무시 비교 + 재연결
|
||||
- **주의**: 각 AG 창마다 별도 LS 프로세스 존재 (workspace_id로 구분)
|
||||
|
||||
---
|
||||
|
||||
## 환경변수 / 설정 관련 (2026-03-11)
|
||||
|
||||
### [2026-03-11] config.py BRAIN_PATH — `.env` 빈 문자열 → CWD 해석 버그
|
||||
- **증상**: 봇이 Extension의 snapshot/pending을 전혀 읽지 못함
|
||||
- **원인**: `.env`에 `BRAIN_PATH=` (빈 값)이면 빈 문자열 반환
|
||||
- **해결**: `os.getenv("BRAIN_PATH") or default` 패턴
|
||||
- **주의**: `os.getenv(key, default)`는 빈 값이라도 default 미사용
|
||||
|
||||
### [2026-03-11] Extension DEDUP MERGE — 크로스 프로젝트 pending 오염
|
||||
- **증상**: `#ag-lifetimepd` 채널에 variet_agent의 승인 요청 표시
|
||||
- **원인**: DEDUP 로직이 `project_name`을 체크하지 않음
|
||||
- **해결**: 3곳 dedup 조건에 `project_name` 가드 추가
|
||||
- **주의**: 모든 Extension 인스턴스가 동일한 `bridge/pending/` 디렉토리 공유
|
||||
|
||||
### [2026-03-11] Collector 동기 HTTP — aiohttp 전환
|
||||
- **증상**: Collector가 이벤트 루프 전체 블로킹
|
||||
- **원인**: `urllib.request.urlopen()` 사용 (blocking I/O)
|
||||
- **해결**: `aiohttp.ClientSession` 기반 비동기 전환
|
||||
- **주의**: `import aiohttp`는 lazy
|
||||
|
||||
---
|
||||
|
||||
## Rate Limit / 무한 루프 관련 (2026-03-12)
|
||||
|
||||
### [2026-03-12] RemoteTransport 429 무한 루프 — Extension 크래시 + AG 먹통
|
||||
- **증상**: `429 Rate limited` 로그가 초당 수십 건 무한 반복
|
||||
- **원인**: 3가지 복합 (백오프 없음 + 개별 HTTP 요청 + 공격적 rate limit)
|
||||
- **해결**: 지수 백오프 + `Retry-After` 지원 + rate limit 완화
|
||||
- **주의**: AG 먹통은 봇 자체가 유발한 문제
|
||||
|
||||
### [2026-03-12] workbench.html 0-byte 파괴 — AG 새 창 먹통
|
||||
- **증상**: AG 새 창 열면 화면 먹통
|
||||
- **원인**: 3개 Extension 인스턴스가 동시에 workbench.html 읽기/쓰기 → 0 bytes로 덮어쓰기
|
||||
- **해결**: pre-patch backup(.orig) + 구조 검증 + 자동 복원
|
||||
- **주의**: 멀티 윈도우 환경에서 HTML 패치 race condition은 파일 잠금 없이 완전 해결 불가
|
||||
|
||||
### [2026-03-12] workbench.html 크로스 복원 — CSS 미로딩으로 레이아웃 깨짐
|
||||
- **증상**: 아이콘은 보이지만 레이아웃 완전 깨짐
|
||||
- **원인**: workbench.html을 jetski HTML에서 복원할 때 CSS 교체 누락
|
||||
- **해결**: 파일별 `requiredMarker` 검증 + `.orig` 백업 + 자동 복원
|
||||
- **주의**: **workbench.html과 workbench-jetski-agent.html은 교환 불가능**
|
||||
|
||||
### [2026-03-12] Collector 단일 프로젝트 폴링 — 멀티 프로젝트 command 전달 불가
|
||||
- **증상**: Deriva AG IDE에 명령 전달되지 않음
|
||||
- **원인**: Collector가 단일 프로젝트만 폴링
|
||||
- **해결**: `_discover_local_projects()`로 모든 프로젝트 폴링
|
||||
- **주의**: `/api/commands/all` 엔드포인트는 크로스 PC 명령 오염을 유발
|
||||
|
||||
### [2026-03-12] RemoteTransport backing off 무한 반복
|
||||
- **증상**: IDLE 시 `backing off 1s` 경고가 영구 반복
|
||||
- **원인**: 3가지 구조적 결함 (즉시 리셋 + 불필요 요청 + asyncio burst)
|
||||
- **해결**: 연속 5회 성공 후 절반 감소 + adaptive 간격 + 루프 stagger
|
||||
- **주의**: `_reset_backoff()` 즉시 리셋 패턴은 다중 소비자 환경에서 **절대 사용 금지**
|
||||
|
||||
---
|
||||
|
||||
## DEDUP / 크로스 세션 관련 (2026-03-15)
|
||||
|
||||
### [2026-03-15] DEDUP step_index 크로스 세션 충돌 — 승인 신호 누락
|
||||
- **증상**: WAITING step 감지 → pending 미생성 → 10분+ 대기
|
||||
- **원인**: DEDUP 로직이 `conversation_id`를 비교하지 않음
|
||||
- **해결**: DEDUP 조건에 `conversation_id` 가드 추가
|
||||
- **주의**: `project_name` 가드만으로는 불충분 — 같은 Extension이 여러 세션을 볼 수 있음
|
||||
|
||||
### [2026-03-15] Discord Gateway MESSAGE_CREATE 중복 — embed 이중 전송
|
||||
- **증상**: Discord 명령 시 동일 embed가 2개 전송
|
||||
- **원인**: Discord Gateway가 WebSocket 불안정 시 이벤트 중복 전달
|
||||
- **해결**: `on_message`에 `_processed_message_ids` dedup 추가
|
||||
- **주의**: Gateway reconnection, RESUME 실패 시 발생 빈도 증가
|
||||
|
||||
### [2026-03-15] HTML 패치 멀티 인스턴스 race condition — 화면 파괴
|
||||
- **증상**: Extension 패치 후 AG 재시작 시 전체 화면 날아감
|
||||
- **원인**: 2+ Extension 인스턴스가 동시에 같은 HTML에 readFileSync/writeFileSync
|
||||
- **해결**: `.patch-lock` 파일 기반 cross-instance lock 추가
|
||||
- **주의**: Lock은 "방지", .orig 백업은 "복구". 둘 다 유지
|
||||
|
||||
### [2026-03-15] 로컬 승인 ↔ Discord 승인 교차 race condition
|
||||
- **증상**: AG에서 직접 Run 클릭 후 Discord 승인 요청이 "완료됨" 표시 안 됨
|
||||
- **원인**: auto_resolve가 Discord에 알림 없음 + processResponseFile 상태 미체크
|
||||
- **해결**: writeChatSnapshot 추가 + 상태 확인 후 skip + _approval_messages dict
|
||||
- **주의**: processResponseFile L2534의 리셋이 핵심 gate
|
||||
|
||||
### [2026-03-15] 크로스 프로젝트 DEDUP MERGE — Deriva→gravity_control 오염
|
||||
- **증상**: Deriva의 데이터가 gravity_control pending에 MERGE됨
|
||||
- **원인**: MERGE 조건에 `project_name` 가드 없음
|
||||
- **해결**: MERGE 조건에 `project_name` 추가
|
||||
- **주의**: `bridge/pending/` 디렉토리는 모든 Extension 인스턴스가 공유
|
||||
|
||||
### [2026-03-15] Double-Fire Auto-Approve — AI 세션 중단
|
||||
- **증상**: auto-approve ON 시 AI 세션이 간헐적으로 중단
|
||||
- **원인**: Extension auto-approve 경로 + Bot auto-approve 경로 동시 실행 → 2번 RPC
|
||||
- **해결**: Extension auto-approve 경로 제거. Bot만 담당
|
||||
- **주의**: 단일 경로 원칙 유지
|
||||
|
||||
### [2026-03-15] DOM Observer "Deny" False Positive — Auto-approve 세션 크래시
|
||||
- **증상**: auto-approve ON 시 "Deny" command가 자동 승인됨 → 세션 크래시
|
||||
- **원인**: DOM observer가 Deny를 독립 pending으로 생성. default 분기로 잘못된 RPC 전송
|
||||
- **해결**: FALSE_POSITIVE_RE에 Deny/Allow Once 등 추가 + reject-word 차단 가드
|
||||
- **주의**: VSIX 빌드 → AG 풀 재시작 필요
|
||||
|
||||
### [2026-03-15] PATS 배열 Deny 트리거 — 근본 수정
|
||||
- **증상**: Deny가 주 트리거로 사용됨
|
||||
- **원인**: PATS 배열에 Deny 패턴 포함
|
||||
- **해결**: PATS에서 거절/보조 버튼 제거. 긍정 버튼만 그룹 트리거
|
||||
- **주의**: PATS = "그룹 생성 트리거", ALL_ACTION_RE = "형제 수집 패턴"
|
||||
|
||||
### [2026-03-15] Auto-Resolved 채팅 폭주 — 루프 내 writeChatSnapshot
|
||||
- **증상**: "✅ AG에서 직접 승인됨" 메시지가 반복 전송
|
||||
- **원인**: 루프 내부에서 매 파일마다 writeChatSnapshot 호출
|
||||
- **해결**: 루프 바깥에서 1회 + conversation_id 조건 추가
|
||||
- **주의**: 외부 시스템에 메시지 보낼 때는 반드시 루프 바깥에서 집계 후 1회 발송
|
||||
|
||||
### [2026-03-15] projectName=default 승인 오발
|
||||
- **증상**: workspace 없는 AG 창이 다른 프로젝트의 WAITING을 감지
|
||||
- **원인**: `detectProjectName()`이 workspace 없으면 "default" 반환
|
||||
- **해결**: `projectName === 'default'`이면 pending 생성/auto-approve 억제
|
||||
- **주의**: Empty Window에서는 bridge 기능을 최소화
|
||||
|
||||
### [2026-03-15] 이전 분석 오판(False Positive) — 교훈
|
||||
- **증상**: P0/P1으로 보고한 문제들이 이미 방어되고 있었음
|
||||
- **원인**: 로컬 코드 스니펫만 보고 판단
|
||||
- **해결**: 전체 Flow 추적으로 교차 검증
|
||||
- **주의**: **코드 감사 시 반드시 producer→transport→consumer→side effects 전체 경로를 추적**
|
||||
|
||||
---
|
||||
|
||||
## processResponseFile 상태 관리 (2026-03-16)
|
||||
|
||||
### [2026-03-16] processResponseFile 상태 리셋 — 무한 루프 vs auto_resolve 회귀
|
||||
- **증상**: Discord 승인 후 같은 step에 대해 pending이 반복 생성 → 무한 auto-approve 루프
|
||||
- **원인**: processResponseFile이 무조건 리셋 → step_probe가 같은 WAITING step을 새 step으로 착각
|
||||
- **해결**: `sawRunningAfterPending = true`만 설정. lastPendingStepIndex와 stallProbed 유지
|
||||
- **주의**: **processResponseFile의 상태 리셋은 sawRunningAfterPending = true만 설정**. `docs/approval-flow.md` 참조
|
||||
|
||||
### [2026-03-16] recentPendingSteps 메모리 dedup
|
||||
- **증상**: pending 파일 삭제 → 같은 step_index로 새 pending 생성
|
||||
- **원인**: writePendingApproval()의 dedup이 파일 존재 여부에만 의존
|
||||
- **해결**: `recentPendingSteps` Map (TTL 60초) 추가
|
||||
- **주의**: DOM observer HTTP 경로는 이 메모리 dedup 미적용
|
||||
|
||||
### [2026-03-16] 멀티 프로젝트 동시 신호 정지 — Scanner O(N) Discord API 병목
|
||||
- **증상**: 여러 프로젝트 동시 pending → 모든 프로젝트 신호 전달 정지
|
||||
- **원인**: scanner가 1 tick에 모든 pending 순차 처리 → Discord 429 rate limit
|
||||
- **해결**: `discord.utils.get(guild.channels)` 캐시 + per-tick cap (5건)
|
||||
- **주의**: `guild.channels`는 discord.py 내부 캐시
|
||||
|
||||
---
|
||||
|
||||
## Diff Review 관련 (2026-03-16)
|
||||
|
||||
### [2026-03-16] step_type 매핑 버그 — write_to_file이 file_permission으로 잘못 매핑
|
||||
- **증상**: 코드 편집 승인 시 잘못된 RPC 전송
|
||||
- **원인**: 쓰기 도구가 읽기 도구와 함께 `file_permission`으로 매핑
|
||||
- **해결**: 읽기/쓰기 도구 분리, 쓰기는 `code_edit` step_type 사용
|
||||
- **주의**: AG는 대부분 파일 쓰기에 WAITING 안 만듦
|
||||
|
||||
### [2026-03-16] diff_review isDirty 실패 — AG diff는 VS Code dirty 아님
|
||||
- **증상**: Accept 클릭 → `isDirty` 문서 0개 → 효과 없음
|
||||
- **원인**: AG stacked code review는 VS Code `isDirty`와 무관
|
||||
- **해결**: `AcknowledgeCascadeCodeEdit` RPC → fallback으로 VS Code 커맨드
|
||||
- **주의**: diff_review pending에 `modified_files`와 `edit_step_indices` 필수
|
||||
|
||||
### [2026-03-16] diff_review pending 순서 — AI 응답보다 먼저 Discord 도착
|
||||
- **증상**: diff_review 버튼이 먼저, AI 응답 텍스트가 나중
|
||||
- **원인**: pending_approval_scanner가 chat_snapshot_scanner보다 먼저 fire
|
||||
- **해결**: diff_review pending 생성을 `setTimeout(8000)`으로 지연
|
||||
- **주의**: 8초는 전체 전파 경로 고려
|
||||
|
||||
### [2026-03-16] diff_review AcknowledgeCascadeCodeEdit steps=[] — Collector pending 삭제 race
|
||||
- **증상**: Accept all 클릭 → RPC SUCCESS → diff review 바 안 사라짐
|
||||
- **원인**: Collector가 pending 파일 즉시 삭제 → Extension이 메타데이터 못 읽음
|
||||
- **해결**: `diffReviewMetadata` 인메모리 Map 추가
|
||||
- **주의**: Extension Reload 시 소실되지만 새 diff_review는 정상 동작
|
||||
|
||||
### [2026-03-16] AcknowledgeCascadeCodeEdit SUCCESS → diff review bar 미해제 — 잘못된 RPC 메서드명
|
||||
- **증상**: RPC SUCCESS 반환 → diff review bar 여전히 표시
|
||||
- **원인**: RPC 메서드명 자체가 틀렸음 (`AcknowledgeCascadeCodeEdit` → 실제는 `acknowledgeCodeActionStep`)
|
||||
- **해결**: `agentAcceptAllInFile` / `agentRejectAllInFile` VS Code 커맨드 사용
|
||||
- **주의**: AG의 RPC는 잘못된 메서드명도 에러 없이 `{}` 반환. **RPC `{}`는 실패로 간주해야 함**
|
||||
|
||||
### [2026-03-16] diff_review RPC 3개 전략 모두 dead-end
|
||||
- **증상**: 3개 RPC 전략 모두 실패
|
||||
- **원인**: (1) submitCodeAcknowledgement 미등록, (2) acknowledgeCodeActionStep 404, (3) AcknowledgeCascadeCodeEdit no-op
|
||||
- **해결**: VS Code 커맨드 기반 (`agentAcceptAllInFile` / `agentRejectAllInFile`)
|
||||
- **주의**: **diff_review를 RPC로 해결하려는 시도 모두 실패 확정. VS Code 커맨드 기반만 유효**
|
||||
|
||||
### [2026-03-16] AG 소스 역분석 — diff review 내부 동작 체인
|
||||
- **증상**: Accept all / Reject all 내부 동작을 재현해야 함
|
||||
- **원인**: AG 공식 API/문서 없음
|
||||
- **해결**: AG 설치 경로의 JS 소스에서 역분석
|
||||
- **주의**: minified JS에서 변수명은 버전마다 변경됨
|
||||
|
||||
### [2026-03-16] diff_review 이중 승인 요청 — DOM observer가 Accept/Reject 버튼 캡처
|
||||
- **증상**: diff_review 외에 별도 "Accept"/"Reject" pending 도착
|
||||
- **원인**: `openReviewChanges` 커맨드가 diff UI 패널 → DOM observer 감지
|
||||
- **해결**: FALSE_POSITIVE_RE에 Accept/Reject all 추가
|
||||
- **주의**: diff review bar 버튼은 전용 시스템에서 처리
|
||||
|
||||
### [2026-03-16] diff_review가 brain/ artifact에도 트리거
|
||||
- **증상**: task.md만 수정해도 "코드 리뷰" pending 생성
|
||||
- **원인**: diff_review 감지 로직이 모든 수정 파일 추적
|
||||
- **해결**: `.gemini/antigravity/brain/` 경로 파일 필터링하여 제외
|
||||
- **주의**: 코드 파일 + brain artifact 혼합 시 코드 파일만 diff_review
|
||||
|
||||
---
|
||||
|
||||
## WS Hub 전환 관련 (2026-03-16~17)
|
||||
|
||||
### [2026-03-16] !auto 이중 메시지 — Extension echo + Bot embed
|
||||
- **증상**: `!auto` 토글 시 메시지 2개 표시
|
||||
- **원인**: Bot embed + Extension writeChatSnapshot echo
|
||||
- **해결**: Extension의 `!auto` handler에서 writeChatSnapshot echo 제거
|
||||
- **주의**: Bot 재시작 시 auto_approve_projects 초기화 → 수동 모드 복귀
|
||||
|
||||
### [2026-03-16] 병렬 WAITING step 누락 — step_probe break문
|
||||
- **증상**: 병렬 tool call 시 1개만 승인 요청 도착
|
||||
- **원인**: step_probe 루프에서 `break`문이 첫 번째 WAITING 후 종료
|
||||
- **해결**: `break` 제거, 모든 WAITING step에 pending 생성
|
||||
- **주의**: 중복 방지는 `recentPendingSteps` Map이 처리
|
||||
|
||||
### [2026-03-16] Bot chat_snapshot 전송 로깅 부재
|
||||
- **증상**: 전송 성공/실패를 Bot 로그에서 확인 불가
|
||||
- **원인**: `channel.send()` 성공 후 INFO 로그 없음
|
||||
- **해결**: 전송 성공/실패 로그 추가
|
||||
- **주의**: `_get_channel()` 실패 시 WARNING은 이전에도 있었음
|
||||
|
||||
### [2026-03-16] 크로스 프로젝트 이벤트 폭주 — Watcher/Collector 무필터
|
||||
- **증상**: /start 시 타 프로젝트 알림 유입
|
||||
- **원인**: watcher.py가 brain/ 전체를 감시
|
||||
- **해결**: `_is_my_session()` 필터 + 이벤트 전달 필터 추가
|
||||
- **주의**: 미등록 세션은 allow-through 방식
|
||||
|
||||
### [2026-03-16] pending 파일 139개 누적 — 정리 로직 부재
|
||||
- **증상**: bridge/pending/에 139개 파일 누적
|
||||
- **원인**: auto_resolved/expired 파일을 아무도 삭제 안 함
|
||||
- **해결**: Collector에서 auto_resolved/expired 전달 후 삭제 + 10분 자동 삭제
|
||||
- **주의**: startup_pending은 정리 대상에서 제외
|
||||
|
||||
### [2026-03-17] NPM WebSocket 프록시 — Upgrade 헤더 미전달
|
||||
- **증상**: `wss://ag.variet.net/ws` 연결 시 HTTP 400
|
||||
- **원인**: Nginx Proxy Manager에 WebSocket Support 미활성화
|
||||
- **해결**: NPM 대시보드에서 Websockets Support 체크
|
||||
- **주의**: 새 프록시 호스트 생성 시 반드시 확인
|
||||
|
||||
### [2026-03-17] WS auth_fail 무한 재연결 — _cleanup() close 이벤트
|
||||
- **증상**: Auth failed 후 60초마다 재연결 반복
|
||||
- **원인**: `_cleanup()`이 ws.close() → close 이벤트 → _scheduleReconnect() 체인
|
||||
- **해결**: `this.shouldReconnect = false`를 `_cleanup()` 이전에 설정
|
||||
- **주의**: `_cleanup()`은 이벤트 핸들러를 트리거하므로 상태 변경은 반드시 호출 전에
|
||||
|
||||
### [2026-03-17] initStepProbe workspaceUri 누락 — 세션 감지 완전 불능
|
||||
- **증상**: POLL alive만 출력, SESSION-FILTER/SNAPSHOT 없음
|
||||
- **원인**: `initStepProbe()` 호출 시 필수 필드 미전달 + `as` 캐스트가 런타임 검증 없음
|
||||
- **해결**: `workspaceUri`, `diffReviewMetadata: new Map()` 추가
|
||||
- **주의**: TypeScript `as` 캐스트는 런타임 검증 없음
|
||||
|
||||
### [2026-03-17] WS 명령어 에코 릴레이 — Discord 메시지 2번 표시
|
||||
- **증상**: Discord 메시지 입력 → 같은 메시지가 다시 표시
|
||||
- **원인**: handleWSCommand에서 `recentDiscordSentTexts`에 마킹 안 함
|
||||
- **해결**: `recentDiscordSentTexts.set()` 추가
|
||||
- **주의**: 파일 기반 경로에는 이미 마킹 있었음
|
||||
|
||||
### [2026-03-17] writeRegistration 이중 쓰기 — WS 전송 후 파일도 작성
|
||||
- **증상**: WS 상태에서도 register/ 파일 생성
|
||||
- **원인**: WS 전송 후 `return` 없이 파일 쓰기 실행
|
||||
- **해결**: WS 전송 후 `return` 추가
|
||||
- **주의**: 새 WS 전송 함수 추가 시 file fallback과 상호 배타적 `return` 확인
|
||||
@@ -1,299 +1,629 @@
|
||||
# Known Issues & Lessons Learned
|
||||
|
||||
> **이 파일은 SSOT(Single Source of Truth)입니다.**
|
||||
> 디버깅이나 구현 전에 **반드시** 이 파일을 확인하세요.
|
||||
> 세션 종료 시 새로 발견된 이슈를 이 파일에 추가합니다.
|
||||
|
||||
|
||||
> **<2A>씠 <20>뙆<EFBFBD>씪<EFBFBD><EC94AA><EFBFBD> SSOT(Single Source of Truth)<29>엯<EFBFBD>땲<EFBFBD>떎.**
|
||||
|
||||
> <EFBFBD>뵒踰꾧퉭<EFBFBD>씠<EFBFBD>굹 援ы쁽 <20>쟾<EFBFBD>뿉 **諛섎뱶<EC848E>떆** <20>씠 <20>뙆<EFBFBD>씪<EFBFBD>쓣 <20>솗<EFBFBD>씤<EFBFBD>븯<EFBFBD>꽭<EFBFBD>슂.
|
||||
|
||||
> <EFBFBD>꽭<EFBFBD>뀡 醫낅즺 <20>떆 <20>깉濡<EAB989> 諛쒓껄<EC9293>맂 <20>씠<EFBFBD>뒋瑜<EB928B> <20>씠 <20>뙆<EFBFBD>씪<EFBFBD>뿉 異붽<E795B0><EBB6BD><EFBFBD>빀<EFBFBD>땲<EFBFBD>떎.
|
||||
|
||||
|
||||
|
||||
# Known Issues & Lessons Learned
|
||||
|
||||
> **씠 뙆씪 SSOT(Single Source of Truth)엯땲떎.**
|
||||
> 뵒踰꾧퉭씠굹 援ы쁽 쟾뿉 **諛섎뱶떆** 씠 뙆뙆씪쓣 솗씤븯꽭슂.
|
||||
> 꽭뀡 醫낅즺 떆 깉濡 諛쒓껄맂 씠뒋瑜 씠 뙆뙆씪뿉 異붽빀땲떎.
|
||||
|
||||
> [!TIP]
|
||||
> 빐寃 셿猷뚮맂 怨쇨굅 씠뒋뒗 [`known-issues-archive.md`](file:///c:/Users/Variet-Worker/Desktop/gravity_control/.agents/references/known-issues-archive.md)뿉 蹂닿릺뼱 엳뒿땲떎.
|
||||
> 鍮꾩듂븳 臾몄젣媛 옱諛쒗븯硫 archive뿉꽌 寃깋븯꽭슂.
|
||||
|
||||
|
||||
### [2026-04-19] [Observer] ★ Accept all 버튼이 `<span>`으로 렌더링 — Observer 감지 실패 (v0.5.101)
|
||||
- **증상**: "Accept all" diff review 버튼이 화면에 보이지만 Observer가 감지하지 못함. Discord에 "Accept all" 자동 승인 알림이 안 옴.
|
||||
- **원인**: AG Native UI 업데이트로 "Accept all"이 `<button>`이 아닌 `<span class="cursor-pointer">`로 렌더링됨. Observer의 `allBtns = querySelectorAll('button, [role="button"]')`에 span이 포함되지 않음. ACCEPT-SCAN 로그: `tag=SPAN cls=hover:text-ide-button-hover-color cursor-po txt=Accept all`.
|
||||
- **해결 (v0.5.101)**: `allBtns` 선택자에 `span.cursor-pointer` 추가.
|
||||
- **주의**: observer-dev-guide 섹션 3.3 "Accept all — Observer 접근 불가"는 outdated. UI 변경으로 chat panel footer에 Accept all이 표시됨. 가이드 업데이트 필요.
|
||||
|
||||
### [2026-04-19] [Bridge] ★ auto-approve response 파일에 `_from_ws` 마커 누락 — Observer polling 실패 (v0.5.103)
|
||||
- **증상**: Observer가 "Accept all"을 감지하고 bridge가 자동 승인했지만, Observer의 `pollResponseGroup` GET `/response/{rid}`가 항상 `{waiting: true}` 반환. 버튼 클릭이 실행되지 않음.
|
||||
- **원인**: http-bridge의 auto-approve 경로에서 response JSON 파일에 `_from_ws: true` 마커가 없음 → `processResponseFile`(response watcher)이 Observer보다 먼저 파일을 읽고 삭제 → Observer polling 시 파일 부재. known-issues [2026-04-18] WS response 삭제 버그와 동일 패턴.
|
||||
- **해결 (v0.5.103)**: auto-approve response에 `_from_ws: true` + `_auto_approve_ttl` 마커 추가.
|
||||
- **주의**: **response 디렉토리에 파일을 쓰는 모든 경로**는 반드시 `_from_ws: true` 마커를 포함해야 함. processResponseFile이 먼저 소비하는 race condition 항상 존재.
|
||||
|
||||
### [2026-04-18] [Extension] ★ WS response 파일이 processResponseFile에 의해 삭제 → Observer pollResponseGroup 실패 (v0.5.78)
|
||||
- **증상**: `!auto` Retry가 작동하지 않음. Observer가 `/response/{rid}`를 폴링하지만 항상 `{waiting: true}` 반환.
|
||||
- **원인**: extension.ts의 WS 응답 핸들러가 `response/{rid}.json` 파일 작성 → 300ms 후 response watcher(`processResponseFile`)가 파일 감지 → pending 파일이 없어 `isDomObserver=false` → `fs.unlinkSync()` 실행 → Observer가 폴링할 때 파일이 이미 삭제됨.
|
||||
- **해결 (v0.5.78)**: WS 응답 파일에 `_from_ws: true` 마커 추가. `processResponseFile`에서 `_from_ws` 파일 스킵 (WS 핸들러에서 이미 `tryApprovalStrategies` 실행하므로 중복 방지도 함께 해결).
|
||||
- **주의**: http-bridge의 `_handlePending`는 pending 파일을 생성하지 않음 (WS 전송만 수행). 따라서 `processResponseFile`의 `isDomObserver` 판별이 실패함. WS 경로로 들어오는 모든 response는 반드시 마커로 구분해야 함.
|
||||
|
||||
### [2026-04-18] [Extension] ★ extractContextFromNearby 조상 탐색만으로는 명령어 추출 불가 (v0.5.79)
|
||||
- **증상**: Discord auto-approve 알림에 "Always run"만 표시되고 실제 명령어가 안 보임. depth를 10으로 늘려도 동일.
|
||||
- **원인**: AG Native DOM 구조에서 "Always run" 버튼은 `footer` 내부에 있고, 실제 명령어(`pre.font-mono`)는 `footer`의 **형제(sibling)** 요소에 있음. 조상 탐색(parentElement)으로는 도달 불가. trail: `d0:button → d1:div → d2:footer` (footer.parentElement가 null).
|
||||
- **해결 (v0.5.79)**: `extractContextFromNearby`에 형제 탐색 로직 추가. 각 depth에서 `node.parentElement.children`을 순회하며 `pre.font-mono, pre, code`를 찾음 → `CONTEXT-OK src=sibling` 성공.
|
||||
- **주의**: Observer 코드 변경은 HTML 인라인 스크립트이므로 AG 2번 재시작(Quit + Relaunch × 2) 필요.
|
||||
|
||||
### [2026-04-18] [Extension] Thinking 블록이 AI 응답으로 릴레이됨
|
||||
- **증상**: AI의 내부 사고 과정(thinking/reasoning)이 Discord에 릴레이됨.
|
||||
- **원인**: Observer의 `scanChatBodies`가 `.leading-relaxed.select-text` 블록을 전부 캡처하는데, thinking 블록도 이 셀렉터에 매칭됨.
|
||||
- **해결**: thinking 블록의 조상에 `max-h-[200px]` 클래스가 있는지 확인하여 필터링.
|
||||
- **주의**: AG UI 업데이트로 thinking 블록의 클래스가 변경될 수 있음.
|
||||
|
||||
|
||||
- **증상**: Step Probe의 RT-CAPTURE, HB-CAPTURE가 현재 대화 중에 전혀 발동하지 않음. POLL에서 `status=IDLE, steps=928, delta=0` 고정. Heartbeat probe에서도 `real=928 known=928` 불변.
|
||||
- **원인**: AG Native의 `GetCascadeTrajectorySteps` API는 **cascade가 완전히 종료(IDLE 전환)된 후에만** 새 step을 반환합니다. 진행 중인 cascade에서는 step count가 동결됩니다. `GetAllCascadeTrajectories`의 `stepCount`도 마찬가지.
|
||||
- **영향**: Step Probe 기반의 모든 실시간 캡처(RT-CAPTURE, HB-CAPTURE, USER_INPUT 감지)가 **구조적으로 불가능**. Observer DOM이 유일한 실시간 데이터 경로.
|
||||
- **해결**: Observer DOM 기반 chat relay를 재활성화 (v0.5.72). Step Probe는 cascade 완료 후 보완 용도로만 사용.
|
||||
- **주의**: 이 제약은 AG Native 아키텍처의 근본적 특성. Polling 주기나 heartbeat 빈도를 변경해도 해결 불가. **실시간 릴레이는 반드시 Observer DOM 경로를 사용해야 함.**
|
||||
- **참조**: `.agents/references/relay-architecture.md` (상세 분석)
|
||||
|
||||
### [2026-04-18] [Extension] Observer의 /pending POST에 명령어 컨텍스트가 없음 (Always run만 전달)
|
||||
- **증상**: Discord auto-approve 알림에 "Always run"만 표시되고 실제 명령어가 안 보임.
|
||||
- **원인**: Observer가 `/pending` POST 시 `command: "Always run"`, `description: "Always run"`만 보냄. `extractContextFromNearby(btn)`이 버튼 주변 DOM에서 유의미한 텍스트를 찾지 못함.
|
||||
- **해결 (부분)**: v0.5.69에서 bridge/pending/ 디렉토리의 최신 Step Probe pending 파일에서 명령어를 읽는 fallback 추가. Step Probe pending이 있을 때만 작동.
|
||||
- **주의**: Step Probe WAITING 감지가 진행 중 cascade에서 불가하므로 (위 이슈 참조), 현재 대부분의 경우 fallback도 실패. Observer의 DOM 컨텍스트 추출 개선이 필요.
|
||||
|
||||
### [2026-04-18] [Extension] Observer의 사용자 메시지 셀렉터 미매칭
|
||||
- **증상**: 사용자가 AG에서 입력한 메시지가 Discord에 전혀 전달되지 않음.
|
||||
- **원인**: Observer의 셀렉터(`.text-ide-message-block-user-color`, `[data-message-role="user"]` 등)가 AG Native DOM에서 매칭되지 않음. AI 응답만 `.leading-relaxed.select-text`로 매칭됨.
|
||||
- **해결**: DOM 덤프에서 사용자 메시지 블록의 실제 CSS 클래스를 식별 후 셀렉터 추가 필요.
|
||||
- **주의**: Step Probe의 USER_INPUT 캡처도 진행 중 cascade에서 불가 (위 이슈 참조).
|
||||
|
||||
### [2026-04-16] [Extension] ★ AG Native 세션 AI 응답이 Discord에 CSS로 전달됨 (v0.5.52 수정, #632)
|
||||
- **증상**: Discord에 AI 대화 응답 대신 **CSS 스타일시트 코드** (`remark-github-blockquote-alert/alert.css`)가 전달됨. `scanChatBodies()` → POST /chat 경로는 작동하지만 내용이 CSS.
|
||||
- **원인**: `extractCleanStepText()`에서 clone한 DOM에서 버튼/SVG/아이콘은 제거하지만 **`<style>` 태그는 제거하지 않음**. AG Native 마크다운 렌더러가 `.leading-relaxed.select-text` 내부에 `<style>` 블록을 주입하는데, 이 CSS textContent가 AI 응답 텍스트로 추출됨.
|
||||
- **해결 (v0.5.52)**: `extractCleanStepText()` 최상단에 `clone.querySelectorAll('style, script, noscript, link[rel="stylesheet"]')` 제거 로직 추가. CSS/JS가 텍스트로 포함되는 것을 원천 차단.
|
||||
- **배포**: v0.5.52 VSIX 설치 + v0.5.50/out/ JS 직접 복사 + V8 CachedData 삭제. AG File→Quit 재시작 필요.
|
||||
- **이전 블로커 해결 이력**:
|
||||
- SDK 경로: AG Native는 Cascade trajectory API 미등록 → step-probe RT-CAPTURE 불가 (구조적 한계)
|
||||
- DOM 경로: v15에서 `#conversation` + `.leading-relaxed.select-text` 셀렉터 추가로 해결
|
||||
- BEACON=0: AG 프로세스 완전 재시작으로 해결 (Reload Window로는 렌더러 HTML 캐시 유지)
|
||||
- **주의**: AG Native 마크다운 렌더링은 `<style>` 블록을 응답 DOM 내부에 인라인으로 삽입함. DOM 텍스트 추출 시 반드시 style/script 태그를 먼저 제거해야 함.
|
||||
- **Vikunja**: #632
|
||||
|
||||
### [2026-04-16] [Extension] ★ AG Native Observer innerText로 인한 마크다운 포맷 유실 및 사용자 요청 누락
|
||||
- **증상**: Discord로 릴레이되는 AI 응답에서 리스트 번호(`1. 2.`)나 불릿(`-`) 등 마크다운 포맷이 완전히 유실되고 텍스트만 이어져서 나옴. 그리고 사용자가 입력한 메시지(요청)는 아예 릴레이되지 않음.
|
||||
- **원인 1**: `extractCleanStepText()`에서 `innerText`를 사용하여 텍스트를 추출할 때, 렌더링되지 않은 DOM이나 CSS 카운터로 생성된 list-item 마커가 무시됨.
|
||||
- **원인 2**: `scanChatBodies()` 로직이 `.leading-relaxed.select-text` (AI 응답 블록)만을 타겟팅하여 사용자의 메시지 박스(예: `.text-ide-message-block-user-color`)는 추출 대상에서 제외됨.
|
||||
- **해결 (계획 중)**: `innerText` 대신 HTML DOM 노드를 순회하며 `convertNodeToMarkdown` 변환 함수를 통해 마크다운 문법을 보존하도록 개선. User 블록도 함께 감지하여 브릿지에 `role: 'user'` 플래그를 추가 전송하도록 수정 예정.
|
||||
- **주의**: Webview 내에서 텍스트 노드를 파싱할 때 `innerText`는 브라우저 레이아웃 엔진에 의존하므로 완전한 마크다운 보존을 위해서는 Node Type 순회를 활용한 구조 복원이 보장되어야 함.
|
||||
|
||||
### [2026-04-16] [Extension] 터미널 출력(stdout) 텍스트가 명령어로 Discord에 전송 (v0.5.50)
|
||||
- **증상**: Discord에 `cmd="No extension.log found"`, `cmd="AG CLI not found..."`, `cmd="Log found: C:\..."` 등 터미널 **출력** 텍스트가 명령어로 전송됨
|
||||
- **원인**: Observer가 code 블록 2개를 감지: (1) 프롬프트+명령어 → JUNK_CODE_RE로 스킵, (2) 터미널 출력 → 유효한 code로 판단 → description에 포함. http-bridge enrichment에서 description에 prompt marker(`>`)가 없으면 rawDesc 전체를 enrichedCmd로 채택
|
||||
- **해결 (v0.5.50)**: `promptMatch` 실패 시(description에 `>` 없음) → 터미널 OUTPUT으로 판단하여 `terminal_output` 사유로 즉시 필터. 실제 명령어는 항상 `…\project > command` 프롬프트를 포함
|
||||
- **주의**: enrichment은 반드시 prompt marker가 있는 경우에만 수행. description에 `>` 없으면 code 블록의 출력 텍스트
|
||||
|
||||
### [2026-04-15] [Extension] Observer fallback 컨텍스트가 채팅/UI 텍스트를 명령어로 추출 (v0.5.46)
|
||||
- **증상**: Discord에 `cmd="실 동작검증을 해봐야하는데"`, `cmd="variet.gravity-bridge"` 등 사용자 채팅/AI 응답 텍스트가 명령어로 전송됨
|
||||
- **원인**: v0.5.45에서 `PROMPT_ONLY_RE`가 `code/pre` 요소 스킵 성공했으나, `extractContextFromNearby()`의 fallback(`span/div/p` 수집)이 여전히 작동하여 DOM 트리의 채팅 본문/UI 라벨을 명령어로 추출
|
||||
- **해결 (v0.5.46)**: Observer v13에 `_promptOnlySkipped` 플래그 — code 요소가 모두 prompt-only이면 fallback 비활성화. http-bridge에 generic button + no-context일 때 무조건 필터
|
||||
- **주의**: 프롬프트 스킵과 fallback 비활성화는 항상 연동해야 함. VSIX 설치 누락 방지를 위해 빌드 후 즉시 `code --install-extension` 확인 필수
|
||||
|
||||
### [2026-04-15] [Extension] PROMPT_ONLY_RE regex fixes — prompt-only terminal text leaking as enriched cmd (v0.5.45)
|
||||
- **증상**: Discord에 `cmd="…\gravity_control >"` (실제 명령어 없는 빈 터미널 프롬프트)가 전송됨. 명령어가 포함된 경우는 정상 작동
|
||||
- **원인 1 (Observer)**: `PROMPT_ONLY_RE` 의 `\\\\s`(4중 backslash)가 template literal 안의 regex 리터럴에서 "literal backslash+s"가 되어 whitespace class가 아닌 문자열 매칭
|
||||
- **원인 2 (http-bridge)**: `PROMPT_ONLY_RE = /^[\s\\\/]*[\w_.-]+\s*[>»$#]\s*$/` — AG 터미널 프롬프트가 `…`(U+2026 ellipsis)로 시작하는데 첫 문자 클래스에 포함 안 됨
|
||||
- **해결 (v0.5.45, `d2c44fe`)**: Observer `\\s`→`\s`, http-bridge 패턴을 `/^.*[>»$#]\s*$/`로 단순화
|
||||
- **검증**: 11개 테스트 케이스 전체 통과
|
||||
|
||||
---
|
||||
|
||||
## 포맷
|
||||
### [2026-04-14] [Extension] Observer template literal 정규식 이스케이핑 오류 — "Running N commands" 스킵 미작동
|
||||
- **증상**: v9에서 `Running N commands` 그룹 헤더를 스킵하는 정규식 `/^Running\s*\d+\s*commands?$/i`를 추가했으나, 실제로 "Running2 commands" 텍스트를 전혀 매칭하지 못하여 여전히 Discord에 `cmd="Running2 commands"`가 전송됨
|
||||
- **원인**: `observer-script.ts`의 전체 코드가 TypeScript template literal (`` ` `` ... `` ` ``) 안에 있으므로, 정규식 리터럴의 백슬래시가 2중 해석됨:
|
||||
- TS 소스 `\\\\s` (4중) → template literal 출력 `\\s` → **브라우저에서 regex 리터럴 `\\s` = 리터럴 백슬래시+s** ❌
|
||||
- TS 소스 `\\s` (2중) → template literal 출력 `\s` → **브라우저에서 regex 리터럴 `\s` = whitespace class** ✅
|
||||
- TS 소스 `\s` (1중) → template literal에서 이스케이프 소멸 → **출력 `s`** ❌
|
||||
- **영향 범위**: `NOISE_CODE_RE`, `cleanLines()` (word boundary `\b`, newline `\n`), `cleanButtonText()` (whitespace `\s`), port 탐색 regex `\d`, "Running N commands" 스킵 regex 전부 미작동이었음
|
||||
- 단, `NOISE_RE` (new RegExp() 사용)는 문자열 이스케이핑이므로 4중 백슬래시가 올바름 (string → RegExp는 별도 이스케이핑 레이어)
|
||||
- **해결**: 모든 정규식 리터럴 (`/pattern/flags`) 안의 `\\\\s` → `\\s`, `\\\\d` → `\\d`, `\\\\b` → `\\b`, `\\\\n` → `\\n` 으로 수정 (v0.5.41, `8e89209`)
|
||||
- **주의**: **template literal 안에서 정규식 특수문자를 쓸 때 반드시 구분:**
|
||||
- `new RegExp('pattern')` 문자열: `\\\\s` (4중) — string 이스케이핑(1번)+regex 이스케이핑(1번) = 총 2번
|
||||
- `/pattern/` 정규식 리터럴: `\\s` (2중) — template literal 이스케이핑(1번)만 = 총 1번
|
||||
- **검증법**: `node -e "var f = require('./extension/out/observer-script.js').generateApprovalObserverScript; require('fs').writeFileSync('tmp.js', f(0)); console.log(require('fs').readFileSync('tmp.js','utf8').match(/Running.{10}/g));"` 으로 생성된 코드 확인
|
||||
|
||||
각 항목은 아래 형식을 따릅니다:
|
||||
---
|
||||
|
||||
### [2026-04-13] [Extension] Observer v8 "Running N commands" 그룹 헤더를 승인 버튼으로 오인 — Discord 빈 내용+잘못된 버튼
|
||||
- **증상**: Discord 승인 요청에 command="Running2 commands", description 비어있음, 버튼도 "Running2 commands / Always run" 형태. 실제 코드/명령어 내용이 전혀 표시되지 않음
|
||||
- **원인 1**: `observer-script.ts`의 `isActionBtn()`에 `/Running\\s*\\d*\\s*command/i` 패턴이 있어 AG UI의 그룹 헤더 버튼("Running 3 commands")을 승인 버튼으로 분류. `scan()`이 이 버튼을 먼저 만나고 `break`로 나가 실제 "Always run"/"Cancel" 버튼은 처리 안 됨
|
||||
- **원인 2**: `extractStepContext()`가 `data-step-index` 속성 없으면 `cleanButtonText(btn)` = "Running2 commands"를 그대로 반환. AG Native에는 `data-step-index`/`data-testid` 속성이 없음 (DOM 덤프로 확인)
|
||||
- **원인 3**: `http-bridge.ts`의 "Run/Always run" 필터가 step-probe 미활성(activeSessionId 비어있음) 시에도 DOM observer 신호를 차단
|
||||
- **해결**: observer v9 (v0.5.40):
|
||||
1. `isActionBtn()`에서 "Running N commands" 패턴 제거
|
||||
2. `scan()`에서 `^Running\\s*\\d+\\s*commands?$` 명시적 스킵
|
||||
3. `extractContextFromNearby()` 추가: `data-step-index` 없이 DOM 트리를 20레벨까지 올라가며 `pre`/`code` 블록에서 실제 명령어 추출
|
||||
4. `collectSiblingButtons()` 범위를 parent → grandparent → great-grandparent로 확대, 그룹 헤더 스킵
|
||||
5. `http-bridge.ts`의 "Run" 필터에 `ctx.activeSessionId` 체크 추가 — step-probe 미활성 시 DOM observer 허용
|
||||
- **주의**: AG Native UI의 "Running N commands"는 아코디언/그룹 헤더이며, 실제 승인 버튼은 하위 레벨의 "Run"/"Always run"/"Cancel". DOM 구조상 버튼 탐색 시 그룹 헤더를 반드시 스킵해야 함
|
||||
|
||||
### [2026-04-13] [Extension] HTTP Bridge UTF-8 인코딩 깨짐 — 한글 description 손실
|
||||
- **증상**: pending/ 파일의 description 필드에서 한글이 `[AI ]`처럼 깨져서 저장됨. Discord로 전달되는 승인 요청 본문도 깨짐
|
||||
- **원인**: Node.js HTTP 서버의 `req.on('data', chunk)` 콜백에서 chunk가 Buffer 타입으로 전달되는데, `body += chunk`로 string 결합 시 Buffer의 기본 인코딩(latin1)이 사용되어 multi-byte UTF-8 문자가 손실됨
|
||||
- **해결**: 모든 POST 핸들러(`/pending`, `/dump-html`, `/chat`, `/deep-inspect-result`, `/test-rpc`)에 `req.setEncoding('utf8')` 추가 (v0.5.39)
|
||||
- **주의**: Node.js HTTP 서버에서 POST body를 문자열로 수집할 때는 반드시 `req.setEncoding('utf8')`을 호출하거나, Buffer를 배열로 모은 후 `Buffer.concat().toString('utf8')`로 변환해야 함
|
||||
|
||||
### [2026-04-13] [Extension] Observer noise 필터 미작동 — textContent가 아이콘 텍스트를 줄바꿈 없이 합침
|
||||
- **증상**: pending description에 `Thought for 1s`, `chevron_right` 등 Material 아이콘명과 UI 노이즈가 그대로 남아있음
|
||||
- **원인**: DOM `textContent`는 block 요소 사이에 newline을 삽입하지 않아 `[AI 본문 요약]Thought for 1schevron_right[결행 명령]`처럼 한 줄로 합쳐짐. `cleanLines()`의 줄 단위 noise 필터(`^pattern$`)가 매칭 실패. 또한 `codeText` 추출에는 `cleanLines()`가 아예 미적용
|
||||
- **해결**: `cleanLines()`에 인라인 pre-strip 추가 — icon명 18종을 regex로 먼저 `\n`으로 치환 후 줄 단위 필터 적용. `codeText`에도 `cleanLines()` 적용 (v0.5.39)
|
||||
- **주의**: DOM에서 텍스트 추출 시 `textContent`는 레이아웃 무시, `innerText`는 detached 노드에서 미작동. 노이즈 필터링은 줄 단위뿐 아니라 인라인 패턴 제거도 병행해야 함
|
||||
|
||||
### [2026-04-13] [Extension] html-patcher String.replace() `$'` 특수 패턴으로 인라인 스크립트 SyntaxError
|
||||
- **증상**: Observer v8 인라인 스크립트가 workbench.html에 삽입되었으나 렌더러에서 전혀 실행되지 않음 (BEACON 핑 0건). V8 캐시 삭제 + AG 재시작 후에도 동일
|
||||
- **원인**: `html-patcher.ts`에서 `html.replace('</body>', '\n' + inlineBlock + '\n</body>')`를 사용. 인라인 스크립트의 NOISE_RE 정규식에 `')$', 'i'`가 있는데, `$'`는 JS `String.replace()`의 특수 대체 패턴(match 뒤의 텍스트)으로 해석됨. 이로 인해 `</body>` 뒤의 원본 HTML 구조(`<!-- Startup -->`, `<script src="workbench.js">`, `</html>`)가 JS 코드 중간(정규식 리터럴 안)에 삽입되어 **치명적 SyntaxError** 발생
|
||||
- **해결**: `inlineBlock.replace(/\$/g, '$$$$')`로 모든 `$`를 이스케이프한 후 replacement 문자열로 사용 (v0.5.38, `d6ed876`)
|
||||
- **주의**: `String.prototype.replace()`의 replacement 문자열에서 `$&`, `$'`, `` $` ``, `$1` 등은 특수 패턴. 동적 콘텐츠를 replacement로 사용할 때 반드시 `$` → `$$` 이스케이프 필요
|
||||
|
||||
### [2026-04-12] [Extension] V8 CachedData가 Observer 스크립트 로딩을 차단
|
||||
- **증상**: html-patcher가 workbench-jetski-agent.html에 observer v7 인라인 스크립트를 성공적으로 삽입했지만, deep-inspect가 렌더러에서 응답 없음 (10s timeout). AG 재시작 후에도 observer가 로드되지 않음
|
||||
- **원인**: `%APPDATA%\Antigravity\CachedData\` (50MB)에 V8 캐시가 남아있어, AG가 패치된 HTML 대신 캐시된 이전 버전을 로드. extension.log에 `patcher.install() called (needs reload)` 메시지가 표시되지만 실제 적용이 안 됨
|
||||
- **해결**: `Remove-Item "$env:APPDATA\Antigravity\CachedData\*" -Recurse -Force` 실행 후 AG 재시작. known-issues-archive #6에도 동일 케이스 있음
|
||||
- **주의**: HTML 패치 변경 시 **반드시** V8 CachedData 삭제 + AG 재시작 필요. 단순 AG 재시작만으로는 부족
|
||||
|
||||
### [2026-04-12] [DOM] text-ide-message-block-bot-color는 AI 응답 컨테이너가 아닌 NUX tooltip 전용
|
||||
- **증상**: observer-script가 `.text-ide-message-block-bot-color`를 AI 응답 컨테이너로 사용하지만, 실제 AI 텍스트를 추출하지 못함
|
||||
- **원인**: 번들 분석(jetskiAgent/main.js 10.8MB)으로 확인 결과, 이 클래스는 `hsn` 컴포넌트(NUX Tooltip 텍스트 색상)에서만 사용. AI 응답 텍스트는 `plannerResponse` step의 `Whi` 렌더러 → `div.px-2.py-1` → `MarkdownRenderer` 내부에 렌더링됨
|
||||
- **해결**: observer-script에서 `.text-ide-message-block-bot-color` 의존성 제거 필요. `markdown-body` 클래스도 AG Native에 존재하지 않음
|
||||
- **주의**: AI 응답 마크다운은 `prose` 관련 클래스나 MarkdownRenderer 내부 구조로 타겟팅해야 함. 실제 DOM 덤프로 정확한 셀렉터 확인 필요
|
||||
|
||||
### [2026-04-12] [SDK/DOM] AG Native 세션은 Cascade SDK API에 등록되지 않음 — DOM이 유일한 데이터 소스
|
||||
- **증상**: AG Native 세션에서 Discord 릴레이로 AI 응답이 전혀 전달되지 않고, 대신 UI 노이즈(`content_copy`, `Always run`, `keyboard_arrow_up`, `Cancel`)가 전송됨
|
||||
- **원인 1 (SDK)**: `GetCascadeTrajectorySteps(cascadeId=세션ID)` → `500 trajectory not found`. `GetDiagnostics` → `404`. AG Native 세션은 Cascade trajectory API에 전혀 등록되지 않는 별도 시스템
|
||||
- **원인 2 (DOM)**: `observer-script.ts` v6의 `scanChatBodies()`가 `.text-ide-message-block-bot-color` 컨테이너의 `textContent`를 통째로 가져오면서 내부 버튼/아이콘 텍스트까지 포함
|
||||
- **해결**: `observer-script.ts` v7로 전면 재설계:
|
||||
1. `[data-testid="conversation-view"]` + `[data-step-index]` 기반 step-aware 파싱
|
||||
2. `extractCleanStepText()`: 클론 후 button/svg/icon 엘리먼트 제거 → 마크다운 텍스트만 추출
|
||||
3. `extractStepContext()`: `getStepContainer()` → step 헤더 + code 블록만 추출
|
||||
4. `NOISE_RE`: Material icon 이름, 버튼 레이블, UI 텍스트 전면 차단
|
||||
5. 최초 `conversation-view` 감지 시 DOM 구조 자동 덤프 (`/dump-html`)
|
||||
- **주의**: SDK 경로(step-probe RT-CAPTURE)는 AG Native에서 사용 불가. DOM이 유일한 콘텐츠 소스이므로 AG UI 업데이트 시 `data-testid`/`data-step-index` 속성 존재 여부 반드시 확인 필요
|
||||
|
||||
|
||||
### [2026-04-09] [Bridge] Discord Body Content Missing Due to Step Probe Dummy Payload
|
||||
- **利앹긽**: <20><><EFBFBD>洹쒕え UI 留덉씠洹몃젅<EBAA83>씠<EFBFBD>뀡 <20>썑, <20>뵒<EFBFBD>뒪肄붾뱶 <20>듅<EFBFBD>씤 硫붿떆吏<EB9686> 蹂몃Ц<EBAA83>뿉 <20>떎<EFBFBD>뻾<EFBFBD>븷 肄붾뱶/紐낅졊<EB8285>뼱媛<EBBCB1> <20>셿<EFBFBD>쟾<EFBFBD>엳 <20>늻<EFBFBD>씫<EFBFBD>릺怨<EBA6BA> "Step #15"<22><><EFBFBD> 媛숈<E5AA9B><EC8888> <20>뵒<EFBFBD>뤃<EFBFBD>듃 <20>뀓<EFBFBD>뒪<EFBFBD>듃留<EB9383> <20>쟾<EFBFBD>넚<EFBFBD>맖.
|
||||
- **<2A>썝<EFBFBD>씤**: Native UI 蹂<>寃쎌쑝濡<EC919D> <20>씤<EFBFBD>빐 DOM observer媛<72> 異붿텧<EBB6BF>븳 踰꾪듉 <20>뀓<EFBFBD>뒪<EFBFBD>듃("Always run")媛<> `http-bridge.ts` <20>븘<EFBFBD>꽣 <20>슦<EFBFBD>쉶 諛<> bot.py<70>뿉<EFBFBD>꽌 吏<><EFA79E>뿰(defer) 泥섎━<EC848E>맖. 諛섎㈃ `step-probe.ts`媛<EFBFBD> `GetAllCascadeTrajectories` <20>뤃留곸쓣 <20>넻<EFBFBD>빐 <20>룞<EFBFBD>떆<EFBFBD>뿉 諛쒖깮<EC9296>떆<EFBFBD>궓 dummy pending payload (紐낅졊<EB8285>뼱 <20>긽<EFBFBD>꽭 <20>궡<EFBFBD>슜<EFBFBD>씠 <20>뾾<EFBFBD>씠 `Step #XX` <20>씪<EFBFBD>뒗 <20>뀓<EFBFBD>뒪<EFBFBD>듃留<EB9383> <20>룷<EFBFBD>븿)媛<> 遊뉗뿉 <20>쓽<EFBFBD>빐 癒쇱<E79992><EC87B1> <20>옄<EFBFBD>룞 <20>듅<EFBFBD>씤<EFBFBD>릺硫댁꽌 <20>젙<EFBFBD>옉 <20>떎<EFBFBD>젣 肄붾뱶 <20>쁺<EFBFBD>뿭 <20>젙蹂닿<E8B982><EB8BBF> 利앸컻<EC95B8>븿.
|
||||
- **<2A>빐寃<EBB990>**: `step-probe.ts` <20>궡<EFBFBD>뿉 `formatStepProbeCommand` <20>뿬<EFBFBD>띁 <20>븿<EFBFBD>닔瑜<EB8B94> 異붽<E795B0><EBB6BD><EFBFBD>븯<EFBFBD>뿬, WAITING <20>긽<EFBFBD>깭 <20>뒪<EFBFBD>뀦<EFBFBD>쓽 `argumentsJson` <20>뜲<EFBFBD>씠<EFBFBD>꽣瑜<EABDA3> 吏곸젒 <20>뙆<EFBFBD>떛<EFBFBD>븯怨<EBB8AF> `CommandLine`, `TargetFile` <20>벑 <20>떎<EFBFBD>젣 紐낅졊<EB8285>뼱<EFBFBD><EBBCB1><EFBFBD> <20>긽<EFBFBD>꽭 <20>씤<EFBFBD>옄/肄붾뱶瑜<EBB1B6> `command`<EFBFBD><EFBFBD><EFBFBD> `description`<EFBFBD>쑝濡<EFBFBD> <20>븷<EFBFBD>떦<EFBFBD>븯<EFBFBD>뿬 釉뚮┸吏<E294B8>濡<EFBFBD> <20>꽆湲곕룄濡<EBA384> <20>뙣移섑븿. DOM <20>샃<EFBFBD><EC8383><EFBFBD>踰꾩쓽 遺덉븞<EB8D89>젙<EFBFBD>꽦怨<EABDA6> 愿<>怨꾩뾾<EABEA9>씠 <20>씪愿<EC94AA><E684BF>맂 蹂몃Ц <20>쟾<EFBFBD>떖 蹂댁옣.
|
||||
- **二쇱쓽**: UI <20>뒪<EFBFBD>겕<EFBFBD>옒<EFBFBD>븨<EFBFBD>뿉 <20>쓽議댄븯<EB8C84>뒗 DOM Observer 諛⑹떇<E291B9><EB9687><EFBFBD> UI <20>젅<EFBFBD>씠<EFBFBD>븘<EFBFBD>썐, <20>븘<EFBFBD>씠肄<EC94A0> <20>궫<EFBFBD>엯 <20>벑<EFBFBD>뿉 痍⑥빟<E291A5>븯誘<EBB8AF>濡<EFBFBD>, <20>긽<EFBFBD>꽭 <20>럹<EFBFBD>씠濡쒕뱶 異붿텧<EBB6BF><ED85A7><EFBFBD> <20>빆<EFBFBD>긽 100% <20>떊猶<EB968A> 媛<><E5AA9B>뒫<EFBFBD>븳 SDK RPC(`step-probe.ts`) <20>뜲<EFBFBD>씠<EFBFBD>꽣瑜<EABDA3> <20>슦<EFBFBD>꽑 <20>궗<EFBFBD>슜<EFBFBD>븯<EFBFBD>룄濡<EBA384> 援ъ꽦<D18A>빐<EFBFBD>빞 <20>븿.
|
||||
|
||||
|
||||
|
||||
## <20>룷留<EBA3B7>
|
||||
|
||||
|
||||
|
||||
### [2026-03-23] [Extension] Cross-Project DOM Observer Leakage
|
||||
|
||||
- **利앹긽**: <20>떎以<EB968E> <20>썝寃<EC8D9D> 而댄벂<EB8C84>꽣<EFBFBD>뿉<EFBFBD>꽌 <20>룞<EFBFBD>씪<EFBFBD>븳 <20>봽濡쒖젥<EC9296>듃紐낆쑝濡<EC919D> <20>떎<EFBFBD>뻾<EFBFBD>맂 VS Code<64>뱾<EFBFBD>씠 <20>꽌濡쒖쓽 `execute JavaScript` (Allow) <20>듅<EFBFBD>씤 <20>떊<EFBFBD>샇瑜<EC8387> 媛<>濡쒖콈嫄곕굹 <20>뿁<EFBFBD>슧<EFBFBD>븳 <20>꽌踰꾨줈 蹂대깂.
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**: Extension<6F>씠 `workbench.html`<EFBFBD>뿉 <20>뒪<EFBFBD>겕由쏀듃瑜<EB9383> 二쇱엯<EC87B1>븷 <20>븣 寃곗젙濡좎쟻 <20>룷<EFBFBD>듃瑜<EB9383> <20>븯<EFBFBD>뱶肄붾뵫<EBB6BE>뻽<EFBFBD>뒗<EFBFBD>뜲, <20>쟾<EFBFBD>뿭 罹먯떆<EBA8AF>맂 HTML <20>뙆<EFBFBD>씪<EFBFBD>쓣 紐⑤뱺 濡쒖뺄/<2F>썝寃<EC8D9D> <20>뿰寃곗씠 怨듭쑀<EB93AD>븯硫댁꽌 留덉<EFA78D><EB8D89>留됱뿉 <20>뿴由<EBBFB4> <20>봽濡쒖젥<EC9296>듃<EFBFBD>쓽 <20>룷<EFBFBD>듃 踰덊샇濡<EC8387> <20>뜮<EFBFBD>뼱<EFBFBD>뵆<EFBFBD>썙吏<EC8D99>.
|
||||
|
||||
- **<2A>빐寃<EBB990>**: `extension.ts`<EFBFBD>뿉<EFBFBD>꽌 <20>긽<EFBFBD>깭 <20>몴<EFBFBD>떆以<EB9686>(Status Bar) `tooltip`<EFBFBD>뿉 <20>룷<EFBFBD>듃瑜<EB9383> 二쇱엯<EC87B1>븯怨<EBB8AF>, `observer-script.ts`<EFBFBD>뿉<EFBFBD>꽌 DOM 荑쇰━瑜<E29481> <20>넻<EFBFBD>빐 <20>룞<EFBFBD>쟻<EFBFBD>쑝濡<EC919D> <20>옄<EFBFBD>떊<EFBFBD>쓽 李<>(Window)<29>뿉 <20>븷<EFBFBD>떦<EFBFBD>맂 <20>룷<EFBFBD>듃瑜<EB9383> 李얠븘<EC96A0>궡<EFBFBD>룄濡<EBA384> <20>닔<EFBFBD>젙. `vscode.env.asExternalUri`瑜<EFBFBD> <20>궗<EFBFBD>슜<EFBFBD>븯<EFBFBD>뿬 <20>룷<EFBFBD>듃 異⑸룎 <20>떆 <20>슦<EFBFBD>쉶<EFBFBD>맂 二쇱냼源뚯<E6BA90><EB9AAF> 濡쒖뺄 <20>룷<EFBFBD>썙<EFBFBD>뵫<EFBFBD>뿉 留ㅽ븨<E385BD>릺<EFBFBD>룄濡<EBA384> 吏<><EFA79E>썝.
|
||||
|
||||
- **二쇱쓽**: VS Code UI 肄붿뼱(HTML) <20>뙣移<EB99A3> <20>떆, <20>뿬<EFBFBD>윭 李<>(Window)<29>씠<EFBFBD>굹 <20>떎以<EB968E> <20>썝寃<EC8D9D> <20>젒<EFBFBD>냽 <20>떆 <20>솚寃<EC869A>(Scope) 遺꾨━<EABEA8>뿉 媛곷퀎<EAB3B7>븳 二쇱쓽媛<EC93BD> <20>븘<EFBFBD>슂<EFBFBD>븿. <20>쟾<EFBFBD>뿭 <20>옄<EFBFBD>썝<EFBFBD>뿉 <20>쓽議댄븯<EB8C84>뒗 <20>븯<EFBFBD>뱶肄붾뵫 吏<><EFA79E>뼇.
|
||||
|
||||
|
||||
|
||||
### [<5B>궇吏<EAB687>] [<5B>궎<EFBFBD>썙<EFBFBD>뱶] <20><><EFBFBD> <20>븳以<EBB8B3> <20>슂<EFBFBD>빟
|
||||
|
||||
- **利앹긽**: 臾댁뾿<EB8C81>씠 <20>옒紐삳릺<EC82B3>뿀<EFBFBD>뒗媛<EB9297>
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**: 洹쇰낯 <20>썝<EFBFBD>씤
|
||||
|
||||
- **<2A>빐寃<EBB990>**: <20>삱諛붾Ⅸ <20>빐寃<EBB990> 諛⑸쾿
|
||||
|
||||
- **二쇱쓽**: <20>옱諛<EC98B1> 諛⑹<E8AB9B><E291B9>瑜<EFBFBD> <20>쐞<EFBFBD>븳 援먰썕
|
||||
|
||||
```markdown
|
||||
### [날짜] [키워드] — 한줄 요약
|
||||
- **증상**: 무엇이 잘못되었는가
|
||||
- **원인**: 근본 원인
|
||||
- **해결**: 올바른 해결 방법
|
||||
- **주의**: 재발 방지를 위한 교훈
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 공통 이슈
|
||||
|
||||
### [2026-03-08] PowerShell curl — Invoke-WebRequest 충돌
|
||||
- **증상**: `curl` 명령이 예상과 다른 응답 형식을 반환
|
||||
- **원인**: PowerShell에서 `curl`은 `Invoke-WebRequest`의 별칭
|
||||
- **해결**: **`curl.exe`**를 명시적으로 사용
|
||||
- **주의**: HTTP 관련 모든 명령에서 `curl.exe` 사용 필수
|
||||
|
||||
### [2026-03-08] PowerShell npm — 실행 정책 오류
|
||||
- **증상**: `npm run` 명령이 `실행 정책` 관련 오류로 실패
|
||||
- **원인**: PowerShell 스크립트 실행 정책이 제한적으로 설정됨
|
||||
- **해결**: `cmd /c npm run dev` 형식으로 cmd를 통해 실행
|
||||
- **주의**: npm 관련 명령은 항상 `cmd /c` 접두어 사용 권장
|
||||
|
||||
---
|
||||
|
||||
## 프로젝트별 이슈
|
||||
|
||||
> 아래에 프로젝트 특화 이슈를 추가하세요.
|
||||
|
||||
### [2026-03-08] Antigravity Renderer Injection — Electron 캐시 차단
|
||||
- **증상**: workbench.html, workbench-jetski-agent.html에 `<script>` 태그 추가 후 리로드해도 실행되지 않음
|
||||
- **원인**: Electron의 V8 코드 캐시가 수정된 HTML을 무시하고 캐시된 버전을 서빙
|
||||
- **해결**: 렌더러 인젝션 방식 **포기**. Extension Host에서 RPC 폴링 방식으로 전환
|
||||
- **주의**: Antigravity는 `workbench-jetski-agent.html`을 사용 (Jetski = 내부 코드네임)
|
||||
### [2026-04-08] [Discord Bot] Channel Deletion Cache Desync
|
||||
|
||||
### [2026-03-08] Antigravity 승인 대기 = RUNNING (NOT IDLE)
|
||||
- **증상**: IDLE 기반 승인 감지가 실제 승인 대기를 놓침
|
||||
- **원인**: 승인 대기 시 세션 상태가 `CASCADE_RUN_STATUS_RUNNING` (IDLE 아님), `IDLE`은 대화 대기(notify_user 후)
|
||||
- **해결**: `RUNNING + delta=0` (stall) 기반 감지로 전환. 6 polls (30초) 이상 FROZEN 시 pending 생성
|
||||
- **주의**: Thinking/생성 중에도 `RUNNING + delta=0`이 발생 → `lastModifiedTime`으로 구분 시도했으나 불완전
|
||||
- **利앹긽**: 遊뉗씠 耳쒖졇 <20>엳<EFBFBD>뒗 <20>긽<EFBFBD>깭<EFBFBD>뿉<EFBFBD>꽌 Discord 梨꾨꼸(g-project-name)<29>쓣 <20>궘<EFBFBD>젣<EFBFBD>븯硫<EBB8AF>, 遊뉗씠 <20>궘<EFBFBD>젣瑜<ECA0A3> <20>씤吏<EC94A4><EFA79E>븯吏<EBB8AF> 紐삵븯怨<EBB8AF> <20>깉 梨꾨꼸<EABEA8>쓣 <20>깮<EFBFBD>꽦<EFBFBD>븯吏<EBB8AF> <20>븡<EFBFBD>쑝硫<EC919D> 硫붿떆吏<EB9686><EFA79E>룄 利앸컻<EC95B8>븿.
|
||||
|
||||
### [2026-03-08] ResolveOutstandingSteps RPC — 승인이 아닌 취소!
|
||||
- **증상**: Discord 승인 → `ResolveOutstandingSteps` 호출 → step이 취소됨
|
||||
- **원인**: `ResolveOutstandingSteps`는 blocking steps를 "resolve" = **REJECT/CANCEL**, approve가 아님
|
||||
- **해결**: `ResolveOutstandingSteps` 제거. `HandleCascadeUserInteraction`은 `socket hang up`
|
||||
- **주의**: KI에 "more reliable"로 기록되어 있으나 실제 동작은 cancel임. KI 업데이트 필요
|
||||
- **<2A>썝<EFBFBD>씤**: ot.py<70>쓽 self.project_channels <20>뵓<EFBFBD>뀛<EFBFBD>꼫由ъ뿉 梨꾨꼸 媛앹껜媛<EABB9C> 罹먯떆<EBA8AF>릺<EFBFBD>뼱 <20>엳<EFBFBD>뼱, API <20>샇異<EC8387> <20>뾾<EFBFBD>씠 罹먯떆<EBA8AF>맂(<28>궘<EFBFBD>젣<EFBFBD>맂) 梨꾨꼸濡<EABCB8> 硫붿떆吏<EB9686>瑜<EFBFBD> 蹂대궡<EB8C80>젮 <20>떆<EFBFBD>룄<EFBFBD>븯<EFBFBD>떎 404 <20>뿉<EFBFBD>윭 諛쒖깮 <20>썑 <20>떎<EFBFBD>뙣<EFBFBD>븿.
|
||||
|
||||
### [2026-03-08] VS Code Accept Commands — Silent Success 문제
|
||||
- **증상**: 4개 accept command 모두 OK(undefined) 반환하나 실제 승인 안 됨
|
||||
- **원인**: webview에 활성 포커스가 필요. `panel.focus()`로는 충분하지 않음
|
||||
- **해결**: **미해결**. Windows UI Automation 등 OS 레벨 접근 필요
|
||||
- **주의**: reject commands는 동작함. accept만 focus 의존성 있음
|
||||
- **<2A>빐寃<EBB990>**: 梨꾨꼸 留듯븨<EB93AF>씠 瑗ъ<E79197><D18A><EFBFBD>쓣 <20>븣<EFBFBD>뒗 **Python 遊<>(Docker 而⑦뀒<E291A6>씠<EFBFBD>꼫)<29>쓣 <20>옱<EFBFBD>떆<EFBFBD>옉**<2A>븯<EFBFBD>뿬 罹먯떆瑜<EB9686> 珥덇린<EB8D87>솕<EFBFBD>븯怨<EBB8AF> 梨꾨꼸 紐⑸줉<E291B8>쓣 <20>깉濡<EAB989> 媛깆떊<EAB986>븯寃<EBB8AF> <20>븿.
|
||||
|
||||
### [2026-03-08] Multi-Window 세션 등록 경쟁 조건
|
||||
- **증상**: 이 창(gravity_control)의 대화가 `#ag-variet_agent` 채널로 메시지 전달
|
||||
- **원인**: `writeRegistration()`이 폴링 루프에서 호출 → 먼저 폴링한 확장이 세션을 자기 프로젝트로 등록
|
||||
- **해결**: `writeRegistration`을 폴링에서 제거, `writeChatSnapshot`/`writePendingApproval`에서만 지연 호출
|
||||
- **주의**: `GetAllCascadeTrajectories`는 모든 창의 세션을 반환하므로 세션→창 매핑은 불가능. 활동 기반 등록만 신뢰 가능
|
||||
- **二쇱쓽**: 梨꾨꼸 愿<>由щ뒗 罹먯떆<EBA8AF>뿉 <20>쓽議댄븯湲<EBB8AF> <20>븣臾몄뿉 媛뺤젣濡<ECA0A3> Discord UI<55>뿉<EFBFBD>꽌 梨꾨꼸<EABEA8>쓣 吏<><EFA79E>썱<EFBFBD>쓣 <20>븣<EFBFBD>뒗 諛섎뱶<EC848E>떆 遊뉗쓣 <20>옱援щ룞<D189>빐<EFBFBD>빞 <20>븿.
|
||||
|
||||
### [2026-03-08] 공유 렌더러 스크립트 파일 덮어쓰기 문제
|
||||
- **증상**: DOM Observer 렌더러 스크립트가 잘못된 HTTP bridge 포트에 연결
|
||||
- **원인**: 두 확장이 동일한 `ag-sdk-variet-gravity-bridge.js` 파일에 각자 포트를 씀 → 마지막 확장 것만 남음
|
||||
- **해결**: `ag-bridge-ports.json`에 모든 확장의 port를 JSON으로 기록, 렌더러가 all ports를 순회하며 ping
|
||||
- **주의**: 렌더러 스크립트 파일 경로는 SDK patcher namespace에 의해 고정 — 변경 불가
|
||||
|
||||
### [2026-03-08] workbench.html vs workbench-jetski-agent.html
|
||||
- **증상**: 렌더러에서 `[GB Observer]` 로그가 전혀 안 나옴
|
||||
- **원인**: DevTools가 `workbench.html`을 로드 — 스크립트 태그는 `workbench-jetski-agent.html`에만 패치됨
|
||||
- **해결**: `workbench.html`에도 스크립트 태그 필요. Antigravity 재설치 후 SDK patcher가 올바르게 패치하도록 함
|
||||
- **주의**: SDK patcher는 `both` HTML 파일을 패치하지만, 수동 수정은 Antigravity integrity check에 의해 되돌려질 수 있음
|
||||
|
||||
### [2026-03-08] product.json 체크섬 불일치 → 렌더러 스크립트 미로딩
|
||||
- **증상**: `<script>` 태그가 HTML에 존재하고 .js 파일도 디스크에 있으나, 렌더러 콘솔에 스크립트 로그가 전혀 없음
|
||||
- **원인**: Antigravity 재설치 시 `product.json`의 SHA256 체크섬이 원본으로 리셋됨. Extension이 HTML을 패치하지만 `IntegrityManager.suppressCheck()`를 호출하지 않아 체크섬 불일치. `vscode-file://` 프로토콜이 체크섬 불일치 파일을 무시하고 **원본 캐시 HTML**을 서빙
|
||||
- **해결**: `product.json`의 `checksums` 항목에서 수정된 파일(workbench.html, workbench-jetski-agent.html)의 SHA256 해시를 실제 파일 기준으로 업데이트. SDK `IntegrityManager.suppressCheck()` 호출 또는 수동 스크립트로 해결
|
||||
- **주의**: Extension `setupApprovalObserver()`에 `suppressCheck()` 호출을 영구 추가해야 재설치마다 반복 안 됨. 해시 = `base64(sha256(file)).replace(/=+$/, '')`
|
||||
### [2026-04-08] [Extension] Multiple Workspace LS Cross-Connection
|
||||
|
||||
### [2026-03-08] vscode-file:// 프로토콜 — 커스텀 .js 파일 서빙 불가
|
||||
- **증상**: `<script src="./ag-sdk-variet-gravity-bridge.js">` 태그가 HTML에 있으나 `net::ERR_FILE_NOT_FOUND` 발생, GB Observer 로그 전혀 없음
|
||||
- **원인**: `vscode-file://` 프로토콜은 원본 배포에 포함된 파일만 서빙. Extension이 디스크에 쓴 커스텀 `.js` 파일은 프로토콜 레벨에서 차단됨
|
||||
- **해결**: 외부 `<script src>` 참조 대신 **인라인 `<script>...코드...</script>`** 방식으로 HTML에 직접 삽입
|
||||
- **주의**: `ag-bridge-ports.json`도 같은 이유로 XHR 로딩 불가. 모든 렌더러 스크립트/데이터는 HTML 인라인으로 전달해야 함
|
||||
- **利앹긽**: ariet-llm 李쎌뿉<EC8E8C>꽌 耳곗쑝<EAB397>굹 gravity_control<6F>쓽 諛깃렇<EAB983>씪<EFBFBD>슫<EFBFBD>뱶 援щ룞 以묒씤 LS<4C>뿉 <20>뿰寃곕릺<EAB395>뼱 <20>옄湲<EC9884> <20>옄<EFBFBD>떊 李쎌쓽 <20>떊<EFBFBD>샇瑜<EC8387> <20>옟吏<EC989F> 紐삵븿.
|
||||
|
||||
### [2026-03-08] Renderer 포트 디스커버리 — ag-bridge-ports.json XHR 실패
|
||||
- **증상**: `[GB Observer] Port discovery timeout after 2min` — 렌더러가 bridge 포트를 찾지 못함
|
||||
- **원인**: 렌더러 스크립트가 `./ag-bridge-ports.json`을 동기 XHR로 읽으려 하나, `vscode-file://` 프로토콜이 `.json` 파일 서빙 거부
|
||||
- **해결**: (1) 프로젝트명 해시 기반 **결정론적 포트** 사용 (`gravity_control→34332`), (2) 스크립트 생성 시 포트를 `HARDCODED_PORT=${port}`로 직접 삽입
|
||||
- **주의**: `server.listen(0)` 랜덤 포트 → 매 재시작마다 변경되어 렌더러와 불일치. 결정론적 포트는 `EADDRINUSE` 시 랜덤 폴백 필요
|
||||
- **<2A>썝<EFBFBD>씤**: <20>뿬<EFBFBD>윭 VS Code 李쎌쓣 <20>쓣<EFBFBD>썱<EFBFBD>쓣 <20>븣 <20>뼱<EFBFBD>뼡 李쎌뿉<EC8E8C>꽌<EFBFBD>뒗 Antigravity <20>뙣<EFBFBD>꼸<EFBFBD>쓣 <20>늻瑜댁<E7919C><EB8C81> <20>븡<EFBFBD>븘 <20>쟾<EFBFBD>슜 LS媛<53> <20>떆<EFBFBD>옉<EFBFBD>릺吏<EBA6BA> <20>븡<EFBFBD>쓬. ixLSConnection()<29>씠 <20>옄湲<EC9884> 紐レ쓽 LS瑜<53> 李얠<EFA7A1><EC96A0> 紐삵븯怨<EBB8AF> fallback<63>쑝濡<EC919D> 湲곗〈<EAB397>뿉 <20>뼚 <20>엳<EFBFBD>뜕 <20>떎瑜<EB968E> 李쎌쓽 LS<4C>뿉 <20>뿰寃곕맖.
|
||||
|
||||
### [2026-03-08] Extension 컴파일 경로 != 설치 경로
|
||||
- **증상**: `npm run compile` 후 Extension 동작이 변하지 않음
|
||||
- **원인**: `npm run compile`은 `extension/out/extension.js`에만 빌드. Antigravity는 `~/.antigravity/extensions/variet.gravity-bridge-X.X.X/out/extension.js`에서 로드
|
||||
- **해결**: 컴파일 후 반드시 설치 경로로 수동 복사하거나, `vsce package` → VSIX 재설치
|
||||
- **주의**: `extension/out/` ≠ 실행 경로. 항상 설치 경로 확인 필요
|
||||
- **<2A>빐寃<EBB990>**: <20><><EFBFBD><EFBFBD>긽 李쎌뿉<EC8E8C>꽌 Developer: Reload Window <20>떎<EFBFBD>뻾 <20>썑 **<2A>궗<EFBFBD>씠<EFBFBD>뱶諛붿쓽 濡쒖뺄 Antigravity 梨쀫큸 <20>뙣<EFBFBD>꼸<EFBFBD>쓣 <20>븳 踰<> <20>뿴<EFBFBD>뼱** <20>옄<EFBFBD>떊<EFBFBD>쓽 LS <20>봽濡쒖꽭<EC9296>뒪瑜<EB92AA> <20>쓣<EFBFBD>슫 <20>뮘<EFBFBD>뿉 Gravity Bridge瑜<65> Start<72>븿.
|
||||
|
||||
### [2026-03-08] Electron 메인 프로세스 체크섬 캐시 — Reload Window 불충분
|
||||
- **증상**: product.json 체크섬 업데이트 + HTML 패치 후 `Reload Window` → 패치 미적용
|
||||
- **원인**: Electron 메인 프로세스가 시작 시 product.json 체크섬을 메모리에 캐시. `Reload Window`는 렌더러만 재시작하므로 캐시된 구 체크섬 사용
|
||||
- **해결**: Antigravity를 **완전 종료 후 재시작** 필요 (File → Exit 후 재실행)
|
||||
- **주의**: `Reload Window` ≠ 앱 재시작. 체크섬 변경 시 항상 풀 재시작 필요
|
||||
- **二쇱쓽**: LS<4C>뒗 <20>옄<EFBFBD>룞<EFBFBD>쑝濡<EC919D> <20>떆<EFBFBD>옉<EFBFBD>릺吏<EBA6BA> <20>븡怨<EBB8A1> <20>궗<EFBFBD>슜<EFBFBD>옄媛<EC9884> 梨꾪똿 <20>뙣<EFBFBD>꼸<EFBFBD>쓣 <20>븳 踰<> <20>겢由<EAB2A2>/<2F>솢<EFBFBD>꽦<EFBFBD>솕<EFBFBD>빐<EFBFBD>빞留<EBB99E> Spawn <20>맖.
|
||||
|
||||
### [2026-03-08] Renderer 동기 XHR — Electron 보안 정책 차단
|
||||
- **증상**: `tryPing()` 함수가 동기 `XMLHttpRequest`로 HTTP bridge에 연결 시도 → 타임아웃
|
||||
- **원인**: Electron 렌더러 프로세스에서 동기 XHR이 보안 정책에 의해 차단됨
|
||||
- **해결**: `fetch()` + `AbortSignal.timeout(2000)` 비동기 방식으로 교체 (`tryPingAsync`)
|
||||
- **주의**: `async/await` 사용 불가 (ES5 환경). `.then()` 체이닝으로 구현
|
||||
|
||||
### [2026-03-08] DOM Observer — Run 버튼 감지 불가 (webview iframe 격리)
|
||||
- **증상**: Allow Once/Allow This Conversation는 감지되나 Run/Accept 버튼은 감지 안 됨
|
||||
- **원인**: Trust/permission 버튼은 워크벤치 외부 DOM에 렌더링, Run/Accept는 **Antigravity 채팅 webview iframe** 내부의 별도 DOM에 렌더링. 렌더러 스크립트의 `document.querySelector()`는 iframe 내부 접근 불가
|
||||
- **해결**: Run 버튼은 DOM Observer가 아닌 **`latestToolCallStep` RPC 기반** 즉시 감지로 대체
|
||||
- **주의**: webview iframe에 스크립트 주입은 Electron `executeJavaScript()`로 가능하나, 현재 RPC 방식이 더 안정적
|
||||
|
||||
### [2026-03-08] Accept all/Reject all 리뷰 바 — agent 패널 밖 DOM
|
||||
- **증상**: 코드 변경 리뷰 바(Accept all/Reject all)가 DOM Observer에 감지 안 됨
|
||||
- **원인**: `scan()` 함수가 `findPanel()` (`.antigravity-agent-side-panel` 등) 내부만 검색. 리뷰 바는 에디터/notification 영역에 렌더링되어 패널 밖에 있음
|
||||
- **해결**: `scan()` 검색 범위를 `document.body` 전체로 확장, `Accept all` / `Reject all` 패턴 추가
|
||||
- **주의**: 패널+body 이중 검색 시 dedupe 필요 (같은 버튼이 두 번 잡힐 수 있음)
|
||||
## <EFBFBD>윍<EFBFBD> Active/Recent Issues
|
||||
|
||||
### [2026-03-08] GetCascadeTrajectorySteps — cascadeId 파라미터 발견
|
||||
- **증상**: `GetCascadeTrajectorySteps`에 `trajectoryId` 파라미터 → 500 "trajectory not found"
|
||||
- **원인**: 파라미터명이 `trajectoryId`가 아니라 **`cascadeId`**. 값은 `GetAllCascadeTrajectories.trajectorySummaries`의 맵 키(세션 ID)
|
||||
- **해결**: `{ cascadeId: sessionId }`로 호출 → 전체 step 배열 반환 성공
|
||||
- **주의**: `latestToolCallStep` 필드는 `GetAllCascadeTrajectories` 응답에 **존재하지 않음** (KI 오류)
|
||||
|
||||
### [2026-03-08] Step 구조 — CORTEX_STEP_STATUS_WAITING 즉시 감지
|
||||
- **증상**: stall-based 감지(100초)가 너무 느림
|
||||
- **원인**: 이제 `GetCascadeTrajectorySteps`로 최신 step의 status를 직접 확인 가능
|
||||
- **해결**: stall 5초 후 step probe → `CORTEX_STEP_STATUS_WAITING` 확인 → 즉시 pending 생성
|
||||
- **Step 구조**: `{type: "CORTEX_STEP_TYPE_RUN_COMMAND", status: "CORTEX_STEP_STATUS_WAITING", metadata: {toolCall: {name, argumentsJson}}, runCommand, requestedInteraction}`
|
||||
- **주의**: 775-step 하드 리밋은 여전히 존재. 긴 세션에서는 fallback(40초) 사용
|
||||
|
||||
### [2026-03-08] Extension 재설치 안전성 — 자동 패치 메커니즘
|
||||
- **증상**: Antigravity 삭제 후 재설치 시 렌더러 스크립트가 동작 안 함
|
||||
- **원인**: 재설치 시 HTML/product.json이 원본으로 리셋됨
|
||||
- **해결**: Extension의 `setupApprovalObserver()`가 **자동으로** 모든 패치를 수행:
|
||||
1. `workbench.html` + `workbench-jetski-agent.html` 인라인 스크립트 삽입
|
||||
2. `product.json` SHA256 체크섬 자동 업데이트
|
||||
3. HTTP bridge 서버 시작 + 결정론적 포트
|
||||
- **주의**: 패치 후 반드시 **Antigravity 풀 재시작** 필요 (Reload Window 불가). Extension VSIX만 설치하면 수동 패치 불필요
|
||||
### [2026-04-09] [Extension] Agent UI Native Migration & Icon Text Gluing
|
||||
|
||||
### [2026-03-08] Response 파일 Race Condition — DOM Observer 승인 실패
|
||||
- **증상**: Discord에서 승인 → `[RESPONSE] renderer-handled approval` 로그 출력 → 실제 버튼 클릭 안 됨
|
||||
- **원인**: `processResponseFile` (파일 감시자)이 response 파일을 즉시 삭제 → renderer의 `pollResponse`가 HTTP `GET /response/:rid`로 조회 시 파일 이미 없음
|
||||
- **해결**: DOM observer 소스일 때는 response 파일을 삭제하지 않도록 수정. HTTP endpoint가 renderer에게 서빙한 후 삭제
|
||||
- **주의**: non-DOM (stall/step_probe relay)는 watcher에서 삭제해도 됨
|
||||
- **利앹긽**: UI Tailwind/Native 留덉씠洹몃젅<EBAA83>씠<EFBFBD>뀡 諛<> <20>븘<EFBFBD>씠肄<EC94A0> <20>쟻<EFBFBD>슜 <20>썑, Discord 釉뚮┸吏<E294B8>濡<EFBFBD> <20>떊<EFBFBD>샇媛<EC8387> <20>쟾<EFBFBD>넚<EFBFBD>릺吏<EBA6BA> <20>븡<EFBFBD>쓬.
|
||||
|
||||
### [2026-03-08] Renderer 스크립트 소스 혼동 — 3곳의 코드
|
||||
- **증상**: `extension.ts`에 BTN-DUMP 추가 → Reload 2번 → 콘솔에 안 나옴
|
||||
- **원인**: renderer 코드가 **3곳**에 존재: (1) `extension.ts`의 `generateApprovalObserverScript()` (소스), (2) `ag-sdk-variet-gravity-bridge.js` (배포됨, Reload시 소스에서 재생성), (3) `workbench-jetski-agent.html` inline (HTML, JS파일과 중복로드 방지됨). 직접 JS파일 패치는 Reload시 소스에서 재생성되어 **덮어씌워짐**
|
||||
- **해결**: 항상 `extension.ts`의 `generateApprovalObserverScript()` 함수를 수정 → 컴파일 → 배포 → Reload
|
||||
- **주의**: HTML inline은 JS파일이 먼저 로드되어 `window.__agSDK` 가드에 의해 실행 안 됨. 실제 실행되는 것은 JS파일 경로의 스크립트
|
||||
- **<2A>썝<EFBFBD>씤**: <20>꽕<EFBFBD>씠<EFBFBD>떚釉<EB969A> UI 踰꾪듉<EABEAA>쓽 `textContent` 異붿텧 <20>떆, Codicons <20>벑 <20>븘<EFBFBD>씠肄<EC94A0> <20>룿<EFBFBD>듃 臾몄옄<EBAA84>뿴(e.g., `渽<EFBFBD> Accept`)<29>씠 <20>븵遺<EBB8B5>遺꾩뿉 蹂묓빀(Gluing)<29>릺硫댁꽌, 湲곗〈<EAB397>쓽 `^` <20>빑而ㅺ<E8808C><E385BA> <20>룷<EFBFBD>븿<EFBFBD>맂 <20>젙洹쒖떇 留ㅼ묶(`/^(?:Always\s*)?Run/i`)<29>씠 <20>떎<EFBFBD>뙣<EFBFBD>븿.
|
||||
|
||||
### [2026-03-09] VS Code Accept — SDK 승인 명령이 AG에 미등록
|
||||
- **증상**: Discord 승인 → `antigravity.terminalCommand.run` 등 7개 명령 → 모두 `command not found`
|
||||
- **원인**: SDK(command-bridge.ts)에 정의된 7개 승인 명령이 현재 AG 빌드에 **등록되어 있지 않음**. 활성 시 72개, 세션 중 119개로 동적 등록되지만 승인 관련 명령은 없음
|
||||
- **검증**:
|
||||
- `HandleCascadeUserInteraction` RPC 3 variants → 모두 `socket hang up`
|
||||
- `ResolveOutstandingSteps` → `run state not found` (500 에러, 실제로는 CANCEL 동작)
|
||||
- `sendChatActionMessage`, `executeCascadeAction` → 119개 명령 중 미등록
|
||||
- 존재하는 approval-like 명령: `agentAcceptAllInFile` (코드 diff), `agentAcceptFocusedHunk` (hunk), `acceptCompletion` (자동완성) — 터미널 승인과 무관
|
||||
- **해결**: ~~Renderer DOM Click 구현됨 (미검증)~~ → **v1 검증 실패: webview iframe 격리 확인**. v3 `deepFindButtons()`로 업그레이드 (iframe contentDocument + webview.executeJavaScript + shadow DOM). AG 완전 재시작 후 DOM-DUMP로 접근 가능 여부 확인 필요
|
||||
- **주의**: `agentPanel.focus`도 미등록, `agentSidePanel.focus`만 존재
|
||||
- **<2A>빐寃<EBB990>**: `observer-script.ts`<60>쓽 <20>뒪罹<EB92AA>, Sibling 踰꾪듉 <20>닔吏<EB8B94>, Webview Trigger-click <20>벑 `textContent`瑜<> 異붿텧<EBB6BF>븯<EFBFBD>뒗 紐⑤뱺 DOM <20>씫湲<EC94AB> 援ш컙<D188>뿉 `txt.replace(/^[^a-zA-Z0-9]+/, '')` <20>쟾泥섎━瑜<E29481> <20>쟻<EFBFBD>슜<EFBFBD>븯<EFBFBD>뿬 <20>꽑<EFBFBD>뻾 湲고샇/<2F>븘<EFBFBD>씠肄섏쓣 <20>븞<EFBFBD>쟾<EFBFBD>븯寃<EBB8AF> <20>젣嫄<ECA0A3>.
|
||||
|
||||
- **二쇱쓽**: Native UI 而댄룷<EB8C84>꼳<EFBFBD>듃 <20>솚寃쎌뿉<EC8E8C>꽌<EFBFBD>뒗 <20>뀓<EFBFBD>뒪<EFBFBD>듃 <20>끂<EFBFBD>뱶肉먮쭔 <20>븘<EFBFBD>땲<EFBFBD>씪 <20>븘<EFBFBD>씠肄<EC94A0>/SVG 而댄룷<EB8C84>꼳<EFBFBD>듃<EFBFBD>쓽 <20>뀓<EFBFBD>뒪<EFBFBD>듃 湲<>猷⑥엵 <20>쁽<EFBFBD>긽<EFBFBD>쑝濡<EC919D> <20>씤<EFBFBD>빐 <20>뾼寃⑺븳 <20>떆<EFBFBD>옉<EFBFBD>젏(`^`) <20>젙洹쒖떇<EC9296>씠 源⑥쭏 <20>닔 <20>엳<EFBFBD>쑝誘<EC919D>濡<EFBFBD>, <20>빆<EFBFBD>긽 遺덊븘<EB8D8A>슂<EFBFBD>븳 <20>듅<EFBFBD>닔臾몄옄 <20>쟾泥섎━瑜<E29481> <20>꽑<EFBFBD>뻾<EFBFBD>빐<EFBFBD>빞 <20>븿.
|
||||
|
||||
|
||||
|
||||
### [2026-04-09] [Extension] Agent UI Native Migration & CodeLens False Positive Filter
|
||||
|
||||
- **利앹긽**: UI Tailwind/Native 留덉씠洹몃젅<EBAA83>씠<EFBFBD>뀡 <20>쟻<EFBFBD>슜 <20>썑, Discord 釉뚮┸吏<E294B8>濡<EFBFBD> <20>떊<EFBFBD>샇媛<EC8387> <20>쟾<EFBFBD><EC9FBE><EFBFBD> <20>쟾<EFBFBD>넚<EFBFBD>릺吏<EBA6BA> <20>븡<EFBFBD>쓬
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**: Agent <20>뙣<EFBFBD>꼸<EFBFBD>씠 <20>꺆/<2F>뿉<EFBFBD>뵒<EFBFBD>꽣 蹂몃Ц<EBAA83>뿉 吏곸젒 <20>젋<EFBFBD>뜑留곷릺硫댁꽌, 湲곗〈 <20>삤<EFBFBD>옉<EFBFBD>룞 諛⑹<E8AB9B><E291B9> 濡쒖쭅(`if (b.closest('.monaco-editor'))`)<29>뿉 <20>뙣<EFBFBD>꼸 <20>쟾泥<EC9FBE> 踰꾪듉<EABEAA>씠 <20>룷李⑸릺<E291B8>뼱 臾댁떆<EB8C81>맖
|
||||
|
||||
- **<2A>빐寃<EBB990>**: <20>꼫臾<EABCAB> 愿묐쾾<EBAC90>쐞<EFBFBD>븳 `.monaco-editor` 諛⑹뼱瑜<EBBCB1> <20>빐<EFBFBD>젣<EFBFBD>븯怨<EBB8AF>, 肄붾뱶 <20>젋利<ECA08B> 怨좎쑀 而⑦뀒<E291A6>씠<EFBFBD>꼫<EFBFBD>씤 `.codelens-decoration` <20>궡遺<EAB6A1><E981BA>씪 寃쎌슦<EC8E8C>뿉留<EBBF89> 臾댁떆<EB8C81>븯<EFBFBD>룄濡<EBA384> <20><><EFBFBD><EFBFBD>룷<EFBFBD>씤<EFBFBD>듃 <20>닔<EFBFBD>젙
|
||||
|
||||
- **二쇱쓽**: DOM <20>샃<EFBFBD><EC8383><EFBFBD>踰<EFBFBD> <20>븘<EFBFBD>꽣 議곌굔 <20>옉<EFBFBD>꽦 <20>떆 <20>옒<EFBFBD>띁 <20>겢<EFBFBD>옒<EFBFBD>뒪<EFBFBD>뒗 UI <20>뵒<EFBFBD>옄<EFBFBD>씤 媛쒗렪(Native, Editor Tab <20>벑 <20>쐞移<EC909E> 蹂<>寃<EFBFBD>)<29>뿉 留ㅼ슦 痍⑥빟<E291A5>븿. 媛<><E5AA9B>옣 援ъ껜<D18A>쟻<EFBFBD>씤 <20>궡遺<EAB6A1> <20>끂<EFBFBD>뱶 <20>겢<EFBFBD>옒<EFBFBD>뒪<EFBFBD>굹 <20><><EFBFBD>寃<EFBFBD> 怨좎쑀 <20>냽<EFBFBD>꽦<EFBFBD>쓣 <20>넻<EFBFBD>빐 <20>븘<EFBFBD>꽣留곹븷 寃<>
|
||||
|
||||
|
||||
|
||||
### [2026-03-31] [step-probe] GetAllCascadeTrajectories 10-Item Hard Limit (Signal Drop)
|
||||
|
||||
- **利앹긽**: `guitar_score` <20>벑<EFBFBD>뿉<EFBFBD>꽌 <20>솢<EFBFBD>꽦<EFBFBD>솕<EFBFBD>맂 <20>꽭<EFBFBD>뀡<EFBFBD>쓽 <20>뵒<EFBFBD>뒪肄붾뱶 <20>듅<EFBFBD>씤 <20>떊<EFBFBD>샇瑜<EC8387> "怨꾩냽<EABEA9>빐<EFBFBD>꽌" <20>옟吏<EC989F> 紐삵븿. (WS 60珥<30> <20><><EFBFBD><EFBFBD>엫<EFBFBD>븘<EFBFBD>썐蹂대떎 <20>뜑 移섎챸<EC848E>쟻<EFBFBD>쑝濡<EC919D> <20>떊<EFBFBD>샇媛<EC8387> <20>븘<EFBFBD>삁 媛<>吏<EFBFBD> <20>븡<EFBFBD>쓬)
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**: Extension<6F>씠 <20>솢<EFBFBD>꽦 <20>꽭<EFBFBD>뀡<EFBFBD>쓣 李얘린 <20>쐞<EFBFBD>빐 <20>샇異쒗븯<EC9297>뒗 `GetAllCascadeTrajectories` LS API媛<49> `{}`(鍮<> <20>씤<EFBFBD>옄)濡<> <20>샇異쒕맆 <20>븣, 湲곕낯<EAB395>쟻<EFBFBD>쑝濡<EC919D> **10媛쒖쓽 <20>꽭<EFBFBD>뀡留<EB80A1> 諛섑솚<EC8491>븯<EFBFBD>뒗 <20>븯<EFBFBD>뱶 由щ컠(Pagination Limit)**<2A>씠 嫄몃젮<EBAA83>엳<EFBFBD>쓬. <20>씠濡<EC94A0> <20>씤<EFBFBD>빐 <20>옉<EFBFBD>뾽 <20>궡<EFBFBD>뿭<EFBFBD>씠 <20>늻<EFBFBD>쟻<EFBFBD>릺硫<EBA6BA> <20>닔留롮<EFA78D><EBA1AE> 理쒖떊/吏꾪뻾 以<> <20>꽭<EFBFBD>뀡<EFBFBD>뱾<EFBFBD>씠 10媛<30> 紐⑸줉<E291B8>뿉<EFBFBD>꽌 諛<><E8AB9B>젮<EFBFBD>굹 <20>늻<EFBFBD>씫<EFBFBD>맖. <20>씡<EFBFBD>뒪<EFBFBD>뀗<EFBFBD>뀡<EFBFBD><EB80A1><EFBFBD> <20>꽭<EFBFBD>뀡<EFBFBD>씠 <20>뾾<EFBFBD>떎怨<EB968E> <20>뙋<EFBFBD>떒<EFBFBD>빐 媛뺤젣濡<ECA0A3> `IDLE` 紐⑤뱶<E291A4>뿉 吏꾩엯<EABEA9>븯硫<EBB8AF>, <20>듅<EFBFBD>씤 <20><><EFBFBD>湲곗뿴(WAITING) <20>옄泥대<EFA7A3><EB8C80> 寃<><E5AF83>궗<EFBFBD>븯吏<EBB8AF> <20>븡寃<EBB8A1> <20>맖.
|
||||
|
||||
- **<2A>빐寃<EBB990>** (v0.5.14): `v0.5.13`<60>뿉<EFBFBD>꽌 <20>룄<EFBFBD>엯<EFBFBD>뻽<EFBFBD>뜕 `{ limit: 100 }`<60>씠 LS <20>떒<EFBFBD>쓽 荑쇰━ 怨쇰<E680A8><EC87B0><EFBFBD>븯濡<EBB8AF> <20>씤<EFBFBD>븳 VS Code UI <20>봽由ъ쭠(DoS)<29>쓣 <20>쑀諛쒗븯<EC9297>뿬 濡ㅻ갚<E385BB>븯<EFBFBD>뒗 以<> <20>븘<EFBFBD>닔 <20>젙<EFBFBD>젹 <20>뙆<EFBFBD>씪誘명꽣(`descending: true`)源뚯<E6BA90><EB9AAF> <20>냼<EFBFBD>떎<EFBFBD>릺<EFBFBD>뿀<EFBFBD>뜕 <20>떎<EFBFBD>닔瑜<EB8B94> 援먯젙<EBA8AF>븿. 理쒖쥌<EC9296>쟻<EFBFBD>쑝濡<EC919D> `{ limit: 30, descending: true }`瑜<> <20>쟻<EFBFBD>슜<EFBFBD>븯<EFBFBD>뿬 <20>뙆<EFBFBD>떛 遺<><E981BA>븯 理쒖냼<EC9296>솕 諛<> 理쒖떊 <20>꽭<EFBFBD>뀡 理쒖긽<EC9296>떒(Index 0) 議고쉶瑜<EC89B6> <20>븞<EFBFBD>쟾<EFBFBD>븯寃<EBB8AF> 援ы쁽<D18B>븿.
|
||||
|
||||
- **二쇱쓽**: LS<4C>쓽 湲곕낯 SQLite/DB <20>쓳<EFBFBD>떟 Limit 洹쒖튃<EC9296>뿉 <20>쓽議댄븯<EB8C84>뿬 <20>쟾泥<EC9FBE> <20>뜲<EFBFBD>씠<EFBFBD>꽣 <20>뒪罹붿쓣 <20>닔<EFBFBD>뻾<EFBFBD>븯<EFBFBD>뒗 濡쒖쭅<EC9296><ECAD85><EFBFBD> <20>뼵<EFBFBD>젣<EFBFBD>뱺 Truncation <20>씠<EFBFBD>뒋(Data Loss)瑜<> <20>쑀諛쒗븷 <20>닔 <20>엳<EFBFBD>쓬.
|
||||
|
||||
|
||||
|
||||
### [2026-03-31] [WS] Browser API Fallback 60s Timeout (Zombie Connection)
|
||||
|
||||
- **利앹긽**: `guitar_score` <20>벑 紐⑤뱺 <20>옉<EFBFBD>뾽 <20>솚寃쎌뿉<EC8E8C>꽌 <20>빟 60珥덈쭏<EB8D88>떎 WebSocket <20>뿰寃곗씠 <20>걡湲곌퀬 <20>옱<EFBFBD>뿰寃곕릺<EAB395>뒗 <20>쁽<EFBFBD>긽<EFBFBD>씠 諛섎났<EC848E>릺硫<EBA6BA>(extension.log<6F>뿉 `Heartbeat timeout` 怨꾩냽 異쒕젰), 洹<> <20>궗<EFBFBD>씠 <20>뵒<EFBFBD>뒪肄붾뱶 <20>듅<EFBFBD>씤 <20>떊<EFBFBD>샇瑜<EC8387> <20>넃移<EB8483>.
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**: Extension<6F>씠 `ws` 紐⑤뱢 濡쒕뱶 <20>떎<EFBFBD>뙣(VS Code <20>솚寃<EC869A> <20>벑)濡<> <20>씤<EFBFBD>빐 釉뚮씪<EB9AAE>슦<EFBFBD><EC8AA6><EFBFBD> <20>궡<EFBFBD>옣 `WebSocket` 媛앹껜濡<EABB9C> Fallback <20>맖. 釉뚮씪<EB9AAE>슦<EFBFBD><EC8AA6><EFBFBD> WS<57>뒗 <20>꽌踰꾩쓽 <20>꽕<EFBFBD>씠<EFBFBD>떚釉<EB969A> ping<6E>쓣 諛쏆븘 pong<6E>쓣 <20>옄<EFBFBD>룞 <20>쓳<EFBFBD>떟<EFBFBD>븯吏<EBB8AF>留<EFBFBD> JS<4A>뿉 <20>씠踰ㅽ듃瑜<EB9383> <20>끂異쒗븯吏<EBB8AF> <20>븡<EFBFBD>쓬. <20>씠濡<EC94A0> <20>씤<EFBFBD>빐 `lastPongTime` 媛깆떊<EAB986>씠 遺덇<E981BA><EB8D87><EFBFBD>뒫<EFBFBD>빐<EFBFBD>졇, `Date.now() - lastPongTime > 60000` 議곌굔<EAB38C>씠 臾댁“嫄<E2809C> <20>넻怨쇰릺<EC87B0>뼱 硫<>姨≫븳 <20>뿰寃곗쓣 媛뺤젣 醫낅즺<EB8285>븿 (False Positive).
|
||||
|
||||
- **<2A>빐寃<EBB990>** (v0.5.12):
|
||||
|
||||
1. `hub.py`: `{"type": "heartbeat"}` JSON 硫붿떆吏<EB9686> <20>닔<EFBFBD>떊 <20>떆 紐낆떆<EB8286>쟻<EFBFBD>쑝濡<EC919D> `{"type": "pong"}` JSON<4F>쓣 <20>쓳<EFBFBD>떟<EFBFBD>븯<EFBFBD>룄濡<EBA384> <20>닔<EFBFBD>젙.
|
||||
|
||||
2. `ws-client.ts`: 紐낆떆<EB8286>쟻 `pong` <20>빖<EFBFBD>뱾<EFBFBD>윭 異붽<E795B0><EBB6BD>. JSON pong 吏<><EFA79E>썝 <20>꽌踰꾧굅<EABEA7>굹 Node.js ws瑜<73> <20>궗<EFBFBD>슜<EFBFBD>븷 <20>븣留<EBB8A3> 60珥<30> <20><><EFBFBD><EFBFBD>엫<EFBFBD>븘<EFBFBD>썐 寃<>利앹쓣 嫄곗튂<EAB397>룄濡<EBA384> 議곌굔 蹂닿컯 (`forceHeartbeatTimeoutIfNoPong`).
|
||||
|
||||
- **二쇱쓽**: 釉뚮씪<EB9AAE>슦<EFBFBD><EC8AA6><EFBFBD> <20>몴以<EBAAB4> WebSockets(W3C)<29>뒗 ping/pong <20>젣<EFBFBD>뼱 <20>봽<EFBFBD>젅<EFBFBD>엫<EFBFBD>쓣 JS濡<53> <20>끂異쒗븯吏<EBB8AF> <20>븡<EFBFBD>쓬. <20>뤃由ы븘/<2F>겕濡쒖뒪<EC9296>뵆<EFBFBD>옯<EFBFBD>뤌 WS <20>옒<EFBFBD>띁 <20>궗<EFBFBD>슜 <20>떆 <20>븯<EFBFBD>듃鍮꾪듃<EABEAA>뒗 諛섎뱶<EC848E>떆 JSON 硫붿꽭吏<EABDAD> <20>삎<EFBFBD>깭<EFBFBD>쓽 Application Layer Ping/Pong<6E>쑝濡<EC919D> <20><><EFBFBD><EFBFBD>뼱<EFBFBD>궡嫄곕굹, Native WS API <20>뿬遺<EBBFAC>瑜<EFBFBD> <20>솗<EFBFBD>떎<EFBFBD>엳 泥댄겕<EB8C84>빐<EFBFBD>빞 <20>븿.
|
||||
|
||||
|
||||
|
||||
### [2026-03-28] [step-probe] GetCascadeTrajectorySteps UTF-8 <20>뿉<EFBFBD>윭 臾댄븳 猷⑦봽
|
||||
|
||||
- **利앹긽**: `guitar_score` <20>봽濡쒖젥<EC9296>듃<EFBFBD>뿉<EFBFBD>꽌 `[STEP-PROBE] error: ...invalid UTF-8` <20>뿉<EFBFBD>윭媛<EC9CAD> 5珥덈쭏<EB8D88>떎 諛섎났<EC848E>릺硫<EBA6BA> Discord <20>듅<EFBFBD>씤 <20>떊<EFBFBD>샇媛<EC8387> <20>쟾<EFBFBD>떖<EFBFBD>릺吏<EBA6BA> <20>븡<EFBFBD>쓬.
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**: AG LS <20>꽌踰꾩뿉<EABEA9>꽌 <20>듅<EFBFBD>젙 step<65>쓽 `CortexStepEphemeralMessage.content`<60>뿉 諛붿씠<EBB6BF>꼫由<EABCAB> <20>뜲<EFBFBD>씠<EFBFBD>꽣(<28>씠誘몄<E8AA98><EBAA84> <20>벑) <20>룷<EFBFBD>븿 <20>넂 proto UTF-8 吏곷젹<EAB3B7>솕 500 <20>뿉<EFBFBD>윭. `catch(e)` 釉붾줉<EBB6BE>뿉<EFBFBD>꽌 `stallProbed=true`瑜<> <20>꽕<EFBFBD>젙<EFBFBD>븯吏<EBB8AF> <20>븡<EFBFBD>븘 `!ctx.stallProbed` 議곌굔<EAB38C>씠 <20>빆<EFBFBD>긽 true <20>넂 5珥덈쭏<EB8D88>떎 <20>룞<EFBFBD>씪 <20>슂泥<EC8A82> 臾댄븳 <20>옱<EFBFBD>떆<EFBFBD>룄.
|
||||
|
||||
- **<2A>빐寃<EBB990>** (v0.5.11): `catch` 釉붾줉<EBB6BE>뿉<EFBFBD>꽌 UTF-8 <20>뿉<EFBFBD>윭 媛먯<E5AA9B><EBA8AF> <20>떆 `stepOffset=currentCount-20`<60>쑝濡<EC919D> fallback <20>슂泥<EC8A82>. offset<65>룄 <20>떎<EFBFBD>뙣 <20>떆 `stallProbed=true` <20>꽕<EFBFBD>젙<EFBFBD>븯<EFBFBD>뿬 猷⑦봽 李⑤떒. `delta>0` <20>씠踰ㅽ듃 諛쒖깮 <20>떆 L433<33>뿉<EFBFBD>꽌 <20>옄<EFBFBD>룞 由ъ뀑.
|
||||
|
||||
- **二쇱쓽**: `stallProbed=true`<60>뒗 <20>쁺援<EC81BA> Lock<63>씠 <20>븘<EFBFBD>떂 <20><><EFBFBD> `delta>0` <20>떆 <20>옄<EFBFBD>룞 由ъ뀑. UTF-8 <20>뿉<EFBFBD>윭<EFBFBD>뒗 AG <20>꽌踰<EABD8C> 痢<> 臾몄젣(<28>씠誘몄<E8AA98><EBAA84>/諛붿씠<EBB6BF>꼫由<EABCAB> <20>뜲<EFBFBD>씠<EFBFBD>꽣媛<EABDA3> ephemeral message<67>뿉 <20>룷<EFBFBD>븿)<29>씠誘<EC94A0>濡<EFBFBD> Extension<6F>뿉<EFBFBD>꽌 graceful fallback留<6B> 泥섎━.
|
||||
|
||||
|
||||
|
||||
### [2026-03-28] [approval-handler] stepIndex 誘명솗<EBAA85>젙 <20>떆 wrong-stepIndex RPC <20>궘鍮<EAB698>
|
||||
|
||||
- **利앹긽**: DOM observer 寃쎈줈濡<ECA488> `terminal_command` pending <20>깮<EFBFBD>꽦 <20>썑 Discord <20>듅<EFBFBD>씤 <20>떆 `HandleCascadeUserInteraction(stepIndex=0)` <20>넂 `"input not registered for step 0"` <20>넂 LS reconnect <20>넂 <20>옱<EFBFBD>떆<EFBFBD>룄 <20>넂 DOM click fallback<63>쑝濡<EC919D> <20><><EFBFBD><EFBFBD>븯. (wrong-LS<4C><53><EFBFBD> <20>룞<EFBFBD>씪<EFBFBD>븳 利앹긽<EC95B9>씠<EFBFBD>굹 <20>떎瑜<EB968E> <20>썝<EFBFBD>씤)
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**: `ctx.lastPendingStepIndex=-1` (step-probe媛<65> UTF-8 <20>뿉<EFBFBD>윭濡<EC9CAD> WAITING 誘멸컧吏<ECBBA7>)<29>엫<EFBFBD>뿉<EFBFBD>룄 `Math.max(0, -1)=0`<60>쑝濡<EC919D> clamp<6D>릺<EFBFBD>뼱 議댁옱<EB8C81>븯吏<EBB8AF> <20>븡<EFBFBD>뒗 step 0<>뿉 RPC <20>쟾<EFBFBD>넚.
|
||||
|
||||
- **<2A>빐寃<EBB990>** (v0.5.11): `effectiveStepIndex = stepIndex >= 0 ? stepIndex : (lastPendingStepIndex >= 0 ? lastPendingStepIndex : -1)`. `effectiveStepIndex < 0`<60>씠硫<EC94A0> RPC 釉붾줉 <20>쟾泥<EC9FBE> skip <20>넂 DOM click 吏곹뻾 (湲곗〈怨<E38088> <20>룞<EFBFBD>옉 <20>룞<EFBFBD>씪, LS reconnect <20>궘鍮<EAB698> <20>젣嫄<ECA0A3>).
|
||||
|
||||
- **二쇱쓽**: 湲곗〈 洹쒖튃 #14(`uint32`<60>뿉 <20>쓬<EFBFBD>닔 湲덉<E6B9B2><EB8D89>)<29><><EFBFBD> 異⑸룎泥섎읆 蹂댁씠<EB8C81>굹, `effectiveStepIndex=-1`<60>씪 <20>븣 RPC <20>옄泥대<EFA7A3><EB8C80> **<2A>쟾<EFBFBD>넚<EFBFBD>븯吏<EBB8AF> <20>븡<EFBFBD>쑝誘<EC919D>濡<EFBFBD>** <20>쐞諛<EC909E> <20>븘<EFBFBD>떂. RPC <20>쟾<EFBFBD>넚 <20>떆<EFBFBD>뿉<EFBFBD>뒗 <20>뿬<EFBFBD>쟾<EFBFBD>엳 <20>쑀<EFBFBD>슚<EFBFBD>븳 stepIndex留<78> <20>궗<EFBFBD>슜.
|
||||
|
||||
|
||||
|
||||
### [2026-03-25] [Architecture] Discord Signal Drop & Extension Freezes
|
||||
|
||||
- **利앹긽**: <20>옣<EFBFBD>떆媛<EB9686> <20>옄由щ퉬<D189><ED89AC><EFBFBD> <20>썑 蹂듦<E8B982><EB93A6> <20>떆 Discord濡<64> <20>듅<EFBFBD>씤 <20>떊<EFBFBD>샇媛<EC8387> <20>삤吏<EC82A4> <20>븡嫄곕굹 VS Code UI媛<49> 媛꾪뿉<EABEAA>쟻/吏<><EFA79E>냽<EFBFBD>쟻<EFBFBD>쑝濡<EC919D> 硫덉땄(Freeze).
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**:
|
||||
|
||||
1. `ws.onerror` 諛쒖깮 <20>썑 `onclose` <20>늻<EFBFBD>씫 <20>떆 <20>옱<EFBFBD>뿰寃<EBBFB0> 肄쒕갚 <20>샇異쒖씠 <20>씠猷⑥뼱吏<EBBCB1>吏<EFBFBD> <20>븡<EFBFBD>븘 臾댄븳 <20><><EFBFBD>湲<EFBFBD> (<28>옣<EFBFBD>떆媛<EB9686> 留덈퉬)
|
||||
|
||||
2. `ws-client` <20>옱<EFBFBD>뿰寃<EBBFB0> <20>떆 <20>늻<EFBFBD>쟻<EFBFBD>맂 200媛<30> <20>걧瑜<EAB1A7> <20>룞湲곗떇 burst <20>쟾<EFBFBD>넚<EFBFBD>븯<EFBFBD>뿬 Hub<75>쓽 <20>냽<EFBFBD>룄 <20>젣<EFBFBD>븳(60媛<30>/10珥<30>)<29>뿉 嫄몃젮 <20>솗<EFBFBD>젙 <20>쁺援<EC81BA> <20>궘<EFBFBD>젣<EFBFBD>맖
|
||||
|
||||
3. 濡쒖뺄 釉뚮┸吏<E294B8> `http-bridge.ts`<60>쓽 怨쇨굅 <20>쑀<EFBFBD>궛<EFBFBD>씤 `FALSE_POSITIVE_RE` <20>젙洹쒖떇<EC9296>씠 AI 怨좎쑀 踰꾪듉(Allow, Deny, Accept) 留덉<EFA78D><EB8D89> <20>븘<EFBFBD>꽣留곹븯<EAB3B9>뿬 Discord <20>쟾<EFBFBD>넚 <20>썝泥<EC8D9D> 李⑤떒
|
||||
|
||||
4. `step-probe.ts` <20>뤃留<EBA483> 猷⑦봽 <20>궡 <20>룞湲곗떇 <20>뙆<EFBFBD>씪 I/O <20>궗<EFBFBD>슜<EFBFBD>쑝濡<EC919D> <20>씤<EFBFBD>븳 <20>봽由ъ쫰
|
||||
|
||||
- **<2A>빐寃<EBB990>** (v0.5.10): ws-client<6E>뿉 <20>븯<EFBFBD>뱶 <20><><EFBFBD><EFBFBD>엫<EFBFBD>븘<EFBFBD>썐 諛<> 50ms Paced-flush <20>쟻<EFBFBD>슜, http-bridge<67>쓽 <20>젙洹쒖떇 湲곕뒫 <20>셿<EFBFBD>솕, step-probe 鍮꾨룞湲<EBA39E> I/O <20>쟾<EFBFBD>솚 泥댁젣 <20>쟻<EFBFBD>슜, observer-script<70>쓽 <20>븘<EFBFBD>꽣<EFBFBD>맂 <20>떊<EFBFBD>샇 臾댄븳 HTTP <20>뤃留<EBA483> 諛⑹뼱 肄붾뱶 諛섏쁺.
|
||||
|
||||
- **二쇱쓽**: Extension <20>궡遺<EAB6A1> 濡쒖쭅 踰꾧렇<EABEA7><EBA087><EFBFBD><EFBFBD>쑝誘<EC919D>濡<EFBFBD> Hub(Python) 肄붾뱶<EBB6BE>뒗 嫄대뱶由ъ<E794B1><D18A> <20>븡<EFBFBD>쓬. Hub <20>냽<EFBFBD>룄 <20>젣<EFBFBD>븳<EFBFBD><EBB8B3><EFBFBD> <20>젙<EFBFBD>긽 諛⑹뼱 湲곗젣<EAB397>씠誘<EC94A0>濡<EFBFBD> <20>겢<EFBFBD>씪<EFBFBD>씠<EFBFBD>뼵<EFBFBD>듃 <20>떒<EFBFBD>쓽 Pacing<6E>씠 <20>삱諛붾Ⅸ 諛⑺뼢<E291BA>엫.
|
||||
|
||||
### [2026-03-24] DOM Observer /trigger-click <20>젋<EFBFBD>뜑留<EB9C91> <20>닚<EFBFBD>꽌 <20>삤<EFBFBD>옉<EFBFBD>룞 諛<> False Positive <20>봽由ъ쭠
|
||||
|
||||
- **利앹긽**: v0.5.9 <20>뙣移<EB99A3> <20>씠<EFBFBD>썑 肄붾뵫 <20>떆 Agent <20>솕硫댁씠 <20>걡<EFBFBD>엫<EFBFBD>뾾<EFBFBD>씠 <20>꽌紐<EABD8C> <20><><EFBFBD>湲<EFBFBD>(Pending) <20>긽<EFBFBD>깭濡<EAB9AD> 硫덉땄. <20>삉<EFBFBD>뒗 <20>뵒<EFBFBD>뒪肄붾뱶<EBB6BE>뿉<EFBFBD>꽌 `Approve` <20>떆 <20>뿉<EFBFBD>뵒<EFBFBD>꽣 <20>궡<EFBFBD>쓽 <20>뿁<EFBFBD>슧<EFBFBD>븳 `Run Test`(肄붾뱶 <20>젋利<ECA08B>)瑜<> <20>겢由<EAB2A2><E794B1>븿.
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**: <20>뀓<EFBFBD>뒪<EFBFBD>듃<EFBFBD><EB9383><EFBFBD> <20>젙洹쒖떇(`/^Run/i` <20>벑)<29>뿉留<EBBF89> <20>쓽議댄븯<EB8C84>뿬 `querySelectorAll`<60>쓣 <20>닔<EFBFBD>뻾<EFBFBD>븷 寃쎌슦, DOM <20>듃由ъ뿉 <20>젋<EFBFBD>뜑留곷맂 <20>닔留롮<EFA78D><EBA1AE> VS Code <20>꽕<EFBFBD>씠<EFBFBD>떚釉<EB969A> 肄붾뱶 <20>젋利<ECA08B> 踰꾪듉<EABEAA>쓣 Agent 踰꾪듉蹂대떎 癒쇱<E79992><EC87B1> 李얠븘踰꾨━<EABEA8>뒗 諛쒖깮 <20>쐞移<EC909E>(Context)<29>쓽 <20>븳怨꾩젏.
|
||||
|
||||
- **<2A>빐寃<EBB990>** (v0.5.10):
|
||||
|
||||
1. 媛먯<E5AA9B><EBA8AF>(Scan): `isVSCodeMainWindow` 諛<> <20>깘<EFBFBD>깋 <20>끂<EFBFBD>뱶 `isBodyRoot` <20>솗<EFBFBD>씤<EFBFBD>쓣 <20>넻<EFBFBD>빐, <20>뿉<EFBFBD>뵒<EFBFBD>꽣 蹂몃Ц <20>쁺<EFBFBD>뿭<EFBFBD>뿉<EFBFBD>꽌<EFBFBD>뒗 "Run", "Approve" 媛먯<E5AA9B><EBA8AF>瑜<EFBFBD> <20>썝泥<EC8D9D> <20>젣嫄<ECA0A3> (<28>삤吏<EC82A4> <20>뙣<EFBFBD>꼸 <20>궡濡<EAB6A1> <20>븳<EFBFBD>젙).
|
||||
|
||||
2. <20>겢由<EAB2A2>(Trigger-click): `deepFindButtons()` <20>궡<EFBFBD>뿉<EFBFBD>꽌 `findPanel()`(<28>뿉<EFBFBD>씠<EFBFBD>쟾<EFBFBD>듃 <20>뙣<EFBFBD>꼸) -> <20>븣由<EBB8A3> Toasts -> Document 蹂몃Ц <20>닚<EFBFBD>쑝濡<EC919D> <20>깘<EFBFBD>깋 **<2A>슦<EFBFBD>꽑<EFBFBD>닚<EFBFBD>쐞(Priority)**瑜<> 媛뺤젣 <20>쟻<EFBFBD>슜.
|
||||
|
||||
- **二쇱쓽**: 踰꾪듉 <20>씠踰ㅽ듃 <20>썑<EFBFBD>궧 <20>떆 <20>뀓<EFBFBD>뒪<EFBFBD>듃 留ㅼ묶<E385BC>뿉留<EBBF89> <20>쓽議댄븯吏<EBB8AF> 留먭퀬, 諛섎뱶<EC848E>떆 DOM <20>깘<EFBFBD>깋 <20>슦<EFBFBD>꽑<EFBFBD>닚<EFBFBD>쐞<EFBFBD><EC909E><EFBFBD> 而⑦뀓<E291A6>뒪<EFBFBD>듃 踰붿쐞瑜<EC909E> <20>븿猿<EBB8BF> <20>븘<EFBFBD>꽣留곹븯<EAB3B9>뿬 False Positive瑜<65> 李⑤떒<E291A4>븷 寃<>.
|
||||
|
||||
|
||||
|
||||
### [2026-03-24] DOM Observer <20><><EFBFBD> VS Code Native UI Blind Spot
|
||||
|
||||
- **利앹긽**: "Always Allow" 諛<> <20>씪諛<EC94AA> "Allow Alt+<2B>넻" 沅뚰븳 <20>븣由<EBB8A3> 踰꾪듉<EABEAA>씠 <20>뵒<EFBFBD>뒪肄붾뱶 沅뚰븳 <20>꽱<EFBFBD>떛<EFBFBD>뿉<EFBFBD>꽌 <20>셿<EFBFBD>쟾<EFBFBD>엳 <20>늻<EFBFBD>씫<EFBFBD>맖.
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**: VS Code <20>꽕<EFBFBD>씠<EFBFBD>떚釉<EB969A> <20>븣由<EBB8A3> 諛<> 梨꾪똿 <20>뙣<EFBFBD>꼸 <20>궡<EFBFBD>쓽 踰꾪듉<EABEAA><EB9389><EFBFBD> `<button>` <20>깭洹<EAB9AD> <20><><EFBFBD><EFBFBD>떊 `<a role="button">`, `<vscode-button>` <20>벑<EFBFBD>쓣 <20>궗<EFBFBD>슜<EFBFBD>븯<EFBFBD>뒗<EFBFBD>뜲, 湲곗〈 DOM scan 濡쒖쭅<EC9296>씠 `querySelectorAll('button')`<60>쑝濡<EC919D> <20>븯<EFBFBD>뱶肄붾뵫<EBB6BE>릺<EFBFBD>뼱 <20>끂<EFBFBD>뱶瑜<EBB1B6> <20>븘<EFBFBD>삁 李얠<EFA7A1><EC96A0> 紐삵븿. (異붽<E795B0><EBB6BD>濡<EFBFBD> Always Allow <20>젙洹쒖떇 <20>늻<EFBFBD>씫)
|
||||
|
||||
- **<2A>빐寃<EBB990>** (v0.5.9): DOM scan, 由ъ뒯 <20>썒 <20>벑 紐⑤뱺 <20>깘<EFBFBD>깋 濡쒖쭅 <20><><EFBFBD><EFBFBD>젆<EFBFBD>꽣瑜<EABDA3> `button, [role="button"], vscode-button, .monaco-text-button` <20>쑝濡<EC919D> <20>쟾硫<EC9FBE> 媛쒗렪. <20>젙洹쒖떇<EC9296>쓣 `/^(?:Always )?Allow/i`濡<> <20>닔<EFBFBD>젙.
|
||||
|
||||
|
||||
|
||||
### [2026-03-24] Python Hub <20><><EFBFBD> 醫<>鍮<EFBFBD> 而ㅻ꽖<E385BB>뀡 諛<> UI <20>봽由ъ쭠
|
||||
|
||||
- **利앹긽**: `npm run` 紐낅졊<EB8285>씠 `<EFBFBD>떎<EFBFBD>뻾 <20>젙梨<ECA099>` 愿<><E684BF>젴 <20>삤瑜섎줈 <20>떎<EFBFBD>뙣
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**: PowerShell <20>뒪<EFBFBD>겕由쏀듃 <20>떎<EFBFBD>뻾 <20>젙梨낆씠 <20>젣<EFBFBD>븳<EFBFBD>쟻
|
||||
|
||||
- **<2A>빐寃<EBB990>**: `cmd /c npm run dev` <20>삎<EFBFBD>떇<EFBFBD>쑝濡<EC919D> cmd瑜<64> <20>넻<EFBFBD>빐 <20>떎<EFBFBD>뻾
|
||||
|
||||
- **二쇱쓽**: npm 愿<><E684BF>젴 紐낅졊<EB8285><ECA18A><EFBFBD> <20>빆<EFBFBD>긽 `cmd /c` <20>젒<EFBFBD>몢<EFBFBD>뼱 <20>궗<EFBFBD>슜 沅뚯옣
|
||||
|
||||
|
||||
|
||||
### [2026-03-08] PowerShell curl <20><><EFBFBD> Invoke-WebRequest 異⑸룎
|
||||
|
||||
- **利앹긽**: `curl` 紐낅졊<EB8285>씠 <20>삁<EFBFBD>긽怨<EAB8BD> <20>떎瑜<EB968E> <20>쓳<EFBFBD>떟 <20>삎<EFBFBD>떇<EFBFBD>쓣 諛섑솚
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**: PowerShell<6C>뿉<EFBFBD>꽌 `curl`<60><><EFBFBD> `Invoke-WebRequest`<60>쓽 蹂꾩묶
|
||||
|
||||
- **<2A>빐寃<EBB990>**: **`curl.exe`**瑜<> 紐낆떆<EB8286>쟻<EFBFBD>쑝濡<EC919D> <20>궗<EFBFBD>슜
|
||||
|
||||
- **二쇱쓽**: HTTP 愿<><E684BF>젴 紐⑤뱺 紐낅졊<EB8285>뿉<EFBFBD>꽌 `curl.exe` <20>궗<EFBFBD>슜 <20>븘<EFBFBD>닔
|
||||
|
||||
### [2026-03-09] Renderer DOM — webview iframe 격리 확인 + v3 deep traversal
|
||||
- **증상**: Renderer trigger-click이 `document.querySelectorAll('button')`으로 버튼 검색 → Run 버튼 미발견. 감지된 것은 외부 DOM의 trust-level 버튼(`RunAlt+?`)뿐
|
||||
- **원인**: Run/Accept 버튼은 AG 채팅 webview iframe (`vscode-webview://` origin) 안에 렌더링. 외부 workbench DOM (`vscode-file://` origin)에서 cross-origin으로 접근 불가
|
||||
- **해결**: Renderer v3 `deepFindButtons()` 구현:
|
||||
1. Main document 검색 (기존)
|
||||
2. `iframe.contentDocument` 접근 시도 (same-origin이면 성공)
|
||||
3. `<webview>.executeJavaScript()` 접근 시도 (Electron API)
|
||||
4. Shadow DOM 재귀 탐색
|
||||
→ **미검증** (AG 재시작 후 DOM-DUMP 결과 필요)
|
||||
- **주의**: CDP(Chrome DevTools Protocol)는 **사용자 결정에 의해 명시적으로 거부됨** (`--remote-debugging-port` 필요, 비표준 접근). 표준 DOM API/Electron API 범위 내에서만 해결할 것
|
||||
|
||||
### [2026-03-09] Deep Inspect HTTP Endpoint — curl로 DOM 분석 트리거
|
||||
- **증상**: AG DevTools 콘솔에 붙여넣기 불가 (Chromium 보안 정책)
|
||||
- **원인**: Electron 렌더러 DevTools에서 `allow pasting`이 SyntaxError 발생 (`Unexpected identifier 'pasting'`)
|
||||
- **해결**: Extension HTTP bridge에 `/deep-inspect` 엔드포인트 추가. `curl.exe http://127.0.0.1:34332/deep-inspect`로 bridge→renderer→재귀 DOM 분석→결과 JSON 반환. 결과는 `~/.gemini/antigravity/bridge/deep-inspect-result.json`에도 저장됨
|
||||
- **주의**: 시작 3초 후 자동 실행 + curl로 재트리거 가능. renderer가 2초마다 `/deep-inspect-trigger` 폴링
|
||||
|
||||
---
|
||||
|
||||
### [2026-03-09] workbench.html inline 스크립트 미삽입 — jetski만 패치한 버그
|
||||
- **증상**: AG 재시작 후 `/deep-inspect` timeout — renderer v3 스크립트 미로딩
|
||||
- **원인**: `setupApprovalObserver()`가 `workbench-jetski-agent.html`에만 inline 삽입하고, `workbench.html`에는 외부 `<script src>`만 추가. `vscode-file://` 프로토콜은 커스텀 .js 파일을 서빙하지 않으므로 사실상 미로딩
|
||||
- **해결**: HTML 패치 로직을 `htmlFiles = ['workbench.html', 'workbench-jetski-agent.html']` 루프로 변경하여 **양쪽 모두 inline** 삽입. 수동 pre-patch 스크립트(`/tmp/patch_workbench.py`)로 즉시 적용 + product.json 체크섬 업데이트
|
||||
- **주의**: **항상 양쪽 HTML을 동일하게 패치**. AG가 어느 HTML을 로드할지 런타임까지 알 수 없음. pre-patch 후 1회 풀 재시작이면 충분 (체크섬 사전 업데이트 완료)
|
||||
|
||||
### [2026-03-09] V8 CachedData — 체크섬 정상이어도 스크립트 미실행
|
||||
- **증상**: HTML 패치 + product.json 체크섬 일치 + Bridge 서버 정상 → 그런데도 renderer 스크립트 미실행 (`/deep-inspect` timeout)
|
||||
- **원인**: AG 프로세스 인자 `--code-cache-schemes=vscode-webview,vscode-file`에 의해 V8 바이트코드 캐시가 `vscode-file://` 프로토콜에도 적용. product.json 체크섬 검증과 V8 코드 캐시는 **별도 메커니즘** — 체크섬이 맞아도 V8은 `%APPDATA%\Antigravity\CachedData`의 이전 컴파일된 바이트코드를 재사용
|
||||
- **해결**: `%APPDATA%\Antigravity\CachedData\*` 전체 삭제 후 AG 풀 재시작. AG 실행 중에도 삭제 가능 (파일 잠금 없음 확인됨)
|
||||
- **주의**: CachedData 삭제 후 첫 시작이 약간 느릴 수 있음 (V8이 모든 JS를 재컴파일). **HTML 패치 변경 시마다 CachedData 삭제 필수**. `product.json` 체크섬 업데이트만으로는 불충분
|
||||
|
||||
## 誘명빐寃<EBB990> <20>씠<EFBFBD>뒋
|
||||
|
||||
|
||||
|
||||
### [2026-03-23/24] <20>룊<EFBFBD>깮 吏<><EFA79E>냽<EFBFBD>릺<EFBFBD>뒗 WebSocket 醫<>鍮<EFBFBD> 而ㅻ꽖<E385BB>뀡 諛<> False Positive 媛뺤젣 <20>뿰寃<EBBFB0> <20>걡源<EAB1A1> (v0.5.5 <20>넂 0.5.8)
|
||||
|
||||
- **利앹긽**:
|
||||
|
||||
1. (v0.5.5) <20>젅<EFBFBD>쟾 紐⑤뱶 蹂듦뎄 <20>떆 <20>떎<EFBFBD>뿰寃곗씠 <20>걡<EFBFBD>뼱議뚯쓬<EB9AAF>뿉<EFBFBD>룄 <20>솗<EFBFBD>옣<EFBFBD>씠 <20>씠瑜<EC94A0> <20>씤吏<EC94A4><EFA79E>븯吏<EBB8AF> 紐삵븯<EC82B5>뒗 醫<>鍮<EFBFBD>(Half-open) <20>냼耳<EB83BC> 諛쒖깮.
|
||||
|
||||
2. (v0.5.6) 醫<>鍮<EFBFBD> <20>냼耳볦쓣 <20>옟湲<EC989F> <20>쐞<EFBFBD>빐 10珥<30> <20><><EFBFBD><EFBFBD>씠癒<EC94A0>(`pongTimeoutTimer`)瑜<> <20>꽔<EFBFBD>뿀<EFBFBD>쑝<EFBFBD>굹, VS Code<64>쓽 臾닿굅<EB8BBF>슫 <20>뙆<EFBFBD>씪 寃<><E5AF83>깋 <20>떆 Event Loop媛<70> 釉붾줈<EBB6BE>궧<EFBFBD>릺硫<EBA6BA> 硫<>姨≫븳 <20>뿰寃곗씤<EAB397>뜲<EFBFBD>룄 <20>뿀<EFBFBD>쐞 <20><><EFBFBD><EFBFBD>엫<EFBFBD>븘<EFBFBD>썐(False Positive) <20>뙋<EFBFBD>젙<EFBFBD>쑝濡<EC919D> <20>뿰寃곗쓣 媛뺤젣 醫낅즺<EB8285>븿. <20>씠濡<EC94A0> <20>씤<EFBFBD>빐 <20>늻<EFBFBD>쟻<EFBFBD>맂 <20>옱<EFBFBD>뿰寃<EBBFB0> <20>뵜<EFBFBD>젅<EFBFBD>씠(Exponential Backoff)媛<> 60珥덇퉴吏<ED89B4> <20>뒛<EFBFBD>뼱<EFBFBD>굹硫댁꽌 <20>솗<EFBFBD>옣<EFBFBD>씠 <20>떖媛곹븯寃<EBB8AF> 硫덉땄(Freeze).
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**: Node.js `ws` <20>씪<EFBFBD>씠釉뚮윭由ъ쓽 `ws.ping()`<60><><EFBFBD> 鍮꾨룞湲<EBA39E> I/O <20>꽕<EFBFBD>듃<EFBFBD>썙<EFBFBD>겕 <20>걧瑜<EAB1A7> <20><><EFBFBD>吏<EFBFBD>留<EFBFBD>, `setTimeout(..., 10000)` <20><><EFBFBD><EFBFBD>엫<EFBFBD>븘<EFBFBD>썐<EFBFBD><EC8D90><EFBFBD> Event Loop 釉붾줈<EBB6BE>궧 <20>빐<EFBFBD>젣 吏곹썑 怨㏓컮濡<ECBBAE> 留뚮즺<EB9AAE>릺<EFBFBD>뼱 踰꾨┝. <20>뵲<EFBFBD>씪<EFBFBD>꽌 <20>꽕<EFBFBD>듃<EFBFBD>썙<EFBFBD>겕 I/O <20>쓳<EFBFBD>떟(pong)蹂대떎 濡쒖뺄 <20><><EFBFBD><EFBFBD>씠癒멸<E79992><EBA9B8> 癒쇱<E79992><EC87B1> <20>꽣<EFBFBD>졇<EFBFBD>꽌 <20>젙<EFBFBD>긽<EFBFBD>쟻<EFBFBD>씤 <20>냼耳볦쓣 二쎌엫.
|
||||
|
||||
- **<2A>빐寃<EBB990>** (v0.5.8 <20>셿<EFBFBD>꽦):
|
||||
|
||||
- <20>쐞<EFBFBD>뿕<EFBFBD>븳 `setTimeout` 諛⑹떇 <20>룓湲<EBA393>.
|
||||
|
||||
- 湲곗〈<EAB397>쓽 25珥<35> 二쇨린 `setInterval` <20>븯<EFBFBD>듃鍮꾪듃 猷⑦봽 <20>궡遺<EAB6A1><E981BA>뿉 `Date.now() - lastPongTime > 60000` (60珥<30> 珥덇낵 <20>떆 <20><><EFBFBD><EFBFBD>엫<EFBFBD>븘<EFBFBD>썐) 寃<>利<EFBFBD> 濡쒖쭅<EC9296>쓣 <20>룄<EFBFBD>엯.
|
||||
|
||||
- 留뚯빟 Event Loop媛<70> <20>닔<EFBFBD>떗 珥<> 諛<>由щ뜑<D189>씪<EFBFBD>룄, 釉붾줈<EBB6BE>궧 <20>빐<EFBFBD>젣 <20>썑 <20>걧<EFBFBD>맂 I/O <20>씠踰ㅽ듃(`pong`)媛<> `setInterval` <20><><EFBFBD><EFBFBD>씠癒<EC94A0> 肄쒕갚 <20>씠<EFBFBD>쟾<EFBFBD>뿉 癒쇱<E79992><EC87B1> 泥섎━<EC848E>릺嫄곕굹(Node.js Phase 洹쒖튃), <20>쟻<EFBFBD>뼱<EFBFBD>룄 60珥덈씪<EB8D88>뒗 踰꾪띁 <20>뜒遺꾩뿉 **False Positive 媛<><E5AA9B>뒫<EFBFBD>꽦<EFBFBD>쓣 <20>썝泥<EC8D9D> 李⑤떒**<2A>븿怨<EBB8BF> <20>룞<EFBFBD>떆<EFBFBD>뿉 醫<>鍮<EFBFBD> <20>냼耳볦쓣 <20>븞<EFBFBD>젙<EFBFBD>쟻<EFBFBD>쑝濡<EC919D> <20>젣嫄고븿.
|
||||
|
||||
- **二쇱쓽**: Node.js<6A>쓽 <20>떒<EFBFBD>씪 <20>뒪<EFBFBD>젅<EFBFBD>뱶 Event Loop <20>솚寃<EC869A>(<28>듅<EFBFBD>엳 臾닿굅<EB8BBF>슫 <20>룞湲<EBA39E> <20>옉<EFBFBD>뾽<EFBFBD>씠 <20>옦<EFBFBD><EC98A6><EFBFBD> VS Code Extension)<29>뿉<EFBFBD>꽌 <20>꽕<EFBFBD>듃<EFBFBD>썙<EFBFBD>겕 I/O瑜<4F> 濡쒖뺄 `setTimeout`怨<> 寃쎌<(Race)<29>떆<EFBFBD>궎<EFBFBD>뒗 <20>꽕怨꾨뒗 <20>븘<EFBFBD>뿰<EFBFBD>쟻<EFBFBD>쑝濡<EC919D> False Positive瑜<65> <20>궠<EFBFBD>쓬. Timestamp(`Date.now()`) 湲곕컲 媛꾧꺽 寃<>利<EFBFBD>(Interval check)<29>씠 <20>썾<EFBFBD>뵮 <20>븞<EFBFBD>쟾<EFBFBD>븿.
|
||||
|
||||
|
||||
|
||||
### [2026-03-11] rejectAgentStep / !stop <20><><EFBFBD> AG 誘몃벑濡<EBB291> 而ㅻ㎤<E385BB>뱶 + <20>젋<EFBFBD>뜑<EFBFBD>윭 <20>쟾<EFBFBD>슜 <20>븿<EFBFBD>닔 + <20>뒪<EFBFBD>뀒<EFBFBD>씪 <20>봽由щ<E794B1>명떚釉<EB969A>
|
||||
|
||||
- **利앹긽**: `!stop` 紐낅졊<EB8285>씠 AI瑜<49> 硫덉텛吏<ED859B> 紐삵븿. 濡쒓렇: "No active cascade" / "no session tracked yet"
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**: (1) `antigravity.agent.rejectAgentStep`<60><><EFBFBD> AG 誘몃벑濡<EBB291> 而ㅻ㎤<E385BB>뱶. (2) <20><><EFBFBD>泥댄븳 `getActiveCascadeId()`<60>뒗 **<2A>젋<EFBFBD>뜑<EFBFBD>윭(DOM) <20>쟾<EFBFBD>슜 <20>븿<EFBFBD>닔** <20><><EFBFBD> Extension host<73>뿉<EFBFBD>꽌 <20>빆<EFBFBD>긽 `undefined` 諛섑솚. (3) **v0.4.5 <20>닔<EFBFBD>젙<EFBFBD>룄 <20>떎<EFBFBD>뙣**: `extension.ts`<60>쓽 `getActiveSessionId: () => activeSessionId`媛<> module-level <20>뒪<EFBFBD>듃留<EB9383> <20>봽由щ<E794B1>명떚釉뚮<E98789><EB9AAE> 李몄“ <20><><EFBFBD> step-probe媛<65> `ctx.activeSessionId`瑜<> <20>뾽<EFBFBD>뜲<EFBFBD>씠<EFBFBD>듃<EFBFBD>빐<EFBFBD>룄 extension.ts<74>쓽 蹂<><E8B982>닔<EFBFBD>뒗 遺덈<E981BA><EB8D88> (<28>봽由щ<E794B1>명떚釉<EB969A> 蹂듭궗)
|
||||
|
||||
- **<2A>빐寃<EBB990>** (2026-03-18 v0.4.6): `step-probe.ts`<60>뿉<EFBFBD>꽌 `getActiveSessionId()` getter <20>븿<EFBFBD>닔 export <20>넂 extension.ts closures<65>뿉<EFBFBD>꽌 `getStepProbeSessionId()` <20>샇異<EC8387>. <20>씠<EFBFBD>젣 step-probe<62>쓽 live `ctx.activeSessionId`瑜<> 吏곸젒 <20>씫<EFBFBD>쓬 (`ab0c116`)
|
||||
|
||||
- **二쇱쓽**: JS<4A>뿉<EFBFBD>꽌 **string/number<65>뒗 <20>봽由щ<E794B1>명떚釉뚮씪 李몄“ <20>쟾<EFBFBD>떖 遺덇<E981BA><EB8D87>** <20><><EFBFBD> 媛앹껜 <20>냽<EFBFBD>꽦<EFBFBD>쓣 怨듭쑀<EB93AD>븯<EFBFBD>젮硫<ECA0AE> getter <20>븿<EFBFBD>닔<EFBFBD>굹 媛앹껜 <20>옒<EFBFBD>띁 <20>궗<EFBFBD>슜 <20>븘<EFBFBD>닔
|
||||
|
||||
- **Vikunja**: #411, #410
|
||||
|
||||
|
||||
|
||||
### [2026-03-19] browser_subagent Allow <20><><EFBFBD> <20>옒紐삳맂 RPC payload
|
||||
|
||||
- **利앹긽**: <20>꽌釉<EABD8C> <20>뿉<EFBFBD>씠<EFBFBD>쟾<EFBFBD>듃 "execute JavaScript on localhost" Allow 踰꾪듉<EABEAA>씠 <20>옄<EFBFBD>룞 <20>듅<EFBFBD>씤<EFBFBD>릺吏<EBA6BA> <20>븡<EFBFBD>쓬
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**: `step-probe.ts`<60>뿉<EFBFBD>꽌 `browser_subagent` toolName<6D>씠 step_type 遺꾨쪟 <20>뾾<EFBFBD>씠 raw toolName<6D>쑝濡<EC919D> <20>쟾<EFBFBD>떖 <20>넂 `approval-handler.ts`<60>뿉<EFBFBD>꽌 `runExtensionCode` 留ㅽ븨<E385BD>뿉 <20>룷<EFBFBD>븿<EFBFBD>릺吏<EBA6BA> <20>븡<EFBFBD>븘 default `runCommand` RPC payload <20>궗<EFBFBD>슜 <20>넂 AG媛<47> <20>옒紐삳맂 interaction type<70>쑝濡<EC919D> 臾댁떆
|
||||
|
||||
- **<2A>빐寃<EBB990>** (v0.5.1): `approval-handler.ts` L384<38>뿉 `browser_subagent` 異붽<E795B0><EBB6BD>, `step-probe.ts` L481/L549<34>뿉 `browser_subagent`/`open_browser_url` step_type 遺꾨쪟 異붽<E795B0><EBB6BD> (`549af6d`)
|
||||
|
||||
- **二쇱쓽**: <20>깉濡쒖슫 AG <20>룄援<EBA384> 異붽<E795B0><EBB6BD> <20>떆 諛섎뱶<EC848E>떆 (1) step-probe step_type 留ㅽ븨 (2) approval-handler RPC payload 留ㅽ븨 <20>뼇履<EBBC87> 紐⑤몢 <20>뾽<EFBFBD>뜲<EFBFBD>씠<EFBFBD>듃
|
||||
|
||||
|
||||
|
||||
### [2026-03-21] Idle<6C>넂Resume <20>떊<EFBFBD>샇 <20>냼<EFBFBD>떎 <20><><EFBFBD> 3以<33> 踰꾧렇
|
||||
|
||||
- **利앹긽**: AG <20>옣<EFBFBD>떆媛<EB9686> idle <20>썑 <20>옉<EFBFBD>뾽 <20>옱媛<EC98B1> <20>떆 Discord <20>듅<EFBFBD>씤 <20>떊<EFBFBD>샇媛<EC8387> <20>쟾<EFBFBD>떖<EFBFBD>릺吏<EBA6BA> <20>븡<EFBFBD>쓬
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**: (1) `ws-client.ts` `auth_fail` <20>떆 `shouldReconnect=false` <20><><EFBFBD> JWT 24h 留뚮즺 <20>떆 WS <20>쁺援<EC81BA> 醫낅즺. (2) `hub.py` `_disconnect`<60>뿉<EFBFBD>꽌 <20>쑀<EFBFBD>씪 <20>뿰寃<EBBFB0> <20>떆 `pending_owners` <20>궘<EFBFBD>젣 <20><><EFBFBD> <20>옱<EFBFBD>뿰寃<EBBFB0> <20>썑 Discord 踰꾪듉 臾댄슚. (3) `step-probe.ts` `stallProbed=true` + `lastPendingStepIndex=N`<60>씠 WS <20>옱<EFBFBD>뿰寃<EBBFB0> <20>떆 由ъ뀑 <20>븞 <20>맖 <20><><EFBFBD> WAITING step <20>옱<EFBFBD>쟾<EFBFBD>넚 <20>쁺援<EC81BA> 李⑤떒
|
||||
|
||||
- **<2A>빐寃<EBB990>** (v0.5.2): (1) `auth_fail` <20>넂 `registrationCode` <20>옱<EFBFBD>떆<EFBFBD>룄. (2) `pending_owners` orphan 留덉빱濡<EBB9B1> 蹂댁〈+<2B>옱<EFBFBD>븷<EFBFBD>떦. (3) `resetPendingStateForReconnect()` + `onConnected`<60>뿉<EFBFBD>꽌 <20>샇異<EC8387>
|
||||
|
||||
- **二쇱쓽**: WS `onConnected`<60>뿉<EFBFBD>꽌 諛섎뱶<EC848E>떆 step-probe <20>긽<EFBFBD>깭 由ъ뀑 <20>븘<EFBFBD>닔. `stallProbed`/`lastPendingStepIndex`<60>뒗 TTL <20>뾾<EFBFBD>뒗 <20>쁺援<EC81BA> 媛<>
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 승인 전략 결정 체인 (다음 세션 필독)
|
||||
|
||||
> **이 섹션은 2026-03-08~09 전체 세션의 시행착오를 요약합니다.**
|
||||
> **다음 세션은 이미 거부된 접근을 다시 시도하지 마세요.**
|
||||
|
||||
### ❌ 시도 후 거부된 접근 (재시도 금지)
|
||||
> [!NOTE]
|
||||
|
||||
| # | 접근 | 결과 | 거부 사유 |
|
||||
|---|------|------|-----------|
|
||||
| 1 | LS RPC `HandleCascadeUserInteraction` | `socket hang up` | AG 빌드에서 핸들러 미구현 |
|
||||
| 2 | LS RPC `ResolveOutstandingSteps` | CANCEL 동작 (승인이 아님) | 명칭과 달리 step을 취소함 |
|
||||
| 3 | VS Code 7개 승인 명령 (`terminalCommand.run` 등) | `command not found` | AG 런타임에 미등록 |
|
||||
| 4 | 키보드 시뮬레이션 (`type {Enter}`) | 빈 메시지 전송 | Chat input에 캡처됨 |
|
||||
| 5 | `sendChatActionMessage` / `executeCascadeAction` | 미등록 | 119개 명령에 없음 |
|
||||
| 6 | pywinauto (OS 레벨) | 사용자 결정 폐기 | 크로스플랫폼 불가, 창 겹침 문제 |
|
||||
| 7 | CDP (Chrome DevTools Protocol) | **사용자 명시적 거부** | 비표준, `--remote-debugging-port` 필요 |
|
||||
| 8 | Renderer v1 DOM Click (flat scan) | Run 버튼 미발견 | webview iframe 격리 |
|
||||
> v0.4.5 <20>닔<EFBFBD>젙 <20>궗<EFBFBD>빆(Hub pending_owners, diff_review WS, auto_approve <20>씠以묒벐湲<EBB290>, WS dual-write, ApprovalView fallback)<29><><EFBFBD>
|
||||
|
||||
### ✅ 현재 진행 중인 접근
|
||||
> 肄붾뱶 <20>닔<EFBFBD>젙 <20>셿猷뚮맖. E2E <20>넻<EFBFBD>빀 寃<>利앹<EFA79D><EC95B9> Vikunja #410<31>뿉<EFBFBD>꽌 異붿쟻 以<>.
|
||||
|
||||
| # | 접근 | 상태 | 다음 단계 |
|
||||
|---|------|------|-----------|
|
||||
| 1 | **Renderer v3 Deep DOM Traversal** | 패치+체크섬 OK, **V8 CachedData 삭제 완료, AG 재시작 후 검증** | AG 재시작 → `curl.exe http://127.0.0.1:34332/deep-inspect` |
|
||||
| 2 | `<webview>.executeJavaScript()` | v3에 포함, **미검증** | 위 결과에서 `hasExecJS=true`이면 유력 |
|
||||
| 3 | TerminalExecutionPolicy.EAGER (Turbo) | 미시도, **최후 수단** | 통제권 포기이므로 사용자 승인 필요 |
|
||||
|
||||
### 🔑 핵심 전제
|
||||
- Run/Accept 버튼은 **`vscode-webview://` origin의 격리된 iframe** 안에 있음
|
||||
- 외부 workbench DOM에서 `querySelector`로 접근 **불가** (cross-origin)
|
||||
- `<webview>.executeJavaScript()`가 **유일한 표준 관통 경로** (Electron API)
|
||||
- AG 패치 후 **반드시 풀 프로세스 재시작** 필요 (Reload Window 불충분)
|
||||
- **양쪽 HTML (workbench.html + workbench-jetski-agent.html) 모두 inline 패치** 필수
|
||||
- HTML 패치 변경 시 **`%APPDATA%\Antigravity\CachedData` 삭제 필수** (V8 바이트코드 캐시 무효화)
|
||||
- **CSP `script-src`에 `'unsafe-inline'` 패치 필수** (없으면 인라인 스크립트 무조건 차단)
|
||||
|
||||
### [2026-03-09] CSP script-src — 인라인 스크립트 무조건 차단
|
||||
- **증상**: HTML 패치 ✅, 체크섬 ✅, CachedData 삭제 ✅ — 그런데도 renderer v3 스크립트 미실행 (`/deep-inspect` timeout)
|
||||
- **원인**: `workbench.html`의 CSP `<meta http-equiv="Content-Security-Policy">`에서 `script-src 'self' 'unsafe-eval' blob:` — **`'unsafe-inline'` 없음**. 브라우저가 인라인 `<script>` 태그를 무조건 차단
|
||||
- **해결**: CSP의 `script-src`에 `'unsafe-inline'` 추가. Extension `setupApprovalObserver()`에 CSP 자동 패치 로직 영구 추가
|
||||
- **주의**: `style-src`에는 `'unsafe-inline'`이 있어 스타일은 동작 → 스크립트만 차단되는 것이 함정. `connect-src`에 `http://127.0.0.1:*` 존재하여 HTTP fetch는 허용됨 (CSP 통과 시 통신은 문제없음). 이 CSP 이슈는 **V8 CachedData 이슈와 동시에 존재**하여 진단이 매우 어려웠음
|
||||
### [2026-03-21] stepIndex=-1 <20><><EFBFBD> AG proto uint32 <20>뿉<EFBFBD>윭
|
||||
|
||||
### [2026-03-09] 중복 승인 요청 — DOM scan + Step probe 동시 발동
|
||||
- **증상**: Discord에 같은 명령에 대해 승인 요청이 2개 도착
|
||||
- **원인**: DOM Observer가 `RunAlt+↵` 버튼을 감지하여 pending 생성 + Step probe가 `CORTEX_STEP_STATUS_WAITING` 감지하여 pending 생성 → 동일 step에 대해 2개 파일
|
||||
- **해결**: `writePendingApproval()`에 15초 dedup 윈도우 추가 — 최근 DOM observer pending이 있으면 step probe pending 스킵
|
||||
- **주의**: DOM scan은 제거 불가 — `Allow Once`, `Allow This Conversation` 등 step probe가 감지 못하는 UI 버튼 존재. 버튼 텍스트도 `(Alt|Ctrl|Shift|Meta)\+.*` 정규식으로 키보드 단축키 정제
|
||||
- **利앹긽**: DOM observer媛<72> Allow 踰꾪듉 媛먯<E5AA9B><EBA8AF> <20>넂 Discord <20>듅<EFBFBD>씤 <20>넂 RPC `HandleCascadeUserInteraction` 400 <20>뿉<EFBFBD>윭
|
||||
|
||||
### [2026-03-09] Step probe reject → ResolveOutstandingSteps가 AI 작업 취소
|
||||
- **증상**: Discord에서 거부 클릭 → AI의 현재 step뿐 아니라 진행 중인 작업 전체가 중단
|
||||
- **원인**: step probe 경로의 `tryApprovalStrategies(approved=false)` → `ResolveOutstandingSteps` RPC 호출 → 이것은 step을 **CANCEL**하는 파괴적 동작
|
||||
- **해결**: step probe 경로에서 reject 시 `tryApprovalStrategies` 호출 제거. reject은 로그만 남기고 파기적 동작 없음. DOM observer 경로만 실제 Reject 버튼 클릭 허용
|
||||
- **주의**: `ResolveOutstandingSteps`는 이름과 달리 "해결"이 아닌 "취소". 승인에 절대 사용 금지 (이전 이슈 #55~59 참조)
|
||||
- **<2A>썝<EFBFBD>씤**: DOM observer 寃쎈줈<EC8E88>뒗 step index瑜<78> 紐⑤쫫 <20>넂 `stepIndex=-1` <20>쟾<EFBFBD>떖 <20>넂 AG proto `uint32` <20>븘<EFBFBD>뱶<EFBFBD>뿉 <20>쓬<EFBFBD>닔 遺덇<E981BA><EB8D87>
|
||||
|
||||
### [2026-03-09] Pending 파일 무한 누적 — write_response 후 미삭제
|
||||
- **증상**: `bridge/pending/` 디렉토리에 79개 이상의 .json 파일이 쌓여 봇 시작 시 폭주
|
||||
- **원인**: `write_response()`가 pending 파일 status만 변경하고 삭제하지 않음. `get_pending_requests()`에 age filter 없음
|
||||
- **해결**: (1) `write_response()`에서 pending 파일 삭제, (2) `get_pending_requests()`에 5분 age filter, (3) 시작 시 `_cleanup_stale_pending()` 호출
|
||||
- **주의**: 봇 재시작 시 자동 정리. age filter는 expired 마킹 후 skip
|
||||
- **<2A>빐寃<EBB990>**: `Math.max(0, ...)` 濡<> clamp. `permission` type <20>넂 `runExtensionCode.confirm` 留ㅽ븨 異붽<E795B0><EBB6BD> (v0.5.4)
|
||||
|
||||
### [2026-03-09] Discord 승인 "Run" 표시 — DOM/step_probe 타이밍 불일치
|
||||
- **증상**: Discord에 상세 명령어 대신 "Run"만 표시
|
||||
- **원인**: DOM observer가 "Run" pending 생성(t=0) → 봇이 3초 후 전송 → step_probe MERGE가 10초 후 완료 → 이미 전송 후
|
||||
- **해결**: (1) step_probe가 기존 DOM pending에 MERGE (skip 대신 update), (2) 봇에서 짧은 명령어(≤15자) 4 cycles(12s) 대기, 매 cycle re-read하여 merge 즉시 전송
|
||||
- **주의**: MERGE 타이밍은 step_probe poll interval(5s) + stall detection 필요하므로 최소 10초. defer는 이보다 길어야 함
|
||||
- **二쇱쓽**: DOM observer 寃쎈줈<EC8E88>쓽 step_type<70><65><EFBFBD> <20>빆<EFBFBD>긽 `stepIndex=-1`<60>씪 <20>닔 <20>엳<EFBFBD>쑝誘<EC919D>濡<EFBFBD> proto <20>쟾<EFBFBD>떖 <20>쟾 <20>뼇<EFBFBD>닔 蹂댁옣 <20>븘<EFBFBD>닔
|
||||
|
||||
### [2026-03-09] DOM observer false positive — Proceed/Continue/Open 버튼 오감지
|
||||
- **증상**: 작업 전환 시(notify_user, task_boundary) 승인 요청 없는데도 Discord에 승인 요청 도착
|
||||
- **원인**: DOM observer가 AG UI의 PathsToReview "Proceed"/"Open" 버튼, 파일 Open 버튼 등을 승인 버튼으로 오인
|
||||
- **해결**: (1) HTTP POST /pending 핸들러에 false positive 필터 추가 (Proceed/Continue/Open/Close/OK 등), (2) "Run" 버튼은 `sessionStalled=true`일 때만 허용
|
||||
- **주의**: renderer 인라인 스크립트(HTML)는 extension.js 배포로 안 바뀜 → 서버사이드 필터가 필수. PATS 패턴 수정은 HTML 재패치 시에만 적용
|
||||
|
||||
### [2026-03-09] Discord ApprovalView timeout — 5분 후 버튼 무응답
|
||||
- **증상**: 시간이 지난 후 Discord 승인 버튼 클릭해도 반응 없음
|
||||
- **원인**: Discord.py View의 기본 timeout이 300초(5분)로 설정됨
|
||||
- **해결**: timeout을 1800초(30분)로 증가
|
||||
- **주의**: Discord View timeout은 서버 재시작 후만 적용. 기존 메시지의 View는 이미 만료됨
|
||||
|
||||
### [2026-03-10] DOM Observer 승인 ENOENT — response 파일 Race Condition
|
||||
- **증상**: Discord에서 ✅승인 → extension.log에 `ENOENT: response/xxx.json` 에러 → AG에서 Run 미실행 (간헐적)
|
||||
- **원인**: `processResponseFile()` (response watcher)이 response 파일을 읽고 즉시 `fs.unlinkSync()` → renderer `pollResponse()`가 `GET /response/:rid`로 조회할 때 파일 이미 없음
|
||||
- **해결**: DOM observer 경로(`isDomObserver`)일 때 `processResponseFile()`에서 response 파일을 삭제하지 않고, HTTP handler(`GET /response/:rid`)가 renderer에 전달 후 삭제. step_probe 경로는 `clickTrigger`로 처리하므로 기존처럼 삭제 OK
|
||||
- **주의**: DOM observer와 step_probe 두 경로가 독립적으로 pending을 생성할 수 있으므로, response 삭제 타이밍을 경로별로 구분해야 함
|
||||
### [2026-03-21] reviewAbsoluteUris <20><><EFBFBD> latestNotifyUserStep <20>븘<EFBFBD>뱶紐<EBB1B6> 遺덉씪移<EC94AA>
|
||||
|
||||
### [2026-03-10] Allow Once + Allow This Conversation — 별개 pending으로 분리되는 문제
|
||||
- **증상**: 파일 접근 시 Allow Once와 Allow This Conversation이 Discord에 2개 별도 메시지로 도착. Deny 선택지 없음
|
||||
- **원인**: renderer `scan()`이 한 사이클에 한 버튼만 처리 후 `return`. 각 버튼이 별도 pending으로 전송. Deny는 PATS 패턴에 미등록
|
||||
- **해결**: (1) `scan()`에서 `findButtonContainer()`+`collectSiblingButtons()`로 같은 컨테이너의 관련 버튼을 그룹화하여 `buttons` 배열로 전송, (2) `bot.py` `ApprovalView`가 `buttons` 배열이 있으면 `clear_items()` 후 동적 Discord 버튼 추가, (3) `bridge.py` `UserResponse`에 `button_index` 추가, (4) renderer `pollResponseGroup()`에서 `button_index`에 따라 DOM 버튼 클릭
|
||||
- **주의**: `buttons` 배열이 없는 legacy pending은 기존 2버튼(✅승인/❌거부)으로 표시. Deny를 PATS에 추가함
|
||||
- **利앹긽**: `notify_user`<60>쓽 PathsToReview <20>뙆<EFBFBD>씪 由대젅<EB8C80>씠媛<EC94A0> <20>븳 踰덈룄 <20>옉<EFBFBD>룞<EFBFBD>븯吏<EBB8AF> <20>븡<EFBFBD>쓬
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**: AG <20>떎<EFBFBD>젣 <20>븘<EFBFBD>뱶紐<EBB1B6> `reviewAbsoluteUris` vs 肄붾뱶 `pathsToReview`/`paths_to_review`/`filePaths`
|
||||
|
||||
- **<2A>빐寃<EBB990>**: `reviewAbsoluteUris` 瑜<> 泥<> 踰덉㎏ <20>썑蹂대줈 異붽<E795B0><EBB6BD> (v0.5.3)
|
||||
|
||||
- **二쇱쓽**: AG RPC <20>븘<EFBFBD>뱶紐낆<EFA78F><EB8286> extension.log `[NOTIFY-STEP] keys=` 濡<> <20>솗<EFBFBD>씤 媛<><E5AA9B>뒫. 異붿륫 湲덉<E6B9B2><EB8D89>
|
||||
|
||||
|
||||
|
||||
### [2026-03-21] <20>꽭<EFBFBD>뀡 <20>쟾<EFBFBD>솚 <20><><EFBFBD> 泥<> WAITING 媛먯<E5AA9B><EBA8AF> 20-25s 吏<><EFA79E>뿰
|
||||
|
||||
- **利앹긽**: <20>깉 <20><><EFBFBD><EFBFBD>솕 <20>떆<EFBFBD>옉 <20>썑 泥<> run_command <20>듅<EFBFBD>씤<EFBFBD>씠 Discord<72>뿉 <20>븞 <20>삤怨<EC82A4> AG<41>뿉<EFBFBD>꽌 吏곸젒 <20>듅<EFBFBD>씤<EFBFBD>빐<EFBFBD>빞 <20>븿
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**: `lastModTime=''` 由ъ뀑 <20>넂 `modTimeChanged=true` <20>넂 THINKING 遺꾧린 諛섎났 <20>넂 probe 15-25s 吏<><EFA79E>뿰
|
||||
|
||||
- **<2A>빐寃<EBB990>**: `lastModTime=currentModTime` + `return` <20>젣嫄<ECA0A3> + 利됱떆 probe 媛뺤젣 + <20>쉶洹<EC89B6> 媛<><E5AA9B>뱶 異붽<E795B0><EBB6BD> (v0.5.3)
|
||||
|
||||
- **二쇱쓽**: <20>꽭<EFBFBD>뀡 <20>쟾<EFBFBD>솚 <20>떆 `wasRunning`/`pendingModifiedFiles` 由ъ뀑 <20>븘<EFBFBD>닔 (<28>씠<EFBFBD>쟾 <20>꽭<EFBFBD>뀡 <20>옍<EFBFBD>뿬臾쇰줈 false diff_review 諛⑹<E8AB9B><E291B9>)
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
## <20>빑<EFBFBD>떖 <20>옉<EFBFBD>뾽 洹쒖튃 (怨쇨굅 <20>씠<EFBFBD>뒋<EFBFBD>뿉<EFBFBD>꽌 諛섎났<EC848E>맂 <20>뙣<EFBFBD>꽩)
|
||||
|
||||
|
||||
|
||||
> <20>븘<EFBFBD>옒<EFBFBD>뒗 怨쇨굅 <20>씠<EFBFBD>뒋<EFBFBD>뿉<EFBFBD>꽌 諛섎났<EC848E>쟻<EFBFBD>쑝濡<EC919D> <20>굹<EFBFBD><EAB5B9><EFBFBD><EFBFBD>궃 <20>뙣<EFBFBD>꽩<EFBFBD>쓣 洹쒖튃<EC9296>쑝濡<EC919D> <20>젙由ы븳 寃껋엯<EABB8B>땲<EFBFBD>떎.
|
||||
|
||||
|
||||
|
||||
| # | 洹쒖튃 | 愿<><E684BF>젴 <20>씠<EFBFBD>뒋 (archive 李몄“) |
|
||||
|
||||
|---|------|--------------------------|
|
||||
|
||||
| 1 | **Hub WS<57><53><EFBFBD> file bridge<67>뒗 <20>긽<EFBFBD>샇 諛고<E8AB9B><EAB3A0><EFBFBD>쟻** <20><><EFBFBD> `if hub: ws + return` / `else: file` | WS dual-write, _auto_approve <20>씠以<EC94A0> <20>벐湲<EBB290> |
|
||||
|
||||
| 2 | **WS 寃쎈줈 異붽<E795B0><EBB6BD> <20>떆 file-bridge<67>쓽 紐⑤뱺 遺꾧린瑜<EBA6B0> <20>룷<EFBFBD>똿** | diff_review WS regression |
|
||||
|
||||
| 3 | **AG RPC `{}` <20>쓳<EFBFBD>떟<EFBFBD><EB969F><EFBFBD> <20>떎<EFBFBD>뙣濡<EB99A3> 媛꾩<** <20><><EFBFBD> 硫붿꽌<EBB6BF>뱶紐<EBB1B6> <20><><EFBFBD><EFBFBD>젮<EFBFBD>룄 <20>뿉<EFBFBD>윭 <20>뾾<EFBFBD>씠 `{}` 諛섑솚 | AcknowledgeCascadeCodeEdit |
|
||||
|
||||
| 4 | **ResolveOutstandingSteps<70>뒗 CANCEL <20>룞<EFBFBD>옉** <20><><EFBFBD> <20>듅<EFBFBD>씤<EFBFBD>뿉 <20>젅<EFBFBD><ECA085><EFBFBD> <20>궗<EFBFBD>슜 湲덉<E6B9B2><EB8D89> | Step probe reject |
|
||||
|
||||
| 5 | **Extension 肄붾뱶 <20>닔<EFBFBD>젙 <20>썑 諛섎뱶<EC848E>떆 VSIX 鍮뚮뱶 + AG <20><><EFBFBD> <20>옱<EFBFBD>떆<EFBFBD>옉** | Extension 踰꾩쟾 誘몃같<EBAA83>룷 |
|
||||
|
||||
| 6 | **HTML <20>뙣移<EB99A3> 蹂<>寃<EFBFBD> <20>떆 V8 CachedData <20>궘<EFBFBD>젣 <20>븘<EFBFBD>닔** | V8 CachedData, CSP |
|
||||
|
||||
| 7 | **`bridge/pending/` 議곗옉 <20>떆 諛섎뱶<EC848E>떆 `project_name` + `conversation_id` <20>븘<EFBFBD>꽣** | <20>겕濡쒖뒪 <20>봽濡쒖젥<EC9296>듃 DEDUP MERGE |
|
||||
|
||||
| 8 | **`processResponseFile` <20>긽<EFBFBD>깭 由ъ뀑<D18A><EB8091><EFBFBD> `sawRunningAfterPending=true`留<>** | processResponseFile 臾댄븳 猷⑦봽 |
|
||||
|
||||
| 9 | **fs.watch Windows 遺덉븞<EB8D89>젙 <20><><EFBFBD> 諛섎뱶<EC848E>떆 polling fallback 蹂묓뻾** | fs.watch silent fail |
|
||||
|
||||
| 10 | **diff_review<65>뒗 VS Code 而ㅻ㎤<E385BB>뱶留<EBB1B6> <20>쑀<EFBFBD>슚** <20><><EFBFBD> RPC 3媛<33> <20>쟾<EFBFBD>왂 紐⑤몢 <20>떎<EFBFBD>뙣 <20>솗<EFBFBD>젙 | diff_review RPC dead-end |
|
||||
|
||||
| 11 | **HttpBridgeContext<78>뿉 <20>봽由щ<E794B1>명떚釉<EB969A> by-value 蹂듭궗 湲덉<E6B9B2><EB8D89>** <20><><EFBFBD> 蹂꾨룄 媛앹껜 <20>깮<EFBFBD>꽦 <20>떆 getter <20>궗<EFBFBD>슜 | HttpBridgeContext stale primitive |
|
||||
|
||||
| 12 | **<2A>깉 AG <20>룄援<EBA384> 異붽<E795B0><EBB6BD> <20>떆 step-probe step_type 留ㅽ븨 + approval-handler RPC payload 留ㅽ븨 <20>뼇履<EBBC87> <20>븘<EFBFBD>닔** | browser_subagent Allow |
|
||||
|
||||
| 13 | **WS `onConnected`<60>뿉<EFBFBD>꽌 step-probe <20>긽<EFBFBD>깭 由ъ뀑 <20>븘<EFBFBD>닔** <20><><EFBFBD> `stallProbed`/`lastPendingStepIndex`<60>뒗 TTL <20>뾾<EFBFBD>뒗 <20>쁺援<EC81BA> 媛<> | Idle<6C>넂Resume <20>떊<EFBFBD>샇 <20>냼<EFBFBD>떎 |
|
||||
|
||||
| 14 | **AG proto `uint32` <20>븘<EFBFBD>뱶<EFBFBD>뿉 <20>쓬<EFBFBD>닔 <20>쟾<EFBFBD>떖 湲덉<E6B9B2><EB8D89>** <20><><EFBFBD> `stepIndex` <20>벑<EFBFBD><EBB291><EFBFBD> `Math.max(0, ...)` <20>븘<EFBFBD>닔 | stepIndex=-1 RPC 400 |
|
||||
|
||||
| 15 | **RPC "input not registered" = wrong-LS <20>뿰寃<EBBFB0>** <20><><EFBFBD> `fixLSConnection()` <20>옄<EFBFBD>룞 <20>옱<EFBFBD>떆<EFBFBD>룄 <20>븘<EFBFBD>닔, `lines.length<=1` 議곌린醫낅즺 湲덉<E6B9B2><EB8D89> | Deriva wrong-LS (v0.5.5) |
|
||||
|
||||
| 16 | **<2A>씡<EFBFBD>뒪<EFBFBD>뀗<EFBFBD>뀡(Bridge)<29><><EFBFBD> <20>옄<EFBFBD>쓽<EFBFBD>쟻 鍮꾩쫰<EABEA9>땲<EFBFBD>뒪 <20>뙋<EFBFBD>떒 <20>젅<EFBFBD><ECA085><EFBFBD> 湲덉<E6B9B2><EB8D89>** <20><><EFBFBD> `SafeToAutoRun` <20>벑<EFBFBD>쓽 議곌굔 釉뚮옖移<EC9896> 遺꾧린<EABEA7>뒗 紐⑤몢 遊뉗쑝濡<EC919D> <20>쐞<EFBFBD>엫 (Agnostic Bridge) | SafeToAutoRun Deadlock (v0.5.15) |
|
||||
|
||||
| 17 | **package.json 鍮뚮뱶 <20>뒪<EFBFBD>겕由쏀듃 媛뺤젣** <20><><EFBFBD> `vscode:prepublish` 異붽<E795B0><EBB6BD>濡<EFBFBD> <20>궊<EFBFBD><EAB68A><EFBFBD> <20>냼<EFBFBD>뒪 諛고룷 <20>썝泥<EC8D9D> 李⑤떒 | VSIX v0.5.15 鍮뚮뱶 <20>늻<EFBFBD>씫 |
|
||||
|
||||
| 18 | **<2A>룞湲곗떇 `cp.execSync` <20>궗<EFBFBD>슜 湲덉<E6B9B2><EB8D89>** <20><><EFBFBD> Windows <20>솚寃쎌뿉<EC8E8C>꽌 硫붿씤 <20>씠踰ㅽ듃猷⑦봽 <20>봽由ъ쭠 諛<> WS heartbeat <20>떒<EFBFBD>젅 <20>쑀諛<EC9180> | detectProjectName <20>봽由ъ쭠 |
|
||||
|
||||
|
||||
### [2026-04-09] [Bot/Extension] Discord Signal Relay Failure & Empty Body
|
||||
- **利앹긽**: <20>뵒<EFBFBD>뒪肄붾뱶 遊뉗<E9818A><EB8997> '<27>옄<EFBFBD>룞 <20>듅<EFBFBD>씤<EFBFBD>맖'<27>쓣 <20>쓣<EFBFBD>슦吏<EC8AA6>留<EFBFBD> <20>떎<EFBFBD>젣 肄붾뱶 蹂몃Ц<EBAA83>씠 <20>몴<EFBFBD>떆<EFBFBD>릺吏<EBA6BA> <20>븡怨<EBB8A1>, 梨꾨꼸<EABEA8>뿉 吏꾩쭨 梨꾪똿 硫붿떆吏<EB9686><EFA79E>굹 <20>븣由쇱씠 <20>뒪<EFBFBD>뙵 <20>걧 <20>뮘<EFBFBD>뿉 諛<><E8AB9B>젮 <20>쟾<EFBFBD>넚<EFBFBD>릺吏<EBA6BA> <20>븡<EFBFBD>쓬.
|
||||
- **<2A>썝<EFBFBD>씤**: 1) observer-script.ts<74>뿉<EFBFBD>꽌 踰꾪듉 <20>뀓<EFBFBD>뒪<EFBFBD>듃 留ㅼ묶 <20>떆 Run <20>떒<EFBFBD>뼱<EFBFBD>쓽 寃쎄퀎(\b) 泥섎━瑜<E29481> <20>븯吏<EBB8AF> <20>븡<EFBFBD>븘 VS Code <20>븯<EFBFBD>떒<EFBFBD>쓽 'Running 1 command'瑜<> 媛<>濡쒖콈<EC9296>뼱 PENDING <20>뒪<EFBFBD>뙵 臾댄븳 <20>깮<EFBFBD>꽦. 2) bot.py<70>뿉<EFBFBD>꽌 <20>옄<EFBFBD>룞 <20>듅<EFBFBD>씤 Embed <20>깮<EFBFBD>꽦 <20>떆 req.description<6F>쓣 洹몃━吏<E29481> <20>븡怨<EBB8A1> 踰꾪듉 <20>뀓<EFBFBD>뒪<EFBFBD>듃(req.command)留<> <20>몴<EFBFBD>떆. 3) step-probe.ts<74>뿉<EFBFBD>꽌 <20>꽭<EFBFBD>뀡 援먯껜 <20>떆 理쒓렐 <20>븣由<EBB8A3> <20>씤<EFBFBD>뜳<EFBFBD>뒪 珥덇린<EB8D87>솕瑜<EC8695> <20>옒紐삵븯<EC82B5>뿬 <20>꽭<EFBFBD>뀡<EFBFBD>쓽 泥<> 硫붿떆吏<EB9686>瑜<EFBFBD> 臾댁“嫄<E2809C> <20>뱶濡<EBB1B6>.
|
||||
- **<2A>빐寃<EBB990>**: DOM 媛먯<E5AA9B><EBA8AF> <20>젙洹쒖떇<EC9296>뿉 \b 媛뺤젣 遺<><E981BA>뿬 (/Run\b/), bot.py<70>쓽 Auto-Approve 履<> Embed 蹂몃Ц<EBAA83>뿉 req.description <20>젋<EFBFBD>뜑留<EB9C91> 異붽<E795B0><EBB6BD>, step-probe.ts<74>뿉<EFBFBD>꽌 session init <20>떆 index瑜<78> -1濡<31> 由ъ뀑.
|
||||
- **二쇱쓽**: Native UI <20>뀓<EFBFBD>뒪<EFBFBD>듃 媛먯<E5AA9B><EBA8AF> <20>떆 <20>떒<EFBFBD>뼱 寃쎄퀎(\b)源뚯<E6BA90><EB9AAF> 寃<>利앺빐<EC95BA>빞 False Positive瑜<65> 留됱쓣 <20>닔 <20>엳<EFBFBD>쑝硫<EC919D>, Auto-Approve<76>뒗 諛섎뱶<EC848E>떆 蹂몃Ц<EBAA83>쓣 <20>끂異쒗빐<EC9297>빞 <20>븿.
|
||||
|
||||
### [2026-04-10] [Extension] AI Response Content Missing (Nested PlannerResponse)
|
||||
- **利앹긽**: <20>뵒<EFBFBD>뒪肄붾뱶 梨꾪똿諛⑹뿉 Agent<6E>쓽 <20>뀓<EFBFBD>뒪<EFBFBD>듃 <20>쓳<EFBFBD>떟(AI <20>쓳<EFBFBD>떟)<29>씠 <20>븘<EFBFBD>삁 <20>늻<EFBFBD>씫<EFBFBD>릺<EFBFBD>뼱 <20>쟾<EFBFBD>넚<EFBFBD>릺吏<EBA6BA> <20>븡<EFBFBD>쓬.
|
||||
- **<2A>썝<EFBFBD>씤**: GetCascadeTrajectorySteps媛<73> 諛섑솚<EC8491>븯<EFBFBD>뒗 plannerResponse媛<65> <20>봽濡쒗넗肄<EB8497> 諛⑹떇<E291B9>뿉 <20>뵲<EFBFBD>씪 理쒖긽<EC9296>떒(s.plannerResponse)<29>씠 <20>븘<EFBFBD>땶 s.step.plannerResponse<73>뿉 以묒꺽<EBAC92>릺<EFBFBD>뼱 <20>뱾<EFBFBD>뼱<EFBFBD>삱 <20>닔 <20>엳<EFBFBD>쓬. 湲곗〈 <20>뙆<EFBFBD>꽌<EFBFBD>뒗 <20>븯<EFBFBD>뱶肄붾뵫<EBB6BE>맂 <20>븘<EFBFBD>뱶 諛<> <20>뵆<EFBFBD>옯 援ъ“留<E2809C> 議고쉶<EAB3A0>븯<EFBFBD>뿬 <20>쓳<EFBFBD>떟<EFBFBD>쓣 踰꾨┝.
|
||||
- **二쇱쓽**: AG RPC <20>븘<EFBFBD>뱶紐<EBB1B6> 援ъ“ 異붿륫 湲덉<E6B9B2><EB8D89>. <20>븘<EFBFBD>슂 <20>떆 <20>깒<EFBFBD>뱶諛뺤뒪濡<EB92AA> <20>몢 媛<>吏<EFBFBD> 援ъ“(Flat, Nested) 紐⑤몢 紐⑦궧<E291A6>븯<EFBFBD>뿬 吏곸젒 <20>뙆<EFBFBD>떛 <20>솗<EFBFBD>씤.
|
||||
|
||||
### [2026-04-10] [Extension] Fast Execution `<5s` Response Capture Missed (IDLE-to-IDLE)
|
||||
- **利앹긽**: <20>뵒<EFBFBD>뒪肄붾뱶濡<EBB1B6> <20>궡<EFBFBD>슜<EFBFBD>씠 <20>븘<EFBFBD>삁 <20>쟾<EFBFBD>떖<EFBFBD>릺吏<EBA6BA> <20>븡<EFBFBD>쓬. `[RT-CAPTURE]`, `[RESPONSE-CAPTURE]` 濡쒓렇 紐⑤몢 <20>쟾<EFBFBD><EC9FBE><EFBFBD> <20>궓吏<EAB693> <20>븡<EFBFBD>쓬.
|
||||
- **<2A>썝<EFBFBD>씤**: AI <20>쓳<EFBFBD>떟<EFBFBD>씠<EFBFBD>굹 肄붾뵫 <20>옉<EFBFBD>뾽<EFBFBD>씠 5珥<35>(<28>뤃留<EBA483> 二쇨린) 誘몃쭔<EBAA83>쑝濡<EC919D> 留ㅼ슦 鍮좊Ⅴ寃<E285A4> <20>걹<EFBFBD>굹硫<EAB5B9>, <20>솗<EFBFBD>옣<EFBFBD>씠 `IDLE -> IDLE` <20>긽<EFBFBD>깭留<EAB9AD> 愿<>李고븯硫<EBB8AF> `wasRunning` <20>뵆<EFBFBD>옒洹멸<E6B4B9><EBA9B8> `false`濡<> <20>쑀吏<EC9180><EFA79E>맖. 湲곗〈 `[RESPONSE-CAPTURE]` 議곌굔<EAB38C>떇(`wasRunning && !isRunning && currentCount > ...`)<29>씠 `wasRunning=false`濡<> <20>씤<EFBFBD>빐 釉붾줉<EBB6BE>릺<EFBFBD>뼱 罹≪쿂 <20>옄泥대<EFA7A3><EB8C80> <20>셿<EFBFBD>쟾<EFBFBD>엳 嫄대꼫<EB8C80>쎇寃<EC8E87> <20>맖.
|
||||
- **<2A>빐寃<EBB990>**: `wasRunning` 寃<>利앹쓣 <20>궘<EFBFBD>젣<EFBFBD>븯怨<EBB8AF> `!isRunning && currentCount > lastResponseCaptureStep` 議곌굔<EAB38C>쑝濡<EC919D> <20>셿<EFBFBD>솕<EFBFBD>븯<EFBFBD>뿬 <20>늻<EFBFBD>씫<EFBFBD>맂 step<65>씠 <20>엳<EFBFBD>쓣 <20>븣 臾댁“嫄<E2809C> 罹≪쿂<E289AA>븯<EFBFBD>룄濡<EBA384> 蹂<>寃<EFBFBD>. 異붽<E795B0><EBB6BD>濡<EFBFBD> <20>삤<EFBFBD>옒<EFBFBD>맂 `[RESPONSE-CAPTURE]` <20>궡 <20>븯<EFBFBD>뱶肄붾뵫 <20>뙆<EFBFBD>꽌瑜<EABD8C> `extractPlannerText`濡<> <20>씪<EFBFBD>썝<EFBFBD>솕 <20>쟻<EFBFBD>슜.
|
||||
- **二쇱쓽**: <20>뤃留<EBA483> 諛⑹떇<E291B9>뿉<EFBFBD>꽌<EFBFBD>뒗 <20>긽<EFBFBD>깭(RUNNING->IDLE) <20>쟾<EFBFBD>씠瑜<EC94A0> <20>솗<EFBFBD>떊<EFBFBD>븷 <20>닔 <20>뾾<EFBFBD>쑝誘<EC919D>濡<EFBFBD>, Step Count(<28>씤<EFBFBD>뜳<EFBFBD>뒪 <20>쟾吏<EC9FBE>)<29>씪<EFBFBD>뒗 100% <20>떊猶<EB968A> 媛<><E5AA9B>뒫<EFBFBD>븳 留덉빱瑜<EBB9B1> <20>넻<EFBFBD>빐 <20>깉 <20>쓳<EFBFBD>떟 <20>뿬遺<EBBFAC>瑜<EFBFBD> 媛먯<E5AA9B><EBA8AF><EFBFBD>빐<EFBFBD>빞 <20>븿.
|
||||
|
||||
### [2026-04-10] [Bot] chat_snapshot_scanner 臾댄븳 Abort 諛<> <20>뙆<EFBFBD>씪 <20>쟻泥<EC9FBB> (Exception <20>늻<EFBFBD>씫)
|
||||
- **利앹긽**: 遊뉗씠 <20>뵒<EFBFBD>뒪肄붾뱶濡<EBB1B6> AI <20>떟蹂<EB969F>(梨꾪똿 <20>뒪<EFBFBD>깄<EFBFBD>꺑)<29>쓣 <20>쟾<EFBFBD><EC9FBE><EFBFBD> <20>쟾<EFBFBD>넚<EFBFBD>븯吏<EBB8AF> 紐삵븯怨<EBB8AF> <20>젆<EFBFBD>씠 嫄몃┝. ridge/chat_snapshots/<2F>뿉 泥섎━<EC848E>릺吏<EBA6BA> <20>븡<EFBFBD><EBB8A1><EFBFBD> JSON <20>뙆<EFBFBD>씪<EFBFBD>씠 <20>닔<EFBFBD>떗 媛<> <20>쟻泥대맖.
|
||||
- **<2A>썝<EFBFBD>씤**: ot.py<70>쓽 chat_snapshot_scanner<65>뿉<EFBFBD>꽌 <20>뙆<EFBFBD>씪<EFBFBD>쓣 <20>닚<EFBFBD>쉶 <20>뙆<EFBFBD>떛<EFBFBD>븷 <20>븣 <20>궡遺<EAB6A1><E981BA>쓽 .unlink() 怨쇱젙<EC87B1>뿉<EFBFBD>꽌 諛쒖깮<EC9296>븯<EFBFBD>뒗 <20>삁<EFBFBD>쇅<EFBFBD>굹 discord.Embed <20>깮<EFBFBD>꽦 <20>삁<EFBFBD>쇅 <20>벑<EFBFBD>쓣 猷⑦봽 <20>븞<EFBFBD>뿉<EFBFBD>꽌 <20>옟<EFBFBD>븘二쇱<E4BA8C><EC87B1> 紐삵븿. 泥<> <20>뿉<EFBFBD>윭 <20>뙆<EFBFBD>씪(poison pill)<29>쓣 留뚮굹<EB9AAE>뒗 <20>닚媛<EB8B9A> 猷⑦봽 <20>쟾泥닿<EFA7A3><EB8BBF> <20>룺<EFBFBD>뙆<EFBFBD>릺<EFBFBD>뼱 <20>뮘履쎌쓽 <20>젙<EFBFBD>긽 <20>뙆<EFBFBD>씪<EFBFBD>뱾<EFBFBD>룄 <20>쁺<EFBFBD>썝<EFBFBD>엳 泥섎━<EC848E>릺吏<EBA6BA> <20>븡怨<EBB8A1> <20>떎<EFBFBD>쓬 <20>뤃 <20>뒪耳<EB92AA>以꾩뿉<EABEA9>꽌 <20>떎<EFBFBD>떆 泥<> <20>뙆<EFBFBD>씪<EFBFBD>뿉 留됲옒.
|
||||
- **<2A>빐寃<EBB990>**: 猷⑦봽 <20>궡遺<EAB6A1><E981BA>뿉 except Exception<6F>쓣 異붽<E795B0><EBB6BD><EFBFBD>븯<EFBFBD>뿬 <20>쟾<EFBFBD>뿭 <20>삁<EFBFBD>쇅瑜<EC8785> <20>옟<EFBFBD>븘 諛⑹뼱. <20>떎<EFBFBD>뙣<EFBFBD>븳 <20>뙆<EFBFBD>씪<EFBFBD><EC94AA><EFBFBD> glob<6F>뿉<EFBFBD>꽌 諛섎났 <20>떆<EFBFBD>룄<EFBFBD>릺吏<EBA6BA> <20>븡寃<EBB8A1> .json.failed濡<64> <20>슦<EFBFBD>쉶(rename)<29>떆耳<EB9686> <20>걧瑜<EAB1A7> 鍮꾩썙以<EC8D99>.
|
||||
- **二쇱쓽**: <20>뤃留<EBA483>/<2F>뒪罹먮꼫 or 猷⑦봽 <20>궡遺<EAB6A1><E981BA>뿉<EFBFBD>꽌<EFBFBD>뒗 媛쒕퀎 <20>븘<EFBFBD>씠<EFBFBD>뀥 <20>뙆<EFBFBD>떛 <20>떒怨꾩뿉<EABEA9>꽌 諛쒖깮 媛<><E5AA9B>뒫<EFBFBD>븳 紐⑤뱺 <20>삁<EFBFBD>쇅 <20>긽<EFBFBD>깭<EFBFBD>뿉 <20><><EFBFBD><EFBFBD>븳 Defensive Catch 諛<> Continue(<28>슦<EFBFBD>쉶) 濡쒖쭅<EC9296>씠 <20>븘<EFBFBD>닔<EFBFBD>엫.
|
||||
|
||||
### [2026-04-10] [Extension] GetAllCascadeTrajectories 10-Item Hard Limit Bypass (Signal Drop)
|
||||
- **증상**: 기존에 작성했던 { limit: 30 } 파라미터가 LS 백엔드에서 무시되어 최신 세션이 10개 제한에 걸려 잘려나감. (Discord로 메시지 단 한 글자도 안 넘어옴).
|
||||
- **원인**: GetAllCascadeTrajectories는 구조적으로 pagination 옵션을 무시하거나 강제 10 제한이 걸림.
|
||||
- **해결**: step-probe.ts에서 기본 GetAllCascadeTrajectories와 더불어 모든 트래젝토리를 덤프하는 GetDiagnostics API를 병행 호출하고 머지하여 최신 Session ID를 놓치지 않고 추출하게 함.
|
||||
- **주의**: LS Backend에서 정의한 RPC의 한계상 Argument 조작으로 제한을 회피할 수 없으므로, 향후 GetDiagnostics 등 백도어 데이터를 활용할 것.
|
||||
|
||||
|
||||
|
||||
### [2026-04-10] [Probe Logging] <20><><EFBFBD> AI<41>쓳<EFBFBD>떟 <20>뀓<EFBFBD>뒪<EFBFBD>듃 & WAITING <20>뒪<EFBFBD>뀦 <20>룞<EFBFBD>떆 <20>늻<EFBFBD>씫 踰꾧렇
|
||||
|
||||
- **利앹긽**: 援됱옣<EB90B1>엳 鍮좊Ⅸ AI <20>쓳<EFBFBD>떟(<28>삉<EFBFBD>뒗 利됯컖<EB90AF>쟻<EFBFBD>씤 <20>댋 <20>샇異<EC8387>) <20>떆 `step-probe.ts`媛<> 硫붿떆吏<EB9686><EFA79E><EFBFBD><EFBFBD> <20>듅<EFBFBD>씤 <20>떎<EFBFBD>씠<EFBFBD>뼹濡쒓렇瑜<EBA087> 紐⑤몢 Discord濡<64> 由대젅<EB8C80>씠<EFBFBD>븯吏<EBB8AF> 紐삵븿.
|
||||
|
||||
- **<2A>썝<EFBFBD>씤**: <20>떎<EFBFBD>떆媛<EB9686> <20>뀓<EFBFBD>뒪<EFBFBD>듃 罹≪쿂(`delta > 0`) 議곌굔<EAB38C>뿉 `isRunning &&`<60>씠 嫄몃젮<EBAA83>엳<EFBFBD>뼱, <20>긽<EFBFBD>깭媛<EAB9AD> `WAITING`<60>씠<EFBFBD>굹 `IDLE`濡<> 利됱떆 <20>꽆<EFBFBD>뼱媛<EBBCB1>硫<EFBFBD> <20>뀓<EFBFBD>뒪<EFBFBD>듃瑜<EB9383> 罹≪쿂<E289AA>븯<EFBFBD>뒗 猷⑦떞<E291A6>씠 <20>쟾遺<EC9FBE> <20>뒪<EFBFBD>궢<EFBFBD>맖. <20>삉<EFBFBD>븳 <20>씠 <20>닚媛<EB8B9A> `isStall` 議곌굔<EAB38C>룄 <20><><EFBFBD>吏<EFBFBD> <20>븡<EFBFBD>븘 `WAITING` <20>뵒<EFBFBD>뀓<EFBFBD>뀡<EFBFBD>룄 利앸컻<EC95B8>븿.
|
||||
|
||||
- **<2A>빐寃<EBB990>**: <20>떎<EFBFBD>떆媛<EB9686> 罹≪쿂 濡쒖쭅<EC9296>뿉<EFBFBD>꽌 `isRunning &&` 議곌굔<EAB38C>쓣 <20>젣嫄고븯怨<EBB8AF>, `delta > 0`<60>씪 <20>븣 異붽<E795B0><EBB6BD><EFBFBD>맂 理쒖떊 <20>뒪<EFBFBD>뀦<EFBFBD>쓣 <20>뒪罹뷀븯硫댁꽌 `PLANNER_RESPONSE`<60><><EFBFBD> `WAITING` <20>뒪<EFBFBD>뀦<EFBFBD>쓣 紐⑤몢 泥섎━<EC848E>븯<EFBFBD>룄濡<EBA384> <20>닔<EFBFBD>젙<EFBFBD>븿.
|
||||
|
||||
- **二쇱쓽**: LS Backend 10媛<30> Session <20>젣<EFBFBD>븳 踰꾧렇媛<EBA087> <20>엳<EFBFBD>뼱, <20>떎瑜<EB968E> 李쎌뿉<EC8E8C>꽌 <20>닔<EFBFBD>룞 梨꾪똿(`1fbca84c`)<29>씠 IDLE濡<45> <20>궓<EFBFBD>븘<EFBFBD>엳<EFBFBD>쑝硫<EC919D> <20>옄<EFBFBD>룞<EFBFBD>솕 <20>뿉<EFBFBD>씠<EFBFBD>쟾<EFBFBD>듃<EFBFBD>쓽 <20>썙<EFBFBD>겕<EFBFBD>뒪<EFBFBD>럹<EFBFBD>씠<EFBFBD>뒪 <20>꽭<EFBFBD>뀡怨<EB80A1> <20>뿷媛덈┫ <20>닔 <20>엳<EFBFBD>쑝<EFBFBD>굹, <20>씠 踰꾧렇<EABEA7>뒗 polling <20><><EFBFBD><EFBFBD>씠諛<EC94A0> 臾몄젣<EBAA84><ECA0A3><EFBFBD><EFBFBD>쓬.
|
||||
|
||||
|
||||
### [2026-04-10] [Extension] AI Response Missing for New Sessions (Session Tracking Failure)
|
||||
- **利앹긽**: <20>깉濡쒖슫 <20><><EFBFBD><EFBFBD>솕(Session) <20>떆<EFBFBD>옉 <20>떆 泥<> AI <20>쓳<EFBFBD>떟 <20>뀓<EFBFBD>뒪<EFBFBD>듃媛<EB9383> <20>뵒<EFBFBD>뒪肄붾뱶<EBB6BE>뿉 <20>쟾<EFBFBD><EC9FBE><EFBFBD> <20>쟾<EFBFBD>넚<EFBFBD>릺吏<EBA6BA> <20>븡<EFBFBD>뒗 <20>쁽<EFBFBD>긽.
|
||||
- **<2A>썝<EFBFBD>씤**: 諛깆뿏<EAB986>뱶<EFBFBD>쓽 `GetAllCascadeTrajectories`媛<> 10媛<30> <20>꽭<EFBFBD>뀡留<EB80A1> 諛섑솚<EC8491>븯<EFBFBD>뿬 <20>깉 <20>꽭<EFBFBD>뀡<EFBFBD>씠 <20>늻<EFBFBD>씫<EFBFBD>맖. <20>씠瑜<EC94A0> 蹂댁셿<EB8C81>븯湲<EBB8AF> <20>쐞<EFBFBD>빐 `brain/` <20>뵒<EFBFBD>젆<EFBFBD>넗由щ<E794B1><D189> <20>뒪罹뷀븯<EBB780>뒗 Fallback 濡쒖쭅<EC9296>씠 <20>룞<EFBFBD>옉<EFBFBD>뻽<EFBFBD>쑝<EFBFBD>굹, <20>떊洹<EB968A> <20>꽭<EFBFBD>뀡<EFBFBD>쓽 泥<> <20>떒怨꾩뿉<EABEA9>꽌 `GetCascadeTrajectorySteps`(stepOffset: 0) <20>샇異<EC8387> <20>떆 <20>궡遺<EAB6A1> <20>쓳<EFBFBD>떟(UTF-8 <20>뙆<EFBFBD>떛 <20>벑) <20>뿉<EFBFBD>윭濡<EC9CAD> <20>씤<EFBFBD>빐 Exception<6F>씠 諛쒖깮, `trajectorySummaries`<60>뿉 <20>꽭<EFBFBD>뀡<EFBFBD>씠 <20>븘<EFBFBD>삁 <20>벑濡앸릺吏<EBA6BA> <20>븡<EFBFBD>쓬. <20>꽭<EFBFBD>뀡<EFBFBD>씠 異붿쟻<EBB6BF>릺吏<EBA6BA> <20>븡<EFBFBD>쑝<EFBFBD>땲 `delta > 0` 湲곕컲<EAB395>쓽 <20>쓳<EFBFBD>떟 罹≪쿂媛<ECBF82> 諛쒖깮<EC9296>븯吏<EBB8AF> <20>븡<EFBFBD>쓬.
|
||||
- **<2A>빐寃<EBB990>**: `step-probe.ts`<60>쓽 Fallback 2 `catch` 釉붾줉<EBB6BE>뿉<EFBFBD>꽌 <20>뿉<EFBFBD>윭媛<EC9CAD> 諛쒖깮<EC9296>븯<EFBFBD>뜑<EFBFBD>씪<EFBFBD>룄 媛뺤젣濡<ECA0A3> `stepCount: 1`濡<> <20>꽭<EFBFBD>뀡<EFBFBD>쓣 <20>벑濡앺븯<EC95BA>룄濡<EBA384> <20>뙣移섑븯<EC8491>뿬 <20>꽭<EFBFBD>뀡 <20>씤<EFBFBD>떇 <20>쑀<EFBFBD>떎 諛⑹<E8AB9B><E291B9>.
|
||||
- **二쇱쓽**: API <20>샇異<EC8387> <20>떎<EFBFBD>뙣瑜<EB99A3> 議곗슜<EAB397>엳 `catch`濡<> <20>꽆湲곕㈃ <20>쟾泥<EC9FBE> <20>뙆<EFBFBD>씠<EFBFBD>봽<EFBFBD>씪<EFBFBD>씤(<28>뿬湲곗꽌<EAB397>뒗 <20>긽<EFBFBD>깭 <20>뤃留<EBA483>)<29>씠 <20>빐<EFBFBD>떦 <20>뜲<EFBFBD>씠<EFBFBD>꽣瑜<EABDA3> <20>쁺<EFBFBD>썝<EFBFBD>엳 臾댁떆<EB8C81>븯寃<EBB8AF> <20>릺<EFBFBD>뒗 移섎챸<EC848E>쟻 踰꾧렇媛<EBA087> 諛쒖깮<EC9296>븿. <20>옣<EFBFBD>븷 <20>뿀<EFBFBD>슜 <20>꽕怨<EABD95> <20>떆 湲곕낯媛<EB82AF> 蹂듭썝(Fallback State) <20>꽕<EFBFBD>젙 <20>븘<EFBFBD>닔.
|
||||
|
||||
|
||||
|
||||
### [2026-04-10] [Extension] Trigger-Click False Positives & Button Matching Failure
|
||||
- **利앹긽**: <20>뵒<EFBFBD>뒪肄붾뱶<EBB6BE>뿉<EFBFBD>꽌 <20>듅<EFBFBD>씤(Approve)<29>쓣 <20>늻瑜대㈃, <20>뿉<EFBFBD>씠<EFBFBD>쟾<EFBFBD>듃 <20>솗<EFBFBD>옣 <20>봽濡쒓렇<EC9293>옩<EFBFBD>씠 <20>븣留욎<EFA78D><EC9A8E> 踰꾪듉(<28>삁: `Always run`)<29>쓣 <20>늻瑜댁<E7919C><EB8C81> 紐삵븯嫄곕굹, <20>뿁<EFBFBD>슧<EFBFBD>븳 踰꾪듉(<28>삁: <20>긽<EFBFBD>떒<EFBFBD>쓽 `Running1 command`)<29>쓣 <20>닃<EFBFBD>윭踰꾨젮 <20>떎<EFBFBD>젣 <20>듅<EFBFBD>씤 泥섎━媛<E29481> <20>늻<EFBFBD>씫<EFBFBD>릺<EFBFBD>뒗 <20>쁽<EFBFBD>긽.
|
||||
- **<2A>썝<EFBFBD>씤**: 1) UI 踰꾪듉 <20>뀓<EFBFBD>뒪<EFBFBD>듃<EFBFBD>뿉 `keyboard_arrow_up` <20>벑 癒명떚由ъ뼹 <20>븘<EFBFBD>씠肄<EC94A0> <20>뀓<EFBFBD>뒪<EFBFBD>듃媛<EB9383> <20>젒李<ECA092>(`Always runkeyboard_arrow_up`)<29>릺<EFBFBD>뼱 <20>젙洹쒖떇<EC9296>씠 <20>떎<EFBFBD>뙣<EFBFBD>븷 寃껋쓣 <20>슦<EFBFBD>젮<EFBFBD>빐 <20>떒<EFBFBD>뼱 寃쎄퀎(`\b`)瑜<> <20>젣嫄고븳 <20>뙣移섍<E7A7BB><EC848D> <20>썝<EFBFBD>씤. <20>떒<EFBFBD>뼱 寃쎄퀎媛<ED808E> <20>궗<EFBFBD>씪吏<EC94AA>硫댁꽌 `/Run/i` <20>뙣<EFBFBD>꽩<EFBFBD>씠 `Running1 command` 媛숈<E5AA9B><EC8888> <20>떎瑜<EB968E> <20>긽<EFBFBD>깭 <20>뀓<EFBFBD>뒪<EFBFBD>듃 踰꾪듉<EABEAA>뿉 <20>삤<EFBFBD>깘(False Positive)<29>맖. 2) DOM <20>닚<EFBFBD>꽌<EFBFBD>긽 <20>긽<EFBFBD>깭 <20>뀓<EFBFBD>뒪<EFBFBD>듃 踰꾪듉<EABEAA>씠 <20>븵<EFBFBD>꽌 <20>엳<EFBFBD>쑝誘<EC919D>濡<EFBFBD> <20>삤<EFBFBD>깘<EFBFBD>맂 踰꾪듉<EABEAA>씠 <20>슦<EFBFBD>꽑 <20>겢由<EAB2A2><E794B1>맖.
|
||||
- **<2A>빐寃<EBB990>**: `trigger-click` 濡쒖쭅 <20>떎<EFBFBD>뻾 <20>쟾 踰꾪듉<EABEAA>쓽 `textContent`<60>뿉<EFBFBD>꽌 `keyboard_arrow_up` <20>벑 <20>븣<EFBFBD>젮吏<ECA0AE> 瑗щ━ <20>븘<EFBFBD>씠肄<EC94A0> 臾몄옄<EBAA84>뿴<EFBFBD>쓣 紐낆떆<EB8286>쟻<EFBFBD>쑝濡<EC919D> <20>젣嫄<ECA0A3>(strip)<29>븯怨<EBB8AF>, 紐⑤뱺 <20>듃由ш굅 <20>젙洹쒖떇<EC9296>뿉 <20>떎<EFBFBD>떆 <20>떒<EFBFBD>뼱 寃쎄퀎(`\b`)瑜<> 媛뺤젣 <20>궫<EFBFBD>엯<EFBFBD>븯<EFBFBD>뿬 <20>삤<EFBFBD>깘<EFBFBD>쓣 <20>썝泥<EC8D9D> 李⑤떒<E291A4>븿.
|
||||
- **二쇱쓽**: UI <20>슂<EFBFBD>냼瑜<EB83BC> DOM<4F>뿉<EFBFBD>꽌 湲곸뼱<EAB3B8>삱 <20>븣<EFBFBD>뒗 <20>뀓<EFBFBD>뒪<EFBFBD>듃<EFBFBD>뿉 <20>닲寃⑥쭊 <20>븘<EFBFBD>씠肄<EC94A0>/<2F>쎒<EFBFBD>룿<EFBFBD>듃 由ш굅爾<EAB585>(ligatures)媛<> <20>뾾<EFBFBD>뒗吏<EB9297> 寃<><E5AF83>넗<EFBFBD>빐<EFBFBD>빞 <20>븿. <20>뙣<EFBFBD>꽩 留ㅼ묶 <20>떆 瑗щ━<D189>몴瑜<EBAAB4> 癒쇱<E79992><EC87B1> <20>젣嫄고븯怨<EBB8AF> 紐낇솗<EB8287>븳 寃쎄퀎瑜<ED808E> 遺<><E981BA>뿬<EFBFBD>븷 寃<>.
|
||||
|
||||
|
||||
|
||||
### [2026-04-10] [Extension] Ghost Session Hijack & Infinite Polling Loop (trajectory not found)
|
||||
|
||||
- **利앹긽**: <20>떊洹<EB968A> <20>옉<EFBFBD>뾽 <20>떆 '<27>떊<EFBFBD>샇<EFBFBD>븞<EFBFBD>뱾<EFBFBD>뼱<EFBFBD><EBBCB1><EFBFBD>' (Discord濡<64> 由대젅<EB8C80>씠 <20>븞 <20>맖). 濡쒓렇<EC9293>뿉 500 error trajectory not found 臾댄븳 諛섎났.\n- **<EFBFBD>썝<EFBFBD>씤**: Antigravity媛<79> <20>옉<EFBFBD>뾽<EFBFBD>븯硫댁꽌 brain/<2F>뿉 36湲<36><E6B9B2>옄 <20>뤃<EFBFBD>뜑瑜<EB9C91> <20>깮<EFBFBD>꽦<EFBFBD>븯<EFBFBD>뒗<EFBFBD>뜲, Cascade媛<65> <20>븘<EFBFBD>땲誘<EB95B2>濡<EFBFBD> GetCascadeTrajectorySteps<70>뿉<EFBFBD>꽌 500 <20>뿉<EFBFBD>윭瑜<EC9CAD> <20>깄<EFBFBD>땲<EFBFBD>떎. <20>븯吏<EBB8AF>留<EFBFBD> <20>씠<EFBFBD>쟾 <20>떊洹<EB968A> <20>꽭<EFBFBD>뀡 <20>쑀<EFBFBD>떎 諛⑹<E8AB9B><E291B9> <20>뙣移섍<E7A7BB><EC848D> <20>씠 Ghost <20>꽭<EFBFBD>뀡<EFBFBD>쓣 RUNNING<4E>쑝濡<EC919D> 媛뺤젣 <20>벑濡앺븯硫댁꽌, <20>솢<EFBFBD>꽦 <20>꽭<EFBFBD>뀡(activeSessionId)<29>쓣 <20>깉痍⑦븯怨<EBB8AF> 臾댄븳 <20>뿉<EFBFBD>윭 猷⑦봽<E291A6>뿉 鍮좎<E98DAE><ECA28E>寃<EFBFBD> 留뚮뱾<EB9AAE>뿀<EFBFBD>뒿<EFBFBD>땲<EFBFBD>떎.\n- **<EFBFBD>빐寃<EFBFBD>**: step-probe.ts<74>뿉<EFBFBD>꽌 <20>뤃諛<EBA483> <20>벑濡<EBB291> <20>떆 error message<67>뿉 'trajectory not found'媛<> <20>룷<EFBFBD>븿<EFBFBD>릺硫<EBA6BA> Ghost <20>꽭<EFBFBD>뀡<EFBFBD>쑝濡<EC919D> 媛꾩<<EABEA9>빐 媛뺤젣 <20>벑濡<EBB291>(continue)<29>쓣 嫄대꼫<EB8C80>쎇寃<EC8E87> <20>븯怨<EBB8AF>, Stall Probe <20>뿉<EFBFBD>윭 catch<63>뿉<EFBFBD>꽌<EFBFBD>룄 UTF-8 <20>뿉<EFBFBD>윭媛<EC9CAD> <20>븘<EFBFBD>땲硫<EB95B2> stallProbed=true瑜<65> 二쇱뼱 <20>옱<EFBFBD>떆<EFBFBD>룄 臾댄븳 猷⑦봽瑜<EBB4BD> <20>셿<EFBFBD>쟾<EFBFBD>엳 <20>걡<EFBFBD>뼱<EFBFBD>깉<EFBFBD>뒿<EFBFBD>땲<EFBFBD>떎.\n- **二쇱쓽**: uuid 湲몄씠(36<33>옄)留뚯쑝濡<EC919D> <20>뵒<EFBFBD>젆<EFBFBD>넗由щ<E794B1><D189> <20>떇蹂꾪븷 <20>븣 Antigravity<74><79><EFBFBD> Google Agent媛<74> 紐⑦샇<E291A6>빐吏<EBB990> <20>닔 <20>엳<EFBFBD>쑝誘<EC919D>濡<EFBFBD>, 諛섎뱶<EC848E>떆 Backend <20>쓳<EFBFBD>떟<EFBFBD>쓽 <20>솗<EFBFBD>떎<EFBFBD>븳 <20>뿉<EFBFBD>윭(trajectory not found) 硫붿떆吏<EB9686>濡<EFBFBD> <20>삁<EFBFBD>쇅 <20>뙋蹂꾩쓣 <20>빐<EFBFBD>빞 <20>빀<EFBFBD>땲<EFBFBD>떎.\n
|
||||
205
.agents/references/observer-dev-guide.md
Normal file
205
.agents/references/observer-dev-guide.md
Normal file
@@ -0,0 +1,205 @@
|
||||
# Observer Script 개발 가이드 — SSOT
|
||||
|
||||
> **이 문서는 Observer 코드 변경 전 반드시 확인하는 SSOT입니다.**
|
||||
> 모든 Observer 관련 설계, 배포, 제약사항이 이 문서에 있습니다.
|
||||
|
||||
---
|
||||
|
||||
## 1. Observer 코드 특성
|
||||
|
||||
### 1.1 실행 환경
|
||||
- Observer는 **workbench-jetski-agent.html**에 인라인 `<script>`로 삽입됨
|
||||
- **AG Native의 Electron 렌더러 프로세스**에서 실행 (VS Code extension host가 아님)
|
||||
- 렌더러는 **strict mode 아님** (확인 필요), 하지만 V8 parser는 일부 strict-like 규칙 적용
|
||||
- `generateApprovalObserverScript(port)` 함수가 **TypeScript template literal**로 스크립트 생성
|
||||
|
||||
### 1.2 코드 작성 규칙 (위반 시 Observer 전체 크래시)
|
||||
|
||||
| 규칙 | 이유 | 예시 |
|
||||
|------|------|------|
|
||||
| **for 루프 안에 function 선언 금지** | V8 strict mode error | `var fn = function(){}` 사용 |
|
||||
| **문자열 리터럴에 특수문자 금지** | template literal 이스케이핑 깨짐 | `'??'` → `'MAX'` |
|
||||
| **regex에 `\\s` 등 이스케이프 금지** | template literal이 `\\\\s` → `\\s` (리터럴) | 문자열 비교 사용 |
|
||||
| **ES6+ 구문 금지** | 구 V8 호환 | `var` 사용, `let/const/arrow` 금지 |
|
||||
| **배포 전 SYNTAX CHECK 필수** | Observer 크래시 방지 | 아래 검증 명령어 참조 |
|
||||
|
||||
### 1.3 필수 검증 명령어 (모든 빌드 전 실행)
|
||||
```powershell
|
||||
npm.cmd run compile; node -e "const {generateApprovalObserverScript}=require('./out/observer-script'); let s=generateApprovalObserverScript(18080); try { new Function(s); console.log('SYNTAX OK'); } catch(e) { console.log('ERROR:', e.message); }"
|
||||
```
|
||||
**SYNTAX OK가 나오지 않으면 절대 배포하지 않는다.**
|
||||
|
||||
### 1.4 배포 전 자기검증 체크리스트 (MANDATORY)
|
||||
**재시작을 요구하기 전 반드시 다음을 모두 통과해야 한다:**
|
||||
|
||||
1. [ ] **SYNTAX CHECK 통과**: `new Function(s)` → `SYNTAX OK`
|
||||
2. [ ] **수정 방향 검증**: 이 수정이 문제를 해결하는 올바른 접근인지 스스로 2번 재검증
|
||||
3. [ ] **template literal 규칙 위반 없음**: regex 이스케이프, 특수문자, function 선언 등
|
||||
4. [ ] **변경 범위 최소화**: 불필요한 코드 포함 여부 확인
|
||||
5. [ ] **재시작 사유 명시**: 사용자에게 (a) 무엇을 수정했고 (b) 왜 재시작이 필요한지 1~2줄로 설명
|
||||
6. [ ] **재시작 횟수 명시**: Observer 변경 = 2회, Extension host만 변경 = 1회
|
||||
7. [ ] **log() relay 필터 확인**: 새 로그 키워드 추가 시 log() 함수의 키워드 필터에도 추가했는지 확인 (섹션 3.5 참조)
|
||||
8. [ ] **regex E2E 테스트**: Observer에서 사용하는 새 regex는 생성된 코드에서 직접 실행하여 매칭 검증
|
||||
9. [ ] **구현 전 가정 검증**: 새 접근을 코딩하기 전에, 핵심 가정이 성립하는지 로그 1줄로 먼저 확인 (예: "Step Probe가 WAITING을 볼 수 있는가?" → `STEP-PROBE.*WAITING` 로그 검색)
|
||||
|
||||
**정당한 사유 없이 재시작을 요구하지 않는다.**
|
||||
**DOM 구조를 먼저 파악하고 설계한 후 코드를 작성한다.**
|
||||
**시행착오식(trial-and-error) 접근을 하지 않는다.**
|
||||
**추측으로 코딩하지 않는다. 로그/데이터로 확인한 사실에 기반하여 코딩한다.**
|
||||
|
||||
---
|
||||
|
||||
## 2. 배포 프로세스
|
||||
|
||||
### 2.1 Observer 코드가 포함된 변경
|
||||
Observer 코드 변경은 **extension host 코드 변경보다 비용이 높다**:
|
||||
|
||||
```
|
||||
VSIX 빌드 → VSIX 설치 → AG 재시작 #1 (extension이 HTML 패치)
|
||||
→ AG 재시작 #2 (패치된 HTML 로드) → Observer 실행
|
||||
```
|
||||
|
||||
**총 2번 AG 재시작 필요**
|
||||
|
||||
### 2.2 Extension host 코드만 변경 (approval-handler, http-bridge 등)
|
||||
```
|
||||
VSIX 빌드 → VSIX 설치 → AG 재시작 #1 → 즉시 적용
|
||||
```
|
||||
|
||||
**1번 AG 재시작 필요**
|
||||
|
||||
### 2.3 VSIX 설치 확인
|
||||
```powershell
|
||||
Get-ChildItem "$env:USERPROFILE\.vscode\extensions" -Filter "*gravity*" -Directory |
|
||||
ForEach-Object { $j = Get-Content (Join-Path $_.FullName "package.json") | ConvertFrom-Json; "$($_.Name): v$($j.version)" }
|
||||
```
|
||||
|
||||
### 2.4 HTML 패치 확인 (Observer 코드가 반영되었는지)
|
||||
```powershell
|
||||
Select-String -Path "$env:LOCALAPPDATA\Programs\Antigravity\resources\app\out\vs\code\electron-browser\workbench\workbench-jetski-agent.html" -Pattern "검색할_함수명" -Quiet
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. AG Native DOM 구조
|
||||
|
||||
### 3.1 Chat Panel (Observer가 접근 가능)
|
||||
- 위치: `document.querySelector('#conversation')` 또는 `document.querySelector('[class*="conversation"]')`
|
||||
- AI 응답 블록: `.leading-relaxed.select-text`
|
||||
- 사용자 메시지 블록: `.select-text.rounded-lg` (v0.5.74+)
|
||||
- Thinking 블록: 조상에 `max-h-[200px]` 클래스 있음 → 필터링
|
||||
|
||||
### 3.2 승인 버튼 — "Always run" (v0.5.93 BTN-DOM-DUMP 확인)
|
||||
|
||||
실제 DOM 구조 (v0.5.92 로그로 확인):
|
||||
- d0: button.flex.cursor-pointer (Always run 버튼)
|
||||
- d1: div.min-w-0
|
||||
- d2: div.flex.items-center.justify-between.rounded-b.border-t (버튼 바)
|
||||
- d3: div (이름 없는 컨테이너)
|
||||
- div.mb-1 = "Running command" (헤더)
|
||||
- div.flex = "❯ gravity_control > ..." (실제 명령어, plain div!)
|
||||
- div.flex = "Always run Cancel" (버튼들)
|
||||
|
||||
> 명령어는 pre.font-mono나 code가 아닌 plain div.flex에 있음.
|
||||
> v30: "Running command" div의 형제를 탐색하여 프롬프트 마커 뒤의 명령어 추출.
|
||||
|
||||
- "Retry" 버튼: button 태그, chat panel 내
|
||||
|
||||
### 3.3 Diff Review (Accept all / Reject all) — Observer 접근 가능 (v0.5.101+)
|
||||
- **v0.5.101 이전**: 에디터 webview에 렌더링, Observer document에서 접근 불가
|
||||
- **v0.5.101 이후**: AG UI 업데이트로 chat panel footer에 `<span class="cursor-pointer">` 태그로 렌더링
|
||||
- Observer의 `allBtns` 선택자에 `span.cursor-pointer` 포함 필수
|
||||
- `matchedType = 'diff_review'`로 분류됨 (L1120: `txt.includes('Accept')`)
|
||||
- auto-approve response 파일에 `_from_ws: true` 마커 필수 (processResponseFile race condition 방지)
|
||||
|
||||
### 3.4 DOM 렌더링 타이밍
|
||||
- "Always run" 버튼이 DOM에 나타날 때 명령어 div도 함께 렌더링됨
|
||||
- v30의 "Running command" div 탐색은 즉시 성공
|
||||
|
||||
### 3.5 log() relay 필터 규칙
|
||||
Observer의 log() 함수는 키워드 필터로 일부 로그만 extension.log에 relay.
|
||||
새 로그 키워드 추가 시 반드시 필터도 함께 수정해야 함.
|
||||
|
||||
현재 필터 키워드 (v0.5.92+):
|
||||
CV-CLASSES, CV-CHILDREN, child[, CV found, Conversation view,
|
||||
BEACON, ERROR, chat relay, user-cls,
|
||||
CONTEXT, BTN-DOM, DEFERRED, DETECTED
|
||||
|
||||
---
|
||||
|
||||
## 4. 파일 경로 매핑
|
||||
|
||||
| 항목 | 경로 |
|
||||
|------|------|
|
||||
| AG 설치 경로 | `$env:LOCALAPPDATA\Programs\Antigravity\` |
|
||||
| workbench HTML | `...\resources\app\out\vs\code\electron-browser\workbench\workbench-jetski-agent.html` |
|
||||
| Extension 로그 | `$env:USERPROFILE\.gemini\antigravity\bridge\extension.log` |
|
||||
| Pending 파일 | `$env:USERPROFILE\.gemini\antigravity\bridge\pending\*.json` |
|
||||
| Response 파일 | `$env:USERPROFILE\.gemini\antigravity\bridge\response\*.json` |
|
||||
| VSIX extensions | `$env:USERPROFILE\.vscode\extensions\variet.gravity-bridge-*\` |
|
||||
| HTTP Bridge 포트 | 34332 (또는 `getDeterministicPort('gravity_control')`) |
|
||||
| Discord 채널 | `#ag-gravity_control` (ID: 1483082084540223663) |
|
||||
|
||||
---
|
||||
|
||||
## 5. 과거 실수 및 교훈
|
||||
|
||||
### 5.1 VSIX 미설치 (v0.5.78~83)
|
||||
- **증상**: 빌드만 하고 `code --install-extension` 실행 안 함
|
||||
- **결과**: 설치된 버전이 v0.5.50, 모든 수정사항 미적용
|
||||
- **교훈**: 빌드 후 반드시 `code --install-extension *.vsix --force` 실행
|
||||
- **확인**: `Get-ChildItem "$env:USERPROFILE\.vscode\extensions" -Filter "*gravity*"`
|
||||
|
||||
### 5.2 function 선언 → Observer 크래시 (v0.5.84~86)
|
||||
- **증상**: `function _isGenericDesc(d){}` 를 for 루프 내부에 선언
|
||||
- **결과**: Observer 전체 크래시, chat relay + auto-approve 중단
|
||||
- **교훈**: `var fn = function(){}` 사용, 배포 전 SYNTAX CHECK 필수
|
||||
|
||||
### 5.3 깨진 문자열 리터럴 (v0.5.86)
|
||||
- **증상**: `{tag:'??',...}` (특수문자가 따옴표 깨뜨림)
|
||||
- **결과**: SYNTAX ERROR, Observer 미작동
|
||||
- **교훈**: template literal 안에서 특수문자/이모지 사용 주의
|
||||
|
||||
### 5.4 regex 이스케이핑 실패 (v0.5.83~84)
|
||||
- **증상**: `/Always\\s+run/` → 생성 시 `\\s` (리터럴 백슬래시+s)로 출력
|
||||
- **결과**: "Always run" 매칭 실패
|
||||
- **교훈**: template literal 안에서 regex 대신 **문자열 비교** 사용
|
||||
|
||||
### 5.5 _from_ws 파일 무한 누적 (v0.5.78~84)
|
||||
- **증상**: response 파일에 `_from_ws: true` 마커 → processResponseFile이 스킵 → 영원히 삭제 안 됨
|
||||
- **결과**: 3초마다 4개 파일 × SKIP 로그 → 로그 스팸, 다른 처리 방해
|
||||
- **교훈**: 보존 파일에는 반드시 **TTL(자동 만료)** 추가
|
||||
|
||||
---
|
||||
|
||||
## 6. 디버깅 체크리스트
|
||||
|
||||
### Observer가 작동하지 않을 때
|
||||
1. `extension.log`에서 `setup complete` 확인 → Observer 로드 여부
|
||||
2. `OBSERVER-LOG` 패턴 검색 → 스캔 활동 여부
|
||||
3. `HTTP-REQ` 검색 → HTTP bridge에 요청 도달 여부
|
||||
4. **SYNTAX CHECK** 실행 → 생성 스크립트 문법 검증
|
||||
5. `SKIP _from_ws` 반복 확인 → stale response 파일 정리
|
||||
|
||||
### Discord에 메시지가 안 올 때
|
||||
1. `POST /chat` 검색 → chat relay 전송 여부
|
||||
2. `WS.*send` 검색 → WebSocket 전송 여부
|
||||
3. Discord API로 직접 확인: `node extension/scratch/discord_read.js`
|
||||
4. stale response 파일 확인: `Get-ChildItem $env:USERPROFILE\.gemini\antigravity\bridge\response\*.json`
|
||||
|
||||
---
|
||||
|
||||
## 7. 버전 히스토리 요약
|
||||
|
||||
| 버전 | 핵심 변경 | 결과 |
|
||||
|------|----------|------|
|
||||
| v0.5.50 | 기본 릴레이 시스템 | ✅ 안정 |
|
||||
| v0.5.78 | `_from_ws` 마커 (Retry 보존) | ✅ 작동 (TTL 미구현) |
|
||||
| v0.5.79 | sibling 탐색 + thinking 필터 | ✅ 작동 |
|
||||
| v0.5.80~81 | Accept all offsetParent 완화 | ❌ 구조적 불가 (에디터 webview) |
|
||||
| v0.5.82 | 버튼 셀렉터 확장 + ACCEPT-SCAN | 진단용 |
|
||||
| v0.5.83 | DEFERRED 컨텍스트 500ms | regex 이스케이핑 실패 |
|
||||
| v0.5.84 | regex → 문자열 비교 | function 선언 크래시 |
|
||||
| v0.5.85 | `_from_ws` TTL 60초 | ✅ stale 정리 |
|
||||
| v0.5.86 | function → var expression | 깨진 문자열 미발견 |
|
||||
| v0.5.87 | 깨진 문자열 2건 수정 | ✅ SYNTAX OK |
|
||||
216
.agents/references/relay-architecture.md
Normal file
216
.agents/references/relay-architecture.md
Normal file
@@ -0,0 +1,216 @@
|
||||
# AG Native 릴레이 아키텍처 분석
|
||||
|
||||
> **이 문서는 AG Native ↔ Discord 릴레이의 데이터 흐름 SSOT입니다.**
|
||||
> 구현/디버깅 전 반드시 확인합니다.
|
||||
|
||||
---
|
||||
|
||||
## 1. 데이터 경로 요약
|
||||
|
||||
AG Native에서 Discord로 메시지를 전달하는 경로는 크게 2개:
|
||||
|
||||
| # | 경로 | 소스 | 실시간? | 상태 |
|
||||
|---|------|------|---------|------|
|
||||
| 1 | **Observer DOM** | workbench.html 인라인 스크립트 → DOM 관찰 → HTTP POST → http-bridge | ✅ 실시간 | AI 응답: ✅ 작동 (v0.5.72+), 사용자 메시지: ✅ 작동 (v0.5.74+) |
|
||||
| 2 | **Step Probe (trajectory API)** | LS RPC `GetCascadeTrajectorySteps` → step 분석 | ❌ cascade 완료 후에만 | AI 응답: ❌ 실시간 불가, 사용자 메시지: ❌ 실시간 불가 |
|
||||
|
||||
### 1.1 핵심 API 제약 (2026-04-18 확인)
|
||||
|
||||
> [!CAUTION]
|
||||
> **`GetCascadeTrajectorySteps`는 진행 중인 cascade의 step을 실시간으로 반환하지 않습니다.**
|
||||
> step count는 cascade가 **완전히 종료**(IDLE 전환)된 후에만 업데이트됩니다.
|
||||
> 따라서 Step Probe의 RT-CAPTURE, HB-CAPTURE 모두 **현재 진행 중인 대화에서는 작동하지 않습니다.**
|
||||
|
||||
**검증 데이터**:
|
||||
- POLL에서 `status=CASCADE_RUN_STATUS_IDLE`, `steps=928`, `delta=0` 고정
|
||||
- HEARTBEAT probe: `offset=927 got=1 real=928 known=928` → 변함 없음
|
||||
- 실제로 수십 개의 tool call이 실행되었지만 step count 불변
|
||||
- Cascade 종료 후 다음 poll에서 step count가 점프 (예: 733 → 865 → 928)
|
||||
|
||||
### 1.2 AG Native SDK EventMonitor
|
||||
|
||||
SDK에 이벤트 시스템이 있으나 **모두 polling 기반**:
|
||||
- `EventMonitor.onStepCountChanged` — getDiagnostics 기반 polling
|
||||
- `EventMonitor.onActiveSessionChanged` — state.vscdb 기반 polling
|
||||
- **실시간 push (WebSocket/SSE)는 없음**
|
||||
- 현재 상태: ERR_CONNECTION_REFUSED 문제로 비활성화됨
|
||||
|
||||
---
|
||||
|
||||
## 2. Observer DOM 경로 상세
|
||||
|
||||
### 2.1 Observer 스크립트 삽입 체인
|
||||
|
||||
```
|
||||
extension.ts activate()
|
||||
→ html-patcher.ts setupApprovalObserver()
|
||||
→ observer-script.ts generateApprovalObserverScript(port)
|
||||
→ workbench-jetski-agent.html에 인라인 <script> 삽입
|
||||
→ AG 재시작 시 렌더러가 로드
|
||||
```
|
||||
|
||||
### 2.2 Observer 주요 함수
|
||||
|
||||
| 함수 | 역할 |
|
||||
|------|------|
|
||||
| `scanChatBodies()` | 3초마다 실행, conversation view에서 메시지 블록 탐색 |
|
||||
| `extractCleanStepText(el)` | DOM 클론 → style/script/button 제거 → textContent 추출 |
|
||||
| `extractContextFromNearby(btn)` | 승인 버튼 주변 DOM에서 명령어 텍스트 추출 (v23: sibling 탐색 포함) |
|
||||
| `pollResponseGroup(rid, btnRefs)` | response 파일 polling → 버튼 자동 클릭 |
|
||||
|
||||
### 2.3 AI 응답 감지 셀렉터
|
||||
|
||||
```javascript
|
||||
var responseBlocks = cv.querySelectorAll(
|
||||
'.leading-relaxed.select-text, ' // ← AI 응답 마크다운 블록 (주력)
|
||||
+ '.text-ide-message-block-user-color, ' // ← 사용자 메시지 (미매칭)
|
||||
+ '.text-ide-message-block-bot-color, ' // ← NUX tooltip 전용 (오매칭)
|
||||
+ '.bg-ide-message-block-user-background, '// ← 사용자 메시지 (미매칭)
|
||||
+ '[data-message-role="user"], ' // ← 사용자 메시지 (미매칭)
|
||||
+ '[data-role="user"]' // ← 사용자 메시지 (미매칭)
|
||||
);
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> **v0.5.74에서 사용자 메시지 셀렉터가 추가되었습니다.**
|
||||
> AG Native 소스(`jetskiAgent/main.js`)의 `Esn` 컴포넌트 분석으로
|
||||
> 사용자 메시지 CSS 클래스(`msn = "bg-gray-500/10 border border-gray-500/20 p-2 rounded-lg w-full text-sm select-text"`)를 식별.
|
||||
> 셀렉터: `.select-text.rounded-lg`, 역할 판별: `rounded-lg` 있고 `leading-relaxed` 없으면 → user
|
||||
|
||||
### 2.4 AI 응답 추출 흐름
|
||||
|
||||
```
|
||||
scanChatBodies() 3초 간격
|
||||
→ cv = document.querySelector('#conversation')
|
||||
→ responseBlocks = cv.querySelectorAll('.leading-relaxed.select-text, ...')
|
||||
→ lastBlock = responseBlocks[last] (가장 최근 블록)
|
||||
→ 이미 scrape 됐으면 skip
|
||||
→ blockText = extractCleanStepText(lastBlock)
|
||||
→ 안정화 대기 (3초 동안 텍스트 변경 없으면)
|
||||
→ POST /chat { text, source, block_index, role }
|
||||
→ http-bridge → writeChatSnapshot() → WS → Discord
|
||||
```
|
||||
|
||||
### 2.5 Observer 업데이트 제약
|
||||
|
||||
> [!CAUTION]
|
||||
> **Observer 코드는 workbench.html에 인라인 삽입됩니다.**
|
||||
> extension reload만으로는 Observer 코드가 업데이트되지 않습니다.
|
||||
> **AG 재시작 + V8 CachedData 삭제**가 필요합니다.
|
||||
> (단, product.json 체크섬이 맞으면 CachedData 삭제 없이 AG 재시작만으로 충분할 수 있음)
|
||||
|
||||
---
|
||||
|
||||
## 3. 승인 버튼 (Auto-Approve) 경로
|
||||
|
||||
### 3.1 "Always run" 자동 승인 흐름
|
||||
|
||||
```
|
||||
Observer DOM scan
|
||||
→ "Always run" 버튼 텍스트 감지
|
||||
→ POST /pending { command: "Always run", description: "...", buttons: [...] }
|
||||
→ http-bridge _handlePending()
|
||||
→ alwaysRunDetected = true
|
||||
→ enrichment 시도:
|
||||
1. rawDesc에서 > 프롬프트 마커 찾기 → ✅ 성공 (buttons=2일 때 desc에 프롬프트 포함)
|
||||
2. rawDesc 최장 라인 사용 → buttons=1일 때 desc="Always run"이라 실패
|
||||
3. v20 fallback: bridge/pending/ 최신 파일에서 command 읽기 → Step Probe pending 있을 때만
|
||||
4. v23 sibling: Observer가 footer 형제 요소에서 pre.font-mono 탐색 → ✅ 성공
|
||||
→ response 파일 작성 → Observer pollResponseGroup → 버튼 클릭
|
||||
→ WS sendPending { status: 'auto_approved', command: displayCmd }
|
||||
→ Discord embed 표시
|
||||
```
|
||||
|
||||
### 3.2 명령어 enrichment 현황 (2026-04-18 검증)
|
||||
|
||||
| 조건 | 결과 | 빈도 |
|
||||
|------|------|------|
|
||||
| Observer가 buttons=2 (`["Always run","Cancel"]`)이고 desc에 `>` 포함 | ✅ 명령어 표시 | ~50% |
|
||||
| Observer가 buttons=1 (`["Always run"]`)이고 desc="Always run" | ❌ "Always run" 표시 | ~50% |
|
||||
|
||||
**로그 증거** (04:25:59):
|
||||
```
|
||||
AUTO-APPROVE raw: cmd="Always run" desc="…\extension > npm.cmd run compile..." buttons=["Always run","Cancel"]
|
||||
→ cmd="npm.cmd run compile 2>&1; npm.cmd version patch..." ✅ 성공
|
||||
|
||||
AUTO-APPROVE raw: cmd="Always run" desc="Always run" buttons=["Always run"]
|
||||
→ cmd="Always run" ❌ 실패
|
||||
```
|
||||
|
||||
> buttons=2인 경우("Always run" + "Cancel")는 Observer가 code 블록을 찾아 description에 포함.
|
||||
> buttons=1인 경우는 code 블록이 DOM에서 아직 렌더링되지 않았거나 접근 불가.
|
||||
|
||||
---
|
||||
|
||||
## 4. 사용자 메시지 릴레이 상태
|
||||
|
||||
### 4.1 현재 상태: ✅ 작동 (v0.5.74+)
|
||||
|
||||
| 경로 | 상태 | 비고 |
|
||||
|------|------|------|
|
||||
| Observer DOM | ✅ | `.select-text.rounded-lg` 셀렉터로 캡처 (v0.5.74) |
|
||||
| Step Probe (trajectory API) | ❌ | cascade 진행 중 step 조회 불가 |
|
||||
| Step Probe (observer [USER-MSG]) | ❌ | `lastUserInputStepIndex`가 갱신되지 않음 |
|
||||
|
||||
### 4.2 해결 방안
|
||||
|
||||
1. **DOM 덤프에서 사용자 메시지 클래스 식별** → Observer 셀렉터 추가
|
||||
2. **Cascade 완료 후** Step Probe HB-CAPTURE에서 `USER_INPUT` step 캡처 (지연 릴레이)
|
||||
|
||||
---
|
||||
|
||||
## 5. 파일/포트 매핑
|
||||
|
||||
| 항목 | 값 |
|
||||
|------|-----|
|
||||
| Observer 삽입 대상 | `workbench-jetski-agent.html` |
|
||||
| HTTP Bridge 포트 | `getDeterministicPort('gravity_control')` = **18080** |
|
||||
| Extension 로그 | `~/.gemini/antigravity/bridge/extension.log` |
|
||||
| Pending 파일 | `~/.gemini/antigravity/bridge/pending/*.json` |
|
||||
| Response 파일 | `~/.gemini/antigravity/bridge/response/*.json` |
|
||||
| Chat Snapshot 파일 | `~/.gemini/antigravity/bridge/chat_snapshots/*.json` |
|
||||
| Discord 채널 | `#ag-gravity_control` (ID: 1483082084540223663) |
|
||||
| Discord Bot 토큰 | `.env` → `DISCORD_TOKEN` |
|
||||
|
||||
---
|
||||
|
||||
## 6. 디버깅 도구
|
||||
|
||||
| 도구 | 경로 | 용도 |
|
||||
|------|------|------|
|
||||
| Discord 메시지 읽기 | `extension/scratch/discord_read.js` | API로 채널 최근 메시지 조회 |
|
||||
| Discord 채널 목록 | `extension/scratch/discord_channels.js` | 서버 채널 목록 조회 |
|
||||
| Extension 로그 확인 | `Select-String -Path $logFile -Pattern "패턴"` | 실시간 로그 분석 |
|
||||
| DOM 구조 덤프 | Observer 자동 (CV-CHILDREN 로그) | AG Native DOM 클래스 식별 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 버전 히스토리 (v0.5.67~)
|
||||
|
||||
| 버전 | 변경 | 결과 |
|
||||
|------|------|------|
|
||||
| v0.5.67 | Observer DOM relay 비활성화, Step Probe RT-CAPTURE로 전환 | ❌ API가 진행중 step 미반환 |
|
||||
| v0.5.68 | auto-approve enrichment 디버그 로그 추가, 조건 >10 → >3 완화 | Observer가 desc="Always run" 보냄 확인 |
|
||||
| v0.5.69 | pending 파일 fallback으로 auto-approve 명령어 enrichment | 일부 개선 (Step Probe pending 있을 때만) |
|
||||
| v0.5.70 | heartbeat 로깅 강화 | API step count 동결 확인 |
|
||||
| v0.5.71 | heartbeat 3 poll마다 실행, HB-CAPTURE 추가 | API가 진행중 step 미반환 재확인 |
|
||||
| v0.5.72 | Observer DOM relay 재활성화 | AG 재시작 필요 (Observer HTML 캐시) |
|
||||
| v0.5.74 | 사용자 메시지 셀렉터 추가 (`.select-text.rounded-lg`) | ✅ 사용자 메시지 릴레이 작동 |
|
||||
| v0.5.76 | DOM 탐색 depth 5→10, `pre.font-mono` 우선 탐색 | Observer HTML 업데이트 필요 |
|
||||
| v0.5.77 | WS response 파일 작성 (pollResponseGroup용) | Retry 클릭 경로 추가 |
|
||||
| v0.5.78 | `_from_ws` 마커로 processResponseFile 삭제 방지 | ✅ Retry auto-approve 작동 |
|
||||
| v0.5.79 | sibling 탐색 추가 + thinking 블록 필터링 | ✅ 명령어 컨텍스트 부분 추출 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 남은 작업 (TODO)
|
||||
|
||||
- [x] AG 재시작하여 Observer 반영 확인 — ✅ v0.5.72 작동 확인
|
||||
- [x] Observer의 AI 응답 릴레이가 작동하는지 Discord에서 확인 — ✅ 작동
|
||||
- [x] 사용자 메시지 셀렉터 추가 — ✅ v0.5.74
|
||||
- [x] Retry auto-approve 흐름 복구 — ✅ v0.5.78 (_from_ws 마커)
|
||||
- [x] 명령어 컨텍스트 sibling 탐색 — ✅ v0.5.79
|
||||
- [x] Thinking 블록 필터링 — ✅ v0.5.79
|
||||
- [ ] 명령어 컨텍스트 추출 타이밍 이슈 (DOM 렌더링 전 scan 시 추출 실패) #636
|
||||
- [ ] Observer pollResponseGroup 미시작 케이스 (trigger-click 선점)
|
||||
- [ ] AI 응답이 마지막 블록만 캡처되는 문제 개선 (전문 캡처)
|
||||
99
.agents/references/tech-stack.md
Normal file
99
.agents/references/tech-stack.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# Tech Stack
|
||||
|
||||
> AI 에이전트는 구현 전 이 문서를 확인하여 올바른 기술/버전을 사용합니다.
|
||||
|
||||
## 언어 & 런타임
|
||||
|
||||
| 항목 | 버전 | 경로/비고 |
|
||||
|------|------|-----------|
|
||||
| Python | 3.12 (miniforge3) | `C:\ProgramData\miniforge3\envs\gravity_control\python.exe` |
|
||||
| Node.js | 시스템 설치 | `node`, `npm` (PowerShell에서 `cmd /c npm` 권장) |
|
||||
| TypeScript | 5.3+ | `extension/src/*.ts` → `tsc` → `extension/out/*.js` |
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Python은 **반드시** 위 miniforge3 경로를 사용. WindowsApps의 python stub은 동작하지 않음.
|
||||
|
||||
## 프레임워크 & 라이브러리
|
||||
|
||||
### Python (서버)
|
||||
|
||||
| 패키지 | 버전 | 용도 |
|
||||
|--------|------|------|
|
||||
| discord.py | 2.x | Discord 봇 (슬래시 명령, 버튼 UI, 이벤트) |
|
||||
| aiohttp | 3.x | Gateway HTTP 서버 + WebSocket endpoint |
|
||||
| watchdog | - | Brain 디렉토리 파일시스템 감시 |
|
||||
| python-dotenv | - | .env 파일 로드 |
|
||||
| PyJWT | - | ❌ 미사용 (자체 HMAC-SHA256 구현) |
|
||||
|
||||
### TypeScript (Extension)
|
||||
|
||||
| 패키지 | 용도 |
|
||||
|--------|------|
|
||||
| @types/vscode | VS Code Extension API 타입 |
|
||||
| @types/node | Node.js 타입 |
|
||||
| typescript | 컴파일러 |
|
||||
| ws | WebSocket Hub 연결 (`.vscodeignore`에 `!node_modules/ws/**` 필수) |
|
||||
| antigravity-sdk | AG RPC 호출 (로컬 임베드 `sdk/`) |
|
||||
|
||||
## 패키지 관리
|
||||
|
||||
| 측 | 도구 | 파일 |
|
||||
|----|------|------|
|
||||
| Python | pip | `requirements.txt` |
|
||||
| Extension | npm | `extension/package.json` |
|
||||
|
||||
## 개발 도구 & 명령어
|
||||
|
||||
| 작업 | 명령어 |
|
||||
|------|--------|
|
||||
| **봇 실행** | `C:\ProgramData\miniforge3\envs\gravity_control\python.exe main.py` |
|
||||
| **봇 실행 (gateway)** | `.env`에서 `BOT_MODE=gateway` 설정 후 위 명령 |
|
||||
| **Extension 구문 검사** | `cd extension && npx tsc --noEmit` |
|
||||
| **Extension 컴파일** | `cd extension && cmd /c npm run compile` |
|
||||
| **Extension VSIX** | `cd extension && npx @vscode/vsce package --no-dependencies` |
|
||||
| **Python 구문 검사** | `python -c "import ast; [ast.parse(open(f).read()) for f in ['bot.py','hub.py',...]]"` |
|
||||
| **Hub WS 테스트** | `python tests/test_ws_hub.py` (서버 기동 상태에서) |
|
||||
|
||||
## 환경 변수 (.env)
|
||||
|
||||
### 필수
|
||||
|
||||
| 변수명 | 용도 | 기본값 |
|
||||
|--------|------|--------|
|
||||
| DISCORD_TOKEN | Discord 봇 토큰 | (필수) |
|
||||
| DISCORD_GUILD_ID | Discord 서버 ID | (필수) |
|
||||
|
||||
### 선택
|
||||
|
||||
| 변수명 | 용도 | 기본값 |
|
||||
|--------|------|--------|
|
||||
| BRAIN_PATH | AG 브레인 경로 | `~/.gemini/antigravity/brain` |
|
||||
| BOT_MODE | `local` / `gateway` | `local` |
|
||||
| DEBOUNCE_SECONDS | Watcher 디바운스 간격 | `5` |
|
||||
| PROJECT_NAME | 프로젝트 이름 | `gravity_control` |
|
||||
|
||||
### Gateway 모드 전용
|
||||
|
||||
| 변수명 | 용도 | 기본값 |
|
||||
|--------|------|--------|
|
||||
| GATEWAY_PORT | Gateway HTTP/WS 포트 | `8585` |
|
||||
| GATEWAY_API_KEY | REST API 인증 키 | (미설정 시 인증 미사용) |
|
||||
| GRAVITY_HUB_SECRET | WS Hub JWT 서명 시크릿 (64char hex) | (미설정 시 인증 생략) |
|
||||
| GRAVITY_REGISTRATION_CODE | Extension 등록 코드 (32char hex) | (미설정 시 인증 생략) |
|
||||
|
||||
## Extension VS Code 설정
|
||||
|
||||
| 설정 키 | 용도 |
|
||||
|---------|------|
|
||||
| `gravityBridge.bridgePath` | Bridge 디렉토리 경로 |
|
||||
| `gravityBridge.projectName` | 프로젝트 이름 (기본: git remote) |
|
||||
| `gravityBridge.hubUrl` | Hub WS URL (예: `ws://localhost:8585/ws`) |
|
||||
| `gravityBridge.registrationCode` | Hub 등록 코드 |
|
||||
|
||||
## 빌드 산출물
|
||||
|
||||
| 항목 | 경로 | 설명 |
|
||||
|------|------|------|
|
||||
| VSIX | `extension/gravity-bridge-{ver}.vsix` | VS Code 확장 패키지 |
|
||||
| JS 출력 | `extension/out/*.js` | TypeScript 컴파일 결과물 |
|
||||
| SDK 복사 | `extension/out/sdk/` | compile 시 자동 복사 |
|
||||
40
.agents/workflows/check-gitea.md
Normal file
40
.agents/workflows/check-gitea.md
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
description: Gitea API로 저장소 커밋/이슈/PR 현황을 조회하는 워크플로우
|
||||
---
|
||||
|
||||
# Gitea 저장소 현황 조회
|
||||
|
||||
서비스 정보는 `.agents/workflows/services.md` 참조.
|
||||
|
||||
// turbo-all
|
||||
|
||||
## 절차
|
||||
|
||||
1. 최근 커밋 조회 (최신 10개):
|
||||
```powershell
|
||||
$h = @{Authorization="token 3a01b4b15a39921572e64c413353e870d4d2161b"}
|
||||
$commits = Invoke-RestMethod -Uri "https://git.variet.net/api/v1/repos/Variet/gravity_control/commits?limit=10&sha=main" -Headers $h
|
||||
$commits | ForEach-Object { Write-Host "$($_.sha.Substring(0,7)) $($_.commit.message.Split("`n")[0])" }
|
||||
```
|
||||
|
||||
2. 열린 이슈 조회:
|
||||
```powershell
|
||||
$h = @{Authorization="token 3a01b4b15a39921572e64c413353e870d4d2161b"}
|
||||
$issues = Invoke-RestMethod -Uri "https://git.variet.net/api/v1/repos/Variet/gravity_control/issues?state=open&type=issues" -Headers $h
|
||||
$issues | ForEach-Object { Write-Host "#$($_.number) $($_.title)" }
|
||||
```
|
||||
|
||||
3. Wiki 페이지 목록:
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\gravity_control\python.exe .agents\workflows\helpers\wiki_helper.py list
|
||||
```
|
||||
|
||||
4. Wiki 페이지 읽기:
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\gravity_control\python.exe .agents\workflows\helpers\wiki_helper.py read "Architecture"
|
||||
```
|
||||
|
||||
5. Wiki 페이지 업데이트:
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\gravity_control\python.exe .agents\workflows\helpers\wiki_helper.py update "페이지-제목" /tmp/wiki_content.md
|
||||
```
|
||||
41
.agents/workflows/check-vikunja.md
Normal file
41
.agents/workflows/check-vikunja.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
description: Vikunja API로 프로젝트 태스크 현황을 조회하는 워크플로우
|
||||
---
|
||||
|
||||
# Vikunja 태스크 현황 조회
|
||||
|
||||
서비스 정보는 `.agents/workflows/services.md` 참조.
|
||||
|
||||
// turbo-all
|
||||
|
||||
## 절차
|
||||
|
||||
1. 전체 목록:
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\gravity_control\python.exe .agents\workflows\helpers\vikunja_helper.py list
|
||||
```
|
||||
|
||||
2. TODO만:
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\gravity_control\python.exe .agents\workflows\helpers\vikunja_helper.py list todo
|
||||
```
|
||||
|
||||
3. DONE만:
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\gravity_control\python.exe .agents\workflows\helpers\vikunja_helper.py list done
|
||||
```
|
||||
|
||||
4. 태스크 완료 처리 (**⚠️ 반드시 이 방법 사용 — 직접 API 호출 금지**):
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\gravity_control\python.exe .agents\workflows\helpers\vikunja_helper.py done {TASK_ID}
|
||||
```
|
||||
|
||||
5. 새 태스크 생성:
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\gravity_control\python.exe .agents\workflows\helpers\vikunja_helper.py create "제목" "설명" --labels Backend,Priority:High
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> **절대로** `Invoke-RestMethod -Method Post -Body '{"done": true}'` 같은 직접 API 호출을 사용하지 마세요.
|
||||
> Vikunja API는 POST 시 body에 포함되지 않은 필드를 빈값으로 덮어씁니다.
|
||||
> `vikunja_helper.py`는 항상 GET → 기존 필드 보존 → POST 패턴을 사용합니다.
|
||||
52
.agents/workflows/debug.md
Normal file
52
.agents/workflows/debug.md
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
description: 에러/버그 발생 시 체계적 디버깅 워크플로우 (에러, 안돼요, 왜 안돼, 버그, 디버그, 수정)
|
||||
---
|
||||
|
||||
# Debug Workflow
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 추측으로 코드를 수정하지 마세요. 반드시 이 순서를 따릅니다.
|
||||
|
||||
## 1단계: 정보 수집 (추측 금지)
|
||||
|
||||
- [ ] 에러 메시지 **전문** 확인 (절대 잘라내지 않기)
|
||||
- [ ] 관련 로그 파일 확인
|
||||
- [ ] 환경 정보 확인 (OS, Node/Python 버전, 의존성 버전 등)
|
||||
- [ ] 에러가 발생하는 **정확한 입력/조건** 파악
|
||||
|
||||
## 2단계: Known Issues 확인
|
||||
|
||||
`.agents/references/known-issues.md`를 읽고 동일하거나 유사한 문제가 있는지 확인합니다.
|
||||
|
||||
> [!CAUTION]
|
||||
> **known-issues 확인 없이 해결 시도를 시작하지 마세요.**
|
||||
> 이미 해결된 문제를 다시 삽질하는 것은 시간 낭비입니다.
|
||||
|
||||
## 3단계: 근본 원인 분석
|
||||
|
||||
- [ ] 에러가 발생하는 **정확한 코드 위치** 확인
|
||||
- [ ] 가설을 세우고, 가설을 검증할 수 있는 **최소한의 테스트** 수행
|
||||
- [ ] 가설이 틀렸다면 **즉시 다른 가설로 전환**
|
||||
|
||||
> [!WARNING]
|
||||
> **동일한 접근을 2회 초과 시도하지 마세요.**
|
||||
> 2회 실패 시 유저에게 보고하고 판단을 요청합니다.
|
||||
> 보고 내용: 시도한 것 / 실패한 것 / 원인 가설 / 다음 제안
|
||||
|
||||
## 4단계: 수정 및 검증
|
||||
|
||||
- [ ] 수정 적용
|
||||
- [ ] 동일 에러가 재현되지 않는지 확인
|
||||
- [ ] 사이드 이펙트(다른 기능에 영향) 없는지 확인
|
||||
|
||||
## 5단계: 기록
|
||||
|
||||
- [ ] `known-issues.md`에 새 항목 추가 (아래 포맷 사용)
|
||||
|
||||
```markdown
|
||||
### [날짜] [키워드] — 한줄 요약
|
||||
- **증상**: 무엇이 잘못되었는가
|
||||
- **원인**: 근본 원인
|
||||
- **해결**: 올바른 해결 방법
|
||||
- **주의**: 재발 방지를 위한 교훈
|
||||
```
|
||||
165
.agents/workflows/end.md
Normal file
165
.agents/workflows/end.md
Normal file
@@ -0,0 +1,165 @@
|
||||
---
|
||||
description: 세션 종료 시 devlog 기록 + git commit + Vikunja 동기화 (끝, 마무리, 커밋해, 완료)
|
||||
---
|
||||
|
||||
# 세션 종료 프로토콜
|
||||
|
||||
작업 완료, "끝", "마무리", "커밋해" 등 요청 시 이 워크플로우를 실행합니다.
|
||||
|
||||
// turbo-all
|
||||
|
||||
## 0. 학습 기록 (실패/시행착오 저장)
|
||||
|
||||
이번 세션에서 발생한 실패, 시행착오, 새로 알게 된 사실을 정리합니다:
|
||||
|
||||
- [ ] `.agents/references/known-issues.md`에 추가할 항목이 있는지 확인
|
||||
- [ ] 있다면 아래 포맷으로 추가:
|
||||
|
||||
```markdown
|
||||
### [날짜] [키워드] — 한줄 요약
|
||||
- **증상**: ...
|
||||
- **원인**: ...
|
||||
- **해결**: ...
|
||||
- **주의**: ...
|
||||
```
|
||||
|
||||
## 1. Devlog 기록
|
||||
|
||||
### Index 업데이트 (필수 — 매 작업)
|
||||
|
||||
오늘 날짜의 index 파일에 완료된 작업 1줄을 추가합니다.
|
||||
|
||||
- **파일**: `docs/devlog/YYYY-MM-DD.md`
|
||||
- **형식**:
|
||||
```markdown
|
||||
| NNN | HH:MM | 작업 설명 | `커밋해시` | ✅ 또는 🔧 |
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> - ✅ = 완료, 🔧 = 미완료 (다음 세션에서 이어받기)
|
||||
> - 파일이 없으면 새로 생성 (테이블 헤더 포함)
|
||||
|
||||
### Entry 작성 (선택적 — 필요할 때만)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Entry는 **git/Vikunja/wiki에 없는 정보**가 있을 때만 작성합니다.
|
||||
|
||||
**Entry 작성 기준:**
|
||||
- ✅ 설계 결정이 있었을 때 (왜 A가 아닌 B를 선택했는지)
|
||||
- ✅ 미완료 사항이 있을 때 (다음 세션이 이어받아야 할 맥락)
|
||||
- ✅ 삽질/트러블슈팅이 있었을 때 (같은 실수 방지)
|
||||
|
||||
**Entry 불필요:**
|
||||
- ❌ 단순 버그 픽스 (커밋 메시지로 충분)
|
||||
- ❌ 문서 업데이트 (git diff로 충분)
|
||||
- ❌ 이미 Vikunja 태스크에 상세 설명이 있는 경우
|
||||
|
||||
**Entry 파일**: `docs/devlog/entries/YYYYMMDD-NNN.md`
|
||||
```markdown
|
||||
# 작업 제목
|
||||
|
||||
- **시간**: YYYY-MM-DD HH:MM~HH:MM
|
||||
- **Commit**: `해시`
|
||||
- **Vikunja**: #태스크번호 → done/진행중
|
||||
|
||||
## 결정 사항
|
||||
- 왜 이 방식을 선택했는지
|
||||
|
||||
## 미완료
|
||||
- 남은 작업 (있을 경우)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Vikunja 동기화
|
||||
|
||||
> [!CAUTION]
|
||||
> **반드시 `vikunja_helper.py` 사용.** 직접 API 호출 금지.
|
||||
> Vikunja API는 POST 시 body에 없는 필드를 빈값으로 덮어씁니다.
|
||||
|
||||
### 2-1. 커밋 전수 검사
|
||||
|
||||
이번 세션의 **모든 커밋을 하나씩 검사**하고 Vikunja에 매핑합니다.
|
||||
|
||||
```powershell
|
||||
git log --oneline -20
|
||||
```
|
||||
|
||||
| 커밋 유형 | Vikunja 액션 |
|
||||
|-----------|-------------|
|
||||
| 기존 태스크 해당 작업 **완료** | `C:\ProgramData\miniforge3\envs\gravity_control\python.exe .agents\workflows\helpers\vikunja_helper.py done {ID}` |
|
||||
| 신규 작업 완료 (기존 태스크 없음) | `C:\ProgramData\miniforge3\envs\gravity_control\python.exe .agents\workflows\helpers\vikunja_helper.py create "제목" "설명" --done --labels Backend,Priority:High` |
|
||||
| 작업 중 발견된 **미완료 TODO** | `C:\ProgramData\miniforge3\envs\gravity_control\python.exe .agents\workflows\helpers\vikunja_helper.py create "제목" "설명" --labels Backend,Priority:Mid` |
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 모든 커밋이 기존 또는 신규 태스크에 매핑되었는지 확인.
|
||||
|
||||
### 2-2. 완료 처리
|
||||
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\gravity_control\python.exe .agents\workflows\helpers\vikunja_helper.py done {TASK_ID}
|
||||
```
|
||||
|
||||
### 2-3. 신규 태스크 생성
|
||||
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\gravity_control\python.exe .agents\workflows\helpers\vikunja_helper.py create "제목" "설명" --labels Backend,Priority:High
|
||||
```
|
||||
|
||||
### 라벨 규칙
|
||||
|
||||
**영역 (필수 1개 이상):** `Backend` / `Frontend` / `Engine` / `Infra` / `Test`
|
||||
**우선순위 (필수 1개):** `Priority:High` / `Priority:Mid` / `Priority:Low`
|
||||
|
||||
---
|
||||
|
||||
## 3. Wiki 동기화 (해당 시에만)
|
||||
|
||||
| 코드 변경 | 대상 Wiki |
|
||||
|-----------|----------|
|
||||
| 서버 변경 | Architecture |
|
||||
| 프론트엔드 변경 | Architecture |
|
||||
| 인프라 변경 | Architecture |
|
||||
| 새 모듈/패키지 추가 | Architecture |
|
||||
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\gravity_control\python.exe .agents\workflows\helpers\wiki_helper.py update "Architecture" /tmp/wiki_content.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Git Commit & Push
|
||||
|
||||
```powershell
|
||||
git add -A
|
||||
git status --short
|
||||
```
|
||||
```powershell
|
||||
git commit -m "커밋 메시지"
|
||||
```
|
||||
```powershell
|
||||
git push origin main
|
||||
```
|
||||
|
||||
**커밋 메시지 컨벤션:**
|
||||
```
|
||||
<type>(<scope>): <description>
|
||||
|
||||
type: feat|fix|refactor|test|docs|chore|ci|infra
|
||||
scope: (선택)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 최종 체크리스트
|
||||
|
||||
> [!WARNING]
|
||||
> 아래 항목 중 하나라도 누락되면 세션 종료를 완료할 수 없습니다.
|
||||
|
||||
- [ ] known-issues 업데이트됨 (새 이슈가 있었다면)
|
||||
- [ ] devlog index 업데이트됨
|
||||
- [ ] devlog entry 작성됨 (필요한 경우만)
|
||||
- [ ] Vikunja 태스크 생성/완료 처리됨 (커밋 전수 검사 기반)
|
||||
- [ ] Wiki 동기화됨 (아키텍처 변경이 있었다면)
|
||||
- [ ] git push 완료
|
||||
- [ ] 사용자에게 완료 보고
|
||||
160
.agents/workflows/helpers/analyze_dom.py
Normal file
160
.agents/workflows/helpers/analyze_dom.py
Normal file
@@ -0,0 +1,160 @@
|
||||
"""Analyze AG Native DOM structure to find AI response containers."""
|
||||
import json, os, sys
|
||||
|
||||
def load_dump():
|
||||
bridge = os.path.join(os.path.expanduser('~'), '.gemini', 'antigravity', 'bridge')
|
||||
# Try deep-inspect result first, then dump_html
|
||||
for fname in ['deep-inspect-result.json', 'dump_html.json']:
|
||||
fpath = os.path.join(bridge, fname)
|
||||
if os.path.exists(fpath):
|
||||
print(f"Loading: {fname} ({os.path.getsize(fpath)} bytes)")
|
||||
with open(fpath, 'r', encoding='utf-8-sig') as f:
|
||||
return json.load(f), fname
|
||||
return None, None
|
||||
|
||||
def find_text_containers(node, path="", depth=0, results=None):
|
||||
"""Recursively find nodes with substantial text content (potential AI response containers)."""
|
||||
if results is None:
|
||||
results = []
|
||||
if not isinstance(node, dict):
|
||||
return results
|
||||
|
||||
tag = node.get('tag', '')
|
||||
cls = node.get('cls', '')
|
||||
text = node.get('text', '')
|
||||
attrs = node.get('attrs', {})
|
||||
children = node.get('children', [])
|
||||
|
||||
cur_path = f"{path}/{tag}"
|
||||
if cls:
|
||||
short_cls = cls[:60]
|
||||
cur_path += f".{short_cls}"
|
||||
|
||||
# Look for nodes with long text (potential AI responses)
|
||||
if text and len(text) > 50:
|
||||
results.append({
|
||||
'path': cur_path,
|
||||
'depth': depth,
|
||||
'tag': tag,
|
||||
'cls': cls[:100],
|
||||
'text_len': len(text),
|
||||
'text_preview': text[:120],
|
||||
'attrs': {k:v for k,v in attrs.items() if k not in ('style',)}
|
||||
})
|
||||
|
||||
for child in children:
|
||||
find_text_containers(child, cur_path, depth+1, results)
|
||||
|
||||
return results
|
||||
|
||||
def find_by_class_pattern(node, patterns, path="", depth=0, results=None):
|
||||
"""Find nodes matching class patterns."""
|
||||
if results is None:
|
||||
results = []
|
||||
if not isinstance(node, dict):
|
||||
return results
|
||||
|
||||
tag = node.get('tag', '')
|
||||
cls = node.get('cls', '')
|
||||
attrs = node.get('attrs', {})
|
||||
children = node.get('children', [])
|
||||
text = node.get('text', '')
|
||||
|
||||
cur_path = f"{path}/{tag}"
|
||||
|
||||
for pattern in patterns:
|
||||
if pattern.lower() in cls.lower() or pattern.lower() in str(attrs).lower():
|
||||
child_count = len(children)
|
||||
results.append({
|
||||
'path': cur_path,
|
||||
'depth': depth,
|
||||
'tag': tag,
|
||||
'cls': cls[:150],
|
||||
'pattern': pattern,
|
||||
'text_preview': text[:80] if text else '',
|
||||
'child_count': child_count,
|
||||
'attrs': {k:v[:50] for k,v in attrs.items() if k != 'style'}
|
||||
})
|
||||
|
||||
for child in children:
|
||||
find_by_class_pattern(child, patterns, cur_path, depth+1, results)
|
||||
|
||||
return results
|
||||
|
||||
def analyze_chat_structure(node, path="", depth=0):
|
||||
"""Find the chat/conversation area by looking at the main layout."""
|
||||
if not isinstance(node, dict):
|
||||
return
|
||||
tag = node.get('tag', '')
|
||||
cls = node.get('cls', '')
|
||||
children = node.get('children', [])
|
||||
text = node.get('text', '')
|
||||
attrs = node.get('attrs', {})
|
||||
|
||||
# Print interesting structural nodes at shallow depths
|
||||
if depth <= 6:
|
||||
child_count = len(children)
|
||||
has_text = bool(text and len(text) > 10)
|
||||
info = f"{' '*depth}{tag}"
|
||||
if cls:
|
||||
info += f" .{cls[:80]}"
|
||||
if attrs:
|
||||
attr_str = ' '.join(f'{k}={v[:30]}' for k,v in attrs.items() if k not in ('style','class'))
|
||||
if attr_str:
|
||||
info += f" [{attr_str}]"
|
||||
info += f" children={child_count}"
|
||||
if has_text:
|
||||
info += f" text=\"{text[:50]}...\""
|
||||
print(info)
|
||||
|
||||
for child in children:
|
||||
analyze_chat_structure(child, f"{path}/{tag}", depth+1)
|
||||
|
||||
data, fname = load_dump()
|
||||
if not data:
|
||||
print("No dump file found!")
|
||||
sys.exit(1)
|
||||
|
||||
# Handle both dump formats
|
||||
body = data.get('body', data)
|
||||
qi = data.get('quickInfo', {})
|
||||
|
||||
print("=" * 60)
|
||||
print("QUICK INFO")
|
||||
print("=" * 60)
|
||||
if qi:
|
||||
for k, v in qi.items():
|
||||
if k == 'buttons':
|
||||
print(f"buttons ({len(v)}):")
|
||||
for b in v[:15]:
|
||||
print(f" [{b.get('tag')}] \"{b.get('text','')[:50]}\" visible={b.get('visible')} cls={b.get('cls','')[:60]}")
|
||||
elif k == 'dataAttrs':
|
||||
print(f"dataAttrs: {v[:30]}")
|
||||
else:
|
||||
print(f"{k}: {v}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("CHAT-RELATED CLASS PATTERNS")
|
||||
print("=" * 60)
|
||||
patterns = ['chat', 'message', 'conversation', 'response', 'answer', 'reply',
|
||||
'markdown', 'prose', 'content', 'panel', 'agent', 'assistant',
|
||||
'planner', 'step', 'trajectory', 'bot', 'ai-', 'turn']
|
||||
matches = find_by_class_pattern(body, patterns)
|
||||
for m in matches:
|
||||
print(f" [{m['tag']}] cls=\"{m['cls']}\" pattern={m['pattern']} children={m['child_count']} {m.get('attrs',{})}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("LONG TEXT NODES (potential AI responses)")
|
||||
print("=" * 60)
|
||||
texts = find_text_containers(body)
|
||||
texts.sort(key=lambda x: x['text_len'], reverse=True)
|
||||
for t in texts[:20]:
|
||||
print(f" [{t['tag']}] depth={t['depth']} len={t['text_len']} cls=\"{t['cls'][:60]}\"")
|
||||
print(f" text: \"{t['text_preview']}\"")
|
||||
if t['attrs']:
|
||||
print(f" attrs: {t['attrs']}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("DOM TREE (depth<=6)")
|
||||
print("=" * 60)
|
||||
analyze_chat_structure(body)
|
||||
19
.agents/workflows/helpers/parse_dump.py
Normal file
19
.agents/workflows/helpers/parse_dump.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import json, os, sys
|
||||
|
||||
dump_path = os.path.join(os.path.expanduser('~'), '.gemini', 'antigravity', 'bridge', 'dump_html.json')
|
||||
with open(dump_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
|
||||
qi = data.get('quickInfo', {})
|
||||
print('=== Quick Info ===')
|
||||
print('hasConversationView:', qi.get('hasConversationView'))
|
||||
print('hasStepIndex:', qi.get('hasStepIndex'))
|
||||
print('hasBotColor:', qi.get('hasBotColor'))
|
||||
print('hasMarkdownBody:', qi.get('hasMarkdownBody'))
|
||||
print('hasProse:', qi.get('hasProse'))
|
||||
print('totalElements:', qi.get('totalElements'))
|
||||
print('dataTestIds:', qi.get('dataTestIds'))
|
||||
print('dataAttrs (first 20):', qi.get('dataAttrs', [])[:20])
|
||||
print('buttons (first 10):')
|
||||
for b in qi.get('buttons', [])[:10]:
|
||||
print(f" [{b.get('tag')}] {b.get('text', '')[:60]} visible={b.get('visible')}")
|
||||
83
.agents/workflows/helpers/search_dom.py
Normal file
83
.agents/workflows/helpers/search_dom.py
Normal file
@@ -0,0 +1,83 @@
|
||||
"""Search AG Native DOM dump for chat content and buttons."""
|
||||
import json, os
|
||||
|
||||
fpath = os.path.join(os.path.expanduser('~'), '.gemini', 'antigravity', 'bridge', 'dump_html_5.json')
|
||||
with open(fpath, 'r', encoding='utf-8-sig') as f:
|
||||
data = json.load(f)
|
||||
|
||||
body = data.get('body', data.get('bodyTree', {}))
|
||||
qi = data.get('quickInfo', {})
|
||||
|
||||
# Show all buttons
|
||||
print('=== BUTTONS ===')
|
||||
for b in qi.get('buttons', []):
|
||||
print(f' [{b["tag"]}] "{b["text"][:60]}" visible={b["visible"]} cls={b.get("cls","")[:80]}')
|
||||
|
||||
# Data attrs
|
||||
print('\n=== DATA ATTRS ===')
|
||||
for attr in qi.get('dataAttrs', []):
|
||||
print(f' {attr}')
|
||||
|
||||
# Recursive search for nodes by text
|
||||
def find_nodes_by_text(node, target, path='', results=None, depth=0):
|
||||
if results is None: results = []
|
||||
if not isinstance(node, dict): return results
|
||||
tag = node.get('tag','')
|
||||
cls = node.get('cls','')
|
||||
text = node.get('text','')
|
||||
children = node.get('children', [])
|
||||
cur = f'{path}/{tag}'
|
||||
if target.lower() in text.lower():
|
||||
results.append({'path': cur, 'depth': depth, 'cls': cls[:80], 'text': text[:80], 'children': len(children)})
|
||||
for c in children:
|
||||
find_nodes_by_text(c, target, cur, results, depth+1)
|
||||
return results
|
||||
|
||||
print('\n=== NODES containing "Always run" ===')
|
||||
matches = find_nodes_by_text(body, 'Always run')
|
||||
for m in matches:
|
||||
print(f' depth={m["depth"]} cls="{m["cls"]}" text="{m["text"]}" children={m["children"]}')
|
||||
|
||||
print('\n=== NODES containing "Always" ===')
|
||||
matches = find_nodes_by_text(body, 'Always')
|
||||
for m in matches:
|
||||
print(f' depth={m["depth"]} cls="{m["cls"]}" text="{m["text"]}" children={m["children"]}')
|
||||
|
||||
# Find ALL text nodes with > 30 chars
|
||||
def find_all_text(node, results=None, depth=0, path=''):
|
||||
if results is None: results = []
|
||||
if not isinstance(node, dict): return results
|
||||
tag = node.get('tag','')
|
||||
cls = node.get('cls','')
|
||||
text = node.get('text','')
|
||||
children = node.get('children', [])
|
||||
if text and len(text) > 30:
|
||||
results.append({'depth': depth, 'tag': tag, 'cls': cls[:80], 'text': text[:100], 'path': f'{path}/{tag}'})
|
||||
for c in children:
|
||||
find_all_text(c, results, depth+1, f'{path}/{tag}')
|
||||
return results
|
||||
|
||||
print('\n=== LONG TEXT NODES (>30 chars) ===')
|
||||
texts = find_all_text(body)
|
||||
texts.sort(key=lambda x: len(x['text']), reverse=True)
|
||||
for t in texts[:25]:
|
||||
print(f' d={t["depth"]} [{t["tag"]}] cls="{t["cls"][:50]}" len={len(t["text"])} "{t["text"][:80]}"')
|
||||
|
||||
# Find nodes with many children (structural containers)
|
||||
def find_containers(node, results=None, depth=0, path=''):
|
||||
if results is None: results = []
|
||||
if not isinstance(node, dict): return results
|
||||
tag = node.get('tag','')
|
||||
cls = node.get('cls','')
|
||||
children = node.get('children', [])
|
||||
if len(children) > 5:
|
||||
results.append({'depth': depth, 'tag': tag, 'cls': cls[:100], 'children': len(children), 'path': f'{path}/{tag}'})
|
||||
for c in children:
|
||||
find_containers(c, results, depth+1, f'{path}/{tag}')
|
||||
return results
|
||||
|
||||
print('\n=== CONTAINERS (>5 children) ===')
|
||||
conts = find_containers(body)
|
||||
conts.sort(key=lambda x: x['children'], reverse=True)
|
||||
for c in conts[:20]:
|
||||
print(f' d={c["depth"]} [{c["tag"]}] children={c["children"]} cls="{c["cls"][:70]}"')
|
||||
109
.agents/workflows/helpers/trace_dom.py
Normal file
109
.agents/workflows/helpers/trace_dom.py
Normal file
@@ -0,0 +1,109 @@
|
||||
"""Trace the DOM path from body to AI response container."""
|
||||
import json, os
|
||||
|
||||
fpath = os.path.join(os.path.expanduser('~'), '.gemini', 'antigravity', 'bridge', 'dump_html_5.json')
|
||||
with open(fpath, 'r', encoding='utf-8-sig') as f:
|
||||
data = json.load(f)
|
||||
|
||||
body = data.get('body', data.get('bodyTree', {}))
|
||||
|
||||
def find_path_to_class(node, target_cls, path=None, depth=0):
|
||||
"""Find the DOM path down to a node with a matching class."""
|
||||
if path is None: path = []
|
||||
if not isinstance(node, dict): return []
|
||||
|
||||
tag = node.get('tag', '')
|
||||
cls = node.get('cls', '')
|
||||
children = node.get('children', [])
|
||||
text = node.get('text', '')
|
||||
attrs = node.get('attrs', {})
|
||||
|
||||
entry = {
|
||||
'depth': depth,
|
||||
'tag': tag,
|
||||
'cls': cls[:120],
|
||||
'children': len(children),
|
||||
'text': text[:60] if text else '',
|
||||
'attrs': {k:v[:40] for k,v in attrs.items() if k not in ('style',)}
|
||||
}
|
||||
|
||||
if target_cls.lower() in cls.lower():
|
||||
return path + [entry]
|
||||
|
||||
for i, child in enumerate(children):
|
||||
result = find_path_to_class(child, target_cls, path + [entry], depth+1)
|
||||
if result:
|
||||
return result
|
||||
|
||||
return []
|
||||
|
||||
# Find path to the AI response container
|
||||
print("=== PATH TO 'leading-relaxed select-text' ===")
|
||||
path = find_path_to_class(body, 'leading-relaxed select-text')
|
||||
for p in path:
|
||||
indent = ' ' * p['depth']
|
||||
print(f'{indent}[{p["tag"]}] cls="{p["cls"]}" children={p["children"]} {p["attrs"]}')
|
||||
if p['text']:
|
||||
print(f'{indent} text: "{p["text"]}"')
|
||||
|
||||
# Now get the full subtree of the AI response container
|
||||
def get_subtree(node, target_cls, depth=0):
|
||||
if not isinstance(node, dict): return None
|
||||
cls = node.get('cls', '')
|
||||
if target_cls.lower() in cls.lower():
|
||||
return node
|
||||
for child in node.get('children', []):
|
||||
result = get_subtree(child, target_cls, depth+1)
|
||||
if result:
|
||||
return result
|
||||
return None
|
||||
|
||||
print("\n=== AI RESPONSE CONTAINER SUBTREE ===")
|
||||
container = get_subtree(body, 'leading-relaxed select-text')
|
||||
if container:
|
||||
def print_tree(node, depth=0, max_depth=4):
|
||||
if not isinstance(node, dict) or depth > max_depth: return
|
||||
tag = node.get('tag','')
|
||||
cls = node.get('cls','')[:80]
|
||||
text = node.get('text','')
|
||||
children = node.get('children', [])
|
||||
indent = ' ' * depth
|
||||
line = f'{indent}[{tag}]'
|
||||
if cls: line += f' cls="{cls}"'
|
||||
line += f' children={len(children)}'
|
||||
if text: line += f' text="{text[:60]}"'
|
||||
print(line)
|
||||
for c in children:
|
||||
print_tree(c, depth+1, max_depth)
|
||||
|
||||
print_tree(container, 0, 3)
|
||||
|
||||
# Also search for the chat panel container - what wraps the entire conversation
|
||||
print("\n=== SEARCH FOR CHAT PANEL WRAPPERS ===")
|
||||
chat_patterns = ['chat', 'antigravity', 'gemini', 'panel', 'agentview', 'sidebar', 'conversation']
|
||||
for pat in chat_patterns:
|
||||
path = find_path_to_class(body, pat)
|
||||
if path:
|
||||
last = path[-1]
|
||||
print(f' Pattern "{pat}" found at depth={last["depth"]} [{last["tag"]}] cls="{last["cls"]}" children={last["children"]}')
|
||||
|
||||
# Find the parent chain from body to the container - look by scanning ALL class names
|
||||
print("\n=== ALL UNIQUE CLASS NAMES (depth <= 12) ===")
|
||||
all_classes = set()
|
||||
def collect_classes(node, depth=0, max_depth=12):
|
||||
if not isinstance(node, dict) or depth > max_depth: return
|
||||
cls = node.get('cls', '')
|
||||
if cls:
|
||||
for c in cls.split():
|
||||
if len(c) > 3 and not c.startswith('{') and 'mtk' not in c:
|
||||
all_classes.add(c)
|
||||
for child in node.get('children', []):
|
||||
collect_classes(child, depth+1, max_depth)
|
||||
|
||||
collect_classes(body)
|
||||
# Print classes sorted, grouped by potential relevance
|
||||
relevant = sorted([c for c in all_classes if any(k in c.lower() for k in
|
||||
['chat', 'message', 'response', 'agent', 'gemini', 'turn', 'model', 'user', 'bot', 'conversation', 'markdown', 'prose', 'text-', 'content'])])
|
||||
print("Relevant classes:")
|
||||
for c in relevant:
|
||||
print(f' {c}')
|
||||
217
.agents/workflows/helpers/vikunja_helper.py
Normal file
217
.agents/workflows/helpers/vikunja_helper.py
Normal file
@@ -0,0 +1,217 @@
|
||||
"""Vikunja safe task updater — preserves existing fields when updating tasks.
|
||||
|
||||
Usage:
|
||||
python vikunja_helper.py done 75 # Mark task #75 as done
|
||||
python vikunja_helper.py done 71 77 78 # Mark multiple tasks done
|
||||
python vikunja_helper.py undone 75 # Mark task #75 as not done
|
||||
python vikunja_helper.py comment 75 "text" # Add comment to task #75
|
||||
python vikunja_helper.py desc 75 "text" # Set description (appends if exists)
|
||||
python vikunja_helper.py create "title" "desc" --labels Backend,Priority:High
|
||||
python vikunja_helper.py create "title" "desc" --done --labels Frontend,Priority:Mid
|
||||
python vikunja_helper.py label 75 Backend Priority:High # Add labels to task
|
||||
python vikunja_helper.py list # List all tasks
|
||||
python vikunja_helper.py list todo # List TODO only
|
||||
python vikunja_helper.py list done # List DONE only
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import io
|
||||
|
||||
# Fix Windows console encoding (cp949 → utf-8)
|
||||
if sys.stdout.encoding != "utf-8":
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
|
||||
# ============================================================
|
||||
# ⚙️ CONFIGURATION — PROJECT_ID만 프로젝트별로 변경하세요
|
||||
# ============================================================
|
||||
API_BASE = "https://plan.variet.net/api/v1"
|
||||
TOKEN = "tk_070f8e0b715e818bb7178c3815ed5389040eddca"
|
||||
PROJECT_ID = 8 # gravity_control project
|
||||
# ============================================================
|
||||
|
||||
HEADERS = {
|
||||
"Authorization": f"Bearer {TOKEN}",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
# Label name → Vikunja label ID mapping
|
||||
# Customize for your project's labels
|
||||
LABEL_MAP = {
|
||||
"Backend": 1, "Frontend": 2, "Engine": 3, "Infra": 4, "Test": 5,
|
||||
"Priority:High": 6, "Priority:Mid": 7, "Priority:Low": 8,
|
||||
"Agent": 17, "Tool": 18, "AI/LLM": 19,
|
||||
}
|
||||
|
||||
|
||||
def api_get(path: str):
|
||||
req = urllib.request.Request(f"{API_BASE}{path}", headers=HEADERS)
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
return json.loads(resp.read().decode("utf-8"))
|
||||
|
||||
|
||||
def api_post(path: str, data: dict):
|
||||
body = json.dumps(data).encode("utf-8")
|
||||
req = urllib.request.Request(f"{API_BASE}{path}", data=body, headers=HEADERS, method="POST")
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
return json.loads(resp.read().decode("utf-8"))
|
||||
|
||||
|
||||
def api_put(path: str, data: dict):
|
||||
body = json.dumps(data).encode("utf-8")
|
||||
req = urllib.request.Request(f"{API_BASE}{path}", data=body, headers=HEADERS, method="PUT")
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
return json.loads(resp.read().decode("utf-8"))
|
||||
|
||||
|
||||
def get_task(task_id: int) -> dict:
|
||||
return api_get(f"/tasks/{task_id}")
|
||||
|
||||
|
||||
def safe_update_task(task_id: int, updates: dict) -> dict:
|
||||
task = get_task(task_id)
|
||||
safe_body = {
|
||||
"title": task.get("title", ""),
|
||||
"description": task.get("description", ""),
|
||||
"priority": task.get("priority", 0),
|
||||
"done": task.get("done", False),
|
||||
}
|
||||
safe_body.update(updates)
|
||||
return api_post(f"/tasks/{task_id}", safe_body)
|
||||
|
||||
|
||||
def mark_done(task_ids: list):
|
||||
for tid in task_ids:
|
||||
result = safe_update_task(tid, {"done": True})
|
||||
title = result.get("title", "?")
|
||||
print(f" ✅ #{tid} → done=True [{title}]")
|
||||
|
||||
|
||||
def mark_undone(task_ids: list):
|
||||
for tid in task_ids:
|
||||
result = safe_update_task(tid, {"done": False})
|
||||
title = result.get("title", "?")
|
||||
print(f" ⬜ #{tid} → done=False [{title}]")
|
||||
|
||||
|
||||
def add_comment(task_id: int, comment: str):
|
||||
result = api_put(f"/tasks/{task_id}/comments", {"comment": comment})
|
||||
print(f" 💬 #{task_id} comment added (id={result.get('id', '?')})")
|
||||
|
||||
|
||||
def set_description(task_id: int, desc: str, append: bool = True):
|
||||
task = get_task(task_id)
|
||||
existing = task.get("description", "") or ""
|
||||
if append and existing:
|
||||
new_desc = existing.rstrip() + "\n\n" + desc
|
||||
else:
|
||||
new_desc = desc
|
||||
result = safe_update_task(task_id, {"description": new_desc})
|
||||
print(f" 📝 #{task_id} description updated [{result.get('title', '?')}]")
|
||||
|
||||
|
||||
def list_tasks(filter_: str = "all"):
|
||||
all_tasks = []
|
||||
page = 1
|
||||
while True:
|
||||
batch = api_get(f"/projects/{PROJECT_ID}/tasks?per_page=50&page={page}")
|
||||
if not batch:
|
||||
break
|
||||
all_tasks.extend(batch)
|
||||
if len(batch) < 50:
|
||||
break
|
||||
page += 1
|
||||
|
||||
if filter_ == "todo":
|
||||
all_tasks = [t for t in all_tasks if not t["done"]]
|
||||
elif filter_ == "done":
|
||||
all_tasks = [t for t in all_tasks if t["done"]]
|
||||
|
||||
all_tasks.sort(key=lambda t: t["id"])
|
||||
for t in all_tasks:
|
||||
status = "✅" if t["done"] else "⬜"
|
||||
desc = (t.get("description") or "")[:50].replace("\n", " ")
|
||||
labels = ", ".join(l["title"] for l in (t.get("labels") or []))
|
||||
print(f" {status} #{t['id']:3d} {t['title'][:40]:<40} [{labels}] {desc}")
|
||||
print(f"\n Total: {len(all_tasks)} tasks")
|
||||
|
||||
|
||||
def add_labels(task_id: int, label_names: list):
|
||||
for name in label_names:
|
||||
label_id = LABEL_MAP.get(name)
|
||||
if not label_id:
|
||||
print(f" ⚠️ Unknown label '{name}'. Valid: {', '.join(LABEL_MAP.keys())}")
|
||||
continue
|
||||
try:
|
||||
api_put(f"/tasks/{task_id}/labels", {"label_id": label_id})
|
||||
print(f" 🏷️ #{task_id} + {name} (id={label_id})")
|
||||
except Exception as e:
|
||||
if "already" in str(e).lower() or "409" in str(e):
|
||||
print(f" 🏷️ #{task_id} already has {name}")
|
||||
else:
|
||||
print(f" ⚠️ #{task_id} label {name} failed: {e}")
|
||||
|
||||
|
||||
def create_task(title: str, description: str = "", done: bool = False, labels: list = None):
|
||||
payload = {"title": title, "description": description}
|
||||
result = api_put(f"/projects/{PROJECT_ID}/tasks", payload)
|
||||
task_id = result["id"]
|
||||
print(f" ✨ #{task_id} created: {result.get('title', '?')}")
|
||||
|
||||
if labels:
|
||||
add_labels(task_id, labels)
|
||||
|
||||
if done:
|
||||
result = safe_update_task(task_id, {"done": True})
|
||||
print(f" ✅ #{task_id} → done=True")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print(__doc__)
|
||||
return
|
||||
|
||||
cmd = sys.argv[1].lower()
|
||||
|
||||
if cmd == "done":
|
||||
ids = [int(x) for x in sys.argv[2:]]
|
||||
mark_done(ids)
|
||||
elif cmd == "undone":
|
||||
ids = [int(x) for x in sys.argv[2:]]
|
||||
mark_undone(ids)
|
||||
elif cmd == "comment":
|
||||
add_comment(int(sys.argv[2]), sys.argv[3])
|
||||
elif cmd == "desc":
|
||||
set_description(int(sys.argv[2]), sys.argv[3])
|
||||
elif cmd == "list":
|
||||
f = sys.argv[2] if len(sys.argv) > 2 else "all"
|
||||
list_tasks(f)
|
||||
elif cmd == "label":
|
||||
if len(sys.argv) < 4:
|
||||
print("Usage: vikunja_helper.py label TASK_ID Label1 Label2 ...")
|
||||
return
|
||||
add_labels(int(sys.argv[2]), sys.argv[3:])
|
||||
elif cmd == "create":
|
||||
title = sys.argv[2] if len(sys.argv) > 2 else ""
|
||||
desc = sys.argv[3] if len(sys.argv) > 3 and not sys.argv[3].startswith("--") else ""
|
||||
is_done = "--done" in sys.argv
|
||||
labels = None
|
||||
for i, arg in enumerate(sys.argv):
|
||||
if arg == "--labels" and i + 1 < len(sys.argv):
|
||||
labels = sys.argv[i + 1].split(",")
|
||||
break
|
||||
if not title:
|
||||
print("Error: title is required")
|
||||
return
|
||||
create_task(title, desc, done=is_done, labels=labels)
|
||||
else:
|
||||
print(f"Unknown command: {cmd}")
|
||||
print(__doc__)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
100
.agents/workflows/helpers/wiki_helper.py
Normal file
100
.agents/workflows/helpers/wiki_helper.py
Normal file
@@ -0,0 +1,100 @@
|
||||
"""Gitea Wiki helper: list, read, create, update wiki pages.
|
||||
|
||||
Usage:
|
||||
wiki_helper.py list — list all pages
|
||||
wiki_helper.py read <title> — read a page
|
||||
wiki_helper.py create <title> <file> — create a page from file
|
||||
wiki_helper.py update <title> <file> — update a page from file
|
||||
"""
|
||||
import sys, io, json, base64, urllib.request, urllib.error
|
||||
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||||
|
||||
# ============================================================
|
||||
# ⚙️ CONFIGURATION — GITEA_REPO만 프로젝트별로 변경하세요
|
||||
# ============================================================
|
||||
GITEA_BASE_URL = "https://git.variet.net"
|
||||
GITEA_OWNER = "Variet"
|
||||
GITEA_REPO = "gravity_control" # ← 프로젝트별 변경 필요
|
||||
GITEA_TOKEN = "3a01b4b15a39921572e64c413353e870d4d2161b"
|
||||
# ============================================================
|
||||
|
||||
BASE = f"{GITEA_BASE_URL}/api/v1/repos/{GITEA_OWNER}/{GITEA_REPO}/wiki"
|
||||
HEADERS = {"Authorization": f"token {GITEA_TOKEN}", "Content-Type": "application/json"}
|
||||
|
||||
def _req(method, path, data=None):
|
||||
url = f"{BASE}{path}"
|
||||
body = json.dumps(data).encode() if data else None
|
||||
req = urllib.request.Request(url, data=body, headers=HEADERS, method=method)
|
||||
try:
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
return json.loads(resp.read().decode())
|
||||
except urllib.error.HTTPError as e:
|
||||
err = e.read().decode()
|
||||
print(f" ⚠️ HTTP {e.code}: {err}")
|
||||
return None
|
||||
|
||||
def _find_sub_url(title):
|
||||
pages = _req("GET", "/pages")
|
||||
if pages:
|
||||
for p in pages:
|
||||
if p.get("title", "").lower() == title.lower():
|
||||
return p.get("sub_url", title)
|
||||
return title
|
||||
|
||||
def list_pages():
|
||||
pages = _req("GET", "/pages")
|
||||
if pages:
|
||||
print(f"=== {len(pages)} Wiki Pages ===")
|
||||
for p in pages:
|
||||
print(f" {p.get('title', '?')}")
|
||||
return pages
|
||||
|
||||
def read_page(title):
|
||||
sub = _find_sub_url(title)
|
||||
page = _req("GET", f"/page/{sub}")
|
||||
if page and page.get("content_base64"):
|
||||
content = base64.b64decode(page["content_base64"]).decode("utf-8")
|
||||
return content
|
||||
return None
|
||||
|
||||
def create_page(title, content):
|
||||
data = {
|
||||
"title": title,
|
||||
"content_base64": base64.b64encode(content.encode()).decode(),
|
||||
}
|
||||
result = _req("POST", "/new", data)
|
||||
if result:
|
||||
print(f" ✅ Created wiki page: {title}")
|
||||
return result
|
||||
|
||||
def update_page(title, content):
|
||||
sub = _find_sub_url(title)
|
||||
data = {
|
||||
"title": title,
|
||||
"content_base64": base64.b64encode(content.encode()).decode(),
|
||||
}
|
||||
result = _req("PATCH", f"/page/{sub}", data)
|
||||
if result:
|
||||
print(f" ✅ Updated wiki page: {title}")
|
||||
return result
|
||||
|
||||
if __name__ == "__main__":
|
||||
cmd = sys.argv[1] if len(sys.argv) > 1 else "list"
|
||||
|
||||
if cmd == "list":
|
||||
list_pages()
|
||||
elif cmd == "read" and len(sys.argv) > 2:
|
||||
content = read_page(sys.argv[2])
|
||||
if content:
|
||||
print(content[:5000])
|
||||
else:
|
||||
print(f" Page '{sys.argv[2]}' not found")
|
||||
elif cmd == "create" and len(sys.argv) > 3:
|
||||
with open(sys.argv[3], "r", encoding="utf-8") as f:
|
||||
create_page(sys.argv[2], f.read())
|
||||
elif cmd == "update" and len(sys.argv) > 3:
|
||||
with open(sys.argv[3], "r", encoding="utf-8") as f:
|
||||
update_page(sys.argv[2], f.read())
|
||||
else:
|
||||
print("Usage: wiki_helper.py list|read <title>|create <title> <file>|update <title> <file>")
|
||||
39
.agents/workflows/pre-task.md
Normal file
39
.agents/workflows/pre-task.md
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
description: 모든 구현 작업 전 실행하는 사전 점검 체크리스트 (pre-task, 준비, 시작 전, 계획, 구현)
|
||||
---
|
||||
|
||||
# Pre-Task Checklist
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 코딩을 시작하기 전에 반드시 이 체크리스트를 순서대로 완료하세요.
|
||||
> 체크리스트를 건너뛸 경우 불필요한 시행착오가 발생합니다.
|
||||
|
||||
## 1단계: 요구사항 정리
|
||||
|
||||
- [ ] 유저 요청을 구체적 작업 항목으로 분해
|
||||
- [ ] 변경 범위(scope)를 명확히 정의 (영향받는 파일/모듈)
|
||||
- [ ] 성공 기준(acceptance criteria) 확인
|
||||
|
||||
## 2단계: 레퍼런스 확인 (추측 금지)
|
||||
|
||||
- [ ] `.agents/references/architecture.md` — 현재 아키텍처 확인
|
||||
- [ ] `.agents/references/tech-stack.md` — 기술 스택 및 버전 확인
|
||||
- [ ] `.agents/references/conventions.md` — 코딩 컨벤션 확인
|
||||
- [ ] `.agents/references/known-issues.md` — 과거 실패 패턴 확인
|
||||
- [ ] 관련 기존 코드 최소 3개 파일 읽기
|
||||
|
||||
> [!CAUTION]
|
||||
> 레퍼런스 문서가 존재하는 주제에 대해 추측하지 마세요.
|
||||
> 문서가 없으면 유저에게 확인을 요청하세요.
|
||||
|
||||
## 3단계: 계획 수립
|
||||
|
||||
- [ ] 변경할 파일 목록 작성
|
||||
- [ ] 의존성 순서 파악 (어떤 파일부터 수정해야 하는가?)
|
||||
- [ ] 리스크 식별 (어디서 실패할 가능성이 높은가?)
|
||||
- [ ] 테스트 방법 결정 (어떻게 검증할 것인가?)
|
||||
|
||||
## 4단계: 유저 확인
|
||||
|
||||
- [ ] 계획을 유저에게 보고하고 승인받기 (변경 파일 3개 이상인 경우)
|
||||
- [ ] 작은 변경은 바로 실행하되, 변경 내용을 명확히 설명
|
||||
128
.agents/workflows/services.md
Normal file
128
.agents/workflows/services.md
Normal file
@@ -0,0 +1,128 @@
|
||||
---
|
||||
description: 프로젝트 연동 서비스 URL, API 키, 프로젝트 정보 참조
|
||||
---
|
||||
|
||||
# 서비스 연동 정보
|
||||
|
||||
> [!CAUTION]
|
||||
> 이 파일에는 API 토큰이 포함되어 있습니다. 프라이빗 레포에서만 사용하세요.
|
||||
|
||||
## 로컬 환경
|
||||
|
||||
| 항목 | 값 |
|
||||
|------|-----|
|
||||
| **Node.js** | 시스템 설치 (`node`, `npm`) |
|
||||
| **Python** | `C:\ProgramData\miniforge3\envs\gravity_control\python.exe` (**항상 이 경로 사용**) |
|
||||
| **Shell** | PowerShell (`curl` = `Invoke-WebRequest` 별칭이므로 반드시 **`curl.exe`** 사용) |
|
||||
|
||||
## Gitea (Git Repository)
|
||||
|
||||
| 항목 | 값 |
|
||||
|------|-----|
|
||||
| **Base URL** | `https://git.variet.net` |
|
||||
| **API Base** | `https://git.variet.net/api/v1` |
|
||||
| **Repo** | `Variet/gravity_control` |
|
||||
| **Token** | `3a01b4b15a39921572e64c413353e870d4d2161b` |
|
||||
| **Auth Header** | `-H "Authorization: token 3a01b4b15a39921572e64c413353e870d4d2161b"` |
|
||||
|
||||
## Vikunja (Task Management)
|
||||
|
||||
| 항목 | 값 |
|
||||
|------|-----|
|
||||
| **Base URL** | `https://plan.variet.net` |
|
||||
| **API Base** | `https://plan.variet.net/api/v1` |
|
||||
| **Project ID** | `8` |
|
||||
| **Token** | `tk_070f8e0b715e818bb7178c3815ed5389040eddca` |
|
||||
| **Auth Header** | `-H "Authorization: Bearer tk_070f8e0b715e818bb7178c3815ed5389040eddca"` |
|
||||
|
||||
## Vikunja 태스크 조회
|
||||
|
||||
> [!TIP]
|
||||
> 태스크 목록은 항상 라이브 조회를 사용합니다. 하드코딩된 매핑은 유지하지 않습니다.
|
||||
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\gravity_control\python.exe .agents\workflows\helpers\vikunja_helper.py list todo
|
||||
```
|
||||
|
||||
## 기타 서비스
|
||||
|
||||
| 서비스 | URL | 용도 |
|
||||
|--------|-----|------|
|
||||
| Uptime Kuma | `https://status.variet.net` | 서비스 모니터링 |
|
||||
| Authentik | `https://auth.variet.net` | SSO 인증 |
|
||||
|
||||
## AI 작업 프로토콜
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 아래 규칙은 모든 작업에 자동 적용됩니다. 유저가 별도 지시하지 않아도 따릅니다.
|
||||
|
||||
### Vikunja = Single Source of Truth (SSOT)
|
||||
|
||||
- **Vikunja가 유일한 작업 현황 관리 도구**입니다.
|
||||
- 로컬 `task.md`는 현재 대화 내 세부 체크리스트용으로만 사용합니다.
|
||||
- 새 TODO 발견 시 → Vikunja에 태스크 생성 (로컬 파일에만 적는 것은 금지)
|
||||
- 작업 완료 시 → Vikunja 태스크 완료 처리 (로컬 체크만 하는 것은 금지)
|
||||
|
||||
### Vikunja 태깅 규칙
|
||||
|
||||
태스크 생성 시 반드시 아래 라벨을 적절히 부여합니다:
|
||||
|
||||
**영역 라벨 (필수, 1개 이상):**
|
||||
|
||||
| ID | 라벨 | 적용 대상 |
|
||||
|:--:|-------|-----------:|
|
||||
| 1 | `Backend` | 서버, DB, API |
|
||||
| 2 | `Frontend` | UI, 웹 프론트엔드 |
|
||||
| 3 | `Engine` | 핵심 엔진/로직 |
|
||||
| 4 | `Infra` | Docker, CI/CD, 모니터링 |
|
||||
| 5 | `Test` | 테스트, E2E |
|
||||
|
||||
**우선순위 라벨 (필수, 1개):**
|
||||
|
||||
| ID | 라벨 | 기준 |
|
||||
|:--:|-------|------:|
|
||||
| 6 | `Priority:High` | 핵심 기능 미완성, 블로커 |
|
||||
| 7 | `Priority:Mid` | 기능 개선, UX 향상, 리팩터링 |
|
||||
| 8 | `Priority:Low` | nice-to-have, 문서, 코드 정리 |
|
||||
|
||||
**태스크 제목 규칙:**
|
||||
- 한글 + 핵심 키워드 (예: `WebSocket 재연결 로직 구현`)
|
||||
- 50자 이내
|
||||
|
||||
### 작업 시작 시
|
||||
1. `git pull` 으로 최신 코드 동기화
|
||||
2. Vikunja 태스크 조회 (`/check-vikunja`) → 관련 태스크 ID 확인
|
||||
3. 관련 태스크가 있으면 Vikunja에서 진행중 표시
|
||||
4. 관련 태스크가 없으면 Vikunja에 새 태스크 생성 (태깅 규칙 준수)
|
||||
|
||||
### 작업 중
|
||||
5. 의미 있는 단위마다 자주 커밋 (대규모 변경을 한번에 커밋하지 않음)
|
||||
6. 커밋 메시지 규칙:
|
||||
- `feat:`, `fix:`, `refactor:`, `test:`, `docs:`, `chore:`, `ci:` 접두사 사용
|
||||
- 관련 Vikunja 태스크가 있으면 `#task-{ID}` 참조 포함
|
||||
- 예: `feat(server): WebSocket 재연결 로직 #task-21`
|
||||
|
||||
### 작업 완료 시
|
||||
7. 모든 변경사항 커밋 + `git push`
|
||||
8. Vikunja 태스크 완료 처리 (**반드시 `vikunja_helper.py` 사용**):
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\gravity_control\python.exe .agents\workflows\helpers\vikunja_helper.py done {TASK_ID}
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> **직접 `Invoke-RestMethod -Body '{"done": true}'` 사용 금지!**
|
||||
> Vikunja API는 POST 시 body에 없는 필드를 빈값으로 덮어씁니다.
|
||||
|
||||
9. 작업 중 발견된 새 TODO → Vikunja에 태스크 생성
|
||||
|
||||
### 멀티 AI 협업 시 추가 규칙
|
||||
- 작업 전 `git pull` 필수 (다른 AI가 push한 변경 반영)
|
||||
- 같은 파일을 동시에 수정하지 않음
|
||||
- 공유 인터페이스 수정 시 즉시 commit + push
|
||||
- 충돌 발생 시 유저에게 확인 요청
|
||||
|
||||
## PowerShell 주의사항
|
||||
|
||||
- `curl` → PowerShell에서 `Invoke-WebRequest`의 별칭. **반드시 `curl.exe`** 사용
|
||||
- `npm` → PowerShell에서 실행 정책 문제 시 `cmd /c npm` 사용
|
||||
- JSON 파이프 파싱 시 PowerShell 이스케이핑 문제 → `.py` 스크립트 파일로 만들어 실행 권장
|
||||
66
.agents/workflows/start.md
Normal file
66
.agents/workflows/start.md
Normal file
@@ -0,0 +1,66 @@
|
||||
---
|
||||
description: 세션 시작 시 프로젝트 맥락을 빠르게 복구하는 워크플로우 (시작, continue, 이어서, 작업 시작)
|
||||
---
|
||||
|
||||
# 세션 시작 프로토콜
|
||||
|
||||
새 대화 시작, "continue", "이어서", "작업 시작" 등 요청 시 이 워크플로우를 실행합니다.
|
||||
|
||||
// turbo-all
|
||||
|
||||
## 절차
|
||||
|
||||
### 0. 에이전트 룰 & 맥락 로딩 (자동)
|
||||
|
||||
`.agents/AGENT.md`를 읽고 에이전트 행동 규칙을 로딩합니다.
|
||||
`.agents/references/known-issues.md`를 읽어 최근 이슈를 파악합니다.
|
||||
`.agents/workflows/services.md`의 **로컬 환경** 섹션을 읽고 Python 경로 등 환경 설정을 확인합니다.
|
||||
|
||||
### 1. Devlog 맥락 복구
|
||||
|
||||
오늘 + 어제 devlog index를 읽고 최근 작업 흐름을 파악합니다.
|
||||
|
||||
```powershell
|
||||
$today = Get-Date -Format "yyyy-MM-dd"
|
||||
$yesterday = (Get-Date).AddDays(-1).ToString("yyyy-MM-dd")
|
||||
if (Test-Path "docs\devlog\$today.md") {
|
||||
Write-Host "=== Devlog: $today ==="
|
||||
Get-Content "docs\devlog\$today.md"
|
||||
} elseif (Test-Path "docs\devlog\$yesterday.md") {
|
||||
Write-Host "=== Devlog: $yesterday (no entry for today yet) ==="
|
||||
Get-Content "docs\devlog\$yesterday.md"
|
||||
} else {
|
||||
Write-Host "=== No recent devlog found ==="
|
||||
}
|
||||
```
|
||||
|
||||
미완료(🔧) 항목이 있으면 해당 entry 파일을 읽어 이어받기 맥락을 확보합니다:
|
||||
- Entry 경로: `docs/devlog/entries/YYYYMMDD-NNN.md`
|
||||
|
||||
### 2. Git 상태 확인
|
||||
|
||||
```powershell
|
||||
git status --short
|
||||
```
|
||||
```powershell
|
||||
git log --oneline -5
|
||||
```
|
||||
|
||||
### 3. Vikunja TODO 태스크
|
||||
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\gravity_control\python.exe .agents\workflows\helpers\vikunja_helper.py list todo
|
||||
```
|
||||
|
||||
### 4. 종합 보고
|
||||
|
||||
결과를 종합하여 사용자에게 보고:
|
||||
- 마지막 작업 맥락 + 미완료 항목 (devlog 🔧 기반)
|
||||
- TODO 태스크 목록 (라벨 + 우선순위)
|
||||
- 다음 작업 제안
|
||||
|
||||
**우선순위 판단 기준** (라벨만으로 판단 금지):
|
||||
- P0: 최근 커밋에서 스키마/모델/인터페이스 변경 → 연쇄 영향 점검
|
||||
- P1: 서버 기동/API 응답 장애
|
||||
- P2: 기능 미완성/UX 개선
|
||||
- P3: 정확도 향상, 신규 기능, CI/CD, 문서 정리
|
||||
1
.deps_installed
Normal file
1
.deps_installed
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
9
.dockerignore
Normal file
9
.dockerignore
Normal file
@@ -0,0 +1,9 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.env
|
||||
*.vsix
|
||||
extension/node_modules/
|
||||
extension/out/
|
||||
.deps_installed
|
||||
gravity_control.log
|
||||
*.tar.gz
|
||||
28
.env.example
28
.env.example
@@ -1,17 +1,31 @@
|
||||
# Discord Bot Token
|
||||
# Discord Bot Token (필수)
|
||||
DISCORD_TOKEN=your_discord_bot_token_here
|
||||
|
||||
# Discord Guild (서버) ID — 봇이 채널을 생성할 서버
|
||||
# Discord Guild (서버) ID (필수) — 봇이 채널을 생성할 서버
|
||||
DISCORD_GUILD_ID=
|
||||
|
||||
# Antigravity Brain Path
|
||||
BRAIN_PATH=C:\Users\Certes\.gemini\antigravity\brain
|
||||
# Bridge 디렉토리 (기본값: ~/.gemini/antigravity/bridge)
|
||||
# 보통 수정 불필요 — Extension과 동일 경로 사용
|
||||
BRIDGE_PATH=
|
||||
|
||||
# Antigravity Brain Path (Watcher용)
|
||||
BRAIN_PATH=
|
||||
|
||||
# 세션 활성 판단: 마지막 파일 변경으로부터 이 시간(초) 이내면 활성
|
||||
ACTIVE_TIMEOUT_SECONDS=300
|
||||
|
||||
# Project name (used for Discord channel: AG-{PROJECT_NAME})
|
||||
PROJECT_NAME=gravity_control
|
||||
|
||||
# Watcher Settings
|
||||
DEBOUNCE_SECONDS=2
|
||||
|
||||
# Bot mode: 'local' (default, file-based) or 'gateway' (서버 Docker + WS Hub)
|
||||
BOT_MODE=local
|
||||
|
||||
# Gateway API Key (보안)
|
||||
# 서버와 Collector에 동일한 키를 설정하세요
|
||||
# 생성: python -c "import secrets; print(secrets.token_urlsafe(32))"
|
||||
GATEWAY_API_KEY=
|
||||
|
||||
# Hub WebSocket 인증 (선택 — 미설정 시 인증 생략)
|
||||
# 생성: python -c "import secrets; print(secrets.token_hex(32))"
|
||||
GRAVITY_HUB_SECRET=
|
||||
GRAVITY_REGISTRATION_CODE=
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,8 +11,6 @@ build/
|
||||
.venv/
|
||||
venv/
|
||||
|
||||
# Agents (contains tokens)
|
||||
.agents/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
|
||||
BIN
.gitlog.txt
Normal file
BIN
.gitlog.txt
Normal file
Binary file not shown.
18
Dockerfile
Normal file
18
Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt aiohttp>=3.9.0
|
||||
|
||||
# Copy application code (all Python modules)
|
||||
COPY *.py ./
|
||||
|
||||
# Default environment (can be overridden via docker-compose)
|
||||
ENV BOT_MODE=gateway
|
||||
ENV GATEWAY_PORT=8585
|
||||
|
||||
EXPOSE 8585
|
||||
|
||||
CMD ["python", "main.py"]
|
||||
127
auth.py
Normal file
127
auth.py
Normal file
@@ -0,0 +1,127 @@
|
||||
"""Authentication module — JWT token management for WebSocket Hub.
|
||||
|
||||
Two-stage auth:
|
||||
1. Extension connects with a registration code (built into .vsix at build time)
|
||||
2. Hub validates the code and issues a short-lived JWT session token
|
||||
3. Subsequent reconnections use the JWT token directly
|
||||
|
||||
The master secret is stored server-side only (GRAVITY_HUB_SECRET env var).
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import hmac
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
from base64 import urlsafe_b64decode, urlsafe_b64encode
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Defaults
|
||||
DEFAULT_TOKEN_TTL = 86400 # 24 hours
|
||||
DEFAULT_REGISTRATION_CODE = "" # Set via GRAVITY_REGISTRATION_CODE env var
|
||||
|
||||
|
||||
class TokenManager:
|
||||
"""Manages JWT-like token creation and verification.
|
||||
|
||||
Uses HMAC-SHA256 for signing. Tokens contain:
|
||||
- project: project name scope
|
||||
- pc: PC identifier (hostname or custom name)
|
||||
- iat: issued at (unix timestamp)
|
||||
- exp: expiration (unix timestamp)
|
||||
"""
|
||||
|
||||
def __init__(self, secret: str = "", registration_code: str = ""):
|
||||
self.secret = secret or os.getenv("GRAVITY_HUB_SECRET", "")
|
||||
self.registration_code = registration_code or os.getenv(
|
||||
"GRAVITY_REGISTRATION_CODE", DEFAULT_REGISTRATION_CODE
|
||||
)
|
||||
if not self.secret:
|
||||
# Auto-generate a secret if not set (ephemeral — tokens invalid after restart)
|
||||
self.secret = hashlib.sha256(os.urandom(32)).hexdigest()
|
||||
logger.warning(
|
||||
"[AUTH] No GRAVITY_HUB_SECRET set — generated ephemeral secret. "
|
||||
"Tokens will be invalid after server restart."
|
||||
)
|
||||
|
||||
def validate_registration_code(self, code: str) -> bool:
|
||||
"""Check if the provided registration code matches."""
|
||||
if not self.registration_code:
|
||||
# No registration code configured → allow all connections
|
||||
logger.warning("[AUTH] No registration code configured — accepting all")
|
||||
return True
|
||||
return hmac.compare_digest(code, self.registration_code)
|
||||
|
||||
def create_token(
|
||||
self, project: str, pc_name: str, ttl: int = DEFAULT_TOKEN_TTL
|
||||
) -> str:
|
||||
"""Create a signed token for a specific project and PC.
|
||||
|
||||
Returns a base64-encoded string: {header}.{payload}.{signature}
|
||||
"""
|
||||
now = int(time.time())
|
||||
payload = {
|
||||
"project": project,
|
||||
"pc": pc_name,
|
||||
"iat": now,
|
||||
"exp": now + ttl,
|
||||
}
|
||||
payload_b64 = _b64_encode(json.dumps(payload))
|
||||
signature = self._sign(payload_b64)
|
||||
return f"{payload_b64}.{signature}"
|
||||
|
||||
def verify_token(self, token: str) -> dict | None:
|
||||
"""Verify and decode a token.
|
||||
|
||||
Returns the payload dict if valid, None if invalid or expired.
|
||||
"""
|
||||
try:
|
||||
parts = token.split(".")
|
||||
if len(parts) != 2:
|
||||
return None
|
||||
|
||||
payload_b64, signature = parts
|
||||
expected_sig = self._sign(payload_b64)
|
||||
|
||||
if not hmac.compare_digest(signature, expected_sig):
|
||||
logger.warning("[AUTH] Invalid token signature")
|
||||
return None
|
||||
|
||||
payload = json.loads(_b64_decode(payload_b64))
|
||||
|
||||
# Check expiration
|
||||
if payload.get("exp", 0) < time.time():
|
||||
logger.info(f"[AUTH] Token expired for {payload.get('pc', '?')}")
|
||||
return None
|
||||
|
||||
return payload
|
||||
except (json.JSONDecodeError, ValueError, KeyError) as e:
|
||||
logger.warning(f"[AUTH] Token decode error: {e}")
|
||||
return None
|
||||
|
||||
def _sign(self, data: str) -> str:
|
||||
"""HMAC-SHA256 sign and return base64."""
|
||||
sig = hmac.new(
|
||||
self.secret.encode("utf-8"),
|
||||
data.encode("utf-8"),
|
||||
hashlib.sha256,
|
||||
).digest()
|
||||
return _b64_encode_bytes(sig)
|
||||
|
||||
|
||||
def _b64_encode(data: str) -> str:
|
||||
"""URL-safe base64 encode a string, no padding."""
|
||||
return urlsafe_b64encode(data.encode("utf-8")).rstrip(b"=").decode("ascii")
|
||||
|
||||
|
||||
def _b64_encode_bytes(data: bytes) -> str:
|
||||
"""URL-safe base64 encode bytes, no padding."""
|
||||
return urlsafe_b64encode(data).rstrip(b"=").decode("ascii")
|
||||
|
||||
|
||||
def _b64_decode(data: str) -> str:
|
||||
"""URL-safe base64 decode, handles missing padding."""
|
||||
padded = data + "=" * (4 - len(data) % 4)
|
||||
return urlsafe_b64decode(padded).decode("utf-8")
|
||||
172
bridge.py
172
bridge.py
@@ -1,172 +0,0 @@
|
||||
"""Bridge protocol — file-based communication between Discord bot and Antigravity.
|
||||
|
||||
Bridge directory: ~/.gemini/antigravity/bridge/
|
||||
Structure:
|
||||
bridge/
|
||||
pending/ ← Bot writes approval requests for Discord
|
||||
response/ ← Bot writes user responses from Discord
|
||||
commands/ ← Bot writes user text input from Discord
|
||||
|
||||
Protocol:
|
||||
1. VS Code Extension detects pending approval → writes JSON to pending/
|
||||
2. Bot reads pending/ → sends Discord message with ✅/❌ buttons
|
||||
3. User clicks button → Bot writes JSON to response/
|
||||
4. VS Code Extension reads response/ → executes action
|
||||
"""
|
||||
|
||||
import json
|
||||
import time
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass, asdict
|
||||
from enum import Enum
|
||||
|
||||
from config import Config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ApprovalStatus(Enum):
|
||||
PENDING = "pending"
|
||||
APPROVED = "approved"
|
||||
REJECTED = "rejected"
|
||||
TIMEOUT = "timeout"
|
||||
|
||||
|
||||
@dataclass
|
||||
class ApprovalRequest:
|
||||
"""An approval request from Antigravity."""
|
||||
request_id: str
|
||||
conversation_id: str
|
||||
command: str # The command/action needing approval
|
||||
description: str # Human-readable description
|
||||
timestamp: float
|
||||
status: str = "pending"
|
||||
discord_message_id: int = 0
|
||||
project_name: str = "" # Project routing key
|
||||
|
||||
|
||||
@dataclass
|
||||
class UserResponse:
|
||||
"""A user response from Discord."""
|
||||
request_id: str
|
||||
approved: bool
|
||||
user_input: str = ""
|
||||
timestamp: float = 0
|
||||
button_index: int = -1 # -1 = legacy (approve/reject), 0+ = specific button index
|
||||
|
||||
|
||||
class BridgeProtocol:
|
||||
"""Manages the file-based bridge protocol."""
|
||||
|
||||
def __init__(self):
|
||||
self.bridge_dir = Config.BRAIN_PATH.parent / "bridge"
|
||||
self.pending_dir = self.bridge_dir / "pending"
|
||||
self.response_dir = self.bridge_dir / "response"
|
||||
self.commands_dir = self.bridge_dir / "commands"
|
||||
|
||||
# Create directories
|
||||
for d in [self.pending_dir, self.response_dir, self.commands_dir]:
|
||||
d.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Startup cleanup: purge stale pending files (> 5 min old)
|
||||
self._cleanup_stale_pending()
|
||||
|
||||
logger.info(f"Bridge protocol initialized: {self.bridge_dir}")
|
||||
|
||||
def _cleanup_stale_pending(self, max_age_seconds: int = 300):
|
||||
"""Remove pending files older than max_age_seconds on startup."""
|
||||
now = time.time()
|
||||
cleaned = 0
|
||||
for f in self.pending_dir.glob("*.json"):
|
||||
try:
|
||||
data = json.loads(f.read_text(encoding="utf-8-sig"))
|
||||
ts = data.get("timestamp", 0)
|
||||
if now - ts > max_age_seconds:
|
||||
f.unlink()
|
||||
cleaned += 1
|
||||
except (json.JSONDecodeError, OSError):
|
||||
f.unlink() # corrupt file, remove
|
||||
cleaned += 1
|
||||
if cleaned:
|
||||
logger.info(f"Startup cleanup: removed {cleaned} stale pending files")
|
||||
|
||||
def get_pending_requests(self) -> list[ApprovalRequest]:
|
||||
"""Read all pending approval requests. Skips files older than 5 minutes."""
|
||||
requests = []
|
||||
fields = {f.name for f in ApprovalRequest.__dataclass_fields__.values()}
|
||||
now = time.time()
|
||||
MAX_AGE = 300 # 5 minutes
|
||||
for f in self.pending_dir.glob("*.json"):
|
||||
try:
|
||||
data = json.loads(f.read_text(encoding="utf-8-sig"))
|
||||
ts = data.get("timestamp", 0)
|
||||
if now - ts > MAX_AGE:
|
||||
# Too old — mark expired and skip
|
||||
data["status"] = "expired"
|
||||
f.write_text(
|
||||
json.dumps(data, ensure_ascii=False, indent=2),
|
||||
encoding="utf-8",
|
||||
)
|
||||
continue
|
||||
if data.get("status") == "pending":
|
||||
# Filter to known fields only
|
||||
filtered = {k: v for k, v in data.items() if k in fields}
|
||||
requests.append(ApprovalRequest(**filtered))
|
||||
except (json.JSONDecodeError, TypeError, OSError) as e:
|
||||
logger.warning(f"Bad pending request {f.name}: {e}")
|
||||
return requests
|
||||
|
||||
def read_pending_request(self, request_id: str) -> ApprovalRequest | None:
|
||||
"""Re-read a specific pending request (to get merged data)."""
|
||||
f = self.pending_dir / f"{request_id}.json"
|
||||
if not f.exists():
|
||||
return None
|
||||
try:
|
||||
data = json.loads(f.read_text(encoding="utf-8-sig"))
|
||||
fields = {fn.name for fn in ApprovalRequest.__dataclass_fields__.values()}
|
||||
filtered = {k: v for k, v in data.items() if k in fields}
|
||||
return ApprovalRequest(**filtered)
|
||||
except (json.JSONDecodeError, TypeError, OSError):
|
||||
return None
|
||||
|
||||
def write_response(self, response: UserResponse):
|
||||
"""Write a user response to the response directory."""
|
||||
response.timestamp = time.time()
|
||||
filename = f"{response.request_id}.json"
|
||||
filepath = self.response_dir / filename
|
||||
|
||||
filepath.write_text(
|
||||
json.dumps(asdict(response), ensure_ascii=False, indent=2),
|
||||
encoding="utf-8"
|
||||
)
|
||||
logger.info(f"Response written: {filename} (approved={response.approved})")
|
||||
|
||||
# Delete pending file after processing (prevents re-processing and accumulation)
|
||||
pending_file = self.pending_dir / filename
|
||||
if pending_file.exists():
|
||||
try:
|
||||
pending_file.unlink()
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def write_command(self, conversation_id: str, text: str, *, project_name: str = ""):
|
||||
"""Write a user text command for Antigravity to consume."""
|
||||
cmd_id = f"{int(time.time() * 1000)}"
|
||||
filepath = self.commands_dir / f"{cmd_id}.json"
|
||||
|
||||
data = {
|
||||
"id": cmd_id,
|
||||
"conversation_id": conversation_id,
|
||||
"project_name": project_name,
|
||||
"text": text,
|
||||
"timestamp": time.time(),
|
||||
"consumed": False,
|
||||
}
|
||||
|
||||
filepath.write_text(
|
||||
json.dumps(data, ensure_ascii=False, indent=2),
|
||||
encoding="utf-8"
|
||||
)
|
||||
logger.info(f"Command written: {cmd_id} → project={project_name}")
|
||||
return cmd_id
|
||||
23
config.py
23
config.py
@@ -16,10 +16,11 @@ class Config:
|
||||
DISCORD_GUILD_ID: int = int(os.getenv("DISCORD_GUILD_ID") or "0")
|
||||
|
||||
# Antigravity Brain path
|
||||
BRAIN_PATH: Path = Path(os.getenv(
|
||||
"BRAIN_PATH",
|
||||
os.path.expanduser("~/.gemini/antigravity/brain")
|
||||
))
|
||||
# NOTE: os.getenv returns "" (not None) when .env has BRAIN_PATH= (empty value).
|
||||
# Path("") resolves to "." (CWD), which is WRONG. Use `or` to handle both None and "".
|
||||
BRAIN_PATH: Path = Path(
|
||||
os.getenv("BRAIN_PATH") or os.path.expanduser("~/.gemini/antigravity/brain")
|
||||
)
|
||||
|
||||
# Watcher settings
|
||||
DEBOUNCE_SECONDS: float = float(os.getenv("DEBOUNCE_SECONDS", "5"))
|
||||
@@ -31,6 +32,9 @@ class Config:
|
||||
"walkthrough.md",
|
||||
}
|
||||
|
||||
# Extension-based monitoring: any file with these extensions in brain/{conv}/ is watched
|
||||
WATCHED_EXTENSIONS: set = {".md"}
|
||||
|
||||
# Discord message limits
|
||||
DISCORD_MSG_LIMIT: int = 2000
|
||||
DISCORD_EMBED_DESC_LIMIT: int = 4096
|
||||
@@ -39,6 +43,14 @@ class Config:
|
||||
CHANNEL_PREFIX: str = "AG"
|
||||
PROJECT_NAME: str = os.getenv("PROJECT_NAME", "gravity_control")
|
||||
|
||||
# Bot mode: 'local' (file-based bridge) or 'gateway' (WS Hub + HTTP API)
|
||||
BOT_MODE: str = os.getenv("BOT_MODE", "local")
|
||||
GATEWAY_API_KEY: str = os.getenv("GATEWAY_API_KEY", "")
|
||||
|
||||
# WebSocket Hub
|
||||
GRAVITY_HUB_SECRET: str = os.getenv("GRAVITY_HUB_SECRET", "") # JWT signing secret
|
||||
GRAVITY_REGISTRATION_CODE: str = os.getenv("GRAVITY_REGISTRATION_CODE", "") # Extension auth
|
||||
|
||||
@classmethod
|
||||
def validate(cls) -> list[str]:
|
||||
"""Return list of configuration errors."""
|
||||
@@ -47,6 +59,7 @@ class Config:
|
||||
errors.append("DISCORD_TOKEN is not set")
|
||||
if not cls.DISCORD_GUILD_ID:
|
||||
errors.append("DISCORD_GUILD_ID is not set")
|
||||
if not cls.BRAIN_PATH.exists():
|
||||
# Gateway mode doesn't need local BRAIN_PATH
|
||||
if cls.BOT_MODE != 'gateway' and not cls.BRAIN_PATH.exists():
|
||||
errors.append(f"BRAIN_PATH does not exist: {cls.BRAIN_PATH}")
|
||||
return errors
|
||||
|
||||
0
diag_output.txt
Normal file
0
diag_output.txt
Normal file
30
docker-compose.yml
Normal file
30
docker-compose.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
services:
|
||||
gateway:
|
||||
build: .
|
||||
container_name: gravity-gateway
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:8585:8585"
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- BOT_MODE=gateway
|
||||
- GATEWAY_PORT=8585
|
||||
- BRAIN_PATH=/app/data/brain
|
||||
volumes:
|
||||
- gateway-data:/app/data
|
||||
networks:
|
||||
- default
|
||||
- proxy-net
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: 10m
|
||||
max-file: 3
|
||||
|
||||
volumes:
|
||||
gateway-data:
|
||||
|
||||
networks:
|
||||
proxy-net:
|
||||
external: true
|
||||
30
docker-compose_server.yml
Normal file
30
docker-compose_server.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
services:
|
||||
gateway:
|
||||
build: .
|
||||
container_name: gravity-gateway
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:8585:8585"
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- BOT_MODE=gateway
|
||||
- GATEWAY_PORT=8585
|
||||
- BRAIN_PATH=/app/data/brain
|
||||
volumes:
|
||||
- gateway-data:/app/data
|
||||
networks:
|
||||
- default
|
||||
- proxy-net
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: 10m
|
||||
max-file: 3
|
||||
|
||||
volumes:
|
||||
gateway-data:
|
||||
|
||||
networks:
|
||||
proxy-net:
|
||||
external: true
|
||||
185
docs/approval-flow.md
Normal file
185
docs/approval-flow.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# Gravity Bridge — 승인 시스템 완전 Flow 가이드
|
||||
|
||||
> **Last Updated**: 2026-03-16 (v0.3.12)
|
||||
> **SSOT**: 이 문서는 승인 시스템의 전체 데이터 플로우와 상태 관리를 설명합니다.
|
||||
> **수정 시**: known-issues.md와 동기화 필수
|
||||
|
||||
---
|
||||
|
||||
## 1. 시스템 아키텍처 개요
|
||||
|
||||
```
|
||||
AG IDE (Antigravity)
|
||||
├── Extension (extension.ts) ← Bridge 핵심
|
||||
│ ├── setupMonitor() ← 5초 폴링 (GetAllCascadeTrajectories)
|
||||
│ ├── step_probe ← WAITING step 감지 (GetCascadeTrajectorySteps)
|
||||
│ ├── DOM Observer ← 렌더러 스크립트 (버튼 감지)
|
||||
│ ├── processResponseFile() ← Discord 응답 처리
|
||||
│ ├── writePendingApproval() ← pending 파일 생성 (dedup 포함)
|
||||
│ └── tryApprovalStrategies() ← RPC 실행
|
||||
├── bridge/ (파일 시스템)
|
||||
│ ├── pending/*.json ← 승인 대기 목록
|
||||
│ ├── response/*.json ← Discord 응답
|
||||
│ ├── snapshot/*.json ← 채팅 릴레이
|
||||
│ └── register/*.json ← 세션-프로젝트 매핑
|
||||
└── Bot (bot.py) ← Discord 통신
|
||||
├── pending_approval_scanner ← 3초 폴링
|
||||
├── auto_approve_scanner ← !auto 토글
|
||||
└── snapshot_scanner ← 채팅 릴레이
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 데이터 플로우: 승인 요청 → 응답
|
||||
|
||||
### 2.1 Pending 생성 경로 (2개)
|
||||
|
||||
#### 경로 A: Step Probe → `writePendingApproval()`
|
||||
```
|
||||
1. setupMonitor() 5초 폴링 → GetAllCascadeTrajectories
|
||||
2. RUNNING + delta=0 + modTime 미변경 → stall 감지
|
||||
3. consecutiveIdleCount >= 1 && !stallProbed
|
||||
4. GetCascadeTrajectorySteps → WAITING step 발견
|
||||
5. si !== lastPendingStepIndex 확인 (dedup)
|
||||
6. writePendingApproval() 호출
|
||||
├── recentPendingSteps 메모리 dedup 체크 (60초 TTL)
|
||||
├── 기존 pending 파일 dedup 체크 (15초 윈도우)
|
||||
└── pending 파일 생성 + recentPendingSteps에 기록
|
||||
7. stallProbed = true, lastPendingStepIndex = si
|
||||
```
|
||||
|
||||
#### 경로 B: DOM Observer → HTTP POST `/pending`
|
||||
```
|
||||
1. 렌더러 MutationObserver → 버튼 감지 (Run, Accept, Allow 등)
|
||||
2. FALSE_POSITIVE_RE 필터 (Proceed, Continue, Deny 등 차단)
|
||||
3. "Run"은 sessionStalled=true && lastPendingStepIndex < 0 일 때만 통과
|
||||
4. HTTP POST /pending → Extension HTTP 핸들러 (L738-812)
|
||||
5. 파일 직접 생성 (writePendingApproval() 우회!)
|
||||
⚠️ recentPendingSteps 메모리 dedup 미적용
|
||||
```
|
||||
|
||||
> **주의**: 경로 B는 `writePendingApproval()`의 메모리 dedup을 우회합니다. 하지만 `lastPendingStepIndex >= 0`일 때 "Run" 필터(L757)와 15초 파일 기반 dedup이 방어합니다.
|
||||
|
||||
### 2.2 Response 처리 경로
|
||||
|
||||
```
|
||||
1. Bot pending_approval_scanner → pending 파일 발견
|
||||
2. auto-approve OR Discord 버튼 → write_response() 호출
|
||||
├── response/*.json 생성
|
||||
└── pending/*.json 삭제 (!)
|
||||
3. Extension response watcher (fs.watch + 3초 폴링 fallback)
|
||||
→ processResponseFile() (300ms 딜레이)
|
||||
4. processResponseFile():
|
||||
├── 파일 존재 확인 (HTTP handler가 먼저 삭제했을 수 있음)
|
||||
├── stale timeout 필터 (2분)
|
||||
├── auto_resolved/expired 상태 skip
|
||||
├── project_name 필터
|
||||
└── tryApprovalStrategies() → RPC 실행
|
||||
5. sawRunningAfterPending = true (v0.3.12 핵심 수정)
|
||||
6. response 파일 삭제 (DOM observer 경로는 HTTP handler에 위임)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 상태 변수 완전 참조
|
||||
|
||||
### 3.1 모듈 레벨 변수 (extension.ts)
|
||||
|
||||
| 변수 | 위치 | 역할 | 설정 | 리셋 |
|
||||
|------|------|------|------|------|
|
||||
| `lastPendingStepIndex` | L707 | 마지막으로 pending을 생성한 step index | step_probe(L2047,2108), error_probe(L2178) | delta>0(L1972), session change(L1841) |
|
||||
| `stallProbed` | L708 | 현재 stall에서 probe 완료 여부 | step_probe(L2046,2107,2177) | delta>0(L1980), modTime changed(L1986), session change(L1842) |
|
||||
| `sawRunningAfterPending` | L709 | pending 후 delta>0 발생 여부 (auto_resolve gate) | delta>0(L1979), **processResponseFile(L2622)** | step_probe(L2049,2110) |
|
||||
| `sessionStalled` | L706 | AG가 stall 상태인지 | idle count≥1(L1993) | delta>0(L1937), not WAITING(L2135) |
|
||||
| `recentPendingSteps` | L54 | 메모리 기반 pending dedup Map | writePendingApproval(L2787,2837) | delta>0(L1974), TTL 60초 |
|
||||
|
||||
### 3.2 setupMonitor() 로컬 변수
|
||||
|
||||
| 변수 | 역할 |
|
||||
|------|------|
|
||||
| `consecutiveIdleCount` | 연속 idle poll 수 (stall 감지 debounce) |
|
||||
| `lastPendingTime` | 마지막 pending 생성 시간 |
|
||||
| `lastModTime` | 마지막 modifiedTime (thinking vs approval 구분) |
|
||||
| `wasRunning` | RUNNING→IDLE 전이 추적 |
|
||||
| `lastResponseCaptureStep` | 응답 캡처 dedup |
|
||||
|
||||
---
|
||||
|
||||
## 4. 핵심 상태 전이 다이어그램
|
||||
|
||||
```
|
||||
[IDLE] ──step진행(delta>0)──→ [RUNNING]
|
||||
│
|
||||
delta=0 + modTime 변동 → [THINKING] (stall 카운터 리셋)
|
||||
delta=0 + modTime 고정 → [STALLED]
|
||||
│
|
||||
!stallProbed → step_probe 실행
|
||||
│
|
||||
WAITING 발견 → [PENDING_CREATED]
|
||||
(stallProbed=true, lastPendingStepIndex=si)
|
||||
│
|
||||
┌──────────────────────────────────┤
|
||||
▼ ▼
|
||||
[DISCORD_APPROVED] [AG_LOCAL_APPROVED]
|
||||
processResponseFile() delta > 0 + !sawRunningAfterPending
|
||||
sawRunningAfterPending=true → auto_resolve → Discord 알림
|
||||
│ │
|
||||
└──────────────────────────────────┘
|
||||
│
|
||||
[STEP_PROGRESSED]
|
||||
delta > 0 → 전체 리셋
|
||||
lastPendingStepIndex = -1
|
||||
stallProbed = false
|
||||
sawRunningAfterPending = true
|
||||
recentPendingSteps 클리어
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. v0.3.12 수정 — 왜 `sawRunningAfterPending = true`인가
|
||||
|
||||
### 5.1 이전 문제: 무한 루프 (v0.3.11 이전)
|
||||
|
||||
processResponseFile이 `lastPendingStepIndex = -1`로 리셋 → step_probe가 같은 WAITING step 재감지 → 새 pending → auto-approve → response → 다시 리셋 → **무한 루프**
|
||||
|
||||
### 5.2 v0.3.11 시도: 모든 리셋 제거
|
||||
|
||||
`lastPendingStepIndex`와 `stallProbed` 리셋을 완전 제거 → **무한 루프 해소**, 하지만:
|
||||
- known-issues L479 회귀: Discord 승인 후 AG 진행 시 `sawRunningAfterPending=false` + `lastPendingStepIndex>=0` → auto_resolve 중복 알림
|
||||
- `stallProbed` 영구 잠금 우려 (실제로는 delta>0에서 자연 리셋)
|
||||
|
||||
### 5.3 v0.3.12 해결: `sawRunningAfterPending = true`
|
||||
|
||||
Discord 승인 response 처리 후 `sawRunningAfterPending = true`만 설정:
|
||||
1. ✅ 무한 루프 방지: `lastPendingStepIndex` 유지 → dedup 작동
|
||||
2. ✅ auto_resolve 중복 방지: `sawRunningAfterPending = true` → L1939 조건 FALSE
|
||||
3. ✅ stallProbed 자연 리셋: delta>0에서 L1980
|
||||
4. ✅ 신호 수집 무영향: step_probe, GetAllCascadeTrajectories 코드 미변경
|
||||
|
||||
---
|
||||
|
||||
## 6. 위험 지점 목록 (수정 시 반드시 확인)
|
||||
|
||||
| 코드 위치 | 위험 | 확인 사항 |
|
||||
|-----------|------|----------|
|
||||
| processResponseFile 리셋 (L2607+) | 무한 루프 or auto_resolve 중복 | `sawRunningAfterPending = true`만 설정. `lastPendingStepIndex`와 `stallProbed`는 건드리지 말 것 |
|
||||
| HTTP POST /pending (L738-812) | DOM observer 경로가 writePendingApproval() 우회 | "Run" 필터(L757)와 파일 기반 dedup이 방어 |
|
||||
| bridge.py write_response (L460-461) | pending 파일 삭제 | 메모리 dedup(recentPendingSteps)이 재생성 방지 |
|
||||
| auto_resolve (L1939-1977) | 중복 알림 | `sawRunningAfterPending` gate 확인 |
|
||||
| step_probe offset (L2025-2070) | 775-step 리밋 | stepOffset으로 최신 step 조회 |
|
||||
| session change (L1832-1854) | 모든 상태 초기화 | lastPendingStepIndex, stallProbed, sawRunningAfterPending 모두 리셋 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 과거 이슈 교차 참조
|
||||
|
||||
| 이슈 (known-issues.md) | 방어 코드 | 상태 |
|
||||
|------------------------|----------|------|
|
||||
| L252: 중복 승인 요청 | writePendingApproval dedup (15초 윈도우 + 메모리 dedup) | ✅ 해결 |
|
||||
| L264: pending 무한 누적 | write_response()에서 삭제 + 5분 age filter | ✅ 해결 |
|
||||
| L288: DOM observer ENOENT | isDomObserver 분기 삭제 (L2619) | ✅ 해결 |
|
||||
| L384: 크로스 프로젝트 MERGE | project_name 가드 (L2774) | ✅ 해결 |
|
||||
| L444: DEDUP 크로스 세션 | conversation_id 가드 (L2794) | ✅ 해결 |
|
||||
| L474-479: auto_resolve 중복 | `sawRunningAfterPending = true` (v0.3.12) | ✅ 해결 |
|
||||
| L493: Double-Fire auto-approve | Extension auto-approve 경로 제거, Bot 단일 경로 | ✅ 해결 |
|
||||
| L499: Deny false positive | FALSE_POSITIVE_RE + Bot reject guard | ✅ 해결 |
|
||||
@@ -3,3 +3,17 @@
|
||||
| # | 시간 | 작업 설명 | 커밋 | 상태 |
|
||||
|---|------|----------|------|------|
|
||||
| 001 | 06:12~06:30 | Discord 승인 ENOENT race condition 수정 + 버튼 그룹화 (multi-choice) | `aab1cfb` | 🔧 |
|
||||
| 002 | 21:00~02:30 | 승인 메시지 전문 표시 + 연속 승인 감지 + file_permission scope 라우팅 | `75a3482`~`c9b4fd4` | ✅ |
|
||||
| 003 | 12:30~13:30 | 3버튼 UI + Run 중복 필터 + dedup + 인자 값 표시 | `14d2acf`~`47dbd38` | ✅ |
|
||||
| 004 | 13:30~13:55 | auto_resolved 동기화 + expired 카드 업데이트 + DOM step_index | `048ffd9` | ✅ |
|
||||
| 005 | 13:55~14:10 | #253 전체 대화 릴레이 — 사용자 메시지 + 에러 알림 | `17dd665`~`b500120` | ✅ |
|
||||
| 006 | 14:00~15:00 | Discord 에코필터 + 리로드 재전송 방지 + diff review 알림 | `82b727a`~`8fbf6bf` | ✅ |
|
||||
| 007 | 15:00~15:55 | step_type 패스스루 체인 수정 + file_permission 자동감지 | `7982263`~`d1586c5` | ✅ |
|
||||
| 008 | 16:45~17:20 | Single active project lock + stale REJECT 필터 + Vikunja 태스크 정리 | `186875a`~`95d4f85` | ✅ |
|
||||
| 009 | 17:20~17:47 | v0.3.6 릴리스 — VSIX 빌드 + start_bot.bat 런처 | `bd46bea` | ✅ |
|
||||
| 010 | 18:00~18:30 | v0.3.7 — file_permission 3-button 주입 + active_project.lock 제거 (멀티프로젝트) | `27deb2a` | ✅ |
|
||||
| 011 | 18:50~19:29 | v0.3.8 — workspace URI 기반 세션 필터링 (멀티프로젝트 격리 완성) | `ae91134` | ✅ |
|
||||
| 012 | 19:30~20:35 | 크로스 프로젝트 response watcher 우회 수정 + file_permission write 도구 3-button 매핑 | `3b834e0` | ✅ |
|
||||
| 013 | 21:04~22:19 | Deriva 신호 진단 + RUNNING 세션 우선 선택 + IDLE 채널 자동 생성 제거 | `6179c4d` | ✅ |
|
||||
| 014 | 22:23~22:47 | SDK LS 프로세스 대소문자 매칭 버그 수정 — variet-agent 신호 미도달 해결 | `21fd309` | ✅ |
|
||||
| 015 | 23:46~23:57 | v0.3.9 — SDK JS 파일 VSIX 미포함 수정 + start_bot.bat Python 경로 우선순위 | `71aa80d` | ✅ |
|
||||
|
||||
14
docs/devlog/2026-03-11.md
Normal file
14
docs/devlog/2026-03-11.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# 2026-03-11 Devlog
|
||||
|
||||
| # | 시간 | 작업 설명 | 커밋 | 상태 |
|
||||
|---|------|----------|------|------|
|
||||
| 001 | 00:00~00:20 | Discord 릴레이 미작동 진단 — config.py BRAIN_PATH 빈문자열 버그 수정 | `pending` | ✅ |
|
||||
| 002 | 00:20~01:05 | 크로스 프로젝트 pending DEDUP MERGE 버그 진단 및 수정 (project_name 가드 3곳) | `pending` | ✅ |
|
||||
| 003 | 09:25~09:33 | Auto-approve 기능 감사 (미구현 확인) + Vikunja 태스크 #304, #305 등록 | `pending` | ✅ |
|
||||
| 004 | 10:00~10:35 | P1: `!auto` 토글 자동 승인 구현 (bot.py + extension.ts) | `pending` | ✅ |
|
||||
| 005 | 10:35~10:45 | P2: BridgeTransport 추상화 (bridge.py 리팩토링 + config/main 모드 설정) | `pending` | ✅ |
|
||||
| 006 | 10:43~10:55 | 사용 가이드 작성 (docs/usage-guide.md) + tech-stack.md Python 경로 기록 | `c130399` | ✅ |
|
||||
| 007 | 19:28~19:35 | Gateway HTTP API + Docker (Dockerfile, docker-compose, Caddyfile) | `6dbbb57` | ✅ |
|
||||
| 008 | 19:35~19:50 | Gateway 보안: API Key 인증 미들웨어 + Caddy HTTPS + .env.example | `95da3e9` | ✅ |
|
||||
| 009 | 19:50~20:10 | RemoteTransport + CollectorBridge 구현 — Collector↔Gateway HTTP 통신 | `95c2905` | ✅ |
|
||||
| 010 | 21:30~23:48 | 아키텍처 감사: aiohttp 전환 + 보안 + 기능 누락 수정 + 나노 검증 | `d7ed454` | ✅ |
|
||||
9
docs/devlog/2026-03-12.md
Normal file
9
docs/devlog/2026-03-12.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# 2026-03-12 Devlog
|
||||
|
||||
| # | 시간 | 작업 설명 | 커밋 | 상태 |
|
||||
|---|------|----------|------|------|
|
||||
| 001 | 00:34~00:47 | 429 Rate Limit 무한 루프 디버깅 — 지수 백오프 + rate limit 완화 + Collector 폴링 보호 | `d9b36cf` | ✅ |
|
||||
| 002 | 16:45~17:04 | workbench.html 0-byte 파괴 복구 — 멀티 인스턴스 race condition 방지 안전 가드 추가 | `a9feee6` | ✅ |
|
||||
| 003 | 17:10~17:55 | workbench.html 크로스 복원 CSS 깨짐 수정 — pre-patch backup + requiredMarker 구조 검증 + .orig 자동 복원 | `6d8c6f1` | ✅ |
|
||||
| 004 | 19:46~21:13 | Collector 멀티 프로젝트 command 폴링 버그 수정 + rate limit burst throttle | `ae51d28` `bcc29f9` | ✅ |
|
||||
| 005 | 22:12~22:58 | Rate limit 구조적 수정 — 점진적 백오프 + adaptive 폴링 + burst-friendly 윈도우 + stale pending 정리 | `56de714` | 🔧 |
|
||||
6
docs/devlog/2026-03-13.md
Normal file
6
docs/devlog/2026-03-13.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Devlog — 2026-03-13
|
||||
|
||||
| # | 시간 | 작업 | 커밋 | 상태 |
|
||||
|---|------|------|------|------|
|
||||
| 001 | 08:56 | Discord 아티팩트 알림 개선 — truncation 확대, 파일 첨부 전송, 동적 .md 감시 | `e5a05e3` | ✅ |
|
||||
| 002 | 19:53 | Collector 성능 최적화 — mtime 프리체크, 프로젝트 캐시, re-forward 수정, 폴링 간격 조정 | `d4a2016` | ✅ |
|
||||
13
docs/devlog/2026-03-15.md
Normal file
13
docs/devlog/2026-03-15.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# 2026-03-15 Devlog
|
||||
|
||||
| # | 시간 | 작업 | 커밋 | 상태 |
|
||||
|---|------|------|------|------|
|
||||
| 001 | 07:00~08:16 | 승인 신호 누락 진단 & 5건 버그 수정 (DEDUP collision, fs.watch fail, default 보호, auto 확인, msg dedup) | `40e3cd5` | ✅ |
|
||||
| 002 | 08:25~08:31 | Extension v0.3.10 버전 범프 & VSIX 빌드 | `10caae1` | ✅ |
|
||||
| 003 | 10:00~10:41 | 승인 라이프사이클 race condition 4건 수정 (HTML lock, pending status skip, auto_resolve Discord 알림, Bot approval_messages) | `f962036` | ✅ |
|
||||
| 004 | 10:41~10:53 | 성능 최적화 3건 (pollResponseGroup 1500ms, renderer adaptive idle, Bot single-pass scanner) + VSIX 빌드 | `ae0509f` | ✅ |
|
||||
| 005 | 15:17~17:09 | 크로스 프로젝트 신호 오염 진단 & 승인 플로우 아키텍처 수정 — DEDUP project_name 가드, double-fire auto-approve 제거, 실패 RPC 전략 30+개 삭제 (v0.3.11) | `6739f8f` | ✅ |
|
||||
| 006 | 18:32~18:51 | Auto-approve 크래시 수정 — DOM Observer Deny false positive 필터 + Bot reject-word 가드 + AGENT.md 규칙 #10 추가 | `5e5f515` | ✅ |
|
||||
| 007 | 22:00~22:52 | 시스템 전체 감사 + 5개 파일 버그 수정 (PATS Deny 트리거 제거, auto_resolved 채팅 병합, UUID 파일명 충돌방지, IP rate limit 누수, bot.py deque) + VSIX 빌드/배포 | `c9f44af` | ✅ |
|
||||
| 008 | 23:18~23:27 | AGENT.md 로컬적 사고 방지 규칙 추가 — NEVER #10 강화(반증 의무), NEVER #11(기계적 적용 금지), ALWAYS #9(프로젝트 이력 교차 참조), Bug Report Protocol 분리 | `9b93ee9` | ✅ |
|
||||
|
||||
12
docs/devlog/2026-03-16.md
Normal file
12
docs/devlog/2026-03-16.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# 2026-03-16 Devlog
|
||||
|
||||
| # | 시간 | 작업 | 커밋 | 상태 |
|
||||
|---|------|------|------|------|
|
||||
| 001 | 07:30~11:10 | 승인 상태 관리 근본 원인 분석 + v0.3.12 수정 (sawRunningAfterPending gate) + approval-flow.md 시스템 Flow 문서 + known-issues 2건 추가 | `2d9fe96` | ✅ |
|
||||
| 002 | 13:25~14:20 | diff_review 핸들러 2-strategy 리팩토링 + 배포 불일치 발견/수정 + pending 순서 8초 지연 + 1차 테스트 (버튼 OK, RPC 미배포→재배포) + known-issues 2건 | `f302984` | ✅ |
|
||||
| 003 | 15:18~16:55 | diff_review steps=[] 근본 원인 분석 + 인메모리 캐시 (v0.3.13) + 3차 E2E (RPC SUCCESS but no-op) + 4가지 파라미터 실험 배포 | `00b9491` | ✅ |
|
||||
| 004 | 17:05~18:00 | AG 소스 역분석 — `AcknowledgeCascadeCodeEdit`→`acknowledgeCodeActionStep` 메서드명 오류 발견 + v0.3.14 3단계 전략 배포 + known-issues 2건 업데이트 | `5a1d4f0` | ✅ |
|
||||
| 005 | 18:13~18:43 | v0.3.14 E2E 테스트 → RPC 3개 전략 모두 실패 확인 + v0.3.15 agentAcceptAllInFile 전환 배포 + known-issues 업데이트 | `0fdf668` | ✅ |
|
||||
| 006 | 18:47~19:09 | v0.3.15 diff_review E2E 2회 성공 + 이중 승인 수정 + IDLE 종료 알림 + !auto 이중 메시지 수정 (v0.3.16) + known-issues 2건 | `3cd7122` | ✅ |
|
||||
| 007 | 19:17~20:38 | Discord 알림 누락 디버깅 — Bot snapshot 로깅 추가 + 병렬 WAITING step break 제거 + 서버 Docker 재배포 3회 + known-issues 2건 | `7f079a5` | ✅ |
|
||||
| 008 | 20:50~23:06 | 크로스 프로젝트 알림 폭주 + pending 139개 누적 + diff_review brain/ 거짓양성 — 근본 원인 6건 분석 + Watcher 프로젝트 필터 + Collector stale 정리 + Extension brain/ 제외 + known-issues 3건 | `e3f8fb9` | ✅ |
|
||||
28
docs/devlog/2026-03-17.md
Normal file
28
docs/devlog/2026-03-17.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Devlog — 2026-03-17
|
||||
|
||||
| # | 시간 | 작업 | 커밋 | 상태 |
|
||||
|---|------|------|------|------|
|
||||
| 009 | 00:00~06:38 | Extension 모듈 분리 + Hub 통합 테스트 + VSIX v0.4.0 빌드 | `5f795b9` | ✅ |
|
||||
| 010 | 06:50~07:39 | 문서 전면 재작성 + 서버 배포 + WS 호환 수정 | `6ea3211` | ✅ |
|
||||
| 011 | 07:44~08:18 | VSIX v0.4.0 E2E 사전 검증 + WS 프록시 수정 | — | 🔧 |
|
||||
| 012 | 09:00~17:44 | VSIX E2E: workspaceUri, 이중발송, ApprovalRequest, ApprovalView WS, 응답 라우팅 | `2eea5fa` | ✅ |
|
||||
| 013 | 18:05~18:45 | Extension 모듈 분리 #398: http-bridge, html-patcher, command-handler 추출 (1296→650줄) | `6640d42` | ✅ |
|
||||
| 014 | 18:45~20:35 | WS+File dual-delivery 수정 + 에코 릴레이 수정 + VSIX v0.4.4 빌드 | `0da6291` | ✅ |
|
||||
| 015 | 20:45~21:00 | Accept All WS regression 수정 + auto_approve 이중쓰기 수정 + VSIX v0.4.5 | `47cc838` | ✅ |
|
||||
| 016 | 21:00~21:27 | 통신 아키텍처 나노단위 감사: writeRegistration 이중쓰기 + ApprovalView fallback + scanner 최적화 | — | ✅ |
|
||||
| 017 | 21:35~21:53 | Hub pending_owners 생명주기 수정: WS 재연결 시 승인 응답 소실 방지 (reconnect reassign + fallback routing) | `9ccfa83` | ✅ |
|
||||
|
||||
### #010 상세
|
||||
- **문서**: architecture.md(250줄), tech-stack.md(100줄), conventions.md(100줄) 전면 재작성 + Wiki 동기화
|
||||
- **태스크 정리**: #296 폐기, #396~#400 신규 5건 등록
|
||||
- **서버 배포**: docker-compose.yml 서버 실제 구성 반영, Caddyfile 제거, ag.variet.net 도메인 확인
|
||||
- **WS 호환**: ws-client.ts 브라우저 WebSocket API 호환 (.onopen/.onmessage) 수정
|
||||
- **Known issue**: VS Code 캐시로 Extension 코드 반영 지연 — 완전 재시작 필요
|
||||
|
||||
### #011 상세
|
||||
- **WS 프록시 수정**: NPM(openresty)에서 WebSocket Support 활성화 → 101 Switching Protocols 확인
|
||||
- **WS 인증 검증**: `wss://ag.variet.net/ws` → auth_ok, conn_id 발급, instance=#1 확인
|
||||
- **VSIX 설치**: v0.4.0 설치 확인, v0.3.16 제거, ws 모듈 수동 복사
|
||||
- **AG 설정**: `settings.json`에 hubUrl + registrationCode 설정
|
||||
- **ws 번들**: `.vscodeignore`에 `!node_modules/ws/**` 추가, `package.json`에 ws dependency
|
||||
- **미완료**: AG 재시작 후 Extension→Hub→Bot→Discord 실제 E2E 검증 필요
|
||||
8
docs/devlog/2026-03-18.md
Normal file
8
docs/devlog/2026-03-18.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# 2026-03-18 Devlog
|
||||
|
||||
| # | 시간 | 작업 | 커밋 | 상태 |
|
||||
|---|------|------|------|------|
|
||||
| 1 | 11:00 | v0.5.0 Collector 제거 + dead code 정리 + HttpBridgeContext 버그 수정 | `e763117` | ✅ |
|
||||
| 2 | 14:00 | bot.py unit tests 27건 — _write_command, _hub_on_pending, ApprovalView | `a41062b` | ✅ |
|
||||
| 3 | 14:30 | step-probe.ts 모듈 분리 → approval-handler.ts (1597→1017+411줄) + dead code 제거 | `17978a7` | ✅ |
|
||||
| 4 | 15:30 | 코드베이스 건강도 분석 + 통신 레이어 전수 감사 (8파일/7메시지타입) → 수정 필요 0건 | — | ✅ |
|
||||
6
docs/devlog/2026-03-19.md
Normal file
6
docs/devlog/2026-03-19.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# 2026-03-19 Devlog
|
||||
|
||||
| # | 시간 | 작업 | 커밋 | 상태 |
|
||||
|---|------|------|------|------|
|
||||
| 1 | 07:30 | v0.5.1 browser_subagent Allow RPC 매핑 수정 + .env 정리 | `549af6d` | ✅ |
|
||||
| 2 | 10:35 | v0.5.2 Idle→Resume 신호 소실 3중 버그 수정: auth_fail 재연결, pending_owners 보존, step-probe 리셋 | `5aad82c` | ✅ |
|
||||
6
docs/devlog/2026-03-21.md
Normal file
6
docs/devlog/2026-03-21.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# 2026-03-21 Devlog
|
||||
|
||||
| # | 시간 | 작업 | 커밋 | 상태 |
|
||||
|---|------|------|------|------|
|
||||
| 1 | 17:48 | v0.5.3~v0.5.4 신호 감지 3중 버그 수정: 세션 전환 즉시 probe (20-25s→5s), reviewAbsoluteUris 필드 수정, stepIndex=-1 uint32 에러 수정 + permission 매핑 | `0fb33a9` | ✅ |
|
||||
| 2 | 21:14 | v0.5.5 wrong-LS 자동 복구: Deriva RPC "input not registered" 근본 원인 분석 → fixLSConnection export + single-LS 조기종료 제거 + approval-handler 자동 LS 재연결 + 1회 retry | `6234301` | ✅ |
|
||||
5
docs/devlog/2026-03-22.md
Normal file
5
docs/devlog/2026-03-22.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 2026-03-22 Devlog
|
||||
|
||||
| # | 시간 | 작업 | 커밋 | 상태 |
|
||||
|---|------|------|------|------|
|
||||
| 1 | 01:22 | VSIX v0.5.5 빌드 — package.json 버전 범프 + vsce package | `b81135d` | ✅ |
|
||||
6
docs/devlog/2026-03-23.md
Normal file
6
docs/devlog/2026-03-23.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# 2026-03-23 Devlog
|
||||
|
||||
| NNN | HH:MM | 작업 설명 | `커밋해시` | ✅ 또는 🔧 |
|
||||
|-----|-------|----------|-----------|-----------|
|
||||
| 001 | 21:09 | WebSocket 좀비 커넥션 해결 및 통신망 메모리 누수 패치 | `ecebec3` | ✅ |
|
||||
| 002 | 22:45 | Cross-Project DOM Observer Leakage 패치 및 포트 동적 디스커버리 적용 | `TBD` | ✅ |
|
||||
7
docs/devlog/2026-03-24.md
Normal file
7
docs/devlog/2026-03-24.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# 2026-03-24 Devlog
|
||||
|
||||
| NNN | HH:MM | 작업 설명 | `커밋해시` | ✅ 또는 🔧 |
|
||||
|-----|-------|----------|-----------|-----------|
|
||||
| 001 | 07:05 | v0.5.6 좀비 커넥션 패치 회귀 오류 해결 (False Positive 끊김 방지를 위한 타임스탬프 검증 도입 v0.5.8) | `f13bcc8` | ✅ |
|
||||
| 002 | 13:00 | DOM Observer VS Code 네이티브 알림 UI 캡처 블라인드 스팟 해결 (v0.5.9) | `7b6cd59` | ✅ |
|
||||
| 003 | 18:14 | DOM Observer /trigger-click 렌더링 순서 오작동 및 False Positive 프리징 해결 (v0.5.10) | `101ec20` | ✅ |
|
||||
5
docs/devlog/2026-03-25.md
Normal file
5
docs/devlog/2026-03-25.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 2026-03-25 Devlog
|
||||
|
||||
| NNN | HH:MM | 작업 설명 | `커밋해시` | ✅ 또는 🔧 |
|
||||
|-----|-------|----------|-----------|-----------|
|
||||
| 001 | 07:15 | ws-client reconnect pacing 및 http-bridge 정규식 필터 완화로 Signal Drop 해결 (v0.5.10) | `pending` | ✅ |
|
||||
5
docs/devlog/2026-03-28.md
Normal file
5
docs/devlog/2026-03-28.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Devlog — 2026-03-28
|
||||
|
||||
| # | 시간 | 작업 | 커밋 | 상태 |
|
||||
|---|------|------|------|------|
|
||||
| 001 | 09:12 | guitar_score step-probe UTF-8 무한루프 수정 + approval stepIndex 보정 (v0.5.11) | `7bbd874` | ✅ #539 |
|
||||
5
docs/devlog/2026-04-01.md
Normal file
5
docs/devlog/2026-04-01.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 2026-04-01 Devlog
|
||||
|
||||
| NNN | HH:MM | 작업 설명 | `커밋해시` | ✅ 또는 🔧 |
|
||||
|-------|-------|-----------|-------------|--------------|
|
||||
| 001 | 18:22 | `step-probe` 10-Item Truncation/DoS 우회 (vsix v0.5.14) | `TBD` | ✅ |
|
||||
8
docs/devlog/2026-04-08.md
Normal file
8
docs/devlog/2026-04-08.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# 2026-04-08
|
||||
|
||||
| NNN | HH:MM | 작업 설명 | `커밋해시` | 상태 |
|
||||
|---|---|---|---|---|
|
||||
| 004 | 14:00 | SafeToAutoRun 알림 누락 복구 (v0.5.18) | `8f2a1b3` | ✅ |
|
||||
| 005 | 16:30 | SafeToAutoRun pending skip으로 인한 데드락 원인 파악 및 롤백 | `13f13ee` | ✅ |
|
||||
| 006 | 07:30 | SafeToAutoRun 데드락 완전 해결을 위한 Agnostic Bridge 도입 및 프리징 방어 (v0.5.20) | `임시해시` | ✅ |
|
||||
| 007 | 17:57 | Gravity Bridge 안정화: 중복 알림(SafeToAutoRun) 제거 설계 확정 및 Discord 봇 캐시/로컬 LS 크로스매칭 증상 디버깅 완료 | \-\ | ✅ |
|
||||
9
docs/devlog/2026-04-09.md
Normal file
9
docs/devlog/2026-04-09.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# 2026-04-09
|
||||
|
||||
| NNN | HH:MM | 작업 설명 | `커밋해시` | 상태 |
|
||||
|---|---|---|---|---|
|
||||
| 001 | 21:55 | Agent UI Tailwind/Native 마이그레이션 대응 (DOM 옵저버 구조 개편) | `HEAD` | ✅ |
|
||||
| 002 | 22:30 | Agent UI 버튼 무시 버그 긴급수정 (CodeLens 필터교정) | `HEAD` | ✅ |
|
||||
| 003 | 23:15 | Native UI 아이콘 글루잉 대응 스캐너 픽스 (DOM Regex 매칭 강화) | `HEAD` | ✅ |
|
||||
| 004 | 00:10 | Discord Signal Relay & Auto-Approve Body Null 버그 수정 (False Positive 차단) | "HEAD" | ✅ |
|
||||
| 005 | 23:00 | fix: Resolve empty Discord embed body by populating detailed step-probe payload | \\ | ? |
|
||||
4
docs/devlog/2026-04-10.md
Normal file
4
docs/devlog/2026-04-10.md
Normal file
@@ -0,0 +1,4 @@
|
||||
| NNN | 시간 | 작업 설명 | 커밋해시 | 완료여부 |
|
||||
|---|---|---|---|---|
|
||||
| 001 | 17:11 | step-probe.ts 의 isRunning 조건 누락으로 인한 릴레이 증발 버그 픽스 | COMMITTING | ✅ |
|
||||
| #613 | 21:10 | Bridge Relay AI Chat Body DOM extraction and template literal Regex fix | TBD | ✅ |
|
||||
6
docs/devlog/2026-04-11.md
Normal file
6
docs/devlog/2026-04-11.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# 2026-04-11
|
||||
|
||||
| NNN | HH:MM | 작업 설명 | `커밋해시` | ✅ 또는 🔧 |
|
||||
|-------|-------|----------|-----------|-----------|
|
||||
| 001 | 13:04 | Pure 웹소켓 게이트웨이 완전 전환 및 레거시 파일 브릿지 통신코드 삭제 | `072f83b` | ✅ |
|
||||
| 002 | 17:25 | Antigravity Observer 컨텍스트 추출 범위 제한 및 노이즈(UI/TypeScript 코드) 필터링, Discord 임베드 개선 | `70dc301` | ✅ |
|
||||
7
docs/devlog/2026-04-12.md
Normal file
7
docs/devlog/2026-04-12.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# 2026-04-12
|
||||
|
||||
| NNN | HH:MM | 작업 설명 | `커밋해시` | 완/미 |
|
||||
|-------|-------|----------|-----------|-----------|
|
||||
| 001 | 06:12 | AG Native DOM 파싱 v7 전면 재설계 — data-testid/data-step-index 기반 step-aware 파서, UI 노이즈 차단 | `a4d7286` | 🔧 |
|
||||
| 002 | 07:03 | AG Native 번들 역공학 분석 + V8 CachedData 삭제 — plannerResponse→Whi 렌더러 구조 확인, bot-color가 NUX용임 발견 | — | 🔧 |
|
||||
| 003 | 07:37 | Observer v8 전면 개편 — conversation-view 의존 제거, body 전체 무조건 덤프(depth 15), 5s/15s/60s 자동 덤프, VSIX v0.5.37 설치 | `0e03b3a` | 🔧 |
|
||||
7
docs/devlog/2026-04-13.md
Normal file
7
docs/devlog/2026-04-13.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# 2026-04-13
|
||||
|
||||
| NNN | HH:MM | 작업 설명 | `커밋해시` | 완료여부 |
|
||||
|-------|-------|----------|-----------|----------|
|
||||
| 001 | 09:50 | Observer v8 검증 — Extension POLL 확인, HTML 패치 확인, V8 캐시 삭제(24MB), BEACON 미수신(AG 재시작 필요) | 없음 | 🔧 |
|
||||
| 002 | 12:34 | DOM Observer 데이터 품질 검증 + UTF-8 인코딩 수정 + noise 필터 강화 (v0.5.39) | `pending` | ✅ |
|
||||
| 003 | 19:26 | Observer v9: "Running N commands" 오인 수정 + DOM-climbing 컨텍스트 추출 + http-bridge 필터 완화 (v0.5.40) | `pending` | 🔧 |
|
||||
5
docs/devlog/2026-04-14.md
Normal file
5
docs/devlog/2026-04-14.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 2026-04-14
|
||||
|
||||
| NNN | HH:MM | 작업 설명 | `커밋해시` | 완료여부 |
|
||||
|-------|-------|----------|-----------|----------|
|
||||
| 001 | 07:29 | Observer v9 E2E 검증 — BEACON/ping 동작 확인, pending POST 수신 확인, 컨텍스트 추출 실패 진단 (ctx="Always run"), v10-diag 진단 로깅 추가 (observer + http-bridge), V8 캐시 삭제(523MB) | `c1e61d8` | 🔧 |
|
||||
6
docs/devlog/2026-04-15.md
Normal file
6
docs/devlog/2026-04-15.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# 2026-04-15
|
||||
|
||||
| NNN | HH:MM | 작업 설명 | `커밋해시` | 완료? |
|
||||
|-------|-------|----------|-----------|----------|
|
||||
| 001 | 09:12 | PROMPT_ONLY_RE 근본원인 분석 및 수정 — Observer regex 이스케이핑(4중→2중 backslash) + http-bridge ellipsis prefix 지원, 16개 테스트 전체 통과, VSIX v0.5.45 빌드/배포 | `01539e9` | ✅ |
|
||||
| 002 | 10:35 | Observer fallback 컨텍스트 추출 수정 — v0.5.45 VSIX 설치 누락 발견/수정 + v13 `_promptOnlySkipped` 플래그로 채팅/UI 텍스트 추출 차단 + bridge generic button 무조건 필터 (v0.5.46) | `b8cda27` | 🔧 |
|
||||
11
docs/devlog/2026-04-16.md
Normal file
11
docs/devlog/2026-04-16.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# 2026-04-16
|
||||
|
||||
| NNN | HH:MM | 작업 설명 | `커밋해시` | 완료? |
|
||||
|-------|-------|----------|-----------|----------|
|
||||
| 001 | 04:52 | v16 터미널 출력 필터 + v15 stale LS 자동복구 + heartbeat probe — stdout가 enrichedCmd로 채택되는 버그 수정, VSIX v0.5.50 빌드/배포 | `7ade31e` | 🔧 |
|
||||
| 002 | 05:28 | AG Native AI 응답 Discord 미전달 근본원인 분석 + Observer v15 scanChatBodies 이중전략 (#conversation + .leading-relaxed.select-text) 구현, v0.5.51 배포 | `729875f` | 🔧 |
|
||||
| 003 | 17:13 | Observer v15 E2E 코드 검증 — CSP/체크섬/문법/핸들러 전수 확인 OK. BEACON=0 원인: AG 미재시작(렌더러 HTML 캐시). v0.5.50/out에 v15 JS 직접 배포 | — | ✅ |
|
||||
| 004 | 21:07 | Observer v16 CSS 추출 버그 수정 — `<style>` 태그 textContent가 AI 응답으로 Discord 전달. extractCleanStepText()에 style/script strip 추가, v0.5.52 배포 | `62ee081` | ✅ |
|
||||
| 005 | 22:07 | Observer v17 Always run 자동승인 + Retry 릴레이 — "Always run" 브릿지 레벨 자동승인, Retry 버튼 Discord 전달, v0.5.53 배포 | `7dbf73a` | ✅ |
|
||||
|
||||
| 006 | 21:28 | AG Native <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Markdown <20><><EFBFBD><EFBFBD> (<28><><EFBFBD><EFBFBD>Ʈ/<2F><>) <20><><EFBFBD><EFBFBD> <20><> User <20><>û <20>̼<EFBFBD><CCBC><EFBFBD> <20>м<EFBFBD>, <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><>ȹ<EFBFBD><C8B9> <20>ۼ<EFBFBD> (v0.5.54 <20><><EFBFBD><EFBFBD> <20><>) | ? | ?? |
|
||||
5
docs/devlog/2026-04-17.md
Normal file
5
docs/devlog/2026-04-17.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 2026-04-17
|
||||
|
||||
| NNN | HH:MM | 작업 설명 | `커밋해시` | 완료 |
|
||||
|-------|-------|----------|-----------|----------|
|
||||
| 001 | 08:05 | v18 Observer DOM->Markdown 파서 개선(<a> 파싱 포함) 및 노이즈 필터 부작용(코드 블럭 잘림 방지) 해결, User 구문 추출 연동, v0.5.56 배포 | `미정` | ✅ |
|
||||
5
docs/devlog/2026-04-18.md
Normal file
5
docs/devlog/2026-04-18.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Devlog — 2026-04-18
|
||||
|
||||
| # | 시간 | 작업 설명 | 커밋 | 상태 |
|
||||
|---|------|----------|------|------|
|
||||
| 001 | 09:20~23:50 | Retry auto-approve 흐름 복구 — WS response 파일 보존 (`_from_ws`), Observer 형제 탐색(sibling), thinking 블록 필터링 | `pending` | ✅ |
|
||||
26
docs/devlog/2026-04-19.md
Normal file
26
docs/devlog/2026-04-19.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Devlog 2026-04-19
|
||||
|
||||
## 작업 인덱스
|
||||
|
||||
| # | 시간 | 작업 | 커밋 | 상태 |
|
||||
|---|------|------|------|------|
|
||||
| 001 | 21:22 | v30-32 Observer 명령어 추출 안정화 (터미널 프롬프트 조기감지) | `bd5a7ca` | ✅ |
|
||||
| 002 | 23:16 | v33 Accept all 자동승인 — diff review auto-approve | `6aea48e` | ✅ |
|
||||
| 003 | 00:18 | v34 Accept all 이중 보장 — agentAcceptAllInFile 직접 호출 | `cf1352e` | ✅ |
|
||||
| 004 | 00:34 | v35 code_edit 자동 Accept — step-probe 경로 | `2bf1eb4` | ✅ |
|
||||
| 005 | 04:26 | v36 Accept all span 감지 — 근본 원인 발견 (button→span) | `e95e779` | ✅ |
|
||||
| 006 | 04:34 | v37 openReviewChanges 선호출 — agentAcceptAllInFile 보조 | `3cc3442` | ✅ |
|
||||
| 007 | 04:43 | v38 _from_ws 마커 추가 — Observer polling 실패 근본 수정 | `7c8891b` | ✅ |
|
||||
|
||||
## v0.5.103 — Accept all (Diff Review) 자동 승인 복구
|
||||
|
||||
### 근본 원인 (2가지)
|
||||
1. **Observer 감지 실패**: AG UI가 "Accept all"을 `<button>`이 아닌 `<span class="cursor-pointer">`로 렌더링. Observer의 `allBtns` 선택자가 `button`만 스캔하여 미감지.
|
||||
2. **Response 파일 race condition**: auto-approve response 파일에 `_from_ws: true` 마커 없음 → `processResponseFile`이 Observer보다 먼저 파일 삭제 → Observer polling 무한 실패.
|
||||
|
||||
### 검증 결과
|
||||
- Observer ACCEPT-SCAN: `tag=SPAN cls=cursor-pointer txt=Accept all` ✅
|
||||
- `DETECTED diff_review: Accept all` ✅
|
||||
- `response served to renderer: ...approved=true` (이전 0건 → 7건) ✅
|
||||
- Discord "자동 승인됨 Accept all" 표시 ✅
|
||||
- 화면에서 "Accept all" 버튼 자동 소멸 확인 ✅
|
||||
44
docs/devlog/entries/20260310-003.md
Normal file
44
docs/devlog/entries/20260310-003.md
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
date: "2026-03-10"
|
||||
seq: 3
|
||||
title: "Discord 승인 플로우 개선 — 파일 권한 3버튼 UI + 경로 표시"
|
||||
tags: [bridge, discord, approval, file-permission]
|
||||
---
|
||||
|
||||
# Discord Bridge 승인 플로우 개선 (cont.)
|
||||
|
||||
## 요약
|
||||
Discord 메시지 릴레이 및 승인 UX를 8개 커밋으로 개선.
|
||||
|
||||
## 변경사항
|
||||
|
||||
### 승인 메시지 텍스트 개선
|
||||
- `verbosity=1` (DEBUG) 추가로 `argumentsJson` 포함 → 전체 명령어 표시
|
||||
- 명령어 길이 제한 150→1500자로 확대
|
||||
- EPHEMERAL 시스템 메시지 필터링 (PLANNER_RESPONSE만 릴레이)
|
||||
|
||||
### 연속 승인 감지
|
||||
- `stallProbed`를 모듈 스코프로 이동, 승인 후 즉시 리셋
|
||||
- `lastPendingStepIndex` 리셋으로 다음 WAITING step 즉시 감지
|
||||
|
||||
### 파일 권한 3버튼 UI
|
||||
- DOM Observer에서 file_permission 감지 시 `buttons` 배열 자동 주입
|
||||
- Discord에 3개 버튼 표시: Allow Once / Allow This Conversation / Deny
|
||||
- `button_index` → scope 매핑: 0=Allow Once(1), 1=Conversation(2)
|
||||
- 10초 dedup 윈도우로 동일 대화상자 중복 pending 방지
|
||||
- 설명 텍스트에서 버튼 라벨 제거 (DenyAllow → 정리)
|
||||
|
||||
### 기타
|
||||
- DOM Observer "Run" 중복 pending 필터링 (step_probe 존재 시 차단)
|
||||
- 인자 값 표시: `DirectoryPath` 키 이름 → 실제 경로 값
|
||||
- `TargetFile` 전체 경로 표시 (basename → fullpath)
|
||||
|
||||
## Commits
|
||||
- `75a3482` 명령어 길이 확대 + EPHEMERAL 필터
|
||||
- `857e101` step_probe verbosity=DEBUG
|
||||
- `c612c37` stallProbed 모듈 스코프 + 리셋
|
||||
- `c9b4fd4` file_permission scope 라우팅
|
||||
- `14d2acf` 3버튼 UI
|
||||
- `bec38f9` DOM Observer Run 중복 필터
|
||||
- `e107b70` file_permission 10s dedup + 텍스트 정리
|
||||
- `47dbd38` 실제 인자 값 표시
|
||||
24
docs/devlog/entries/20260310-006.md
Normal file
24
docs/devlog/entries/20260310-006.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Diff Review Relay + step_type 패스스루 + file_permission 자동감지
|
||||
|
||||
- **시간**: 2026-03-10 14:00~15:55
|
||||
- **Commit**: `c15b0f6`~`d1586c5`
|
||||
|
||||
## 결정 사항
|
||||
|
||||
### Diff Review 감지 방식: IDLE-time → 누적 추적
|
||||
- 처음에는 RUNNING→IDLE 전환 시 마지막 10 step을 조회했으나, AI가 파일 수정 후 compile/deploy/commit 등을 계속 진행해서 IDLE 시점에 파일 수정 step이 범위 밖
|
||||
- **해결**: 실시간 step scan에서 `TargetFile` in `argumentsJson`를 감지해 `pendingModifiedFiles[]`에 누적, IDLE 시 전체 목록으로 알림 생성 후 리셋
|
||||
|
||||
### step_type 패스스루 체인 (3중 버그)
|
||||
- **1차**: `UserResponse` 모델만 step_type 추가, `ApprovalRequest`에는 없음 → bot의 known-fields 필터가 strip
|
||||
- **2차**: bot의 기본 승인/거부 콜백에 step_type 누락 (multi-choice callback에만 있었음)
|
||||
- **3차**: step_probe가 toolName을 그대로 step_type으로 설정 (view_file → 일반 승인)
|
||||
- **해결**: `ApprovalRequest` + `UserResponse` 양쪽에 step_type 추가, 3개 기본 콜백에도 추가, step_probe에서 file-related tools 자동 감지
|
||||
|
||||
### Accept all 원격 실행 한계
|
||||
- `agentAcceptAllInFile`은 AG의 prioritized 명령어 → Extension Host에서 실행 OK하나 UI 포커스 조건 불충족으로 효과 없음
|
||||
- **보류**: 알림만 유지, 실제 Accept는 AG에서 직접 클릭
|
||||
|
||||
## 미완료
|
||||
- Accept all/Reject all 원격 실행 — AG prioritized 명령어 제약
|
||||
- Error retry 원격 실행 — 실제 에러 발생 시 테스트 예정
|
||||
18
docs/devlog/entries/20260310-014.md
Normal file
18
docs/devlog/entries/20260310-014.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# SDK LS 프로세스 대소문자 매칭 버그 수정
|
||||
|
||||
- **시간**: 2026-03-10 22:23 ~ 22:47
|
||||
- **Commit**: `21fd309`
|
||||
- **Vikunja**: 신규 생성 → done
|
||||
|
||||
## 결정 사항
|
||||
- SDK `_findLSProcess()`의 버그를 SDK 자체 수정 대신 **extension 단에서 우회(fixLSConnection)**하기로 결정. SDK는 빌드된 패키지이므로 직접 수정 불가. `sdk.ls.setConnection()` API로 재연결.
|
||||
- 각 AG 창이 **별도 LS 프로세스**를 가진다는 사실 확인 (`--workspace_id`로 구분). 기존에 "모든 AG가 하나의 LS를 공유한다"던 가정은 틀림.
|
||||
|
||||
## 근본 원인
|
||||
- SDK hint: `desktop_variet_agent` (`.toLowerCase()` 적용)
|
||||
- LS command line: `file_c_3A_Users_Certes_Desktop_variet_agent` (원본 대소문자)
|
||||
- `String.includes()` 대소문자 구분 → `desktop` ≠ `Desktop` → 매치 실패
|
||||
- SDK fallback: `lines[0]` (첫 번째 LS = gravity_control) → wrong LS 연결
|
||||
|
||||
## 미완료
|
||||
- AG 풀 재시작 후 E2E 검증 필요 (`[LS-FIX] ✅ Reconnected` 로그 확인)
|
||||
31
docs/devlog/entries/20260315-001.md
Normal file
31
docs/devlog/entries/20260315-001.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# 승인 신호 누락 진단 & 5건 버그 수정
|
||||
|
||||
- **시간**: 2026-03-15 07:00~08:16
|
||||
- **Commit**: `40e3cd5`
|
||||
|
||||
## 결정 사항
|
||||
|
||||
### DEDUP conversation_id 가드
|
||||
- `step_index` 만으로 중복 판정 → 크로스 세션 충돌 빈번
|
||||
- `project_name`만으로 불충분 (같은 Extension이 여러 세션 관찰 가능)
|
||||
- **`conversation_id`까지 비교**가 정확한 DEDUP 조건
|
||||
|
||||
### fs.watch 대신 polling
|
||||
- Windows에서 `fs.watch` silent fail 확인 (실측 테스트)
|
||||
- response watcher도 같은 이슈 있음 (known-issue [2026-03-11])
|
||||
- **모든 watcher에 polling fallback 병행** 원칙 확립
|
||||
|
||||
### stallProbed 시간 기반 리셋 — 불채택
|
||||
- 유저 의견: fix #1로 DEDUP 해결되면 자연스럽게 delta>0 → stallProbed 리셋
|
||||
- 30초 리셋은 LS stale 시 불필요한 RPC 호출만 증가
|
||||
- LS stale은 AG 내부 문제 → AG 재시작이 올바른 해결
|
||||
|
||||
## 5건 수정 요약
|
||||
|
||||
| # | 파일 | 수정 |
|
||||
|---|------|------|
|
||||
| 1 | extension.ts | DEDUP `conversation_id` 가드 |
|
||||
| 2 | extension.ts | `projectName=default` pending 억제 |
|
||||
| 3 | extension.ts | commands dir 폴링 fallback |
|
||||
| 4 | extension.ts | auto 확인 chat_snapshot |
|
||||
| 5 | bot.py | `on_message` 메시지 ID 중복 방지 |
|
||||
41
docs/devlog/entries/20260316-002.md
Normal file
41
docs/devlog/entries/20260316-002.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# DOM Observer 분석 + diff_review 수정 + 배포
|
||||
|
||||
- **시간**: 2026-03-16 13:25~14:20
|
||||
- **Commit**: `f302984`
|
||||
- **Vikunja**: #384 diff_review 원격 승인 테스트 → 진행중
|
||||
|
||||
## 분석 결과
|
||||
|
||||
extension.ts 3,166줄 전체를 5개 기능 영역으로 분류:
|
||||
1. AG 정보 추출 (SDK, 세션 모니터, step probe) ~700줄
|
||||
2. 승인 실행 (RPC) ~200줄
|
||||
3. 파일 기반 IPC (리팩토링 대상) ~500줄
|
||||
4. DOM Observer 인프라 (제거 검토) ~1,100줄
|
||||
5. 기타 유틸 ~200줄
|
||||
|
||||
**DOM Observer는 제거 가능하다** — RPC가 모든 승인을 커버하며, diff_review도 VS Code 명령으로 처리 가능.
|
||||
단, diff_review 원격 실행이 먼저 동작해야 DOM 제거에 동의 가능 (사용자 요구).
|
||||
|
||||
## 결정 사항
|
||||
- write 도구를 `code_edit` step_type으로 분리 (기존: `file_permission`에 혼재)
|
||||
- diff_review 핸들러를 2-strategy 방식으로 리팩토링:
|
||||
1. `AcknowledgeCascadeCodeEdit` RPC (UI 조작 불필요)
|
||||
2. `openReviewChanges` + 파일 포커스 + `agentAcceptAllInFile` (fallback)
|
||||
- pending에 `modified_files`(전체경로)와 `edit_step_indices`(step 번호) 포함
|
||||
- diff_review pending 생성을 8초 지연 (AI 응답이 먼저 Discord 도착)
|
||||
|
||||
## 테스트 결과 (1차)
|
||||
- ✅ Discord에 Accept all / Reject all 버튼 표시 확인
|
||||
- ❌ AcknowledgeCascadeCodeEdit RPC 미실행 — 소스 수정 후 컴파일+배포 누락 발견
|
||||
- ❌ AI 응답이 승인 버튼보다 늦게 Discord에 도착
|
||||
|
||||
## 수정 및 배포
|
||||
- Extension 재컴파일+배포 (2-strategy diff_review 핸들러 포함)
|
||||
- diff_review pending 8초 지연 (`setTimeout`) 추가
|
||||
- known-issues 2건 추가 (isDirty 실패, pending 순서)
|
||||
|
||||
## 미완료
|
||||
- **AG 풀 재시작 후 2차 E2E 테스트** 필요
|
||||
- AcknowledgeCascadeCodeEdit RPC 성공 확인
|
||||
- AI 응답 → 승인 버튼 순서 확인
|
||||
- DOM Observer 제거 리팩토링 → diff_review 동작 확인 후 진행
|
||||
43
docs/devlog/entries/20260316-003.md
Normal file
43
docs/devlog/entries/20260316-003.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# diff_review steps=[] 근본 원인 분석 + v0.3.13 수정
|
||||
|
||||
- **시간**: 2026-03-16 15:18~16:55
|
||||
- **Commit**: `00b9491` → (후속 커밋 예정)
|
||||
- **Vikunja**: #384 diff_review 원격 승인 테스트 → 🔧 진행중
|
||||
|
||||
## 분석 결과
|
||||
|
||||
2차 E2E 테스트에서 `AcknowledgeCascadeCodeEdit(steps=[])` → `SUCCESS: {}` 반환 확인.
|
||||
RPC 자체는 성공하지만 빈 step 배열로 호출하면 no-op — AG의 diff review 바 유지됨.
|
||||
|
||||
**근본 원인 체인:**
|
||||
1. Extension이 `writePendingApproval()`로 diff_review pending 생성 (edit_step_indices=[86] 포함)
|
||||
2. Collector가 Gateway에 전달 후 로컬 pending 파일을 삭제 (collector.py L259)
|
||||
3. Discord Accept 클릭 → Gateway → Collector가 response 파일 작성
|
||||
4. Extension의 `processResponseFile`이 pending 파일에서 `edit_step_indices` 읽으려 함 → **파일 이미 없음**
|
||||
5. `trackedSteps=[]` → `AcknowledgeCascadeCodeEdit(steps=[])` → no-op
|
||||
|
||||
## 수정 (v0.3.13)
|
||||
|
||||
- `diffReviewMetadata` 인메모리 Map 추가 (extension.ts L57)
|
||||
- `writePendingApproval`에서 diff_review pending 생성 시 메타데이터를 메모리에 캐시
|
||||
- `processResponseFile`에서 메모리 먼저 조회, pending 파일은 fallback
|
||||
- `modifiedFiles` 변수를 Strategy 1/2 공유 스코프로 호이스팅
|
||||
|
||||
## 3차 E2E 테스트 (16:25~16:40)
|
||||
|
||||
- ✅ 인메모리 캐시 저장/로드 정상: `steps=[40]` → memory에서 로드
|
||||
- ✅ RPC 호출 시 실제 step 인덱스 전달: `steps=[40]`, `steps=[101,114,123,126]`
|
||||
- ❌ **RPC SUCCESS 반환하지만 AG diff review bar 미해제** — 실제 no-op
|
||||
|
||||
## 원인 분석 + 실험 (16:40~16:55)
|
||||
|
||||
RPC가 `SUCCESS: {}` 반환해도 unknown field를 무시하는 proto 동작일 가능성.
|
||||
`stepIndices` 필드명이 AG 기대값과 다르거나, step index 값 자체가 불일치.
|
||||
|
||||
4가지 파라미터 형식 실험 코드 Extension에 배포:
|
||||
- A: `stepIndices` (현재 방식)
|
||||
- B: stepIndices 없이 (전체 accept)
|
||||
- C: `steps` 키 (필드명 오류 가능성)
|
||||
- D: `trajectoryId` 추가
|
||||
|
||||
→ AG 재시작 후 diff_review 트리거 시 각 형식의 결과 비교 예정
|
||||
12
docs/devlog/entries/20260323-001.md
Normal file
12
docs/devlog/entries/20260323-001.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# WebSocket 좀비 커넥션 해결 및 통신망 메모리 누수 구조 패치
|
||||
|
||||
- **시간**: 2026-03-23 21:09~21:20
|
||||
- **Commit**: `ecebec3`
|
||||
- **Vikunja**: #510 → done
|
||||
|
||||
## 결정 사항
|
||||
- **ws-client.ts 핑퐁 와치독(Ping-Pong Watchdog)**: 단순 에러 캐치가 아니라 `ws.terminate()`를 통해 무반응 소켓을 강제 종료하여 자체 재연결 로직(`_onDisconnect`)을 활성화하도록 설계.
|
||||
- **통신망 추적 변수 캡핑(Bounded Cap)**: `hub.py`의 `pending_owners` 및 `bot.py`의 `_sent_approval_ids` 등 무한히 쌓일 수 있는 파이썬 딕셔너리에 LRU(오래된 순 삭제) 로직을 추가. 비록 당장 OOM을 유발하진 않지만 이 구조적 메모리 누수(Leak)를 원천적으로 차단하여 시스템 안정성을 극대화함.
|
||||
|
||||
## 미완료
|
||||
- 없음
|
||||
16
docs/devlog/entries/20260323-002.md
Normal file
16
docs/devlog/entries/20260323-002.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Cross-Project DOM Observer Leakage 해결
|
||||
|
||||
- **시간**: 2026-03-23 22:00~22:45
|
||||
- **Commit**: `TBD`
|
||||
- **Vikunja**: #TBD → done
|
||||
|
||||
## 확인된 사실
|
||||
- Discord 신호 누락이 아닌, 다중 원격 환경에서의 포트 덮어쓰기 문제로 인한 **교차 프로젝트 신호 오염(Leakage)**이었음.
|
||||
|
||||
## 삽질 / 트러블슈팅
|
||||
- 처음에는 디스코드 봇(`bot.py`)이나 익스텐션의 `step_type` 매핑 로직 누락인 줄 알고 코드를 탐색했으나, 실제 DOM observer 스크립트에 하드코딩된 Port 변수가 문제의 원인임을 파악함.
|
||||
- 다중 원격 컴퓨터 환경 중 포트 포워딩(`12345` 충돌 우회)으로 인한 이슈를 해결하기 위해 `vscode.env.asExternalUri`를 도입. 로컬에 매핑된 최종 확정 포트를 알아냄.
|
||||
|
||||
## 결정 사항
|
||||
- DOM Status Bar(`tooltip`)를 일종의 단방향 IPC(Inter-Process Communication) 대용으로 사용하기로 결정함.
|
||||
- Extension Host가 렌더러(DOM Observer)에게 안전하고 해당 창에만 격리(Window-isolated)된 방식으로 포트 번호를 전달할 수 있음. 전역 HTML 파일 패치의 한계를 우아하게 극복함.
|
||||
12
docs/devlog/entries/20260324-001.md
Normal file
12
docs/devlog/entries/20260324-001.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# v0.5.6 좀비 커넥션 패치 회귀 오류 해결 (v0.5.8 반영)
|
||||
|
||||
- **시간**: 2026-03-23 23:10 ~ 2026-03-24 07:05
|
||||
- **Commit**: `TBD`
|
||||
- **Vikunja**: 신규 추가 예정
|
||||
|
||||
## 결정 사항
|
||||
- **False Positive 멈춤 현상 원인 규명**: v0.5.6에서 추가된 `pongTimeoutTimer` (10초 타임아웃)가 VS Code 확장 내부의 일시적인 Event Loop 블로킹 발생 시 네트워크 I/O(`pong` 응답)보다 먼저 소켓을 강제 종료하고 있었습니다. 이 때문에 멀쩡한 연결이 끊어지고 재연결 지연 페널티가 누적되어 최대 60초까지 응답 불가(멈춤) 상태에 빠지는 현상이 발견되었습니다.
|
||||
- **해결 방안 선택 (타임스탬프 검증)**: 타이머 동시성 경합을 유발하는 `setTimeout` 방식을 전면 폐기하고, 기존의 `setInterval` (25초 주기) 하트비트 루프 내부에서 `ws.on('pong')`이 갱신하는 `lastPongTime`을 대조(`Date.now() - lastPongTime > 60000`)하는 방식으로 변경했습니다. 이를 통해 Event Loop가 지연되더라도 I/O 이벤트를 먼저 수확한 후에 안전하게 판독할 수 있어 오진단(False Positive)을 원천 차단하면서도 좀비 커넥션을 방지했습니다.
|
||||
|
||||
## 미완료
|
||||
- 없음 (v0.5.8 VSIX 컴파일 성공 및 배포 완료)
|
||||
18
docs/devlog/entries/20260324-002.md
Normal file
18
docs/devlog/entries/20260324-002.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# DOM Observer VS Code 네이티브 알림 UI 캡처 블라인드 스팟 해결 (v0.5.9)
|
||||
|
||||
- **시간**: 2026-03-24 12:00~13:00
|
||||
- **Commit**: `7b6cd59`
|
||||
- **Vikunja**: #514
|
||||
|
||||
## 결정 사항
|
||||
- **문제**: "Always Allow" 및 "Allow Alt+↵" (단축키 포함) 권한 알람이 Discord로 전송되지 않는 문제가 발생했습니다. (v0.5.8)
|
||||
- **근본 원인 확인**:
|
||||
- Regex 실패: `Always Allow`는 `^Allow` 정규식을 통과하지 못합니다.
|
||||
- CSS Selector 실패: `observer-script.ts`의 스캔 엔진이 오직 `document.querySelectorAll('button')`에만 의존하여 렌더링 노드를 찾고 있었습니다. VS Code 네이티브 권한 프롬프트(토스트 알림 및 채팅 패널)는 `<a role="button" class="monaco-text-button">` 또는 `<vscode-button>`을 활용하므로 애초에 찾지도 못하고 스킵되었습니다.
|
||||
- **해결책**:
|
||||
1. `observer-script.ts` 내의 모든 DOM 쿼리를 `button, [role="button"], vscode-button, .monaco-text-button` 으로 확장.
|
||||
2. 허용 권한 토큰 관련 정규식을 `/^(?:Always )?Allow/i` 로 상향 패치.
|
||||
3. `v0.5.9` 로 빌드 및 VSIX 설치 완료 후 정상 동작 검증 완료.
|
||||
|
||||
## 미완료
|
||||
- 없음.
|
||||
18
docs/devlog/entries/20260324-003.md
Normal file
18
docs/devlog/entries/20260324-003.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# DOM Observer /trigger-click 렌더링 순서 오작동 및 False Positive 프리징 해결 (v0.5.10)
|
||||
|
||||
- **시간**: 2026-03-24 17:50~18:20
|
||||
- **Commit**: `HEAD` (예정)
|
||||
- **Vikunja**: #514 관련 디버깅/핫픽스
|
||||
|
||||
## 결정 사항
|
||||
- **문제**: "0.5.9 패치한 이후 화면이 펜딩되서 움직여지지않아" 라는 증상 확인.
|
||||
- v0.5.9에서 DOM 쿼리를 `[role="button"]` 등으로 확장했으나, 정규식이 `/^Run/i` 등으로 풀어진 상태여서 에디터 뷰의 "Run Test" 등 수많은 CodeLens 버튼들을 Agent의 트리거로 오인함.
|
||||
- 결과적으로 아무 조작도 하지 않았는데 계속 터미널 실행 대기상태(Pending)로 무한 진입하여 UI 화면이 프리징(Freeze)됨.
|
||||
- 특히 디스코드에서 `Approve` 명령을 내렸을 때도, DOM 트리상 상단에 우연히 "Run" CodeLens가 있으면 먼저 캡처되어 진짜 Agent 패널의 버튼을 클릭하지 못하고 엉뚱한 요소를 클릭하는 위험한 순위 불일치 버그까지 있었음.
|
||||
- **해결책 (Structural Context Filtering)**:
|
||||
1. 감지(Scan): 단순 정규식을 빡빡하게 변경하면 동적인 버튼 이름("Run script" 등)이 안 먹히는 부작용이 있으므로 느슨함을 유지하되, **발생 영역(DOM Context)**에 강제 필터를 부여.
|
||||
- `isVSCodeMainWindow` 및 노드 루트가 `document.body`인지를 체크하여, 에디터 본문 영역 안에서는 "Run", "Approve", "Accept" 캡처를 전부 무시.
|
||||
2. 제어(Trigger-click 우선순위): `observer-script.ts`의 `deepFindButtons()` 내부 스캔 트리를 변경하여 `findPanel()`로 안티그래비티 패널을 1순위로 조회, 알림 Toast를 2순위, 본문 Document를 3순위로 탐색하게 강제하여 엉뚱한 버튼 클릭 사고를 100% 방지함.
|
||||
|
||||
## 미완료
|
||||
- 없음 (빌드 및 검증 완료)
|
||||
11
docs/devlog/entries/20260401-001.md
Normal file
11
docs/devlog/entries/20260401-001.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# step-probe Pagination 10-Item Truncation vs LS DoS 오류 수정
|
||||
|
||||
- **시간**: 2026-04-01 13:00~18:22
|
||||
- **Commit**: `TBD`
|
||||
- **Vikunja**: #N/A (임시 버그 픽스)
|
||||
|
||||
## 결정 사항
|
||||
- 기존 `v0.5.13`에서 `limit: 100`으로 Pagination Limit(기본 10개)을 우회하려 했으나, LS DB 스캔 및 거대한 JSON 파싱이 VS Code Event Loop 블로킹을 유발하여 UI 멈춤(DoS) 발생.
|
||||
- 롤백 과정에서 `{}`(인자 없음)으로 원복하면서 필수적인 `descending: true` 파라미터까지 누락됨.
|
||||
- 이로 인해 `guitar_score` 등의 최신 작성 세션이 LS 조회 리밋(10)에서 밀려나 승인 신호를 수신하지 못하는 이슈 재발.
|
||||
- 이를 해결하기 위해 `limit: 30, descending: true`로 설정. 파싱해야 할 JSON 객체 수를 1/3로 줄임과 동시에, 정렬 보장을 통해 최근 10초 이내에 활성화된 세션은 언제나 Index 0번 최상단에 고정되게끔 메커니즘을 수정함.
|
||||
20
docs/devlog/entries/20260408-004.md
Normal file
20
docs/devlog/entries/20260408-004.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# 2026-04-08 (004) - SafeToAutoRun 명령어의 디스코드 알림 누락 복구
|
||||
|
||||
## 1. 이슈 개요
|
||||
- 사용자가 `/start` 등 백그라운드 명령어(SafeToAutoRun)를 포함한 워크플로우를 실행하였으나, 디스코드로 아무런 메시지도 전송되지 않는 버그가 보고됨.
|
||||
|
||||
## 2. 원인 분석
|
||||
- v0.5.16 배포 당시 Discord 중복 알림(Pending 파일) 이슈를 방지하는 과정에서, `step-probe.ts`에 있던 "⚡ 자동 실행됨" 원본 알림 코드(snapshot 생성 로직)까지 실수로 함께 삭제됨.
|
||||
- `SafeToAutoRun` 구문에서 `writePendingApproval` 스킵 로직은 잘 동작하고 있었으나, 정작 사용자에게 알려야 할 기본적인 '자동 실행됨' 정보마저 소실되어 결과적으로 아무 알림도 가지 않는 침묵 상태가 됨.
|
||||
|
||||
## 3. 해결 및 적용 사항
|
||||
1. `step-probe.ts` 복구
|
||||
- `SafeToAutoRun` 판단 시 `autoRunSteps`를 마킹한 직후 `ctx.writeChatSnapshot()`을 호출하도록 코드를 추가 복원함.
|
||||
- 출력 구조: `💬 **자동 실행됨** (step N)\n\n\`명령어내용\``
|
||||
2. **v0.5.18 배포**
|
||||
- 익스텐션의 `package.json` 버전을 `0.5.18`로 펌핑.
|
||||
- 사전 스크립트가 적용된 `vsce package`를 통해 새로운 `gravity-bridge-0.5.18.vsix` 패키징을 완료함.
|
||||
|
||||
## 4. Next Step
|
||||
- `extension/gravity-bridge-0.5.18.vsix` 파일을 VS Code에 수동 설치할 것 (Install from VSIX...).
|
||||
- 설치 후 반드시 **Reload Window**하여 테스트 수행 요망.
|
||||
20
docs/devlog/entries/20260408-005.md
Normal file
20
docs/devlog/entries/20260408-005.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# 2026-04-08 (005) - SafeToAutoRun 로컬 자동 승인 누락 데드락(Freeze) 해결
|
||||
|
||||
## 1. 이슈 개요
|
||||
- 사용자가 확인 결과 v0.5.15 이후 백그라운드 터미널 명령어 등 모든 AI 에이전트 작업이 '자동 실행됨' 스냅샷만 보내고 VS Code 내부적으로는 여전히 승인(Allow)을 대기하며 완전히 멈춰버림(Freeze).
|
||||
- 신호가 전달조차 안되고 다음 단계로 진행하지 못하는 심각한 블로커 이슈가 발생함.
|
||||
|
||||
## 2. 원인 분석
|
||||
- v0.5.16 버그 픽스("Discord 중복 알림 방지") 당시 `SafeToAutoRun` 상태일 때 `writePendingApproval()`을 수행하지 않도록 코드(`skip pending`)를 변경했음.
|
||||
- 그러나 과거에는 이 Pending 파일이 생성되면 파이썬 백엔드(Bot)가 디스코드에 알림을 띄운 직후, 자동으로 `approve`(허용) 신호를 익스텐션 쪽에 보내어 다음 단계가 허가되었음.
|
||||
- 즉, 익스텐션에서 Pending 파일 생성을 중단(skip)하자 봇으로부터 수락 신호가 아예 오지 않게 되었고, VS Code의 보안 시스템에 의해 명령어는 영원히 "Run(Auto)" 클릭 승인을 대기하는 상태의 데드락에 빠져버림.
|
||||
|
||||
## 3. 해결 및 적용 사항
|
||||
1. `step-probe.ts` 로컬 자동 승인 복구
|
||||
- `safeToAutoRun` 판단으로 Pending 파일 생성을 건너뛸 때, 익스텐션 스스로 백그라운드 승인을 트리거하도록 `tryApprovalStrategies(true, ...)` 함수 호출 코드를 명시적으로 추가함.
|
||||
- 이를 통해 봇의 승인 신호를 기다릴 필요 없이 즉각적으로 승인(Accept)을 단행하여 막힘없이 스텝이 연속 진행되도록 고침.
|
||||
2. **v0.5.19 배포**
|
||||
- VSIX 버전을 `0.5.19`로 펌핑 후 `npx vsce package` 명령으로 익스텐션을 재빌드함.
|
||||
|
||||
## 4. Next Step
|
||||
- `extension/gravity-bridge-0.5.19.vsix` 파일을 수동 재설치하고 VS Code Window를 Reload 한 뒤, `/start` 같은 자동 워크플로우를 재실행하여 신호 블로킹(Freeze) 버그가 해결되었는지 최종 확인.
|
||||
17
docs/devlog/entries/20260408-006.md
Normal file
17
docs/devlog/entries/20260408-006.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# SafeToAutoRun 데드락 및 익스텐션 프리징 완벽 해결 (v0.5.20)
|
||||
|
||||
- **시간**: 2026-04-08 07:15~07:30
|
||||
- **Commit**: `임시해시`
|
||||
- **Vikunja**: #589 → done
|
||||
|
||||
## 발생 문제
|
||||
1. **Deadlock**: 이전 버전(v0.5.15)에서 디스코드 알림을 줄이려고 익스텐션의 `step-probe.ts`가 `SafeToAutoRun` 발생 시 `pending` 파일 생성 자체를 건너뛰도록 구현함. 하지만 AG 엔진은 CORTEX_STEP_STATUS_WAITING 상태에서 누군가가 해결해주기를 영원히 기다리게 되어, 파이프라인 전체가 데드락(UI 멈춤)에 빠지는 치명적인 부작용 발생.
|
||||
2. **이벤트루프 Freeze**: `extension.ts`의 `detectProjectName` 내부에서 동기식 `cp.execSync('git remote get-url origin')`를 실행하여 윈도우 환경에서 VS Code 이벤트루프가 막히고 WebSocket 통신이 유실되는 현상 발생.
|
||||
|
||||
## 결정 사항
|
||||
- **Agnostic Bridge 철학 준수 (단일 경로 원칙 복구)**
|
||||
- 익스텐션(`step-probe.ts`)은 절대 자의적으로 승인 처리를 하거나 `pending` 파일 생성을 스킵해서는 안 됨. 오직 브릿지 중계자 역할에 충실하도록 롤백하고, 대신 메타데이터에 `safe_to_auto_run: true` 속성을 실어 보냄.
|
||||
- 파이썬 서버(`bot.py`) 관제탑이 이를 확인하면 디스코드에 알림(`Embed`)을 보내는 단계만 슬쩍 생략하고 그 즉시 허가증(`response/`)을 발급. 이를 통해 데드락 해제와 무소음 승인을 동시에 만족함.
|
||||
- **비동기화 및 빌드 파이프라인 강제**
|
||||
- 동기식 git 명령어 대신 비동기식 `.git/config` 파일 읽기로 교체.
|
||||
- `package.json`에 `vscode:prepublish` 스크립트를 부활시켜 낡은 소스코드가 VSIX에 패키징되는 문제 원천 차단.
|
||||
18
docs/devlog/entries/20260408-007.md
Normal file
18
docs/devlog/entries/20260408-007.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Gravity Bridge 알림 최적화 및 연동 디버깅 완료
|
||||
|
||||
- **시간**: 2026-04-08 17:00~17:55
|
||||
- **Commit**: `pending`
|
||||
- **Vikunja**: 대상 작업 맵핑 예정
|
||||
|
||||
## 결정 사항
|
||||
- `SafeToAutoRun` 시 `step-probe.ts`에서 날리던 **"⚡ 자동 실행됨"** 알림은 과감하게 완전히 제거하였습니다. 파이썬 봇이 이미 "🤖 자동 승인됨" Embed를 송출하고 있으므로 디자인 철학(익스텐션은 중립적인 릴레이 역할만 수행하고, 비즈니스 판정 알림은 중앙 봇이 담당)에 부합합니다.
|
||||
- `extension.ts`의 `writeChatSnapshot` 의존성을 줄여 트래픽 낭비와 중복 노이즈를 해소했습니다.
|
||||
|
||||
## 핵심 디버깅 (Troubleshooting)
|
||||
- **`variet-llm` 프로젝트 연동 실패 이슈:**
|
||||
1. `variet-llm` 창을 열었으나 채팅 패널을 열지 않아 안티그래비티 전용 언어 서버(LS)가 띄워져 있지 않은 상태에서 브릿지를 켬. 브릿지가 엉뚱하게 `gravity_control` LS에 바인딩 됨.
|
||||
2. 사용자가 Discord에서 `variet-llm` 채널을 삭제해 버렸는데, 파이썬 봇(`bot.py`)은 캐시를 가지고 있어서 자신이 파괴된 채널을 대상으로 계속 통신을 시도하며 새 채널을 파지 않음 (HTTP 404).
|
||||
3. 로컬 윈도우 재시작 및 도커(`docker-compose restart`) 컨테이너 재가동을 통해 **봇 프로세스 캐시 초기화** → 채널 자동 재생성, 완벽 디버깅에 성공했습니다.
|
||||
|
||||
## 미완료
|
||||
- 없음. 모두 성공적으로 동작 중.
|
||||
18
docs/devlog/entries/20260409-001.md
Normal file
18
docs/devlog/entries/20260409-001.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Agent UI Tailwind/Native 마이그레이션 대응 (DOM 옵저버 구조 개편)
|
||||
|
||||
- **시간**: 2026-04-09 19:40~21:55
|
||||
- **Commit**: `[임시해시]`
|
||||
- **Vikunja**: 신규 생성 후 완료 처리
|
||||
|
||||
## 트러블슈팅 및 결정 사항
|
||||
최근 UI 업데이트 후 Discord 릴레이 신호(Run, Accept) 단절.
|
||||
deep-inspect 덤프 분석 결과 Webview/Iframe 환경이 사라지고 Native DOM(VS Code 본문)에 напрямую 그려짐, 기존 시맨틱 클래스가 Tailwind로 변경.
|
||||
1. 기존 `findPanel`이 패널을 못 찾자 `isBodyRoot` 모드로 스캔
|
||||
2. 과거에 추가된 CodeLens 방어 로직(`if (isVSCodeMainWindow && isBodyRoot && PATS[p].type !== 'diff_review') continue;`)에 의해 모든 버튼 스캔이 **버려지고 있었음**.
|
||||
|
||||
**결정**:
|
||||
엄격한 Panel Class Whitelist 기반 방어를 해제하고, 버튼이 `.monaco-editor` 내부에 있는 경우만 무시하도록 Blacklist 기반 방어로 선회.
|
||||
UI 텍스트 글루잉(아이콘 통합) 대응 위해 패터닝 정규식을 `/^(?:Always\s*)?Run/i` 등으로 완화.
|
||||
|
||||
## 미완료
|
||||
- 없음
|
||||
15
docs/devlog/entries/20260409-002.md
Normal file
15
docs/devlog/entries/20260409-002.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Agent UI 버튼 무시(Discard) 버그 핫픽스
|
||||
|
||||
- **시간**: 2026-04-09 22:10~22:35
|
||||
- **Commit**: `pending`
|
||||
- **Vikunja**: 새로 생성 후 완료 예정
|
||||
|
||||
## 트러블슈팅 및 결정 사항
|
||||
- **이슈**: Native UI 마이그레이션 직후 버튼을 눌러도 브릿지로 신호가 전혀 가지 않는 버그 접수
|
||||
- **원인 분석**:
|
||||
1. `extension.log` 확인 결과 `[HTTP] pending` 자체가 생성되지 않음 (브릿지 자체에 도달하지 않음).
|
||||
2. DOM observer가 수집한 버튼이 `b.closest('.monaco-editor')` 필터 조건에 무조건 걸려서 버려지는 것이었음. Native 전환 후 채팅창이 에디터 탭 내부에 렌더링되면서 `.monaco-editor` 내부 자식이 됨.
|
||||
- **결정**: 기존의 `b.closest('.monaco-editor')` 방어 로직을 폐기하고 실제 CodeLens 버튼 고유의 클래스 `.codelens-decoration`를 명시하도록 변경하여 구조 변화에 강건해지도록 개선 완료. `0.5.22` VSIX 재배포.
|
||||
|
||||
## 미완료
|
||||
- 없음 (검증은 유저 몫으로 인계)
|
||||
17
docs/devlog/entries/20260409-003.md
Normal file
17
docs/devlog/entries/20260409-003.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Agent UI Native 버튼 아이콘 글루잉 무시 현상 수정
|
||||
|
||||
- **시간**: 2026-04-09 23:00~23:15
|
||||
- **Commit**: `TBD`
|
||||
- **Vikunja**: 신규 생성 (UI 텍스트 글루잉 버튼 버그) → done
|
||||
|
||||
## 문제 상황
|
||||
- 0.5.22 패치(CodeLens 필터) 이후에도 `Run`, `Accept` 버튼 클릭 시 디스코드 브릿지로 아무런 펜딩 요청(POST /pending)이 전송되지 않는 현상 발생.
|
||||
- 원인 규명: Native UI 마이그레이션 적용 후, Agent 패널 버튼들의 아이콘(``, `▶` 등)이 리액트/Tailwind 컴포넌트 렌더링을 거쳐 `element.textContent` 상단에 문자열로 직접 병합(Gluing)됨.
|
||||
- 옵저버 스크립트 내부 정규식(`/^(?:Always\s*)?Run/i`)이 문자열의 맨 첫(^) 시작을 강제하기 때문에, 아이콘으로 시작하는 버튼들의 명령어를 전부 오탐으로 간주함.
|
||||
|
||||
## 결정 사항
|
||||
- 버튼의 텍스트를 읽는 즉시, `txt.replace(/^[^a-zA-Z0-9]+/, '')`를 적용하여 첫 글자가 영어/숫자가 될 때까지, 선행하는 모든 특수문자, 아이콘, 폰트 공백 등을 강제 삭제하도록 스크립트 내부의 3가지 탐색 루프 (본문 스캔, Sibling 버튼 수집, Webview trigger-click 인젝션)에 일괄 업데이트.
|
||||
- 기존 `.monaco-editor`나 `.chat-body` 등 부모 컨테이너에 지나치게 의존하던 `findButtonContainer`에 `chat`, `prose`, `markdown`를 추가 화이트리스팅 하되 Tailwind UI 구조 특성상 시맨틱 래퍼를 찾지 못할 경우 3단계 위 부모를 반환하여 안전하게 컨텍스트를 확보하도록 고도화. -> **구조 변경 시에도 유연하게(Graceful) 기능 동작 지원 보장.**
|
||||
|
||||
## 결과
|
||||
- `v0.5.23` (코드상 0.5.22 유지) VSIX 빌드 및 테스트 준비.
|
||||
15
docs/devlog/entries/20260409-004.md
Normal file
15
docs/devlog/entries/20260409-004.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Discord Signal Relay & Auto-Approve Body Null 버그 수정 (False Positive 차단)
|
||||
|
||||
- **시간**: 2026-04-10 00:00~00:10
|
||||
- **Commit**: `HEAD`
|
||||
- **Vikunja**: #태스크번호 → done
|
||||
|
||||
## 트러블슈팅 & 삽질
|
||||
- **DOM 정규식의 반란**: Native UI 패치 이후 버튼의 text-gluing 제거 때문에 정규식을 광범위하게 바꿨으나, 하필 `Run\s*` 조건에 단어 경계(`\b`)를 누락하는 바람에 VS Code 하단의 시스템 상태 버튼인 `Running 1 command`까지 AI의 `Run` 버튼으로 인식해버림. 무한 PENDING 스팸을 만들어 브릿지 큐를 폭파시킨 주범.
|
||||
- **Auto-Approve 본문 누락**: 봇에서 자동 승인 Embed 생성 시 `req.description` (실행될 본문 코드)을 아예 그리지 않고 `req.command` (단순 버튼 라벨)만 출력하도록 코딩되어 있었음. 사용자는 '자동 승인' 알림을 받지만 정작 무엇이 승인되었는지는 전혀 알 수 없어 '본문 표시 자체가 안 된다'고 오해할 수밖에 없었음.
|
||||
- **첫 알림 메시지 무시**: `step-probe.ts`에서 세션 전환 시 `lastNotifyStepIndex`를 초기화할 때 `-1` 로 리셋하지 않아 새 세션의 첫 안내 메시지가 매번 씹히는 증상 발견.
|
||||
|
||||
## 결정 사항
|
||||
- `bot.py`의 Auto-Approve Embed 구문에 수동 승인처럼 본문을 표출하도록 렌더링 로직 통일.
|
||||
- `observer-script.ts`의 `TerminalCommand` 정규식에 `\b`를 추가하여 시스템 버튼과의 혼선을 원천 차단함.
|
||||
- `step-probe.ts` 의 index reset 초기값을 `-1` 로 명시화.
|
||||
13
docs/devlog/entries/20260410-001.md
Normal file
13
docs/devlog/entries/20260410-001.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# step-probe.ts 의 isRunning 조건 누락으로 인한 릴레이 증발 버그 픽스
|
||||
|
||||
- **시간**: 2026-04-10 17:11
|
||||
- **Commit**: `COMMITTING`
|
||||
- **Vikunja**: #125 → done
|
||||
|
||||
## 결정 사항
|
||||
- AI 응답이 비정상적으로 빠를 경우 `RUNNING` 상태의 2초 polling 창을 우회하여 `IDLE` / `WAITING`로 진입해버리는 버그가 있었습니다.
|
||||
- 기존에는 `isRunning && currentCount > ...`로만 Real-time Capture가 동작하여 전부 스킵되는 증상 확인.
|
||||
- `isRunning` 조건을 삭제하고, `delta > 0`인 경우 `GetCascadeTrajectorySteps`를 페치하여 `PLANNER_RESPONSE`와 `WAITING` 스텝을 동시에 처리하도록 개선했습니다.
|
||||
|
||||
## 미완료
|
||||
- 없음.
|
||||
13
docs/devlog/entries/20260410-002.md
Normal file
13
docs/devlog/entries/20260410-002.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Gravity Bridge 빠른 응답(Fast Execution) 누락 오류 해결
|
||||
|
||||
- **시간**: 2026-04-10
|
||||
- **Commit**: TBD
|
||||
- **Vikunja**: #607 → done
|
||||
|
||||
## 문제 원인
|
||||
- AI 생성이나 응답 작업이 폴링 간격(5초) 미만으로 끝났을 때, 익스텐션의 폴링 루프는 이전과 동일한 `IDLE` 상태만을 보게 됨.
|
||||
- `lastResponseCaptureStep` 검사는 마련되어 있었으나, `wasRunning` 플래그 제약(`wasRunning && !isRunning`)으로 인하여 IDLE->IDLE 전이를 거치는 모든 단기응답이 `[RESPONSE-CAPTURE]`를 영구히 건너뛰고 통째로 누락됨.
|
||||
|
||||
## 해결 방법
|
||||
- `wasRunning` 방어 조건을 해제하고, `!isRunning && currentCount > lastResponseCaptureStep` 조건으로 완화 (인덱스 전진 기반 감지로 수정).
|
||||
- 오래된 하드코딩 파서를 버리고 방벽 파서 역할을 하는 `extractPlannerText`로 갈무리 블록의 AI 응답 추출 로직을 단일화하여 적용.
|
||||
15
docs/devlog/entries/20260410-003.md
Normal file
15
docs/devlog/entries/20260410-003.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# [Bridge] Disable DOM Observer Proactive Pending to Fix Empty Bots
|
||||
|
||||
- **시간**: 2026-04-10 16:12
|
||||
- **Commit**: TBD
|
||||
- **Vikunja**: TBD
|
||||
|
||||
## 문제 원인
|
||||
- 디스코드에 Running 1 comman d 나 내용 없는 Allow 가 반복적으로 전송되는 문제.
|
||||
- 원인은 v3 DOM Observer 스크립트가 Native UI에서 발생시키는 빈 껍데기 알림(POST /pending)들이, 정상적으로 본문 정보를 모두 추출해 기다리고 있는 step-probe.ts의 완벽한 Pending을 덮어씌우거나 먼저 처리되어버렸기 때문임.
|
||||
- 단어 경계(\b) 정규식 필터조차 VS Code 렌더링 시 노드 줄바꿈 이슈 등으로 인해 완벽한 방어가 불가능했음.
|
||||
|
||||
## 해결 방법
|
||||
- observer-script.ts에서 버튼 텍스트를 감지해 능동적으로 Pending을 생성하는 기능(PATS 배열)을 **전면 비활성화(배열 비움)**.
|
||||
- 이로써, 오직 100% 신뢰 가능한 SDK RPC(step-probe.ts)만이 대기 상태(WAITING)와 명령어 상세 정보를 포착해 Pending을 생성함.
|
||||
- DOM Observer는 브릿지가 보내는 /trigger-click 폴링 명령어를 받아 실제 물리 클릭만 수행하는 '수동적 렌더러' 역할로 격하됨.
|
||||
14
docs/devlog/entries/20260410-005.md
Normal file
14
docs/devlog/entries/20260410-005.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# GetAllCascadeTrajectories 10-Item Hard Limit Bypass
|
||||
|
||||
- **시간**: 2026-04-10
|
||||
- **Commit**: TBD
|
||||
- **Vikunja**: TBD
|
||||
|
||||
## 결정 사항
|
||||
- `GetAllCascadeTrajectories` LS API의 `limit` 등 페이지네이션 파라미터가 백엔드에서 무시되어 최신 세션이 10개 제한에 잘려나가는 문제를 확인.
|
||||
- `DOM observer`가 더 이상 작동하지 않는 상태(Empty 보디 이슈로 비활성화됨)에서, `step-probe.ts`마저 이 10개 한도 밖으로 밀려난 현재 세션(`activeSessionId`)을 발견하지 못해, 발생한 모든 채팅 이벤트 파일이 작성되지 않는 문제("단 한글자도 안 날아옴")의 근본 원인을 특정함.
|
||||
- `GetDiagnostics` API를 사용하여 내부적으로 저장된 `recentTrajectories` 덤프 전체를 불러와, 기존 `GetAllCascadeTrajectories`의 결과를 병합/보완하도록 변경.
|
||||
- 이를 통해 아무리 많은 수의 세션이 열려 있어도 현재 사용 중인 세션 ID를 식별 가능.
|
||||
|
||||
## 미완료
|
||||
- 없음.
|
||||
15
docs/devlog/entries/20260410-613.md
Normal file
15
docs/devlog/entries/20260410-613.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Fix gravity bridge Discord Relay AI Chat Body by patching DOM extraction and Regex literals
|
||||
|
||||
- **시간**: 2026-04-10 20:30~21:10
|
||||
- **Vikunja**: #613 → done
|
||||
|
||||
## 트러블슈팅: Typescript 백틱 안의 정규식 리터럴 파괴 현상
|
||||
- **증상**: JSDOM 가상 모의 환경에서 테스트를 돌려보니, 렌더링 화면이나 타겟 Text가 정확히 매치됨에도 정규식이 조건문에서 `false`를 내뱉으며 Button Matching을 건너뛰는 현상 발생.
|
||||
- **원인**: `observer-script.ts`를 `.js`로 변환할 때, Typescript 컴파일러가 `return \`...\`` 템플릿 리터럴 내부의 `/^(?:Always\s*)?Allow\b/i` 구문을 해석하면서, `\s`를 일반 문자 `s`로, `\b`를 아스키 특수문자 `Backspace(0x08)`로 직렬화하여 클라이언트에 꽂아버리는 문제가 있었음. 이로 인해 정규식 자체가 오염되어 어떠한 버튼도 매칭하지 못하고 있었음.
|
||||
- **해결**: `observer-script` 내부의 정규식 리터럴 내부의 이스케이프 문자(`\s`, `\b` 등)를 전부 이중 백슬래시(`\\s`, `\\b`)로 패치하여 브라우저에서 스크립트가 실행될 때 올바른 정규식 파서가 열리도록 수정 보완함.
|
||||
|
||||
## 결정 사항: 웹뷰 내 로컬 fetch CSP 패치 통과
|
||||
- `html-patcher.ts`에서 웹뷰 렌더링 시점에 CSP를 조작하여 `default-src 'none'` 방어막을 뚫고 `connect-src`에 `http://127.0.0.1:* wss://127.0.0.1:*`를 주입하도록 강제 적용함. 이를 통해 Bridge 서버로의 로컬 HTTP 통신이 활성화됨.
|
||||
|
||||
## 완료 상태
|
||||
VSCode VSIX (0.5.27) 빌드 완료 및 릴리스 커밋 패키징 수행.
|
||||
13
docs/devlog/entries/20260411-001.md
Normal file
13
docs/devlog/entries/20260411-001.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Pure 웹소켓 게이트웨이 전환 (Legacy 파일 브릿지 통신 완전히 제거)
|
||||
|
||||
- **시간**: 2026-04-11 11:00~13:00
|
||||
- **Commit**: `(To be updated)`
|
||||
- **Vikunja**: #N/A
|
||||
|
||||
## 결정 사항
|
||||
- 기존 VS Code 익스텐션과 로컬 Discord Bot 간에 이루어지던 `.gemini/antigravity/bridge/` 기반 파일 공유 통신 체계를 100% 제거하였습니다.
|
||||
- 파이썬 봇 서버(`bot.py`) 내부에서 동작하던 물리적인 폴링 디렉토리 스캐너(`pending_approval_scanner` 및 `chat_snapshot_scanner`) 파일 디펜던시 루프를 완전히 삭제하고 `Hub` WS 핸들러로 대체했습니다. 봇 패키지에 남아있던 `bridge.py`와 `watcher.py` 또한 사용할 이유가 없어져 레포지토리에서 영구적으로 폐기 구별을 내렸습니다.
|
||||
|
||||
## 새로 알게된 사실 혹은 트러블슈팅
|
||||
- 익스텐션에서 `activeSessionId` 변경 시 `watcher.py` 대신 Node.js 네이티브 `fs.watch` 기반으로 자체적인 `BrainWatcher`를 인하우스로 구현해 `step-probe.ts`에 주입함으로써 파이썬 의존도를 완전히 분리할 수 있었습니다.
|
||||
- 권한 팝업 중복 처리 역시 폴더 스캔 대신 단순히 인메모리 `lastFilePermissionTime` 단일 변수로 최적화되었습니다.
|
||||
22
docs/devlog/entries/20260412-001.md
Normal file
22
docs/devlog/entries/20260412-001.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# AG Native DOM 파싱 v7 전면 재설계
|
||||
|
||||
- **시간**: 2026-04-12 05:49~06:12
|
||||
- **Commit**: `a4d7286`
|
||||
|
||||
## 배경
|
||||
- AG Native 세션에서 Discord 릴레이가 전혀 동작하지 않음
|
||||
- SDK `GetCascadeTrajectorySteps`가 `trajectory not found` 반환 — AG Native는 Cascade API에 등록 안 됨
|
||||
- DOM observer가 UI 노이즈(content_copy, keyboard_arrow_up, Always run, Cancel)를 AI 응답으로 오인
|
||||
|
||||
## 결정 사항
|
||||
- SDK 경로 대신 **DOM이 유일한 데이터 소스**로 확정
|
||||
- `jetskiAgent/main.js` (11MB) 번들 분석으로 AG Native DOM 구조 역공학:
|
||||
- `data-testid="conversation-view"` — 대화 최상위 컨테이너
|
||||
- `data-step-index` — 각 step 식별 속성
|
||||
- `text-ide-message-block-bot-color` — 봇 메시지 클래스 (확인됨)
|
||||
- observer-script v6→v7 전면 재설계: step-aware 파싱
|
||||
|
||||
## 미완료
|
||||
- AG 재시작 후 실제 동작 검증 필요 (DOM 덤프 → 셀렉터 미세조정)
|
||||
- deep-inspect 정상 동작 확인
|
||||
- Discord에 실제 AI 응답 전달 확인
|
||||
22
docs/devlog/entries/20260412-002.md
Normal file
22
docs/devlog/entries/20260412-002.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# AG Native 번들 역공학 + V8 캐시 정리 + Observer 미작동 원인 규명
|
||||
|
||||
- **시간**: 2026-04-12 06:28~07:03
|
||||
- **Commit**: — (분석/조사 세션, 코드 변경 없음)
|
||||
|
||||
## 결정 사항
|
||||
- `text-ide-message-block-bot-color`는 AI 응답 컨테이너가 **아닌** NUX tooltip 전용 클래스로 확인 → observer 셀렉터에서 제거 필요
|
||||
- `markdown-body` 클래스도 AG Native에 존재하지 않음 → 폴백 셀렉터 변경 필요
|
||||
- AI 응답 텍스트는 `plannerResponse` step → `Whi` 렌더러 → `div.px-2.py-1` → `MarkdownRenderer` 내부에 위치
|
||||
- `data-step-index`는 디버그 패널에서 확인되었으나 메인 대화 뷰에서의 존재 여부는 라이브 DOM 덤프로 확인 필요
|
||||
|
||||
## 새로 알게 된 사실
|
||||
- AG Native 번들(jetskiAgent/main.js 10.8MB): 전체 step.case→renderer 매핑 확보 (pan 객체)
|
||||
- Allow/Deny는 `lHr` 컴포넌트, `border-t border-gray-500/25` 클래스
|
||||
- Observer v7이 HTML에 삽입되었지만 V8 CachedData(50MB) 때문에 실제 렌더러에서 로드되지 않았음
|
||||
- CachedData 삭제 완료 → AG 리로드 후 observer 작동 예상
|
||||
|
||||
## 미완료
|
||||
- AG 리로드 후 observer 작동 확인
|
||||
- deep-inspect로 실제 DOM 구조 캡처
|
||||
- observer-script 셀렉터 미세조정 (bot-color 제거, MarkdownRenderer 타겟팅)
|
||||
- Discord 릴레이 E2E 검증
|
||||
28
docs/devlog/entries/20260412-003.md
Normal file
28
docs/devlog/entries/20260412-003.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Observer v8 Electron 실행 보장 + 진단 beacon 추가
|
||||
|
||||
- **시간**: 2026-04-12 21:00~21:30
|
||||
- **Commit**: (이전 세션 크래시 복구 커밋)
|
||||
|
||||
## 배경
|
||||
- 이전 세션(f9491880)에서 Observer v8이 렌더러에서 실행되지 않는 문제 디버깅 중 크래시 발생
|
||||
- deep-inspect 엔드포인트가 `timeout` 반환 — 인라인 스크립트가 Electron에서 실행 안 됨
|
||||
- 원인: 인라인 스크립트가 `</html>` 뒤에 삽입되어 Electron이 무시하는 것으로 추정
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### observer-script.ts
|
||||
- DIAGNOSTIC BEACON 추가: 스크립트 로드 즉시 `/ping?beacon=1`으로 fetch → 실행 여부 확인 가능
|
||||
|
||||
### html-patcher.ts
|
||||
- 인라인 스크립트 삽입 위치를 `</html>` 앞에서 **`</body>` 앞**으로 변경
|
||||
- 기존 잘못된 위치의 인라인 블록을 제거 후 재삽입하는 로직 추가
|
||||
- 이전 패칭에서 발생한 중복 `</html>` 태그 정리 로직 추가
|
||||
|
||||
### http-bridge.ts
|
||||
- 진단용 HTTP 요청 로깅 추가 (폴링 엔드포인트 제외)
|
||||
|
||||
## 미완료
|
||||
- AG 리로드 후 observer 작동 확인 (beacon ping 수신 확인)
|
||||
- deep-inspect로 실제 DOM 구조 캡처
|
||||
- observer-script 셀렉터 미세조정 (bot-color 제거, MarkdownRenderer 타겟팅)
|
||||
- Discord 릴레이 E2E 검증
|
||||
25
docs/devlog/entries/20260413-001.md
Normal file
25
docs/devlog/entries/20260413-001.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Observer v8 검증 — V8 캐시 차단 재확인
|
||||
|
||||
- **시간**: 2026-04-13 09:50~11:00
|
||||
- **Commit**: 없음 (진단/검증 세션)
|
||||
- **Vikunja**: #619, #620 (진행 중)
|
||||
|
||||
## 진단 결과
|
||||
|
||||
1. **Extension POLL**: ✅ 정상 — 세션 `a91b5318` 추적 중, WS 명령 수신 정상
|
||||
2. **bridge/ 위치**: `~/.gemini/antigravity/bridge/` (프로젝트 루트 아님)
|
||||
3. **HTML 패치**: ✅ workbench.html + workbench-jetski-agent.html 모두 인라인 스크립트 + BEACON 삽입, `</body>` 앞 위치 정상
|
||||
4. **Observer 실행**: ❌ BEACON 핑 0건, dom_dumps 디렉토리 없음
|
||||
5. **V8 CachedData**: 24.42 MB 존재 → 삭제 완료
|
||||
6. **Discord 릴레이**: ❌ AI 응답 텍스트 추출 불가
|
||||
|
||||
## 결정 사항
|
||||
- Observer 스크립트 미실행의 원인은 V8 CachedData가 패치된 HTML 로드를 차단하는 known-issue와 동일 패턴
|
||||
- AG가 09:41에 재시작되었지만, 이전 세션에서 삭제한 V8 캐시가 AG 시작과 함께 다시 생성됨
|
||||
- 해결: V8 캐시 삭제 → AG 재시작 순서 필수 (이번 세션에서 캐시 삭제 완료)
|
||||
|
||||
## 미완료
|
||||
- AG 재시작 후 BEACON 핑 수신 확인
|
||||
- DOM 덤프 분석: 실제 DOM 구조에서 AI 응답 셀렉터 검증
|
||||
- 셀렉터 미세조정: bot-color 제거, MarkdownRenderer 타겟팅
|
||||
- Discord 릴레이 E2E 검증
|
||||
27
docs/devlog/entries/20260413-002.md
Normal file
27
docs/devlog/entries/20260413-002.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# DOM Observer 데이터 품질 검증 + UTF-8/noise 수정
|
||||
|
||||
- **시간**: 2026-04-13 12:34~12:52
|
||||
- **Commit**: `pending`
|
||||
- **Vikunja**: #619, #620 (진행 중)
|
||||
|
||||
## 검증 결과
|
||||
- DOM Observer v8 **동작 확인**: `pending/`에 45개 시그널 생성됨 (`source: "dom_observer"`)
|
||||
- 버튼 분류 정상: `command`(30), `permission`(15)
|
||||
- 명령어/conversation_id/버튼(Allow/Deny/Cancel) 추출 정상
|
||||
- **한글 인코딩 깨짐** 발견: description 필드에 `[AI 본문 요약]` → `[AI <20> <20>]`
|
||||
|
||||
## 변경 사항 (v0.5.39)
|
||||
|
||||
### http-bridge.ts
|
||||
- 모든 POST 핸들러에 `req.setEncoding('utf8')` 추가
|
||||
- Node.js HTTP 서버의 Buffer→string latin1 기본 인코딩으로 인한 multi-byte UTF-8 손실 수정
|
||||
|
||||
### observer-script.ts
|
||||
- `cleanLines()`에 인라인 pre-strip 추가: Material 아이콘명 18종을 regex로 `\n`으로 치환
|
||||
- `Thought for Xs` 패턴 인라인 제거 추가
|
||||
- `codeText` 추출에 `cleanLines()` 적용 (이전 미적용)
|
||||
|
||||
## 미완료
|
||||
- AG 재시작 후 v0.5.39 적용 검증 (한글 정상 출력 확인)
|
||||
- DOM dump 추출 검증
|
||||
- Discord 릴레이 E2E 검증
|
||||
28
docs/devlog/entries/20260413-003.md
Normal file
28
docs/devlog/entries/20260413-003.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# DOM Observer 컨텍스트 추출 수정 — v9 (v0.5.40)
|
||||
|
||||
- **시간**: 2026-04-13 19:26~
|
||||
- **Commit**: `pending`
|
||||
- **Vikunja**: #619, #620 (진행 중)
|
||||
|
||||
## 문제
|
||||
|
||||
Discord 승인 요청에 내용이 비어있음:
|
||||
- command = "Running2 commands" (그룹 헤더 버튼을 잘못 캡처)
|
||||
- description = 비어있거나 UI 노이즈만 포함
|
||||
- buttons = "Running2 commands / Always run" (잘못된 구조)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### observer-script.ts (v8 → v9)
|
||||
1. `isActionBtn()`에서 "Running N commands" 패턴 제거 — 이것은 그룹 헤더이며 승인 버튼이 아님
|
||||
2. `scan()`에서 `^Running\s*\d+\s*commands?$` 명시적 스킵
|
||||
3. `extractContextFromNearby()` 신규 함수 추가 — `data-step-index` 없이 DOM 트리를 20레벨까지 올라가며 `pre`/`code` 블록에서 실제 명령어 추출
|
||||
4. `collectSiblingButtons()` 범위를 parent → grandparent → great-grandparent 3레벨로 확대, 그룹 헤더 스킵, 텍스트 기반 dedup 추가
|
||||
5. `matchedType` 판별에서 `/Running\d/` 패턴 제거
|
||||
|
||||
### http-bridge.ts
|
||||
6. "Run/Always run" 필터에 `ctx.activeSessionId` 체크 추가 — step-probe가 세션 미추적 시 DOM observer 신호 허용
|
||||
|
||||
## 미완료
|
||||
- AG 재시작 후 v0.5.40 적용 검증
|
||||
- Discord E2E 검증 (실제 명령어/코드 내용 표시 확인)
|
||||
49
docs/devlog/entries/20260414-001.md
Normal file
49
docs/devlog/entries/20260414-001.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Observer v9 E2E 검증 + v10-diag 진단 추가
|
||||
|
||||
- **시간**: 2026-04-14 07:29~07:36
|
||||
- **Commit**: `pending`
|
||||
- **Vikunja**: #task-619 → 진행중
|
||||
|
||||
## 검증 결과
|
||||
|
||||
### ✅ 동작 확인 (정상)
|
||||
1. **Observer v9 스크립트 실행**: `/ping` 수신 확인 (22:30:13~)
|
||||
2. **BEACON ping**: `/ping?beacon=1&t=...` 형태로 GET /ping 수신
|
||||
3. **DOM dump**: `dump_html_5.json` (7858 bytes) 정상 저장
|
||||
4. **WS Hub 연결**: `wsConnected: true`
|
||||
5. **세션 감지**: `activeSessionId: 39c51225` (현재 세션)
|
||||
6. **pending POST**: `/pending` → `1776119428450_gnpw` 등 다수 수신
|
||||
|
||||
### ❌ 실패: 컨텍스트 추출
|
||||
- 모든 pending에서 `cmd="Always run"`, `btns=1`, `ctx="Always run"`
|
||||
- `extractContextFromNearby()` 실패: pre/code 요소를 찾지 못함
|
||||
- `collectSiblingButtons()` 실패: 1개 버튼만 감지 ("Cancel" 미감지)
|
||||
- `sessionStalled: true` + `lastPendingStepIndex: -1` (trajectory not found)
|
||||
|
||||
### 원인 분석 (추정)
|
||||
- AG Native UI의 "Always run" 버튼 근처에 `<pre>`, `<code>`, `[class*="terminal"]` 요소가 없음
|
||||
- 실제 명령어 텍스트가 다른 요소 유형(span, div 등)에 담겨있을 가능성
|
||||
- DOM 구조가 이전 세션에서 확인한 것과 다를 수 있음 (Settings/Launchpad dump vs 대화 dump)
|
||||
|
||||
## 수정 사항 (v10-diag)
|
||||
|
||||
### observer-script.ts
|
||||
- `extractContextFromNearby()`: 각 depth에서 tag/class/codeEls 수를 `_debugTrail`에 기록
|
||||
- depth ≤ 5에서 `span, div, p` 요소의 실제 텍스트도 trail에 포함
|
||||
- 성공 시 `CONTEXT-OK`, 실패 시 `CONTEXT-FAIL` 로그 출력
|
||||
- pending payload에 `_debug_trail` 필드 추가
|
||||
|
||||
### http-bridge.ts
|
||||
- `_handlePending()`에서 `_debug_trail` 필드를 `[HTTP-DIAG] trail:` 로그로 출력
|
||||
|
||||
## 미완료
|
||||
1. **AG 재시작 필요** — v10-diag 빌드 완료 + V8 캐시 삭제됨, AG 재시작 후 trail 분석 필요
|
||||
2. **trail 분석 후 대응**:
|
||||
- pre/code 대신 실제 명령어가 담긴 요소 유형 파악
|
||||
- `extractContextFromNearby()`의 셀렉터 확장 (span/div 등)
|
||||
- `collectSiblingButtons()` 범위 확장 검토
|
||||
3. **"trajectory not found" 에러** — AG Native 세션이 Cascade trajectory API에 등록되지 않는 근본 문제 (known-issues에 이미 기록)
|
||||
|
||||
## 결정 사항
|
||||
- 진단을 먼저 진행하는 것이 올바른 접근 — blind fix 대신 실제 DOM 구조를 trail로 확인한 후 정확한 셀렉터 수정
|
||||
- trail 데이터가 extension.log에 기록되므로 AG 재시작 후 즉시 확인 가능
|
||||
27
docs/devlog/entries/20260415-001.md
Normal file
27
docs/devlog/entries/20260415-001.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# PROMPT_ONLY_RE 근본원인 수정 (v0.5.45)
|
||||
|
||||
- **시간**: 2026-04-15 09:12~09:57
|
||||
- **Commit**: `01539e9`
|
||||
- **Vikunja**: #619 → 진행중
|
||||
|
||||
## 결정 사항
|
||||
|
||||
### Template Literal 안의 Regex 리터럴 이스케이핑 규칙
|
||||
|
||||
**혼동 포인트**: `observer-script.ts`의 전체 코드는 TypeScript template literal 안에 있다. 이 안에서 regex 리터럴(`/pattern/`)을 쓸 때:
|
||||
|
||||
- `\\s` (TS 소스 2-backslash) → template 출력 `\s` → **JS에서 invalid escape → 원본 보존** → regex `\s` = whitespace class ✅
|
||||
- `\\\\s` (TS 소스 4-backslash) → template 출력 `\\s` → **JS에서 valid escape `\s`** → regex `\s` = whitespace class ✅
|
||||
|
||||
**결론**: 2중과 4중 **둘 다 작동**하지만, 4중이 의도적이고 명시적. 그러나 PROMPT_ONLY_RE는 **기존 4중에서 실패하고 있었으므로** 실제 원인은 다른 곳에 있었음 — `(.*[\\/>»$#]\\\\s*)` 패턴 자체가 `>` 다음에 `\\s*` 매칭이 아닌 `\\\\s*` 리터럴 매칭이 되고 있었던 것.
|
||||
|
||||
### http-bridge PROMPT_ONLY_RE 단순화
|
||||
|
||||
- 기존: `/^[\s\\\/]*[\w_.-]+\s*[>»$#]\s*$/` — `…`(U+2026 ellipsis) prefix 미지원
|
||||
- 변경: `/^.*[>»$#]\s*$/` — prompt marker로 끝나는 모든 텍스트 스킵
|
||||
- 트레이드오프: `echo >` 같은 극단적 edge case에서 false positive → 1% 미만 확률, 허용
|
||||
|
||||
## 미완료
|
||||
|
||||
- AG 재시작 후 v0.5.45 실제 동작 확인 필요 (현재 v0.5.44 메모리 로드 상태)
|
||||
- `Running N commands` 4-backslash 패턴은 정상 동작 확인됨 — 그대로 유지
|
||||
30
docs/devlog/entries/20260415-002.md
Normal file
30
docs/devlog/entries/20260415-002.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Observer fallback 컨텍스트 추출 수정 (v0.5.46)
|
||||
|
||||
- **시간**: 2026-04-15 10:35~11:02
|
||||
- **Commit**: `pending`
|
||||
- **Vikunja**: #619 → 진행중
|
||||
|
||||
## 결정 사항
|
||||
|
||||
### `_promptOnlySkipped` 플래그 설계
|
||||
|
||||
**문제**: v0.5.45에서 PROMPT_ONLY_RE가 code/pre 요소의 프롬프트 텍스트를 정상 스킵했으나, `extractContextFromNearby()`의 **fallback 경로**(span/div/p 텍스트 수집)가 DOM 트리를 올라가면서 채팅 본문, UI 라벨, AI 응답을 명령어로 잘못 추출.
|
||||
|
||||
**해결 접근**: code 요소가 존재하지만 **모두** PROMPT_ONLY_RE로 스킵된 경우 → 이 터미널 블록에는 실행할 명령어가 없다고 판단 → fallback span/div/p 수집을 통째로 비활성화.
|
||||
|
||||
**대안 검토**:
|
||||
- ❌ fallback 텍스트에 CJK/자연어 필터 추가 → false negative 위험 (한국어 명령어 경로명 등)
|
||||
- ❌ fallback 수집 depth 제한 → DOM 구조가 바뀌면 다시 깨짐
|
||||
- ✅ **prompt-only 스킵과 fallback 비활성화 연동** → 가장 간결하고 확실
|
||||
|
||||
### VSIX 설치 누락 발견
|
||||
|
||||
이전 세션(fd78c28e)에서 v0.5.45 VSIX를 빌드했으나 **설치를 하지 않았음**. extensions.json 확인 결과 v0.5.43이 설치되어 있었음. 원인: 이전 세션에서 `code --install-extension` 실행 없이 AG 재시작만 수행.
|
||||
|
||||
→ known-issues에 "빌드 후 즉시 install 확인 필수" 주의사항 추가
|
||||
|
||||
## 미완료
|
||||
|
||||
- AG 재시작 후 v0.5.46 실제 동작 검증 필요
|
||||
- Discord에 빈 프롬프트/채팅 텍스트가 전송되지 않는지 확인
|
||||
- 검증 완료 후 devlog에 커밋 해시 업데이트 + Vikunja #619 완료 처리
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user