@arcships/morula-runtime 0.1.0-alpha.15 → 0.1.0-alpha.17

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/morula.js CHANGED
@@ -704,8 +704,8 @@ function page(ok, message) {
704
704
  }
705
705
 
706
706
  // ../morula-cli-core/dist/build-env.js
707
- var BUILD_VERSION = "0.1.0-alpha.15";
708
- var BUILD_ID = "20260924065153.0fc1a372";
707
+ var BUILD_VERSION = "0.1.0-alpha.17";
708
+ var BUILD_ID = "20260930065011.80ac5142";
709
709
  var BUILD_CONTEXTS = {
710
710
  "local": "http://localhost:8080/api",
711
711
  "test": "https://morula.basecastle.com/api",
@@ -794,13 +794,6 @@ var AttachmentApi = class {
794
794
  getDownloadUrl(id) {
795
795
  return this.client.get(`/attachments/${encodeURIComponent(id)}/download-url`);
796
796
  }
797
- /**
798
- * 读取附件的解析产物全文(MinerU markdown)。
799
- * 未解析/解析中/解析失败由服务端在返回体里说明,不当作 HTTP 错误。
800
- */
801
- content(id, maxChars) {
802
- return this.client.get(`/attachments/${encodeURIComponent(id)}/content`, { maxChars });
803
- }
804
797
  /** 更新附件关联的资源 ID */
805
798
  updateResourceId(id, resourceId) {
806
799
  return this.client.patch(`/attachments/${encodeURIComponent(id)}`, { resourceId });
@@ -2153,13 +2146,6 @@ async function attachmentCommand(context) {
2153
2146
  rejectUnknownFlags(flags, []);
2154
2147
  return context.client.attachments.get(requireId(positionals, "attachment get"));
2155
2148
  }
2156
- if (action === "content") {
2157
- rejectUnknownFlags(flags, ["max-chars"]);
2158
- return context.client.attachments.content(
2159
- requireId(positionals, "attachment content"),
2160
- flagNumber(flags, "max-chars")
2161
- );
2162
- }
2163
2149
  if (action === "download-url") {
2164
2150
  rejectUnknownFlags(flags, []);
2165
2151
  return context.client.attachments.getDownloadUrl(requireId(positionals, "attachment download-url"));
@@ -2436,21 +2422,31 @@ async function commentCommand(context) {
2436
2422
  return context.client.comments.get(id);
2437
2423
  }
2438
2424
  if (action === "add") {
2439
- rejectUnknownFlags(flags, ["object", "content", "parent", "format", "mention"]);
2425
+ rejectUnknownFlags(flags, ["object", "content", "parent", "format", "mention", "mention-agent", "final"]);
2440
2426
  const objectId = requireFlag(flags, "object");
2441
2427
  const content = requireFlag(flags, "content");
2442
2428
  const formatFlag = flagString(flags, "format");
2443
2429
  const format = formatFlag === "markdown" || formatFlag === "plain" || formatFlag === "richtext" ? formatFlag : detectCommentFormat(content);
2444
- const mentions = parseMentions(flagStrings(context.parsed, "mention"));
2430
+ const mentions = [
2431
+ ...parseMentions(flagStrings(context.parsed, "mention")),
2432
+ ...parseAgentMentions(flagStrings(context.parsed, "mention-agent"))
2433
+ ];
2434
+ const isFinal = flagBoolean(flags, "final");
2445
2435
  const input = compact({
2446
2436
  objectId,
2447
2437
  content,
2448
2438
  parentCommentId: flagString(flags, "parent"),
2449
2439
  contentFormat: format,
2450
- // 无 --mention 时**不带** mentions 字段(服务端按缺省处理,不发任何通知)
2451
- ...mentions.length ? { mentions } : {}
2440
+ // 无 --mention / --mention-agent 时**不带** mentions 字段(服务端按缺省处理,不发任何通知)
2441
+ ...mentions.length ? { mentions } : {},
2442
+ ...isFinal ? { kind: "final" } : {}
2443
+ });
2444
+ return context.client.comments.create(input).catch((error) => {
2445
+ if (error?.code === "FINAL_ALREADY_EXISTS" || error?.response?.data?.code === "FINAL_ALREADY_EXISTS") {
2446
+ throw new CliError("FINAL_ALREADY_EXISTS", EXIT.OTHER, "\u8BE5\u4EFB\u52A1\u5DF2\u6709\u6700\u7EC8\u603B\u7ED3\u8BC4\u8BBA\uFF08\u540C\u4E00\u4EFB\u52A1\u53EA\u4FDD\u7559\u4E00\u6761\uFF0C\u91CD\u8BD5/\u91CD\u8DD1\u4E0D\u91CD\u590D\u53D1\u9001\uFF09");
2447
+ }
2448
+ throw error;
2452
2449
  });
2453
- return context.client.comments.create(input);
2454
2450
  }
2455
2451
  if (action === "update") {
2456
2452
  rejectUnknownFlags(flags, ["content"]);
@@ -2488,6 +2484,20 @@ function parseMentions(rawValues) {
2488
2484
  }
2489
2485
  return mentions;
2490
2486
  }
2487
+ function parseAgentMentions(rawValues) {
2488
+ const seen = /* @__PURE__ */ new Set();
2489
+ const mentions = [];
2490
+ for (const raw of rawValues) {
2491
+ for (const part of raw.split(",")) {
2492
+ const personaId = part.trim();
2493
+ if (!personaId || seen.has(personaId))
2494
+ continue;
2495
+ seen.add(personaId);
2496
+ mentions.push({ type: "agent", personaId });
2497
+ }
2498
+ }
2499
+ return mentions;
2500
+ }
2491
2501
 
2492
2502
  // ../morula-cli/src/commands/dashboard.ts
2493
2503
  async function dashboardCommand(context) {
@@ -3322,10 +3332,23 @@ var SPEC = {
3322
3332
  },
3323
3333
  {
3324
3334
  action: "state",
3325
- summary: "self-transition task state (machine protocol; agent drives todo\u2192in_progress\u2192review, runtimeId from env)",
3335
+ summary: "self-transition task state (machine protocol; agent drives todo\u2192in_progress\u2192review; review\u2192in_progress = \u9000\u56DE\u5F00\u53D1\uFF0C\u5E73\u53F0\u81EA\u52A8\u6D3E\u4FEE\u590D\u8F6E\u6B21)",
3326
3336
  args: [{ name: "taskId", required: true, description: "\u672C\u4EFB\u52A1 AgentTask UUID" }],
3327
- flags: [{ name: "state", type: "string", valueHint: "<in_progress|review>", description: "\u76EE\u6807\u72B6\u6001 code\uFF08\u5FC5\u586B\uFF09", required: true }],
3328
- examples: ["morula task state <taskId> --state in_progress"],
3337
+ flags: [
3338
+ { name: "state", type: "string", valueHint: "<in_progress|review>", description: "\u76EE\u6807\u72B6\u6001 code\uFF08\u5FC5\u586B\uFF09\uFF1Breview\u2192in_progress = \u9000\u56DE\u5F00\u53D1\uFF08\u81EA\u52A8\u6D3E\u4FEE\u590D\uFF0C\u4E0D\u9700\u8981\u518D\u81EA\u5DF1 @ \u5F00\u53D1\u6267\u884C\u5668\uFF09", required: true },
3339
+ { name: "reason", type: "string", valueHint: "<text>", description: "\u6D41\u8F6C\u539F\u56E0\uFF08\u9000\u56DE\u5F00\u53D1\u65F6\u5199\u8FDB\u8F6E\u6B21\u8BB0\u8D26\u4E0E\u4FEE\u590D\u4EFB\u52A1 prompt\uFF09" }
3340
+ ],
3341
+ examples: ["morula task state <taskId> --state in_progress", "morula task state <taskId> --state in_progress --reason <\u9000\u56DE\u539F\u56E0>"],
3342
+ writes: "remote"
3343
+ },
3344
+ {
3345
+ action: "assign",
3346
+ summary: "assign the task owner (machine protocol; target = space member or stage executor principal)",
3347
+ args: [{ name: "taskId", required: true, description: "\u672C\u4EFB\u52A1 AgentTask UUID" }],
3348
+ flags: [
3349
+ { name: "to", type: "string", valueHint: "<principalId>", description: "\u76EE\u6807\u8D1F\u8D23\u4EBA principal UUID\uFF08\u672C\u7A7A\u95F4\u6210\u5458\u6216\u9636\u6BB5\u6267\u884C\u5668\uFF1B\u4E0D\u5B58\u5728/\u4E0D\u5728\u672C\u7A7A\u95F4\u4F1A\u663E\u5F0F\u62A5\u9519\uFF09", required: true }
3350
+ ],
3351
+ examples: ["morula task assign <taskId> --to <principalId>"],
3329
3352
  writes: "remote"
3330
3353
  },
3331
3354
  {
@@ -3655,15 +3678,17 @@ var SPEC = {
3655
3678
  },
3656
3679
  {
3657
3680
  action: "add",
3658
- summary: "add comment (format auto-detected; use markdown for agent final summaries). --mention \u53EF @ \u4EBA\uFF08\u89E6\u53D1\u901A\u77E5/\u50AC\u529E\uFF09",
3681
+ summary: "add comment (format auto-detected; use markdown for agent final summaries). --final \u53D1\u4EFB\u52A1\u6700\u7EC8\u603B\u7ED3\uFF08\u540C\u4EFB\u52A1\u53EA\u4FDD\u7559\u4E00\u6761\uFF09\uFF1B--mention \u53EF @ \u4EBA\u3001--mention-agent \u53EF @ \u9636\u6BB5\u8FD0\u884C\u65F6\uFF08\u89E6\u53D1\u6D3E\u53D1\uFF09",
3659
3682
  flags: [
3660
3683
  { name: "object", type: "string", valueHint: "<id>", description: "\u5BF9\u8C61 ID\uFF08\u5FC5\u586B\uFF09", required: true },
3661
3684
  { name: "content", type: "string", valueHint: "<text>", description: "\u8BC4\u8BBA\u5185\u5BB9\uFF08\u5FC5\u586B\uFF09", required: true },
3662
3685
  { name: "parent", type: "string", valueHint: "<id>", description: "\u7236\u8BC4\u8BBA\uFF08\u56DE\u590D\uFF09" },
3663
3686
  { name: "format", type: "string", valueHint: "<markdown|plain|richtext>", enum: ["markdown", "plain", "richtext"], description: "\u5185\u5BB9\u683C\u5F0F\uFF08\u7F3A\u7701\u81EA\u52A8\u68C0\u6D4B\uFF09" },
3664
- { name: "mention", type: "string", valueHint: "<principalId[,principalId]>", repeatable: true, description: "@ \u4EBA\uFF1A\u88AB\u63D0\u53CA\u8005\u7684 principalId\uFF0C\u53EF\u91CD\u590D\u4F20\uFF08--mention a --mention b\uFF09\u6216\u7528\u9017\u53F7\u5206\u9694\u591A\u4E2A\uFF08\u4E24\u79CD\u6DF7\u7528\u81EA\u52A8\u5408\u5E76\u53BB\u91CD\u4FDD\u5E8F\uFF09\uFF1B\u53EA\u652F\u6301\u4EBA\uFF0C\u4E0D\u652F\u6301 @ \u8FD0\u884C\u65F6/\u667A\u80FD\u4F53" }
3687
+ { name: "mention", type: "string", valueHint: "<principalId[,principalId]>", repeatable: true, description: "@ \u4EBA\uFF1A\u88AB\u63D0\u53CA\u8005\u7684 principalId\uFF0C\u53EF\u91CD\u590D\u4F20\u6216\u9017\u53F7\u5206\u9694\uFF08\u5408\u5E76\u53BB\u91CD\u4FDD\u5E8F\uFF09" },
3688
+ { name: "mention-agent", type: "string", valueHint: "<stageExecutorId[,stageExecutorId]>", repeatable: true, description: "@ \u9636\u6BB5\u8FD0\u884C\u65F6\uFF1A\u7A7A\u95F4\u300C\u9636\u6BB5 \u2192 \u8FD0\u884C\u65F6\u300D\u69FD\u4F4D\u7684 stageExecutorId\uFF0C\u53EF\u91CD\u590D\u4F20\u6216\u9017\u53F7\u5206\u9694\uFF1B\u89E6\u53D1\u8BE5\u6267\u884C\u5668\u5728\u4EFB\u52A1\u4E0A\u5E72\u6D3B\uFF08\u8BC4\u8BBA @ \u6D3E\u53D1\uFF09" },
3689
+ { name: "final", type: "boolean", description: "\u6807\u8BB0\u672C\u6761\u4E3A\u4EFB\u52A1\u6700\u7EC8\u603B\u7ED3\uFF08\u4EC5\u4EFB\u52A1\u7EA7\u51ED\u8BC1\u53EF\u7528\uFF1B\u540C\u4E00\u4EFB\u52A1\u53EA\u4FDD\u7559\u4E00\u6761\uFF0C\u91CD\u590D\u53D1\u4F1A\u88AB\u670D\u52A1\u7AEF\u62D2\u7EDD\u2014\u2014\u6536\u5C3E\u65F6\u5148\u53D1 final \u518D\u4E0A\u62A5\u7ED3\u679C\uFF09" }
3665
3690
  ],
3666
- examples: ["morula comment add --object <id> --content <text>", "morula comment add --object <id> --content <text> --mention <principalId>", "morula comment add --object <id> --content <text> --mention <principalIdA> --mention <principalIdB>"],
3691
+ examples: ["morula comment add --object <id> --content <text>", "morula comment add --object <id> --content <text> --final", "morula comment add --object <id> --content <text> --mention-agent <stageExecutorId>", "morula comment add --object <id> --content <text> --mention <principalId>"],
3667
3692
  writes: "remote"
3668
3693
  },
3669
3694
  {
@@ -4014,15 +4039,6 @@ var SPEC = {
4014
4039
  writes: "none",
4015
4040
  idempotent: true
4016
4041
  },
4017
- {
4018
- action: "content",
4019
- summary: "read the parsed text content (MinerU markdown) of an attachment; unparsed/parsing/failed is reported in the body, not as an HTTP error",
4020
- args: [{ name: "id", required: true, description: "\u9644\u4EF6 ID" }],
4021
- flags: [{ name: "max-chars", type: "number", valueHint: "<n>", description: "\u6700\u5927\u8FD4\u56DE\u5B57\u7B26\u6570\uFF08\u670D\u52A1\u7AEF\u7F3A\u7701 8000\uFF09" }],
4022
- examples: ["morula attachment content <id>", "morula attachment content <id> --max-chars 20000"],
4023
- writes: "none",
4024
- idempotent: true
4025
- },
4026
4042
  {
4027
4043
  action: "download-url",
4028
4044
  summary: "get signed download URL for an attachment",
@@ -4255,16 +4271,39 @@ async function taskCommand(context) {
4255
4271
  });
4256
4272
  }
4257
4273
  if (action === "state") {
4258
- rejectUnknownFlags(flags, ["state"]);
4274
+ rejectUnknownFlags(flags, ["state", "reason"]);
4259
4275
  const id = requireId4(positionals, "task state");
4260
4276
  const stateCode = requireFlag(flags, "state");
4261
4277
  const runtimeId = context.env.MORULA_RUNTIME_ID;
4262
4278
  if (!runtimeId)
4263
4279
  throw usage(`task state \u9700\u8981 MORULA_RUNTIME_ID\uFF08\u7531 daemon \u6CE8\u5165\uFF1Bagent \u4E0D\u624B\u52A8\u6307\u5B9A\uFF09
4264
4280
 
4265
- \u7528\u6CD5: morula task state <taskId> --state <in_progress|review>
4266
- \u8BF4\u660E: \u4EFB\u52A1\u72B6\u6001\u63A8\u8FDB\u7531\u670D\u52A1\u7AEF\u9A71\u52A8\uFF08\u6D3E\u5355\u65F6\u6309\u9636\u6BB5\u5BF9\u9F50\u3001PR \u4E8B\u4EF6\u81EA\u52A8\u6D41\u8F6C\uFF09\uFF1B\u4EC5\u5E73\u53F0\u660E\u786E\u8981\u6C42 agent \u81EA\u63A8\u72B6\u6001\u65F6\u4F7F\u7528\u3002`);
4267
- return context.client.http.post(`/agent-tasks/${id}/state`, { stateCode, runtimeId });
4281
+ \u7528\u6CD5: morula task state <taskId> --state <in_progress|review> [--reason <\u6587\u672C>]
4282
+ \u8BF4\u660E: \u6B63\u5411\u63A8\u8FDB\uFF08\u5F85\u5206\u6790\u2192\u5F00\u53D1\u4E2D\u3001\u5F00\u53D1\u4E2D\u2192\u5F85\u8BC4\u5BA1\uFF09\u4F18\u5148\u7531\u7ED3\u8BBA\u534F\u8BAE\u9A71\u52A8\uFF08analysis-verdict / review submit / PR \u4E8B\u4EF6\uFF09\uFF1B
4283
+ \u4EC5\u5E73\u53F0\u660E\u786E\u8981\u6C42 agent \u81EA\u63A8\u72B6\u6001\u65F6\u4F7F\u7528\u3002review\u2192in_progress = \u9000\u56DE\u5F00\u53D1\uFF1A\u4F1A\u81EA\u52A8\u6D3E\u4FEE\u590D\u8F6E\u6B21\uFF08\u540C PR\uFF09\uFF0C--reason \u5199\u660E\u9000\u56DE\u539F\u56E0\u3002`);
4284
+ const body = { stateCode, runtimeId };
4285
+ const reason = flagString(flags, "reason");
4286
+ if (reason?.trim())
4287
+ body.reason = reason.trim();
4288
+ const result = await context.client.http.post(`/agent-tasks/${id}/state`, body);
4289
+ if (result?.ok === false)
4290
+ throw new CliError(result.code ?? "STATE_FAILED", EXIT.OTHER, `\u72B6\u6001\u6D41\u8F6C\u5931\u8D25: ${result.message ?? "\u672A\u77E5\u539F\u56E0"}`);
4291
+ return result;
4292
+ }
4293
+ if (action === "assign") {
4294
+ rejectUnknownFlags(flags, ["to"]);
4295
+ const id = requireId4(positionals, "task assign");
4296
+ const targetPrincipalId = requireFlag(flags, "to");
4297
+ const runtimeId = context.env.MORULA_RUNTIME_ID;
4298
+ if (!runtimeId)
4299
+ throw usage(`task assign \u9700\u8981 MORULA_RUNTIME_ID\uFF08\u7531 daemon \u6CE8\u5165\uFF1Bagent \u4E0D\u624B\u52A8\u6307\u5B9A\uFF09
4300
+
4301
+ \u7528\u6CD5: morula task assign <taskId> --to <principalId>
4302
+ \u8BF4\u660E: \u628A\u672C\u4EFB\u52A1\u5173\u8054\u7684\u4E1A\u52A1\u4EFB\u52A1\u8D1F\u8D23\u4EBA\u6307\u6D3E\u7ED9\u672C\u7A7A\u95F4\u7684\u6210\u5458\u6216\u9636\u6BB5\u6267\u884C\u5668\uFF08principal UUID\uFF09\u3002`);
4303
+ const result = await context.client.http.post(`/agent-tasks/${id}/assignee`, { targetPrincipalId, runtimeId });
4304
+ if (result?.ok === false)
4305
+ throw new CliError(result.code ?? "ASSIGN_FAILED", EXIT.OTHER, `\u6307\u6D3E\u5931\u8D25: ${result.message ?? "\u672A\u77E5\u539F\u56E0"}`);
4306
+ return result;
4268
4307
  }
4269
4308
  if (action === "analysis-verdict") {
4270
4309
  rejectUnknownFlags(flags, ["verdict", "reason"]);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "morula-cli",
3
- "version": "0.1.0-alpha.15",
3
+ "version": "0.1.0-alpha.17",
4
4
  "description": "Use Morula CLI task management from DimCode.",
5
5
  "interface": {
6
6
  "displayName": "Morula CLI",
@@ -269,9 +269,9 @@ morula task branch <taskId> --name <type>/<slug>
269
269
 
270
270
  ### `morula task state`
271
271
 
272
- **用法**:`morula task state <taskId> [--state <in_progress|review>]`
272
+ **用法**:`morula task state <taskId> [--state <in_progress|review>] [--reason <text>]`
273
273
 
274
- self-transition task state (machine protocol; agent drives todo→in_progress→review, runtimeId from env)
274
+ self-transition task state (machine protocol; agent drives todo→in_progress→review; review→in_progress = 退回开发,平台自动派修复轮次)
275
275
 
276
276
  | 位置参数 | 必填 | 说明 |
277
277
  |---|---|---|
@@ -279,12 +279,36 @@ self-transition task state (machine protocol; agent drives todo→in_progress→
279
279
 
280
280
  | Flag | 类型 | 必填 | 取值 | 默认 | 说明 |
281
281
  |---|---|---|---|---|---|
282
- | `--state <in_progress\|review>` | string | 是 | `in_progress` `review` | - | 目标状态 code(必填) |
282
+ | `--state <in_progress\|review>` | string | 是 | `in_progress` `review` | - | 目标状态 code(必填);review→in_progress = 退回开发(自动派修复,不需要再自己 @ 开发执行器) |
283
+ | `--reason <text>` | string | - | - | - | 流转原因(退回开发时写进轮次记账与修复任务 prompt) |
283
284
 
284
285
  **示例**
285
286
 
286
287
  ```bash
287
288
  morula task state <taskId> --state in_progress
289
+ morula task state <taskId> --state in_progress --reason <退回原因>
290
+ ```
291
+
292
+ > `writes: remote` · `interactive: no` · `idempotent: no`
293
+
294
+ ### `morula task assign`
295
+
296
+ **用法**:`morula task assign <taskId> [--to <principalId>]`
297
+
298
+ assign the task owner (machine protocol; target = space member or stage executor principal)
299
+
300
+ | 位置参数 | 必填 | 说明 |
301
+ |---|---|---|
302
+ | `<taskId>` | 是 | 本任务 AgentTask UUID |
303
+
304
+ | Flag | 类型 | 必填 | 取值 | 默认 | 说明 |
305
+ |---|---|---|---|---|---|
306
+ | `--to <principalId>` | string | 是 | - | - | 目标负责人 principal UUID(本空间成员或阶段执行器;不存在/不在本空间会显式报错) |
307
+
308
+ **示例**
309
+
310
+ ```bash
311
+ morula task assign <taskId> --to <principalId>
288
312
  ```
289
313
 
290
314
  > `writes: remote` · `interactive: no` · `idempotent: no`
@@ -971,9 +995,9 @@ morula comment get <id>
971
995
 
972
996
  ### `morula comment add`
973
997
 
974
- **用法**:`morula comment add [--object <id>] [--content <text>] [--parent <id>] [--format <markdown|plain|richtext>] [--mention <principalId[,principalId]>]`
998
+ **用法**:`morula comment add [--object <id>] [--content <text>] [--parent <id>] [--format <markdown|plain|richtext>] [--mention <principalId[,principalId]>] [--mention-agent <stageExecutorId[,stageExecutorId]>] [--final]`
975
999
 
976
- add comment (format auto-detected; use markdown for agent final summaries). --mention 可 @ 人(触发通知/催办)
1000
+ add comment (format auto-detected; use markdown for agent final summaries). --final 发任务最终总结(同任务只保留一条);--mention 可 @ 人、--mention-agent 可 @ 阶段运行时(触发派发)
977
1001
 
978
1002
  | Flag | 类型 | 必填 | 取值 | 默认 | 说明 |
979
1003
  |---|---|---|---|---|---|
@@ -981,14 +1005,17 @@ add comment (format auto-detected; use markdown for agent final summaries). --me
981
1005
  | `--content <text>` | string | 是 | - | - | 评论内容(必填) |
982
1006
  | `--parent <id>` | string | - | - | - | 父评论(回复) |
983
1007
  | `--format <markdown\|plain\|richtext>` | string | - | `markdown` `plain` `richtext` | - | 内容格式(缺省自动检测) |
984
- | `--mention <principalId[,principalId]>` | string | - | - | - | @ 人:被提及者的 principalId,可重复传(--mention a --mention b)或用逗号分隔多个(两种混用自动合并去重保序);只支持人,不支持 @ 运行时/智能体 |
1008
+ | `--mention <principalId[,principalId]>` | string | - | - | - | @ 人:被提及者的 principalId,可重复传或逗号分隔(合并去重保序) |
1009
+ | `--mention-agent <stageExecutorId[,stageExecutorId]>` | string | - | - | - | @ 阶段运行时:空间「阶段 → 运行时」槽位的 stageExecutorId,可重复传或逗号分隔;触发该执行器在任务上干活(评论 @ 派发) |
1010
+ | `--final` | boolean | - | - | - | 标记本条为任务最终总结(仅任务级凭证可用;同一任务只保留一条,重复发会被服务端拒绝——收尾时先发 final 再上报结果) |
985
1011
 
986
1012
  **示例**
987
1013
 
988
1014
  ```bash
989
1015
  morula comment add --object <id> --content <text>
1016
+ morula comment add --object <id> --content <text> --final
1017
+ morula comment add --object <id> --content <text> --mention-agent <stageExecutorId>
990
1018
  morula comment add --object <id> --content <text> --mention <principalId>
991
- morula comment add --object <id> --content <text> --mention <principalIdA> --mention <principalIdB>
992
1019
  ```
993
1020
 
994
1021
  > `writes: remote` · `interactive: no` · `idempotent: no`
@@ -1632,29 +1659,6 @@ morula attachment get <id>
1632
1659
 
1633
1660
  > `writes: none` · `interactive: no` · `idempotent: yes`
1634
1661
 
1635
- ### `morula attachment content`
1636
-
1637
- **用法**:`morula attachment content <id> [--max-chars <n>]`
1638
-
1639
- read the parsed text content (MinerU markdown) of an attachment; unparsed/parsing/failed is reported in the body, not as an HTTP error
1640
-
1641
- | 位置参数 | 必填 | 说明 |
1642
- |---|---|---|
1643
- | `<id>` | 是 | 附件 ID |
1644
-
1645
- | Flag | 类型 | 必填 | 取值 | 默认 | 说明 |
1646
- |---|---|---|---|---|---|
1647
- | `--max-chars <n>` | number | - | - | - | 最大返回字符数(服务端缺省 8000) |
1648
-
1649
- **示例**
1650
-
1651
- ```bash
1652
- morula attachment content <id>
1653
- morula attachment content <id> --max-chars 20000
1654
- ```
1655
-
1656
- > `writes: none` · `interactive: no` · `idempotent: yes`
1657
-
1658
1662
  ### `morula attachment download-url`
1659
1663
 
1660
1664
  **用法**:`morula attachment download-url <id>`
@@ -135,8 +135,9 @@ allowed-tools: Bash(morula *), Bash(git *), Bash(gh *)
135
135
  - **git / gh 命令一律在对应仓库目录内执行**(`cd <工作区根>/<仓库目录>`,或 `git -C <仓库目录> ...`):
136
136
  工作区根不是 git 仓库,在根目录跑 git 命令必然失败。`morula` CLI 可在工作区根或其任意子目录执行(见 0.4)。
137
137
  - **主仓库**:平台已解析出的仓库在**工作区根下就绪**(目录名 = 仓库全名把 `/` 换成 `-`,如 `human-resources/xxx` → `human-resources-xxx`);`ls` 工作区根即可看到,直接在该目录内建分支/提交/推分支/提 PR。**需要空间绑定了哪些仓库时自己查**:`morula repo list --space <spaceId>`(spaceId 见系统提示「基本信息」段)。
138
- - **⚠️ `morula repo checkout` 的语义 = 「声明/切换本任务的**主仓库**」**(服务端 generation+1 + daemon 物化工作区):
139
- **它决定后续开发阶段在哪个仓库干活**。因此只在**判定出主仓库之后**调用,**一次任务只声明一次**(判错要纠正时才再调一次)。
138
+ - **⚠️ `morula repo checkout` 的语义 = 「声明/切换本任务的主仓库」**(服务端 generation+1 + daemon 物化工作区):
139
+ 声明只作用于当前任务(当前阶段的执行者);**开发阶段任务不继承分析阶段的声明**——需要改代码时由你自己
140
+ 按任务描述/分析结论 checkout。因此只在**判定出主仓库之后**调用,**一次任务只声明一次**(判错要纠正时才再调一次)。
140
141
  不要用它「把空间里的仓库逐个逛一遍」——每调一次主仓库就跟着换一次(真机踩到:分析阶段为查证 checkout 了 3 个仓库,
141
142
  主仓库最终停在最后那个「只看了一眼」的仓库上)。
142
143
  - **若任务需要了解仓库内容/代码/提交状态(读场景,含纯问答)**:
@@ -171,8 +172,9 @@ allowed-tools: Bash(morula *), Bash(git *), Bash(gh *)
171
172
  morula repo checkout <taskId> <repoFullName> [--ref <branch|sha>] # 拉取/切换仓库(返回物化路径,见 0.5)
172
173
  morula repo prepare <taskId> <repoFullName> # 多仓库任务:物化其他仓库到任务工作区根下(见 0.5)
173
174
  morula comment list --object <objectId> # 读评论(任务=objectId)
175
+ morula comment add --object <objectId> --content <文本> # 发评论(最终总结加 --final,见第 5 条)
174
176
  morula task inbox [--peek] # 运行中检查点:取本线程未消费消息,纳入本回合(见 1.3)
175
- # 注:最终总结评论由服务端统一发送(作者=智能体),不要调用 comment add 自行置评。
177
+ # 注:任务收尾先发 final 总结评论(morula comment add --final),再上报结果;服务端只在你没发时兜底代发一条。
176
178
  ```
177
179
  - **可用仓库清单**:系统提示**不再列出**仓库——需要枚举空间绑定的仓库时用
178
180
  `morula repo list --space <spaceId>`(`<spaceId>` 见系统提示「基本信息」段,或 `morula task get` 返回的 `contextSnapshot.spaceId`)。
@@ -302,26 +304,34 @@ morula task inbox --peek # 只看不取(确认有没有新意见,不标
302
304
  - PR 创建被阻塞(权限/测试失败/远端状态异常)→ 在最终总结中**报告阻塞原因**,不要假装任务已完成;
303
305
  - **不要**切回 `main` 或 `master` 直接提交。
304
306
 
305
- ## 4. 状态变更由服务端驱动,不要手动翻状态
307
+ ## 4. 状态与任务操作:结论优先,动作为辅
306
308
 
307
- - 任务状态推进(如完成)由 PR merge 事件自动触发(配合第 2 条的关单写法)。
308
- - 不要直接调 CLI 改任务状态;把状态流转交给服务端。
309
+ - **状态推进由结论协议与事实事件驱动**:分析任务上报 `analysis-verdict`、评审任务用 `morula review submit`、合并/发布由 PR 事件驱动——**不要**用状态动作替代结论协议。
310
+ - **你可以在任务上做基础操作**(像人一样,作用域仅限本任务):
311
+ - `morula task state <taskId> --state <in_progress|review> [--reason <文本>]`:在「开发中 / 待评审」范围内自推状态;**待评审 → 开发中 = 退回开发**——平台会自动回退状态、记评审轮次并派发修复任务(同一 PR 迭代),**不需要、也不应该**再自己 @ 开发执行器;`--reason` 写明退回原因(会进修复任务 prompt)。
312
+ - `morula comment add`:见第 5 条评论纪律。
313
+ - `morula task assign <taskId> --to <principalId>`:指派本任务负责人(本空间成员或阶段执行器;目标不存在会显式报错)。
314
+ - **终态(已合并/已发布/已关闭)与需求池状态不由你声明**;同态重复推送是幂等的(无害)。
309
315
 
310
316
  ## 4.1 分析任务:必须上报分析结论(verdict)
311
317
 
312
318
  > **仅当本任务是「待分析」任务时适用**(任务停在「待分析」阶段、要求做需求分析与方案拆解、不写代码)。
313
319
  > 其余任务(开发 / 评审 / 调研 / 问答)跳过本节,不要调用该命令。
314
320
 
315
- - 分析结论分两部分,**都要给**:
316
- 1. **人看的**:分析结论写进最终输出(需求是否清晰、怎么做、范围与风险、验收点)——服务端作为任务评论发布;
317
- 2. **机器判定的**:执行 `morula task analysis-verdict <taskId> --verdict ready|blocked [--reason "…"]` 上报结论。
321
+ - 分析结论分两部分,**都要给**(顺序:先发评论,再上报 verdict):
322
+ 1. **人看的**:用**你自己的话**把分析结论发成最终总结评论——需求是否清晰、怎么做、范围与风险、验收点:
323
+ ```bash
324
+ morula comment add --object <objectId> --final --content <你的分析结论>
325
+ ```
326
+ 这是用户看到的分析交付:用自己的话写,不要套固定格式,更不要复述任务描述;结论先说、理由跟上。
327
+ 2. **机器判定的**:执行 `morula task analysis-verdict <taskId> --verdict ready|blocked [--reason "…"]` 上报结论(`--reason` 只需一句话,详细内容在评论里)。
318
328
  - `<taskId>` = `morula task get`(无参)返回的 `id`(本任务 AgentTask UUID)。
319
329
  - **`ready`** = 需求清晰、可直接开发(`--reason` 可写一句话范围摘要);
320
330
  **`blocked`** = 不可做 / 需澄清(**必须**在 `--reason` 或最终输出里写清阻塞点与需要澄清的问题)。
321
331
  - **服务端据此自动推进,不需要任何人点确认**:`ready` → 任务自动进入「开发中」并派发开发任务;
322
332
  `blocked` / 未上报 → 任务**停在「待分析」**,由用户补充需求后重派分析。
323
333
  - **必须上报**:漏报会让任务静默停在「待分析」;同一任务只需上报一次(重复上报以首次为准,不会改写)。
324
- - **不要手动流转本任务状态**(不要调 `morula task state`):分析任务的推进由服务端按 verdict 决定。
334
+ - 分析任务的推进由服务端按 verdict 决定——**不要用状态动作替代分析结论**(不要调 `morula task state`)。
325
335
 
326
336
  ## 4.2 评审任务:只读评审 + 必须回传结论(verdict)
327
337
 
@@ -342,9 +352,11 @@ morula task inbox --peek # 只看不取(确认有没有新意见,不标
342
352
  - `<objectId>` = `morula task get`(无参)返回的 `objectId`;
343
353
  - `--verdict`:`approved`(通过)/ `changes_requested`(不通过,**必须**在 `--details` 写明具体意见:文件/行/问题/建议,否则开发无法修复)/ `commented`(有意见但不阻塞);
344
354
  - `--pr-number`:评审对象的 PR 编号(有则填)。
355
+ - **评审任务的交付就是这份结论**:平台会按结论写权威评论(✅/💬/🔁)——**不要**再发最终总结评论(`--final` 会被服务端拒绝),也不要在评论区复述结论。
356
+ - **结论新鲜度(无新提交不重复投 `approved`)**:提交结论前先核对被评审 MR 的 head commit 与上一轮结论时是否相同(`morula repo mr list` / `mr-detail`;评论区也有上一轮结论)。**相同且无新提交时不要重复投 `approved`**:本轮任务上下文里有人的新要求(如要求修复上一轮意见)→ 按人的要求处理(意见未修复就投 `changes_requested`);确实无可评审内容 → 投 `commented` 说明「无新提交」即可。复述上一轮结论对任务没有任何推进。
345
357
  - **禁止评审自己开发的任务**:若待评审改动由你自己提交(同一智能体既开发又评审),不下结论,直接报告该配置问题。
346
- - **不要手动流转本任务状态**(不要调 `morula task state`):评审结论由服务端据此推进。
347
358
  - **必须上报**:漏报会让任务停在「待评审」,开发无法进入下一轮。
359
+ - 退回开发**首选结论协议**:`--verdict changes_requested`(带具体意见)与服务端结论链路完全等价;`morula task state --state in_progress` 是等价的替代入口(见第 4 条),不是绕过。
348
360
 
349
361
  ## 4.3 在 MR/PR 上留评审意见(巡检/审查类任务,非评审任务时)
350
362
 
@@ -360,36 +372,30 @@ morula task inbox --peek # 只看不取(确认有没有新意见,不标
360
372
  - 若本自动化已就同一问题留过评论、结论与这次一致 → **不要重复评论**;
361
373
  - ⛔ **不要用 `morula review submit`**(那是评审任务的机器协议,会驱动业务任务状态回退)。
362
374
 
363
- ## 5. 评论交付纪律
364
-
365
- - **何时发评论**:**最终总结评论由服务端统一以智能体身份发送**(任务完成时自动发布到任务对象,作者=当前智能体)。**你不需要、也不应调用 `morula comment add`**——只要在最终输出里给出用户关心的结论即可,服务端会作为该智能体的完成评论发布。chat 会话无 objectId 的任务(纯对话派发)不回评论——最终交付由 daemon 回填 assistant 消息。
366
- - **终端输出、思考过程、工具日志用户都看不到**——用户看到的只有服务端整合后发布的最终总结评论。
367
- - **评论 @触发任务(本任务由评论触发)**:服务端会把最终总结自动作为**该触发评论的回复**发出(挂在被 @ 的评论下)。你无需、也不要自己调用 `morula comment add`/add_comment。
368
- - **防重复(服务端保证)**:服务端对「同一个任务只发一条完成评论」做幂等,不会重复刷屏。你不需要、也不要去查/发多条。
369
- - **最终输出 = 只留结论**(不设字数上限、不要求固定格式,自然组织即可):
370
- - 必答:**做了什么、结果如何、用户需要知道的后续**。
371
- - **PR 汇报仅当「任务涉及代码改动」或「用户明确问到 PR/合并」时给出**;纯问答/调研类任务不要出现 PR/分支章节,直接回答问题。
372
- - **不要写**:思考过程、探索步骤、工具日志、终端输出、英文过程性叙述——用户只关心「做了什么、结果怎样」,不关心「怎么做的」。
373
- - 内容较多时用小标题/列表组织,让结论先置顶。
374
- - **过程文本必须加分隔符**:服务端取你的最终输出时按优先级截取「总结段」,**最高优先级是分隔符 `---FINAL---`**(独占一行、前后各空一行)。若你的输出里混入了中间过程(英文探索叙述、工具尝试记录、思考片段),**必须**在真正的总结之前插入该分隔符——服务端只保留它之后的内容:
375
+ ## 5. 评论交付纪律(最终总结由你自己发布)
375
376
 
377
+ - **最终总结评论由你自己发布**:任务收尾时执行
378
+ ```bash
379
+ morula comment add --object <objectId> --final --content <结论>
376
380
  ```
377
- ...(前面的过程文本,会被服务端自动截掉)...
378
-
379
- ---FINAL---
380
-
381
- ## 总结
382
-
383
- ...(真正的总结内容,会展示给用户)...
381
+ 作者=你(智能体身份);评论触发任务时服务端会**自动把它挂到触发评论下**作为回复(无需自己查评论 ID)。
382
+ **先发 final 评论、再上报任务结果**——服务端在你完成时检查:没发过 final 才会兜底代发一条(同一任务只会保留一条最终总结,重复发送会被服务端拒绝,这是预期内的幂等保护)。
383
+ - **例外:评审任务不需要(也不应)发 final**——你的结论已经通过 `morula review submit` 机器协议上报,平台会据此写权威结论评论(✅ 通过 / 💬 非阻塞 / 🔁 未通过);再发一条 final 总结会让同一结论在评论区**说两遍**(服务端也会拒绝评审任务的 final)。需要发 final 的是开发 / 修复 / 分析 / 问答 / 评论触发类任务——内容**用你自己的话写**,不要套模板。
384
+ - **普通评论**(说明进展、@ 其他人或运行时)随时可发:
385
+ ```bash
386
+ morula comment add --object <objectId> --content <文本>
387
+ morula comment add --object <objectId> --content <文本> --mention-agent <stageExecutorId> # @ 阶段运行时(触发该执行器在本任务上干活)
384
388
  ```
385
-
386
- 服务端同时兼容 `---SUMMARY---`。**不写分隔符时**,服务端只能退回「按总结类标题 / 水平线猜测」,猜不中就会把你的过程文本一起发给用户——所以混了过程就一定要加。
387
- - 不要包含工具调试过程、内部日志、任何凭据或 token 信息。
389
+ chat 会话无 objectId 的任务(纯对话派发)不发评论——最终交付由 daemon 回填 assistant 消息。
390
+ - **评论纪律:只写结论与事实**——做了什么、结果如何、用户需要知道的后续;**不写**思考过程、探索步骤、工具日志、终端输出、命令回显。用户只关心「做了什么、结果怎样」,不关心「怎么做的」。内容较多时用小标题/列表组织,结论先置顶。
391
+ - **PR 汇报仅当「任务涉及代码改动」或「用户明确问到 PR/合并」时给出**;纯问答/调研类任务不要出现 PR/分支章节。
392
+ - **任务结果文本同样只写结论**:上报的任务结果会被服务端用于缺失兜底与通知摘要——若结果里混入了中间过程(英文探索叙述、工具尝试记录、思考片段),**必须**在真正的总结之前插入独占一行的 `---FINAL---` 分隔符,服务端兜底时只保留它之后的内容(`---SUMMARY---` 同样兼容;不写时服务端退回「按总结类标题 / 水平线猜测」,猜不中会把过程文本一起兜底出去)。
388
393
  - **不要把本地绝对路径当交付物**:工作目录路径只属于执行它的机器,读者打不开;生成的文件**必须先回传平台**(见 1.1 `morula attachment upload`),交付物用平台下载链接或仓库内相对路径。
394
+ - 不要包含工具调试过程、内部日志、任何凭据或 token 信息。
389
395
 
390
396
  ## 6. 禁止事项
391
397
 
392
- - 禁止直接调用 morula REST API / 非 CLI 方式写评论、改状态、读数据;**不要调用 `morula comment add` 自行置评**——完成评论由服务端统一发送。
398
+ - 禁止直接调用 morula REST API / 非 CLI 方式写评论、改状态、读数据——与平台交互只走 morula CLI(评论用第 5 条的 `morula comment add`,状态用第 4 条的 `morula task state`)。
393
399
  - 禁止读取 `~/.morula-cli`、凭据文件、环境变量中的密钥。
394
400
  - 只在任务指定的工作目录与仓库内操作,不要探索工作目录之外的文件系统。
395
401
  - 禁止把 token、安装 ID、内部 URL 写进评论或 commit。
@@ -15,7 +15,7 @@
15
15
  | 契约 | 实现 | 位置 |
16
16
  |------|------|------|
17
17
  | CLI 读评论 | `morula comment list --object <objectId>` | `packages/morula-cli/src/commands/comment.ts` |
18
- | CLI 写评论(**契约禁止 agent 自行调用**:完成评论由服务端统一发送,见本文件第 5 条) | `morula comment add`(命令存在,但本契约要求不调用) | `packages/morula-cli/src/commands/comment.ts` |
18
+ | CLI 写评论(**由 agent 自己发**,含 `--final` 最终总结与 `--mention-agent` @ 运行时;见本文件第 5 条) | `morula comment add` → `POST /comments`(agent_task 凭证;`kind='final'` 按 `(taskId, kind)` 唯一防重) | `packages/morula-cli/src/commands/comment.ts`、`src/modules/task-management/comment/comment.service.ts` |
19
19
  | CLI 任务详情(**无参**取本任务;带参取业务对象) | `morula task get` → `GET /agent-tasks/:id`;`morula task get <objectId>` → `GET /objects/:id` | `packages/morula-cli/src/commands/task.ts` |
20
20
  | daemon 注入凭据(agent 无需自行登录) | runner 注入 `env` 给 Provider 子进程 | `packages/runtime/src/daemon/runner.ts` |
21
21
  | 调 CLI 用 `$MORULA_CLI`(绝对路径,与当前 daemon 同源;PATH 上的裸 `morula` 可能是宿主自带的旧版) | `resolveAgentCliPath`(优先本包 `dist/morula.js`,产物缺失回退插件启动器);注入点:任务链路 `env`、会话链路 `env` | `packages/runtime/src/daemon/plugin-register.ts`、`runner.ts`、`session-host.ts` |
@@ -34,12 +34,15 @@
34
34
  | delivery 以本地实际分支为准 | `reportPrDelivery` 读 `git branch --show-current`,跳过占位/主干 | `packages/runtime/src/daemon/runner.ts` |
35
35
  | 交付上报遍历本任务全部仓库 | 主仓库目录(工作区根下的仓库子目录)+ 其余同级仓库子目录(prepare 产物,磁盘事实;按 repoFullName 归属排除主仓库),逐仓上报 branch/pushState、逐仓独立成败;次仓库 `workContextId` 省略时服务端按 `(lastAgentTaskId, repoFullName)` 反查 | `reportPrDelivery`/`reportRepoTargets`(`runner.ts`)、`collectMaterializedRepoDeliveryTargets`(`repo-checkout-service.ts`) |
36
36
 
37
- ## 4. 状态由服务端驱动(agent 不手动翻状态)
37
+ ## 4. 状态与任务操作(结论优先、动作为辅;proposal stage-runtime-as-task-actor)
38
38
 
39
39
  | 契约 | 实现 | 位置 |
40
40
  |------|------|------|
41
- | merge 自动关单(服务端驱动状态) | webhook merged → 按平台事实定位已关联链接 → 汇总 → Object 完成 | `src/modules/vcs-integration/webhook/vcs-event.service.ts`、`link/vcs-link.service.ts` |
42
- | agent 不调 CLI 改状态 | 协作契约第 4 条。**注意:CLI 有 `morula task state <id> --state <code>`**(机器协议 `POST /agent-tasks/:id/state`,供平台/自流转使用)——是**契约要求 agent 不调用**它,不是「CLI 没有这个命令」 | `packages/morula-cli/src/commands/task.ts`(action `state`)、`src/modules/agent-task/agent-task.controller.ts`(端点) |
41
+ | merge 自动关单(事实事件驱动状态) | webhook merged → 按平台事实定位已关联链接 → 汇总 → Object 完成 | `src/modules/vcs-integration/webhook/vcs-event.service.ts`、`link/vcs-link.service.ts` |
42
+ | 运行时可声明状态集合 = `in_progress` / `review`(D8 落地口径;终态与 backlog/todo 不可声明,D5) | `resolveRuntimeStateTransition`(唯一定义:控制器端点与 change_object_status 工具共用);正向走事件级规则 + ObjectService.update | `src/modules/agent-task/domain/runtime-state-actions.ts`、`agent-task.controller.ts`(`POST :id/state`) |
43
+ | **待评审 → 开发中 = 退回开发,单一效应入口**(记账 + 回退 + 派修复 + 双触发去重;不做裸状态写) | `ReviewLoopService.runtimeRevertToDev`(与 verdict 路径 `handleRejected` 共用 `dispatchDevRework`;事实去重 = 对象已有未完成 dev 任务则只记账不派单) | `src/modules/agent-task/review-loop.service.ts` |
44
+ | 任务活跃门禁(终态任务的 token 不得再推状态/指派) | `ACTIVE_TASK_STATUSES`(queued/dispatched/running/awaiting_approval) | `src/modules/agent-task/domain/runtime-state-actions.ts` |
45
+ | 指派负责人(目标 = 本空间成员或阶段执行器 principal;失败显式报错) | `POST /agent-tasks/:id/assignee`(ObjectService.update 落库,agent_task 字段白名单含 assigneePrincipalId) | `agent-task.controller.ts`、`object.service.ts` |
43
46
 
44
47
  ## 4.1 评审任务结论回传(morula review submit)
45
48
 
@@ -48,15 +51,17 @@
48
51
  | 评审结论机器协议(approved / changes_requested / commented,changes_requested 必须带 details) | `morula review submit <objectId> --verdict ... [--details] [--pr-number]` → `POST /agent-collaborations/review/submit`(服务端校验调用者 = 评审任务后落库并驱动回环) | `packages/morula-cli/src/commands/review.ts`、`src/modules/agent-task/review-loop.service.ts` |
49
52
  | 评审对象(objectId)经 AgentTask 本体下发(`morula task get` 无参返回) | AgentTask 记录 `objectId` 字段(enqueue 时写入) | `src/modules/agent-task/review-loop.service.ts` |
50
53
 
51
- ## 5. 评论交付纪律(一条最终评论,结果上平台)
54
+ ## 5. 评论交付纪律(最终总结由运行时自发,服务端防重兜底)
52
55
 
53
56
  | 契约 | 实现 | 位置 |
54
57
  |------|------|------|
55
- | 评论是用户可见的唯一通道;**完成评论由服务端统一以智能体身份发送**(agent 不调 `comment add`) | 服务端 `finalOutput` 回填 + 聚合发布;评论 @触发任务的总结自动挂到触发评论下(hasAgentRepliedToTrigger 去重) | `src/modules/agent-task/task-lifecycle.service.ts`、本 SKILL 第 5 条 |
56
- | 最终输出截取「总结段」(最高优先级 = `---FINAL---` 分隔符) | `cleanFinalOutput`:分隔符 → 总结类标题 → 水平线 → 原文兜底 | `src/modules/agent-task/task-lifecycle.service.ts` |
57
- | **评论 @触发任务 → 最终总结由服务端自动回复到触发评论下**(agent 无需评论 ID,不注入提示词) | 结构性挂靠:`CommentService.create`(resolveCommentAuthor 返回 triggerCommentId → parentCommentId 默认值);生命周期兜底:`task-lifecycle.service.ts` hasAgentRepliedToTrigger / isMentionReply | LOCAL-AGENT-COMMENT-REPLY-001 |
58
+ | 评论是用户可见的产出通道;**最终总结由 agent 自己发**(`morula comment add --final`,作者=智能体) | `POST /comments`(agent_task 凭证;`kind='final'` 服务端回填 `taskId`,`(tenantId, taskId, kind)` 唯一约束 → 同一任务一条) | `packages/morula-cli/src/commands/comment.ts`、`src/modules/task-management/comment/comment.service.ts`(FINAL-COMMENT-001) |
59
+ | **服务端缺失兜底**(DP-1):任务完成时检查没发过 final 才代发一条(占住唯一键;运行时迟到补发被拒绝) | `task-lifecycle.service.ts` onTaskCompleted:`runtimeFinalExists` 检查 + 兜底 create 带 `taskId/kind`(并发由 P2002 捕获) | `src/modules/agent-task/task-lifecycle.service.ts` |
60
+ | 兜底路径截取「总结段」(`---FINAL---` 分隔符 → 总结标题 → 水平线 → 原文兜底) | `cleanFinalOutput`(仅兜底代发路径使用;运行时自发路径内容纪律靠契约) | `src/modules/agent-task/task-lifecycle.service.ts` |
61
+ | **评论 @触发任务 → agent 的 final 自动挂到触发评论下**(agent 无需评论 ID) | 结构性挂靠:`CommentService.create`(resolveCommentAuthor 返回 triggerCommentId → parentCommentId 默认值);生命周期兜底:hasAgentRepliedToTrigger / isMentionReply | LOCAL-AGENT-COMMENT-REPLY-001 |
62
+ | **@ 阶段运行时**(`--mention-agent <stageExecutorId>`)→ 触发该执行器在任务上干活 | mentions 数组 agent 类型项 → `comment.mentioned` 事件 → AgentCollaborationService 派发 | `comment.service.ts`、`agent-collaboration.service.ts` |
58
63
  | 禁止本地绝对路径作为交付物 | 契约第 5 条;`reportPrDelivery` 只回报仓库相对信息 | `packages/runtime/src/daemon/runner.ts` |
59
- | 单条最终评论(不刷进度) | 契约第 5 条 | 本 SKILL |
64
+ | 单条最终评论(不刷进度;评论只写结论与事实,D10) | 契约第 5 条 + 唯一约束 | 本 SKILL |
60
65
 
61
66
  ## 6. 禁止事项
62
67
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arcships/morula-runtime",
3
3
  "type": "module",
4
- "version": "0.1.0-alpha.15",
4
+ "version": "0.1.0-alpha.17",
5
5
  "description": "Device-level CLI for the Morula local agent runtime (daemon/portal/models/auth) and the Morula business CLI (morula), distributed via npm",
6
6
  "bin": {
7
7
  "morula-runtime": "./bin/morula-runtime.js",