feat(my-deepagent): v0.3 PR #5 — plan mode (/plan, /approve, /reject)

Claude Code의 plan mode 등가.  `/plan` 진입 시 write_file / edit_file /
execute / bash / task (sub-agent) 도구가 차단되고 read_file / glob / grep /
ls / write_todos 만 허용.

핵심 동작:
- `PlanModeMiddleware(is_active: Callable[[], bool])` 가 `awrap_tool_call` /
  `wrap_tool_call` 에서 활성 + 차단 도구면 synthetic
  `ToolMessage(status="error")` 반환.  raise 하지 않음 — LLM 이 차단 메시지를
  보고 다른 도구로 전환하거나 plan 다듬기로 자동 복귀.
- `is_active` 는 closure 라서 슬래시 토글 후 agent 재빌드 불필요.
- `InteractiveSessionRow.plan_mode` 영속 + resume 시 복원.

데이터·라이브러리:
- `middleware/plan_mode.py` (신규):
  - `BLOCKED_TOOLS_IN_PLAN_MODE = write_file / edit_file / bash / execute /
    run_command / shell / task`.
  - `PlanModeMiddleware` async + sync 양쪽 구현.

REPL 통합 (`cli/interactive.py`):
- `InteractiveSession._plan_mode: bool` + `set_plan_mode(enabled)` async →
  flag 토글 + `thread_suffix` bump + row 영속.
- resume path 에서 `sess._plan_mode = row.plan_mode` 로 복원.
- `_register_plan_mode_slash`: `/plan`, `/approve`, `/reject` 등록.
- `/reject` 는 thread 까지 리셋해 plan thread 폐기.

테스트 (`tests/integration/test_plan_mode.py`, 9 케이스):
- inactive 시 모든 도구 패스스루
- active 시 write_file / execute / task 차단 (status=error,
  tool_call_id 유지, 메시지에 도구명 + "Plan-mode" 포함)
- active 시 read_file / glob / grep / ls / write_todos 허용
- closure 토글로 동작 변경 (rebuild 없이)
- 동기 wrap_tool_call 도 동일 동작
- BLOCKED_TOOLS_IN_PLAN_MODE 상수 sanity

게이트:
- ruff check / format --check / mypy: PASS
- pytest -q --ignore=tests/integration/test_e2e_workflow.py
  --ignore=tests/integration/test_openrouter_smoke.py: 657 passed (9 신규 포함)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
chungyeong
2026-05-17 20:47:30 +09:00
parent 2685cb26db
commit fb7e67fd20
4 changed files with 380 additions and 2 deletions

View File

@@ -2,6 +2,38 @@
## [Unreleased]
### Added
- **v0.3 PR #5 — Plan mode (`/plan` / `/approve` / `/reject`)**. Claude Code의
plan mode 등가. `/plan` 진입 시 `write_file` / `edit_file` / `execute` /
`bash` / `task` (sub-agent) 도구가 차단되고 `read_file` / `glob` / `grep` /
`ls` / `write_todos`만 허용. LLM 은 차단된 도구를 호출하면 `ToolMessage(
status="error")` 를 받고 자체적으로 계획만 다듬도록 유도. `/approve`
쓰기 허용, `/reject` 시 thread 리셋 + 쓰기 허용.
- `middleware/plan_mode.py` (신규):
- `PlanModeMiddleware(is_active: Callable[[], bool])``awrap_tool_call` /
`wrap_tool_call` 에서 plan_mode 활성 + 차단 도구면 synthetic
`ToolMessage(status="error", content=...)` 반환. raise 하지 않음
(LLM이 무한 루프 없이 다른 도구로 전환할 수 있도록).
- `BLOCKED_TOOLS_IN_PLAN_MODE` 상수: write_file / edit_file / bash /
execute / run_command / shell / task. read_file·write_todos 등 안전한
도구는 화이트리스트.
- `cli/interactive.py`:
- `InteractiveSession._plan_mode: bool`. `set_plan_mode(enabled)` async →
flag 토글 + thread_suffix bump + `InteractiveSessionRow.plan_mode` 영속
(PR #1에서 이미 컬럼 추가했음). resume 시 row.plan_mode 로 복원.
- `build_agent_if_needed`에서 `PlanModeMiddleware(is_active=lambda: ...)`
를 middleware 리스트 첫 자리에 삽입 — closure 가 self._plan_mode 를 읽으니
슬래시 토글 후 agent 재빌드 필요 없음.
- `_register_plan_mode_slash`: `/plan`, `/approve`, `/reject` 등록.
- `tests/integration/test_plan_mode.py` (신규, 9 케이스):
- inactive → 모든 도구 패스스루
- active → write_file / execute / task 차단 (status=error, tool_call_id
유지, 메시지에 도구명 + "Plan-mode" 포함)
- active → read_file / glob / grep / ls / write_todos 허용
- closure 토글로 동작 변경 (rebuild 없이)
- 동기 wrap_tool_call 도 동일 동작
- BLOCKED_TOOLS_IN_PLAN_MODE 상수 sanity
### Added
- **v0.3 PR #4 — Agent Skills (LLM-routing, no embeddings)**. Anthropic Agent
Skills 명세를 그대로 따르는 progressive-disclosure 패턴. deepagents