feat: expand intelligent investigation workspace
This commit is contained in:
@@ -2,6 +2,7 @@ import { createInteractionRequestApi, type InteractionRequestApi } from "./inter
|
||||
import { env } from "@/shared/config/env";
|
||||
import type { AccessTokenProvider } from "@/shared/auth/keycloak-auth";
|
||||
import { agentMessagePageSchema, type AgentMessagePage } from "./agent-messages";
|
||||
import { AnswerViewStateApiError, readAnswerViewStateResponse, type AnswerViewStateRecord, type AnswerViewStateWrite } from "./answer-view-state";
|
||||
|
||||
export type AgentRunStatus = "running" | "completed" | "error" | "aborted" | "waiting_input";
|
||||
export type AgentSessionPurpose = "ordinary_chat" | "task_execution" | "task_collaboration";
|
||||
@@ -20,8 +21,8 @@ export type AgentEventTriggerContext = {
|
||||
summary: string;
|
||||
location?: string | null;
|
||||
priority: "low" | "normal" | "high" | "urgent";
|
||||
scenario_code: "WQ-SECONDARY-SUPPLY-001";
|
||||
origin: "synthetic";
|
||||
scenario_code?: string | null;
|
||||
origin: "system_data";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -156,6 +157,8 @@ export type AgentSessionStreamEvent =
|
||||
|
||||
export type AgentApiClient = InteractionRequestApi & {
|
||||
getHealth: (signal: AbortSignal) => Promise<void>;
|
||||
getAnswerViewState: (sessionId: string, messageId: string, blockId: string) => Promise<AnswerViewStateRecord>;
|
||||
putAnswerViewState: (sessionId: string, state: AnswerViewStateWrite) => Promise<AnswerViewStateRecord>;
|
||||
downloadFile: (path: string) => Promise<void>;
|
||||
submitResponseInput: (sessionId: string, text: string, delivery: AgentInputDelivery, id: string) => Promise<AgentResponseInput>;
|
||||
listResponseInputs: (sessionId: string) => Promise<AgentResponseInput[]>;
|
||||
@@ -249,14 +252,41 @@ export function createAgentApiClient(
|
||||
|
||||
return data as T;
|
||||
};
|
||||
let answerStateActor: string | undefined;
|
||||
const checkedAnswerStateToken = options.getAccessToken ? async () => {
|
||||
const token = await options.getAccessToken!();
|
||||
const actor = answerStateTokenActor(token);
|
||||
if (answerStateActor !== undefined && actor !== answerStateActor) {
|
||||
throw new AnswerViewStateApiError("登录身份已变化,请刷新后继续。", 403, "ANSWER_VIEW_IDENTITY_CHANGED");
|
||||
}
|
||||
answerStateActor = actor;
|
||||
return token;
|
||||
} : undefined;
|
||||
const answerStateRequest = async (path: string, init?: RequestInit) => {
|
||||
const response = await fetchWithFallback(candidates, activeBaseUrl, setActiveBaseUrl, path, init,
|
||||
checkedAnswerStateToken, options.getProjectId);
|
||||
// An identity change while awaiting the network also invalidates the result.
|
||||
await checkedAnswerStateToken?.();
|
||||
return response;
|
||||
};
|
||||
|
||||
return {
|
||||
...createInteractionRequestApi(request),
|
||||
...createInteractionRequestApi(answerStateRequest),
|
||||
async getHealth(signal) {
|
||||
const response = await fetchWithFallback(candidates, activeBaseUrl, setActiveBaseUrl, "/health", { signal, cache: "no-store" }, undefined, undefined, "");
|
||||
if (!response.ok) throw new Error("Agent 服务暂不可用");
|
||||
const data: unknown = await response.json();
|
||||
if (!isRecord(data) || data.ok !== true || data.ready !== true) throw new Error("Agent 服务尚未就绪");
|
||||
},
|
||||
async getAnswerViewState(sessionId, messageId, blockId) {
|
||||
const query = new URLSearchParams({ message_id: messageId, block_id: blockId });
|
||||
return readAnswerViewStateResponse(await answerStateRequest(`/session/${encodeURIComponent(sessionId)}/ui-state?${query}`));
|
||||
},
|
||||
async putAnswerViewState(sessionId, state) {
|
||||
return readAnswerViewStateResponse(await answerStateRequest(`/session/${encodeURIComponent(sessionId)}/ui-state`, {
|
||||
method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(state)
|
||||
}));
|
||||
},
|
||||
async downloadFile(path) {
|
||||
const match = /^\/api\/v1(\/session\/[^/?#]+\/files\/mcp-[a-f0-9-]{36}\.(?:zip|xlsx))$/.exec(path);
|
||||
if (!match) throw new Error("无效的会话文件地址");
|
||||
@@ -845,3 +875,12 @@ function readMessagePagination(value: unknown): AgentMessagePagination | undefin
|
||||
if (!isRecord(value)) return undefined;
|
||||
return { hasMore: value.has_more === true, beforeCursor: typeof value.before_cursor === "string" ? value.before_cursor : null };
|
||||
}
|
||||
|
||||
function answerStateTokenActor(token: string | null): string {
|
||||
if (!token) return "development-context";
|
||||
try {
|
||||
const payload: unknown = JSON.parse(atob(token.split(".")[1].replaceAll("-", "+").replaceAll("_", "/")));
|
||||
if (isRecord(payload) && typeof payload.sub === "string") return JSON.stringify([payload.iss, payload.sub]);
|
||||
} catch { /* Non-JWT development credentials use the enclosing workbench owner. */ }
|
||||
return "development-context";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user