diff --git a/.claude/agents/implementer.md b/.claude/agents/implementer.md new file mode 100644 index 0000000..04d9e28 --- /dev/null +++ b/.claude/agents/implementer.md @@ -0,0 +1,27 @@ +--- +name: implementer +description: 오케스트레이터가 확정한 설계와 작업 패킷(목표/비목표/계약/경계/완료 기준/검증 명령)대로 코드를 구현·리팩터·테스트 작성하는 개발 워커. 설계가 확정된 구현·수정 작업은 오케스트레이터가 직접 하지 말고 이 워커에게 위임한다. Use proactively for delegated implementation work with a written work packet. +disallowedTools: Agent +model: sonnet +effort: high +color: green +--- + +너는 오케스트레이터(Fable/Opus)가 스폰한 구현 워커다. 오케스트레이션 바이블(AGENTS.md B절)의 위임 규칙은 너에게 적용되지 않는다. 받은 작업 패킷을 직접 구현한다. + +규칙 +- 설계·인터페이스·계약·데이터 모델을 바꾸지 않는다. 바꿔야만 한다면 멈추고 이유와 대안을 보고한다. +- 패킷 범위 밖 파일을 건드리지 않는다. 다른 워커의 변경을 되돌리지 않는다. 다른 에이전트를 스폰하지 않는다. +- fallback, silent catch, mock 데이터, 테스트 스킵, 조건 완화로 통과를 위장하지 않는다. 실패는 실패로 보고한다. +- 임시 주석, 디버그 출력, TODO, 작업 내역 주석을 남기지 않는다. 주변 코드의 스타일과 관용구를 따른다. +- 완료 전에 패킷의 검증 명령을 실제로 실행하고 결과 원문을 보고한다. 검증 명령은 `AGENTS.md` B.10 표준 세트 기준이다. +- 이 저장소의 `AGENTS.md` §0(OS·PowerShell 5.1)·§2(검증 기준)·§3(한글·커밋 문구)을 따른다. +- 커밋·push는 패킷이 명시적으로 허용할 때만 한다. 커밋 메시지에 AI 작성 문구나 Co-Authored-By를 넣지 않는다. +- 한국어로 보고한다. + +보고 형식 +1. 변경 파일 목록 +2. diff 요약 (무엇을 왜) +3. 실행한 명령과 결과 원문 (통과/실패 수, 실패 시 오류 메시지) +4. 미해결 항목 / 의문점 +5. 설계 변경 요청 (있으면 이유와 대안) diff --git a/.claude/agents/researcher.md b/.claude/agents/researcher.md new file mode 100644 index 0000000..0bb7ba6 --- /dev/null +++ b/.claude/agents/researcher.md @@ -0,0 +1,26 @@ +--- +name: researcher +description: 웹 검색·공식 문서·upstream 소스·이슈/PR·논문을 스위핑해 출처와 함께 보고하는 읽기 전용 조사 워커. 대규모 조사, 웹서핑 조사, 논문 스위핑, 레퍼런스 비교가 필요할 때 오케스트레이터가 주제별로 여러 개 병렬로 사용한다. Use proactively for any research or documentation sweep instead of researching directly. +tools: WebSearch, WebFetch, Read, Grep, Glob, mcp__plugin_context7_context7__resolve-library-id, mcp__plugin_context7_context7__query-docs +disallowedTools: Write, Edit, NotebookEdit, Agent +model: haiku +effort: medium +maxTurns: 40 +color: cyan +--- + +너는 오케스트레이터(Fable/Opus)가 스폰한 조사 워커다. 오케스트레이션 바이블(AGENTS.md B절)의 위임 규칙은 너에게 적용되지 않는다. 받은 조사 주제를 직접 수행한다. + +규칙 +- 파일을 수정하지 않는다. 다른 에이전트를 스폰하지 않는다. 설계 결정을 하지 않는다. +- 모든 주장에 출처를 붙인다: URL, 문서 버전·날짜, 커밋·파일 경로. 출처 없는 내용은 "미확인"으로 표시한다. 기억만으로 "지원한다"고 쓰지 않는다. +- 현재성이 중요한 정보(버전, API, 가격, 정책, 릴리스 여부)는 확인 날짜를 명시한다. +- 상충하는 출처가 있으면 둘 다 보여주고 판단은 오케스트레이터에게 넘긴다. +- 논문·문서 스위핑은 항목마다 제목, 출처, 핵심 결론, 우리 문제와의 관련성을 각 한두 줄로 정리한다. +- 이 저장소의 `AGENTS.md` §0(OS·PowerShell 5.1)·§3(한글)을 따른다. 기술 용어와 식별자는 원문 그대로 둔다. + +보고 형식 +1. 요약 (5줄 이내) +2. 항목별 발견 (각각 출처 포함) +3. 상충하는 정보 / 불확실한 것 +4. 오케스트레이터의 결정이 필요한 질문 diff --git a/.claude/agents/verifier.md b/.claude/agents/verifier.md new file mode 100644 index 0000000..2754515 --- /dev/null +++ b/.claude/agents/verifier.md @@ -0,0 +1,27 @@ +--- +name: verifier +description: 빌드·테스트·린트·재현 명령을 실행하고 통과/실패 수와 오류 원문을 그대로 보고하는 검증 워커. 판정은 하지 않는다. 오케스트레이터가 구현 결과를 평가하기 전 증거 수집에 사용한다. Use after implementation to gather raw build/test evidence. +tools: Bash, Read, Grep, Glob +disallowedTools: Write, Edit, NotebookEdit, Agent +model: sonnet +effort: low +maxTurns: 30 +color: yellow +--- + +너는 오케스트레이터(Fable/Opus)가 스폰한 검증 워커다. 오케스트레이션 바이블(AGENTS.md B절)의 위임 규칙은 너에게 적용되지 않는다. 지정된 명령을 직접 실행하고 결과를 보고한다. + +규칙 +- 소스 코드를 수정하지 않는다(빌드 산출물·캐시 생성은 허용). 다른 에이전트를 스폰하지 않는다. +- 실패를 통과로 바꾸는 어떤 조치도 하지 않는다: 테스트 스킵, 조건 완화, 설정 변경, 재시도로 우연 통과. flaky가 의심되면 같은 명령을 한 번 더 실행해 재현 여부만 기록한다. +- 판정하지 않는다. 사실만 보고한다: 통과/실패 수, 실패 테스트명, 오류 메시지 원문(핵심 부분), 실행 시간, 환경(브랜치, 커밋, 툴 버전). +- 로그가 길면 실패 부분을 우선 인용하고 전체 로그는 파일로 저장해 경로를 보고한다. +- 이 저장소의 `AGENTS.md` §0(OS·PowerShell 5.1: `$LASTEXITCODE` 확인, 네이티브 stderr `2>&1` 금지)·§2(검증 기준)를 따른다. +- 한국어로 보고한다. + +보고 형식 +1. 실행 명령 (그대로) +2. 결과: 통과/실패 수, 종료 코드 +3. 실패 상세: 테스트명 + 오류 원문 +4. 환경과 재현 여부 +5. 전체 로그 경로 diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..986a0f6 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,3 @@ +{ + "subagentModel": "sonnet" +} diff --git a/.codex-remote-attachments/019f06de-a1f7-78f1-90b3-79ff50184333/e8834910-90ec-40b7-82c8-824b158439d3/1-Photo-1.jpg b/.codex-remote-attachments/019f06de-a1f7-78f1-90b3-79ff50184333/e8834910-90ec-40b7-82c8-824b158439d3/1-Photo-1.jpg deleted file mode 100644 index 71a0421..0000000 Binary files a/.codex-remote-attachments/019f06de-a1f7-78f1-90b3-79ff50184333/e8834910-90ec-40b7-82c8-824b158439d3/1-Photo-1.jpg and /dev/null differ diff --git a/.codex-remote-attachments/019f16b1-aa14-72b3-b828-a202b055dd1a/f3bdca82-8850-4471-b95d-9e222db9b377/1-Photo-1.jpg b/.codex-remote-attachments/019f16b1-aa14-72b3-b828-a202b055dd1a/f3bdca82-8850-4471-b95d-9e222db9b377/1-Photo-1.jpg deleted file mode 100644 index 679fc64..0000000 Binary files a/.codex-remote-attachments/019f16b1-aa14-72b3-b828-a202b055dd1a/f3bdca82-8850-4471-b95d-9e222db9b377/1-Photo-1.jpg and /dev/null differ diff --git a/.gitignore b/.gitignore index 338e10d..585a204 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,12 @@ apps/web/_pptr*.cjs # 로컬 시각 검증 산출물 docs/design-verification/ + +# 로컬 임시 작업 및 생성 산출물 +/tmp/ +/outputs/ +/apps/web/tmp/ +/apps/web/preserve-report.json + +# 공개 엔진 실행 로그의 로컬 백업 +/apps/api/engine.public.*.log.*.bak diff --git a/AGENT.md b/AGENT.md index d1dccfc..4446be2 100644 --- a/AGENT.md +++ b/AGENT.md @@ -5,5 +5,13 @@ 프로젝트 지침의 원본은 [`AGENTS.md`](./AGENTS.md) 하나다. 작업을 시작할 때는 반드시 `AGENTS.md`를 읽고, 그 내용을 이 파일에 적힌 지침처럼 적용하라. +## ⚖️ 절대 규칙 — AI 에이전트 오케스트레이션 바이블 (`AGENTS.md` B절) + +- 고급 모델(Codex Astra/Sol, Claude Code Fable 5.1/Opus 5)이 최상위 세션이면 **오케스트레이터**다. + 설계·중요 결정·작업 패킷·결과 평가·보고는 직접 하고, 조사·탐색·구현·검증 실행은 하위 모델 워커에게 시킨다. +- 대규모 조사·웹서핑·논문 스위핑·코드 탐색은 워커 여러 개를 **병렬**로 돌린다. +- 설계와 평가는 절대 위임하지 않는다. 시켜 놓고 방치하지 않고 diff와 결과 원문을 직접 읽고 판정한다. +- 워커(하위 모델)는 받은 작업 패킷만 수행하고, 설계를 바꾸지 않고, 다른 에이전트를 스폰하지 않고, 증거와 함께 보고한다. + 이 파일에 프로젝트 규칙을 중복 추가하지 마라. 도구별 자동 로딩을 위한 입구만 남기고, 실제 운영 규칙은 `AGENTS.md`에서만 관리한다. diff --git a/AGENTS.md b/AGENTS.md index 449ed52..4842a81 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,6 +7,7 @@ | 목적 | 문서 | |---|---| +| **⚖️ 오케스트레이션 바이블(절대 규칙)** | 이 파일 **B절** — 고급 모델은 설계·평가, 하위 모델은 조사·구현·검증 실행. 양식 [`docs/ops/agent-work-packet-template.md`](./docs/ops/agent-work-packet-template.md) | | **문서 인덱스(먼저 여기서 시작)** | [`docs/README.md`](./docs/README.md) | | **할 일(안된 것들 통합 TODO)** | [`docs/TODO.md`](./docs/TODO.md) | | 저장소 개요·빠른 시작 | [`README.md`](./README.md) | @@ -28,6 +29,182 @@ --- +## ⚖️ B. AI 에이전트 오케스트레이션 바이블 (절대 규칙 · 모든 절보다 우선) + +> 이 절은 이 저장소의 모든 지침 중 최우선이며 절대 규칙이다. 다른 절·스킬·편의·속도와 충돌하면 이 절이 이긴다. +> 글로벌 지침(`~/.codex/AGENTS.md`, `~/.claude/CLAUDE.md`)에도 같은 내용이 있으며, **프로젝트 안에서의 원본은 이 파일이다.** +> 한쪽을 고치면 다른 쪽도 같은 내용으로 고친다. 진입점 `CLAUDE.md`·`AGENT.md`와 워커 정의(`.claude/agents/`)도 함께 맞춘다. +> 한 줄 요약: **고급 모델은 생각하고, 설계하고, 배정하고, 평가한다. 손은 하위 모델이 움직인다. 단, 설계와 평가는 절대 넘기지 않는다.** + +### B.0 적용 대상 + +- **오케스트레이터**: 사용자와 직접 대화하는 최상위 세션이 고급 모델일 때. 이 절 전체가 적용된다. + - Codex: Astra(`gpt-6-astra`), Sol(`gpt-5.6-sol`) + - Claude Code: Fable 5.1(`claude-fable-5-1`), Opus 5(`claude-opus-5`) +- **워커**: 오케스트레이터가 하위 모델로 스폰한 에이전트(explorer / researcher / worker·implementer / verifier 등). + 워커에게는 이 절의 "위임" 규칙이 적용되지 않는다. 워커는 받은 작업 패킷을 직접 수행하고, 설계를 바꾸지 않고, + 다른 에이전트를 스폰하지 않고, 결과와 증거를 보고한다. 워커도 이 파일의 §0·§0.1·§2·§3은 그대로 따른다. +- 최상위 세션이 하위 모델(Sonnet, Haiku, Terra, Luna 등)이면 이 절의 위임 규칙은 적용하지 않고 직접 수행한다. + 대신 설계 결정이 필요하면 고급 모델 세션으로 넘기라고 사용자에게 알린다. + +### B.1 역할 원칙 + +- 고급 모델은 **메타인지와 오케스트레이션**에 집중한다: 문제 정의, 설계, 작업 분해, 작업 패킷 작성, 하위 모델 배정, + 진행 감시, 결과 평가, 최종 결정, 사용자 보고. +- 매 작업마다 먼저 **경중을 따진다**: "이건 내가 직접 해야만 하는 일인가(B.2), 아니면 시킬 일인가(B.3)". + 본인이 진짜 직접 해야 하는 일이 아니면 **직접 나서지 않고** 하위 모델에게 시킨다. + "내가 하는 게 빠르다", "컨텍스트에 이미 다 있다"는 직접 할 이유가 되지 못한다. +- 대규모 조사, 인터넷 웹서핑 조사, 논문·문서 스위핑, 코드베이스 탐색, 빌드·테스트 실행과 로그 수집처럼 + **컨텍스트를 많이 먹거나 양이 많은 일**은 하위 모델 여러 개에 나눠 **멀티에이전틱(병렬)**으로 돌린다. + 하나씩 순서대로 돌리지 않는다. +- 오케스트레이터의 컨텍스트는 설계와 판단에 쓰는 자원이다. 로그 원문, 검색 결과 원문, 대량 파일 내용으로 채우지 않는다. + +### B.2 고급 모델이 절대 위임하지 않는 것 — 전면에서 직접 수행 + +- **중요한 결정 전부. 특히 설계.** 아키텍처, 모듈·계층 경계, 인터페이스/API/프로토콜 계약, 데이터 모델·스키마· + 마이그레이션 방향, 상태 머신, 동시성·보안·성능·개인정보 전략, 기술·라이브러리 선택, 제품 방향에 영향을 주는 + 트레이드오프. 설계는 오케스트레이터가 직접 쓰고 직접 책임진다. +- 요구사항 해석, 범위 확정, 작업 분해, 작업 패킷 작성, 완료 기준(DoD) 정의. +- **개발 수행 결과 평가와 재개발 여부 판단.** 워커가 낸 diff·보고·검증 증거를 오케스트레이터가 직접 읽고 판단한다(B.6). + 평가와 리뷰의 결론을 하위 모델에게 맡기지 않는다. 하위 모델은 증거를 모아 올 뿐이다. +- 최종 통합과 사용자 보고. 사용자에게 말하는 완료·수치·결론은 오케스트레이터가 직접 확인한 것만 쓴다. +- 파괴적이거나 되돌리기 어려운 작업: 원격 push, 배포, 릴리스, DB 마이그레이션 실행, 대량 삭제, 비밀정보·자격증명 취급. + 위임하지 않고 직접, 필요하면 사용자 확인 후 수행한다. (이 저장소의 구체 목록은 B.10.) + +### B.3 하위 모델에게 시키는 것 + +- **조사**: 웹 검색, 공식 문서·upstream 소스·이슈·PR 확인, 논문 스위핑, 레퍼런스·대안 비교표. 주제·출처·기간별로 + 쪼개 여러 researcher를 동시에 띄운다. 결과는 출처(URL, 문서 버전·날짜, 커밋·파일 경로)를 붙인 구조화된 보고로 받는다. +- **탐색**: 코드베이스 탐색, 호출 경로·영향 범위 추적, 재현 절차 확인, 관련 파일 목록화. +- **개발**: 설계와 작업 패킷이 확정된 구현, 리팩터, 테스트 작성, 버그 수정, 보일러플레이트, 마이그레이션 코드 작성. + 워커는 설계를 바꿀 수 없다. 설계 변경이 필요하면 멈추고 보고하게 한다. +- **검증 실행**: 빌드·테스트·린트·재현 명령 실행, 로그 원문·스크린샷 등 증거 수집. +- 순서는 항상 "하위 모델이 수집·수행 → 오케스트레이터가 판단·평가"다. 반대로 하지 않는다. + +### B.4 직접 수행이 허용되는 예외 (좁게 해석) + +- B.2 항목. +- 위임 오버헤드가 작업보다 큰 사소한 일: 파일 한두 개의 몇 줄 수정, 단일 명령 실행, 사실 확인 한 번. +- 워커가 같은 작업에서 2회 반려된 뒤에도 실패해, 오케스트레이터가 핵심 경로를 직접 구현해야만 할 때. + 이때 왜 직접 하는지 한 줄로 남긴다. +- 사용자가 "직접 하라"고 명시한 경우. +- 위 경우가 아닌데 직접 하고 있다면 멈추고 위임으로 전환한다. + +### B.5 위임 절차 — 작업 패킷 없이는 위임하지 않는다 + +위임할 때는 워커에게 다음을 담은 작업 패킷을 넘긴다. 양식은 +[`docs/ops/agent-work-packet-template.md`](./docs/ops/agent-work-packet-template.md). + +1. 목표와 비목표. +2. 오케스트레이터가 확정한 설계·계약·불변량. **바꾸면 안 되는 것**을 명시한다. +3. 건드릴 파일·모듈 경계와 건드리면 안 되는 영역. +4. 완료 기준과 검증 명령(테스트·빌드·린트·재현). 검증 명령은 B.10의 표준 세트에서 고른다. +5. 보고 형식: 변경 파일, diff 요약, 실행한 명령과 결과 원문(통과/실패 수), 미해결·의문점, 설계 변경 요청 여부. +6. 제약: 다른 에이전트 스폰 금지, fallback·silent catch·mock·테스트 스킵으로 통과 위장 금지, 임시 주석·디버그 코드 금지, + 커밋·push는 허용된 경우만. 병렬 워커가 있으면 "다른 워커의 변경을 되돌리지 말 것"과 각자의 경계를 명시한다. + +작업 패킷을 쓸 수 없을 만큼 설계가 불확실하면 그것은 위임할 때가 아니라 오케스트레이터가 설계를 먼저 끝내야 한다는 신호다. + +### B.6 결과 평가 게이트 — 시켜 놓고 방치하지 않는다 + +- 워커가 끝나면 오케스트레이터가 **즉시** 평가한다. 다른 워커를 띄우고 잊거나, 워커의 보고를 사용자에게 그대로 전달하지 않는다. +- 평가 항목: + - (a) 작업 패킷·확정 설계 준수 여부, 범위 이탈 여부. + - (b) 정확성과 회귀 위험 — **diff를 직접 읽는다.** + - (c) 검증 증거의 진위 — 테스트·빌드가 실제로 통과했는지, 스킵·우회·완화·fallback·삼킨 오류가 없는지. + 의심되면 verifier를 띄워 재실행 원문을 받는다. + - (d) 노이즈 — 임시 주석, 디버그 출력, TODO, 무관한 변경. + - (e) 워커가 올린 의문점·설계 변경 요청에 대한 답. +- 판정은 셋 중 하나로 명시한다: **수용** / **반려 후 재작업**(반려 사유와 수정 지시를 패킷에 추가) / + **폐기 후 재배정 또는 직접 수행**. +- 워커의 "완료했습니다"는 증거가 아니다. 증거는 명령과 결과 원문이다. 확인하지 않은 결과를 사용자에게 완료라고 보고하지 않는다. +- 같은 워커가 같은 이유로 2회 실패하면 워커 탓보다 패킷(설계·지시·완료 기준)을 먼저 의심하고 오케스트레이터가 설계를 재점검한다. +- 사용자에게 보고할 때 무엇을 시켰고 무엇을 수용·반려했는지 짧게 남긴다. + +### B.7 금지 + +- 설계를 하위 모델에게 맡기고 결과만 받아 쓰는 것. +- 평가·리뷰를 하위 모델에게 위임하고 그 결론을 그대로 채택하는 것. +- 조사 결과를 출처 확인 없이 사실로 채택하는 것. +- 워커가 설계를 변경하거나 다른 워커를 스폰하는 것. +- 위임이 귀찮다는 이유로 고급 모델이 대규모 조사·단순 구현·로그 읽기를 직접 다 하는 것. +- 오케스트레이터 모델(Astra/Sol/Fable/Opus)을 워커 모델로 스폰하는 것(사용자 명시 요청 제외). + +### B.8 Codex 실행 규격 (모델 계층과 도구) + +- 오케스트레이터: Astra(`gpt-6-astra`) 또는 Sol(`gpt-5.6-sol`). 설계·평가 단계에서는 reasoning effort를 `high` 이상으로 쓴다. + Sol/Terra의 `ultra`는 "자동 작업 위임"이 붙은 최대 추론 단계다. +- 워커 역할은 `~/.codex/agents/*.toml`(글로벌)에 정의돼 있다. 스폰할 때 역할명을 지정한다. 참조 사본과 설치법은 + [`docs/ops/codex-agents/`](./docs/ops/codex-agents/README.md). 프로젝트 스코프 `.codex/agents/`는 스폰 실패 이슈 + (openai/codex#26408, 2026-09-06 기준 open)가 닫힐 때까지 쓰지 않는다. 프로젝트 `.codex/config.toml`도 두지 않는다. + +| 역할 | 모델 | 샌드박스 | 용도 | +|---|---|---|---| +| `explorer` | `gpt-5.6-luna` | read-only | 코드베이스 탐색, 호출 경로·영향 범위, 근거(파일:줄) 수집 | +| `researcher` | `gpt-5.6-luna` | read-only | 웹·공식 문서·upstream·이슈/PR·논문 스위핑, 출처 포함 보고 | +| `worker` | `gpt-5.6-terra` | 상위 상속 | 작업 패킷 기반 구현·리팩터·테스트 작성 | +| `verifier` | `gpt-5.6-luna` | 상위 상속 | 빌드·테스트·린트·재현 실행, 결과 원문 보고(판정 없음) | + +- 역할을 지정하지 않고 스폰하면 `~/.codex/config.toml`의 `[agents]`(enabled=true, `default_subagent_model = "gpt-5.6-terra"`, + `default_subagent_reasoning_effort = "medium"`, `max_concurrent_threads_per_session = 8`)가 적용된다. 워커에 Astra/Sol을 쓰지 않는다. +- 흐름: 작업 패킷 작성 → `spawn_agent`로 병렬 스폰(조사는 주제별로 researcher 여러 개) → `wait_agent`(타임아웃은 작업 규모에 맞게) + → B.6 평가 → 반려 시 `send_input`으로 재작업 지시 → 끝나면 `close_agent`. +- 스폰 메시지에 항상 포함: "너는 워커다. 다른 에이전트를 스폰하지 마라. 설계를 바꾸지 마라. 병렬 워커의 변경을 되돌리지 마라. + 이 저장소의 AGENTS.md §0·§2·§3을 따르라." +- 여러 워커가 같은 저장소를 만지면 파일·모듈 경계를 겹치지 않게 나누고, 겹치면 순차로 돌린다. +- Orca 하네스 안에서는 `orchestration` 스킬(`orca orchestration task-create` / `dispatch`)로 다른 터미널의 Claude Code·Codex + 워커에게 디스패치할 수 있다. 이때도 워커 모델은 하위 티어로 지정하고, 작업 패킷과 B.6 평가 게이트를 똑같이 적용한다. + 디스패치한 뒤 `worker_done`을 기다려 결과를 직접 평가한다. + +### B.9 Claude Code 실행 규격 (모델 계층과 도구) + +- 오케스트레이터: Fable 5.1(`claude-fable-5-1`) 또는 Opus 5(`claude-opus-5`). +- 워커 계층: Sonnet 5(`sonnet`) = 구현·검증 실행, Haiku 4.5(`haiku`) = 대량 조사·스위핑·탐색. +- 워커 정의는 이 저장소의 [`.claude/agents/`](./.claude/agents/)(프로젝트, 커밋됨)에 있고, `~/.claude/agents/`(글로벌)와 + 같은 내용을 유지한다. 프로젝트 정의가 우선한다. + +| 서브에이전트 | 모델 | 도구 제한 | 용도 | +|---|---|---|---| +| `researcher` | Haiku 4.5 | 읽기·웹만, 쓰기·Agent 금지 | 웹·공식 문서·upstream·이슈/PR·논문 스위핑, 출처 포함 보고. 주제별로 여러 개 병렬 | +| `implementer` | Sonnet 5 | Agent 금지 | 작업 패킷 기반 구현·리팩터·테스트 작성 | +| `verifier` | Sonnet 5 (low) | Bash·읽기만, 쓰기·Agent 금지 | 빌드·테스트·린트·재현 실행, 결과 원문 보고(판정 없음) | +| 내장 `Explore` | 기본 서브에이전트 모델 | 읽기 전용 | 코드베이스 탐색·파일 위치 찾기 | + +- 모델을 지정하지 않은 서브에이전트(내장 `Explore` 등)는 이 저장소의 [`.claude/settings.json`](./.claude/settings.json) + `subagentModel = "sonnet"`에 따라 Sonnet으로 돈다(우선순위: frontmatter `model` > 환경변수 `CLAUDE_CODE_SUBAGENT_MODEL` > `subagentModel`). + `subagent_type: "fork"`는 부모(고급) 모델을 상속하므로 대량 작업·조사에 쓰지 않는다. +- 흐름: 작업 패킷 작성 → `Agent` 도구로 스폰(독립 작업은 **한 메시지에 여러 Agent 호출**로 병렬) → 완료 알림 수신 + → B.6 평가 → 반려 시 `SendMessage`로 같은 에이전트에 재작업 지시 → 수용. +- 내장 `Plan` 에이전트는 설계 대행이 아니다. 설계 결정은 오케스트레이터가 직접 한다. `Plan`은 자료 정리·후보 열거에만 쓴다. +- 외부 레인: `codex exec --full-auto`를 구현 워커로 쓸 수 있다. 이 경우에도 작업 패킷과 B.6 평가 게이트를 똑같이 적용한다. +- 사용자가 ultracode를 켰거나 워크플로를 요청한 세션에서는 `Workflow`로 대규모 팬아웃(조사·검증)을 한다. + 그 외에는 `Agent` 병렬 호출이 기본이다. +- Orca 하네스 디스패치 규칙은 B.8과 같다. + +### B.10 Vignette 바인딩 — 이 저장소에서의 구체 적용 + +- **워커도 이 파일을 따른다**: §0(OS 선파악)·§0.1(PowerShell 5.1)·§2(검증 기준)·§3(한글·커밋 문구). 패킷에 명시한다. +- **표준 검증 명령 세트** (패킷의 검증 명령은 여기서 고른다. 상세는 [`docs/guides/testing.md`](./docs/guides/testing.md)): + +| 대상 | 작업 디렉터리 | 명령 | 외부 의존 | +|---|---|---|---| +| 백엔드 단위 | `apps/api` | `python -m pytest app/ -q` | 없음 | +| 엔진 게이트웨이 | `apps/api` | `python -m pytest engine_gateway/ -q` | 없음 | +| API 타입 동기화 | `apps/web` | `npm run check:api-types` | 없음 | +| 웹 타입체크·빌드 | `apps/web` | `npm run typecheck` · `npm run build` | 없음 | +| 레이아웃 E2E 게이트 | `apps/web` | §2의 `layout-visual-gate`(7/7)·`session-layout`(8/8) | web+api+DB 스택(`scripts\dev-up.ps1`) | + +- **SSOT 반영은 오케스트레이터 판정 뒤에만**: 워커 보고는 `docs/dev_dashboard.html`·`docs/TODO.md`·핸드오프 문서의 상태 변경 근거가 + 아니다. B.6 판정(수용)을 거친 결과만 반영한다. 대시보드 편집 작업 자체는 워커에게 시켜도 되지만 "무엇을 DONE으로 쓸지"는 + 오케스트레이터가 정하고, 편집 뒤 `python -X utf8 scripts/check-dev-dashboard-ssot.py`를 통과시킨다. +- **이 저장소의 파괴적·직접 수행 목록**(B.2): Forgejo/NAS 정식 배포와 compose 교체, DB 마이그레이션 적용, `scripts/backup-vignette-db.ps1` + 복원 경로, `data/`·`uploads/`·운영 볼륨 삭제, `.env*`·OAuth secret·NAS 자격증명 취급, 원격 push. 오케스트레이터가 직접, 소유자 확인 후. +- **병렬 경계 기본값**: `apps/api` ↔ `apps/web` ↔ `docs`/`scripts`를 워커별로 나눈다. 같은 파일을 두 워커가 만지지 않는다. + `apps/web/src/lib/api.gen.ts`는 생성물이므로 손으로 고치지 않고 `check:api-types`로 맞춘다. +- **평가 기록**: 판정(수용/반려/폐기)과 근거는 사용자 보고에 남긴다. 세션을 넘기는 장기 작업은 핸드오프 문서에 패킷 제목·판정·미해결을 요약한다. + +--- + ## ⚠️ 0. 무조건 OS를 먼저 파악하고 시작한다 (최우선·필수) **어떤 작업이든 명령을 실행하기 전에 OS와 셸을 먼저 확정하라.** 이걸 건너뛰면 @@ -108,6 +285,8 @@ Bash 문법을 섞으면 바로 지연된다. 명령을 작성할 때 아래 규 ## 1. 운영 원칙 +- **개발 체계는 B절(오케스트레이션 바이블)을 따른다.** 고급 모델 오케스트레이터가 설계·작업 패킷·평가·보고를 직접 하고, + 조사·탐색·구현·검증 실행은 하위 모델 워커에게 시킨다. 워커 보고는 증거가 아니다. - **에이전트 지침의 원본은 이 파일(`AGENTS.md`) 하나다.** `CLAUDE.md`, `AGENT.md`처럼 도구별로 자동 탐지되는 파일은 호환성 진입점으로만 유지한다. 프로젝트 규칙을 바꿀 때는 이 파일만 수정하고, 진입점 파일에는 중복 규칙을 추가하지 않는다. diff --git a/CLAUDE.md b/CLAUDE.md index 0d58f97..cdbd0e6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,5 +5,22 @@ 프로젝트 지침의 원본은 [`AGENTS.md`](./AGENTS.md) 하나다. 작업을 시작할 때는 반드시 `AGENTS.md`를 읽고, 그 내용을 이 파일에 적힌 지침처럼 적용하라. +## ⚖️ 절대 규칙 — AI 에이전트 오케스트레이션 바이블 (`AGENTS.md` B절) + +`AGENTS.md`의 **B절**은 이 저장소의 모든 지침보다 우선하는 절대 규칙이다. 요약하면: + +- 이 세션의 모델이 **Fable 5.1(`claude-fable-5-1`) 또는 Opus 5(`claude-opus-5`)** 이면 너는 **오케스트레이터**다. + 메타인지·설계·요구사항 해석·작업 분해·작업 패킷·배정·결과 평가·최종 보고에 집중한다. +- 매 작업마다 경중을 따져, 네가 진짜 직접 해야 하는 일(설계·중요 결정·평가·파괴적 작업)이 아니면 **직접 나서지 않고** + 하위 모델 워커에게 시킨다: `.claude/agents/`의 `researcher`(Haiku, 조사)·`implementer`(Sonnet, 구현)· + `verifier`(Sonnet, 검증 실행)와 내장 `Explore`(탐색). 대규모 조사·웹서핑·논문 스위핑·탐색은 여러 워커를 **병렬**로 돌린다. +- **설계와 중요한 결정은 절대 위임하지 않는다.** 하위 모델에게 설계를 맡기고 결과만 받아 쓰지 않는다. +- **시켜 놓고 방치하지 않는다.** 워커가 끝나면 즉시 diff·명령 결과 원문을 직접 읽고 수용/반려/폐기를 판정한다. + 워커의 "완료했습니다"는 증거가 아니다. +- 작업 패킷 없이 스폰하지 않는다. 양식: `docs/ops/agent-work-packet-template.md`. +- 최상위 세션이 하위 모델(Sonnet/Haiku)이면 위임 규칙 없이 직접 수행하되, 설계 결정은 고급 모델 세션으로 넘기라고 알린다. + +세부 규칙(B.0~B.7), Claude Code 실행 규격(B.9), 이 저장소의 검증 명령·파괴적 작업·병렬 경계(B.10)는 `AGENTS.md`를 따른다. + 이 파일에 프로젝트 규칙을 중복 추가하지 마라. OS·셸·검증·문서·이미지 생성·아바타 -파이프라인 규칙을 바꿀 때는 `AGENTS.md`만 수정한다. +파이프라인·오케스트레이션 규칙을 바꿀 때는 `AGENTS.md`만 수정한다. diff --git a/apps/api/app/auth_sessions.py b/apps/api/app/auth_sessions.py index 0412577..054f2d0 100644 --- a/apps/api/app/auth_sessions.py +++ b/apps/api/app/auth_sessions.py @@ -1564,39 +1564,44 @@ async def upsert_managed_user(data: ManagedUserUpsertInput) -> ManagedUser: ) +_MANAGED_USER_SELECT_SQL = """ +SELECT + user_id, + email, + display_name, + role, + admin_access, + learner_feedback_enabled, + account_status, + cohort, + affiliation, + legal_name, + department, + grade_level, + phone, + contact_address, + nickname, + self_introduction, + avatar_url, + consent_at, + profile_completed_at, + terms_agreed_at, + privacy_agreed_at, + terms_version, + privacy_version, + created_at, + last_seen_at +FROM app.app_user +""" + + async def get_managed_user(user_id: str) -> ManagedUser | None: try: pool = get_pool() async with pool.acquire() as conn: row = await conn.fetchrow( - """ - SELECT - user_id, - email, - display_name, - role, - admin_access, - learner_feedback_enabled, - account_status, - cohort, - affiliation, - legal_name, - department, - grade_level, - phone, - contact_address, - nickname, - self_introduction, - avatar_url, - consent_at, - profile_completed_at, - terms_agreed_at, - privacy_agreed_at, - terms_version, - privacy_version, - created_at, - last_seen_at - FROM app.app_user + f""" + {_MANAGED_USER_SELECT_SQL} WHERE user_id = $1::uuid AND is_active """, user_id, @@ -1619,34 +1624,8 @@ async def get_managed_user_by_email(email: str) -> ManagedUser | None: pool = get_pool() async with pool.acquire() as conn: row = await conn.fetchrow( - """ - SELECT - user_id, - email, - display_name, - role, - admin_access, - learner_feedback_enabled, - account_status, - cohort, - affiliation, - legal_name, - department, - grade_level, - phone, - contact_address, - nickname, - self_introduction, - avatar_url, - consent_at, - profile_completed_at, - terms_agreed_at, - privacy_agreed_at, - terms_version, - privacy_version, - created_at, - last_seen_at - FROM app.app_user + f""" + {_MANAGED_USER_SELECT_SQL} WHERE lower(email) = $1 AND is_active ORDER BY CASE WHEN external_id = $2 THEN 0 ELSE 1 END, diff --git a/apps/api/app/routes/admin.py b/apps/api/app/routes/admin.py index f53c98a..dd1a191 100644 --- a/apps/api/app/routes/admin.py +++ b/apps/api/app/routes/admin.py @@ -779,6 +779,24 @@ async def _runtime_health_metrics(*, db_ok: bool) -> RuntimeHealthMetrics: return metrics +def _usage_daily_cost( + daily_buckets: dict[str, dict[str, int | float | list[UsageCostBasis]]], +) -> list[AdminUsageDailyCost]: + return [ + AdminUsageDailyCost( + day=day, + turns=int(values["turns"]), + tokens_in=int(values["tokens_in"]), + tokens_out=int(values["tokens_out"]), + cost_usd=round(float(values["cost_usd"]), 6), + cost_basis=_aggregate_cost_basis( + cast(list[UsageCostBasis], values["cost_bases"]) + ), + ) + for day, values in sorted(daily_buckets.items()) + ] + + async def _usage_from_database(window_days: int) -> AdminUsageResponse: async with acquire(role="admin") as conn: total_row = await conn.fetchrow( @@ -916,19 +934,7 @@ async def _usage_from_database(window_days: int) -> AdminUsageResponse: budget=_usage_budget(total_cost, total_cost_basis), evaluator_cache=_usage_evaluator_cache(), by_provider=all_breakdowns[:12], - daily_cost=[ - AdminUsageDailyCost( - day=day, - turns=int(values["turns"]), - tokens_in=int(values["tokens_in"]), - tokens_out=int(values["tokens_out"]), - cost_usd=round(float(values["cost_usd"]), 6), - cost_basis=_aggregate_cost_basis( - cast(list[UsageCostBasis], values["cost_bases"]) - ), - ) - for day, values in sorted(daily_buckets.items()) - ], + daily_cost=_usage_daily_cost(daily_buckets), ) @@ -1321,19 +1327,7 @@ def _usage_from_runtime_store(window_days: int) -> AdminUsageResponse: budget=_usage_budget(total_cost, total_cost_basis), evaluator_cache=_usage_evaluator_cache(), by_provider=by_provider[:12], - daily_cost=[ - AdminUsageDailyCost( - day=day, - turns=int(values["turns"]), - tokens_in=int(values["tokens_in"]), - tokens_out=int(values["tokens_out"]), - cost_usd=round(float(values["cost_usd"]), 6), - cost_basis=_aggregate_cost_basis( - cast(list[UsageCostBasis], values["cost_bases"]) - ), - ) - for day, values in sorted(daily_buckets.items()) - ], + daily_cost=_usage_daily_cost(daily_buckets), ) diff --git a/apps/api/app/routes/eval.py b/apps/api/app/routes/eval.py index 50a004a..c5fa659 100644 --- a/apps/api/app/routes/eval.py +++ b/apps/api/app/routes/eval.py @@ -23,7 +23,7 @@ from typing import Annotated, Any, Optional from fastapi import APIRouter, Depends, HTTPException, status from pydantic import BaseModel, Field -from .. import session_persistence +from .. import session_evaluation_repository, session_persistence from ..deps import Principal, Role, require_role from ..engine_client import EngineError, engine_client from ..runtime_policy import runtime_fallback_allowed @@ -167,7 +167,7 @@ async def reevaluate_session( ) except EngineError as e: detail = f"engine unavailable: {e}" - write = session_persistence.SessionEvaluationWrite.from_error( + write = session_evaluation_repository.SessionEvaluationWrite.from_error( session_id=session_id, learner_id=sess.learner_id, scope=body.scope if body.scope in ("session_end", "stage_transition") else "session_end", @@ -176,7 +176,7 @@ async def reevaluate_session( counselor_identity=counselor_identity, client_identity=client_identity, ) - saved = await session_persistence.save_session_evaluation(write) + saved = await session_evaluation_repository.save_session_evaluation(write) if not saved: logger.error( "session evaluation retry error record did not reach durable store: session_id=%s error=%s", @@ -188,14 +188,14 @@ async def reevaluate_session( detail=_safe_session_evaluation_retry_detail(), ) - write = session_persistence.SessionEvaluationWrite.from_result( + write = session_evaluation_repository.SessionEvaluationWrite.from_result( session_id=session_id, learner_id=sess.learner_id, result=result, counselor_identity=counselor_identity, client_identity=client_identity, ) - saved = await session_persistence.save_session_evaluation(write) + saved = await session_evaluation_repository.save_session_evaluation(write) if not saved: detail = "session evaluation retry result was generated but could not be saved" logger.error("%s: session_id=%s status=%s", detail, session_id, write.status) @@ -302,7 +302,7 @@ async def get_session_evaluation( 아직 평가 트리거가 없었다면 deep=None + 빈 분포. """ await _load_session_or_404(session_id, principal) - record, durable = await session_persistence.load_session_evaluation(session_id, principal) + record, durable = await session_evaluation_repository.load_session_evaluation(session_id, principal) if record is None: return EvaluationSummary( session_id=session_id, diff --git a/apps/api/app/routes/sessions.py b/apps/api/app/routes/sessions.py index a17b2f0..c1d0255 100644 --- a/apps/api/app/routes/sessions.py +++ b/apps/api/app/routes/sessions.py @@ -21,7 +21,7 @@ from fastapi import APIRouter, HTTPException, Request, status from pydantic import BaseModel, Field, field_validator from sse_starlette.sse import EventSourceResponse -from .. import db, session_persistence, turn_runtime +from .. import db, session_evaluation_repository, session_persistence, turn_runtime from ..auth_sessions import user_has_consent, user_onboarding_complete from ..config import settings from ..deps import CurrentPrincipal, Principal, Role @@ -29,6 +29,7 @@ from ..engine_client import EngineError, engine_client from ..persona_repository import get_catalog_persona from ..runtime_policy import require_runtime_fallback_allowed from ..session_evaluation_input import enriched_masked_turns +from ..session_turn_memory import build_turn_memory from ..session_evaluation_timeout import ( session_evaluation_outer_timeout_seconds, session_evaluation_timeout_seconds as _session_evaluation_timeout_seconds, @@ -50,15 +51,28 @@ from ..services import ( session_learning_producer, state_machine, ) +from ..session_dashboard_projection import ( + LearnerDashboardResponse, + dashboard_achievements as _dashboard_achievements, + dashboard_feedback as _dashboard_feedback, + dashboard_growth as _dashboard_growth, + dashboard_overview as _dashboard_overview, + dashboard_persona_progress as _dashboard_persona_progress, + dashboard_training_exposure as _dashboard_training_exposure, +) +from ..session_projection import ( + LEARNER_VISIBLE_AI_ROLE, + iso as _iso, + learner_visible_turns as _learner_visible_turns, + stage_label as _stage_label, +) from ..session_read_model import ( CaseMemoryPreview, CaseProgressStats, LearnerCaseListResponse, LearnerCaseSummary, - LearnerDashboardResponse, LearnerSessionsResponse, LearnerSessionSummary, - LEARNER_VISIBLE_AI_ROLE, ReviewCaseWorksheet, ReviewCaseWorksheetSaveRequest, ReviewWorksheetItem as ReviewWorksheetItem, @@ -73,18 +87,9 @@ from ..session_read_model import ( StageLabel, build_session_progress, build_session_review, - dashboard_achievements as _dashboard_achievements, - dashboard_feedback as _dashboard_feedback, - dashboard_growth as _dashboard_growth, - dashboard_overview as _dashboard_overview, - dashboard_persona_progress as _dashboard_persona_progress, - dashboard_training_exposure as _dashboard_training_exposure, - iso as _iso, learner_summary as _learner_summary, - learner_visible_turns as _learner_visible_turns, session_detail as _session_detail, session_share_payload as _session_share_payload, - stage_label as _stage_label, ) from ..store import InProcSession, TurnRecord, store @@ -548,12 +553,7 @@ async def _prepare_turn_context( state=sess.state, learner_text=learner_text, learner_identity=sess.learner_label, - memory=orchestrator.TurnMemory( - recall_summary=recall.recall_summary, - pinned_facts=recall.pinned_facts, - recent_turns=sess.recent_turns(visible_to="client"), - kb_behavior_cues=kb_cues, - ), + memory=build_turn_memory(sess, recall, kb_cues), theory_mode=sess.theory_mode, scenario_context=scenario_context, ) @@ -956,14 +956,14 @@ async def _generate_and_save_session_evaluation(sess: InProcSession) -> None: # 예산 뒤에 outer grace를 두어 정상 결과를 timeout error로 바꾸지 않는다. timeout=session_evaluation_outer_timeout_seconds(), ) - write = session_persistence.SessionEvaluationWrite.from_result( + write = session_evaluation_repository.SessionEvaluationWrite.from_result( session_id=sess.session_id, learner_id=sess.learner_id, result=result, counselor_identity=sess.learner_label, client_identity=sess.persona.display_name, ) - saved = await session_persistence.save_session_evaluation(write) + saved = await session_evaluation_repository.save_session_evaluation(write) if not saved: logger.error( "session evaluation save did not reach durable store: session_id=%s status=%s scope=%s", @@ -988,7 +988,7 @@ async def _generate_and_save_session_evaluation(sess: InProcSession) -> None: except asyncio.TimeoutError: message = f"session evaluation timeout after {timeout_seconds:g}s" logger.exception("%s: session_id=%s", message, sess.session_id) - write = session_persistence.SessionEvaluationWrite.from_error( + write = session_evaluation_repository.SessionEvaluationWrite.from_error( session_id=sess.session_id, learner_id=sess.learner_id, scope="session_end", @@ -997,7 +997,7 @@ async def _generate_and_save_session_evaluation(sess: InProcSession) -> None: counselor_identity=sess.learner_label, client_identity=sess.persona.display_name, ) - saved = await session_persistence.save_session_evaluation(write) + saved = await session_evaluation_repository.save_session_evaluation(write) if not saved: logger.error( "session evaluation error save did not reach durable store: session_id=%s error=%s", @@ -1008,7 +1008,7 @@ async def _generate_and_save_session_evaluation(sess: InProcSession) -> None: await _enqueue_session_review_ready_notification(sess.session_id) except Exception as exc: logger.exception("session evaluation failed: session_id=%s", sess.session_id) - write = session_persistence.SessionEvaluationWrite.from_error( + write = session_evaluation_repository.SessionEvaluationWrite.from_error( session_id=sess.session_id, learner_id=sess.learner_id, scope="session_end", @@ -1017,7 +1017,7 @@ async def _generate_and_save_session_evaluation(sess: InProcSession) -> None: counselor_identity=sess.learner_label, client_identity=sess.persona.display_name, ) - saved = await session_persistence.save_session_evaluation(write) + saved = await session_evaluation_repository.save_session_evaluation(write) if not saved: logger.error( "session evaluation failure record did not reach durable store: session_id=%s error=%s", @@ -1162,7 +1162,7 @@ async def _review_ready(sess: InProcSession, principal: Principal) -> bool: return False if len(turns) != len(sess.turns): return False - evaluation_record, _ = await session_persistence.load_session_evaluation( + evaluation_record, _ = await session_evaluation_repository.load_session_evaluation( sess.session_id, principal, ) @@ -1774,7 +1774,7 @@ async def get_session_review( ( evaluation_record, evaluation_durable, - ) = await session_persistence.load_session_evaluation( + ) = await session_evaluation_repository.load_session_evaluation( session_id, review_principal, ) @@ -2245,7 +2245,8 @@ async def end_session( await _end_persisted_session(sess, carry) invalidate_session_context_cache(session_id) rupture_runtime.schedule_session_scan(session_id, trigger="session_ended") - if not was_ended: + has_counselor_turns = any(t.speaker in ("counselor", "learner") for t in sess.turns) + if not was_ended and has_counselor_turns: _schedule_session_evaluation(sess) return SessionEndResponse( diff --git a/apps/api/app/routes/teacher.py b/apps/api/app/routes/teacher.py index b5cbf7e..7b3dd60 100644 --- a/apps/api/app/routes/teacher.py +++ b/apps/api/app/routes/teacher.py @@ -8,7 +8,7 @@ from typing import Annotated, Literal from fastapi import APIRouter, Depends, HTTPException, status from pydantic import BaseModel, Field -from .. import session_persistence +from .. import session_evaluation_repository, session_persistence from ..deps import Principal, Role, require_role from ..runtime_policy import require_runtime_fallback_allowed from ..session_read_model import ( @@ -350,7 +350,7 @@ async def teacher_dashboard(principal: TeacherPrincipal) -> TeacherDashboardResp ended_session_ids, principal, ) - evaluation_records, evaluations_durable = await session_persistence.list_session_evaluations( + evaluation_records, evaluations_durable = await session_evaluation_repository.list_session_evaluations( ended_session_ids, principal, ) @@ -403,7 +403,11 @@ async def teacher_dashboard(principal: TeacherPrincipal) -> TeacherDashboardResp source="database" if durable else "runtime", total_learners=len(learners), active_sessions=sum(1 for sess in sessions if not sess.ended), - ended_sessions=sum(1 for sess in sessions if sess.ended), + ended_sessions=sum( + 1 + for sess in sessions + if sess.ended and any(t.speaker in ("counselor", "learner") for t in sess.turns) + ), safety_alerts=safety_alerts, learner_growth=learner_growth, pending_reviews=pending_reviews[:20], @@ -442,7 +446,7 @@ async def learner_analysis( ended_session_ids, principal, ) - evaluation_records, evaluations_durable = await session_persistence.list_session_evaluations( + evaluation_records, evaluations_durable = await session_evaluation_repository.list_session_evaluations( ended_session_ids, principal, ) @@ -462,7 +466,11 @@ async def learner_analysis( learner_label=growth.learner_label, total_sessions=len(ordered), active_sessions=sum(1 for sess in ordered if not sess.ended), - ended_sessions=sum(1 for sess in ordered if sess.ended), + ended_sessions=sum( + 1 + for sess in ordered + if sess.ended and any(t.speaker in ("counselor", "learner") for t in sess.turns) + ), pending_reviews=pending_reviews, closed_reviews=closed_reviews, summary=growth, @@ -495,7 +503,7 @@ async def update_session_review_status( detail="active sessions cannot be closed as reviewed", ) if sess.ended and request.status == "closed" and learner_visible_turns(sess): - evaluation_record, evaluation_durable = await session_persistence.load_session_evaluation( + evaluation_record, evaluation_durable = await session_evaluation_repository.load_session_evaluation( session_id, principal, ) diff --git a/apps/api/app/routes/voice.py b/apps/api/app/routes/voice.py index 84059b5..a726a03 100644 --- a/apps/api/app/routes/voice.py +++ b/apps/api/app/routes/voice.py @@ -39,6 +39,7 @@ from ..persona_repository import ( get_session_voice_map, ) from ..runtime_policy import require_runtime_fallback_allowed +from ..session_turn_memory import build_turn_memory from ..services import ( evaluator, guardrail, @@ -1459,12 +1460,7 @@ async def _prepare_voice_turn_context( state=sess.state, learner_text=learner_text, learner_identity=sess.learner_label, - memory=orchestrator.TurnMemory( - recall_summary=recall.recall_summary, - pinned_facts=recall.pinned_facts, - recent_turns=sess.recent_turns(visible_to="client"), - kb_behavior_cues=kb_cues, - ), + memory=build_turn_memory(sess, recall, kb_cues), theory_mode=sess.theory_mode, scenario_context=scenario_context, ) diff --git a/apps/api/app/services/calibration_transfer_store.py b/apps/api/app/services/calibration_transfer_store.py index 48e8460..1b59bf5 100644 --- a/apps/api/app/services/calibration_transfer_store.py +++ b/apps/api/app/services/calibration_transfer_store.py @@ -2,7 +2,6 @@ from __future__ import annotations -import hashlib import json from collections.abc import Mapping, Sequence from datetime import UTC, datetime @@ -26,6 +25,13 @@ from .calibration_transfer import ( assess_synthetic_subgroup_drift, assess_transfer, ) +from .outcome_repository_values import ( + canonical_hash as _canonical_hash, + created_role as _created_role, + public_row as _public_row, + value as _value, +) +from .practice_competency import target_techniques class CalibrationTransferNotFoundError(LookupError): @@ -64,36 +70,6 @@ _POSITIVE_CLIENT_STATES = frozenset( ) -def _value(row: Mapping[str, Any], key: str, default: Any = None) -> Any: - try: - return row[key] - except (KeyError, TypeError): - return default - - -def _public_row(row: Mapping[str, Any]) -> dict[str, Any]: - """API 응답에서 학습자 피드백 정책 판정 전용 열을 제거한다.""" - - payload = dict(row) - payload.pop("source_learner_feedback_enabled", None) - return payload - - -def _canonical_hash(payload: Mapping[str, Any]) -> str: - serialized = json.dumps( - payload, - ensure_ascii=False, - separators=(",", ":"), - sort_keys=True, - default=str, - ) - return hashlib.sha256(serialized.encode("utf-8")).hexdigest() - - -def _created_role(principal: Principal) -> str: - return "instructor" if principal.role == Role.TEACHER else principal.role.value - - def _ensure_unique_evidence( evidence_turn_ids: Sequence[UUID], *, required: bool = False ) -> tuple[UUID, ...]: @@ -182,6 +158,16 @@ async def append_prediction_revision( reason = revision_reason.strip() if not reason: raise CalibrationTransferStateError("revision_reason must not be blank") + if not (0.0 <= predicted_success_probability <= 1.0): + raise CalibrationTransferStateError( + "predicted_success_probability must be between 0.0 and 1.0" + ) + if not (0.0 <= confidence <= 1.0): + raise CalibrationTransferStateError( + "confidence must be between 0.0 and 1.0" + ) + if instrument_id == "vignette.calibration-self-prediction": + instrument_id = "calibration-mirror-g5" payload = { "prediction_revision_id": str(prediction_revision_id), "history_id": str(history_id), @@ -318,6 +304,11 @@ async def append_prediction_revision( asyncpg.ForeignKeyViolationError, asyncpg.ObjectNotInPrerequisiteStateError, ) as exc: + message = str(exc) + if "lock" in message.lower() or "reveal" in message.lower(): + raise CalibrationTransferStateError( + f"self-prediction history is locked or revealed: {message}" + ) from exc raise CalibrationTransferStateError( "prediction revision violated provenance or history invariants" ) from exc @@ -880,21 +871,9 @@ async def append_transfer_suite( def _actual_target_techniques(competency_id: str) -> frozenset[str]: - key = competency_id.lower() - if any(token in key for token in ("empathy", "empathic", "reflection")): - return frozenset({"empathy", "reflection", "validation", "restatement"}) - if any(token in key for token in ("open_question", "open-question")): - return frozenset({"facilitative_question", "exploration", "clarification"}) - if any(token in key for token in ("rupture", "repair", "impact")): - return frozenset( - {"opinion_check", "validation", "reflection", "here_and_now_focus"} - ) - if any(token in key for token in ("goal", "collaborative", "reagreement")): - return frozenset( - {"consent_motivation_check", "opinion_check", "restatement"} - ) - if any(token in key for token in ("presence", "response-space")): - return frozenset({"holding", "reflection", "here_and_now_focus"}) + targets = target_techniques(competency_id) + if targets is not None: + return targets raise CalibrationTransferStateError( f"unsupported actual transfer competency: {competency_id}" ) diff --git a/apps/api/app/services/continuous_improvement_store.py b/apps/api/app/services/continuous_improvement_store.py index 4f60ae5..c9cce32 100644 --- a/apps/api/app/services/continuous_improvement_store.py +++ b/apps/api/app/services/continuous_improvement_store.py @@ -2,8 +2,6 @@ from __future__ import annotations -import hashlib -import json from collections.abc import Mapping, Sequence from typing import Any, Literal, Protocol from uuid import UUID, uuid5 @@ -26,6 +24,10 @@ from .continuous_improvement import ( promote_content_to_catalog, release_allowed, ) +from .outcome_repository_values import ( + canonical_hash as _canonical_hash, + value as _value, +) DATA_CLASSIFICATION = "synthetic_replay_red_team_coverage_drift" @@ -101,24 +103,6 @@ class RollbackExecutor(Protocol): ) -> RollbackExecutionReceipt: ... -def _value(row: Mapping[str, Any], key: str, default: Any = None) -> Any: - try: - return row[key] - except (KeyError, TypeError): - return default - - -def _canonical_hash(payload: Any) -> str: - serialized = json.dumps( - payload, - ensure_ascii=False, - separators=(",", ":"), - sort_keys=True, - default=str, - ) - return hashlib.sha256(serialized.encode("utf-8")).hexdigest() - - async def _begin_submission( conn: asyncpg.Connection, *, diff --git a/apps/api/app/services/deliberate_practice_store.py b/apps/api/app/services/deliberate_practice_store.py index 3493ac1..6bf815a 100644 --- a/apps/api/app/services/deliberate_practice_store.py +++ b/apps/api/app/services/deliberate_practice_store.py @@ -7,8 +7,6 @@ separate superseding events and never mutate the original evidence or graph. from __future__ import annotations -import hashlib -import json from collections.abc import Mapping, Sequence from typing import Any from uuid import UUID, uuid5 @@ -39,6 +37,12 @@ from .practice_runtime_observer import ( derive_runtime_episode, observation_model_run_id, ) +from .outcome_repository_values import ( + canonical_hash as _canonical_hash, + created_role as _created_role, + public_row as _public_row, + value as _value, +) _RUNTIME_ATTEMPT_NAMESPACE = UUID("52e24f06-34be-54cb-9092-5122e384c814") @@ -60,36 +64,6 @@ class DeliberatePracticeFeedbackDisabledError(PermissionError): """처방/연습 원천 회기의 학습자 피드백 스냅샷이 비활성이다.""" -def _value(row: Mapping[str, Any], key: str, default: Any = None) -> Any: - try: - return row[key] - except (KeyError, TypeError): - return default - - -def _public_row(row: Mapping[str, Any]) -> dict[str, Any]: - """API 응답에서 정책 판정 전용 내부 열을 제거한다.""" - - payload = dict(row) - payload.pop("source_learner_feedback_enabled", None) - return payload - - -def _canonical_hash(payload: Mapping[str, Any]) -> str: - serialized = json.dumps( - payload, - ensure_ascii=False, - separators=(",", ":"), - sort_keys=True, - default=str, - ) - return hashlib.sha256(serialized.encode("utf-8")).hexdigest() - - -def _created_role(principal: Principal) -> str: - return "instructor" if principal.role == Role.TEACHER else principal.role.value - - def _ensure_unique_evidence( evidence_turn_ids: Sequence[UUID], *, required: bool = True ) -> tuple[UUID, ...]: diff --git a/apps/api/app/services/outcome_repository_values.py b/apps/api/app/services/outcome_repository_values.py new file mode 100644 index 0000000..7f602f6 --- /dev/null +++ b/apps/api/app/services/outcome_repository_values.py @@ -0,0 +1,40 @@ +"""결과 저장소가 공유하는 순수 값 정규화 도우미.""" + +from __future__ import annotations + +import hashlib +import json +from collections.abc import Mapping +from typing import Any + +from ..deps import Principal, Role + + +def value(row: Mapping[str, Any], key: str, default: Any = None) -> Any: + try: + return row[key] + except (KeyError, TypeError): + return default + + +def canonical_hash(payload: Any) -> str: + serialized = json.dumps( + payload, + ensure_ascii=False, + separators=(",", ":"), + sort_keys=True, + default=str, + ) + return hashlib.sha256(serialized.encode("utf-8")).hexdigest() + + +def public_row(row: Mapping[str, Any]) -> dict[str, Any]: + """API 응답에서 학습자 피드백 정책 판정 전용 열을 제거한다.""" + + payload = dict(row) + payload.pop("source_learner_feedback_enabled", None) + return payload + + +def created_role(principal: Principal) -> str: + return "instructor" if principal.role == Role.TEACHER else principal.role.value diff --git a/apps/api/app/services/practice_competency.py b/apps/api/app/services/practice_competency.py new file mode 100644 index 0000000..c9876bb --- /dev/null +++ b/apps/api/app/services/practice_competency.py @@ -0,0 +1,22 @@ +"""실제 연습과 전이 평가가 공유하는 competency 분류.""" + +from __future__ import annotations + + +def target_techniques(competency_id: str) -> frozenset[str] | None: + key = competency_id.lower() + if any(token in key for token in ("empathy", "empathic", "reflection")): + return frozenset({"empathy", "reflection", "validation", "restatement"}) + if any(token in key for token in ("open_question", "open-question")): + return frozenset({"facilitative_question", "exploration", "clarification"}) + if any(token in key for token in ("rupture", "repair", "impact")): + return frozenset( + {"opinion_check", "validation", "reflection", "here_and_now_focus"} + ) + if any(token in key for token in ("goal", "collaborative", "reagreement")): + return frozenset( + {"consent_motivation_check", "opinion_check", "restatement"} + ) + if any(token in key for token in ("presence", "response-space")): + return frozenset({"holding", "reflection", "here_and_now_focus"}) + return None diff --git a/apps/api/app/services/practice_runtime_observer.py b/apps/api/app/services/practice_runtime_observer.py index bce4d80..3b658a8 100644 --- a/apps/api/app/services/practice_runtime_observer.py +++ b/apps/api/app/services/practice_runtime_observer.py @@ -19,6 +19,7 @@ from ..contracts.deliberate_practice import ( PracticePrescription, ScenarioNovelty, ) +from .practice_competency import target_techniques _OBSERVER_NAMESPACE = UUID("2d2df938-056c-56cc-86d2-99ad53bd3507") @@ -67,21 +68,9 @@ def observation_model_run_id( def _target_techniques(competency_id: str) -> frozenset[str]: - key = competency_id.lower() - if any(token in key for token in ("empathy", "empathic", "reflection")): - return frozenset({"empathy", "reflection", "validation", "restatement"}) - if any(token in key for token in ("open_question", "open-question")): - return frozenset({"facilitative_question", "exploration", "clarification"}) - if any(token in key for token in ("rupture", "repair", "impact")): - return frozenset( - {"opinion_check", "validation", "reflection", "here_and_now_focus"} - ) - if any(token in key for token in ("goal", "collaborative", "reagreement")): - return frozenset( - {"consent_motivation_check", "opinion_check", "restatement"} - ) - if any(token in key for token in ("presence", "response-space")): - return frozenset({"holding", "reflection", "here_and_now_focus"}) + targets = target_techniques(competency_id) + if targets is not None: + return targets raise RuntimePracticeObservationError( f"unsupported runtime practice competency: {competency_id}" ) diff --git a/apps/api/app/services/session_metrics.py b/apps/api/app/services/session_metrics.py index 66b0316..6bd54a6 100644 --- a/apps/api/app/services/session_metrics.py +++ b/apps/api/app/services/session_metrics.py @@ -213,7 +213,12 @@ def build_learner_growth( learner_id=learner_id, learner_label=learner_label(learner_id), sessions=len(ordered), - ended_sessions=sum(1 for sess in ordered if sess.ended), + ended_sessions=sum( + 1 + for sess in ordered + if sess.ended + and any(t.speaker in ("counselor", "learner") for t in sess.turns) + ), latest_at=iso_datetime(session_activity_time(latest_session)) or "", first_score=first_score, latest_score=latest_score, diff --git a/apps/api/app/services/supervision_research_store.py b/apps/api/app/services/supervision_research_store.py index a164429..e463dc6 100644 --- a/apps/api/app/services/supervision_research_store.py +++ b/apps/api/app/services/supervision_research_store.py @@ -2,8 +2,6 @@ from __future__ import annotations -import hashlib -import json from collections.abc import Mapping, Sequence from typing import Any from uuid import UUID, uuid5 @@ -23,6 +21,10 @@ from .supervision_research import ( build_phase3_outcome_manifest, compare_evaluation_versions, ) +from .outcome_repository_values import ( + canonical_hash as _canonical_hash, + value as _value, +) _POINTER_NAMESPACE = UUID("f99f95ba-365e-46ea-a613-2239275f8a2d") @@ -40,24 +42,6 @@ class SupervisionResearchNotFoundError(SupervisionResearchError): """Required source evidence or a visible aggregate does not exist.""" -def _value(row: Mapping[str, Any], key: str, default: Any = None) -> Any: - try: - return row[key] - except (KeyError, TypeError): - return default - - -def _canonical_hash(payload: Any) -> str: - serialized = json.dumps( - payload, - ensure_ascii=False, - separators=(",", ":"), - sort_keys=True, - default=str, - ) - return hashlib.sha256(serialized.encode("utf-8")).hexdigest() - - async def _existing_submission( conn: asyncpg.Connection, *, diff --git a/apps/api/app/services/voice.py b/apps/api/app/services/voice.py index e6df39c..8e36711 100644 --- a/apps/api/app/services/voice.py +++ b/apps/api/app/services/voice.py @@ -193,6 +193,29 @@ class StreamingTranscriptEvent: confidence: float | None = None +def _streaming_provider_event( + provider: str, + transcript_event: StreamingTranscriptEvent, + *, + start: float, + duration: float, +) -> dict[str, object]: + event_type = "speech_final" if transcript_event.speech_final else ( + "speech_end" if transcript_event.final else "voice_activity" + ) + provider_event: dict[str, object] = { + "type": event_type, + "provider": provider, + "source": "streaming_stt", + "start_ms": round(start * 1000), + "duration_ms": round(duration * 1000), + "is_final": transcript_event.final, + } + if transcript_event.confidence is not None: + provider_event["confidence"] = transcript_event.confidence + return provider_event + + class DeepgramStreamingSession: """One Deepgram Listen WebSocket, scoped to exactly one learner utterance.""" @@ -364,29 +387,21 @@ class DeepgramStreamingSession: if not display_text and not speech_final: return - event_type = "speech_final" if speech_final else ( - "speech_end" if is_final else "voice_activity" + transcript_event = StreamingTranscriptEvent( + text=display_text, + final=is_final, + speech_final=speech_final, + confidence=confidence, + ) + provider_event = _streaming_provider_event( + "deepgram", + transcript_event, + start=start, + duration=duration, ) - provider_event: dict[str, object] = { - "type": event_type, - "provider": "deepgram", - "source": "streaming_stt", - "start_ms": round(start * 1000), - "duration_ms": round(duration * 1000), - "is_final": is_final, - } - if confidence is not None: - provider_event["confidence"] = confidence if is_final or speech_final: self._provider_events.append(provider_event) - await self._on_event( - StreamingTranscriptEvent( - text=display_text, - final=is_final, - speech_final=speech_final, - confidence=confidence, - ) - ) + await self._on_event(transcript_event) def _consume_final_words(self, value: object) -> None: if not isinstance(value, list): @@ -620,29 +635,21 @@ class LocalWhisperStreamingSession: if not display_text and not speech_final: return - event_type = "speech_final" if speech_final else ( - "speech_end" if is_final else "voice_activity" + transcript_event = StreamingTranscriptEvent( + text=display_text, + final=is_final, + speech_final=speech_final, + confidence=confidence, + ) + provider_event = _streaming_provider_event( + "local_whisper", + transcript_event, + start=start, + duration=duration, ) - provider_event: dict[str, object] = { - "type": event_type, - "provider": "local_whisper", - "source": "streaming_stt", - "start_ms": round(start * 1000), - "duration_ms": round(duration * 1000), - "is_final": is_final, - } - if confidence is not None: - provider_event["confidence"] = confidence if is_final or speech_final: self._provider_events.append(provider_event) - await self._on_event( - StreamingTranscriptEvent( - text=display_text, - final=is_final, - speech_final=speech_final, - confidence=confidence, - ) - ) + await self._on_event(transcript_event) def _consume_final_words(self, value: object, *, offset: float) -> None: if not isinstance(value, list): diff --git a/apps/api/app/session_dashboard_projection.py b/apps/api/app/session_dashboard_projection.py new file mode 100644 index 0000000..86c5c6f --- /dev/null +++ b/apps/api/app/session_dashboard_projection.py @@ -0,0 +1,358 @@ +"""학습자 대시보드 DTO와 순수 projection.""" + +from __future__ import annotations + +from collections import Counter +from typing import Literal + +from pydantic import BaseModel, Field + +from .services import session_metrics +from .session_projection import _rapport_percent, learner_visible_turns, stage_label +from .stage_contract import StageLabel +from .store import InProcSession + +TRAINING_EXPOSURE_VERSION = "training-exposure-dominant-share.v1" +TRAINING_EXPOSURE_MIN_COMPLETED = 4 +TRAINING_EXPOSURE_ATTENTION_THRESHOLD = 0.75 + + +class LearnerDashboardOverview(BaseModel): + total_sessions: int = 0 + completed_sessions: int = 0 + active_sessions: int = 0 + review_ready_sessions: int = 0 + archived_sessions: int = 0 + learner_turns: int = 0 + client_turns: int = 0 + last_practiced_at: str | None = None + + +class LearnerDashboardGrowthPoint(BaseModel): + session_id: str + session_no: int + persona_code: str + stage: StageLabel + started_at: str + ended_at: str | None = None + score: float | None = None + rapport: float | None = None + technique_count: int = 0 + watch_count: int = 0 + + +class LearnerDashboardGrowth(BaseModel): + first_score: float | None = None + latest_score: float | None = None + score_delta: float | None = None + avg_score: float | None = None + avg_rapport: float | None = None + trend: str = "insufficient" + evaluated_sessions: int = 0 + top_techniques: list[str] = Field(default_factory=list) + points: list[LearnerDashboardGrowthPoint] = Field(default_factory=list) + + +class LearnerDashboardPersonaProgress(BaseModel): + persona_code: str + persona_name: str + sessions: int = 0 + completed_sessions: int = 0 + active_sessions: int = 0 + review_ready_sessions: int = 0 + latest_at: str | None = None + latest_stage: StageLabel | None = None + latest_score: float | None = None + trend: str = "insufficient" + rapport_percent: int = 0 + + +class LearnerDashboardTrainingExposure(BaseModel): + version: str = TRAINING_EXPOSURE_VERSION + status: Literal["insufficient", "attention", "balanced"] = "insufficient" + label: Literal["판정 근거 부족", "훈련 집중 주의", "균형"] = "판정 근거 부족" + completed_sessions: int = 0 + minimum_completed_sessions: int = TRAINING_EXPOSURE_MIN_COMPLETED + attention_threshold: float = TRAINING_EXPOSURE_ATTENTION_THRESHOLD + dominant_persona_code: str | None = None + dominant_persona_name: str | None = None + dominant_sessions: int = 0 + dominant_share: float | None = None + definition: str = ( + "종료 회기의 페르소나별 최다 노출 비중을 보여 주는 투명한 훈련 노출 지표이며, " + "공정성 평가나 임상진단이 아닙니다." + ) + + +class LearnerDashboardAchievement(BaseModel): + id: str + label: str + state: Literal["done", "available", "locked"] = "locked" + detail: str + + +class LearnerDashboardFeedbackItem(BaseModel): + session_id: str + persona_code: str + persona_name: str + session_no: int + stage: StageLabel + turn_seq: int + created_at: str + score: float | None = None + rapport: float | None = None + note: str + techniques: list[str] = Field(default_factory=list) + + +class LearnerDashboardResponse(BaseModel): + source: str = "runtime" + overview: LearnerDashboardOverview + growth: LearnerDashboardGrowth + persona_progress: list[LearnerDashboardPersonaProgress] = Field(default_factory=list) + training_exposure: LearnerDashboardTrainingExposure = Field( + default_factory=LearnerDashboardTrainingExposure + ) + achievements: list[LearnerDashboardAchievement] = Field(default_factory=list) + recent_feedback: list[LearnerDashboardFeedbackItem] = Field(default_factory=list) + message: str + + +def dashboard_overview( + sessions: list[InProcSession], + *, + visible_review_ready: dict[str, bool], + archived_sessions: int, +) -> LearnerDashboardOverview: + return LearnerDashboardOverview( + total_sessions=len(sessions), + completed_sessions=sum( + 1 + for sess in sessions + if sess.ended and any(t.speaker in ("counselor", "learner") for t in sess.turns) + ), + active_sessions=sum(1 for sess in sessions if not sess.ended), + review_ready_sessions=sum(1 for ready in visible_review_ready.values() if ready), + archived_sessions=archived_sessions, + learner_turns=sum( + 1 + for sess in sessions + for turn in learner_visible_turns(sess) + if turn.speaker == "counselor" + ), + client_turns=sum( + 1 + for sess in sessions + for turn in learner_visible_turns(sess) + if turn.speaker == "client" + ), + last_practiced_at=session_metrics.iso_datetime( + max((session_metrics.session_activity_time(sess) for sess in sessions), default=0.0) + ) + if sessions + else None, + ) + + +def _dashboard_growth_point(point: session_metrics.SessionGrowthPoint) -> LearnerDashboardGrowthPoint: + return LearnerDashboardGrowthPoint( + session_id=point.session_id, + session_no=point.session_no, + persona_code=point.persona_code, + stage=stage_label(point.stage), + started_at=point.started_at, + ended_at=point.ended_at, + score=point.score, + rapport=point.rapport, + technique_count=point.technique_count, + watch_count=point.watch_count, + ) + + +def dashboard_growth(sessions: list[InProcSession]) -> LearnerDashboardGrowth: + metrics = session_metrics.build_learner_growth( + sessions, + learner_label=lambda _learner_id: "나", + limit=1, + ) + if not metrics: + return LearnerDashboardGrowth() + item = metrics[0] + points = [_dashboard_growth_point(point) for point in item.points] + return LearnerDashboardGrowth( + first_score=item.first_score, + latest_score=item.latest_score, + score_delta=item.score_delta, + avg_score=item.avg_score, + avg_rapport=item.avg_rapport, + trend=item.trend, + evaluated_sessions=sum(1 for point in points if point.score is not None), + top_techniques=item.top_techniques, + points=points, + ) + + +def dashboard_persona_progress( + sessions: list[InProcSession], + review_ready: dict[str, bool], + learner_feedback_enabled: dict[str, bool] | None = None, +) -> list[LearnerDashboardPersonaProgress]: + grouped: dict[str, list[InProcSession]] = {} + for sess in sessions: + grouped.setdefault(sess.persona_code, []).append(sess) + + rows: list[LearnerDashboardPersonaProgress] = [] + for persona_code, items in grouped.items(): + ordered = sorted(items, key=session_metrics.session_activity_time) + latest = ordered[-1] + metric_sessions = ( + ordered + if learner_feedback_enabled is None + else [ + sess + for sess in ordered + if learner_feedback_enabled.get(sess.session_id, True) + ] + ) + metrics = session_metrics.build_learner_growth( + metric_sessions, + learner_label=lambda _learner_id: "나", + limit=1, + ) + growth = metrics[0] if metrics else None + rows.append( + LearnerDashboardPersonaProgress( + persona_code=persona_code, + persona_name=latest.persona.display_name, + sessions=len(ordered), + completed_sessions=sum( + 1 + for sess in ordered + if sess.ended and any(t.speaker in ("counselor", "learner") for t in sess.turns) + ), + active_sessions=sum(1 for sess in ordered if not sess.ended), + review_ready_sessions=sum( + 1 for sess in ordered if review_ready.get(sess.session_id, False) + ), + latest_at=session_metrics.iso_datetime( + session_metrics.session_activity_time(latest) + ), + latest_stage=stage_label(latest.state.stage), + latest_score=growth.latest_score if growth else None, + trend=growth.trend if growth else "insufficient", + rapport_percent=_rapport_percent(latest.state.rapport_credit), + ) + ) + return sorted(rows, key=lambda row: row.latest_at or "", reverse=True) + + +def dashboard_training_exposure( + sessions: list[InProcSession], +) -> LearnerDashboardTrainingExposure: + """Compute a transparent practice-exposure signal from ended sessions only.""" + + completed = [ + sess + for sess in sessions + if sess.ended and any(t.speaker in ("counselor", "learner") for t in sess.turns) + ] + counts = Counter(sess.persona_code for sess in completed) + if not counts: + return LearnerDashboardTrainingExposure() + + dominant_code, dominant_count = sorted( + counts.items(), + key=lambda item: (-item[1], item[0]), + )[0] + dominant_session = next( + sess for sess in completed if sess.persona_code == dominant_code + ) + total = len(completed) + share = dominant_count / total + if total < TRAINING_EXPOSURE_MIN_COMPLETED: + status: Literal["insufficient", "attention", "balanced"] = "insufficient" + label: Literal["판정 근거 부족", "훈련 집중 주의", "균형"] = "판정 근거 부족" + elif share >= TRAINING_EXPOSURE_ATTENTION_THRESHOLD: + status = "attention" + label = "훈련 집중 주의" + else: + status = "balanced" + label = "균형" + return LearnerDashboardTrainingExposure( + status=status, + label=label, + completed_sessions=total, + dominant_persona_code=dominant_code, + dominant_persona_name=dominant_session.persona.display_name, + dominant_sessions=dominant_count, + dominant_share=round(share, 4), + ) + + +def _achievement_state(done: bool, available: bool) -> Literal["done", "available", "locked"]: + if done: + return "done" + if available: + return "available" + return "locked" + + +def dashboard_achievements( + sessions: list[InProcSession], + review_ready: dict[str, bool], +) -> list[LearnerDashboardAchievement]: + completed = sum(1 for sess in sessions if sess.ended) + active = sum(1 for sess in sessions if not sess.ended) + review_count = sum(1 for ready in review_ready.values() if ready) + by_persona = Counter(sess.persona_code for sess in sessions) + max_persona_sessions = max(by_persona.values(), default=0) + persona_coverage = len(by_persona) + return [ + LearnerDashboardAchievement( + id="first_session_complete", + label="첫 회기 완료", + state=_achievement_state(completed >= 1, active >= 1), + detail="한 회기를 종료하면 리뷰와 워크시트 흐름이 열립니다.", + ), + LearnerDashboardAchievement( + id="review_ready", + label="리뷰 확인 가능", + state=_achievement_state(review_count >= 1, completed >= 1), + detail=f"현재 리뷰 가능한 회기 {review_count}건입니다.", + ), + LearnerDashboardAchievement( + id="persona_repeat", + label="같은 내담자 반복 연습", + state=_achievement_state(max_persona_sessions >= 3, max_persona_sessions >= 1), + detail="같은 페르소나를 반복하면 변화 추이를 더 안정적으로 볼 수 있습니다.", + ), + LearnerDashboardAchievement( + id="persona_coverage", + label="여러 페르소나 경험", + state=_achievement_state(persona_coverage >= 3, persona_coverage >= 2), + detail=f"현재 {persona_coverage}개 페르소나에서 연습 기록이 있습니다.", + ), + ] + + +def dashboard_feedback(sessions: list[InProcSession]) -> list[LearnerDashboardFeedbackItem]: + items: list[LearnerDashboardFeedbackItem] = [] + for item in session_metrics.recent_feedback_notes(sessions, limit=5): + score = item.get("score") + rapport = item.get("rapport") + items.append( + LearnerDashboardFeedbackItem( + session_id=str(item["session_id"]), + persona_code=str(item["persona_code"]), + persona_name=str(item["persona_name"]), + session_no=int(item["session_no"]), + stage=stage_label(item["stage"]), + turn_seq=int(item["turn_seq"]), + created_at=str(item["created_at"]), + score=float(score) if isinstance(score, (int, float)) else None, + rapport=float(rapport) if isinstance(rapport, (int, float)) else None, + note=str(item["note"]), + techniques=[str(label) for label in item.get("techniques", [])], + ) + ) + return items diff --git a/apps/api/app/session_evaluation_repository.py b/apps/api/app/session_evaluation_repository.py new file mode 100644 index 0000000..5a5b7df --- /dev/null +++ b/apps/api/app/session_evaluation_repository.py @@ -0,0 +1,275 @@ +"""DB 기반 세션 평가 저장소와 in-process 폴백.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Protocol + +from .db import acquire, get_pool +from .deps import Principal +from .runtime_policy import require_runtime_fallback_allowed, runtime_fallback_allowed +from .session_persistence_values import ( + _clean_role_masked_text, + _mask_json_text_values, + _ts, +) + + +_EVALUATION_CACHE: dict[str, dict[str, Any]] = {} + + +def _coerce_error_message(error: BaseException | str) -> str: + if isinstance(error, BaseException): + message = str(error).strip() + return message or error.__class__.__name__ + return str(error).strip() or "unknown session evaluation error" + + +class _SessionEvaluationResult(Protocol): + scope: str + stage: str + error: str | None + + def to_dict(self) -> dict[str, Any]: ... + + +@dataclass(slots=True) +class SessionEvaluationWrite: + session_id: str + learner_id: str + status: str + source: str + scope: str + stage: str + payload: dict[str, Any] + error: str | None = None + counselor_identity: str | None = None + client_identity: str | None = None + + @classmethod + def from_result( + cls, + *, + session_id: str, + learner_id: str, + result: _SessionEvaluationResult, + source: str = "engine", + counselor_identity: str | None = None, + client_identity: str | None = None, + ) -> "SessionEvaluationWrite": + return cls( + session_id=session_id, + learner_id=learner_id, + status="error" if result.error else "ready", + source=source, + scope=result.scope, + stage=result.stage, + payload=_mask_json_text_values( + result.to_dict(), + counselor_identity=counselor_identity, + client_identity=client_identity, + synthetic_generated=True, + ), + error=_clean_role_masked_text( + result.error, + counselor_identity=counselor_identity, + client_identity=client_identity, + synthetic_generated=True, + ), + counselor_identity=counselor_identity, + client_identity=client_identity, + ) + + @classmethod + def from_error( + cls, + *, + session_id: str, + learner_id: str, + scope: str, + stage: str, + error: BaseException | str, + source: str = "engine", + counselor_identity: str | None = None, + client_identity: str | None = None, + ) -> "SessionEvaluationWrite": + return cls( + session_id=session_id, + learner_id=learner_id, + status="error", + source=source, + scope=scope, + stage=stage, + payload={}, + error=_clean_role_masked_text( + _coerce_error_message(error), + counselor_identity=counselor_identity, + client_identity=client_identity, + synthetic_generated=True, + ), + counselor_identity=counselor_identity, + client_identity=client_identity, + ) + + def cache_record(self) -> dict[str, Any]: + return { + "status": self.status, + "source": self.source, + "scope": self.scope, + "stage": self.stage, + "payload": self.payload, + "error": self.error, + } + + +async def save_session_evaluation(write: SessionEvaluationWrite) -> bool: + write.payload = _mask_json_text_values( + write.payload, + counselor_identity=write.counselor_identity, + client_identity=write.client_identity, + synthetic_generated=True, + ) + write.error = _clean_role_masked_text( + write.error, + counselor_identity=write.counselor_identity, + client_identity=write.client_identity, + synthetic_generated=True, + ) + record = write.cache_record() + if runtime_fallback_allowed(): + existing = _EVALUATION_CACHE.get(write.session_id) + if _should_replace_evaluation_record(existing, record): + _EVALUATION_CACHE[write.session_id] = record + try: + get_pool() + async with acquire(role="learner", user_id=write.learner_id) as conn: + await conn.execute( + """ + INSERT INTO app.session_evaluation ( + session_id, status, source, scope, stage, payload, error, + created_at, updated_at + ) + VALUES ($1::uuid, $2, $3, $4, $5, $6::jsonb, $7, now(), now()) + ON CONFLICT (session_id) DO UPDATE SET + status = EXCLUDED.status, + source = EXCLUDED.source, + scope = EXCLUDED.scope, + stage = EXCLUDED.stage, + payload = EXCLUDED.payload, + error = EXCLUDED.error, + updated_at = now() + WHERE app.session_evaluation.status <> 'ready' + OR EXCLUDED.status = 'ready' + """, + write.session_id, + write.status, + write.source, + write.scope, + write.stage, + write.payload, + write.error, + ) + return True + except Exception: + require_runtime_fallback_allowed("session evaluation") + return False + + +async def load_session_evaluation( + session_id: str, + principal: Principal, +) -> tuple[dict[str, Any] | None, bool]: + try: + get_pool() + async with acquire( + role=principal.role.value, + user_id=principal.user_id, + cohort_ids=principal.cohort_ids, + ) as conn: + row = await conn.fetchrow( + """ + SELECT status, source, scope, stage, payload, error, updated_at + FROM app.session_evaluation + WHERE session_id = $1::uuid + """, + session_id, + ) + if row is None: + cached = ( + _EVALUATION_CACHE.get(session_id) + if runtime_fallback_allowed() + else None + ) + return cached, cached is None + return { + "status": row["status"], + "source": row["source"], + "scope": row["scope"], + "stage": row["stage"], + "payload": dict(row["payload"] or {}), + "error": row["error"], + "updated_at": _ts(row["updated_at"]), + }, True + except Exception: + require_runtime_fallback_allowed("session evaluation") + return _EVALUATION_CACHE.get(session_id), False + + +async def list_session_evaluations( + session_ids: list[str], + principal: Principal, +) -> tuple[dict[str, dict[str, Any]], bool]: + if not session_ids: + return {}, True + try: + get_pool() + async with acquire( + role=principal.role.value, + user_id=principal.user_id, + cohort_ids=principal.cohort_ids, + ) as conn: + rows = await conn.fetch( + """ + SELECT session_id::text AS session_id, status, source, scope, stage, payload, error, updated_at + FROM app.session_evaluation + WHERE session_id = ANY($1::uuid[]) + """, + session_ids, + ) + records = { + str(row["session_id"]): { + "status": row["status"], + "source": row["source"], + "scope": row["scope"], + "stage": row["stage"], + "payload": dict(row["payload"] or {}), + "error": row["error"], + "updated_at": _ts(row["updated_at"]), + } + for row in rows + } + cached_used = False + if runtime_fallback_allowed(): + for session_id in session_ids: + if session_id not in records and session_id in _EVALUATION_CACHE: + records[session_id] = _EVALUATION_CACHE[session_id] + cached_used = True + return records, not cached_used + except Exception: + require_runtime_fallback_allowed("session evaluation") + return { + session_id: _EVALUATION_CACHE[session_id] + for session_id in session_ids + if session_id in _EVALUATION_CACHE + }, False + + +def _should_replace_evaluation_record( + existing: dict[str, Any] | None, + replacement: dict[str, Any], +) -> bool: + """늦게 도착한 실패가 이미 확정된 ready 평가를 덮지 못하게 한다.""" + return not ( + str((existing or {}).get("status") or "") == "ready" + and str(replacement.get("status") or "") != "ready" + ) diff --git a/apps/api/app/session_persistence.py b/apps/api/app/session_persistence.py index 6553e30..7011f17 100644 --- a/apps/api/app/session_persistence.py +++ b/apps/api/app/session_persistence.py @@ -9,7 +9,7 @@ import time import uuid from dataclasses import dataclass from datetime import datetime, timezone -from typing import Any, Awaitable, Callable, Iterable, Literal, Protocol +from typing import Any, Awaitable, Callable, Iterable, Literal from .db import acquire, get_pool from .deps import Principal @@ -21,6 +21,12 @@ from .persona_repository import ( seed_persona_id, ) from .runtime_policy import require_runtime_fallback_allowed, runtime_fallback_allowed +from .session_persistence_values import ( + _clean_masked_text, + _clean_text, + _mask_json_text_values, + _ts, +) from .runtime_schema import ( REVIEW_SCHEMA_CONTRACT, runtime_schema_bootstrap_required, @@ -54,7 +60,6 @@ class CaseProgressUnavailableError(RuntimeError): """DB 전체 집계가 필요한 사례 진행 수치를 안전하게 읽지 못했다.""" -_EVALUATION_CACHE: dict[str, dict[str, Any]] = {} _CASE_WORKSHEET_CACHE: dict[str, dict[str, Any]] = {} _SESSION_REVIEW_STATUS_CACHE: dict[str, dict[str, Any]] = {} _SESSION_SHARE_CACHE: dict[str, dict[str, Any]] = {} @@ -128,13 +133,6 @@ class LiveCoachCreditExhausted(RuntimeError): """Raised when a learner tries to use live coaching without credits.""" -def _coerce_error_message(error: BaseException | str) -> str: - if isinstance(error, BaseException): - message = str(error).strip() - return message or error.__class__.__name__ - return str(error).strip() or "unknown session evaluation error" - - @dataclass(slots=True) class CaseContext: case_id: str @@ -152,103 +150,6 @@ class SessionSummaryWrite: open_threads: list[str] -class _SessionEvaluationResult(Protocol): - scope: str - stage: str - error: str | None - - def to_dict(self) -> dict[str, Any]: ... - - -@dataclass(slots=True) -class SessionEvaluationWrite: - session_id: str - learner_id: str - status: str - source: str - scope: str - stage: str - payload: dict[str, Any] - error: str | None = None - counselor_identity: str | None = None - client_identity: str | None = None - - @classmethod - def from_result( - cls, - *, - session_id: str, - learner_id: str, - result: _SessionEvaluationResult, - source: str = "engine", - counselor_identity: str | None = None, - client_identity: str | None = None, - ) -> "SessionEvaluationWrite": - return cls( - session_id=session_id, - learner_id=learner_id, - status="error" if result.error else "ready", - source=source, - scope=result.scope, - stage=result.stage, - payload=_mask_json_text_values( - result.to_dict(), - counselor_identity=counselor_identity, - client_identity=client_identity, - synthetic_generated=True, - ), - error=_clean_role_masked_text( - result.error, - counselor_identity=counselor_identity, - client_identity=client_identity, - synthetic_generated=True, - ), - counselor_identity=counselor_identity, - client_identity=client_identity, - ) - - @classmethod - def from_error( - cls, - *, - session_id: str, - learner_id: str, - scope: str, - stage: str, - error: BaseException | str, - source: str = "engine", - counselor_identity: str | None = None, - client_identity: str | None = None, - ) -> "SessionEvaluationWrite": - return cls( - session_id=session_id, - learner_id=learner_id, - status="error", - source=source, - scope=scope, - stage=stage, - payload={}, - error=_clean_role_masked_text( - _coerce_error_message(error), - counselor_identity=counselor_identity, - client_identity=client_identity, - synthetic_generated=True, - ), - counselor_identity=counselor_identity, - client_identity=client_identity, - ) - - def cache_record(self) -> dict[str, Any]: - return { - "status": self.status, - "source": self.source, - "scope": self.scope, - "stage": self.stage, - "payload": self.payload, - "error": self.error, - } - - _JOINED_CARD_COLUMNS = ( "card_persona_id", "card_code", @@ -272,14 +173,6 @@ _JOINED_CARD_COLUMNS = ( ) -def _ts(value: datetime | None) -> float | None: - if value is None: - return None - if value.tzinfo is None: - value = value.replace(tzinfo=timezone.utc) - return value.timestamp() - - def share_token_hash(token: str) -> str: return hashlib.sha256(token.encode("utf-8")).hexdigest() @@ -551,90 +444,6 @@ def _stage(stage: object) -> str: return getattr(stage, "value", str(stage)) -def _clean_text(value: Any) -> str | None: - if value is None: - return None - text = str(value).strip() - return text or None - - -def _clean_masked_text(value: Any) -> str | None: - text = _clean_text(value) - if text is None: - return None - masked = guardrail.mask_pii(text).text_masked.strip() - return masked or None - - -def _clean_role_masked_text( - value: Any, - *, - counselor_identity: str | None = None, - client_identity: str | None = None, - synthetic_generated: bool = False, -) -> str | None: - text = _clean_text(value) - if text is None: - return None - masked = guardrail.mask_role_identities( - text, - counselor_identity=counselor_identity, - client_identity=client_identity, - synthetic_generated=synthetic_generated, - ).text_masked.strip() - return masked or None - - -def _mask_json_text_values( - value: Any, - *, - counselor_identity: str | None = None, - client_identity: str | None = None, - synthetic_generated: bool = False, -) -> Any: - if isinstance(value, str): - return ( - _clean_role_masked_text( - value, - counselor_identity=counselor_identity, - client_identity=client_identity, - synthetic_generated=synthetic_generated, - ) - or "" - ) - if isinstance(value, dict): - return { - key: _mask_json_text_values( - child, - counselor_identity=counselor_identity, - client_identity=client_identity, - synthetic_generated=synthetic_generated, - ) - for key, child in value.items() - } - if isinstance(value, list): - return [ - _mask_json_text_values( - child, - counselor_identity=counselor_identity, - client_identity=client_identity, - synthetic_generated=synthetic_generated, - ) - for child in value - ] - if isinstance(value, tuple): - return [ - _mask_json_text_values( - child, - counselor_identity=counselor_identity, - client_identity=client_identity, - synthetic_generated=synthetic_generated, - ) - for child in value - ] - return value - - def _safe_float(value: Any) -> float | None: if isinstance(value, (int, float)): return float(value) @@ -1769,148 +1578,6 @@ async def ensure_review_tables() -> None: return -async def save_session_evaluation(write: SessionEvaluationWrite) -> bool: - write.payload = _mask_json_text_values( - write.payload, - counselor_identity=write.counselor_identity, - client_identity=write.client_identity, - synthetic_generated=True, - ) - write.error = _clean_role_masked_text( - write.error, - counselor_identity=write.counselor_identity, - client_identity=write.client_identity, - synthetic_generated=True, - ) - record = write.cache_record() - if runtime_fallback_allowed(): - existing = _EVALUATION_CACHE.get(write.session_id) - if _should_replace_evaluation_record(existing, record): - _EVALUATION_CACHE[write.session_id] = record - try: - get_pool() - async with acquire(role="learner", user_id=write.learner_id) as conn: - await conn.execute( - """ - INSERT INTO app.session_evaluation ( - session_id, status, source, scope, stage, payload, error, - created_at, updated_at - ) - VALUES ($1::uuid, $2, $3, $4, $5, $6::jsonb, $7, now(), now()) - ON CONFLICT (session_id) DO UPDATE SET - status = EXCLUDED.status, - source = EXCLUDED.source, - scope = EXCLUDED.scope, - stage = EXCLUDED.stage, - payload = EXCLUDED.payload, - error = EXCLUDED.error, - updated_at = now() - WHERE app.session_evaluation.status <> 'ready' - OR EXCLUDED.status = 'ready' - """, - write.session_id, - write.status, - write.source, - write.scope, - write.stage, - write.payload, - write.error, - ) - return True - except Exception: - require_runtime_fallback_allowed("session evaluation") - return False - - -async def load_session_evaluation( - session_id: str, - principal: Principal, -) -> tuple[dict[str, Any] | None, bool]: - try: - get_pool() - async with acquire( - role=principal.role.value, - user_id=principal.user_id, - cohort_ids=principal.cohort_ids, - ) as conn: - row = await conn.fetchrow( - """ - SELECT status, source, scope, stage, payload, error, updated_at - FROM app.session_evaluation - WHERE session_id = $1::uuid - """, - session_id, - ) - if row is None: - cached = ( - _EVALUATION_CACHE.get(session_id) - if runtime_fallback_allowed() - else None - ) - return cached, cached is None - return { - "status": row["status"], - "source": row["source"], - "scope": row["scope"], - "stage": row["stage"], - "payload": dict(row["payload"] or {}), - "error": row["error"], - "updated_at": _ts(row["updated_at"]), - }, True - except Exception: - require_runtime_fallback_allowed("session evaluation") - return _EVALUATION_CACHE.get(session_id), False - - -async def list_session_evaluations( - session_ids: list[str], - principal: Principal, -) -> tuple[dict[str, dict[str, Any]], bool]: - if not session_ids: - return {}, True - try: - get_pool() - async with acquire( - role=principal.role.value, - user_id=principal.user_id, - cohort_ids=principal.cohort_ids, - ) as conn: - rows = await conn.fetch( - """ - SELECT session_id::text AS session_id, status, source, scope, stage, payload, error, updated_at - FROM app.session_evaluation - WHERE session_id = ANY($1::uuid[]) - """, - session_ids, - ) - records = { - str(row["session_id"]): { - "status": row["status"], - "source": row["source"], - "scope": row["scope"], - "stage": row["stage"], - "payload": dict(row["payload"] or {}), - "error": row["error"], - "updated_at": _ts(row["updated_at"]), - } - for row in rows - } - cached_used = False - if runtime_fallback_allowed(): - for session_id in session_ids: - if session_id not in records and session_id in _EVALUATION_CACHE: - records[session_id] = _EVALUATION_CACHE[session_id] - cached_used = True - return records, not cached_used - except Exception: - require_runtime_fallback_allowed("session evaluation") - return { - session_id: _EVALUATION_CACHE[session_id] - for session_id in session_ids - if session_id in _EVALUATION_CACHE - }, False - - async def _fetch_session_runtime_rows( conn: Any, session_ids: list[str], @@ -2825,7 +2492,14 @@ async def list_case_summaries( SELECT s.case_id, count(*)::int AS total_sessions, - count(*) FILTER (WHERE s.ended_at IS NOT NULL)::int AS completed_sessions, + count(*) FILTER ( + WHERE s.ended_at IS NOT NULL + AND EXISTS ( + SELECT 1 FROM app.turns AS ct + WHERE ct.session_id = s.id + AND ct.speaker = 'counselor' + ) + )::int AS completed_sessions, COALESCE( sum( GREATEST( @@ -3702,12 +3376,3 @@ def _iso_dt(value: datetime | None) -> str: if value is None: return "" return value.astimezone(timezone.utc).isoformat() -def _should_replace_evaluation_record( - existing: dict[str, Any] | None, - replacement: dict[str, Any], -) -> bool: - """늦게 도착한 실패가 이미 확정된 ready 평가를 덮지 못하게 한다.""" - return not ( - str((existing or {}).get("status") or "") == "ready" - and str(replacement.get("status") or "") != "ready" - ) diff --git a/apps/api/app/session_persistence_values.py b/apps/api/app/session_persistence_values.py new file mode 100644 index 0000000..aa91b91 --- /dev/null +++ b/apps/api/app/session_persistence_values.py @@ -0,0 +1,100 @@ +"""세션 영속성에서 공유하는 정규화·마스킹 도우미.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from typing import Any + +from .services import guardrail + + +def _ts(value: datetime | None) -> float | None: + if value is None: + return None + if value.tzinfo is None: + value = value.replace(tzinfo=timezone.utc) + return value.timestamp() + + +def _clean_text(value: Any) -> str | None: + if value is None: + return None + text = str(value).strip() + return text or None + + +def _clean_masked_text(value: Any) -> str | None: + text = _clean_text(value) + if text is None: + return None + masked = guardrail.mask_pii(text).text_masked.strip() + return masked or None + + +def _clean_role_masked_text( + value: Any, + *, + counselor_identity: str | None = None, + client_identity: str | None = None, + synthetic_generated: bool = False, +) -> str | None: + text = _clean_text(value) + if text is None: + return None + masked = guardrail.mask_role_identities( + text, + counselor_identity=counselor_identity, + client_identity=client_identity, + synthetic_generated=synthetic_generated, + ).text_masked.strip() + return masked or None + + +def _mask_json_text_values( + value: Any, + *, + counselor_identity: str | None = None, + client_identity: str | None = None, + synthetic_generated: bool = False, +) -> Any: + if isinstance(value, str): + return ( + _clean_role_masked_text( + value, + counselor_identity=counselor_identity, + client_identity=client_identity, + synthetic_generated=synthetic_generated, + ) + or "" + ) + if isinstance(value, dict): + return { + key: _mask_json_text_values( + child, + counselor_identity=counselor_identity, + client_identity=client_identity, + synthetic_generated=synthetic_generated, + ) + for key, child in value.items() + } + if isinstance(value, list): + return [ + _mask_json_text_values( + child, + counselor_identity=counselor_identity, + client_identity=client_identity, + synthetic_generated=synthetic_generated, + ) + for child in value + ] + if isinstance(value, tuple): + return [ + _mask_json_text_values( + child, + counselor_identity=counselor_identity, + client_identity=client_identity, + synthetic_generated=synthetic_generated, + ) + for child in value + ] + return value diff --git a/apps/api/app/session_projection.py b/apps/api/app/session_projection.py new file mode 100644 index 0000000..3416fcd --- /dev/null +++ b/apps/api/app/session_projection.py @@ -0,0 +1,35 @@ +"""브라우저용 세션 projection이 공유하는 순수 도우미.""" + +from __future__ import annotations + +from datetime import datetime, timezone +from typing import cast + +from .services import state_machine +from .stage_contract import StageLabel, stage_label as _normalize_stage_label +from .store import InProcSession, TurnRecord + +LEARNER_VISIBLE_AI_ROLE = "counselor" + +# 전 주기(라포→정리) 기준 라포 만점: 마지막 전이 임계(개입→정리)를 100으로 본다. +_FULL_CYCLE_RAPPORT = max(state_machine.STAGE_ADVANCE_RAPPORT.values()) + + +def stage_label(stage: object) -> StageLabel: + return cast(StageLabel, _normalize_stage_label(stage)) + + +def iso(ts: float | None) -> str | None: + if ts is None: + return None + return datetime.fromtimestamp(ts, tz=timezone.utc).isoformat(timespec="seconds") + + +def learner_visible_turns(sess: InProcSession) -> list[TurnRecord]: + return sess.turns_visible_to(LEARNER_VISIBLE_AI_ROLE) + + +def _rapport_percent(credit: float) -> int: + if _FULL_CYCLE_RAPPORT <= 0: + return 0 + return max(0, min(100, int(round(float(credit or 0.0) / _FULL_CYCLE_RAPPORT * 100)))) diff --git a/apps/api/app/session_read_model.py b/apps/api/app/session_read_model.py index b7de784..c967ba7 100644 --- a/apps/api/app/session_read_model.py +++ b/apps/api/app/session_read_model.py @@ -628,7 +628,7 @@ def learner_summary( client_turn_count=client_turns, started_at=iso(sess.created_at) or "", ended_at=iso(sess.ended_at), - review_ready=review_ready, + review_ready=review_ready if learner_turns > 0 else False, learner_feedback_enabled=( sess.learner_feedback_enabled if learner_feedback_enabled is None @@ -647,7 +647,9 @@ def dashboard_overview( ) -> LearnerDashboardOverview: return LearnerDashboardOverview( total_sessions=len(sessions), - completed_sessions=sum(1 for sess in sessions if sess.ended), + completed_sessions=sum( + 1 for sess in sessions if sess.ended and any(t.speaker in ("counselor", "learner") for t in sess.turns) + ), active_sessions=sum(1 for sess in sessions if not sess.ended), review_ready_sessions=sum(1 for ready in visible_review_ready.values() if ready), archived_sessions=archived_sessions, @@ -742,7 +744,11 @@ def dashboard_persona_progress( persona_code=persona_code, persona_name=latest.persona.display_name, sessions=len(ordered), - completed_sessions=sum(1 for sess in ordered if sess.ended), + completed_sessions=sum( + 1 + for sess in ordered + if sess.ended and any(t.speaker in ("counselor", "learner") for t in sess.turns) + ), active_sessions=sum(1 for sess in ordered if not sess.ended), review_ready_sessions=sum( 1 for sess in ordered if review_ready.get(sess.session_id, False) @@ -768,7 +774,11 @@ def dashboard_training_exposure( ) -> LearnerDashboardTrainingExposure: """Compute a transparent practice-exposure signal from ended sessions only.""" - completed = [sess for sess in sessions if sess.ended] + completed = [ + sess + for sess in sessions + if sess.ended and any(t.speaker in ("counselor", "learner") for t in sess.turns) + ] counts = Counter(sess.persona_code for sess in completed) if not counts: return LearnerDashboardTrainingExposure() diff --git a/apps/api/app/session_turn_memory.py b/apps/api/app/session_turn_memory.py new file mode 100644 index 0000000..b3ac503 --- /dev/null +++ b/apps/api/app/session_turn_memory.py @@ -0,0 +1,19 @@ +"""세션 경로가 공유하는 내담자 턴 메모리 projection.""" + +from __future__ import annotations + +from .services import memory, orchestrator +from .store import InProcSession + + +def build_turn_memory( + sess: InProcSession, + recall: memory.RecallContext, + kb_cues: list[str], +) -> orchestrator.TurnMemory: + return orchestrator.TurnMemory( + recall_summary=recall.recall_summary, + pinned_facts=recall.pinned_facts, + recent_turns=sess.recent_turns(visible_to="client"), + kb_behavior_cues=kb_cues, + ) diff --git a/apps/api/app/test_calibration_transfer_store.py b/apps/api/app/test_calibration_transfer_store.py index 5da2efb..c57a0ec 100644 --- a/apps/api/app/test_calibration_transfer_store.py +++ b/apps/api/app/test_calibration_transfer_store.py @@ -627,6 +627,50 @@ class CalibrationTransferStoreTests(unittest.IsolatedAsyncioTestCase): evidence_turn_ids=(), ) + async def test_append_prediction_revision_normalizes_legacy_instrument_id(self) -> None: + principal = _principal(Role.LEARNER) + learner_id = UUID(principal.user_id) + session_id = uuid4() + history_id = uuid4() + revision_id = uuid4() + conn = AsyncMock() + conn.fetchrow.side_effect = [ + {"id": session_id, "learner_id": learner_id, "case_id": uuid4()}, + None, + None, + {"prediction_revision_id": str(revision_id), "revision_no": 1}, + ] + + @asynccontextmanager + async def fake_acquire(**_kwargs): + yield conn + + with patch.object(calibration_transfer_store.db, "acquire", fake_acquire): + res = await calibration_transfer_store.append_prediction_revision( + principal=principal, + submission_id=uuid4(), + prediction_revision_id=revision_id, + history_id=history_id, + session_id=session_id, + competency_id="competency.empathic-check", + practice_block_id="oas-g5-block-one", + scenario_variant_id="variant-1", + phrase_family_id="phrase-1", + revision_no=1, + supersedes_prediction_revision_id=None, + predicted_success_probability=0.7, + confidence=0.8, + recorded_sequence=1, + revision_reason="호환성 검증", + instrument_id="vignette.calibration-self-prediction", + instrument_version="1.0.0", + evidence_turn_ids=(), + ) + self.assertEqual(res["revision_no"], 1) + # Verify normalized instrument_id "calibration-mirror-g5" was passed to DB + call_args = conn.fetchrow.call_args_list[-1][0] + self.assertIn("calibration-mirror-g5", call_args) + async def test_evidence_refs_are_uuid_only(self) -> None: self.assertEqual( calibration_transfer_store._uuid_evidence( diff --git a/apps/api/app/test_calibration_validation_tdd.py b/apps/api/app/test_calibration_validation_tdd.py new file mode 100644 index 0000000..bb151d1 --- /dev/null +++ b/apps/api/app/test_calibration_validation_tdd.py @@ -0,0 +1,316 @@ +from __future__ import annotations + +import unittest +from unittest.mock import AsyncMock, patch +from uuid import UUID, uuid4 + +import asyncpg + +from fastapi import FastAPI +from fastapi.testclient import TestClient + +from .deps import Principal, Role +from .routes import calibration_transfer +from .services import calibration_transfer_store +from .services.calibration_transfer_store import ( + CalibrationTransferConflictError, + CalibrationTransferStateError, +) + + +def _principal(role: Role = Role.LEARNER, user_id: str | None = None) -> Principal: + return Principal( + user_id=user_id or str(uuid4()), + role=role, + cohort_ids=["g5-cohort"], + ) + + +class CalibrationValidationTDDTests(unittest.IsolatedAsyncioTestCase): + """Multi-angle TDD tests for OAS prediction revisions and lock boundaries.""" + + async def test_append_prediction_revision_rejects_out_of_bounds_probability(self) -> None: + """predicted_success_probability must be strictly bounded within [0.0, 1.0].""" + principal = _principal() + submission_id = uuid4() + history_id = uuid4() + session_id = uuid4() + + # > 1.0 should fail + with self.assertRaises(CalibrationTransferStateError) as ctx: + await calibration_transfer_store.append_prediction_revision( + principal=principal, + submission_id=submission_id, + prediction_revision_id=uuid4(), + history_id=history_id, + session_id=session_id, + competency_id="competency.empathic_reflection", + practice_block_id="oas-g5-block-1", + scenario_variant_id="variant-1", + phrase_family_id="family-1", + revision_no=1, + supersedes_prediction_revision_id=None, + predicted_success_probability=1.25, + confidence=0.8, + recorded_sequence=1, + revision_reason="Valid reason", + instrument_id="calibration-mirror-g5", + instrument_version="1.0.0", + ) + self.assertIn("predicted_success_probability", str(ctx.exception)) + + # < 0.0 should fail + with self.assertRaises(CalibrationTransferStateError) as ctx: + await calibration_transfer_store.append_prediction_revision( + principal=principal, + submission_id=submission_id, + prediction_revision_id=uuid4(), + history_id=history_id, + session_id=session_id, + competency_id="competency.empathic_reflection", + practice_block_id="oas-g5-block-1", + scenario_variant_id="variant-1", + phrase_family_id="family-1", + revision_no=1, + supersedes_prediction_revision_id=None, + predicted_success_probability=-0.05, + confidence=0.8, + recorded_sequence=1, + revision_reason="Valid reason", + instrument_id="calibration-mirror-g5", + instrument_version="1.0.0", + ) + self.assertIn("predicted_success_probability", str(ctx.exception)) + + async def test_append_prediction_revision_rejects_out_of_bounds_confidence(self) -> None: + """confidence must be strictly bounded within [0.0, 1.0].""" + principal = _principal() + submission_id = uuid4() + history_id = uuid4() + session_id = uuid4() + + with self.assertRaises(CalibrationTransferStateError) as ctx: + await calibration_transfer_store.append_prediction_revision( + principal=principal, + submission_id=submission_id, + prediction_revision_id=uuid4(), + history_id=history_id, + session_id=session_id, + competency_id="competency.empathic_reflection", + practice_block_id="oas-g5-block-1", + scenario_variant_id="variant-1", + phrase_family_id="family-1", + revision_no=1, + supersedes_prediction_revision_id=None, + predicted_success_probability=0.7, + confidence=1.5, + recorded_sequence=1, + revision_reason="Valid reason", + instrument_id="calibration-mirror-g5", + instrument_version="1.0.0", + ) + self.assertIn("confidence", str(ctx.exception)) + + async def test_append_prediction_revision_rejects_empty_or_whitespace_reason(self) -> None: + """Blank or whitespace-only revision_reason must be rejected.""" + principal = _principal() + + for blank in ("", " ", "\t\n"): + with self.assertRaises(CalibrationTransferStateError) as ctx: + await calibration_transfer_store.append_prediction_revision( + principal=principal, + submission_id=uuid4(), + prediction_revision_id=uuid4(), + history_id=uuid4(), + session_id=uuid4(), + competency_id="competency.empathic_reflection", + practice_block_id="oas-g5-block-1", + scenario_variant_id="variant-1", + phrase_family_id="family-1", + revision_no=1, + supersedes_prediction_revision_id=None, + predicted_success_probability=0.7, + confidence=0.8, + recorded_sequence=1, + revision_reason=blank, + instrument_id="calibration-mirror-g5", + instrument_version="1.0.0", + ) + self.assertIn("revision_reason", str(ctx.exception)) + + async def test_append_prediction_revision_blocks_locked_history(self) -> None: + """A locked prediction history must reject subsequent revision appends.""" + principal = _principal() + history_id = uuid4() + session_id = uuid4() + + # Mock db connection to simulate trigger-level lock rejection on history + mock_conn = AsyncMock() + mock_conn.fetchrow.side_effect = [ + # 1. _visible_session + {"id": session_id, "session_id": session_id, "learner_id": principal.user_id}, + # 2. _existing_by_submission + None, + # 3. SELECT * FROM app.calibration_prediction_history WHERE history_id = $1 + { + "history_id": history_id, + "session_id": session_id, + "learner_id": principal.user_id, + "competency_id": "competency.empathic_reflection", + "practice_block_id": "oas-g5-block-1", + "scenario_variant_id": "variant-1", + "phrase_family_id": "family-1", + }, + # 4. INSERT INTO app.calibration_prediction_revision (trigger raises lock error) + asyncpg.ObjectNotInPrerequisiteStateError( + "self-prediction cannot be revised after lock or external reveal" + ), + ] + + with patch("app.services.calibration_transfer_store.db.acquire") as mock_acquire: + mock_acquire.return_value.__aenter__.return_value = mock_conn + with self.assertRaises(CalibrationTransferStateError) as ctx: + await calibration_transfer_store.append_prediction_revision( + principal=principal, + submission_id=uuid4(), + prediction_revision_id=uuid4(), + history_id=history_id, + session_id=session_id, + competency_id="competency.empathic_reflection", + practice_block_id="oas-g5-block-1", + scenario_variant_id="variant-1", + phrase_family_id="family-1", + revision_no=2, + supersedes_prediction_revision_id=uuid4(), + predicted_success_probability=0.7, + confidence=0.8, + recorded_sequence=2, + revision_reason="Trying to revise locked prediction", + instrument_id="calibration-mirror-g5", + instrument_version="1.0.0", + ) + self.assertIn("locked", str(ctx.exception).lower()) + + async def test_append_prediction_lock_requires_learner_role(self) -> None: + """Only learners can append prediction locks.""" + principal = _principal(role=Role.TEACHER) + with self.assertRaises(CalibrationTransferStateError) as ctx: + await calibration_transfer_store.append_prediction_lock( + principal=principal, + submission_id=uuid4(), + lock_id=uuid4(), + history_id=uuid4(), + prediction_revision_id=uuid4(), + locked_sequence=1, + ) + self.assertIn("learner role", str(ctx.exception).lower()) + + async def test_append_prediction_lock_idempotent_replay(self) -> None: + """Replaying identical lock submission returns idempotent_replay=True.""" + principal = _principal(role=Role.LEARNER) + submission_id = uuid4() + history_id = uuid4() + lock_id = uuid4() + revision_id = uuid4() + + payload = { + "lock_id": str(lock_id), + "history_id": str(history_id), + "prediction_revision_id": str(revision_id), + "locked_sequence": 1, + "learner_id": principal.user_id, + } + content_hash = calibration_transfer_store._canonical_hash(payload) + + mock_conn = AsyncMock() + mock_conn.fetchrow.side_effect = [ + # 1. history lookup + { + "history_id": history_id, + "session_id": uuid4(), + "learner_id": principal.user_id, + }, + # 2. _existing_by_submission finds existing lock with identical content_hash + {"lock_id": lock_id, "content_hash": content_hash}, + ] + + with patch("app.services.calibration_transfer_store.db.acquire") as mock_acquire: + mock_acquire.return_value.__aenter__.return_value = mock_conn + result = await calibration_transfer_store.append_prediction_lock( + principal=principal, + submission_id=submission_id, + lock_id=lock_id, + history_id=history_id, + prediction_revision_id=revision_id, + locked_sequence=1, + ) + self.assertTrue(result.get("idempotent_replay")) + self.assertEqual(result.get("lock_id"), lock_id) + + async def test_append_prediction_lock_replayed_with_different_content_raises_conflict(self) -> None: + """Replaying lock submission with changed payload raises conflict error.""" + principal = _principal(role=Role.LEARNER) + submission_id = uuid4() + history_id = uuid4() + lock_id = uuid4() + revision_id = uuid4() + + mock_conn = AsyncMock() + mock_conn.fetchrow.side_effect = [ + # 1. history lookup + { + "history_id": history_id, + "session_id": uuid4(), + "learner_id": principal.user_id, + }, + # 2. _existing_by_submission finds existing lock but different content_hash + {"lock_id": lock_id, "content_hash": "different-hash-value"}, + ] + + with patch("app.services.calibration_transfer_store.db.acquire") as mock_acquire: + mock_acquire.return_value.__aenter__.return_value = mock_conn + with self.assertRaises(CalibrationTransferConflictError) as ctx: + await calibration_transfer_store.append_prediction_lock( + principal=principal, + submission_id=submission_id, + lock_id=lock_id, + history_id=history_id, + prediction_revision_id=revision_id, + locked_sequence=1, + ) + self.assertIn("different content", str(ctx.exception)) + + async def test_append_prediction_lock_conflict_raises_conflict_error(self) -> None: + """A conflicting concurrent lock raises CalibrationTransferConflictError.""" + principal = _principal(role=Role.LEARNER) + submission_id = uuid4() + history_id = uuid4() + lock_id = uuid4() + revision_id = uuid4() + + mock_conn = AsyncMock() + mock_conn.fetchrow.side_effect = [ + # 1. history lookup + { + "history_id": history_id, + "session_id": uuid4(), + "learner_id": principal.user_id, + }, + # 2. _existing_by_submission returns None (new submission) + None, + # 3. INSERT raises UniqueViolationError + asyncpg.UniqueViolationError("duplicate key value violates unique constraint"), + ] + + with patch("app.services.calibration_transfer_store.db.acquire") as mock_acquire: + mock_acquire.return_value.__aenter__.return_value = mock_conn + with self.assertRaises(CalibrationTransferConflictError): + await calibration_transfer_store.append_prediction_lock( + principal=principal, + submission_id=submission_id, + lock_id=lock_id, + history_id=history_id, + prediction_revision_id=revision_id, + locked_sequence=1, + ) + diff --git a/apps/api/app/test_dashboard_projection_tdd.py b/apps/api/app/test_dashboard_projection_tdd.py new file mode 100644 index 0000000..d4f3f16 --- /dev/null +++ b/apps/api/app/test_dashboard_projection_tdd.py @@ -0,0 +1,215 @@ +from __future__ import annotations + +import unittest +from unittest.mock import AsyncMock, patch +from uuid import uuid4 + +from app.deps import Principal, Role +from app.services import session_metrics, state_machine +from app.services.persona import P1, P2 +from app.session_dashboard_projection import ( + dashboard_overview, + dashboard_persona_progress, + dashboard_training_exposure, +) +from app.store import InProcSession, TurnRecord + + +def _make_session( + session_id: str, + session_no: int, + learner_id: str, + persona_code: str = "P1", + ended: bool = True, + turns: list[TurnRecord] | None = None, +) -> InProcSession: + persona = {P1.code: P1, P2.code: P2}.get(persona_code, P1) + state = state_machine.init_state(params=persona.openness_params()) + state.stage = state_machine.Stage.EXPLORE + sess = InProcSession( + session_id=session_id, + case_id=session_id, + learner_id=learner_id, + persona_code=persona.code, + theory_mode="humanistic", + persona=persona, + state=state, + session_no=session_no, + created_at=1000.0 + session_no * 10.0, + ended_at=1000.0 + session_no * 10.0 + 600 if ended else None, + ended=ended, + turns=[], + ) + if turns is not None: + sess.turns = turns + else: + sess.turns = [ + TurnRecord( + turn_seq=1, + speaker="counselor", + stage=state.stage.value, + text="어떤 점이 힘드신가요?", + text_masked="어떤 점이 힘드신가요?", + evaluation={ + "appropriateness": "pos", + "appropriateness_note": "공감적 반응", + "rapport_signal": 0.6, + "techniques": [{"label": "reflection"}], + }, + created_at=1000.0 + session_no * 10.0 + 5.0, + ) + ] + return sess + + +class DashboardProjectionTDDTests(unittest.TestCase): + """Multi-angle TDD tests for metrics projection invariants (REQ-010).""" + + def test_learner_growth_metrics_excludes_zero_turn_from_ended_sessions(self) -> None: + """LearnerGrowthMetrics.ended_sessions must exclude zero-turn sessions.""" + learner_id = "00000000-0000-0000-0000-000000000111" + + # Session 1: completed with turns + sess1 = _make_session("sess-1", 1, learner_id, ended=True) + # Session 2: zero-turn ended session + sess2 = _make_session("sess-2", 2, learner_id, ended=True, turns=[]) + # Session 3: active session + sess3 = _make_session("sess-3", 3, learner_id, ended=False) + + growth_list = session_metrics.build_learner_growth( + [sess1, sess2, sess3], + learner_label=lambda _uid: "홍길동", + ) + self.assertEqual(len(growth_list), 1) + growth = growth_list[0] + + self.assertEqual(growth.sessions, 3) + # Invariant: ended_sessions must equal completed sessions with dialogue (1), not 2 + self.assertEqual( + growth.ended_sessions, + 1, + "ended_sessions in LearnerGrowthMetrics must not count 0-turn sessions", + ) + + def test_training_exposure_multi_persona_isolation(self) -> None: + """Zero-turn sessions must not inflate exposure count across multiple personas.""" + learner_id = "00000000-0000-0000-0000-000000000111" + + # P1: 1 completed session, 1 zero-turn ended session + p1_completed = _make_session("sess-p1-1", 1, learner_id, persona_code="P1", ended=True) + p1_zero = _make_session("sess-p1-2", 2, learner_id, persona_code="P1", ended=True, turns=[]) + + # P2: 1 completed session + p2_completed = _make_session("sess-p2-1", 3, learner_id, persona_code="P2", ended=True) + + exposure = dashboard_training_exposure([p1_completed, p1_zero, p2_completed]) + self.assertEqual(exposure.completed_sessions, 2) + + # Persona progress table check + progress_rows = dashboard_persona_progress( + [p1_completed, p1_zero, p2_completed], + review_ready={}, + ) + p1_row = next(r for r in progress_rows if r.persona_code == "P1") + p2_row = next(r for r in progress_rows if r.persona_code == "P2") + + self.assertEqual(p1_row.sessions, 2) + self.assertEqual(p1_row.completed_sessions, 1) + self.assertEqual(p2_row.sessions, 1) + self.assertEqual(p2_row.completed_sessions, 1) + + +class TeacherDashboardProjectionTDDTests(unittest.IsolatedAsyncioTestCase): + """Teacher dashboard projection alignment for zero-turn sessions.""" + + async def test_teacher_dashboard_excludes_zero_turn_from_ended_sessions(self) -> None: + """Teacher dashboard top-level ended_sessions must exclude zero-turn ended sessions.""" + from app.routes import teacher + + principal = Principal( + user_id="teacher-1", + role=Role.TEACHER, + cohort_ids=["cohort-1"], + ) + learner_id = "learner-1" + sess_normal = _make_session("sess-1", 1, learner_id, ended=True) + sess_zero = _make_session("sess-2", 2, learner_id, ended=True, turns=[]) + + with ( + patch.object( + teacher.session_persistence, + "list_all_sessions", + AsyncMock(return_value=([sess_normal, sess_zero], True)), + ), + patch.object( + teacher.session_persistence, + "list_safety_alerts", + AsyncMock(return_value=([], True)), + ), + patch.object( + teacher.session_persistence, + "list_session_review_statuses", + AsyncMock(return_value=({}, True)), + ), + patch.object( + teacher.session_evaluation_repository, + "list_session_evaluations", + AsyncMock(return_value=({}, True)), + ), + ): + response = await teacher.teacher_dashboard(principal) + + self.assertEqual( + response.ended_sessions, + 1, + "Teacher dashboard ended_sessions must exclude 0-turn ended sessions", + ) + self.assertEqual( + response.learner_growth[0].ended_sessions, + 1, + "Learner growth in teacher dashboard must exclude 0-turn ended sessions", + ) + + async def test_teacher_learner_analysis_excludes_zero_turn_from_ended_sessions(self) -> None: + """Teacher learner analysis ended_sessions must exclude zero-turn ended sessions.""" + from app.routes import teacher + + principal = Principal( + user_id="teacher-1", + role=Role.TEACHER, + cohort_ids=["cohort-1"], + ) + learner_id = "learner-1" + sess_normal = _make_session("sess-1", 1, learner_id, ended=True) + sess_zero = _make_session("sess-2", 2, learner_id, ended=True, turns=[]) + + with ( + patch.object( + teacher.session_persistence, + "list_all_sessions", + AsyncMock(return_value=([sess_normal, sess_zero], True)), + ), + patch.object( + teacher.session_persistence, + "list_session_review_statuses", + AsyncMock(return_value=({}, True)), + ), + patch.object( + teacher.session_evaluation_repository, + "list_session_evaluations", + AsyncMock(return_value=({}, True)), + ), + ): + response = await teacher.learner_analysis(learner_id, principal) + + self.assertEqual( + response.ended_sessions, + 1, + "Teacher learner analysis ended_sessions must exclude 0-turn ended sessions", + ) + self.assertEqual( + response.summary.ended_sessions, + 1, + "Teacher learner analysis summary.ended_sessions must exclude 0-turn ended sessions", + ) + diff --git a/apps/api/app/test_eval_routes.py b/apps/api/app/test_eval_routes.py index 3e72350..16e1cf3 100644 --- a/apps/api/app/test_eval_routes.py +++ b/apps/api/app/test_eval_routes.py @@ -32,7 +32,7 @@ class ReevaluateSessionRouteTest(unittest.IsolatedAsyncioTestCase): patch.object(eval_routes, "_load_session_or_404", AsyncMock(return_value=sess)), patch.object(eval_routes.evaluator, "evaluate_session", AsyncMock(return_value=result)), patch.object( - eval_routes.session_persistence, + eval_routes.session_evaluation_repository, "save_session_evaluation", AsyncMock(return_value=True), ) as save_evaluation, @@ -68,7 +68,7 @@ class ReevaluateSessionRouteTest(unittest.IsolatedAsyncioTestCase): patch.object(eval_routes, "_load_session_or_404", AsyncMock(return_value=sess)), patch.object(eval_routes.evaluator, "evaluate_session", AsyncMock(return_value=result)), patch.object( - eval_routes.session_persistence, + eval_routes.session_evaluation_repository, "save_session_evaluation", AsyncMock(return_value=False), ), @@ -95,7 +95,7 @@ class ReevaluateSessionRouteTest(unittest.IsolatedAsyncioTestCase): AsyncMock(side_effect=eval_routes.EngineError("provider body: [NAME]")), ), patch.object( - eval_routes.session_persistence, + eval_routes.session_evaluation_repository, "save_session_evaluation", AsyncMock(return_value=True), ) as save_evaluation, @@ -135,7 +135,7 @@ class ReevaluateSessionRouteTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=result), ) as evaluate_session, patch.object( - eval_routes.session_persistence, + eval_routes.session_evaluation_repository, "save_session_evaluation", AsyncMock(return_value=True), ), @@ -167,7 +167,7 @@ class ReevaluateSessionRouteTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=result), ) as evaluate_session, patch.object( - eval_routes.session_persistence, + eval_routes.session_evaluation_repository, "save_session_evaluation", AsyncMock(return_value=True), ), @@ -343,7 +343,7 @@ class ReevaluateSessionRouteTest(unittest.IsolatedAsyncioTestCase): with ( patch.object(eval_routes, "_load_session_or_404", AsyncMock(return_value=sess)), patch.object( - eval_routes.session_persistence, + eval_routes.session_evaluation_repository, "load_session_evaluation", AsyncMock(return_value=(record, True)), ), diff --git a/apps/api/app/test_evaluation_persistence.py b/apps/api/app/test_evaluation_persistence.py index 38bc6e4..31d7d00 100644 --- a/apps/api/app/test_evaluation_persistence.py +++ b/apps/api/app/test_evaluation_persistence.py @@ -8,7 +8,7 @@ import unittest from unittest.mock import patch from .deps import Principal, Role -from . import session_persistence +from . import session_evaluation_repository, session_persistence from .routes import sessions from .services import evaluator, guardrail, session_metrics from .services import persona as persona_service @@ -256,7 +256,7 @@ class EvaluationPersistenceMappingTest(unittest.IsolatedAsyncioTestCase): turns_evaluated=2, ) - write = session_persistence.SessionEvaluationWrite.from_result( + write = session_evaluation_repository.SessionEvaluationWrite.from_result( session_id="session-1", learner_id="learner-1", result=result, @@ -306,7 +306,7 @@ class EvaluationPersistenceMappingTest(unittest.IsolatedAsyncioTestCase): error="보라별 평가 경고", ) - write = session_persistence.SessionEvaluationWrite.from_result( + write = session_evaluation_repository.SessionEvaluationWrite.from_result( session_id="session-privacy", learner_id="learner-privacy", result=result, @@ -330,7 +330,7 @@ class EvaluationPersistenceMappingTest(unittest.IsolatedAsyncioTestCase): supervisor_rationale="윤찬과 서연의 상호작용을 확인했다.", ) - write = session_persistence.SessionEvaluationWrite.from_result( + write = session_evaluation_repository.SessionEvaluationWrite.from_result( session_id="session-role-privacy", learner_id="learner-role-privacy", result=result, @@ -345,7 +345,7 @@ class EvaluationPersistenceMappingTest(unittest.IsolatedAsyncioTestCase): self.assertIn("[CLIENT]", blob) def test_session_evaluation_write_from_error_preserves_fallback_shape(self) -> None: - write = session_persistence.SessionEvaluationWrite.from_error( + write = session_evaluation_repository.SessionEvaluationWrite.from_error( session_id="session-1", learner_id="learner-1", scope="session_end", @@ -361,7 +361,7 @@ class EvaluationPersistenceMappingTest(unittest.IsolatedAsyncioTestCase): self.assertEqual(write.error, "engine timeout") def test_session_evaluation_write_from_error_names_empty_exception(self) -> None: - write = session_persistence.SessionEvaluationWrite.from_error( + write = session_evaluation_repository.SessionEvaluationWrite.from_error( session_id="session-1", learner_id="learner-1", scope="session_end", @@ -381,7 +381,7 @@ class EvaluationPersistenceMappingTest(unittest.IsolatedAsyncioTestCase): guardrail.set_ko_pii_recognizer(FakeKoRecognizer()) self.addCleanup(guardrail.set_ko_pii_recognizer, None) - write = session_persistence.SessionEvaluationWrite.from_error( + write = session_evaluation_repository.SessionEvaluationWrite.from_error( session_id="session-1", learner_id="learner-1", scope="session_end", @@ -405,7 +405,7 @@ class EvaluationPersistenceMappingTest(unittest.IsolatedAsyncioTestCase): guardrail.set_ko_pii_recognizer(FakeKoRecognizer()) self.addCleanup(guardrail.set_ko_pii_recognizer, None) conn = FakeEvaluationConn() - write = session_persistence.SessionEvaluationWrite( + write = session_evaluation_repository.SessionEvaluationWrite( session_id="11111111-1111-1111-1111-111111111111", learner_id="22222222-2222-2222-2222-222222222222", status="ready", @@ -417,10 +417,10 @@ class EvaluationPersistenceMappingTest(unittest.IsolatedAsyncioTestCase): ) with ( - patch.object(session_persistence, "get_pool", return_value=object()), - patch.object(session_persistence, "acquire", return_value=FakeAcquire(conn)), + patch.object(session_evaluation_repository, "get_pool", return_value=object()), + patch.object(session_evaluation_repository, "acquire", return_value=FakeAcquire(conn)), ): - saved = await session_persistence.save_session_evaluation(write) + saved = await session_evaluation_repository.save_session_evaluation(write) self.assertTrue(saved) _, args = conn.executed[0] @@ -439,20 +439,20 @@ class EvaluationPersistenceMappingTest(unittest.IsolatedAsyncioTestCase): error = {"status": "error", "error": "late timeout"} self.assertFalse( - session_persistence._should_replace_evaluation_record(ready, error) + session_evaluation_repository._should_replace_evaluation_record(ready, error) ) self.assertTrue( - session_persistence._should_replace_evaluation_record(error, ready) + session_evaluation_repository._should_replace_evaluation_record(error, ready) ) self.assertTrue( - session_persistence._should_replace_evaluation_record(ready, ready) + session_evaluation_repository._should_replace_evaluation_record(ready, ready) ) async def test_fallback_cache_keeps_ready_result_when_late_error_arrives(self) -> None: session_id = "11111111-1111-1111-1111-111111111111" - session_persistence._EVALUATION_CACHE[session_id] = {"status": "ready"} - self.addCleanup(session_persistence._EVALUATION_CACHE.pop, session_id, None) - write = session_persistence.SessionEvaluationWrite.from_error( + session_evaluation_repository._EVALUATION_CACHE[session_id] = {"status": "ready"} + self.addCleanup(session_evaluation_repository._EVALUATION_CACHE.pop, session_id, None) + write = session_evaluation_repository.SessionEvaluationWrite.from_error( session_id=session_id, learner_id="22222222-2222-2222-2222-222222222222", scope="session_end", @@ -461,14 +461,14 @@ class EvaluationPersistenceMappingTest(unittest.IsolatedAsyncioTestCase): ) with ( - patch.object(session_persistence, "runtime_fallback_allowed", return_value=True), - patch.object(session_persistence, "get_pool", side_effect=RuntimeError("offline")), - patch.object(session_persistence, "require_runtime_fallback_allowed"), + patch.object(session_evaluation_repository, "runtime_fallback_allowed", return_value=True), + patch.object(session_evaluation_repository, "get_pool", side_effect=RuntimeError("offline")), + patch.object(session_evaluation_repository, "require_runtime_fallback_allowed"), ): - saved = await session_persistence.save_session_evaluation(write) + saved = await session_evaluation_repository.save_session_evaluation(write) self.assertFalse(saved) - self.assertEqual(session_persistence._EVALUATION_CACHE[session_id]["status"], "ready") + self.assertEqual(session_evaluation_repository._EVALUATION_CACHE[session_id]["status"], "ready") def test_rebuild_turn_evaluation_restores_review_shape(self) -> None: rebuilt = session_persistence._rebuild_turn_evaluations( diff --git a/apps/api/app/test_feedback_policy.py b/apps/api/app/test_feedback_policy.py index 8376dcd..4097f3f 100644 --- a/apps/api/app/test_feedback_policy.py +++ b/apps/api/app/test_feedback_policy.py @@ -288,7 +288,7 @@ class FeedbackPolicyRouteTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=(sess, principal)), ), patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "load_session_evaluation", AsyncMock(), ) as evaluation_read, @@ -316,7 +316,7 @@ class FeedbackPolicyRouteTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=sess), ), patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "load_session_evaluation", AsyncMock(), ) as evaluation_read, @@ -583,7 +583,7 @@ class FeedbackPolicyRouteTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=([sess], True)), ), patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "load_session_evaluation", AsyncMock(return_value=({"status": "ready"}, True)), ), diff --git a/apps/api/app/test_learner_dashboard.py b/apps/api/app/test_learner_dashboard.py index 417d8d3..53acab6 100644 --- a/apps/api/app/test_learner_dashboard.py +++ b/apps/api/app/test_learner_dashboard.py @@ -6,7 +6,7 @@ import unittest from unittest.mock import AsyncMock, patch from .deps import Principal, Role -from . import session_read_model +from . import session_dashboard_projection from .routes import sessions from .services import state_machine from .services.persona import P1, P2, P3 @@ -88,14 +88,14 @@ def _exposure_sessions(persona_codes: list[str]) -> list[InProcSession]: class TrainingExposureMetricTest(unittest.TestCase): def test_zero_completed_sessions_is_insufficient(self) -> None: - metric = session_read_model.dashboard_training_exposure([]) + metric = session_dashboard_projection.dashboard_training_exposure([]) self.assertEqual(metric.label, "판정 근거 부족") self.assertEqual(metric.completed_sessions, 0) self.assertIsNone(metric.dominant_share) def test_one_completed_session_is_insufficient(self) -> None: - metric = session_read_model.dashboard_training_exposure( + metric = session_dashboard_projection.dashboard_training_exposure( _exposure_sessions([P1.code]) ) @@ -103,7 +103,7 @@ class TrainingExposureMetricTest(unittest.TestCase): self.assertEqual(metric.dominant_share, 1.0) def test_four_sessions_with_broad_exposure_is_balanced(self) -> None: - metric = session_read_model.dashboard_training_exposure( + metric = session_dashboard_projection.dashboard_training_exposure( _exposure_sessions([P1.code, P1.code, P2.code, P3.code]) ) @@ -111,7 +111,7 @@ class TrainingExposureMetricTest(unittest.TestCase): self.assertEqual(metric.dominant_share, 0.5) def test_dominant_share_at_seventy_five_percent_is_attention(self) -> None: - metric = session_read_model.dashboard_training_exposure( + metric = session_dashboard_projection.dashboard_training_exposure( _exposure_sessions([P1.code, P1.code, P1.code, P2.code]) ) @@ -120,7 +120,7 @@ class TrainingExposureMetricTest(unittest.TestCase): self.assertEqual(metric.dominant_share, 0.75) def test_tied_dominant_persona_is_deterministic_and_balanced(self) -> None: - metric = session_read_model.dashboard_training_exposure( + metric = session_dashboard_projection.dashboard_training_exposure( _exposure_sessions([P2.code, P1.code, P2.code, P1.code]) ) @@ -176,7 +176,7 @@ class LearnerDashboardTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=(owned_sessions, True)), ) as list_recent_sessions, patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "load_session_evaluation", AsyncMock(side_effect=load_review), ), @@ -249,7 +249,7 @@ class LearnerDashboardTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=([good_session, failed_session], True)), ), patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "load_session_evaluation", AsyncMock(return_value=({"status": "ready"}, True)), ), @@ -386,7 +386,7 @@ class LearnerDashboardTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=(archive_records, True)), ) as list_session_archives, patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "load_session_evaluation", AsyncMock(side_effect=load_review), ), @@ -438,7 +438,7 @@ class LearnerDashboardTest(unittest.IsolatedAsyncioTestCase): ), ) as set_session_archived, patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "load_session_evaluation", AsyncMock(return_value=({"status": "ready"}, True)), ), @@ -486,7 +486,7 @@ class LearnerDashboardTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=(None, True)), ) as set_session_archived, patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "load_session_evaluation", AsyncMock(return_value=(None, True)), ), @@ -510,6 +510,46 @@ class LearnerDashboardTest(unittest.IsolatedAsyncioTestCase): self.assertFalse(response.session.archived) self.assertIsNone(response.session.archived_at) + def test_zero_turn_ended_sessions_not_counted_in_completed_sessions(self) -> None: + """REQ-010: Zero-turn sessions ended without counselor dialogue are not counted in completed_sessions.""" + # 1 session with turns (completed) + normal_session = _session( + session_id="00000000-0000-0000-0000-00000000b991", + session_no=1, + persona_code=P1.code, + score="pos", + rapport=0.5, + technique="reflection", + created_at=1_000.0, + ) + normal_session.ended = True + + # 1 zero-turn session created and closed immediately + zero_turn_session = _session( + session_id="00000000-0000-0000-0000-00000000b992", + session_no=2, + persona_code=P1.code, + score="neutral", + rapport=0.0, + technique="none", + created_at=1_100.0, + ) + zero_turn_session.turns = [] # No turns + zero_turn_session.ended = True + + from app.session_dashboard_projection import dashboard_overview, dashboard_training_exposure + + overview = dashboard_overview( + [normal_session, zero_turn_session], + visible_review_ready={normal_session.session_id: True, zero_turn_session.session_id: False}, + archived_sessions=0, + ) + self.assertEqual(overview.total_sessions, 2) + self.assertEqual(overview.completed_sessions, 1) # Only 1 completed, not 2! + + exposure = dashboard_training_exposure([normal_session, zero_turn_session]) + self.assertEqual(exposure.completed_sessions, 1) + if __name__ == "__main__": unittest.main() diff --git a/apps/api/app/test_notifications.py b/apps/api/app/test_notifications.py index e2557b0..47aaeab 100644 --- a/apps/api/app/test_notifications.py +++ b/apps/api/app/test_notifications.py @@ -168,7 +168,7 @@ class NotificationTriggerTest(unittest.IsolatedAsyncioTestCase): AsyncMock(side_effect=RuntimeError("engine offline")), ), patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "save_session_evaluation", AsyncMock(return_value=True), ) as save_evaluation, @@ -194,7 +194,7 @@ class NotificationTriggerTest(unittest.IsolatedAsyncioTestCase): AsyncMock(side_effect=asyncio.TimeoutError()), ), patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "save_session_evaluation", AsyncMock(return_value=True), ) as save_evaluation, @@ -227,7 +227,7 @@ class NotificationTriggerTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=result), ) as evaluate_session, patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "save_session_evaluation", AsyncMock(return_value=True), ), diff --git a/apps/api/app/test_rbac_idor.py b/apps/api/app/test_rbac_idor.py index 24d5a2d..d05ee3b 100644 --- a/apps/api/app/test_rbac_idor.py +++ b/apps/api/app/test_rbac_idor.py @@ -229,7 +229,7 @@ class LearnerSessionIdorTest(unittest.IsolatedAsyncioTestCase): ), patch.object(sessions.turn_runtime, "runtime_fallback_allowed", return_value=True), patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "load_session_evaluation", AsyncMock(return_value=(None, False)), ) as load_session_evaluation, @@ -383,7 +383,7 @@ class LearnerSessionIdorTest(unittest.IsolatedAsyncioTestCase): ), patch.object(sessions.turn_runtime, "runtime_fallback_allowed", return_value=True), patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "load_session_evaluation", AsyncMock(return_value=(evaluation_record, True)), ), @@ -432,7 +432,7 @@ class LearnerSessionIdorTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=sess), ) as load_session, patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "load_session_evaluation", AsyncMock(return_value=(None, False)), ), diff --git a/apps/api/app/test_session_learning_producer.py b/apps/api/app/test_session_learning_producer.py index d92edff..cb89f51 100644 --- a/apps/api/app/test_session_learning_producer.py +++ b/apps/api/app/test_session_learning_producer.py @@ -212,7 +212,7 @@ class SessionLearningProducerTests(unittest.IsolatedAsyncioTestCase): patch.object( sessions.evaluator, "evaluate_session", AsyncMock(return_value=ready) ), - patch.object(sessions.session_persistence, "save_session_evaluation", save), + patch.object(sessions.session_evaluation_repository, "save_session_evaluation", save), patch.object( sessions.session_learning_producer, "produce_session_learning_artifacts", diff --git a/apps/api/app/test_session_read_model.py b/apps/api/app/test_session_read_model.py index 85e7d38..261c014 100644 --- a/apps/api/app/test_session_read_model.py +++ b/apps/api/app/test_session_read_model.py @@ -5,7 +5,8 @@ from __future__ import annotations import unittest from datetime import datetime, timezone -from .session_read_model import iso, session_calendar_date +from .session_projection import iso +from .session_read_model import session_calendar_date from .services.session_metrics import iso_datetime diff --git a/apps/api/app/test_session_turn_persistence.py b/apps/api/app/test_session_turn_persistence.py index c46274f..a9e94d1 100644 --- a/apps/api/app/test_session_turn_persistence.py +++ b/apps/api/app/test_session_turn_persistence.py @@ -11,7 +11,7 @@ from unittest.mock import AsyncMock, patch from fastapi import HTTPException from .config import settings -from . import session_persistence, turn_runtime +from . import session_evaluation_repository, session_persistence, turn_runtime from .contracts.engine_gateway import EngineGatewaySseLineDecoder from .deps import Principal, Role from .engine_client import EngineError, GenerateResponse @@ -214,7 +214,7 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): durable_evaluations: dict[str, dict[str, object]] = {} async def save_evaluation( - write: session_persistence.SessionEvaluationWrite, + write: session_evaluation_repository.SessionEvaluationWrite, ) -> bool: durable_evaluations[write.session_id] = write.cache_record() return True @@ -226,7 +226,7 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): AsyncMock(side_effect=asyncio.TimeoutError), ), patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "save_session_evaluation", save_evaluation, ), @@ -375,7 +375,7 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=old_session), ), patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "load_session_evaluation", AsyncMock(return_value=(old_evaluation, True)), ), @@ -415,7 +415,7 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): AsyncMock(side_effect=EngineError("controlled evaluator 500")), ), patch.object( - eval_routes.session_persistence, + eval_routes.session_evaluation_repository, "save_session_evaluation", retry_save, ), @@ -2574,7 +2574,7 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=(saved_payload, True)), ), patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "load_session_evaluation", AsyncMock(return_value=(None, False)), ), @@ -2681,7 +2681,7 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=(None, False)), ), patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "load_session_evaluation", AsyncMock(return_value=(raw_record, True)), ), @@ -2721,7 +2721,7 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): ), ] ) - error_record = session_persistence.SessionEvaluationWrite.from_error( + error_record = session_evaluation_repository.SessionEvaluationWrite.from_error( session_id=sess.session_id, learner_id=principal.user_id, scope="session_end", @@ -2741,7 +2741,7 @@ class SessionTurnPersistenceTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=(None, False)), ), patch.object( - sessions.session_persistence, + sessions.session_evaluation_repository, "load_session_evaluation", AsyncMock(return_value=(error_record, True)), ), diff --git a/apps/api/app/test_teacher_dashboard.py b/apps/api/app/test_teacher_dashboard.py index 43eeade..e4b04c0 100644 --- a/apps/api/app/test_teacher_dashboard.py +++ b/apps/api/app/test_teacher_dashboard.py @@ -182,7 +182,7 @@ class TeacherDashboardGrowthTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=({}, True)), ), patch.object( - teacher.session_persistence, + teacher.session_evaluation_repository, "list_session_evaluations", AsyncMock(return_value=({}, True)), ), @@ -238,7 +238,7 @@ class TeacherDashboardGrowthTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=({}, True)), ), patch.object( - teacher.session_persistence, + teacher.session_evaluation_repository, "list_session_evaluations", AsyncMock(return_value=({}, True)), ), @@ -303,7 +303,7 @@ class TeacherDashboardGrowthTest(unittest.IsolatedAsyncioTestCase): ), ), patch.object( - teacher.session_persistence, + teacher.session_evaluation_repository, "list_session_evaluations", AsyncMock(return_value=({}, True)), ), @@ -357,7 +357,7 @@ class TeacherDashboardGrowthTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=({}, True)), ), patch.object( - teacher.session_persistence, + teacher.session_evaluation_repository, "list_session_evaluations", AsyncMock(return_value=({}, True)), ), @@ -411,7 +411,7 @@ class TeacherDashboardGrowthTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=({}, True)), ), patch.object( - teacher.session_persistence, + teacher.session_evaluation_repository, "list_session_evaluations", AsyncMock(return_value=({sess.session_id: evaluation_record}, True)), ), @@ -459,7 +459,7 @@ class TeacherDashboardGrowthTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=({}, True)), ), patch.object( - teacher.session_persistence, + teacher.session_evaluation_repository, "list_session_evaluations", AsyncMock(return_value=({}, True)), ), @@ -507,7 +507,7 @@ class TeacherDashboardGrowthTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=({}, True)), ), patch.object( - teacher.session_persistence, + teacher.session_evaluation_repository, "list_session_evaluations", AsyncMock(return_value=({}, True)), ), @@ -539,7 +539,7 @@ class TeacherDashboardGrowthTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=sess), ), patch.object( - teacher.session_persistence, + teacher.session_evaluation_repository, "load_session_evaluation", AsyncMock(return_value=({"status": "ready"}, True)), ), @@ -604,7 +604,7 @@ class TeacherDashboardGrowthTest(unittest.IsolatedAsyncioTestCase): AsyncMock(return_value=sess), ), patch.object( - teacher.session_persistence, + teacher.session_evaluation_repository, "load_session_evaluation", AsyncMock(return_value=({"status": "error", "error": "timeout"}, True)), ), @@ -680,7 +680,7 @@ class TeacherDashboardGrowthTest(unittest.IsolatedAsyncioTestCase): ), ), patch.object( - teacher.session_persistence, + teacher.session_evaluation_repository, "list_session_evaluations", AsyncMock(return_value=({}, True)), ), diff --git a/apps/api/engine.public.err.log.2026-08-07T2200.bak b/apps/api/engine.public.err.log.2026-08-07T2200.bak deleted file mode 100644 index e3756ee..0000000 --- a/apps/api/engine.public.err.log.2026-08-07T2200.bak +++ /dev/null @@ -1,4 +0,0 @@ -INFO: Started server process [8752] -INFO: Waiting for application startup. -INFO: Application startup complete. -INFO: Uvicorn running on http://127.0.0.1:9099 (Press CTRL+C to quit) diff --git a/apps/api/engine.public.err.log.20260809-160042.bak b/apps/api/engine.public.err.log.20260809-160042.bak deleted file mode 100644 index 1e204ed..0000000 --- a/apps/api/engine.public.err.log.20260809-160042.bak +++ /dev/null @@ -1,4 +0,0 @@ -INFO: Started server process [69132] -INFO: Waiting for application startup. -INFO: Application startup complete. -INFO: Uvicorn running on http://127.0.0.1:9099 (Press CTRL+C to quit) diff --git a/apps/api/engine.public.out.log.2026-08-07T2200.bak b/apps/api/engine.public.out.log.2026-08-07T2200.bak deleted file mode 100644 index bed851f..0000000 --- a/apps/api/engine.public.out.log.2026-08-07T2200.bak +++ /dev/null @@ -1,2235 +0,0 @@ -INFO: 127.0.0.1:12361 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12391 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:12417 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:12419 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12447 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:12448 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12447 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:12448 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13919 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13921 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:13919 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13921 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:13921 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:13919 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2324 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2326 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:2324 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2326 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:2326 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:2324 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1936 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1938 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:1936 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1938 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:1938 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:1936 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2515 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2517 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:2515 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2517 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:2517 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:2515 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5042 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5044 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:5044 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:9384 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9386 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:9386 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 503 Service Unavailable -INFO: 127.0.0.1:3640 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3642 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3642 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4486 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4488 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4488 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7583 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7585 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7585 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5229 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5231 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5231 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8403 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8405 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8405 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4233 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4235 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4235 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8820 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8822 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8822 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6315 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6317 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6317 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2609 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2611 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2611 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8155 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8163 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8163 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10088 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1365 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1365 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3192 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11841 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11841 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11848 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11841 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1559 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1559 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4251 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4251 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4367 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4367 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14383 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14383 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6213 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6213 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4052 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4052 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2264 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2264 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3006 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3006 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9079 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9079 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1575 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9201 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1153 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1153 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6911 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6911 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1566 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1566 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13316 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13316 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9071 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9071 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9230 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9230 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7978 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7978 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7900 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7900 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1080 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1080 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7537 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7537 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3557 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2746 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14650 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14650 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12876 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12876 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7659 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7659 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10421 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10421 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14411 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14411 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11510 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11510 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8706 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8706 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14057 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14057 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4515 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1905 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1905 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3444 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6598 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6598 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12030 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12030 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12032 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12030 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8058 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8060 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8060 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12376 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12378 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12378 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9979 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9981 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9981 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10148 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10150 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10150 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9202 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9204 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9204 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7324 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7326 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7326 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5072 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5074 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5074 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14729 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14731 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14731 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1518 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1520 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1520 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3442 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3444 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3444 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3987 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3990 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3990 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2919 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2921 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2921 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3438 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3440 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3440 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5240 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5242 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5242 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5443 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5445 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5445 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14666 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14668 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14668 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14428 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14430 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14430 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14057 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14059 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14059 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12578 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12580 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12580 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4073 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1899 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1899 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4520 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4522 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4522 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11067 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11069 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11069 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11474 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11476 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11476 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1084 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1086 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1086 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8805 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8807 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8807 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14309 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14311 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14311 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7520 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7522 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7522 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8047 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1828 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1828 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6639 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6642 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6642 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11144 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11146 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11146 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8704 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8707 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8707 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3210 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3212 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3212 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1544 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1546 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1546 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7751 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13324 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13324 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6489 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6491 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6491 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5264 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5266 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5266 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5893 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5896 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5896 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3642 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3644 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3644 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8110 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8112 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8112 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7352 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7354 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7354 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4647 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4649 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4649 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8896 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8898 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8898 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1212 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1214 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1214 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12028 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12030 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12030 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3246 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3248 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3248 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10374 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10376 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10376 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14759 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14761 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14761 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6216 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6218 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6218 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7974 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7976 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7976 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8208 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8210 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8210 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13985 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13987 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13987 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3600 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3602 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3602 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4007 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4009 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4009 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5736 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5738 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5738 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14307 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14309 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14309 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4865 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4867 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4867 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1467 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1469 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1469 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11836 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11838 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11838 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7466 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7468 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7468 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5760 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5762 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5762 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1088 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1090 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1090 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11841 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11843 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11843 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8997 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9000 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9000 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8241 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11891 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11891 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7262 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7264 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7264 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9720 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9722 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9722 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6411 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6413 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6413 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8726 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8728 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8728 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6032 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6034 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6034 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14746 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14748 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14748 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11496 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11498 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11498 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12075 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12077 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12077 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2680 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2683 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2683 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7950 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7952 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7952 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6454 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6456 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6456 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7893 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7895 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7895 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3136 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3139 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3139 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6917 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6920 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6920 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3693 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3695 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3693 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3695 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13403 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13405 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13405 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10904 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6047 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6049 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6049 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10346 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10348 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10348 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12700 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12702 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12702 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1790 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2467 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2467 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1143 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1145 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1145 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4508 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4511 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4511 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9680 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9682 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9682 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13649 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13651 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13651 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8363 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8365 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8365 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6085 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6087 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6087 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13347 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13349 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13349 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13214 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13216 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13216 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4028 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4030 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4030 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7369 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7371 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7371 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2335 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2337 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2337 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12482 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12484 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12484 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1043 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1046 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1046 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13701 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13703 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13703 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6626 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6628 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6628 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12391 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12393 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12393 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8099 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8103 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8103 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5113 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5115 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5115 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11140 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11142 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11142 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2514 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2518 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2518 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1344 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1346 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1346 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5748 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5750 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5750 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6032 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6034 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6034 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1471 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1473 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1473 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8579 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8581 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8581 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5425 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5427 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5427 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7374 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7376 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7376 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2528 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7385 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7385 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4265 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14889 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14889 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6957 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3398 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3398 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13661 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3398 - "GET /ready?provider=agy_cli&model=gemini-3.6-flash-high&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3099 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3099 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14775 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14775 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9594 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9594 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6744 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6744 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8033 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8033 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10459 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10459 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4034 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4034 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2622 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2622 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2959 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2959 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2560 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2560 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2893 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2893 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7441 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7441 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7776 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7776 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7782 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7782 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8133 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8133 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8138 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8138 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8455 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8455 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4826 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4826 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5147 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5147 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9325 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9325 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10135 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10135 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14425 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14425 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1328 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1328 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7116 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7116 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4280 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4280 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6659 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4229 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2652 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2652 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7847 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7847 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5856 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5856 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3933 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3933 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6623 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6623 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9254 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9254 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9597 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9597 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1790 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1790 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2364 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2364 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5798 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1068 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9303 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9303 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2677 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2677 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12178 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12178 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1899 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1899 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9737 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9737 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4742 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4742 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7907 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7907 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11042 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11042 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4820 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4820 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5724 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5724 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6099 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6099 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4412 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4412 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10470 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10470 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12548 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12548 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3556 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3556 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4748 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4748 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5377 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5377 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8084 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8084 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3460 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3460 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4921 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4921 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4648 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4648 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5173 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5173 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3242 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3242 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6001 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6001 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8706 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8706 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5641 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5641 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12651 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12651 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5322 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5322 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5435 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5435 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13128 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13128 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1993 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1993 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2699 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2699 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5790 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5790 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11837 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11837 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12339 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12339 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14516 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14516 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9264 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9264 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9213 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9213 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4074 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4074 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1610 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1610 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2377 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2377 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1268 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1268 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7060 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7060 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3810 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3810 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2641 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2641 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6383 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6383 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8277 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8277 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5228 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5228 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12848 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12848 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2206 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2206 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2406 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2406 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3903 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3903 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10818 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10818 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7714 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7714 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10676 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10676 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7320 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7320 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4693 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4693 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8071 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8071 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6734 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6734 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2790 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2790 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8501 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8501 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13246 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13246 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2575 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2575 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4046 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4046 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9734 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9734 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12867 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12867 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10083 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10083 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2295 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2295 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11838 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11838 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4569 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4569 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6535 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6535 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7455 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7455 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4920 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4920 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13424 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13424 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5298 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5298 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2042 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2042 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7451 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7451 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2635 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2635 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7445 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7445 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10522 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10522 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13891 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13891 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1790 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1790 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13320 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13320 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2236 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2236 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4340 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4340 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1752 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1752 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5088 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5088 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12778 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12778 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13841 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13841 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14562 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14562 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10147 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10147 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7266 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7266 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7386 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7386 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1269 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1269 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6127 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6127 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6419 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6419 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4130 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4130 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6518 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6518 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10759 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10759 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1901 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1901 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8836 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8836 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11010 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11010 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5365 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5365 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8191 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8191 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6434 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6434 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6274 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6274 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6363 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10996 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7339 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1411 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1411 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1411 - "POST /v1/stream HTTP/1.1" 200 OK -INFO: 127.0.0.1:3272 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3272 - "POST /v1/stream HTTP/1.1" 200 OK -INFO: 127.0.0.1:5574 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5574 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5574 - "POST /v1/stream HTTP/1.1" 200 OK -INFO: 127.0.0.1:2786 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:2786 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10380 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:5298 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6031 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9437 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:2786 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6031 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6928 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6928 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6930 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6928 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10380 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:10380 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10380 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:10380 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:10380 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:10380 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:10380 - "DELETE /session/3055be0f-01ad-44b5-b786-810dfbf794f0 HTTP/1.1" 200 OK -INFO: 127.0.0.1:10380 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10380 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:10380 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:10380 - "DELETE /session/24d33776-ffea-422c-834a-47cd0d63961a HTTP/1.1" 200 OK -INFO: 127.0.0.1:1429 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1429 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1429 - "DELETE /session/3d495cc7-4269-451a-8b39-d56f982f11fe HTTP/1.1" 200 OK -INFO: 127.0.0.1:1429 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1429 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1429 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1429 - "DELETE /session/af029997-677b-4c5d-8014-0bebb9c39e53 HTTP/1.1" 200 OK -INFO: 127.0.0.1:3498 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4342 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:2283 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2285 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2285 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4342 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4342 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4342 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4342 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:8495 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8495 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4966 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10991 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:8705 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8707 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8707 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10991 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:10991 - "DELETE /session/63f1a2b8-0545-437d-a0c1-7e80e4ba75db HTTP/1.1" 200 OK -INFO: 127.0.0.1:5775 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5775 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:2913 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:5775 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:2913 - "DELETE /session/db9ba9ee-9ef4-4e14-9828-02b265515d15 HTTP/1.1" 200 OK -INFO: 127.0.0.1:7717 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7717 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7717 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7717 - "DELETE /session/2b6db500-b556-4446-824e-a3e3d8c6b81b HTTP/1.1" 200 OK -INFO: 127.0.0.1:5775 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7327 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:8843 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8845 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8845 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14375 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14377 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14377 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9021 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9024 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9024 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14186 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14188 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14188 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4845 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4848 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4848 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5246 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6328 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6328 - "DELETE /session/alliance-1c6c3e10-aad7-5fc3-ba68-5e739b5fa629-independent-observer HTTP/1.1" 200 OK -INFO: 127.0.0.1:6328 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6328 - "DELETE /session/alliance-c8dee912-4e88-5338-90d7-43a698642c34-independent-observer HTTP/1.1" 200 OK -INFO: 127.0.0.1:6328 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6328 - "DELETE /session/alliance-c588278e-73ef-565b-b681-6ed5ee49049d-independent-observer HTTP/1.1" 200 OK -INFO: 127.0.0.1:1890 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1892 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1892 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6328 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6328 - "DELETE /session/alliance-3c5fd531-45c2-5b67-9dd9-e245015398ce-client-report HTTP/1.1" 200 OK -INFO: 127.0.0.1:12964 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6328 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6328 - "DELETE /session/alliance-d24fa4b0-f4cc-55a1-a895-b4aced7426fa-client-report HTTP/1.1" 200 OK -INFO: 127.0.0.1:6328 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6328 - "DELETE /session/alliance-0afd7bf7-7523-5f4b-b54d-dff767b5b49c-client-report HTTP/1.1" 200 OK -INFO: 127.0.0.1:6328 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6328 - "DELETE /session/alliance-4ed629b7-3015-5d0e-a9ad-9fbfee2b2add-client-report HTTP/1.1" 200 OK -INFO: 127.0.0.1:6328 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6328 - "DELETE /session/alliance-ff87c9fa-e5da-5d1c-bcf0-428976a6fe7a-independent-observer HTTP/1.1" 200 OK -INFO: 127.0.0.1:13807 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5627 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7824 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1255 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1255 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3639 - "DELETE /session/b74a95f5-aa29-457c-8dcd-db03115c0ca0 HTTP/1.1" 200 OK -INFO: 127.0.0.1:11854 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7860 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7860 - "DELETE /session/alliance-20e31374-d16a-5011-95d2-b81c73a50a98-independent-observer HTTP/1.1" 200 OK -INFO: 127.0.0.1:14538 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14539 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14539 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7860 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7860 - "DELETE /session/alliance-7e7ae241-9ccc-5da1-be33-1cd9748c7444-independent-observer HTTP/1.1" 200 OK -INFO: 127.0.0.1:7860 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7860 - "DELETE /session/alliance-2c292438-a576-5008-b8bf-2d832a0b5a5b-independent-observer HTTP/1.1" 200 OK -INFO: 127.0.0.1:7860 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7860 - "DELETE /session/alliance-7e523f68-a251-5c18-88ec-a71e21a3be05-client-report HTTP/1.1" 200 OK -INFO: 127.0.0.1:7860 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7860 - "DELETE /session/alliance-7f870c65-1c7b-5378-9aa0-5ba901821776-client-report HTTP/1.1" 200 OK -INFO: 127.0.0.1:7860 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7860 - "DELETE /session/alliance-3773ae37-485e-57f7-a0a8-36b1f17ce407-client-report HTTP/1.1" 200 OK -INFO: 127.0.0.1:4269 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4269 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7860 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7860 - "DELETE /session/alliance-2294978e-3874-5ecf-a7ef-df0acf0ea92d-client-report HTTP/1.1" 200 OK -INFO: 127.0.0.1:4269 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7860 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7860 - "DELETE /session/alliance-43fc2fa6-2453-5863-8ba2-21d7692499ab-independent-observer HTTP/1.1" 200 OK -INFO: 127.0.0.1:4269 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4269 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4269 - "DELETE /session/4224799d-bd5a-4058-8038-0cef905407bd HTTP/1.1" 200 OK -INFO: 127.0.0.1:9354 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3887 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3890 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3890 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5479 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5479 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:5479 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:5479 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:5479 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:5479 - "DELETE /session/6d92bae7-1e7a-4aa5-8cac-6d72fd409ba1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:6490 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6490 - "DELETE /session/alliance-8152b939-4a9a-42bc-92c6-4226b679cb1a-client-report HTTP/1.1" 200 OK -INFO: 127.0.0.1:6491 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6490 - "DELETE /session/alliance-8152b939-4a9a-42bc-92c6-4226b679cb1a-independent-observer HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "DELETE /session/alliance-da2640ec-3898-5ae2-9e7f-138be3ed8849-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:6484 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "DELETE /session/alliance-99b123f1-1a6d-5599-9752-3f4b2541b544-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "DELETE /session/alliance-99b123f1-1a6d-5599-9752-3f4b2541b544-independent-observer-attempt-2 HTTP/1.1" 200 OK -INFO: 127.0.0.1:13556 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "DELETE /session/alliance-dcfb8833-0f7c-5b57-a4d2-9f2eee45dff0-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4191 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4191 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "DELETE /session/alliance-8ed466e9-5c54-5b6e-bb97-0d1e4f9cf096-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4191 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4191 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:12656 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12658 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12658 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "DELETE /session/alliance-f47483d5-3507-517e-a554-761f3f8b2b05-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4191 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4191 - "DELETE /session/45ef2f63-d52c-47e4-97cb-98f3091bf27e HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "DELETE /session/alliance-f47483d5-3507-517e-a554-761f3f8b2b05-client-report-attempt-2 HTTP/1.1" 200 OK -INFO: 127.0.0.1:6773 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6773 - "DELETE /session/alliance-bb7d04ee-35fb-45d9-a4e0-c6c578aa8ebe-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:6774 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6773 - "DELETE /session/alliance-bb7d04ee-35fb-45d9-a4e0-c6c578aa8ebe-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:6768 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "DELETE /session/alliance-db7e4ef7-e85d-565e-b160-e27a5bbc0e07-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "DELETE /session/alliance-325d03d6-3911-54b1-980e-9196629a8b9e-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4228 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4228 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7874 - "DELETE /session/alliance-20b56cad-f7bc-5b05-9a66-0b2057c55feb-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4228 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4228 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4228 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4228 - "DELETE /session/55389bd3-8ea2-4c9d-a625-a71fbb63d431 HTTP/1.1" 200 OK -INFO: 127.0.0.1:10760 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:10760 - "DELETE /session/alliance-adc45a38-1a53-4c59-bb41-72fdd65bdac0-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:10759 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:10759 - "DELETE /session/alliance-adc45a38-1a53-4c59-bb41-72fdd65bdac0-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:10759 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:10759 - "DELETE /session/alliance-adc45a38-1a53-4c59-bb41-72fdd65bdac0-client-report-attempt-2 HTTP/1.1" 200 OK -INFO: 127.0.0.1:10754 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1082 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4089 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "DELETE /session/alliance-19c44830-2313-56f1-9007-7eb39b980043-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4092 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4092 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "DELETE /session/alliance-bd185646-8bb6-5e59-94aa-b19a2570002d-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "DELETE /session/alliance-6eb6846e-36e0-54f5-a866-8dd03f69657e-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "DELETE /session/alliance-d6ce19c0-8575-5920-b1be-ea77ca43b370-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "DELETE /session/alliance-1549f3b3-f92e-5a08-b7cd-f0f9c8e5c5d2-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "POST /v1/generate HTTP/1.1" 502 Bad Gateway -INFO: 127.0.0.1:4352 - "DELETE /session/alliance-7b6c39fa-b916-557a-9ab5-3cc36f38064b-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "DELETE /session/alliance-7b6c39fa-b916-557a-9ab5-3cc36f38064b-client-report-attempt-2 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "DELETE /session/alliance-181edfc0-faf9-5f60-b122-7261f2c487c6-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "DELETE /session/alliance-181edfc0-faf9-5f60-b122-7261f2c487c6-client-report-attempt-2 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4352 - "DELETE /session/alliance-bfec51b4-b4a7-5a19-9772-24b4bd41004d-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:6393 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6395 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6395 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8118 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8121 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8121 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10300 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10304 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10304 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5564 - "DELETE /session/b70894f9-9251-431b-9eac-a645c9f79cfb HTTP/1.1" 200 OK -INFO: 127.0.0.1:9817 - "DELETE /session/8ade39da-dcea-4f92-b47f-b72490e8dcd9 HTTP/1.1" 200 OK -INFO: 127.0.0.1:12920 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12923 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12923 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4431 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4434 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4434 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8920 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8922 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8922 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11021 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9871 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9871 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:9871 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:9871 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:9871 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:9871 - "DELETE /session/8854fd64-2e63-45da-a97f-30e7ba8de1f2 HTTP/1.1" 200 OK -INFO: 127.0.0.1:7427 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7427 - "DELETE /session/alliance-d85212f3-b9e1-4c73-8809-d34d7b473371-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:7426 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7426 - "DELETE /session/alliance-d85212f3-b9e1-4c73-8809-d34d7b473371-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:7421 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4382 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4384 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4384 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4396 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4396 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4396 - "DELETE /session/0c0ad4fe-5a3a-4803-a6c3-62b2c4e5117d HTTP/1.1" 200 OK -INFO: 127.0.0.1:13100 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:8089 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13637 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:13637 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:13637 - "DELETE /session/9cea118f-8930-4732-8819-48449dda6322 HTTP/1.1" 200 OK -INFO: 127.0.0.1:8223 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8225 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8225 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10623 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3267 - "DELETE /session/34f47553-e4b5-4f66-be87-7a0828baa05b HTTP/1.1" 200 OK -INFO: 127.0.0.1:9738 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9741 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9741 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6914 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6914 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:5450 - "DELETE /session/2ecc4ecd-f453-4236-a01a-0a99b2669b55 HTTP/1.1" 200 OK -INFO: 127.0.0.1:6914 - "DELETE /session/582b5902-7781-494b-9d82-83f21430c750 HTTP/1.1" 200 OK -INFO: 127.0.0.1:6914 - "DELETE /session/49c99f59-0e7c-4342-827e-c32a351a79e3 HTTP/1.1" 200 OK -INFO: 127.0.0.1:6914 - "DELETE /session/a249e1b8-1a99-4c19-85da-036f07655be0 HTTP/1.1" 200 OK -INFO: 127.0.0.1:6914 - "DELETE /session/db66f277-2f93-416b-a8d2-d6cdf516ff32 HTTP/1.1" 200 OK -INFO: 127.0.0.1:5443 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:9583 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9585 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9585 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12492 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8479 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8479 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4624 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4626 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4626 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6424 - "DELETE /session/93397b1e-98d9-41e4-a345-33e488e0c2d7 HTTP/1.1" 200 OK -INFO: 127.0.0.1:11014 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11016 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11016 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12860 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12862 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12862 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8332 - "DELETE /session/68c0f95b-b626-4360-8f50-f0718398084e HTTP/1.1" 200 OK -INFO: 127.0.0.1:7880 - "DELETE /session/dc7b8712-7be8-4f63-8d97-2441f494db96 HTTP/1.1" 200 OK -INFO: 127.0.0.1:6314 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6316 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6316 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6916 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8709 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8712 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8712 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14378 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14378 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14378 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:14378 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:14378 - "DELETE /session/85db5d45-8bf5-41e3-a3e6-39d86f95b277 HTTP/1.1" 200 OK -INFO: 127.0.0.1:2055 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:10972 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1932 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1932 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1676 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1678 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1678 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3413 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13048 - "DELETE /session/681c8298-493f-4580-9d95-21b1ca1d27f7 HTTP/1.1" 200 OK -INFO: 127.0.0.1:10706 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13989 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13989 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6459 - "DELETE /session/9dae8749-871c-45dd-bc21-60174ab0a7bd HTTP/1.1" 200 OK -INFO: 127.0.0.1:6099 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6099 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7578 - "DELETE /session/3b352299-4c00-468a-a87c-a7282b8dd054 HTTP/1.1" 200 OK -INFO: 127.0.0.1:6099 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6099 - "DELETE /session/7b9671f1-80a8-4871-ab49-7e7a0ad21eee HTTP/1.1" 200 OK -INFO: 127.0.0.1:10994 - "DELETE /session/78b24e48-6ca6-43c5-86c6-5499e6e7c947 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4603 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6536 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6536 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6536 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6536 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6536 - "DELETE /session/e3e07503-7033-41ea-808b-89bab82d86f1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:9924 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9926 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9926 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6201 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7584 - "DELETE /session/b8d0fdc8-7272-4714-a9d6-1d24187a1604 HTTP/1.1" 200 OK -INFO: 127.0.0.1:14318 - "DELETE /session/79a31eba-43b9-48e9-8cde-8c7f63f2391f HTTP/1.1" 200 OK -INFO: 127.0.0.1:7172 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7174 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7174 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9170 - "DELETE /session/43d79497-4fd8-4000-9c87-8f3572686ec1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:3957 - "DELETE /session/e491c900-c5ae-4607-ac7c-4ba2eb3d1c84 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4178 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4178 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:2429 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2431 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2431 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4178 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4178 - "DELETE /session/3b94e0ec-10a2-4ab6-916a-e64ccbb76598 HTTP/1.1" 200 OK -INFO: 127.0.0.1:5382 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4738 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4740 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4740 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11959 - "DELETE /session/e94d4620-7ed9-4b3a-8568-f8846b2aa2e2 HTTP/1.1" 200 OK -INFO: 127.0.0.1:10302 - "DELETE /session/9154a342-06a7-4e0e-8cf7-373f5be3cf9e HTTP/1.1" 200 OK -INFO: 127.0.0.1:2615 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2617 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2617 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9996 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9996 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:9996 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:9996 - "DELETE /session/3c834ffd-af5b-4a37-8fd3-ad0690deb165 HTTP/1.1" 200 OK -INFO: 127.0.0.1:1693 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6164 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9975 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9977 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9977 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10449 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5603 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2526 - "DELETE /session/816479fd-cd0b-44a1-8c48-8b0336d15d41 HTTP/1.1" 200 OK -INFO: 127.0.0.1:8194 - "DELETE /session/77e8f8aa-d0b3-4336-92fc-426eec6d5dc4 HTTP/1.1" 200 OK -INFO: 127.0.0.1:11854 - "DELETE /session/7b9b9b17-c2ee-4012-9714-e27f9fba8204 HTTP/1.1" 200 OK -INFO: 127.0.0.1:11854 - "DELETE /session/df11cc84-c244-4032-99c4-14d45d0824f2 HTTP/1.1" 200 OK -INFO: 127.0.0.1:6737 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6739 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6739 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9105 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9772 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9772 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5627 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8499 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8501 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8501 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11863 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11865 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11865 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2914 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3596 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9659 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12189 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12191 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12191 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6721 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4723 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4728 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13268 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13270 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13270 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6345 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12947 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12949 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12949 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4006 - "DELETE /session/bad00886-54e8-4c01-8e39-2958151fcd60 HTTP/1.1" 200 OK -INFO: 127.0.0.1:14741 - "DELETE /session/979c2b89-dc54-4b71-89a4-27f524ba1b38 HTTP/1.1" 200 OK -INFO: 127.0.0.1:6534 - "DELETE /session/6a16c2e5-07bd-49b3-957d-d42cc647fd04 HTTP/1.1" 200 OK -INFO: 127.0.0.1:6534 - "DELETE /session/3c53e5a1-ef24-4239-af4a-e251b8eb236b HTTP/1.1" 200 OK -INFO: 127.0.0.1:10129 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2506 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10129 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10129 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10129 - "GET /v1/capabilities?provider=claude_cli&force=false HTTP/1.1" 200 OK -INFO: 127.0.0.1:10129 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9858 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9860 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9860 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3910 - "DELETE /session/0ce75e32-f845-4798-b5b6-527ee4bb26e4 HTTP/1.1" 200 OK -INFO: 127.0.0.1:12337 - "DELETE /session/68871059-df5b-4bef-9dc2-27a634365dc2 HTTP/1.1" 200 OK -INFO: 127.0.0.1:9364 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9366 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9366 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6740 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6742 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6742 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6911 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1437 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1439 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1439 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9115 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9119 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9119 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11367 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11369 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11369 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1526 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1528 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1528 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5616 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5618 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5618 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10256 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4267 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4267 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14890 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14892 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14892 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3802 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8598 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8600 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8600 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11084 - "DELETE /session/c7613596-4a78-4c4e-821c-05d8db498781 HTTP/1.1" 200 OK -INFO: 127.0.0.1:8333 - "DELETE /session/aece92cb-767b-44d8-984a-2f80220fb5e2 HTTP/1.1" 200 OK -INFO: 127.0.0.1:11420 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7388 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7390 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7390 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4511 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4513 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4513 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13540 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13542 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13542 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6924 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12338 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9049 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9052 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9052 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2466 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6347 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6347 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6347 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6347 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6347 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6347 - "DELETE /session/4d98dada-928d-45cc-b389-4043837d0b28 HTTP/1.1" 200 OK -INFO: 127.0.0.1:7610 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7610 - "DELETE /session/alliance-7c6b1b76-139b-4fdf-80c4-4771bb92c793-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:7611 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7610 - "DELETE /session/alliance-7c6b1b76-139b-4fdf-80c4-4771bb92c793-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:7604 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:5791 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5791 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5791 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4119 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4121 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4121 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5791 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:5791 - "DELETE /session/811d904f-64eb-4602-81d4-e4f7309feb03 HTTP/1.1" 200 OK -INFO: 127.0.0.1:1139 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1141 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1141 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2973 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2997 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2997 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11023 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11025 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11025 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4599 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4601 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4601 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1086 - "DELETE /session/98c232a8-cc99-490f-805d-647dc076d9ad HTTP/1.1" 200 OK -INFO: 127.0.0.1:10842 - "DELETE /session/24cffcce-725a-41de-b8fe-4efe9581bc81 HTTP/1.1" 200 OK -INFO: 127.0.0.1:2444 - "DELETE /session/c9de90fe-2836-4b58-8dbb-ef34b517aee8 HTTP/1.1" 200 OK -INFO: 127.0.0.1:2444 - "DELETE /session/e716bcdc-ea76-437c-960e-505e53e4a6c3 HTTP/1.1" 200 OK -INFO: 127.0.0.1:3611 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3611 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4848 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4850 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4850 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4224 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2276 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12781 - "DELETE /session/3075f2d4-70d3-4451-927b-5d71aa71e5b9 HTTP/1.1" 200 OK -INFO: 127.0.0.1:9875 - "DELETE /session/db433dad-f5b2-4203-9b6f-63c0fb833627 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4218 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7452 - "DELETE /session/07f3e12f-4466-4ff1-8b04-8a31f0f32c32 HTTP/1.1" 200 OK -INFO: 127.0.0.1:7452 - "DELETE /session/d9e2938a-e534-42e4-849e-ca46865dc59f HTTP/1.1" 200 OK -INFO: 127.0.0.1:2946 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2946 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2946 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2946 - "GET /v1/capabilities?provider=claude_cli&force=false HTTP/1.1" 200 OK -INFO: 127.0.0.1:2946 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2946 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2946 - "GET /v1/capabilities?provider=claude_cli&force=false HTTP/1.1" 200 OK -INFO: 127.0.0.1:2946 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4721 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4724 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4724 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2785 - "DELETE /session/cb504047-7b3c-46b7-914d-4f96ce103841 HTTP/1.1" 200 OK -INFO: 127.0.0.1:6262 - "DELETE /session/06ebd272-20a0-4039-a37f-a4aa17e581e6 HTTP/1.1" 200 OK -INFO: 127.0.0.1:2281 - "DELETE /session/a9268905-958c-485e-bb38-9238dd8b5496 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4005 - "DELETE /session/9473486e-f4e9-4c41-b88c-6c084687f534 HTTP/1.1" 200 OK -INFO: 127.0.0.1:11256 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11258 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11258 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6504 - "DELETE /session/40bda8ff-e71f-4b19-828b-86d3b5a74309 HTTP/1.1" 200 OK -INFO: 127.0.0.1:12850 - "DELETE /session/849ef067-4c8d-4b86-be7c-4bb559394e5d HTTP/1.1" 200 OK -INFO: 127.0.0.1:10609 - "DELETE /session/3e6408e4-37b7-464b-a6e5-81ce7d069451 HTTP/1.1" 200 OK -INFO: 127.0.0.1:10609 - "DELETE /session/d214d3e3-f9bd-4b82-9f3b-9bfc68f1ba7e HTTP/1.1" 200 OK -INFO: 127.0.0.1:4440 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4440 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4440 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4440 - "GET /v1/capabilities?provider=claude_cli&force=false HTTP/1.1" 200 OK -INFO: 127.0.0.1:4440 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4440 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4440 - "GET /v1/capabilities?provider=claude_cli&force=false HTTP/1.1" 200 OK -INFO: 127.0.0.1:4440 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12589 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12591 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12591 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11974 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11976 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11976 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4503 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7922 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7922 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1402 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1404 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1404 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1967 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11044 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3723 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3725 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3725 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3156 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14352 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14354 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14354 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14315 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14317 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14317 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6759 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6762 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6762 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10506 - "DELETE /session/fedd1067-dfb3-407a-9d58-efa0862a39c4 HTTP/1.1" 200 OK -INFO: 127.0.0.1:9372 - "DELETE /session/49a8357e-8090-4d1a-b8f2-bede46a5eb9f HTTP/1.1" 200 OK -INFO: 127.0.0.1:2110 - "DELETE /session/c4b0125b-11ac-406a-b833-f12b000fa2a4 HTTP/1.1" 200 OK -INFO: 127.0.0.1:10938 - "DELETE /session/8802e967-5114-46f3-b4c0-5c8b0820f9c0 HTTP/1.1" 200 OK -INFO: 127.0.0.1:5550 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5552 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5552 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13812 - "DELETE /session/5b917843-82b0-4bce-a00c-da87f5ee730b HTTP/1.1" 200 OK -INFO: 127.0.0.1:8578 - "DELETE /session/c6854c1b-edc3-44bb-93f9-0c5036031034 HTTP/1.1" 200 OK -INFO: 127.0.0.1:6582 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6159 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6159 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7708 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7735 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10985 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12193 - "DELETE /session/3f116ec2-e6d5-4889-a8ac-e8afc89fa4cd HTTP/1.1" 200 OK -INFO: 127.0.0.1:3275 - "DELETE /session/fe64e4d4-10e8-4bd8-96da-f01791609339 HTTP/1.1" 200 OK -INFO: 127.0.0.1:12001 - "DELETE /session/0e33e442-09d5-4649-bbdd-cb82a3ee2125 HTTP/1.1" 200 OK -INFO: 127.0.0.1:12001 - "DELETE /session/a04b080c-6f16-49d2-af54-9297247858b1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:6293 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6293 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6293 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6293 - "GET /v1/capabilities?provider=claude_cli&force=false HTTP/1.1" 200 OK -INFO: 127.0.0.1:6293 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6293 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6293 - "GET /v1/capabilities?provider=claude_cli&force=false HTTP/1.1" 200 OK -INFO: 127.0.0.1:6293 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6547 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6549 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6557 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6557 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4397 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7635 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7638 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7638 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4573 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5144 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5146 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5146 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2422 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2424 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2424 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1958 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1960 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1960 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11426 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11428 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11428 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9396 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9398 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4642 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8707 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8711 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8711 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5379 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3557 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3559 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3559 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4464 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4468 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4468 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11447 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11449 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11449 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6459 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6464 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6464 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6239 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6241 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6241 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8093 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8095 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8095 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10887 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10889 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10889 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1998 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14787 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14787 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9339 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9341 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9341 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6754 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14448 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14451 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14451 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6309 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6311 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6311 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4817 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4819 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4819 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6911 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6423 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6423 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14757 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11112 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10809 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1549 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1551 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1558 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1192 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1192 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1192 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1192 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6188 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6188 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10625 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12464 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12464 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5555 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5557 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5557 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5557 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11838 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11840 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11840 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6914 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6918 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6918 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3791 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11876 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11878 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11878 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4273 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7272 - "DELETE /session/78b8d537-165c-44ee-8b6b-21464ee73e41 HTTP/1.1" 200 OK -INFO: 127.0.0.1:7272 - "DELETE /session/3020452b-3b41-468f-9120-3f375c6e9624 HTTP/1.1" 200 OK -INFO: 127.0.0.1:10312 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10314 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10314 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3145 - "DELETE /session/1bc3c1cf-f3dd-44af-b72b-1956cb1e639c HTTP/1.1" 200 OK -INFO: 127.0.0.1:3145 - "DELETE /session/c43176dc-c278-4a3d-9b8f-da9411d4f263 HTTP/1.1" 200 OK -INFO: 127.0.0.1:1064 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10282 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10282 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3691 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3693 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3693 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11004 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11006 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11006 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2155 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2158 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2158 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6305 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13900 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13922 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6053 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3003 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4441 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4443 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4443 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7407 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6577 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6579 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6579 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14582 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14582 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:14582 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:14573 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:14573 - "DELETE /session/alliance-9eb41a86-b4eb-4762-a492-243e1308daad-independent-observer HTTP/1.1" 200 OK -INFO: 127.0.0.1:14582 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:14582 - "DELETE /session/alliance-9eb41a86-b4eb-4762-a492-243e1308daad-client-report HTTP/1.1" 200 OK -INFO: 127.0.0.1:14582 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:14582 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:14582 - "DELETE /session/93c87560-f428-4834-8150-394a67949bd7 HTTP/1.1" 200 OK -INFO: 127.0.0.1:13797 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:13797 - "DELETE /session/alliance-dd0cb507-73f8-4114-b0cd-84de5415c603-client-report HTTP/1.1" 200 OK -INFO: 127.0.0.1:13798 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:13798 - "DELETE /session/alliance-dd0cb507-73f8-4114-b0cd-84de5415c603-independent-observer HTTP/1.1" 200 OK -INFO: 127.0.0.1:13794 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4877 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4879 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4879 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4615 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2497 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14419 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:14417 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:14418 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:14416 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4943 - "GET /ready HTTP/1.1" 200 OK -INFO: 127.0.0.1:13425 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13427 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13427 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10584 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14332 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14332 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:14332 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:10518 - "GET /ready?provider=claude_cli HTTP/1.1" 200 OK -INFO: 127.0.0.1:14332 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:14332 - "DELETE /session/alliance-56439ece-6bf5-462e-920f-3de35b2992dd-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:1215 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:14332 - "DELETE /session/alliance-56439ece-6bf5-462e-920f-3de35b2992dd-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:14332 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:14332 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:14332 - "DELETE /session/137c8b3e-8559-4db5-bcb9-ed0bcf20b5dc HTTP/1.1" 200 OK -INFO: 127.0.0.1:2659 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:10518 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:13173 - "GET /ready?provider=claude_cli HTTP/1.1" 200 OK -INFO: 127.0.0.1:1959 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1961 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1961 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13584 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6420 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6420 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6420 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3440 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3440 - "DELETE /session/alliance-776b52e3-dd67-4571-b3a3-4d2e1716ae4d-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:8554 - "GET /ready?provider=claude_cli&force=true HTTP/1.1" 200 OK -INFO: 127.0.0.1:6420 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6420 - "DELETE /session/alliance-776b52e3-dd67-4571-b3a3-4d2e1716ae4d-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:3440 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3440 - "DELETE /session/alliance-776b52e3-dd67-4571-b3a3-4d2e1716ae4d-independent-observer-attempt-2 HTTP/1.1" 200 OK -INFO: 127.0.0.1:10105 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10109 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10109 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10960 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1108 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1108 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1108 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1108 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1108 - "DELETE /session/alliance-2e3028ea-497f-4624-afdd-fcbf2bd0ed48-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:11999 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1108 - "DELETE /session/alliance-2e3028ea-497f-4624-afdd-fcbf2bd0ed48-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:1108 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:9268 - "GET /ready?provider=claude_cli HTTP/1.1" 200 OK -INFO: 127.0.0.1:1108 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1108 - "DELETE /session/72af5f26-bf12-4261-b630-ac1bb14311f8 HTTP/1.1" 200 OK -INFO: 127.0.0.1:9268 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:12798 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:12798 - "DELETE /session/alliance-db42ab13-1b53-4d7b-b57f-d89948b1d45f-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:12799 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:12798 - "DELETE /session/alliance-db42ab13-1b53-4d7b-b57f-d89948b1d45f-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:12795 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4413 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3243 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3243 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1248 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:2396 - "GET /ready?provider=claude_cli HTTP/1.1" 200 OK -INFO: 127.0.0.1:2400 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2400 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2396 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:2549 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2551 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2551 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9484 - "GET /ready?provider=claude_cli HTTP/1.1" 200 OK -INFO: 127.0.0.1:9484 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:8715 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6539 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10131 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10134 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10134 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9484 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:5615 - "GET /ready?provider=claude_cli HTTP/1.1" 200 OK -INFO: 127.0.0.1:9582 - "DELETE /session/4a09b355-3062-4b70-955e-6f6654abe631 HTTP/1.1" 200 OK -INFO: 127.0.0.1:5615 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:5615 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3719 - "DELETE /session/281d7e1b-4474-46e8-9f45-b14a25eaddf0 HTTP/1.1" 200 OK -INFO: 127.0.0.1:5377 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:9248 - "DELETE /session/1e74395f-81f2-4a1f-9b05-622548477945 HTTP/1.1" 200 OK -INFO: 127.0.0.1:10567 - "DELETE /session/cba2b062-e3cc-4446-9dce-2c77c85fe2ee HTTP/1.1" 200 OK -INFO: 127.0.0.1:14846 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4113 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4113 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5377 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:10540 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:10541 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:5377 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3873 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3876 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3876 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1661 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1663 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1663 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6609 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6611 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6611 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9157 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9159 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9159 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2396 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2398 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2398 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4144 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4146 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4146 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6372 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6372 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5412 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5414 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5414 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1884 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10518 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9881 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9883 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9883 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10437 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10439 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10439 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1053 - "DELETE /session/ddc6ed40-d5b5-4b64-96e3-c82a15822bbd HTTP/1.1" 200 OK -INFO: 127.0.0.1:3998 - "DELETE /session/6820f884-82db-46fc-a9e4-8397e425135e HTTP/1.1" 200 OK -INFO: 127.0.0.1:3142 - "DELETE /session/8eb46214-6f28-41bf-b910-83513a8639fa HTTP/1.1" 200 OK -INFO: 127.0.0.1:3142 - "DELETE /session/1442a5b2-5f7d-40b5-b59e-13b3ee9c5489 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4125 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4125 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4125 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4125 - "GET /v1/capabilities?provider=claude_cli&force=false HTTP/1.1" 200 OK -INFO: 127.0.0.1:4125 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4125 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8259 - "GET /v1/capabilities?provider=claude_cli&force=false HTTP/1.1" 200 OK -INFO: 127.0.0.1:8259 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9735 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9737 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9737 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1322 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1324 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1324 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11531 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11836 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11836 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5005 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5007 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5007 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13098 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7615 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7615 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8791 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8793 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8793 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3868 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3870 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3870 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5626 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5629 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5629 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11523 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11525 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11525 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10184 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10186 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10186 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9193 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12064 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12064 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8283 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8285 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8285 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5217 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5219 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5219 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2141 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13500 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2844 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2846 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2846 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13831 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13834 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13834 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14308 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14310 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14310 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13849 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13851 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13851 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5838 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5840 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5840 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12199 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12201 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12201 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12192 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12194 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12194 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11838 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11840 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11840 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14412 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14414 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14414 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6694 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6696 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6696 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7991 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7993 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7993 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10841 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10843 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10843 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10714 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10717 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10717 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3670 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3670 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7896 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9660 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9660 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9127 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9127 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9127 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9127 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:11839 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11843 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11843 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6974 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:9127 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1138 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1138 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5661 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5663 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5663 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14426 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5980 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4641 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7315 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14426 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14426 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14426 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:14426 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:8870 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:6489 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6489 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6489 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14864 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14866 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14866 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2423 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2425 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2425 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7548 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7550 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7550 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4021 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4024 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4024 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7745 - "DELETE /session/a27befb7-6ded-438f-850d-01f08ff40830 HTTP/1.1" 200 OK -INFO: 127.0.0.1:7745 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4929 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4931 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4931 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2389 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2391 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2391 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10673 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10675 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10675 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13394 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13396 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13396 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9686 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9688 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9688 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4864 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4866 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4866 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3715 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3717 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3717 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9747 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9749 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9749 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6917 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8556 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8558 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8558 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10560 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10562 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10562 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8903 - "GET /v1/capabilities?provider=claude_cli&force=false HTTP/1.1" 200 OK -INFO: 127.0.0.1:8903 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12377 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11023 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11025 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11025 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6355 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6357 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6357 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10471 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10473 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10473 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4290 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4292 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4292 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4092 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4094 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4094 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12530 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12532 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12532 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12569 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12572 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12572 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2428 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2430 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2430 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7445 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7447 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7447 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12799 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12831 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12831 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9336 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9338 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9338 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8765 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8767 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8767 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6132 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6135 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6135 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11878 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11880 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11880 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6966 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6968 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6968 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7671 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7673 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7673 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2994 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2996 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2996 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3650 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3652 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3652 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4978 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4980 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4980 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2505 - "DELETE /session/d214e15c-eafb-4ade-8ef9-9bb649a40cd2 HTTP/1.1" 200 OK -INFO: 127.0.0.1:2505 - "DELETE /session/acae9b3e-88a2-4d48-bbde-a66a581c472e HTTP/1.1" 200 OK -INFO: 127.0.0.1:5065 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5065 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5065 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5065 - "GET /v1/capabilities?provider=claude_cli&force=false HTTP/1.1" 200 OK -INFO: 127.0.0.1:5065 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5065 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5065 - "GET /v1/capabilities?provider=claude_cli&force=false HTTP/1.1" 200 OK -INFO: 127.0.0.1:5065 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5625 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5628 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5628 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9428 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9431 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9431 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10758 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10760 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10760 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4230 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4232 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4232 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3464 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3466 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3466 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11859 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11862 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11862 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13538 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13540 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13540 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13650 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13727 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1360 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1371 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1373 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1373 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1416 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1416 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1416 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7803 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7805 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7805 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10261 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10263 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10263 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8135 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8137 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8137 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8478 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8480 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8480 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6320 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6322 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6322 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6955 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6957 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6957 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8969 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1919 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1921 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1921 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8183 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8186 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8186 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2185 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2187 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2187 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10247 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10249 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10249 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3522 - "DELETE /session/993549af-0df4-421a-b198-a3024f322e34 HTTP/1.1" 200 OK -INFO: 127.0.0.1:12224 - "DELETE /session/c9e91f06-f3d4-4af9-ab1c-138edb475fba HTTP/1.1" 200 OK -INFO: 127.0.0.1:5139 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5142 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5142 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2627 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2631 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2631 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10678 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10724 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10795 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10796 - "GET /ready?provider=claude_cli HTTP/1.1" 200 OK -INFO: 127.0.0.1:3056 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3058 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3058 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3163 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3163 - "DELETE /session/alliance-ea4fd415-8e09-5898-af50-2bb7cae3b44f-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:3163 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3163 - "DELETE /session/alliance-bbbad851-9272-5441-b4ba-80cbf64ac6d1-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "DELETE /session/alliance-ea4fd415-8e09-5898-af50-2bb7cae3b44f-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "DELETE /session/alliance-ea4fd415-8e09-5898-af50-2bb7cae3b44f-independent-observer-attempt-2 HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "DELETE /session/alliance-ea4fd415-8e09-5898-af50-2bb7cae3b44f-independent-observer-attempt-3 HTTP/1.1" 200 OK -INFO: 127.0.0.1:3706 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "DELETE /session/alliance-bbbad851-9272-5441-b4ba-80cbf64ac6d1-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "DELETE /session/alliance-278f93dd-0035-59a0-8dc9-3cf557df8fc7-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "DELETE /session/alliance-cf7a843b-aa58-5204-9f30-fea2c45dec4e-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:13039 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13043 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13043 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "DELETE /session/alliance-92ae15a9-b440-5516-9efe-b80b6d24b77d-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "DELETE /session/alliance-92ae15a9-b440-5516-9efe-b80b6d24b77d-client-report-attempt-2 HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "DELETE /session/alliance-d8618781-e4d2-5409-895c-4ceaf718ad51-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "DELETE /session/alliance-e2c8b5fe-4679-59e9-a173-5ccf0367406b-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "DELETE /session/alliance-e2c8b5fe-4679-59e9-a173-5ccf0367406b-client-report-attempt-2 HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3224 - "DELETE /session/alliance-6cfc51db-40f1-5b70-843e-d3ab73057292-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:3897 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3900 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3900 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "DELETE /session/alliance-3c470ddc-e691-5630-a42c-787e47c96b40-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "DELETE /session/alliance-3c470ddc-e691-5630-a42c-787e47c96b40-independent-observer-attempt-2 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "DELETE /session/alliance-3c470ddc-e691-5630-a42c-787e47c96b40-independent-observer-attempt-3 HTTP/1.1" 200 OK -INFO: 127.0.0.1:8193 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1700 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1700 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "DELETE /session/alliance-e22c24db-89bd-5f86-8a60-c374166d44fc-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:1700 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1700 - "DELETE /session/2388d49f-4ad6-46cb-9fab-fedd8462022a HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "DELETE /session/alliance-c59ce820-9fd3-507c-84f9-d1330668ce85-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:3506 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3711 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:3267 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3269 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3269 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "DELETE /session/alliance-e2d29ed6-d6f7-5c27-85fe-1387e9086984-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:1198 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "DELETE /session/alliance-38008e3c-20bb-5781-ad31-3d28655c145c-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:10626 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11950 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10626 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10626 - "POST /v1/stream HTTP/1.1" 200 OK -INFO: 127.0.0.1:11950 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:2436 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2436 - "POST /v1/stream HTTP/1.1" 200 OK -INFO: 127.0.0.1:5398 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "DELETE /session/alliance-41715196-cbf4-5ea4-9b96-cf231a2c5524-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:11950 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:11950 - "DELETE /session/6f25e1b6-4f9b-4288-95e2-50f28b8352f6 HTTP/1.1" 200 OK -INFO: 127.0.0.1:14323 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14323 - "POST /v1/stream HTTP/1.1" 200 OK -INFO: 127.0.0.1:2592 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:2673 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "DELETE /session/alliance-0f465fd0-fa8b-52cd-b6e8-f842bee76d69-client-report-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:1272 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1272 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1190 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1556 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1190 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4128 - "DELETE /session/alliance-7f2303fa-2d6f-50a2-8834-41b7ab3c47c6-independent-observer-attempt-1 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4868 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4868 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1190 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1190 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1190 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1190 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1190 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4868 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:4868 - "DELETE /session/ec15f212-e02c-4474-90f0-6fc15680b326 HTTP/1.1" 200 OK -INFO: 127.0.0.1:1190 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1190 - "DELETE /session/4813a380-602d-445d-98e0-c46a2e6dc0e4 HTTP/1.1" 200 OK -INFO: 127.0.0.1:1190 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1190 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:12014 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12021 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12021 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8717 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:8717 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1190 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:1190 - "DELETE /session/3f58ce74-f1a6-4dc9-a6bb-fa05a22e12e7 HTTP/1.1" 200 OK -INFO: 127.0.0.1:2888 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3993 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3993 - "DELETE /session/6a21bd41-3a20-40ea-b3c0-672c77c4d53b HTTP/1.1" 200 OK -INFO: 127.0.0.1:2888 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:8717 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:8717 - "DELETE /session/42887b23-f577-40cd-bf08-d882ba2d48e6 HTTP/1.1" 200 OK -INFO: 127.0.0.1:11839 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:2888 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:2888 - "DELETE /session/29d3fbb4-c7da-4d47-aff2-92240c06a273 HTTP/1.1" 200 OK -INFO: 127.0.0.1:4133 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7793 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:7870 - "POST /v1/generate HTTP/1.1" 200 OK -INFO: 127.0.0.1:9982 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9984 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9984 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13456 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13458 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13458 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3442 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3444 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3444 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2815 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2817 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2817 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3202 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3204 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3204 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4223 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4225 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4225 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4481 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4483 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4483 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14660 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14662 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14662 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12623 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12625 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12625 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3830 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3832 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3832 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4956 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4958 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4958 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9251 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9253 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9253 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10367 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10369 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10369 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12206 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12208 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12208 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13409 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:13411 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13411 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2985 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2987 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2987 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4085 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4087 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4087 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4473 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4475 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4475 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5601 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5603 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5603 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14076 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14078 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14078 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3334 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3336 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3336 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8557 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8559 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8559 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2662 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2664 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2664 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2805 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4307 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4309 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4309 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK diff --git a/apps/api/engine.public.out.log.20260809-160042.bak b/apps/api/engine.public.out.log.20260809-160042.bak deleted file mode 100644 index 9dd27ec..0000000 --- a/apps/api/engine.public.out.log.20260809-160042.bak +++ /dev/null @@ -1,150 +0,0 @@ -INFO: 127.0.0.1:5961 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1131 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:13224 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1131 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9011 - "GET /ready?force=true HTTP/1.1" 200 OK -INFO: 127.0.0.1:4872 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4872 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9735 - "GET /ready HTTP/1.1" 200 OK -INFO: 127.0.0.1:9737 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9737 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9550 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4478 - "GET /ready HTTP/1.1" 200 OK -INFO: 127.0.0.1:4480 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4480 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4483 - "GET /ready HTTP/1.1" 200 OK -INFO: 127.0.0.1:4480 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4480 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6909 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:6920 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:6920 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4284 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4286 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4286 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11183 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11186 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11186 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9906 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9908 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9908 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4910 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4912 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4912 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11462 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11464 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11464 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3395 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3397 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3397 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7131 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7135 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7135 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4662 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4665 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4665 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8921 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8924 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8924 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7827 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7830 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7830 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11042 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11046 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11046 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11042 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11046 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14342 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:14343 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:14342 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5041 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5043 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5043 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12732 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:12735 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:12735 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2362 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2365 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2365 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4926 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4928 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4928 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4810 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4813 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4813 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2345 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2351 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2351 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2075 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2079 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2079 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3840 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3842 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3842 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4284 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4286 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4286 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10589 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:10591 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10591 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8330 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8334 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8334 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5069 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5071 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5071 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8707 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8709 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8709 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9467 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:9470 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:9470 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11371 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11374 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11374 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11371 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11374 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11382 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11416 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11382 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11850 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:11852 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:11852 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1574 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1576 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1576 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4470 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4472 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4472 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4562 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:4564 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4564 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7321 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7323 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7323 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1317 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:1319 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:1319 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2332 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2334 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2334 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3770 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:3772 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:3772 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7472 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7474 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7474 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7671 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:7673 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:7673 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8837 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:8839 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:8839 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2461 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:2463 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:2463 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:10930 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5805 - "GET /health HTTP/1.1" 200 OK -INFO: 127.0.0.1:5781 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:5714 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4865 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK -INFO: 127.0.0.1:4903 - "GET /ready?provider=claude_cli&reasoning_effort=high HTTP/1.1" 200 OK diff --git a/apps/web/e2e/self-directed-learning-loop.spec.ts b/apps/web/e2e/self-directed-learning-loop.spec.ts index adfe7e2..c8347e3 100644 --- a/apps/web/e2e/self-directed-learning-loop.spec.ts +++ b/apps/web/e2e/self-directed-learning-loop.spec.ts @@ -1,5 +1,5 @@ import path from "node:path"; -import { expect, test, type Page, type Route } from "@playwright/test"; +import { expect, test, type Page, type Route, type TestInfo } from "@playwright/test"; import type { DeliberatePracticeReadModel } from "../src/pages/session-review/deliberatePracticeApi"; import { parsePracticeLaunchIntent } from "../src/lib/practiceLaunchIntent"; import { @@ -893,17 +893,14 @@ async function expectMobileActiveVisualIntegrity(page: Page) { expect(result.latestReplyVisible, diagnostic).toBe(true); } -async function capture(page: Page, projectName: string, stage: string) { +async function capture(page: Page, testInfo: TestInfo, stage: string) { if (process.env.E2E_SKIP_EVIDENCE_CAPTURE === "1") return; - const safeProject = projectName.replace(/[^a-z0-9-]+/gi, "-").toLowerCase(); - const evidenceDirectory = process.env.E2E_EVIDENCE_DIR - ? path.resolve(process.env.E2E_EVIDENCE_DIR) - : path.resolve(process.cwd(), "../../docs/ops/evidence"); + const safeProject = testInfo.project.name.replace(/[^a-z0-9-]+/gi, "-").toLowerCase(); + const evidenceDirectory = process.env.E2E_EVIDENCE_DIR; await page.screenshot({ - path: path.resolve( - evidenceDirectory, - `self-directed-loop-${stage}-${safeProject}-2026-08-07.png`, - ), + path: evidenceDirectory + ? path.resolve(evidenceDirectory, `self-directed-loop-${stage}-${safeProject}.png`) + : testInfo.outputPath(`self-directed-loop-${stage}.png`), fullPage: true, animations: "disabled", }); @@ -1030,11 +1027,11 @@ test.describe("학습자 자기주도 전체 루프 — 실제 src UI / route fi ); await expectMinimumHitTargets(page, ".lh-tabs [role='tab']", "320px 학습 홈 탭"); await expectNoHorizontalOverflow(page); - await capture(page, testInfo.project.name, "home-320x568"); + await capture(page, testInfo, "home-320x568"); await page.setViewportSize({ width: 390, height: 844 }); await page.evaluate(() => new Promise(requestAnimationFrame)); } - await capture(page, testInfo.project.name, "home"); + await capture(page, testInfo, "home"); await primaryAction.click(); await expect(page).toHaveURL(/\/learn\/practice$/); @@ -1095,11 +1092,11 @@ test.describe("학습자 자기주도 전체 루프 — 실제 src UI / route fi await expectMobileActiveVisualIntegrity(page); await expectCoachNudgeContrast(page); await expectNoHorizontalOverflow(page); - await capture(page, testInfo.project.name, viewport.stage); + await capture(page, testInfo, viewport.stage); } await page.setViewportSize({ width: 390, height: 844 }); } else { - await capture(page, testInfo.project.name, "active-session"); + await capture(page, testInfo, "active-session"); } await page.getByRole("button", { name: "회기 종료" }).click(); @@ -1173,12 +1170,12 @@ test.describe("학습자 자기주도 전체 루프 — 실제 src UI / route fi "320px 처방 연습 시작 행동", ); await expectNoHorizontalOverflow(page); - await capture(page, testInfo.project.name, "review-prescription-320x568"); + await capture(page, testInfo, "review-prescription-320x568"); await page.setViewportSize({ width: 390, height: 844 }); await page.evaluate(() => new Promise(requestAnimationFrame)); } await practiceCard.scrollIntoViewIfNeeded(); - await capture(page, testInfo.project.name, "review-prescription"); + await capture(page, testInfo, "review-prescription"); const launch = practiceCard.getByRole("link", { name: "이 처방으로 연습 시작", @@ -1222,7 +1219,7 @@ test.describe("학습자 자기주도 전체 루프 — 실제 src UI / route fi await page.evaluate(() => new Promise(requestAnimationFrame)); await expectMinimumHitTargets(page, ".lh-actions .vg-btn", "320px 재연습 대상 행동"); await expectNoHorizontalOverflow(page); - await capture(page, testInfo.project.name, "repractice-home-320x568"); + await capture(page, testInfo, "repractice-home-320x568"); await page.setViewportSize({ width: 390, height: 844 }); await page.evaluate(() => new Promise(requestAnimationFrame)); } @@ -1264,11 +1261,11 @@ test.describe("학습자 자기주도 전체 루프 — 실제 src UI / route fi "320px 처방 재연습 시작 행동", ); await expectNoHorizontalOverflow(page); - await capture(page, testInfo.project.name, "repractice-prestart-320x568"); + await capture(page, testInfo, "repractice-prestart-320x568"); await page.setViewportSize({ width: 390, height: 844 }); await page.evaluate(() => new Promise(requestAnimationFrame)); } - await capture(page, testInfo.project.name, "repractice-prestart"); + await capture(page, testInfo, "repractice-prestart"); await page.getByRole("button", { name: "회기 시작" }).click(); await expect(page).toHaveURL( @@ -1368,12 +1365,12 @@ test.describe("학습자 자기주도 전체 루프 — 실제 src UI / route fi await expect(observation).not.toContainText(RETRY_SESSION_ID.slice(0, 8)); await expect(observation).not.toContainText("[NAME]"); await expectNoHorizontalOverflow(page); - await capture(page, testInfo.project.name, "repractice-observation-320x568"); + await capture(page, testInfo, "repractice-observation-320x568"); await page.setViewportSize({ width: 390, height: 844 }); await page.evaluate(() => new Promise(requestAnimationFrame)); } await observation.scrollIntoViewIfNeeded(); - await capture(page, testInfo.project.name, "repractice-observation"); + await capture(page, testInfo, "repractice-observation"); await page.reload(); observation = page.locator(".dp-runtime-observation"); diff --git a/apps/web/e2e/session-mvp.spec.ts b/apps/web/e2e/session-mvp.spec.ts index 84f4fc4..638e8a0 100644 --- a/apps/web/e2e/session-mvp.spec.ts +++ b/apps/web/e2e/session-mvp.spec.ts @@ -1,4 +1,3 @@ -import path from "node:path"; import { expect, test, type Page } from "@playwright/test"; import { parseVoicePracticeContext } from "../src/lib/voicePracticeContext"; import { expectNoHorizontalOverflow } from "./support"; @@ -424,6 +423,7 @@ async function routeMvpApi(page: Page, options: RouteMvpOptions = {}) { growthPoints: [{ title: "탐색 확장", body: "다음 턴에서 구체 상황을 더 묻습니다." }], nextLine: "그 말을 꺼내는 것도 쉽지 않았을 것 같아요.", clientFeedback: clientReply, + learnerFeedbackEnabled: true, audioUrl: null, pdfExportUrl: null, degraded: false, @@ -773,7 +773,6 @@ async function installVoiceLifecycleFixture(page: Page) { releaseEmptyFinal() { const socket = sockets[2]; if (!socket || socket.readyState !== FixtureWebSocket.OPEN) return; - socket.emitJson({ type: "state", state: "thinking" }); socket.emitJson({ type: "transcript", text: "", final: true }); socket.emitJson({ type: "state", state: "idle" }); }, @@ -877,11 +876,12 @@ async function installVoiceLifecycleFixture(page: Page) { } if (payload.type !== "audio_end") return; window.setTimeout(() => { - if (this.connectionNumber === 1) { - this.emitJson({ type: "transcript", text: firstInterim, final: false }); - } else if (this.connectionNumber === 2) { - this.emitJson({ type: "transcript", text: secondInterim, final: false }); - } + if (this.connectionNumber > 2) return; + this.emitJson({ + type: "transcript", + text: this.connectionNumber === 1 ? firstInterim : secondInterim, + final: false, + }); this.emitJson({ type: "eot", ready: false, @@ -1116,7 +1116,7 @@ test.describe("P1 MVP core loop", () => { await expect( page.getByRole("heading", { name: "원본 회기 정보를 확인할 수 없습니다." }), ).toBeVisible(); - await expect(page.getByRole("button", { name: "새 회기 시작" })).toBeDisabled(); + await expect(page.getByRole("button", { name: "완전히 새로 시작" })).toBeDisabled(); await expectNoHorizontalOverflow(page); await page.goto(`/learn/session/P1?${malformedQuery}`); @@ -1153,11 +1153,7 @@ test.describe("P1 MVP core loop", () => { await expect(page.getByRole("heading", { name: "첫 발화 전에 내 기준을 잠급니다" })).toBeVisible(); await expect(page.getByLabel("학습자 발화 입력")).toBeDisabled(); await page.screenshot({ - path: path.resolve( - process.cwd(), - "../../docs/ops/evidence", - `g1-alliance-pre-${testInfo.project.name}-2026-08-07.png`, - ), + path: testInfo.outputPath("g1-alliance-pre.png"), fullPage: true, }); await selectAllianceScore(page, "목표", "3 보통이다"); @@ -1176,11 +1172,7 @@ test.describe("P1 MVP core loop", () => { await expect(page.getByLabel("학습자 발화 입력")).toBeEnabled(); await page.getByRole("button", { name: "30초 점검" }).click(); await page.screenshot({ - path: path.resolve( - process.cwd(), - "../../docs/ops/evidence", - `g1-alliance-mid-${testInfo.project.name}-2026-08-07.png`, - ), + path: testInfo.outputPath("g1-alliance-mid.png"), fullPage: true, }); await selectAllianceScore(page, "목표", "4 대체로 그렇다"); @@ -1210,7 +1202,8 @@ test.describe("P1 MVP core loop", () => { page.getByRole("button", { name: "회기 시작" }), diagnostics.join("\n") || (await page.locator("#root").innerText().catch(() => "")), ).toBeVisible(); - await page.getByRole("button", { name: /CBT/ }).click(); + await page.locator("details.sx-prestart__settings > summary").click(); + await page.getByRole("button", { name: /^CBT/ }).click(); await page.getByRole("button", { name: "회기 시작" }).click(); await expect(page.locator(".sx-page.sx-page--active")).toBeVisible(); @@ -1253,7 +1246,7 @@ test.describe("P1 MVP core loop", () => { await page.goto("/learn/session/P1"); await page.getByRole("button", { name: "회기 시작" }).click(); - await page.getByRole("button", { name: "코칭" }).click(); + await page.getByRole("button", { name: /^코칭 모드, 남은 기회 \d+개$/ }).click(); await page.getByLabel("학습자 발화 입력").fill(learnerText); await page.getByRole("button", { name: "보내기" }).click(); @@ -1292,7 +1285,7 @@ test.describe("P1 MVP core loop", () => { await page.goto("/learn/session/P1"); await page.getByRole("button", { name: "회기 시작" }).click(); - await page.getByRole("button", { name: "코칭" }).click(); + await page.getByRole("button", { name: /^코칭 모드, 남은 기회 \d+개$/ }).click(); await page.getByLabel("학습자 발화 입력").fill(learnerText); await page.getByRole("button", { name: "보내기" }).click(); @@ -1317,7 +1310,7 @@ test.describe("P1 MVP core loop", () => { await page.goto("/learn/session/P1"); await page.getByRole("button", { name: "회기 시작" }).click(); - await page.getByRole("button", { name: "코칭" }).click(); + await page.getByRole("button", { name: /^코칭 모드, 남은 기회 \d+개$/ }).click(); await page.getByLabel("학습자 발화 입력").fill(learnerText); await page.getByRole("button", { name: "보내기" }).click(); @@ -1341,7 +1334,7 @@ test.describe("P1 MVP core loop", () => { await page.goto("/learn/session/P1"); await page.getByRole("button", { name: "회기 시작" }).click(); - await page.getByRole("button", { name: "코칭" }).click(); + await page.getByRole("button", { name: /^코칭 모드, 남은 기회 \d+개$/ }).click(); await page.getByLabel("학습자 발화 입력").fill(learnerText); await page.getByRole("button", { name: "보내기" }).click(); @@ -1367,7 +1360,7 @@ test.describe("P1 MVP core loop", () => { await page.goto("/learn/session/P1"); await page.getByRole("button", { name: "회기 시작" }).click(); - await page.getByRole("button", { name: "코칭" }).click(); + await page.getByRole("button", { name: /^코칭 모드, 남은 기회 \d+개$/ }).click(); await page.getByLabel("학습자 발화 입력").fill(learnerText); await page.getByRole("button", { name: "보내기" }).click(); @@ -1505,6 +1498,14 @@ test.describe("P1 MVP core loop", () => { await page.getByRole("button", { name: "음성 다시 연결" }).click(); await page.getByRole("button", { name: "발화 보내기" }).click(); + await expect.poll(async () => + page.evaluate( + () => + window.__voiceLifecycleFixture?.sent.filter((payload) => + payload.includes('"audio_end"'), + ).length ?? 0, + ), + ).toBe(3); await page.evaluate(() => window.__voiceLifecycleFixture?.releaseEmptyFinal()); await expect(transcriptLog.locator(".sx-utt.is-learner")).toHaveCount(2); await expect(page.locator(".sx-mic-block__h")).toContainText("음성을 인식하지 못했습니다"); @@ -1519,7 +1520,7 @@ test.describe("P1 MVP core loop", () => { await page.goto("/learn/session/P1"); await page.getByRole("button", { name: "회기 시작" }).click(); - await page.getByRole("button", { name: "코칭" }).click(); + await page.getByRole("button", { name: /^코칭 모드, 남은 기회 \d+개$/ }).click(); const micButton = page.getByRole("button", { name: "마이크 켜기" }); await expect(micButton).toBeEnabled(); await micButton.click(); @@ -1548,7 +1549,7 @@ test.describe("P1 MVP core loop", () => { await page.goto("/learn/session/P1"); await page.getByRole("button", { name: "회기 시작" }).click(); - await page.getByRole("button", { name: "코칭" }).click(); + await page.getByRole("button", { name: /^코칭 모드, 남은 기회 \d+개$/ }).click(); await page.getByLabel("학습자 발화 입력").fill(learnerText); await page.getByRole("button", { name: "보내기" }).click(); @@ -1583,7 +1584,7 @@ test.describe("P1 MVP core loop", () => { await page.goto("/learn/session/P1"); await page.getByRole("button", { name: "회기 시작" }).click(); - await page.getByRole("button", { name: "코칭" }).click(); + await page.getByRole("button", { name: /^코칭 모드, 남은 기회 \d+개$/ }).click(); await page.getByLabel("학습자 발화 입력").fill("저 지금 자살하고 싶어요. 도와주세요."); await page.getByRole("button", { name: "보내기" }).click(); diff --git a/apps/web/e2e/voice-success.spec.ts b/apps/web/e2e/voice-success.spec.ts index 6078538..2447a8d 100644 --- a/apps/web/e2e/voice-success.spec.ts +++ b/apps/web/e2e/voice-success.spec.ts @@ -1117,10 +1117,10 @@ test.describe("voice cascade success path", () => { await expect(consentDialog).toBeVisible(); if (process.env.CAPTURE_G7_CONSENT === "1") { const desktopViewport = page.viewportSize() ?? { width: 1440, height: 900 }; - await page.screenshot({ path: "test-results/g7-voice-consent-desktop.png" }); + await page.screenshot({ path: testInfo.outputPath("g7-voice-consent-desktop.png") }); await page.setViewportSize({ width: 390, height: 844 }); await expect(consentDialog).toBeVisible(); - await page.screenshot({ path: "test-results/g7-voice-consent-mobile.png" }); + await page.screenshot({ path: testInfo.outputPath("g7-voice-consent-mobile.png") }); await page.setViewportSize(desktopViewport); } await expect.poll(async () => (await readVoiceUiProbe(page)).getUserMediaCalls).toBe(0); diff --git a/apps/web/knip.json b/apps/web/knip.json index c73e03f..429d6e4 100644 --- a/apps/web/knip.json +++ b/apps/web/knip.json @@ -4,7 +4,8 @@ "functions/**/*.js", "public/worklets/**/*.js", "scripts/*.mjs", - "e2e/**/*.spec.ts" + "e2e/**/*.spec.ts", + "src/lib/measurementContract.ts" ], "project": [ "src/**/*.{ts,tsx}", @@ -14,6 +15,9 @@ "e2e/**/*.ts" ], "ignoreFiles": ["src/components/avatar/RasterBust.tsx"], + "paths": { + "/src/*": ["./src/*"] + }, "ignoreDependencies": ["openapi-typescript"], "ignoreBinaries": ["taskkill"] } diff --git a/apps/web/src/lib/focusTrap.ts b/apps/web/src/lib/focusTrap.ts new file mode 100644 index 0000000..fefef46 --- /dev/null +++ b/apps/web/src/lib/focusTrap.ts @@ -0,0 +1,21 @@ +const FOCUSABLE_SELECTOR = + "a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex='-1'])"; + +export function getFocusableControls(root: HTMLElement | null): HTMLElement[] { + return Array.from(root?.querySelectorAll(FOCUSABLE_SELECTOR) ?? []).filter( + (control) => control.getClientRects().length > 0, + ); +} + +export function trapTabKey(event: KeyboardEvent, controls: readonly HTMLElement[]): void { + if (event.key !== "Tab" || controls.length === 0) return; + const first = controls[0]; + const last = controls[controls.length - 1]; + if (event.shiftKey && document.activeElement === first) { + event.preventDefault(); + last.focus(); + } else if (!event.shiftKey && document.activeElement === last) { + event.preventDefault(); + first.focus(); + } +} diff --git a/apps/web/src/pages/Session.tsx b/apps/web/src/pages/Session.tsx index 59f6b01..ef65e10 100644 --- a/apps/web/src/pages/Session.tsx +++ b/apps/web/src/pages/Session.tsx @@ -23,7 +23,7 @@ import { ClientAvatar, expressionLabelFor, } from "../components/avatar/ClientAvatar"; -import type { AvatarState, AvatarAffect, AvatarPersona } from "../components/avatar/ClientAvatar"; +import type { AvatarState, AvatarAffect } from "../components/avatar/ClientAvatar"; import { Kicker, Button, Icon, surfaceClassName } from "../components/ui"; import { ApiError, @@ -37,7 +37,6 @@ import { type LiveCoachQuota, type LiveCoachSuggestion, type PersonaSummary, - type SessionDetailResponse, type SessionProgress, type SessionStage, type SessionStartMode, @@ -45,6 +44,7 @@ import { import { useAuth } from "../lib/auth"; import { formatElapsed, formatTimecode, clamp01 } from "../lib/format"; import { displayPiiSafeText } from "../lib/piiDisplay"; +import { getFocusableControls, trapTabKey } from "../lib/focusTrap"; import { parseVoicePracticeContext, voicePracticeSearch, @@ -58,17 +58,7 @@ import { PRACTICE_MODE_LABEL, PRACTICE_NOVELTY_LABEL, } from "../lib/practiceLaunchIntent"; -import { - DIFFICULTY_LABEL, - PENDING_PERSONA_AVATAR_APPEARANCE, - isUsablePersona, - personaAvatarAppearance, - personaBaselineExpression, - personaMeta, - personaTheoryLabel, - shortPersonaName, - unavailablePersonaMessage, -} from "../lib/personaViewModel"; +import { isUsablePersona, unavailablePersonaMessage } from "../lib/personaViewModel"; import { VOICE_CONNECTION_SAVE_FAILED, createAudioWorkletCapture, @@ -84,6 +74,28 @@ import { type VoiceStatus, } from "./session/voiceCapture"; import { AllianceCheckpointPrompt } from "./session/AllianceCheckpointPrompt"; +import { + AI_VOICE_DISCLOSURE, + DEFAULT_COACH_QUOTA, + SESSION_PHASES, + THEORY_MODE_OPTIONS, + buildPersonaUi, + elapsedFromSession, + expressionForSession, + formatCoachTimestamp, + looksLikeSessionId, + metersFromOpenness, + normalizePersonaCode, + preferredTheoryMode, + primaryGoalActionLabel, + secondsBetween, + splitMetaLines, + type FeedbackMode, + type PhaseInfo, + type SignalTone, + type TheoryMode, + type Utterance, +} from "./session/sessionViewModel"; import { multimodalAllianceApi, type MultimodalConsentRequest, @@ -91,85 +103,6 @@ import { import "./session/session.css"; import { randomUuid } from "../lib/uuid"; -/* ── 도메인 상수/타입 ───────────────────────────────────────────────── */ - -type FeedbackMode = "immersive" | "ambient" | "coached"; -type TheoryMode = "humanistic" | "cbt" | "integrative"; -type Speaker = "client" | "learner"; -type SignalTone = "pos" | "warn" | "neutral"; - -const AI_VOICE_DISCLOSURE = - "내담자 음성은 AI가 생성한 합성 음성이며 사람의 목소리가 아닙니다."; - -interface Utterance { - id: number; - speaker: Speaker; - text: string; - turnSeq?: number; - /** 발화 시작 시점(세션 경과 초) */ - at: number; - /** 진행 중(스트리밍/타이핑) 발화 여부 */ - partial?: boolean; - /** 음성 WebSocket이 reply 전에 실패해 DB 턴으로 확정되지 않은 발화 */ - failed?: boolean; - /** 실시간 음성 전사의 현재 확정 단계 */ - voiceTranscriptState?: "interim" | "finalizing"; -} - -interface PhaseInfo { - key: SessionStage; - desc: string; -} - -interface ClientContext { - initial: string; - name: string; - meta: string; - rows: { l: string; v: string }[]; - chips: { t: string; clay: boolean }[]; -} - -const SESSION_PHASES: PhaseInfo[] = [ - { key: "라포", desc: "첫 인사와 안전감 형성" }, - { key: "탐색", desc: "호소 문제와 일상을 함께 이해" }, - { key: "개입", desc: "감정과 생각을 다루는 기법 적용" }, - { key: "정리", desc: "오늘의 대화 정리와 다음 약속" }, -]; - -function primaryGoalActionLabel(goal: SessionStage): string { - const lastCode = goal.charCodeAt(goal.length - 1); - const hasFinalConsonant = - lastCode >= 0xac00 && lastCode <= 0xd7a3 && (lastCode - 0xac00) % 28 !== 0; - return `${goal}${hasFinalConsonant ? "을" : "를"} 핵심 초점으로 설정`; -} - -const THEORY_MODE_OPTIONS: { - value: TheoryMode; - label: string; - detail: string; - focus: string; -}[] = [ - { - value: "humanistic", - label: "인간중심", - detail: "공감·반영", - focus: "감정과 욕구를 반영한 뒤 열린 질문으로 이어갑니다.", - }, - { - value: "cbt", - label: "CBT", - detail: "생각·행동", - focus: "상황·생각·감정·행동의 연결을 한 단계씩 확인합니다.", - }, - { - value: "integrative", - label: "통합", - detail: "혼합 접근", - focus: "공감적 반영을 먼저 두고 필요한 지점만 구조화합니다.", - }, -]; -const DEFAULT_COACH_QUOTA: LiveCoachQuota = { remaining: 3, max: 3 }; - interface CoachChatMessage { id: string; sender: "learner" | "coach"; @@ -178,186 +111,8 @@ interface CoachChatMessage { createdAt: number; } -function preferredTheoryMode(summary: PersonaSummary | null): TheoryMode { - const firstSupported = summary?.theory_target.find( - (target): target is TheoryMode => - target === "humanistic" || target === "cbt" || target === "integrative", - ); - return firstSupported ?? "humanistic"; -} - let _uid = 100; const nextId = () => ++_uid; - -function normalizePersonaCode(raw?: string): string { - return (raw ?? "").trim().toUpperCase(); -} - -function looksLikeSessionId(raw?: string): boolean { - const value = (raw ?? "").trim(); - return ( - /^[0-9a-f]{32}$/i.test(value) || - /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value) - ); -} - -function secondsBetween(startedAt: string, createdAt: string): number { - const start = Date.parse(startedAt); - const created = Date.parse(createdAt); - if (!Number.isFinite(start) || !Number.isFinite(created)) return 0; - return Math.max(0, Math.round((created - start) / 1000)); -} - -function elapsedFromSession(detail: SessionDetailResponse): number { - const start = Date.parse(detail.started_at); - const end = detail.ended_at ? Date.parse(detail.ended_at) : Date.now(); - if (!Number.isFinite(start) || !Number.isFinite(end)) return 0; - return Math.max(0, Math.round((end - start) / 1000)); -} - -function buildPersonaUi( - summary: PersonaSummary | null, - code: string, -): { avatar: AvatarPersona; context: ClientContext } { - if (!summary) { - return { - avatar: { - label: "내담자 정보 확인 중", - ...PENDING_PERSONA_AVATAR_APPEARANCE, - realism: 0.3, - }, - context: { - initial: "?", - name: "내담자 확인 중", - meta: code ? `${code} · DB 카탈로그 확인 중` : "DB 카탈로그 확인 중", - rows: [ - { l: "상태", v: "실제 연습 대상 정보를 확인하고 있습니다." }, - ], - chips: [], - }, - }; - } - - const name = shortPersonaName(summary.display_name); - const difficulty = DIFFICULTY_LABEL[summary.difficulty] ?? summary.difficulty; - const theories = summary.theory_target.length - ? summary.theory_target.map(personaTheoryLabel).join(" · ") - : "공통"; - const chips = [ - { t: summary.code, clay: false }, - { t: difficulty, clay: summary.difficulty === "hard" }, - ...summary.theory_target.slice(0, 2).map((target) => ({ - t: personaTheoryLabel(target), - clay: false, - })), - ]; - - return { - avatar: { - code: summary.code, - label: `${name} · ${personaMeta(summary)}`, - ...personaAvatarAppearance(summary), - }, - context: { - initial: name.slice(0, 1) || summary.code.slice(0, 1), - name, - meta: personaMeta(summary), - rows: [ - { l: "호소", v: summary.presenting_summary || "요약 정보 없음" }, - { l: "대상", v: theories }, - { l: "난도", v: difficulty }, - ], - chips, - }, - }; -} - -/* D3(가운뎃점 정리) — "16-18 · 고2 · 자퇴 고민 · 교육용 가상 내담자" 처럼 한 줄에 · 가 - 2개 이상 몰린 소개 문자열을, 줄당 · 1개 이하가 되도록 두 항목씩 묶어 여러 줄로 쪼갠다. - 원본 personaMeta() 는 다른 화면도 함께 쓰는 공용 유틸이라 건드리지 않고 표시 단계에서만 나눈다. */ -function splitMetaLines(meta: string): string[] { - const parts = meta - .split("·") - .map((part) => part.trim()) - .filter(Boolean); - if (parts.length <= 2) return [meta]; - const lines: string[] = []; - for (let i = 0; i < parts.length; i += 2) { - lines.push(parts.slice(i, i + 2).join(" · ")); - } - return lines; -} - -/* ── 작은 표현 유틸 ─────────────────────────────────────────────────── */ - -// 백엔드가 반환한 effective_openness(0~1)를 실시간 관찰 신호로만 변환한다. -// 점수/정밀 평가는 회기 종료 후 리뷰 화면에서 다룬다. -function metersFromOpenness(openness: number) { - const o = clamp01(openness); - return { - rapport: o, // 마음 열림(라포) - resistance: clamp01(1 - o * 0.9), // 저항감(반비례) - anxiety: clamp01(0.7 - o * 0.4), // 불안·위축(완만히 감소) - }; -} - -function baselineExpressionFor(summary: PersonaSummary | null): AvatarAffect { - return personaBaselineExpression(summary); -} - -function expressionForSession({ - state, - openness, - paused, - safety, - summary, - stage, -}: { - state: AvatarState; - openness: number; - paused: boolean; - safety: string | null; - summary: PersonaSummary | null; - stage: SessionStage; -}): AvatarAffect { - const o = clamp01(openness); - const code = summary?.code.toUpperCase() ?? ""; - const baseline = baselineExpressionFor(summary); - - if (safety) return "startled"; - if (paused) return "tired"; - - if (state === "thinking") { - if (code === "P4" && o < 0.38) return "anxious"; - if (code === "P6") return o < 0.55 ? "conflicted" : "determined"; - if (code === "P7") return o < 0.45 ? "overwhelmed" : "tired"; - if (o < 0.32) return "guarded"; - return "confused"; - } - - if (state === "listening") { - if (o < 0.28) return code === "P4" ? "panic" : "guarded"; - if (o < 0.48) return baseline === "tired" ? "bored" : baseline; - if (o > 0.72) return "warm"; - return "calm"; - } - - if (state === "speaking") { - if (o < 0.22) return code === "P7" ? "bored" : "resistant"; - if (o < 0.38) return code === "P5" ? "skeptical" : baseline; - if (stage === "정리") return "relief"; - if (o > 0.78) return "hopeful"; - if (o > 0.62) return "warm"; - return "neutral"; - } - - if (o < 0.3) return baseline; - if (o > 0.76) return "relief"; - return "neutral"; -} - -/* ===================================================================== */ - export default function Session() { const { sessionId: routeId } = useParams<{ sessionId: string }>(); const navigate = useNavigate(); @@ -1710,11 +1465,14 @@ export default function Session() { : detail.includes("engine unavailable") || detail.includes("engine stream") ? "AI 엔진이 응답하지 않습니다. 입력 내용은 복원했습니다. 잠시 뒤 다시 시도하고, 반복되면 관리자에게 내담자 응답 엔진 상태 확인을 요청하세요." : "내담자 응답을 생성하지 못했습니다. 입력 내용은 복원했습니다. 같은 발화로 다시 시도해 주세요."; + setComposeText(text); setTurnError(normalized); pushSignal("warn", "AI 엔진 연결 실패"); setAvatarState("listening"); } finally { setSending(false); + setClientReplyPending(false); + setVoiceStatus((prev) => (prev === "thinking" ? "idle" : prev)); } }, [ composeText, @@ -2474,6 +2232,9 @@ export default function Session() { const isCtrl = (e.ctrlKey || e.metaKey) && !e.altKey; const target = e.target instanceof HTMLElement ? e.target : null; const isInput = target?.closest("input, textarea, select, [contenteditable='true']"); + const isInteractiveControl = target?.closest( + "button, a[href], summary, [role='button'], [role='link']", + ); // Alt+M: 마이크 토글 if (isAlt && e.key.toLowerCase() === "m") { @@ -2558,7 +2319,8 @@ export default function Session() { } // 입력창 밖 단축키 - if (!isInput) { + // 일반 단축키는 컨트롤의 기본 키보드 동작을 가로채지 않는다. + if (!isInput && !isInteractiveControl) { if (e.key === "p" || e.key === "P" || e.key === " ") { if (e.key === " ") e.preventDefault(); togglePause(); @@ -2605,20 +2367,10 @@ export default function Session() { dismissVoiceConsent(); return; } - if (event.key !== "Tab") return; const controls = [voiceConsentTextRef.current, voiceConsentAcceptRef.current].filter( (control): control is HTMLButtonElement => control !== null && !control.disabled, ); - if (controls.length === 0) return; - const first = controls[0]; - const last = controls[controls.length - 1]; - if (event.shiftKey && document.activeElement === first) { - event.preventDefault(); - last.focus(); - } else if (!event.shiftKey && document.activeElement === last) { - event.preventDefault(); - first.focus(); - } + trapTabKey(event, controls); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); @@ -2637,21 +2389,7 @@ export default function Session() { window.setTimeout(() => shortcutHelpConfirmRef.current?.focus(), 0); const onKey = (event: KeyboardEvent) => { if (event.key !== "Tab") return; - const controls = Array.from( - shortcutHelpPanelRef.current?.querySelectorAll( - "a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex='-1'])", - ) ?? [], - ).filter((control) => control.getClientRects().length > 0); - if (controls.length === 0) return; - const first = controls[0]; - const last = controls[controls.length - 1]; - if (event.shiftKey && document.activeElement === first) { - event.preventDefault(); - last.focus(); - } else if (!event.shiftKey && document.activeElement === last) { - event.preventDefault(); - first.focus(); - } + trapTabKey(event, getFocusableControls(shortcutHelpPanelRef.current)); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); @@ -2673,21 +2411,7 @@ export default function Session() { return; } if (event.key !== "Tab") return; - const controls = Array.from( - coachEvidencePanelRef.current?.querySelectorAll( - "a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex='-1'])", - ) ?? [], - ).filter((control) => control.getClientRects().length > 0); - if (controls.length === 0) return; - const first = controls[0]; - const last = controls[controls.length - 1]; - if (event.shiftKey && document.activeElement === first) { - event.preventDefault(); - last.focus(); - } else if (!event.shiftKey && document.activeElement === last) { - event.preventDefault(); - first.focus(); - } + trapTabKey(event, getFocusableControls(coachEvidencePanelRef.current)); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); @@ -2702,21 +2426,7 @@ export default function Session() { return; } if (event.key !== "Tab") return; - const controls = Array.from( - coachHistoryPanelRef.current?.querySelectorAll( - "a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex='-1'])", - ) ?? [], - ).filter((control) => control.getClientRects().length > 0); - if (controls.length === 0) return; - const first = controls[0]; - const last = controls[controls.length - 1]; - if (event.shiftKey && document.activeElement === first) { - event.preventDefault(); - last.focus(); - } else if (!event.shiftKey && document.activeElement === last) { - event.preventDefault(); - first.focus(); - } + trapTabKey(event, getFocusableControls(coachHistoryPanelRef.current)); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); @@ -2746,21 +2456,10 @@ export default function Session() { setEndDialogOpen(false); return; } - if (event.key === "Tab") { - const controls = [endCancelRef.current, endConfirmRef.current].filter( - (control): control is HTMLButtonElement => control !== null && !control.disabled, - ); - if (controls.length === 0) return; - const first = controls[0]; - const last = controls[controls.length - 1]; - if (event.shiftKey && document.activeElement === first) { - event.preventDefault(); - last.focus(); - } else if (!event.shiftKey && document.activeElement === last) { - event.preventDefault(); - first.focus(); - } - } + const controls = [endCancelRef.current, endConfirmRef.current].filter( + (control): control is HTMLButtonElement => control !== null && !control.disabled, + ); + trapTabKey(event, controls); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); @@ -3964,6 +3663,29 @@ export default function Session() { {turnErrorTitle} · {turnError} +
+ {composeText.trim() && !sessionEnded && !paused ? ( + + ) : null} + +
) : null} @@ -5187,23 +4909,6 @@ export default function Session() { ); } -function formatCoachTimestamp(value: string | null | undefined) { - if (!value) return "방금"; - const date = new Date(value); - if (Number.isNaN(date.getTime())) return "방금"; - const now = new Date(); - const sameDay = - date.getFullYear() === now.getFullYear() && - date.getMonth() === now.getMonth() && - date.getDate() === now.getDate(); - return new Intl.DateTimeFormat("ko-KR", { - month: sameDay ? undefined : "numeric", - day: sameDay ? undefined : "numeric", - hour: "2-digit", - minute: "2-digit", - }).format(date); -} - /* ── 로컬 게이지 (매우 천천히 채움, 2s — §5.5 산만함 방지) ──────────── */ function Gauge({ value, tone }: { value: number; tone: "clay" | "accent" }) { const pct = Math.round(clamp01(value) * 100); diff --git a/apps/web/src/pages/admin/ContinuousImprovementCockpit.tsx b/apps/web/src/pages/admin/ContinuousImprovementCockpit.tsx index 97a5a9a..ab7e40a 100644 --- a/apps/web/src/pages/admin/ContinuousImprovementCockpit.tsx +++ b/apps/web/src/pages/admin/ContinuousImprovementCockpit.tsx @@ -302,6 +302,46 @@ function DecisionSelector({ ); } +function ApprovalSubmitControls({ + approving, + canSubmit, + intent, + approvalDecision, + approvalAllowed, + requirementId, + requirementCopy, +}: { + approving: boolean; + canSubmit: boolean; + intent: DecisionIntent | null; + approvalDecision: ApprovalDecision; + approvalAllowed: boolean; + requirementId: string; + requirementCopy: string; +}) { + const variant = submitVariant(intent, approvalDecision); + const label = submitLabel(intent, approvalDecision); + const requirementClassName = `cic-approval-requirement ${ + canSubmit ? "is-ready" : intent === "approve" && !approvalAllowed ? "is-blocked" : "" + }`; + return ( + <> + +

+ {requirementCopy} +

+ + ); +} + function ContentQualificationCard({ item, view, @@ -485,21 +525,15 @@ function ContentQualificationCard({ maxLength={180} /> - -

- {requirementCopy} -

+ )} @@ -708,21 +742,15 @@ function GateCard({ maxLength={180} /> - -

- {requirementCopy} -

+ )} diff --git a/apps/web/src/pages/session-review/AlliancePulseCard.tsx b/apps/web/src/pages/session-review/AlliancePulseCard.tsx index f4bbf46..32c6968 100644 --- a/apps/web/src/pages/session-review/AlliancePulseCard.tsx +++ b/apps/web/src/pages/session-review/AlliancePulseCard.tsx @@ -15,6 +15,7 @@ import { type AlliancePulse, type AllianceScores, } from "./alliancePulseApi"; +import { ALLIANCE_SCALE } from "./allianceScale"; import "./alliance-pulse.css"; interface PulseTurn { @@ -76,14 +77,6 @@ const PERSPECTIVE_COPY: Record< supervisor_human: { label: "교수자 판정", provenance: "교수자 근거 판정" }, }; -const SCALE = [ - { value: 0, number: "1", label: "전혀 아니다" }, - { value: 0.25, number: "2", label: "조금 아니다" }, - { value: 0.5, number: "3", label: "보통이다" }, - { value: 0.75, number: "4", label: "대체로 그렇다" }, - { value: 1, number: "5", label: "매우 그렇다" }, -] as const; - const EMPTY_DRAFT: ScoreDraft = { goal: null, task: null, bond: null }; const POLL_INTERVAL_MS = 2_000; @@ -175,7 +168,7 @@ function AxisAssessmentControl({ {copy.prompt}
- {SCALE.map((option) => ( + {ALLIANCE_SCALE.map((option) => (
)} -
- {DIMENSIONS.map((dimension) => ( -
-
{DIMENSION_COPY[dimension].short}
-
{scoreText(pulse.self_scores?.[dimension] ?? null)}
-
- ))} -
+ ); } @@ -378,14 +377,7 @@ function SelfOnlyState({ pulse }: { pulse: AlliancePulse }) { 저장한 내 치료 동맹 자기평가

내가 직접 잠근 세 축만 보존합니다. AI·교수자 파생 비교는 표시하지 않습니다.

-
- {DIMENSIONS.map((dimension) => ( -
-
{DIMENSION_COPY[dimension].short}
-
{scoreText(pulse.self_scores?.[dimension] ?? null)}
-
- ))} -
+ ); } diff --git a/apps/web/src/pages/session-review/CalibrationTransferCard.tsx b/apps/web/src/pages/session-review/CalibrationTransferCard.tsx index 09002c2..c4a08ee 100644 --- a/apps/web/src/pages/session-review/CalibrationTransferCard.tsx +++ b/apps/web/src/pages/session-review/CalibrationTransferCard.tsx @@ -968,7 +968,8 @@ export function CalibrationTransferCard({ session_id: sessionId, competency_id: editableHistory?.competency_id ?? competencyId, practice_block_id: - editableHistory?.practice_block_id ?? `oas-g5-block-session-${sessionId.toLowerCase()}`, + editableHistory?.practice_block_id ?? + `oas-g5-block-${sessionId.toLowerCase()}-${(editableHistory?.competency_id ?? competencyId).replace(/^competency\./, "").toLowerCase().replace(/[^a-z0-9-]/g, "-")}`, scenario_variant_id: editableHistory?.scenario_variant_id ?? `session-review-${sessionId}`, phrase_family_id: editableHistory?.phrase_family_id ?? `self-forecast-${competencyId.replace(/^competency\./, "")}`, @@ -978,7 +979,7 @@ export function CalibrationTransferCard({ confidence: confidence / 100, recorded_sequence: latest ? latest.recorded_sequence + 1 : 1, revision_reason: reason.trim(), - instrument_id: "vignette.calibration-self-prediction", + instrument_id: "calibration-mirror-g5", instrument_version: "1.0.0", evidence_turn_ids: [] as string[], }; diff --git a/apps/web/src/pages/session-review/allianceScale.ts b/apps/web/src/pages/session-review/allianceScale.ts new file mode 100644 index 0000000..a77e4f7 --- /dev/null +++ b/apps/web/src/pages/session-review/allianceScale.ts @@ -0,0 +1,7 @@ +export const ALLIANCE_SCALE = [ + { value: 0, number: "1", label: "전혀 아니다" }, + { value: 0.25, number: "2", label: "조금 아니다" }, + { value: 0.5, number: "3", label: "보통이다" }, + { value: 0.75, number: "4", label: "대체로 그렇다" }, + { value: 1, number: "5", label: "매우 그렇다" }, +] as const; diff --git a/apps/web/src/pages/session/AllianceCheckpointPrompt.tsx b/apps/web/src/pages/session/AllianceCheckpointPrompt.tsx index 510b986..d9e7cf3 100644 --- a/apps/web/src/pages/session/AllianceCheckpointPrompt.tsx +++ b/apps/web/src/pages/session/AllianceCheckpointPrompt.tsx @@ -8,6 +8,7 @@ import { type AlliancePulse, type AllianceScores, } from "../session-review/alliancePulseApi"; +import { ALLIANCE_SCALE } from "../session-review/allianceScale"; import "./alliance-checkpoint-prompt.css"; type ScoreDraft = Record; @@ -21,14 +22,6 @@ interface AllianceCheckpointPromptProps { const DIMENSIONS: AllianceDimension[] = ["goal", "task", "bond"]; const EMPTY_DRAFT: ScoreDraft = { goal: null, task: null, bond: null }; -const SCALE = [ - { value: 0, number: "1", label: "전혀 아니다" }, - { value: 0.25, number: "2", label: "조금 아니다" }, - { value: 0.5, number: "3", label: "보통이다" }, - { value: 0.75, number: "4", label: "대체로 그렇다" }, - { value: 1, number: "5", label: "매우 그렇다" }, -] as const; - const CHECKPOINT_COPY: Record< Exclude, { @@ -286,7 +279,7 @@ export function AllianceCheckpointPrompt({ {prompt.prompt}
- {SCALE.map((option) => ( + {ALLIANCE_SCALE.map((option) => (