diff --git a/src/api.ts b/src/api.ts index 4aea859..29ea657 100644 --- a/src/api.ts +++ b/src/api.ts @@ -83,6 +83,8 @@ async function json(res: Response): Promise { return res.json() as Promise } +export type ConversationView = Conversation & { /** 无项目对话:服务端内部项目只承载这一条会话,不出现在项目列表里。 */ scratch?: boolean } + export const api = { permissionCatalog: () => auth.fetch(`${BASE}/api/admin/permissions/catalog`).then(r => json<{ permissions: PermissionDescriptor[] }>(r)).then(d => d.permissions ?? []), listRoles: (request: { query?: string; mode?: 'exact' | 'fuzzy'; status?: 'all' | 'enabled' | 'disabled'; action?: string; cursor?: string; limit?: number } = {}) => { @@ -117,13 +119,13 @@ export const api = { explainSystemAction: (action: string) => auth.fetch(`${BASE}/api/authz/explain?action=${encodeURIComponent(action)}`).then(r => json<{ allowed: boolean; actionId: string; source?: string; reason?: string; authorityEpoch: number }>(r)), listProjects: (signal?: AbortSignal) => auth.fetch(`${BASE}/api/projects`, signal ? { signal } : undefined).then(r => json(r)), createProject: (name: string) => auth.fetch(`${BASE}/api/projects`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name }) }).then(r => json(r)), - createScratchSession: (title?: string) => auth.fetch(`${BASE}/api/scratch-sessions`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(title ? { title } : {}) }).then(r => json<{ project: Project; conversation: Conversation }>(r)), + createScratchSession: (title?: string) => auth.fetch(`${BASE}/api/scratch-sessions`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(title ? { title } : {}) }).then(r => json<{ project: Project; conversation: ConversationView }>(r)), renameProject: (id: string, name: string) => auth.fetch(`${BASE}/api/projects/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name }) }).then(r => json(r)), deleteProject: (id: string) => auth.fetch(`${BASE}/api/projects/${id}`, { method: 'DELETE' }).then(checked), - listConversations: (projectId: string) => auth.fetch(`${BASE}/api/projects/${projectId}/conversations`).then(r => json(r)), + listConversations: (projectId: string) => auth.fetch(`${BASE}/api/projects/${projectId}/conversations`).then(r => json(r)), // 跨项目任务列表(方案 A 侧栏):默认仅未归档;includeArchived 含归档。 - listTasks: (includeArchived?: boolean) => auth.fetch(`${BASE}/api/conversations${includeArchived ? '?archived=1' : ''}`).then(r => json(r)), + listTasks: (includeArchived?: boolean) => auth.fetch(`${BASE}/api/conversations${includeArchived ? '?archived=1' : ''}`).then(r => json(r)), setConversationFlags: (id: string, flags: { pinned?: boolean; archived?: boolean; allowCrossRead?: boolean; crossPrivate?: boolean }) => auth.fetch(`${BASE}/api/conversations/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(flags) }).then(r => { if (!r.ok) throw new Error(`${r.status}: ${r.statusText}`) }), setPermissionMode: (id: string, permissionMode: string) => auth.fetch(`${BASE}/api/conversations/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ permissionMode }) }).then(r => { if (!r.ok) throw new Error(`${r.status}: ${r.statusText}`) }), checkpoints: (id: string, messageId: string) => auth.fetch(`${BASE}/api/conversations/${id}/checkpoints?messageId=${encodeURIComponent(messageId)}`).then(r => json<{ restored?: string[]; skipped?: string[] }>(r)), @@ -189,7 +191,7 @@ export const api = { setConversationProfile: (id: string, profileId: string) => auth.fetch(`${BASE}/api/conversations/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ profileId }) }).then(r => json(r)), conversationDetail: (id: string) => auth.fetch(`${BASE}/api/conversations/${id}`).then(r => - json<{ conversation: Conversation; messages: Message[]; runs: Run[]; activeHitl: HITLCard[]; lastSeq: number; resumeSeq?: number }>(r)), + json<{ conversation: ConversationView; messages: Message[]; runs: Run[]; activeHitl: HITLCard[]; lastSeq: number; resumeSeq?: number }>(r)), runEventsPage: (convId: string, runId: string, after: number, signal?: AbortSignal) => auth.fetch(`${BASE}/api/conversations/${encodeURIComponent(convId)}/runs/${encodeURIComponent(runId)}/events?after=${after}&limit=500`, { signal }) diff --git a/src/components/TaskSidebar.tsx b/src/components/TaskSidebar.tsx index 9421142..33eac53 100644 --- a/src/components/TaskSidebar.tsx +++ b/src/components/TaskSidebar.tsx @@ -3,9 +3,8 @@ import { useEffect, useRef, useState } from 'react' import { store, useStore } from '../store' import { auth } from '../auth' -import { api } from '../api' +import { api, type ConversationView } from '../api' import { Icon } from '@ui' -import type { Conversation } from '../contracts' import { relativeTime, filterTasks } from '../tasklist' import { canCreateProject, emptyProjectsNotice, projectCapabilitySummary, projectStage, projectStateLabel, projectWorkError, @@ -96,7 +95,7 @@ export function TaskSidebar({ onCollapse = store.toggleLeft, onConversationOpen, const syncNotice = syncStatus === 'degraded' ? '项目列表同步异常,正在自动重试' : syncStatus === 'offline' ? '网络已断开,恢复后自动同步项目列表' : null - const rowActions = (t: Conversation) => ( + const rowActions = (t: ConversationView) => (