feat: numerous bugfixes and features - #1037
Conversation
Guard retained PTY ownership so delayed close events cannot detach a replacement socket. Terminate half-open sockets after a missed pong. Cover both lifecycle paths with focused tests.
- show a "Run git init" button in Source Control when the project is not
a git repository (new POST /api/git/init), hide the branch/refresh
header controls in that state, and stop logging the expected no-repo
state as a server error
- track commit-history loading separately so "No commits found" no longer
flashes while commits are still being fetched
- hide "Create Initial Commit" when the reposi
- auto-refresh the window after a platform update ("This will refresh the
window in N seconds") and cancel the refresh if the update fails
- handle non-JSON responses from /api/system/update so hosted/Docker
deployments no longer surface "Unexpected token '<'" when the server
restarts mid-update
…rror rows Session routing: /session/<id> URLs could select the wrong project or show nothing, because the frontend only searched the paginated project payloads (first session page per project) and fell back to binding the session to whatever project was selected. - add GET /api/providers/sessions/:sessionId resolving a session (by app id, falling back to the provider-native id) to its metadata and owning project - resolve unmatched URL sessions thro correct project, and renavigate provider-native alias ids to the canonical app id - keep the selected-project placeholder only as a fallback for unknown ids Chat errors and commands: - Bash command rows no longer auto-ex and status badge signal the failure and output stays behind the chevron - collapsed commands truncate to one line again: the global `.chat-message code` pre-wrap !imponcate`, so the command now renders in a span - non-Bash tool error results render as a collapsed red row that expands to the full error content (new ToolErrorDisplay)
The chat websocket handler used to substitute the provider-native session id into the spawn options, so runtimes keyed their process maps by provider ids and abort/pending-approval lookups had to translate back and forth. Now the app session id flows through everywhere, and each runtime resolves the provider-native id itself via the new sessionsService.resolveProviderSessionId() only where its CLI/SDK actually needs it (resume flags, resumeThread, OpenCode's token DB). - chat.send passes the app session id; the unused `resume` flag is gone - chat.abort addresses runtimes by aps aborting a session's first run actually kill the runtime (previously abort was a no-op until the provider announced its native id) - pending permission approvals are tr, removing the remap in chat.subscribe - runtimes key process maps by the app session id when given, falling back to the captured provider id fo - resolveResumeModel now receives the app id, matching how the active-model endpoint records session model changes - run notifications (stopped/failed/permission) carry the app session id, matching frontend deep links
- Render user bubbles through the shared Markdown component with remark-breaks so typed line breaks are preserved - Add explicit ul/ol/li styling so list markers render correctly in both user and assistant messages - Open composer image attachments in the fullscreen lightbox on click - Allow sending/queueing messages with images but no text - Remove the AI commit message generator button from the git panel
…heck, and UI fixes User messages: - Render user messages as markdown with remark-breaks for line break preservation - Add explicit ul/ol/li styling so list markers render correctly in both bubbles - Enable sending/queueing messages with images but no text Chat export: - Add comprehensive export to Markdown, HTML, and PDF with beautiful formatting - Export button in top-right corner of messages pane - Include timestamps and message metadata in exports Provider authentication: - Check provider authentication before sending messages - Show error with instructions: "Not logged in to [provider]. Please log in via Settings > Login in CloudCLI, or in the Shell tab." Image attachments: - Open composer image attachments in fullscreen lightbox on click (reuse existing lightbox from sent images) UI fixes: - Move QuickSettingsPanel to app-level (out of MainContent overflow context) to fix z-index layering on desktop and mobile - Set panel z-index to z-[9999] with backdrop at z-[9998] to ensure it appears above all content including sidebars and file trees - Remove AI commit message generator button from git panel source control tab
- Disable single-dollar math in remark-math to prevent dollar amounts (e.g., $20/psf) from being parsed as LaTeX; keep double-dollar display math ($$…$$) for math expressions
Drop the client-side "Not logged in to [provider]" gate that ran before sending messages. The backend still validates provider authentication, so this pre-flight check was redundant and blocked sends on its own status lookup.
…merge
Add a Worktrees tab to the git panel for Crystal-style parallel workflows:
- Create worktrees in sibling <repo>-worktrees/<branch> folders, from a new
or existing branch, with a live folder preview
- Register each worktree as a linked project ("repo · branch") so chat
sessions, shell, files, and git all work inside it; one-click switching
- Squash or --no-ff merge a worktree back into the base branch, with
optional cleanup (remove worktree + delete branch); conflicts are rolled
back server-side and reported with the conflicting files
- Safe removal: archives the linked project, blocks dirty worktrees unless
changes are explicitly discarded, never touches the main worktree
Backend: new server/modules/worktrees module (routes + list/create/open/
merge/remove services, barrel-only cross-module imports, node:test suite
with injected git runners) mounted at /api/worktrees.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe PR restructures server startup around TypeScript modules and dependency-injected routes, adds provider runtime/session APIs, Git worktree and file-tree capabilities, CLI services, and related client UI integrations. It also updates chat rendering, exports, repository initialization, and WebSocket lifecycle handling. ChangesApplication modularization and runtime services
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 20
🧹 Nitpick comments (3)
server/modules/providers/services/sessions.service.ts (1)
35-53: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueExport the
SessionDetailstype.If
sessionsServiceis exported, keepingSessionDetailsprivate might cause TypeScript declaration errors (TS4023: Exported variable has or is using private name) when generating.d.tsfiles, and it prevents downstream consumers (like the API route handlers) from importing the type explicitly.Consider exporting the type.
♻️ Proposed refactor
-type SessionDetails = { +export type SessionDetails = { /** Canonical app-facing session id (may differ from the looked-up id when a provider-native id was given). */ sessionId: string;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/modules/providers/services/sessions.service.ts` around lines 35 - 53, Export the SessionDetails type so the exported sessionsService API can reference it safely in generated declarations and allow downstream consumers to import it explicitly; change only the type declaration while preserving its existing shape.server/modules/worktrees/tests/worktree-open.service.test.ts (1)
95-122: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winCover the
reactivated_archivedcreation outcome.Add a case where
createProjectreturnsreactivated_archived, then verifyrestoreProjectis called and the active row is returned. This guards the production branch that keeps reopened worktrees visible in the sidebar.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/modules/worktrees/tests/worktree-open.service.test.ts` around lines 95 - 122, Extend the openWorktreeAsProject tests with a case where createProject returns the reactivated_archived outcome. Verify restoreProject is called for the returned project and that the active project row/result is returned, while preserving the existing new-project creation coverage.src/components/chat/utils/chatExport.ts (1)
46-47: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueUse a generic name for the assistant in exports.
The assistant is hardcoded as "Claude" in both the Markdown and HTML exports. Since the application supports multiple providers (e.g., Cursor, Codex, OpenCode), consider using a more generic term like "Assistant" or passing the active provider's name dynamically.
♻️ Proposed fixes
For Markdown export:
} else if (msg.type === 'user') { markdown += '## You\n\n'; } else if (msg.type === 'assistant') { - markdown += '## Claude\n\n'; + markdown += '## Assistant\n\n'; } else if (msg.type === 'error') {For HTML export:
const htmlContent = messages .map((msg) => { - const type = msg.type === 'user' ? '👤 You' : msg.type === 'assistant' ? '🤖 Claude' : `${msg.type}`; + const type = msg.type === 'user' ? '👤 You' : msg.type === 'assistant' ? '🤖 Assistant' : `${msg.type}`; const time = includeMeta && msg.timestamp ? `<p style="font-size: 12px; color: `#999`; margin-top: 8px;">${formatTimestamp(msg.timestamp)}</p>` : '';Also applies to: 94-94
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/chat/utils/chatExport.ts` around lines 46 - 47, Update the assistant export labels in the chat export logic, including the Markdown branch around msg.type === 'assistant' and the corresponding HTML branch, to use a provider-neutral name such as “Assistant” instead of the hardcoded “Claude”.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@server/index.js`:
- Around line 187-188: Add a rate limiter to the `/api/worktrees` mount
alongside `authenticateToken` and before `worktreesRoutes`, using the existing
shared throttle or an appropriate route-local limiter. Ensure all worktree
endpoints inherit the limit without changing authentication or route behavior.
In `@server/modules/providers/list/codex/codex-sessions.provider.ts`:
- Around line 565-582: Prevent orphan custom-tool results by checking
ignoredToolCallIds in the custom_tool_call_output handling before adding a
tool_result; update the provider logic around execToolCallIds and messages.push
accordingly. In server/modules/providers/tests/codex-sessions.test.ts lines
139-143, assert that plan-1 produces no tool_result.
In `@server/modules/websocket/services/shell-websocket.service.ts`:
- Around line 528-541: The message handlers retaining shellProcess must reject
input and resize events from stale sockets. Update the relevant input and resize
handling in the shell websocket service to require
ptySessionsMap.get(ptySessionKey)?.ws === ws before operating on the PTY, and
add a regression test covering stale input after socket replacement.
In `@server/modules/worktrees/services/worktree-git.service.ts`:
- Around line 193-202: Update countChangedFiles so failures from runGit are not
converted to a zero count; remove the catch or propagate an unknown/error
result. Adjust removeWorktree’s safety check to treat an unknown count as unsafe
and prevent non-forced removal unless the status check succeeds and confirms
zero changes.
- Around line 57-71: Update validateWorktreeBranchName to enforce Git ref-name
rules, rejecting double dots, trailing dots, repeated or leading/trailing
slashes, and any component ending in .lock while preserving trimming and
existing invalid-input errors. Extend the validation cases in
server/modules/worktrees/tests/worktree-git.service.test.ts around lines 72-79
to cover each rejected form and confirm valid branch names remain accepted.
In `@server/modules/worktrees/services/worktree-list.service.ts`:
- Around line 87-114: Limit concurrent worktree processing in the Promise.all
flow around the entries.map callback so Git operations do not launch for every
worktree at once. Replace the unbounded mapper with the project’s existing small
concurrency-limited mapping utility, preserving the WorktreeDescriptor
transformation and result ordering.
In `@server/modules/worktrees/services/worktree-merge.service.ts`:
- Around line 97-118: Update the catch block around the rollback in the worktree
merge flow to retain any error from runGit(['reset', '--merge'], repositoryRoot)
instead of suppressing it. When rollback fails, throw a distinct AppError that
reports rollback failure and preserves the rollback error details; only emit
WORKTREE_MERGE_CONFLICT with the “merge was aborted” message when the rollback
succeeds.
In `@server/modules/worktrees/services/worktree-remove.service.ts`:
- Around line 51-74: Model post-operation cleanup as partial success: in
server/modules/worktrees/services/worktree-remove.service.ts lines 51-74, make
the project archival step idempotent/compensating or preserve successful
worktree removal while returning archival failure details; in
server/modules/worktrees/services/worktree-merge.service.ts lines 121-130,
preserve successful merge status while reporting worktree-cleanup status or
expose a safely resumable cleanup operation. Use the existing removal, merge,
project archival, and cleanup service symbols without changing the primary Git
operation semantics.
In `@server/modules/worktrees/worktrees.routes.ts`:
- Around line 93-99: Update the merge request payload handling around
services.merge and the related branch/removal operations to validate boolean
fields rather than coercing them with Boolean(). Accept only actual boolean
values, preserving false when provided; use the existing safe default for
missing or invalid values so malformed strings such as "false" cannot enable
squash, removeAfterMerge, force deletion, or branch deletion.
- Around line 61-70: Move the worktree creation-and-registration workflow out of
the route handler into an application service that calls services.create, then
services.open, and compensates when opening fails by removing the newly created
worktree. Use created.createdBranch to ensure cleanup deletes only a branch
created by this operation, then have the route invoke that service and return
its result.
In `@server/routes/git.js`:
- Around line 423-428: Update the validateGitRepository handling in the
repository initialization flow to catch the error object, continue to git init
only for the specific typed/not-a-repository failure, and rethrow Git execution,
permission, ownership, corruption, and other errors. Keep setting
isAlreadyRepository only after successful validation.
In `@src/components/chat/utils/chatExport.ts`:
- Around line 177-187: Handle the null result from window.open in the PDF export
flow by notifying the user that the popup was blocked and the export could not
proceed. Update the existing win check in the export function while preserving
the current document-writing and delayed print behavior when a window is
successfully opened.
- Around line 97-98: Update the HTML escaping in the chat export flow around
contentStr/contentHTML to escape ampersands before escaping angle brackets,
while preserving the existing conversion of message content to a string. Ensure
entities such as <script> remain literal text in the exported HTML and
cannot be interpreted as markup.
In `@src/components/git-panel/hooks/useWorktreesController.ts`:
- Around line 145-259: Update openWorktree, mergeWorktree, and removeWorktree to
enforce a single in-flight worktree operation using a request-owned ref/token,
rejecting new operations while one is active. Ensure each operation’s cleanup
only clears busyWorktreePath and releases the guard when it still owns the
active token, preventing a newer or concurrent request from being interrupted.
- Around line 102-143: Update createWorktree and the related action around the
referenced lines to capture the selected project ID before the request, then
verify it still matches the current selectedProject before applying response
results. Discard stale responses by skipping onProjectSelect, onProjectsRefresh,
fetchWorktrees, and success handling when the project changed, while preserving
loading cleanup and error handling.
- Around line 102-180: Guard completion side effects against stale project
selections by capturing the initiating projectId in createWorktree and
openWorktree, then only call onProjectsRefresh, onProjectSelect, or
fetchWorktrees when it still matches the currently selected project; apply the
corresponding captured-projectId check in useGitPanelController.ts lines 728-759
before launching status, branch, and remote refreshes.
In `@src/components/git-panel/view/modals/NewWorktreeModal.tsx`:
- Around line 41-48: Update the NewWorktreeModal effect that sets
selectedBaseBranch so a null baseBranch falls back to the first available local
branch rather than ''. Keep the controlled select value synchronized with an
existing valid option, and ensure submission uses that selected branch instead
of sending null when the main worktree is detached.
In `@src/components/git-panel/view/worktrees/WorktreesView.tsx`:
- Around line 108-111: Update the actions container in WorktreesView so
non-current worktree actions are also visible on mobile and whenever one of
their controls receives keyboard focus, while preserving the existing
current/busy visibility behavior and hover transition for pointer interactions.
In `@src/hooks/useProjectsState.ts`:
- Around line 437-444: Reset sessionLookupRef.current to null whenever sessionId
changes, while preserving the existing one-attempt-per-ID guard during a
session's lifetime. Update the effect or dependency-driven logic near the
sessionIdRef synchronization and ensure revisiting a previously resolved
deep-linked session can execute the lookup and selection flow instead of exiting
early.
- Around line 844-944: Remove the early return that prevents session resolution
when projects.length is zero, allowing the session lookup effect containing
sessionLookupRef and api.sessionDetails to run with an empty active project
list. Preserve the existing backend lookup and synthesized-project behavior for
archived projects or users without active projects.
---
Nitpick comments:
In `@server/modules/providers/services/sessions.service.ts`:
- Around line 35-53: Export the SessionDetails type so the exported
sessionsService API can reference it safely in generated declarations and allow
downstream consumers to import it explicitly; change only the type declaration
while preserving its existing shape.
In `@server/modules/worktrees/tests/worktree-open.service.test.ts`:
- Around line 95-122: Extend the openWorktreeAsProject tests with a case where
createProject returns the reactivated_archived outcome. Verify restoreProject is
called for the returned project and that the active project row/result is
returned, while preserving the existing new-project creation coverage.
In `@src/components/chat/utils/chatExport.ts`:
- Around line 46-47: Update the assistant export labels in the chat export
logic, including the Markdown branch around msg.type === 'assistant' and the
corresponding HTML branch, to use a provider-neutral name such as “Assistant”
instead of the hardcoded “Claude”.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 1c273d69-3d68-4b1e-a65a-4cd1f1942bdd
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (76)
package.jsonserver/claude-sdk.jsserver/cursor-cli.jsserver/index.jsserver/modules/database/index.tsserver/modules/projects/index.tsserver/modules/providers/list/claude/claude-session-synchronizer.provider.tsserver/modules/providers/list/codex/codex-sessions.provider.tsserver/modules/providers/provider.routes.tsserver/modules/providers/services/sessions-watcher.service.tsserver/modules/providers/services/sessions.service.tsserver/modules/providers/tests/codex-sessions.test.tsserver/modules/providers/tests/sessions-details.test.tsserver/modules/websocket/README.mdserver/modules/websocket/services/chat-websocket.service.tsserver/modules/websocket/services/shell-websocket.service.tsserver/modules/websocket/services/websocket-server.service.tsserver/modules/websocket/tests/shell-websocket.service.test.tsserver/modules/websocket/tests/websocket-heartbeat.service.test.tsserver/modules/worktrees/index.tsserver/modules/worktrees/services/worktree-create.service.tsserver/modules/worktrees/services/worktree-git.service.tsserver/modules/worktrees/services/worktree-list.service.tsserver/modules/worktrees/services/worktree-merge.service.tsserver/modules/worktrees/services/worktree-open.service.tsserver/modules/worktrees/services/worktree-remove.service.tsserver/modules/worktrees/tests/worktree-create.service.test.tsserver/modules/worktrees/tests/worktree-git.service.test.tsserver/modules/worktrees/tests/worktree-merge.service.test.tsserver/modules/worktrees/tests/worktree-open.service.test.tsserver/modules/worktrees/tests/worktree-remove.service.test.tsserver/modules/worktrees/tests/worktrees.routes.test.tsserver/modules/worktrees/worktrees.module.tsserver/modules/worktrees/worktrees.routes.tsserver/openai-codex.jsserver/opencode-cli.jsserver/routes/git.jsserver/shared/types.tssrc/components/app/AppContent.tsxsrc/components/chat/hooks/useChatComposerState.tssrc/components/chat/tools/ToolRenderer.tsxsrc/components/chat/tools/components/BashCommandDisplay.tsxsrc/components/chat/tools/components/ToolErrorDisplay.tsxsrc/components/chat/tools/components/index.tssrc/components/chat/tools/configs/toolConfigs.tssrc/components/chat/utils/chatExport.tssrc/components/chat/view/ChatInterface.tsxsrc/components/chat/view/subcomponents/ChatComposer.tsxsrc/components/chat/view/subcomponents/ChatExportMenu.tsxsrc/components/chat/view/subcomponents/ChatMessageImages.tsxsrc/components/chat/view/subcomponents/ChatMessagesPane.tsxsrc/components/chat/view/subcomponents/ImageAttachment.tsxsrc/components/chat/view/subcomponents/Markdown.tsxsrc/components/chat/view/subcomponents/MessageComponent.tsxsrc/components/code-editor/view/subcomponents/markdown/MarkdownPreview.tsxsrc/components/git-panel/hooks/useGitPanelController.tssrc/components/git-panel/hooks/useWorktreesController.tssrc/components/git-panel/types/types.tssrc/components/git-panel/view/GitPanel.tsxsrc/components/git-panel/view/GitRepositoryErrorState.tsxsrc/components/git-panel/view/GitViewTabs.tsxsrc/components/git-panel/view/changes/ChangesView.tsxsrc/components/git-panel/view/changes/CommitComposer.tsxsrc/components/git-panel/view/modals/MergeWorktreeModal.tsxsrc/components/git-panel/view/modals/NewWorktreeModal.tsxsrc/components/git-panel/view/modals/RemoveWorktreeModal.tsxsrc/components/git-panel/view/worktrees/WorktreesView.tsxsrc/components/main-content/types/types.tssrc/components/main-content/view/MainContent.tsxsrc/components/quick-settings-panel/view/QuickSettingsPanelView.tsxsrc/components/shell/hooks/useShellRuntime.tssrc/components/sidebar/view/subcomponents/SidebarContent.tsxsrc/components/sidebar/view/subcomponents/SidebarProjectItem.tsxsrc/components/version-upgrade/view/VersionUpgradeModal.tsxsrc/hooks/useProjectsState.tssrc/utils/api.js
💤 Files with no reviewable changes (1)
- src/components/chat/view/ChatInterface.tsx
Move provider-specific token calculations out of the legacy server entrypoint. The backend now resolves storage details from the app session record. Expose a session-id-only provider endpoint and cover every provider path with focused tests.
Move provider SDK and CLI execution beside each provider implementation so runtime ownership matches the existing auth, model, session, and MCP layout. Dispatch runs, aborts, and Claude permission approvals through one typed runtime service instead of wiring provider-specific functions in server/index. Keep the stateful runtime entrypoint separate from the lower-level provider barrel to prevent cycles through session and project services.
Track request-owned resources so cleanup cannot delete reused checkouts. Compensate or report post-operation failures so retries do not repeat completed Git work. Reject unsafe inputs and stale responses before they trigger unintended actions. Keep exported chat content literal to prevent unsafe rendering.
Serialize worktree operations so requests cannot overwrite each other's busy state. Use selectable branch defaults and expose actions across touch and keyboard input.
Respect project .gitignore rules when building the @ mention list. Keep the explorer and other file consumers unchanged.
Key refreshes by stable project values so settings rerenders do not restart discovery. Keep cached results visible while an explicit refresh is running.
When Claude invokes a Skill, the CLI injects the entire SKILL.md as a synthetic user turn. Persisted transcripts tag it isMeta: true, but the live SDK stream does not. Without a content-level check, the skill body renders as a giant user bubble during the run and vanishes on reload. The skill is already represented by the Skill tool call itself, so the injected body should be filtered as internal content. Filter by the "Base directory for this skill:" prefix that precedes the markdown. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
d9f7b2c to
9f6b815
Compare
Closes PR #578. Closes PR #962. Closes PR #980. Closes PR #1055. Closes PR #985.
This pull request introduces several improvements and bug fixes related to session ID handling, worktree management, and dependency updates. The main focus is on ensuring that session and process management consistently use the correct session identifiers (distinguishing between app-level and provider-native IDs), adding support for Git worktree management, and updating dependencies for enhanced Markdown support.
Session and Process Management Improvements:
server/claude-sdk.jsandserver/cursor-cli.jsto always use the provider-native session ID for SDK/CLI operations, while ensuring notifications and process maps use the app session ID when available. This prevents mismatches and ensures aborts and lookups work reliably across all code paths. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17]Worktree Management:
Dependency and SDK Updates:
remark-breakspackage to improve Markdown line break handling and introduced a newtestscript for running server-side tests. [1] [2]Claude Provider Synchronizer Fix:
Bug Fixes:
mapCliOptionsToSDKto useproviderSessionIdinstead ofsessionIdfor correct SDK resume behavior. [1] [2]Let me know if you have questions about any of these changes!
Summary by CodeRabbit