|
|
|
|
@@ -1,7 +1,6 @@
|
|
|
|
|
import { randomBytes } from "node:crypto";
|
|
|
|
|
import {
|
|
|
|
|
AUTH_SESSION_TTL_MS,
|
|
|
|
|
aiRoleLabel,
|
|
|
|
|
aiProviderLabel,
|
|
|
|
|
appendProjectMessage,
|
|
|
|
|
completeMasterAgentTask,
|
|
|
|
|
@@ -70,7 +69,7 @@ const CLAW_RUNTIME_DEVICE_ID = "master-agent-claw";
|
|
|
|
|
|
|
|
|
|
type ApiCompatibleProvider = Extract<
|
|
|
|
|
AiProvider,
|
|
|
|
|
"openai_api" | "aliyun_qwen_api" | "minimax_api" | "glm_api" | "hyzq_api" | "custom_api"
|
|
|
|
|
"openai_api" | "aliyun_qwen_api" | "deepseek_api" | "minimax_api" | "glm_api" | "hyzq_api" | "custom_api"
|
|
|
|
|
>;
|
|
|
|
|
|
|
|
|
|
const API_PROVIDER_CONFIG: Record<
|
|
|
|
|
@@ -97,6 +96,13 @@ const API_PROVIDER_CONFIG: Record<
|
|
|
|
|
loginLabel: "阿里百炼 API Key",
|
|
|
|
|
protocol: "responses",
|
|
|
|
|
},
|
|
|
|
|
deepseek_api: {
|
|
|
|
|
label: "DeepSeek API",
|
|
|
|
|
defaultBaseUrl: "https://api.deepseek.com",
|
|
|
|
|
defaultModel: "deepseek-v4-pro",
|
|
|
|
|
loginLabel: "DeepSeek API Key",
|
|
|
|
|
protocol: "chat_completions",
|
|
|
|
|
},
|
|
|
|
|
minimax_api: {
|
|
|
|
|
label: "MiniMax API",
|
|
|
|
|
defaultBaseUrl: "https://api.minimaxi.com/v1",
|
|
|
|
|
@@ -130,6 +136,7 @@ const API_PROVIDER_CONFIG: Record<
|
|
|
|
|
const API_PROVIDER_MODEL_OPTIONS: Record<ApiCompatibleProvider, string[]> = {
|
|
|
|
|
openai_api: ["gpt-5.4-mini", "gpt-5.4", "gpt-5.1", "gpt-4.1"],
|
|
|
|
|
aliyun_qwen_api: ["qwen3.5-plus", "qwen3.5-flash"],
|
|
|
|
|
deepseek_api: ["deepseek-v4-pro", "deepseek-v4-flash"],
|
|
|
|
|
minimax_api: ["MiniMax-M1"],
|
|
|
|
|
glm_api: ["glm-4.5"],
|
|
|
|
|
hyzq_api: ["gpt-5.4-mini", "gpt-5.4"],
|
|
|
|
|
@@ -139,6 +146,7 @@ const API_PROVIDER_MODEL_OPTIONS: Record<ApiCompatibleProvider, string[]> = {
|
|
|
|
|
const API_EXECUTION_PROVIDER_PRIORITY: ApiCompatibleProvider[] = [
|
|
|
|
|
"hyzq_api",
|
|
|
|
|
"openai_api",
|
|
|
|
|
"deepseek_api",
|
|
|
|
|
"aliyun_qwen_api",
|
|
|
|
|
"glm_api",
|
|
|
|
|
"minimax_api",
|
|
|
|
|
@@ -528,7 +536,7 @@ export async function resolveMasterAgentExecutionConfig(
|
|
|
|
|
const state = await readState();
|
|
|
|
|
const resolvedAccountId = accountId?.trim() || state.user.account || runtime.account.accountId;
|
|
|
|
|
const scopedAgentControls = await getProjectAgentControls(projectId, resolvedAccountId);
|
|
|
|
|
const modeResolution = resolveMasterAgentExecutionMode(scopedAgentControls, requestText);
|
|
|
|
|
const modeResolution = resolveMasterAgentExecutionMode(scopedAgentControls, requestText, runtime.account);
|
|
|
|
|
const reasoningEffort =
|
|
|
|
|
modeResolution.effectiveReasoningEffort ||
|
|
|
|
|
(runtime.account as typeof runtime.account & { reasoningEffort?: ReasoningEffort }).reasoningEffort ||
|
|
|
|
|
@@ -584,12 +592,44 @@ function normalizeAgentControlText(value?: string | null) {
|
|
|
|
|
return trimmed ? trimmed : undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function resolveConfiguredFastModel(agentControls?: ProjectAgentControls | null) {
|
|
|
|
|
return normalizeAgentControlText(agentControls?.fastModelOverride) || DEFAULT_FAST_MODEL;
|
|
|
|
|
function isProviderCompatibleModel(model: string | undefined, account?: AiAccount | null) {
|
|
|
|
|
if (!model || !account || !isApiCompatibleProvider(account.provider)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (account.provider !== "deepseek_api") {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
const modelOptions = API_PROVIDER_MODEL_OPTIONS[account.provider];
|
|
|
|
|
return modelOptions.length === 0 || modelOptions.includes(model);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function resolveConfiguredDeepModel(agentControls?: ProjectAgentControls | null) {
|
|
|
|
|
return normalizeAgentControlText(agentControls?.deepModelOverride) || DEFAULT_DEEP_MODEL;
|
|
|
|
|
function normalizeAgentControlModelForAccount(value?: string | null, account?: AiAccount | null) {
|
|
|
|
|
const model = normalizeAgentControlText(value);
|
|
|
|
|
return isProviderCompatibleModel(model, account) ? model : undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function defaultFastModelForAccount(account?: AiAccount | null) {
|
|
|
|
|
if (account && isApiCompatibleProvider(account.provider)) {
|
|
|
|
|
if (account.provider === "deepseek_api") return "deepseek-v4-flash";
|
|
|
|
|
if (account.provider === "aliyun_qwen_api") return "qwen3.5-flash";
|
|
|
|
|
return apiProviderConfig(account.provider).defaultModel;
|
|
|
|
|
}
|
|
|
|
|
return DEFAULT_FAST_MODEL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function defaultDeepModelForAccount(account?: AiAccount | null) {
|
|
|
|
|
if (account && isApiCompatibleProvider(account.provider)) {
|
|
|
|
|
return account.model || apiProviderConfig(account.provider).defaultModel;
|
|
|
|
|
}
|
|
|
|
|
return DEFAULT_DEEP_MODEL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function resolveConfiguredFastModel(agentControls?: ProjectAgentControls | null, account?: AiAccount | null) {
|
|
|
|
|
return normalizeAgentControlModelForAccount(agentControls?.fastModelOverride, account) || defaultFastModelForAccount(account);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function resolveConfiguredDeepModel(agentControls?: ProjectAgentControls | null, account?: AiAccount | null) {
|
|
|
|
|
return normalizeAgentControlModelForAccount(agentControls?.deepModelOverride, account) || defaultDeepModelForAccount(account);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function shouldAutoEscalateMasterAgentRequest(requestText?: string | null) {
|
|
|
|
|
@@ -623,12 +663,21 @@ export function shouldAutoEscalateMasterAgentRequest(requestText?: string | null
|
|
|
|
|
function resolveMasterAgentExecutionMode(
|
|
|
|
|
agentControls?: ProjectAgentControls | null,
|
|
|
|
|
requestText?: string | null,
|
|
|
|
|
account?: AiAccount | null,
|
|
|
|
|
): MasterAgentExecutionModeResolution {
|
|
|
|
|
const storedAgentControls = agentControls ?? null;
|
|
|
|
|
const currentModelOverride = normalizeAgentControlText(agentControls?.modelOverride);
|
|
|
|
|
const currentModelOverride = normalizeAgentControlModelForAccount(agentControls?.modelOverride, account);
|
|
|
|
|
const currentReasoningEffort = agentControls?.reasoningEffortOverride;
|
|
|
|
|
const fastModelOverride = resolveConfiguredFastModel(agentControls);
|
|
|
|
|
const deepModelOverride = resolveConfiguredDeepModel(agentControls);
|
|
|
|
|
const fastModelOverride = resolveConfiguredFastModel(agentControls, account);
|
|
|
|
|
const deepModelOverride = resolveConfiguredDeepModel(agentControls, account);
|
|
|
|
|
const effectiveStoredAgentControls: ProjectAgentControls | null = agentControls
|
|
|
|
|
? {
|
|
|
|
|
...agentControls,
|
|
|
|
|
modelOverride: currentModelOverride,
|
|
|
|
|
fastModelOverride,
|
|
|
|
|
deepModelOverride,
|
|
|
|
|
}
|
|
|
|
|
: null;
|
|
|
|
|
|
|
|
|
|
let activeMode: MasterAgentExecutionModeResolution["activeMode"] = "custom";
|
|
|
|
|
if (!currentModelOverride && !currentReasoningEffort) {
|
|
|
|
|
@@ -663,7 +712,7 @@ function resolveMasterAgentExecutionMode(
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
storedAgentControls,
|
|
|
|
|
effectiveAgentControls: storedAgentControls,
|
|
|
|
|
effectiveAgentControls: effectiveStoredAgentControls,
|
|
|
|
|
activeMode,
|
|
|
|
|
effectiveMode: activeMode,
|
|
|
|
|
fastPathEligible: activeMode === "fast",
|
|
|
|
|
@@ -1616,6 +1665,7 @@ function isApiCompatibleProvider(provider: AiProvider): provider is ApiCompatibl
|
|
|
|
|
return (
|
|
|
|
|
provider === "openai_api" ||
|
|
|
|
|
provider === "aliyun_qwen_api" ||
|
|
|
|
|
provider === "deepseek_api" ||
|
|
|
|
|
provider === "minimax_api" ||
|
|
|
|
|
provider === "glm_api" ||
|
|
|
|
|
provider === "hyzq_api" ||
|
|
|
|
|
@@ -2263,7 +2313,7 @@ async function queueAndStartOpenAiMasterAgentReply(params: {
|
|
|
|
|
taskId: params.taskId,
|
|
|
|
|
deviceId: candidate.deviceId,
|
|
|
|
|
accountId: candidate.account.accountId,
|
|
|
|
|
accountLabel: candidate.account.label,
|
|
|
|
|
accountLabel: candidate.model,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -2392,7 +2442,7 @@ async function enqueueOpenAiMasterAgentReply(params: {
|
|
|
|
|
requestedByAccount: params.requestedByAccount,
|
|
|
|
|
deviceId: primaryCandidate.deviceId,
|
|
|
|
|
accountId: primaryCandidate.account.accountId,
|
|
|
|
|
accountLabel: primaryCandidate.account.label,
|
|
|
|
|
accountLabel: primaryCandidate.model,
|
|
|
|
|
...params.taskAuthorization,
|
|
|
|
|
relayViaMasterAgent: params.relayViaMasterAgent,
|
|
|
|
|
externalReplyTarget: params.externalReplyTarget,
|
|
|
|
|
@@ -3683,6 +3733,8 @@ export async function replyToMasterAgentUserMessage(params: {
|
|
|
|
|
const replyMetadata = buildMasterAgentModeMetadata(modeResolution);
|
|
|
|
|
const controlIntent = classifyMasterAgentControlIntent(params.requestText);
|
|
|
|
|
const relayViaMasterAgent = params.interactionMode === "takeover_single_thread";
|
|
|
|
|
const forcePrimaryApiExecution =
|
|
|
|
|
isApiCompatibleProvider(runtime.account.provider) && Boolean(runtime.account.apiKey?.trim());
|
|
|
|
|
const selectedMasterAccount = await resolveMasterNodeExecutionCandidate({
|
|
|
|
|
backendChoices,
|
|
|
|
|
runtimeAccount: runtime.account,
|
|
|
|
|
@@ -3700,13 +3752,16 @@ export async function replyToMasterAgentUserMessage(params: {
|
|
|
|
|
modeResolution,
|
|
|
|
|
backendOverride: executionConfig.agentControls?.backendOverride,
|
|
|
|
|
});
|
|
|
|
|
const selectedReplyBackendId = forcePrimaryApiExecution && apiExecutionCandidates.length > 0
|
|
|
|
|
? "openai-api"
|
|
|
|
|
: selectedBackend.backendId;
|
|
|
|
|
const replyMode = resolveMasterAgentReplyMode({
|
|
|
|
|
requestedMode: params.mode,
|
|
|
|
|
selectedBackendId: preferApiExecutionForSmartMode && apiExecutionCandidates.length > 0
|
|
|
|
|
? apiExecutionCandidates[0]?.provider === "aliyun_qwen_api"
|
|
|
|
|
? "aliyun-qwen"
|
|
|
|
|
: "openai-api"
|
|
|
|
|
: selectedBackend.backendId,
|
|
|
|
|
: selectedReplyBackendId,
|
|
|
|
|
apiCandidateCount: apiExecutionCandidates.length,
|
|
|
|
|
modeResolution,
|
|
|
|
|
});
|
|
|
|
|
@@ -4019,7 +4074,7 @@ export async function replyToMasterAgentUserMessage(params: {
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
apiExecutionCandidates.length > 0 &&
|
|
|
|
|
(preferApiExecutionForSmartMode || selectedBackend.backendId !== "master-codex-node")
|
|
|
|
|
(forcePrimaryApiExecution || preferApiExecutionForSmartMode || selectedBackend.backendId !== "master-codex-node")
|
|
|
|
|
) {
|
|
|
|
|
const queuedReply = await enqueueOpenAiMasterAgentReply({
|
|
|
|
|
candidates: apiExecutionCandidates,
|
|
|
|
|
@@ -4080,7 +4135,7 @@ export async function replyToMasterAgentUserMessage(params: {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selectedBackend.backendId === "master-codex-node" && !preferApiExecutionForSmartMode) {
|
|
|
|
|
if (selectedBackend.backendId === "master-codex-node" && !preferApiExecutionForSmartMode && !forcePrimaryApiExecution) {
|
|
|
|
|
return runMasterNodeExecution();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -4093,7 +4148,7 @@ export async function replyToMasterAgentUserMessage(params: {
|
|
|
|
|
requestText: params.requestText,
|
|
|
|
|
projectId: replyProjectId,
|
|
|
|
|
currentSessionExpiresAt: params.currentSessionExpiresAt,
|
|
|
|
|
senderLabel: `主 Agent · ${candidate.account.label || aiRoleLabel(candidate.account.role)}`,
|
|
|
|
|
senderLabel: `主 Agent · ${candidate.model}`,
|
|
|
|
|
agentControls,
|
|
|
|
|
promptPolicy: executionConfig.promptPolicy,
|
|
|
|
|
userPrompt: executionConfig.userPrompt,
|
|
|
|
|
|