실사용 중 발견: 모델이 satellite_snapshot 결과의 /api/files/... 마크다운을
답변에 그대로 복사하지 않고 재작성하면서 /api/files/ 접두사를 빠뜨림 —
그러면 클라이언트 정규식이 매치 안 돼서 사진탭이 아예 안 열림.
weather_map_screenshot과 같은 패턴으로, 도구 결과의 마크다운을 그대로
SSE로 주입해서 모델의 재현 정확도에 의존하지 않게 함. 태풍 돌핀 실제
NASA 위성사진으로 재현·검증 완료.
실제 테스트에서 발견: "지도 이동해줘" 후속 요청에 모델(deepseek-v4-flash)이
도구를 재호출하지 않고 이전 응답을 그대로 반복하는 경우가 많아 프롬프트
가드만으로는 신뢰도가 부족했음. 근본 해결로 사진 뷰어에 "지도에서 위치
보기" 버튼 추가 — satellite_snapshot 파일명에 이미 박혀있는 좌표를
그대로 파싱해 지도를 이동시키므로 모델 개입이 전혀 필요 없음.
부수적으로 execute-tool.ts에도 weather_map_screenshot 성공 시 그 URL을
SSE로 직접 주입하는 방어선 추가(모델이 실제로 도구를 호출했을 때 대비).
실제 로그 분석 결과: "위치로 이동" 같은 후속 요청은 트리거 단어 목록에
없어서 모델이 Windy URL을 재출력하지 않고, 대신 weather_map_screenshot
(서빙 안 되는 모델 전용 스크린샷 도구) 결과를 [REDACTED-HE].jpg 같은
가짜 마크다운 이미지로 답변에 끼워넣음 — 실제 지도는 전혀 안 움직이고
깨진 이미지 링크만 표시됨. 후속 이동 요청도 트리거로 인정하고,
screenshot 도구 결과를 이미지로 삽입하지 말라는 가드 추가.
accountant/lawyer/investor-app의 SKILL_CONTENT가 모든 한글을 6자리
유니코드 이스케이프(\uXXXX)로 저장하고 있어 소스 읽기가 사실상 불가능했음.
디코드→재인코딩 스크립트로 실제 UTF-8 텍스트로 변환, 디코드된 내용은
변환 전후 완전히 동일함을 스크립트로 검증. 기능 변화 없음, 가독성만 개선.
날씨 스킬에서 태풍 번호 혼동 사고 확인 후, 같은 패턴(단일 소스에서
가져온 데이터를 다른 개체에 잘못 귀속) 위험이 큰 세 스킬에 가드 추가:
- investor: 종목코드/거래소/보통주-우선주/분기 대조
- lawyer: 법령 시행일·개정판, 판례 사건번호 전체 일치 대조
- detective: 동명이인·유사상호 — 추가 식별자 없이 귀속 금지
EFFIS(유럽 산불 전용 DB) 공개 WFS를 붙여볼까 테스트했으나 GetCapabilities조차
35초+ 걸리다 500 에러 — 신뢰성 부족으로 보류. 대신 EONET이 북미 편향이라는
사실을 도구 설명에 적어 모델이 유럽 등 이벤트는 자동으로 web_search 우회하게 함.
- eonet_events/satellite_snapshot 도구 추가 (키 불필요 공개 API)
- weather-app.html: 위성사진 채팅 이미지 렌더링, 지도/사진 멀티탭 센터패널,
확대/축소·드래그팬, localStorage+히스토리 이중 복원으로 새로고침 시
탭 유지(하드리셋으로 localStorage가 지워져도 대화 이력에서 재구성)
- 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>
- 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>
- 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>
- 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>
- 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>
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>
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>
- 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) 모두 정상 동작 확인
- 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 채팅 모두 정상 동작 확인
- handle-chat.ts에서 maybeRunBrowserAdvisorPass(430줄) + maybeRunDesktopAdvisorPass(140줄)
+ 헬퍼 4개를 별도 파일로 이동 (handle-chat.ts 3,900→3,400줄, 500줄 감소)
- 메인 루프의 가변 상태는 그대로 두고, getter/setter 기반 advisorCtx 뷰 객체를 통해
새 파일과 공유 → 메인 루프 변수명/조건식은 모두 유지
- 현재 배포에선 dormantBrowserTools / Linux desktop 제한으로 여전히 도달 불가능한
코드이나, 향후 브라우저/데스크톱 자동화 복구 시 이 파일만 다시 연결하면 됨
- 빌드 성공 + 재시작 후 일반 채팅/code_ai 채팅 모두 정상 동작 확인
정의만 있고 호출부 없는 함수들. readDailyMemoryContext/readMemoryCategories는
Chroma 벡터 메모리(2026-07-24 도입, buildPersonalityContext의 [RECALLED_FACTS])가
관련성 기반 회상으로 대체하면서 이미 실질적으로 무용해진 상태였음.
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 생성 후 실제 구현을 대입
실제 백그라운드 태스크 생성→조회→삭제 왕복으로 순환 경로 전체 검증 완료.
detectToolCategories/TOOL_BLOCKS/readMemorySnippets 등 관련 헬퍼 클러스터를
createPersonalityContext(resolvePromptPath) 팩토리로 통째 이동. 외부 의존은
resolvePromptPath 하나뿐이라 순환 의존성 없이 분리 가능했음.
오늘 분석에서 찾은 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>
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>
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>
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>
export {} 뿐인 빈 껍데기, 아무 곳에서도 참조 안 됨(reactor.ts +
orchestration/multi-agent.ts로 대체된 지 오래). 빌드/재시작 확인 완료.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- 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>
- 9개 라우트 클러스터 추가 추출 (14,002 → 12,785줄)
- 오늘 두 차례 분리로 총 14,913 → 12,785줄 (-2,128줄, -14.3%)
- 남은 detective/auth/admin/user/chat 클러스터는 최대 4,700줄 범위에 걸쳐
뒤섞여 있어 이 패턴으로는 분리 어려움 — 별도 전략 필요
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- news_search 스키마에 지역코드 안내 부족 버그 수정 (동유럽 country code 예시 추가)
- "소식"을 뉴스 동의어로 게이트에 추가, "무슨 좋은 소식 있어" 같은 개인 안부 관용구는
PERSONAL_NEWS_IDIOM 예외로 오탐 방지
- 하드코딩 가드 9개 추가 정리: prompt-gates.ts로 통합(isNewsRequest 중복 제거,
task-intent 6개, isHighStakesFile/requestedFullTemplate/
shouldForceSessionScopeForTemporalClaim/userRequestedImageEdit 이동), 죽은
코드 4개 삭제
- 브라우저 자동화 재시도 가드가 dormant된 browser_* 도구를 강제 호출하려던
사각지대 수정 (도구 가용성 체크로 게이팅)
- tool_choice를 provider 체인 전체에 플러밍, 모델별 프로필에
forceToolChoiceOnLiveData 플래그 추가 — 미스트랄이 뉴스/사실질의에서 도구
호출 없이 답변을 지어내던 문제를 라운드 0부터 강제 도구호출로 차단
(프롬프트 경고만으로는 불충분함을 실측 확인)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- USER.md/SOUL.md 문자캡 잘림 보완용 벡터 검색(Chroma, EmbeddingGemma) 신설
- 일별 대화로그 LLM 추출 파이프라인(user_facts/daily_extracts 컬렉션 분리)
- 검증-강제 게이트(isFactualInfoRequest 등)를 prompt-gates.ts로 분리, 개인 인프라
별명(지서버/클로서버) 관련 불필요한 web_search 강제 호출 버그 수정
- Qwen3-Embedding-8B는 실측 결과 이미지생성 GPU와 자원 충돌 확인되어 폐기,
EmbeddingGemma(경량+다국어)로 최종 선택
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v4.2.1 이북 리더 커밋에서 빠뜨렸던 부분 — 이제 이 두 공급자 키를 평문으로
저장하면 다음 config 저장 때 자동으로 vault로 이전된다(기존 nvr.password 등과 동일 패턴).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- 메인 NVR과 별개인 독립 IP카메라(예: Mercusys/Tapo) 등록 지원
(nvr.extraCameras, id 1000+) — RTSP/화면 표시는 메인 NVR과 동일 그리드에 통합
- ONVIF(WS-Security) 기반 PTZ 제어 — 라이브 모달의 기존 PTZ 버튼이 독립 카메라에서도
그대로 작동(백엔드에서 채널 id로 NVR/독립 카메라 라우팅)
- 카메라 자체에 상하반전 옵션이 없는 모델을 위한 ffmpeg 소프트웨어 180도 회전,
반전 시 PTZ 팬/틸트 방향도 화면 기준으로 자동 보정
- 채널 이름/위치 라벨 커스텀 표시, 채널별 화질(메인/서브) 강제 고정
- 6분할 라이브로 확장(기존 5분할 하드코딩 제거)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Project Gutenberg/Google Books/알라딘 검색 및 서재(EPUB) 보관
- 페이지 넘김 리더: 책 판형 비율 고정 페이지, 챕터 자동 새 페이지 시작,
단어/문단 경계 유지, 브라우저 창 크기와 무관한 고정 페이지 분량(스케일만 조정)
- Gutenberg 원문의 강제 줄바꿈을 문단 단위로 재조판(reflowHardWrappedProse)
- 서재에서도 바로 읽기 지원(다운로드 시 원문 URL 함께 저장)
- TTS 낭독(현재 페이지 기준)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- weather.ts: "Rome"처럼 국가 미명시 동명 도시 조회시 지오코딩 API의
index 0을 그대로 믿지 않도록 개선. Open-Meteo 결과는 인구수 기준으로
재정렬해 최적 후보를 고르고, OpenWeather 쪽은 country 미명시일 때
Open-Meteo 좌표로 재조회해 이름 모호성 자체를 피함. 국가 파싱 로직
(parseExplicitCountry)을 두 경로가 공유하도록 정리.
- server-v2.ts: PTY 셸 세션 생성시 cwd 기본값을 고정된 homeclaw 경로
대신 사용자별 워크스페이스로 사용, 세션 시작 전 워크스페이스 보장.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- EseeCloud K8208-3WS NVR RTSP(포트80, /ch{N}_{S}.264)를 ffmpeg로
MJPEG(multipart/x-mixed-replace) 변환, 4분할 자동재생+모달 확대
- PTZ 팬/틸트: NVR 웹UI와 동일한 /netsdk/Channel/PTZ + S.PtzControl
포맷, 숫자 Cmd코드로 수정(기존 PTZControl 문자열 명령은 무반응이었음)
- ffmpeg NVDEC를 GPU1에 고정(GPU0은 이미지 스튜디오가 이미 점유)
- mid-stream 드롭(PTZ 이동 등) 자동 respawn으로 화면 끊김 복구
- 화면 전환시 서버에 명시적 stop 요청 + activeStreams를 abort()
콜백으로 전환해 레이스컨디션으로 인한 ffmpeg 프로세스 배가 수정
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
브랜드명 전체 변경: "CherryClaw"→"HomeClaw", localStorage 키 6종
(cherryclaw_theme 등)→homeclaw_*, alt="🍒"→alt="🏠". 15개 web-ui 파일 +
OpenAI OAuth 완료 페이지 전체 반영.
로고를 체리 아이콘에서 집 모양으로 교체 후, 사용자 피드백을 반영해 동화풍
성(castle) 실루엣으로 재작업 — 대리석/벽돌 텍스처의 회색 톤, 검은 윤곽선,
중앙 첨탑 우측에 작은 반짝임 별. FLUX.1로 생성 후 배경 제거해 투명 PNG로
저장. 파일명도 cherry_logo.png → homeclaw_logo.png로 통일(web-ui/, assets/
양쪽).
시스템 프롬프트("You are SmallClaw 🦞")와 IDENTITY.md(papa/cherry/jasmine
+ 템플릿 4곳)의 Name 필드에서 🦞를 빼고, 습관적으로 이모지를 붙이지 말라는
지시를 추가 — 모델이 응답 끝마다 무의식적으로 🦞를 붙이던 문제 해결.
텔레그램 봇 응답/self-update 알림/연동 테스트 메시지 등 하드코딩된 🦞도
전부 제거.
부수적으로 발견한 문서 버그도 수정: 웹훅 가이드와 curl 복사 버튼이
"x-cherryclaw-token" 헤더를 안내하고 있었는데, 서버가 실제로 검사하는
헤더는 X-Gateway-Token이었음 — 그대로 썼으면 401 에러가 났을 문제.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
NewsData.io는 country를 6개 이상 넘기면 하드 에러("Number of country
cannot exceeded 5")를 내는데, "소아시아 뉴스"처럼 자연스럽게 6개 이상의
국가코드로 매핑되는 요청에서 모델이 왕복을 한 번 날리게 되던 문제 —
5개 초과분은 조용히 잘라내도록 수정.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
video_generate에 model 파라미터 추가 — 기존 LTX-Video(빠름, 기본값)에
CogVideoX-2B(THUDM, 느리지만 화질 비교용)를 선택지로 추가. CogVideoX는
12GB VRAM 한계에 거의 딱 맞아(enable_model_cpu_offload에도 peak ~11GB)
해상도/프레임수 기본값 이상으로 올리면 여유가 없음.
스튜디오앱 동영상 탭에서 모델/프레임수/FPS 세 필드가 320px 폭 폼에
한 줄로 욱여넣어져 프레임수부터 우측 패널에 가려 안 보이던 레이아웃
버그 수정 — 모델 드롭다운을 단독 줄로 분리하고 프레임수/FPS는 그 아래
별도 행으로 이동.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
검색예산 하드캡·image_edit 차단·FILE_OP v2 대체실행 등 도구호출을 가로채는
5곳이 messages 배열에 매칭되는 role:'tool' 응답을 안 넣고 continue만 해서,
Ollama가 다음 호출에서 "Not the same number of function calls and responses"로
거부하는 버그 수정.
검색예산은 소프트캡 5회 유지하되 새 토큰(도시명 등)이 포함된 진짜 새 주제
검색이면 하드캡 12회까지 자동 연장 — "유럽 주요 도시 최고온도"처럼 정당한
다건 검색이 캡에 걸려 차단되던 문제 해결.
시스템프롬프트에 TEMPORAL CONSISTENCY 문단 추가 — 시장개장/영업 여부 판단시
검색결과보다 주입된 현재날짜(요일 포함)를 우선 신뢰하도록 지시. mistral이
검색은 하면서도 요일을 반영 못해 주말에 "시장 열려있음" 같은 오답을 내던
잔여 문제 대응.
ollama-adapter.ts에 parseOrphanTrailingToolJson 추가 — 모델이 함수명을
빠뜨리고 원시 JSON(예: write_note 인자)을 답변 끝에 그대로 노출하는 케이스를,
알려진 도구 스키마와 매칭될 때만 실제 tool_call로 복구하거나(유일 매칭)
화면에서만 제거(모호한 매칭)하도록 처리.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
toolsDisabledForRestOfTurn(검색예산 2회 초과 시 그 턴 tools를 []로 비우는 안전장치)가
걸려도, "모델이 함수호출 대신 텍스트로 toolname{...}를 흉내낸 경우 복구"하는 로직이
이 상태를 확인하지 않고 원래 도구 목록으로 계속 텍스트를 재파싱해 tool call로
되살리고 있었음 — 실질적으로 tools:[] 캡을 완전히 무력화시키는 구멍이었음.
실측: "GPT-5.6 가격 비교" 질문 하나에 web_search가 37라운드 넘게 반복되다 결국
"못 찾았다"로 종료(로그로 재현·확인). toolsDisabledForRestOfTurn일 때 이 복구
로직을 스킵하도록 수정.
AUTO-RECOVER의 근거도구 인식 정규식에 news_search가 빠져있어서, 뉴스 검색을
몇 번을 성공시켜도 "아직 도구 안 불렀다"고 오판해 강제 재호출을 반복 주입하던
버그 수정(server-v2.ts) — 실측상 "오늘 유럽 뉴스" 요청 하나에 13라운드 넘게
반복 호출되던 원인이었음, 모델 종류와 무관하게 재현됨.
news_search(NewsData.io)는 country+language 불일치, country+category=world
조합에서 country 필터를 조용히 무시하고 엉뚱한 나라 뉴스를 반환함을 확인 —
country 지정 시 language/category=world를 드롭하도록 수정하고, "유럽" 같은
대륙 코드가 없다는 점 + country는 콤마로 최대 5개까지 받는다는 점을 도구
설명에 명시(news.ts).
SearXNG 쿼리에 language/time_range 파라미터 추가로 오래된 결과 필터링
정교화(web.ts).