You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
156 lines
7.7 KiB
156 lines
7.7 KiB
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import test from "node:test";
|
|
|
|
const root = path.resolve(import.meta.dirname, "..");
|
|
const read = (...parts) => fs.readFileSync(path.join(root, ...parts), "utf8");
|
|
|
|
const {
|
|
SuperAgentApiError,
|
|
assertAgentUuid,
|
|
isAgentUuid,
|
|
parseAgentErrorPayload,
|
|
parseAgentPage,
|
|
getSuperAgentErrorMessage,
|
|
shouldSendIdempotencyKey,
|
|
} = await import("../client/src/sections/superAgent/utils/agentApiParse.js");
|
|
const {
|
|
superAgentFrameworkProxyMatch,
|
|
superAgentServiceProxyMatch,
|
|
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", () => {
|
|
assert.equal(superAgentServiceProxyMatch, "/_super-agent");
|
|
assert.equal(superAgentFrameworkProxyMatch, "/_super-agent-framework");
|
|
assert.equal(superAgentServiceApiTable.tasksets, "/tasksets");
|
|
assert.equal(superAgentServiceApiTable.conversations, "/conversations");
|
|
assert.equal(
|
|
superAgentServiceApiTable.attachmentUploadIntents,
|
|
"/conversations/:conversationId/attachments/upload-intents",
|
|
);
|
|
assert.equal(superAgentServiceApiTable.threadCommands, "/threads/:threadId/commands");
|
|
assert.equal(superAgentServiceApiTable.threadEvents, "/threads/:threadId/stream/events");
|
|
assert.equal(superAgentServiceApiTable.threadCancel, "/threads/:threadId/runs/:runId/cancel");
|
|
assert.equal(superAgentServiceApiTable.artifacts, "/conversations/:conversationId/artifacts");
|
|
assert.doesNotMatch(JSON.stringify(superAgentServiceApiTable), /\/api\/v1/);
|
|
});
|
|
|
|
test("uuid and page helpers normalize agent payloads", () => {
|
|
assert.equal(isAgentUuid("2c1f3c0e-1a2b-4c5d-8e9f-0123456789ab"), true);
|
|
assert.equal(isAgentUuid("task-1"), false);
|
|
assert.equal(assertAgentUuid("2c1f3c0e-1a2b-4c5d-8e9f-0123456789ab"), "2c1f3c0e-1a2b-4c5d-8e9f-0123456789ab");
|
|
assert.throws(
|
|
() => assertAgentUuid("task-1", "conversation_id"),
|
|
(error) => error instanceof SuperAgentApiError && error.status === 422,
|
|
);
|
|
assert.deepEqual(parseAgentPage({ items: [{ id: "a" }], next_cursor: "c1" }), {
|
|
items: [{ id: "a" }],
|
|
nextCursor: "c1",
|
|
});
|
|
assert.deepEqual(parseAgentPage({}), { items: [], nextCursor: null });
|
|
const error = parseAgentErrorPayload(
|
|
{ code: "AUTHENTICATION_REQUIRED", message: "missing", request_id: "req_1" },
|
|
401,
|
|
"fallback",
|
|
);
|
|
assert.equal(error.code, "AUTHENTICATION_REQUIRED");
|
|
assert.equal(error.requestId, "req_1");
|
|
assert.equal(shouldSendIdempotencyKey("POST", "k"), true);
|
|
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("agent errors keep request_id without exposing credentials", () => {
|
|
const error = parseAgentErrorPayload(
|
|
{ code: "UNAUTHORIZED", message: "expired", request_id: "req_auth_1" },
|
|
401,
|
|
"fallback",
|
|
);
|
|
assert.match(getSuperAgentErrorMessage(error), /换票失败/);
|
|
assert.match(getSuperAgentErrorMessage(error), /req_auth_1/);
|
|
});
|
|
|
|
test("adapter source exchanges login token for agent jwt and omits identity headers", () => {
|
|
const table = read("client", "src", "utils", "superAgentApiTable.js");
|
|
const agentApi = read("client", "src", "sections", "superAgent", "utils", "agentApi.js");
|
|
const streamHook = read("client", "src", "sections", "superAgent", "hooks", "useSuperAgentStream.js");
|
|
const protocol = read("..", "api", "app", "controllers", "superAgentProtocol.js");
|
|
const actions = read("client", "src", "sections", "superAgent", "actions", "api.js");
|
|
const resources = read("client", "src", "sections", "superAgent", "actions", "agentResources.js");
|
|
const config = read("config.cjs");
|
|
const chatHook = read("client", "src", "sections", "superAgent", "hooks", "useSuperAgentChat.js");
|
|
const uploadUtils = read("client", "src", "sections", "superAgent", "utils", "attachmentUpload.js");
|
|
const workspace = read("client", "src", "sections", "superAgent", "hooks", "useSuperAgentWorkspace.js");
|
|
assert.doesNotMatch(table, /\/api\/v1\/tasks/);
|
|
assert.match(agentApi, /ACCESS_TOKEN_PATH = "\/_api\/super-agent\/access-token"/);
|
|
assert.match(agentApi, /Authorization: `Bearer \$\{token\}`/);
|
|
assert.match(agentApi, /headers\.delete\("X-User-Id"\)/);
|
|
assert.match(agentApi, /headers\.delete\("X-Search-Token"\)/);
|
|
assert.match(agentApi, /Idempotency-Key/);
|
|
assert.match(streamHook, /withAgentAuthorization/);
|
|
assert.doesNotMatch(streamHook, /X-User-Id/);
|
|
assert.doesNotMatch(streamHook, /\/api\/v1/);
|
|
assert.match(protocol, /issueAgentAccessToken/);
|
|
assert.doesNotMatch(protocol, /'X-User-Id'/);
|
|
assert.match(protocol, /\/threads\/\$\{encodeURIComponent/);
|
|
assert.match(actions, /requestAgentJson/);
|
|
assert.match(resources, /createAttachmentUploadIntent/);
|
|
assert.match(resources, /service: "framework"/);
|
|
assert.match(config, /FS_SUPER_AGENT_BUSINESS_API/);
|
|
assert.match(config, /FS_SUPER_AGENT_FRAMEWORK_API/);
|
|
assert.match(config, /_super-agent-framework/);
|
|
assert.match(agentApi, /buildSuperAgentServiceUrl = \(path = "", service = superAgentBusinessService\)/);
|
|
assert.match(agentApi, /superAgentFrameworkProxyMatch/);
|
|
assert.match(agentApi, /getSuperAgentFrameworkApiUrl/);
|
|
assert.match(agentApi, /response\.status === 401 && retryOnUnauthorized/);
|
|
assert.match(streamHook, /fetchAuthorizedStream/);
|
|
assert.doesNotMatch(streamHook, /X-PEP-Token/);
|
|
assert.match(resources, /completeAttachmentUpload/);
|
|
assert.match(resources, /listAttachments/);
|
|
assert.match(resources, /getArtifactChapter/);
|
|
assert.match(resources, /returnMeta: true/);
|
|
assert.match(workspace, /listArtifacts\(resourceConversationId/);
|
|
assert.match(workspace, /listArtifactChapters/);
|
|
assert.match(workspace, /updateArtifactChapter/);
|
|
assert.match(resources, /If-Match/);
|
|
assert.doesNotMatch(workspace, /getSuperAgentSessionSectionsDirect/);
|
|
assert.doesNotMatch(workspace, /updateSuperAgentSessionSectionDirect/);
|
|
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/);
|
|
});
|
|
|
|
test("superAgent diagnostics redact message and file payload text", () => {
|
|
const debugLog = read("client", "src", "sections", "superAgent", "utils", "debugLog.js");
|
|
assert.doesNotMatch(debugLog, /preview:/);
|
|
assert.match(debugLog, /token|authorization/);
|
|
assert.match(debugLog, /requestId/);
|
|
});
|
|
|