Skip to content

feat: Add file management IPC handlers#40

Merged
stepandel merged 4 commits intofeat/assistant-modefrom
pine-40-file-handlers
Feb 5, 2026
Merged

feat: Add file management IPC handlers#40
stepandel merged 4 commits intofeat/assistant-modefrom
pine-40-file-handlers

Conversation

@stepandel
Copy link
Copy Markdown
Owner

@stepandel stepandel commented Feb 5, 2026

Summary

Extend AssistantService with file operations scoped to a specific assistant.

Changes

  • Add AssistantFile type with status enum (Processing, Available, Deleting, ProcessingFailed)
  • Add file methods to AssistantService: listFiles, describeFile, uploadFile, deleteFile
  • Add IPC handlers: assistant:files:list/describe/upload/delete
  • Add preload bindings under assistant.files

API

window.electronAPI.assistant.files.list(profileId, assistantName, filter?)
window.electronAPI.assistant.files.describe(profileId, assistantName, fileId)
window.electronAPI.assistant.files.upload(profileId, assistantName, params)
window.electronAPI.assistant.files.delete(profileId, assistantName, fileId)

Files Modified

  • electron/types.ts
  • electron/assistant-service.ts
  • electron/main.ts
  • electron/preload.ts
  • src/types/electron.d.ts

Testing

  • Build passes: pnpm test:build

Closes PINE-40

Summary by CodeRabbit

  • New Features

    • Full assistant management (create, list, view, update, delete) with file upload and management.
    • Mode switcher UI (Index vs Assistant) with keyboard shortcuts and persisted per-profile preference.
    • New Assistants panel and active-assistant selection across the app.
  • Chores

    • Added Linear CLI and workspace configuration files.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 5, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • 🔍 Trigger a full review
📝 Walkthrough

Walkthrough

Adds Assistant management: service, IPC handlers, preload APIs, types, React context/hooks/components, mode switcher, and persistence for a per-profile preferred Explorer mode; integrates AssistantService into PineconeService and exposes assistant file operations.

Changes

Cohort / File(s) Summary
Configuration
\.linear\.toml, pnpm-workspace.yaml
Adds Linear CLI config and pnpm workspace build dependency settings.
Public Types / Declarations
electron/types.ts, src/types/electron.d.ts
Adds ExplorerMode, AssistantModel, AssistantFile and related types; extends ConnectionProfile with preferredMode. (Note: assistant types duplicated in electron/types.ts.)
Assistant Service
electron/assistant-service.ts, electron/pinecone-service.ts
New AssistantService wrapping Pinecone Assistant API (CRUD + file ops); PineconeService gets getAssistantService() and clears it on disconnect.
IPC / Preload API
electron/main.ts, electron/preload.ts, src/types/electron.d.ts
Adds IPC handlers and preload methods for profiles (get/set preferred mode), assistant CRUD, assistant file ops, and window helpers; standardized {success,data?,error?} responses and corresponding type surface.
Store: Connection Preferences
electron/connection-store.ts
Adds getPreferredMode(profileId) and setPreferredMode(profileId, mode) to persist per-profile preferred Explorer mode.
Mode Context & Provider
src/context/ModeContext.tsx, src/windows/ConnectionWindow.tsx
New ModeProvider + useMode hook (persists to store, keyboard shortcuts); ConnectionWindow wraps providers with ModeProvider.
Assistant Selection Context
src/context/AssistantSelectionContext.tsx
New context/provider and hook to manage currently active assistant.
React Query Hooks
src/hooks/useAssistantQueries.ts
New query keys and hooks: list/detail queries and create/update/delete mutations (delete uses optimistic update and rollback).
UI Components
src/components/mode/ModeSwitcher.tsx, src/components/assistants/AssistantsPanel.tsx, src/components/layout/TopBar.tsx
ModeSwitcher UI for switching modes; AssistantsPanel lists assistants with status and interactions; TopBar integrates ModeSwitcher and adjusts center layout.
Global Types for Renderer
src/types/electron.d.ts
Expands ElectronAPI typings to include profiles and assistant namespaces and new assistant/file types for renderer consumption.

Sequence Diagram(s)

sequenceDiagram
  participant Renderer as Renderer (UI)
  participant Preload as Preload (electronAPI)
  participant Main as Main (ipcMain)
  participant PineconeSvc as PineconeService
  participant AssistantSvc as AssistantService
  participant PineconeSDK as Pinecone SDK

  Renderer->>Preload: call assistant.list(profileId)
  Preload->>Main: invoke 'assistant:list' IPC
  Main->>PineconeSvc: ensure connected, getAssistantService()
  PineconeSvc->>AssistantSvc: delegate listAssistants()
  AssistantSvc->>PineconeSDK: API call -> list assistants
  PineconeSDK-->>AssistantSvc: assistants data
  AssistantSvc-->>PineconeSvc: mapped AssistantModel[]
  PineconeSvc-->>Main: return data
  Main-->>Preload: IPC response {success:true,data}
  Preload-->>Renderer: resolve Promise with assistants
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 New assistants hop in, tabs switch with a wink,
IPC paths bustle while queries sync,
Services and contexts in neat little rows,
Files flutter in, status lights glow,
I nibble code crumbs and cheer as it ships! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Add file management IPC handlers' accurately describes the primary change—adding IPC handlers for assistant file operations (list, describe, upload, delete).

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch pine-40-file-handlers

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

@claude
Copy link
Copy Markdown

claude bot commented Feb 5, 2026

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In @.linear.toml:
- Around line 1-4: The workspace name in .linear.toml is inconsistent with the
repository comment: update the workspace value to match this project's name (or
correct the comment) by changing the workspace = "chroma-explorer" entry or
replacing the top comment that mentions "Pinecone Explorer" so they align;
locate the workspace key in .linear.toml and make the string match the actual
project/repo name or adjust the descriptive comment accordingly.

In `@electron/assistant-service.ts`:
- Around line 27-30: The call to .map(this.mapAssistantModel) loses the class
instance context; change it to use a bound or arrow wrapper so the method keeps
the correct this (e.g., replace .map(this.mapAssistantModel) with .map(a =>
this.mapAssistantModel(a)) or .map(this.mapAssistantModel.bind(this)) in
listAssistants and in any other places where mapAssistantModel is passed
directly to .map to ensure the method retains its class context if it later uses
instance members.

In `@electron/pinecone-service.ts`:
- Line 40: The disconnect() method currently clears client, embeddingService,
profile, and caches but leaves the old AssistantService instance alive; update
disconnect() to also clear the AssistantService reference by calling any
available cleanup on it (e.g., a close/dispose/shutdown method if
AssistantService exposes one) and then set this.assistantService = null so a
stale AssistantService tied to old credentials cannot be reused; locate the
assistantService field and the disconnect() method and perform this cleanup
there.

In `@electron/types.ts`:
- Around line 53-57: The ExplorerMode type exported from electron/types.ts is
unused and duplicates the definition in ModeContext.tsx; either delete the
ExplorerMode declaration from electron/types.ts or centralize it by
moving/exporting the single ExplorerMode type from electron/types.ts and
updating src/context/ModeContext.tsx to import ExplorerMode instead of
redefining it; locate the ExplorerMode type in electron/types.ts and
ModeContext.tsx and apply one of the two options consistently so only one shared
definition remains.

In `@src/components/assistants/AssistantsPanel.tsx`:
- Around line 155-158: The title attribute on the assistant list item uses a
template literal with "\n" which may not render line breaks across browsers;
update the title to a single-line format (e.g., `${assistant.name} — Status:
${getStatusTooltip(assistant.status)}`) or remove the status from title and rely
on the existing status indicator/tooltip; modify the JSX where onClick uses
handleAssistantClick and onContextMenu uses handleAssistantContextMenu to
replace the multiline title with a single-line string or omit it entirely.

In `@src/context/ModeContext.tsx`:
- Around line 36-52: The keyboard listener's useEffect in ModeContext.tsx
captures a stale setMode because it's omitted from the dependency array; update
the effect to depend on the current setMode (or stabilize setMode with
useCallback) so the handler always calls the latest function: in the useEffect
that defines handleKeyDown, replace the empty deps array with [setMode] (and
remove the eslint-disable-line react-hooks/exhaustive-deps) or alternatively
memoize handleKeyDown via useCallback referencing setMode and keep the effect
deps consistent; ensure the symbols referenced are useEffect, handleKeyDown, and
setMode.
🧹 Nitpick comments (8)
src/context/AssistantSelectionContext.tsx (1)

14-16: Unnecessary useCallback wrapper.

The setActiveAssistantState from useState is already referentially stable across renders. Wrapping it in useCallback adds no benefit here.

♻️ Suggested simplification
-  const setActiveAssistant = useCallback((assistantName: string | null) => {
-    setActiveAssistantState(assistantName)
-  }, [])
+  const setActiveAssistant = setActiveAssistantState

Then update the imports to remove useCallback:

-import { createContext, useContext, useState, useCallback, useMemo, ReactNode } from 'react'
+import { createContext, useContext, useState, useMemo, ReactNode } from 'react'
electron/assistant-service.ts (1)

56-64: Extra network round-trip in updateAssistant.

The method makes two API calls: one to update and one to fetch the updated assistant. The comment explains the rationale, but this adds latency and potential inconsistency if the assistant changes between calls.

Consider whether returning the response from updateAssistant directly (if it contains sufficient data) or documenting this behavior for callers would be preferable.

src/components/mode/ModeSwitcher.tsx (1)

44-45: Consider using the label from the modes array for consistency.

Line 45 duplicates the label logic rather than reusing the label property from the modes array. This could lead to inconsistencies if labels change.

💡 Suggested improvement
             <Icon className="h-3.5 w-3.5" />
-            <span className="hidden sm:inline">{value === 'index' ? 'Index' : 'Assistant'}</span>
+            <span className="hidden sm:inline">{label.replace(' Explorer', '')}</span>
           </button>

Alternatively, add a shortLabel property to ModeOption if you want different display values.

src/components/assistants/AssistantsPanel.tsx (1)

84-88: Unnecessary useCallback wrapper.

This callback simply forwards to onCreateNew without any additional logic. You can pass onCreateNew directly to the button's onClick or inline it.

💡 Simplification
-  const handleCreateNew = useCallback(() => {
-    if (onCreateNew) {
-      onCreateNew()
-    }
-  }, [onCreateNew])

Then use onClick={onCreateNew} directly on line 107, since the button already handles undefined gracefully.

src/hooks/useAssistantQueries.ts (1)

135-140: Consider also invalidating the list query on successful delete.

The optimistic update removes the assistant from the cached list in onMutate, but if the mutation succeeds, the list query cache isn't explicitly invalidated. This works because the optimistic update persists, but for consistency with create/update mutations and to ensure eventual consistency with the server, consider adding a list invalidation.

💡 Suggested addition
     onSuccess: (data) => {
+      // Invalidate list to ensure consistency with server
+      queryClient.invalidateQueries({
+        queryKey: assistantQueryKeys.list(data.profileId),
+      })
       // Remove detail query for the deleted assistant
       queryClient.removeQueries({
         queryKey: assistantQueryKeys.detail(data.profileId, data.assistantName),
       })
     },
electron/main.ts (2)

819-832: Consider using imported type for consistency.

The handler uses an inline type definition for params instead of the imported CreateAssistantParams type. While functionally equivalent, using the imported type improves maintainability.

💡 Suggested change
+import {
+  // ... existing imports
+  CreateAssistantParams,
+  UpdateAssistantParams,
+  UploadAssistantFileParams,
+  ListAssistantFilesFilter,
+} from './types'

-ipcMain.handle('assistant:create', async (_event, profileId: string, params: { name: string; instructions?: string; metadata?: Record<string, string>; region?: 'us' | 'eu' }) => {
+ipcMain.handle('assistant:create', async (_event, profileId: string, params: CreateAssistantParams) => {

Apply similar changes to assistant:update (line 849), assistant:files:list (line 883), and assistant:files:upload (line 913).


804-877: Consider adding analytics tracking for assistant operations.

Index operations include analytics tracking (e.g., track('index_created')), but the new assistant handlers don't include similar tracking. If usage analytics are important for assistants, consider adding tracking calls.

src/context/ModeContext.tsx (1)

4-4: Consolidate duplicate Electron type definitions.

ExplorerMode is defined in three locations: src/context/ModeContext.tsx (line 4), electron/types.ts (line 55), and src/types/electron.d.ts. The more critical issue is that electron/types.ts and src/types/electron.d.ts define the entire Electron API surface identically. Consider consolidating these parallel type definitions and, if the ModeContext export is only used locally, documenting whether it should remain isolated as a React-layer type or be imported from the electron types.

Comment on lines +53 to +57
/**
* Explorer mode type - Index Explorer or Assistant Explorer
*/
export type ExplorerMode = 'index' | 'assistant'

Copy link
Copy Markdown

@coderabbitai coderabbitai bot Feb 5, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Search for ExplorerMode type definitions
rg -n "type ExplorerMode\s*=" --type ts -A 2

Repository: stepandel/pinecone-explorer

Length of output: 512


🏁 Script executed:

# Check where ExplorerMode is imported from
rg -n "import.*ExplorerMode|from.*ExplorerMode" --type ts -B 2 -A 2

Repository: stepandel/pinecone-explorer

Length of output: 361


🏁 Script executed:

# Show the full context of both definitions
echo "=== electron/types.ts ===" && sed -n '50,60p' electron/types.ts
echo -e "\n=== src/context/ModeContext.tsx ===" && head -10 src/context/ModeContext.tsx

Repository: stepandel/pinecone-explorer

Length of output: 676


🏁 Script executed:

# Find all imports of ExplorerMode, showing which file it's imported from
rg -n "import.*ExplorerMode" --type ts --type tsx -B 1 -A 1

Repository: stepandel/pinecone-explorer

Length of output: 98


🏁 Script executed:

# Check if electron/types.ts is exported from anywhere
rg -n "from.*electron/types|from.*electron['\"]" --type ts --type tsx | head -20

Repository: stepandel/pinecone-explorer

Length of output: 98


🏁 Script executed:

# Check if src/types/electron.d.ts is auto-generated or intentionally maintained
head -20 src/types/electron.d.ts

Repository: stepandel/pinecone-explorer

Length of output: 732


🏁 Script executed:

# Search for all imports of ExplorerMode without type filter
rg -n "import.*ExplorerMode" -A 1

Repository: stepandel/pinecone-explorer

Length of output: 215


🏁 Script executed:

# Check what exports from electron/types.ts are actually used
rg -n "from.*['\"].*electron/types['\"]"

Repository: stepandel/pinecone-explorer

Length of output: 719


🏁 Script executed:

# Find all TypeScript/React files and search for ExplorerMode usage
fd -e ts -e tsx | xargs grep -l "ExplorerMode" | head -20

Repository: stepandel/pinecone-explorer

Length of output: 177


🏁 Script executed:

# Check if electron/types.ts is used for main process or IPC types
head -30 electron/types.ts

# Check git history or comments about why ExplorerMode is in electron/types.ts
rg -B 5 "export type ExplorerMode" electron/types.ts

Repository: stepandel/pinecone-explorer

Length of output: 989


🏁 Script executed:

# Verify if anything imports ExplorerMode from electron/types.ts
rg "ExplorerMode.*from.*electron/types"

Repository: stepandel/pinecone-explorer

Length of output: 53


Remove unused ExplorerMode type from electron/types.ts.

ExplorerMode is defined in three locations but only src/context/ModeContext.tsx is actually imported and used in the codebase. The definition in electron/types.ts (line 56) is dead code. Since electron/types.ts is the central types module for shared Pinecone types, consolidate by either:

  1. Remove the unused definition from electron/types.ts, or
  2. Export from electron/types.ts and import it into ModeContext.tsx to align with other shared types.
🤖 Prompt for AI Agents
In `@electron/types.ts` around lines 53 - 57, The ExplorerMode type exported from
electron/types.ts is unused and duplicates the definition in ModeContext.tsx;
either delete the ExplorerMode declaration from electron/types.ts or centralize
it by moving/exporting the single ExplorerMode type from electron/types.ts and
updating src/context/ModeContext.tsx to import ExplorerMode instead of
redefining it; locate the ExplorerMode type in electron/types.ts and
ModeContext.tsx and apply one of the two options consistently so only one shared
definition remains.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@claude apply proposed fix

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Copy Markdown

@claude claude bot Feb 5, 2026

Choose a reason for hiding this comment

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

Claude finished @stepandel's task in 1m 8s —— View job


Applying proposed fix ✅

  • Read electron/types.ts to confirm the duplicate ExplorerMode definition
  • Verify ExplorerMode from electron/types.ts is unused
  • Remove the unused type definition
  • Commit and push changes

Changes made:

  • Removed unused ExplorerMode type definition from electron/types.ts (lines 53-56)
  • Updated ConnectionProfile.preferredMode to use inline union type 'index' | 'assistant' instead of referencing the removed type

The fix has been applied and pushed to the pine-40-file-handlers branch in commit c3a86ad.


Atlas (Engineering Lead) and others added 3 commits February 5, 2026 18:16
Add file operations scoped to a specific assistant:

Types (electron/types.ts):
- AssistantFileStatus enum: Processing, Available, Deleting, ProcessingFailed
- AssistantFile interface with id, name, status, percentDone, metadata, signedUrl, errorMessage
- ListAssistantFilesFilter for filtering files
- UploadAssistantFileParams for file upload with metadata

AssistantService (electron/assistant-service.ts):
- listFiles(assistantName, filter?) - List files for an assistant
- describeFile(assistantName, fileId) - Get file details with signed URL
- uploadFile(assistantName, params) - Upload file from disk path
- deleteFile(assistantName, fileId) - Delete a file

IPC handlers (electron/main.ts):
- assistant:files:list
- assistant:files:describe
- assistant:files:upload
- assistant:files:delete

Preload bindings (electron/preload.ts):
- assistant.files.list()
- assistant.files.describe()
- assistant.files.upload()
- assistant.files.delete()

TypeScript declarations (src/types/electron.d.ts):
- Added all file types and API methods
- AssistantsPanel: Convert assistant row div to button for keyboard accessibility
- AssistantsPanel: Add aria-pressed attribute for active state
- ModeContext: Fix stale setMode closure by adding to useEffect dependencies
- ModeContext: Reorder setMode definition before keyboard effect
@stepandel stepandel force-pushed the pine-40-file-handlers branch from 859cff8 to 43709fd Compare February 5, 2026 18:17
@stepandel stepandel changed the base branch from master to feat/assistant-mode February 5, 2026 18:17
The ExplorerMode type was defined in electron/types.ts but never imported
or used. The only active definition is in src/context/ModeContext.tsx.
This change removes the duplicate definition and updates
ConnectionProfile.preferredMode to use an inline union type.

Co-authored-by: Stepan Arsentjev <stepandel@users.noreply.github.com>
@stepandel stepandel merged commit 53f68f4 into feat/assistant-mode Feb 5, 2026
1 of 2 checks passed
@stepandel stepandel deleted the pine-40-file-handlers branch February 5, 2026 18:28
stepandel added a commit that referenced this pull request Feb 6, 2026
* feat: Add ModeContext and ModeSwitcher UI (#37)

* feat: Add ModeContext and ModeSwitcher UI

- Add ModeContext for managing index/assistant mode state
- Add ModeSwitcher segmented control with Database/Bot icons
- Persist mode preference per connection profile
- Add keyboard shortcuts Cmd+1 (Index) and Cmd+2 (Assistant)

Closes PINE-36

* fix: address review comments on PINE-36

- Update .linear.toml workspace from chroma-explorer to pinecone-explorer
- Make setPreferredMode throw on missing profile instead of silently no-op
- Change ModeSwitcher from tablist/tab to radiogroup/radio for accessibility

* fix: add packages field to pnpm-workspace.yaml for CI

* fix: add aria-label for accessible name on ModeSwitcher buttons

---------

Co-authored-by: Atlas (Engineering Lead) <atlas@openclaw.ai>
Co-authored-by: Scout (Lead Tester) <scout@openclaw.ai>

* feat: Add AssistantService with CRUD operations (#38)

* feat: Add AssistantService with CRUD operations

- Add AssistantService class wrapping Pinecone SDK assistant methods
- Add IPC handlers for assistant:list/create/describe/update/delete
- Add preload bindings for window.electronAPI.assistant
- Add TypeScript types for AssistantModel, CreateAssistantParams, UpdateAssistantParams
- Wire up service to PineconeService with getAssistantService() method

Closes PINE-37

* fix: address review comments on PINE-37

- Normalize metadata null to undefined in mapAssistantModel
- Fix stale setMode closure in keyboard shortcut handler (moved setMode before useEffect, added to deps)

---------

Co-authored-by: Atlas (Engineering Lead) <atlas@openclaw.ai>
Co-authored-by: Scout (Lead Tester) <scout@openclaw.ai>

* feat: Add AssistantsPanel component (#39)

* feat: Add AssistantsPanel component

- Add useAssistantQueries.ts with React Query hooks for assistant CRUD
- Add AssistantSelectionContext.tsx for managing selected assistant
- Add AssistantsPanel.tsx mirroring IndexesPanel pattern
- Status indicators: Ready (green), Initializing (yellow), Failed (red)
- Loading, error, and empty states handled

Closes PINE-38

* feat(assistant): add file management IPC handlers (PINE-40)

Add file operations scoped to a specific assistant:

Types (electron/types.ts):
- AssistantFileStatus enum: Processing, Available, Deleting, ProcessingFailed
- AssistantFile interface with id, name, status, percentDone, metadata, signedUrl, errorMessage
- ListAssistantFilesFilter for filtering files
- UploadAssistantFileParams for file upload with metadata

AssistantService (electron/assistant-service.ts):
- listFiles(assistantName, filter?) - List files for an assistant
- describeFile(assistantName, fileId) - Get file details with signed URL
- uploadFile(assistantName, params) - Upload file from disk path
- deleteFile(assistantName, fileId) - Delete a file

IPC handlers (electron/main.ts):
- assistant:files:list
- assistant:files:describe
- assistant:files:upload
- assistant:files:delete

Preload bindings (electron/preload.ts):
- assistant.files.list()
- assistant.files.describe()
- assistant.files.upload()
- assistant.files.delete()

TypeScript declarations (src/types/electron.d.ts):
- Added all file types and API methods

* feat: Add FilesPanel component

- Add useFilesQuery hook with dynamic polling (5s while processing)
- Add FileSelectionContext for tracking selected file
- Add FilesPanel component with status indicators and upload button
- Add dialog:showOpenDialog IPC handler for native file picker
- Wire up providers in ConnectionWindow

Closes PINE-41

* feat(files): add UploadFileDialog component with drag-and-drop support

PINE-42

- Add useUploadFileMutation hook to useAssistantQueries.ts
- Create UploadFileDialog component with:
  - Drag-and-drop file zone
  - File picker button using native dialog
  - Selected file preview with name and size
  - Optional metadata JSON editor with validation
  - Multimodal checkbox (enabled only for PDF files)
  - Upload progress indicator
  - Error handling with inline message
  - Dialog closes on successful upload
- Wire up FilesPanel upload button to open UploadFileDialog

* feat(PINE-43): Create FileDetailPanel component

- Create FileDetailPanel.tsx showing file metadata when selected
- Add ID field with copy-to-clipboard button
- Display status with color indicator (Available/Processing/Failed/Deleting)
- Show processing progress bar when file is processing
- Show error message section for failed files
- Display timestamps (created/updated) with formatted dates
- Show custom metadata as JSON
- Add Download button that opens signedUrl via shell.openExternal
- Add Delete button with confirmation dialog
- Add useDeleteFileMutation and useFileDetailQuery hooks to useAssistantQueries.ts
- Export FileDetailPanel from files/index.ts

Note: File size display not implemented as AssistantFile type from
Pinecone API does not include a size field.

* feat: Add chat IPC handlers with streaming support

- Add ChatMessage, ChatParams, ChatResponse, ChatStreamChunk types
- Add chat() and chatStream() methods to AssistantService
- Add IPC handlers for assistant:chat, assistant:chat:stream:start/cancel
- Add preload bindings with onChunk event listener for streaming
- Track active streams with AbortController for cancellation

Closes PINE-44

* feat(PINE-45): Create ChatView main component

- Create src/components/chat/ChatView.tsx
  - Layout with scrollable message list and fixed input area
  - Message display with user/assistant avatars and styling
  - Model dropdown selector (gpt-4o, claude-3-5-sonnet, gemini-2.0-flash)
  - Clear conversation button
  - Auto-scroll to bottom on new messages
  - Submit on Enter, Shift+Enter for newline
  - Send button disabled while streaming
  - Stop generation button during streaming
  - Citation display for assistant messages
  - Empty state with helpful instructions

- Create src/hooks/useChatStream.ts
  - Manages streaming state and message accumulation
  - Handles chunk events (message_start, content, citation, message_end, error)
  - Returns: messages, isStreaming, sendMessage, clearMessages, cancelStream
  - Properly cleans up subscriptions on unmount

- Update src/components/layout/MainContent.tsx
  - Import ModeContext and AssistantSelectionContext
  - Render ChatView when mode === 'assistant' and an assistant is selected
  - Show empty state message when in assistant mode without selection
  - Keep existing VectorsView for mode === 'index'

* feat(PINE-46): Create ChatMessage component with streaming

- Add ChatMessage component with markdown support via react-markdown
- User messages right-aligned with blue/primary background
- Assistant messages left-aligned with muted background
- Typing indicator (animated dots) when streaming with empty content
- Live cursor animation during content streaming
- Citation numbers as clickable superscripts inline with text
- Styled code blocks and inline code
- Update ChatView to use new ChatMessage component

* feat(chat): add CitationPopover component for interactive citations

- Create CitationPopover component using Radix Popover
- Shows file name and page numbers for each reference
- View File button navigates using FileSelectionContext
- Update ChatMessage to wrap citation superscripts with popover
- Popover closes on outside click (Radix default behavior)

Closes PINE-47

* feat: Wire up Assistant mode in MainContent

- Conditionally render AssistantsPanel/IndexesPanel based on mode
- Conditionally render FilesPanel/NamespacesPanel based on mode
- Conditionally render FileDetailPanel/VectorDetailPanel based on mode
- Preserve panel resize handles and widths
- Selection state isolated between modes via separate contexts

Closes PINE-48

* feat(PINE-49): Add context menus for Assistants and Files

- Add IPC handlers in main.ts for context-menu:show-assistant and context-menu:show-file
- Add preload bindings for showAssistantMenu, onAssistantAction, showFileMenu, onFileAction
- Update AssistantsPanel.tsx with:
  - Native context menu on right-click with Edit and Delete options
  - Delete confirmation dialog with name verification
  - Hook integration with useDeleteAssistantMutation
- Update FilesPanel.tsx with:
  - Native context menu on right-click with Download and Delete options
  - Delete confirmation dialog
  - Download via signedUrl fetch and shell.openExternal
  - Hook integration with useDeleteFileMutation and useFileDetailQuery
- Update TypeScript declarations in electron.d.ts

Acceptance criteria:
- Right-click assistant shows Edit/Delete menu ✓
- Right-click file shows Download/Delete menu ✓
- Delete actions show confirmation dialog ✓
- Menu actions trigger correct operations ✓

* feat(PINE-50): Add keyboard shortcuts for Assistant mode

- Update keyboard shortcuts constants with new assistant/chat categories:
  - INDEX_MODE (Cmd+1): Switch to Index mode
  - ASSISTANT_MODE (Cmd+2): Switch to Assistant mode
  - NEW_ASSISTANT (Cmd+Shift+N): Create new assistant
  - SEND_MESSAGE (Cmd+Enter): Send chat message
  - FOCUS_CHAT_INPUT (Cmd+K): Focus chat input
  - CLEAR_CONVERSATION (Cmd+Shift+Backspace): Clear conversation

- Update electron menu.ts:
  - Replace panel toggle items with Index Mode/Assistant Mode in View menu
  - Add Assistant menu with New Assistant, Chat submenu, Edit/Delete items
  - Chat submenu includes Send, Focus Input, Clear Conversation

- Add IPC bindings in preload.ts for new menu events:
  - Mode switching: onSwitchToIndexMode, onSwitchToAssistantMode
  - Assistant: onNewAssistant, onEditAssistant, onDeleteAssistant
  - Chat: onSendMessage, onFocusChatInput, onClearConversation

- Update ModeContext.tsx to listen for menu IPC events

- Update ChatView.tsx with keyboard shortcut handlers:
  - Cmd+K focuses chat input
  - Cmd+Shift+Backspace clears conversation
  - Cmd+Enter sends message (via menu)

- Update AssistantsPanel.tsx:
  - Cmd+Shift+N creates new assistant
  - Handle menu events for edit/delete assistant

- Update TypeScript types in electron.d.ts

- Clean up obsolete toggle panel handlers from useMenuHandlers.ts

---------

Co-authored-by: Atlas (Engineering Lead) <atlas@openclaw.ai>

* feat: Add file management IPC handlers (#40)

* feat(assistant): add file management IPC handlers (PINE-40)

Add file operations scoped to a specific assistant:

Types (electron/types.ts):
- AssistantFileStatus enum: Processing, Available, Deleting, ProcessingFailed
- AssistantFile interface with id, name, status, percentDone, metadata, signedUrl, errorMessage
- ListAssistantFilesFilter for filtering files
- UploadAssistantFileParams for file upload with metadata

AssistantService (electron/assistant-service.ts):
- listFiles(assistantName, filter?) - List files for an assistant
- describeFile(assistantName, fileId) - Get file details with signed URL
- uploadFile(assistantName, params) - Upload file from disk path
- deleteFile(assistantName, fileId) - Delete a file

IPC handlers (electron/main.ts):
- assistant:files:list
- assistant:files:describe
- assistant:files:upload
- assistant:files:delete

Preload bindings (electron/preload.ts):
- assistant.files.list()
- assistant.files.describe()
- assistant.files.upload()
- assistant.files.delete()

TypeScript declarations (src/types/electron.d.ts):
- Added all file types and API methods

* fix: address review comments - pnpm workspace, linear config, AssistantStatus type

* fix: address review comments on PINE-40

- AssistantsPanel: Convert assistant row div to button for keyboard accessibility
- AssistantsPanel: Add aria-pressed attribute for active state
- ModeContext: Fix stale setMode closure by adding to useEffect dependencies
- ModeContext: Reorder setMode definition before keyboard effect

* fix: remove unused ExplorerMode type from electron/types.ts

The ExplorerMode type was defined in electron/types.ts but never imported
or used. The only active definition is in src/context/ModeContext.tsx.
This change removes the duplicate definition and updates
ConnectionProfile.preferredMode to use an inline union type.

Co-authored-by: Stepan Arsentjev <stepandel@users.noreply.github.com>

---------

Co-authored-by: Atlas (Engineering Lead) <atlas@openclaw.ai>
Co-authored-by: Scout (Lead Tester) <scout@openclaw.ai>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Stepan Arsentjev <stepandel@users.noreply.github.com>

* feat: Add FilesPanel component (#41)

* feat: Add FilesPanel component

- Add useFilesQuery hook with dynamic polling (5s while processing)
- Add FileSelectionContext for tracking selected file
- Add FilesPanel component with status indicators and upload button
- Add dialog:showOpenDialog IPC handler for native file picker
- Wire up providers in ConnectionWindow

Closes PINE-41

* fix: address review comments - pnpm workspace, linear config, AssistantStatus type

* fix: clear assistantService on disconnect, remove files from later PRs

---------

Co-authored-by: Atlas (Engineering Lead) <atlas@openclaw.ai>
Co-authored-by: Scout (Lead Tester) <scout@openclaw.ai>

* fix build: add stub FileDetailPanel

* fix: Address PR review comments

- Add registerAccelerator: false to New Index menu item for consistency
- Add cancellation guard in ModeContext to prevent stale mode updates
- Handle 'InitializationFailed' status in AssistantsPanel
- Make citation superscripts keyboard accessible (button with aria-label)
- Validate URL protocol before calling openExternal for security
- Disable delete button when file status is 'Deleting'
- Clear messages when switching assistants in useChatStream

* feat: Add AssistantConfigView for create/edit (#51)

- Add AssistantConfigView component for creating/editing assistants
- Add DraftAssistantContext for managing draft assistant state
- Wire up AssistantConfigView in MainContent
- Add DraftAssistantProvider to ConnectionWindow

Closes PINE-39

Co-authored-by: Scout (Lead Tester) <scout@openclaw.ai>

* fix: address review comments on PINE-42 (#42)

Co-authored-by: Scout (Lead Tester) <scout@openclaw.ai>

* fix: address review comments on PINE-44 (#45)

- Remove @ts-expect-error, add temperature/contextOptions to chat params
- Add isDestroyed check and error handling for streaming in main.ts
- Clear assistantService on disconnect in pinecone-service.ts
- Make assistant rows keyboard-accessible (use button element)
- Add IME composition check (isComposing) to prevent accidental submit
- Use stable message keys (message.id) instead of array index
- Reset conversation state when assistantName changes
- Remove empty assistant placeholder when canceling stream
- Pass multimodal flag through file upload flow

Co-authored-by: Scout (Lead Tester) <scout@openclaw.ai>

* fix: address review comments on PINE-39 (#47)

- Fix .linear.toml workspace (chroma-explorer → pinecone-explorer)
- Add packages field to pnpm-workspace.yaml for CI
- Add InitializationFailed to AssistantStatus type
- Reset assistantService on connect/disconnect

Co-authored-by: Scout (Lead Tester) <scout@openclaw.ai>

* fix: address review comments on PINE-49 (#49)

- Pass temperature and contextOptions to chat() and chatStream() methods
- Add multimodal param to upload file flow (types, hook, service)
- Guard handleConfirmDelete against null currentProfile
- Use stable message.id key instead of array index in ChatView
- Fix download race condition by verifying fileDetail matches fileToDownload
- Keep file detail cache consistent during delete operations
- Handle early stream chunks before stream ID is set

Co-authored-by: Scout (Lead Tester) <scout@openclaw.ai>

* fix: Address additional PR review comments (round 2)

- Add assistant and chat menu event handlers to preload.ts
- Add types for new menu handlers in electron.d.ts
- Add pending guard to prevent duplicate delete calls in AssistantsPanel
- Reset initialization state when profile changes in ModeContext
- Extract shared Markdown components to reduce duplication in ChatMessage

* fix buid errors

* feat: Add E2E test suite for Assistant feature (#53)

* feat(e2e): add data-testid attributes to assistant components

Add comprehensive data-testid attributes for E2E testing:

Mode:
- mode-switcher, mode-index, mode-assistant

Assistants:
- assistants-panel, assistant-item, assistant-status
- new-assistant-button, assistant-config-view
- assistant-name-input, assistant-instructions-input
- assistant-save-button, assistant-cancel-button

Files:
- files-panel, files-empty-state, file-item
- upload-file-button, file-detail-panel
- file-detail-empty-state, file-download-button
- file-delete-button, upload-file-dialog
- browse-files-button, upload-submit-button

Chat:
- chat-view, chat-message-list, chat-input
- chat-send-button, chat-stop-button
- chat-clear-button, chat-model-selector
- chat-message-user, chat-message-assistant

Citations:
- citation-superscript, citation-popover
- citation-reference, citation-file-name
- citation-view-file-button

Part of PINE-51

* fix(e2e): Address PR review comments - 13 actionable items

Fixes from CodeRabbit review:

1. assistant-citations.spec.ts:
   - Citation test now explicitly skips with message when no citations
   - Multiple citations test uses test.skip() when citationCount <= 1

2. assistant-crud.spec.ts:
   - Added assertion for errorMessage visibility in validation test
   - Context menu edit test now explicitly skipped (native menu limitation)

3. assistant-file-detail.spec.ts:
   - File selection test explicitly skips when fileCount === 0
   - Delete test now asserts file disappears and count decreases

4. assistant-integration.spec.ts:
   - Network failure test now uses page.route() to simulate failures

5. assistant-mode.spec.ts:
   - Cross-platform keyboard shortcuts (Meta on macOS, Control on others)
   - Mode persistence test asserts switcher visibility (no silent skip)

6. assistant-upload.spec.ts:
   - API upload test: skip until real file fixture available
   - Metadata input test: skip until file dialog mocking available
   - Processing status test: explicit skip when no files
   - Progress test: skip until upload flow is wired

---------

Co-authored-by: Atlas (Engineering Lead) <atlas@openclaw.ai>

* fix(assistant): wire chatStream API through preload (#54)

PINE-55: Fix 'Cannot read properties of undefined (reading onChunk)'

Root cause: useChatStream.ts expected assistant.chatStream.* APIs but
electron/preload.ts never exposed them. IPC handlers existed in main.ts
but weren't wired through the preload bridge.

Changes:
- Add Chat types to electron/types.ts (ChatMessage, Citation,
  CitationReference, ChatUsage, ChatParams, ChatResponse, ChatStreamChunk)
- Add chatStream namespace to assistant API in preload.ts with
  start/cancel/onChunk methods
- Add corresponding TypeScript types to src/types/electron.d.ts

The chatStream API now properly exposes:
- start(profileId, assistantName, params) -> streamId
- cancel(streamId) -> void
- onChunk(callback) -> cleanup function

Co-authored-by: Atlas (Engineering Lead) <atlas@openclaw.ai>

* fix: Phase 6 feedback - dialog API and mode labels (PINE-53, PINE-54) (#55)

* fix(assistant): wire chatStream API through preload

PINE-55: Fix 'Cannot read properties of undefined (reading onChunk)'

Root cause: useChatStream.ts expected assistant.chatStream.* APIs but
electron/preload.ts never exposed them. IPC handlers existed in main.ts
but weren't wired through the preload bridge.

Changes:
- Add Chat types to electron/types.ts (ChatMessage, Citation,
  CitationReference, ChatUsage, ChatParams, ChatResponse, ChatStreamChunk)
- Add chatStream namespace to assistant API in preload.ts with
  start/cancel/onChunk methods
- Add corresponding TypeScript types to src/types/electron.d.ts

The chatStream API now properly exposes:
- start(profileId, assistantName, params) -> streamId
- cancel(streamId) -> void
- onChunk(callback) -> cleanup function

* fix: Phase 6 feedback - dialog API and mode labels

PINE-54: Wire dialog API through preload
- Add dialog.showOpenDialog to electron/preload.ts
- Add corresponding TypeScript types

PINE-53: Update mode labels to 'Database' instead of 'Index'
- Change 'Index Explorer' → 'Database Explorer' in tooltip
- Change 'Index' → 'Database' in button text

---------

Co-authored-by: Atlas (Engineering Lead) <atlas@openclaw.ai>

* fix uploader

* fix(assistant): use SDK chatStream method instead of chat with stream flag

The Pinecone SDK has a dedicated `assistant.chatStream()` method for streaming.
Passing `stream: true` to `assistant.chat()` is invalid and causes an API error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(assistant): update supported models list to match Pinecone Assistant

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(chat): compact macOS-native chat UI

Remove avatars and role labels for an Apple Messages-style layout.
Tighten spacing, use pill-shaped bubbles, circular send button, and
smaller typography to match the app's TopBar and sidebar density.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(files): add right-click context menu for file actions (#56)

PINE-56: Phase 6 feedback - Part 2

Add file context menu with Download and Delete actions:
- Add showFileMenu/onFileAction to electron/preload.ts
- IPC handler already existed in main.ts (lines 656-673)
- Add onContextMenu handler to FilesPanel file buttons
- Delete action shows confirmation dialog, then deletes file
- Download action opens signed URL in browser
- Add TypeScript types to electron.d.ts

Shortcuts in Settings were already implemented (keyboard-shortcuts.ts
has 'assistant' and 'chat' categories).

Co-authored-by: Atlas (Engineering Lead) <atlas@openclaw.ai>

* feat(analytics): add tracking for Assistant events (PINE-57) (#57)

* feat(files): add right-click context menu for file actions

PINE-56: Phase 6 feedback - Part 2

Add file context menu with Download and Delete actions:
- Add showFileMenu/onFileAction to electron/preload.ts
- IPC handler already existed in main.ts (lines 656-673)
- Add onContextMenu handler to FilesPanel file buttons
- Delete action shows confirmation dialog, then deletes file
- Download action opens signed URL in browser
- Add TypeScript types to electron.d.ts

Shortcuts in Settings were already implemented (keyboard-shortcuts.ts
has 'assistant' and 'chat' categories).

* feat(analytics): add tracking for Assistant events

PINE-57: Phase 7 - Add analytics

Add track() calls to Assistant IPC handlers:
- assistant_created (with region)
- assistant_deleted
- file_uploaded (with multimodal flag)
- file_deleted
- chat_message_sent (with model, messageCount)
- chat_stream_started (with model)

Follows existing analytics pattern from index operations.

---------

Co-authored-by: Atlas (Engineering Lead) <atlas@openclaw.ai>

* fix(files): poll for Deleting status to update UI after file removal

The files query only polled while files had 'Processing' status, so files
stuck in 'Deleting' status never refreshed until a manual page reload.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(assistant): production readiness fixes from audit

Address 10 issues found during production readiness audit:

1. Fix IPC wiring for menu-driven mode switching (onIndexMode/onAssistantMode)
2. Add URL protocol validation in shell:openExternal (block non-http(s))
3. Add confirmation dialog before file deletion in FileDetailPanel
4. Surface file operation errors to users in FilesPanel (upload/delete/download)
5. Resolve keyboard shortcut collision (NEW_ASSISTANT → CmdOrCtrl+Shift+A)
6. Add synchronous ref guard to prevent double-submit in useChatStream
7. Abort active chat streams when window is destroyed
8. Add vitest framework with 17 unit tests for AssistantService and matchesShortcut
9. Add file path validation (exists check) before upload in main process
10. Remove unused dependencies (sharp, dotenv, bufferutil, utf-8-validate)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* clean up

---------

Co-authored-by: Atlas (Engineering Lead) <atlas@openclaw.ai>
Co-authored-by: Scout (Lead Tester) <scout@openclaw.ai>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Stepan Arsentjev <stepandel@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
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.

1 participant