**Changes:** 1. Added comprehensive authentication flow documentation to AGENTS.md - Documented 8-step auth process: login → token exchange → persistence → refresh → renewal - Included key files reference for token management and deep link handling - Explained 30-day rolling refresh token mechanism 2. Implemented session tracking in OpenAI compatibility proxy - Added `setCoworkProxySessionId()` to track active cowork sessions - Integrated session ID tracking in OpenClawRuntimeAdapter lifecycle - Extract and log last user message text for better observability - Added regex patterns to strip Claude SDK time context and metadata prefixes 3. Enhanced proxy request logging - Log session ID and user message preview with each upstream request - Improved debugging capability for multi-session scenarios **Why:** - Documentation helps developers understand the complete auth lifecycle - Session tracking enables better request attribution and debugging - Message extraction provides context for proxy operations without exposing full payloads
18 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Build and Development Commands
# Development - starts Vite dev server (port 5175) + Electron app with hot reload
npm run electron:dev
# Development with OpenClaw engine (clones/builds OpenClaw on first run)
npm run electron:dev:openclaw
# Build production bundle (TypeScript + Vite)
npm run build
# Lint with ESLint
npm run lint
# Run memory extractor tests (Node.js built-in test runner)
npm run test:memory
# Compile Electron main process only
npm run compile:electron
# Package for distribution (platform-specific)
npm run dist:mac # macOS (.dmg)
npm run dist:win # Windows (.exe)
npm run dist:linux # Linux (.AppImage)
# Build OpenClaw runtime manually
npm run openclaw:runtime:host # current platform
Requirements: Node.js >=24 <25. Windows builds require PortableGit (see README.md for setup).
OpenClaw env vars: OPENCLAW_SRC (default ../openclaw), OPENCLAW_FORCE_BUILD=1 (force rebuild), OPENCLAW_SKIP_ENSURE=1 (skip version checkout).
Architecture Overview
LobsterAI is an Electron + React desktop application with two primary modes:
- Cowork Mode - AI-assisted coding sessions using Claude Agent SDK with tool execution
- Artifacts System - Rich preview of code outputs (HTML, SVG, React, Mermaid)
Uses strict process isolation with IPC communication.
Authentication Flow
- 登录: 打开系统浏览器 → Portal 登录页 → URS 登录成功 → deep link
lobsterai://auth/callback?code=<authCode> - 换取令牌:
POST /api/auth/exchange消费一次性 authCode → 返回accessToken(2h) +refreshToken(30d) - 持久化: SQLite kv store
auth_tokens存储双 token,应用重启后自动恢复登录态 - 请求认证:
fetchWithAuth()在每个 API 请求附加Authorization: Bearer <accessToken> - 被动刷新: 收到 HTTP 401 → 使用 refreshToken 调用
POST /api/auth/refresh→ 获取新 accessToken → 重试原请求 - 主动刷新: 定期检查 accessToken 距 exp < 5 分钟 → 后台静默刷新,避免请求失败
- 滚动续期: 每次 refresh 签发新 refreshToken(新 30 天有效期),连续使用不掉线
- 退出条件: 连续 30 天不使用(refreshToken 过期)→ 清除本地 token → 用户需重新登录
关键文件:
- Token 存储与请求:
src/renderer/services/api.ts(fetchWithAuth()、token 管理) - 登录流程:
src/main/main.ts(deep link 处理lobsterai://协议) - 持久化:
src/main/sqliteStore.ts(kv 表存储auth_tokens)
Process Model
Main Process (src/main/main.ts):
- Window lifecycle management
- SQLite storage via
sql.js(src/main/sqliteStore.ts) - Agent engine routing (
src/main/libs/agentEngine/coworkEngineRouter.ts) - dispatches toclaudeRuntimeAdapter.ts(built-in) oropenclawRuntimeAdapter.ts(OpenClaw) - IM gateways (
src/main/im/) - DingTalk, Feishu, Telegram, Discord, NetEase IM - Skill management (
src/main/skillManager.ts) - IPC handlers for store, cowork, and API operations (40+ channels)
- Security: context isolation enabled, node integration disabled, sandbox enabled
Preload Script (src/main/preload.ts):
- Exposes
window.electronAPI viacontextBridge - Includes
coworknamespace for session management and streaming events
Renderer Process (React in src/renderer/):
- All UI and business logic
- Communicates with main process exclusively through IPC
Key Directories
src/main/
├── main.ts # Entry point, IPC handlers
├── sqliteStore.ts # SQLite database (kv + cowork tables)
├── coworkStore.ts # Cowork session/message CRUD operations
├── skillManager.ts # Skill loading and management
├── im/ # IM gateway integrations (DingTalk/Feishu/Telegram/Discord)
└── libs/
├── agentEngine/
│ ├── coworkEngineRouter.ts # Routes to built-in or OpenClaw runtime
│ ├── claudeRuntimeAdapter.ts # Built-in Claude Agent SDK adapter
│ └── openclawRuntimeAdapter.ts # OpenClaw gateway adapter
├── coworkRunner.ts # Claude Agent SDK execution engine
├── claudeSdk.ts # SDK loader utilities
├── openclawEngineManager.ts # OpenClaw runtime lifecycle (install/start/status)
├── openclawConfigSync.ts # Syncs cowork config → OpenClaw config files
├── coworkMemoryExtractor.ts # Extracts memory changes from conversations
└── coworkMemoryJudge.ts # Validates memory candidates with scoring/LLM
src/renderer/
├── types/cowork.ts # Cowork type definitions
├── store/slices/
│ ├── coworkSlice.ts # Cowork sessions and streaming state
│ └── artifactSlice.ts # Artifacts state
├── services/
│ ├── cowork.ts # Cowork service (IPC wrapper, Redux integration)
│ ├── api.ts # LLM API with SSE streaming
│ └── artifactParser.ts # Artifact detection and parsing
├── components/
│ ├── cowork/ # Cowork UI components
│ │ ├── CoworkView.tsx # Main cowork interface
│ │ ├── CoworkSessionList.tsx # Session sidebar
│ │ ├── CoworkSessionDetail.tsx # Message display
│ │ └── CoworkPermissionModal.tsx # Tool permission UI
│ └── artifacts/ # Artifact renderers
SKILLs/ # Custom skill definitions for cowork sessions
├── skills.config.json # Skill enable/order configuration
├── docx/ # Word document generation skill
├── xlsx/ # Excel skill
├── pptx/ # PowerPoint skill
└── ...
Data Flow
- Initialization:
src/renderer/App.tsx→coworkService.init()→ loads config/sessions via IPC → sets up stream listeners - Cowork Session: User sends prompt →
coworkService.startSession()→ IPC to main →CoworkRunner.startSession()→ Claude Agent SDK execution → streaming events back to renderer via IPC → Redux updates - Tool Permissions: Claude requests tool use →
CoworkRunneremitspermissionRequest→ UI showsCoworkPermissionModal→ user approves/denies → result sent back to SDK - Persistence: Cowork sessions stored in SQLite (
cowork_sessions,cowork_messagestables)
Cowork System
The Cowork feature provides AI-assisted coding sessions:
Execution Modes (CoworkExecutionMode):
auto- Automatically choose based on context (OpenClaw:sandbox.mode=non-main)local- Run tools directly on the local machine (OpenClaw:sandbox.mode=off)sandbox- Full sandbox isolation (OpenClaw:sandbox.mode=all)
Agent Engines (configured via agentEngine in cowork config):
yd_cowork- Built-in Claude Agent SDK runner (claudeRuntimeAdapter.ts)openclaw- OpenClaw gateway (openclawRuntimeAdapter.ts); requires the bundled OpenClaw runtime to be running. Engine lifecycle managed byOpenClawEngineManagerwith states:not_installed → ready → starting → running | error
Both engines expose identical stream events through CoworkEngineRouter, so the renderer is engine-agnostic. Engine-specific IPC: openclaw:engine:* channels manage runtime lifecycle separately from cowork:* session channels.
Memory System: Automatically extracts and manages user memories from conversations:
coworkMemoryExtractor.ts- Detects explicit remember/forget commands (Chinese/English) and implicitly extracts personal facts using signal patterns (profile, preferences, ownership). Uses guard levels (strict/standard/relaxed) with confidence thresholds.coworkMemoryJudge.ts- Validates memory candidates with rule-based scoring and optional LLM secondary judgment for borderline cases. Includes TTL-based caching for LLM results.
Stream Events (IPC from main to renderer):
message- New message added to sessionmessageUpdate- Streaming content update for existing messagepermissionRequest- Tool needs user approvalcomplete- Session execution finishederror- Session encountered an error
Key IPC Channels:
cowork:startSession,cowork:continueSession,cowork:stopSessioncowork:getSession,cowork:listSessions,cowork:deleteSessioncowork:respondToPermission,cowork:getConfig,cowork:setConfig
Key Patterns
- Streaming responses:
apiService.chat()uses SSE withonProgresscallback for real-time message updates - Cowork streaming: Uses IPC event listeners (
onStreamMessage,onStreamMessageUpdate, etc.) for bidirectional communication - Markdown rendering:
react-markdownwithremark-gfm,remark-math,rehype-katexfor GitHub markdown and LaTeX - Theme system: Class-based Tailwind dark mode, applies
darkclass to<html>element - i18n: Simple key-value translation in
services/i18n.ts, supports Chinese (default) and English. Language auto-detected from system locale on first run. - Path alias:
@maps tosrc/renderer/in Vite config for imports. - Skills: Custom skill definitions in
SKILLs/directory, configured viaskills.config.json
Artifacts System
The Artifacts feature provides rich preview of code outputs similar to Claude's artifacts:
Supported Types:
html- Full HTML pages rendered in sandboxed iframesvg- SVG graphics with DOMPurify sanitization and zoom controlsmermaid- Flowcharts, sequence diagrams, class diagrams via Mermaid.jsreact- React/JSX components compiled with Babel in isolated iframecode- Syntax highlighted code with line numbers
Detection Methods:
- Explicit markers:
```artifact:html title="My Page" - Heuristic detection: Analyzes code block language and content patterns
UI Components:
- Right-side panel (300-800px resizable width)
- Header with type icon, title, copy/download/close buttons
- Artifact badges in messages to switch between artifacts
Security:
- HTML:
sandbox="allow-scripts"with noallow-same-origin - SVG: DOMPurify removes all script content
- React: Completely isolated iframe with no network access
- Mermaid:
securityLevel: 'strict'configuration
Configuration
- App config stored in SQLite
kvtable - Cowork config stored in
cowork_configtable (workingDirectory, systemPrompt, executionMode, agentEngine) - Cowork sessions and messages stored in
cowork_sessionsandcowork_messagestables - Scheduled tasks stored in
scheduled_taskstable (cron expressions, task content) - Database file:
lobsterai.sqlitein user data directory - OpenClaw pinned version declared in
package.jsonunder"openclaw": { "version": "...", "repo": "..." }; update the version field and re-run to upgrade
TypeScript Configuration
tsconfig.json: React/renderer code (ES2020, ESNext modules)electron-tsconfig.json: Electron main process (CommonJS output todist-electron/)
Key Dependencies
@anthropic-ai/claude-agent-sdk- Claude Agent SDK for cowork sessionssql.js- SQLite database for persistencereact-markdown,remark-gfm,rehype-katex- Markdown rendering with math supportmermaid- Diagram renderingdompurify- SVG/HTML sanitization
Coding Style & Naming Conventions
- Use TypeScript, functional React components, and Hooks; keep logic in
src/renderer/services/when it is not UI-specific. - Match existing formatting: 2-space indentation, single quotes, and semicolons.
- Naming:
PascalCasefor components (e.g.,Chat.tsx),camelCasefor functions/vars, and*Slice.tsfor Redux slices. - Tailwind CSS is the primary styling approach; prefer utility classes over bespoke CSS.
Logging Guidelines
The main process uses electron-log via src/main/logger.ts, which intercepts all console.* calls and writes them to daily-rotated log files. No additional logging library is needed — use the standard console API everywhere in src/main/.
Log Levels
Choose the level that matches the significance of the event:
| Level | API | When to use |
|---|---|---|
| Error | console.error |
Unrecoverable failures that need investigation — caught exceptions, broken invariants, data corruption |
| Warn | console.warn |
Unexpected but recoverable situations — missing optional config, fallback behavior, degraded service |
| Info | console.log |
Key lifecycle events worth keeping in production logs — service started/stopped, connection established/lost, session created/destroyed, configuration changed |
| Debug | console.debug |
Development-time detail useful only when actively debugging — intermediate state, request/response payloads, loop iterations, sync cursors |
Message Format
Log messages must read as plain English sentences, not as variable dumps.
Tag: Every message starts with a bracketed module tag: [ModuleName].
// Good — describes what happened in natural language
console.log('[ChannelSync] discovered 3 new channel sessions, notified 2 windows');
console.warn('[ChannelSync] session list returned unexpected type, skipping');
console.error('[ChannelSync] polling failed:', error);
// Bad — dumps variable names and raw values
console.log('[ChannelSync] pollChannelSessions: got', sessions.length, 'sessions, keys:', sessions.map(s => s?.key).join(', '));
console.log('[Debug:syncChannelUserMessages] cursor:', cursor, 'history entries:', historyEntries.length);
Rules
- No per-tick logging at info level. Polling loops, sync cycles, and heartbeats that fire every few seconds must use
console.debugor be removed entirely. A single summary line at info level is acceptable only when something meaningful changed (e.g. new session discovered, messages synced). - No function-entry logging. Do not log "function X called with args Y" unless it is a rare or important operation. Routine calls (per-poll, per-message) must not produce info-level output.
- No variable-name labels. Write
received 5 messagesnothistoryMessages: 5. Writesession not foundnotsessionId: null. - Include context only when useful. An error log should include the relevant identifier (session ID, channel key) so the issue can be traced. A routine success log should not list every parameter.
- Keep messages concise. One line per event. Do not spread a single log across multiple
console.logcalls. - Errors must include the error object. Always pass the caught error as the last argument:
console.error('[Module] operation failed:', error). - Use English for all log messages. No Chinese or other non-ASCII text in logs.
Before Submitting
When adding or modifying log statements, verify:
- No new
console.logcalls inside hot loops or polling callbacks — useconsole.debuginstead. - Messages read as natural English, not as stringified code.
- Error/warn logs include enough context to diagnose without a debugger.
Testing Guidelines
- Unit tests use Vitest and are co-located with the source files they cover.
- Test files must use the
.test.tsextension and be placed next to the source file (e.g.src/main/foo.ts→src/main/foo.test.ts). - Import test utilities from
vitest:import { test, expect } from 'vitest'; - Never use
.test.mjsor any other extension —.test.tsis the only accepted format. - Run all tests:
npm test. Filter by module:npm test -- <name>(e.g.npm test -- logger). - Avoid importing Electron-only APIs (e.g.
electron-log) in tests — inline any logic that depends on them. - Validate UI changes manually by running
npm run electron:devand exercising key flows:- Cowork: start session, send prompts, approve/deny tool permissions, stop session
- Artifacts: preview HTML, SVG, Mermaid diagrams, React components
- Settings: theme switching, language switching
- Keep console warnings/errors clean; lint via
npm run lintbefore submitting.
Internationalization (i18n)
- Never hardcode user-visible strings. All UI text, labels, messages, and titles must go through the i18n system.
- Renderer process: use
t('key')fromsrc/renderer/services/i18n.ts. Add new keys to both thezhandensections in that file. - Main process (tray menu, session titles, notifications, etc.): use
t('key')fromsrc/main/i18n.ts. Add new keys to both thezhandensections in that file. - When adding a new key, always provide translations for both languages. If unsure of a translation, leave a comment like
// TODO: translaterather than omitting the key. - Error messages shown only in DevTools/logs (not visible to users) are exempt.
Commit & Pull Request Guidelines
All commit messages must follow the Conventional Commits spec and be written in English.
Commit Message Format
type(scope): short imperative summary
Optional body in English markdown explaining *why* (not what).
Optional footer: BREAKING CHANGE: ..., Closes #123, etc.
Types: feat, fix, refactor, chore, docs, test, perf, style, ci, build, revert
Rules:
- Subject line: lowercase, imperative mood, no trailing period, ≤72 chars
- Scope (optional): the affected area, e.g.
feat(cowork):,fix(im): - Body and footer must be in English markdown
- Breaking changes: add
!after type/scope (feat!:) and aBREAKING CHANGE:footer
Examples:
feat(cowork): add streaming progress indicator
fix(sqlite): prevent duplicate session insert on retry
chore: bump version to 2026.3.18
- PRs should include a concise description, linked issue if applicable, and screenshots for UI changes.
- Call out any Electron-specific behavior changes (IPC, storage, windowing) in the PR description.