- Auth: move from state.json to SQLite authed_chats table, with memory cache
- Remove Persistent/state.json, all state now in noc.db
- HTTP API: POST /api/chat (end-to-end LLM), GET /api/logs (failed API requests)
- API logging: store raw request/response for 400 errors in api_log table
- Agent completion: spawn_agent sends LifeEvent::AgentDone via channel,
life loop picks up with full conversation context and responds
- Config structs: derive Clone for HTTP server
- System prompt: instruct LLM not to add timestamps
- Makefile: rsync without --delete to preserve VPS-only tools
- Extract http.rs: unified HTTP server with /api/timers and gitea webhook
- Life loop: select! on interval tick + mpsc channel for force-fire
- Predefined diary timer (cron 22:55 daily), auto-registered on startup
- BufferOutput for system timers (chat_id=0), no TG message
- state: ensure_timer(), get_timer()
- context.md: add blog and Hugo docs for AI
- Add src/gitea.rs: axum webhook server on :9800, handles @mention in
issues and PRs, spawns claude -p for review, posts result as comment
- Add call_gitea_api tool: LLM can directly call Gitea REST API with
pre-configured admin token (noc_bot identity)
- Add Caddy to Docker image as ingress layer (subdomain/path routing)
- Config: add gitea section with token_file support for auto-provisioned token
- Update suite.md: VPS-first deployment, SubAgent architecture, Caddy role
- doc/heart.md: emotional system design (motivation, reflection, relationship memory)
- Auto-reflection: every 10 messages, async LLM call updates inner_state
with feelings and understanding changes (not conversation summary)
- Life Loop emotional motivation: "you care, not because timer fired"
- Remove all instance-specific names from code/docs — persona, name,
memories are instance data (SQLite), not code
- Rewrite doc/life.md and doc/todo.md for instance isolation principle
- update_inner_state: LLM can update its own persistent inner state
- inner_state injected into chat loop system prompt (read-only)
- Life Loop now uses run_openai_with_tools (full tool access)
- Life Loop LLM calls wrapped in 120s tokio::time::timeout
- All reqwest clients: 120s timeout (whisper: 60s)
- doc/life.md: life loop architecture design doc
- todo.md: removed completed items
- gen_voice: IndexTTS2 voice cloning via tools/gen_voice script, ref audio
cached on server to avoid re-upload
- Message timestamps: created_at column in messages table, prepended to
content in API calls so LLM sees message times
- Image understanding: photos converted to base64 multimodal content
for vision-capable models
- Group chat: independent session contexts per chat_id, sendMessageDraft
disabled in groups (private chat only)
- Voice transcription: whisper service integration, transcribed text
injected as [语音消息] prefix
- Integration tests marked #[ignore] (require external services)
- Reference voice asset: assets/ref_voice.mp3
- .gitignore: target/, noc.service, config/state/db files
- "cc" prefix messages bypass LLM backend and history, directly invoke claude -p
- diag command now dumps all registered tools and sends as .md file
- system prompt instructs LLM to use spawn_agent for search tasks
- spawn_agent tool description updated to mention search/browser capabilities
- Configurable backend: claude (CLI) or openai (API), selected in config.yaml
- OpenAI streaming via SSE with conversation history in memory
- Session isolation: config name included in session UUID
- Markdown to Telegram HTML conversion (pulldown-cmark) for final messages
- Fix sendMessageDraft: skip cursor to preserve monotonic text growth,
skip empty content chunks from SSE stream
- Simplify Makefile: single deploy target
Telegram Bot API 9.3+ sendMessageDraft provides smooth streaming text
rendering without the flickering of repeated edits. Falls back to
editMessageText automatically if the API is unavailable (e.g. older
clients or group chats). Also reduces edit interval from 5s to 3s and
uses 1s interval for draft mode.
- Streaming: use claude --output-format stream-json, edit TG message
every 5s with progress, show tool use status during execution,
◎ cursor indicator while processing
- File transfer: download user uploads to ~/incoming/, scan
~/outgoing/{sid}/ for new files after claude completes
- Error handling: wrap post-auth logic in handle_inner, all errors
reply to user instead of silently failing
- Remote deploy: make deploy-hera via SSH, generate service from
template with dynamic PATH/REPO
- Service: binary installed to ~/bin/noc, WorkingDirectory=%h
- Invoke claude directly instead of ms wrapper
- Session state persisted to disk across restarts
Async Rust bot (teloxide + tokio) that:
- Authenticates users per chat with a passphrase (resets daily at 5am)
- Generates deterministic UUID v5 session IDs from chat_id + date
- Pipes messages to `claude -p --session-id/--resume <uuid>`
- Persists auth and session state to disk across restarts
- Deploys as systemd --user service via `make deploy`