ai-project-manage-cli 7.0.4 → 7.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +466 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -461,6 +461,10 @@ var requestConfig = {
|
|
|
461
461
|
method: "PUT",
|
|
462
462
|
path: "/cli/mailbox-messages/complete"
|
|
463
463
|
}),
|
|
464
|
+
createCursorAgentQuestions: defineEndpoint({
|
|
465
|
+
method: "PUT",
|
|
466
|
+
path: "/cli/cursor-agent/questions"
|
|
467
|
+
}),
|
|
464
468
|
upsertCursorLog: defineEndpoint({
|
|
465
469
|
method: "PUT",
|
|
466
470
|
path: "/cli/cursor-logs"
|
|
@@ -1376,13 +1380,65 @@ function validateReceivedMailPush(o) {
|
|
|
1376
1380
|
}
|
|
1377
1381
|
};
|
|
1378
1382
|
}
|
|
1383
|
+
function validateCursorAgentResumePush(o) {
|
|
1384
|
+
if (o.type !== "cursor_agent_resume") {
|
|
1385
|
+
return { ok: false, reason: "\u671F\u671B cursor_agent_resume" };
|
|
1386
|
+
}
|
|
1387
|
+
if (o.anchorType !== "mailbox") {
|
|
1388
|
+
return { ok: false, reason: "cursor_agent_resume.anchorType \u65E0\u6548" };
|
|
1389
|
+
}
|
|
1390
|
+
if (!nonEmptyString(o.anchorId)) {
|
|
1391
|
+
return { ok: false, reason: "cursor_agent_resume \u7F3A\u5C11 anchorId" };
|
|
1392
|
+
}
|
|
1393
|
+
if (!nonEmptyString(o.taskId)) {
|
|
1394
|
+
return { ok: false, reason: "cursor_agent_resume \u7F3A\u5C11 taskId" };
|
|
1395
|
+
}
|
|
1396
|
+
if (!nonEmptyString(o.cursorAgentId)) {
|
|
1397
|
+
return { ok: false, reason: "cursor_agent_resume \u7F3A\u5C11 cursorAgentId" };
|
|
1398
|
+
}
|
|
1399
|
+
if (!nonEmptyString(o.draftReplyId)) {
|
|
1400
|
+
return { ok: false, reason: "cursor_agent_resume \u7F3A\u5C11 draftReplyId" };
|
|
1401
|
+
}
|
|
1402
|
+
if (!nonEmptyString(o.workdir)) {
|
|
1403
|
+
return { ok: false, reason: "cursor_agent_resume \u7F3A\u5C11 workdir" };
|
|
1404
|
+
}
|
|
1405
|
+
if (!nonEmptyString(o.model)) {
|
|
1406
|
+
return { ok: false, reason: "cursor_agent_resume \u7F3A\u5C11 model" };
|
|
1407
|
+
}
|
|
1408
|
+
if (!nonEmptyString(o.memberDisplayName)) {
|
|
1409
|
+
return { ok: false, reason: "cursor_agent_resume \u7F3A\u5C11 memberDisplayName" };
|
|
1410
|
+
}
|
|
1411
|
+
const mode = o.mode;
|
|
1412
|
+
if (mode !== "plan" && mode !== "agent") {
|
|
1413
|
+
return { ok: false, reason: "cursor_agent_resume.mode \u65E0\u6548" };
|
|
1414
|
+
}
|
|
1415
|
+
if (!nonEmptyString(o.prompt)) {
|
|
1416
|
+
return { ok: false, reason: "cursor_agent_resume \u7F3A\u5C11 prompt" };
|
|
1417
|
+
}
|
|
1418
|
+
return {
|
|
1419
|
+
ok: true,
|
|
1420
|
+
data: {
|
|
1421
|
+
type: "cursor_agent_resume",
|
|
1422
|
+
anchorType: "mailbox",
|
|
1423
|
+
anchorId: o.anchorId.trim(),
|
|
1424
|
+
taskId: o.taskId.trim(),
|
|
1425
|
+
cursorAgentId: o.cursorAgentId.trim(),
|
|
1426
|
+
draftReplyId: o.draftReplyId.trim(),
|
|
1427
|
+
workdir: o.workdir.trim(),
|
|
1428
|
+
model: o.model.trim(),
|
|
1429
|
+
memberDisplayName: o.memberDisplayName.trim(),
|
|
1430
|
+
mode,
|
|
1431
|
+
prompt: o.prompt.trim()
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1379
1435
|
function validateAgentWsMessage(value, kind) {
|
|
1380
1436
|
if (typeof value !== "object" || value === null) {
|
|
1381
1437
|
return { ok: false, reason: "\u6D88\u606F\u4F53\u4E0D\u662F JSON \u5BF9\u8C61" };
|
|
1382
1438
|
}
|
|
1383
1439
|
const o = value;
|
|
1384
1440
|
const type = o.type;
|
|
1385
|
-
if (type !== "heartbeat" && type !== "deploy" && type !== "received_mail") {
|
|
1441
|
+
if (type !== "heartbeat" && type !== "deploy" && type !== "received_mail" && type !== "cursor_agent_resume") {
|
|
1386
1442
|
return { ok: false, reason: `\u672A\u77E5 type: ${String(type)}` };
|
|
1387
1443
|
}
|
|
1388
1444
|
if (kind === "heartbeat" || type === "heartbeat") {
|
|
@@ -1391,6 +1447,9 @@ function validateAgentWsMessage(value, kind) {
|
|
|
1391
1447
|
if (type === "deploy") {
|
|
1392
1448
|
return validateDeployPush(o);
|
|
1393
1449
|
}
|
|
1450
|
+
if (type === "cursor_agent_resume") {
|
|
1451
|
+
return validateCursorAgentResumePush(o);
|
|
1452
|
+
}
|
|
1394
1453
|
return validateReceivedMailPush(o);
|
|
1395
1454
|
}
|
|
1396
1455
|
|
|
@@ -2010,6 +2069,176 @@ function clearTaskAgentId(workdir, taskId, user) {
|
|
|
2010
2069
|
writeRegistry(path13, registry);
|
|
2011
2070
|
}
|
|
2012
2071
|
|
|
2072
|
+
// src/commands/connect/ask-question-tool.ts
|
|
2073
|
+
import { randomUUID } from "node:crypto";
|
|
2074
|
+
|
|
2075
|
+
// src/ask-question.ts
|
|
2076
|
+
function nonEmptyString2(value) {
|
|
2077
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
2078
|
+
}
|
|
2079
|
+
function parseOption(value, index) {
|
|
2080
|
+
if (typeof value !== "object" || value === null) {
|
|
2081
|
+
return `questions[].options[${index}] \u5FC5\u987B\u662F\u5BF9\u8C61`;
|
|
2082
|
+
}
|
|
2083
|
+
const option = value;
|
|
2084
|
+
if (!nonEmptyString2(option.id)) {
|
|
2085
|
+
return `questions[].options[${index}].id \u4E0D\u80FD\u4E3A\u7A7A`;
|
|
2086
|
+
}
|
|
2087
|
+
if (!nonEmptyString2(option.label)) {
|
|
2088
|
+
return `questions[].options[${index}].label \u4E0D\u80FD\u4E3A\u7A7A`;
|
|
2089
|
+
}
|
|
2090
|
+
return {
|
|
2091
|
+
id: option.id.trim(),
|
|
2092
|
+
label: option.label.trim()
|
|
2093
|
+
};
|
|
2094
|
+
}
|
|
2095
|
+
function parseQuestion(value, index) {
|
|
2096
|
+
if (typeof value !== "object" || value === null) {
|
|
2097
|
+
return `questions[${index}] \u5FC5\u987B\u662F\u5BF9\u8C61`;
|
|
2098
|
+
}
|
|
2099
|
+
const question = value;
|
|
2100
|
+
if (!nonEmptyString2(question.id)) {
|
|
2101
|
+
return `questions[${index}].id \u4E0D\u80FD\u4E3A\u7A7A`;
|
|
2102
|
+
}
|
|
2103
|
+
if (!nonEmptyString2(question.prompt)) {
|
|
2104
|
+
return `questions[${index}].prompt \u4E0D\u80FD\u4E3A\u7A7A`;
|
|
2105
|
+
}
|
|
2106
|
+
if (!Array.isArray(question.options)) {
|
|
2107
|
+
return `questions[${index}].options \u5FC5\u987B\u662F\u6570\u7EC4`;
|
|
2108
|
+
}
|
|
2109
|
+
if (question.options.length < 2) {
|
|
2110
|
+
return `questions[${index}].options \u81F3\u5C11\u9700\u8981 2 \u4E2A\u9009\u9879`;
|
|
2111
|
+
}
|
|
2112
|
+
const options = [];
|
|
2113
|
+
for (let i = 0; i < question.options.length; i++) {
|
|
2114
|
+
const parsed = parseOption(question.options[i], i);
|
|
2115
|
+
if (typeof parsed === "string") {
|
|
2116
|
+
return parsed;
|
|
2117
|
+
}
|
|
2118
|
+
options.push(parsed);
|
|
2119
|
+
}
|
|
2120
|
+
const ids = new Set(options.map((option) => option.id));
|
|
2121
|
+
if (ids.size !== options.length) {
|
|
2122
|
+
return `questions[${index}].options \u7684 id \u4E0D\u80FD\u91CD\u590D`;
|
|
2123
|
+
}
|
|
2124
|
+
return {
|
|
2125
|
+
id: question.id.trim(),
|
|
2126
|
+
prompt: question.prompt.trim(),
|
|
2127
|
+
...question.allow_multiple === true ? { allow_multiple: true } : {},
|
|
2128
|
+
options
|
|
2129
|
+
};
|
|
2130
|
+
}
|
|
2131
|
+
function validateAskQuestionInput(value) {
|
|
2132
|
+
if (typeof value !== "object" || value === null) {
|
|
2133
|
+
return { ok: false, error: "AskQuestion \u5165\u53C2\u5FC5\u987B\u662F\u5BF9\u8C61" };
|
|
2134
|
+
}
|
|
2135
|
+
const input = value;
|
|
2136
|
+
if (!Array.isArray(input.questions)) {
|
|
2137
|
+
return { ok: false, error: "questions \u5FC5\u987B\u662F\u6570\u7EC4" };
|
|
2138
|
+
}
|
|
2139
|
+
if (input.questions.length < 1) {
|
|
2140
|
+
return { ok: false, error: "questions \u81F3\u5C11\u9700\u8981 1 \u9053\u9898" };
|
|
2141
|
+
}
|
|
2142
|
+
const questions = [];
|
|
2143
|
+
for (let i = 0; i < input.questions.length; i++) {
|
|
2144
|
+
const parsed = parseQuestion(input.questions[i], i);
|
|
2145
|
+
if (typeof parsed === "string") {
|
|
2146
|
+
return { ok: false, error: parsed };
|
|
2147
|
+
}
|
|
2148
|
+
questions.push(parsed);
|
|
2149
|
+
}
|
|
2150
|
+
const questionIds = new Set(questions.map((question) => question.id));
|
|
2151
|
+
if (questionIds.size !== questions.length) {
|
|
2152
|
+
return { ok: false, error: "questions[].id \u4E0D\u80FD\u91CD\u590D" };
|
|
2153
|
+
}
|
|
2154
|
+
const title = nonEmptyString2(input.title) ? input.title.trim() : void 0;
|
|
2155
|
+
return {
|
|
2156
|
+
ok: true,
|
|
2157
|
+
input: {
|
|
2158
|
+
...title ? { title } : {},
|
|
2159
|
+
questions
|
|
2160
|
+
}
|
|
2161
|
+
};
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
// src/commands/connect/ask-question-tool.ts
|
|
2165
|
+
var ASK_QUESTION_DESCRIPTION = "Collect structured multiple-choice answers from the user. Use when you need to clarify requirements or preferences before proceeding. When options are provided, do not re-ask the same question in prose.";
|
|
2166
|
+
var ASK_QUESTION_WAIT_MESSAGE = "\u95EE\u9898\u5DF2\u63D0\u4EA4\uFF0C\u7B49\u5F85\u7528\u6237\u56DE\u7B54\u3002\u8BF7\u52FF\u7EE7\u7EED\u89C4\u5212\u6216\u4EA7\u51FA\u6700\u7EC8\u7ED3\u8BBA\uFF0C\u76F4\u5230\u540E\u7EED\u6D88\u606F\u6CE8\u5165\u7528\u6237\u7B54\u6848\u3002";
|
|
2167
|
+
function createAskQuestionTool(handlers) {
|
|
2168
|
+
const batches = [];
|
|
2169
|
+
const tool = {
|
|
2170
|
+
description: ASK_QUESTION_DESCRIPTION,
|
|
2171
|
+
inputSchema: {
|
|
2172
|
+
type: "object",
|
|
2173
|
+
properties: {
|
|
2174
|
+
title: {
|
|
2175
|
+
type: "string",
|
|
2176
|
+
description: "Optional title for the question form"
|
|
2177
|
+
},
|
|
2178
|
+
questions: {
|
|
2179
|
+
type: "array",
|
|
2180
|
+
description: "Questions to ask the user",
|
|
2181
|
+
items: {
|
|
2182
|
+
type: "object",
|
|
2183
|
+
properties: {
|
|
2184
|
+
id: {
|
|
2185
|
+
type: "string",
|
|
2186
|
+
description: "Unique identifier for this question"
|
|
2187
|
+
},
|
|
2188
|
+
prompt: {
|
|
2189
|
+
type: "string",
|
|
2190
|
+
description: "The question text to display"
|
|
2191
|
+
},
|
|
2192
|
+
allow_multiple: {
|
|
2193
|
+
type: "boolean",
|
|
2194
|
+
description: "Whether the user may select multiple options"
|
|
2195
|
+
},
|
|
2196
|
+
options: {
|
|
2197
|
+
type: "array",
|
|
2198
|
+
description: "At least 2 answer options",
|
|
2199
|
+
items: {
|
|
2200
|
+
type: "object",
|
|
2201
|
+
properties: {
|
|
2202
|
+
id: { type: "string" },
|
|
2203
|
+
label: { type: "string" }
|
|
2204
|
+
},
|
|
2205
|
+
required: ["id", "label"]
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
},
|
|
2209
|
+
required: ["id", "prompt", "options"]
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
},
|
|
2213
|
+
required: ["questions"]
|
|
2214
|
+
},
|
|
2215
|
+
execute: async (args) => {
|
|
2216
|
+
const validated = validateAskQuestionInput(args);
|
|
2217
|
+
if (!validated.ok) {
|
|
2218
|
+
return {
|
|
2219
|
+
content: [{ type: "text", text: validated.error }],
|
|
2220
|
+
isError: true
|
|
2221
|
+
};
|
|
2222
|
+
}
|
|
2223
|
+
const batch = {
|
|
2224
|
+
batchId: randomUUID(),
|
|
2225
|
+
...validated.input
|
|
2226
|
+
};
|
|
2227
|
+
batches.push(batch);
|
|
2228
|
+
await handlers.onAskQuestion(batch);
|
|
2229
|
+
console.log(
|
|
2230
|
+
`[apm] AskQuestion \u5DF2\u63D0\u4EA4 batchId=${batch.batchId} questions=${batch.questions.length}`
|
|
2231
|
+
);
|
|
2232
|
+
return ASK_QUESTION_WAIT_MESSAGE;
|
|
2233
|
+
}
|
|
2234
|
+
};
|
|
2235
|
+
return {
|
|
2236
|
+
tool,
|
|
2237
|
+
getAskedBatches: () => [...batches],
|
|
2238
|
+
hasPendingQuestions: () => batches.length > 0
|
|
2239
|
+
};
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2013
2242
|
// src/commands/connect/cursor-log.ts
|
|
2014
2243
|
var CURSOR_LOG_SYNC_INTERVAL_MS = 2e3;
|
|
2015
2244
|
function createThrottledCursorLogSync(cfg, ctx, onError) {
|
|
@@ -2136,6 +2365,34 @@ async function obtainAgent(ctx) {
|
|
|
2136
2365
|
}
|
|
2137
2366
|
return { agent, resumed: false };
|
|
2138
2367
|
}
|
|
2368
|
+
var DEFAULT_MAIL_PROMPT_FOOTER = (taskId) => `\u8BF7\u5B8C\u6210\u4E0A\u8FF0\u4EFB\u52A1\u3002\u6267\u884C\u8FC7\u7A0B\u4E2D\u8BF7\u7528 append_mail_reply \u589E\u91CF\u66F4\u65B0\u7ED9\u53D1\u4FE1\u4EBA\u7684\u56DE\u590D\uFF08\u53EF\u5148\u7B80\u77ED\u786E\u8BA4\uFF0C\u6709\u8FDB\u5C55\u7EE7\u7EED\u8FFD\u52A0\uFF09\uFF1BAgent \u6B63\u5E38\u7ED3\u675F\u540E\u4F1A\u81EA\u52A8\u63D0\u4EA4\u5B8C\u6574\u56DE\u4FE1\u3002\u5982\u9700\u7F16\u5199\u6587\u6863\uFF0C\u4FDD\u5B58\u5230 \`.apm/tasks/${taskId}/docs/\` \u76EE\u5F55\u3002`;
|
|
2369
|
+
function buildPrompt(basePrompt, taskId, mode, promptFooter, hasAskQuestion) {
|
|
2370
|
+
const trimmed = basePrompt.trim();
|
|
2371
|
+
let footer;
|
|
2372
|
+
if (promptFooter === null || promptFooter === "") {
|
|
2373
|
+
footer = "";
|
|
2374
|
+
} else if (promptFooter === void 0) {
|
|
2375
|
+
footer = DEFAULT_MAIL_PROMPT_FOOTER(taskId);
|
|
2376
|
+
} else {
|
|
2377
|
+
footer = promptFooter;
|
|
2378
|
+
}
|
|
2379
|
+
const askQuestionHint = hasAskQuestion && mode === "plan" ? "\u9700\u8981\u7528\u6237\u786E\u8BA4\u65F6\u8BF7\u4F7F\u7528 AskQuestion \u5DE5\u5177\uFF0C\u52FF\u5728\u6B63\u6587\u4E2D\u7F57\u5217\u9009\u9879\u8BA9\u7528\u6237\u6587\u5B57\u56DE\u590D\u3002" : "";
|
|
2380
|
+
const parts = [trimmed];
|
|
2381
|
+
const hints = [footer, askQuestionHint].filter(Boolean);
|
|
2382
|
+
if (hints.length > 0) {
|
|
2383
|
+
parts.push("---", ...hints);
|
|
2384
|
+
}
|
|
2385
|
+
return parts.join("\n\n");
|
|
2386
|
+
}
|
|
2387
|
+
function mergeCustomTools(customTools, askQuestionTool) {
|
|
2388
|
+
if (!askQuestionTool) {
|
|
2389
|
+
return customTools;
|
|
2390
|
+
}
|
|
2391
|
+
return {
|
|
2392
|
+
...customTools,
|
|
2393
|
+
AskQuestion: askQuestionTool
|
|
2394
|
+
};
|
|
2395
|
+
}
|
|
2139
2396
|
async function runCursorAgent(cfg, ctx, options) {
|
|
2140
2397
|
const signal = options?.signal;
|
|
2141
2398
|
logAbortSignalStats(signal, "runCursorAgent:start");
|
|
@@ -2147,12 +2404,24 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2147
2404
|
throw new Error("\u7F3A\u5C11 Cursor API Key");
|
|
2148
2405
|
}
|
|
2149
2406
|
const workdir = requireExistingWorkdir(ctx.workdir);
|
|
2150
|
-
const
|
|
2151
|
-
const
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2407
|
+
const baseCustomTools = options?.customTools ?? {};
|
|
2408
|
+
const agentContext = { agentId: "" };
|
|
2409
|
+
const askQuestion = options?.onAskQuestion ? createAskQuestionTool({
|
|
2410
|
+
onAskQuestion: async (batch) => {
|
|
2411
|
+
await options.onAskQuestion(batch, {
|
|
2412
|
+
agentId: agentContext.agentId
|
|
2413
|
+
});
|
|
2414
|
+
}
|
|
2415
|
+
}) : void 0;
|
|
2416
|
+
const customTools = mergeCustomTools(baseCustomTools, askQuestion?.tool);
|
|
2155
2417
|
const mode = ctx.mode ?? "agent";
|
|
2418
|
+
const prompt = buildPrompt(
|
|
2419
|
+
ctx.prompt,
|
|
2420
|
+
ctx.taskId,
|
|
2421
|
+
mode,
|
|
2422
|
+
options?.promptFooter,
|
|
2423
|
+
Boolean(askQuestion)
|
|
2424
|
+
);
|
|
2156
2425
|
console.log(
|
|
2157
2426
|
`[apm] Cursor Agent \u5F00\u59CB mailId=${ctx.mailId} taskId=${ctx.taskId} mode=${mode} cwd=${workdir}`
|
|
2158
2427
|
);
|
|
@@ -2165,6 +2434,7 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2165
2434
|
mode,
|
|
2166
2435
|
customTools
|
|
2167
2436
|
});
|
|
2437
|
+
agentContext.agentId = agent.agentId;
|
|
2168
2438
|
const eventSession = new EventSession(prompt);
|
|
2169
2439
|
const syncRemoteLog = options?.skipRemoteLogSync ? noopRemoteLogSync : createThrottledCursorLogSync(
|
|
2170
2440
|
cfg,
|
|
@@ -2223,11 +2493,14 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2223
2493
|
throw new Error(`Cursor run \u5DF2\u53D6\u6D88: ${result.id}`);
|
|
2224
2494
|
}
|
|
2225
2495
|
console.log(`[apm] Cursor Agent \u5B8C\u6210 mailId=${ctx.mailId}`);
|
|
2496
|
+
const askQuestionBatches = askQuestion?.getAskedBatches() ?? [];
|
|
2226
2497
|
return {
|
|
2227
2498
|
runId: result.id,
|
|
2228
2499
|
agentId: agent.agentId,
|
|
2229
2500
|
status: result.status,
|
|
2230
2501
|
assistantText: assistantText || result.result || "",
|
|
2502
|
+
askQuestionBatches,
|
|
2503
|
+
hasPendingQuestions: askQuestionBatches.length > 0,
|
|
2231
2504
|
...createPlanContent ? { createPlanContent } : {}
|
|
2232
2505
|
};
|
|
2233
2506
|
} catch (err) {
|
|
@@ -2543,13 +2816,36 @@ async function processMail(mail, options) {
|
|
|
2543
2816
|
});
|
|
2544
2817
|
try {
|
|
2545
2818
|
const cursorMode = resolveCursorAgentMode(detail.studio?.phase);
|
|
2819
|
+
const mailContext = {
|
|
2820
|
+
mailId: detail.id,
|
|
2821
|
+
taskId: detail.taskId,
|
|
2822
|
+
draftReplyId,
|
|
2823
|
+
workdir: detail.workdir,
|
|
2824
|
+
model: detail.recipient.model?.trim() || "default",
|
|
2825
|
+
memberDisplayName: detail.recipient.displayName,
|
|
2826
|
+
cursorMode
|
|
2827
|
+
};
|
|
2828
|
+
const submitQuestions = async (batch, ctx) => {
|
|
2829
|
+
await api.cli.createCursorAgentQuestions({
|
|
2830
|
+
anchorType: "mailbox",
|
|
2831
|
+
anchorId: mailContext.mailId,
|
|
2832
|
+
taskId: mailContext.taskId,
|
|
2833
|
+
cursorAgentId: ctx.agentId,
|
|
2834
|
+
draftReplyId: mailContext.draftReplyId,
|
|
2835
|
+
workdir: mailContext.workdir,
|
|
2836
|
+
model: mailContext.model,
|
|
2837
|
+
memberDisplayName: mailContext.memberDisplayName,
|
|
2838
|
+
mode: mailContext.cursorMode,
|
|
2839
|
+
batch
|
|
2840
|
+
});
|
|
2841
|
+
};
|
|
2546
2842
|
const result = await runCursorAgent(
|
|
2547
2843
|
options.cfg,
|
|
2548
2844
|
{
|
|
2549
2845
|
mailId: detail.id,
|
|
2550
2846
|
taskId: detail.taskId,
|
|
2551
2847
|
prompt: detail.content,
|
|
2552
|
-
model:
|
|
2848
|
+
model: mailContext.model,
|
|
2553
2849
|
apiKey: cursorApiKey,
|
|
2554
2850
|
workdir: detail.workdir,
|
|
2555
2851
|
user: detail.recipient.displayName,
|
|
@@ -2557,9 +2853,16 @@ async function processMail(mail, options) {
|
|
|
2557
2853
|
},
|
|
2558
2854
|
{
|
|
2559
2855
|
signal: options.signal,
|
|
2560
|
-
customTools: { append_mail_reply: replyTool }
|
|
2856
|
+
customTools: { append_mail_reply: replyTool },
|
|
2857
|
+
onAskQuestion: submitQuestions
|
|
2561
2858
|
}
|
|
2562
2859
|
);
|
|
2860
|
+
if (result.hasPendingQuestions) {
|
|
2861
|
+
console.log(
|
|
2862
|
+
`[apm] \u4FE1\u4EF6 id=${mail.id} \u6709\u5F85\u7B54 AskQuestion\uFF0C\u7B49\u5F85\u7528\u6237\u5728\u5DE5\u4F5C\u5BA4\u7B54\u9898\u540E\u7EED\u804A`
|
|
2863
|
+
);
|
|
2864
|
+
return;
|
|
2865
|
+
}
|
|
2563
2866
|
const replyContent = (hasReplyContent() ? getReplyContent() : result.assistantText).trim();
|
|
2564
2867
|
if (!replyContent) {
|
|
2565
2868
|
throw new Error("Agent \u672A\u4EA7\u51FA\u53EF\u63D0\u4EA4\u7684\u56DE\u4FE1\u5185\u5BB9");
|
|
@@ -2659,6 +2962,148 @@ function createMailProcessor(options) {
|
|
|
2659
2962
|
};
|
|
2660
2963
|
}
|
|
2661
2964
|
|
|
2965
|
+
// src/commands/connect/cursor-agent-resume-handler.ts
|
|
2966
|
+
async function runMailboxCursorAgentResume(push, options) {
|
|
2967
|
+
const cursorApiKey = options.getCursorApiKey().trim();
|
|
2968
|
+
if (!cursorApiKey) {
|
|
2969
|
+
options.onFatalError(
|
|
2970
|
+
"[apm] \u5BA2\u6237\u673A\u672A\u914D\u7F6E Cursor API Key\uFF0C\u65E0\u6CD5\u7EED\u804A Cursor Agent"
|
|
2971
|
+
);
|
|
2972
|
+
return;
|
|
2973
|
+
}
|
|
2974
|
+
const api = createApmApiClient(options.cfg);
|
|
2975
|
+
let detail;
|
|
2976
|
+
try {
|
|
2977
|
+
detail = await api.cli.getMailboxMessageDetail({ id: push.anchorId });
|
|
2978
|
+
} catch (err) {
|
|
2979
|
+
console.error(
|
|
2980
|
+
`[apm] \u7EED\u804A\u83B7\u53D6\u4FE1\u4EF6\u8BE6\u60C5\u5931\u8D25 id=${push.anchorId}:`,
|
|
2981
|
+
err instanceof Error ? err.message : err
|
|
2982
|
+
);
|
|
2983
|
+
return;
|
|
2984
|
+
}
|
|
2985
|
+
const {
|
|
2986
|
+
tool: replyTool,
|
|
2987
|
+
getReplyContent,
|
|
2988
|
+
hasReplyContent
|
|
2989
|
+
} = createMailReplyDraft({
|
|
2990
|
+
appendDraft: async (content) => {
|
|
2991
|
+
await api.cli.appendMailboxDraft({
|
|
2992
|
+
id: push.draftReplyId,
|
|
2993
|
+
content
|
|
2994
|
+
});
|
|
2995
|
+
}
|
|
2996
|
+
});
|
|
2997
|
+
const cursorMode = push.mode;
|
|
2998
|
+
const submitQuestions = async (batch, ctx) => {
|
|
2999
|
+
await api.cli.createCursorAgentQuestions({
|
|
3000
|
+
anchorType: "mailbox",
|
|
3001
|
+
anchorId: push.anchorId,
|
|
3002
|
+
taskId: push.taskId,
|
|
3003
|
+
cursorAgentId: ctx.agentId,
|
|
3004
|
+
draftReplyId: push.draftReplyId,
|
|
3005
|
+
workdir: push.workdir,
|
|
3006
|
+
model: push.model,
|
|
3007
|
+
memberDisplayName: push.memberDisplayName,
|
|
3008
|
+
mode: cursorMode,
|
|
3009
|
+
batch
|
|
3010
|
+
});
|
|
3011
|
+
};
|
|
3012
|
+
try {
|
|
3013
|
+
const result = await runCursorAgent(
|
|
3014
|
+
options.cfg,
|
|
3015
|
+
{
|
|
3016
|
+
mailId: push.anchorId,
|
|
3017
|
+
taskId: push.taskId,
|
|
3018
|
+
prompt: push.prompt,
|
|
3019
|
+
model: push.model,
|
|
3020
|
+
apiKey: cursorApiKey,
|
|
3021
|
+
workdir: push.workdir,
|
|
3022
|
+
user: push.memberDisplayName,
|
|
3023
|
+
mode: cursorMode
|
|
3024
|
+
},
|
|
3025
|
+
{
|
|
3026
|
+
signal: options.signal,
|
|
3027
|
+
customTools: { append_mail_reply: replyTool },
|
|
3028
|
+
onAskQuestion: submitQuestions,
|
|
3029
|
+
forceSend: true
|
|
3030
|
+
}
|
|
3031
|
+
);
|
|
3032
|
+
if (result.hasPendingQuestions) {
|
|
3033
|
+
console.log(
|
|
3034
|
+
`[apm] \u7EED\u804A\u540E\u4ECD\u6709\u5F85\u7B54 AskQuestion mailId=${push.anchorId}\uFF0C\u7B49\u5F85\u7528\u6237\u7B54\u9898`
|
|
3035
|
+
);
|
|
3036
|
+
return;
|
|
3037
|
+
}
|
|
3038
|
+
const replyContent = (hasReplyContent() ? getReplyContent() : result.assistantText).trim();
|
|
3039
|
+
if (!replyContent) {
|
|
3040
|
+
throw new Error("Agent \u672A\u4EA7\u51FA\u53EF\u63D0\u4EA4\u7684\u56DE\u4FE1\u5185\u5BB9");
|
|
3041
|
+
}
|
|
3042
|
+
if (cursorMode === "plan" && result.createPlanContent) {
|
|
3043
|
+
saveMemberPlanDocument({
|
|
3044
|
+
workdir: push.workdir,
|
|
3045
|
+
taskId: push.taskId,
|
|
3046
|
+
memberDisplayName: push.memberDisplayName,
|
|
3047
|
+
content: result.createPlanContent
|
|
3048
|
+
});
|
|
3049
|
+
}
|
|
3050
|
+
await api.cli.completeMailboxMessage({
|
|
3051
|
+
id: push.draftReplyId,
|
|
3052
|
+
status: "SUCCEEDED",
|
|
3053
|
+
replyContent
|
|
3054
|
+
});
|
|
3055
|
+
markMailReplied(push.anchorId);
|
|
3056
|
+
await syncTaskDocuments(options.cfg, push.taskId, push.workdir, {
|
|
3057
|
+
api,
|
|
3058
|
+
remoteDocuments: detail.documents
|
|
3059
|
+
});
|
|
3060
|
+
console.log(`[apm] Cursor Agent \u7EED\u804A\u5B8C\u6210 mailId=${push.anchorId}`);
|
|
3061
|
+
} catch (err) {
|
|
3062
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3063
|
+
console.error(
|
|
3064
|
+
`[apm] Cursor Agent \u7EED\u804A\u5931\u8D25 mailId=${push.anchorId}: ${message}`
|
|
3065
|
+
);
|
|
3066
|
+
try {
|
|
3067
|
+
await api.cli.completeMailboxMessage({
|
|
3068
|
+
id: push.draftReplyId,
|
|
3069
|
+
status: "FAILED",
|
|
3070
|
+
error: message
|
|
3071
|
+
});
|
|
3072
|
+
} catch (completeErr) {
|
|
3073
|
+
console.error(
|
|
3074
|
+
`[apm] \u6807\u8BB0\u7EED\u804A\u5931\u8D25\u72B6\u6001\u65F6\u51FA\u9519 mailId=${push.anchorId}:`,
|
|
3075
|
+
completeErr instanceof Error ? completeErr.message : completeErr
|
|
3076
|
+
);
|
|
3077
|
+
}
|
|
3078
|
+
markMailReplied(push.anchorId);
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
3081
|
+
function createCursorAgentResumeHandler(options) {
|
|
3082
|
+
let running = false;
|
|
3083
|
+
return {
|
|
3084
|
+
receive(push) {
|
|
3085
|
+
if (options.signal.aborted) {
|
|
3086
|
+
return;
|
|
3087
|
+
}
|
|
3088
|
+
if (running) {
|
|
3089
|
+
console.warn(
|
|
3090
|
+
`[apm] \u5DF2\u6709 Cursor Agent \u7EED\u804A\u4EFB\u52A1\u8FDB\u884C\u4E2D\uFF0C\u5FFD\u7565 mailId=${push.anchorId}`
|
|
3091
|
+
);
|
|
3092
|
+
return;
|
|
3093
|
+
}
|
|
3094
|
+
running = true;
|
|
3095
|
+
void runMailboxCursorAgentResume(push, options).catch((err) => {
|
|
3096
|
+
console.error(
|
|
3097
|
+
`[apm] Cursor Agent \u7EED\u804A\u5F02\u5E38 mailId=${push.anchorId}:`,
|
|
3098
|
+
err instanceof Error ? err.message : err
|
|
3099
|
+
);
|
|
3100
|
+
}).finally(() => {
|
|
3101
|
+
running = false;
|
|
3102
|
+
});
|
|
3103
|
+
}
|
|
3104
|
+
};
|
|
3105
|
+
}
|
|
3106
|
+
|
|
2662
3107
|
// src/commands/connect/mail-sync.ts
|
|
2663
3108
|
function parsePendingMail(value) {
|
|
2664
3109
|
if (typeof value !== "object" || value === null) {
|
|
@@ -2751,6 +3196,15 @@ async function runConnect(options) {
|
|
|
2751
3196
|
shutdown(1);
|
|
2752
3197
|
}
|
|
2753
3198
|
});
|
|
3199
|
+
const cursorAgentResumeHandler = createCursorAgentResumeHandler({
|
|
3200
|
+
cfg,
|
|
3201
|
+
signal: shutdownAbort.signal,
|
|
3202
|
+
getCursorApiKey: () => cursorApiKey,
|
|
3203
|
+
onFatalError: (message) => {
|
|
3204
|
+
console.error(message);
|
|
3205
|
+
shutdown(1);
|
|
3206
|
+
}
|
|
3207
|
+
});
|
|
2754
3208
|
const shutdown = (code = 0) => {
|
|
2755
3209
|
if (shuttingDown) return;
|
|
2756
3210
|
shuttingDown = true;
|
|
@@ -2815,6 +3269,10 @@ async function runConnect(options) {
|
|
|
2815
3269
|
}
|
|
2816
3270
|
if (validated.data.type === "received_mail") {
|
|
2817
3271
|
mailProcessor.receive(validated.data);
|
|
3272
|
+
return;
|
|
3273
|
+
}
|
|
3274
|
+
if (validated.data.type === "cursor_agent_resume") {
|
|
3275
|
+
cursorAgentResumeHandler.receive(validated.data);
|
|
2818
3276
|
}
|
|
2819
3277
|
});
|
|
2820
3278
|
ws.on("close", (code, reason) => {
|