feat: rwfx 任务深链支持目的树闸门 + 任务↔对话(1:N)绑定

后端
- config.yaml 新增 task_deeplink 段:完整 URL(无 base)+ test/delete_test 两个独立测试开关
  + mock 假数据(目的树 / delete 结果,可随时编辑)
- 新增 TaskDeeplinkConfig 模型挂到 AppConfig;新增公开只读接口
  GET /api/public/config/task-deeplink 暴露给前端(/api/public 已在鉴权白名单)

前端
- 登录深链 goPath=rwfx 进入「任务工作区」:左侧按 metadata.taskId 过滤的对话列表
  (新建对话 / 编辑 / 下一步 / 清除任务记录)+ 右侧问答
- 新建对话(含首次进入)顶部展示目的树闸门:有数据弹覆盖确认→调 delete,success 才开始问答;
  恢复历史对话不跑闸门
- 新建 thread 打 metadata.taskId 标签实现「一个任务挂多个对话」(1:N)
- 地址/开关/假数据全部走后端配置,拿不到时用内置默认值兜底;test/delete_test=true 走假数据

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
吉鑫升andClaude Opus 4.8 committed 2026-06-17 15:59:02 +08:00
1 parent 5b610fd807
commit a2d767e2db
15 files changed
+907 -36

No files matched your search

+12 -4
View File
@@ -34,6 +34,9 @@ export type ThreadStreamOptions = {
context: LocalSettings["context"];
isMock?: boolean;
assistantId?: string;
/** 额外写到新建 thread 的 metadata(与 agent_id 合并)。
* 任务深链用 `{ taskId }` 把对话挂到任务下(1:N),列表按 metadata.taskId 过滤。 */
threadMetadata?: Record<string, unknown>;
onStart?: (threadId: string) => void;
onFinish?: (state: AgentThreadState) => void;
onToolEnd?: (event: ToolEndEvent) => void;
@@ -200,6 +203,7 @@ export function useThreadStream({
context,
isMock,
assistantId,
threadMetadata,
onStart,
onFinish,
onToolEnd,
@@ -277,11 +281,15 @@ export function useThreadStream({
handleStreamStart(meta.thread_id);
setOnStreamThreadId(meta.thread_id);
const metadataAgentId = context.agent_id ?? context.agent_name;
if (metadataAgentId && !isMock) {
const extraMetadata =
threadMetadata && typeof threadMetadata === "object" ? threadMetadata : {};
const metadata: Record<string, unknown> = {
...(metadataAgentId ? { agent_id: metadataAgentId } : {}),
...extraMetadata,
};
if (Object.keys(metadata).length > 0 && !isMock) {
void getAPIClient()
.threads.update(meta.thread_id, {
metadata: { agent_id: metadataAgentId },
})
.threads.update(meta.thread_id, { metadata })
.catch(() => ({}));
}
},