Commit Graph
457 Commits
Author SHA1 Message Date
kimandClaude Opus 5 4f31af0593 v4.3.23: 시스템 프롬프트 조립을 system-prompt.ts로 분리 + 테스트 19개
tool-scope.ts와 같은 이유로 추출. 시스템 메시지는 모델 동작을 가장 크게 좌우
하는 단일 문자열인데, 2,900줄 함수 안에서 인라인으로 조립돼 결과를 보려면 실제
채팅을 보내 SSE의 tool_overhead를 읽는 수밖에 없었음.

- src/gateway/chat/system-prompt.ts (신규) — buildChatSystemPrompt(input).
  날짜/모델프로필/성격/스킬 컨텍스트는 호출자가 해결해 넘기므로 순수 함수
- tests/system-prompt.test.ts — 19개
- handle-chat.ts 2,992 → 2,936줄 (오늘 누적 3,131 → 2,936)

테스트가 고정한 핵심 불변식: 도구를 억제하는 규칙은 그 도구가 호출 가능한 한
반드시 함께 온다. image_edit이 스키마에 있으면 "함부로 편집하지 말라"가 없을
수 없다 — 이걸 사용자 문구로 게이팅했다면 정규식이 놓친 표현에서 규칙만 조용히
사라졌을 것. 반대로 tool-scope.ts의 키워드 게이팅은 최악의 경우가 "도구가 없어
못 한다"이므로 허용된다. 두 모듈 헤더에 이 구분을 명시해둠.

동작 무변경 검증: 리팩터 전후 tool_overhead가 "안녕" 3,574 / "RTX 5090 스펙
비교" 3,724로 바이트 단위 동일. 전체 66개 테스트 통과.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 16:09:02 +09:00
kimandClaude Opus 5 6461e73a5e v4.3.22: 도구 선택 로직을 tool-scope.ts로 분리 + 테스트 17개 — 버그 3개 발견
handleChat()에서 도구 선택 로직(145줄)을 순수 모듈로 추출. 이 로직은 2,959줄
함수 안의 클로저라 테스트가 불가능했고, 게이트를 확인하려면 게이트웨이를 띄우고
실제 채팅을 보내 SSE의 토큰 수를 읽는 수밖에 없었음.

- src/gateway/chat/tool-scope.ts (신규) — I/O 없는 순수 함수.
  buildSkillToolFilter()는 스키마 객체가 아니라 도구 '이름'을 받게 해서
  테스트에서 문자열만으로 구동 가능하도록 함
- tests/tool-scope.test.ts — 17개. 비용(불필요한 도구 미노출)과
  도달성(요청한 도구는 반드시 노출) 양쪽을 모두 고정
- handle-chat.ts 3,131 → 2,992줄

추출과 동시에 테스트가 버그 3종을 찾음:

1. 한글 뒤 \b 는 절대 매칭되지 않음 (죽은 패턴 5개)
   \b는 ASCII 단어경계라 한글 음절 뒤에서는 성립하지 않음. `메일\b`가 그 예로,
   "메일 확인해줘"가 게이트를 통과하지 못해 이메일 도구가 아예 노출되지 않았음
   ("이메일"이라고 써야만 동작). 같은 죽은 패턴이 hasCodeKeyword(함수/클래스/
   버그), hasNewsKeyword(기사), hasPptxKeyword(덱)에도 있었음. 배포 후 확인:
   "메일 확인해줘"가 email_list를 정상 호출함

2. pptx 게이트 조건 반전
   `presenterEnabled && !hasPptxKeyword`라 스킬이 '꺼진' 경우 배제가 아예
   동작하지 않았음. 스킬이 꺼졌으면 더 제한해야 하는데 반대로 열려 있었던 것.
   실제 영향 범위(skills_state.json 대조): papa·cherry는 presenter=true라 무관,
   jasmine만 매 턴 ~1,600토큰(create_presentation 833은 최대 스키마)을 지고
   있었음. weather/legal과 같은 형태로 정정

3. (테스트 작성 중) 원문을 줄여 쓴 케이스가 실패 → 이전 커밋에서 수정한
   SPEC_CONTEXT_KEYWORD 구멍과 동일 계열임을 재확인

전체 47개 테스트 통과. 실측 오버헤드는 papa 기준 3,574로 동일(위 2번이
papa에겐 원래 정상 동작했으므로 예상된 결과).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 16:03:23 +09:00
kimandClaude Opus 5 2ba39bca76 v4.3.21: 테스트 인프라 복구 — npm test 실행 가능하게 + 회귀 테스트 30개
tests/ 디렉터리가 비어 있었고 package.json의 두 스크립트가 삭제된 파일을
가리킨 채 방치돼 있었음:
- test    → tests/test-v2.ts (존재하지 않음)
- gateway → src/gateway/server-v2.ts (v4.3.5에서 삭제됨)

즉 npm test가 몇 달간 실행 자체가 불가능한 상태였고, 그 대가로 프롬프트 게이트
정규식을 고칠 때마다 node -e 임시 스크립트를 손으로 짜고 버리는 일이 반복됐음
(2026-07-29 하루에만 5회). 회귀 방지는 하나도 남지 않았음.

- Node 22 내장 러너(node:test) + tsx 사용, 테스트 프레임워크 의존성 없음
- tests/prompt-gates.test.ts (25) — 모델 동작을 강제/억제하는 정규식 게이트 전반
- tests/usage-log.test.ts (5)  — Ollama 외 provider의 토큰 집계 단일 지점
- tests/README.md — 무엇을 왜 테스트하는지, 케이스 작성 규칙, 다음 확장 대상
- 전체 30개 통과, 0.6초

부수 성과 — 테스트가 실제 버그를 찾음:
케이스를 쓰면서 프로덕션 원문을 한 단어(GPU) 줄여 썼더니 실패했고, 그게 진짜
구멍이었음. "토큰 처리 속도 손실이 약 15%~25%"처럼 하드웨어 명사가 없는 처리량
날조는 SPEC_CONTEXT_KEYWORD에 걸리지 않아 그대로 통과하고 있었음 — 원문에
우연히 들어있던 "GPU" 덕에 잡히던 것. 토큰/대역폭/추론/처리속도를 키워드에
추가해 막고, 그 케이스를 테스트로 고정함(강수확률·할인율 오탐 없음 확인).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 15:29:02 +09:00
kimandClaude Opus 5 8a121457ff v4.3.20-3: ollama_web_* 도구 키워드 게이팅 — 오버헤드 214토큰 절감
ollama_web_search / ollama_web_fetch가 매 턴 무조건 스키마에 포함되고 있었음.
두 도구는 자기 description에 "뉴스·정보 검색은 반드시 web_search를 사용. 이
툴은 web_search가 없는 환경에서만 fallback으로 사용"이라고 적혀 있어서, 모델
에게 "이 도구를 쓰지 말라"고 알리는 데 214토큰을 쓰던 셈. 게다가 그 폴백
체인은 이미 executeWebSearch 내부에서 서버가 처리하므로 모델이 직접 부를
이유가 없음.

삭제가 아니라 게이팅을 택함: personality-context.ts에 "올라마로 검색" 류
표현에만 뜨는 ollama_web 힌트 블록이 이미 있어서, 도구를 제거하면 그 힌트가
존재하지 않는 도구를 설명하게 됨. 같은 표현으로 게이트해 평소엔 0토큰,
명시 요청 시엔 정상 동작하도록 함.

실측: 3,788 → 3,574. "올라마로 검색해줘" 요청에서는 4,980으로 도구가 정상
복귀하는 것도 확인.

조사 중 정정: eonet_events/satellite_snapshot과 run_command는 이미
skillToolFilter에 게이트가 있었음(각각 hasSatelliteKeyword, hasCodeKeyword).
초기 분석에서 필터 전체를 보지 못해 "무조건 켜짐"으로 잘못 판단했던 것을
바로잡음 — 실제 always-on은 8개(1,109토큰)뿐이며 07-26 스코핑 작업이
이미 대부분을 처리해둔 상태.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 15:18:06 +09:00
kimandClaude Opus 5 37257801ed v4.3.20-2: 도구 전용 프롬프트 블록 4개 조건부 전환 — 오버헤드 542토큰 절감
특정 도구를 제약하려고 존재하는 블록들이 도구 유무와 무관하게 매 턴 무조건
주입되고 있었음. 기존 BROWSER RULE이 쓰던 도구 목록 기반 게이팅을 그대로 확장.

조건부로 전환한 블록(측정 정적 비용):
- IMAGE EDITING RULE (90)      → image_edit 있을 때만
- IMAGE/VIDEO GENERATION (225) → image_generate/video_generate 있을 때만
- CODE OUTPUT (154)            → coder_* 있을 때만
- PACKAGE INSTALL (79)         → shell/run_command 있을 때만

실측: tool_overhead 4,330 → 3,788 (-542, 12.5%). 서로 다른 두 질문에서
동일하게 542 감소 확인.

메시지 문구가 아니라 도구 존재 여부로 게이트한 것이 핵심. "업로드한 사진을
함부로 편집하지 말라" 같은 억제 규칙은 그 도구가 호출 가능한 동안 항상 떠
있어야 하며, 정규식 문구 매칭으로 게이트하면 정규식이 놓친 표현에서 규칙이
사라진다 — 오늘 환각 조사에서 확인한 것과 같은 실패 모드.

CHEMISTRY NOTATION(180)은 의도적으로 무조건 유지: 도구가 아니라 출력 형식을
제약하므로 키로 삼을 도구가 없고, 치과/의학 질문은 화학 키워드 없이도 모델이
구조식을 그리게 만들 수 있어 문구 기반 게이팅이 위험함.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 15:08:48 +09:00
kimandClaude Opus 5 75b35eede1 v4.3.20-1: USER.md에 클로서버 대시보드 주소 확정 기록
모델이 "클로서버.local"이나 ":3000" 같은 존재하지 않는 주소를 지어내던 것을
막기 위해, 실제 주소(192.168.0.5:18789 / localhost:18789)와 함께 "그 값들은
실재하지 않으니 답변에 쓰지 말 것"을 USER.md에 명시.

USER.md는 벡터 메모리(Chroma)로 대체된 것이 아니라 병행 사용됨 — 벡터 쪽은
관련도 기반이라 사용자가 주소를 명시적으로 묻지 않으면 유사도 컷을 못 넘고,
Chroma 조회 실패 시엔 조용히 빈 값이 되므로, 항상 떠 있어야 하는 이런 사실은
전량 주입되는 USER.md에 있어야 한다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 14:59:20 +09:00
kimandClaude Opus 5 e85ebe6608 v4.3.20: 미커밋 자산 일괄 정리 + 유저 워크스페이스/세션토큰 gitignore
그동안 쌓여 있던 미커밋 파일들을 정리. 소스·자산만 올리고, 개인 데이터와
런타임 상태는 gitignore로 차단.

추가된 자산:
- web-ui/audiomass, web-ui/wavacity — 자체 호스팅 오디오 에디터
- web-ui/drums — 드럼 샘플
- wol-gate — WOL 프록시 컨테이너(Dockerfile + server.js)
- glm-1m-fix — GLM 1M 컨텍스트 설치 스크립트(linux/windows)
- RENODE_FIXES_SUMMARY*.md, CCTV정보_서울특별시.csv, .claude/plans

수정:
- edge_tts_synth.py: rate 인자 추가(말하기 속도 조절)
- .smallclaw/config.json: google(gemini) provider 등록 — 키는 vault 참조
- assets/SmallClaw*.png 삭제분 반영

gitignore (중요):
- .smallclaw/users/*/workspace/ 전체 차단. 기존 패턴이 memory/uploads와 일부
  확장자만 막아서 detective/(고소장·통화녹취·의사소견서·가족관계증명서),
  generated-media/, music/, pptx/ 등 앱 생성 개인 데이터 403MB가 노출돼 있었음
- .smallclaw/active-sessions.json 차단 — 살아있는 bearer 토큰과 admin 역할이
  평문으로 들어있어 커밋 시 게이트웨이 관리자 자격증명이 그대로 공개됨
- google-usage.json, config.json.bak-*, workspace/memory·weather-maps·
  task_result_*, voice/ 등 런타임 상태도 함께 차단

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 14:55:37 +09:00
kimandClaude Opus 5 8359901cac v4.3.19-26: 환각 방어 전역화 + 답변 길이 압박 완화 + 토큰 로깅 구멍 수정
실사용 로그 감사(07-29 GPU/PCIe 대화)에서 발견한 문제들 일괄 수정.

환각 방어:
- looksLikeUnverifiedSpecClaim이 놓치던 패턴 2개 추가 — 단위 없는
  "15토큰"(strict tier), 퍼센트 벤치마크 "약 5%"(ambiguous tier,
  SPEC_CONTEXT_KEYWORD 게이트로 오탐 방지)
- forceToolChoiceOnLiveData를 모델별 opt-in → 전역 기본 ON + opt-out.
  모델을 수시로 바꾸는데 profiles에 등재된 2개만 방어되던 구멍이 원인
  (프로필 없던 gemini-3.5-flash-lite가 tok/s 날조 + 없는 제품명 창작)

답변 길이:
- "Keep responses SHORT" 1-2 → 2-4문장, 대화/실행 답변으로 범위 한정.
  정보성 답변(뉴스·날씨·설명·비교)은 OUTPUT FORMAT을 따르도록 명시
- 불확실성 표현은 길이 제한 면제. 1-2문장 압박이 "모른다"고 말할 자리를
  없애 환각을 확신형 한 줄로 포장하던 것이 실측으로 확인됨
- 뉴스는 도구가 반환한 기사 전부 표기(3건 체리픽 금지, 8-10행 목표)

토큰 로깅:
- logUsage가 ollama-adapter 내부 private이라 Ollama 경유만 기록되고,
  openai-compat은 usage 필드를 추출조차 안 하고 버리던 문제. 비용 비교
  대상인 Gemini가 통째로 누락돼 공짜처럼 보이는 상태였음
- providers/usage-log.ts로 공용화 → openai-compat(chat/generate)과
  openai-codex(SSE response.completed)에서도 캡처·로깅·호출자 반환

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 14:44:21 +09:00
kimandClaude Sonnet 5 3bff465c08 v4.3.19-25: 탐정 앱 자체 WOPI 호스트로 NextCloud 없이 Collabora 직결
기존 col.applecherry.net Collabora 서버를 재사용하되, NextCloud(WebDAV
업로드/가져오기 왕복) 없이 홈클로 게이트웨이가 직접 WOPI 호스트 역할
수행 (CheckFileInfo/GetFile/PutFile). hwp는 사건 폴더 안에서 odt로
1회 변환 후 그 자리에서 바로 편집 — 타이핑하는 대로 사건 폴더에 즉시
저장되어 별도 "가져오기" 버튼이 필요 없어짐(dtFetchFromNextcloud 제거).
config.json에 collabora.baseUrl/publicUrl 추가. Express 5의
res.sendFile 이슈(이미 known) 재발 → createReadStream으로 우회.

실기기(col.applecherry.net + 실제 사건 고소장.odt)로 전체 라운드트립
검증 완료.

부수: 사이드바 사건목록이 빠른조사/법률질문 버튼 늘어나며 눌리는 문제
— 잘 안쓰는 버튼 정리(19개→11개)로 완화 + .dt-cases min-height 안전장치.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 14:18:18 +09:00
kimandClaude Sonnet 5 e180c3978e v4.3.19-24: 일별 메모리 추출 스크립트 깨진 import 경로 수정
memory-vector.ts가 src/gateway/memory/ 하위로 옮겨진 뒤 이 스크립트만
경로가 안 고쳐져서 MODULE_NOT_FOUND로 매일 새벽 1시 systemd 타이머가
3일 연속 즉시 실패하고 있었음(daily_extracts Chroma 컬렉션이 07-24
이후로 안 쌓이던 원인). 경로 수정 후 밀린 07-25~27 백필 확인.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 13:22:18 +09:00
kimandClaude Sonnet 5 8a7f6443fc v4.3.19-23: 글로벌 하트비트가 빈 워크스페이스를 보던 버그 수정
멀티유저 전환 후 USER.md/SOUL.md가 유저별 워크스페이스로 옮겨졌는데
HeartbeatRunner는 여전히 글로벌 워크스페이스(파일 없음)를 보고 있어서
30분마다 memory_read(user/soul)가 매번 "not found"로 실패하던 문제.
workspacePath를 papa 워크스페이스로, handleChat에 username도 함께
전달하도록 수정.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 13:22:09 +09:00
kimandClaude Sonnet 5 4c3d90c3d2 v4.3.19-22: 탐정 앱 증거자료 파일/폴더 다운로드 버튼 + 관련 사이트 링크 추가
파일별 다운로드(download 속성), 폴더 전체 zip 다운로드(archiver 신규
의존성 + /api/detective/files/download-folder 라우트) 추가. 사이드바에
법률 서식(대한법률구조공단)·법원 소송 서식(전자소송포털)·대법원 판례
검색(사법정보공개포털)·경찰청 링크 블록 신설.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 13:21:59 +09:00
kim e39c430485 v4.3.19-21: NextCloud 편집 버튼(🌐/⬇️)을 odt 파일에도 확장
hwp에서 가져온 odt 사본을 다시 편집하려 해도 버튼이 없었음 — 이제
.odt 파일도 hwp와 동일하게 🌐(NextCloud에서 열기)/⬇️(가져오기+PDF
변환) 대상이 됨. 백엔드는 이미 비-hwp 파일을 변환 없이 그대로
업로드/처리하도록 되어 있어서 프론트엔드 조건만 확장하면 됐음.
2026-07-27 23:43:52 +09:00
kim 667a539343 v4.3.19-20: NextCloud 가져오기에 odt→PDF 자동 변환 추가
h2orestart는 hwp 내보내기(쓰기)를 지원 안 해서 편집본을 진짜 hwp로
되돌릴 방법이 없음(리눅스에서 근본적으로 불가) — 대신 odt→PDF는
특별한 필터 없이 표준 변환이라 이걸로 실질적 요구(제출용 PDF)를 충족.

⬇️ 가져오기 시 odt와 함께 PDF도 자동 생성. 파일명은 "{이름}
(NextCloud변환).pdf"로 접미사를 붙여서, 같은 폴더에 이미 있는 원본
증거자료 PDF(예: 고소장.pdf)를 덮어쓰지 않도록 함 — 실제로 이 충돌
가능성을 발견하고 배포 직전에 수정함.
2026-07-27 23:32:43 +09:00
kim 6cfb2db5b1 v4.3.19-19: 탐정 앱 hwp 파일 NextCloud/Collabora 실시간 편집 연동 + 지도탭 누락 커밋 반영
주요 변경:
- 탐정 앱의 .hwp 파일에 🌐 버튼 추가: 서버가 자동으로 hwp→odt 변환(host의
  h2orestart 필터 사용) → 사용자 소유 NextCloud(cloud.applecherry.net)의
  homeclaw-detective/{caseId}/ 폴더로 WebDAV 업로드 → Collabora Online
  편집화면으로 리다이렉트. 타이핑하는 대로 자동 저장, 다운로드/재업로드 불필요.
- NextCloud 앱 비밀번호는 vault에 저장(nextcloud.appPassword), config.json엔
  평문 없음.
- (부수적, 별도 이슈) Stirling-PDF는 이 사건의 원본 PDF가 실제 폼필드가
  없는 인쇄용 레이아웃이라 텍스트 스트림 편집이 계속 실패해서 우회 경로로
  채택함.

추가로 이전 세션에서 커밋 안 하고 넘어간 weather-app.html의 지도탭
자동전환 fix(processWindyUrl 이동 시 mapLat/mapLon 동기화 + switchCenterTab)도
같이 반영됨.
2026-07-27 22:56:45 +09:00
kim ebb605bc24 v4.3.19-18: PDF 편집기 자동다운로드 되돌림 - 사용자가 원치 않음
작업 디렉토리에 로컬로 접근 가능해서 Stirling-PDF 업로드 대화상자에서
직접 파일을 찾아 선택하는 걸 선호함. 클릭할 때마다 자동 다운로드되는
건 불필요한 부작용이었음. 툴팁만 업로드 버튼에서 직접 선택하라고
안내하도록 수정.
2026-07-27 21:57:43 +09:00
kim ec62051251 v4.3.19-17: 탐정 앱 PDF 편집기 — 파일 자동 오픈 불가를 다운로드 보조로 완화
원인: /pdf-editor-open은 caseId만 전달하고 파일명은 전혀 안 넘김 —
Stirling-PDF(외부 PDF 편집기)의 실제 프론트엔드 번들을 직접 열어봤는데
"token" 쿼리파라미터 하나만 읽고 원격 파일 로드 기능 자체가 없음(자체
storage API는 있지만 별도 로그인 필요, 우리는 미설정). 즉 특정 파일을
자동으로 여는 건 우리 쪽에서 고칠 수 있는 버그가 아니라 외부 툴의
한계임을 확인.

대신 ✏️ 클릭 시 편집기 새창 열기와 동시에 그 파일을 다운로드시켜서,
사용자가 편집기 창에 끌어다 놓기만 하면 되도록 마찰을 최소화. 툴팁도
실제 동작을 정확히 설명하도록 수정.
2026-07-27 17:37:08 +09:00
kim bb0f44b0c3 v4.3.19-16: 탐정 앱 메모 textarea에서 Tab 키로 들여쓰기 가능하도록 수정
기본 브라우저 동작은 Tab을 누르면 포커스가 다음 요소로 넘어가버려서
메모 안에서 들여쓰기를 못 했음. keydown에서 Tab을 가로채 탭 문자를
커서 위치에 삽입하고 포커스는 textarea에 유지.
2026-07-27 15:19:37 +09:00
kim f49d7a903c v4.3.19-15: satellite_snapshot 이미지도 SSE로 직접 주입 (모델이 경로 오타내는 문제)
실사용 중 발견: 모델이 satellite_snapshot 결과의 /api/files/... 마크다운을
답변에 그대로 복사하지 않고 재작성하면서 /api/files/ 접두사를 빠뜨림 —
그러면 클라이언트 정규식이 매치 안 돼서 사진탭이 아예 안 열림.
weather_map_screenshot과 같은 패턴으로, 도구 결과의 마크다운을 그대로
SSE로 주입해서 모델의 재현 정확도에 의존하지 않게 함. 태풍 돌핀 실제
NASA 위성사진으로 재현·검증 완료.
2026-07-27 12:18:32 +09:00
kim ce9f556e84 v4.3.19-14: 위성사진 위치→지도 이동을 클라이언트에서 결정론적으로 처리
실제 테스트에서 발견: "지도 이동해줘" 후속 요청에 모델(deepseek-v4-flash)이
도구를 재호출하지 않고 이전 응답을 그대로 반복하는 경우가 많아 프롬프트
가드만으로는 신뢰도가 부족했음. 근본 해결로 사진 뷰어에 "지도에서 위치
보기" 버튼 추가 — satellite_snapshot 파일명에 이미 박혀있는 좌표를
그대로 파싱해 지도를 이동시키므로 모델 개입이 전혀 필요 없음.

부수적으로 execute-tool.ts에도 weather_map_screenshot 성공 시 그 URL을
SSE로 직접 주입하는 방어선 추가(모델이 실제로 도구를 호출했을 때 대비).
2026-07-27 12:11:44 +09:00
kim 0b9ce3d18a v4.3.19-13: Windy 지도 "이동" 후속요청 무시 버그 수정
실제 로그 분석 결과: "위치로 이동" 같은 후속 요청은 트리거 단어 목록에
없어서 모델이 Windy URL을 재출력하지 않고, 대신 weather_map_screenshot
(서빙 안 되는 모델 전용 스크린샷 도구) 결과를 [REDACTED-HE].jpg 같은
가짜 마크다운 이미지로 답변에 끼워넣음 — 실제 지도는 전혀 안 움직이고
깨진 이미지 링크만 표시됨. 후속 이동 요청도 트리거로 인정하고,
screenshot 도구 결과를 이미지로 삽입하지 말라는 가드 추가.
2026-07-27 11:54:51 +09:00
kim ced421cac9 v4.3.19-12: 스킬 프롬프트 소스 가독성 개선 — \uXXXX 이스케이프 → 실제 한글
accountant/lawyer/investor-app의 SKILL_CONTENT가 모든 한글을 6자리
유니코드 이스케이프(\uXXXX)로 저장하고 있어 소스 읽기가 사실상 불가능했음.
디코드→재인코딩 스크립트로 실제 UTF-8 텍스트로 변환, 디코드된 내용은
변환 전후 완전히 동일함을 스크립트로 검증. 기능 변화 없음, 가독성만 개선.
2026-07-27 10:54:39 +09:00
kim f615475618 v4.3.19-11: 어제 추가한 가드 텍스트 오탈자 수정
investor-app에 유니코드 이스케이프를 손으로 옮기다 "상호명이"가
"사이앨목이"로 깨졌던 것 수정, lawyer-app 조사 오류 2건 수정.
2026-07-27 10:48:10 +09:00
kim 5ac766381a v4.3.19-10: investor/lawyer/detective 스킬에 식별자 대조 가드 추가
날씨 스킬에서 태풍 번호 혼동 사고 확인 후, 같은 패턴(단일 소스에서
가져온 데이터를 다른 개체에 잘못 귀속) 위험이 큰 세 스킬에 가드 추가:
- investor: 종목코드/거래소/보통주-우선주/분기 대조
- lawyer: 법령 시행일·개정판, 판례 사건번호 전체 일치 대조
- detective: 동명이인·유사상호 — 추가 식별자 없이 귀속 금지
2026-07-27 10:38:22 +09:00
kim 72855ea77a v4.3.19-9: 날씨 스킬 프롬프트에 태풍 번호/이름 대조 가드 추가
실제 대화에서 KMA 통보문 단일 페이지만 보고 제13호 태풍 돌핀의 좌표를
제12호 태풍 노을(이미 광둥성 상륙·소멸)의 것으로 착각해 답한 사례 발견.
번호 대조 + 한/영 교차검색 지침 추가로 재발 방지.
2026-07-27 10:31:52 +09:00
kim 74af6fdfd3 v4.3.19-8: eonet_events 설명에 유럽/중동/아프리카 커버리지 부족 명시
EFFIS(유럽 산불 전용 DB) 공개 WFS를 붙여볼까 테스트했으나 GetCapabilities조차
35초+ 걸리다 500 에러 — 신뢰성 부족으로 보류. 대신 EONET이 북미 편향이라는
사실을 도구 설명에 적어 모델이 유럽 등 이벤트는 자동으로 web_search 우회하게 함.
2026-07-26 19:10:37 +09:00
kim 071e36110e v4.3.19-7: NASA EONET/Worldview 위성사진 기능 + weather-app 멀티탭 뷰어
- eonet_events/satellite_snapshot 도구 추가 (키 불필요 공개 API)
- weather-app.html: 위성사진 채팅 이미지 렌더링, 지도/사진 멀티탭 센터패널,
  확대/축소·드래그팬, localStorage+히스토리 이중 복원으로 새로고침 시
  탭 유지(하드리셋으로 localStorage가 지워져도 대화 이력에서 재구성)
2026-07-26 18:43:20 +09:00
kimandClaude Sonnet 5 a80ad9e809 v4.3.19-6: Google 일일 사용량 표시 + 한도 도달시 자동 폴백
- google-usage.ts: Gemini 무료티어 RPD(하루 500회 추정치) 대비 요청 카운트를
  날짜별로 추적(.smallclaw/google-usage.json, 자정 자동 리셋)
- openai-compat-adapter.ts: google provider일 때 post() 호출마다 카운트
- factory.ts: getProvider()/getModelForRole() 둘 다 getEffectiveProviderConfig()
  경유하도록 통일 — 카운트가 480(여유분 20 적용) 넘으면 자동으로
  ollama+deepseek-v4-flash:cloud로 전환(429 맞고 반응하는 게 아니라 사전 차단).
  provider/model 선택이 별도 함수라 따로 고치면 불일치 날 수 있어서 공유
  헬퍼로 묶음
- 메인 채팅 사이드바 모델 pill에 "N/500" 뱃지 추가(Google일 때만 표시,
  70%↑ 노랑/90%↑ 빨강), 기존 10초 폴링(checkStatus)에 얹어서 실시간 반영
- 실사용 확인: 배포 직후 실제 채팅으로 카운트 0→1 증가 확인, 480 시뮬레이션
  으로 폴백 전환/복귀 양쪽 다 확인

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 17:37:03 +09:00
kimandClaude Sonnet 5 814f32a63e v4.3.19-5: 컨텍스트 게이지 provider-aware하게 수정 + 캐시 버그 제거
- resolveNumCtx()가 Ollama 전용이라 primary가 Google로 바뀐 뒤 세션
  컨텍스트 게이지가 419%까지 잘못 표시되던 버그 수정 — google provider면
  Gemini API에서 inputTokenLimit을 실시간 조회(하드코딩 아님, API 실패시만
  1048576 폴백)
- 최종 폴백(8192)을 캐시하던 버그 제거 — 재시작 직후 첫 조회가 일시적으로
  실패하면 프로세스 수명 내내 8192에 고정되던 문제, 이제 재시도 가능
- /api/model-context: model 파라미터 없을 때(메인 채팅 사이드바 게이지)
  무조건 200000(클로드 가정) 반환하던 걸 resolveNumCtx() 재사용으로 교체
  — 이제 실제 활성 provider와 항상 동기화됨

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 17:17:14 +09:00
kimandClaude Sonnet 5 5f83803ed4 v4.3.19-4: Google(Gemini) LLM provider 추가
- LLMProvider.ts/factory.ts: 'google' ProviderID 추가, 기존 openai_compat
  어댑터를 Gemini의 OpenAI 호환 엔드포인트로 재사용(새 어댑터 클래스 없음).
  api_key는 resolveEnvKey가 아닌 getConfig().resolveSecret()로 해석 —
  vault: 참조를 실제로 못 푸는 기존 openai/anthropic 케이스의 버그를
  google 케이스에서는 피해감
- config.ts: llm.providers.google 기본값 추가(기본모델 gemini-3.6-flash),
  SECRET_FIELD_MAP에 api_key 자동 vault 이관 등록
- 설정 화면(index.html/settings-provider.js): LLM 제공자 드롭다운에
  Google 옵션 + API키/모델 선택 UI, anthropic 패턴 그대로 재사용
- openai-compat-adapter.ts: 기본 max_tokens 512→4000 — 사고형 모델
  (gemini-3.6-flash)이 내부 사고 토큰에 예산을 다 써서 빈 응답 반환하는
  버그 실측 후 수정(llama_cpp/lm_studio/openai/google 전부 공유하는 값)
- 5문항 벤치마크로 gemini-3.6-flash/3.5-flash-lite/3.1-flash-lite 비교,
  secondary 후보로 3.5-flash-lite 확정(정직함 동률 최상위+5배 빠름+저가)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 15:50:26 +09:00
kimandClaude Sonnet 5 f5ae095837 v4.3.19-3: 도구 스키마 컨텍스트 스코핑 + web-ui/js 서브디렉토리 정리
- handle-chat.ts: excel/coder+shell/email/kakao/sqlite_query/image_edit/
  image_read/run_command/news_search/pdf_*/schedule_job를 전용 앱 세션·
  키워드·첨부 신호로 스코핑 (기존 weather/legal/pptx/dental/mind 패턴과
  동일). 일반 대화 기준 도구 스키마 오버헤드 10,204→3,951 토큰(-61%)
- desktop_*(10개, Windows 전용) 완전 제외 — 이 리눅스 서버에서 전량
  실패 확인(tool_audit.log), process.platform 체크라 윈도우 이전시 자동 복구
- 채팅 기본 num_predict 4096→6000 (사고형 모델 빈 응답 여유분)
- ollama-adapter.ts: 턴마다 prompt/completion 토큰을 token-usage.jsonl에
  기록 (Ollama Cloud vs 종량제 API 비용 비교용)
- web-ui/js/ 25개 파일을 app/·code-editor/·voice/·music/·claude-app/
  서브디렉토리로 재배치, 전체 참조 경로 갱신
- package.json build 스크립트에 dist 클린 단계 추가

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 14:58:32 +09:00
kimandClaude Sonnet 5 0963231327 v4.3.19-2: 회계사 앱 스프레드시트 뷰어 수정 + helpers.js 5개 파일 분리
- coder_write_file이 xlsx/pptx/docx/pdf/zip 같은 바이너리 확장자로 파일을
  만들려 하면 차단 (텍스트 escape로 바이너리를 흉내내다 파일이 깨지던 문제)
- 레거시 HTML 리다이렉트(/xlsx-viewer.html 등)가 쿼리스트링을 버리던 버그 수정
- xlsx-viewer: "새 시트"가 존재하지 않는 __new__.xlsx를 fetch하다 404나던
  것을 빈 워크북 생성 후 저장 시 파일명 입력받는 흐름으로 교체
- xlsx-viewer: LuckyExcel 변환이 내놓는 손상된 숫자 서식("???.???")을
  Luckysheet SSF 파서가 못 읽고 죽던 문제에 sanitizer 적용
- 회계사 앱 업로드가 존재하지 않는 /api/upload를 호출하던 것을
  /api/upload/file로 수정
- web-ui/js/helpers.js(2240줄)를 helpers-core/markdown/voice/media/files.js
  5개로 분리 (기능 변경 없음, 함수 단위 1:1 이관 확인됨)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 21:30:05 +09:00
kimandClaude 4d0404ea0d v4.3.19-1: Helpers 섹션을 helpers.js로 분리 + escHtml 중복 제거
app.js 4728-6961(Helpers 섹션 전체)을 web-ui/js/helpers.js로 추출.
escHtml/_normalizeFileUrl/_renderLinkButton, renderMarkdown+embed 변환기,
TTS/voice(mic 포함), music/MIDI, smiles/tradingview, 파일 미리보기/업로드,
동적 로더(SheetJS/Luckysheet/PDF.js) 등 75개 함수 + midiRouter 객체.

모든 함수를 전역 function 선언으로 유지(모듈 래핑 안 함)해 inline onclick
문자열(downloadFile, openImageZoom, togglePdfEmbed, playTTSMsg, ...)이
그대로 동작. state vars(_xxxLoaded, stagedAttachments, _tts*, _appSendFn
등)도 함께 이동.

escHtml 중복(원래 3625, dead code — 5412 버전이 이김) 제거로 단일 정의.
escapeHtml(이메일 렌더링 전용, 다른 매핑)은 app.js에 유지.

index.html: voice-call → helpers → chat-sse → app 순서로 로드.
node --check 통과, 재시작 후 /js/helpers.js 200 확인.

다음(B2)에서 helpers.js를 5개 파일로 세분화 예정.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-25 20:07:32 +09:00
kimandClaude e2dc133934 v4.3.19: sendChat SSE 함수를 chat-sse.js로 분리
app.js 1411-2092의 async function sendChat을 web-ui/js/chat-sse.js로
추출. 범위 내 top-level 선언은 sendChat 하나뿝이라 외부 노출 대상은
sendChat 단일. 끝에 window.sendChat 할당 추가. app.js에서 제거 후
index.html에서 app.js 직전에 로드.

공유 상태(currentAbortController/isThinking)와 외부 헬퍼
(authHeaders/renderChatMessages/addProcessEntry 등)는 전역 그대로
참조. node --check 통과, 재시작 후 /js/chat-sse.js 200 확인.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-25 20:05:41 +09:00
kimandClaude 7b74b2315d v4.3.18-1: web-ui 로고 경로 상대→절대 /homeclaw_logo.png 수정
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-25 19:44:42 +09:00
kimandClaude 52b373f2d4 v4.3.18: web-ui 폴더 정리 - html/js/css 분리, server.ts 레거시 리다이렉트 추가
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-25 19:39:44 +09:00
kim d1032019de v4.3.17: handleCodeChat → gateway/chat/code-chat.ts 분리 보관
- handle-chat.ts에서 handleCodeChat(180줄) + _overlapLen 헬퍼를 별도 파일로 이동
  (handle-chat.ts 3,222→3,033줄, 189줄 감소)
- code-chat.ts는 createCodeChat 팩토리로 정의, handle-chat.ts에서 필요한 의존성만 주입
- server.ts는 기존 createHandleChat 팩토리 사용 그대로 유지, handleCodeChat은
  handle-chat.ts 내부에서 code-chat.ts 팩토리로 생성
- 빌드 성공 + 재시작 후 일반 채팅/code_ai 채팅(python_eval) 모두 정상 동작 확인
2026-07-25 19:24:14 +09:00
kim 41f7e702a9 v4.3.16: preflight advisor → gateway/chat/preflight-advisor.ts 분리 보관
- handle-chat.ts에서 preflight advisor 블록(1092-1284줄, 약 195줄)과
  replaceCurrentUserPromptWithAdvisorObjective 헬퍼를 별도 파일로 이동
  (handle-chat.ts 3,400→3,222줄, 178줄 감소)
- preflightRoute/preflightReasonForTurn은 getter/setter 뷰 객체로 연결하여
  메인 루프의 sanitizeFinalReply 및 route 조건에서 계속 사용 가능
- cron 스케줄러(server.ts)의 callSecondaryPreflight 사용은 그대로 유지
- 빌드 성공 + 재시작 후 일반 채팅/code_ai 채팅 모두 정상 동작 확인
2026-07-25 19:14:41 +09:00
kim 6271b65f84 v4.3.15: browser/desktop advisor closures → gateway/chat/browser-desktop-advisor.ts 분리 보관
- handle-chat.ts에서 maybeRunBrowserAdvisorPass(430줄) + maybeRunDesktopAdvisorPass(140줄)
  + 헬퍼 4개를 별도 파일로 이동 (handle-chat.ts 3,900→3,400줄, 500줄 감소)
- 메인 루프의 가변 상태는 그대로 두고, getter/setter 기반 advisorCtx 뷰 객체를 통해
  새 파일과 공유 → 메인 루프 변수명/조건식은 모두 유지
- 현재 배포에선 dormantBrowserTools / Linux desktop 제한으로 여전히 도달 불가능한
  코드이나, 향후 브라우저/데스크톱 자동화 복구 시 이 파일만 다시 연결하면 됨
- 빌드 성공 + 재시작 후 일반 채팅/code_ai 채팅 모두 정상 동작 확인
2026-07-25 19:04:50 +09:00
kim cf15061e39 v4.3.14: 죽은 코드 5개 정리 (readDailyMemoryContext, readMemoryCategories, renderTaskCandidatesForHuman, latestTaskForSession, buildBlockedTaskStatusMessage)
정의만 있고 호출부 없는 함수들. readDailyMemoryContext/readMemoryCategories는
Chroma 벡터 메모리(2026-07-24 도입, buildPersonalityContext의 [RECALLED_FACTS])가
관련성 기반 회상으로 대체하면서 이미 실질적으로 무용해진 상태였음.
2026-07-25 18:34:48 +09:00
kim 394d990fba v4.3.13: handleTaskControlAction → gateway/tasks/handle-task-control.ts 분리 (5,021→4,668줄)
3자 순환 의존성(executeTool→handleTaskControlAction→handleChat→executeTool) 발견:
- inferTaskChannelFromSession/findBlockedTaskForSession/parseTaskIdFromText는 실제로는
  handleChat에 의존하지 않는 순수 쿼리 헬퍼임을 확인하고 팩토리 밖으로 빼서 일반 export로
  전환 → handle-chat.ts가 deps 주입 없이 직접 import하도록 변경 (HandleChatDeps 2개 필드 제거)
- handleTaskControlAction 자체만 진짜 순환: launchBackgroundTaskRunner가 handleChat을 필요로
  하는데 handleChat이 executeTool을, executeTool이 handleTaskControlAction을 필요로 함
  → lazy binding으로 해결: executeTool 인스턴스화 이전에 hoisted wrapper 함수
  (let _handleTaskControlActionImpl + function handleTaskControlAction)를 선언해 dep로 넘기고,
  handleChat 생성 후 실제 구현을 대입

실제 백그라운드 태스크 생성→조회→삭제 왕복으로 순환 경로 전체 검증 완료.
2026-07-25 18:31:45 +09:00
kim dc546ab5e2 v4.3.12: buildPersonalityContext → gateway/chat/personality-context.ts 분리 (5,455→5,021줄)
detectToolCategories/TOOL_BLOCKS/readMemorySnippets 등 관련 헬퍼 클러스터를
createPersonalityContext(resolvePromptPath) 팩토리로 통째 이동. 외부 의존은
resolvePromptPath 하나뿐이라 순환 의존성 없이 분리 가능했음.
2026-07-25 18:23:50 +09:00
kimandClaude Sonnet 5 5ed28240f3 v4.3.11: handleChat()+handleCodeChat()을 gateway/chat/handle-chat.ts로 분리 (server.ts -3712줄)
오늘 분석에서 찾은 3대 핵심 엔진 함수(buildTools/executeTool/handleChat,
합쳐서 원래 6043줄=51%) 중 가장 크고 마지막 조각. handleChat 바로 앞에
정의된 형제 함수 handleCodeChat(코드 에디터 /api/chat 전용 경로)이 정확히
같은 의존성 집합을 공유하는 걸 확인하고 함께 이동 — 둘 다 buildTools/
executeTool/normalizeToolArgs/logToolCall/ToolResult를 그대로 씀.

app/config/server로 잡혔던 "외부 심볼 참조"는 buildTools 때와 동일하게
전부 도구 설명/주석 안 우연한 단어 오탐으로 확인. 실제 dep는 34개
(밖에서도 쓰이는 telegramChannel/skillsManager/broadcastWS/codeAiLocalSessions/
buildTools/executeTool 등 + handleChat 전용이지만 재귀적 의존성 파악 부담을
피하기 위해 dep로 남긴 로컬 헬퍼 30개), createHandleChat(deps) 팩토리로 주입.
MAX_TOOL_ROUNDS/ExecutionMode/SubagentProfile+TOOL_PROFILES(죽은 코드)는
server.ts에서 정리.

server.ts: 9167 → 5455줄. handle-chat.ts는 3903줄.

검증: 빌드 1차 시도에서 에러 4종류(HandleChatResult 미export, getModelForRole/
fs/path 미import)만 발생 — 나머지 30여개 dep는 전부 정확히 맞았음. 재시작 후
실제 채팅으로 (1) 일반 텍스트 (2) 도구 호출 (3) handleCodeChat 코드에디터 경로
(4) web_search (5) 백그라운드 태스크(BackgroundTaskRunner 경유 handleChat) —
5개 시나리오 전부 실제 왕복 검증. 백그라운드 태스크가 needs_assistance로 끝난
건 handleChat 문제가 아니라 이 환경의 오케스트레이션 disabled 설정 때문임을
daily memory 로그("1+1은 2입니다" 3회 정상 응답 확인)로 교차검증 후 확인.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 18:06:31 +09:00
kimandClaude Sonnet 5 0d2372d0df v4.3.10: executeTool()을 gateway/chat/execute-tool.ts로 분리 (server.ts -1417줄)
executeTool 전용으로만 쓰이던 헬퍼(SAFE_COMMANDS, buildUrlOpenCommand,
buildBrowserLaunchCommand, hasUriScheme, quoteShellArg, BLOCKED_PATTERNS,
lastFilenameUsed, normalizeScheduleJobAction, summarizeCronJob,
normalizeDeliveryChannel, ToolResult 타입)도 함께 이동 — 전부 executeTool
밖에서 전혀 참조되지 않는 것을 grep으로 확인 후 판단. 죽은 코드였던
isLinux(정의만 있고 미사용)도 이 김에 삭제.

밖에서도 쓰이는 10개(isPathInsideDir/cronScheduler/resolvePromptPath/
webSearch/webFetch/imageSearch/IMAGE_TYPES/codeAiLocalSessions/
handleTaskControlAction/broadcastWS)는 createExecuteTool(deps) 팩토리로 주입,
호출부 시그니처는 그대로 유지.

server.ts: 10584 → 9167줄. 빌드/재시작 후 실제 채팅으로 shell/coder_write_file/
web_search 3개 도구 실행 왕복 검증(생성 파일 확인 후 정리 완료), 기존
라우트 8개 재확인.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 17:46:46 +09:00
kimandClaude Sonnet 5 44de254ab4 v4.3.9: buildTools()를 gateway/chat/build-tools.ts로 분리 (server.ts -1201줄)
3개 핵심 엔진 함수(buildTools/executeTool/handleChat, 합쳐서 6043줄=51%) 중
가장 독립적인 것부터 분리 시작. 실제 참조하는 외부 심볼은
isOrchestrationSkillEnabled 단 하나뿐 — app/config/server는 텍스트 검색에서
도구 설명 문자열("node app.js" 등) 안 우연한 단어와 오탐이었음. 캐시(_toolsCache)
도 함수 스코프 안에서만 쓰여 통째로 이동. createBuildTools(dep) 팩토리 패턴으로
감싸 호출부 시그니처는 그대로 유지.

server.ts: 11785 → 10584줄. 빌드/재시작 후 실제 채팅 메시지로 coder_list_files
도구 호출 왕복까지 검증(HTTP 200 체크만으론 부족 — 실제 도구 실행까지 확인).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 17:32:50 +09:00
kimandClaude Sonnet 5 7dba6f8816 v4.3.8: fix — gateway 폴더 재구성 후 __dirname 기반 경로 6곳 깨짐 (치과사전 DB 500 등)
routes-*.ts를 routes/ 서브디렉토리로 옮기면서 import 경로는 스크립트로
재계산했지만, __dirname 기반 런타임 파일 경로(디렉토리 한 단계 더 깊어짐)는
놓쳤음. dental_dict.db·web-ui/schematics·scripts/pptx_preview.py·
edge_tts_synth.py(voice/writer/language 3곳) 전부 ../를 하나씩 추가.
실제 DB 통계(4809건)·검색 API로 재검증 완료.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 17:19:30 +09:00
kimandClaude Sonnet 5 f8fc787fe8 v4.3.7: 죽은 레거시 orchestrator.ts 삭제
export {} 뿐인 빈 껍데기, 아무 곳에서도 참조 안 됨(reactor.ts +
orchestration/multi-agent.ts로 대체된 지 오래). 빌드/재시작 확인 완료.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 17:12:54 +09:00
kimandClaude Sonnet 5 05976e3f05 v4.3.6: gateway 루트에 남아있던 나머지 20개 파일도 서브디렉토리로 분류
- memory/ (3): fact-store, memory-manager, memory-vector
- hooks/ (2): hooks, hook-loader
- infra/ (4): boot, gpu-detector, ollama-process-manager, mcp-manager
- guards/ (6): link-validator, visual-error-detection, context-injection,
  prompt-gates, retry-strategy, verification-flow
- internal-agent-task.ts(실제 express 라우터), music-renderer.ts(routes-music.ts
  전용 백엔드)는 routes/로 편입
- server.ts/session.ts/skills-manager.ts는 5곳 안팎에서 두루 참조되는 핵심
  모듈이라 루트 유지, orchestrator.ts(export {}뿐인 죽은 레거시 껍데기)도
  분류 대상이 아니라 별도 이슈로 루트에 남김
- 이번에도 import 경로 자동 재계산 스크립트 사용, 41곳 수정 후 빌드/재시작/
  20개 엔드포인트 재검증 완료

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 17:10:44 +09:00
kimandClaude Sonnet 5 db7260ac46 v4.3.5: gateway 폴더 구조 정리 — routes/channels/mocks/errors/tasks/automation 서브디렉토리 분리
- src/gateway/ 76개 .ts 파일 중 55개를 종류별 서브디렉토리로 이동
  (routes/ 35, channels/ 4, mocks/ 3, errors/ 5, tasks/ 8, automation/ 3)
- 루트엔 server.ts(server-v2.ts에서 개명) 포함 21개 핵심 인프라 파일만 유지
- gateway 내부 상대경로 import 101곳 + 외부 참조 5곳(cli/index.ts,
  tools/kakao.ts, tools/pptx.ts, tools/shell.ts, tools/task-control.ts)을
  스크립트로 재계산, 빌드/재시작/26개 엔드포인트 스모크테스트로 검증

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 17:02:54 +09:00
kimandClaude Sonnet 5 a1cfeb7710 v4.3.4: server-v2.ts 라우트 분리 3차 (chat-sessions/auth-openai/users/admin/channel-mappings/detective)
- 7개 라우트 클러스터 추가 추출 (12,785 → 11,785줄)
- detective는 2곳에 흩어진 사례(9761~)와 네트워크도구(11581~)를 하나로 통합,
  /pdf-editor 프록시가 쓰는 detectiveFilesDir/resolveDetectiveUploadPath는 export
- users는 CRUD/이메일계정/텔레그램봇 3곳을 통합, 생성→조회→삭제 전체 사이클 실제
  테스트로 검증(테스트 계정 정리 완료)
- 핵심 로그인/세션(/api/auth/status·setup·login·logout·change-password)과
  handleChat에 직결된 거대 POST /api/chat 루프는 위험도 높아 이번에도 제외
  (기존 /api/code/round 제외와 동일한 이유)
- 오늘 세 차례 분리 누적: 14,913 → 11,785줄 (-3,128줄, -21%)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 16:52:17 +09:00