Files
memind/wechat-cursor-agent-run.mjs
T
john 617ff0d1dd
Memind CI / Test, build, and release guards (push) Has been cancelled
Add per-feature Cursor channel toggles for admin and runtime.
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>
2026-08-31 09:40:52 +08:00

192 lines
5.2 KiB
JavaScript

import { buildCodeRunCompletionReply } from './agent-run-gateway.mjs';
import { enforcePageGenerationCursorRuntime } from './cursor-page-routing.mjs';
const DEFAULT_POLL_MS = 2000;
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function buildWechatCursorUserMessage({
displayText,
intentKind,
channel = 'wechat_mp',
}) {
const taskText = String(displayText ?? '').trim();
return {
role: 'user',
content: [{ type: 'text', text: taskText }],
metadata: {
displayText: taskText,
memindRun: {
channel,
wechatCursorChannel: channel === 'wechat_mp',
taskType: channel === 'wechat_mp' ? 'wechat_page_generate' : 'h5_chat_code_task',
toolMode: 'code',
requiredExecutor: 'cursor',
intentKind: String(intentKind ?? '').trim() || null,
},
},
};
}
async function readCursorCompletionText(agentRunGateway, userId, runId) {
const eventsResult = await agentRunGateway.listRunEventsForUser(userId, runId, { limit: 200 });
const events = Array.isArray(eventsResult?.events) ? eventsResult.events : [];
const resultEvent = [...events].reverse().find((item) => item.eventType === 'tool_gateway_result');
if (!resultEvent?.data) {
return buildCodeRunCompletionReply({ executor: 'cursor', stdout: '' });
}
return buildCodeRunCompletionReply({
executor: resultEvent.data.executor ?? 'cursor',
stdout: resultEvent.data.stdoutTail ?? '',
});
}
export async function executeCursorChannelCodeRun({
agentRunGateway,
userId,
sessionId = null,
requestId,
displayText,
agentPrompt = null,
intentKind = 'page.generate',
channel = 'wechat_mp',
taskType = null,
policy = null,
forceCursorExecutor = false,
timeoutMs = 15 * 60 * 1000,
pollMs = DEFAULT_POLL_MS,
logger = console,
} = {}) {
if (!agentRunGateway?.createRun || !agentRunGateway?.dispatchRun || !agentRunGateway?.getRunForUser) {
throw Object.assign(new Error('Cursor 执行网关不可用'), {
code: 'CURSOR_CHANNEL_GATEWAY_UNAVAILABLE',
});
}
const promptText = String(agentPrompt ?? displayText ?? '').trim();
let userMessage = buildWechatCursorUserMessage({
displayText: String(displayText ?? promptText).trim(),
intentKind,
channel,
});
if (promptText) {
userMessage = {
...userMessage,
content: [{ type: 'text', text: promptText }],
};
}
const resolvedTaskType = taskType
?? (channel === 'wechat_mp' ? 'wechat_page_generate' : 'h5_chat_code_task');
let cursorRuntime = enforcePageGenerationCursorRuntime(userMessage, {
rawToolMode: 'code',
taskType: resolvedTaskType,
env: process.env,
channelEligible: true,
policy,
});
if (forceCursorExecutor && !cursorRuntime.requiredExecutor) {
cursorRuntime = {
...cursorRuntime,
rawToolMode: 'code',
taskType: resolvedTaskType,
requiredExecutor: 'cursor',
};
userMessage = {
...userMessage,
metadata: {
...(userMessage.metadata ?? {}),
memindRun: {
...(userMessage.metadata?.memindRun ?? {}),
requiredExecutor: 'cursor',
executor: 'cursor',
toolMode: 'code',
},
},
};
} else {
userMessage = cursorRuntime.userMessage;
}
const run = await agentRunGateway.createRun(userId, {
sessionId,
requestId,
userMessage,
toolMode: 'code',
taskType: cursorRuntime.taskType ?? resolvedTaskType,
});
agentRunGateway.dispatchRun(run.id);
const deadline = Date.now() + Math.max(0, Number(timeoutMs) || 0);
while (Date.now() <= deadline) {
const latest = await agentRunGateway.getRunForUser(userId, run.id);
if (!latest) {
throw Object.assign(new Error('Cursor 任务丢失'), {
code: 'CURSOR_CHANNEL_RUN_MISSING',
});
}
if (latest.status === 'succeeded') {
const text = await readCursorCompletionText(agentRunGateway, userId, run.id);
logger.info?.('[cursor-channel] run succeeded', {
userId,
runId: run.id,
requestId,
channel,
});
return {
text,
runId: run.id,
tokenState: null,
messages: [{
role: 'assistant',
content: [{ type: 'text', text }],
metadata: { userVisible: true, source: 'cursor-channel-agent-run', channel },
}],
requestMessages: [],
};
}
if (latest.status === 'failed') {
const error = new Error(latest.error || 'Cursor 执行失败');
error.code = 'CURSOR_CHANNEL_RUN_FAILED';
throw error;
}
await sleep(Math.max(500, Number(pollMs) || DEFAULT_POLL_MS));
}
throw Object.assign(new Error('Cursor 执行超时'), {
code: 'CURSOR_CHANNEL_RUN_TIMEOUT',
});
}
export async function executeWechatCursorAgentRun({
agentRunGateway,
userId,
sessionId = null,
requestId,
displayText,
agentPrompt,
intentKind = 'page.generate',
policy = null,
timeoutMs = 15 * 60 * 1000,
pollMs = DEFAULT_POLL_MS,
logger = console,
} = {}) {
return executeCursorChannelCodeRun({
agentRunGateway,
userId,
sessionId,
requestId,
displayText,
agentPrompt,
intentKind,
channel: 'wechat_mp',
taskType: 'wechat_page_generate',
policy,
timeoutMs,
pollMs,
logger,
});
}