feat(my-deepagent): v0.4 chat UX boost + A/B live verification

Claude-Code 동급 chat 경험으로 끌어올림 + 7개 핵심 흐름 실제 OpenRouter verify.

A — Live verification (scripts/live_verify.py, 7 PASS, 약 $0.02):
- A1 1-turn chat (CLI-eq) → Haiku 4.5 한국어 응답
- A2 sessions resume → 같은 session_id 재투입 시 LangGraph state 복원
- A3 /skill <name> system inject → SKILL.md ("한국어 haiku 3 lines") 가 정확히
  3행 한국어 시 생성 (LLM 행동 제어 강력한 증거)
- A4 /plan → /approve → LLM plan markdown only, 차단 도구 시도 없음
- A5 /agents spawn → 실제 sub-agent ainvoke + parent stream push
- A6 auto-compaction → 14 메시지 → 4 archive + 77 토큰 summary
- A7 /workflow wiring → role↔persona 매칭 사전 검증

B1 — Markdown rendering:
- app.js pure-JS 미니 파서: 코드 펜스 / ATX 헤더 / ul/ol / `code`/**bold**/
  *italic*/[link](url)
- XSS 정책 유지: createElement + textContent only.  링크 href 는 http(s):
  스킴 강제.

B2 — System event card (collapsible):
- _classifySystemMessage 가 [sub-agent .../workflow .../Earlier conversation
  history/당신은 plan mode/The user APPROVED/skill] 접두사 분류 후 <details>
  카드로 렌더.

B3 — Token streaming via AsyncCallbackHandler:
- ChatOpenAI(streaming=True)
- _StreamingChunkPusher (AsyncCallbackHandler) → asyncio.Queue per session.
- SSE _session_event_stream 이 queue drain → event: chunk SSE.  100ms poll.
- 순서 보장: chunk drain → message rows yield (placeholder 가 메시지로
  교체되기 전에 토큰 visible).
- 라이브: 5 chunk events + 1 final message, "안녕하세요, / 무 / 엇을 도와드 /
  릴까요?" 토큰 단위 push.

B4 — Cancel mid-turn:
- POST /api/sessions/{id}/abort + app.state.pending_per_session 인덱스.
- 새 user 메시지 도착 시 이전 in-flight task 자동 cancel.
- "■ 중단" 버튼 — 대기 중 visible, 완료/취소 시 hide.

B5 — IME composition-safe Enter:
- compositionstart/compositionend 플래그 — 한글 IME 후보 commit Enter 무시.
- Cmd/Ctrl+Enter 는 항상 전송.

DB hot-fix:
- Database.__init__ pool_pre_ping=True — Postgres asyncpg stale connection
  → SSE 부하에서 500 발생 해결.

기타:
- createNewSession 의 repo_path: "" → "." (min_length=1 검증 통과).
- test_conversation_gui.py fake_invoke 가 chunk_queue kwarg 받도록 업데이트.

게이트:
- ruff / format / mypy: PASS (143 source files)
- pytest -q --ignore=tests/integration/test_e2e_workflow.py
  --ignore=tests/integration/test_openrouter_smoke.py: 709 passed

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
chungyeong
2026-05-18 01:08:40 +09:00
parent 6d371afadd
commit 9a02f22acb
9 changed files with 1169 additions and 57 deletions

View File

@@ -1093,3 +1093,121 @@ details[open] summary {
border-color: rgba(180, 70, 30, 0.5);
font-weight: 600;
}
/* =================================================================
v0.4 — Markdown + system event cards in conversation
================================================================= */
.msg-body .md-p {
margin: 0 0 8px 0;
line-height: 1.6;
}
.msg-body .md-p:last-child { margin-bottom: 0; }
.msg-body .md-h {
margin: 12px 0 6px 0;
font-weight: 700;
line-height: 1.3;
}
.msg-body .md-ul,
.msg-body .md-ol {
margin: 4px 0 8px 0;
padding-left: 22px;
line-height: 1.6;
}
.msg-body .md-ul li,
.msg-body .md-ol li {
margin: 2px 0;
}
.msg-body .md-code {
background: rgba(0, 0, 0, 0.04);
border: 1px solid var(--border);
border-radius: 6px;
padding: 10px 12px;
margin: 8px 0;
overflow-x: auto;
font-family: var(--font-mono);
font-size: 12.5px;
line-height: 1.45;
}
.msg-body .md-code code {
background: transparent;
padding: 0;
}
.msg-body code {
background: rgba(0, 0, 0, 0.06);
border-radius: 4px;
padding: 1px 5px;
font-family: var(--font-mono);
font-size: 0.9em;
}
.msg-bubble.role-user .msg-body .md-code,
.msg-bubble.role-user .msg-body code {
background: rgba(255, 255, 255, 0.18);
border-color: rgba(255, 255, 255, 0.3);
color: white;
}
.msg-body a {
color: rgb(180, 70, 30);
text-decoration: underline;
text-underline-offset: 2px;
}
.msg-bubble.role-user .msg-body a {
color: white;
}
.msg-body strong { font-weight: 700; }
.msg-body em { font-style: italic; }
/* System event card */
.msg-bubble.role-system-event {
align-self: stretch;
max-width: 100%;
background: rgba(245, 158, 11, 0.06);
border: 1px solid rgba(245, 158, 11, 0.25);
border-style: dashed;
font-style: normal;
color: var(--text);
}
.md-system-event summary {
cursor: pointer;
font-size: 12.5px;
display: flex;
align-items: center;
gap: 6px;
list-style: none;
}
.md-system-event summary::-webkit-details-marker { display: none; }
.md-system-event summary .event-icon {
font-size: 14px;
}
.md-system-event summary .event-label {
font-weight: 600;
letter-spacing: 0.02em;
color: rgb(120, 53, 15);
}
.md-system-event[open] summary {
margin-bottom: 8px;
border-bottom: 1px dashed rgba(245, 158, 11, 0.3);
padding-bottom: 6px;
}
.md-system-event .event-body {
font-size: 12.5px;
line-height: 1.55;
color: var(--text-muted);
}