Browse Source

feat: 文件上传迁移

master
Senor-Liu 1 month ago
parent
commit
f08dda4fcc
  1. 8
      web/client/src/sections/superAgent/components/SuperAgentInputBar.jsx
  2. 15
      web/client/src/sections/superAgent/container/index.jsx
  3. 186
      web/client/src/sections/superAgent/hooks/useSuperAgentChat.js
  4. 94
      web/client/src/sections/superAgent/utils/attachmentUpload.js
  5. 33
      web/tests/agentApiAdapter.test.mjs

8
web/client/src/sections/superAgent/components/SuperAgentInputBar.jsx

@ -17,7 +17,7 @@
import React from "react"; import React from "react";
import { Button, Input, Progress, Space, Tag, Upload } from "antd"; import { Button, Input, Progress, Space, Tag, Upload } from "antd";
import { StopOutlined } from "@ant-design/icons"; import { CloseOutlined, StopOutlined } from "@ant-design/icons";
import SendIcon from "@assets/img/newAiChat/icon (3).png"; import SendIcon from "@assets/img/newAiChat/icon (3).png";
import FileUploadIcon from "@assets/img/aichat/file_upload.png"; import FileUploadIcon from "@assets/img/aichat/file_upload.png";
@ -34,6 +34,7 @@ const SuperAgentInputBar = ({
isStreamingAnswer, isStreamingAnswer,
handleUploadFile, handleUploadFile,
handleRemoveUploadedFile, handleRemoveUploadedFile,
handleCancelUpload,
handleSend, handleSend,
handleStopAnswer, handleStopAnswer,
}) => { }) => {
@ -108,11 +109,14 @@ const SuperAgentInputBar = ({
beforeUpload={handleUploadFile} beforeUpload={handleUploadFile}
showUploadList={false} showUploadList={false}
> >
<Button title="上传文件"> <Button title="上传文件" disabled={isUploadingFile}>
<img src={FileUploadIcon} alt="" /> <img src={FileUploadIcon} alt="" />
上传文件 上传文件
</Button> </Button>
</Upload> </Upload>
{isUploadingFile ? (
<Button icon={<CloseOutlined />} onClick={handleCancelUpload} title="取消上传" />
) : null}
</Space> </Space>
<Button <Button
type="primary" type="primary"

15
web/client/src/sections/superAgent/container/index.jsx

@ -488,6 +488,7 @@ const SuperAgent = () => {
handleSelectTaskType, handleSelectTaskType,
handleUploadFile, handleUploadFile,
handleRemoveUploadedFile, handleRemoveUploadedFile,
handleCancelUpload,
handleSelectTask: selectLegacyTask, handleSelectTask: selectLegacyTask,
handleSelectChat: selectLegacyChat, handleSelectChat: selectLegacyChat,
} = chat; } = chat;
@ -1438,13 +1439,14 @@ const SuperAgent = () => {
const userInput = inputValue.trim(); const userInput = inputValue.trim();
const currentFileList = uploadedFileList; const currentFileList = uploadedFileList;
const attachmentIds = Array.from(new Set( const attachmentIds = Array.from(new Set(
currentFileList.map((file) => file.attachmentId).filter(Boolean) currentFileList
.filter((file) => file.status === "ready")
.map((file) => file.attachmentId)
.filter(Boolean)
)); ));
const fileReferences = currentFileList const content = userInput || (attachmentIds.length
.map((file) => file.url || file.localPath || file.name) ? `请处理已上传的附件:${currentFileList.map((file) => file.name).filter(Boolean).join("、")}`
.filter(Boolean) : "");
.join("\n");
const content = [fileReferences, userInput].filter(Boolean).join("\n");
const generatedInterrupt = activeGeneratedInterrupt; const generatedInterrupt = activeGeneratedInterrupt;
if (generatedInterrupt?.value?.type === "generated_ui_interrupt" && userInput) { if (generatedInterrupt?.value?.type === "generated_ui_interrupt" && userInput) {
if (stream.isLoading) return; if (stream.isLoading) return;
@ -2648,6 +2650,7 @@ const SuperAgent = () => {
isStreamingAnswer={isStreamingAnswer} isStreamingAnswer={isStreamingAnswer}
handleUploadFile={handleUploadFile} handleUploadFile={handleUploadFile}
handleRemoveUploadedFile={handleRemoveUploadedFile} handleRemoveUploadedFile={handleRemoveUploadedFile}
handleCancelUpload={handleCancelUpload}
handleSend={handleSend} handleSend={handleSend}
handleStopAnswer={handleStopAnswer} handleStopAnswer={handleStopAnswer}
/> />

186
web/client/src/sections/superAgent/hooks/useSuperAgentChat.js

@ -24,9 +24,12 @@ import {
deleteSuperAgentSession, deleteSuperAgentSession,
respondSuperAgentCheckpointStream, respondSuperAgentCheckpointStream,
sendSuperAgentChat, sendSuperAgentChat,
uploadSuperAgentFile,
} from "../actions/api"; } from "../actions/api";
import { createResourceFileFromUpload } from "../actions/resourceLibrary"; import {
completeAttachmentUpload,
createAttachmentUploadIntent,
listAttachments,
} from "../actions/agentResources";
import useSuperAgentSidebar from "./useSuperAgentSidebar"; import useSuperAgentSidebar from "./useSuperAgentSidebar";
import { import {
TASK_STAGE, TASK_STAGE,
@ -62,6 +65,11 @@ import {
} from "../utils/workspace"; } from "../utils/workspace";
import { normalizeCheckpoint } from "../utils/checkpoint"; import { normalizeCheckpoint } from "../utils/checkpoint";
import { isWorkflowBodyText } from "../utils/tenderWorkflow"; import { isWorkflowBodyText } from "../utils/tenderWorkflow";
import {
calculateFileSha256,
normalizeAttachmentUploadIntent,
uploadFileToIntent,
} from "../utils/attachmentUpload";
const getActionData = (res) => { const getActionData = (res) => {
const payload = res?.payload ?? res; const payload = res?.payload ?? res;
@ -76,41 +84,19 @@ const ensureActionSuccess = (res, fallbackMessage) => {
return getActionData(res); return getActionData(res);
}; };
const toReadyUploadedFile = (attachment) => ({
id: String(attachment.id || attachment.attachment_id || attachment.attachmentId),
attachmentId: String(attachment.id || attachment.attachment_id || attachment.attachmentId),
name: attachment.file_name || attachment.filename || "未命名文件",
status: attachment.status,
});
const buildSessionParams = (sessionId) => ({ const buildSessionParams = (sessionId) => ({
conversationId: sessionId, conversationId: sessionId,
sessionId, sessionId,
":sessionId": sessionId, ":sessionId": sessionId,
}); });
const hasFilePathPrefix = (filePath = "") => {
return /^(https?:)?\/\//i.test(filePath) || /^(data|blob):/i.test(filePath);
};
const buildQiniuFilePath = (filePath = "") => {
const cleanFilePath = String(filePath || "").trim();
if (!cleanFilePath || hasFilePathPrefix(cleanFilePath)) return cleanFilePath;
const qiniuDomain = window?.env?.FS_QINIU_DOMAIN || "";
const cleanDomain = String(qiniuDomain || "").replace(/\/+$/, "");
if (!cleanDomain) return cleanFilePath;
return `${cleanDomain}/${cleanFilePath.replace(/^\/+/, "")}`;
};
//[文件上下文] 上传文件只向智能体传递访问地址,不附加固定业务文案。
const buildUploadedFileReference = (fileList = []) => {
return fileList
.map((file) => {
const fileName = file?.name || file?.originalFilename || "未命名文件";
const rawFilePath = file?.url || file?.path || file?.objectKey || file?.localPath || "";
const filePath = buildQiniuFilePath(rawFilePath);
if (!filePath) return "";
return `(文件名:${fileName},文件路径:${filePath})`;
})
.filter(Boolean)
.join("\n");
};
const useSuperAgentChat = ({ const useSuperAgentChat = ({
currentUserId, currentUserId,
openAuthModal, openAuthModal,
@ -156,6 +142,7 @@ const useSuperAgentChat = ({
const streamingMessageKeyRef = useRef(""); const streamingMessageKeyRef = useRef("");
const chatRunPromiseRef = useRef(Promise.resolve()); const chatRunPromiseRef = useRef(Promise.resolve());
const chatRunActiveRef = useRef(false); const chatRunActiveRef = useRef(false);
const uploadAbortControllerRef = useRef(null);
const creatingSessionRef = useRef(false); const creatingSessionRef = useRef(false);
const sidebarRefreshTimerRef = useRef(null); const sidebarRefreshTimerRef = useRef(null);
const artifactProgressSessionRef = useRef(""); const artifactProgressSessionRef = useRef("");
@ -168,6 +155,33 @@ const useSuperAgentChat = ({
activeSessionIdRef.current = activeSessionId; activeSessionIdRef.current = activeSessionId;
}, [activeSessionId]); }, [activeSessionId]);
useEffect(() => {
const conversationId = String(activeConversationId || "");
if (!conversationId) {
setUploadedFileList([]);
return undefined;
}
let active = true;
listAttachments(conversationId)
.then((page) => {
if (!active) return;
setUploadedFileList(
page.items
.filter((attachment) => attachment.status === "ready" && !attachment.message_id)
.map(toReadyUploadedFile)
);
})
.catch((error) => {
superAgentWarn("chat.upload", "attachment_list_failed", {
conversationId,
error: error?.message,
});
});
return () => {
active = false;
};
}, [activeConversationId]);
const activeTask = useMemo( const activeTask = useMemo(
() => taskList.find((task) => task.id === activeTaskId) || null, () => taskList.find((task) => task.id === activeTaskId) || null,
[activeTaskId, taskList] [activeTaskId, taskList]
@ -357,6 +371,9 @@ const useSuperAgentChat = ({
if (chatAbortControllerRef.current) { if (chatAbortControllerRef.current) {
chatAbortControllerRef.current.abort(); chatAbortControllerRef.current.abort();
} }
if (uploadAbortControllerRef.current) {
uploadAbortControllerRef.current.abort();
}
if (sidebarRefreshTimerRef.current) { if (sidebarRefreshTimerRef.current) {
window.clearTimeout(sidebarRefreshTimerRef.current); window.clearTimeout(sidebarRefreshTimerRef.current);
} }
@ -398,6 +415,7 @@ const useSuperAgentChat = ({
folderId = null, folderId = null,
title = "", title = "",
shouldResetMessages = false, shouldResetMessages = false,
returnConversationId = false,
} = {}) => { } = {}) => {
if (!currentUserId) { if (!currentUserId) {
openAuthModal("login"); openAuthModal("login");
@ -469,7 +487,7 @@ const useSuperAgentChat = ({
} }
await loadSidebar({ silent: true }); await loadSidebar({ silent: true });
return nextSessionId; return returnConversationId ? nextSessionId : nextThreadId;
}; };
const getCurrentSessionType = () => { const getCurrentSessionType = () => {
@ -1545,14 +1563,14 @@ const useSuperAgentChat = ({
//[执行上传文件的实际逻辑] //[执行上传文件的实际逻辑]
const performUpload = (file, fileName, currentSessionType) => { const performUpload = (file, fileName, currentSessionType) => {
const currentSessionId = activeSessionId || activeTaskId; const currentConversationId = activeConversationId || activeTaskId;
const shouldCreateSession = !currentSessionId; const shouldCreateSession = !currentConversationId;
const shouldUseTaskSession = const shouldUseTaskSession =
currentSessionType === "task" || (shouldCreateSession && isTaskRoute); currentSessionType === "task" || (shouldCreateSession && isTaskRoute);
const shouldCreateNewTask = shouldCreateSession && shouldUseTaskSession; const shouldCreateNewTask = shouldCreateSession && shouldUseTaskSession;
superAgentLog("chat.upload", "perform.start", { superAgentLog("chat.upload", "perform.start", {
fileName, fileName,
currentSessionId, currentConversationId,
currentSessionType, currentSessionType,
shouldCreateSession, shouldCreateSession,
shouldUseTaskSession, shouldUseTaskSession,
@ -1583,23 +1601,32 @@ const useSuperAgentChat = ({
status: "preparing", status: "preparing",
}); });
try { try {
const sessionId = const conversationId =
currentSessionId || currentConversationId ||
(await createAndActivateSession({ (await createAndActivateSession({
isTaskSession: shouldUseTaskSession, isTaskSession: shouldUseTaskSession,
title: shouldUseTaskSession title: shouldUseTaskSession
? fileName.replace(/\.[^.]+$/, "") || "AI 标书任务" ? fileName.replace(/\.[^.]+$/, "") || "AI 标书任务"
: "", : "",
returnConversationId: true,
})); }));
superAgentLog("chat.upload", "session.ready", { superAgentLog("chat.upload", "session.ready", {
sessionId, conversationId,
fileName, fileName,
shouldUseTaskSession, shouldUseTaskSession,
}); });
const uploadResult = await uploadSuperAgentFile({ const sha256 = await calculateFileSha256(file);
sessionId, const uploadIntent = normalizeAttachmentUploadIntent(
userId: currentUserId, await createAttachmentUploadIntent(conversationId, {
file, file_name: fileName,
mime_type: file.type || "application/octet-stream",
file_size: Number(file.size || 0),
...(sha256 ? { sha256 } : {}),
})
);
uploadAbortControllerRef.current = new AbortController();
await uploadFileToIntent(uploadIntent, file, {
signal: uploadAbortControllerRef.current.signal,
onProgress: (progress) => { onProgress: (progress) => {
setUploadProgress({ setUploadProgress({
fileName, fileName,
@ -1613,57 +1640,36 @@ const useSuperAgentChat = ({
percent: 100, percent: 100,
status: "processing", status: "processing",
})); }));
const uploadData = uploadResult?.data || uploadResult || {}; const uploadData = await completeAttachmentUpload(uploadIntent.attachmentId);
try { const attachmentPage = await listAttachments(conversationId);
await createResourceFileFromUpload(uploadData); const readyAttachment = attachmentPage.items.find((attachment) =>
} catch (resourceError) { String(attachment.id || attachment.attachment_id || attachment.attachmentId) === uploadIntent.attachmentId
superAgentWarn("chat.upload", "resource_library_save_failed", { ) || uploadData?.data || uploadData || {};
fileName, if (readyAttachment.status !== "ready") {
error: resourceError?.message, throw new Error("附件尚未准备完成,请稍后重试");
});
message.warning("文件已上传,但保存到资源库失败");
} }
const uploadedFileName = const uploadedFileName =
uploadData.filename || readyAttachment.file_name ||
uploadData.original_filename || readyAttachment.filename ||
uploadData.originalFilename || readyAttachment.original_filename ||
readyAttachment.originalFilename ||
fileName; fileName;
const rawUploadedFilePath =
uploadData.path ||
uploadData.url ||
uploadData.object_key ||
uploadData.objectKey ||
"";
const uploadedFilePath = buildQiniuFilePath(rawUploadedFilePath);
const uploadedLocalPath = uploadData.local_path || uploadData.localPath || "";
const uploadedFileStorage = uploadData.storage || "";
const normalizedUploadedFileStorage =
uploadedFileStorage || uploadData.storage_provider || uploadData.storageProvider || "";
superAgentLog("chat.upload", "upload.done", { superAgentLog("chat.upload", "upload.done", {
sessionId, conversationId,
uploadedFileName, uploadedFileName,
uploadedFilePath, attachmentId: uploadIntent.attachmentId,
uploadedLocalPath, });
uploadedFileStorage: normalizedUploadedFileStorage, const uploadedFile = toReadyUploadedFile({
uploadResult, ...readyAttachment,
id: uploadIntent.attachmentId,
file_name: uploadedFileName,
status: "ready",
}); });
const uploadedFile = {
id: `${sessionId}_${uploadedFileName}_${Date.now()}`,
attachmentId: uploadData.id || "",
name: uploadedFileName,
url: uploadedFilePath,
objectKey: uploadData.object_key || uploadData.objectKey || "",
localPath: uploadedLocalPath,
storage: normalizedUploadedFileStorage,
};
if (shouldCreateNewTask) { if (shouldCreateNewTask) {
updateActiveTask({ updateActiveTask({
title: uploadedFileName.replace(/\.[^.]+$/, "") || "AI 标书任务", title: uploadedFileName.replace(/\.[^.]+$/, "") || "AI 标书任务",
fileName: uploadedFileName, fileName: uploadedFileName,
filePath: uploadedFilePath,
localPath: uploadedLocalPath,
fileStorage: normalizedUploadedFileStorage,
}); });
} }
@ -1677,6 +1683,7 @@ const useSuperAgentChat = ({
}); });
message.error(error?.message || "上传文件失败"); message.error(error?.message || "上传文件失败");
} finally { } finally {
uploadAbortControllerRef.current = null;
setIsUploadingFile(false); setIsUploadingFile(false);
setUploadProgress(null); setUploadProgress(null);
} }
@ -1689,6 +1696,10 @@ const useSuperAgentChat = ({
setUploadedFileList((prev) => prev.filter((file) => file.id !== fileId)); setUploadedFileList((prev) => prev.filter((file) => file.id !== fileId));
}; };
const handleCancelUpload = () => {
uploadAbortControllerRef.current?.abort();
};
const confirmBasicInfo = () => { const confirmBasicInfo = () => {
superAgentLog("chat.confirm", "basic_info", { activeSessionId, activeTaskId }); superAgentLog("chat.confirm", "basic_info", { activeSessionId, activeTaskId });
setTaskStage(TASK_STAGE.SCORE); setTaskStage(TASK_STAGE.SCORE);
@ -1727,8 +1738,9 @@ const useSuperAgentChat = ({
const handleSend = () => { const handleSend = () => {
const userInput = inputValue.trim(); const userInput = inputValue.trim();
const currentFileList = uploadedFileList; const currentFileList = uploadedFileList;
const fileReferenceText = buildUploadedFileReference(currentFileList); const finalMessage = userInput || (currentFileList.length
const finalMessage = [fileReferenceText, userInput].filter(Boolean).join("\n"); ? `请处理已上传的附件:${currentFileList.map((file) => file.name).filter(Boolean).join("、")}`
: "");
if (!finalMessage || isStreamingAnswer) return; if (!finalMessage || isStreamingAnswer) return;
@ -1770,7 +1782,10 @@ const useSuperAgentChat = ({
//[文件引用写入历史展示字段,刷新或切换会话后才能还原文件标签] //[文件引用写入历史展示字段,刷新或切换会话后才能还原文件标签]
displayContent: finalMessage, displayContent: finalMessage,
attachmentIds: Array.from( attachmentIds: Array.from(
new Set(currentFileList.map((file) => file.attachmentId).filter(Boolean)) new Set(currentFileList
.filter((file) => file.status === "ready")
.map((file) => file.attachmentId)
.filter(Boolean))
), ),
}); });
}; };
@ -1887,6 +1902,7 @@ const useSuperAgentChat = ({
handleTenderQuickStart, handleTenderQuickStart,
handleUploadFile, handleUploadFile,
handleRemoveUploadedFile, handleRemoveUploadedFile,
handleCancelUpload,
confirmBasicInfo, confirmBasicInfo,
confirmScoreItems, confirmScoreItems,
confirmCatalog, confirmCatalog,

94
web/client/src/sections/superAgent/utils/attachmentUpload.js

@ -0,0 +1,94 @@
/**
* Attachment upload primitives for the Agent upload-intent protocol.
*/
"use strict";
const getPayload = (value) => value?.data ?? value ?? {};
export const calculateFileSha256 = async (file) => {
if (!file?.arrayBuffer || !globalThis.crypto?.subtle?.digest) return "";
const buffer = await file.arrayBuffer();
const digest = await globalThis.crypto.subtle.digest("SHA-256", buffer);
return Array.from(new Uint8Array(digest))
.map((byte) => byte.toString(16).padStart(2, "0"))
.join("");
};
export const normalizeAttachmentUploadIntent = (response) => {
const payload = getPayload(response);
const upload = getPayload(payload.upload);
const attachment = getPayload(payload.attachment);
const attachmentId = attachment.id || payload.attachment_id || payload.attachmentId || payload.id || "";
const method = String(upload.method || "").toUpperCase();
if (!attachmentId || !upload.url || !["POST", "PUT"].includes(method)) {
throw new Error("上传意图响应不完整");
}
return {
attachmentId: String(attachmentId),
expiresAt: upload.expires_at || upload.expiresAt || payload.expires_at || payload.expiresAt || "",
upload: {
method,
url: upload.url,
fields: upload.fields && typeof upload.fields === "object" ? upload.fields : {},
headers: upload.headers && typeof upload.headers === "object" ? upload.headers : {},
},
};
};
export const isUploadIntentExpired = (intent, now = Date.now()) => {
if (!intent?.expiresAt) return false;
const expiresAt = Date.parse(intent.expiresAt);
return Number.isFinite(expiresAt) && expiresAt <= now;
};
export const uploadFileToIntent = (intent, file, { onProgress, signal } = {}) => {
if (isUploadIntentExpired(intent)) {
return Promise.reject(new Error("上传凭证已过期,请重新选择文件"));
}
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
const abort = () => xhr.abort();
xhr.open(intent.upload.method, intent.upload.url);
Object.entries(intent.upload.headers || {}).forEach(([key, value]) => {
if (value !== undefined && value !== null && value !== "") {
xhr.setRequestHeader(key, String(value));
}
});
xhr.upload.onprogress = (event) => {
const total = Number(event.total || file?.size || 0);
const loaded = Number(event.loaded || 0);
onProgress?.({
loaded,
total,
percent: total > 0 ? Math.min(100, Math.round((loaded / total) * 100)) : 0,
});
};
xhr.onerror = () => reject(new Error("上传文件失败,请检查网络连接"));
xhr.onabort = () => reject(new Error("文件上传已取消"));
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve();
return;
}
reject(new Error(`文件直传失败(${xhr.status || "网络错误"})`));
};
signal?.addEventListener("abort", abort, { once: true });
if (signal?.aborted) {
abort();
return;
}
if (intent.upload.method === "POST") {
const formData = new FormData();
Object.entries(intent.upload.fields || {}).forEach(([key, value]) => {
formData.append(key, String(value));
});
formData.append("file", file);
xhr.send(formData);
return;
}
xhr.send(file);
});
};

33
web/tests/agentApiAdapter.test.mjs

@ -15,6 +15,10 @@ const {
shouldSendIdempotencyKey, shouldSendIdempotencyKey,
} = await import("../client/src/sections/superAgent/utils/agentApiParse.js"); } = await import("../client/src/sections/superAgent/utils/agentApiParse.js");
const { superAgentServiceApiTable } = await import("../client/src/utils/superAgentApiTable.js"); const { superAgentServiceApiTable } = await import("../client/src/utils/superAgentApiTable.js");
const {
isUploadIntentExpired,
normalizeAttachmentUploadIntent,
} = await import("../client/src/sections/superAgent/utils/attachmentUpload.js");
test("new api table uses live agent paths", () => { test("new api table uses live agent paths", () => {
assert.equal(superAgentServiceApiTable.tasksets, "/tasksets"); assert.equal(superAgentServiceApiTable.tasksets, "/tasksets");
@ -54,6 +58,23 @@ test("uuid and page helpers normalize agent payloads", () => {
assert.equal(shouldSendIdempotencyKey("GET", "k"), false); assert.equal(shouldSendIdempotencyKey("GET", "k"), false);
}); });
test("attachment upload intent follows the v2 three-stage payload", () => {
const intent = normalizeAttachmentUploadIntent({
attachment: { id: "2c1f3c0e-1a2b-4c5d-8e9f-0123456789ab" },
upload: {
method: "POST",
url: "https://upload.example.test",
fields: { token: "opaque", key: "uploads/file.txt" },
expires_at: "2030-01-01T00:00:00Z",
},
});
assert.equal(intent.attachmentId, "2c1f3c0e-1a2b-4c5d-8e9f-0123456789ab");
assert.equal(intent.upload.method, "POST");
assert.equal(intent.upload.fields.key, "uploads/file.txt");
assert.equal(isUploadIntentExpired(intent, Date.parse("2029-01-01T00:00:00Z")), false);
assert.equal(isUploadIntentExpired(intent, Date.parse("2031-01-01T00:00:00Z")), true);
});
test("adapter source exchanges login token for agent jwt and omits identity headers", () => { test("adapter source exchanges login token for agent jwt and omits identity headers", () => {
const table = read("client", "src", "utils", "superAgentApiTable.js"); const table = read("client", "src", "utils", "superAgentApiTable.js");
const agentApi = read("client", "src", "sections", "superAgent", "utils", "agentApi.js"); const agentApi = read("client", "src", "sections", "superAgent", "utils", "agentApi.js");
@ -61,6 +82,8 @@ test("adapter source exchanges login token for agent jwt and omits identity head
const protocol = read("..", "api", "app", "controllers", "superAgentProtocol.js"); const protocol = read("..", "api", "app", "controllers", "superAgentProtocol.js");
const actions = read("client", "src", "sections", "superAgent", "actions", "api.js"); const actions = read("client", "src", "sections", "superAgent", "actions", "api.js");
const resources = read("client", "src", "sections", "superAgent", "actions", "agentResources.js"); const resources = read("client", "src", "sections", "superAgent", "actions", "agentResources.js");
const chatHook = read("client", "src", "sections", "superAgent", "hooks", "useSuperAgentChat.js");
const uploadUtils = read("client", "src", "sections", "superAgent", "utils", "attachmentUpload.js");
assert.doesNotMatch(table, /\/api\/v1\/tasks/); assert.doesNotMatch(table, /\/api\/v1\/tasks/);
assert.match(agentApi, /ACCESS_TOKEN_PATH = "\/_api\/super-agent\/access-token"/); assert.match(agentApi, /ACCESS_TOKEN_PATH = "\/_api\/super-agent\/access-token"/);
assert.match(agentApi, /Authorization: `Bearer \$\{token\}`/); assert.match(agentApi, /Authorization: `Bearer \$\{token\}`/);
@ -75,5 +98,15 @@ test("adapter source exchanges login token for agent jwt and omits identity head
assert.match(protocol, /\/threads\/\$\{encodeURIComponent/); assert.match(protocol, /\/threads\/\$\{encodeURIComponent/);
assert.match(actions, /requestAgentJson/); assert.match(actions, /requestAgentJson/);
assert.match(resources, /createAttachmentUploadIntent/); assert.match(resources, /createAttachmentUploadIntent/);
assert.match(resources, /completeAttachmentUpload/);
assert.match(resources, /listAttachments/);
assert.match(chatHook, /createAttachmentUploadIntent\(conversationId/);
assert.match(chatHook, /completeAttachmentUpload\(uploadIntent\.attachmentId\)/);
assert.match(chatHook, /listAttachments\(conversationId\)/);
assert.match(chatHook, /attachment\.status === "ready" && !attachment\.message_id/);
assert.match(chatHook, /handleCancelUpload/);
assert.doesNotMatch(chatHook, /uploadSuperAgentFile/);
assert.match(uploadUtils, /FormData/);
assert.match(uploadUtils, /xhr\.send\(file\)/);
assert.match(resources, /cancelThreadRun/); assert.match(resources, /cancelThreadRun/);
}); });

Loading…
Cancel
Save