# pi-loop-guard

[![npm](https://img.shields.io/npm/v/@wkqco33/pi-loop-guard)](https://www.npmjs.com/package/@wkqco33/pi-loop-guard)
[![license](https://img.shields.io/npm/l/@wkqco33/pi-loop-guard)](LICENSE)
[![pi-package](https://img.shields.io/badge/pi-package-blue)](https://pi.dev/packages)

[pi](https://pi.dev)에서 모델이 도구 호출, 오류, 응답을 반복하며 토큰을 낭비하는
루프를 감지하고 중단하는 확장입니다.

모델이 외부로 드러내는 행동(도구 호출·도구 오류·최종 응답)만 관찰하며, 길더라도
실제로 진전이 있는 실행은 중단하지 않습니다.

- [English README](README.md)

## 설치

```bash
pi install npm:@wkqco33/pi-loop-guard
```

설치 없이 일회성으로 시험:

```bash
pi -e npm:@wkqco33/pi-loop-guard
```

실행 중인 pi 세션에서는 `/reload`로 다시 로드하거나 pi를 재시작합니다.

## 감지 항목

| 신호                | 패턴                           | 기본값                   | 동작            |
| ------------------- | ------------------------------ | ------------------------ | --------------- |
| 동일 도구 반복      | `A A A`                        | 연속 3회                 | **차단 + 중단** |
| 도구 순환 반복      | `A B A B A B`, `A B C A B C …` | 3회 순환 (순환 길이 ≤ 3) | **차단 + 중단** |
| 윈도우 도구 빈도    | `A … A … A … A` (간헐적 반복)  | 10회 윈도우 내 4회       | **차단 + 중단** |
| 동일 오류 반복      | 같은 오류 연속 3회             | 연속 3회                 | **중단**        |
| 동일 assistant 응답 | 같은 텍스트 연속 3회           | 연속 3회                 | **중단**        |
| turn 수             | 한 실행에서 12 초과            | 12                       | 경고만          |
| 실행 시간           | 한 실행에서 180초 초과         | 180초                    | 경고만          |

도구 호출은 도구 이름과 인자의 결정적 직렬화를 함께 비교하므로 `read(a.ts)`와
`read(b.ts)`는 서로 다른 호출입니다.

오류 키는 숫자, UUID, 타임스탬프, 16진수 포인터를 정규화하므로 가변 토큰이
포함되어도 동일한 실패로 인식됩니다.

turn 수와 실행 시간은 의도적으로 **소프트** 신호입니다. 진전이 있는 긴 작업은
끝까지 진행됩니다. 하드 중단은 실제 반복 패턴에서만 발생합니다.

## 모드

| 모드        | 동작                                                     |
| ----------- | -------------------------------------------------------- |
| `on` (기본) | 반복을 알리고 하드 중단                                  |
| `observe`   | 반복을 알리되 차단/중단하지 않음 — 새 환경에서 먼저 권장 |
| `off`       | 시스템 프롬프트 힌트를 포함해 완전 비활성화              |

새 PC에서는 `observe`로 며칠 사용해 오탐을 확인한 뒤 `on`으로 전환하는 것을
권장합니다.

## 명령어

```text
/loop-guard           현재 상태 확인
/loop-guard status    위와 동일
/loop-guard inspect   최근 도구 호출 히스토리 확인
/loop-guard on        차단 활성화
/loop-guard off       비활성화
/loop-guard observe   알림만, 차단 안 함
/loop-guard reset     현재 실행의 카운터와 tripped 플래그 초기화
```

## 설정

### 설정 파일

프로젝트에 `.pi/loop-guard.json`, 전역으로 `~/.pi/loop-guard.json`을 만들 수
있습니다. 프로젝트 설정이 전역 설정을 덮어씁니다.

```json
{
 "mode": "on",
 "locale": "ko",
 "maxTurns": 12,
 "turnTimeoutMs": 180000,
 "repeatToolThreshold": 3,
 "repeatErrorThreshold": 3,
 "repeatAssistantThreshold": 3,
 "cycleRepeats": 3,
 "maxCycleLength": 3,
 "repeatFrequencyThreshold": 4,
 "frequencyWindowSize": 10,
 "ignoreTools": ["sleep"],
 "toolThresholdOverrides": { "bash": 2 }
}
```

`"enabled": false`는 `"mode": "off"`의 별칭으로 사용할 수 있습니다.

### 환경 변수

환경 변수는 설정 파일보다 우선합니다.

```bash
export PI_LOOP_GUARD_MODE=on            # on | off | observe
export PI_LOOP_GUARD_LOCALE=ko          # en | ko
export PI_LOOP_GUARD_MAX_TURNS=12
export PI_LOOP_GUARD_TURN_TIMEOUT_MS=180000
export PI_LOOP_GUARD_REPEAT_TOOL=3
export PI_LOOP_GUARD_REPEAT_ERROR=3
export PI_LOOP_GUARD_REPEAT_ASSISTANT=3
export PI_LOOP_GUARD_CYCLE_REPEATS=3
export PI_LOOP_GUARD_MAX_CYCLE_LENGTH=3
export PI_LOOP_GUARD_REPEAT_FREQUENCY=4
export PI_LOOP_GUARD_FREQUENCY_WINDOW=10
export PI_LOOP_GUARD_IGNORE_TOOLS=sleep,wait
export PI_LOOP_GUARD_TOOL_OVERRIDES="bash=2,read=5"
```

더 보수적인 설정:

```bash
export PI_LOOP_GUARD_REPEAT_TOOL=2
export PI_LOOP_GUARD_REPEAT_ERROR=2
export PI_LOOP_GUARD_REPEAT_ASSISTANT=2
```

### 우선순위

```
내장 기본값  <  설정 파일  <  환경 변수  <  /loop-guard 명령
```

## 동작 방식

1. `before_agent_start`에서 시스템 프롬프트에 "반복하지 말고 막힌 점을 보고하라"는
   짧은 힌트를 덧붙입니다.
2. `tool_call`에서 `toolName + stableJson(input)` 서명을 다이제스트로 기록하고 연속
   반복과 순환 패턴을 확인합니다.
3. `tool_execution_end`에서 연속 동일 실패를 추적하고, 성공하면 streak를
   초기화합니다.
4. `message_end`에서 최종 assistant 텍스트를 비교합니다.
5. `turn_start`와 주기 타이머는 소프트 경고만 발생시킵니다.
6. 하드 중단 시 실행을 abort하고 도구 호출을
   `{ block: true, terminate: true }`로 차단합니다.
7. `agent_settled`와 `session_shutdown`에서 타이머와 카운터를 정리합니다.

탐지 로직(`src/detector.ts`)은 pi 의존성이 없는 순수 모듈이라 단위 테스트로
철저히 검증됩니다.

## 주의사항

- pi 프로세스와 동일한 권한으로 실행됩니다.
- 세션 내용이나 API 키를 디스크·로그에 저장하지 않습니다.
- 메모리에 카운트와 서명만 유지하며 아무것도 영속화하지 않습니다.
- 정상적인 재시도를 중단시킬 수 있으므로 `observe` 모드로 시작하는 것을
  권장합니다.
- turn/시간 제한은 그 자체로 실행을 멈추지 않습니다.

## 개발

```bash
npm install
npm run verify        # typecheck + format:check + 테스트
npm run coverage      # src/ 100% 라인 커버리지
npm run bench         # 핫패스 마이크로 벤치마크
npm run pack:check
```

테스트는 Node.js 내장 테스트 러너와 네이티브 TypeScript type-stripping을 사용하며
테스트 프레임워크 의존성이 없습니다. 기여자와 에이전트는
[AGENTS.md](AGENTS.md)를 따라야 합니다.

## 라이선스

MIT © wkqco33
