Skip to content

feat(wework): 手动输入文件路径和分支的新建、切换 - #1256

Merged
qdaxb merged 5 commits into
wecode-ai:mainfrom
Micro66:human/swan-20260603-061904
Jun 4, 2026
Merged

qdaxb merged 5 commits into
wecode-ai:mainfrom
Micro66:human/swan-20260603-061904

Conversation

@Micro66

@Micro66 Micro66 commented Jun 3, 2026 •

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Interactive searchable UI to list, switch, and create environment git branches
    • Create directories on connected devices from project setup and dialogs
    • Environment panel shows current branch, branch switching, and inline branch-creation flow
  • Tests

    • Expanded coverage for branch management, device-directory API, and related UI flows
  • Localization

    • Added English and Chinese strings for branch and folder creation UI

@coderabbitai

coderabbitai Bot commented Jun 3, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 65f9f681-f1f6-495f-a98c-2ad7d7c7c9ce

📥 Commits

Reviewing files that changed from the base of the PR and between 5fb6006 and e3132be.

📒 Files selected for processing (1)
  • wework/src/api/adapters.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • wework/src/api/adapters.test.ts

📝 Walkthrough

Walkthrough

Adds backend device commands (mkdir_p and git helpers), a device createDirectory API, environment branch helpers and validation, workbench context callbacks, UI for branch selection/creation and folder creation in project dialog, component wiring, tests, and i18n keys.

Changes

Local Device and Environment Operations

Layer / File(s) Summary
Backend device commands: mkdir_p and git branch operations
backend/app/services/device/command_registry.py, backend/tests/services/test_local_device_command_service.py
Four new local device commands added to DEFAULT_LOCAL_DEVICE_COMMANDS: mkdir_p, git_branch_list, git_checkout, git_checkout_new. Tests assert command strings and post-processing.
Device API: createDirectory method
wework/src/api/devices.ts, wework/src/api/adapters.test.ts
New createDirectory(deviceId, path) sends a mkdir_p device command via POST; trims/validates input and throws on command failure. Tests verify payload, validation, and error propagation.
Environment API: branch management functions
wework/src/api/environment.ts, wework/src/api/environment.test.ts
Exports listProjectBranches, checkoutProjectBranch, createAndCheckoutProjectBranch. Implements branch-name validation, parses/sorts branch lists, and issues git_checkout/git_checkout_new commands. Tests cover listing, checkout, creation, and invalid-name rejection.
Workbench context: export device and branch operations
wework/src/features/workbench/WorkbenchProvider.tsx
Adds createDeviceDirectory, listEnvironmentBranches, checkoutEnvironmentBranch, createEnvironmentBranch to WorkbenchContextValue, implemented with useCallback delegating to device/environment APIs; also tightens guidance send error handling.
Component tree callback propagation: layout to panel actions
wework/src/components/layout/DesktopWorkbenchLayout.tsx, wework/src/components/layout/DesktopWorkbenchMain.tsx, wework/src/components/layout/DesktopSidebar.tsx, wework/src/components/layout/workspace-panels/WorkspacePanelActions.tsx, wework/src/components/projects/ProjectCreateDialog.tsx
Threads directory-creation and branch callbacks through layout, main, sidebar, and panel actions; layout adds handlers to call branch callbacks and refresh environment info; sidebar forwards create-directory callback into ProjectCreateDialog.
EnvironmentInfoPopover branch menu implementation
wework/src/components/layout/EnvironmentInfoPopover.tsx
Replaces static branch row with interactive dropdown: search/filtering (branchMatchesQuery), lazy-loaded branch list, checkout and create handlers with switching/creating states and error handling, and updated popover styling.
ProjectCreateDialog folder creation and path navigation UI
wework/src/components/projects/ProjectCreateDialog.tsx
Adds path parsing, debounced path→parent+query, filtered directory list (directoryMatchesQuery), create-folder UI/state and validation, handler that calls onCreateDeviceDirectory, and project-create error surface.
Page component integration: WorkbenchPage and plugin pages
wework/src/pages/WorkbenchPage.tsx, wework/src/pages/PluginManagementPage.tsx, wework/src/pages/PluginsPage.tsx
Extracts createDeviceDirectory and branch helpers from useWorkbench() and forwards them to layout/sidebar props.
Tests, mocks, and localization
wework/src/App.plugins.test.tsx, wework/src/components/layout/DesktopWorkbenchLayout.test.tsx, wework/src/components/projects/ProjectCreateDialog.test.tsx, wework/src/i18n/locales/en/common.json, wework/src/i18n/locales/zh-CN/common.json
Adds mocks for createDirectory and branch callbacks, extends UI tests for branch menu and project dialog flows, and adds 17 i18n keys (English + Chinese) for folder creation and branch management.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • qdaxb

Poem

🐰 I dug a path where folders grow,

I nudged a branch where git winds blow,
A popover hums and lists the trees,
Dialogs plant folders with gentle ease,
Hop—your workspace blooms, ready to go.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title describes the main features being added: manual input of file paths and branches with creation and switching capabilities, which aligns with the changeset's focus on adding device directory creation and git branch management functionality.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…-061904

# Conflicts:
#	wework/src/api/adapters.test.ts
#	wework/src/features/workbench/WorkbenchProvider.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
wework/src/components/projects/ProjectCreateDialog.tsx (1)

627-648: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Missing error handling for project creation.

The submit handler lacks a catch block, so if onCreateProject throws, the user receives no feedback. This contrasts with handleCreateDirectory which properly catches and displays errors.

Suggested fix
 onClick={async () => {
   setSubmitting(true)
   try {
     await onCreateProject({
       name: finalProjectName,
       description: '',
       config: {
         mode: 'workspace',
         execution: {
           targetType: 'local',
           deviceId,
         },
         workspace: {
           source: 'local_path',
           localPath: finalPath,
         },
       },
     })
     onClose()
+  } catch (error) {
+    setDirectoryError(
+      error instanceof Error
+        ? error.message
+        : t('workbench.project_create_failed', '项目创建失败'),
+    )
   } finally {
     setSubmitting(false)
   }
 }}
🤖 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 `@wework/src/components/projects/ProjectCreateDialog.tsx` around lines 627 -
648, The onClick submit handler awaiting onCreateProject lacks a catch, so
thrown errors aren't surfaced to the user; update the onClick async handler
around the await onCreateProject(...) to include a catch that captures the error
and displays it the same way as handleCreateDirectory (e.g., show a user-facing
error/toast or set an error state and log it), keep the existing finally {
setSubmitting(false) } behavior, and avoid calling onClose when creation fails
so the dialog stays open for corrective action.
🧹 Nitpick comments (2)
wework/src/api/adapters.test.ts (1)

255-274: ⚡ Quick win

Add a failure-path test for createDirectory throw behavior.

This suite currently covers only success; please also assert rejection when command execution returns success: false.

Based on learnings: "Apply AAA pattern (Arrange, Act, Assert) for test code, mock external services, and test edge cases and error conditions."

🤖 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 `@wework/src/api/adapters.test.ts` around lines 255 - 274, Add a new test that
covers the failure path of createDirectory: arrange by creating mockClient and
vi.mocked(client.post).mockResolvedValueOnce({ success: false, stdout: '',
stderr: 'some error' }), act by calling createDeviceApi(client) and invoking
api.createDirectory('device-1', '/home/ubuntu/new-app'), and assert that the
promise rejects (e.g., await expect(api.createDirectory(...)).rejects.toThrow()
or rejects.toMatch/ToContain the stderr). Reference the createDirectory method
on the object returned by createDeviceApi and mock client.post to simulate the
failing command response.
wework/src/components/layout/EnvironmentInfoPopover.tsx (1)

341-430: ⚡ Quick win

Optional: add keyboard dismissal for the branch menu.

The menu only closes on outside pointerdown; there's no Escape handler, which is a common expectation for a popover/dropdown. Consider closing the branch menu on Escape for keyboard users.

🤖 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 `@wework/src/components/layout/EnvironmentInfoPopover.tsx` around lines 341 -
430, When the branch popover (branchMenuOpen) is shown, add a keydown listener
that closes it on Escape by calling setBranchMenuOpen(false); implement this in
a useEffect that watches branchMenuOpen, registers a window/key event handler
when true, and removes it on cleanup or when branchMenuOpen becomes false;
ensure the handler is named clearly (e.g. onBranchMenuKeyDown) and only triggers
on key === 'Escape' so existing handlers like handleCheckoutBranch/newBranch
form are unaffected.
🤖 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 `@wework/src/api/devices.ts`:
- Around line 74-87: createDirectory currently sends a backend mkdir_p command
even for blank or whitespace-only paths; validate the path parameter (e.g.,
trim() and check length) at the top of createDirectory and throw a synchronous
Error like "Invalid directory path" when path is empty, null, or only
whitespace, so the function rejects locally instead of calling client.post with
an invalid arg; keep deviceId validation if needed and leave the existing
client.post block (command_key 'mkdir_p') unchanged for valid paths.

In `@wework/src/api/environment.ts`:
- Around line 251-291: Both checkoutProjectBranch and
createAndCheckoutProjectBranch accept branch names unsafely: update them to
insert the branch as a post-option argument by adding a "--" before the branch
in the args passed to runGitCommand (i.e., args: ['--', trimmedBranch] or args:
['-b', '--', trimmedBranch] for new-branch semantics if your git helper expects
'-b'); and extract the branch-name validation into a shared helper (e.g.,
validateBranchName) that enforces git check-ref-format --branch rules (either by
implementing those checks locally or by calling git check-ref-format on-device
before calling runGitCommand). Keep use of commandContext and runGitCommand
unchanged except for replacing the args with the safe form and calling
validateBranchName(trimmedBranch) at the top of both functions.

In `@wework/src/components/layout/EnvironmentInfoPopover.tsx`:
- Around line 161-199: The branch-menu collapse and checkout early/success
returns only toggle branchMenuOpen or partially reset state, causing stale
branchQuery/newBranchFormOpen/newBranchName to persist; replace those manual
resets with a single call to closeBranchMenu() in handleToggleBranchMenu (when
nextOpen is false or onListBranches missing), in the early return of
handleCheckoutBranch (when nextBranchName === info.branchName), and in the
successful checkout path (after await onCheckoutBranch) so all cleanup
(branchQuery, newBranchFormOpen, newBranchName and branchMenuOpen) is
centralized and consistent.

In `@wework/src/components/projects/ProjectCreateDialog.tsx`:
- Around line 497-551: The three folder-related buttons (data-testid
"open-create-folder-button", "cancel-create-folder-button", and
"confirm-create-folder-button") currently use h-8 (32px) which is below the 44px
touch target; update their className to use h-11 and add min-w-[44px] while
preserving the rest of their utility classes (e.g., keep rounded-md, px-2, font,
color, hover classes) so they meet the "44×44" mobile guideline without changing
behavior; ensure the same adjustment is applied for both the visible
"open-create-folder-button" and the two buttons inside the newFolderOpen block
that call handleCreateDirectory and cancel actions.

---

Outside diff comments:
In `@wework/src/components/projects/ProjectCreateDialog.tsx`:
- Around line 627-648: The onClick submit handler awaiting onCreateProject lacks
a catch, so thrown errors aren't surfaced to the user; update the onClick async
handler around the await onCreateProject(...) to include a catch that captures
the error and displays it the same way as handleCreateDirectory (e.g., show a
user-facing error/toast or set an error state and log it), keep the existing
finally { setSubmitting(false) } behavior, and avoid calling onClose when
creation fails so the dialog stays open for corrective action.

---

Nitpick comments:
In `@wework/src/api/adapters.test.ts`:
- Around line 255-274: Add a new test that covers the failure path of
createDirectory: arrange by creating mockClient and
vi.mocked(client.post).mockResolvedValueOnce({ success: false, stdout: '',
stderr: 'some error' }), act by calling createDeviceApi(client) and invoking
api.createDirectory('device-1', '/home/ubuntu/new-app'), and assert that the
promise rejects (e.g., await expect(api.createDirectory(...)).rejects.toThrow()
or rejects.toMatch/ToContain the stderr). Reference the createDirectory method
on the object returned by createDeviceApi and mock client.post to simulate the
failing command response.

In `@wework/src/components/layout/EnvironmentInfoPopover.tsx`:
- Around line 341-430: When the branch popover (branchMenuOpen) is shown, add a
keydown listener that closes it on Escape by calling setBranchMenuOpen(false);
implement this in a useEffect that watches branchMenuOpen, registers a
window/key event handler when true, and removes it on cleanup or when
branchMenuOpen becomes false; ensure the handler is named clearly (e.g.
onBranchMenuKeyDown) and only triggers on key === 'Escape' so existing handlers
like handleCheckoutBranch/newBranch form are unaffected.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: ff84b720-5868-4a21-8aa4-f0d04312b572

📥 Commits

Reviewing files that changed from the base of the PR and between a16db2f and b81a3fc.

📒 Files selected for processing (21)
  • backend/app/services/device/command_registry.py
  • backend/tests/services/test_local_device_command_service.py
  • wework/src/App.plugins.test.tsx
  • wework/src/api/adapters.test.ts
  • wework/src/api/devices.ts
  • wework/src/api/environment.test.ts
  • wework/src/api/environment.ts
  • wework/src/components/layout/DesktopSidebar.tsx
  • wework/src/components/layout/DesktopWorkbenchLayout.test.tsx
  • wework/src/components/layout/DesktopWorkbenchLayout.tsx
  • wework/src/components/layout/DesktopWorkbenchMain.tsx
  • wework/src/components/layout/EnvironmentInfoPopover.tsx
  • wework/src/components/layout/workspace-panels/WorkspacePanelActions.tsx
  • wework/src/components/projects/ProjectCreateDialog.test.tsx
  • wework/src/components/projects/ProjectCreateDialog.tsx
  • wework/src/features/workbench/WorkbenchProvider.tsx
  • wework/src/i18n/locales/en/common.json
  • wework/src/i18n/locales/zh-CN/common.json
  • wework/src/pages/PluginManagementPage.tsx
  • wework/src/pages/PluginsPage.tsx
  • wework/src/pages/WorkbenchPage.tsx

Comment thread wework/src/api/devices.ts
Comment thread wework/src/api/environment.ts
Comment thread wework/src/components/layout/EnvironmentInfoPopover.tsx
Comment thread wework/src/components/projects/ProjectCreateDialog.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 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 `@wework/src/features/workbench/WorkbenchProvider.tsx`:
- Around line 1033-1055: Wrap the await
resolvedServices.chatStream.sendMessage(payload) call in a try/catch/finally so
thrown exceptions don't leave the composer stuck: in the try block keep the
existing ack handling, in the catch setIsAwaitingAssistantStart(false),
dispatch({ type: 'error_set', error: (caught error) }) and return false, and in
finally always dispatch({ type: 'sending_finished' }) to ensure sending_finished
is dispatched; reference resolvedServices.chatStream.sendMessage,
setIsAwaitingAssistantStart, dispatch({ type: 'sending_finished' }), and
dispatch({ type: 'error_set', ... }) when making the change.
- Around line 1271-1301: The guard guidanceSendInFlightRef.current is set true
before calling resolvedServices.chatStream.cancelStream, but if cancelStream
throws the ref never gets reset; wrap the cancelStream call in a
try/catch/finally inside the function in WorkbenchProvider (around the call to
resolvedServices.chatStream.cancelStream) so that any thrown error is caught,
set the queued item status to 'failed' via setQueuedSends using
normalizeGuidanceError for the error, and always reset
guidanceSendInFlightRef.current = false in the finally block; ensure you still
handle the existing cancelAck error/success path when cancelStream returns
normally.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7b7259ec-384c-4349-a12c-d78e2e3a7e9d

📥 Commits

Reviewing files that changed from the base of the PR and between b81a3fc and 448b738.

📒 Files selected for processing (12)
  • backend/app/services/device/command_registry.py
  • backend/tests/services/test_local_device_command_service.py
  • wework/src/App.plugins.test.tsx
  • wework/src/api/adapters.test.ts
  • wework/src/api/devices.ts
  • wework/src/components/layout/DesktopWorkbenchLayout.test.tsx
  • wework/src/components/layout/DesktopWorkbenchLayout.tsx
  • wework/src/components/layout/DesktopWorkbenchMain.tsx
  • wework/src/features/workbench/WorkbenchProvider.tsx
  • wework/src/i18n/locales/en/common.json
  • wework/src/i18n/locales/zh-CN/common.json
  • wework/src/pages/WorkbenchPage.tsx
✅ Files skipped from review due to trivial changes (2)
  • wework/src/i18n/locales/en/common.json
  • wework/src/i18n/locales/zh-CN/common.json
🚧 Files skipped from review as they are similar to previous changes (7)
  • backend/app/services/device/command_registry.py
  • wework/src/api/adapters.test.ts
  • wework/src/App.plugins.test.tsx
  • wework/src/components/layout/DesktopWorkbenchLayout.tsx
  • wework/src/components/layout/DesktopWorkbenchLayout.test.tsx
  • wework/src/api/devices.ts
  • wework/src/components/layout/DesktopWorkbenchMain.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🤖 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 `@wework/src/features/workbench/WorkbenchProvider.tsx`:
- Around line 1033-1055: Wrap the await
resolvedServices.chatStream.sendMessage(payload) call in a try/catch/finally so
thrown exceptions don't leave the composer stuck: in the try block keep the
existing ack handling, in the catch setIsAwaitingAssistantStart(false),
dispatch({ type: 'error_set', error: (caught error) }) and return false, and in
finally always dispatch({ type: 'sending_finished' }) to ensure sending_finished
is dispatched; reference resolvedServices.chatStream.sendMessage,
setIsAwaitingAssistantStart, dispatch({ type: 'sending_finished' }), and
dispatch({ type: 'error_set', ... }) when making the change.
- Around line 1271-1301: The guard guidanceSendInFlightRef.current is set true
before calling resolvedServices.chatStream.cancelStream, but if cancelStream
throws the ref never gets reset; wrap the cancelStream call in a
try/catch/finally inside the function in WorkbenchProvider (around the call to
resolvedServices.chatStream.cancelStream) so that any thrown error is caught,
set the queued item status to 'failed' via setQueuedSends using
normalizeGuidanceError for the error, and always reset
guidanceSendInFlightRef.current = false in the finally block; ensure you still
handle the existing cancelAck error/success path when cancelStream returns
normally.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7b7259ec-384c-4349-a12c-d78e2e3a7e9d

📥 Commits

Reviewing files that changed from the base of the PR and between b81a3fc and 448b738.

📒 Files selected for processing (12)
  • backend/app/services/device/command_registry.py
  • backend/tests/services/test_local_device_command_service.py
  • wework/src/App.plugins.test.tsx
  • wework/src/api/adapters.test.ts
  • wework/src/api/devices.ts
  • wework/src/components/layout/DesktopWorkbenchLayout.test.tsx
  • wework/src/components/layout/DesktopWorkbenchLayout.tsx
  • wework/src/components/layout/DesktopWorkbenchMain.tsx
  • wework/src/features/workbench/WorkbenchProvider.tsx
  • wework/src/i18n/locales/en/common.json
  • wework/src/i18n/locales/zh-CN/common.json
  • wework/src/pages/WorkbenchPage.tsx
✅ Files skipped from review due to trivial changes (2)
  • wework/src/i18n/locales/en/common.json
  • wework/src/i18n/locales/zh-CN/common.json
🚧 Files skipped from review as they are similar to previous changes (7)
  • backend/app/services/device/command_registry.py
  • wework/src/api/adapters.test.ts
  • wework/src/App.plugins.test.tsx
  • wework/src/components/layout/DesktopWorkbenchLayout.tsx
  • wework/src/components/layout/DesktopWorkbenchLayout.test.tsx
  • wework/src/api/devices.ts
  • wework/src/components/layout/DesktopWorkbenchMain.tsx
🛑 Comments failed to post (2)
wework/src/features/workbench/WorkbenchProvider.tsx (2)

1033-1055: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Unhandled send exceptions can leave composer state stuck

If sendMessage() throws at Line 1048, sending_finished is never dispatched and isAwaitingAssistantStart stays true, which can block subsequent queued sends and keep UI in a sending state.

Suggested fix
   const sendPreparedMessage = useCallback(
     async (
       message: string,
       payload: ChatSendPayload,
       activeDeviceId?: string,
       attachments?: Attachment[]
     ): Promise<boolean> => {
       dispatch({ type: 'sending_started' })
       setIsAwaitingAssistantStart(true)
       dispatchMessages({
         type: 'user_added',
         message: {
           id: `local-${Date.now()}`,
           taskId: payload.task_id,
           role: 'user',
           content: message,
           status: 'done',
           attachments: attachments && attachments.length > 0 ? attachments : undefined,
           createdAt: new Date().toISOString(),
         },
       })
-
-      const ack = await resolvedServices.chatStream.sendMessage(payload)
-      dispatch({ type: 'sending_finished' })
+      let ack
+      try {
+        ack = await resolvedServices.chatStream.sendMessage(payload)
+      } catch (error) {
+        setIsAwaitingAssistantStart(false)
+        dispatch({
+          type: 'error_set',
+          error: error instanceof Error ? error.message : '发送失败',
+        })
+        return false
+      } finally {
+        dispatch({ type: 'sending_finished' })
+      }
 
       if (ack.error || ack.success === false) {
         setIsAwaitingAssistantStart(false)
         dispatch({ type: 'error_set', error: ack.error ?? '发送失败' })
         return false
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

      dispatch({ type: 'sending_started' })
      setIsAwaitingAssistantStart(true)
      dispatchMessages({
        type: 'user_added',
        message: {
          id: `local-${Date.now()}`,
          taskId: payload.task_id,
          role: 'user',
          content: message,
          status: 'done',
          attachments: attachments && attachments.length > 0 ? attachments : undefined,
          createdAt: new Date().toISOString(),
        },
      })

      let ack
      try {
        ack = await resolvedServices.chatStream.sendMessage(payload)
      } catch (error) {
        setIsAwaitingAssistantStart(false)
        dispatch({
          type: 'error_set',
          error: error instanceof Error ? error.message : '发送失败',
        })
        return false
      } finally {
        dispatch({ type: 'sending_finished' })
      }

      if (ack.error || ack.success === false) {
        setIsAwaitingAssistantStart(false)
        dispatch({ type: 'error_set', error: ack.error ?? '发送失败' })
        return false
      }
🤖 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 `@wework/src/features/workbench/WorkbenchProvider.tsx` around lines 1033 -
1055, Wrap the await resolvedServices.chatStream.sendMessage(payload) call in a
try/catch/finally so thrown exceptions don't leave the composer stuck: in the
try block keep the existing ack handling, in the catch
setIsAwaitingAssistantStart(false), dispatch({ type: 'error_set', error: (caught
error) }) and return false, and in finally always dispatch({ type:
'sending_finished' }) to ensure sending_finished is dispatched; reference
resolvedServices.chatStream.sendMessage, setIsAwaitingAssistantStart, dispatch({
type: 'sending_finished' }), and dispatch({ type: 'error_set', ... }) when
making the change.

1271-1301: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

guidanceSendInFlightRef can get permanently locked on cancel errors

At Line 1271 the in-flight guard is set true, but if cancelStream() throws at Line 1280, control exits before reset, so later guidance sends are silently blocked.

Suggested fix
-      guidanceSendInFlightRef.current = true
-      setQueuedSends(items =>
-        items.map(queued =>
-          queued.id === id
-            ? { ...queued, status: 'sending', error: undefined }
-            : queued
-        )
-      )
-
-      const cancelAck = await resolvedServices.chatStream.cancelStream({
-        subtask_id: activeSubtaskId,
-        partial_content: activeAssistantMessage.content,
-        shell_type: activeAssistantMessage.shellType,
-      })
+      guidanceSendInFlightRef.current = true
+      setQueuedSends(items =>
+        items.map(queued =>
+          queued.id === id
+            ? { ...queued, status: 'sending', error: undefined }
+            : queued
+        )
+      )
 
-      if (cancelAck.error || cancelAck.success === false) {
-        setQueuedSends(items =>
-          items.map(queued =>
-            queued.id === id
-              ? {
-                  ...queued,
-                  status: 'failed',
-                  error: normalizeGuidanceError(cancelAck.error ?? '取消当前回复失败'),
-                }
-              : queued
-            )
-        )
-        guidanceSendInFlightRef.current = false
-        return
-      }
+      try {
+        const cancelAck = await resolvedServices.chatStream.cancelStream({
+          subtask_id: activeSubtaskId,
+          partial_content: activeAssistantMessage.content,
+          shell_type: activeAssistantMessage.shellType,
+        })
+
+        if (cancelAck.error || cancelAck.success === false) {
+          setQueuedSends(items =>
+            items.map(queued =>
+              queued.id === id
+                ? {
+                    ...queued,
+                    status: 'failed',
+                    error: normalizeGuidanceError(cancelAck.error ?? '取消当前回复失败'),
+                  }
+                : queued
+              )
+          )
+          return
+        }
 
-      dispatchMessages({
-        type: 'assistant_done',
-        subtaskId: activeSubtaskId,
-        content: activeAssistantMessage.content,
-      })
+        dispatchMessages({
+          type: 'assistant_done',
+          subtaskId: activeSubtaskId,
+          content: activeAssistantMessage.content,
+        })
 
-      try {
         const sent = await sendPreparedMessage(
           item.content,
           item.payload,
           item.activeDeviceId,
           item.attachments
         )
@@
-      } finally {
+      } finally {
         guidanceSendInFlightRef.current = false
       }
🤖 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 `@wework/src/features/workbench/WorkbenchProvider.tsx` around lines 1271 -
1301, The guard guidanceSendInFlightRef.current is set true before calling
resolvedServices.chatStream.cancelStream, but if cancelStream throws the ref
never gets reset; wrap the cancelStream call in a try/catch/finally inside the
function in WorkbenchProvider (around the call to
resolvedServices.chatStream.cancelStream) so that any thrown error is caught,
set the queued item status to 'failed' via setQueuedSends using
normalizeGuidanceError for the error, and always reset
guidanceSendInFlightRef.current = false in the finally block; ensure you still
handle the existing cancelAck error/success path when cancelStream returns
normally.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
wework/src/features/workbench/WorkbenchProvider.tsx (1)

77-152: 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Split WorkbenchProvider before adding more responsibilities.

This file is already 1,474 lines, and these new device/environment callbacks plus the expanded send/guidance flows make the provider even harder to reason about. Please extract the environment/device actions and message-send orchestration into dedicated hooks or submodules instead of growing this file further. As per coding guidelines, File size limit: If a file exceeds 1000 lines, split it into multiple sub-modules.

Also applies to: 924-958, 1026-1109, 1258-1365, 1400-1466

🤖 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 `@wework/src/features/workbench/WorkbenchProvider.tsx` around lines 77 - 152,
The WorkbenchProvider has grown too large—extract device/environment callbacks
and message-send orchestration into smaller modules: create a useDeviceActions
hook (implementing getDeviceHomeDirectory, getProjectWorkspaceRoot,
listDeviceDirectories, createDeviceDirectory, rememberExecutionDevice,
refreshDevices), a useEnvironmentActions hook (implementing loadEnvironmentInfo,
commitEnvironmentChanges, listEnvironmentBranches, checkoutEnvironmentBranch,
createEnvironmentBranch), and a useMessageOrchestration hook (implementing
setInput, sendCurrentInput, pauseCurrentResponse, isResponseStreaming,
cancelQueuedMessage, sendQueuedAsGuidance, editQueuedMessage,
cancelGuidanceMessage and related queued/guidance flows); move the
implementations from WorkbenchProvider into those hooks, keep the
WorkbenchContextValue signatures (e.g., WorkbenchProvider, WorkbenchContextValue
methods like sendCurrentInput and getDeviceHomeDirectory) but delegate their
bodies to the new hooks, and update imports/exports so WorkbenchProvider
composes these hooks—this split keeps public API intact while reducing file size
and complexity.
🧹 Nitpick comments (1)
wework/src/api/adapters.test.ts (1)

255-274: ⚡ Quick win

Cover the trimmed-path happy path.

These tests verify blank rejection and backend failures, but they never assert that a non-empty path is actually normalized before posting. If a later regression validates with trim() and still sends the raw string, this suite will keep passing. Use a whitespace-padded input here and assert args contains the trimmed path.

🤖 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 `@wework/src/api/adapters.test.ts` around lines 255 - 274, Update the test for
createDirectory to cover the trimmed-path happy path: call createDirectory with
a whitespace-padded path (e.g., '  /home/ubuntu/new-app  ') and assert that the
mocked client.post (from mockClient) is called with an args array containing the
trimmed path '/home/ubuntu/new-app'; locate the test using createDeviceApi and
api.createDirectory and change the expect on client.post to verify the trimmed
value in the posted payload's args rather than the raw input.
🤖 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.

Outside diff comments:
In `@wework/src/features/workbench/WorkbenchProvider.tsx`:
- Around line 77-152: The WorkbenchProvider has grown too large—extract
device/environment callbacks and message-send orchestration into smaller
modules: create a useDeviceActions hook (implementing getDeviceHomeDirectory,
getProjectWorkspaceRoot, listDeviceDirectories, createDeviceDirectory,
rememberExecutionDevice, refreshDevices), a useEnvironmentActions hook
(implementing loadEnvironmentInfo, commitEnvironmentChanges,
listEnvironmentBranches, checkoutEnvironmentBranch, createEnvironmentBranch),
and a useMessageOrchestration hook (implementing setInput, sendCurrentInput,
pauseCurrentResponse, isResponseStreaming, cancelQueuedMessage,
sendQueuedAsGuidance, editQueuedMessage, cancelGuidanceMessage and related
queued/guidance flows); move the implementations from WorkbenchProvider into
those hooks, keep the WorkbenchContextValue signatures (e.g., WorkbenchProvider,
WorkbenchContextValue methods like sendCurrentInput and getDeviceHomeDirectory)
but delegate their bodies to the new hooks, and update imports/exports so
WorkbenchProvider composes these hooks—this split keeps public API intact while
reducing file size and complexity.

---

Nitpick comments:
In `@wework/src/api/adapters.test.ts`:
- Around line 255-274: Update the test for createDirectory to cover the
trimmed-path happy path: call createDirectory with a whitespace-padded path
(e.g., '  /home/ubuntu/new-app  ') and assert that the mocked client.post (from
mockClient) is called with an args array containing the trimmed path
'/home/ubuntu/new-app'; locate the test using createDeviceApi and
api.createDirectory and change the expect on client.post to verify the trimmed
value in the posted payload's args rather than the raw input.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ce3c5101-cb6e-4015-9af8-8b2fc23b69c7

📥 Commits

Reviewing files that changed from the base of the PR and between 448b738 and 5fb6006.

📒 Files selected for processing (9)
  • wework/src/api/adapters.test.ts
  • wework/src/api/devices.ts
  • wework/src/api/environment.test.ts
  • wework/src/api/environment.ts
  • wework/src/components/layout/DesktopWorkbenchLayout.test.tsx
  • wework/src/components/layout/EnvironmentInfoPopover.tsx
  • wework/src/components/projects/ProjectCreateDialog.test.tsx
  • wework/src/components/projects/ProjectCreateDialog.tsx
  • wework/src/features/workbench/WorkbenchProvider.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • wework/src/components/layout/DesktopWorkbenchLayout.test.tsx
  • wework/src/api/environment.ts
  • wework/src/components/layout/EnvironmentInfoPopover.tsx
  • wework/src/components/projects/ProjectCreateDialog.tsx

@qdaxb
qdaxb merged commit 6085851 into wecode-ai:main Jun 4, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants