@ai-setting/roy-agent-core 1.7.63 → 1.7.65

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.
Files changed (131) hide show
  1. package/dist/config/index.js +9 -9
  2. package/dist/env/agent/agent-component.js +15 -15
  3. package/dist/env/agent/index.js +15 -15
  4. package/dist/env/commands/index.js +9 -9
  5. package/dist/env/context/index.js +5 -5
  6. package/dist/env/debug/index.js +9 -9
  7. package/dist/env/environment.d.ts +27 -0
  8. package/dist/env/error-reply.js +3 -3
  9. package/dist/env/event-source/bounty-im-handler.d.ts +1 -1
  10. package/dist/env/event-source/cli-command-handler.js +3 -3
  11. package/dist/env/event-source/event-source-handlers.js +22 -21
  12. package/dist/env/event-source/event-store.js +4 -4
  13. package/dist/env/event-source/index.js +23 -22
  14. package/dist/env/hook/index.js +7 -7
  15. package/dist/env/index.js +49 -48
  16. package/dist/env/llm/index.js +10 -10
  17. package/dist/env/log-trace/index.js +9 -9
  18. package/dist/env/log-trace/logger.js +2 -2
  19. package/dist/env/mcp/index.js +10 -10
  20. package/dist/env/mcp/tool/index.js +3 -3
  21. package/dist/env/memory/index.js +10 -10
  22. package/dist/env/memory/plugin/index.js +3 -3
  23. package/dist/env/plugin/index.js +9 -9
  24. package/dist/env/prompt/index.js +9 -9
  25. package/dist/env/session/index.js +12 -12
  26. package/dist/env/session/storage/index.js +6 -6
  27. package/dist/env/skill/index.js +9 -9
  28. package/dist/env/task/delegate/delegate-tool.d.ts +2 -2
  29. package/dist/env/task/delegate/delegate-tool.js +10 -10
  30. package/dist/env/task/delegate/index.js +10 -10
  31. package/dist/env/task/hooks/contexts.d.ts +34 -0
  32. package/dist/env/task/hooks/verdict.d.ts +170 -0
  33. package/dist/env/task/index.js +17 -16
  34. package/dist/env/task/mcp/index.js +6 -5
  35. package/dist/env/task/plugins/__e2e__/laya-chain-validate.d.ts +15 -0
  36. package/dist/env/task/plugins/__e2e__/laya-decision-branches.d.ts +18 -0
  37. package/dist/env/task/plugins/__e2e__/laya-decision-e2e-live.d.ts +27 -0
  38. package/dist/env/task/plugins/__e2e__/laya-e2e-verify.d.ts +17 -0
  39. package/dist/env/task/plugins/decision-hint-plugin.d.ts +144 -0
  40. package/dist/env/task/plugins/decision-port.d.ts +119 -0
  41. package/dist/env/task/plugins/decision-renderers.d.ts +33 -0
  42. package/dist/env/task/plugins/index.d.ts +1 -0
  43. package/dist/env/task/plugins/index.js +19 -12
  44. package/dist/env/task/plugins/lark-card-patch-notifier.js +4 -4
  45. package/dist/env/task/plugins/laya-decision-plugin.d.ts +136 -0
  46. package/dist/env/task/plugins/laya-port.d.ts +143 -0
  47. package/dist/env/task/plugins/laya-render.d.ts +100 -0
  48. package/dist/env/task/storage/index.js +5 -5
  49. package/dist/env/task/tools/index.js +6 -5
  50. package/dist/env/tool/built-in/index.js +5 -5
  51. package/dist/env/tool/index.js +12 -12
  52. package/dist/env/workflow/engine/index.js +14 -14
  53. package/dist/env/workflow/hooks/index.js +9 -9
  54. package/dist/env/workflow/index.js +24 -24
  55. package/dist/env/workflow/nodes/index.js +4 -4
  56. package/dist/env/workflow/plugins/index.js +10 -10
  57. package/dist/env/workflow/service/index.js +4 -4
  58. package/dist/env/workflow/storage/index.js +5 -5
  59. package/dist/env/workflow/tools/index.js +16 -16
  60. package/dist/env/workflow/utils/index.js +6 -6
  61. package/dist/index.js +60 -59
  62. package/dist/shared/@ai-setting/{roy-agent-core-sj4y3wdm.js → roy-agent-core-002g6nfk.js} +3 -3
  63. package/dist/shared/@ai-setting/{roy-agent-core-06jakebr.js → roy-agent-core-07fvy74y.js} +2 -2
  64. package/dist/shared/@ai-setting/{roy-agent-core-60f7h284.js → roy-agent-core-0cb6w0k7.js} +5 -5
  65. package/dist/shared/@ai-setting/{roy-agent-core-cxpgd415.js → roy-agent-core-0ys85x46.js} +6 -6
  66. package/dist/shared/@ai-setting/{roy-agent-core-6wxww10t.js → roy-agent-core-12n08pg5.js} +2 -2
  67. package/dist/shared/@ai-setting/{roy-agent-core-t6nx0rsz.js → roy-agent-core-18am1h07.js} +11 -11
  68. package/dist/shared/@ai-setting/{roy-agent-core-ebws32tv.js → roy-agent-core-37tj4y6a.js} +14 -14
  69. package/dist/shared/@ai-setting/{roy-agent-core-gjsvehfs.js → roy-agent-core-3wrveh5p.js} +2 -2
  70. package/dist/shared/@ai-setting/{roy-agent-core-03k5drfp.js → roy-agent-core-40rv283c.js} +2 -2
  71. package/dist/shared/@ai-setting/{roy-agent-core-kvq4a4p6.js → roy-agent-core-5pr4zp88.js} +2 -2
  72. package/dist/shared/@ai-setting/{roy-agent-core-qmqe8ds3.js → roy-agent-core-6j02sbee.js} +3 -3
  73. package/dist/shared/@ai-setting/{roy-agent-core-b4zdm2n4.js → roy-agent-core-6z412atw.js} +45 -8
  74. package/dist/shared/@ai-setting/{roy-agent-core-hfyxv2kx.js → roy-agent-core-7cbpmk4q.js} +2 -2
  75. package/dist/shared/@ai-setting/{roy-agent-core-6awgfh3x.js → roy-agent-core-7f7t9wwp.js} +1 -1
  76. package/dist/shared/@ai-setting/roy-agent-core-83bewqx8.js +10 -0
  77. package/dist/shared/@ai-setting/{roy-agent-core-jnszp0jq.js → roy-agent-core-85xyctnb.js} +7 -2
  78. package/dist/shared/@ai-setting/{roy-agent-core-2pma868e.js → roy-agent-core-87942bw4.js} +5 -5
  79. package/dist/shared/@ai-setting/{roy-agent-core-ja4sr63d.js → roy-agent-core-8cx7cfdg.js} +7 -7
  80. package/dist/shared/@ai-setting/{roy-agent-core-th92f833.js → roy-agent-core-9bs6xrav.js} +2 -2
  81. package/dist/shared/@ai-setting/{roy-agent-core-bm3gav5d.js → roy-agent-core-9bshyqjn.js} +4 -4
  82. package/dist/shared/@ai-setting/{roy-agent-core-va4m04e8.js → roy-agent-core-add1zqsh.js} +1 -1
  83. package/dist/shared/@ai-setting/{roy-agent-core-7tce05jt.js → roy-agent-core-ax9e9szb.js} +1 -1
  84. package/dist/shared/@ai-setting/{roy-agent-core-8xv4qzx7.js → roy-agent-core-br9cdm8d.js} +1 -1
  85. package/dist/shared/@ai-setting/{roy-agent-core-zjgckbdd.js → roy-agent-core-c2gbt68e.js} +2 -2
  86. package/dist/shared/@ai-setting/{roy-agent-core-v7mwj8eq.js → roy-agent-core-cpcd24n2.js} +2 -2
  87. package/dist/shared/@ai-setting/{roy-agent-core-hczj1ryh.js → roy-agent-core-dc9zq75z.js} +5 -5
  88. package/dist/shared/@ai-setting/{roy-agent-core-zy8vkbnk.js → roy-agent-core-dvtg4sn0.js} +1 -1
  89. package/dist/shared/@ai-setting/{roy-agent-core-g6pw4esq.js → roy-agent-core-e42v9bcq.js} +4 -4
  90. package/dist/shared/@ai-setting/{roy-agent-core-hwkc0bg2.js → roy-agent-core-efkjyh69.js} +1 -1
  91. package/dist/shared/@ai-setting/{roy-agent-core-dfqhc9ey.js → roy-agent-core-es203w7p.js} +2 -2
  92. package/dist/shared/@ai-setting/{roy-agent-core-w2ev3z5q.js → roy-agent-core-es467wsz.js} +6 -6
  93. package/dist/shared/@ai-setting/{roy-agent-core-ye4bfzqg.js → roy-agent-core-f6c0nadn.js} +969 -105
  94. package/dist/shared/@ai-setting/{roy-agent-core-p8rf4z89.js → roy-agent-core-f77k3gqt.js} +1 -1
  95. package/dist/shared/@ai-setting/{roy-agent-core-jw63js86.js → roy-agent-core-fttgm52a.js} +1 -1
  96. package/dist/shared/@ai-setting/{roy-agent-core-32ck9rqt.js → roy-agent-core-g8htfwxj.js} +2 -2
  97. package/dist/shared/@ai-setting/{roy-agent-core-5mrynf9g.js → roy-agent-core-gxzxq1sf.js} +1 -1
  98. package/dist/shared/@ai-setting/{roy-agent-core-gnt23yx0.js → roy-agent-core-hjejpcg5.js} +8 -8
  99. package/dist/shared/@ai-setting/{roy-agent-core-5hq4v41q.js → roy-agent-core-hmdcz3g9.js} +10 -9
  100. package/dist/shared/@ai-setting/{roy-agent-core-1dnsk6ht.js → roy-agent-core-hy3zn2q2.js} +3 -3
  101. package/dist/shared/@ai-setting/{roy-agent-core-wzp4tbsn.js → roy-agent-core-jvv2h3d6.js} +1 -1
  102. package/dist/shared/@ai-setting/{roy-agent-core-prnry2mw.js → roy-agent-core-jz8dsypm.js} +3 -3
  103. package/dist/shared/@ai-setting/{roy-agent-core-rfhxj8v2.js → roy-agent-core-kmbct9yg.js} +1 -1
  104. package/dist/shared/@ai-setting/{roy-agent-core-gc6wcd7z.js → roy-agent-core-mcy5apay.js} +1 -1
  105. package/dist/shared/@ai-setting/{roy-agent-core-p6a4v4fq.js → roy-agent-core-mds8tqd0.js} +1 -1
  106. package/dist/shared/@ai-setting/{roy-agent-core-bpvz1dqr.js → roy-agent-core-mw2cen6s.js} +8 -8
  107. package/dist/shared/@ai-setting/{roy-agent-core-a08dpf71.js → roy-agent-core-na18zwj0.js} +1 -1
  108. package/dist/shared/@ai-setting/{roy-agent-core-2hkyp51n.js → roy-agent-core-nm4xbs1d.js} +1 -1
  109. package/dist/shared/@ai-setting/{roy-agent-core-79gyka2q.js → roy-agent-core-nwbfepba.js} +2 -2
  110. package/dist/shared/@ai-setting/{roy-agent-core-gtdtzbs7.js → roy-agent-core-pgvcf91v.js} +2 -2
  111. package/dist/shared/@ai-setting/{roy-agent-core-084zthqx.js → roy-agent-core-q96qynhb.js} +1 -1
  112. package/dist/shared/@ai-setting/{roy-agent-core-386wa4wy.js → roy-agent-core-qq96p0tj.js} +1 -1
  113. package/dist/shared/@ai-setting/{roy-agent-core-33hn1beg.js → roy-agent-core-qqaak24r.js} +1 -1
  114. package/dist/shared/@ai-setting/{roy-agent-core-vwzan9v0.js → roy-agent-core-r5wt1h3c.js} +2 -2
  115. package/dist/shared/@ai-setting/{roy-agent-core-2cw72f16.js → roy-agent-core-rjzdj0xb.js} +5 -5
  116. package/dist/shared/@ai-setting/{roy-agent-core-c57278kx.js → roy-agent-core-sjr1kmfz.js} +1 -1
  117. package/dist/shared/@ai-setting/{roy-agent-core-as0dd70m.js → roy-agent-core-t0ktaevq.js} +50 -4
  118. package/dist/shared/@ai-setting/{roy-agent-core-anb53t3v.js → roy-agent-core-tm1mxttk.js} +4 -4
  119. package/dist/shared/@ai-setting/roy-agent-core-tr3ep81v.js +397 -0
  120. package/dist/shared/@ai-setting/{roy-agent-core-p37kh0kt.js → roy-agent-core-v0hf14bj.js} +1 -1
  121. package/dist/shared/@ai-setting/{roy-agent-core-c4t4y18j.js → roy-agent-core-v6vr6za7.js} +5 -5
  122. package/dist/shared/@ai-setting/{roy-agent-core-c8xvy6hj.js → roy-agent-core-w07ymg29.js} +4 -4
  123. package/dist/shared/@ai-setting/{roy-agent-core-mbwaxqs0.js → roy-agent-core-xcp8vwax.js} +4 -4
  124. package/dist/shared/@ai-setting/{roy-agent-core-g23yp3t1.js → roy-agent-core-xhrrgfdy.js} +29 -12
  125. package/dist/shared/@ai-setting/{roy-agent-core-mkpsz128.js → roy-agent-core-xvt7kf7p.js} +5 -5
  126. package/dist/shared/@ai-setting/{roy-agent-core-kmvw6b4v.js → roy-agent-core-yr1mryb8.js} +1 -1
  127. package/dist/shared/@ai-setting/{roy-agent-core-m3w3ytxt.js → roy-agent-core-zk2jg0v7.js} +2 -2
  128. package/dist/shared/@ai-setting/{roy-agent-core-mrrv80jy.js → roy-agent-core-zxz5fkwj.js} +1 -1
  129. package/dist/utils/temp-file-manager/index.js +3 -3
  130. package/package.json +1 -1
  131. package/dist/shared/@ai-setting/roy-agent-core-a6cz4d23.js +0 -10
@@ -1,44 +1,54 @@
1
1
  import {
2
2
  getSharedEventStore
3
- } from "./roy-agent-core-a08dpf71.js";
3
+ } from "./roy-agent-core-na18zwj0.js";
4
4
  import {
5
5
  createCompatibleDatabase,
6
6
  init_sqlite_task_store
7
- } from "./roy-agent-core-th92f833.js";
7
+ } from "./roy-agent-core-9bs6xrav.js";
8
8
  import {
9
9
  TaskHookPoints
10
10
  } from "./roy-agent-core-1pg0fepg.js";
11
11
  import {
12
12
  WORKFLOW_EXTRACT_PROMPT
13
13
  } from "./roy-agent-core-6masakz4.js";
14
+ import {
15
+ assertNoLeakage,
16
+ buildHintText,
17
+ deriveActionFromStatus,
18
+ formatRelativeAge,
19
+ parseSimilarTaskIds,
20
+ renderCandidateKey,
21
+ renderCandidateValue,
22
+ writeDecisionHint
23
+ } from "./roy-agent-core-tr3ep81v.js";
14
24
  import {
15
25
  LarkCardPatchNotifier,
16
26
  humanizeValue
17
- } from "./roy-agent-core-m3w3ytxt.js";
27
+ } from "./roy-agent-core-zk2jg0v7.js";
18
28
  import {
19
29
  validateWorkflowDefinition
20
- } from "./roy-agent-core-kmvw6b4v.js";
30
+ } from "./roy-agent-core-yr1mryb8.js";
21
31
  import {
22
32
  enqueueToAddFakeUserMessage
23
- } from "./roy-agent-core-p8rf4z89.js";
33
+ } from "./roy-agent-core-f77k3gqt.js";
24
34
  import {
25
35
  BasePlugin
26
36
  } from "./roy-agent-core-az13yzmc.js";
27
37
  import {
28
38
  init_context
29
- } from "./roy-agent-core-33hn1beg.js";
39
+ } from "./roy-agent-core-qqaak24r.js";
30
40
  import {
31
41
  getEnvContextOrEmpty
32
- } from "./roy-agent-core-hfyxv2kx.js";
42
+ } from "./roy-agent-core-7cbpmk4q.js";
33
43
  import {
34
44
  TracedAs,
35
45
  init_decorator,
36
46
  wrapFunction
37
- } from "./roy-agent-core-386wa4wy.js";
47
+ } from "./roy-agent-core-qq96p0tj.js";
38
48
  import {
39
49
  createLogger,
40
50
  init_logger
41
- } from "./roy-agent-core-6awgfh3x.js";
51
+ } from "./roy-agent-core-7f7t9wwp.js";
42
52
  import {
43
53
  __legacyDecorateClassTS,
44
54
  __require
@@ -303,11 +313,11 @@ ${prompt}`;
303
313
  }
304
314
  if (!this._workflowRepo) {
305
315
  try {
306
- const sqlite = await import("./roy-agent-core-hczj1ryh.js");
316
+ const sqlite = await import("./roy-agent-core-dc9zq75z.js");
307
317
  this.ensureDataDir();
308
318
  const db = sqlite.getDatabase();
309
319
  sqlite.initializeTables();
310
- const { WorkflowRepository } = await import("./roy-agent-core-mkpsz128.js");
320
+ const { WorkflowRepository } = await import("./roy-agent-core-xvt7kf7p.js");
311
321
  this._workflowRepo = new WorkflowRepository(db);
312
322
  return this._workflowRepo;
313
323
  } catch {
@@ -322,42 +332,896 @@ ${prompt}`;
322
332
  __require("fs").mkdirSync(dataDir, { recursive: true });
323
333
  }
324
334
  }
335
+ // src/env/task/plugins/laya-render.ts
336
+ var LAYA_CANDIDATE_TITLE_MAX = 50;
337
+ var LAYA_CANDIDATE_DESC_MAX = 45;
338
+ var LAYA_CANDIDATE_TAGS_MAX = 4;
339
+ var LAYA_STATE_DESC_MAX = 600;
340
+ var LAYA_NEW_TASK_DESC = "新建为独立任务(与所有现有候选均无明确关联)";
341
+ var LAYA_STATUS_LETTER = {
342
+ active: "A",
343
+ todo: "T",
344
+ completed: "C",
345
+ paused: "P",
346
+ cancelled: "X",
347
+ archived: "R"
348
+ };
349
+ function layaCandidateKey(c) {
350
+ return `#${c.id} ${truncate(c.title, LAYA_CANDIDATE_TITLE_MAX)}`;
351
+ }
352
+ function layaCandidateValue(c) {
353
+ const letter = LAYA_STATUS_LETTER[c.status] ?? (c.status.charAt(0) || "T").toUpperCase();
354
+ const desc = truncate(c.description.replace(/\n/g, " "), LAYA_CANDIDATE_DESC_MAX);
355
+ const tags = (c.tags ?? []).slice(0, LAYA_CANDIDATE_TAGS_MAX);
356
+ const tagsPart = tags.length > 0 ? ` [tags: ${tags.join(",")}]` : "";
357
+ return `${letter}|${c.ago}|${desc}${tagsPart}`;
358
+ }
359
+ function renderLayaState(input) {
360
+ const desc = (input.description || "").slice(0, LAYA_STATE_DESC_MAX) || "(无)";
361
+ const lines = [];
362
+ for (const c of input.candidates) {
363
+ lines.push(`${layaCandidateKey(c)}
364
+ ${layaCandidateValue(c)}`);
365
+ }
366
+ const tags = (input.tags ?? []).map((t) => t.trim()).filter((t) => t.length > 0);
367
+ const tagsLine = tags.length > 0 ? `【请求标签】${tags.join(",")}
368
+ ` : "";
369
+ return `【新任务标题】${input.title}
370
+ ` + `【新任务描述】${desc}
371
+ ` + tagsLine + `【候选任务】
372
+ ` + lines.join(`
373
+ `);
374
+ }
375
+ function renderLayaCriteria(candidates) {
376
+ const crit = {};
377
+ for (const c of candidates) {
378
+ crit[layaCandidateKey(c)] = layaCandidateValue(c);
379
+ }
380
+ crit["NEW_TASK"] = LAYA_NEW_TASK_DESC;
381
+ return crit;
382
+ }
383
+ function matchChoiceToCandidate(choice, candidates) {
384
+ if (typeof choice !== "string" || choice.length === 0)
385
+ return;
386
+ for (const c of candidates) {
387
+ if (layaCandidateKey(c) === choice)
388
+ return c.id;
389
+ }
390
+ if (choice.startsWith("#")) {
391
+ const bare = Number(choice.slice(1));
392
+ if (Number.isInteger(bare) && candidates.some((c) => c.id === bare))
393
+ return bare;
394
+ }
395
+ const m = /^#(\d+)\s/.exec(choice);
396
+ if (m) {
397
+ const bare = Number(m[1]);
398
+ if (Number.isInteger(bare) && candidates.some((c) => c.id === bare))
399
+ return bare;
400
+ }
401
+ return;
402
+ }
403
+ function truncate(text, max) {
404
+ if (!text)
405
+ return "";
406
+ if (text.length <= max)
407
+ return text;
408
+ return text.slice(0, max);
409
+ }
410
+
411
+ // src/env/task/plugins/decision-renderers.ts
412
+ var SYSTEMONE_STAGE_A_INSTRUCTIONS = "判断本次『新任务请求』应该新建还是复用某个已有任务。若应复用,选该任务的 #id;若应新建,选 NEW_TASK。输出 choice/confidence/probabilities。";
413
+ var LAYA_STAGE_A_INSTRUCTIONS = "优先复用:若候选与请求属于同一工作流/同一系列/目标重叠,必须选该候选的 #id;仅当完全无关时才选 NEW_TASK。";
414
+ var systemoneRenderer = {
415
+ buildStageA(input) {
416
+ const criteria = {};
417
+ const similarTasks = input.candidates.map((t) => {
418
+ const key = renderCandidateKey(t);
419
+ criteria[key] = renderCandidateValue({
420
+ id: t.id,
421
+ status: t.status,
422
+ ago: formatRelativeAge(t.updatedAt, input.now),
423
+ description: t.description ?? "",
424
+ tags: t.tags ?? []
425
+ });
426
+ return {
427
+ id: t.id,
428
+ status: t.status,
429
+ ago: formatRelativeAge(t.updatedAt, input.now),
430
+ title: t.title,
431
+ snippet: (t.description ?? "").slice(0, 120),
432
+ tags: t.tags ?? []
433
+ };
434
+ });
435
+ criteria["NEW_TASK"] = "新建为独立任务(与所有现有候选均无明确关联)";
436
+ return {
437
+ state: {
438
+ task_to_route: {
439
+ title: input.title,
440
+ description: input.description,
441
+ goals: input.goals,
442
+ tags: input.tags,
443
+ project_path: input.projectPath
444
+ },
445
+ similar_tasks: similarTasks
446
+ },
447
+ questions: {
448
+ reuse_or_create: {
449
+ type: "choice",
450
+ instructions: SYSTEMONE_STAGE_A_INSTRUCTIONS,
451
+ criteria
452
+ }
453
+ }
454
+ };
455
+ },
456
+ matchChoice(choice, candidates) {
457
+ const keyById = new Map(candidates.map((t) => [renderCandidateKey(t), t.id]));
458
+ let targetTaskId = keyById.get(choice);
459
+ if (targetTaskId === undefined && choice.startsWith("#")) {
460
+ const bareId = Number(choice.slice(1));
461
+ if (Number.isInteger(bareId) && candidates.some((t) => t.id === bareId)) {
462
+ targetTaskId = bareId;
463
+ }
464
+ }
465
+ return targetTaskId;
466
+ }
467
+ };
468
+ var layaRenderer = {
469
+ buildStageA(input) {
470
+ const candidates = input.candidates.map((t) => ({
471
+ id: t.id,
472
+ title: t.title,
473
+ status: t.status,
474
+ ago: formatRelativeAge(t.updatedAt, input.now),
475
+ description: t.description ?? "",
476
+ tags: t.tags ?? []
477
+ }));
478
+ const reqTags = (input.tags ?? []).map((t) => t.trim()).filter((t) => t.length > 0);
479
+ return {
480
+ state: renderLayaState({
481
+ title: input.title,
482
+ description: input.description,
483
+ tags: reqTags,
484
+ candidates
485
+ }),
486
+ questions: {
487
+ reuse_or_create: {
488
+ type: "choice",
489
+ instructions: reqTags.length > 0 ? `${LAYA_STAGE_A_INSTRUCTIONS}
490
+ 请求标签:${reqTags.join(",")}` : LAYA_STAGE_A_INSTRUCTIONS,
491
+ criteria: renderLayaCriteria(candidates)
492
+ }
493
+ }
494
+ };
495
+ },
496
+ matchChoice(choice, candidates) {
497
+ const layaCands = candidates.map((t) => ({
498
+ id: t.id,
499
+ title: t.title,
500
+ status: t.status,
501
+ ago: "",
502
+ description: t.description ?? "",
503
+ tags: t.tags ?? []
504
+ }));
505
+ const direct = matchChoiceToCandidate(choice, layaCands);
506
+ if (direct !== undefined)
507
+ return direct;
508
+ const m = /^#(\d+)/.exec(choice);
509
+ if (m) {
510
+ const bare = Number(m[1]);
511
+ if (Number.isInteger(bare) && candidates.some((t) => t.id === bare))
512
+ return bare;
513
+ }
514
+ return;
515
+ }
516
+ };
517
+
518
+ // src/env/task/plugins/decision-hint-plugin.ts
519
+ init_logger();
520
+ init_decorator();
521
+ var logger2 = createLogger("plugin:decision-hint");
522
+ function normalizeHookCtx(ctx) {
523
+ const maybe = ctx;
524
+ return maybe && typeof maybe === "object" && maybe.data ? maybe.data : ctx;
525
+ }
526
+ var MAX_STAGE_A_CANDIDATES = 20;
527
+ var MAX_LONGTERM_ROOTS = 6;
528
+ var MAX_KEYWORD_ROOTS = 10;
529
+ var FALLBACK_TOP_N = 5;
530
+ var REUSE_ADOPT_THRESHOLD = 0.6;
531
+ var SOFT_HINT_THRESHOLD = 0.4;
532
+ var MAX_ROOT_ANCESTOR_DEPTH = 20;
533
+
534
+ class DecisionHintPlugin {
535
+ name = "decision-hint";
536
+ version = "1.0.0";
537
+ description = "Task #4467 — task:before.create 的 decision 检测:判定新建/复用 + root/子挂载,只提示不拦截";
538
+ decisionPort;
539
+ envParentTaskIdProvider;
540
+ renderer;
541
+ reuseThreshold;
542
+ softHintThreshold;
543
+ singleCandidateShortcut;
544
+ constructor(options) {
545
+ this.decisionPort = options.decisionPort;
546
+ this.envParentTaskIdProvider = options.envParentTaskIdProvider ?? (() => {
547
+ return;
548
+ });
549
+ this.renderer = options.renderer ?? systemoneRenderer;
550
+ this.reuseThreshold = options.reuseThreshold ?? REUSE_ADOPT_THRESHOLD;
551
+ this.softHintThreshold = options.softHintThreshold ?? SOFT_HINT_THRESHOLD;
552
+ this.singleCandidateShortcut = options.singleCandidateShortcut ?? true;
553
+ }
554
+ async execute(ctx) {
555
+ try {
556
+ await this.run(normalizeHookCtx(ctx));
557
+ } catch (err) {
558
+ logger2.warn(`[decision-hint] degraded to pass-through: ${err?.message}`);
559
+ }
560
+ }
561
+ async run(ctx) {
562
+ const taskComponent = ctx.taskComponent;
563
+ if (!taskComponent) {
564
+ logger2.warn("[decision-hint] taskComponent not injected — skip");
565
+ return;
566
+ }
567
+ const explicitIds = parseSimilarTaskIds(ctx.options.context);
568
+ let candidates = [];
569
+ if (explicitIds.length > 0) {
570
+ for (const id of explicitIds) {
571
+ const t = await taskComponent.getTask(id);
572
+ if (t)
573
+ candidates.push(t);
574
+ }
575
+ } else {
576
+ candidates = await this.fallbackCandidates(taskComponent, ctx.options.tags);
577
+ }
578
+ if (candidates.length === 0) {
579
+ logger2.debug("[decision-hint] 0 candidates — pass through");
580
+ return;
581
+ }
582
+ if (candidates.length === 1 && this.singleCandidateShortcut) {
583
+ const hint = buildHintText(this.reuseHintInput(candidates[0], 1));
584
+ writeDecisionHint(ctx.options, hint);
585
+ return;
586
+ }
587
+ const trimmed = candidates.slice(0, MAX_STAGE_A_CANDIDATES);
588
+ const stageA = await this.runStageA(ctx, trimmed);
589
+ if (!stageA)
590
+ return;
591
+ if (stageA.kind === "reuse") {
592
+ const target = trimmed.find((t) => t.id === stageA.targetTaskId);
593
+ if (!target)
594
+ return;
595
+ const hint = buildHintText(this.reuseHintInput(target, stageA.confidence));
596
+ writeDecisionHint(ctx.options, hint);
597
+ return;
598
+ }
599
+ if (stageA.kind === "silent") {
600
+ logger2.debug("[decision-hint] Stage A silent — skip Stage B (no hint)");
601
+ return;
602
+ }
603
+ const stageB = await this.runStageB(ctx, taskComponent);
604
+ if (!stageB)
605
+ return;
606
+ if (stageB.kind === "remount") {
607
+ const hint = buildHintText({
608
+ scenario: "remount",
609
+ newTaskId: stageB.newTaskId,
610
+ recommendedRootId: stageB.recommendedRootId,
611
+ recommendedRootTitle: stageB.recommendedRootTitle,
612
+ currentParentId: stageB.currentParentId,
613
+ currentRootAncestorId: stageB.currentRootAncestorId
614
+ });
615
+ writeDecisionHint(ctx.options, hint);
616
+ }
617
+ }
618
+ async fallbackCandidates(taskComponent, tags) {
619
+ const cleanTags = (tags ?? []).map((t) => t.trim()).filter((t) => t.length > 0);
620
+ if (cleanTags.length === 0)
621
+ return [];
622
+ try {
623
+ const res = await taskComponent.searchTasksByKeywords(cleanTags, {
624
+ limit: FALLBACK_TOP_N,
625
+ include_archived: true
626
+ });
627
+ return (res.tasks ?? []).filter((t) => {
628
+ const titleHit = cleanTags.some((k) => t.title.toLowerCase().includes(k.toLowerCase()));
629
+ const tagHit = (t.tags ?? []).some((tag) => cleanTags.some((k) => tag.toLowerCase().includes(k.toLowerCase())));
630
+ return titleHit || tagHit;
631
+ }).slice(0, FALLBACK_TOP_N);
632
+ } catch (err) {
633
+ logger2.warn(`[decision-hint] fallback retrieval failed: ${err?.message}`);
634
+ return [];
635
+ }
636
+ }
637
+ async runStageA(ctx, candidates) {
638
+ const now = Date.now();
639
+ const request = this.buildStageARequest(ctx, candidates, now);
640
+ let response;
641
+ try {
642
+ response = await this.decisionPort.decide(request);
643
+ } catch (err) {
644
+ logger2.warn(`[decision-hint] Stage A decision failed: ${err?.message}`);
645
+ return null;
646
+ }
647
+ const answer = response?.answers?.["reuse_or_create"];
648
+ if (!answer || typeof answer.choice !== "string") {
649
+ logger2.warn("[decision-hint] Stage A answer malformed — pass through");
650
+ return null;
651
+ }
652
+ const confidence = pickGateConfidence(answer);
653
+ if (confidence < this.softHintThreshold) {
654
+ return { kind: "silent" };
655
+ }
656
+ if (answer.choice === "NEW_TASK") {
657
+ return { kind: "create" };
658
+ }
659
+ const targetTaskId = this.renderer.matchChoice(answer.choice, candidates);
660
+ if (targetTaskId === undefined) {
661
+ logger2.warn(`[decision-hint] Stage A choice not in candidate set: ${answer.choice}`);
662
+ return null;
663
+ }
664
+ return { kind: "reuse", targetTaskId, confidence };
665
+ }
666
+ buildStageARequest(ctx, candidates, now) {
667
+ return this.renderer.buildStageA({
668
+ title: ctx.options.title,
669
+ description: ctx.options.description ?? "",
670
+ goals: ctx.options.goals_and_expected_deliverables ?? "",
671
+ tags: ctx.options.tags ?? [],
672
+ projectPath: ctx.options.project_path ?? "",
673
+ candidates,
674
+ now
675
+ });
676
+ }
677
+ async runStageB(ctx, taskComponent) {
678
+ const now = Date.now();
679
+ const roots = await this.collectRootCandidates(taskComponent, ctx);
680
+ if (roots.length === 0)
681
+ return null;
682
+ const criteria = {};
683
+ const rootCandidates = roots.map((t) => {
684
+ const key = renderCandidateKey(t);
685
+ criteria[key] = renderCandidateValue({
686
+ id: t.id,
687
+ status: t.status,
688
+ ago: formatRelativeAge(t.updatedAt, now),
689
+ description: t.description ?? "",
690
+ tags: t.tags ?? []
691
+ });
692
+ return {
693
+ id: t.id,
694
+ type: t.type,
695
+ ago: formatRelativeAge(t.updatedAt, now),
696
+ title: t.title,
697
+ snippet: truncateSnippet(t.description ?? ""),
698
+ tags: t.tags ?? []
699
+ };
700
+ });
701
+ criteria["NEW_ROOT"] = "新建为顶级根任务(与所有现有根任务均无明确关联)";
702
+ const request = {
703
+ state: {
704
+ task_to_route: {
705
+ title: ctx.options.title,
706
+ description: ctx.options.description ?? "",
707
+ goals: ctx.options.goals_and_expected_deliverables ?? "",
708
+ tags: ctx.options.tags ?? [],
709
+ project_path: ctx.options.project_path ?? "",
710
+ has_explicit_parent: ctx.options.parent_task_id != null,
711
+ env_parent_task_id: this.envParentTaskIdProvider() ?? null,
712
+ available_roots_count: roots.length,
713
+ pool_size_before_prune: roots.length,
714
+ pool_size_after_prune: roots.length,
715
+ candidates_truncated: false,
716
+ now: new Date(now).toISOString()
717
+ },
718
+ root_candidates: rootCandidates
719
+ },
720
+ questions: {
721
+ root_choice: {
722
+ type: "choice",
723
+ instructions: "判断本次『新任务』应该新建为独立根任务,还是挂载到某个已有根任务下。若新任务和所有现有根任务都没明确关联,选 NEW_ROOT。输出 choice/confidence/probabilities。",
724
+ criteria
725
+ }
726
+ }
727
+ };
728
+ let response;
729
+ try {
730
+ response = await this.decisionPort.decide(request);
731
+ } catch (err) {
732
+ logger2.warn(`[decision-hint] Stage B decision failed: ${err?.message}`);
733
+ return null;
734
+ }
735
+ const answer = response?.answers?.["root_choice"];
736
+ if (!answer || typeof answer.choice !== "string")
737
+ return null;
738
+ const confidence = pickGateConfidence(answer);
739
+ if (confidence < this.softHintThreshold)
740
+ return null;
741
+ const keyById = new Map(roots.map((t) => [renderCandidateKey(t), t]));
742
+ let recommended = keyById.get(answer.choice);
743
+ if (!recommended && answer.choice.startsWith("#")) {
744
+ const bareId = Number(answer.choice.slice(1));
745
+ if (Number.isInteger(bareId))
746
+ recommended = roots.find((t) => t.id === bareId);
747
+ }
748
+ if (!recommended)
749
+ return null;
750
+ const currentParentId = ctx.options.parent_task_id ?? undefined;
751
+ if (currentParentId === undefined)
752
+ return null;
753
+ const currentRootAncestorId = await this.resolveRootAncestor(taskComponent, currentParentId);
754
+ if (currentRootAncestorId === recommended.id) {
755
+ return null;
756
+ }
757
+ return {
758
+ kind: "remount",
759
+ newTaskId: 0,
760
+ recommendedRootId: recommended.id,
761
+ recommendedRootTitle: recommended.title,
762
+ currentParentId,
763
+ currentRootAncestorId
764
+ };
765
+ }
766
+ async collectRootCandidates(taskComponent, ctx) {
767
+ try {
768
+ const all = (await taskComponent.listTasks({ depth: 0, include_archived: true }))?.tasks ?? [];
769
+ const longterm = all.filter((t) => t.type === "longterm").slice(0, MAX_LONGTERM_ROOTS);
770
+ const keywords = (ctx.options.tags ?? []).map((t) => t.trim()).filter((t) => t.length > 0);
771
+ let matched = [];
772
+ if (keywords.length > 0) {
773
+ matched = all.filter((t) => {
774
+ const titleHit = keywords.some((k) => t.title.toLowerCase().includes(k.toLowerCase()));
775
+ const tagHit = (t.tags ?? []).some((tag) => keywords.some((k) => tag.toLowerCase().includes(k.toLowerCase())));
776
+ return titleHit || tagHit;
777
+ });
778
+ }
779
+ const seen = new Set;
780
+ const out = [];
781
+ for (const t of [...longterm, ...matched]) {
782
+ if (seen.has(t.id))
783
+ continue;
784
+ seen.add(t.id);
785
+ out.push(t);
786
+ }
787
+ return out.slice(0, MAX_LONGTERM_ROOTS + MAX_KEYWORD_ROOTS);
788
+ } catch (err) {
789
+ logger2.warn(`[decision-hint] root collection failed: ${err?.message}`);
790
+ return [];
791
+ }
792
+ }
793
+ async resolveRootAncestor(taskComponent, taskId) {
794
+ const visited = new Set([taskId]);
795
+ let current = taskId;
796
+ for (let depth = 0;depth < MAX_ROOT_ANCESTOR_DEPTH; depth++) {
797
+ const t = await taskComponent.getTask(current);
798
+ const parentId = t?.parent_task_id;
799
+ if (parentId === undefined || parentId === null)
800
+ return current;
801
+ if (visited.has(parentId)) {
802
+ logger2.warn(`[decision-hint] root ancestor cycle detected at #${parentId}`);
803
+ return current;
804
+ }
805
+ visited.add(parentId);
806
+ current = parentId;
807
+ }
808
+ return current;
809
+ }
810
+ reuseHintInput(target, confidence) {
811
+ const action = deriveActionFromStatus(target.status);
812
+ const scenario = action === "reopen" ? "reopen" : confidence >= this.reuseThreshold ? "reuse" : "soft_reuse";
813
+ return {
814
+ scenario,
815
+ newTaskId: 0,
816
+ targetTaskId: target.id,
817
+ targetTask: { title: target.title, status: target.status, type: target.type },
818
+ confidence
819
+ };
820
+ }
821
+ }
822
+ __legacyDecorateClassTS([
823
+ TracedAs("task.decision.hint.onBeforeCreate", {
824
+ recordParams: true,
825
+ recordResult: true,
826
+ log: false,
827
+ paramFilter: (args) => {
828
+ const raw = args[0] ?? {};
829
+ const ctx = raw?.data ?? raw;
830
+ const opts = ctx?.options ?? {};
831
+ const decisionHint = typeof opts.context === "string" ? opts.context : "";
832
+ return {
833
+ hasTaskComponent: Boolean(ctx?.taskComponent),
834
+ titleLen: typeof opts.title === "string" ? opts.title.length : 0,
835
+ title: typeof opts.title === "string" ? opts.title.slice(0, 120) : null,
836
+ tagsCount: Array.isArray(opts.tags) ? opts.tags.length : 0,
837
+ hasParentTaskId: opts.parent_task_id != null,
838
+ contextLen: decisionHint.length,
839
+ similarTaskIdsCount: /similar_task_ids/.test(decisionHint) ? "present" : "absent",
840
+ hookCtxShape: raw?.data ? "HookContext" : "bare"
841
+ };
842
+ },
843
+ resultFilter: (_res) => ({ ok: true })
844
+ })
845
+ ], DecisionHintPlugin.prototype, "execute", null);
846
+ __legacyDecorateClassTS([
847
+ TracedAs("task.decision.hint.fallbackCandidates", {
848
+ recordParams: true,
849
+ recordResult: true,
850
+ log: false,
851
+ paramFilter: (args) => ({
852
+ tags: args[1] ?? []
853
+ }),
854
+ resultFilter: (res) => ({
855
+ count: Array.isArray(res) ? res.length : -1,
856
+ ids: Array.isArray(res) ? res.map((t) => t.id).join(",") : ""
857
+ })
858
+ })
859
+ ], DecisionHintPlugin.prototype, "fallbackCandidates", null);
860
+ __legacyDecorateClassTS([
861
+ TracedAs("task.decision.hint.stageA", {
862
+ recordParams: true,
863
+ recordResult: true,
864
+ log: false,
865
+ paramFilter: (args) => {
866
+ const ctx = args[0] ?? {};
867
+ const candidates = args[1] ?? [];
868
+ return {
869
+ candidateCount: candidates.length,
870
+ candidateIds: candidates.map((c) => c.id).join(","),
871
+ title: (ctx?.options?.title ?? "").slice(0, 120),
872
+ tagsCount: Array.isArray(ctx?.options?.tags) ? ctx.options.tags.length : 0
873
+ };
874
+ },
875
+ resultFilter: (res) => ({
876
+ kind: res?.kind ?? "null",
877
+ targetTaskId: res?.targetTaskId ?? null,
878
+ confidence: res?.confidence ?? null
879
+ })
880
+ })
881
+ ], DecisionHintPlugin.prototype, "runStageA", null);
882
+ __legacyDecorateClassTS([
883
+ TracedAs("task.decision.hint.stageB", {
884
+ recordParams: true,
885
+ recordResult: true,
886
+ log: false,
887
+ paramFilter: (args) => {
888
+ const ctx = args[0] ?? {};
889
+ return {
890
+ title: (ctx?.options?.title ?? "").slice(0, 120),
891
+ hasExplicitParent: ctx?.options?.parent_task_id != null,
892
+ explicitParentId: ctx?.options?.parent_task_id ?? null,
893
+ projectPath: ctx?.options?.project_path ?? null
894
+ };
895
+ },
896
+ resultFilter: (res) => ({
897
+ kind: res?.kind ?? "null",
898
+ recommendedRootId: res?.recommendedRootId ?? null,
899
+ currentParentId: res?.currentParentId ?? null,
900
+ currentRootAncestorId: res?.currentRootAncestorId ?? null
901
+ })
902
+ })
903
+ ], DecisionHintPlugin.prototype, "runStageB", null);
904
+ function clampConfidence(value) {
905
+ if (typeof value !== "number" || Number.isNaN(value))
906
+ return 0;
907
+ if (value > 1)
908
+ return 1;
909
+ if (value < 0)
910
+ return 0;
911
+ return value;
912
+ }
913
+ function pickGateConfidence(answer) {
914
+ if (!answer)
915
+ return 0;
916
+ const jev = typeof answer.x_jev_confidence === "number" ? answer.x_jev_confidence : undefined;
917
+ const answerConf = typeof answer.answer_confidence === "number" ? answer.answer_confidence : undefined;
918
+ const entropy = typeof answer.confidence === "number" ? answer.confidence : undefined;
919
+ const picked = jev ?? answerConf ?? entropy;
920
+ return clampConfidence(picked);
921
+ }
922
+ function truncateSnippet(text) {
923
+ if (text.length <= 45)
924
+ return text;
925
+ return text.slice(0, 45) + "…";
926
+ }
927
+
928
+ // src/env/task/plugins/laya-port.ts
929
+ var DEFAULT_TIMEOUT_MS = 2000;
930
+ var DEFAULT_MAX_STRIKES = 3;
931
+ var DEFAULT_MODEL = "task-decision";
932
+ var SYSTEMONE_PATH = "/v1/systemone";
933
+
934
+ class LayaDecisionPort {
935
+ baseURL;
936
+ apiKey;
937
+ model;
938
+ timeoutMs;
939
+ maxStrikes;
940
+ fetchImpl;
941
+ confidencePref;
942
+ timeoutStrikes = 0;
943
+ disabled = false;
944
+ constructor(options) {
945
+ this.baseURL = options.baseURL.replace(/\/+$/, "");
946
+ this.apiKey = options.apiKey;
947
+ this.model = options.model ?? DEFAULT_MODEL;
948
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
949
+ this.maxStrikes = options.maxStrikes ?? DEFAULT_MAX_STRIKES;
950
+ this.fetchImpl = options.fetchImpl ?? globalThis.fetch;
951
+ this.confidencePref = options.confidencePref ?? "auto";
952
+ }
953
+ isDisabled() {
954
+ return this.disabled || this.timeoutStrikes >= this.maxStrikes;
955
+ }
956
+ get endpoint() {
957
+ if (this.baseURL.endsWith(SYSTEMONE_PATH))
958
+ return this.baseURL;
959
+ return `${this.baseURL}${SYSTEMONE_PATH}`;
960
+ }
961
+ async decide(req) {
962
+ if (this.isDisabled()) {
963
+ throw new Error("[LayaDecisionPort] port self-disabled (3 consecutive timeouts)");
964
+ }
965
+ assertNoLeakage(req.state, req.questions);
966
+ const controller = new AbortController;
967
+ let timedOut = false;
968
+ const timer = setTimeout(() => {
969
+ timedOut = true;
970
+ controller.abort();
971
+ }, this.timeoutMs);
972
+ try {
973
+ const fetchPromise = this.fetchImpl(this.endpoint, {
974
+ method: "POST",
975
+ headers: {
976
+ "Content-Type": "application/json",
977
+ Authorization: `Bearer ${this.apiKey}`
978
+ },
979
+ body: JSON.stringify({
980
+ model: this.model,
981
+ state: req.state,
982
+ questions: req.questions
983
+ }),
984
+ signal: controller.signal
985
+ });
986
+ const timeoutPromise = new Promise((_resolve, reject) => {
987
+ const onTimeout = () => {
988
+ const err = new Error(`[LayaDecisionPort] request timed out after ${this.timeoutMs}ms`);
989
+ err.name = "TimeoutError";
990
+ reject(err);
991
+ };
992
+ if (timedOut)
993
+ onTimeout();
994
+ else {
995
+ const t = setTimeout(onTimeout, this.timeoutMs);
996
+ t.unref?.();
997
+ }
998
+ });
999
+ const res = await Promise.race([fetchPromise, timeoutPromise]);
1000
+ if (!res.ok) {
1001
+ throw new Error(`[LayaDecisionPort] HTTP ${res.status}`);
1002
+ }
1003
+ const body = await res.json();
1004
+ this.timeoutStrikes = 0;
1005
+ return this.normalize(body);
1006
+ } catch (err) {
1007
+ if (timedOut || isTimeoutError(err)) {
1008
+ this.timeoutStrikes += 1;
1009
+ if (this.timeoutStrikes >= this.maxStrikes) {
1010
+ this.disabled = true;
1011
+ }
1012
+ }
1013
+ throw err;
1014
+ } finally {
1015
+ clearTimeout(timer);
1016
+ }
1017
+ }
1018
+ normalize(body) {
1019
+ const usage = body?.usage;
1020
+ const dropped = usage?.state_tokens_dropped ?? 0;
1021
+ const truncated = Boolean(usage?.truncated) || dropped > 0;
1022
+ const answers = {};
1023
+ for (const [qid, a] of Object.entries(body?.answers ?? {})) {
1024
+ answers[qid] = {
1025
+ ...a,
1026
+ truncated: truncated || Boolean(a?.truncated),
1027
+ confidence: this.pickConfidence(a)
1028
+ };
1029
+ }
1030
+ return { ...body, answers };
1031
+ }
1032
+ pickConfidence(a) {
1033
+ if (!a)
1034
+ return 0;
1035
+ const jev = numberOrUndefined(a.x_jev_confidence);
1036
+ const answerConf = numberOrUndefined(a.answer_confidence);
1037
+ const entropy = numberOrUndefined(a.confidence);
1038
+ switch (this.confidencePref) {
1039
+ case "jev":
1040
+ return jev ?? 0;
1041
+ case "entropy":
1042
+ return entropy ?? 0;
1043
+ case "auto":
1044
+ default:
1045
+ return jev ?? answerConf ?? entropy ?? 0;
1046
+ }
1047
+ }
1048
+ }
1049
+ function numberOrUndefined(v) {
1050
+ return typeof v === "number" && Number.isFinite(v) ? v : undefined;
1051
+ }
1052
+ function isTimeoutError(err) {
1053
+ if (err instanceof Error) {
1054
+ if (err.name === "AbortError" || err.name === "TimeoutError")
1055
+ return true;
1056
+ if (/timeout|timed out|abort/i.test(err.message))
1057
+ return true;
1058
+ }
1059
+ return false;
1060
+ }
1061
+ async function loadLayaAuth(authPath = `${process.env.HOME ?? "~"}/.roy-agent/auth.json`) {
1062
+ try {
1063
+ const fs = await import("fs/promises");
1064
+ const raw = await fs.readFile(authPath, "utf-8");
1065
+ const parsed = JSON.parse(raw);
1066
+ const p = parsed.providers?.["laya-decision"];
1067
+ if (!p?.baseURL || !p?.apiKey)
1068
+ return null;
1069
+ return {
1070
+ baseURL: p.baseURL,
1071
+ apiKey: p.apiKey,
1072
+ model: p.model ?? DEFAULT_MODEL
1073
+ };
1074
+ } catch {
1075
+ return null;
1076
+ }
1077
+ }
1078
+
1079
+ // src/env/task/plugins/laya-decision-plugin.ts
1080
+ init_logger();
1081
+ var logger3 = createLogger("plugin:laya-decision");
1082
+ var LAYA_REUSE_THRESHOLD = 0.92;
1083
+
1084
+ class LayaDecisionPlugin {
1085
+ name = "laya-decision";
1086
+ version = "1.0.0";
1087
+ description = "Task #4620 — task:before.create 决策检测(本地 Laya 服务载体,只提示不拦截)";
1088
+ options;
1089
+ port = null;
1090
+ portResolved = false;
1091
+ inner = null;
1092
+ constructor(options = {}) {
1093
+ this.options = options;
1094
+ if (options.decisionPort) {
1095
+ this.port = options.decisionPort;
1096
+ this.portResolved = true;
1097
+ }
1098
+ }
1099
+ isDisabled() {
1100
+ return this.port?.isDisabled() ?? false;
1101
+ }
1102
+ async execute(ctx) {
1103
+ try {
1104
+ const inner = await this.ensureInner();
1105
+ if (!inner) {
1106
+ logger3.debug("[laya-decision] no decision port configured — skip");
1107
+ return;
1108
+ }
1109
+ await inner.execute(ctx);
1110
+ } catch (err) {
1111
+ logger3.warn(`[laya-decision] unexpected failure, ignored: ${err?.message}`);
1112
+ }
1113
+ }
1114
+ async ensureInner() {
1115
+ if (this.inner)
1116
+ return this.inner;
1117
+ const port = await this.resolvePort();
1118
+ if (!port)
1119
+ return null;
1120
+ this.inner = new DecisionHintPlugin({
1121
+ decisionPort: port,
1122
+ envParentTaskIdProvider: this.options.envParentTaskIdProvider,
1123
+ renderer: layaRenderer,
1124
+ reuseThreshold: this.options.reuseThreshold ?? LAYA_REUSE_THRESHOLD,
1125
+ softHintThreshold: this.options.softHintThreshold ?? LAYA_REUSE_THRESHOLD,
1126
+ singleCandidateShortcut: this.options.singleCandidateShortcut ?? false
1127
+ });
1128
+ return this.inner;
1129
+ }
1130
+ async resolvePort() {
1131
+ if (this.portResolved)
1132
+ return this.port;
1133
+ this.portResolved = true;
1134
+ if (this.options.decisionPort) {
1135
+ this.port = this.options.decisionPort;
1136
+ return this.port;
1137
+ }
1138
+ let cfg = null;
1139
+ try {
1140
+ cfg = await loadLayaAuth(this.options.authPath);
1141
+ } catch (err) {
1142
+ logger3.warn(`[laya-decision] auth load failed: ${err?.message}`);
1143
+ return null;
1144
+ }
1145
+ if (!cfg) {
1146
+ logger3.info("[laya-decision] providers['laya-decision'] not configured in auth.json — skip");
1147
+ return null;
1148
+ }
1149
+ try {
1150
+ this.port = new LayaDecisionPort({
1151
+ baseURL: cfg.baseURL,
1152
+ apiKey: cfg.apiKey,
1153
+ model: cfg.model,
1154
+ timeoutMs: this.options.timeoutMs,
1155
+ maxStrikes: this.options.maxStrikes,
1156
+ fetchImpl: this.options.fetchImpl,
1157
+ confidencePref: this.options.confidencePref
1158
+ });
1159
+ logger3.info(`[laya-decision] port ready: ${cfg.baseURL} model=${cfg.model} ` + `timeout=${this.options.timeoutMs ?? 2000}ms strikes=${this.options.maxStrikes ?? 3}`);
1160
+ return this.port;
1161
+ } catch (err) {
1162
+ logger3.warn(`[laya-decision] port construction failed: ${err?.message}`);
1163
+ return null;
1164
+ }
1165
+ }
1166
+ }
1167
+ var sharedPlugin = null;
1168
+ function getSharedLayaDecisionPlugin(options) {
1169
+ if (options && Object.keys(options).length > 0) {
1170
+ return new LayaDecisionPlugin(options);
1171
+ }
1172
+ if (!sharedPlugin) {
1173
+ sharedPlugin = new LayaDecisionPlugin;
1174
+ }
1175
+ return sharedPlugin;
1176
+ }
1177
+ function createLayaDecisionPlugin(options) {
1178
+ const inner = getSharedLayaDecisionPlugin(options);
1179
+ return {
1180
+ name: "laya-decision",
1181
+ priority: 20,
1182
+ execute: async (ctx) => {
1183
+ const data = ctx && typeof ctx === "object" && "data" in ctx ? ctx.data : ctx;
1184
+ await inner.execute(data);
1185
+ },
1186
+ plugin: inner
1187
+ };
1188
+ }
325
1189
  // src/env/task/plugins/auto-task-plugin.ts
326
1190
  init_logger();
327
1191
  init_decorator();
328
- var logger2 = createLogger("AutoTaskPlugin");
1192
+ var logger4 = createLogger("AutoTaskPlugin");
329
1193
  function createAutoTaskPlugin(config) {
330
1194
  const threshold = config?.threshold ?? 20;
331
1195
  const agentName = config?.agentName ?? "default";
332
1196
  let lastProcessedTraceId = "";
333
1197
  async function executeImpl(ctx) {
334
1198
  try {
335
- logger2.info(`[AutoTaskPlugin:ENTER] agentName=${ctx.agent.name} iteration=${ctx.iteration} threshold=${threshold}`);
1199
+ logger4.info(`[AutoTaskPlugin:ENTER] agentName=${ctx.agent.name} iteration=${ctx.iteration} threshold=${threshold}`);
336
1200
  if (ctx.agent.name !== agentName)
337
1201
  return { continue: true };
338
1202
  if (ctx.iteration !== threshold)
339
1203
  return { continue: true };
340
1204
  const traceId = ctx.context?.traceId ?? ctx.context?.sessionId ?? "unknown";
341
1205
  if (traceId === lastProcessedTraceId) {
342
- logger2.debug(`[AutoTaskPlugin] skip - traceId=${traceId.slice(0, 20)}... already processed`);
1206
+ logger4.debug(`[AutoTaskPlugin] skip - traceId=${traceId.slice(0, 20)}... already processed`);
343
1207
  return { continue: true };
344
1208
  }
345
1209
  lastProcessedTraceId = traceId;
346
- logger2.debug(`[AutoTaskPlugin] new query detected (traceId=${traceId.slice(0, 20)}...)`);
1210
+ logger4.debug(`[AutoTaskPlugin] new query detected (traceId=${traceId.slice(0, 20)}...)`);
347
1211
  const reminderContent = `⚠️ **系统提醒**: 你已经调用了 ${ctx.iteration} 次工具,达到 iteration 阈值(默认 ${threshold})。
348
1212
  ` + `当前请求较为复杂,建议通过 \`delegate_task\` 工具将整个任务委托给 \`roy\` sub-agent 处理。` + `注意,需要让roy为这个任务调用task create工具来创建任务进行跟踪,这个是必走程序哈。`;
349
1213
  if (!ctx.toolResult) {
350
- logger2.warn(`[AutoTaskPlugin] toolResult missing, skip meta-protocol (iteration=${ctx.iteration})`);
1214
+ logger4.warn(`[AutoTaskPlugin] toolResult missing, skip meta-protocol (iteration=${ctx.iteration})`);
351
1215
  return { continue: true };
352
1216
  }
353
1217
  enqueueToAddFakeUserMessage(ctx.toolResult, {
354
1218
  role: "user",
355
1219
  content: reminderContent
356
1220
  }, "delegate");
357
- logger2.info(`[AutoTaskPlugin] user reminder meta-protocol set (session=${(ctx.context?.sessionId ?? "unknown").slice(0, 12)}..., iteration=${ctx.iteration})`);
1221
+ logger4.info(`[AutoTaskPlugin] user reminder meta-protocol set (session=${(ctx.context?.sessionId ?? "unknown").slice(0, 12)}..., iteration=${ctx.iteration})`);
358
1222
  return { continue: true };
359
1223
  } catch (err) {
360
- logger2.error(`[AutoTaskPlugin] hook failed: ${err instanceof Error ? err.message : String(err)}`);
1224
+ logger4.error(`[AutoTaskPlugin] hook failed: ${err instanceof Error ? err.message : String(err)}`);
361
1225
  return { continue: true };
362
1226
  }
363
1227
  }
@@ -385,7 +1249,7 @@ init_context();
385
1249
  init_logger();
386
1250
  init_sqlite_task_store();
387
1251
  import { join } from "path";
388
- var logger3 = createLogger("lark-cli:card-state");
1252
+ var logger5 = createLogger("lark-cli:card-state");
389
1253
 
390
1254
  class MemoryLarkCardStateStore {
391
1255
  records = new Map;
@@ -549,7 +1413,7 @@ function createLarkCardStateStore(dbPath) {
549
1413
  try {
550
1414
  return new SqliteLarkCardStateStore(path);
551
1415
  } catch (err) {
552
- logger3.warn(`LarkCardStateStore: sqlite unavailable at ${path} (${err.message}) ` + `→ falling back to in-memory (卡片仍可发,但重启后无法 patch 旧卡)`);
1416
+ logger5.warn(`LarkCardStateStore: sqlite unavailable at ${path} (${err.message}) ` + `→ falling back to in-memory (卡片仍可发,但重启后无法 patch 旧卡)`);
553
1417
  return new MemoryLarkCardStateStore;
554
1418
  }
555
1419
  }
@@ -631,7 +1495,7 @@ function decideCardChatTarget(input) {
631
1495
  }
632
1496
 
633
1497
  // src/env/task/plugins/lark-cli-task-notify-plugin.ts
634
- var logger4 = createLogger("lark-cli:task-notify");
1498
+ var logger6 = createLogger("lark-cli:task-notify");
635
1499
  var CTX_FIELD_CHAT_ID = "larkPerTaskChatId";
636
1500
  var CTX_FIELD_CHAT_PROFILE = "larkPerTaskChatProfile";
637
1501
  function parseLarkPerTaskChatId(ctxStr) {
@@ -721,7 +1585,7 @@ class LarkCliTaskNotifyHook {
721
1585
  }
722
1586
  setCardRoutingPorts(ports) {
723
1587
  this.cardRoutingPorts = ports;
724
- logger4.debug(`CardRoutingPorts injected: getTask=${typeof ports.getTask}`);
1588
+ logger6.debug(`CardRoutingPorts injected: getTask=${typeof ports.getTask}`);
725
1589
  }
726
1590
  hasCardRoutingPorts() {
727
1591
  return this.cardRoutingPorts !== undefined;
@@ -734,7 +1598,7 @@ class LarkCliTaskNotifyHook {
734
1598
  if (this.pluginName && this.sourceId) {
735
1599
  const key = `${this.sourceId}:${this.pluginName}`;
736
1600
  if (env.pluginEnabled && env.pluginEnabled[key] !== true) {
737
- logger4.debug(`Plugin not enabled for source: ${key}`);
1601
+ logger6.debug(`Plugin not enabled for source: ${key}`);
738
1602
  return;
739
1603
  }
740
1604
  }
@@ -837,11 +1701,11 @@ class LarkCliTaskNotifyHook {
837
1701
  try {
838
1702
  record = this.cardStateStore?.get(taskId);
839
1703
  } catch (err) {
840
- logger4.warn(`onAfterDelete: 读取卡片记录失败 task #${taskId}: ${err.message}`);
1704
+ logger6.warn(`onAfterDelete: 读取卡片记录失败 task #${taskId}: ${err.message}`);
841
1705
  }
842
1706
  const messageId = record?.messageId || this.cardMessageIds.get(taskId);
843
1707
  if (!messageId) {
844
- logger4.debug(`onAfterDelete: task #${taskId} 无卡片记录,skip(不发新卡)`);
1708
+ logger6.debug(`onAfterDelete: task #${taskId} 无卡片记录,skip(不发新卡)`);
845
1709
  return;
846
1710
  }
847
1711
  this.restoreCardState(taskId);
@@ -856,17 +1720,17 @@ class LarkCliTaskNotifyHook {
856
1720
  finalStep: "任务已取消(删除)"
857
1721
  });
858
1722
  } catch (err) {
859
- logger4.warn(`onAfterDelete: patch 卡片失败 task #${taskId} message=${messageId}: ${err.message}`);
1723
+ logger6.warn(`onAfterDelete: patch 卡片失败 task #${taskId} message=${messageId}: ${err.message}`);
860
1724
  }
861
1725
  try {
862
1726
  await this.cardNotifier.unpinMessage(messageId, profile);
863
1727
  } catch (err) {
864
- logger4.warn(`onAfterDelete: unpin 失败 task #${taskId} message=${messageId}: ${err.message}`);
1728
+ logger6.warn(`onAfterDelete: unpin 失败 task #${taskId} message=${messageId}: ${err.message}`);
865
1729
  }
866
1730
  this.persistClosed(taskId);
867
1731
  this.persistPinned(taskId, false);
868
1732
  this.cardMessageIds.delete(taskId);
869
- logger4.debug(`onAfterDelete: task #${taskId} 卡片已收尾 status=deleted message=${messageId}`);
1733
+ logger6.debug(`onAfterDelete: task #${taskId} 卡片已收尾 status=deleted message=${messageId}`);
870
1734
  }
871
1735
  async onAfterOperationCreate(ctx) {
872
1736
  const { operation } = ctx.data;
@@ -881,11 +1745,11 @@ class LarkCliTaskNotifyHook {
881
1745
  const persisted = this.restoreCardState(operation.taskId);
882
1746
  const messageId = this.cardMessageIds.get(operation.taskId);
883
1747
  if (!messageId || messageId === LarkCliTaskNotifyHook.CARD_START_FAILED_SENTINEL || messageId === LarkCliTaskNotifyHook.CARD_START_INFLIGHT_SENTINEL) {
884
- logger4.debug(`onAfterOperationCreate: no usable card for task #${operation.taskId}, skip`);
1748
+ logger6.debug(`onAfterOperationCreate: no usable card for task #${operation.taskId}, skip`);
885
1749
  return;
886
1750
  }
887
1751
  if (persisted && persisted.status !== "open") {
888
- logger4.debug(`onAfterOperationCreate: task #${operation.taskId} card status=${persisted.status}, skip`);
1752
+ logger6.debug(`onAfterOperationCreate: task #${operation.taskId} card status=${persisted.status}, skip`);
889
1753
  return;
890
1754
  }
891
1755
  const { milestones, processSteps } = this.deriveTimelineFromOperation(operation);
@@ -902,7 +1766,7 @@ class LarkCliTaskNotifyHook {
902
1766
  processSteps
903
1767
  });
904
1768
  } catch (err) {
905
- logger4.error(`sendCardOperation failed for task #${operation.taskId}: ${err.message}`);
1769
+ logger6.error(`sendCardOperation failed for task #${operation.taskId}: ${err.message}`);
906
1770
  }
907
1771
  }
908
1772
  restoreCardState(taskId) {
@@ -917,12 +1781,12 @@ class LarkCliTaskNotifyHook {
917
1781
  if (rec.status !== "closed" && rec.messageId) {
918
1782
  this.cardMessageIds.set(taskId, rec.messageId);
919
1783
  this.hydrateNotifierState(rec);
920
- logger4.debug(`restoreCardState: task #${taskId} ← 持久层 message ${rec.messageId} ` + `(status=${rec.status}, pinned=${rec.pinned}, timeline=${rec.timeline ? "yes" : "no"})`);
1784
+ logger6.debug(`restoreCardState: task #${taskId} ← 持久层 message ${rec.messageId} ` + `(status=${rec.status}, pinned=${rec.pinned}, timeline=${rec.timeline ? "yes" : "no"})`);
921
1785
  }
922
1786
  }
923
1787
  return rec;
924
1788
  } catch (err) {
925
- logger4.warn(`restoreCardState failed for task #${taskId}: ${err.message}`);
1789
+ logger6.warn(`restoreCardState failed for task #${taskId}: ${err.message}`);
926
1790
  return;
927
1791
  }
928
1792
  }
@@ -933,7 +1797,7 @@ class LarkCliTaskNotifyHook {
933
1797
  const snapshot = JSON.parse(rec.timeline);
934
1798
  this.cardNotifier.hydrateState?.(rec.messageId, snapshot);
935
1799
  } catch (err) {
936
- logger4.warn(`hydrateNotifierState failed for task #${rec.taskId}: ${err.message}`);
1800
+ logger6.warn(`hydrateNotifierState failed for task #${rec.taskId}: ${err.message}`);
937
1801
  }
938
1802
  }
939
1803
  snapshotTimeline(state) {
@@ -945,7 +1809,7 @@ class LarkCliTaskNotifyHook {
945
1809
  return state;
946
1810
  return { ...state, timeline: JSON.stringify(snap) };
947
1811
  } catch (err) {
948
- logger4.warn(`snapshotTimeline failed for task #${state.taskId}: ${err.message}`);
1812
+ logger6.warn(`snapshotTimeline failed for task #${state.taskId}: ${err.message}`);
949
1813
  return state;
950
1814
  }
951
1815
  }
@@ -956,7 +1820,7 @@ class LarkCliTaskNotifyHook {
956
1820
  const payload = includeTimeline ? this.snapshotTimeline(state) : state;
957
1821
  this.cardStateStore.upsert(payload);
958
1822
  } catch (err) {
959
- logger4.warn(`persistCardState failed for task #${state.taskId}: ${err.message}`);
1823
+ logger6.warn(`persistCardState failed for task #${state.taskId}: ${err.message}`);
960
1824
  }
961
1825
  }
962
1826
  persistPinned(taskId, pinned) {
@@ -965,7 +1829,7 @@ class LarkCliTaskNotifyHook {
965
1829
  try {
966
1830
  this.cardStateStore.markPinned(taskId, pinned);
967
1831
  } catch (err) {
968
- logger4.warn(`persistPinned failed for task #${taskId}: ${err.message}`);
1832
+ logger6.warn(`persistPinned failed for task #${taskId}: ${err.message}`);
969
1833
  }
970
1834
  }
971
1835
  persistPaused(taskId) {
@@ -974,7 +1838,7 @@ class LarkCliTaskNotifyHook {
974
1838
  try {
975
1839
  this.cardStateStore.markPaused(taskId);
976
1840
  } catch (err) {
977
- logger4.warn(`persistPaused failed for task #${taskId}: ${err.message}`);
1841
+ logger6.warn(`persistPaused failed for task #${taskId}: ${err.message}`);
978
1842
  }
979
1843
  }
980
1844
  dropStaleMessageId(taskId) {
@@ -991,7 +1855,7 @@ class LarkCliTaskNotifyHook {
991
1855
  updatedAt: Date.now()
992
1856
  });
993
1857
  } catch (err) {
994
- logger4.warn(`dropStaleMessageId failed for task #${taskId}: ${err.message}`);
1858
+ logger6.warn(`dropStaleMessageId failed for task #${taskId}: ${err.message}`);
995
1859
  }
996
1860
  }
997
1861
  persistReopened(taskId) {
@@ -1000,7 +1864,7 @@ class LarkCliTaskNotifyHook {
1000
1864
  try {
1001
1865
  this.cardStateStore.markReopened(taskId);
1002
1866
  } catch (err) {
1003
- logger4.warn(`persistReopened failed for task #${taskId}: ${err.message}`);
1867
+ logger6.warn(`persistReopened failed for task #${taskId}: ${err.message}`);
1004
1868
  }
1005
1869
  }
1006
1870
  persistClosed(taskId) {
@@ -1009,7 +1873,7 @@ class LarkCliTaskNotifyHook {
1009
1873
  try {
1010
1874
  this.cardStateStore.markClosed(taskId);
1011
1875
  } catch (err) {
1012
- logger4.warn(`persistClosed failed for task #${taskId}: ${err.message}`);
1876
+ logger6.warn(`persistClosed failed for task #${taskId}: ${err.message}`);
1013
1877
  }
1014
1878
  }
1015
1879
  refreshTimeline(taskId) {
@@ -1021,7 +1885,7 @@ class LarkCliTaskNotifyHook {
1021
1885
  return;
1022
1886
  this.cardStateStore.upsert(this.snapshotTimeline(rec));
1023
1887
  } catch (err) {
1024
- logger4.warn(`refreshTimeline failed for task #${taskId}: ${err.message}`);
1888
+ logger6.warn(`refreshTimeline failed for task #${taskId}: ${err.message}`);
1025
1889
  }
1026
1890
  }
1027
1891
  async repinAfterResume(task, persisted) {
@@ -1037,9 +1901,9 @@ class LarkCliTaskNotifyHook {
1037
1901
  try {
1038
1902
  await this.cardNotifier.pinMessage(persisted.messageId, profile);
1039
1903
  this.persistPinned(task.id, true);
1040
- logger4.debug(`repinAfterResume: task #${task.id} 卡片 ${persisted.messageId} 已重新置顶`);
1904
+ logger6.debug(`repinAfterResume: task #${task.id} 卡片 ${persisted.messageId} 已重新置顶`);
1041
1905
  } catch (err) {
1042
- logger4.warn(`repinAfterResume failed (non-fatal) for task #${task.id}: ${err.message}`);
1906
+ logger6.warn(`repinAfterResume failed (non-fatal) for task #${task.id}: ${err.message}`);
1043
1907
  }
1044
1908
  }
1045
1909
  buildCardState(task, messageId, chatId, profile, pinned) {
@@ -1047,7 +1911,7 @@ class LarkCliTaskNotifyHook {
1047
1911
  try {
1048
1912
  existing = this.cardStateStore?.get(task.id);
1049
1913
  } catch (err) {
1050
- logger4.warn(`buildCardState: read existing failed for task #${task.id}: ${err.message}`);
1914
+ logger6.warn(`buildCardState: read existing failed for task #${task.id}: ${err.message}`);
1051
1915
  }
1052
1916
  const now = Date.now();
1053
1917
  return {
@@ -1081,7 +1945,7 @@ class LarkCliTaskNotifyHook {
1081
1945
  }
1082
1946
  isCardModeEnabled() {
1083
1947
  if (this.config.cardMode === false || !this.cardNotifier) {
1084
- logger4.debug("cardMode=false (legacy opt-out), skip task notification (no text fallback)");
1948
+ logger6.debug("cardMode=false (legacy opt-out), skip task notification (no text fallback)");
1085
1949
  return false;
1086
1950
  }
1087
1951
  return true;
@@ -1151,7 +2015,7 @@ class LarkCliTaskNotifyHook {
1151
2015
  }
1152
2016
  const claim = this.cardMessageIds.get(task.id);
1153
2017
  if (claim) {
1154
- logger4.debug(`sendCardStart: task #${task.id} 已有首卡(${claim})→ 跳过重复发卡`);
2018
+ logger6.debug(`sendCardStart: task #${task.id} 已有首卡(${claim})→ 跳过重复发卡`);
1155
2019
  return;
1156
2020
  }
1157
2021
  this.cardMessageIds.set(task.id, LarkCliTaskNotifyHook.CARD_START_INFLIGHT_SENTINEL);
@@ -1166,7 +2030,7 @@ class LarkCliTaskNotifyHook {
1166
2030
  const profile = target.profile;
1167
2031
  if (!chatId) {
1168
2032
  this.cardMessageIds.delete(task.id);
1169
- logger4.debug(`sendCardStart: no chatId for task #${task.id}, skip`);
2033
+ logger6.debug(`sendCardStart: no chatId for task #${task.id}, skip`);
1170
2034
  return;
1171
2035
  }
1172
2036
  try {
@@ -1184,10 +2048,10 @@ class LarkCliTaskNotifyHook {
1184
2048
  });
1185
2049
  this.cardMessageIds.set(task.id, messageId);
1186
2050
  this.persistCardState(this.buildCardState(task, messageId, chatId, profile, pinned), true);
1187
- logger4.debug(`sendCardStart: task #${task.id} → message ${messageId} (pinned=${pinned}) ` + `chat=${chatId} source=${target.source} reason=${target.reason}`);
2051
+ logger6.debug(`sendCardStart: task #${task.id} → message ${messageId} (pinned=${pinned}) ` + `chat=${chatId} source=${target.source} reason=${target.reason}`);
1188
2052
  } catch (err) {
1189
2053
  this.cardMessageIds.set(task.id, LarkCliTaskNotifyHook.CARD_START_FAILED_SENTINEL);
1190
- logger4.error(`sendCardStart failed for task #${task.id}: ${err.message}`);
2054
+ logger6.error(`sendCardStart failed for task #${task.id}: ${err.message}`);
1191
2055
  }
1192
2056
  }
1193
2057
  async sendCardUpdate(task, changes) {
@@ -1197,21 +2061,21 @@ class LarkCliTaskNotifyHook {
1197
2061
  if (persisted?.status === "closed") {
1198
2062
  const stillTerminal = isTerminalTaskStatus(String(task.status));
1199
2063
  if (stillTerminal) {
1200
- logger4.debug(`sendCardUpdate: task #${task.id} 卡片已终结(closed)且任务仍终态 → skip patch`);
2064
+ logger6.debug(`sendCardUpdate: task #${task.id} 卡片已终结(closed)且任务仍终态 → skip patch`);
1201
2065
  return;
1202
2066
  }
1203
- logger4.info(`sendCardUpdate: task #${task.id} 已 reopen(closed 记录 + 当前 status=${task.status})` + `→ 重新建卡`);
2067
+ logger6.info(`sendCardUpdate: task #${task.id} 已 reopen(closed 记录 + 当前 status=${task.status})` + `→ 重新建卡`);
1204
2068
  this.cardMessageIds.delete(task.id);
1205
2069
  this.dropStaleMessageId(task.id);
1206
2070
  }
1207
2071
  if (persisted?.status === "paused" && String(task.status) !== "paused") {
1208
2072
  this.persistReopened(task.id);
1209
- logger4.debug(`sendCardUpdate: task #${task.id} resume(paused → open),复用卡片 ${persisted.messageId}`);
2073
+ logger6.debug(`sendCardUpdate: task #${task.id} resume(paused → open),复用卡片 ${persisted.messageId}`);
1210
2074
  await this.repinAfterResume(task, persisted);
1211
2075
  }
1212
2076
  let messageId = this.cardMessageIds.get(task.id);
1213
2077
  if (!messageId && isTerminalTaskStatus(String(task.status))) {
1214
- logger4.debug(`sendCardUpdate: task #${task.id} 状态=${task.status}(终结/归档)且无卡片 → skip`);
2078
+ logger6.debug(`sendCardUpdate: task #${task.id} 状态=${task.status}(终结/归档)且无卡片 → skip`);
1215
2079
  return;
1216
2080
  }
1217
2081
  if (!messageId) {
@@ -1223,15 +2087,15 @@ class LarkCliTaskNotifyHook {
1223
2087
  if (!chatId) {
1224
2088
  if (!messageId)
1225
2089
  this.cardMessageIds.delete(task.id);
1226
- logger4.debug(`sendCardUpdate: no chatId for task #${task.id}, skip`);
2090
+ logger6.debug(`sendCardUpdate: no chatId for task #${task.id}, skip`);
1227
2091
  return;
1228
2092
  }
1229
2093
  if (messageId === LarkCliTaskNotifyHook.CARD_START_FAILED_SENTINEL) {
1230
- logger4.debug(`sendCardUpdate: startTask failed previously for task #${task.id}, skip`);
2094
+ logger6.debug(`sendCardUpdate: startTask failed previously for task #${task.id}, skip`);
1231
2095
  return;
1232
2096
  }
1233
2097
  if (messageId === LarkCliTaskNotifyHook.CARD_START_INFLIGHT_SENTINEL) {
1234
- logger4.debug(`sendCardUpdate: task #${task.id} 首卡正在发送中(in-flight)→ 跳过本次 update`);
2098
+ logger6.debug(`sendCardUpdate: task #${task.id} 首卡正在发送中(in-flight)→ 跳过本次 update`);
1235
2099
  return;
1236
2100
  }
1237
2101
  if (!messageId) {
@@ -1246,7 +2110,7 @@ class LarkCliTaskNotifyHook {
1246
2110
  this.persistCardState(this.buildCardState(task, messageId, chatId, profile, res.pinned), true);
1247
2111
  } catch (err) {
1248
2112
  this.cardMessageIds.set(task.id, LarkCliTaskNotifyHook.CARD_START_FAILED_SENTINEL);
1249
- logger4.error(`sendCardUpdate fallback start failed: ${err.message}`);
2113
+ logger6.error(`sendCardUpdate fallback start failed: ${err.message}`);
1250
2114
  return;
1251
2115
  }
1252
2116
  }
@@ -1273,7 +2137,7 @@ class LarkCliTaskNotifyHook {
1273
2137
  this.persistPinned(task.id, current?.pinned ?? persisted?.pinned ?? true);
1274
2138
  this.refreshTimeline(task.id);
1275
2139
  } catch (err) {
1276
- logger4.error(`sendCardUpdate failed: ${err.message}`);
2140
+ logger6.error(`sendCardUpdate failed: ${err.message}`);
1277
2141
  }
1278
2142
  }
1279
2143
  async sendCardFinish(task) {
@@ -1284,7 +2148,7 @@ class LarkCliTaskNotifyHook {
1284
2148
  const ctx = getEnvContextOrEmpty();
1285
2149
  const profile = this.resolveProfile(task, ctx);
1286
2150
  if (!messageId || messageId === LarkCliTaskNotifyHook.CARD_START_FAILED_SENTINEL) {
1287
- logger4.debug(`sendCardFinish: no card messageId for task #${task.id}, skip ` + `(记录缺失 → 不发新卡,保持幂等)`);
2151
+ logger6.debug(`sendCardFinish: no card messageId for task #${task.id}, skip ` + `(记录缺失 → 不发新卡,保持幂等)`);
1288
2152
  return;
1289
2153
  }
1290
2154
  const rawStatus = String(task.status);
@@ -1309,9 +2173,9 @@ class LarkCliTaskNotifyHook {
1309
2173
  this.persistPinned(task.id, false);
1310
2174
  this.cardMessageIds.delete(task.id);
1311
2175
  }
1312
- logger4.debug(`sendCardFinish: task #${task.id} → status=${status} ` + `(raw=${rawStatus}) message=${messageId}`);
2176
+ logger6.debug(`sendCardFinish: task #${task.id} → status=${status} ` + `(raw=${rawStatus}) message=${messageId}`);
1313
2177
  } catch (err) {
1314
- logger4.error(`sendCardFinish failed: ${err.message}`);
2178
+ logger6.error(`sendCardFinish failed: ${err.message}`);
1315
2179
  }
1316
2180
  }
1317
2181
  resolveChatId(task, env) {
@@ -1388,7 +2252,7 @@ class LarkCliTaskNotifyHook {
1388
2252
  const now = Date.now();
1389
2253
  const lastTime = this.lastNotifyTimestamps.get(key);
1390
2254
  if (lastTime && now - lastTime < throttleMs) {
1391
- logger4.debug(`[throttle] skip OPERATION_CREATED patch for task #${operation.taskId} (${now - lastTime}ms ago)`);
2255
+ logger6.debug(`[throttle] skip OPERATION_CREATED patch for task #${operation.taskId} (${now - lastTime}ms ago)`);
1392
2256
  return true;
1393
2257
  }
1394
2258
  this.lastNotifyTimestamps.set(key, now);
@@ -1431,7 +2295,7 @@ init_decorator();
1431
2295
  init_logger();
1432
2296
  init_context();
1433
2297
  import { spawn } from "child_process";
1434
- var logger5 = createLogger("lark-cli:per-task-group");
2298
+ var logger7 = createLogger("lark-cli:per-task-group");
1435
2299
  function isPluginDebugEnabled() {
1436
2300
  return process.env.ROY_TASK_NOTIFY_DEBUG === "1" || process.env.ROY_TRACE_DEBUG === "1";
1437
2301
  }
@@ -1486,7 +2350,7 @@ class PerTaskGroupChatPlugin {
1486
2350
  const renderedName = this.renderTemplate(this.config.nameTemplate, task);
1487
2351
  const existingChatId = this.getTaskChatId(task);
1488
2352
  if (existingChatId) {
1489
- logger5.debug(`ensureGroupChat[${this.pluginName}]: task #${task.id} 已有专属群 ${existingChatId} → 复用(不重复建群)`);
2353
+ logger7.debug(`ensureGroupChat[${this.pluginName}]: task #${task.id} 已有专属群 ${existingChatId} → 复用(不重复建群)`);
1490
2354
  return {
1491
2355
  chatId: existingChatId,
1492
2356
  profile: this.parseProfileFromContext(task.context),
@@ -1495,7 +2359,7 @@ class PerTaskGroupChatPlugin {
1495
2359
  }
1496
2360
  const inflight = this.groupChatInflight.get(task.id);
1497
2361
  if (inflight) {
1498
- logger5.debug(`ensureGroupChat[${this.pluginName}]: task #${task.id} 建群已在进行中 → 复用同一 promise`);
2362
+ logger7.debug(`ensureGroupChat[${this.pluginName}]: task #${task.id} 建群已在进行中 → 复用同一 promise`);
1499
2363
  return inflight;
1500
2364
  }
1501
2365
  const pending = this.doEnsureGroupChat(task, renderedName, replyChannelOverride);
@@ -1509,7 +2373,7 @@ class PerTaskGroupChatPlugin {
1509
2373
  async doEnsureGroupChat(task, name, replyChannelOverride) {
1510
2374
  const replyChannel = replyChannelOverride ?? getEnvContextOrEmpty().replyChannel;
1511
2375
  if (!replyChannel?.chatId && this.config.requireReplyChannel) {
1512
- logger5.warn(`ensureGroupChat[${this.pluginName}]: no replyChannel.chatId for task #${task.id} ` + `(requireReplyChannel=true) → 无法建群`);
2376
+ logger7.warn(`ensureGroupChat[${this.pluginName}]: no replyChannel.chatId for task #${task.id} ` + `(requireReplyChannel=true) → 无法建群`);
1513
2377
  return;
1514
2378
  }
1515
2379
  const description = this.renderTemplate(this.config.descriptionTemplate, task);
@@ -1527,30 +2391,30 @@ class PerTaskGroupChatPlugin {
1527
2391
  await this.writeChatBinding(task, chatId, profile, "creating");
1528
2392
  await this.attachChatIdToTask(task, chatId, profile);
1529
2393
  await this.writeChatBinding(task, chatId, profile, "ready");
1530
- logger5.info(`ensureGroupChat[${this.pluginName}]: created group ${chatId} for task #${task.id}`);
2394
+ logger7.info(`ensureGroupChat[${this.pluginName}]: created group ${chatId} for task #${task.id}`);
1531
2395
  return { chatId, profile, name };
1532
2396
  } catch (error) {
1533
- logger5.error(`ensureGroupChat[${this.pluginName}]: failed for task #${task.id}: ${error}`);
2397
+ logger7.error(`ensureGroupChat[${this.pluginName}]: failed for task #${task.id}: ${error}`);
1534
2398
  return;
1535
2399
  }
1536
2400
  }
1537
2401
  setTaskUpdater(updater) {
1538
2402
  this.taskUpdater = updater;
1539
- logger5.debug(`TaskUpdater injected for plugin ${this.name} (sourceId=${this.sourceId})`);
2403
+ logger7.debug(`TaskUpdater injected for plugin ${this.name} (sourceId=${this.sourceId})`);
1540
2404
  }
1541
2405
  hasTaskUpdater() {
1542
2406
  return this.taskUpdater !== undefined;
1543
2407
  }
1544
2408
  setTaskGetter(getter) {
1545
2409
  this.taskGetter = getter;
1546
- logger5.debug(`TaskGetter injected for plugin ${this.name} (sourceId=${this.sourceId})`);
2410
+ logger7.debug(`TaskGetter injected for plugin ${this.name} (sourceId=${this.sourceId})`);
1547
2411
  }
1548
2412
  hasTaskGetter() {
1549
2413
  return this.taskGetter !== undefined;
1550
2414
  }
1551
2415
  setSessionComponent(sessionComponent) {
1552
2416
  this.sessionComponent = sessionComponent;
1553
- logger5.debug(`SessionComponent injected for plugin ${this.name} (sourceId=${this.sourceId})`);
2417
+ logger7.debug(`SessionComponent injected for plugin ${this.name} (sourceId=${this.sourceId})`);
1554
2418
  }
1555
2419
  hasSessionComponent() {
1556
2420
  return this.sessionComponent !== undefined;
@@ -1563,7 +2427,7 @@ class PerTaskGroupChatPlugin {
1563
2427
  if (process.env.ROY_HOOK_DEBUG === "1") {
1564
2428
  console.error(`[DEBUG per-task-group-chat.execute] → SKIP (config.enabled=false)`);
1565
2429
  }
1566
- logger5.debug(`execute: plugin disabled (pluginName=${this.pluginName})`);
2430
+ logger7.debug(`execute: plugin disabled (pluginName=${this.pluginName})`);
1567
2431
  return;
1568
2432
  }
1569
2433
  if (this.pluginName && this.sourceId) {
@@ -1576,13 +2440,13 @@ class PerTaskGroupChatPlugin {
1576
2440
  if (process.env.ROY_HOOK_DEBUG === "1") {
1577
2441
  console.error(`[DEBUG per-task-group-chat.execute] → SKIP (plugin NOT enabled for ${key})`);
1578
2442
  }
1579
- logger5.debug(`execute[${this.pluginName}]: plugin NOT enabled (key=${key}, ` + `env.pluginEnabled=${JSON.stringify(env.pluginEnabled)}, ` + `hookPoint=${ctx.hookPoint}). ` + `→ skip. 修复:roy-agent eventsource update <id> 必须包含 PerTaskGroupChatPlugin`);
2443
+ logger7.debug(`execute[${this.pluginName}]: plugin NOT enabled (key=${key}, ` + `env.pluginEnabled=${JSON.stringify(env.pluginEnabled)}, ` + `hookPoint=${ctx.hookPoint}). ` + `→ skip. 修复:roy-agent eventsource update <id> 必须包含 PerTaskGroupChatPlugin`);
1580
2444
  return;
1581
2445
  }
1582
2446
  const enabledState = env.pluginEnabled ? env.pluginEnabled[key] === true ? "✅ enabled" : "❌ NOT enabled" : "⚠️ env.pluginEnabled 未设置 (env 没创建 / 插件未注册到 eventsource)";
1583
- logger5.debug(`execute[${this.pluginName}]: ${enabledState} (key=${key}, ` + `hookPoint=${ctx.hookPoint}, taskUpdaterInjected=${this.hasTaskUpdater()})`);
2447
+ logger7.debug(`execute[${this.pluginName}]: ${enabledState} (key=${key}, ` + `hookPoint=${ctx.hookPoint}, taskUpdaterInjected=${this.hasTaskUpdater()})`);
1584
2448
  } else {
1585
- logger5.debug(`execute[${this.pluginName}]: missing pluginName/sourceId metadata ` + `(pluginName=${this.pluginName}, sourceId=${this.sourceId}), ` + `skipping pluginEnabled check. hookPoint=${ctx.hookPoint}`);
2449
+ logger7.debug(`execute[${this.pluginName}]: missing pluginName/sourceId metadata ` + `(pluginName=${this.pluginName}, sourceId=${this.sourceId}), ` + `skipping pluginEnabled check. hookPoint=${ctx.hookPoint}`);
1586
2450
  }
1587
2451
  if (process.env.ROY_HOOK_DEBUG === "1") {
1588
2452
  console.error(`[DEBUG per-task-group-chat.execute] → PASSED pluginEnabled check, will dispatch hookPoint=${ctx.hookPoint}`);
@@ -1601,13 +2465,13 @@ class PerTaskGroupChatPlugin {
1601
2465
  await this.onAfterComplete(ctx);
1602
2466
  break;
1603
2467
  default:
1604
- logger5.debug(`execute: unhandled hookPoint=${ctx.hookPoint}, skip`);
2468
+ logger7.debug(`execute: unhandled hookPoint=${ctx.hookPoint}, skip`);
1605
2469
  }
1606
2470
  }
1607
2471
  async onAfterCreate(ctx) {
1608
2472
  const data = ctx.data;
1609
2473
  if (!data?.task) {
1610
- logger5.debug("onAfterCreate: task is missing, skip");
2474
+ logger7.debug("onAfterCreate: task is missing, skip");
1611
2475
  return;
1612
2476
  }
1613
2477
  const { task } = data;
@@ -1618,38 +2482,38 @@ class PerTaskGroupChatPlugin {
1618
2482
  console.error(`[DEBUG per-task-group-chat.onAfterCreate] env.messageId=${env.messageId}`);
1619
2483
  console.error(`[DEBUG per-task-group-chat.onAfterCreate] env.sourceId=${env.sourceId}`);
1620
2484
  }
1621
- logger5.debug(`onAfterCreate[${this.pluginName}]: enter task=${task.id} "${task.title}" ` + `parent_task_id=${task.parent_task_id ?? "none"} ` + `requireReplyChannel=${this.config.requireReplyChannel} ` + `disableSubTaskSkip=${this.config.disableSubTaskSkip} ` + `sendWelcomeMessage=${this.config.sendWelcomeMessage} ` + `autoCreateGroup=${this.config.autoCreateGroup}`);
2485
+ logger7.debug(`onAfterCreate[${this.pluginName}]: enter task=${task.id} "${task.title}" ` + `parent_task_id=${task.parent_task_id ?? "none"} ` + `requireReplyChannel=${this.config.requireReplyChannel} ` + `disableSubTaskSkip=${this.config.disableSubTaskSkip} ` + `sendWelcomeMessage=${this.config.sendWelcomeMessage} ` + `autoCreateGroup=${this.config.autoCreateGroup}`);
1622
2486
  if (!this.config.autoCreateGroup) {
1623
- logger5.debug(`onAfterCreate[${this.pluginName}]: task #${task.id} autoCreateGroup=false ` + `→ skip group chat creation(按需建群:用户在卡片顶部按钮触发)`);
2487
+ logger7.debug(`onAfterCreate[${this.pluginName}]: task #${task.id} autoCreateGroup=false ` + `→ skip group chat creation(按需建群:用户在卡片顶部按钮触发)`);
1624
2488
  return;
1625
2489
  }
1626
2490
  const replyChannel = this.getReplyChannelFromCtx(ctx);
1627
2491
  if (!replyChannel?.chatId && this.config.requireReplyChannel) {
1628
- logger5.debug(`onAfterCreate[${this.pluginName}]: no replyChannel.chatId for task #${task.id}, skip. ` + `修复:eventsource 收到 im.message.receive_v1 事件时 replyChannel 应有值;` + `或在 plugin config 设 requireReplyChannel=false 强制创建群聊(profile 降级到 fallbackProfile/无)`);
2492
+ logger7.debug(`onAfterCreate[${this.pluginName}]: no replyChannel.chatId for task #${task.id}, skip. ` + `修复:eventsource 收到 im.message.receive_v1 事件时 replyChannel 应有值;` + `或在 plugin config 设 requireReplyChannel=false 强制创建群聊(profile 降级到 fallbackProfile/无)`);
1629
2493
  return;
1630
2494
  }
1631
2495
  if (!replyChannel?.chatId && !this.config.requireReplyChannel) {
1632
- logger5.debug(`onAfterCreate[${this.pluginName}]: no replyChannel but requireReplyChannel=false, ` + `proceeding with profile fallback (fallbackProfile="${this.config.fallbackProfile}")`);
2496
+ logger7.debug(`onAfterCreate[${this.pluginName}]: no replyChannel but requireReplyChannel=false, ` + `proceeding with profile fallback (fallbackProfile="${this.config.fallbackProfile}")`);
1633
2497
  }
1634
2498
  if (this.isSubTask(task) && !this.config.disableSubTaskSkip) {
1635
- logger5.debug(`onAfterCreate[${this.pluginName}]: task #${task.id} is sub-task, ` + `skip group chat creation. 关闭方式:config.disableSubTaskSkip=true`);
2499
+ logger7.debug(`onAfterCreate[${this.pluginName}]: task #${task.id} is sub-task, ` + `skip group chat creation. 关闭方式:config.disableSubTaskSkip=true`);
1636
2500
  return;
1637
2501
  }
1638
2502
  if (this.isSubTask(task) && this.config.disableSubTaskSkip) {
1639
- logger5.debug(`onAfterCreate[${this.pluginName}]: task #${task.id} is sub-task but ` + `disableSubTaskSkip=true, proceeding to create group chat`);
2503
+ logger7.debug(`onAfterCreate[${this.pluginName}]: task #${task.id} is sub-task but ` + `disableSubTaskSkip=true, proceeding to create group chat`);
1640
2504
  }
1641
2505
  try {
1642
2506
  const created = await this.ensureGroupChat(task, replyChannel);
1643
2507
  if (!created?.chatId) {
1644
- logger5.error(`onAfterCreate[${this.pluginName}]: ensureGroupChat 未返回 chatId for task #${task.id}`);
2508
+ logger7.error(`onAfterCreate[${this.pluginName}]: ensureGroupChat 未返回 chatId for task #${task.id}`);
1645
2509
  if (!this.config.fallbackToMain) {
1646
2510
  throw new Error(`per-task group chat unavailable for task #${task.id}`);
1647
2511
  }
1648
2512
  return;
1649
2513
  }
1650
- logger5.info(`onAfterCreate[${this.pluginName}]: task #${task.id} → group ${created.chatId} ` + `(initial card owned by LarkCliTaskNotifyHook)`);
2514
+ logger7.info(`onAfterCreate[${this.pluginName}]: task #${task.id} → group ${created.chatId} ` + `(initial card owned by LarkCliTaskNotifyHook)`);
1651
2515
  } catch (error) {
1652
- logger5.error(`onAfterCreate[${this.pluginName}]: group chat failed for task #${task.id}: ${error}`);
2516
+ logger7.error(`onAfterCreate[${this.pluginName}]: group chat failed for task #${task.id}: ${error}`);
1653
2517
  if (!this.config.fallbackToMain)
1654
2518
  throw error;
1655
2519
  }
@@ -1657,7 +2521,7 @@ class PerTaskGroupChatPlugin {
1657
2521
  async writeChatBinding(task, chatId, profile, status, errorMessage) {
1658
2522
  if (!this.sessionComponent) {
1659
2523
  if (isPluginDebugEnabled()) {
1660
- logger5.debug(`writeChatBinding[${this.pluginName}]: no sessionComponent injected, ` + `skip chat-task binding for chat ${chatId} (status=${status})`);
2524
+ logger7.debug(`writeChatBinding[${this.pluginName}]: no sessionComponent injected, ` + `skip chat-task binding for chat ${chatId} (status=${status})`);
1661
2525
  }
1662
2526
  return;
1663
2527
  }
@@ -1670,37 +2534,37 @@ class PerTaskGroupChatPlugin {
1670
2534
  errorMessage: errorMessage ?? null
1671
2535
  });
1672
2536
  } catch (bindErr) {
1673
- logger5.warn(`writeChatBinding[${this.pluginName}]: failed to write chat-task binding for ` + `chat ${chatId} / task #${task.id} (status=${status}): ${bindErr}. ` + `群消息 handler 将无法自动注入 parentTaskId。`);
2537
+ logger7.warn(`writeChatBinding[${this.pluginName}]: failed to write chat-task binding for ` + `chat ${chatId} / task #${task.id} (status=${status}): ${bindErr}. ` + `群消息 handler 将无法自动注入 parentTaskId。`);
1674
2538
  }
1675
2539
  }
1676
2540
  async onAfterUpdate(ctx) {
1677
2541
  const data = ctx.data;
1678
2542
  if (!data?.task) {
1679
- logger5.debug("onAfterUpdate: task is missing, skip");
2543
+ logger7.debug("onAfterUpdate: task is missing, skip");
1680
2544
  return;
1681
2545
  }
1682
- logger5.debug(`onAfterUpdate[${this.pluginName}]: task #${data.task.id} 卡片由 ` + `LarkCliTaskNotifyHook 负责(本插件不再 patch),skip`);
2546
+ logger7.debug(`onAfterUpdate[${this.pluginName}]: task #${data.task.id} 卡片由 ` + `LarkCliTaskNotifyHook 负责(本插件不再 patch),skip`);
1683
2547
  }
1684
2548
  async onAfterOperationCreate(ctx) {
1685
2549
  const data = ctx.data;
1686
2550
  if (!data?.operation) {
1687
- logger5.debug("onAfterOperationCreate: operation is missing, skip");
2551
+ logger7.debug("onAfterOperationCreate: operation is missing, skip");
1688
2552
  return;
1689
2553
  }
1690
- logger5.debug(`onAfterOperationCreate[${this.pluginName}]: operation #${data.operation.id} ` + `卡片由 LarkCliTaskNotifyHook 负责,skip`);
2554
+ logger7.debug(`onAfterOperationCreate[${this.pluginName}]: operation #${data.operation.id} ` + `卡片由 LarkCliTaskNotifyHook 负责,skip`);
1691
2555
  }
1692
2556
  async onAfterComplete(ctx) {
1693
2557
  const data = ctx.data;
1694
2558
  if (!data?.task) {
1695
- logger5.debug("onAfterComplete: task is missing, skip");
2559
+ logger7.debug("onAfterComplete: task is missing, skip");
1696
2560
  return;
1697
2561
  }
1698
- logger5.debug(`onAfterComplete[${this.pluginName}]: task #${data.task.id} 终态卡片由 ` + `LarkCliTaskNotifyHook 负责,skip`);
2562
+ logger7.debug(`onAfterComplete[${this.pluginName}]: task #${data.task.id} 终态卡片由 ` + `LarkCliTaskNotifyHook 负责,skip`);
1699
2563
  }
1700
2564
  createGroupChat(opts) {
1701
2565
  const args = this.buildCreateGroupChatArgs(opts);
1702
2566
  if (isPluginDebugEnabled()) {
1703
- logger5.debug(`Creating group chat: lark-cli ${args.join(" ")}`);
2567
+ logger7.debug(`Creating group chat: lark-cli ${args.join(" ")}`);
1704
2568
  }
1705
2569
  return new Promise((resolve, reject) => {
1706
2570
  const proc = spawn("lark-cli", args, {
@@ -1775,16 +2639,16 @@ class PerTaskGroupChatPlugin {
1775
2639
  parsed[CTX_FIELD_CHAT_TASK_ID] = task.id;
1776
2640
  const newContext = JSON.stringify(parsed);
1777
2641
  if (!this.taskUpdater) {
1778
- logger5.warn(`attachChatIdToTask[${this.pluginName}]: ⚠️ no taskUpdater injected, ` + `chat_id ${chatId} NOT persisted to db for task #${task.id}. ` + `后果:下次 AFTER_UPDATE 从 DB 加载 task 时 getTaskChatId() 返回 undefined,` + `所有 update/complete/operation 通知 silently 丢失。` + `修复:event-source-handlers.ts:registerPluginHooks 应自动注入 setTaskUpdater。`);
2642
+ logger7.warn(`attachChatIdToTask[${this.pluginName}]: ⚠️ no taskUpdater injected, ` + `chat_id ${chatId} NOT persisted to db for task #${task.id}. ` + `后果:下次 AFTER_UPDATE 从 DB 加载 task 时 getTaskChatId() 返回 undefined,` + `所有 update/complete/operation 通知 silently 丢失。` + `修复:event-source-handlers.ts:registerPluginHooks 应自动注入 setTaskUpdater。`);
1779
2643
  task.context = newContext;
1780
2644
  return;
1781
2645
  }
1782
2646
  try {
1783
2647
  await this.taskUpdater(task.id, newContext);
1784
2648
  task.context = newContext;
1785
- logger5.debug(`attachChatIdToTask[${this.pluginName}]: ✅ persisted chat_id ${chatId} ` + `to task #${task.id} context`);
2649
+ logger7.debug(`attachChatIdToTask[${this.pluginName}]: ✅ persisted chat_id ${chatId} ` + `to task #${task.id} context`);
1786
2650
  } catch (error) {
1787
- logger5.error(`Failed to persist chat_id to task #${task.id}: ${error}`);
2651
+ logger7.error(`Failed to persist chat_id to task #${task.id}: ${error}`);
1788
2652
  task.context = newContext;
1789
2653
  }
1790
2654
  }
@@ -1879,15 +2743,15 @@ class PerTaskGroupChatPlugin {
1879
2743
  }
1880
2744
  resolveBotAppIds(replyChannel) {
1881
2745
  if (this.config.botAppIds.length > 0) {
1882
- logger5.debug(`resolveBotAppIds[${this.pluginName}]: using config.botAppIds=${JSON.stringify(this.config.botAppIds)}`);
2746
+ logger7.debug(`resolveBotAppIds[${this.pluginName}]: using config.botAppIds=${JSON.stringify(this.config.botAppIds)}`);
1883
2747
  return this.config.botAppIds;
1884
2748
  }
1885
2749
  const appId = replyChannel ? replyChannel.appId : undefined;
1886
2750
  if (appId) {
1887
- logger5.debug(`resolveBotAppIds[${this.pluginName}]: using replyChannel.appId=${appId} (auto-detected from event header.app_id)`);
2751
+ logger7.debug(`resolveBotAppIds[${this.pluginName}]: using replyChannel.appId=${appId} (auto-detected from event header.app_id)`);
1888
2752
  return [appId];
1889
2753
  }
1890
- logger5.warn(`resolveBotAppIds[${this.pluginName}]: ⚠️ no botAppIds found, creating group WITHOUT --bots. ` + `Bot will NOT be invited automatically. ` + `可能原因:(1) eventsource 没有收到 im.message.receive_v1 事件(replyChannel.appId 缺失);` + `(2) 配置了 requireReplyChannel=false 模式;` + `(3) buildReplyChannel 没透传 appId(应在 v1.10+ 修复)。` + `修复:手动设置 config.botAppIds: ["cli_xxx"] 或确认 eventsource 处理了 lark event。`);
2754
+ logger7.warn(`resolveBotAppIds[${this.pluginName}]: ⚠️ no botAppIds found, creating group WITHOUT --bots. ` + `Bot will NOT be invited automatically. ` + `可能原因:(1) eventsource 没有收到 im.message.receive_v1 事件(replyChannel.appId 缺失);` + `(2) 配置了 requireReplyChannel=false 模式;` + `(3) buildReplyChannel 没透传 appId(应在 v1.10+ 修复)。` + `修复:手动设置 config.botAppIds: ["cli_xxx"] 或确认 eventsource 处理了 lark event。`);
1891
2755
  return [];
1892
2756
  }
1893
2757
  resolveUserIds(replyChannel) {
@@ -1897,7 +2761,7 @@ class PerTaskGroupChatPlugin {
1897
2761
  if (id && id !== "sender" && id.startsWith("ou_")) {
1898
2762
  ids.add(id);
1899
2763
  } else if (id && id !== "sender") {
1900
- logger5.debug(`resolveUserIds[${this.pluginName}]: skip non-open_id in defaultInviteUserIds: "${id}"`);
2764
+ logger7.debug(`resolveUserIds[${this.pluginName}]: skip non-open_id in defaultInviteUserIds: "${id}"`);
1901
2765
  }
1902
2766
  }
1903
2767
  }
@@ -1920,7 +2784,7 @@ class PerTaskGroupChatPlugin {
1920
2784
  }
1921
2785
  }
1922
2786
  if (isPluginDebugEnabled()) {
1923
- logger5.debug(`resolveUserIds[${this.pluginName}]: ` + `defaultInviteUserIds=${JSON.stringify(this.config.defaultInviteUserIds)} ` + `inviteUserIds=${JSON.stringify(this.config.inviteUserIds)} ` + `replyChannel.userId=${replyChannel?.userId ?? "(none)"} ` + `→ final userIds=${JSON.stringify(Array.from(ids))}`);
2787
+ logger7.debug(`resolveUserIds[${this.pluginName}]: ` + `defaultInviteUserIds=${JSON.stringify(this.config.defaultInviteUserIds)} ` + `inviteUserIds=${JSON.stringify(this.config.inviteUserIds)} ` + `replyChannel.userId=${replyChannel?.userId ?? "(none)"} ` + `→ final userIds=${JSON.stringify(Array.from(ids))}`);
1924
2788
  }
1925
2789
  return Array.from(ids);
1926
2790
  }
@@ -2073,4 +2937,4 @@ __legacyDecorateClassTS([
2073
2937
  function createPerTaskGroupChatPlugin(config = {}) {
2074
2938
  return new PerTaskGroupChatPlugin(config);
2075
2939
  }
2076
- export { createAutoTaskPlugin, WORKFLOW_EXTRACTOR_PROMPT, formatExtractorInput, formatExtractorInputFromDescription, parseExtractorOutput, TaskTagPlugin, extractPerTaskChatId, extractPerTaskChatName, NotifyType, LarkCliTaskNotifyHook, createLarkCliTaskNotifyHook, PerTaskGroupChatPlugin, createPerTaskGroupChatPlugin };
2940
+ export { createAutoTaskPlugin, LAYA_REUSE_THRESHOLD, LayaDecisionPlugin, createLayaDecisionPlugin, WORKFLOW_EXTRACTOR_PROMPT, formatExtractorInput, formatExtractorInputFromDescription, parseExtractorOutput, TaskTagPlugin, extractPerTaskChatId, extractPerTaskChatName, NotifyType, LarkCliTaskNotifyHook, createLarkCliTaskNotifyHook, PerTaskGroupChatPlugin, createPerTaskGroupChatPlugin };