- provider-catalog: getDefaultModelsForProvider() now returns preset's
defaultModels even when empty, instead of falling through to
ANTHROPIC_DEFAULT_MODELS. Fixes Volcengine (defaultModels: []) incorrectly
showing Sonnet/Opus/Haiku.
- providers/models route: remove DEFAULT_MODELS fallback when both DB and
catalog are empty — providers like Volcengine that require user-specified
model names should show only those models.
- Add missing src/app/api/chat/sessions/by-cwd/route.ts that was untracked;
without it worktree switching would 404 in release builds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Assistant trigger CAS (P1):
- hook-triggered route: added expectedOwner param for compare-and-swap —
rejects if current owner doesn't match, closing the race where two tabs
both clear then both claim the lock
- useAssistantTrigger: clear sends expectedOwner (stale session), set sends
expectedOwner: null; non-2xx responses now bail instead of falling through
- Server-side completion (chat/route.ts): only clears lock if owner matches
current sessionId, preventing completed sessions from wiping active locks
- Compensation branch: clear now passes expectedOwner to avoid bypassing CAS
Image-gen LIKE escape (P2):
- db.ts: escape backslashes before % and _ since ESCAPE '\' makes \ the
escape char — prevents Windows paths / escaped JSON from corrupting the
LIKE pattern and silently falling back to prompt-prefix matching
Provider options fetch race (P2):
- page.tsx + ChatView.tsx: provider options fetch now uses AbortController
with cleanup on dependency change — prevents stale slow responses from
overwriting the current provider's thinking/context_1m state
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New plugin-discovery.ts: scans ~/.claude/plugins/marketplaces/ and
external_plugins/, reads .claude-plugin/plugin.json manifests, merges
enabledPlugins across user/project/local settings layers with write
escalation to local when higher layers override
- Rewrite /api/plugins and /api/plugins/[id] to use real plugin manifests
with name@marketplace composite keys, blocklist, and multi-layer
enable/disable with CAS-style write escalation
- SDK plugin loading delegated to SDK itself (no explicit queryOptions.plugins)
— ensures parity with Claude CLI's settingSources resolution
- Extended init meta forwarding (plugins, mcp_servers, output_style) in
claude-client.ts and useSSEStream.ts
- Per-provider plugin cache in agent-sdk-capabilities.ts with
getCachedPlugins/setCachedPlugins
- Skills route: provider-scoped plugin cache lookup via sessionId param,
external_plugins commands scan, loaded status annotation
- Updated PluginInfo type with marketplace, location, author, capability flags
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rewrite install-check-prerequisites with multi-path Claude CLI detection
(native, bun, homebrew, npm) and structured result reporting
- Improve InstallWizard with step-by-step progress and better error handling
- Enhance ConnectionStatus with detailed status display and invalidation
- Add /api/claude-status/invalidate route for cache-busting
- Expand platform.ts with richer detection utilities
- Add onInitMeta handler in stream-session-manager
- Update electron.d.ts types for new IPC channels
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace fixed-timeout stale detection with localStorage heartbeat mechanism
- Heartbeat writes {sid, ts} every 10s; isOwnerAlive checks sid matches AND freshness
- Stop heartbeat when triggered stream completes (useEffect on !isStreaming)
- removeHeartbeatIfOwned: only delete key if it still belongs to this session
(prevents new owner's heartbeat from being wiped by old session's cleanup)
- Export clearOnboardingHeartbeat with optional sessionId parameter
- hook-triggered route sets/clears hookTriggeredAt alongside hookTriggeredSessionId
- MiniMax model auto-correction: reset model when not in current provider's list
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Image reference store session-scoped:
- Rewrite image-ref-store.ts: all keys/functions now scoped by sessionId
- Thread sessionId through MessageList → StreamingMessage/MessageItem → ImageGenConfirmation
- ChatView loads last-generated images for current session on mount
Image write-back robustness:
- Remove localStorage caching from ImageGenConfirmation (root cause of stale results)
- Add rawRequestBlock for exact content matching in DB fallback query
- updateMessageBySessionAndHint prefers exact block match, falls back to prompt hint
- PUT route passes raw_request_block to DB function
- Add retry-after-3s when first PUT fails and no messageId available
- Exclude already-completed messages (NOT LIKE image-gen-result) from update query
New session page (/chat) fix:
- Add createdSessionId state, set after session creation, pass to MessageList
- Prevents first-message image-gen-request from inheriting stale panel sessionId
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add ProviderOptionsSection component for per-provider thinking_mode and context_1m
- Store options in options_json column (DB providers) or settings table (env provider)
- Add /api/providers/options GET/PUT route
- Fix SDK integration: use `betas` (not `betaHeaders`) for context-1m-2025-08-07
- ChatView and /chat page always reset thinkingMode on provider change (isolation fix)
- Auto-correct model when switching providers if model not in new provider's list
- Add i18n keys for provider options in both en.ts and zh.ts
- Update test fixtures with options_json field
- Remove unused lastAssistantText variable in claude-client.ts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract 4 self-contained sub-components from MessageInput.tsx:
- SlashCommandPopover (243L): slash command filtering, rendering, selection
- CliToolsPopover (128L): CLI tool selection with search/filter
- ModelSelectorDropdown (150L): provider/model selector dropdown
- EffortSelectorDropdown (75L): thinking effort level selector
All components have clean props interfaces ready for integration.
MessageInput.tsx wiring deferred to follow-up — the file's 1,659 lines
of tightly coupled state make this a careful, incremental process.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merge chat/CodeBlock.tsx (Prism via react-syntax-highlighter) into
ai-elements/code-block.tsx (Shiki):
- Port collapse/expand (COLLAPSE_THRESHOLD=20, VISIBLE_LINES=10) from Prism version
- Port InlineCode component export
- Port "Copy as Markdown" functionality
- Update ToolCallBlock.tsx import to use ai-elements/code-block
- Delete src/components/chat/CodeBlock.tsx
Note: react-syntax-highlighter still used by DocPreview and FilePreview,
so dependency not removed yet.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New hooks:
- useSettings: generic settings CRUD (fetch/save with loading/saving states)
- useBridgeAdapter: bridge adapter settings + verify flow
- useBridgeStatus: bridge status polling + start/stop control
- useAccountInfo: Claude SDK account info fetching
Refactored components:
- GeneralSection: replace inline fetchAccountInfo with useAccountInfo hook
- BridgeSection: replace inline fetchStatus/startBridge/stopBridge + polling
interval with useBridgeStatus hook (removed ~50 lines)
The remaining bridge adapter sections (Telegram/Discord/Feishu/QQ) can be
migrated to useBridgeAdapter in a follow-up — hooks are ready for adoption.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create src/components/patterns/ with 6 pure-presentation components
that abstract repeated layout patterns from settings/bridge pages:
- SettingsCard: rounded card with optional title/description
- FieldRow: label+description left, control right, optional separator
- SectionPage: max-width page container with consistent spacing
- StatusBanner: success/warning/error/info inline status messages
- EmptyState: centered empty state with icon, title, description, action
- IconAction: ghost icon button with optional tooltip wrapper
All components are zero-side-effect, only import from React and @/lib/utils
(plus ui/button and ui/tooltip for IconAction).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add .editorconfig for consistent formatting (2-space indent, LF, UTF-8)
- Add docs/ui-governance.md defining 4-layer architecture, icon strategy,
color token rules, component size limits, and new primitive approval flow
- Update eslint.config.mjs with governance rules:
- warn on native <button/input/select/textarea> in business components
- warn on lucide-react imports (prefer @phosphor-icons/react)
- warn on component files exceeding 500 lines
- error on data logic imports in patterns/ layer
- Update components.json iconLibrary from "hugeicons" to "phosphor"
Note: 4 pre-existing test failures (assistant-workspace, db-shutdown,
provider-resolver, timezone-boundaries) unrelated to this change.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The bg-[var(--user-bubble)] arbitrary value syntax wasn't working with
Tailwind v4's Lightning CSS pipeline. Switch to bg-(--user-bubble) which
is Tailwind v4's native CSS variable reference syntax.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
File tree "+" button (Bug 1):
- Replace fragile fetch/blob attachment bridge with @filepath text insertion
- Works identically on web and Electron (pure text, no /api/files/raw fetch)
- Remove unused mimeFromFilename helper
Model selection follows session (Bug 2):
- page.tsx: include model/provider_id in session creation request
- [id]/page.tsx: add sessionInfoLoaded gate — ChatView only mounts after
session metadata is fetched, preventing stale localStorage fallback
- SplitColumn.tsx: same sessionInfoLoaded gate for split-pane mode
- ChatView.tsx: useEffect sync only overrides model/provider when props
are non-empty; empty = genuinely no model saved (old/imported sessions)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Icon library migration:
- Replace @hugeicons/react with @phosphor-icons/react across 60+ components
(Phosphor supports 6 weight variants: thin/light/regular/bold/fill/duotone)
- Update apps/site (marketing) to use Phosphor icons
- Remove leftover svg.hugeicons CSS rules from globals.css
UI improvements:
- NavRail: selected tab icons use filled weight, unselected use regular
- User chat bubble: dark bg + white text in light mode, light bg + dark text
in dark mode (new --user-bubble / --user-bubble-foreground CSS variables)
- Fix expand/collapse icons: use CaretRight (triangle) instead of ArrowRight
in file-tree.tsx and tool-actions-group.tsx
Lint fixes:
- Fix eslint react-hooks/static-components in CodeBlock, ToolCallBlock,
tool-actions-group — use createElement() instead of PascalCase variable
- Fix react-hooks/rules-of-hooks in MessageItem — move early return after hooks
- Fix react-hooks/set-state-in-effect in GalleryDetail — use ref pattern
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Link to codepilot.sh docs (en + zh) for Providers, MCP, Skills,
Bridge, Assistant Workspace, Installation, and FAQ.
Updated English, Chinese, and Japanese READMEs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Anthropic Third-party API: add API Key / Auth Token selector in connect dialog
with proper extra_env injection and auth style detection
- Edit mode: reuse PresetConnectDialog for matched providers instead of generic
ProviderForm, showing only relevant fields consistent with connect experience
- Edit mode: show masked key (dots) instead of empty field; switching auth style
clears key and requires re-entry; switching back restores masked value
- Edit mode: expose headers_json, env_overrides_json, notes in advanced section
so all fields remain visible and editable
- Edit mode: role_models_json merge logic preserves unseen roles (reasoning, small)
while allowing clearing of visible mapping fields
- Dialog: add max-h-[calc(100vh-4rem)] + overflow-y-auto to DialogContent base;
remove overflow-hidden from preset dialog so scrolling actually works
- Connected provider badge: detect ANTHROPIC_AUTH_TOKEN in extra_env to show
correct "Auth Token" vs "API Key" label
- i18n: add provider.authToken, provider.authStyle keys (en + zh)
- Bump version to 0.31.0
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- RainbowButton: dark mode text white→dark for contrast on white bg
- DownloadButton: dark mode invert to white bg + dark text
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Vercel blocks deployment of vulnerable Next.js versions.
15.3.6 is the patched release for the App Router vulnerability.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Skills system:
- Replace `CommandBadge.isSkill: boolean` with `kind: SkillKind` (agent_skill | slash_command | sdk_command | codepilot_command)
- handleSubmit dispatches by kind: agent_skill retains skill name in prompt, slash/sdk commands pass through to SDK, codepilot commands expand via COMMAND_PROMPTS
- Popover groups commands into Commands / Slash Commands / Agent Skills
- API `/api/skills` returns `kind` field; `displayOverride` persisted through full chain (frontend → API → DB)
- Forward SDK `system:init` slash_commands/skills metadata to frontend via SSE
MCP management:
- Fix remote server (SSE/HTTP) creation: validate `url` instead of `command`
- Fix dual-config read/write asymmetry: PUT routes servers by `_source` to correct file (~/.claude.json vs ~/.claude/settings.json)
- DELETE checks both config files; POST checks both for name collision
- McpManager preserves `_source` tags on edit/rename; JSON editor only manages settings.json servers
Completion processing:
- Extract core logic into `onboarding-processor.ts` and `checkin-processor.ts` (shared by route handlers and server-side detection)
- Remove frontend `detectAssistantCompletion` from ChatView — server-side path in `finally` block is now the single source of truth
- Replace hard-coded `fetch("http://localhost:PORT/...")` with direct function imports
- Both processors are internally idempotent (check state before processing)
Other:
- `settingSources` always includes 'user' so SDK loads ~/.claude/skills/
- Add 24 unit tests for SkillKind dispatch logic
- Add onboarding-completion extraction tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Issue 1 — Onboarding re-triggering after completion:
- ChatView.tsx: fence regex now tolerates CRLF, trailing whitespace, leading
whitespace before closing fence; JSON.parse input trimmed
- ChatView.tsx: hookTriggeredSessionId only cleared when backend returns ok
- assistant-workspace.ts: saveState uses atomic write (tmp + rename)
Issue 2 — "CLI output was not valid JSON" during document reads:
- claude-client.ts: remove all SDK hooks (Notification + PostToolUse) to
eliminate hook_callback control_request transport that corrupts stdout
- claude-client.ts: TodoWrite sync moved to message stream — tool_use records
pending input, tool_result emits task_update only on success
- claude-client.ts: notifications derived from system/task_notification and
result/is_error messages; Telegram forwarding preserved via notifyGeneric
Issue 3 — File tree not updating on project switch:
- FileTree.tsx: AbortController cancels in-flight requests on directory change;
abort fires before empty-directory early return to prevent stale overwrites;
error state distinguishes empty dir from load failure; loading always reset
- page.tsx: clear workingDirectory immediately on session switch
- SplitColumn.tsx: clear workingDirectory when active column has no directory
Tests: 9 new tests for fence parsing, atomic write, corrupted state fallback.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract provider resolution logic into two new modules:
- provider-catalog.ts: vendor presets, default model catalogs, protocol definitions
- provider-resolver.ts: unified resolution for all consumers (Claude SDK, AI SDK, model listing)
DB migration:
- Add protocol, headers_json, env_overrides_json, role_models_json columns to api_providers
- Create provider_models table for per-provider model catalog
Consumer refactoring:
- claude-client.ts: replace inline env-building with toClaudeCodeEnv()
- text-generator.ts: replace inline config with toAiSdkConfig()
- conversation-engine.ts, onboarding, checkin, plan routes: use shared resolveProvider()
- /api/providers/models: read from catalog + provider_models table
UI improvements:
- Merge confusing "Custom API (Anthropic-compatible)" into "Anthropic Third-party API"
- Add 3-field model name mapping (sonnet/opus/haiku) in advanced options
- ProviderForm.tsx: support new fields (protocol, env_overrides, role_models)
Bug fixes:
- Protect auth credentials from being deleted by legacy extra_env empty-string overrides
- Fix env var names: ANTHROPIC_DEFAULT_SONNET → ANTHROPIC_DEFAULT_SONNET_MODEL (SDK convention)
Tests: 229/229 passed. Verified with aiberm (no mapping) and pipellm (with mapping).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: `new Date().toISOString().slice(0, 10)` returns UTC date,
causing date-based features to be off by up to ±1 day for non-UTC users.
Check-in / assistant workspace:
- Add `getLocalDateString()` utility, replace all UTC date comparisons
- Schema v2→v3 migration: normalize lastCheckInDate only when it matches
UTC today (ambiguous window); leave past dates untouched so check-in
triggers correctly after upgrade
- Runtime compat in `needsDailyCheckIn`: accept UTC today as fallback
for edge cases the migration cannot catch
- `needsDailyCheckIn(state, now?)` accepts optional time for testability
Usage stats:
- Replace single-offset SQL bucketing (`DATE(datetime(..., '+N minutes'))`)
with JS-side per-row aggregation using `getLocalDateString(new Date(utc))`
- Date methods account for historical DST offset at each message's timestamp
- `getTokenUsageStats(days, now?)` accepts optional time to pin window start
- Window boundary via `localDayStartAsUTC()` is already DST-aware
Tests (new file: timezone-boundaries.test.ts, 22 tests):
- getLocalDateString at UTC+9/UTC-5/UTC boundaries with fixed timestamps
- needsDailyCheckIn: compat suppression, clearly-past trigger, local match
- v2→v3 migration: targeted normalization, past-date preservation, null safe
- localDayStartAsUTC: UTC+8/UTC-5 midnight boundaries
- Usage stats bucketing: UTC+9 cross-day, summary window, DST spring-forward
- All usage stats tests use pinned `now` — no time-bomb from fixed test data
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
P1 fixes:
- Replace "Extensions page" with actual MCP + Skills sidebar pages
- Update signing status: macOS is signed (not notarized), Win/Linux unsigned
- Fix Gatekeeper warning description to match signed-but-unnotarized state
P2 fixes:
- Correct Assistant Workspace file locations: soul.md/user.md/claude.md/memory.md
are at workspace root, .assistant/ is state-only subdirectory
- Translate "Why CodePilot" section to full Japanese (was English regression)
- Add provider auth path clarification (CLI/env optional vs custom required)
- Add specific page names in First Launch (Settings > Providers, Settings > Assistant, MCP, Skills)
Missing content:
- Add structured Documentation section with user guides and developer docs
- Add FAQ section covering: claude not found, no models after provider config,
dev vs electron:dev, workspace file locations, Bridge platform tokens
- Sync all additions across EN, CN, JA
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace 17-item flat feature list with "Why CodePilot" benefit paragraphs
- Add Quick Links nav bar, First Launch guide, and 3 grouped capability tables
- Collapse ~120-line installation troubleshooting into <details> blocks
- Remove Tech Stack and Project Structure (already in ARCHITECTURE.md)
- Move WeChat QR from hero to Community section
- Add Documentation section linking to ARCHITECTURE.md and docs/
- README_CN.md: full Chinese translation, identical structure
- README_JA.md: Japanese headings/tables, English kept for narrative sections
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
QQ Bridge Adapter (new):
- qq-api.ts: AppAccessToken, WebSocket Gateway, heartbeat, passive reply,
msg_seq auto-increment — pure QQ protocol layer
- qq-adapter.ts: BaseChannelAdapter impl with WebSocket lifecycle,
C2C_MESSAGE_CREATE handling, image download (base64), dedup, reconnect
- Settings UI (QqBridgeSection.tsx) + API routes (/api/settings/qq)
- i18n keys for en/zh, PLATFORM_LIMITS qq: 2000
replyToMessageId threading:
- bridge-manager.ts: thread msg.messageId through all deliver/command/
permission paths — required for QQ passive reply (msg_id mandatory)
- permission-broker.ts: accept and forward replyToMessageId
SDK crash resilience (bug fix):
- bridge-manager.ts: check hasError BEFORE saving sdkSessionId — the SDK
may emit a session_id before crashing, and saving that broken ID caused
ALL subsequent messages to fail by repeatedly resuming a corrupted session
- claude-client.ts: always clear sdk_session_id on crash, even for fresh
sessions (SDK can persist a new ID via status event before dying)
- conversation-engine.ts: attach filePath to file objects after disk write
so streamClaude() reuses on-disk copies (prevents duplicate writes,
matches desktop route behavior)
QQ permission handling (P1 fix):
- permission-broker.ts: for channels without inline button support (QQ),
render text-based /perm allow|deny|allow_session commands instead of
invisible inlineButtons that would deadlock the stream
QQ passive reply budget (P2 fix):
- bridge-manager.ts: QQ-specific delivery limits chunks to 3 max,
truncates with notice to avoid exhausting passive reply quota
- delivery-layer.ts: export chunkText for reuse
Error message improvement:
- claude-client.ts: when exit code 1 with image files, hint "Provider may
not support image/vision input" instead of generic auth/network message
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Slash command:
- Fix first-click bug: set textarea.value and cursor position before
calling handleInputChange so selectionStart reads correctly
- Move slash button to right of + (attach) button
- Simplify icon from CommandLineIcon to plain "/" text
Composer action bar:
- Move Agent toggle and permission selector to left side (was centered)
- Adjust spacing: reduce input padding, increase bottom margin
Permission selector:
- Add dropdown arrow indicator (ArrowDown01Icon)
- Change full_access color from orange to red (icon + text + background)
- Check res.ok before updating UI state (prevent false success)
- Make sessionId optional to support /chat new session page
- Log warnings on PATCH failure instead of silent catch
New chat page (/chat):
- Add ChatPermissionSelector with local state before session exists
- Pass permission_profile in session creation payload
- Backend createSession() now accepts and writes permission_profile
Context usage indicator:
- Reduce circle size from 22px to 16px
- Change used-portion color to dark gray (zinc-600/400)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Composer restructure:
- MessageInput internal footer now only contains: file button, model
selector, slash command button, and send button
- Removed mode toggle (Code/Plan) from composer area (logic preserved)
- New ChatComposerActionBar below input with three slots: left (Design
Agent), center (permission selector), right (context indicator)
New components:
- SlashCommandButton: inserts `/` at cursor, reuses existing slash
command detection and popover logic
- ChatPermissionSelector: session-level permission toggle (default /
full_access) with danger confirmation dialog
- ContextUsageIndicator: SVG donut chart showing context usage ratio
with HoverCard displaying model, tokens, cache stats
- ChatComposerActionBar: slot-based container for action bar items
Session permission (permission_profile):
- DB: new column on chat_sessions, default 'default'
- API: PATCH sessions supports updating permission_profile
- Chat API: bypassPermissions = global setting OR session full_access
- PermissionPrompt: auto-approves and hides when full_access
- Claude client: combines global and per-session bypass flags
Bridge integration:
- permission-broker: auto-approves permissions for full_access sessions
without sending IM notifications
- conversation-engine: passes bypassPermissions to SDK for full_access
- PATCH route: auto-approves pending bridge permissions on switch to
full_access
Other:
- ImageGenToggle relabeled to "Design Agent" / "设计 Agent"
- New model-context.ts with MODEL_CONTEXT_WINDOWS lookup
- Provider models API returns optional contextWindow field
- New useContextUsage hook for token usage calculation
- i18n: added composer.*, permission.*, context.* keys (en + zh)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>