Commit Graph

7 Commits

Author SHA1 Message Date
hailin 55b983a950 feat(it0_app): add WhatsApp-style voice message with async agent interrupt
New VoiceMicButton widget (press-and-hold to record, release to send):
- Records audio to a temp .m4a file via the `record` package
- Slide-up gesture cancels recording without sending
- Pulsing red mic icon + "松开发送/松开取消" feedback during recording

New flow for voice messages:
  1. Temp "🎤 识别中..." bubble shown immediately
  2. Audio uploaded to POST /api/v1/agent/sessions/:id/voice-message
     (multipart/form-data; backend runs Whisper STT)
  3. Placeholder replaced with real transcript
  4. WS stream subscribed via new subscribeExistingTask() to receive
     agent's streaming response — same pipeline as text chat

Voice messages act as async interrupts: if the agent is mid-task the
backend hard-cancels it before processing the new voice command,
so whoever presses the mic button always takes priority.

Files changed:
  chat_remote_datasource.dart — sendVoiceMessage() multipart upload
  chat_repository.dart        — subscribeExistingTask() interface method
  chat_repository_impl.dart   — implement subscribeExistingTask(); fix
                                sendVoiceMessage() stub
  chat_providers.dart         — ChatNotifier.sendVoiceMessage()
  voice_mic_button.dart       — NEW press-and-hold recording widget
  chat_page.dart              — mic button added to input area

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 03:20:41 -08:00
hailin e4c2505048 feat: add multimodal image input with streaming markdown optimization
Two major features in this commit:

1. Streaming Markdown Rendering Optimization
   - Replace deprecated flutter_markdown with gpt_markdown (active, AI-optimized)
   - Real-time markdown rendering during streaming (was showing raw syntax)
   - Solid block cursor (█) instead of AnimationController blink
   - 80ms token throttle buffer reducing rebuilds from per-token to ~12.5/sec
   - RepaintBoundary isolation for markdown widget repaints
   - StreamTextWidget simplified from StatefulWidget to StatelessWidget

2. Multimodal Image Input (camera + gallery + display)
   - Flutter: image_picker for gallery/camera, base64 encoding, attachment
     preview strip with delete, thumbnails in sent messages
   - Data layer: List<String>? → List<Map<String, dynamic>>? for structured
     attachment payloads through datasource/repository/usecase
   - ChatAttachment model with base64Data, mediaType, fileName
   - ChatMessage entity + ChatMessageModel both support attachments field
   - Backend DTO, Entity (JSONB), Controller, ConversationContextService
     all extended to receive, store, and reconstruct Anthropic image
     content blocks in loadContext()
   - Claude API engine skips duplicate user message when history already
     ends with multimodal content blocks
   - NestJS body parser limit raised to 10MB for base64 image payloads
   - Android CAMERA permission added to manifest
   - Image.memory uses cacheWidth/cacheHeight for memory efficiency
   - Max 5 images per message enforced in UI

Data flow:
  ImagePicker → base64Encode → ChatAttachment → POST body →
  DB (JSONB) → loadContext → Anthropic image content blocks → Claude API

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 03:24:17 -08:00
hailin d5f663f7af feat: inject-message support for mid-stream task interruption
Backend (agent-engine.port.ts):
- Add `cancelled` event type: emitted when a task is cancelled (user-initiated
  or injection), so Flutter can close the old stream cleanly
- Add `task_info` event type: emitted after inject to pass the new taskId to
  the client, enabling cancel/re-inject on the replacement task

Flutter (features/chat/):
- ChatState: track current `taskId` alongside `sessionId`; clear on completion
  or error
- Handle `TaskInfoEvent`: update taskId in state when server issues a new task
- Handle `CancelledEvent`: treat as stream termination (agentStatus → idle)
- MessageType.interrupted: new UI node (warning style) for mid-stream cancels
- _inject(): send text as an inject request while streaming; backend cancels
  the current task and starts a new one with the injected message
- Input area: during streaming, hint changes to "追加指令...", Enter key calls
  _inject() instead of _send(), and both inject-send + stop buttons are shown
- isAwaitingApproval kept separate from isStreaming so approval flow is not
  blocked by inject mode

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 21:33:50 -08:00
hailin 2403ce5636 feat: multi-turn conversation context management with session history UI
Implement DB-based conversation message storage (engine-agnostic) that
works across both Claude API and Agent SDK engines. Add ChatGPT/Claude-style
conversation history drawer in Flutter with date-grouped session list,
session switching, and new chat functionality.

Backend: entity, repository, context service, migration 004, session/message
API endpoints. Flutter: ConversationDrawer, sessionId flow from backend
response via SessionInfoEvent, session list/switch/delete support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 19:04:35 -08:00
hailin 803cea0fe4 fix: pass JWT token in WebSocket connection headers
WebSocket connections to /ws/agent were rejected by Kong (401)
because the Authorization header was not included. Now reads
access_token from secure storage and passes it in the WebSocket
upgrade request headers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 16:43:31 -08:00
hailin 15e6fca6c0 fix: translate all remaining English UI strings to Chinese and remove dead code
- Translate approval_action_card (Approve/Reject/Cancel/Expired)
- Translate tool_execution_card status labels (Executing/Completed/Error)
- Translate chat_providers error messages and stream content
- Translate message_bubble "Thinking..." indicator
- Translate terminal page tooltips (Reconnect/Disconnect)
- Translate fallback values (Untitled/Unknown/No message) across all pages
- Translate auth error "Login failed" and stream error messages
- Remove dead voice_providers.dart (used speech_to_text which is not installed)
- Remove dead voice_input_button.dart (not referenced anywhere)
- Fix widget_test.dart (was referencing non-existent MyApp class)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 02:07:57 -08:00
hailin 00f8801d51 Initial commit: IT0 AI-powered server cluster operations platform
Full-stack monorepo with DDD + Clean Architecture:
- Backend: 7 NestJS microservices + 5 shared libraries (TypeScript)
- Mobile: Flutter app with Riverpod (Dart)
- Web Admin: Next.js dashboard with Zustand + React Query
- Voice: Python voice service (STT/TTS/VAD)
- Infra: Docker Compose, K8s manifests, Turborepo build

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 22:54:37 -08:00