ai-project-manage-cli 7.0.3 → 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 +484 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -453,10 +453,18 @@ var requestConfig = {
|
|
|
453
453
|
method: "PUT",
|
|
454
454
|
path: "/cli/mailbox-messages/claim"
|
|
455
455
|
}),
|
|
456
|
+
appendMailboxDraft: defineEndpoint({
|
|
457
|
+
method: "PUT",
|
|
458
|
+
path: "/cli/mailbox-messages/append-draft"
|
|
459
|
+
}),
|
|
456
460
|
completeMailboxMessage: defineEndpoint({
|
|
457
461
|
method: "PUT",
|
|
458
462
|
path: "/cli/mailbox-messages/complete"
|
|
459
463
|
}),
|
|
464
|
+
createCursorAgentQuestions: defineEndpoint({
|
|
465
|
+
method: "PUT",
|
|
466
|
+
path: "/cli/cursor-agent/questions"
|
|
467
|
+
}),
|
|
460
468
|
upsertCursorLog: defineEndpoint({
|
|
461
469
|
method: "PUT",
|
|
462
470
|
path: "/cli/cursor-logs"
|
|
@@ -1372,13 +1380,65 @@ function validateReceivedMailPush(o) {
|
|
|
1372
1380
|
}
|
|
1373
1381
|
};
|
|
1374
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
|
+
}
|
|
1375
1435
|
function validateAgentWsMessage(value, kind) {
|
|
1376
1436
|
if (typeof value !== "object" || value === null) {
|
|
1377
1437
|
return { ok: false, reason: "\u6D88\u606F\u4F53\u4E0D\u662F JSON \u5BF9\u8C61" };
|
|
1378
1438
|
}
|
|
1379
1439
|
const o = value;
|
|
1380
1440
|
const type = o.type;
|
|
1381
|
-
if (type !== "heartbeat" && type !== "deploy" && type !== "received_mail") {
|
|
1441
|
+
if (type !== "heartbeat" && type !== "deploy" && type !== "received_mail" && type !== "cursor_agent_resume") {
|
|
1382
1442
|
return { ok: false, reason: `\u672A\u77E5 type: ${String(type)}` };
|
|
1383
1443
|
}
|
|
1384
1444
|
if (kind === "heartbeat" || type === "heartbeat") {
|
|
@@ -1387,6 +1447,9 @@ function validateAgentWsMessage(value, kind) {
|
|
|
1387
1447
|
if (type === "deploy") {
|
|
1388
1448
|
return validateDeployPush(o);
|
|
1389
1449
|
}
|
|
1450
|
+
if (type === "cursor_agent_resume") {
|
|
1451
|
+
return validateCursorAgentResumePush(o);
|
|
1452
|
+
}
|
|
1390
1453
|
return validateReceivedMailPush(o);
|
|
1391
1454
|
}
|
|
1392
1455
|
|
|
@@ -2006,6 +2069,176 @@ function clearTaskAgentId(workdir, taskId, user) {
|
|
|
2006
2069
|
writeRegistry(path13, registry);
|
|
2007
2070
|
}
|
|
2008
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
|
+
|
|
2009
2242
|
// src/commands/connect/cursor-log.ts
|
|
2010
2243
|
var CURSOR_LOG_SYNC_INTERVAL_MS = 2e3;
|
|
2011
2244
|
function createThrottledCursorLogSync(cfg, ctx, onError) {
|
|
@@ -2132,6 +2365,34 @@ async function obtainAgent(ctx) {
|
|
|
2132
2365
|
}
|
|
2133
2366
|
return { agent, resumed: false };
|
|
2134
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
|
+
}
|
|
2135
2396
|
async function runCursorAgent(cfg, ctx, options) {
|
|
2136
2397
|
const signal = options?.signal;
|
|
2137
2398
|
logAbortSignalStats(signal, "runCursorAgent:start");
|
|
@@ -2143,12 +2404,24 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2143
2404
|
throw new Error("\u7F3A\u5C11 Cursor API Key");
|
|
2144
2405
|
}
|
|
2145
2406
|
const workdir = requireExistingWorkdir(ctx.workdir);
|
|
2146
|
-
const
|
|
2147
|
-
const
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
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);
|
|
2151
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
|
+
);
|
|
2152
2425
|
console.log(
|
|
2153
2426
|
`[apm] Cursor Agent \u5F00\u59CB mailId=${ctx.mailId} taskId=${ctx.taskId} mode=${mode} cwd=${workdir}`
|
|
2154
2427
|
);
|
|
@@ -2161,6 +2434,7 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2161
2434
|
mode,
|
|
2162
2435
|
customTools
|
|
2163
2436
|
});
|
|
2437
|
+
agentContext.agentId = agent.agentId;
|
|
2164
2438
|
const eventSession = new EventSession(prompt);
|
|
2165
2439
|
const syncRemoteLog = options?.skipRemoteLogSync ? noopRemoteLogSync : createThrottledCursorLogSync(
|
|
2166
2440
|
cfg,
|
|
@@ -2219,11 +2493,14 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2219
2493
|
throw new Error(`Cursor run \u5DF2\u53D6\u6D88: ${result.id}`);
|
|
2220
2494
|
}
|
|
2221
2495
|
console.log(`[apm] Cursor Agent \u5B8C\u6210 mailId=${ctx.mailId}`);
|
|
2496
|
+
const askQuestionBatches = askQuestion?.getAskedBatches() ?? [];
|
|
2222
2497
|
return {
|
|
2223
2498
|
runId: result.id,
|
|
2224
2499
|
agentId: agent.agentId,
|
|
2225
2500
|
status: result.status,
|
|
2226
2501
|
assistantText: assistantText || result.result || "",
|
|
2502
|
+
askQuestionBatches,
|
|
2503
|
+
hasPendingQuestions: askQuestionBatches.length > 0,
|
|
2227
2504
|
...createPlanContent ? { createPlanContent } : {}
|
|
2228
2505
|
};
|
|
2229
2506
|
} catch (err) {
|
|
@@ -2281,7 +2558,7 @@ function removeMailById(mailId) {
|
|
|
2281
2558
|
}
|
|
2282
2559
|
|
|
2283
2560
|
// src/commands/connect/reply-mail-tool.ts
|
|
2284
|
-
function createMailReplyDraft() {
|
|
2561
|
+
function createMailReplyDraft(input) {
|
|
2285
2562
|
const parts = [];
|
|
2286
2563
|
const tool = {
|
|
2287
2564
|
description: "\u5411\u53D1\u4FE1\u4EBA\u589E\u91CF\u66F4\u65B0\u56DE\u590D\u5185\u5BB9\u3002\u53EF\u591A\u6B21\u8C03\u7528\u8FFD\u52A0\u8FDB\u5C55\uFF08\u5982\u5148\u786E\u8BA4\u6536\u5230\u3001\u518D\u6C47\u62A5\u7ED3\u679C\uFF09\uFF1BAgent \u6B63\u5E38\u7ED3\u675F\u540E\u4F1A\u81EA\u52A8\u63D0\u4EA4\u5B8C\u6574\u56DE\u4FE1\uFF0C\u65E0\u9700\u53E6\u884C\u6536\u5C3E\u3002",
|
|
@@ -2304,6 +2581,7 @@ function createMailReplyDraft() {
|
|
|
2304
2581
|
};
|
|
2305
2582
|
}
|
|
2306
2583
|
parts.push(content);
|
|
2584
|
+
await input.appendDraft(content);
|
|
2307
2585
|
console.log(`[apm] \u589E\u91CF\u66F4\u65B0\u56DE\u4FE1\uFF08\u7B2C ${parts.length} \u6BB5\uFF09`);
|
|
2308
2586
|
return "\u5DF2\u8FFD\u52A0\u5230\u56DE\u4FE1\u8349\u7A3F\uFF0CAgent \u7ED3\u675F\u540E\u5C06\u4E00\u5E76\u63D0\u4EA4\u3002";
|
|
2309
2587
|
}
|
|
@@ -2508,15 +2786,17 @@ async function processMail(mail, options) {
|
|
|
2508
2786
|
);
|
|
2509
2787
|
return;
|
|
2510
2788
|
}
|
|
2511
|
-
if (detail.status !== "
|
|
2789
|
+
if (detail.status !== "UNREAD") {
|
|
2512
2790
|
console.log(`[apm] \u4FE1\u4EF6 id=${mail.id} \u72B6\u6001\u4E3A ${detail.status}\uFF0C\u8DF3\u8FC7\u5904\u7406`);
|
|
2513
|
-
if (detail.status === "
|
|
2791
|
+
if (detail.status === "READ") {
|
|
2514
2792
|
markMailReplied(mail.id);
|
|
2515
2793
|
}
|
|
2516
2794
|
return;
|
|
2517
2795
|
}
|
|
2796
|
+
let draftReplyId;
|
|
2518
2797
|
try {
|
|
2519
|
-
await api.cli.claimMailboxMessage({ id: mail.id });
|
|
2798
|
+
const claimed = await api.cli.claimMailboxMessage({ id: mail.id });
|
|
2799
|
+
draftReplyId = claimed.draftReplyId;
|
|
2520
2800
|
} catch (err) {
|
|
2521
2801
|
console.error(
|
|
2522
2802
|
`[apm] \u8BA4\u9886\u4FE1\u4EF6\u5931\u8D25 id=${mail.id}:`,
|
|
@@ -2529,16 +2809,43 @@ async function processMail(mail, options) {
|
|
|
2529
2809
|
tool: replyTool,
|
|
2530
2810
|
getReplyContent,
|
|
2531
2811
|
hasReplyContent
|
|
2532
|
-
} = createMailReplyDraft(
|
|
2812
|
+
} = createMailReplyDraft({
|
|
2813
|
+
appendDraft: async (content) => {
|
|
2814
|
+
await api.cli.appendMailboxDraft({ id: draftReplyId, content });
|
|
2815
|
+
}
|
|
2816
|
+
});
|
|
2533
2817
|
try {
|
|
2534
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
|
+
};
|
|
2535
2842
|
const result = await runCursorAgent(
|
|
2536
2843
|
options.cfg,
|
|
2537
2844
|
{
|
|
2538
2845
|
mailId: detail.id,
|
|
2539
2846
|
taskId: detail.taskId,
|
|
2540
2847
|
prompt: detail.content,
|
|
2541
|
-
model:
|
|
2848
|
+
model: mailContext.model,
|
|
2542
2849
|
apiKey: cursorApiKey,
|
|
2543
2850
|
workdir: detail.workdir,
|
|
2544
2851
|
user: detail.recipient.displayName,
|
|
@@ -2546,9 +2853,16 @@ async function processMail(mail, options) {
|
|
|
2546
2853
|
},
|
|
2547
2854
|
{
|
|
2548
2855
|
signal: options.signal,
|
|
2549
|
-
customTools: { append_mail_reply: replyTool }
|
|
2856
|
+
customTools: { append_mail_reply: replyTool },
|
|
2857
|
+
onAskQuestion: submitQuestions
|
|
2550
2858
|
}
|
|
2551
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
|
+
}
|
|
2552
2866
|
const replyContent = (hasReplyContent() ? getReplyContent() : result.assistantText).trim();
|
|
2553
2867
|
if (!replyContent) {
|
|
2554
2868
|
throw new Error("Agent \u672A\u4EA7\u51FA\u53EF\u63D0\u4EA4\u7684\u56DE\u4FE1\u5185\u5BB9");
|
|
@@ -2562,7 +2876,7 @@ async function processMail(mail, options) {
|
|
|
2562
2876
|
});
|
|
2563
2877
|
}
|
|
2564
2878
|
await api.cli.completeMailboxMessage({
|
|
2565
|
-
id:
|
|
2879
|
+
id: draftReplyId,
|
|
2566
2880
|
status: "SUCCEEDED",
|
|
2567
2881
|
replyContent
|
|
2568
2882
|
});
|
|
@@ -2577,7 +2891,7 @@ async function processMail(mail, options) {
|
|
|
2577
2891
|
console.error(`[apm] \u4FE1\u4EF6\u5904\u7406\u5931\u8D25 id=${mail.id}: ${message}`);
|
|
2578
2892
|
try {
|
|
2579
2893
|
await api.cli.completeMailboxMessage({
|
|
2580
|
-
id:
|
|
2894
|
+
id: draftReplyId,
|
|
2581
2895
|
status: "FAILED",
|
|
2582
2896
|
error: message
|
|
2583
2897
|
});
|
|
@@ -2648,6 +2962,148 @@ function createMailProcessor(options) {
|
|
|
2648
2962
|
};
|
|
2649
2963
|
}
|
|
2650
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
|
+
|
|
2651
3107
|
// src/commands/connect/mail-sync.ts
|
|
2652
3108
|
function parsePendingMail(value) {
|
|
2653
3109
|
if (typeof value !== "object" || value === null) {
|
|
@@ -2740,6 +3196,15 @@ async function runConnect(options) {
|
|
|
2740
3196
|
shutdown(1);
|
|
2741
3197
|
}
|
|
2742
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
|
+
});
|
|
2743
3208
|
const shutdown = (code = 0) => {
|
|
2744
3209
|
if (shuttingDown) return;
|
|
2745
3210
|
shuttingDown = true;
|
|
@@ -2804,6 +3269,10 @@ async function runConnect(options) {
|
|
|
2804
3269
|
}
|
|
2805
3270
|
if (validated.data.type === "received_mail") {
|
|
2806
3271
|
mailProcessor.receive(validated.data);
|
|
3272
|
+
return;
|
|
3273
|
+
}
|
|
3274
|
+
if (validated.data.type === "cursor_agent_resume") {
|
|
3275
|
+
cursorAgentResumeHandler.receive(validated.data);
|
|
2807
3276
|
}
|
|
2808
3277
|
});
|
|
2809
3278
|
ws.on("close", (code, reason) => {
|