feat: 任务目的树表格化展示 + 切换会话停止老对话服务端 run

- task-purpose: normalizeNode 容错(未知 *Dto 兜底/文本字段改名兜底);新增 flattenPurposeTree 拍平目的树
- TaskPurposePanel: 目的树改表格展示(列数自适应),支持弹框放大查看
- threads/hooks: 新增 stopThreadRun,真正取消服务端 run(非仅断开本地 SSE)
- AgentChatPage: taskGate 下切换/卸载会话时停掉老对话服务端 run
- TaskFxWorkspace: 操作按钮(编辑/下一步/清除)改右下角常驻红色浮层

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 16:42:05 +08:00
1 parent ba0ec20532
commit a2bb8c80a2
5 files changed
+273 -106

No files matched your search

+33
View File
@@ -121,6 +121,39 @@ function getRunMetadataStorage(): {
};
}
/**
* 显式停止某会话「服务端正在跑」的 run。
*
* 注意:useStream / `thread.stop()` 只中断本地 SSE 监听,服务端 run 仍会继续跑。
* 任务流程要求「开启新对话时老对话要停止」,所以这里读出 SDK 存在 sessionStorage
* (``lg:stream:{threadId}``)里的 run id,调用 ``runs.cancel`` 真正取消后端 run,
* 再清掉本地 reconnect 记录。已结束 / 不存在的 run 取消失败时静默忽略。
*/
export async function stopThreadRun(
threadId: string | null | undefined,
): Promise<void> {
if (!threadId || typeof window === "undefined") return;
let runId: string | null = null;
try {
runId = normalizeStoredRunId(
window.sessionStorage.getItem(`lg:stream:${threadId}`),
);
} catch {
runId = null;
}
if (!runId) return;
try {
await getAPIClient().runs.cancel(threadId, runId);
} catch {
/* 已结束 / 不存在 / 网络错误等忽略 */
}
try {
window.sessionStorage.removeItem(`lg:stream:${threadId}`);
} catch {
/* noop */
}
}
function getStreamErrorMessage(error: unknown): string {
if (typeof error === "string" && error.trim()) {
return error;