.claude/skills/message-flow-architecture/SKILL.md
This skill should be used when the user asks about "message flow", "how messages are processed", "Engine handleMessage", "interactive session", "agent session lifecycle", "processInteractiveEvents", "event loop", "how cc-connect talks to Claude Code", "stdio protocol", "stream-json", "permission request flow", "streaming preview", "how typing indicator works", "pending messages queue", "session lock", "TryLock", "how API key is passed", "provider proxy", "ANTHROPIC_API_KEY", "router_url", "authentication", "how Claude Code is started", or needs to debug, extend, or understand the end-to-end message processing pipeline from IM platform to agent execution.
npx skillsauth add liuyu520/cc-connect-fork message-flow-architectureInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Document the complete data flow from when a user sends a message in an IM platform to when Claude Code executes coding tasks and the response is delivered back. This is the core pipeline of cc-connect.
IM User → Platform → [ProjectRouter] → Engine.handleMessage()
→ processInteractiveMessageWith()
→ getOrCreateInteractiveStateWith() → agent.StartSession()
→ agentSession.Send() → stdin JSON to claude CLI
→ processInteractiveEvents() ← stdout JSON from claude CLI
→ EventThinking/ToolUse/Text/Permission/Result → Platform.Reply()
→ User receives response
| File | Role |
|------|------|
| core/engine.go | handleMessage() main entry, Engine struct, lifecycle, resolveAlias(), checkRateLimit(), matchBannedWord() |
| core/engine_session.go | processInteractiveMessage(), processInteractiveMessageWith(), getOrCreateInteractiveStateWith(), handlePendingPermission(), queueMessageForBusySession(), cleanupInteractiveState(), type definitions (interactiveState, pendingPermission, queuedMessage) |
| core/engine_events.go | processInteractiveEvents() — the core event select loop (~600 lines), drainPendingMessages(), notifyDroppedQueuedMessages() |
| core/engine_commands.go | handleCommand() — slash command dispatch via registry pattern |
| core/engine_util.go | send(), reply(), sendPermissionPrompt(), sendAskQuestionPrompt(), HandleRelay(), splitMessage() |
| core/engine_cards.go | handleCardNav(), executeCardAction(), all render*Card() functions |
| core/engine_workspace.go | buildSenderPrompt(), commandContext(), resolveWorkspace(), workspace helpers |
| core/interfaces.go | Platform, Agent, AgentSession, SessionEnvInjector interfaces |
| core/streaming.go | streamPreview for real-time message updates |
| agent/claudecode/claudecode.go | Agent constructor, StartSession(), provider/router env injection |
| agent/claudecode/session.go | CLI subprocess management, stdin/stdout JSON protocol, event parsing |
| core/providerproxy.go | Local reverse proxy for third-party providers |
Engine.handleMessage() (in core/engine.go) performs these checks before agent dispatch:
e.resolveAlias())e.checkRateLimit())/status, /new, /model, etc.) — routed to handleCommand() in engine_commands.goIf not a command, the message proceeds to interactive processing.
session.TryLock()
├─ Success (idle) → go processInteractiveMessageWith()
├─ Fail + "/btw" → inject into running session (no new turn)
├─ Fail + queueable → pendingMessages queue (auto-processed after current turn)
└─ Fail + full → reply "previous request still processing"
The pendingMessages queue ensures consecutive messages are not lost—they
are automatically dequeued and processed when EventResult completes.
Session locking functions are in core/engine_session.go:
queueMessageForBusySession() — queues messages for busy sessionsdrainOrphanedQueue() — handles orphaned queued messagesgetOrCreateInteractiveStateWith() (in core/engine_session.go) decides whether
to start a new claude CLI process or reuse an existing one:
Check e.interactiveStates[sessionKey]
├─ Exists + Alive() + session ID matches → REUSE (no new process)
├─ Exists + Alive() + session ID changed → Close old, CREATE new
└─ Not exists / dead process → CREATE new
├─ First user message → startSessionID = "--continue" (bridge to latest CLI session)
├─ Saved session ID → startSessionID = "uuid" (resume)
└─ No saved ID → startSessionID = "" (fresh session)
The interactiveState struct (defined in core/engine_session.go) holds:
agentSession — running claude process handleplatform / replyCtx — where to send responsespending — current permission request awaiting user responsependingMessages — queued messages when agent is busyapproveAll — user clicked "allow all"agent.StartSession(ctx, sessionID) launches the claude CLI subprocess.
For implementation details, consult references/stdio-protocol.md.
For authentication and API key injection, consult references/auth-and-providers.md.
sendDone := make(chan error, 1)
go func() {
sendDone <- state.agentSession.Send(prompt, images, files)
}()
Send runs concurrently so the event loop can start immediately. The JSON
message is written to the claude process stdin. See references/stdio-protocol.md
for the wire format.
processInteractiveEvents() (in core/engine_events.go) is the core select loop:
| Event Type | Action | Platform Method |
|------------|--------|-----------------|
| EventThinking | Flush text, send thinking status | send() |
| EventToolUse | Flush text, freeze preview, send tool info | send() |
| EventToolResult | Send tool execution result | send() |
| EventText | Accumulate in textParts[], update stream preview | (buffered) |
| EventPermissionRequest | Send permission card, block until resolved | sendPermissionPrompt() / sendAskQuestionPrompt() |
| EventResult | Send final response, save history, drain queue | send() / stream finish() |
| EventError | Send error message, cleanup | send() |
Helper functions used by the event loop (in core/engine_util.go):
send(), reply() — platform message sending with loggingsendPermissionPrompt() — renders permission cards/buttonssendAskQuestionPrompt() — renders user question UIsplitMessage() — splits long messages at newline boundariesPermission requests are interrupt events that pause the event loop:
pendingPermission with a Resolved channel (defined in engine_session.go)<-pending.ResolvedhandlePendingPermission() (in engine_session.go) parses responsecontrol_response to claude CLI stdinWhen platform supports MessageUpdater:
EventText content is appended to streamPreviewWhen EventResult arrives:
textParts into final responsesession.AddHistory("assistant", response)pendingMessages queue → if non-empty, send next and continue loopQueue draining is handled by drainPendingMessages() in core/engine_events.go.
| Symptom | Check |
|---------|-------|
| Agent never responds | Logs for session spawned — did StartSession succeed? |
| "Previous request still processing" | Session lock held — check if permission request is pending |
| Response appears twice | Stream preview + final send — check finish() return value |
| Agent process dies mid-turn | Check stderr output in logs, look for OOM or crash |
| Messages lost when busy | Verify pendingMessages queue — check drainPendingMessages() in engine_events.go |
references/stdio-protocol.md — Complete JSON wire protocol between cc-connect and claude CLIreferences/auth-and-providers.md — How API keys, providers, and routers are configured and injectedcard-callback-performance — IM card callback 3s timeout constraint and caching pattern for card rendering pathsslash-command-system — Command registration via registry pattern, dispatch, and routingtools
This skill should be used when the user asks about "webui", "web ui", "vibe coding", "WebUIServer", "web interface", "browser Claude Code", "web frontend", "React admin dashboard", "web/src", "VibeCoding page", "WebSocket /api/vibe/ws", "webui config", "port 9830", "static file serving", "webuiSession", "claude code subprocess from web", "project dropdown", "work dir select", "listProjects", "Management API frontend", "multi tab vibe", "TabBar", "VibeSession component", "copy work dir", "clipboard copy", "disconnect confirm", "断开确认", "复制路径", "copyWorkDir", "AgentSystemPrompt", "project awareness", "/project command in agent", "attachment upload", "sendWithAttachments", "file upload vibe", "image upload vibe", "drag drop vibe", "paste image vibe", or needs to debug, extend, or understand the browser-based Vibe Coding interface and its Go backend.
tools
This skill should be used when the user asks about "permission request", "control_request", "control_response", "permission prompt tool", "permission-prompt-tool stdio", "permission popup not showing", "permission dialog missing", "authorize tool use", "allow deny button", "webuiSession permission", "respondPermission", "updatedInput", "control_cancel_request", "permission cancelled", "permission_cancelled", "pendingInputs", "permission flow webui", "vibe permission", "前端没有弹出授权", "权限请求不显示", "权限弹窗", "webuiSession vs claudeSession", "webui parity", or needs to debug, extend, or understand how tool permission requests flow between Claude Code CLI, the Go WebUI backend, and the React frontend.
tools
This skill should be used when the user asks about "export markdown", "export chat", "download markdown", "export conversation", "导出 Markdown", "导出聊天记录", "ExportRequest", "ExportMessage", "handleVibeExportMarkdown", "buildExportMarkdown", "POST /api/vibe/export", "exportVibeSession", "chat export", "markdown download", "sanitizeFilename", "Content-Disposition attachment", "export button vibe", "history export", "tool_use in export", "message type mapping export", or needs to debug, extend, or understand the chat history Markdown export feature including the full-stack data flow from frontend trigger to file download.
tools
This skill should be used when the user asks about "attachment upload", "file upload", "image upload", "paste image", "drag drop file", "vibe attachment", "pendingAttachments", "sendWithAttachments", "fileToAttachment", "AttachmentItem", "base64 attachment", "WebSocket attachment protocol", "multimodal content", "image content block", "file content block", "clipboard paste image", "drag and drop upload", "attachment preview", "file size limit", "10MB limit", "ExtFromMime", "附件上传", "图片上传", "拖拽上传", "粘贴图片", "附件预览", "文件大小限制", or needs to debug, extend, or understand the WebUI attachment upload feature including the full-stack data flow from browser to Claude Code CLI.