Add per-feature Cursor channel toggles for admin and runtime.
Memind CI / Test, build, and release guards (push) Has been cancelled

Expose page/data/scheduled-task/chat-bridge switches in the智趣体验通道 config so Tang can roll out Cursor paths independently with DeepSeek fallback.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-08-31 09:40:52 +08:00
parent ac2c87be43
commit 617ff0d1dd
21 changed files with 915 additions and 127 deletions
+92 -35
View File
@@ -14,6 +14,8 @@ import {
materializeMissingPublicHtmlWrites,
} from './mindspace-public-finish-sync.mjs';
import { localDateLabel } from './schedule-time.mjs';
import { executeCursorChannelCodeRun } from './wechat-cursor-agent-run.mjs';
import { resolveCursorScheduledTaskEligible } from './wechat-cursor-executor-policy.mjs';
function messageText(message) {
if (typeof message?.content === 'string') return message.content.trim();
@@ -529,6 +531,8 @@ export async function resendScheduledTaskWechatForReadyPage({
export async function executeScheduledTask(task, {
userAuth,
tkmindProxy,
agentRunGateway = null,
cursorExecutorPolicyService = null,
sessionSnapshotService = null,
pool = null,
h5Root = null,
@@ -539,13 +543,6 @@ export async function executeScheduledTask(task, {
if (!userAuth || typeof userAuth.canUseChat !== 'function') {
throw new Error('缺少 userAuth.canUseChat');
}
if (
!tkmindProxy
|| typeof tkmindProxy.startSessionForUser !== 'function'
|| typeof tkmindProxy.submitSessionReplyAndAwaitFinishForUser !== 'function'
) {
throw new Error('缺少 tkmindProxy 会话执行能力');
}
const gate = await userAuth.canUseChat(task.userId);
if (!gate?.ok) {
@@ -555,38 +552,97 @@ export async function executeScheduledTask(task, {
}
const requestId = crypto.randomUUID();
const started = await tkmindProxy.startSessionForUser(task.userId, {
origin: 'h5',
});
const sessionId = started?.id ?? started?.sessionId;
if (!sessionId) throw new Error('创建定时任务会话失败');
const userMessage = buildScheduledTaskExecutionPrompt(task);
logger.info?.('[ScheduledTask] executing', {
taskId: task.id,
userId: task.userId,
sessionId,
requestId,
});
await tkmindProxy.submitSessionReplyAndAwaitFinishForUser(
task.userId,
sessionId,
requestId,
userMessage,
{ timeoutMs },
);
let messages = await refreshScheduledTaskMessages({
userId: task.userId,
sessionId,
tkmindProxy,
sessionSnapshotService,
});
const publishDir = h5Root && task.userId
? path.join(h5Root, 'MindSpace', task.userId)
: null;
let cursorPolicy = null;
let useCursorPath = false;
if (agentRunGateway && cursorExecutorPolicyService?.getEffectivePolicy) {
try {
cursorPolicy = await cursorExecutorPolicyService.getEffectivePolicy(task.userId, {
userId: task.userId,
});
useCursorPath = resolveCursorScheduledTaskEligible({
user: { userId: task.userId },
policy: cursorPolicy,
});
} catch (err) {
logger.warn?.('[ScheduledTask] cursor policy lookup failed:', err);
}
}
let sessionId = null;
let messages = [];
if (useCursorPath) {
logger.info?.('[ScheduledTask] executing via cursor channel', {
taskId: task.id,
userId: task.userId,
requestId,
});
try {
const cursorResult = await executeCursorChannelCodeRun({
agentRunGateway,
userId: task.userId,
requestId,
displayText: `定时任务:${task.title}`,
agentPrompt: userMessage.content[0]?.text ?? userMessage.content,
intentKind: 'page.generate',
channel: 'scheduled_task',
taskType: 'h5_chat_code_task',
policy: cursorPolicy,
forceCursorExecutor: true,
timeoutMs,
logger,
});
messages = cursorResult.messages ?? [];
sessionId = cursorResult.runId ?? null;
} catch (cursorErr) {
if (cursorPolicy?.fallbackToDeepseek === false) throw cursorErr;
logger.warn?.('[ScheduledTask] cursor execution failed, falling back to goose:', cursorErr);
useCursorPath = false;
}
}
if (!useCursorPath) {
if (
!tkmindProxy
|| typeof tkmindProxy.startSessionForUser !== 'function'
|| typeof tkmindProxy.submitSessionReplyAndAwaitFinishForUser !== 'function'
) {
throw new Error('缺少 tkmindProxy 会话执行能力');
}
const started = await tkmindProxy.startSessionForUser(task.userId, {
origin: 'h5',
});
sessionId = started?.id ?? started?.sessionId;
if (!sessionId) throw new Error('创建定时任务会话失败');
logger.info?.('[ScheduledTask] executing via goose session', {
taskId: task.id,
userId: task.userId,
sessionId,
requestId,
});
await tkmindProxy.submitSessionReplyAndAwaitFinishForUser(
task.userId,
sessionId,
requestId,
userMessage,
{ timeoutMs },
);
messages = await refreshScheduledTaskMessages({
userId: task.userId,
sessionId,
tkmindProxy,
sessionSnapshotService,
});
}
let deliveryText = extractScheduledTaskDeliveryText(messages, task);
const deliveryResult = publishDir
? await awaitScheduledTaskPageDelivery({
@@ -624,5 +680,6 @@ export async function executeScheduledTask(task, {
deliveryText,
messages,
readyPaths,
executor: useCursorPath ? 'cursor' : 'goose',
};
}