feat: gate code agent runs server side
This commit is contained in:
@@ -20,7 +20,9 @@ H5_PUBLIC_BASE_URL=http://127.0.0.1:5173
|
||||
# MEMIND_RUNTIME_REDIS_NAMESPACE=memind:runtime
|
||||
|
||||
# Code-tool agent run 灰度开关(默认关闭)。
|
||||
# 后端必须先开启 MEMIND_AGENT_CODE_RUNS_ENABLED,H5 构建开关才会生效。
|
||||
# 开启后,页面编辑子聊天会传 tool_mode=code;普通聊天仍需同时开启 AUTODETECT 才会按文本自动识别代码任务。
|
||||
# MEMIND_AGENT_CODE_RUNS_ENABLED=0
|
||||
# VITE_AGENT_CODE_RUNS_ENABLED=0
|
||||
# VITE_AGENT_CODE_RUNS_AUTODETECT=0
|
||||
|
||||
|
||||
@@ -140,6 +140,8 @@ const toolGrants = await queryToolGrants().catch((err) => ({
|
||||
}));
|
||||
|
||||
const buildFlags = {
|
||||
serverCodeRunsEnabledEnv: process.env.MEMIND_AGENT_CODE_RUNS_ENABLED ?? null,
|
||||
serverCodeRunsEnabledTruthy: truthy(process.env.MEMIND_AGENT_CODE_RUNS_ENABLED),
|
||||
enabledEnv: process.env.VITE_AGENT_CODE_RUNS_ENABLED ?? null,
|
||||
autodetectEnv: process.env.VITE_AGENT_CODE_RUNS_AUTODETECT ?? null,
|
||||
enabledTruthy: truthy(process.env.VITE_AGENT_CODE_RUNS_ENABLED),
|
||||
@@ -157,6 +159,7 @@ const ok = Boolean(
|
||||
runtimePolicy.defaultMode === 'chat' &&
|
||||
runtimePolicy.codeToolMode === 'code' &&
|
||||
runtimePolicy.chatInjectsCodeTools === false &&
|
||||
runtimePolicy.codeRunsEnabled === buildFlags.serverCodeRunsEnabledTruthy &&
|
||||
roleDefaults.aider === false &&
|
||||
roleDefaults.openhands === false &&
|
||||
(toolGrants.sampledUsers ?? []).every((user) => user.chatHasCodeTools === false) &&
|
||||
|
||||
@@ -6744,7 +6744,14 @@ function createAgentRunGateway({
|
||||
}
|
||||
|
||||
// agent-run-routes.mjs
|
||||
function createPostAgentRunsHandler({ userAuth: userAuth2, agentRunGateway: agentRunGateway2 }) {
|
||||
function envFlag(value) {
|
||||
return ["1", "true", "yes", "on"].includes(String(value ?? "").trim().toLowerCase());
|
||||
}
|
||||
function createPostAgentRunsHandler({
|
||||
userAuth: userAuth2,
|
||||
agentRunGateway: agentRunGateway2,
|
||||
codeRunsEnabled = envFlag(process.env.MEMIND_AGENT_CODE_RUNS_ENABLED)
|
||||
}) {
|
||||
return async function postAgentRuns(request, response) {
|
||||
try {
|
||||
const sessionId = String(request.body?.session_id ?? "").trim() || null;
|
||||
@@ -6769,6 +6776,10 @@ function createPostAgentRunsHandler({ userAuth: userAuth2, agentRunGateway: agen
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (toolMode === "code" && !codeRunsEnabled) {
|
||||
response.status(403).json({ message: "\u4EE3\u7801\u4EFB\u52A1\u7070\u5EA6\u672A\u5F00\u542F" });
|
||||
return;
|
||||
}
|
||||
if (sessionId) {
|
||||
const owns = await userAuth2.ownsSession(request.currentUser.id, sessionId);
|
||||
if (!owns) {
|
||||
@@ -9338,6 +9349,9 @@ function createTkmindProxy({
|
||||
defaultMode: "chat",
|
||||
codeToolMode: "code",
|
||||
chatInjectsCodeTools: false,
|
||||
codeRunsEnabled: ["1", "true", "yes", "on"].includes(
|
||||
String(process.env.MEMIND_AGENT_CODE_RUNS_ENABLED ?? "").trim().toLowerCase()
|
||||
),
|
||||
aiderTimeoutMs: Number(process.env.MEMIND_AIDER_TIMEOUT_MS ?? 6e5),
|
||||
openhandsTimeoutMs: Number(process.env.MEMIND_OPENHANDS_TIMEOUT_MS ?? 9e5)
|
||||
},
|
||||
|
||||
+13
-1
@@ -1,6 +1,14 @@
|
||||
import { normalizeAgentRunToolMode } from './agent-run-gateway.mjs';
|
||||
|
||||
export function createPostAgentRunsHandler({ userAuth, agentRunGateway }) {
|
||||
function envFlag(value) {
|
||||
return ['1', 'true', 'yes', 'on'].includes(String(value ?? '').trim().toLowerCase());
|
||||
}
|
||||
|
||||
export function createPostAgentRunsHandler({
|
||||
userAuth,
|
||||
agentRunGateway,
|
||||
codeRunsEnabled = envFlag(process.env.MEMIND_AGENT_CODE_RUNS_ENABLED),
|
||||
}) {
|
||||
return async function postAgentRuns(request, response) {
|
||||
try {
|
||||
const sessionId = String(request.body?.session_id ?? '').trim() || null;
|
||||
@@ -25,6 +33,10 @@ export function createPostAgentRunsHandler({ userAuth, agentRunGateway }) {
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (toolMode === 'code' && !codeRunsEnabled) {
|
||||
response.status(403).json({ message: '代码任务灰度未开启' });
|
||||
return;
|
||||
}
|
||||
if (sessionId) {
|
||||
const owns = await userAuth.ownsSession(request.currentUser.id, sessionId);
|
||||
if (!owns) {
|
||||
|
||||
@@ -114,6 +114,7 @@ test('POST /agent/runs accepts explicit code tool mode and task type', async ()
|
||||
return { id: 'run-code', status: 'queued' };
|
||||
},
|
||||
},
|
||||
codeRunsEnabled: true,
|
||||
});
|
||||
const res = createResponseRecorder();
|
||||
|
||||
@@ -141,6 +142,39 @@ test('POST /agent/runs accepts explicit code tool mode and task type', async ()
|
||||
});
|
||||
});
|
||||
|
||||
test('POST /agent/runs rejects code tool mode when server gate is disabled', async () => {
|
||||
const handler = createPostAgentRunsHandler({
|
||||
userAuth: {
|
||||
async ownsSession() {
|
||||
throw new Error('should not check ownership after disabled code mode');
|
||||
},
|
||||
},
|
||||
agentRunGateway: {
|
||||
async createRun() {
|
||||
throw new Error('should not be called');
|
||||
},
|
||||
},
|
||||
codeRunsEnabled: false,
|
||||
});
|
||||
const res = createResponseRecorder();
|
||||
|
||||
await handler(
|
||||
{
|
||||
currentUser: { id: 'user-1' },
|
||||
body: {
|
||||
session_id: 'session-1',
|
||||
request_id: 'req-code',
|
||||
user_message: { role: 'user', content: [] },
|
||||
tool_mode: 'code',
|
||||
},
|
||||
},
|
||||
res,
|
||||
);
|
||||
|
||||
assert.equal(res.statusCode, 403);
|
||||
assert.deepEqual(res.body, { message: '代码任务灰度未开启' });
|
||||
});
|
||||
|
||||
test('POST /agent/runs rejects unsupported tool mode', async () => {
|
||||
const handler = createPostAgentRunsHandler({
|
||||
userAuth: {
|
||||
|
||||
@@ -140,6 +140,8 @@ const toolGrants = await queryToolGrants().catch((err) => ({
|
||||
}));
|
||||
|
||||
const buildFlags = {
|
||||
serverCodeRunsEnabledEnv: process.env.MEMIND_AGENT_CODE_RUNS_ENABLED ?? null,
|
||||
serverCodeRunsEnabledTruthy: truthy(process.env.MEMIND_AGENT_CODE_RUNS_ENABLED),
|
||||
enabledEnv: process.env.VITE_AGENT_CODE_RUNS_ENABLED ?? null,
|
||||
autodetectEnv: process.env.VITE_AGENT_CODE_RUNS_AUTODETECT ?? null,
|
||||
enabledTruthy: truthy(process.env.VITE_AGENT_CODE_RUNS_ENABLED),
|
||||
@@ -157,6 +159,7 @@ const ok = Boolean(
|
||||
runtimePolicy.defaultMode === 'chat' &&
|
||||
runtimePolicy.codeToolMode === 'code' &&
|
||||
runtimePolicy.chatInjectsCodeTools === false &&
|
||||
runtimePolicy.codeRunsEnabled === buildFlags.serverCodeRunsEnabledTruthy &&
|
||||
roleDefaults.aider === false &&
|
||||
roleDefaults.openhands === false &&
|
||||
(toolGrants.sampledUsers ?? []).every((user) => user.chatHasCodeTools === false) &&
|
||||
|
||||
@@ -912,6 +912,9 @@ export function createTkmindProxy({
|
||||
defaultMode: 'chat',
|
||||
codeToolMode: 'code',
|
||||
chatInjectsCodeTools: false,
|
||||
codeRunsEnabled: ['1', 'true', 'yes', 'on'].includes(
|
||||
String(process.env.MEMIND_AGENT_CODE_RUNS_ENABLED ?? '').trim().toLowerCase(),
|
||||
),
|
||||
aiderTimeoutMs: Number(process.env.MEMIND_AIDER_TIMEOUT_MS ?? 600_000),
|
||||
openhandsTimeoutMs: Number(process.env.MEMIND_OPENHANDS_TIMEOUT_MS ?? 900_000),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user