This commit is contained in:
fengxiangdebaozi committed 2026-06-22 21:03:19 +08:00
commit adabfa2fe3
6 files changed
+69 -18

No files matched your search

+12 -4
View File
@@ -233,12 +233,16 @@ export interface TaskDeeplinkTarget {
state?: Record<string, unknown>;
}
/** 用 taskName / taskContent 拼出开场信息文本;末尾附上任务 id(task_id),供后端识别任务。 */
function buildOpeningText(detail: CopTaskDetail, taskId: string): string {
/**
* 用 taskName / taskContent 拼出开场信息文本;末尾附上任务 id(task_id),供后端识别任务。
* xdfx(行动分析)额外附上「行动id(action_id)」——传入 actionId 时追加该行。
*/
function buildOpeningText(detail: CopTaskDetail, taskId: string, actionId?: string): string {
return [
detail.taskName ? `任务名称:${detail.taskName}` : "",
detail.taskContent ? `任务内容:${detail.taskContent}` : "",
taskId ? `任务id(task_id)为${taskId}` : "",
actionId ? `行动id(action_id)为${actionId}` : "",
]
.filter(Boolean)
.join("\n");
@@ -312,11 +316,15 @@ export async function resolveTaskDeeplinkTarget(
// 暂存任务上下文(taskId + agentId + 开场信息),跳到 chats 索引,由工作区按 taskId 决定
// 开最新历史对话 / 新建。rwfx 在工作区里额外过「目的树闸门」;3qfx/xdfx 仅 1:N 列表,
// 新建对话直接带开场信息自动发出(见 task-purpose / AgentChatPage 的 taskGate / taskBind)。
// xdfx(行动分析):聊天记录按「行动id」划分(partitionByAction),且开场信息追加 action_id。
const isXdfx = parsed.businessCode === XDFX_BUSINESS_CODE;
const actionId = rawId || taskId;
persistTaskContext({
taskId,
agentId,
openingText: buildOpeningText(detail, taskId),
actionId: rawId || taskId,
openingText: buildOpeningText(detail, taskId, isXdfx ? actionId : undefined),
actionId,
partitionByAction: isXdfx,
});
return { path: `${taskBase}/agent/${agentId}/chats` };
}
@@ -41,6 +41,12 @@ export interface TaskContext {
openingText: string;
/** 深链原始 id(xdfx 为「行动id」,需追加行动id 的按钮用;其它业务等于 taskId)。 */
actionId?: string;
/**
* xdfx(行动分析)专属:聊天记录按「行动id」(actionId) 而非「任务id」划分。
* 为 true 时,新建对话额外打 `metadata.actionId` 标签、对话列表按 actionId 过滤
* (仍保留 taskId 标签以维持任务深链对话「全局共享、不分用户」的后端语义)。
*/
partitionByAction?: boolean;
}
const TASK_CONTEXT_STORAGE_KEY = "task.rwfxContext";
@@ -311,12 +317,29 @@ export function readTaskContext(): TaskContext | null {
agentId: String(parsed.agentId),
openingText: typeof parsed.openingText === "string" ? parsed.openingText : "",
actionId: typeof parsed.actionId === "string" ? parsed.actionId : undefined,
partitionByAction: parsed.partitionByAction === true,
};
} catch {
return null;
}
}
/**
* 任务深链对话的 thread metadata 标签(既用于「新建对话」打标签,也用于对话列表过滤)。
* - 普通业务(rwfx/3qfx):`{ taskId }` —— 按任务划分(1:N)。
* - xdfx(partitionByAction):`{ taskId, actionId }` —— 额外按「行动id」划分聊天记录;
* 仍带 taskId 以保留后端「任务深链对话全局共享」的语义(后端据 metadata.taskId 跳过分用户过滤)。
* 无有效 taskId 时返回 undefined(调用方据此不打标签)。
*/
export function taskThreadMetadata(
ctx: TaskContext | null | undefined,
): Record<string, unknown> | undefined {
if (!ctx?.taskId) return undefined;
return ctx.partitionByAction && ctx.actionId
? { taskId: ctx.taskId, actionId: ctx.actionId }
: { taskId: ctx.taskId };
}
export function clearTaskContext(): void {
try {
sessionStorage.removeItem(TASK_CONTEXT_STORAGE_KEY);
@@ -8,6 +8,10 @@
* 同一个 taskId 可能挂在多条业务链上(rwfx / 3qfx / xdfx 共用任务 id),每条业务用各自的
* 智能体。传入 ``agentId`` 时额外按 ``metadata.agent_id`` 过滤(后端 metadata 过滤是 AND/子集
* 匹配),把不同业务的对话列表隔离开——否则三种业务的对话会混在一起。
*
* xdfx(行动分析):聊天记录按「行动id」划分——传入 ``actionId`` 时额外按 ``metadata.actionId``
* 过滤(同一任务下的不同行动各自一份对话列表);仍保留 taskId 过滤以命中后端「任务深链对话
* 全局共享」的判定(后端据 metadata.taskId 跳过分用户过滤)。
*/
import { useThreadsPage } from "@/core/threads/hooks";
@@ -16,13 +20,16 @@ import type { AgentThread } from "@/core/threads/types";
export function useTaskThreads(
taskId: string | null | undefined,
agentId?: string | null | undefined,
actionId?: string | null | undefined,
) {
const enabled = !!taskId;
// 任务工作区只在有 taskId 时渲染;无 taskId 时传一个不可能命中的过滤,返回空列表。
const metadata = enabled
? agentId
? { taskId, agent_id: agentId }
: { taskId }
? {
taskId,
...(actionId ? { actionId } : {}),
...(agentId ? { agent_id: agentId } : {}),
}
: { taskId: "__none__" };
const query = useThreadsPage({ page: 1, pageSize: 100, metadata });
const threads: AgentThread[] = enabled ? (query.data?.threads ?? []) : [];
+4 -3
View File
@@ -31,7 +31,7 @@ import { saveLastAgentId, useThreadSettings } from "@/core/settings";
import { stopThreadRun, useThreadStream } from "@/core/threads/hooks";
import { textOfMessage } from "@/core/threads/utils";
import { env } from "@/env";
import { readTaskContext } from "@/core/auth/task-purpose";
import { readTaskContext, taskThreadMetadata } from "@/core/auth/task-purpose";
import { TaskPurposePanel } from "@/strategy-components/components/task-components/TaskPurposePanel";
import { desensitize } from "@/lib/desensitize";
@@ -143,8 +143,9 @@ export default function AgentChatPage({
threadId: isNewThread ? undefined : threadId,
assistantId: agent_id,
context: { ...settings.context, agent_id },
// 任务闸门:新建 thread 打 metadata.taskId 标签,供任务对话列表(1:N)按任务过滤。
threadMetadata: taskId ? { taskId } : undefined,
// 任务闸门:新建 thread 打 metadata.taskId 标签,供任务对话列表(1:N)按任务过滤;
// xdfx 额外打 metadata.actionId 标签,按「行动id」划分聊天记录(见 taskThreadMetadata)。
threadMetadata: taskId ? taskThreadMetadata(taskContext) : undefined,
onStart: (createdThreadId) => {
setThreadId(createdThreadId);
setIsNewThread(false);
@@ -46,11 +46,19 @@ import AgentChatPage from "@/pages/AgentChatPage";
import { ChatRuntime } from "@/pages/WorkspaceRoutes";
import { TaskJumpBar } from "@/strategy-components/components/task-components/TaskJumpBar";
function useChatsBase(taskBase: string): { taskId: string; agentId: string; chatsBase: string } {
function useChatsBase(taskBase: string): {
taskId: string;
agentId: string;
/** xdfx(partitionByAction):按「行动id」划分聊天记录用;其它业务为 ""(仍按 taskId 划分)。 */
actionId: string;
chatsBase: string;
} {
const { agent_id } = useParams<{ agent_id: string }>();
const agentId = agent_id ?? "";
const taskId = readTaskContext()?.taskId ?? "";
return { taskId, agentId, chatsBase: `${taskBase}/agent/${agentId}/chats` };
const ctx = readTaskContext();
const taskId = ctx?.taskId ?? "";
const actionId = ctx?.partitionByAction ? (ctx.actionId ?? "") : "";
return { taskId, agentId, actionId, chatsBase: `${taskBase}/agent/${agentId}/chats` };
}
/**
@@ -142,19 +150,22 @@ function parseThreadId(pathname: string, chatsBase: string): string | null {
function TaskConversationsList({
taskId,
agentId,
actionId,
chatsBase,
business,
lite,
}: {
taskId: string;
agentId: string;
/** xdfx:按「行动id」划分聊天记录;其它业务为 ""。 */
actionId: string;
chatsBase: string;
business: TaskBusiness;
lite?: boolean;
}) {
const { pathname } = useLocation();
const navigate = useNavigate();
const { threads, isLoading } = useTaskThreads(taskId, agentId);
const { threads, isLoading } = useTaskThreads(taskId, agentId, actionId);
const currentThreadId = useMemo(() => parseThreadId(pathname, chatsBase), [pathname, chatsBase]);
const businessName = desensitize(TASK_BUSINESSES.find((b) => b.code === business)?.name ?? "");
@@ -371,13 +382,14 @@ export function TaskFxWorkspace({
business: TaskBusiness;
lite?: boolean;
}) {
const { taskId, agentId, chatsBase } = useChatsBase(taskBase);
const { taskId, agentId, actionId, chatsBase } = useChatsBase(taskBase);
return (
<div className="flex size-full min-h-0 flex-col overflow-hidden">
<div className="flex min-h-0 flex-1 overflow-hidden">
<TaskConversationsList
taskId={taskId}
agentId={agentId}
actionId={actionId}
chatsBase={chatsBase}
business={business}
lite={lite}
@@ -395,9 +407,9 @@ export function TaskFxWorkspace({
/** chats 索引:有历史对话 → 跳最新;否则 → 跳 new(rwfx 走闸门 / 3qfx/xdfx 直接带开场信息)。 */
export function TaskFxIndexResolver({ taskBase }: { taskBase: string }) {
const { taskId, agentId, chatsBase } = useChatsBase(taskBase);
const { taskId, agentId, actionId, chatsBase } = useChatsBase(taskBase);
const navigate = useNavigate();
const { isLoading, hasThreads, latestThreadId } = useTaskThreads(taskId, agentId);
const { isLoading, hasThreads, latestThreadId } = useTaskThreads(taskId, agentId, actionId);
useEffect(() => {
if (isLoading) return;