결정(9/21): 공용 하나, 개인 로컬 없음.
- backend apps/snippets: Snippet(name PK·desc·body·category) + SnippetUsage(사람별 count/last_used).
GET/POST /api/v1/snippets, PUT/DELETE /snippets/{name}, POST /snippets/{name}/use — 옛 브릿지 5개와 1:1,
이름 정규화·중복 409·빈 내용 400 규칙 동일. import_snippets 커맨드로 옛 snippets.db 적재. 테스트 3개
- frontend snippets.api.ts 만 HTTP 로 교체(훅·페이지·타입 무변경). 테스트 axios-mock-adapter 로
- ponytail: Tauri 로컬 SQLite 커맨드는 미사용 상태로 남김. 지울 때 Rust+브릿지 테스트 함께
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
세 군데(새 대화 세션 생성 중 / 스트리밍 첫 토큰 전 / 재진입 대기)가 각자 손으로 그린 테두리 카드라
실제 답변(Message: AI 배지 + CODEASSIST + 상자 없는 본문)과 달랐음. 컴포넌트 하나로 통일.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
세션 생성(OpenCode 왕복)이 느리면 헤로 화면에 '응답 중' 만 떠서 안 넘어간 것처럼 보였고,
실패하면 pending 이 안 풀려 영원히 갇혔음.
- 전송 순간 헤로 → 내 말풍선 + 시머(SessionChatPage 첫 화면과 같은 모양)
- mutate 콜백 패턴(SnapUserControls 와 동일): onSuccess 이동, onError 헤로 복귀 + 토스트
- 테스트 3개(훅 mock, SnippetPalettePage.test 패턴)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- .agents/skills: Leonxlnx/taste-skill 3개 설치(랜딩용이라 채팅 앱엔 일부만 적용)
- samsung 팔레트: foreground #000→#111. 전역 --radius 20 을 10 으로 내리고 --radius-pill 로 버튼만 알약
- Button: rounded-[var(--radius-pill,var(--radius-md))], active:scale-[0.98]
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
로컬(OpenRouter)은 게이트웨이를 안 거쳐서 백엔드가 OpenCode prompt 에 model{providerID,modelID} 를 실어 분기.
기본 openrouter/google/gemma-4-31b-it(고객사 605 와 같은 모델). 텍스트는 기본 GLM 5.2.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- .opencode/tools/abap_index.ts: ABAP_INDEXING 질의 서버(:8100) 를 툴 3개로 —
abap_index_search(로직 조각 검색) / abap_index_chunk(원문+정의부) / abap_index_source(전체 소스·요약)
- AGENTS.md: 코드 질문은 인덱스 먼저, 코드블럭 위에 [출처: 프로그램/unit L줄] · [출처: SAP 조회] · [출처: 없음 — 생성 코드]
- 서버 없으면 툴이 실패 문자열을 주고 에이전트는 생성 코드로. 실측: 인덱스 미연결 상태에서 '[출처: 없음 — 생성 코드]' 표기 확인
- .env ABAP_INDEX_URL, README 절차. .opencode/package.json 은 gitignore 라 고객사엔 폴더째 옮겨야 함
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- 프론트: 스트림 onDone 에서 chat.done(title) 한 번. 브라우저에선 no-op
- Rust shell/notify: 창 보이고 포커스면 아무것도 안 함. 아니면 Windows 토스트(tauri-plugin-notification)
+ 트레이 아이콘을 점 찍힌 tray-alert.png 로. 창 Focused(true) 에서 원복
- 토스트는 설치본(msi)에서만 알림센터에 등록돼 보임. dev 에선 트레이 점으로 확인
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- backend apps/stats: GET /api/v1/admin/stats?from&to → totals / byUser / byDay. ChatMessage(assistant) 집계만, is_superuser 아니면 403. 테스트 3개
- frontend features/admin: 요약 카드 4개(요청·토큰·비용·평균 응답), 일별 토큰 막대(div), 사용자별 표, 기간 프리셋(오늘/7/30/90일). 헤더에 ADMIN 만 아이콘. 테스트 2개
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Gemma4 가 큐 밀리면 '안녕'에도 200초라 첫 이벤트 60s 로는 no-events 로 잘림.
STREAM_FIRST_EVENT_TIMEOUT_S / STREAM_TURN_TIMEOUT_S / AAF_FABRIX_READ_TIMEOUT_S / AAF_FABRIX_TOTAL_TIMEOUT_S 를 .env 로.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
중단 버튼·백엔드 cancel·OpenCode abort 체인은 이미 있었고 Esc 배선만 없었음.
서버만 생성 중(재진입, generatingRemotely)일 때도 Esc 가 cancel 을 보냄.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
opencode.fabrix.json.tmpl 의 sap-icf 를 MCP_PORT/MCP_API_KEY 로 렌더, 키 없으면 enabled:false.
.env.example 에 SAP_*·MCP_* 블록. README 에 -13 번들 재활용 절차(컨테이너 간 통신 금지라 -12 에 따로).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Gemma4 가 '안녕' 한 단어에 94s(580 은 188s) 라 텍스트 기본으로는 못 씀.
AAF_FABRIX_VISION_MODEL_ID 가 있으면 messages 에 image_url 파트가 있을 때만 그 모델로 x-llm-model-id 를 바꿈.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
앱이 기대하는 base-backend 계약(envelope·JWT·/chat/stream SSE)을 그대로 구현.
Django 는 로그인·세션 미러(SQLite/PG)·OpenCode 이벤트 번역만 맡고, 답변은 전용
OpenCode 인스턴스(opencode/ workspace, codeassist 에이전트)가 만듦.
- accounts: 이메일 로그인, access 60분 / refresh 14일, entra/config 는 501
- chat: 세션 목록·검색·메시지·취소 + POST /chat/stream 어댑터(part.delta→token,
idle→usage/done, 클라 끊겨도 턴 감시 태스크가 DB 마무리, 첫 이벤트 60초 타임아웃)
- OpenCode 1.18 멀티 프로젝트라 모든 요청에 ?directory= 부착
- docs-lib 에 OpenCode SDK 1.18.6 타입 원본, pytest 32
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>