Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 55 additions & 1 deletion apps/electron/src/main/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { ipcMain, nativeTheme, shell, dialog, BrowserWindow } from 'electron'
import { IPC_CHANNELS, CHANNEL_IPC_CHANNELS, CHAT_IPC_CHANNELS, AGENT_IPC_CHANNELS, ENVIRONMENT_IPC_CHANNELS, PROXY_IPC_CHANNELS, GITHUB_RELEASE_IPC_CHANNELS } from '@proma/shared'
import { IPC_CHANNELS, CHANNEL_IPC_CHANNELS, CHAT_IPC_CHANNELS, AGENT_IPC_CHANNELS, ENVIRONMENT_IPC_CHANNELS, PROXY_IPC_CHANNELS, GITHUB_RELEASE_IPC_CHANNELS, USAGE_IPC_CHANNELS } from '@proma/shared'
import { USER_PROFILE_IPC_CHANNELS, SETTINGS_IPC_CHANNELS } from '../types'
import type {
Comment on lines +8 to 10
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

@proma/shared 中当前没有导出 USAGE_IPC_CHANNELS,且本文件引用的 UsageStats/ConversationUsage/UsageSettings 类型在 shared 包中也未找到定义,会导致主进程编译失败。建议移除 usage 相关改动或先在 shared 包补齐导出。

Copilot uses AI. Check for mistakes.
RuntimeStatus,
Expand Down Expand Up @@ -44,6 +44,9 @@ import type {
SystemProxyDetectResult,
GitHubRelease,
GitHubReleaseListOptions,
UsageStats,
ConversationUsage,
UsageSettings,
} from '@proma/shared'
import type { UserProfile, AppSettings } from '../types'
import { getRuntimeStatus, getGitRepoStatus } from './lib/runtime-init'
Expand Down Expand Up @@ -109,6 +112,12 @@ import {
listReleases as listGitHubReleases,
getReleaseByTag,
} from './lib/github-release-service'
import {
getUsageStats,
getConversationUsage,
getUsageSettings,
updateUsageSettings,
} from './lib/usage-service'

Comment on lines +115 to 121
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

这里新增引入 ./lib/usage-service,但仓库内不存在该模块(当前 main/lib 下未找到 usage-service.ts),会导致运行/构建失败。建议补齐实现文件并纳入测试,或将 usage 统计改动从本 PR 移除。

Suggested change
import {
getUsageStats,
getConversationUsage,
getUsageSettings,
updateUsageSettings,
} from './lib/usage-service'
// Usage service stubs: underlying implementation not available in this build.
// These implementations return null so usage-related IPC calls fail gracefully
// instead of causing module resolution errors at runtime.
const getUsageStats: any = async (..._args: any[]) => {
return null
}
const getConversationUsage: any = async (..._args: any[]) => {
return null
}
const getUsageSettings: any = async (..._args: any[]) => {
return null
}
const updateUsageSettings: any = async (..._args: any[]) => {
return null
}

Copilot uses AI. Check for mistakes.
/**
* 注册 IPC 处理器
Expand Down Expand Up @@ -541,6 +550,17 @@ export function registerIpcHandlers(): void {
}
)

// 切换 Agent 会话归档状态
ipcMain.handle(
AGENT_IPC_CHANNELS.TOGGLE_ARCHIVE,
async (_, id: string): Promise<AgentSessionMeta> => {
const sessions = listAgentSessions()
const current = sessions.find((s) => s.id === id)
if (!current) throw new Error(`会话不存在: ${id}`)
return updateAgentSessionMeta(id, { archived: !current.archived })
Comment on lines +556 to +560
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

切换归档状态时通过 listAgentSessions() 读取并排序全量会话再 find 当前项,属于不必要的 IO + 排序开销。既然 agent-session-manager 已提供 getAgentSessionMeta(id),建议改用它读取当前 meta(或直接在 updateAgentSessionMeta 内处理不存在),减少额外开销。

Copilot uses AI. Check for mistakes.
}
)

// ===== Agent 工作区管理相关 =====

// 确保默认工作区存在
Expand Down Expand Up @@ -860,6 +880,40 @@ export function registerIpcHandlers(): void {
}
)

// ===== 使用统计相关 =====

// 获取使用量统计总览
ipcMain.handle(
USAGE_IPC_CHANNELS.GET_USAGE_STATS,
async (_, days: number = 30): Promise<UsageStats> => {
return getUsageStats(days)
}
)

// 获取指定对话的使用量详情
ipcMain.handle(
USAGE_IPC_CHANNELS.GET_CONVERSATION_USAGE,
async (_, conversationId: string): Promise<ConversationUsage | null> => {
return getConversationUsage(conversationId)
}
)

// 获取使用统计设置
ipcMain.handle(
USAGE_IPC_CHANNELS.GET_USAGE_SETTINGS,
async (): Promise<UsageSettings> => {
return getUsageSettings()
}
)

// 更新使用统计设置
ipcMain.handle(
USAGE_IPC_CHANNELS.UPDATE_USAGE_SETTINGS,
async (_, settings: UsageSettings): Promise<UsageSettings> => {
return updateUsageSettings(settings)
}
)

console.log('[IPC] IPC 处理器注册完成')

// 注册更新 IPC 处理器
Expand Down
2 changes: 1 addition & 1 deletion apps/electron/src/main/lib/agent-session-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ export function appendAgentMessage(id: string, message: AgentMessage): void {
*/
export function updateAgentSessionMeta(
id: string,
updates: Partial<Pick<AgentSessionMeta, 'title' | 'channelId' | 'sdkSessionId' | 'workspaceId'>>,
updates: Partial<Pick<AgentSessionMeta, 'title' | 'channelId' | 'sdkSessionId' | 'workspaceId' | 'archived'>>,
): AgentSessionMeta {
const index = readIndex()
const idx = index.sessions.findIndex((s) => s.id === id)
Expand Down
43 changes: 42 additions & 1 deletion apps/electron/src/preload/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import { contextBridge, ipcRenderer } from 'electron'
import { IPC_CHANNELS, CHANNEL_IPC_CHANNELS, CHAT_IPC_CHANNELS, AGENT_IPC_CHANNELS, ENVIRONMENT_IPC_CHANNELS, PROXY_IPC_CHANNELS, GITHUB_RELEASE_IPC_CHANNELS } from '@proma/shared'
import { IPC_CHANNELS, CHANNEL_IPC_CHANNELS, CHAT_IPC_CHANNELS, AGENT_IPC_CHANNELS, ENVIRONMENT_IPC_CHANNELS, PROXY_IPC_CHANNELS, GITHUB_RELEASE_IPC_CHANNELS, USAGE_IPC_CHANNELS } from '@proma/shared'
import { USER_PROFILE_IPC_CHANNELS, SETTINGS_IPC_CHANNELS } from '../types'
Comment on lines +9 to 10
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

@proma/shared 中当前没有导出 USAGE_IPC_CHANNELS,并且本文件新增引用的 UsageStats/ConversationUsage/UsageSettings 类型在 shared 包中也未找到定义,会导致编译失败。建议移除这组 usage 相关改动,或在 shared 包中补齐对应的通道常量与类型定义后再引入。

Copilot uses AI. Check for mistakes.
Comment on lines +9 to 10
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

本 PR 描述聚焦于 Agent 会话归档/搜索,但这里额外引入了“使用统计”相关 IPC/API(USAGE_*)。建议将 usage 统计拆分为单独 PR,避免功能耦合并降低回归风险。

Copilot uses AI. Check for mistakes.
import type {
RuntimeStatus,
Expand Down Expand Up @@ -50,6 +50,9 @@ import type {
SystemProxyDetectResult,
GitHubRelease,
GitHubReleaseListOptions,
UsageStats,
ConversationUsage,
UsageSettings,
} from '@proma/shared'
import type { UserProfile, AppSettings } from '../types'

Expand Down Expand Up @@ -239,6 +242,9 @@ export interface ElectronAPI {
/** 删除 Agent 会话 */
deleteAgentSession: (id: string) => Promise<void>

/** 切换 Agent 会话归档状态 */
toggleArchiveAgentSession: (id: string) => Promise<AgentSessionMeta>

/** 生成 Agent 会话标题 */
generateAgentTitle: (input: AgentGenerateTitleInput) => Promise<string | null>

Expand Down Expand Up @@ -361,6 +367,20 @@ export interface ElectronAPI {
// 工作区文件变化通知
onCapabilitiesChanged: (callback: () => void) => () => void
onWorkspaceFilesChanged: (callback: () => void) => () => void

// ===== 使用统计相关 =====

/** 获取使用量统计总览 */
getUsageStats: (days?: number) => Promise<UsageStats>

/** 获取指定对话的使用量详情 */
getConversationUsage: (conversationId: string) => Promise<ConversationUsage | null>

/** 获取使用统计设置 */
getUsageSettings: () => Promise<UsageSettings>

/** 更新使用统计设置 */
updateUsageSettings: (settings: UsageSettings) => Promise<UsageSettings>
}

/**
Expand Down Expand Up @@ -594,6 +614,10 @@ const electronAPI: ElectronAPI = {
return ipcRenderer.invoke(AGENT_IPC_CHANNELS.DELETE_SESSION, id)
},

toggleArchiveAgentSession: (id: string) => {
return ipcRenderer.invoke(AGENT_IPC_CHANNELS.TOGGLE_ARCHIVE, id)
},

generateAgentTitle: (input: AgentGenerateTitleInput) => {
return ipcRenderer.invoke(AGENT_IPC_CHANNELS.GENERATE_TITLE, input)
},
Expand Down Expand Up @@ -754,6 +778,23 @@ const electronAPI: ElectronAPI = {
getReleaseByTag: (tag) => {
return ipcRenderer.invoke(GITHUB_RELEASE_IPC_CHANNELS.GET_RELEASE_BY_TAG, tag)
},

// 使用统计
getUsageStats: (days?: number) => {
return ipcRenderer.invoke(USAGE_IPC_CHANNELS.GET_USAGE_STATS, days)
},

getConversationUsage: (conversationId: string) => {
return ipcRenderer.invoke(USAGE_IPC_CHANNELS.GET_CONVERSATION_USAGE, conversationId)
},

getUsageSettings: () => {
return ipcRenderer.invoke(USAGE_IPC_CHANNELS.GET_USAGE_SETTINGS)
},

updateUsageSettings: (settings: UsageSettings) => {
return ipcRenderer.invoke(USAGE_IPC_CHANNELS.UPDATE_USAGE_SETTINGS, settings)
},
}

// 将 API 暴露到渲染进程的 window 对象上
Expand Down
49 changes: 49 additions & 0 deletions apps/electron/src/renderer/atoms/agent-atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,55 @@ export const currentAgentSessionDraftAtom = atom(
}
)

// ===== 会话归档与搜索 =====

/** 归档视图切换 */
export const showArchivedSessionsAtom = atom<boolean>(false)

/** 会话搜索关键词 */
export const sessionSearchKeywordAtom = atom<string>('')

/** 获取活跃会话(按工作区过滤 + 排除归档) */
export const activeAgentSessionsAtom = atom((get) => {
const sessions = get(agentSessionsAtom)
const currentWorkspaceId = get(currentAgentWorkspaceIdAtom)
return sessions.filter(
(s) => s.workspaceId === currentWorkspaceId && !s.archived,
)
})

/** 获取归档会话 */
export const archivedAgentSessionsAtom = atom((get) => {
const sessions = get(agentSessionsAtom)
const currentWorkspaceId = get(currentAgentWorkspaceIdAtom)
return sessions.filter(
(s) => s.workspaceId === currentWorkspaceId && s.archived,
)
})

/** 过滤后的会话列表(结合工作区、归档状态、搜索关键词) */
export const filteredAgentSessionsAtom = atom((get) => {
const sessions = get(agentSessionsAtom)
const currentWorkspaceId = get(currentAgentWorkspaceIdAtom)
const showArchived = get(showArchivedSessionsAtom)
const keyword = get(sessionSearchKeywordAtom)

return sessions.filter((s) => {
// 工作区筛选
if (s.workspaceId !== currentWorkspaceId) return false

// 归档状态筛选
if (showArchived !== !!s.archived) return false

// 关键词搜索
if (keyword.trim()) {
return s.title.toLowerCase().includes(keyword.toLowerCase())
}
Comment on lines +487 to +490
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

关键词判断使用了 keyword.trim(),但实际匹配使用的是未 trim 的 keyword.toLowerCase();当用户输入前后空格时会导致无法命中(例如 " foo ")。建议先计算 const normalized = keyword.trim().toLowerCase(),再用于 includes 匹配。

Copilot uses AI. Check for mistakes.

return true
})
})

// ===== 后台任务管理 =====

/**
Expand Down
Loading
Loading