ai-project-manage-cli 7.0.4 → 7.0.6
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 +467 -23
- package/package.json +1 -1
- package/template/deploy/README.md +0 -8
package/dist/index.js
CHANGED
|
@@ -177,20 +177,6 @@ function isWorkspaceApmInitialized(workdir) {
|
|
|
177
177
|
}
|
|
178
178
|
return readdirSync(fsApmDir).length > 0;
|
|
179
179
|
}
|
|
180
|
-
function assertWorkspaceApmDirExists(workdir) {
|
|
181
|
-
if (!isWorkspaceApmInitialized(workdir)) {
|
|
182
|
-
const apmDir = workspaceApmDir(workdir);
|
|
183
|
-
const fsApmDir = toFsPath(apmDir);
|
|
184
|
-
if (!existsSync2(fsApmDir)) {
|
|
185
|
-
throw new Error(
|
|
186
|
-
`\u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u4E0B\u672A\u68C0\u6D4B\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5728\u8BE5\u4ED3\u5E93\u6839\u76EE\u5F55\u6267\u884C apm init \u5B8C\u6210\u63A5\u5165\u3002`
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
throw new Error(
|
|
190
|
-
`\u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u4E0B .apm \u76EE\u5F55\u4E3A\u7A7A\uFF0C\u8BF7\u5728\u8BE5\u4ED3\u5E93\u6839\u76EE\u5F55\u6267\u884C apm init \u5B8C\u6210\u63A5\u5165\u3002`
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
180
|
var APM_GITIGNORE_PATTERNS = [
|
|
195
181
|
/^\.apm\/?$/,
|
|
196
182
|
/^\.apm\/\*\*$/,
|
|
@@ -461,6 +447,10 @@ var requestConfig = {
|
|
|
461
447
|
method: "PUT",
|
|
462
448
|
path: "/cli/mailbox-messages/complete"
|
|
463
449
|
}),
|
|
450
|
+
createCursorAgentQuestions: defineEndpoint({
|
|
451
|
+
method: "PUT",
|
|
452
|
+
path: "/cli/cursor-agent/questions"
|
|
453
|
+
}),
|
|
464
454
|
upsertCursorLog: defineEndpoint({
|
|
465
455
|
method: "PUT",
|
|
466
456
|
path: "/cli/cursor-logs"
|
|
@@ -1376,13 +1366,65 @@ function validateReceivedMailPush(o) {
|
|
|
1376
1366
|
}
|
|
1377
1367
|
};
|
|
1378
1368
|
}
|
|
1369
|
+
function validateCursorAgentResumePush(o) {
|
|
1370
|
+
if (o.type !== "cursor_agent_resume") {
|
|
1371
|
+
return { ok: false, reason: "\u671F\u671B cursor_agent_resume" };
|
|
1372
|
+
}
|
|
1373
|
+
if (o.anchorType !== "mailbox") {
|
|
1374
|
+
return { ok: false, reason: "cursor_agent_resume.anchorType \u65E0\u6548" };
|
|
1375
|
+
}
|
|
1376
|
+
if (!nonEmptyString(o.anchorId)) {
|
|
1377
|
+
return { ok: false, reason: "cursor_agent_resume \u7F3A\u5C11 anchorId" };
|
|
1378
|
+
}
|
|
1379
|
+
if (!nonEmptyString(o.taskId)) {
|
|
1380
|
+
return { ok: false, reason: "cursor_agent_resume \u7F3A\u5C11 taskId" };
|
|
1381
|
+
}
|
|
1382
|
+
if (!nonEmptyString(o.cursorAgentId)) {
|
|
1383
|
+
return { ok: false, reason: "cursor_agent_resume \u7F3A\u5C11 cursorAgentId" };
|
|
1384
|
+
}
|
|
1385
|
+
if (!nonEmptyString(o.draftReplyId)) {
|
|
1386
|
+
return { ok: false, reason: "cursor_agent_resume \u7F3A\u5C11 draftReplyId" };
|
|
1387
|
+
}
|
|
1388
|
+
if (!nonEmptyString(o.workdir)) {
|
|
1389
|
+
return { ok: false, reason: "cursor_agent_resume \u7F3A\u5C11 workdir" };
|
|
1390
|
+
}
|
|
1391
|
+
if (!nonEmptyString(o.model)) {
|
|
1392
|
+
return { ok: false, reason: "cursor_agent_resume \u7F3A\u5C11 model" };
|
|
1393
|
+
}
|
|
1394
|
+
if (!nonEmptyString(o.memberDisplayName)) {
|
|
1395
|
+
return { ok: false, reason: "cursor_agent_resume \u7F3A\u5C11 memberDisplayName" };
|
|
1396
|
+
}
|
|
1397
|
+
const mode = o.mode;
|
|
1398
|
+
if (mode !== "plan" && mode !== "agent") {
|
|
1399
|
+
return { ok: false, reason: "cursor_agent_resume.mode \u65E0\u6548" };
|
|
1400
|
+
}
|
|
1401
|
+
if (!nonEmptyString(o.prompt)) {
|
|
1402
|
+
return { ok: false, reason: "cursor_agent_resume \u7F3A\u5C11 prompt" };
|
|
1403
|
+
}
|
|
1404
|
+
return {
|
|
1405
|
+
ok: true,
|
|
1406
|
+
data: {
|
|
1407
|
+
type: "cursor_agent_resume",
|
|
1408
|
+
anchorType: "mailbox",
|
|
1409
|
+
anchorId: o.anchorId.trim(),
|
|
1410
|
+
taskId: o.taskId.trim(),
|
|
1411
|
+
cursorAgentId: o.cursorAgentId.trim(),
|
|
1412
|
+
draftReplyId: o.draftReplyId.trim(),
|
|
1413
|
+
workdir: o.workdir.trim(),
|
|
1414
|
+
model: o.model.trim(),
|
|
1415
|
+
memberDisplayName: o.memberDisplayName.trim(),
|
|
1416
|
+
mode,
|
|
1417
|
+
prompt: o.prompt.trim()
|
|
1418
|
+
}
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1379
1421
|
function validateAgentWsMessage(value, kind) {
|
|
1380
1422
|
if (typeof value !== "object" || value === null) {
|
|
1381
1423
|
return { ok: false, reason: "\u6D88\u606F\u4F53\u4E0D\u662F JSON \u5BF9\u8C61" };
|
|
1382
1424
|
}
|
|
1383
1425
|
const o = value;
|
|
1384
1426
|
const type = o.type;
|
|
1385
|
-
if (type !== "heartbeat" && type !== "deploy" && type !== "received_mail") {
|
|
1427
|
+
if (type !== "heartbeat" && type !== "deploy" && type !== "received_mail" && type !== "cursor_agent_resume") {
|
|
1386
1428
|
return { ok: false, reason: `\u672A\u77E5 type: ${String(type)}` };
|
|
1387
1429
|
}
|
|
1388
1430
|
if (kind === "heartbeat" || type === "heartbeat") {
|
|
@@ -1391,6 +1433,9 @@ function validateAgentWsMessage(value, kind) {
|
|
|
1391
1433
|
if (type === "deploy") {
|
|
1392
1434
|
return validateDeployPush(o);
|
|
1393
1435
|
}
|
|
1436
|
+
if (type === "cursor_agent_resume") {
|
|
1437
|
+
return validateCursorAgentResumePush(o);
|
|
1438
|
+
}
|
|
1394
1439
|
return validateReceivedMailPush(o);
|
|
1395
1440
|
}
|
|
1396
1441
|
|
|
@@ -2010,6 +2055,176 @@ function clearTaskAgentId(workdir, taskId, user) {
|
|
|
2010
2055
|
writeRegistry(path13, registry);
|
|
2011
2056
|
}
|
|
2012
2057
|
|
|
2058
|
+
// src/commands/connect/ask-question-tool.ts
|
|
2059
|
+
import { randomUUID } from "node:crypto";
|
|
2060
|
+
|
|
2061
|
+
// src/ask-question.ts
|
|
2062
|
+
function nonEmptyString2(value) {
|
|
2063
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
2064
|
+
}
|
|
2065
|
+
function parseOption(value, index) {
|
|
2066
|
+
if (typeof value !== "object" || value === null) {
|
|
2067
|
+
return `questions[].options[${index}] \u5FC5\u987B\u662F\u5BF9\u8C61`;
|
|
2068
|
+
}
|
|
2069
|
+
const option = value;
|
|
2070
|
+
if (!nonEmptyString2(option.id)) {
|
|
2071
|
+
return `questions[].options[${index}].id \u4E0D\u80FD\u4E3A\u7A7A`;
|
|
2072
|
+
}
|
|
2073
|
+
if (!nonEmptyString2(option.label)) {
|
|
2074
|
+
return `questions[].options[${index}].label \u4E0D\u80FD\u4E3A\u7A7A`;
|
|
2075
|
+
}
|
|
2076
|
+
return {
|
|
2077
|
+
id: option.id.trim(),
|
|
2078
|
+
label: option.label.trim()
|
|
2079
|
+
};
|
|
2080
|
+
}
|
|
2081
|
+
function parseQuestion(value, index) {
|
|
2082
|
+
if (typeof value !== "object" || value === null) {
|
|
2083
|
+
return `questions[${index}] \u5FC5\u987B\u662F\u5BF9\u8C61`;
|
|
2084
|
+
}
|
|
2085
|
+
const question = value;
|
|
2086
|
+
if (!nonEmptyString2(question.id)) {
|
|
2087
|
+
return `questions[${index}].id \u4E0D\u80FD\u4E3A\u7A7A`;
|
|
2088
|
+
}
|
|
2089
|
+
if (!nonEmptyString2(question.prompt)) {
|
|
2090
|
+
return `questions[${index}].prompt \u4E0D\u80FD\u4E3A\u7A7A`;
|
|
2091
|
+
}
|
|
2092
|
+
if (!Array.isArray(question.options)) {
|
|
2093
|
+
return `questions[${index}].options \u5FC5\u987B\u662F\u6570\u7EC4`;
|
|
2094
|
+
}
|
|
2095
|
+
if (question.options.length < 2) {
|
|
2096
|
+
return `questions[${index}].options \u81F3\u5C11\u9700\u8981 2 \u4E2A\u9009\u9879`;
|
|
2097
|
+
}
|
|
2098
|
+
const options = [];
|
|
2099
|
+
for (let i = 0; i < question.options.length; i++) {
|
|
2100
|
+
const parsed = parseOption(question.options[i], i);
|
|
2101
|
+
if (typeof parsed === "string") {
|
|
2102
|
+
return parsed;
|
|
2103
|
+
}
|
|
2104
|
+
options.push(parsed);
|
|
2105
|
+
}
|
|
2106
|
+
const ids = new Set(options.map((option) => option.id));
|
|
2107
|
+
if (ids.size !== options.length) {
|
|
2108
|
+
return `questions[${index}].options \u7684 id \u4E0D\u80FD\u91CD\u590D`;
|
|
2109
|
+
}
|
|
2110
|
+
return {
|
|
2111
|
+
id: question.id.trim(),
|
|
2112
|
+
prompt: question.prompt.trim(),
|
|
2113
|
+
...question.allow_multiple === true ? { allow_multiple: true } : {},
|
|
2114
|
+
options
|
|
2115
|
+
};
|
|
2116
|
+
}
|
|
2117
|
+
function validateAskQuestionInput(value) {
|
|
2118
|
+
if (typeof value !== "object" || value === null) {
|
|
2119
|
+
return { ok: false, error: "AskQuestion \u5165\u53C2\u5FC5\u987B\u662F\u5BF9\u8C61" };
|
|
2120
|
+
}
|
|
2121
|
+
const input = value;
|
|
2122
|
+
if (!Array.isArray(input.questions)) {
|
|
2123
|
+
return { ok: false, error: "questions \u5FC5\u987B\u662F\u6570\u7EC4" };
|
|
2124
|
+
}
|
|
2125
|
+
if (input.questions.length < 1) {
|
|
2126
|
+
return { ok: false, error: "questions \u81F3\u5C11\u9700\u8981 1 \u9053\u9898" };
|
|
2127
|
+
}
|
|
2128
|
+
const questions = [];
|
|
2129
|
+
for (let i = 0; i < input.questions.length; i++) {
|
|
2130
|
+
const parsed = parseQuestion(input.questions[i], i);
|
|
2131
|
+
if (typeof parsed === "string") {
|
|
2132
|
+
return { ok: false, error: parsed };
|
|
2133
|
+
}
|
|
2134
|
+
questions.push(parsed);
|
|
2135
|
+
}
|
|
2136
|
+
const questionIds = new Set(questions.map((question) => question.id));
|
|
2137
|
+
if (questionIds.size !== questions.length) {
|
|
2138
|
+
return { ok: false, error: "questions[].id \u4E0D\u80FD\u91CD\u590D" };
|
|
2139
|
+
}
|
|
2140
|
+
const title = nonEmptyString2(input.title) ? input.title.trim() : void 0;
|
|
2141
|
+
return {
|
|
2142
|
+
ok: true,
|
|
2143
|
+
input: {
|
|
2144
|
+
...title ? { title } : {},
|
|
2145
|
+
questions
|
|
2146
|
+
}
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
// src/commands/connect/ask-question-tool.ts
|
|
2151
|
+
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.";
|
|
2152
|
+
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";
|
|
2153
|
+
function createAskQuestionTool(handlers) {
|
|
2154
|
+
const batches = [];
|
|
2155
|
+
const tool = {
|
|
2156
|
+
description: ASK_QUESTION_DESCRIPTION,
|
|
2157
|
+
inputSchema: {
|
|
2158
|
+
type: "object",
|
|
2159
|
+
properties: {
|
|
2160
|
+
title: {
|
|
2161
|
+
type: "string",
|
|
2162
|
+
description: "Optional title for the question form"
|
|
2163
|
+
},
|
|
2164
|
+
questions: {
|
|
2165
|
+
type: "array",
|
|
2166
|
+
description: "Questions to ask the user",
|
|
2167
|
+
items: {
|
|
2168
|
+
type: "object",
|
|
2169
|
+
properties: {
|
|
2170
|
+
id: {
|
|
2171
|
+
type: "string",
|
|
2172
|
+
description: "Unique identifier for this question"
|
|
2173
|
+
},
|
|
2174
|
+
prompt: {
|
|
2175
|
+
type: "string",
|
|
2176
|
+
description: "The question text to display"
|
|
2177
|
+
},
|
|
2178
|
+
allow_multiple: {
|
|
2179
|
+
type: "boolean",
|
|
2180
|
+
description: "Whether the user may select multiple options"
|
|
2181
|
+
},
|
|
2182
|
+
options: {
|
|
2183
|
+
type: "array",
|
|
2184
|
+
description: "At least 2 answer options",
|
|
2185
|
+
items: {
|
|
2186
|
+
type: "object",
|
|
2187
|
+
properties: {
|
|
2188
|
+
id: { type: "string" },
|
|
2189
|
+
label: { type: "string" }
|
|
2190
|
+
},
|
|
2191
|
+
required: ["id", "label"]
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
},
|
|
2195
|
+
required: ["id", "prompt", "options"]
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
},
|
|
2199
|
+
required: ["questions"]
|
|
2200
|
+
},
|
|
2201
|
+
execute: async (args) => {
|
|
2202
|
+
const validated = validateAskQuestionInput(args);
|
|
2203
|
+
if (!validated.ok) {
|
|
2204
|
+
return {
|
|
2205
|
+
content: [{ type: "text", text: validated.error }],
|
|
2206
|
+
isError: true
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
const batch = {
|
|
2210
|
+
batchId: randomUUID(),
|
|
2211
|
+
...validated.input
|
|
2212
|
+
};
|
|
2213
|
+
batches.push(batch);
|
|
2214
|
+
await handlers.onAskQuestion(batch);
|
|
2215
|
+
console.log(
|
|
2216
|
+
`[apm] AskQuestion \u5DF2\u63D0\u4EA4 batchId=${batch.batchId} questions=${batch.questions.length}`
|
|
2217
|
+
);
|
|
2218
|
+
return ASK_QUESTION_WAIT_MESSAGE;
|
|
2219
|
+
}
|
|
2220
|
+
};
|
|
2221
|
+
return {
|
|
2222
|
+
tool,
|
|
2223
|
+
getAskedBatches: () => [...batches],
|
|
2224
|
+
hasPendingQuestions: () => batches.length > 0
|
|
2225
|
+
};
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2013
2228
|
// src/commands/connect/cursor-log.ts
|
|
2014
2229
|
var CURSOR_LOG_SYNC_INTERVAL_MS = 2e3;
|
|
2015
2230
|
function createThrottledCursorLogSync(cfg, ctx, onError) {
|
|
@@ -2136,6 +2351,34 @@ async function obtainAgent(ctx) {
|
|
|
2136
2351
|
}
|
|
2137
2352
|
return { agent, resumed: false };
|
|
2138
2353
|
}
|
|
2354
|
+
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`;
|
|
2355
|
+
function buildPrompt(basePrompt, taskId, mode, promptFooter, hasAskQuestion) {
|
|
2356
|
+
const trimmed = basePrompt.trim();
|
|
2357
|
+
let footer;
|
|
2358
|
+
if (promptFooter === null || promptFooter === "") {
|
|
2359
|
+
footer = "";
|
|
2360
|
+
} else if (promptFooter === void 0) {
|
|
2361
|
+
footer = DEFAULT_MAIL_PROMPT_FOOTER(taskId);
|
|
2362
|
+
} else {
|
|
2363
|
+
footer = promptFooter;
|
|
2364
|
+
}
|
|
2365
|
+
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" : "";
|
|
2366
|
+
const parts = [trimmed];
|
|
2367
|
+
const hints = [footer, askQuestionHint].filter(Boolean);
|
|
2368
|
+
if (hints.length > 0) {
|
|
2369
|
+
parts.push("---", ...hints);
|
|
2370
|
+
}
|
|
2371
|
+
return parts.join("\n\n");
|
|
2372
|
+
}
|
|
2373
|
+
function mergeCustomTools(customTools, askQuestionTool) {
|
|
2374
|
+
if (!askQuestionTool) {
|
|
2375
|
+
return customTools;
|
|
2376
|
+
}
|
|
2377
|
+
return {
|
|
2378
|
+
...customTools,
|
|
2379
|
+
AskQuestion: askQuestionTool
|
|
2380
|
+
};
|
|
2381
|
+
}
|
|
2139
2382
|
async function runCursorAgent(cfg, ctx, options) {
|
|
2140
2383
|
const signal = options?.signal;
|
|
2141
2384
|
logAbortSignalStats(signal, "runCursorAgent:start");
|
|
@@ -2147,12 +2390,24 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2147
2390
|
throw new Error("\u7F3A\u5C11 Cursor API Key");
|
|
2148
2391
|
}
|
|
2149
2392
|
const workdir = requireExistingWorkdir(ctx.workdir);
|
|
2150
|
-
const
|
|
2151
|
-
const
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2393
|
+
const baseCustomTools = options?.customTools ?? {};
|
|
2394
|
+
const agentContext = { agentId: "" };
|
|
2395
|
+
const askQuestion = options?.onAskQuestion ? createAskQuestionTool({
|
|
2396
|
+
onAskQuestion: async (batch) => {
|
|
2397
|
+
await options.onAskQuestion(batch, {
|
|
2398
|
+
agentId: agentContext.agentId
|
|
2399
|
+
});
|
|
2400
|
+
}
|
|
2401
|
+
}) : void 0;
|
|
2402
|
+
const customTools = mergeCustomTools(baseCustomTools, askQuestion?.tool);
|
|
2155
2403
|
const mode = ctx.mode ?? "agent";
|
|
2404
|
+
const prompt = buildPrompt(
|
|
2405
|
+
ctx.prompt,
|
|
2406
|
+
ctx.taskId,
|
|
2407
|
+
mode,
|
|
2408
|
+
options?.promptFooter,
|
|
2409
|
+
Boolean(askQuestion)
|
|
2410
|
+
);
|
|
2156
2411
|
console.log(
|
|
2157
2412
|
`[apm] Cursor Agent \u5F00\u59CB mailId=${ctx.mailId} taskId=${ctx.taskId} mode=${mode} cwd=${workdir}`
|
|
2158
2413
|
);
|
|
@@ -2165,6 +2420,7 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2165
2420
|
mode,
|
|
2166
2421
|
customTools
|
|
2167
2422
|
});
|
|
2423
|
+
agentContext.agentId = agent.agentId;
|
|
2168
2424
|
const eventSession = new EventSession(prompt);
|
|
2169
2425
|
const syncRemoteLog = options?.skipRemoteLogSync ? noopRemoteLogSync : createThrottledCursorLogSync(
|
|
2170
2426
|
cfg,
|
|
@@ -2223,11 +2479,14 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2223
2479
|
throw new Error(`Cursor run \u5DF2\u53D6\u6D88: ${result.id}`);
|
|
2224
2480
|
}
|
|
2225
2481
|
console.log(`[apm] Cursor Agent \u5B8C\u6210 mailId=${ctx.mailId}`);
|
|
2482
|
+
const askQuestionBatches = askQuestion?.getAskedBatches() ?? [];
|
|
2226
2483
|
return {
|
|
2227
2484
|
runId: result.id,
|
|
2228
2485
|
agentId: agent.agentId,
|
|
2229
2486
|
status: result.status,
|
|
2230
2487
|
assistantText: assistantText || result.result || "",
|
|
2488
|
+
askQuestionBatches,
|
|
2489
|
+
hasPendingQuestions: askQuestionBatches.length > 0,
|
|
2231
2490
|
...createPlanContent ? { createPlanContent } : {}
|
|
2232
2491
|
};
|
|
2233
2492
|
} catch (err) {
|
|
@@ -2433,7 +2692,7 @@ async function syncPlatformRules(cfg, workdirPath, apmRoot) {
|
|
|
2433
2692
|
// src/commands/connect/task-pull.ts
|
|
2434
2693
|
async function runTaskPull(cfg, detail) {
|
|
2435
2694
|
const { taskId, workdir } = detail;
|
|
2436
|
-
|
|
2695
|
+
await ensureWorkspaceInitialized(workdir);
|
|
2437
2696
|
const apmRoot = workspaceApmDir(workdir);
|
|
2438
2697
|
const dir = taskDir(taskId, apmRoot, workdir);
|
|
2439
2698
|
const docsDir = taskDocsDir(taskId, apmRoot, workdir);
|
|
@@ -2543,13 +2802,36 @@ async function processMail(mail, options) {
|
|
|
2543
2802
|
});
|
|
2544
2803
|
try {
|
|
2545
2804
|
const cursorMode = resolveCursorAgentMode(detail.studio?.phase);
|
|
2805
|
+
const mailContext = {
|
|
2806
|
+
mailId: detail.id,
|
|
2807
|
+
taskId: detail.taskId,
|
|
2808
|
+
draftReplyId,
|
|
2809
|
+
workdir: detail.workdir,
|
|
2810
|
+
model: detail.recipient.model?.trim() || "default",
|
|
2811
|
+
memberDisplayName: detail.recipient.displayName,
|
|
2812
|
+
cursorMode
|
|
2813
|
+
};
|
|
2814
|
+
const submitQuestions = async (batch, ctx) => {
|
|
2815
|
+
await api.cli.createCursorAgentQuestions({
|
|
2816
|
+
anchorType: "mailbox",
|
|
2817
|
+
anchorId: mailContext.mailId,
|
|
2818
|
+
taskId: mailContext.taskId,
|
|
2819
|
+
cursorAgentId: ctx.agentId,
|
|
2820
|
+
draftReplyId: mailContext.draftReplyId,
|
|
2821
|
+
workdir: mailContext.workdir,
|
|
2822
|
+
model: mailContext.model,
|
|
2823
|
+
memberDisplayName: mailContext.memberDisplayName,
|
|
2824
|
+
mode: mailContext.cursorMode,
|
|
2825
|
+
batch
|
|
2826
|
+
});
|
|
2827
|
+
};
|
|
2546
2828
|
const result = await runCursorAgent(
|
|
2547
2829
|
options.cfg,
|
|
2548
2830
|
{
|
|
2549
2831
|
mailId: detail.id,
|
|
2550
2832
|
taskId: detail.taskId,
|
|
2551
2833
|
prompt: detail.content,
|
|
2552
|
-
model:
|
|
2834
|
+
model: mailContext.model,
|
|
2553
2835
|
apiKey: cursorApiKey,
|
|
2554
2836
|
workdir: detail.workdir,
|
|
2555
2837
|
user: detail.recipient.displayName,
|
|
@@ -2557,9 +2839,16 @@ async function processMail(mail, options) {
|
|
|
2557
2839
|
},
|
|
2558
2840
|
{
|
|
2559
2841
|
signal: options.signal,
|
|
2560
|
-
customTools: { append_mail_reply: replyTool }
|
|
2842
|
+
customTools: { append_mail_reply: replyTool },
|
|
2843
|
+
onAskQuestion: submitQuestions
|
|
2561
2844
|
}
|
|
2562
2845
|
);
|
|
2846
|
+
if (result.hasPendingQuestions) {
|
|
2847
|
+
console.log(
|
|
2848
|
+
`[apm] \u4FE1\u4EF6 id=${mail.id} \u6709\u5F85\u7B54 AskQuestion\uFF0C\u7B49\u5F85\u7528\u6237\u5728\u5DE5\u4F5C\u5BA4\u7B54\u9898\u540E\u7EED\u804A`
|
|
2849
|
+
);
|
|
2850
|
+
return;
|
|
2851
|
+
}
|
|
2563
2852
|
const replyContent = (hasReplyContent() ? getReplyContent() : result.assistantText).trim();
|
|
2564
2853
|
if (!replyContent) {
|
|
2565
2854
|
throw new Error("Agent \u672A\u4EA7\u51FA\u53EF\u63D0\u4EA4\u7684\u56DE\u4FE1\u5185\u5BB9");
|
|
@@ -2659,6 +2948,148 @@ function createMailProcessor(options) {
|
|
|
2659
2948
|
};
|
|
2660
2949
|
}
|
|
2661
2950
|
|
|
2951
|
+
// src/commands/connect/cursor-agent-resume-handler.ts
|
|
2952
|
+
async function runMailboxCursorAgentResume(push, options) {
|
|
2953
|
+
const cursorApiKey = options.getCursorApiKey().trim();
|
|
2954
|
+
if (!cursorApiKey) {
|
|
2955
|
+
options.onFatalError(
|
|
2956
|
+
"[apm] \u5BA2\u6237\u673A\u672A\u914D\u7F6E Cursor API Key\uFF0C\u65E0\u6CD5\u7EED\u804A Cursor Agent"
|
|
2957
|
+
);
|
|
2958
|
+
return;
|
|
2959
|
+
}
|
|
2960
|
+
const api = createApmApiClient(options.cfg);
|
|
2961
|
+
let detail;
|
|
2962
|
+
try {
|
|
2963
|
+
detail = await api.cli.getMailboxMessageDetail({ id: push.anchorId });
|
|
2964
|
+
} catch (err) {
|
|
2965
|
+
console.error(
|
|
2966
|
+
`[apm] \u7EED\u804A\u83B7\u53D6\u4FE1\u4EF6\u8BE6\u60C5\u5931\u8D25 id=${push.anchorId}:`,
|
|
2967
|
+
err instanceof Error ? err.message : err
|
|
2968
|
+
);
|
|
2969
|
+
return;
|
|
2970
|
+
}
|
|
2971
|
+
const {
|
|
2972
|
+
tool: replyTool,
|
|
2973
|
+
getReplyContent,
|
|
2974
|
+
hasReplyContent
|
|
2975
|
+
} = createMailReplyDraft({
|
|
2976
|
+
appendDraft: async (content) => {
|
|
2977
|
+
await api.cli.appendMailboxDraft({
|
|
2978
|
+
id: push.draftReplyId,
|
|
2979
|
+
content
|
|
2980
|
+
});
|
|
2981
|
+
}
|
|
2982
|
+
});
|
|
2983
|
+
const cursorMode = push.mode;
|
|
2984
|
+
const submitQuestions = async (batch, ctx) => {
|
|
2985
|
+
await api.cli.createCursorAgentQuestions({
|
|
2986
|
+
anchorType: "mailbox",
|
|
2987
|
+
anchorId: push.anchorId,
|
|
2988
|
+
taskId: push.taskId,
|
|
2989
|
+
cursorAgentId: ctx.agentId,
|
|
2990
|
+
draftReplyId: push.draftReplyId,
|
|
2991
|
+
workdir: push.workdir,
|
|
2992
|
+
model: push.model,
|
|
2993
|
+
memberDisplayName: push.memberDisplayName,
|
|
2994
|
+
mode: cursorMode,
|
|
2995
|
+
batch
|
|
2996
|
+
});
|
|
2997
|
+
};
|
|
2998
|
+
try {
|
|
2999
|
+
const result = await runCursorAgent(
|
|
3000
|
+
options.cfg,
|
|
3001
|
+
{
|
|
3002
|
+
mailId: push.anchorId,
|
|
3003
|
+
taskId: push.taskId,
|
|
3004
|
+
prompt: push.prompt,
|
|
3005
|
+
model: push.model,
|
|
3006
|
+
apiKey: cursorApiKey,
|
|
3007
|
+
workdir: push.workdir,
|
|
3008
|
+
user: push.memberDisplayName,
|
|
3009
|
+
mode: cursorMode
|
|
3010
|
+
},
|
|
3011
|
+
{
|
|
3012
|
+
signal: options.signal,
|
|
3013
|
+
customTools: { append_mail_reply: replyTool },
|
|
3014
|
+
onAskQuestion: submitQuestions,
|
|
3015
|
+
forceSend: true
|
|
3016
|
+
}
|
|
3017
|
+
);
|
|
3018
|
+
if (result.hasPendingQuestions) {
|
|
3019
|
+
console.log(
|
|
3020
|
+
`[apm] \u7EED\u804A\u540E\u4ECD\u6709\u5F85\u7B54 AskQuestion mailId=${push.anchorId}\uFF0C\u7B49\u5F85\u7528\u6237\u7B54\u9898`
|
|
3021
|
+
);
|
|
3022
|
+
return;
|
|
3023
|
+
}
|
|
3024
|
+
const replyContent = (hasReplyContent() ? getReplyContent() : result.assistantText).trim();
|
|
3025
|
+
if (!replyContent) {
|
|
3026
|
+
throw new Error("Agent \u672A\u4EA7\u51FA\u53EF\u63D0\u4EA4\u7684\u56DE\u4FE1\u5185\u5BB9");
|
|
3027
|
+
}
|
|
3028
|
+
if (cursorMode === "plan" && result.createPlanContent) {
|
|
3029
|
+
saveMemberPlanDocument({
|
|
3030
|
+
workdir: push.workdir,
|
|
3031
|
+
taskId: push.taskId,
|
|
3032
|
+
memberDisplayName: push.memberDisplayName,
|
|
3033
|
+
content: result.createPlanContent
|
|
3034
|
+
});
|
|
3035
|
+
}
|
|
3036
|
+
await api.cli.completeMailboxMessage({
|
|
3037
|
+
id: push.draftReplyId,
|
|
3038
|
+
status: "SUCCEEDED",
|
|
3039
|
+
replyContent
|
|
3040
|
+
});
|
|
3041
|
+
markMailReplied(push.anchorId);
|
|
3042
|
+
await syncTaskDocuments(options.cfg, push.taskId, push.workdir, {
|
|
3043
|
+
api,
|
|
3044
|
+
remoteDocuments: detail.documents
|
|
3045
|
+
});
|
|
3046
|
+
console.log(`[apm] Cursor Agent \u7EED\u804A\u5B8C\u6210 mailId=${push.anchorId}`);
|
|
3047
|
+
} catch (err) {
|
|
3048
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3049
|
+
console.error(
|
|
3050
|
+
`[apm] Cursor Agent \u7EED\u804A\u5931\u8D25 mailId=${push.anchorId}: ${message}`
|
|
3051
|
+
);
|
|
3052
|
+
try {
|
|
3053
|
+
await api.cli.completeMailboxMessage({
|
|
3054
|
+
id: push.draftReplyId,
|
|
3055
|
+
status: "FAILED",
|
|
3056
|
+
error: message
|
|
3057
|
+
});
|
|
3058
|
+
} catch (completeErr) {
|
|
3059
|
+
console.error(
|
|
3060
|
+
`[apm] \u6807\u8BB0\u7EED\u804A\u5931\u8D25\u72B6\u6001\u65F6\u51FA\u9519 mailId=${push.anchorId}:`,
|
|
3061
|
+
completeErr instanceof Error ? completeErr.message : completeErr
|
|
3062
|
+
);
|
|
3063
|
+
}
|
|
3064
|
+
markMailReplied(push.anchorId);
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
function createCursorAgentResumeHandler(options) {
|
|
3068
|
+
let running = false;
|
|
3069
|
+
return {
|
|
3070
|
+
receive(push) {
|
|
3071
|
+
if (options.signal.aborted) {
|
|
3072
|
+
return;
|
|
3073
|
+
}
|
|
3074
|
+
if (running) {
|
|
3075
|
+
console.warn(
|
|
3076
|
+
`[apm] \u5DF2\u6709 Cursor Agent \u7EED\u804A\u4EFB\u52A1\u8FDB\u884C\u4E2D\uFF0C\u5FFD\u7565 mailId=${push.anchorId}`
|
|
3077
|
+
);
|
|
3078
|
+
return;
|
|
3079
|
+
}
|
|
3080
|
+
running = true;
|
|
3081
|
+
void runMailboxCursorAgentResume(push, options).catch((err) => {
|
|
3082
|
+
console.error(
|
|
3083
|
+
`[apm] Cursor Agent \u7EED\u804A\u5F02\u5E38 mailId=${push.anchorId}:`,
|
|
3084
|
+
err instanceof Error ? err.message : err
|
|
3085
|
+
);
|
|
3086
|
+
}).finally(() => {
|
|
3087
|
+
running = false;
|
|
3088
|
+
});
|
|
3089
|
+
}
|
|
3090
|
+
};
|
|
3091
|
+
}
|
|
3092
|
+
|
|
2662
3093
|
// src/commands/connect/mail-sync.ts
|
|
2663
3094
|
function parsePendingMail(value) {
|
|
2664
3095
|
if (typeof value !== "object" || value === null) {
|
|
@@ -2751,6 +3182,15 @@ async function runConnect(options) {
|
|
|
2751
3182
|
shutdown(1);
|
|
2752
3183
|
}
|
|
2753
3184
|
});
|
|
3185
|
+
const cursorAgentResumeHandler = createCursorAgentResumeHandler({
|
|
3186
|
+
cfg,
|
|
3187
|
+
signal: shutdownAbort.signal,
|
|
3188
|
+
getCursorApiKey: () => cursorApiKey,
|
|
3189
|
+
onFatalError: (message) => {
|
|
3190
|
+
console.error(message);
|
|
3191
|
+
shutdown(1);
|
|
3192
|
+
}
|
|
3193
|
+
});
|
|
2754
3194
|
const shutdown = (code = 0) => {
|
|
2755
3195
|
if (shuttingDown) return;
|
|
2756
3196
|
shuttingDown = true;
|
|
@@ -2815,6 +3255,10 @@ async function runConnect(options) {
|
|
|
2815
3255
|
}
|
|
2816
3256
|
if (validated.data.type === "received_mail") {
|
|
2817
3257
|
mailProcessor.receive(validated.data);
|
|
3258
|
+
return;
|
|
3259
|
+
}
|
|
3260
|
+
if (validated.data.type === "cursor_agent_resume") {
|
|
3261
|
+
cursorAgentResumeHandler.receive(validated.data);
|
|
2818
3262
|
}
|
|
2819
3263
|
});
|
|
2820
3264
|
ws.on("close", (code, reason) => {
|
package/package.json
CHANGED