weaver-work-cli 0.1.33 → 0.1.38

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 (126) hide show
  1. package/dist/chunks/{archive-FVFY6GZN.js → archive-PDV724YK.js} +5 -4
  2. package/dist/chunks/{archive-oa-NW3OD4UV.js → archive-oa-4XE4MNXQ.js} +4 -3
  3. package/dist/chunks/{asset-7QJKMIDV.js → asset-PDL2QE2N.js} +8 -7
  4. package/dist/chunks/{attachment-content-OSAVA6K3.js → attachment-content-MDDGOE5E.js} +3 -3
  5. package/dist/chunks/{blog-I7SDPNW4.js → blog-UYR3EUXW.js} +5 -4
  6. package/dist/chunks/{caizhilian-O37ZIQNY.js → caizhilian-LG2X5NMF.js} +5 -4
  7. package/dist/chunks/{calendar-7KXE4ED3.js → calendar-FG4BAZHE.js} +4 -3
  8. package/dist/chunks/{chunk-Q6LWB5HX.js → chunk-6FTTSA45.js} +4 -3
  9. package/dist/chunks/{chunk-MQ2I5AA3.js → chunk-6LTFIFLM.js} +3 -2
  10. package/dist/chunks/{chunk-WRRJ4GG2.js → chunk-CORFRD7Q.js} +4 -3
  11. package/dist/chunks/{chunk-WY74UBJ6.js → chunk-DMV2TOMH.js} +2194 -240
  12. package/dist/chunks/{chunk-MEEFGWTM.js → chunk-ELYDWBZJ.js} +135 -10
  13. package/dist/chunks/{chunk-EEWPQE3O.js → chunk-FUWVHMVV.js} +7 -6
  14. package/dist/chunks/{chunk-HEL5XTYI.js → chunk-GGJFMNJM.js} +26 -4
  15. package/dist/chunks/{chunk-KAS45KNR.js → chunk-IZUDA353.js} +1 -1
  16. package/dist/chunks/{chunk-2FLWXFVS.js → chunk-J7USS3ED.js} +4 -4
  17. package/dist/chunks/{chunk-IHUYP6LO.js → chunk-JSMFPO43.js} +1 -1
  18. package/dist/chunks/{chunk-RBGOMK4Y.js → chunk-KKRL3REV.js} +5 -4
  19. package/dist/chunks/{chunk-OCU4RLHC.js → chunk-NI6GIPIM.js} +195 -36
  20. package/dist/chunks/{chunk-PRHJGPMA.js → chunk-NR6RKXPA.js} +2 -2
  21. package/dist/chunks/{chunk-D223PTXU.js → chunk-OJQUJZQF.js} +33 -7
  22. package/dist/chunks/{chunk-ILMBNVSG.js → chunk-UJJD4QWF.js} +4 -3
  23. package/dist/chunks/{chunk-6ATGWRZM.js → chunk-UZZBA6PV.js} +177 -14
  24. package/dist/chunks/{chunk-GFPXQIY2.js → chunk-VRYHGG6O.js} +2 -2
  25. package/dist/chunks/{chunk-NHA62QD7.js → chunk-XJY7G4DD.js} +3 -3
  26. package/dist/chunks/{chunk-VBCFFFGT.js → chunk-Z32KWY6G.js} +4 -1
  27. package/dist/chunks/{conflict-SFNEVJY5.js → conflict-BQZEQ4SP.js} +2 -2
  28. package/dist/chunks/contract-XIRAFTEV.js +9111 -0
  29. package/dist/chunks/{cowork-CKWYHXYB.js → cowork-BLG62BL2.js} +46 -26
  30. package/dist/chunks/{ebuilder-form-KOLJRVZD.js → ebuilder-form-NUKG5QW4.js} +3 -3
  31. package/dist/chunks/{ehr-WFOC3B4D.js → ehr-2STW5ILH.js} +174 -10
  32. package/dist/chunks/{esb-LJZTN663.js → esb-UUDQUNKY.js} +4 -4
  33. package/dist/chunks/{fna-F7QRIIZC.js → fna-6RCY4NU5.js} +15 -9
  34. package/dist/chunks/{host-VQ6JJXGQ.js → host-7RXEDMAH.js} +3 -3
  35. package/dist/chunks/{host-KXPTEWFS.js → host-EJZFROU3.js} +3 -3
  36. package/dist/chunks/{host-BCGUVEWX.js → host-FWDOSRH5.js} +3 -3
  37. package/dist/chunks/{host-DGE2TZD4.js → host-RHM3NPXV.js} +3 -3
  38. package/dist/chunks/{host-BQBRHVR3.js → host-UFF33TWJ.js} +3 -3
  39. package/dist/chunks/{hrm-NI6XAKW5.js → hrm-PKJ6ELSH.js} +3 -3
  40. package/dist/chunks/{im-7TRTF3KT.js → im-YJAXS7CO.js} +3 -3
  41. package/dist/chunks/{invoice-AOXWJKYT.js → invoice-HWVSXGX6.js} +3 -3
  42. package/dist/chunks/{jiuchuanhui-7NQWEZAW.js → jiuchuanhui-L7VKILZB.js} +4 -3
  43. package/dist/chunks/{lazy-dispatch-RNS4EE4S.js → lazy-dispatch-NMYEIHRH.js} +4 -4
  44. package/dist/chunks/{mail-VDHJAB4O.js → mail-B42RRU7G.js} +46 -24
  45. package/dist/chunks/{meeting-GA7XCNNP.js → meeting-CFYVBJSS.js} +77 -12
  46. package/dist/chunks/{okr-TQIRERPQ.js → okr-GCLF6V7W.js} +4 -3
  47. package/dist/chunks/{plan-4RY4LN2P.js → plan-ZVL7GBRI.js} +4 -3
  48. package/dist/chunks/{project-D6XT2OC5.js → project-4YWAAE2K.js} +4 -3
  49. package/dist/chunks/{registry-QKMI43Q6.js → registry-EP4VTHZS.js} +8 -8
  50. package/dist/chunks/{registry-JHRIDQ3A.js → registry-FRJ6H7BT.js} +4 -4
  51. package/dist/chunks/{registry-VOGKFFTR.js → registry-SG2PGVCF.js} +2 -2
  52. package/dist/chunks/{room-recommend-D4EUOOWY.js → room-recommend-R3JVUUAC.js} +4 -4
  53. package/dist/chunks/{schedule-XPFBHPCV.js → schedule-NIJBTYDO.js} +4 -4
  54. package/dist/chunks/{workflow-HLBZRWIY.js → workflow-BB24DFBY.js} +11 -4
  55. package/dist/index.js +31 -26
  56. package/dist/internal/e10/auth-recovery.js +114 -1
  57. package/dist/internal/e10/client.js +16 -2
  58. package/dist/internal/e10/fetch-timing.js +31 -0
  59. package/dist/internal/e10/request-runtime.js +108 -7
  60. package/dist/shortcuts/archive/host.js +3 -2
  61. package/dist/shortcuts/archive-oa/host.js +2 -1
  62. package/dist/shortcuts/asset/host.js +6 -5
  63. package/dist/shortcuts/blog/host.js +3 -2
  64. package/dist/shortcuts/caizhilian/host.js +3 -2
  65. package/dist/shortcuts/calendar/host.js +2 -1
  66. package/dist/shortcuts/contract/host.js +2 -1
  67. package/dist/shortcuts/cowork/host.js +10 -1
  68. package/dist/shortcuts/cowork/operations/types.js +17 -2
  69. package/dist/shortcuts/ebuilder-form/host.js +3 -1
  70. package/dist/shortcuts/ehr/host.js +2 -1
  71. package/dist/shortcuts/ehr/operations/recruit-write.js +35 -0
  72. package/dist/shortcuts/ehr/operations/recruit.js +147 -0
  73. package/dist/shortcuts/esb/host.js +2 -1
  74. package/dist/shortcuts/fna/host.js +2 -1
  75. package/dist/shortcuts/fna/operations/expense-chart.js +19 -7
  76. package/dist/shortcuts/hrm/host.js +2 -1
  77. package/dist/shortcuts/im/host.js +3 -2
  78. package/dist/shortcuts/index.js +5 -0
  79. package/dist/shortcuts/invoice/host.js +5 -4
  80. package/dist/shortcuts/jiuchuanhui/host.js +2 -1
  81. package/dist/shortcuts/mail/host.js +10 -1
  82. package/dist/shortcuts/mail/operations/types.js +17 -2
  83. package/dist/shortcuts/meeting/host.js +3 -2
  84. package/dist/shortcuts/meeting/index.js +79 -1
  85. package/dist/shortcuts/meeting/invitation/text.js +71 -0
  86. package/dist/shortcuts/meeting/manifest.js +10 -0
  87. package/dist/shortcuts/meeting/operations/eb/catalog.js +5 -0
  88. package/dist/shortcuts/meeting/operations/eb/conflict.js +6 -1
  89. package/dist/shortcuts/meeting/operations/eb/create-flow.js +2105 -169
  90. package/dist/shortcuts/meeting/operations/eb/crm-search.js +478 -0
  91. package/dist/shortcuts/meeting/operations/eb/factory.js +161 -8
  92. package/dist/shortcuts/meeting/operations/eb/index.js +2 -0
  93. package/dist/shortcuts/meeting/operations/eb/room-recommend.js +36 -2
  94. package/dist/shortcuts/meeting/operations/eb-fallback.js +4 -0
  95. package/dist/shortcuts/meeting/operations/env.js +6 -0
  96. package/dist/shortcuts/meeting/operations/invitation.js +200 -28
  97. package/dist/shortcuts/okr/host.js +2 -1
  98. package/dist/shortcuts/plan/host.js +2 -1
  99. package/dist/shortcuts/project/host.js +2 -1
  100. package/dist/shortcuts/workflow/endpoints.js +8 -3
  101. package/dist/shortcuts/workflow/operations/form.js +341 -36
  102. package/dist/shortcuts/workflow/operations.js +23 -1
  103. package/docs/ehr.md +1 -1
  104. package/docs/fna.md +2 -1
  105. package/package.json +1 -1
  106. package/skill-template/business-info.json +18 -0
  107. package/skill-template/domains/cowork.md +14 -0
  108. package/skills/weaver-e10-cowork/SKILL.md +101 -0
  109. package/skills/weaver-e10-cowork/references/cowork-read.md +90 -0
  110. package/skills/weaver-e10-cowork/references/cowork-write.md +131 -0
  111. package/skills/weaver-e10-cowork/references/safety-boundaries.md +45 -0
  112. package/skills/weaver-e10-jucailin/SKILL.md +21 -3
  113. package/skills/weaver-e10-jucailin/references/recruit-read.md +25 -0
  114. package/skills/weaver-e10-jucailin/references/recruit-write.md +13 -0
  115. package/skills/weaver-e10-jucailin/references/salary.md +15 -11
  116. package/skills/weaver-e10-mail/SKILL.md +12 -9
  117. package/skills/weaver-e10-meeting/SKILL.md +499 -300
  118. package/skills/weaver-e10-meeting/references/meeting-eb.md +334 -0
  119. package/skills/weaver-e10-meeting/references/meeting-invitation.md +64 -6
  120. package/skills/weaver-e10-meeting.zip +0 -0
  121. package/skills/weaver-e10-okr/references/source-manifest.json +3 -3
  122. package/skills/weaver-e10-qiyecheng/SKILL.md +5 -2
  123. package/skills/weaver-e10-qiyecheng/references/catalog.md +2 -2
  124. package/skills/weaver-e10-qiyecheng/references/expense-chart.md +15 -1
  125. package/skills/weaver-e10-qiyecheng/references/source-manifest.json +22 -21
  126. package/skills/weaver-e10-yimiaoban/references/source-manifest.json +3 -3
@@ -2,28 +2,30 @@ import { createRequire as __esbuildCreateRequire } from 'node:module';
2
2
  const require = __esbuildCreateRequire(import.meta.url);
3
3
  import {
4
4
  ebRoomRecommendOperation,
5
+ loadRooms,
5
6
  recommendRooms,
6
7
  rememberFrequentRoom
7
- } from "./chunk-HEL5XTYI.js";
8
+ } from "./chunk-GGJFMNJM.js";
8
9
  import {
9
10
  meetingEbScheduleOperations
10
- } from "./chunk-2FLWXFVS.js";
11
+ } from "./chunk-J7USS3ED.js";
11
12
  import {
12
13
  ebMeetingWritePreflight,
13
14
  fetchEbAppSettings,
14
15
  findCommonFreeSlots,
15
16
  meetingEbConflictOperations
16
- } from "./chunk-NHA62QD7.js";
17
+ } from "./chunk-XJY7G4DD.js";
17
18
  import {
19
+ PENDING_MARK,
18
20
  listMeetingAttachments,
19
21
  meetingAttachmentContentOperations,
20
22
  renderDocx
21
- } from "./chunk-MQ2I5AA3.js";
23
+ } from "./chunk-6LTFIFLM.js";
22
24
  import {
23
25
  meetingEnvOperations,
24
26
  meetingStateDir,
25
27
  resolveMeetingEnvMode
26
- } from "./chunk-VBCFFFGT.js";
28
+ } from "./chunk-Z32KWY6G.js";
27
29
  import {
28
30
  EB_BASE,
29
31
  EB_ENDPOINTS,
@@ -55,7 +57,7 @@ import {
55
57
  toIdString,
56
58
  verifyLease,
57
59
  writeBody
58
- } from "./chunk-MEEFGWTM.js";
60
+ } from "./chunk-ELYDWBZJ.js";
59
61
 
60
62
  // src/shortcuts/meeting/operations/meetings.ts
61
63
  function overlapsDate(beginDatetime, endDatetime, date) {
@@ -2221,7 +2223,7 @@ var meetingAttachmentAttach = {
2221
2223
  if (mode === "append" && existingIds.length === 0) {
2222
2224
  existingIds.push(...beforeIds);
2223
2225
  }
2224
- const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-QKMI43Q6.js");
2226
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
2225
2227
  const list = getMeetingOperation2("meeting.eb.meeting.list");
2226
2228
  let fileId = incomingFileId;
2227
2229
  let name = String(args.name ?? "").trim();
@@ -2309,7 +2311,7 @@ function textOf(value) {
2309
2311
  return value === void 0 || value === null ? "" : String(value);
2310
2312
  }
2311
2313
  async function readMinute(env, id) {
2312
- const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-QKMI43Q6.js");
2314
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
2313
2315
  const detail = getMeetingOperation2("meeting.eb.minute.detail");
2314
2316
  const result = await detail.handler({ id }, env);
2315
2317
  const row = result?.data?.data;
@@ -2388,7 +2390,7 @@ var meetingMinuteWriteContent = {
2388
2390
  } else {
2389
2391
  statusReason = "\u4EC5\u6E05\u7A7A\u5185\u5BB9\uFF0C\u672A\u6539\u72B6\u6001";
2390
2392
  }
2391
- const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-QKMI43Q6.js");
2393
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
2392
2394
  const prepare = getMeetingOperation2("meeting.eb.minute.update.prepare");
2393
2395
  const apply = getMeetingOperation2("meeting.eb.minute.update.apply");
2394
2396
  const prepared = await prepare.handler({ id, mt_id: mtId, ...patch }, env);
@@ -2518,8 +2520,8 @@ function parseLocalTime(value) {
2518
2520
  return Number.isNaN(parsed.getTime()) ? null : parsed;
2519
2521
  }
2520
2522
  function formatStamp(d) {
2521
- const pad2 = (n) => String(n).padStart(2, "0");
2522
- return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())} ${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`;
2523
+ const pad3 = (n) => String(n).padStart(2, "0");
2524
+ return `${d.getFullYear()}-${pad3(d.getMonth() + 1)}-${pad3(d.getDate())} ${pad3(d.getHours())}:${pad3(d.getMinutes())}:${pad3(d.getSeconds())}`;
2523
2525
  }
2524
2526
  function computeChatWindow(meeting, now = /* @__PURE__ */ new Date()) {
2525
2527
  const begin = parseLocalTime(meeting.begin_datetime);
@@ -2647,16 +2649,16 @@ function buildDigest(messages, nameByUid, keepSystem, maxChars) {
2647
2649
  return { transcript, kept: lines.length, dropped, truncated };
2648
2650
  }
2649
2651
  async function saveTranscript(ctx, mtId, transcript) {
2650
- const { mkdir: mkdir4, writeFile: writeFile4 } = await import("node:fs/promises");
2652
+ const { mkdir: mkdir5, writeFile: writeFile5 } = await import("node:fs/promises");
2651
2653
  const { join: join3 } = await import("node:path");
2652
2654
  const { stateDir } = await import("./state-Y2XBP6YE.js");
2653
2655
  const dir = join3(stateDir(ctx), "transcripts");
2654
- await mkdir4(dir, { recursive: true });
2655
- const pad2 = (n) => String(n).padStart(2, "0");
2656
+ await mkdir5(dir, { recursive: true });
2657
+ const pad3 = (n) => String(n).padStart(2, "0");
2656
2658
  const now = /* @__PURE__ */ new Date();
2657
- const stamp2 = `${now.getFullYear()}${pad2(now.getMonth() + 1)}${pad2(now.getDate())}-${pad2(now.getHours())}${pad2(now.getMinutes())}${pad2(now.getSeconds())}`;
2659
+ const stamp2 = `${now.getFullYear()}${pad3(now.getMonth() + 1)}${pad3(now.getDate())}-${pad3(now.getHours())}${pad3(now.getMinutes())}${pad3(now.getSeconds())}`;
2658
2660
  const filePath = join3(dir, `${mtId}-${stamp2}.txt`);
2659
- await writeFile4(filePath, transcript, "utf8");
2661
+ await writeFile5(filePath, transcript, "utf8");
2660
2662
  return filePath;
2661
2663
  }
2662
2664
  var meetingMinuteFromGroupChat = {
@@ -2746,7 +2748,7 @@ var meetingMinuteFromGroupChat = {
2746
2748
  const mtId = textOf2(args.mt_id).trim();
2747
2749
  if (!mtId) throw meetingValidationError("mt_id_required", "\u5FC5\u987B\u63D0\u4F9B\u4F1A\u8BAE id\uFF08mt_id\uFF09");
2748
2750
  const pipeline = ["text", "minutes", "document"].includes(args.pipeline) ? args.pipeline : "text";
2749
- const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-QKMI43Q6.js");
2751
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
2750
2752
  const ctx = env.ctx;
2751
2753
  let stepStart = Date.now();
2752
2754
  const listed = await getMeetingOperation2("meeting.eb.meeting.list").handler(
@@ -2770,7 +2772,7 @@ var meetingMinuteFromGroupChat = {
2770
2772
  }
2771
2773
  mark("readMeeting", stepStart);
2772
2774
  const pullStart = Date.now();
2773
- const { createImHost } = await import("./host-DGE2TZD4.js");
2775
+ const { createImHost } = await import("./host-RHM3NPXV.js");
2774
2776
  const { createImContinuationSigner } = await import("./continuation-2CKSBLA6.js");
2775
2777
  const { getImOperation } = await import("./registry-OZSMDVPP.js");
2776
2778
  const imEnv = { ctx, host: createImHost(ctx), continuation: createImContinuationSigner(ctx) };
@@ -2838,9 +2840,9 @@ var meetingMinuteFromGroupChat = {
2838
2840
  const extraSections = [];
2839
2841
  for (const file of extraFiles) {
2840
2842
  try {
2841
- const { readFile: readFile4 } = await import("node:fs/promises");
2842
- const buffer = await readFile4(file);
2843
- const { extractText } = await import("./attachment-content-OSAVA6K3.js");
2843
+ const { readFile: readFile5 } = await import("node:fs/promises");
2844
+ const buffer = await readFile5(file);
2845
+ const { extractText } = await import("./attachment-content-MDDGOE5E.js");
2844
2846
  const extracted = extractText(buffer, file, "");
2845
2847
  if (extracted.text.trim()) {
2846
2848
  extraSections.push(`\u3010\u6750\u6599\uFF1A${file.split(/[\\/]/).pop() ?? file}\u3011
@@ -2967,9 +2969,9 @@ ${groupTranscript}` : ""
2967
2969
  if (!employeeId) {
2968
2970
  warnings.push("\u672A\u53D6\u5230 ESB \u89E6\u53D1\u4EBA id\uFF08employeeId\uFF09\uFF0C\u5DF2\u8DF3\u8FC7\u540E\u7EED ESB \u8C03\u7528");
2969
2971
  } else {
2970
- const { createEsbHost } = await import("./host-BCGUVEWX.js");
2972
+ const { createEsbHost } = await import("./host-FWDOSRH5.js");
2971
2973
  const { createEsbContinuationSigner } = await import("./continuation-NLECEJPX.js");
2972
- const { getEsbOperation } = await import("./registry-JHRIDQ3A.js");
2974
+ const { getEsbOperation } = await import("./registry-FRJ6H7BT.js");
2973
2975
  const esbEnv = { ctx, host: createEsbHost(ctx), continuation: createEsbContinuationSigner(ctx) };
2974
2976
  const prepareOp = getEsbOperation("esb.trigger.prepare");
2975
2977
  const applyOp = getEsbOperation("esb.trigger.apply");
@@ -3153,7 +3155,7 @@ var meetingDecisionNotifyGroup = {
3153
3155
  const warnings = [];
3154
3156
  const mtId = textOf3(args.mt_id).trim();
3155
3157
  if (!mtId) throw meetingValidationError("mt_id_required", "\u5FC5\u987B\u63D0\u4F9B\u4F1A\u8BAE id\uFF08mt_id\uFF09");
3156
- const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-QKMI43Q6.js");
3158
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
3157
3159
  const ctx = env.ctx;
3158
3160
  const listed = await getMeetingOperation2("meeting.eb.meeting.list").handler(
3159
3161
  { mt_id: mtId, pageNo: 1, pageSize: 1 },
@@ -3217,7 +3219,7 @@ var meetingDecisionNotifyGroup = {
3217
3219
  charCount: formatted.text.length,
3218
3220
  text: formatted.text
3219
3221
  };
3220
- const { createImHost } = await import("./host-DGE2TZD4.js");
3222
+ const { createImHost } = await import("./host-RHM3NPXV.js");
3221
3223
  const imHost = createImHost(ctx);
3222
3224
  let displayText = formatted.text;
3223
3225
  let wireText = formatted.text;
@@ -3359,14 +3361,14 @@ function defaultDueDate(begin, days) {
3359
3361
  const base = new Date(normalized.replace(/-/g, "/"));
3360
3362
  const start = Number.isNaN(base.getTime()) ? /* @__PURE__ */ new Date() : base;
3361
3363
  const due = new Date(start.getTime() + Math.max(days, 1) * 24 * 36e5);
3362
- const pad2 = (n) => String(n).padStart(2, "0");
3363
- return `${due.getFullYear()}-${pad2(due.getMonth() + 1)}-${pad2(due.getDate())} 18:00`;
3364
+ const pad3 = (n) => String(n).padStart(2, "0");
3365
+ return `${due.getFullYear()}-${pad3(due.getMonth() + 1)}-${pad3(due.getDate())} 18:00`;
3364
3366
  }
3365
3367
  async function waitForTaskIncrease(fetchTotal, baseline, timeoutMs = 15e3, intervalMs = 2e3) {
3366
3368
  const start = Date.now();
3367
3369
  let total = baseline;
3368
3370
  while (Date.now() - start < timeoutMs) {
3369
- await new Promise((resolve5) => setTimeout(resolve5, intervalMs));
3371
+ await new Promise((resolve6) => setTimeout(resolve6, intervalMs));
3370
3372
  total = await fetchTotal();
3371
3373
  if (total > baseline) return { total, waitedMs: Date.now() - start, increased: true };
3372
3374
  }
@@ -3455,7 +3457,7 @@ var meetingPipelineRun = {
3455
3457
  const needsConfirm = level === "tasks" || level === "notify";
3456
3458
  const confirmed = args.confirm === true;
3457
3459
  const executedLevel = needsConfirm && !confirmed ? "document" : level;
3458
- const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-QKMI43Q6.js");
3460
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
3459
3461
  const ctx = env.ctx;
3460
3462
  let stepStart = Date.now();
3461
3463
  const reuseMinuteId = textOf4(args.minuteId).trim();
@@ -3524,7 +3526,7 @@ var meetingPipelineRun = {
3524
3526
  ]);
3525
3527
  }
3526
3528
  stepStart = Date.now();
3527
- const { createHrmHost } = await import("./host-KXPTEWFS.js");
3529
+ const { createHrmHost } = await import("./host-EJZFROU3.js");
3528
3530
  const { getHrmOperation } = await import("./registry-NYPYMZXG.js");
3529
3531
  const hrmEnv = { ctx, host: createHrmHost(ctx) };
3530
3532
  const queryOp = getHrmOperation("hrm.employee.query");
@@ -3562,8 +3564,8 @@ var meetingPipelineRun = {
3562
3564
  })();
3563
3565
  const beginAt = textOf4(meetingInfo.end_datetime).trim() || textOf4(meetingInfo.begin_datetime).trim() || (() => {
3564
3566
  const now = /* @__PURE__ */ new Date();
3565
- const pad2 = (n) => String(n).padStart(2, "0");
3566
- return `${now.getFullYear()}-${pad2(now.getMonth() + 1)}-${pad2(now.getDate())} ${pad2(now.getHours())}:${pad2(now.getMinutes())}`;
3567
+ const pad3 = (n) => String(n).padStart(2, "0");
3568
+ return `${now.getFullYear()}-${pad3(now.getMonth() + 1)}-${pad3(now.getDate())} ${pad3(now.getHours())}:${pad3(now.getMinutes())}`;
3567
3569
  })();
3568
3570
  const dueDays = typeof args.taskDueDays === "number" && args.taskDueDays > 0 ? args.taskDueDays : 7;
3569
3571
  const dueAt = defaultDueDate(beginAt, dueDays);
@@ -3721,13 +3723,75 @@ var meetingPipelineRun = {
3721
3723
  var meetingPipelineOperations = [meetingPipelineRun];
3722
3724
 
3723
3725
  // src/shortcuts/meeting/operations/invitation.ts
3726
+ import { existsSync as existsSync2 } from "node:fs";
3724
3727
  import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
3725
3728
  import { dirname, join, resolve as resolve3 } from "node:path";
3726
3729
  import { fileURLToPath } from "node:url";
3730
+
3731
+ // src/shortcuts/meeting/invitation/text.ts
3732
+ function cell(value) {
3733
+ return String(value ?? "").replace(/\|/g, "/").replace(/\s*\r?\n\s*/g, " ").trim();
3734
+ }
3735
+ function renderInvitationMarkdown(resolver, agendaRows, options = {}) {
3736
+ const pending = /* @__PURE__ */ new Set();
3737
+ const val = (key) => {
3738
+ const rendered = resolver(key);
3739
+ if (rendered.pending) {
3740
+ pending.add(key);
3741
+ return PENDING_MARK;
3742
+ }
3743
+ return String(rendered.text ?? "").trim();
3744
+ };
3745
+ const lines = [];
3746
+ lines.push("# \u9080\u8BF7\u51FD");
3747
+ lines.push("");
3748
+ lines.push(`**\u5C0A\u656C\u7684 ${val("guest_org")}\uFF1A**`);
3749
+ lines.push("");
3750
+ lines.push(`${val("caller")} \u8BDA\u9080\u60A8\u53C2\u52A0\u300C${val("mt_name")}\u300D\uFF0C\u73B0\u5C06\u4F1A\u8BAE\u5B89\u6392\u901A\u77E5\u5982\u4E0B\uFF1A`);
3751
+ lines.push("");
3752
+ lines.push(`- **\u4F1A\u8BAE\u540D\u79F0**\uFF1A${val("mt_name")}`);
3753
+ lines.push(`- **\u4F1A\u8BAE\u65F6\u95F4**\uFF1A${val("begin_datetime")}~${val("end_datetime")}`);
3754
+ lines.push(`- **\u4F1A\u8BAE\u5730\u70B9**\uFF1A${val("address")}`);
3755
+ lines.push(`- **\u53C2\u4F1A\u4EBA\u5458**\uFF1A${val("hrm_members")}`);
3756
+ lines.push(`- **\u4F1A\u8BAE\u5185\u5BB9**\uFF1A${val("mt_desc")}`);
3757
+ lines.push("");
3758
+ lines.push(`**${val("begin_date")}\u4F1A\u8BAE\u8BAE\u7A0B\u5B89\u6392\uFF1A**`);
3759
+ lines.push("");
3760
+ lines.push("| \u8BAE\u7A0B\u5B89\u6392 | \u8BAE\u7A0B\u5185\u5BB9 | \u8BAE\u7A0B\u65F6\u95F4 |");
3761
+ lines.push("| --- | --- | --- |");
3762
+ if (agendaRows.length === 0) {
3763
+ lines.push("| \u2014 | \u6682\u65E0\u8BAE\u7A0B | \u2014 |");
3764
+ } else {
3765
+ for (const [name, content, timeRange] of agendaRows) {
3766
+ lines.push(`| ${cell(name) || "\u2014"} | ${cell(content) || "\u2014"} | ${cell(timeRange) || "\u2014"} |`);
3767
+ }
3768
+ }
3769
+ lines.push("");
3770
+ lines.push(val("company_name"));
3771
+ lines.push("");
3772
+ lines.push(val("issue_date"));
3773
+ lines.push("");
3774
+ lines.push("> \u6CE8\uFF1A\u4EE5\u4E0A\u4E3A\u62DF\u5B9A\u4F1A\u8BAE\u5B89\u6392\uFF0C\u5177\u4F53\u4F1A\u8BAE\u65F6\u95F4\u4E0E\u8BAE\u7A0B\u9700\u6839\u636E\u5B9E\u9645\u60C5\u51B5\u786E\u8BA4\u3002");
3775
+ if (options.pendingHint !== false && pending.size > 0) {
3776
+ lines.push("");
3777
+ lines.push(`> \u26A0\uFE0F \u6807 ${PENDING_MARK} \u7684\u5B57\u6BB5\u4F1A\u8BAE\u91CC\u6CA1\u6709\u503C\uFF0C\u9700\u8981\u4EBA\u5DE5\u8865\uFF1B\u8865\u5B8C\u53EF\u7528 mtId \u91CD\u65B0\u751F\u6210\u3002`);
3778
+ }
3779
+ return { markdown: lines.join("\n"), pendingFields: [...pending] };
3780
+ }
3781
+
3782
+ // src/shortcuts/meeting/operations/invitation.ts
3727
3783
  function templatePath() {
3728
3784
  const override = process.env.WEAVER_MEETING_INVITATION_TEMPLATE;
3729
3785
  if (override) return resolve3(override);
3730
3786
  const here = dirname(fileURLToPath(import.meta.url));
3787
+ let dir = here;
3788
+ for (let depth = 0; depth < 6; depth += 1) {
3789
+ const candidate = join(dir, "templates", "meeting", "invitation.docx");
3790
+ if (existsSync2(candidate)) return candidate;
3791
+ const parent = dirname(dir);
3792
+ if (parent === dir) break;
3793
+ dir = parent;
3794
+ }
3731
3795
  return resolve3(here, "../../../../templates/meeting/invitation.docx");
3732
3796
  }
3733
3797
  var WEEKDAYS = ["\u661F\u671F\u65E5", "\u661F\u671F\u4E00", "\u661F\u671F\u4E8C", "\u661F\u671F\u4E09", "\u661F\u671F\u56DB", "\u661F\u671F\u4E94", "\u661F\u671F\u516D"];
@@ -3765,7 +3829,7 @@ function formatTimeRange(start, end) {
3765
3829
  }
3766
3830
  async function loadAgendaFromTopics(env, mtId) {
3767
3831
  try {
3768
- const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-QKMI43Q6.js");
3832
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
3769
3833
  const operation = getMeetingOperation2("meeting.eb.topic.list");
3770
3834
  const result = await operation.handler({ mt_id: mtId, pageNo: 1, pageSize: 200 }, env);
3771
3835
  const rows = result.data?.rows ?? [];
@@ -3862,6 +3926,65 @@ async function resolveIssuerOrganization(env, creatorId) {
3862
3926
  return null;
3863
3927
  }
3864
3928
  }
3929
+ async function resolveSessionUserName(env) {
3930
+ try {
3931
+ const session = await env.host.getSession();
3932
+ const userId = String(session?.userId ?? "").trim();
3933
+ if (!userId) return "";
3934
+ const { hrmEmployeeOperations } = await import("./employee-SAK2QYXX.js");
3935
+ const batch = hrmEmployeeOperations.find((operation) => operation.name === "hrm.employee.batch");
3936
+ if (!batch) return "";
3937
+ const result = await batch.handler({ ids: [userId] }, env);
3938
+ const row = (result?.data?.rows ?? [])[0];
3939
+ return String(row?.name ?? "").trim();
3940
+ } catch {
3941
+ return "";
3942
+ }
3943
+ }
3944
+ var DETAIL_FALLBACK_KEYS = [
3945
+ "hrm_members",
3946
+ "mt_desc",
3947
+ "total_member",
3948
+ "caller",
3949
+ "contacter",
3950
+ "begin_datetime",
3951
+ "end_datetime",
3952
+ "other_members",
3953
+ "mt_creator",
3954
+ "topic_presenter",
3955
+ "mt_format",
3956
+ "mt_type",
3957
+ "video_url",
3958
+ "org_members",
3959
+ "with_secauth"
3960
+ ];
3961
+ async function supplementRowFromDetail(row, mtId, env) {
3962
+ const isBlank = (value) => {
3963
+ if (value === void 0 || value === null) return true;
3964
+ if (Array.isArray(value)) return value.length === 0;
3965
+ return String(value).trim() === "";
3966
+ };
3967
+ const missing = DETAIL_FALLBACK_KEYS.filter((key) => isBlank(row[key]));
3968
+ if (missing.length === 0) return { row, filled: [] };
3969
+ try {
3970
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
3971
+ const detailOperation = getMeetingOperation2("meeting.eb.meeting.detail");
3972
+ const result = await detailOperation.handler({ id: mtId }, env);
3973
+ const flat = result?.data?.data ?? result?.data ?? {};
3974
+ const merged = { ...row };
3975
+ const filled = [];
3976
+ for (const key of missing) {
3977
+ const value = flat[key];
3978
+ if (!isBlank(value)) {
3979
+ merged[key] = value;
3980
+ filled.push(key);
3981
+ }
3982
+ }
3983
+ return { row: merged, filled };
3984
+ } catch {
3985
+ return { row, filled: [] };
3986
+ }
3987
+ }
3865
3988
  function buildResolver(row, extra, issuerOrg) {
3866
3989
  const begin = parseDateTime(row.begin_datetime);
3867
3990
  const end = parseDateTime(row.end_datetime);
@@ -3899,6 +4022,9 @@ function buildResolver(row, extra, issuerOrg) {
3899
4022
  org_members: orgMembers,
3900
4023
  // 落款公司全称:`extra.company_name` 可覆盖(对外开放时客户传自己的公司名)
3901
4024
  company_name: String(extra.company_name ?? "").trim() || DEFAULT_COMPANY_NAME,
4025
+ // 客户单位(「尊敬的 XXX」):先看 extra(建会时由 create.flow 把参会客户名带进来),
4026
+ // 再看会议主表的 crm_members(部分租户会存),都没有才留【待补充】
4027
+ guest_org: String(extra.guest_org ?? "").trim() || flattenFieldValue(row.crm_members) || "",
3902
4028
  // 单独暴露上级公司,模板需要「公司级落款」时可直接用
3903
4029
  issuer_company: issuerCompany,
3904
4030
  other_members: humanizeMembers(row.other_members),
@@ -3921,7 +4047,10 @@ function buildResolver(row, extra, issuerOrg) {
3921
4047
  };
3922
4048
  const lookup = (key) => {
3923
4049
  if (key in derived) return derived[key];
3924
- if (key in standard) return standard[key];
4050
+ if (key in standard) {
4051
+ const standardValue = standard[key];
4052
+ if (String(standardValue ?? "").trim() !== "") return standardValue;
4053
+ }
3925
4054
  if (key.startsWith("extra.") && key.slice(6) in extra) return extra[key.slice(6)];
3926
4055
  if (key in extra) return extra[key];
3927
4056
  const raw = row[key];
@@ -3945,14 +4074,24 @@ var meetingInvitationRender = {
3945
4074
  resource: "invitation",
3946
4075
  method: "render",
3947
4076
  action: "meeting.invitation.render",
3948
- summary: "\u6309\u4F1A\u8BAE\u771F\u5B9E\u6570\u636E\u751F\u6210\u9080\u8BF7\u51FD docx\uFF08\u53EA\u8BFB\uFF0C\u4E0D\u53D1\u9001\uFF09\uFF1A\u6570\u636E\u53D6 meeting.eb.meeting.list \u5355\u67E5\uFF1B\u8BAE\u7A0B\u6709\u8BAE\u9898\u5C31\u6309\u8BAE\u9898\u81EA\u52A8\u586B\u3001\u6CA1\u6709\u5219\u4FDD\u7559\u6A21\u677F\u793A\u4F8B\u884C\uFF1B\u53D6\u4E0D\u5230\u7684\u5B57\u6BB5\u6E32\u67D3\u6210\u9EC4\u8272\u9AD8\u4EAE\u3010\u5F85\u8865\u5145\u3011\uFF1B\u79DF\u6237\u81EA\u5B9A\u4E49\u5B57\u6BB5\u7528 extra \u4F20\u5165",
4077
+ summary: "\u6309\u4F1A\u8BAE\u771F\u5B9E\u6570\u636E\u751F\u6210\u9080\u8BF7\u51FD\uFF08\u53EA\u8BFB\uFF0C\u4E0D\u53D1\u9001\uFF09\uFF1A**\u540C\u65F6\u8FD4\u56DE docx \u4E0E\u5BF9\u8BDD\u5185\u53EF\u5C55\u793A\u7684 Markdown \u5168\u6587**\uFF08`text` \u5B57\u6BB5\uFF09\uFF0C\u4F1A\u8BDD\u91CC\u5E94\u76F4\u63A5\u628A `text` \u8D34\u51FA\u6765\uFF0C\u4E0D\u8981\u53EA\u7ED9\u6587\u4EF6\u540D\u8BA9\u4EBA\u81EA\u5DF1\u4E0B\u8F7D\uFF1B\u6570\u636E\u53D6 meeting.eb.meeting.list \u5355\u67E5\uFF1B\u8BAE\u7A0B\u6709\u8BAE\u9898\u5C31\u6309\u8BAE\u9898\u81EA\u52A8\u586B\u3001\u6CA1\u6709\u5219\u4FDD\u7559\u6A21\u677F\u793A\u4F8B\u884C\uFF1B\u53D6\u4E0D\u5230\u7684\u5B57\u6BB5\u6E32\u67D3\u6210\u3010\u5F85\u8865\u5145\u3011\uFF08docx \u91CC\u662F\u9EC4\u8272\u9AD8\u4EAE\uFF09\uFF1B\u79DF\u6237\u81EA\u5B9A\u4E49\u5B57\u6BB5\u7528 extra \u4F20\u5165",
3949
4078
  capability: "invitation",
3950
4079
  risk: "read",
3951
4080
  inputSchema: {
3952
4081
  type: "object",
3953
4082
  additionalProperties: false,
3954
4083
  properties: {
3955
- mtId: { ...SCHEMA_ID, description: "\u4F1A\u8BAE id\uFF08EB \u7248 mt_detail \u4E3B\u952E\uFF09" },
4084
+ mtId: { ...SCHEMA_ID, description: "\u4F1A\u8BAE id\uFF08EB \u7248 mt_detail \u4E3B\u952E\uFF09\u3002**\u4E0E\u4E0B\u9762\u7684\u5B57\u6BB5\u4E8C\u9009\u4E00**\uFF1A\u6709 mtId \u8D70\u771F\u5B9E\u4F1A\u8BAE\u6570\u636E\uFF1B\u6CA1\u6709\u5219\u7528\u4F20\u5165\u5B57\u6BB5\u76F4\u63A5\u751F\u6210\uFF08\u6D41\u7A0B\u5EFA\u4F1A\u5BA1\u6279\u4E2D\u4E5F\u80FD\u51FA\u9080\u8BF7\u51FD\uFF09" },
4085
+ mt_name: { type: "string", description: "\u4F1A\u8BAE\u540D\u79F0\uFF08\u65E0 mtId \u65F6\u5FC5\u586B\uFF09" },
4086
+ begin_datetime: { type: "string", description: "\u5F00\u59CB\u65F6\u95F4 `yyyy-MM-dd HH:mm`\uFF08\u65E0 mtId \u65F6\u5FC5\u586B\uFF09" },
4087
+ end_datetime: { type: "string", description: "\u7ED3\u675F\u65F6\u95F4 `yyyy-MM-dd HH:mm`\uFF08\u65E0 mtId \u65F6\u5EFA\u8BAE\u586B\uFF0C\u7F3A\u4E86\u7ED3\u675F\u65F6\u95F4\u4F1A\u662F\u3010\u5F85\u8865\u5145\u3011\uFF09" },
4088
+ address: { type: "string", description: "\u4F1A\u8BAE\u5730\u70B9/\u4F1A\u8BAE\u5BA4**\u540D\u79F0**\uFF08\u65E0 mtId \u65F6\u7528\uFF1B\u8FD9\u91CC\u8981\u4F20\u540D\u79F0\uFF0C\u4E0D\u662F id\uFF09" },
4089
+ hrm_members: { type: "string", description: "\u53C2\u4F1A\u4EBA\u59D3\u540D\uFF08\u9017\u53F7/\u987F\u53F7\u5206\u9694\uFF0C\u65E0 mtId \u65F6\u7528\uFF09" },
4090
+ mt_desc: { type: "string", description: "\u4F1A\u8BAE\u5185\u5BB9/\u8BAE\u9898\u6982\u8981" },
4091
+ org_members: { type: "string", description: "\u843D\u6B3E\u5355\u4F4D\uFF08\u4E0D\u4F20\u5219\u5C1D\u8BD5\u7528\u53D1\u8D77\u4EBA\u90E8\u95E8 / \u7559\u3010\u5F85\u8865\u5145\u3011\uFF09" },
4092
+ caller: { type: "string", description: "\u53EC\u96C6\u4EBA" },
4093
+ contacter: { type: "string", description: "\u8054\u7CFB\u4EBA" },
4094
+ total_member: { type: "string", description: "\u53C2\u4F1A\u4EBA\u6570" },
3956
4095
  extra: {
3957
4096
  type: "object",
3958
4097
  additionalProperties: { type: "string" },
@@ -3963,41 +4102,78 @@ var meetingInvitationRender = {
3963
4102
  description: "\u8F93\u51FA\u6587\u4EF6\u5B8C\u6574\u8DEF\u5F84\uFF08\u53EF\u9009\uFF09\u3002\u7F3A\u7701\u5199\u5230 ~/.weaver-work-cli/meeting/invitations/\u9080\u8BF7\u51FD-{\u4F1A\u8BAE\u540D}-{\u65E5\u671F}.docx"
3964
4103
  }
3965
4104
  },
3966
- required: ["mtId"]
4105
+ required: []
3967
4106
  },
3968
4107
  handler: async (args, env) => {
3969
4108
  const mtId = String(args.mtId ?? "").trim();
3970
- if (!mtId) {
3971
- throw meetingValidationError("mt_id_required", "\u5FC5\u987B\u63D0\u4F9B\u4F1A\u8BAE id\uFF08mtId\uFF09");
3972
- }
3973
4109
  const extra = {};
3974
4110
  if (args.extra && typeof args.extra === "object") {
3975
4111
  for (const [key, value] of Object.entries(args.extra)) {
3976
4112
  if (value !== void 0 && value !== null) extra[key] = String(value);
3977
4113
  }
3978
4114
  }
3979
- const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-QKMI43Q6.js");
3980
- const listOperation = getMeetingOperation2("meeting.eb.meeting.list");
3981
4115
  let row;
3982
- try {
3983
- const listed = await listOperation.handler({ mt_id: mtId, pageNo: 1, pageSize: 1 }, env);
3984
- const rows = listed.data?.rows ?? [];
3985
- row = rows[0]?.mainTable ?? rows[0];
3986
- } catch (error) {
3987
- throw meetingValidationError(
3988
- "invitation_source_unavailable",
3989
- `\u8BFB\u53D6\u4F1A\u8BAE\u6570\u636E\u5931\u8D25\uFF08\u9080\u8BF7\u51FD\u5F53\u524D\u4F9D\u8D56 EB \u7248\u4F1A\u8BAE\u5217\u8868\uFF09\uFF1A${error instanceof Error ? error.message : String(error)}`,
3990
- { mtId }
3991
- );
3992
- }
3993
- if (!row) {
3994
- throw meetingValidationError("not_found", `\u672A\u627E\u5230\u4F1A\u8BAE id=${mtId}\uFF0C\u6216\u5F53\u524D\u8D26\u53F7\u65E0\u6743\u9650\u67E5\u770B`, { mtId });
4116
+ let dataSource = "meeting";
4117
+ let detailSupplementary = [];
4118
+ if (!mtId) {
4119
+ const mtName = String(args.mt_name ?? "").trim();
4120
+ const beginText = String(args.begin_datetime ?? "").trim();
4121
+ if (!mtName || !beginText) {
4122
+ throw meetingValidationError(
4123
+ "invitation_source_required",
4124
+ "\u8981\u4E48\u4F20 mtId\uFF08\u6309\u771F\u5B9E\u4F1A\u8BAE\u751F\u6210\uFF09\uFF0C\u8981\u4E48\u4F20 mt_name + begin_datetime\uFF08\u6309\u7533\u8BF7\u5355\u4FE1\u606F\u9884\u586B\u751F\u6210\uFF09"
4125
+ );
4126
+ }
4127
+ const session = await env.host.getSession().catch(() => null);
4128
+ const sessionUserId = String(session?.userId ?? "");
4129
+ row = {
4130
+ mt_name: mtName,
4131
+ begin_datetime: beginText,
4132
+ end_datetime: String(args.end_datetime ?? "").trim(),
4133
+ address: String(args.address ?? "").trim(),
4134
+ hrm_members: String(args.hrm_members ?? "").trim(),
4135
+ mt_desc: String(args.mt_desc ?? "").trim(),
4136
+ org_members: String(args.org_members ?? "").trim(),
4137
+ caller: String(args.caller ?? "").trim(),
4138
+ contacter: String(args.contacter ?? "").trim(),
4139
+ total_member: String(args.total_member ?? "").trim(),
4140
+ creator: sessionUserId
4141
+ };
4142
+ dataSource = "payload";
4143
+ } else {
4144
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
4145
+ const listOperation = getMeetingOperation2("meeting.eb.meeting.list");
4146
+ try {
4147
+ const listed = await listOperation.handler({ mt_id: mtId, pageNo: 1, pageSize: 1 }, env);
4148
+ const rows = listed.data?.rows ?? [];
4149
+ row = rows[0]?.mainTable ?? rows[0];
4150
+ } catch (error) {
4151
+ throw meetingValidationError(
4152
+ "invitation_source_unavailable",
4153
+ `\u8BFB\u53D6\u4F1A\u8BAE\u6570\u636E\u5931\u8D25\uFF08\u9080\u8BF7\u51FD\u5F53\u524D\u4F9D\u8D56 EB \u7248\u4F1A\u8BAE\u5217\u8868\uFF09\uFF1A${error instanceof Error ? error.message : String(error)}`,
4154
+ { mtId }
4155
+ );
4156
+ }
4157
+ if (!row) {
4158
+ throw meetingValidationError("not_found", `\u672A\u627E\u5230\u4F1A\u8BAE id=${mtId}\uFF0C\u6216\u5F53\u524D\u8D26\u53F7\u65E0\u6743\u9650\u67E5\u770B`, { mtId });
4159
+ }
4160
+ const supplement = await supplementRowFromDetail(row, mtId, env);
4161
+ row = supplement.row;
4162
+ detailSupplementary = supplement.filled;
4163
+ }
4164
+ let callerFromSession = false;
4165
+ if (!String(row.caller ?? "").trim() && !String(row.mt_creator ?? "").trim()) {
4166
+ const sessionName = await resolveSessionUserName(env);
4167
+ if (sessionName) {
4168
+ row = { ...row, caller: sessionName };
4169
+ callerFromSession = true;
4170
+ }
3995
4171
  }
3996
4172
  const creatorId = String(row.creator ?? "").trim();
3997
4173
  const needsIssuerOrg = !String(row.org_members ?? "").trim() && creatorId !== "";
3998
4174
  const issuerOrg = needsIssuerOrg ? await resolveIssuerOrganization(env, creatorId) : null;
3999
4175
  const { resolver, orgSource } = buildResolver(row, extra, issuerOrg);
4000
- let agendaRows = await loadAgendaFromTopics(env, mtId);
4176
+ let agendaRows = dataSource === "meeting" && mtId ? await loadAgendaFromTopics(env, mtId) : [];
4001
4177
  let agendaSource = agendaRows.length > 0 ? "meeting.topic" : "template";
4002
4178
  if (agendaRows.length === 0) {
4003
4179
  const fromExtra = parseAgendaFromExtra(extra.agenda);
@@ -4019,6 +4195,7 @@ var meetingInvitationRender = {
4019
4195
  const rendered = renderDocx(template, resolver, {
4020
4196
  agenda: { headerKeyword: AGENDA_HEADER_KEYWORD, rows: agendaRows }
4021
4197
  });
4198
+ const textVersion = renderInvitationMarkdown(resolver, agendaRows);
4022
4199
  const begin = parseDateTime(row.begin_datetime);
4023
4200
  const dateTag = begin ? `${begin.getFullYear()}${pad(begin.getMonth() + 1)}${pad(begin.getDate())}` : "\u672A\u77E5\u65E5\u671F";
4024
4201
  const fileName = `\u9080\u8BF7\u51FD-${safeFileName(String(row.mt_name ?? ""))}-${dateTag}.docx`;
@@ -4026,6 +4203,11 @@ var meetingInvitationRender = {
4026
4203
  await mkdir2(dirname(target), { recursive: true });
4027
4204
  await writeFile2(target, rendered.buffer);
4028
4205
  const warnings = [];
4206
+ if (dataSource === "payload") {
4207
+ warnings.push(
4208
+ "\u672C\u9080\u8BF7\u51FD\u6309**\u7533\u8BF7\u5355\u5DF2\u77E5\u4FE1\u606F**\u9884\u751F\u6210\uFF08\u4F1A\u8BAE\u53EF\u80FD\u8FD8\u5728\u5BA1\u6279\u4E2D\u3001\u5C1A\u672A\u751F\u6210\uFF09\uFF0C\u5C5E\u4E8E**\u9884\u7F6E\u51FD**\uFF1A\u8BF7\u4E0B\u8F7D\u540E\u4EBA\u5DE5\u6838\u5BF9/\u4FEE\u6539\u518D\u81EA\u884C\u53D1\u9001\u3002\u4F1A\u8BAE\u6B63\u5F0F\u751F\u6210\u540E\u53EF\u7528 mtId \u91CD\u65B0\u751F\u6210\u4E00\u7248\u62FF\u5230\u5B8C\u6574\u5B57\u6BB5\u3002"
4209
+ );
4210
+ }
4029
4211
  if (rendered.pending.length > 0) {
4030
4212
  warnings.push(
4031
4213
  `\u4EE5\u4E0B\u5B57\u6BB5\u53D6\u4E0D\u5230\u503C\uFF0C\u5DF2\u6E32\u67D3\u6210\u9EC4\u8272\u9AD8\u4EAE\u3010\u5F85\u8865\u5145\u3011\uFF0C\u8BF7\u4EBA\u5DE5\u8865\u5145\uFF1A${rendered.pending.join("\u3001")}`
@@ -4050,14 +4232,21 @@ var meetingInvitationRender = {
4050
4232
  filePath: target,
4051
4233
  fileName,
4052
4234
  bytes: rendered.buffer.length,
4053
- meetingId: mtId,
4235
+ meetingId: mtId || null,
4054
4236
  meetingName: String(row.mt_name ?? ""),
4237
+ // meeting=按真实会议数据;payload=按申请单字段预生成(会议可能还没生成,属预置函)
4238
+ dataSource,
4239
+ // 列表单查丢字段时,用 detail 补回来的字段名(有值 = 邀请函用的是合并后的数据)
4240
+ detailSupplemented: detailSupplementary,
4241
+ // 对话内展示用的 Markdown 全文(与 docx 同源)。上层应直接把这段贴进回复正文,
4242
+ // ⛔ 不要只丢一个 filePath 让用户自己去本地打开看内容。
4243
+ text: textVersion.markdown,
4055
4244
  filledFields: rendered.filled,
4056
4245
  pendingFields: rendered.pending,
4057
4246
  // 落款单位的实际来源,便于调用方判断是否需要人工改写
4058
4247
  orgSource,
4059
- // 召集人来源:meeting.caller=会议自己填的 / meeting.mt_creator=回落到创建人 / none=都没有
4060
- callerSource: String(row.caller ?? "").trim() ? "meeting.caller" : String(row.mt_creator ?? "").trim() ? "meeting.mt_creator" : "none",
4248
+ // 召集人来源:meeting.caller=会议自己填的 / meeting.mt_creator=回落到创建人 / session.user=回落到发起人本人 / none=都没有
4249
+ callerSource: callerFromSession ? "session.user" : String(row.caller ?? "").trim() ? "meeting.caller" : String(row.mt_creator ?? "").trim() ? "meeting.mt_creator" : "none",
4061
4250
  // 议程来源:meeting.topic=按议题自动填 / extra=调用方传入 / template=保留模板示例行
4062
4251
  agendaSource,
4063
4252
  agendaRows: agendaRows.length,
@@ -4354,7 +4543,7 @@ function buildCodewords(bytes, version, ecc) {
4354
4543
  for (const byte of bytes) appendBits(byte, 8);
4355
4544
  appendBits(0, Math.min(4, capacityBits - bits.length));
4356
4545
  appendBits(0, (8 - bits.length % 8) % 8);
4357
- for (let pad2 = 236; bits.length < capacityBits; pad2 ^= 236 ^ 17) appendBits(pad2, 8);
4546
+ for (let pad3 = 236; bits.length < capacityBits; pad3 ^= 236 ^ 17) appendBits(pad3, 8);
4358
4547
  const dataCodewords = new Array(bits.length / 8).fill(0);
4359
4548
  bits.forEach((bit, i) => {
4360
4549
  dataCodewords[i >>> 3] |= bit << 7 - (i & 7);
@@ -4540,8 +4729,8 @@ function renderQrSvg(matrix, options = {}) {
4540
4729
  var ECC_LEVELS = ["L", "M", "Q", "H"];
4541
4730
  function stamp() {
4542
4731
  const now = /* @__PURE__ */ new Date();
4543
- const pad2 = (n) => String(n).padStart(2, "0");
4544
- return `${now.getFullYear()}${pad2(now.getMonth() + 1)}${pad2(now.getDate())}-${pad2(now.getHours())}${pad2(now.getMinutes())}${pad2(now.getSeconds())}`;
4732
+ const pad3 = (n) => String(n).padStart(2, "0");
4733
+ return `${now.getFullYear()}${pad3(now.getMonth() + 1)}${pad3(now.getDate())}-${pad3(now.getHours())}${pad3(now.getMinutes())}${pad3(now.getSeconds())}`;
4545
4734
  }
4546
4735
  function safeName(input) {
4547
4736
  return input.replace(/[\\/:*?"<>|\s]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60);
@@ -4706,7 +4895,7 @@ var meetingSummaryGet = {
4706
4895
  const mtId = textOf5(args.mt_id).trim();
4707
4896
  if (!mtId) throw meetingValidationError("mt_id_required", "\u5FC5\u987B\u63D0\u4F9B\u4F1A\u8BAE id\uFF08mt_id\uFF09");
4708
4897
  const ctx = env.ctx;
4709
- const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-QKMI43Q6.js");
4898
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
4710
4899
  const warnings = [];
4711
4900
  const timings = {};
4712
4901
  const include = Array.isArray(args.include) && args.include.length > 0 ? args.include.filter((item) => ALL_SECTIONS.includes(item)) : ALL_SECTIONS;
@@ -4842,7 +5031,7 @@ var meetingSummaryGet = {
4842
5031
  warnings.push("\u8BE5\u4F1A\u8BAE\u6CA1\u6709\u5173\u8054\u7FA4\u804A\uFF08imchat_groupid \u4E3A\u7A7A\uFF09\uFF0Cchat \u6BB5\u4E3A null");
4843
5032
  } else {
4844
5033
  try {
4845
- const { createImHost } = await import("./host-DGE2TZD4.js");
5034
+ const { createImHost } = await import("./host-RHM3NPXV.js");
4846
5035
  const { createImContinuationSigner } = await import("./continuation-2CKSBLA6.js");
4847
5036
  const { getImOperation } = await import("./registry-OZSMDVPP.js");
4848
5037
  const imEnv = {
@@ -4968,7 +5157,7 @@ var meetingFormFields = {
4968
5157
  hint: "\u6CA1\u89E3\u6790\u5230\u8BE5\u8868\u7684 objId\uFF08\u53EF\u80FD\u5F53\u524D\u73AF\u5883\u6CA1\u6709\u8FD9\u5F20\u8868\uFF0C\u6216\u8D26\u53F7\u65E0\u6743\u9650\uFF09"
4969
5158
  }, { warnings: resolved.warnings });
4970
5159
  }
4971
- const { createEbuilderFormHost } = await import("./host-BQBRHVR3.js");
5160
+ const { createEbuilderFormHost } = await import("./host-UFF33TWJ.js");
4972
5161
  const { createEbuilderFormContinuationSigner } = await import("./continuation-RN6PZN2M.js");
4973
5162
  const { executeEbuilderFormOperation } = await import("./operations-5QL3AECO.js");
4974
5163
  const formEnv = {
@@ -5020,15 +5209,15 @@ var CONTINUATION_TTL_SECONDS6 = 600;
5020
5209
  var DATE_PATTERN = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}$";
5021
5210
  function formatSecondNow() {
5022
5211
  const now = /* @__PURE__ */ new Date();
5023
- const pad2 = (value) => String(value).padStart(2, "0");
5024
- return `${now.getFullYear()}-${pad2(now.getMonth() + 1)}-${pad2(now.getDate())} ${pad2(now.getHours())}:${pad2(now.getMinutes())}:${pad2(now.getSeconds())}`;
5212
+ const pad3 = (value) => String(value).padStart(2, "0");
5213
+ return `${now.getFullYear()}-${pad3(now.getMonth() + 1)}-${pad3(now.getDate())} ${pad3(now.getHours())}:${pad3(now.getMinutes())}:${pad3(now.getSeconds())}`;
5025
5214
  }
5026
5215
  var DECISION_DONE = "2";
5027
5216
  var MT_STATUS_OVER = "8";
5028
5217
  var CANCELLED_MARK_DELAY_MS = 800;
5029
5218
  async function isCancelledMeeting(env, mtId) {
5030
5219
  try {
5031
- const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-QKMI43Q6.js");
5220
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
5032
5221
  const listed = await getMeetingOperation2("meeting.eb.meeting.list").handler(
5033
5222
  // withCount:false —— 这里只要一行状态,不必额外请求 count(少一次往返,也便于测试替身)
5034
5223
  { mt_id: mtId, pageNo: 1, pageSize: 1, withCount: false },
@@ -5247,8 +5436,8 @@ var decisionPublishApply = {
5247
5436
  );
5248
5437
  }
5249
5438
  if (cancelled) {
5250
- await new Promise((resolve5) => {
5251
- setTimeout(resolve5, CANCELLED_MARK_DELAY_MS);
5439
+ await new Promise((resolve6) => {
5440
+ setTimeout(resolve6, CANCELLED_MARK_DELAY_MS);
5252
5441
  });
5253
5442
  warnings.push(
5254
5443
  `\u8BE5\u4F1A\u8BAE\u6B64\u524D\u88AB\u53D6\u6D88\u8FC7\uFF1A\u5DF2\u5728\u300C\u5199\u51B3\u8BAE\u300D\u4E0E\u300C\u6807\u8BB0\u5DF2\u51B3\u8BAE\u300D\u4E4B\u95F4\u7559 ${CANCELLED_MARK_DELAY_MS}ms \u95F4\u9694\uFF0C\u907F\u514D ESB \u628A\u540C\u4E00\u6279\u660E\u7EC6\u751F\u6210\u4E24\u8F6E\u4EFB\u52A1\uFF08\u771F\u673A\u9A8C\u8BC1\u8FC7\u7684\u5751\uFF09`
@@ -5549,7 +5738,383 @@ var ebMeetingAssociation = async (ctx) => {
5549
5738
  };
5550
5739
  };
5551
5740
 
5741
+ // src/shortcuts/meeting/operations/eb/crm-search.ts
5742
+ var DEFAULT_CRM_FLOW_ID = "904205116264026112";
5743
+ var DEFAULT_KEYWORD_FIELD = "custom_name";
5744
+ var KEYWORD_FIELD_HINTS = [
5745
+ "custom_name",
5746
+ "mc",
5747
+ "sjmc",
5748
+ "khmc",
5749
+ "customerName",
5750
+ "customer_name",
5751
+ "clientName",
5752
+ "name",
5753
+ "keyword",
5754
+ "keywordName",
5755
+ "searchKey"
5756
+ ];
5757
+ var ID_KEYS = ["sjID", "sjid", "sj_id", "sjjId", "customerId", "custId", "clientId", "id"];
5758
+ var NAME_KEYS = ["mc", "sjmc", "sj_name", "customerName", "custName", "clientName", "name"];
5759
+ var LIST_KEYS = [
5760
+ "detail1",
5761
+ "detail2",
5762
+ "detail3",
5763
+ "detail",
5764
+ "details",
5765
+ "detailList",
5766
+ "list",
5767
+ "rows",
5768
+ "items",
5769
+ "data",
5770
+ "records",
5771
+ "result",
5772
+ "customers",
5773
+ "sjList"
5774
+ ];
5775
+ function asRecord(value) {
5776
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
5777
+ }
5778
+ function pickFirst(row, keys) {
5779
+ for (const key of keys) {
5780
+ const value = row[key];
5781
+ if (value === void 0 || value === null) continue;
5782
+ if (typeof value === "object") {
5783
+ const nested = asRecord(value);
5784
+ if (nested) {
5785
+ const inner = pickFirst(nested, keys) || pickFirst(nested, ["id", "name"]);
5786
+ if (inner) return inner;
5787
+ }
5788
+ continue;
5789
+ }
5790
+ const text = String(value).trim();
5791
+ if (text !== "") return text;
5792
+ }
5793
+ return "";
5794
+ }
5795
+ function unwrapRow(value) {
5796
+ const record = asRecord(value);
5797
+ if (!record) return null;
5798
+ const main = asRecord(record.mainTable);
5799
+ return main ?? record;
5800
+ }
5801
+ function collectCandidateLists(customData) {
5802
+ const found = [];
5803
+ const main = asRecord(customData.mainTable);
5804
+ if (main) {
5805
+ found.push({ source: "customData.mainTable", rows: [main] });
5806
+ for (const key of LIST_KEYS) {
5807
+ const value = main[key];
5808
+ if (Array.isArray(value)) found.push({ source: `customData.mainTable.${key}`, rows: value });
5809
+ }
5810
+ }
5811
+ for (const key of LIST_KEYS) {
5812
+ const value = customData[key];
5813
+ if (Array.isArray(value)) found.push({ source: `customData.${key}`, rows: value });
5814
+ }
5815
+ return found;
5816
+ }
5817
+ var CONTACT_LIST_KEYS = ["lxr", "contacts", "contactList", "lxrList"];
5818
+ var ADDRESS_LIST_KEYS = ["khdz", "addresses", "addressList", "dz"];
5819
+ var CONTACT_NAME_KEYS = ["field_1173221220888062706", "xm", "lxrxm", "contactName", "name"];
5820
+ var CONTACT_MOBILE_KEYS = ["field_1173221220888062708", "sjhm", "mobile", "phone", "tel", "lxrdh"];
5821
+ var CONTACT_DEPT_KEYS = ["field_1173221220888062707", "bm", "dept", "department", "bmmc"];
5822
+ var MOBILE_RE = /^[+\d][\d\s-]{6,}$/;
5823
+ var DEPT_RE = /(部|科|处|室|中心|事业部|分公司|集团|公司)$/;
5824
+ function pickRawList(row, keys) {
5825
+ for (const key of keys) {
5826
+ if (Array.isArray(row[key])) return row[key];
5827
+ }
5828
+ return void 0;
5829
+ }
5830
+ function parseCrmContacts(raw) {
5831
+ if (!Array.isArray(raw)) return [];
5832
+ const out = [];
5833
+ const seen = /* @__PURE__ */ new Set();
5834
+ for (const item of raw) {
5835
+ const row = asRecord(item);
5836
+ if (!row) continue;
5837
+ const entries = Object.entries(row).filter(([key, value]) => !key.startsWith("subForm") && value !== null && value !== void 0 && String(value).trim() !== "");
5838
+ if (entries.length === 0) continue;
5839
+ const pick = (keys) => {
5840
+ for (const key of keys) {
5841
+ const hit = entries.find(([k]) => k === key);
5842
+ if (hit) return String(hit[1]).trim();
5843
+ }
5844
+ return "";
5845
+ };
5846
+ let name = pick(CONTACT_NAME_KEYS);
5847
+ let mobile = pick(CONTACT_MOBILE_KEYS);
5848
+ let dept = pick(CONTACT_DEPT_KEYS);
5849
+ for (const [, value] of entries) {
5850
+ const text = String(value).trim();
5851
+ if (text === "") continue;
5852
+ if (!mobile && MOBILE_RE.test(text)) {
5853
+ mobile = text;
5854
+ continue;
5855
+ }
5856
+ if (!name && !DEPT_RE.test(text) && !/^\d+$/.test(text)) {
5857
+ name = text;
5858
+ continue;
5859
+ }
5860
+ if (!dept && DEPT_RE.test(text)) dept = text;
5861
+ }
5862
+ if (name === "" && mobile === "") continue;
5863
+ const dedupeKey = `${name}|${mobile}`;
5864
+ if (seen.has(dedupeKey)) continue;
5865
+ seen.add(dedupeKey);
5866
+ out.push({ name, mobile, ...dept === "" ? {} : { dept } });
5867
+ }
5868
+ return out;
5869
+ }
5870
+ function parseCrmAddresses(raw) {
5871
+ if (!Array.isArray(raw)) return [];
5872
+ const out = [];
5873
+ for (const item of raw) {
5874
+ const row = asRecord(item);
5875
+ if (!row) continue;
5876
+ const parts = Object.entries(row).filter(([key, value]) => !key.startsWith("subForm") && value !== null && value !== void 0 && String(value).trim() !== "").map(([, value]) => String(value).trim());
5877
+ if (parts.length > 0) out.push(parts.join(" "));
5878
+ }
5879
+ return out;
5880
+ }
5881
+ function normalizeCustomers(rows, limit) {
5882
+ const seen = /* @__PURE__ */ new Map();
5883
+ for (const raw of rows) {
5884
+ const row = unwrapRow(raw);
5885
+ if (!row) continue;
5886
+ const customerId = pickFirst(row, ID_KEYS);
5887
+ const name = pickFirst(row, NAME_KEYS);
5888
+ if (customerId === "" && name === "") continue;
5889
+ const key = customerId !== "" ? customerId : `name:${name}`;
5890
+ if (seen.has(key)) continue;
5891
+ const contacts = parseCrmContacts(pickRawList(row, CONTACT_LIST_KEYS));
5892
+ const addresses = parseCrmAddresses(pickRawList(row, ADDRESS_LIST_KEYS));
5893
+ seen.set(key, {
5894
+ customerId,
5895
+ name,
5896
+ ...contacts.length > 0 ? { contacts } : {},
5897
+ ...addresses.length > 0 ? { addresses } : {}
5898
+ });
5899
+ }
5900
+ const all = [...seen.values()];
5901
+ const total = all.length;
5902
+ return { items: limit > 0 ? all.slice(0, limit) : all, total };
5903
+ }
5904
+ function parseCrmSearchPayload(actionData, options) {
5905
+ const action = asRecord(actionData) ?? {};
5906
+ const responseData = asRecord(action.responseData) ?? {};
5907
+ const customData = asRecord(responseData.customData) ?? {};
5908
+ const rawHasData = action.hasData;
5909
+ const hasData = rawHasData === void 0 || rawHasData === null ? null : Number(rawHasData) > 0;
5910
+ const templateFields = options.templateFields ?? [];
5911
+ const base = {
5912
+ keyword: options.keyword,
5913
+ limit: options.limit,
5914
+ total: 0,
5915
+ items: [],
5916
+ truncated: false,
5917
+ source: "customData",
5918
+ hasData,
5919
+ exact: false,
5920
+ singleBestMatch: false,
5921
+ message: "",
5922
+ templateFields,
5923
+ keywordField: options.keywordField ?? null
5924
+ };
5925
+ if (options.templateBlocked === true) {
5926
+ return {
5927
+ ...base,
5928
+ blocked: "esb_template_empty",
5929
+ message: "\u52A8\u4F5C\u6D41\u914D\u4E86\u300C\u81EA\u5B9A\u4E49\u5165\u53C2\u6A21\u677F\u300D\u4F46\u4E3B\u8868\u5B57\u6BB5\u4E3A\u7A7A\uFF08customParams.mainTable = {}\uFF09\uFF0C\u89E6\u53D1\u65F6\u4F20\u5165\u7684\u67E5\u8BE2\u6761\u4EF6\u4F1A\u88AB\u4E22\u5F03\uFF0C\u56E0\u6B64\u67E5\u4E0D\u5230\u5BA2\u6237\u3002\u9700\u8981\u5148\u5728 ESB \u8BBE\u8BA1\u5668\u91CC\u7ED9\u8BE5\u52A8\u4F5C\u6D41\u8865\u5165\u53C2\uFF08`custom_name` \u5BA2\u6237\u540D\u79F0\uFF09\u540E\u518D\u67E5\u3002"
5930
+ };
5931
+ }
5932
+ const lists = collectCandidateLists(customData);
5933
+ const best = lists.map((item) => ({ ...item, normalized: normalizeCustomers(item.rows, options.limit) })).sort((a, b) => b.normalized.total - a.normalized.total)[0];
5934
+ if (!best) {
5935
+ return {
5936
+ ...base,
5937
+ message: "\u52A8\u4F5C\u6D41\u8FD4\u56DE\u91CC\u6CA1\u6709\u53EF\u8BC6\u522B\u7684\u5BA2\u6237\u7ED3\u6784\uFF08\u65E2\u6CA1\u6709 mainTable \u884C\uFF0C\u4E5F\u6CA1\u6709 detail/list \u4E4B\u7C7B\u7684\u5217\u8868\uFF09\uFF0C\u8BF7\u68C0\u67E5\u52A8\u4F5C\u6D41\u7684\u8F93\u51FA\u6A21\u677F\u662F\u5426\u4ECD\u8F93\u51FA sjID/mc"
5938
+ };
5939
+ }
5940
+ if (best.normalized.total === 0) {
5941
+ return {
5942
+ ...base,
5943
+ source: best.source,
5944
+ message: `\u6309\u300C${options.keyword}\u300D\u6CA1\u6709\u5339\u914D\u5230\u5BA2\u6237\uFF08\u52A8\u4F5C\u6D41\u8FD4\u56DE\u7A7A\u884C\uFF09`
5945
+ };
5946
+ }
5947
+ const items = best.normalized.items;
5948
+ const singleBestMatch = best.normalized.total === 1;
5949
+ const exact = singleBestMatch && items[0].name === options.keyword;
5950
+ const rawAddress = pickRawList(best.rows[0], ADDRESS_LIST_KEYS);
5951
+ const addressHint = Array.isArray(rawAddress) && rawAddress.length > 0 && (items[0].addresses?.length ?? 0) === 0 ? `\u52A8\u4F5C\u6D41\u7684\u300C\u5BA2\u6237\u5730\u5740\u300D\uFF08khdz\uFF09\u53EA\u56DE\u4E86 ${rawAddress.length} \u6761\u660E\u7EC6\u884C id\uFF0C\u6CA1\u6709\u5730\u5740\u6587\u672C \u2014\u2014 \u9700\u8981\u5728 ESB \u8F93\u51FA\u6A21\u677F\u91CC\u8865\u4E0A\u5730\u5740\u5B57\u6BB5\uFF08\u5982 \u8BE6\u7EC6\u5730\u5740/\u7701\u5E02\u533A\uFF09\uFF0C\u5426\u5219\u9080\u8BF7\u51FD\u548C\u660E\u7EC6\u8868\u91CC\u62FF\u4E0D\u5230\u5730\u5740\u3002` : void 0;
5952
+ return {
5953
+ ...base,
5954
+ items,
5955
+ total: best.normalized.total,
5956
+ truncated: best.normalized.total > options.limit,
5957
+ source: best.source,
5958
+ exact,
5959
+ singleBestMatch,
5960
+ ...addressHint ? { addressHint } : {},
5961
+ message: exact ? `\u7CBE\u786E\u547D\u4E2D\u5BA2\u6237\u300C${items[0].name}\u300D\uFF08${items[0].customerId}\uFF09` : `\u6309\u300C${options.keyword}\u300D\u6A21\u7CCA\u547D\u4E2D ${best.normalized.total} \u4E2A\u5BA2\u6237` + (singleBestMatch ? "\uFF08\u52A8\u4F5C\u6D41\u53EA\u56DE\u9996\u4E2A\u5339\u914D\uFF09" : "") + (best.normalized.total > options.limit ? `\uFF0C\u5DF2\u6309 limit=${options.limit} \u622A\u65AD` : "")
5962
+ };
5963
+ }
5964
+ function pickKeywordField(templateMainTable) {
5965
+ const keys = Object.keys(templateMainTable ?? {});
5966
+ if (keys.length === 0) return null;
5967
+ for (const hint of KEYWORD_FIELD_HINTS) {
5968
+ const hit = keys.find((key) => key.toLowerCase() === hint.toLowerCase());
5969
+ if (hit) return hit;
5970
+ }
5971
+ return keys.length === 1 ? keys[0] : null;
5972
+ }
5973
+ function pickLimitField(templateMainTable) {
5974
+ const keys = Object.keys(templateMainTable ?? {});
5975
+ const hints = ["pageSize", "pagesize", "limit", "rows", "size", "num"];
5976
+ for (const hint of hints) {
5977
+ const hit = keys.find((key) => key.toLowerCase() === hint.toLowerCase());
5978
+ if (hit) return hit;
5979
+ }
5980
+ return null;
5981
+ }
5982
+ async function runCrmSearchFlow(env, input) {
5983
+ const session = await env.host.getSession();
5984
+ const employeeId = String(input.employeeId ?? session?.userId ?? "").trim();
5985
+ if (!employeeId) {
5986
+ throw meetingValidationError("crm_employee_required", "\u53D6\u4E0D\u5230\u5F53\u524D\u767B\u5F55\u7528\u6237 id\uFF0C\u65E0\u6CD5\u89E6\u53D1 ESB \u5BA2\u6237\u67E5\u8BE2");
5987
+ }
5988
+ const { createEsbHost } = await import("./host-FWDOSRH5.js");
5989
+ const { createEsbContinuationSigner } = await import("./continuation-NLECEJPX.js");
5990
+ const { getEsbOperation } = await import("./registry-FRJ6H7BT.js");
5991
+ const esbEnv = {
5992
+ ctx: env.ctx,
5993
+ host: createEsbHost(env.ctx),
5994
+ continuation: createEsbContinuationSigner(env.ctx)
5995
+ };
5996
+ const templateOp = getEsbOperation("esb.input-format");
5997
+ const prepareOp = getEsbOperation("esb.trigger.prepare");
5998
+ const applyOp = getEsbOperation("esb.trigger.apply");
5999
+ let templateMainTable = null;
6000
+ let customParamsTemplate = null;
6001
+ let templateReadOk = false;
6002
+ try {
6003
+ const template = await templateOp.handler({ applicationId: input.flowId }, esbEnv);
6004
+ customParamsTemplate = asRecord(template?.data?.customParams);
6005
+ templateMainTable = asRecord(customParamsTemplate?.mainTable);
6006
+ templateReadOk = true;
6007
+ } catch {
6008
+ }
6009
+ const templateFields = Object.keys(templateMainTable ?? {});
6010
+ const templateBlocked = templateReadOk && customParamsTemplate !== null && templateFields.length === 0;
6011
+ const keywordField = pickKeywordField(templateMainTable) ?? DEFAULT_KEYWORD_FIELD;
6012
+ const limitField = pickLimitField(templateMainTable);
6013
+ const mainTable = {};
6014
+ mainTable[keywordField] = input.keyword;
6015
+ if (limitField) mainTable[limitField] = input.limit;
6016
+ const pre = await prepareOp.handler(
6017
+ {
6018
+ esbFlowId: input.flowId,
6019
+ employeeId,
6020
+ customParams: { mainTable }
6021
+ },
6022
+ esbEnv
6023
+ );
6024
+ const applied = await applyOp.handler(
6025
+ { continuation: pre?.data?.continuation, confirm: true },
6026
+ esbEnv
6027
+ );
6028
+ const actionData = applied?.data?.actionData;
6029
+ const parsed = parseCrmSearchPayload(actionData, {
6030
+ keyword: input.keyword,
6031
+ limit: input.limit,
6032
+ templateFields,
6033
+ keywordField,
6034
+ templateBlocked
6035
+ });
6036
+ if (actionData && typeof actionData === "object") {
6037
+ const reminder = actionData.reminder;
6038
+ const reminderText = reminder === void 0 || reminder === null ? "" : String(reminder);
6039
+ if (reminderText !== "" && reminderText !== "0") parsed.actionMessage = reminderText;
6040
+ }
6041
+ return { result: parsed, raw: applied?.data };
6042
+ }
6043
+ var ebCrmSearch = {
6044
+ name: "meeting.eb.crm.search",
6045
+ resource: "eb",
6046
+ method: "meeting.eb.crm.search",
6047
+ action: "meeting.eb.crm.search",
6048
+ summary: "\u6309\u5BA2\u6237\u540D\u79F0\u67E5\u8BE2 CRM \u5BA2\u6237\uFF08ESB \u52A8\u4F5C\u6D41 904205116264026112\uFF0C\u5165\u53C2 custom_name\uFF0C\u51FA\u53C2 sjID/mc\uFF09\uFF1B\u52A8\u4F5C\u6D41\u4E00\u6B21\u53EA\u56DE\u9996\u4E2A\u5339\u914D\uFF0C\u540D\u79F0\u4E0D\u5B8C\u5168\u4E00\u81F4\u65F6\u987B\u56DE\u95EE\u7528\u6237\u786E\u8BA4",
6049
+ capability: "eb.crm",
6050
+ risk: "read",
6051
+ inputSchema: schemaOf(
6052
+ {
6053
+ keyword: {
6054
+ type: "string",
6055
+ minLength: 1,
6056
+ description: "\u5BA2\u6237\u540D\u79F0\uFF0C\u8D70\u52A8\u4F5C\u6D41\u5165\u53C2 custom_name\uFF08\u5305\u542B\u5339\u914D\uFF09\uFF1A\u7ED9\u5168\u79F0\u6700\u7A33\uFF0C\u7247\u6BB5\u53EA\u56DE\u9996\u4E2A\u5339\u914D"
6057
+ },
6058
+ limit: { type: "number", description: "\u8FD4\u56DE\u6761\u6570\u4E0A\u9650\uFF0C\u9ED8\u8BA4 10\uFF08\u5F53\u524D\u52A8\u4F5C\u6D41\u4E00\u6B21\u53EA\u56DE 1 \u6761\uFF0C\u4EC5\u4F5C\u517C\u5BB9\uFF09" },
6059
+ flowId: { type: "string", description: "\u8986\u76D6 ESB \u52A8\u4F5C\u6D41 id\uFF08\u9ED8\u8BA4 904205116264026112\uFF09" },
6060
+ employeeId: { type: "string", description: "\u8986\u76D6\u89E6\u53D1\u4EBA\u5458 id\uFF08\u9ED8\u8BA4\u5F53\u524D\u767B\u5F55\u7528\u6237\uFF09" },
6061
+ includeRaw: { type: "boolean", description: "true=\u9644\u5E26 ESB \u539F\u59CB\u8FD4\u56DE\uFF0C\u4FBF\u4E8E\u6392\u67E5\u8F93\u51FA\u7ED3\u6784" }
6062
+ },
6063
+ { required: ["keyword"] }
6064
+ ),
6065
+ handler: async (args, env) => {
6066
+ const keyword = String(args.keyword ?? "").trim();
6067
+ if (!keyword) {
6068
+ throw meetingValidationError("crm_keyword_required", "keyword \u4E0D\u80FD\u4E3A\u7A7A\uFF08\u5BA2\u6237\u540D\u79F0\u5173\u952E\u5B57\uFF09");
6069
+ }
6070
+ const limitRaw = Number(args.limit ?? 10);
6071
+ const limit = Number.isFinite(limitRaw) && limitRaw > 0 ? Math.min(Math.floor(limitRaw), 100) : 10;
6072
+ const { result, raw } = await runCrmSearchFlow(env, {
6073
+ keyword,
6074
+ limit,
6075
+ flowId: String(args.flowId ?? "").trim() || DEFAULT_CRM_FLOW_ID,
6076
+ ...args.employeeId ? { employeeId: String(args.employeeId) } : {}
6077
+ });
6078
+ const warnings = [];
6079
+ if (result.blocked === "esb_template_empty") {
6080
+ warnings.push(result.message);
6081
+ warnings.push("\u26D4 \u4E0D\u8981\u56DE\u590D\u300C\u6CA1\u6709\u8FD9\u4E2A\u5BA2\u6237\u300D\uFF1A\u8FD9\u662F\u52A8\u4F5C\u6D41\u5165\u53C2\u6A21\u677F\u672A\u914D\u7F6E\uFF0C\u5C5E\u4E8E\u914D\u7F6E\u95EE\u9898\u3002");
6082
+ } else if (result.items.length === 0) {
6083
+ warnings.push(`\u6309\u300C${keyword}\u300D\u6CA1\u6709\u5339\u914D\u5230\u5BA2\u6237\uFF1B\u53EF\u6362\u66F4\u77ED\u7684\u5173\u952E\u5B57\uFF08\u53BB\u6389\u5730\u57DF/\u540E\u7F00\uFF09\uFF0C\u6216\u786E\u8BA4\u8BE5\u5BA2\u6237\u662F\u5426\u5DF2\u5728 CRM \u5EFA\u6863\uFF08\u8FD9\u662F\u300C\u67E5\u65E0\u300D\uFF0C\u53EF\u4EE5\u5982\u5B9E\u56DE\u590D\u7528\u6237\uFF09\u3002`);
6084
+ } else if (!result.exact) {
6085
+ warnings.push(`\u52A8\u4F5C\u6D41\u4E00\u6B21\u53EA\u56DE\u9996\u4E2A\u5339\u914D\uFF1A\u300C${result.items[0].name}\u300D\uFF08${result.items[0].customerId}\uFF09\uFF0C\u4E0E\u300C${keyword}\u300D\u4E0D\u5B8C\u5168\u4E00\u81F4\u3002\u8BF7\u628A**\u547D\u4E2D\u5168\u540D\u539F\u6837\u5FF5\u51FA\u6765\u95EE"\u662F\u8FD9\u5BB6\u5417"**\uFF0C\u7528\u6237\u8BA4\u4E86\u518D\u628A\u8BE5 id \u5199\u8FDB create.flow.prepare \u7684 crm_members\uFF0C\u4E0D\u8981\u731C\u3002\u26D4 \u4E0D\u8981\u95EE\u6210"\u586B\u8FDB\u53C2\u4F1A\u5BA2\u6237\u5B57\u6BB5\u8FD8\u662F\u53EA\u5199\u8FDB\u4F1A\u8BAE\u5185\u5BB9\u5907\u6CE8"\u8FD9\u79CD\u9009\u62E9\u9898 \u2014\u2014 \u5BA2\u6237\u4E00\u5F8B\u8981\u5173\u8054\u5BA2\u6237\u6863\u6848\uFF0C"\u53EA\u5199\u5907\u6CE8"\u662F\u660E\u4EE4\u7981\u6B62\u7684\u505A\u6CD5\u3002`);
6086
+ }
6087
+ if (result.items.some((item) => item.name === "")) {
6088
+ warnings.push("\u6709\u5BA2\u6237\u53EA\u8FD4\u56DE\u4E86 sjID \u6CA1\u8FD4\u56DE mc\uFF08\u540D\u79F0\uFF09\uFF0C\u8BF7\u8BA9\u52A8\u4F5C\u6D41\u8F93\u51FA mc \u5B57\u6BB5\u3002");
6089
+ }
6090
+ const firstCustomer = result.items[0];
6091
+ if (firstCustomer) {
6092
+ const contacts = firstCustomer.contacts ?? [];
6093
+ if (contacts.length > 0) {
6094
+ warnings.push(
6095
+ `\u8BE5\u5BA2\u6237\u6709 ${contacts.length} \u4F4D\u8054\u7CFB\u4EBA\uFF1A` + contacts.map((c) => `${c.name}${c.dept ? `\uFF08${c.dept}\uFF09` : ""} ${c.mobile || "\u65E0\u624B\u673A\u53F7"}`).join("\u3001") + '\u3002\u5EFA\u4F1A\u65F6\u4F20 `crm_members` \u5C31\u4F1A\u628A\u8FD9**\u5168\u90E8\u8054\u7CFB\u4EBA\u81EA\u52A8\u5199\u6210\u300C\u53C2\u4F1A\u5BA2\u6237\u660E\u7EC6\u300D\u884C**\uFF08\u4E00\u4EBA\u4E00\u884C\uFF09\uFF0C\u26D4 \u4E0D\u8981\u95EE\u7528\u6237"\u9009\u54EA\u4F4D"\u3001\u4E5F\u4E0D\u8981\u53EA\u5E26\u5176\u4E2D\u4E00\u4F4D\uFF1B\u53EA\u6709\u7528\u6237\u660E\u786E\u8BF4"\u53EA\u5199\u67D0\u67D0"\u65F6\u624D\u7528 crm_member_details:[{"xm":"<\u59D3\u540D>","sjhm":"<\u624B\u673A\u53F7>","zw":"<\u90E8\u95E8\u6216\u804C\u4F4D>"}] \u8986\u76D6\u3002'
6096
+ );
6097
+ } else {
6098
+ warnings.push(
6099
+ '\u8BE5\u5BA2\u6237**\u6CA1\u6709\u8054\u7CFB\u4EBA**\uFF08\u52A8\u4F5C\u6D41\u672A\u8F93\u51FA lxr\uFF0C\u6216\u8BE5\u5BA2\u6237\u6CA1\u5EFA\u6863\u8054\u7CFB\u4EBA\uFF09\u2014\u2014 \u8FD9\u662F\u552F\u4E00\u9700\u8981\u95EE\u8054\u7CFB\u4EBA\u7684\u573A\u666F\u3002\u53EF\u4EE5\u95EE\u7528\u6237\u300C\u8981\u4E0D\u8981\u8865\u4E00\u4E0B\u8054\u7CFB\u4EBA\u59D3\u540D\u548C\u624B\u673A\u53F7\uFF08\u65B9\u4FBF\u7684\u8BDD\u518D\u7ED9\u804C\u4F4D\uFF09\u300D\uFF0C\u518D\u4EE5 crm_member_details:[{"xm":"\u5F20\u4E09","sjhm":"13800000000"}] \u4F20\u7ED9 create.flow.prepare/quick\uFF1B\u7528\u6237\u8BF4"\u6CA1\u6709"\u5C31\u7559\u7A7A\uFF0C\u4E0D\u8981\u786C\u7F16\u3002'
6100
+ );
6101
+ }
6102
+ if (result.addressHint) warnings.push(result.addressHint);
6103
+ }
6104
+ return meetingSuccess(
6105
+ "meeting.eb.crm.search",
6106
+ { ...result, ...args.includeRaw === true ? { raw } : {} },
6107
+ warnings.length > 0 ? { warnings } : {}
6108
+ );
6109
+ }
6110
+ };
6111
+ var meetingEbCrmOperations = [ebCrmSearch];
6112
+
5552
6113
  // src/shortcuts/meeting/operations/eb/create-flow.ts
6114
+ import { existsSync as existsSync3 } from "node:fs";
6115
+ import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
6116
+ import { homedir } from "node:os";
6117
+ import { resolve as resolve5 } from "node:path";
5553
6118
  var FIELD_ALIASES = {
5554
6119
  mt_name: ["\u4F1A\u8BAE\u540D\u79F0", "\u4F1A\u8BAE\u4E3B\u9898", "\u4E3B\u9898"],
5555
6120
  begin_datetime: ["\u5F00\u59CB\u65F6\u95F4", "\u4F1A\u8BAE\u5F00\u59CB\u65F6\u95F4"],
@@ -5559,18 +6124,22 @@ var FIELD_ALIASES = {
5559
6124
  customize_address: ["\u81EA\u5B9A\u4E49\u4F1A\u8BAE\u5730\u70B9", "\u81EA\u5B9A\u4E49\u5730\u70B9"],
5560
6125
  hrm_members: ["\u53C2\u4F1A\u4EBA\u5458", "\u53C2\u4F1A\u4EBA"],
5561
6126
  total_member: ["\u53C2\u4F1A\u4EBA\u6570", "\u4EBA\u6570"],
6127
+ /** 参会客户(CustomerComponent 客户浏览按钮):值是客户 id 列表,不是客户名称文本。 */
6128
+ crm_members: ["\u53C2\u4F1A\u5BA2\u6237", "\u5BA2\u6237"],
6129
+ crm_member_count: ["\u5E94\u5230\u5BA2\u6237\u6570", "\u5BA2\u6237\u6570"],
5562
6130
  caller: ["\u53EC\u96C6\u4EBA", "\u53D1\u8D77\u4EBA"],
5563
6131
  contacter: ["\u8054\u7CFB\u4EBA"],
5564
6132
  mt_type: ["\u4F1A\u8BAE\u7C7B\u578B"],
5565
6133
  other_remark: ["\u5907\u6CE8", "\u5176\u4ED6\u8BF4\u660E"]
5566
6134
  };
6135
+ var PERSON_FIELD_LABELS = { caller: "\u53EC\u96C6\u4EBA", contacter: "\u8054\u7CFB\u4EBA" };
5567
6136
  function workflowEnv(env) {
5568
6137
  return env;
5569
6138
  }
5570
6139
  async function loadCreateForm(env, workflowId) {
5571
- const { createWorkflowHost } = await import("./host-VQ6JJXGQ.js");
6140
+ const { createWorkflowHost } = await import("./host-7RXEDMAH.js");
5572
6141
  const { createWorkflowContinuationSigner } = await import("./continuation-HZIZRZBX.js");
5573
- const { getWorkflowOperation } = await import("./registry-VOGKFFTR.js");
6142
+ const { getWorkflowOperation } = await import("./registry-SG2PGVCF.js");
5574
6143
  const wfEnv = {
5575
6144
  ...workflowEnv(env),
5576
6145
  host: createWorkflowHost(env.ctx),
@@ -5614,39 +6183,126 @@ async function resolveEmployeeNames(env, ids) {
5614
6183
  }
5615
6184
  return map;
5616
6185
  }
5617
- async function resolveRoomValue(env, input) {
6186
+ async function resolvePersonTokens(env, tokens) {
6187
+ const map = /* @__PURE__ */ new Map();
6188
+ const unique = [...new Set(tokens.map((token) => String(token).trim()).filter((token) => token !== ""))];
6189
+ if (unique.length === 0) return map;
6190
+ const idLike = unique.filter((token) => /^\d{8,}$/.test(token));
6191
+ const nameLike = unique.filter((token) => !/^\d{8,}$/.test(token));
6192
+ if (idLike.length > 0) {
6193
+ const names = await resolveEmployeeNames(env, idLike);
6194
+ for (const id of idLike) map.set(id, { id, name: names.get(id) ?? id });
6195
+ }
6196
+ if (nameLike.length > 0) {
6197
+ try {
6198
+ const { hrmEmployeeOperations } = await import("./employee-SAK2QYXX.js");
6199
+ const query = hrmEmployeeOperations.find((operation) => operation.name === "hrm.employee.query");
6200
+ for (const name of nameLike) {
6201
+ if (!query) break;
6202
+ const found = await query.handler({ keyword: name, pageSize: 10 }, env).catch(() => null);
6203
+ const rows = found?.data?.rows ?? [];
6204
+ const exact = rows.find((row) => String(row.name ?? "").trim() === name);
6205
+ const hit = exact ?? (rows.length === 1 ? rows[0] : void 0);
6206
+ const id = String(hit?.id ?? "").trim();
6207
+ if (id === "") continue;
6208
+ map.set(name, { id, name: String(hit?.name ?? name) });
6209
+ }
6210
+ } catch {
6211
+ }
6212
+ }
6213
+ return map;
6214
+ }
6215
+ async function resolveSelfPerson(env) {
6216
+ let session = null;
6217
+ try {
6218
+ session = await env.host?.getSession?.();
6219
+ } catch {
6220
+ return null;
6221
+ }
6222
+ const id = String(session?.userId ?? "").trim();
6223
+ if (id === "" || id === "unknown") return null;
6224
+ const names = await resolveEmployeeNames(env, [id]);
6225
+ const name = String(names.get(id) ?? "").trim();
6226
+ if (name === "") return null;
6227
+ return { id, name };
6228
+ }
6229
+ function applySelfPersonDefaults(args, self) {
6230
+ const filled = [];
6231
+ const missing = ["caller", "contacter"].filter((field) => String(args[field] ?? "").trim() === "");
6232
+ if (missing.length === 0) return { filled, warning: "" };
6233
+ if (!self) {
6234
+ return {
6235
+ filled,
6236
+ warning: missing.length === 2 ? "\u26A0\uFE0F \u6CA1\u80FD\u786E\u8BA4\u5F53\u524D\u767B\u5F55\u7528\u6237\u5BF9\u5E94\u7684\u5458\u5DE5 \u2014\u2014\u300C\u53EC\u96C6\u4EBA\u300D\u300C\u8054\u7CFB\u4EBA\u300D\u4F1A\u7559\u7A7A\uFF0C\u8BF7\u663E\u5F0F\u4F20 caller / contacter\uFF08\u5458\u5DE5 id \u6216\u59D3\u540D\uFF09\u3002" : ""
6237
+ };
6238
+ }
6239
+ for (const field of missing) {
6240
+ args[field] = self.id;
6241
+ filled.push({ field, id: self.id, name: self.name });
6242
+ }
6243
+ return { filled, warning: "" };
6244
+ }
6245
+ function parseRoomRef(input) {
6246
+ const text = String(input ?? "").trim();
6247
+ if (!text) return {};
6248
+ const withId = /^(.*?)[((]\s*(\d{4,})\s*[))]$/.exec(text);
6249
+ if (withId) {
6250
+ const name = withId[1].trim();
6251
+ const id = withId[2];
6252
+ return name ? { id, name } : { id };
6253
+ }
6254
+ if (/^\d+$/.test(text)) return { id: text };
6255
+ return { name: text };
6256
+ }
6257
+ async function resolveAddressRoom(env, input) {
5618
6258
  const text = String(input ?? "").trim();
5619
6259
  if (!text) return null;
5620
- const { loadRooms } = await import("./room-recommend-D4EUOOWY.js");
6260
+ const ref = parseRoomRef(text);
6261
+ const { loadRooms: loadRooms2 } = await import("./room-recommend-R3JVUUAC.js");
6262
+ let rooms = [];
5621
6263
  try {
5622
- const { rooms } = await loadRooms(env, {});
5623
- const byId = rooms.find((room) => room.id === text);
5624
- if (byId) return { id: byId.id, name: byId.name };
5625
- const byName = rooms.find((room) => room.name === text);
5626
- if (byName) return { id: byName.id, name: byName.name };
6264
+ rooms = (await loadRooms2(env, {})).rooms;
5627
6265
  } catch {
6266
+ return { id: ref.id ?? "", name: ref.name ?? text, status: "unknown", lookupFailed: true };
6267
+ }
6268
+ const hit = (ref.id ? rooms.find((room) => room.id === ref.id) : void 0) ?? (ref.name ? rooms.find((room) => room.name === ref.name) : void 0);
6269
+ if (hit) return { id: hit.id, name: hit.name, status: hit.status, statusText: hit.statusText };
6270
+ if (ref.id) return { id: ref.id, name: ref.name ?? "", status: "unknown" };
6271
+ return null;
6272
+ }
6273
+ async function resolveRoomValue(env, input) {
6274
+ const text = String(input ?? "").trim();
6275
+ if (!text) return null;
6276
+ const resolved = await resolveAddressRoom(env, text).catch(() => null);
6277
+ if (resolved?.id && !resolved.lookupFailed) {
6278
+ return { id: resolved.id, name: resolved.name || resolved.id };
5628
6279
  }
5629
- if (/^\d+$/.test(text)) {
6280
+ const ref = parseRoomRef(text);
6281
+ if (ref.id) {
5630
6282
  try {
5631
- const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-QKMI43Q6.js");
5632
- const detail = await getMeetingOperation2("meeting.eb.room.detail").handler({ id: text }, env);
6283
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
6284
+ const detail = await getMeetingOperation2("meeting.eb.room.detail").handler({ id: ref.id }, env);
5633
6285
  const main = detail?.data?.mainTable ?? detail?.data?.data?.mainTable ?? {};
5634
- const name = String(main.room_name ?? "").trim();
5635
- if (name) return { id: text, name };
6286
+ const name = String(main.room_name ?? "").trim() || ref.name || "";
6287
+ if (name) return { id: ref.id, name };
5636
6288
  } catch {
5637
6289
  }
5638
- return { id: text, name: text };
6290
+ return { id: ref.id, name: ref.name || ref.id };
5639
6291
  }
5640
6292
  return null;
5641
6293
  }
5642
6294
  async function shapeBrowserValues(env, values) {
5643
6295
  const notes = [];
5644
6296
  const shaped = { ...values };
6297
+ const readable = {};
5645
6298
  if (shaped.address !== void 0 && shaped.address !== "") {
5646
6299
  const room = await resolveRoomValue(env, String(shaped.address));
5647
6300
  if (room) {
5648
6301
  shaped.address = room;
6302
+ readable.address = room.name;
5649
6303
  notes.push(`\u4F1A\u8BAE\u5730\u70B9\u6309\u6D4F\u89C8\u6309\u94AE\u683C\u5F0F\u63D0\u4EA4\uFF1A${room.name}(${room.id})`);
6304
+ } else {
6305
+ readable.address = String(shaped.address);
5650
6306
  }
5651
6307
  }
5652
6308
  const personFields = ["hrm_members", "caller", "contacter"];
@@ -5658,42 +6314,476 @@ async function shapeBrowserValues(env, values) {
5658
6314
  idPool.push(...ids);
5659
6315
  }
5660
6316
  if (idPool.length > 0) {
5661
- const names = await resolveEmployeeNames(env, idPool);
5662
- const toObject = (id) => ({ id, name: names.get(id) ?? id });
6317
+ const people = await resolvePersonTokens(env, idPool);
6318
+ const toObject = (token) => people.get(token) ?? { id: token, name: token };
6319
+ const toName = (token) => people.get(token)?.name ?? token;
5663
6320
  if (shaped.hrm_members !== void 0 && shaped.hrm_members !== "") {
5664
6321
  const ids = String(shaped.hrm_members).split(/[,;,;\s]+/).map((item) => item.trim()).filter((item) => item !== "");
5665
6322
  shaped.hrm_members = ids.map(toObject);
6323
+ readable.hrm_members = ids.map(toName).join("\u3001");
5666
6324
  notes.push(`\u53C2\u4F1A\u4EBA\u5458\u6309\u6D4F\u89C8\u6309\u94AE\u683C\u5F0F\u63D0\u4EA4\uFF1A${ids.length} \u4EBA`);
5667
6325
  }
5668
6326
  if (shaped.caller !== void 0 && shaped.caller !== "") {
5669
6327
  const id = String(shaped.caller).trim();
5670
6328
  shaped.caller = toObject(id);
6329
+ readable.caller = toName(id);
5671
6330
  }
5672
6331
  if (shaped.contacter !== void 0 && shaped.contacter !== "") {
5673
6332
  const id = String(shaped.contacter).trim();
5674
6333
  shaped.contacter = toObject(id);
6334
+ readable.contacter = toName(id);
6335
+ }
6336
+ const unresolved = [...new Set(idPool)].filter((token) => !people.has(token));
6337
+ if (unresolved.length > 0) {
6338
+ notes.push(
6339
+ `\u26A0\uFE0F \u6709 ${unresolved.length} \u4E2A\u4EBA\u5458\u6CA1\u89E3\u6790\u5230\u5458\u5DE5\uFF1A${unresolved.join("\u3001")} \u2014\u2014 \u8868\u5355\u91CC\u7684\u300C\u53C2\u4F1A\u4EBA\u5458/\u53EC\u96C6\u4EBA/\u8054\u7CFB\u4EBA\u300D\u53EF\u80FD\u663E\u793A\u4E0D\u51FA\u6765\u3002\u8BF7\u6539\u4F20\u5458\u5DE5 id\uFF0C\u6216\u786E\u8BA4\u59D3\u540D\u4E0E\u82B1\u540D\u518C\u5B8C\u5168\u4E00\u81F4\u3002`
6340
+ );
6341
+ }
6342
+ }
6343
+ if (shaped.crm_members !== void 0 && shaped.crm_members !== "") {
6344
+ const rows = [];
6345
+ const push = (idRaw, nameRaw) => {
6346
+ const id = String(idRaw ?? "").trim();
6347
+ const name = String(nameRaw ?? "").trim();
6348
+ if (id === "" && name === "") return;
6349
+ rows.push({ id, name });
6350
+ };
6351
+ const pushToken = (tokenRaw) => {
6352
+ const token = String(tokenRaw ?? "").trim();
6353
+ if (token === "") return;
6354
+ const matched = token.match(/^(.*?)[((]\s*([0-9A-Za-z_-]+)\s*[))]$/);
6355
+ if (matched) {
6356
+ push(matched[2], matched[1]);
6357
+ return;
6358
+ }
6359
+ if (/^\d{8,}$/.test(token)) {
6360
+ push(token, "");
6361
+ return;
6362
+ }
6363
+ push("", token);
6364
+ };
6365
+ const raw = shaped.crm_members;
6366
+ if (Array.isArray(raw)) {
6367
+ for (const item of raw) {
6368
+ if (item && typeof item === "object") {
6369
+ const obj = item;
6370
+ push(obj.id ?? obj.sjID ?? obj.customerId, obj.name ?? obj.mc);
6371
+ } else {
6372
+ for (const token of String(item).split(/[,;,;]+/).map((one) => one.trim()).filter((one) => one !== "")) {
6373
+ pushToken(token);
6374
+ }
6375
+ }
6376
+ }
6377
+ } else if (typeof raw === "object") {
6378
+ const obj = raw;
6379
+ push(obj.id ?? obj.sjID ?? obj.customerId, obj.name ?? obj.mc);
6380
+ } else {
6381
+ for (const token of String(raw).split(/[,;,;]+/).map((item) => item.trim()).filter((item) => item !== "")) {
6382
+ pushToken(token);
6383
+ }
6384
+ }
6385
+ if (rows.length > 0) {
6386
+ const nameOnly = rows.filter((row) => row.id === "" && row.name !== "");
6387
+ if (nameOnly.length > 0) {
6388
+ try {
6389
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
6390
+ const search = getMeetingOperation2("meeting.eb.crm.search");
6391
+ for (const row of nameOnly) {
6392
+ const found = await search.handler({ keyword: row.name, limit: 5 }, env).catch(() => null);
6393
+ const items = found?.data?.items ?? [];
6394
+ const hit = items.find((item) => String(item?.name ?? "").trim() === row.name) ?? items[0];
6395
+ const id = String(hit?.customerId ?? "").trim();
6396
+ if (id === "") continue;
6397
+ row.id = id;
6398
+ const fullName = String(hit?.name ?? "").trim();
6399
+ if (fullName !== "") row.name = fullName;
6400
+ }
6401
+ } catch {
6402
+ }
6403
+ }
6404
+ const unresolved = rows.filter((row) => row.id === "");
6405
+ if (unresolved.length > 0) {
6406
+ throw meetingValidationError(
6407
+ "crm_not_found",
6408
+ `\u53C2\u4F1A\u5BA2\u6237\u6709 ${unresolved.length} \u4E2A\u5728 CRM \u91CC\u67E5\u4E0D\u5230\uFF1A${unresolved.map((row) => row.name).join("\u3001")}\u3002\u8BF7\u786E\u8BA4\u5BA2\u6237\u540D\u79F0\uFF0C\u6216\u5148\u5230\u5BA2\u6237\u8868\u5EFA\u6863\uFF1B\u4E5F\u53EF\u4EE5\u6539\u7528\u300C\u5BA2\u6237\u540D(\u5BA2\u6237id)\u300D\u6216 \`[{id,name}]\` \u4F20 crm_members\u3002`
6409
+ );
6410
+ }
6411
+ shaped.crm_members = rows;
6412
+ readable.crm_members = rows.map((row) => row.name || row.id).join("\u3001");
6413
+ notes.push(`\u53C2\u4F1A\u5BA2\u6237\u6309\u5BA2\u6237\u6D4F\u89C8\u6309\u94AE\u683C\u5F0F\u63D0\u4EA4\uFF1A${rows.length} \u4E2A\u5BA2\u6237`);
6414
+ const missingNames = rows.filter((row) => row.name === "");
6415
+ if (missingNames.length > 0) {
6416
+ throw meetingValidationError(
6417
+ "crm_name_required",
6418
+ `\u53C2\u4F1A\u5BA2\u6237\u6709 ${missingNames.length} \u4E2A\u53EA\u7ED9\u4E86 id\u3001\u6CA1\u6709\u5BA2\u6237\u540D\u79F0\uFF1A${missingNames.map((row) => row.id).join("\u3001")}\u3002\u5BA2\u6237\u6D4F\u89C8\u6309\u94AE\u5FC5\u987B\u5E26\u540D\u79F0\u624D\u80FD\u5199\u5165\uFF08\u53EA\u7ED9 id \u4F1A\u88AB\u670D\u52A1\u7AEF\u9759\u9ED8\u5B58\u6210\u7A7A\u503C\uFF09\u3002\u8BF7\u5148\u7528 \`meeting.eb.crm.search\` \u6309\u5BA2\u6237\u540D\u67E5\u5230 \`{ customerId, name }\`\uFF0C\u518D\u4EE5 \`[{id,name}]\` \u6216\u300C\u5BA2\u6237\u540D(\u5BA2\u6237id)\u300D\u7684\u5F62\u5F0F\u4F20 \`crm_members\`\u3002`
6419
+ );
6420
+ }
6421
+ } else {
6422
+ delete shaped.crm_members;
6423
+ }
6424
+ }
6425
+ for (const [key, raw] of Object.entries(values)) {
6426
+ if (readable[key] !== void 0) continue;
6427
+ if (raw === void 0 || raw === null || raw === "") continue;
6428
+ if (typeof raw === "object") continue;
6429
+ readable[key] = String(raw);
6430
+ }
6431
+ return { values: shaped, notes, readable };
6432
+ }
6433
+ function normalizeFieldName(name) {
6434
+ return String(name ?? "").replace(/[((][^(())]*[))]/g, "").replace(/[\s\u3000]+/g, "").trim();
6435
+ }
6436
+ var CRM_DETAIL_COLUMNS = {
6437
+ xm: ["\u59D3\u540D", "\u5BA2\u6237\u59D3\u540D", "\u53C2\u4F1A\u4EBA", "\u53C2\u4F1A\u4EBA\u59D3\u540D"],
6438
+ xb: ["\u6027\u522B"],
6439
+ zw: ["\u804C\u4F4D", "\u804C\u52A1"],
6440
+ sjhm: ["\u624B\u673A\u53F7\u7801", "\u624B\u673A\u53F7", "\u624B\u673A", "\u7535\u8BDD", "\u8054\u7CFB\u7535\u8BDD"],
6441
+ bz: ["\u5907\u6CE8", "\u8BF4\u660E"]
6442
+ };
6443
+ function resolveCrmDetailTarget(fields) {
6444
+ const groups = /* @__PURE__ */ new Map();
6445
+ for (const field of fields) {
6446
+ const sid = String(field.subFormId ?? "").trim();
6447
+ if (!sid) continue;
6448
+ const list = groups.get(sid);
6449
+ if (list) list.push(field);
6450
+ else groups.set(sid, [field]);
6451
+ }
6452
+ let best = null;
6453
+ for (const [subFormId, list] of groups) {
6454
+ const columns = {};
6455
+ const labels = {};
6456
+ for (const [semantic, aliases] of Object.entries(CRM_DETAIL_COLUMNS)) {
6457
+ const hit = list.find((field) => aliases.includes(normalizeFieldName(field.name)));
6458
+ if (!hit) continue;
6459
+ columns[semantic] = hit.id;
6460
+ labels[semantic] = hit.name;
6461
+ }
6462
+ if (!columns.xm) continue;
6463
+ const score = Object.keys(columns).length + (columns.sjhm ? 1 : 0) + (columns.zw ? 1 : 0);
6464
+ if (!best || score > best.score) best = { subFormId, columns, labels, score };
6465
+ }
6466
+ return best ? { subFormId: best.subFormId, columns: best.columns, labels: best.labels } : null;
6467
+ }
6468
+ function detectCrmMentionInText(...texts) {
6469
+ for (const text of texts) {
6470
+ if (typeof text !== "string") continue;
6471
+ const value = text.trim();
6472
+ if (value === "") continue;
6473
+ const hit = /(?:参会|拜访|来访)?客户\s*(?:名称)?\s*(?:是|为|[::])\s*([^\s,,。;;、))]{2,40})/.exec(value);
6474
+ if (hit?.[1]) return hit[1].trim();
6475
+ }
6476
+ return null;
6477
+ }
6478
+ function normalizeCrmDetailRows(raw) {
6479
+ if (raw === void 0 || raw === null || raw === "") return [];
6480
+ const list = Array.isArray(raw) ? raw : [raw];
6481
+ const keyAlias = {
6482
+ xm: "xm",
6483
+ name: "xm",
6484
+ customername: "xm",
6485
+ \u59D3\u540D: "xm",
6486
+ \u5BA2\u6237\u59D3\u540D: "xm",
6487
+ xb: "xb",
6488
+ gender: "xb",
6489
+ sex: "xb",
6490
+ \u6027\u522B: "xb",
6491
+ zw: "zw",
6492
+ title: "zw",
6493
+ position: "zw",
6494
+ job: "zw",
6495
+ \u804C\u4F4D: "zw",
6496
+ \u804C\u52A1: "zw",
6497
+ sjhm: "sjhm",
6498
+ mobile: "sjhm",
6499
+ phone: "sjhm",
6500
+ tel: "sjhm",
6501
+ \u624B\u673A\u53F7\u7801: "sjhm",
6502
+ \u624B\u673A\u53F7: "sjhm",
6503
+ \u7535\u8BDD: "sjhm",
6504
+ \u8054\u7CFB\u7535\u8BDD: "sjhm",
6505
+ bz: "bz",
6506
+ remark: "bz",
6507
+ note: "bz",
6508
+ \u5907\u6CE8: "bz",
6509
+ \u8BF4\u660E: "bz"
6510
+ };
6511
+ const rows = [];
6512
+ for (const item of list) {
6513
+ if (item === void 0 || item === null || typeof item !== "object") continue;
6514
+ const row = {};
6515
+ for (const [key, value] of Object.entries(item)) {
6516
+ const semantic = keyAlias[String(key).trim().toLowerCase()] ?? keyAlias[String(key).trim()];
6517
+ if (!semantic) continue;
6518
+ if (value === void 0 || value === null) continue;
6519
+ const text = String(value).trim();
6520
+ if (text === "") continue;
6521
+ row[semantic] = text;
6522
+ }
6523
+ if (!row.xm && !row.sjhm) continue;
6524
+ rows.push(row);
6525
+ }
6526
+ return rows;
6527
+ }
6528
+ function crmMemberEntriesFromArgs(raw) {
6529
+ const list = Array.isArray(raw) ? raw : raw === void 0 || raw === null || raw === "" ? [] : [raw];
6530
+ const out = [];
6531
+ const push = (idRaw, nameRaw, contactsRaw) => {
6532
+ const id = String(idRaw ?? "").trim();
6533
+ const name = String(nameRaw ?? "").trim();
6534
+ if (id === "" && name === "") return;
6535
+ const contacts = (Array.isArray(contactsRaw) ? contactsRaw : []).map((item) => {
6536
+ const row = item ?? {};
6537
+ return {
6538
+ name: String(row.name ?? row.xm ?? "").trim(),
6539
+ mobile: String(row.mobile ?? row.sjhm ?? row.phone ?? "").trim(),
6540
+ ...String(row.dept ?? row.bm ?? "").trim() === "" ? {} : { dept: String(row.dept ?? row.bm).trim() }
6541
+ };
6542
+ }).filter((one) => one.name !== "" || one.mobile !== "");
6543
+ out.push({ id: id === "" ? name : id, name, contacts });
6544
+ };
6545
+ for (const item of list) {
6546
+ if (item && typeof item === "object") {
6547
+ const obj = item;
6548
+ push(obj.customerId ?? obj.sjID ?? obj.id, obj.name ?? obj.mc, obj.contacts ?? obj.lxr);
6549
+ } else {
6550
+ const token = String(item).trim();
6551
+ if (token === "") continue;
6552
+ const matched = token.match(/^(.*?)[((]\s*([0-9A-Za-z_-]+)\s*[))]$/);
6553
+ if (matched) push(matched[2], matched[1], void 0);
6554
+ else push(token, "", void 0);
6555
+ }
6556
+ }
6557
+ return out;
6558
+ }
6559
+ function crmContactsToDetailRows(entries) {
6560
+ const rows = [];
6561
+ const seen = /* @__PURE__ */ new Set();
6562
+ for (const entry of entries) {
6563
+ for (const contact of entry.contacts) {
6564
+ const xm = contact.name.trim();
6565
+ const sjhm = contact.mobile.trim();
6566
+ if (xm === "" && sjhm === "") continue;
6567
+ const key = `${xm}|${sjhm}`;
6568
+ if (seen.has(key)) continue;
6569
+ seen.add(key);
6570
+ const row = { xm: xm === "" ? sjhm : xm };
6571
+ if (sjhm !== "") row.sjhm = sjhm;
6572
+ if (contact.dept && contact.dept.trim() !== "") row.bz = `\u90E8\u95E8\uFF1A${contact.dept.trim()}`;
6573
+ rows.push(row);
6574
+ }
6575
+ }
6576
+ return rows;
6577
+ }
6578
+ async function resolveAutoCrmDetailRows(env, raw) {
6579
+ const entries = crmMemberEntriesFromArgs(raw);
6580
+ const missing = [];
6581
+ if (entries.length > 0) {
6582
+ try {
6583
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
6584
+ const search = getMeetingOperation2("meeting.eb.crm.search");
6585
+ for (const entry of entries) {
6586
+ const keyword = (entry.name || entry.id).trim();
6587
+ if (keyword === "") {
6588
+ missing.push(entry.id);
6589
+ continue;
6590
+ }
6591
+ const searchOnce = async (kw) => {
6592
+ const found = await search.handler({ keyword: kw, limit: 5 }, env).catch(() => null);
6593
+ const items = found?.data?.items ?? [];
6594
+ const hit = items.find((item) => String(item?.customerId ?? "") === entry.id) ?? items[0];
6595
+ return hit?.contacts ?? [];
6596
+ };
6597
+ let live = await searchOnce(keyword);
6598
+ if (live.length === 0 && keyword.includes("--")) {
6599
+ const short = keyword.split("--")[0].trim();
6600
+ if (short !== "") live = await searchOnce(short);
6601
+ }
6602
+ if (live.length === 0) {
6603
+ if (entry.contacts.length === 0) missing.push(entry.name || entry.id);
6604
+ continue;
6605
+ }
6606
+ const keyOf = (one) => `${String(one.name ?? "").trim()}|${String(one.mobile ?? "").trim()}`;
6607
+ const merged = /* @__PURE__ */ new Map();
6608
+ for (const one of live) merged.set(keyOf(one), one);
6609
+ for (const one of entry.contacts) {
6610
+ const key = keyOf(one);
6611
+ const base = merged.get(key);
6612
+ merged.set(key, base ? { ...base, ...one } : one);
6613
+ }
6614
+ entry.contacts = [...merged.values()];
6615
+ }
6616
+ } catch {
5675
6617
  }
5676
6618
  }
5677
- return { values: shaped, notes };
6619
+ return {
6620
+ rows: crmContactsToDetailRows(entries),
6621
+ customers: entries.map((entry) => entry.name || entry.id).filter((one) => one !== ""),
6622
+ missing
6623
+ };
5678
6624
  }
5679
- function mapFields(fields, values) {
6625
+ function mapFields(fields, values, preferred = {}) {
5680
6626
  const byName = new Map(fields.map((field) => [field.name.trim(), field]));
6627
+ const byId = new Map(fields.map((field) => [field.id, field]));
6628
+ const byNormalized = /* @__PURE__ */ new Map();
6629
+ for (const field of fields) {
6630
+ const key = normalizeFieldName(field.name);
6631
+ if (key && !byNormalized.has(key)) byNormalized.set(key, field);
6632
+ }
5681
6633
  const main = {};
5682
6634
  const mapped = {};
5683
6635
  const unmapped = [];
6636
+ const resolved = {};
6637
+ const findField = (aliases) => {
6638
+ for (const name of aliases) {
6639
+ const field = byName.get(name);
6640
+ if (field) return field;
6641
+ }
6642
+ for (const name of aliases) {
6643
+ const field = byNormalized.get(normalizeFieldName(name));
6644
+ if (field) return field;
6645
+ }
6646
+ for (const name of aliases) {
6647
+ const needle = normalizeFieldName(name);
6648
+ if (needle.length < 2) continue;
6649
+ for (const [key, field] of byNormalized) {
6650
+ if (key.includes(needle)) return field;
6651
+ }
6652
+ }
6653
+ return void 0;
6654
+ };
5684
6655
  for (const [key, raw] of Object.entries(values)) {
5685
6656
  if (raw === void 0 || raw === null || raw === "") continue;
5686
6657
  const aliases = FIELD_ALIASES[key] ?? [key];
5687
- const hit = aliases.map((name) => byName.get(name)).find((field) => field !== void 0);
6658
+ const prefId = preferred[key];
6659
+ const hit = (prefId ? byId.get(prefId) : void 0) ?? findField(aliases);
5688
6660
  if (!hit) {
5689
6661
  unmapped.push(key);
5690
6662
  continue;
5691
6663
  }
5692
6664
  main[hit.id] = raw;
5693
6665
  mapped[key] = `${hit.name}(${hit.id})`;
5694
- }
5695
- return { main, mapped, unmapped };
5696
- }
6666
+ resolved[key] = hit.id;
6667
+ }
6668
+ return { main, mapped, unmapped, resolved };
6669
+ }
6670
+ var PREVIEW_PERSON_FIELD_RE = /^(召集人|联系人|参会人员|会议创建人|主讲人|负责人|费用承担人)$/;
6671
+ async function decoratePreviewPersonNames(env, main, fields, known = /* @__PURE__ */ new Map()) {
6672
+ const targets = fields.filter((field) => PREVIEW_PERSON_FIELD_RE.test(String(field?.name ?? "").trim()));
6673
+ if (targets.length === 0) return main;
6674
+ const ID_ONLY_RE = /^\d{8,}$/;
6675
+ const missingNameId = (value) => {
6676
+ if (typeof value === "string") return ID_ONLY_RE.test(value.trim()) ? value.trim() : null;
6677
+ if (value && typeof value === "object" && !Array.isArray(value)) {
6678
+ const obj = value;
6679
+ const id = String(obj.id ?? "").trim();
6680
+ const name = String(obj.name ?? "").trim();
6681
+ if (id !== "" && (name === "" || name === id)) return id;
6682
+ }
6683
+ return null;
6684
+ };
6685
+ const ids = /* @__PURE__ */ new Set();
6686
+ for (const field of targets) {
6687
+ const value = main[field.id];
6688
+ const one = missingNameId(value);
6689
+ if (one) ids.add(one);
6690
+ else if (Array.isArray(value)) for (const item of value) {
6691
+ const hit = missingNameId(item);
6692
+ if (hit) ids.add(hit);
6693
+ }
6694
+ }
6695
+ if (ids.size === 0) return main;
6696
+ let names = new Map(known);
6697
+ const unknown = [...ids].filter((id) => !names.has(id));
6698
+ if (unknown.length > 0) {
6699
+ try {
6700
+ const resolved = await resolveEmployeeNames(env, unknown);
6701
+ for (const [id, name] of resolved) names.set(id, name);
6702
+ } catch {
6703
+ }
6704
+ }
6705
+ if (names.size === 0) return main;
6706
+ const rename = (value) => {
6707
+ const one = missingNameId(value);
6708
+ if (one) return names.get(one) ?? value;
6709
+ if (value && typeof value === "object" && !Array.isArray(value)) {
6710
+ const obj = value;
6711
+ const id = String(obj.id ?? "").trim();
6712
+ const name = names.get(id);
6713
+ if (name) return { ...obj, name };
6714
+ }
6715
+ return value;
6716
+ };
6717
+ const decorated = { ...main };
6718
+ for (const field of targets) {
6719
+ const value = decorated[field.id];
6720
+ decorated[field.id] = Array.isArray(value) ? value.map(rename) : rename(value);
6721
+ }
6722
+ return decorated;
6723
+ }
6724
+ function renderFormPreview(fields, main) {
6725
+ const fmt = (value) => {
6726
+ if (value === void 0 || value === null) return "\u2014";
6727
+ if (Array.isArray(value)) {
6728
+ const parts = value.map((one) => fmt(one)).filter((one) => one !== "\u2014");
6729
+ return parts.length > 0 ? parts.join("\u3001") : "\u2014";
6730
+ }
6731
+ if (typeof value === "object") {
6732
+ const obj = value;
6733
+ const name = String(obj.name ?? obj.mc ?? "").trim();
6734
+ if (name !== "") return name.replace(/\|/gu, "/");
6735
+ const id = String(obj.id ?? obj.customerId ?? obj.sjID ?? "").trim();
6736
+ return id === "" ? "\u2014" : id;
6737
+ }
6738
+ const text = String(value).trim();
6739
+ return text === "" ? "\u2014" : text.replace(/\|/gu, "/").replace(/\r?\n/gu, " ");
6740
+ };
6741
+ const lines = ["| \u5B57\u6BB5 | \u503C |", "| --- | --- |"];
6742
+ const known = /* @__PURE__ */ new Set();
6743
+ for (const field of fields) {
6744
+ known.add(field.id);
6745
+ if (!(field.id in main)) continue;
6746
+ lines.push(`| ${String(field.name).replace(/\|/gu, "/")} | ${fmt(main[field.id])} |`);
6747
+ }
6748
+ for (const [id, value] of Object.entries(main)) {
6749
+ if (known.has(id)) continue;
6750
+ lines.push(`| ${id} | ${fmt(value)} |`);
6751
+ }
6752
+ return lines.join("\n");
6753
+ }
6754
+ function flowFieldCacheDir(env) {
6755
+ const configured = env?.ctx?.config?.path?.();
6756
+ const root = configured ? resolve5(String(configured), "..") : process.env.WEAVER_WORK_CLI_HOME || process.env.WORK_CLI_HOME || resolve5(homedir(), ".weaver-work-cli");
6757
+ return resolve5(root, "meeting", "cache");
6758
+ }
6759
+ function flowFieldCacheFile(env, workflowId) {
6760
+ return resolve5(flowFieldCacheDir(env), `flow-fields-${workflowId}.json`);
6761
+ }
6762
+ async function readFlowFieldMap(env, workflowId) {
6763
+ try {
6764
+ const file = flowFieldCacheFile(env, workflowId);
6765
+ if (!existsSync3(file)) return {};
6766
+ const parsed = JSON.parse(await readFile4(file, "utf8"));
6767
+ return parsed && typeof parsed.mapping === "object" && parsed.mapping ? parsed.mapping : {};
6768
+ } catch {
6769
+ return {};
6770
+ }
6771
+ }
6772
+ async function writeFlowFieldMap(env, workflowId, workflowName, mapping) {
6773
+ if (Object.keys(mapping).length === 0) return;
6774
+ try {
6775
+ const file = flowFieldCacheFile(env, workflowId);
6776
+ await mkdir4(resolve5(file, ".."), { recursive: true });
6777
+ const cache = {
6778
+ workflowId,
6779
+ ...workflowName ? { workflowName } : {},
6780
+ savedAt: (/* @__PURE__ */ new Date()).toISOString(),
6781
+ mapping
6782
+ };
6783
+ await writeFile4(file, JSON.stringify(cache, null, 2), "utf8");
6784
+ } catch {
6785
+ }
6786
+ }
5697
6787
  function normalizeWorkflowIds(raw) {
5698
6788
  if (raw === void 0 || raw === null) return [];
5699
6789
  const list = Array.isArray(raw) ? raw : [raw];
@@ -5709,6 +6799,45 @@ function normalizeWorkflowIds(raw) {
5709
6799
  }
5710
6800
  return ids;
5711
6801
  }
6802
+ var FIXED_FLOW_IDS = ["1325677244111462606", "1326190634427154474"];
6803
+ var FIXED_FLOW_PROFILES = [
6804
+ {
6805
+ host: "www.e-cology.com.cn",
6806
+ tenantKey: "t7akvdnf84",
6807
+ label: "\u6CDB\u5FAE\u751F\u4EA7\uFF08\u6CDB\u5FAE\u7F51\u7EDC\uFF09",
6808
+ ids: [...FIXED_FLOW_IDS]
6809
+ }
6810
+ ];
6811
+ function fixedFlowFlagOn() {
6812
+ const flag = String(process.env.WEAVER_MEETING_FIXED_FLOW ?? "1").trim().toLowerCase();
6813
+ return !(flag === "0" || flag === "false" || flag === "off");
6814
+ }
6815
+ function fixedFlowOverride() {
6816
+ const override = String(process.env.WEAVER_MEETING_FIXED_FLOW_IDS ?? "").trim();
6817
+ if (!override) return [];
6818
+ return override.split(/[,;,;\s]+/).map((item) => item.trim()).filter((item) => item !== "");
6819
+ }
6820
+ async function fixedFlowIdsForEnv(env) {
6821
+ if (!fixedFlowFlagOn()) return { ids: [] };
6822
+ const override = fixedFlowOverride();
6823
+ if (override.length > 0) return { ids: override, label: "\u73AF\u5883\u53D8\u91CF\u6307\u5B9A" };
6824
+ let baseUrl = "";
6825
+ let tenantKey = "";
6826
+ try {
6827
+ const session = await env?.host?.getSession?.();
6828
+ baseUrl = String(session?.baseUrl ?? "");
6829
+ tenantKey = String(session?.tenantKey ?? session?.tenantId ?? "");
6830
+ } catch {
6831
+ }
6832
+ if (!baseUrl) return { ids: [...FIXED_FLOW_IDS], label: "\u9ED8\u8BA4\u56FA\u5316\u914D\u7F6E\uFF08\u672A\u53D6\u5230\u767B\u5F55\u73AF\u5883\uFF09" };
6833
+ const host = baseUrl.replace(/^https?:\/\//i, "").replace(/\/.*$/, "").toLowerCase();
6834
+ const hit = FIXED_FLOW_PROFILES.find((profile) => profile.host === host && (!profile.tenantKey || !tenantKey || profile.tenantKey === tenantKey));
6835
+ return hit ? { ids: [...hit.ids], label: hit.label } : { ids: [] };
6836
+ }
6837
+ function shouldAutoPickFlow(args) {
6838
+ if (args.flowChoice === "auto") return true;
6839
+ return String(process.env.WEAVER_MEETING_FLOW_CHOICE ?? "").trim().toLowerCase() === "auto";
6840
+ }
5712
6841
  async function resolveWorkflowId(env, args) {
5713
6842
  const explicitList = normalizeWorkflowIds(args.workflowId);
5714
6843
  if (explicitList.length > 0) {
@@ -5718,37 +6847,113 @@ async function resolveWorkflowId(env, args) {
5718
6847
  source: "argument"
5719
6848
  };
5720
6849
  }
6850
+ const fixed = await fixedFlowIdsForEnv(env);
6851
+ if (fixed.ids.length > 0) {
6852
+ return {
6853
+ workflowId: fixed.ids.length === 1 ? fixed.ids[0] : "",
6854
+ candidates: fixed.ids,
6855
+ source: "fixed",
6856
+ ...fixed.label ? { label: fixed.label } : {}
6857
+ };
6858
+ }
5721
6859
  const settings = await fetchEbAppSettings(env, "meeting.eb.meeting.create.flow.prepare");
5722
6860
  const candidates = String(settings.mtNewWf ?? "").split(/[,;,;\s]+/).map((item) => item.trim()).filter((item) => item !== "");
5723
6861
  if (candidates.length === 0) {
5724
6862
  throw meetingValidationError(
5725
6863
  "flow_not_configured",
5726
- "\u5E94\u7528\u8BBE\u7F6E\u91CC\u6CA1\u6709\u914D\u7F6E\u300C\u65B0\u5EFA\u4F1A\u8BAE\u5DE5\u4F5C\u6D41\u300D\uFF08mt_new_wf \u4E3A\u7A7A\uFF09\uFF0C\u65E0\u6CD5\u8D70\u6D41\u7A0B\u5EFA\u4F1A\uFF1B\u8BF7\u7BA1\u7406\u5458\u5728\u4F1A\u8BAE\u5E94\u7528\u8BBE\u7F6E\u91CC\u914D\u7F6E\uFF0C\u6216\u5173\u95ED\u300C\u53EA\u5141\u8BB8\u901A\u8FC7\u6D41\u7A0B\u65B0\u5EFA\u4F1A\u8BAE\u300D"
6864
+ "\u5E94\u7528\u8BBE\u7F6E\u91CC\u6CA1\u6709\u914D\u7F6E\u300C\u65B0\u5EFA\u4F1A\u8BAE\u5DE5\u4F5C\u6D41\u300D\uFF08mt_new_wf \u4E3A\u7A7A\uFF09\uFF0C\u4E5F\u6CA1\u6709\u542F\u7528\u56FA\u5316\u6D41\u7A0B\uFF0C\u65E0\u6CD5\u8D70\u6D41\u7A0B\u5EFA\u4F1A\uFF1B\u8BF7\u7BA1\u7406\u5458\u5728\u4F1A\u8BAE\u5E94\u7528\u8BBE\u7F6E\u91CC\u914D\u7F6E\uFF0C\u6216\u7528 WEAVER_MEETING_FIXED_FLOW_IDS \u6307\u5B9A\u9ED8\u8BA4\u6D41\u7A0B"
5727
6865
  );
5728
6866
  }
5729
6867
  return { workflowId: candidates.length === 1 ? candidates[0] : "", candidates, source: "settings" };
5730
6868
  }
6869
+ function pad2(value) {
6870
+ return String(value).padStart(2, "0");
6871
+ }
6872
+ function tomorrowIso() {
6873
+ const date = new Date(Date.now() + 24 * 60 * 60 * 1e3);
6874
+ return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;
6875
+ }
6876
+ function formatMinute(date) {
6877
+ return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())} ${pad2(date.getHours())}:${pad2(date.getMinutes())}`;
6878
+ }
6879
+ async function probeWorkflowCandidates(env, ids) {
6880
+ const probed = await Promise.all(ids.map(async (id) => {
6881
+ try {
6882
+ const probeForm = await loadCreateForm(env, id);
6883
+ return {
6884
+ id,
6885
+ probeForm,
6886
+ probe: {
6887
+ workflowId: id,
6888
+ ...probeForm.workflowName ? { name: probeForm.workflowName } : {},
6889
+ launchable: true
6890
+ }
6891
+ };
6892
+ } catch (error) {
6893
+ return {
6894
+ id,
6895
+ probeForm: null,
6896
+ probe: {
6897
+ workflowId: id,
6898
+ launchable: false,
6899
+ reason: String(error?.message ?? error).slice(0, 160)
6900
+ }
6901
+ };
6902
+ }
6903
+ }));
6904
+ const probes = [];
6905
+ const loadedForms = /* @__PURE__ */ new Map();
6906
+ for (const item of probed) {
6907
+ if (item.probeForm) loadedForms.set(item.id, item.probeForm);
6908
+ probes.push(item.probe);
6909
+ }
6910
+ return { probes, loadedForms };
6911
+ }
5731
6912
  var ebMeetingCreateFlowPrepare = {
5732
6913
  name: "meeting.eb.meeting.create.flow.prepare",
5733
6914
  resource: "eb",
5734
6915
  method: "meeting.eb.meeting.create.flow.prepare",
5735
6916
  action: "meeting.eb.meeting.create.flow.prepare",
5736
- summary: "\u6D41\u7A0B\u5EFA\u4F1A\uFF08\u51C6\u5907\uFF09\uFF1A\u6309\u5E94\u7528\u8BBE\u7F6E mt_new_wf \u8F7D\u5165\u5EFA\u4F1A\u6D41\u7A0B\u8868\u5355\uFF0C\u628A\u4F1A\u8BAE\u5B57\u6BB5\u6309\u4E2D\u6587\u540D\u6620\u5C04\u6210\u8868\u5355\u503C\uFF0C\u8FD4\u56DE\u9884\u89C8\u4E0E\u786E\u8BA4\u7968\u636E\uFF08\u4E0D\u63D0\u4EA4\uFF09",
6917
+ summary: '\u6D41\u7A0B\u5EFA\u4F1A\uFF08\u51C6\u5907\uFF09\uFF1A\u7528\u56FA\u5316\u6D41\u7A0B\u5019\u9009\u9010\u4E2A\u63A2\u6D4B\u53D1\u8D77\u6743\u9650 \u2014\u2014 \u4EC5 1 \u4E2A\u53EF\u53D1\u8D77\u5C31\u81EA\u52A8\u7528\u5B83\u3001\u591A\u4E2A\u90FD\u53EF\u53D1\u8D77\u5219\u8FD4\u56DE\u9009\u9879\u8BA9\u7528\u6237\u9009\u3001\u90FD\u4E0D\u53EF\u53D1\u8D77\u62A5\u300C\u6CA1\u6709\u53D1\u8D77\u6743\u9650\u300D\uFF1B\u4E0D\u4F20 begin_datetime \u65F6\u81EA\u52A8\u63A8\u8350\u300C\u4EBA+\u4F1A\u8BAE\u5BA4\u90FD\u7A7A\u95F2\u300D\u7684\u65F6\u6BB5\u3002\u53EA\u51FA\u9884\u89C8\u4E0E\u786E\u8BA4\u7968\u636E\uFF0C\u4E0D\u63D0\u4EA4\u3002\u26A0\uFE0F \u7528\u6237\u63D0\u5230\u5BA2\u6237\u540D\uFF08\u300C\u5BA2\u6237\u662F X\u300D\uFF09\u65F6**\u5FC5\u987B\u5148** meeting.eb.crm.search {"keyword":"X"} \u62FF customerId\uFF0C\u518D\u4F20 crm_members:[{id,name}]\uFF1B\u53EA\u628A\u5BA2\u6237\u540D\u5199\u8FDB\u4F1A\u8BAE\u540D\u79F0/\u5185\u5BB9\uFF0C\u8868\u5355\u300C\u53C2\u4F1A\u5BA2\u6237\u300D\u5C31\u662F\u7A7A\u7684',
5737
6918
  capability: "eb.meeting",
5738
6919
  risk: "read-before-write",
5739
6920
  requiresConfirmation: true,
5740
6921
  inputSchema: schemaOf(
5741
6922
  {
5742
6923
  mt_name: { type: "string", description: "\u4F1A\u8BAE\u540D\u79F0\uFF08\u5FC5\u586B\uFF09" },
5743
- begin_datetime: { ...SCHEMA_DATETIME_MINUTE, description: "\u5F00\u59CB\u65F6\u95F4\uFF08\u5FC5\u586B\uFF0Cyyyy-MM-dd HH:mm\uFF09" },
5744
- end_datetime: { ...SCHEMA_DATETIME_MINUTE, description: "\u7ED3\u675F\u65F6\u95F4\uFF08\u5FC5\u586B\uFF0Cyyyy-MM-dd HH:mm\uFF09" },
6924
+ begin_datetime: {
6925
+ ...SCHEMA_DATETIME_MINUTE,
6926
+ description: "\u5F00\u59CB\u65F6\u95F4\uFF08yyyy-MM-dd HH:mm\uFF09\u3002**\u53EF\u4EE5\u4E0D\u4F20**\uFF1A\u4E0D\u4F20\u4F1A\u6309\u53C2\u4F1A\u4EBA\u4E0E\u4F1A\u8BAE\u5BA4\u7A7A\u95F2\u60C5\u51B5\u63A8\u8350 3 \u4E2A\u65F6\u6BB5\u8BA9\u4F60\u9009\uFF1B\u53EA\u4F20\u5F00\u59CB\u65F6\u95F4\u3001\u4E0D\u4F20\u7ED3\u675F\u65F6\u95F4\u65F6\uFF0C\u6309 durationMinutes\uFF08\u9ED8\u8BA4 60 \u5206\u949F\uFF09\u81EA\u52A8\u7B97\u7ED3\u675F\u65F6\u95F4"
6927
+ },
6928
+ end_datetime: {
6929
+ ...SCHEMA_DATETIME_MINUTE,
6930
+ description: "\u7ED3\u675F\u65F6\u95F4\uFF08yyyy-MM-dd HH:mm\uFF09\uFF1B\u4E0D\u4F20\u5219\u6309 durationMinutes \u4ECE\u5F00\u59CB\u65F6\u95F4\u63A8\u7B97\uFF08\u9ED8\u8BA4 60 \u5206\u949F\uFF09"
6931
+ },
5745
6932
  mt_desc: { type: "string", description: "\u4F1A\u8BAE\u5185\u5BB9/\u8BF4\u660E" },
5746
6933
  address: { type: "string", description: "\u4F1A\u8BAE\u5BA4\uFF08\u540D\u79F0\u6216 id\uFF0C\u6309\u6D41\u7A0B\u8868\u5355\u5B57\u6BB5\u8981\u6C42\uFF09" },
5747
6934
  customize_address: { type: "string", description: "\u81EA\u5B9A\u4E49\u4F1A\u8BAE\u5730\u70B9" },
5748
6935
  hrm_members: { type: "string", description: "\u53C2\u4F1A\u4EBA\u5458\uFF08\u9017\u53F7\u5206\u9694\u5458\u5DE5 id \u6216\u59D3\u540D\uFF0C\u6309\u6D41\u7A0B\u8868\u5355\u8981\u6C42\uFF09" },
5749
6936
  total_member: { type: "number", description: "\u53C2\u4F1A\u4EBA\u6570" },
5750
- caller: { type: "string", description: "\u53EC\u96C6\u4EBA" },
5751
- contacter: { type: "string", description: "\u8054\u7CFB\u4EBA" },
6937
+ crm_members: {
6938
+ oneOf: [{ type: "string" }, { type: "array", items: { oneOf: [{ type: "string" }, { type: "object" }] } }],
6939
+ description: "\u53C2\u4F1A\u5BA2\u6237\uFF1A**\u5FC5\u987B\u5E26\u5BA2\u6237\u540D\u79F0**\uFF0C\u5199\u6CD5 `[{id,name}]` \u6216\u300C\u5BA2\u6237\u540D(\u5BA2\u6237id)\u300D\u6216\u9017\u53F7\u5206\u9694\u7684\u8FD9\u4E24\u79CD\u3002\u5BA2\u6237\u5148\u4ECE `meeting.eb.crm.search` \u6309\u5BA2\u6237\u540D\u67E5\u51FA\uFF08\u5165\u53C2 custom_name\uFF0C\u51FA\u53C2 items[].customerId / name\uFF09\uFF0C\u8BE5\u67E5\u8BE2**\u4E00\u6B21\u53EA\u56DE\u9996\u4E2A\u5339\u914D** \u21D2 exact=false \u65F6\u5FC5\u987B\u5148\u8BA9\u7528\u6237\u786E\u8BA4\u5BA2\u6237\u540D\u518D\u5199\u8FD9\u4E2A\u5B57\u6BB5\u3002\u26D4 \u53EA\u7ED9 id \u4E0D\u7ED9\u540D\u79F0\u4F1A\u88AB\u62D2\u7EDD\uFF08\u5BA2\u6237\u6D4F\u89C8\u6309\u94AE\u6309\u540D\u79F0\u843D\u503C\uFF0C\u53EA\u7ED9 id \u4F1A\u9759\u9ED8\u5B58\u6210\u7A7A\uFF09"
6940
+ },
6941
+ crm_member_count: { type: "number", description: "\u5E94\u5230\u5BA2\u6237\u6570\uFF1B\u4E0D\u4F20\u5219\u6309 crm_members \u6761\u6570\u81EA\u52A8\u7B97" },
6942
+ crm_member_details: {
6943
+ type: "array",
6944
+ description: '\u53C2\u4F1A\u5BA2\u6237\u660E\u7EC6\uFF08\u5BBF\u4E3B\u8868\u5355\u91CC\u7684\u660E\u7EC6\u8868\uFF0C\u5217 \u59D3\u540D/\u6027\u522B/\u804C\u4F4D/\u624B\u673A\u53F7\u7801/\u5907\u6CE8\uFF09\uFF1A\u6BCF\u884C `{"xm":"\u5F20\u4E09","xb":"\u7537","zw":"\u7ECF\u7406","sjhm":"138...","bz":""}`\uFF0C\u4E5F\u63A5\u53D7\u4E2D\u6587\u952E\uFF08\u59D3\u540D/\u6027\u522B/\u804C\u4F4D/\u624B\u673A\u53F7\u7801/\u5907\u6CE8\uFF09\u3002\u5217\u5B57\u6BB5 id \u7531 CLI \u4ECE\u8868\u5355\u91CC\u6309\u5217\u540D\u53CD\u67E5\uFF0C\u4E0D\u7528\u4F20\u3002\u26A0\uFE0F \u8BE5\u660E\u7EC6\u8868\u5FC5\u987B\u5728\u53D1\u8D77\u8282\u70B9\u5E03\u5C40\u91CC\uFF08\u5426\u5219 CLI \u4F1A\u63D0\u793A"\u8868\u5355\u91CC\u6CA1\u6709\u53C2\u4F1A\u5BA2\u6237\u660E\u7EC6"\u5E76\u5FFD\u7565\u8FD9\u4E9B\u884C\uFF09\u26D4 \u9ED8\u8BA4\u4E0D\u751F\u6548\uFF1A\u5BA2\u6237\u8054\u7CFB\u4EBA\u7531 CLI \u6309 CRM \u5168\u91CF\u81EA\u52A8\u5E26\u5165\uFF0C\u8FD9\u91CC\u53EA\u5728 crm_detail_mode:"only" \u65F6\u624D\u7528\u6765"\u53EA\u5199\u8FD9\u51E0\u4F4D"',
6945
+ items: { type: "object" }
6946
+ },
6947
+ crm_detail_mode: {
6948
+ type: "string",
6949
+ description: '\u660E\u7EC6\u884C\u53E3\u5F84\uFF1A\u4E0D\u4F20\uFF08\u9ED8\u8BA4\uFF09= \u6309 CRM \u628A\u8BE5\u5BA2\u6237**\u5168\u90E8**\u8054\u7CFB\u4EBA\u81EA\u52A8\u5199\u5165\u300C\u53C2\u4F1A\u5BA2\u6237\u660E\u7EC6\u300D\uFF08\u4E00\u4EBA\u4E00\u884C\uFF0C\u4E0D\u7528\u95EE\u7528\u6237\u9009\u54EA\u4F4D\uFF09\uFF1B\u4F20 "only" = \u53EA\u5199 crm_member_details \u91CC\u70B9\u540D\u7684\u8FD9\u51E0\u4F4D'
6950
+ },
6951
+ caller: { type: "string", description: "\u53EC\u96C6\u4EBA\uFF08\u5458\u5DE5 id \u6216\u59D3\u540D\uFF09\u3002\u7F3A\u7701 = \u5F53\u524D\u767B\u5F55\u4EBA\uFF0C\u4E0D\u7528\u4F20\uFF1B\u53EA\u6709\u6307\u5B9A\u522B\u4EBA\u5F53\u53EC\u96C6\u4EBA\u624D\u4F20" },
6952
+ contacter: { type: "string", description: "\u8054\u7CFB\u4EBA\uFF08\u5458\u5DE5 id \u6216\u59D3\u540D\uFF09\u3002\u7F3A\u7701 = \u5F53\u524D\u767B\u5F55\u4EBA\uFF0C\u4E0D\u7528\u4F20\uFF1B\u53EA\u6709\u6307\u5B9A\u522B\u4EBA\u5F53\u8054\u7CFB\u4EBA\u624D\u4F20" },
6953
+ mt_type: {
6954
+ type: "string",
6955
+ description: "\u4F1A\u8BAE\u7C7B\u578B\uFF1A\u53EF\u4F20\u7C7B\u578B\u540D\u79F0\u6216 id\uFF08\u5148\u7528 `meeting.eb.type.list` \u67E5\u6709\u54EA\u4E9B\uFF09\u3002**\u5F88\u591A\u79DF\u6237\u7684\u6D41\u7A0B\u8868\u5355\u628A\u300C\u4F1A\u8BAE\u7C7B\u578B\u300D\u8BBE\u4E3A\u5FC5\u586B**\uFF0C\u4E0D\u4F20\u4F1A\u88AB `workflow.createRequest` \u6253\u56DE\u7F3A\u5B57\u6BB5"
6956
+ },
5752
6957
  other_remark: { type: "string", description: "\u5907\u6CE8" },
5753
6958
  workflowId: {
5754
6959
  oneOf: [{ type: "string" }, { type: "array", items: { type: "string" } }],
@@ -5756,117 +6961,171 @@ var ebMeetingCreateFlowPrepare = {
5756
6961
  },
5757
6962
  autoRoom: { type: "boolean", description: "\u9ED8\u8BA4 false\uFF1A\u672A\u6307\u5B9A\u4F1A\u8BAE\u5BA4\u65F6\u8FD4\u56DE\u5019\u9009\u8BA9\u7528\u6237\u9009\uFF1B\u4F20 true \u624D\u81EA\u52A8\u91C7\u7528\u63A8\u8350" },
5758
6963
  allowNoRoom: { type: "boolean", description: "\u786E\u5B9E\u4E0D\u9700\u8981\u4F1A\u8BAE\u5BA4\uFF08\u7EBF\u4E0A\u4F1A\u7B49\uFF09\u65F6\u4F20 true\uFF0C\u907F\u514D\u88AB\u201C\u8BF7\u9009\u62E9\u4F1A\u8BAE\u5BA4\u201D\u62E6\u4F4F" },
6964
+ timeConfirmed: {
6965
+ type: "boolean",
6966
+ description: '\u26D4 **\u65F6\u95F4\u7684"\u7528\u6237\u5DF2\u786E\u8BA4"\u6807\u8BB0**\u3002\u53EA\u6709\u4EE5\u4E0B\u4E24\u79CD\u60C5\u51B5\u624D\u4F20 true\uFF1A\u2460 \u7528\u6237\u539F\u8BDD\u91CC\u660E\u786E\u7ED9\u51FA\u4E86\u65F6\u95F4\uFF08\u5982"\u660E\u5929 9 \u70B9\u5230 10 \u70B9"\uFF09\uFF1B\u2461 \u7528\u6237\u5728\u65F6\u6BB5\u9009\u9879\u91CC\u70B9\u9009\u540E\uFF0C\u628A `options[].value` **\u6574\u4F53**\u539F\u6837\u56DE\u4F20\uFF08value \u81EA\u5E26\u8BE5\u6807\u8BB0\uFF09\u3002\u4E0D\u4F20 = \u672A\u786E\u8BA4\uFF1A\u5373\u4F7F\u4F60\u4F20\u4E86 begin_datetime\uFF0CCLI \u4E5F**\u4E0D\u4F1A**\u5F53\u6210\u5DF2\u5B9A\uFF0C\u800C\u662F\u8FD4\u56DE\u65F6\u6BB5\u9009\u9879\u8BA9\u7528\u6237\u70B9\u9009\u3002\u26D4 \u7528\u6237\u53EA\u8BF4"\u660E\u5929/\u4E0A\u5348/\u4E0B\u5348"\u8FD9\u7C7B\u6A21\u7CCA\u8BF4\u6CD5\u65F6\uFF0C**\u5FC5\u987B\u4E0D\u4F20**\uFF0C\u8BA9 CLI \u7ED9\u5019\u9009'
6967
+ },
6968
+ roomConfirmed: {
6969
+ type: "boolean",
6970
+ description: '\u26D4 **\u4F1A\u8BAE\u5BA4\u7684"\u7528\u6237\u5DF2\u786E\u8BA4"\u6807\u8BB0**\u3002\u53EA\u6709\u4EE5\u4E0B\u4E24\u79CD\u60C5\u51B5\u624D\u4F20 true\uFF1A\u2460 \u7528\u6237\u539F\u8BDD\u660E\u786E\u6307\u5B9A\u4E86\u67D0\u95F4\u4F1A\u8BAE\u5BA4\uFF1B\u2461 \u7528\u6237\u5728\u623F\u95F4\u9009\u9879\u91CC\u70B9\u9009\u540E\u628A `options[].value` \u6574\u4F53\u539F\u6837\u56DE\u4F20\u3002\u4E0D\u4F20 = \u672A\u786E\u8BA4\uFF1A\u5373\u4F7F\u4F20\u4E86 address\uFF0CCLI \u4E5F\u4F1A\u628A"\u4F60\u6307\u5B9A\u7684\u8FD9\u95F4"\u548C\u63A8\u8350\u5019\u9009\u4E00\u8D77\u5217\u51FA\u6765\u8BA9\u7528\u6237\u70B9\u9009\u3002\u26D4 \u4E0D\u8981\u81EA\u5DF1\u4ECE room.list / room.recommend \u91CC\u6311\u4E00\u95F4\u585E\u8FDB address \u2014\u2014 \u90A3\u662F\u66FF\u7528\u6237\u62CD\u677F'
6971
+ },
5759
6972
  extraFields: {
5760
6973
  type: "object",
5761
- description: '\u6309**\u6D41\u7A0B\u8868\u5355\u91CC\u7684\u5B57\u6BB5\u540D**\u586B\u989D\u5916\u5B57\u6BB5\uFF0C\u5F62\u5982 {"\u53C2\u4F1A\u5BA2\u6237": "xx\u516C\u53F8", "\u8054\u7CFB\u65B9\u5F0F": "138xxxx"}\uFF1A\u7528\u4E8E\u79DF\u6237\u5728\u6D41\u7A0B\u8868\u5355\u4E0A\u81EA\u5EFA\u3001CLI \u5185\u7F6E\u522B\u540D\u91CC\u6CA1\u6709\u7684\u5B57\u6BB5\u3002\u952E\u5FC5\u987B\u4E0E\u8BE5\u6D41\u7A0B\u8868\u5355\u7684\u5B57\u6BB5\u540D\u5B8C\u5168\u4E00\u81F4\uFF08prepare \u7684 warnings \u4F1A\u5217\u51FA\u8868\u5355\u73B0\u6709\u5168\u90E8\u5B57\u6BB5\u540D\uFF09\uFF1B\u5339\u914D\u4E0D\u5230\u7684\u4F1A\u8FDB unmapped \u63D0\u793A\uFF0C\u4E0D\u4F1A\u778E\u586B'
6974
+ description: '\u6309**\u6D41\u7A0B\u8868\u5355\u91CC\u7684\u5B57\u6BB5\u540D**\u586B\u989D\u5916\u5B57\u6BB5\uFF0C\u5F62\u5982 {"\u53C2\u4F1A\u5BA2\u6237": "xx\u516C\u53F8", "\u8054\u7CFB\u65B9\u5F0F": "138xxxx"}\uFF1A\u7528\u4E8E\u79DF\u6237\u5728\u6D41\u7A0B\u8868\u5355\u4E0A\u81EA\u5EFA\u3001CLI \u5185\u7F6E\u522B\u540D\u91CC\u6CA1\u6709\u7684\u5B57\u6BB5\u3002\u952E\u5FC5\u987B\u4E0E\u8BE5\u6D41\u7A0B\u8868\u5355\u7684\u5B57\u6BB5\u540D\u5B8C\u5168\u4E00\u81F4\uFF08prepare \u7684 warnings \u4F1A\u5217\u51FA\u8868\u5355\u73B0\u6709\u5168\u90E8\u5B57\u6BB5\u540D\uFF09\uFF1B\u5339\u914D\u4E0D\u5230\u7684\u4F1A\u8FDB unmapped \u63D0\u793A\uFF0C\u4E0D\u4F1A\u778E\u586B\u3002\u26D4 **\u4F1A\u8BAE\u5730\u70B9 / \u53C2\u4F1A\u4EBA\u5458 / \u53EC\u96C6\u4EBA / \u8054\u7CFB\u4EBA / \u4F1A\u8BAE\u7C7B\u578B \u4E0D\u8981\u585E\u8FD9\u91CC** \u2014\u2014 \u5B83\u4EEC\u6709\u4E13\u7528\u5165\u53C2\uFF08address / hrm_members / caller / contacter / mt_type\uFF09\uFF0CCLI \u4F1A\u628A\u503C\u8865\u6210\u6D4F\u89C8\u6309\u94AE\u8981\u7684 `{id,name}` \u5BF9\u8C61\uFF1B\u585E\u8FDB extraFields \u4F1A\u6309\u7EAF\u6587\u672C\u63D0\u4EA4\uFF0C\u670D\u52A1\u7AEF\u4F1A\u6253\u56DE\u300C\u8BF7\u6309\u540D\u79F0\u9009\u62E9\u300D'
6975
+ },
6976
+ dateFrom: {
6977
+ type: "string",
6978
+ description: "\u6CA1\u7ED9\u5177\u4F53\u65F6\u95F4\u65F6\u6309\u54EA\u4E00\u5929\u63A8\u8350\u65F6\u6BB5\uFF08yyyy-MM-dd\uFF1B\u4E0D\u4F20\u9ED8\u8BA4\u660E\u5929\uFF09"
6979
+ },
6980
+ durationMinutes: {
6981
+ type: "number",
6982
+ description: "\u4F1A\u8BAE\u65F6\u957F\uFF08\u5206\u949F\uFF0C\u9ED8\u8BA4 60\uFF09\uFF1A\u53EA\u7ED9\u4E86\u5F00\u59CB\u65F6\u95F4\u65F6\u7528\u6765\u7B97\u7ED3\u675F\u65F6\u95F4\uFF1B\u63A8\u8350\u65F6\u6BB5\u65F6\u4E5F\u6309\u8FD9\u4E2A\u65F6\u957F\u627E"
6983
+ },
6984
+ workStartHour: { type: "number", description: "\u63A8\u8350\u65F6\u6BB5\u7684\u6700\u65E9\u5C0F\u65F6\uFF08\u9ED8\u8BA4 9\uFF09" },
6985
+ workEndHour: { type: "number", description: "\u63A8\u8350\u65F6\u6BB5\u7684\u6700\u665A\u5C0F\u65F6\uFF08\u9ED8\u8BA4 18\uFF09" },
6986
+ fieldValues: {
6987
+ type: "object",
6988
+ additionalProperties: true,
6989
+ description: '\u6309**\u8868\u5355\u5B57\u6BB5 datakey\uFF08fieldId\uFF09**\u76F4\u63A5\u7ED9\u503C\uFF0C\u5F62\u5982 {"1325677252711735881": "\u7814\u8BA8\u4F1A"}\u3002\u4F18\u5148\u7EA7\u6700\u9AD8\uFF08\u540C\u540D\u5B57\u6BB5\u4EE5\u5B83\u4E3A\u51C6\uFF09\uFF1A\u9002\u5408\u5B57\u6BB5\u540D\u5E26\u63D0\u793A\u8BED\u3001\u6216\u79DF\u6237\u81EA\u5EFA\u5B57\u6BB5\u8FD9\u7C7B\u6309\u4E2D\u6587\u540D\u731C\u4E0D\u5230\u7684\u60C5\u51B5\u3002datakey \u4ECE prepare \u8FD4\u56DE\u7684 preview.mapped\uFF08\u5F62\u5982 "\u4F1A\u8BAE\u540D\u79F0(1325677252711735881)"\uFF09\u91CC\u53D6\uFF1B\u6BCF\u4E2A\u6D41\u7A0B\u7684 datakey \u56FA\u5B9A\u4E0D\u53D8\uFF0C\u53EF\u957F\u671F\u786C\u7F16\u7801\u3002'
6990
+ },
6991
+ flowChoice: {
6992
+ type: "string",
6993
+ enum: ["auto", "ask"],
6994
+ description: "\u591A\u4E2A\u5EFA\u4F1A\u6D41\u7A0B**\u90FD\u6709\u53D1\u8D77\u6743\u9650**\u65F6\u600E\u4E48\u529E\uFF1A**\u9ED8\u8BA4\uFF08\u4E0D\u4F20 / ask\uFF09= \u8FD4\u56DE\u9009\u9879\u8BA9\u7528\u6237\u6311**\uFF1Bauto = \u81EA\u52A8\u53D6\u4F18\u5148\u7EA7\u6700\u9AD8\u7684\u90A3\u4E2A\uFF08\u7701\u4E00\u8F6E\uFF0C\u4F46\u9009\u9519\u6D41\u7A0B\u4F1A\u6F0F\u5B57\u6BB5\uFF0C\u614E\u7528\uFF09\u3002\u53EA\u6709 1 \u4E2A\u6709\u6743\u9650\u65F6\u4E0D\u53D7\u6B64\u53C2\u6570\u5F71\u54CD\uFF0CCLI \u76F4\u63A5\u53D1\u8D77"
5762
6995
  }
5763
6996
  },
5764
- { required: ["mt_name", "begin_datetime", "end_datetime"] }
6997
+ { required: ["mt_name"] }
5765
6998
  ),
5766
6999
  handler: async (args, env) => {
5767
- const resolved = await resolveWorkflowId(env, args);
5768
- const candidates = resolved.candidates;
5769
- let workflowId = resolved.workflowId;
5770
- const sourceLabel = resolved.source === "argument" ? "\u8C03\u7528\u65B9\u4F20\u5165" : "\u5E94\u7528\u8BBE\u7F6E\u914D\u7F6E";
5771
- const probes = [];
5772
- const loadedForms = /* @__PURE__ */ new Map();
5773
- if (!workflowId && candidates.length > 1) {
5774
- const probed = await Promise.all(candidates.map(async (id) => {
5775
- try {
5776
- const probeForm = await loadCreateForm(env, id);
5777
- return {
5778
- id,
5779
- probeForm,
5780
- probe: {
5781
- workflowId: id,
5782
- ...probeForm.workflowName ? { name: probeForm.workflowName } : {},
5783
- launchable: true
5784
- }
5785
- };
5786
- } catch (error) {
5787
- return {
5788
- id,
5789
- probeForm: null,
5790
- probe: {
5791
- workflowId: id,
5792
- launchable: false,
5793
- reason: String(error?.message ?? error).slice(0, 160)
5794
- }
5795
- };
5796
- }
5797
- }));
5798
- for (const item of probed) {
5799
- if (item.probeForm) loadedForms.set(item.id, item.probeForm);
5800
- probes.push(item.probe);
5801
- }
5802
- const launchable = probes.filter((probe) => probe.launchable);
5803
- if (launchable.length === 0) {
5804
- throw meetingValidationError(
5805
- "flow_not_launchable",
5806
- `${sourceLabel}\u4E86 ${candidates.length} \u4E2A\u5EFA\u4F1A\u6D41\u7A0B\uFF0C\u4F46\u5F53\u524D\u8D26\u53F7\u4E00\u4E2A\u90FD\u53D1\u8D77\u4E0D\u4E86\uFF08\u65E0\u53D1\u8D77\u6743\u9650\u6216\u6D41\u7A0B\u5DF2\u505C\u7528\uFF09\uFF1A` + probes.map((probe) => `${probe.workflowId}\uFF08${probe.reason ?? "\u4E0D\u53EF\u53D1\u8D77"}\uFF09`).join("\uFF1B"),
5807
- { probes }
5808
- );
5809
- }
5810
- if (launchable.length > 1) {
5811
- return meetingSuccess("meeting.eb.meeting.create.flow.prepare", {
5812
- status: "NEEDS_SELECTION",
5813
- message: `${sourceLabel}\u4E86 ${candidates.length} \u4E2A\u5EFA\u4F1A\u6D41\u7A0B\uFF0C\u5F53\u524D\u8D26\u53F7\u53EF\u53D1\u8D77\u5176\u4E2D ${launchable.length} \u4E2A\uFF0C\u8BF7\u8BA9\u7528\u6237\u9009\u62E9\u8981\u89E6\u53D1\u54EA\u4E00\u4E2A \u2014\u2014 **\u4E0D\u8981\u9ED8\u8BA4\u53D6\u7B2C\u4E00\u4E2A**\uFF08\u5404\u6D41\u7A0B\u8868\u5355\u5B57\u6BB5\u53EF\u80FD\u5B8C\u5168\u4E0D\u540C\uFF0C\u9009\u9519\u4F1A\u6F0F\u5B57\u6BB5\uFF1B2026-10-29 \u771F\u5B9E\u4E8B\u6545\uFF1A\u53D6\u7B2C\u4E00\u4E2A\u5BFC\u81F4\u5B57\u6BB5\u5168\u586B\u4E0D\u8FDB\u53E6\u4E00\u6761\u6D41\u7A0B\uFF0C\u7528\u6237\u4EE5\u4E3A"\u7CFB\u7EDF\u53EA\u652F\u6301\u4E00\u6761\u6D41\u7A0B"\uFF09`,
5814
- options: launchable.map((probe, index) => ({
5815
- key: `W${index + 1}`,
5816
- label: probe.name ? `${probe.name}\uFF08${probe.workflowId}\uFF09` : probe.workflowId,
5817
- value: { workflowId: probe.workflowId },
5818
- reason: "\u5F53\u524D\u8D26\u53F7\u6709\u53D1\u8D77\u6743\u9650",
5819
- // 把该流程的字段名如实带出来:不同流程字段差异很大(有的有设备需求/涉密项目,有的没有),
5820
- // 用户要靠这个才能选对(2026-10-29 用户反馈:选错方式后字段全填不进去)。
5821
- fields: (loadedForms.get(probe.workflowId)?.fields ?? []).map((f) => f.name).filter(Boolean)
5822
- })),
5823
- probes
5824
- }, {
5825
- warnings: [
5826
- `${sourceLabel}\u4E86 ${candidates.length} \u4E2A\u5EFA\u4F1A\u6D41\u7A0B\uFF0C\u5176\u4E2D ${launchable.length} \u4E2A\u5F53\u524D\u8D26\u53F7\u53EF\u53D1\u8D77\uFF1B**\u5FC5\u987B\u628A\u8FD9\u51E0\u4E2A\u6D41\u7A0B\u505A\u6210\u9009\u9879\u8BA9\u7528\u6237\u6311\u4E00\u4E2A\uFF0C\u7981\u6B62\u81EA\u5DF1\u66FF\u7528\u6237\u9009**\uFF1B\u9009\u5B9A\u540E\u5E26\u5355\u4E2A workflowId \u91CD\u65B0 prepare\u3002`,
5827
- ...probes.filter((probe) => !probe.launchable).map((probe) => `\u6D41\u7A0B ${probe.workflowId} \u5F53\u524D\u8D26\u53F7\u65E0\u6CD5\u53D1\u8D77\uFF08${probe.reason ?? "\u65E0\u6743\u9650"}\uFF09\uFF0C\u5DF2\u6392\u9664`)
5828
- ]
5829
- });
5830
- }
5831
- workflowId = launchable[0].workflowId;
5832
- }
5833
- const form = loadedForms.get(workflowId) ?? await loadCreateForm(env, workflowId);
5834
- if (!form.continuation) {
5835
- throw meetingValidationError("flow_context_missing", "\u5EFA\u4F1A\u6D41\u7A0B\u8868\u5355\u6CA1\u6709\u8FD4\u56DE continuation\uFF0C\u65E0\u6CD5\u7EE7\u7EED\u63D0\u4EA4");
5836
- }
7000
+ let workflowId = "";
7001
+ let flowPickNote = null;
5837
7002
  const decisions = {};
5838
7003
  const enrich = args.enrich !== false;
5839
7004
  const beginText = String(args.begin_datetime ?? "").trim();
5840
- const endText = String(args.end_datetime ?? "").trim();
5841
- const memberIds = String(args.hrm_members ?? "").split(/[,;,;\s]+/).map((item) => item.trim()).filter((item) => item !== "");
7005
+ let endText = String(args.end_datetime ?? "").trim();
7006
+ const rawMembers = String(args.hrm_members ?? "").split(/[,;,;\s]+/).map((item) => item.trim()).filter((item) => item !== "");
7007
+ const nameLike = rawMembers.filter((item) => !/^\d+$/.test(item));
7008
+ const nameToId = nameLike.length > 0 ? await import("./schedule-NIJBTYDO.js").then((m) => m.resolveMemberIdsByName(env, nameLike)).catch(() => /* @__PURE__ */ new Map()) : /* @__PURE__ */ new Map();
7009
+ const memberIds = rawMembers.map((item) => nameToId.get(item) ?? item);
7010
+ const timeConfirmed = args.timeConfirmed === true;
7011
+ const callerWorkflowId = String(args.workflowId ?? "").trim();
7012
+ const earlyWorkflowNote = callerWorkflowId === "" ? "" : `\u26D4 \u4F60\u4F20\u4E86 workflowId=${callerWorkflowId}\uFF0C\u4F46**\u6D41\u7A0B\u8FD8\u6CA1\u5230\u786E\u8BA4\u6B65\u9AA4**\uFF08\u987A\u5E8F\u662F \u540D\u79F0\u2192\u65F6\u95F4\u2192\u53C2\u4F1A\u4EBA\u2192\u5730\u70B9\u2192\u6D41\u7A0B\uFF09\u3002\u672C\u6B21\u53EA\u5904\u7406\u65F6\u95F4/\u5730\u70B9\uFF1B\u26D4 \u4E0D\u8981\u5728\u56DE\u590D\u91CC\u8BF4"\u6309 X \u6D41\u7A0B\u53D1\u8D77"\u2014\u2014\u56DB\u8981\u7D20\u5B9A\u5B8C\u540E CLI \u4F1A\u8FD4\u56DE\u6D41\u7A0B\u9009\u9879\u8BA9\u7528\u6237\u70B9\u3002`;
7013
+ const beginHasTime = /^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}/.test(beginText);
7014
+ const endHasTime = /^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}/.test(endText);
7015
+ if (beginHasTime && !endHasTime) {
7016
+ const duration = Number(args.durationMinutes) > 0 ? Math.round(Number(args.durationMinutes)) : 60;
7017
+ const startMs = Date.parse(beginText.slice(0, 16).replace(" ", "T"));
7018
+ if (Number.isFinite(startMs)) endText = formatMinute(new Date(startMs + duration * 6e4));
7019
+ }
7020
+ if (!beginHasTime || !timeConfirmed) {
7021
+ const day2 = /^\d{4}-\d{2}-\d{2}/.test(beginText) ? beginText.slice(0, 10) : String(args.dateFrom ?? "").trim().slice(0, 10) || tomorrowIso();
7022
+ const duration = Number(args.durationMinutes) > 0 ? Math.round(Number(args.durationMinutes)) : 60;
7023
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
7024
+ const suggested = await getMeetingOperation2("meeting.eb.meeting.create.suggest").handler(
7025
+ {
7026
+ dateFrom: day2,
7027
+ durationMinutes: duration,
7028
+ hrm_members: memberIds.join(","),
7029
+ maxSlots: 3,
7030
+ topN: 3,
7031
+ ...Number(args.total_member) > 0 ? { memberCount: Number(args.total_member) } : {},
7032
+ ...args.workStartHour !== void 0 ? { workStartHour: Number(args.workStartHour) } : {},
7033
+ ...args.workEndHour !== void 0 ? { workEndHour: Number(args.workEndHour) } : {}
7034
+ },
7035
+ env
7036
+ ).catch((error) => ({ data: { slots: [] }, slotError: String(error?.message ?? error).slice(0, 160) }));
7037
+ const slotOptions2 = suggested?.data?.slots ?? [];
7038
+ const slotWarnings = [
7039
+ ...suggested?.warnings ?? [],
7040
+ ...suggested?.slotError ? [`\u65F6\u6BB5\u63A8\u8350\u672A\u8DD1\u6210\u529F\uFF1A${suggested.slotError}`] : [],
7041
+ ...memberIds.length === 0 ? ['\u26A0\uFE0F \u6CA1\u6709\u53C2\u4F1A\u4EBA\u5458\uFF0C\u65E0\u6CD5\u6838\u9A8C"\u4EBA\u662F\u5426\u7A7A\u95F2" \u2014\u2014 \u4E0B\u9762\u53EA\u662F**\u4F1A\u8BAE\u5BA4\u6709\u7A7A**\u7684\u65F6\u6BB5\uFF0C\u8BF7\u5982\u5B9E\u8BF4\u660E\u3002'] : []
7042
+ ];
7043
+ return meetingSuccess("meeting.eb.meeting.create.flow.prepare", {
7044
+ status: "NEEDS_SELECTION",
7045
+ selectionType: "slot",
7046
+ ui: { component: "choice", multiple: false, recommendedKey: "T1" },
7047
+ message: slotOptions2.length > 0 ? `${beginHasTime ? `\u4F60\u4F20\u5165\u7684\u65F6\u95F4 ${beginText}~${endText} **\u672A\u7ECF\u7528\u6237\u786E\u8BA4**\uFF08\u6CA1\u5E26 timeConfirmed\uFF09` : "\u6CA1\u6709\u7ED9\u51FA\u5177\u4F53\u65F6\u95F4"}\uFF0C\u5DF2\u6309\u53C2\u4F1A\u4EBA\u4E0E\u4F1A\u8BAE\u5BA4\u7A7A\u95F2\u60C5\u51B5\u63A8\u8350 ${slotOptions2.length} \u4E2A\u65F6\u6BB5\uFF08${day2}\uFF0C\u65F6\u957F ${duration} \u5206\u949F\uFF09\u2014\u2014 \u26D4 \u5FC5\u987B\u8BA9\u7528\u6237\u5728\u9009\u9879\u91CC\u70B9\u9009\uFF0C**\u4E0D\u8981**\u628A\u4EFB\u4F55\u4E00\u4E2A\u5019\u9009\u5199\u6210"\u5DF2\u5B9A"\u6216\u653E\u8FDB\u786E\u8BA4\u6458\u8981` : `${beginHasTime ? `\u4F60\u4F20\u5165\u7684\u65F6\u95F4 ${beginText}~${endText} \u672A\u7ECF\u7528\u6237\u786E\u8BA4\uFF08\u6CA1\u5E26 timeConfirmed\uFF09` : "\u6CA1\u6709\u7ED9\u51FA\u5177\u4F53\u65F6\u95F4"}\uFF0C\u4E14 ${day2} \u6CA1\u80FD\u627E\u5230\u300C\u53C2\u4F1A\u4EBA\u7A7A\u95F2 + \u4F1A\u8BAE\u5BA4\u7A7A\u95F2\u300D\u7684\u65F6\u6BB5\uFF0C\u8BF7\u8BA9\u7528\u6237\u6362\u4E2A\u65E5\u671F\u6216\u6307\u5B9A\u65F6\u95F4`,
7048
+ options: slotOptions2.map((slot, index) => ({
7049
+ key: `T${index + 1}`,
7050
+ label: String(slot.label ?? ""),
7051
+ // ⛔ 时间不在 slot 顶层,而在 slot.value 里(suggest 的返回结构)—— 取错会让调用方
7052
+ // 拿不到 begin/end,只能再问用户一次时间(2026-10-30 冒烟实测踩到)。
7053
+ // `timeConfirmed: true` 在这里是**关键**:用户点选后 value 原样回传,下一轮才被认作"已确认";
7054
+ // agent 自己编的时间不带这个标记,就永远停在选项这一步。
7055
+ value: {
7056
+ begin_datetime: String(slot.value?.begin ?? slot.begin ?? "").trim(),
7057
+ end_datetime: String(slot.value?.end ?? slot.end ?? "").trim(),
7058
+ address: slot.value?.address ?? null,
7059
+ timeConfirmed: true
7060
+ },
7061
+ reason: String(slot.reason ?? ""),
7062
+ rooms: slot.rooms ?? []
7063
+ })),
7064
+ day: day2,
7065
+ durationMinutes: duration,
7066
+ memberIds,
7067
+ // ⛔ 这里**不能**出现 workflowId:流程确认在四要素之后(2026-10-30 顺序调整)。
7068
+ // 返回 workflowId 会让 agent 误以为"流程已定",于是在回复里先讲流程、再问时间。
7069
+ workflowPending: true,
7070
+ hint: "\u9009\u5B9A\u540E\u628A value **\u6574\u4F53**\u539F\u6837\u5E26\u56DE\u91CD\u65B0\u8C03\u7528\uFF08\u542B timeConfirmed: true\uFF09\uFF1Baddress \u5DF2\u81EA\u52A8\u5E26\u4E0A\u8BE5\u65F6\u6BB5\u53EF\u7528\u7684\u4F1A\u8BAE\u5BA4\uFF0C\u4ECD\u9700\u8BA9\u7528\u6237\u5355\u72EC\u786E\u8BA4\u6216\u7528 roomConfirmed: true"
7071
+ }, {
7072
+ warnings: [
7073
+ `${beginHasTime ? `\u672C\u6B21**\u6CA1\u6709\u63D0\u4EA4**\uFF1A\u4F60\u4F20\u5165\u7684\u65F6\u95F4 ${beginText}~${endText} \u6CA1\u6709\u7528\u6237\u786E\u8BA4\u6807\u8BB0\uFF08timeConfirmed\uFF09\uFF0C\u5DF2\u6539\u4E3A\u7ED9\u5019\u9009\u3002` : "\u672C\u6B21**\u6CA1\u6709\u63D0\u4EA4**\uFF1A\u65F6\u95F4\u672A\u786E\u5B9A\uFF08\u7F3A begin_datetime\uFF09\u3002"}\u5DF2\u6309 ${day2} \u63A8\u8350\u65F6\u6BB5\uFF0C\u8BF7\u7528\u9009\u9879\u8BA9\u7528\u6237\u6311\u4E00\u4E2A\u3002`,
7074
+ ...slotWarnings,
7075
+ ...earlyWorkflowNote ? [earlyWorkflowNote] : []
7076
+ ]
7077
+ });
7078
+ }
5842
7079
  let addressInput = args.address;
5843
7080
  const hadAddressInput = String(args.address ?? "").trim() !== "";
5844
7081
  const day = beginText.slice(0, 10);
5845
7082
  const durationMinutes = beginText && endText ? Math.max(15, Math.round((Date.parse(endText.replace(" ", "T")) - Date.parse(beginText.replace(" ", "T"))) / 6e4) || 60) : 60;
5846
7083
  const timings = {};
5847
7084
  const startedAt = Date.now();
7085
+ const enrichFailures = [];
7086
+ const noteFailure = (label) => (error) => {
7087
+ enrichFailures.push(`${label}\u672A\u5B8C\u6210\uFF1A${String(error?.message ?? error).slice(0, 120)}`);
7088
+ return null;
7089
+ };
7090
+ const needSelfPerson = String(args.caller ?? "").trim() === "" || String(args.contacter ?? "").trim() === "";
7091
+ const autoPerson = applySelfPersonDefaults(
7092
+ args,
7093
+ needSelfPerson ? await resolveSelfPerson(env) : null
7094
+ );
7095
+ const autoFilledPerson = autoPerson.filled;
7096
+ const selfPersonWarning = autoPerson.warning;
7097
+ const declaredTotal = Number(args.total_member) > 0 ? Number(args.total_member) : null;
7098
+ const callerToken = String(args.caller ?? "").trim();
7099
+ const callerInMembers = callerToken !== "" && (rawMembers.includes(callerToken) || memberIds.includes(callerToken));
7100
+ const roomMemberCount = declaredTotal ?? (memberIds.length > 0 ? memberIds.length + (callerInMembers ? 0 : 1) : void 0);
5848
7101
  const roomTask = enrich && beginText && endText ? recommendRooms(env, {
5849
7102
  beginDatetime: beginText,
5850
7103
  endDatetime: endText,
5851
- ...memberIds.length > 0 ? { memberCount: memberIds.length } : {},
7104
+ ...roomMemberCount ? { memberCount: roomMemberCount } : {},
7105
+ // 建会是"要下单"的场景:宁可多一次请求也必须拿最新台账(24h 缓存会让刚导入的会议室看不到)。
7106
+ refreshRooms: true,
5852
7107
  topN: 3
5853
- }).catch(() => null) : Promise.resolve(null);
7108
+ }).catch(noteFailure("\u4F1A\u8BAE\u5BA4\u63A8\u8350")) : Promise.resolve(null);
5854
7109
  const addressId = String(addressInput ?? "").trim();
5855
- const addressIsRoomId = /^\d+$/.test(addressId);
5856
- const roomConflictTask = enrich && beginText && endText && addressIsRoomId ? (async () => {
5857
- const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-QKMI43Q6.js");
5858
- return getMeetingOperation2("meeting.eb.conflict.room").handler(
5859
- { beginDatetime: beginText, endDatetime: endText, address: addressId },
7110
+ const roomConfirmed = args.roomConfirmed === true;
7111
+ const roomConflictTask = enrich && beginText && endText && addressId !== "" ? (async () => {
7112
+ const room = await resolveAddressRoom(env, addressId);
7113
+ if (!room) return { unresolved: true, input: addressId };
7114
+ if (!room.id || room.lookupFailed || room.status !== "0") return { room };
7115
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
7116
+ const result = await getMeetingOperation2("meeting.eb.conflict.room").handler(
7117
+ { beginDatetime: beginText, endDatetime: endText, address: room.id },
5860
7118
  env
5861
7119
  );
5862
- })().catch(() => null) : Promise.resolve(null);
7120
+ return { room, ...result?.data ?? {} };
7121
+ })().catch(noteFailure("\u4F1A\u8BAE\u5BA4**\u5360\u7528**\u68C0\u6D4B")) : Promise.resolve(null);
5863
7122
  const conflictTask = enrich && beginText && endText && memberIds.length > 0 ? (async () => {
5864
- const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-QKMI43Q6.js");
7123
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
5865
7124
  return getMeetingOperation2("meeting.eb.conflict.member").handler(
5866
7125
  { beginDatetime: beginText, endDatetime: endText, hrmMembers: memberIds.join(",") },
5867
7126
  env
5868
7127
  );
5869
- })().catch(() => null) : Promise.resolve(null);
7128
+ })().catch(noteFailure("\u53C2\u4F1A\u4EBA**\u4F1A\u8BAE\u5360\u7528**\u68C0\u6D4B")) : Promise.resolve(null);
5870
7129
  const availabilityTask = enrich && beginText && endText && memberIds.length > 0 ? findCommonFreeSlots(env, {
5871
7130
  rangeStart: `${day} 00:00`,
5872
7131
  rangeEnd: `${day} 23:59`,
@@ -5875,7 +7134,7 @@ var ebMeetingCreateFlowPrepare = {
5875
7134
  maxSlots: 3,
5876
7135
  maxDays: 1,
5877
7136
  includeSchedule: true
5878
- }).catch(() => null) : Promise.resolve(null);
7137
+ }).catch(noteFailure("\u53C2\u4F1A\u4EBA**\u65E5\u7A0B\u7A7A\u95F2**\u68C0\u6D4B")) : Promise.resolve(null);
5879
7138
  const [roomRec, memberConflict, free, roomConflict] = await Promise.all([
5880
7139
  roomTask,
5881
7140
  conflictTask,
@@ -5883,17 +7142,84 @@ var ebMeetingCreateFlowPrepare = {
5883
7142
  roomConflictTask
5884
7143
  ]);
5885
7144
  timings.parallelMs = Date.now() - startedAt;
7145
+ const memberConflicts = memberConflict?.data?.conflicts ?? [];
7146
+ if (memberConflicts.length > 0) decisions.memberConflicts = memberConflicts;
7147
+ const freeSlots = free?.slots ?? [];
7148
+ if (free) {
7149
+ decisions.scheduleItems = free.scheduleItems;
7150
+ decisions.availabilityCost = free.cost;
7151
+ if (freeSlots.length > 0) {
7152
+ decisions.suggestedSlots = freeSlots;
7153
+ decisions.slotOptions = freeSlots.map((slot, index) => ({
7154
+ key: `T${index + 1}`,
7155
+ label: `${slot.begin.slice(11)}~${slot.end.slice(11)}\uFF08${day}\uFF09 \xB7 \u5168\u90E8\u53C2\u4F1A\u4EBA\u7A7A\u95F2`,
7156
+ value: { begin: slot.begin, end: slot.end },
7157
+ reason: "\u8BE5\u65F6\u6BB5\u6240\u6709\u53C2\u4F1A\u4EBA\u90FD\u6CA1\u6709\u4F1A\u8BAE\u4E0E\u65E5\u7A0B\u5360\u7528\uFF08\u5DF2\u540C\u65F6\u6838\u5BF9\u4F1A\u8BAE\u56DE\u6267\u4E0E\u65E5\u7A0B\uFF09"
7158
+ }));
7159
+ } else if (free.blockingSummary) {
7160
+ decisions.dayBlocked = { day, summary: free.blockingSummary };
7161
+ }
7162
+ }
7163
+ const conflictBrief = {
7164
+ memberConflicts,
7165
+ suggestedSlots: freeSlots,
7166
+ ...enrichFailures.length > 0 ? { enrichFailures } : {}
7167
+ };
7168
+ const failureWarnings = enrichFailures.map(
7169
+ (failure) => `\u26A0\uFE0F ${failure} \u2014\u2014 **\u8FD9\u4E0D\u4EE3\u8868\u6CA1\u6709\u51B2\u7A81**\uFF0C\u8BF7\u5982\u5B9E\u544A\u77E5\u7528\u6237\u8BE5\u9879\u672A\u6838\u9A8C\uFF0C\u4E0D\u8981\u58F0\u79F0"\u65F6\u95F4\u5DF2\u786E\u8BA4\u7A7A\u95F2"`
7170
+ );
5886
7171
  const roomOptions = (roomRec?.candidates ?? []).map((candidate, index) => ({
5887
7172
  key: `R${index + 1}`,
5888
7173
  label: `${candidate.name}${candidate.capacity.max > 0 ? `\uFF08\u53EF\u5BB9 ${candidate.capacity.max} \u4EBA\uFF09` : ""}${index === 0 ? " \xB7 \u63A8\u8350" : ""}`,
5889
- value: { id: candidate.id, name: candidate.name },
7174
+ // roomConfirmed: true = 用户点选该选项即代表"确认用这间",原样回传后下一轮才被认。
7175
+ value: { id: candidate.id, name: candidate.name, roomConfirmed: true },
5890
7176
  reason: candidate.reasons.join("\uFF1B")
5891
7177
  }));
5892
- const roomConflictData = roomConflict?.data;
5893
- if (addressInput && roomConflictData?.hasConflict === true) {
7178
+ const roomConflictData = roomConflict;
7179
+ const resolvedAddress = roomConflictData?.room ?? null;
7180
+ const resolvedAddressId = resolvedAddress?.id || addressId;
7181
+ const addressDisplay = resolvedAddress?.name || addressId;
7182
+ if (roomConflictData?.unresolved === true) {
7183
+ decisions.roomNotFound = { input: addressId, candidates: roomOptions.length };
7184
+ if (roomOptions.length > 0) decisions.roomOptions = roomOptions;
7185
+ return meetingSuccess("meeting.eb.meeting.create.flow.prepare", {
7186
+ status: "NEEDS_SELECTION",
7187
+ selectionType: "room",
7188
+ ui: { component: "choice", multiple: false, recommendedKey: "R1" },
7189
+ message: `\u6CA1\u627E\u5230\u540D\u4E3A\u300C${addressId}\u300D\u7684\u4F1A\u8BAE\u5BA4\uFF08\u4F1A\u8BAE\u5BA4\u53F0\u8D26\u91CC\u6CA1\u6709\u8FD9\u4E2A\u540D\u5B57\uFF09\uFF0C\u672C\u6B21**\u6CA1\u6709\u63D0\u4EA4**\u3002` + (roomOptions.length > 0 ? `\u8BF7\u4ECE\u4E0B\u9762 ${roomOptions.length} \u4E2A\u5728\u8BE5\u65F6\u6BB5\u7A7A\u95F2\u7684\u4F1A\u8BAE\u5BA4\u91CC\u9009\u4E00\u95F4\uFF0C\u6216\u6838\u5BF9\u4E00\u4E0B\u4F1A\u8BAE\u5BA4\u540D\u79F0` : "\u8BE5\u65F6\u6BB5\u4E5F\u6CA1\u6709\u53EF\u7528\u7684\u7A7A\u95F2\u4F1A\u8BAE\u5BA4\uFF0C\u8BF7\u6539\u4F1A\u8BAE\u65F6\u95F4\u6216\u6539\u7EBF\u4E0A\u4F1A\u8BAE\uFF08allowNoRoom: true\uFF09"),
7190
+ workflowPending: true,
7191
+ options: roomOptions,
7192
+ recommended: roomOptions[0]?.value ?? null,
7193
+ roomNotFound: decisions.roomNotFound,
7194
+ hint: "\u9009\u5B9A\u540E\u628A value **\u6574\u4F53**\u539F\u6837\u5E26\u56DE\uFF08\u542B roomConfirmed: true\uFF09\uFF1B\u6838\u5BF9\u540D\u79F0\u53EF\u7528 `meeting.eb.room.list` \u770B\u5168\u91CF\u6E05\u5355"
7195
+ }, {
7196
+ warnings: [
7197
+ `\u4F1A\u8BAE\u5BA4\u300C${addressId}\u300D\u5728\u53F0\u8D26\u91CC\u4E0D\u5B58\u5728\uFF0C\u5DF2\u62E6\u4E0B\u672A\u63D0\u4EA4\u3002`,
7198
+ '\u26D4 \u4E0D\u8981\u628A\u5B83\u5F53\u6210"\u7528\u6237\u6307\u5B9A\u7684\u623F\u95F4"\u5199\u8FDB\u786E\u8BA4\u6458\u8981 \u2014\u2014 \u8FD9\u95F4\u623F\u6839\u672C\u6CA1\u6709\u88AB\u89E3\u6790\u51FA\u6765\u3002'
7199
+ ]
7200
+ });
7201
+ }
7202
+ if (resolvedAddress && resolvedAddress.status === "1") {
7203
+ decisions.roomSealed = { id: resolvedAddress.id, name: resolvedAddress.name };
7204
+ if (roomOptions.length > 0) decisions.roomOptions = roomOptions;
7205
+ return meetingSuccess("meeting.eb.meeting.create.flow.prepare", {
7206
+ status: "NEEDS_SELECTION",
7207
+ selectionType: "room",
7208
+ ui: { component: "choice", multiple: false, recommendedKey: "R1" },
7209
+ message: `\u4F1A\u8BAE\u5BA4\u300C${resolvedAddress.name}\u300D\u5F53\u524D\u662F**\u5C01\u5B58**\u72B6\u6001\uFF08\u4E0D\u53EF\u9884\u7EA6\uFF09\uFF0C\u672C\u6B21**\u6CA1\u6709\u63D0\u4EA4**\u3002` + (roomOptions.length > 0 ? "\u8BF7\u6539\u7528\u4E0B\u9762\u7A7A\u95F2\u7684\u4F1A\u8BAE\u5BA4" : "\u8BE5\u65F6\u6BB5\u6CA1\u6709\u5176\u5B83\u53EF\u7528\u4F1A\u8BAE\u5BA4\uFF0C\u8BF7\u6539\u4F1A\u8BAE\u65F6\u95F4"),
7210
+ workflowPending: true,
7211
+ options: roomOptions,
7212
+ recommended: roomOptions[0]?.value ?? null,
7213
+ roomSealed: decisions.roomSealed
7214
+ }, {
7215
+ warnings: [`\u4F1A\u8BAE\u5BA4\u300C${resolvedAddress.name}\u300D\uFF08${resolvedAddress.id}\uFF09\u5DF2\u5C01\u5B58\uFF0C\u4E0D\u53EF\u9884\u7EA6\uFF0C\u5DF2\u62E6\u4E0B\u672A\u63D0\u4EA4\u3002`]
7216
+ });
7217
+ }
7218
+ if (addressInput && roomConfirmed && roomConflictData?.hasConflict === true) {
5894
7219
  const busy = Array.isArray(roomConflictData.conflicts) ? roomConflictData.conflicts : [];
5895
7220
  decisions.roomConflict = {
5896
- address: addressId,
7221
+ address: resolvedAddressId,
7222
+ addressName: addressDisplay,
5897
7223
  hasConflict: true,
5898
7224
  conflicts: busy,
5899
7225
  verdict: roomConflictData.verdict ?? "conflict"
@@ -5902,14 +7228,17 @@ var ebMeetingCreateFlowPrepare = {
5902
7228
  return meetingSuccess("meeting.eb.meeting.create.flow.prepare", {
5903
7229
  status: "NEEDS_SELECTION",
5904
7230
  selectionType: "room_conflict",
5905
- message: `\u4F60\u6307\u5B9A\u7684\u4F1A\u8BAE\u5BA4\u5728 ${beginText}~${endText} \u5DF2\u88AB\u5360\u7528\uFF08${busy.length} \u6761\u5360\u7528\u8BB0\u5F55\uFF09\uFF0C\u672C\u6B21**\u6CA1\u6709\u63D0\u4EA4**\u3002\u8BF7\u6539\u7528\u4E0B\u9762\u7A7A\u95F2\u7684\u4F1A\u8BAE\u5BA4\uFF0C\u6216\u8C03\u6574\u4F1A\u8BAE\u65F6\u95F4`,
7231
+ ui: { component: "choice", multiple: false, recommendedKey: "R1" },
7232
+ message: `\u4F60\u6307\u5B9A\u7684\u4F1A\u8BAE\u5BA4\u300C${addressDisplay}\u300D\u5728 ${beginText}~${endText} \u5DF2\u88AB\u5360\u7528\uFF08${busy.length} \u6761\u5360\u7528\u8BB0\u5F55\uFF09\uFF0C\u672C\u6B21**\u6CA1\u6709\u63D0\u4EA4**\u3002\u8BF7\u6539\u7528\u4E0B\u9762\u7A7A\u95F2\u7684\u4F1A\u8BAE\u5BA4\uFF0C\u6216\u8C03\u6574\u4F1A\u8BAE\u65F6\u95F4`,
7233
+ workflowPending: true,
5906
7234
  options: roomOptions,
5907
7235
  recommended: roomOptions[0]?.value ?? null,
5908
7236
  conflicts: busy,
7237
+ roomConflict: decisions.roomConflict,
5909
7238
  hint: "\u9009\u5B9A\u540E\u628A value.id \u4F5C\u4E3A address \u91CD\u65B0\u8C03\u7528\uFF1B\u8981\u6539\u65F6\u95F4\u5C31\u540C\u65F6\u4F20\u65B0\u7684 begin_datetime/end_datetime"
5910
7239
  }, {
5911
7240
  warnings: [
5912
- `\u4F1A\u8BAE\u5BA4 ${addressId} \u5728\u8BE5\u65F6\u6BB5\u5DF2\u6709 ${busy.length} \u6761\u5360\u7528\uFF0C\u5DF2\u62E6\u4E0B\u672A\u63D0\u4EA4\u3002`,
7241
+ `\u4F1A\u8BAE\u5BA4\u300C${addressDisplay}\u300D\uFF08${resolvedAddressId}\uFF09\u5728\u8BE5\u65F6\u6BB5\u5DF2\u6709 ${busy.length} \u6761\u5360\u7528\uFF0C\u5DF2\u62E6\u4E0B\u672A\u63D0\u4EA4\u3002`,
5913
7242
  roomOptions.length > 0 ? `\u5DF2\u6309\u4EBA\u6570/\u5E38\u7528/\u7A7A\u95F2\u7ED9\u51FA ${roomOptions.length} \u4E2A\u66FF\u4EE3\u4F1A\u8BAE\u5BA4\uFF08R1 \u4E3A\u9996\u9009\uFF09\u3002` : "\u8BE5\u65F6\u6BB5\u6CA1\u6709\u53EF\u66FF\u4EE3\u7684\u7A7A\u95F2\u4F1A\u8BAE\u5BA4\uFF0C\u5EFA\u8BAE\u8C03\u6574\u4F1A\u8BAE\u65F6\u95F4\u6216\u6539\u7EBF\u4E0A\u4F1A\u8BAE\uFF08allowNoRoom: true\uFF09\u3002"
5914
7243
  ]
5915
7244
  });
@@ -5922,18 +7251,38 @@ var ebMeetingCreateFlowPrepare = {
5922
7251
  meta: roomRec.meta
5923
7252
  };
5924
7253
  const autoRoom = args.autoRoom === true;
5925
- if (!addressInput && !autoRoom && roomOptions.length > 0 && args.allowNoRoom !== true) {
7254
+ const needRoomPick = !addressInput || !roomConfirmed;
7255
+ let pickOptions = roomOptions;
7256
+ if (addressInput && !roomConfirmed) {
7257
+ const hit = roomOptions.find((item) => String(item.value?.id) === resolvedAddressId);
7258
+ const specified = hit ? { ...hit, label: `${hit.label}\uFF08\u4F60\u6307\u5B9A\u7684\uFF0C\u5F85\u786E\u8BA4\uFF09`, value: { ...hit.value, roomConfirmed: true } } : {
7259
+ key: "R0",
7260
+ label: `${addressDisplay}\uFF08\u4F60\u6307\u5B9A\u7684\uFF0C\u5F85\u786E\u8BA4\uFF09`,
7261
+ value: { ...resolvedAddressId ? { id: resolvedAddressId } : {}, name: addressDisplay, roomConfirmed: true },
7262
+ reason: "\u4F60\u4F20\u5165\u7684\u4F1A\u8BAE\u5BA4\uFF0C\u672A\u63D0\u4EA4\u3001\u672A\u786E\u8BA4"
7263
+ };
7264
+ pickOptions = [specified, ...roomOptions.filter((item) => String(item.value?.id) !== resolvedAddressId)].map((item, index) => ({ ...item, key: `R${index + 1}` }));
7265
+ }
7266
+ if (needRoomPick && !autoRoom && pickOptions.length > 0 && args.allowNoRoom !== true) {
5926
7267
  return meetingSuccess("meeting.eb.meeting.create.flow.prepare", {
5927
7268
  status: "NEEDS_SELECTION",
5928
7269
  selectionType: "room",
5929
- message: `\u672A\u6307\u5B9A\u4F1A\u8BAE\u5BA4\uFF0C\u8BF7\u4ECE ${roomOptions.length} \u4E2A\u5019\u9009\u91CC\u9009\u62E9\uFF08\u5DF2\u6309\u4EBA\u6570/\u5E38\u7528/\u65F6\u6BB5\u5360\u7528\u6392\u5E8F\uFF09`,
5930
- options: roomOptions,
5931
- recommended: roomOptions[0]?.value ?? null,
5932
- hint: "\u9009\u5B9A\u540E\u628A value.id \u4F5C\u4E3A address \u91CD\u65B0\u8C03\u7528\uFF08quick \u6216 prepare\uFF09\uFF1B\u786E\u5B9E\u4E0D\u9700\u8981\u4F1A\u8BAE\u5BA4\u8BF7\u4F20 allowNoRoom: true"
7270
+ ui: { component: "choice", multiple: false, recommendedKey: "R1" },
7271
+ message: addressInput ? `\u4F60\u4F20\u5165\u7684\u4F1A\u8BAE\u5BA4\u300C${addressDisplay}\u300D**\u672A\u7ECF\u7528\u6237\u786E\u8BA4**\uFF08\u6CA1\u5E26 roomConfirmed\uFF09\uFF0C\u5DF2\u628A\u5B83\u548C\u5176\u5B83\u5019\u9009\u4E00\u8D77\u5217\u51FA\u6765 \u2014\u2014 \u26D4 \u5FC5\u987B\u8BA9\u7528\u6237\u5728\u9009\u9879\u91CC\u70B9\u9009\uFF0C\u4E0D\u8981\u76F4\u63A5\u63D0\u4EA4` : `\u672A\u6307\u5B9A\u4F1A\u8BAE\u5BA4\uFF0C\u8BF7\u4ECE ${pickOptions.length} \u4E2A\u5019\u9009\u91CC\u9009\u62E9\uFF08\u5DF2\u6309\u4EBA\u6570/\u5E38\u7528/\u65F6\u6BB5\u5360\u7528\u6392\u5E8F\uFF09`,
7272
+ // 固化自动选的流程要带出来:调用方下一轮带 address 重调时不用再猜用的是哪条流程。
7273
+ workflowPending: true,
7274
+ options: pickOptions,
7275
+ recommended: pickOptions[0]?.value ?? null,
7276
+ hint: "\u9009\u5B9A\u540E\u628A value **\u6574\u4F53**\u539F\u6837\u5E26\u56DE\uFF08\u542B roomConfirmed: true\uFF09\uFF1B\u786E\u5B9E\u4E0D\u9700\u8981\u4F1A\u8BAE\u5BA4\u8BF7\u4F20 allowNoRoom: true",
7277
+ ...conflictBrief
5933
7278
  }, {
5934
7279
  warnings: [
5935
- "\u672A\u6307\u5B9A\u4F1A\u8BAE\u5BA4\uFF1A\u672C\u6B21**\u6CA1\u6709**\u63D0\u4EA4\uFF0C\u4E5F\u6CA1\u6709\u66FF\u7528\u6237\u6311\u623F\u95F4\u3002",
5936
- "\u5982\u9700\u76F4\u63A5\u5EFA\u4F1A\u5E76\u6CBF\u7528\u63A8\u8350\uFF0C\u53EF\u4F20 autoRoom: true\uFF1B\u4E0D\u9700\u8981\u4F1A\u8BAE\u5BA4\u5219\u4F20 allowNoRoom: true\u3002"
7280
+ addressInput ? `\u4F1A\u8BAE\u5BA4\u300C${addressDisplay}\u300D\u672A\u7ECF\u7528\u6237\u786E\u8BA4\uFF1A\u672C\u6B21**\u6CA1\u6709**\u63D0\u4EA4\u3002` : "\u672A\u6307\u5B9A\u4F1A\u8BAE\u5BA4\uFF1A\u672C\u6B21**\u6CA1\u6709**\u63D0\u4EA4\uFF0C\u4E5F\u6CA1\u6709\u66FF\u7528\u6237\u6311\u623F\u95F4\u3002",
7281
+ "\u5982\u9700\u76F4\u63A5\u5EFA\u4F1A\u5E76\u6CBF\u7528\u63A8\u8350\uFF0C\u53EF\u4F20 autoRoom: true\uFF1B\u4E0D\u9700\u8981\u4F1A\u8BAE\u5BA4\u5219\u4F20 allowNoRoom: true\u3002",
7282
+ ...earlyWorkflowNote ? [earlyWorkflowNote] : [],
7283
+ ...memberConflicts.length > 0 ? [`\u26A0\uFE0F \u6CE8\u610F\uFF1A\u53C2\u4F1A\u4EBA\u5728\u8BE5\u65F6\u6BB5\u5DF2\u6709 ${memberConflicts.length} \u573A\u4F1A\u8BAE\u51B2\u7A81 \u2014\u2014 \u9009\u623F\u524D\u5148\u628A\u8FD9\u6761\u544A\u8BC9\u7528\u6237\uFF0C\u522B\u8BA9\u4ED6\u9009\u5B8C\u624D\u53D1\u73B0\u3002`] : [],
7284
+ ...freeSlots.length > 0 ? [`\u5F53\u5929\u5168\u90E8\u53C2\u4F1A\u4EBA\u90FD\u7A7A\u95F2\u7684\u65F6\u6BB5\uFF1A${freeSlots.map((s) => `${s.begin.slice(11)}~${s.end.slice(11)}`).join("\u3001")}`] : [],
7285
+ ...failureWarnings
5937
7286
  ]
5938
7287
  });
5939
7288
  }
@@ -5950,38 +7299,160 @@ var ebMeetingCreateFlowPrepare = {
5950
7299
  return meetingSuccess("meeting.eb.meeting.create.flow.prepare", {
5951
7300
  status: "NEEDS_SELECTION",
5952
7301
  selectionType: "room_unavailable",
7302
+ ui: { component: "choice", multiple: false, recommendedKey: "C2" },
5953
7303
  message: "\u672A\u6307\u5B9A\u4F1A\u8BAE\u5BA4\uFF0C\u4E14\u6309\u4EBA\u6570/\u65F6\u6BB5\u6CA1\u80FD\u63A8\u8350\u51FA\u53EF\u7528\u7684\u7A7A\u95F2\u4F1A\u8BAE\u5BA4\u3002\u8BF7\u8BA9\u7528\u6237\u4ECE\u4E0B\u9762\u4E09\u6761\u91CC\u9009\u4E00\u6761\uFF0C**\u4E0D\u8981**\u76F4\u63A5\u63D0\u4EA4\u4E00\u4E2A\u6CA1\u6709\u5730\u70B9\u7684\u4F1A\u8BAE",
7304
+ workflowPending: true,
5954
7305
  options: [
5955
7306
  { key: "C1", label: "\u6211\u6307\u5B9A\u5177\u4F53\u4F1A\u8BAE\u5BA4\uFF08\u8868\u5355\u91CC\u70B9\u9009\uFF0C\u6216\u544A\u8BC9\u6211\u4F1A\u8BAE\u5BA4\u540D\u79F0/id\uFF09", value: { action: "specify" } },
5956
7307
  { key: "C2", label: "\u6539\u7528\u7EBF\u4E0A\u4F1A\u8BAE\uFF08\u817E\u8BAF\u4F1A\u8BAE/\u89C6\u9891\uFF09", value: { action: "no_room" } },
5957
7308
  { key: "C3", label: '\u5148\u6309"\u5730\u70B9\u5F85\u5B9A"\u63D0\u4EA4', value: { action: "allow_no_room" } }
5958
7309
  ],
5959
- roomUnavailable: decisions.roomUnavailable
7310
+ roomUnavailable: decisions.roomUnavailable,
7311
+ ...conflictBrief
5960
7312
  }, {
5961
7313
  warnings: [
5962
7314
  `\u4F1A\u8BAE\u5BA4\u63A8\u8350\u65E0\u7ED3\u679C\uFF1A${String(decisions.roomUnavailable.reason)}\u3002\u672C\u6B21**\u6CA1\u6709\u63D0\u4EA4**\u3002`,
5963
- "\u786E\u5B9E\u4E0D\u9700\u8981\u4F1A\u8BAE\u5BA4\uFF08\u7EBF\u4E0A\u4F1A\uFF09\u5C31\u5E26 allowNoRoom: true \u91CD\u65B0\u8C03\u7528\uFF1B\u8981\u6307\u5B9A\u623F\u95F4\u5C31\u5E26 address\u3002"
7315
+ "\u786E\u5B9E\u4E0D\u9700\u8981\u4F1A\u8BAE\u5BA4\uFF08\u7EBF\u4E0A\u4F1A\uFF09\u5C31\u5E26 allowNoRoom: true \u91CD\u65B0\u8C03\u7528\uFF1B\u8981\u6307\u5B9A\u623F\u95F4\u5C31\u5E26 address\u3002",
7316
+ ...memberConflicts.length > 0 ? [`\u26A0\uFE0F \u53E6\u5916\uFF1A\u53C2\u4F1A\u4EBA\u5728\u8BE5\u65F6\u6BB5\u5DF2\u6709 ${memberConflicts.length} \u573A\u4F1A\u8BAE\u51B2\u7A81 \u2014\u2014 \u6362\u65F6\u95F4\u53EF\u80FD\u6BD4\u6362\u623F\u95F4\u66F4\u5212\u7B97\u3002`] : [],
7317
+ ...freeSlots.length > 0 ? [`\u5F53\u5929\u5168\u90E8\u53C2\u4F1A\u4EBA\u90FD\u7A7A\u95F2\u7684\u65F6\u6BB5\uFF1A${freeSlots.map((s) => `${s.begin.slice(11)}~${s.end.slice(11)}`).join("\u3001")}`] : [],
7318
+ ...failureWarnings
5964
7319
  ]
5965
7320
  });
5966
7321
  }
5967
- const conflicts = memberConflict?.data?.conflicts ?? [];
5968
- if (conflicts.length > 0) decisions.memberConflicts = conflicts;
5969
- if (free) {
5970
- decisions.scheduleItems = free.scheduleItems;
5971
- decisions.availabilityCost = free.cost;
5972
- if (free.slots.length > 0) {
5973
- decisions.suggestedSlots = free.slots;
5974
- decisions.slotOptions = free.slots.map((slot, index) => ({
5975
- key: `T${index + 1}`,
5976
- label: `${slot.begin.slice(11)}~${slot.end.slice(11)}\uFF08${day}\uFF09 \xB7 \u5168\u90E8\u53C2\u4F1A\u4EBA\u7A7A\u95F2`,
5977
- value: { begin: slot.begin, end: slot.end },
5978
- reason: "\u8BE5\u65F6\u6BB5\u6240\u6709\u53C2\u4F1A\u4EBA\u90FD\u6CA1\u6709\u4F1A\u8BAE\u4E0E\u65E5\u7A0B\u5360\u7528\uFF08\u5DF2\u540C\u65F6\u6838\u5BF9\u4F1A\u8BAE\u56DE\u6267\u4E0E\u65E5\u7A0B\uFF09"
5979
- }));
5980
- } else if (free.blockingSummary) {
5981
- decisions.dayBlocked = { day, summary: free.blockingSummary };
7322
+ if (enrich && addressInput && !roomConfirmed && roomOptions.length === 0 && args.allowNoRoom !== true) {
7323
+ const specified = {
7324
+ key: "R1",
7325
+ label: `${addressDisplay}\uFF08\u4F60\u6307\u5B9A\u7684\uFF0C\u5F85\u786E\u8BA4\uFF09`,
7326
+ value: { ...resolvedAddressId ? { id: resolvedAddressId } : {}, name: addressDisplay, roomConfirmed: true },
7327
+ reason: "\u4F60\u4F20\u5165\u7684\u4F1A\u8BAE\u5BA4\uFF0C\u672A\u63D0\u4EA4\u3001\u672A\u786E\u8BA4"
7328
+ };
7329
+ return meetingSuccess("meeting.eb.meeting.create.flow.prepare", {
7330
+ status: "NEEDS_SELECTION",
7331
+ selectionType: "room",
7332
+ ui: { component: "choice", multiple: false, recommendedKey: "R1" },
7333
+ message: "\u4F60\u4F20\u5165\u7684\u4F1A\u8BAE\u5BA4\u672A\u7ECF\u7528\u6237\u786E\u8BA4\uFF0C\u4E14\u672C\u6B21\u6CA1\u80FD\u63A8\u8350\u51FA\u5176\u5B83\u5019\u9009 \u2014\u2014 \u8BF7\u8BA9\u7528\u6237\u786E\u8BA4\u8FD9\u4E00\u95F4\uFF0C\u6216\u6362\u4E00\u95F4 / \u6539\u65F6\u6BB5",
7334
+ workflowPending: true,
7335
+ options: [specified],
7336
+ recommended: specified.value,
7337
+ hint: "\u7528\u6237\u786E\u8BA4\u7528\u8FD9\u95F4\u5C31\u628A value \u539F\u6837\u5E26\u56DE\uFF08\u542B roomConfirmed: true\uFF09"
7338
+ }, {
7339
+ warnings: ["\u4F1A\u8BAE\u5BA4\u672A\u7ECF\u7528\u6237\u786E\u8BA4\uFF0C\u672C\u6B21**\u6CA1\u6709**\u63D0\u4EA4\u3002", ...failureWarnings]
7340
+ });
7341
+ }
7342
+ const resolved = await resolveWorkflowId(env, args);
7343
+ const confirmedAddressId = resolvedAddressId;
7344
+ const confirmedAddressLabel = addressId === "" ? "" : resolvedAddress?.name || String(roomOptions.find((item) => String(item.value?.id ?? "") === confirmedAddressId)?.value?.name ?? "") || addressId;
7345
+ const confirmedWhen = `${beginText}~${String(endText).slice(11) || endText}`;
7346
+ let candidates = resolved.candidates;
7347
+ workflowId = resolved.workflowId;
7348
+ let sourceLabel = resolved.source === "argument" ? "\u8C03\u7528\u65B9\u4F20\u5165" : resolved.source === "fixed" ? `\u56FA\u5316\u914D\u7F6E\uFF08${resolved.label ?? "\u9ED8\u8BA4"}\uFF09` : "\u5E94\u7528\u8BBE\u7F6E\u914D\u7F6E";
7349
+ let probes = [];
7350
+ let loadedForms = /* @__PURE__ */ new Map();
7351
+ const autoPick = shouldAutoPickFlow(args);
7352
+ if (!workflowId && candidates.length > 1) {
7353
+ let probed = await probeWorkflowCandidates(env, candidates);
7354
+ probes = probed.probes;
7355
+ loadedForms = probed.loadedForms;
7356
+ let launchable = probes.filter((probe) => probe.launchable);
7357
+ if (launchable.length === 0 && resolved.source === "fixed") {
7358
+ const settings = await fetchEbAppSettings(env, "meeting.eb.meeting.create.flow.prepare");
7359
+ const fallbackIds = String(settings.mtNewWf ?? "").split(/[,;,;\s]+/).map((item) => item.trim()).filter((item) => item !== "");
7360
+ if (fallbackIds.length > 0) {
7361
+ probed = await probeWorkflowCandidates(env, fallbackIds);
7362
+ probes = probed.probes;
7363
+ loadedForms = probed.loadedForms;
7364
+ candidates = fallbackIds;
7365
+ sourceLabel = "\u5E94\u7528\u8BBE\u7F6E\u914D\u7F6E";
7366
+ launchable = probes.filter((probe) => probe.launchable);
7367
+ }
7368
+ }
7369
+ if (launchable.length === 0) {
7370
+ throw meetingValidationError(
7371
+ "flow_not_launchable",
7372
+ `\u5F53\u524D\u8D26\u53F7**\u6CA1\u6709\u53D1\u8D77\u5EFA\u4F1A\u6D41\u7A0B\u7684\u6743\u9650**\uFF1A${sourceLabel}\u7684 ${candidates.length} \u4E2A\u5EFA\u4F1A\u6D41\u7A0B\u90FD\u53D1\u8D77\u4E0D\u4E86\uFF08\u65E0\u53D1\u8D77\u6743\u9650\u6216\u6D41\u7A0B\u5DF2\u505C\u7528\uFF09\u2014\u2014${probes.map((probe) => `${probe.workflowId}\uFF08${probe.reason ?? "\u4E0D\u53EF\u53D1\u8D77"}\uFF09`).join("\uFF1B")}\u3002\u8BF7\u5982\u5B9E\u544A\u77E5\u7528\u6237\uFF1A\u9700\u8981\u627E\u7BA1\u7406\u5458\u5F00\u901A\u8BE5\u6D41\u7A0B\u7684\u53D1\u8D77\u6743\u9650\uFF0C\u6216\u8BF7\u6709\u6743\u9650\u7684\u540C\u4E8B\u4EE3\u4E3A\u53D1\u8D77\uFF1B**\u4E0D\u8981**\u6539\u7528\u76F4\u8FDE\u5EFA\u4F1A\u7ED5\u8FC7\u6D41\u7A0B\u7BA1\u63A7\u3002`,
7373
+ { probes }
7374
+ );
7375
+ }
7376
+ if (launchable.length > 1 && !autoPick) {
7377
+ return meetingSuccess("meeting.eb.meeting.create.flow.prepare", {
7378
+ status: "NEEDS_SELECTION",
7379
+ // ⚠️ 以前漏了这个字段(agent 拿到 undefined 无法判断是哪类选择,2026-10-30 真机发现)。
7380
+ selectionType: "flow",
7381
+ // 明确告诉调用方"这是一道选择题,请用选项控件呈现",不要降级成文本列表
7382
+ // (2026-10-30 用户反馈:agent 把会议室候选写成文本 + "需要你指定一间",没给选项卡)。
7383
+ ui: { component: "choice", multiple: false, recommendedKey: "W1" },
7384
+ // 四要素已经确认到这一步了:摘要一起给出去,用户只需点一下流程,就能直接提交。
7385
+ confirmed: {
7386
+ mt_name: String(args.mt_name ?? ""),
7387
+ begin_datetime: beginText,
7388
+ end_datetime: endText,
7389
+ address: confirmedAddressId || null,
7390
+ addressName: confirmedAddressLabel || null,
7391
+ hrm_members: memberIds
7392
+ },
7393
+ message: `\u4F1A\u8BAE\u8981\u7D20\u5DF2\u786E\u8BA4\uFF08${String(args.mt_name ?? "")} \xB7 ${confirmedWhen}${confirmedAddressLabel ? ` \xB7 ${confirmedAddressLabel}` : " \xB7 \u65E0\u4F1A\u8BAE\u5BA4"}\uFF09\uFF1B\u5F53\u524D\u8D26\u53F7\u5BF9 ${sourceLabel}\u7684 ${candidates.length} \u4E2A\u5EFA\u4F1A\u6D41\u7A0B**\u90FD\u6709\u53D1\u8D77\u6743\u9650**\uFF0C\u8BF7\u8BA9\u7528\u6237\u6700\u540E\u9009\u4E00\u4E0B\u300C\u7528\u54EA\u6761\u6D41\u7A0B\u53D1\u8D77\u300D\u2014\u2014 **\u4E0D\u8981\u9ED8\u8BA4\u53D6\u7B2C\u4E00\u4E2A**\uFF08\u5404\u6D41\u7A0B\u8868\u5355\u5B57\u6BB5\u53EF\u80FD\u5B8C\u5168\u4E0D\u540C\uFF0C\u9009\u9519\u4F1A\u6F0F\u5B57\u6BB5\uFF1B2026-10-29 \u771F\u5B9E\u4E8B\u6545\uFF1A\u53D6\u7B2C\u4E00\u4E2A\u5BFC\u81F4\u5B57\u6BB5\u5168\u586B\u4E0D\u8FDB\u53E6\u4E00\u6761\u6D41\u7A0B\uFF0C\u7528\u6237\u4EE5\u4E3A"\u7CFB\u7EDF\u53EA\u652F\u6301\u4E00\u6761\u6D41\u7A0B"\uFF09\u3002\u26A0\uFE0F \u53EA\u6709\u5176\u4E2D 1 \u4E2A\u6709\u6743\u9650\u65F6 CLI \u4F1A\u76F4\u63A5\u53D1\u8D77\uFF0C\u4E0D\u4F1A\u8D70\u5230\u8FD9\u4E00\u6B65`,
7394
+ options: launchable.map((probe, index) => ({
7395
+ key: `W${index + 1}`,
7396
+ label: probe.name ? `${probe.name}\uFF08${probe.workflowId}\uFF09` : probe.workflowId,
7397
+ // ⛔ 把已确认的四要素一起带回去:用户点选流程后**原样回传 value** 就能直接出确认摘要,
7398
+ // 不用再走一遍时间/房间闸门(否则"先定四要素再选流程"会多出一轮无效往返)。
7399
+ value: {
7400
+ workflowId: probe.workflowId,
7401
+ begin_datetime: beginText,
7402
+ end_datetime: endText,
7403
+ timeConfirmed: true,
7404
+ ...confirmedAddressId ? { address: confirmedAddressId, roomConfirmed: true } : {}
7405
+ },
7406
+ reason: "\u5F53\u524D\u8D26\u53F7\u6709\u53D1\u8D77\u6743\u9650",
7407
+ // 把该流程的字段名如实带出来:不同流程字段差异很大(有的有设备需求/涉密项目,有的没有),
7408
+ // 用户要靠这个才能选对(2026-10-29 用户反馈:选错方式后字段全填不进去)。
7409
+ fields: (loadedForms.get(probe.workflowId)?.fields ?? []).map((f) => f.name).filter(Boolean)
7410
+ })),
7411
+ probes
7412
+ }, {
7413
+ warnings: [
7414
+ `${sourceLabel}\uFF1A\u5171 ${candidates.length} \u4E2A\u5EFA\u4F1A\u6D41\u7A0B\uFF0C\u5176\u4E2D ${launchable.length} \u4E2A\u5F53\u524D\u8D26\u53F7\u53EF\u53D1\u8D77\uFF1B**\u5FC5\u987B\u628A\u8FD9\u51E0\u4E2A\u6D41\u7A0B\u505A\u6210\u9009\u9879\u8BA9\u7528\u6237\u6311\u4E00\u4E2A\uFF0C\u7981\u6B62\u81EA\u5DF1\u66FF\u7528\u6237\u9009**\uFF1B\u9009\u5B9A\u540E\u5E26\u5355\u4E2A workflowId \u91CD\u65B0 prepare\u3002`,
7415
+ ...probes.filter((probe) => !probe.launchable).map((probe) => `\u6D41\u7A0B ${probe.workflowId} \u5F53\u524D\u8D26\u53F7\u65E0\u6CD5\u53D1\u8D77\uFF08${probe.reason ?? "\u65E0\u6743\u9650"}\uFF09\uFF0C\u5DF2\u6392\u9664`)
7416
+ ]
7417
+ });
7418
+ }
7419
+ workflowId = launchable[0].workflowId;
7420
+ if (launchable.length === 1 && candidates.length > 1) {
7421
+ const only = launchable[0];
7422
+ flowPickNote = `${sourceLabel}\uFF1A\u5171 ${candidates.length} \u4E2A\u5EFA\u4F1A\u6D41\u7A0B\uFF0C\u5F53\u524D\u8D26\u53F7\u53EA\u6709\u300C${only.name ?? only.workflowId}\u300D\uFF08${only.workflowId}\uFF09\u6709\u53D1\u8D77\u6743\u9650\uFF0C\u5DF2\u76F4\u63A5\u7528\u5B83\u53D1\u8D77`;
7423
+ }
7424
+ if (launchable.length > 1) {
7425
+ const picked2 = launchable[0];
7426
+ const others = launchable.slice(1).map((probe) => probe.name ? `${probe.name}\uFF08${probe.workflowId}\uFF09` : probe.workflowId);
7427
+ flowPickNote = `${sourceLabel}\uFF1A\u5171 ${candidates.length} \u4E2A\u5EFA\u4F1A\u6D41\u7A0B\uFF0C\u5F53\u524D\u8D26\u53F7\u53EF\u53D1\u8D77 ${launchable.length} \u4E2A\uFF0C\u5DF2\u6309\u4F18\u5148\u7EA7\u81EA\u52A8\u9009\u7528\u300C${picked2.name ?? picked2.workflowId}\u300D\uFF08${picked2.workflowId}\uFF09\uFF1B\u5176\u4ED6\u53EF\u7528\u6D41\u7A0B\uFF1A${others.join("\u3001")} \u2014\u2014 \u8981\u7528\u5B83\u4EEC\u8BF7\u663E\u5F0F\u4F20 workflowId\u3002`;
7428
+ }
7429
+ }
7430
+ let form = loadedForms.get(workflowId);
7431
+ if (!form) {
7432
+ try {
7433
+ form = await loadCreateForm(env, workflowId);
7434
+ } catch (error) {
7435
+ throw meetingValidationError(
7436
+ "flow_not_launchable",
7437
+ `\u5F53\u524D\u8D26\u53F7**\u6CA1\u6709\u53D1\u8D77\u5EFA\u4F1A\u6D41\u7A0B\u7684\u6743\u9650**\uFF08\u6216\u6D41\u7A0B\u5DF2\u505C\u7528\uFF09\uFF1A\u5EFA\u4F1A\u6D41\u7A0B ${workflowId} \u7684\u8868\u5355\u8F7D\u5165\u5931\u8D25 \u2014\u2014 ${String(error?.message ?? error).slice(0, 200)}\u3002\u8BF7\u5982\u5B9E\u544A\u77E5\u7528\u6237\uFF1A\u9700\u8981\u627E\u7BA1\u7406\u5458\u5F00\u901A\u8BE5\u6D41\u7A0B\u7684\u53D1\u8D77\u6743\u9650\uFF0C\u6216\u8BF7\u6709\u6743\u9650\u7684\u540C\u4E8B\u4EE3\u4E3A\u53D1\u8D77\uFF1B**\u4E0D\u8981**\u6539\u7528\u76F4\u8FDE\u5EFA\u4F1A\u7ED5\u8FC7\u6D41\u7A0B\u7BA1\u63A7\u3002`,
7438
+ { workflowId }
7439
+ );
5982
7440
  }
5983
7441
  }
7442
+ if (!form.continuation) {
7443
+ throw meetingValidationError("flow_context_missing", "\u5EFA\u4F1A\u6D41\u7A0B\u8868\u5355\u6CA1\u6709\u8FD4\u56DE continuation\uFF0C\u65E0\u6CD5\u7EE7\u7EED\u63D0\u4EA4");
7444
+ }
5984
7445
  decisions.timings = timings;
7446
+ const crmMemberCount = (() => {
7447
+ if (args.crm_member_count !== void 0 && args.crm_member_count !== "") return args.crm_member_count;
7448
+ const raw = args.crm_members;
7449
+ if (raw === void 0 || raw === null || raw === "") return void 0;
7450
+ if (Array.isArray(raw)) return raw.length > 0 ? raw.length : void 0;
7451
+ const text = String(raw).trim();
7452
+ if (!text) return void 0;
7453
+ const count = text.split(/[,;,;]+/).map((item) => item.trim()).filter((item) => item !== "").length;
7454
+ return count > 0 ? count : void 0;
7455
+ })();
5985
7456
  const shaped = await shapeBrowserValues(env, {
5986
7457
  mt_name: args.mt_name,
5987
7458
  begin_datetime: args.begin_datetime,
@@ -5991,15 +7462,118 @@ var ebMeetingCreateFlowPrepare = {
5991
7462
  customize_address: args.customize_address,
5992
7463
  hrm_members: args.hrm_members,
5993
7464
  total_member: args.total_member,
7465
+ crm_members: args.crm_members,
7466
+ ...crmMemberCount === void 0 ? {} : { crm_member_count: crmMemberCount },
5994
7467
  caller: args.caller,
5995
7468
  contacter: args.contacter,
5996
7469
  other_remark: args.other_remark,
5997
7470
  mt_type: args.mt_type,
5998
7471
  ...args.extraFields && typeof args.extraFields === "object" && !Array.isArray(args.extraFields) ? args.extraFields : {}
5999
7472
  });
6000
- const { main, mapped, unmapped } = mapFields(form.fields, shaped.values);
7473
+ const preferredFields = await readFlowFieldMap(env, workflowId);
7474
+ const { main, mapped, unmapped, resolved: resolvedFields } = mapFields(form.fields, shaped.values, preferredFields);
7475
+ await writeFlowFieldMap(env, workflowId, form.workflowName, resolvedFields);
6001
7476
  const warnings = [];
7477
+ if (autoFilledPerson.length > 0) {
7478
+ const labels = autoFilledPerson.map((one) => `\u300C${PERSON_FIELD_LABELS[one.field]}\u300D`).join("");
7479
+ warnings.push(
7480
+ `${labels}\u6CA1\u6709\u4F20\u503C\uFF0C\u5DF2\u6309**\u5F53\u524D\u767B\u5F55\u4EBA**\u8865\u9F50\uFF1A${autoFilledPerson[0].name}\uFF08${autoFilledPerson[0].id}\uFF09\u3002\u8981\u6362\u6210\u522B\u4EBA\uFF08\u4F8B\u5982\u66FF\u4ED6\u4EBA\u5EFA\u4F1A\u3001\u6307\u5B9A\u522B\u7684\u8054\u7CFB\u4EBA\uFF09\u8BF7\u663E\u5F0F\u4F20 caller / contacter\u3002`
7481
+ );
7482
+ } else if (selfPersonWarning) {
7483
+ warnings.push(selfPersonWarning);
7484
+ }
7485
+ if (flowPickNote) warnings.push(flowPickNote);
6002
7486
  for (const note of shaped.notes) warnings.push(note);
7487
+ const crmMentioned = detectCrmMentionInText(args.mt_name, args.mt_desc, args.other_remark);
7488
+ if (crmMentioned && !args.crm_members) {
7489
+ const hasCrmField = form.fields.some((field) => /参会客户/.test(String(field?.label ?? field?.name ?? field?.fieldName ?? "")));
7490
+ warnings.push(
7491
+ `\u4F1A\u8BAE\u540D\u79F0/\u5185\u5BB9\u91CC\u63D0\u5230\u5BA2\u6237\u300C${crmMentioned}\u300D\uFF0C\u4F46\u672C\u6B21\u6CA1\u4F20 crm_members` + (hasCrmField ? `\uFF0C\u8BE5\u6D41\u7A0B\u8868\u5355**\u6709\u300C\u53C2\u4F1A\u5BA2\u6237\u300D\u5B57\u6BB5\uFF0C\u73B0\u5728\u4F1A\u662F\u7A7A\u7684**\u3002\u8BF7\u5148 meeting.eb.crm.search {"keyword":"${crmMentioned}"} \u62FF items[].customerId\uFF0C\u518D\u7528 crm_members:[{id,name}] \u91CD\u65B0\u63D0\u4EA4\uFF08\u5FC5\u987B\u5E26\u5BA2\u6237\u540D\u79F0\uFF09\u3002` : "\uFF08\u8BE5\u6D41\u7A0B\u8868\u5355\u91CC\u6CA1\u6709\u300C\u53C2\u4F1A\u5BA2\u6237\u300D\u5B57\u6BB5\uFF0C\u672C\u6761\u53EF\u5FFD\u7565\uFF09\u3002")
7492
+ );
7493
+ }
7494
+ const crmMembersGiven = Array.isArray(args.crm_members) ? args.crm_members.length > 0 : String(args.crm_members ?? "").trim() !== "";
7495
+ if (crmMembersGiven) {
7496
+ const crmFieldId = String(resolvedFields.crm_members ?? "").trim();
7497
+ const crmField = form.fields.find((field) => String(field?.id ?? "") === crmFieldId);
7498
+ const componentKey = String(crmField?.componentKey ?? "").toLowerCase();
7499
+ if (crmField && componentKey !== "" && componentKey !== "customer" && componentKey !== "customercomponent") {
7500
+ warnings.push(
7501
+ `\u300C\u53C2\u4F1A\u5BA2\u6237\u300D\u5B57\u6BB5\uFF08${crmFieldId}\uFF09\u662F ${crmField.componentKey} \u7EC4\u4EF6\uFF1A\u5BA2\u6237 id \u4E0E\u540D\u79F0\u7167\u5E38\u5199\u8FDB\u6D41\u7A0B\u5355\u636E\uFF0C\u518D\u7531 ESB \u7684\u5B57\u6BB5\u6620\u5C04\u540C\u6B65\u5230\u4F1A\u8BAE\u8868 \u2014\u2014 \u6B63\u5E38\u914D\u7F6E\uFF0C\u4E0D\u9700\u8981\u6539\u8868\u5355\u3002`
7502
+ );
7503
+ }
7504
+ }
7505
+ const providedCrmDetailRows = normalizeCrmDetailRows(args.crm_member_details);
7506
+ const onlyDetailMode = String(args.crm_detail_mode ?? "").trim().toLowerCase() === "only";
7507
+ let crmDetailRows = onlyDetailMode ? providedCrmDetailRows : [];
7508
+ if (crmMembersGiven && !onlyDetailMode) {
7509
+ const detailTarget = resolveCrmDetailTarget(form.fields);
7510
+ if (detailTarget) {
7511
+ const auto = await resolveAutoCrmDetailRows(env, args.crm_members);
7512
+ if (auto.rows.length > 0) {
7513
+ crmDetailRows = auto.rows;
7514
+ if (providedCrmDetailRows.length > 0) {
7515
+ const byName = new Map(providedCrmDetailRows.map((row) => [String(row.xm ?? "").trim(), row]));
7516
+ crmDetailRows = crmDetailRows.map((row) => {
7517
+ const given = byName.get(String(row.xm ?? "").trim());
7518
+ if (!given) return row;
7519
+ const merged = { ...row };
7520
+ for (const [key, value] of Object.entries(given)) {
7521
+ if (String(value ?? "").trim() !== "") merged[key] = String(value);
7522
+ }
7523
+ return merged;
7524
+ });
7525
+ }
7526
+ warnings.push(
7527
+ `\u5DF2\u81EA\u52A8\u628A\u300C${auto.customers.join("\u3001")}\u300D\u7684 ${auto.rows.length} \u4F4D\u8054\u7CFB\u4EBA\u5199\u8FDB\u300C\u53C2\u4F1A\u5BA2\u6237\u660E\u7EC6\u300D\uFF1A${auto.rows.map((row) => `${row.xm}${row.sjhm ? ` ${row.sjhm}` : ""}`).join("\u3001")}\uFF08\u660E\u7EC6\u8868\u672C\u6765\u5C31\u4E00\u4EBA\u4E00\u884C\uFF0C\u4E0D\u9700\u8981\u8BA9\u7528\u6237\u6311\uFF09\u3002\u786E\u5B9E\u53EA\u60F3\u8981\u5176\u4E2D\u51E0\u4F4D\u65F6\uFF0C\u4F20 crm_detail_mode:"only" \u518D\u914D crm_member_details \u624D\u8986\u76D6\u672C\u6B21\u81EA\u52A8\u5E26\u5165\u3002`
7528
+ );
7529
+ } else if (auto.missing.length > 0) {
7530
+ warnings.push(
7531
+ `\u5DF2\u5199\u300C\u53C2\u4F1A\u5BA2\u6237\u300D\uFF0C\u4F46\u6CA1\u80FD\u81EA\u52A8\u5E26\u51FA\u300C${auto.missing.join("\u3001")}\u300D\u7684\u8054\u7CFB\u4EBA\uFF08CRM \u91CC\u67E5\u4E0D\u5230\uFF09\u3002\u8BF7\u628A\u5DF2\u67E5\u5230\u7684\u8054\u7CFB\u4EBA\u76F4\u63A5\u544A\u7ED9\u7528\u6237\u6838\u5BF9\uFF1B\u786E\u5B9E\u6CA1\u6709\u8054\u7CFB\u4EBA\u65F6\u95EE\u4E00\u6B21\u59D3\u540D+\u624B\u673A\u53F7\uFF0C\u518D\u4EE5 crm_member_details:[{"xm":"\u5F20\u4E09","sjhm":"13800000000"}] \u91CD\u65B0 prepare\u3002`
7532
+ );
7533
+ }
7534
+ }
7535
+ }
7536
+ let detailPayload;
7537
+ if (crmDetailRows.length > 0) {
7538
+ const target = resolveCrmDetailTarget(form.fields);
7539
+ if (!target) {
7540
+ warnings.push(
7541
+ `\u4F20\u4E86 crm_member_details\uFF08${crmDetailRows.length} \u884C\uFF09\uFF0C\u4F46\u8BE5\u5EFA\u4F1A\u6D41\u7A0B\u7684**\u53D1\u8D77\u8282\u70B9\u8868\u5355\u91CC\u6CA1\u6709\u300C\u53C2\u4F1A\u5BA2\u6237\u660E\u7EC6\u300D\u660E\u7EC6\u8868**\uFF08\u6216\u5B83\u88AB\u6446\u5728\u9690\u85CF\u884C\u4E0A\uFF09\uFF0C\u660E\u7EC6\u65E0\u6CD5\u5199\u5165\uFF0C\u5DF2\u5FFD\u7565\u8FD9\u4E9B\u884C\uFF1B\u4E3B\u8868\u7684\u300C\u53C2\u4F1A\u5BA2\u6237\u300D\u7167\u5E38\u5199\u5165\u3002`
7542
+ );
7543
+ } else {
7544
+ detailPayload = {
7545
+ [target.subFormId]: crmDetailRows.map((row) => {
7546
+ const line = {};
7547
+ for (const [semantic, fieldId] of Object.entries(target.columns)) {
7548
+ const value = row[semantic];
7549
+ if (value !== void 0 && value !== "") line[fieldId] = value;
7550
+ }
7551
+ return line;
7552
+ })
7553
+ };
7554
+ warnings.push(
7555
+ `\u53C2\u4F1A\u5BA2\u6237\u660E\u7EC6\uFF1A${crmDetailRows.length} \u884C\u5199\u5165\u660E\u7EC6\u8868 ${target.subFormId}\uFF08\u5217\uFF1A${Object.values(target.labels).join("/")}\uFF09`
7556
+ );
7557
+ }
7558
+ }
7559
+ if (args.fieldValues && typeof args.fieldValues === "object" && !Array.isArray(args.fieldValues)) {
7560
+ const knownIds = new Set(form.fields.map((field) => field.id));
7561
+ for (const [fieldId, raw] of Object.entries(args.fieldValues)) {
7562
+ if (raw === void 0 || raw === null || raw === "") continue;
7563
+ if (!knownIds.has(fieldId)) {
7564
+ warnings.push(`fieldValues \u91CC\u7684 datakey\u300C${fieldId}\u300D\u4E0D\u5728\u8BE5\u6D41\u7A0B\u8868\u5355\u91CC\uFF0C\u5DF2\u5FFD\u7565\uFF08\u5148\u7528 prepare \u4E0D\u5E26 fieldValues \u8DD1\u4E00\u6B21\uFF0C\u770B unmapped/\u8868\u5355\u5B57\u6BB5\u6E05\u5355\uFF09`);
7565
+ continue;
7566
+ }
7567
+ main[fieldId] = raw;
7568
+ mapped[`datakey:${fieldId}`] = fieldId;
7569
+ }
7570
+ }
7571
+ if (enrichFailures.length > 0) {
7572
+ decisions.enrichFailures = enrichFailures;
7573
+ for (const failure of enrichFailures) {
7574
+ warnings.push(`\u26A0\uFE0F ${failure} \u2014\u2014 **\u8FD9\u4E0D\u4EE3\u8868\u6CA1\u6709\u51B2\u7A81**\uFF0C\u8BF7\u5982\u5B9E\u544A\u77E5\u7528\u6237\u8BE5\u9879\u672A\u6838\u9A8C\uFF0C\u4E0D\u8981\u58F0\u79F0"\u65F6\u95F4\u5DF2\u786E\u8BA4\u7A7A\u95F2"`);
7575
+ }
7576
+ }
6003
7577
  const picked = decisions.autoRoom?.picked;
6004
7578
  if (picked && !hadAddressInput) {
6005
7579
  warnings.push(
@@ -6007,21 +7581,30 @@ var ebMeetingCreateFlowPrepare = {
6007
7581
  );
6008
7582
  }
6009
7583
  const slotOptions = decisions.slotOptions ?? [];
6010
- if (slotOptions.length > 0) {
7584
+ const memberConflictCount = decisions.memberConflicts?.length ?? 0;
7585
+ if (memberConflictCount > 0 && slotOptions.length > 0) {
7586
+ warnings.push(
7587
+ `\u53C2\u4F1A\u4EBA\u65F6\u95F4\u6709\u51B2\u7A81\uFF08${memberConflictCount} \u6761\u5360\u7528\uFF09\uFF1B\u5F53\u5929\u6240\u6709\u4EBA\u90FD\u7A7A\u7684\u65F6\u6BB5\uFF1A` + slotOptions.map((item) => `[${item.key}] ${item.label}`).join("\uFF1B") + "\u2014\u2014\u8BF7\u7528\u9009\u9879\u65B9\u5F0F\u8BA9\u7528\u6237\u6311\uFF08\u9009\u5B9A\u540E\u91CD\u65B0 prepare \u5E76\u6539\u65F6\u95F4\uFF09\u3002"
7588
+ );
7589
+ } else if (memberConflictCount === 0 && slotOptions.length > 0) {
6011
7590
  warnings.push(
6012
- `\u53C2\u4F1A\u4EBA\u65F6\u95F4\u6709\u51B2\u7A81\uFF1B\u5F53\u5929\u6240\u6709\u4EBA\u90FD\u7A7A\u7684\u65F6\u6BB5\uFF1A${slotOptions.map((item) => `[${item.key}] ${item.label}`).join("\uFF1B")}\u2014\u2014\u8BF7\u7528\u9009\u9879\u65B9\u5F0F\u8BA9\u7528\u6237\u6311\uFF08\u9009\u5B9A\u540E\u91CD\u65B0 prepare \u5E76\u6539\u65F6\u95F4\uFF09\u3002`
7591
+ `\u8BE5\u65F6\u6BB5\u53C2\u4F1A\u4EBA\u6CA1\u6709\u4F1A\u8BAE/\u65E5\u7A0B\u5360\u7528\uFF08\u5DF2\u6838\u5BF9\u4F1A\u8BAE\u56DE\u6267\u4E0E\u65E5\u7A0B\uFF09\u3002\u5F53\u5929\u5176\u4ED6\u5171\u540C\u7A7A\u95F2\u65F6\u6BB5\uFF1A${slotOptions.map((item) => `[${item.key}] ${item.label}`).join("\uFF1B")}`
6013
7592
  );
6014
7593
  } else if (decisions.dayBlocked?.summary) {
6015
7594
  warnings.push(String(decisions.dayBlocked.summary));
6016
7595
  }
6017
- if (unmapped.length > 0) {
7596
+ const unmappedReal = unmapped.filter((name) => name !== "mt_name" && name !== "crm_member_count");
7597
+ if (unmappedReal.length > 0) {
6018
7598
  warnings.push(
6019
- `\u4EE5\u4E0B\u5B57\u6BB5\u6CA1\u6709\u5728\u5EFA\u4F1A\u6D41\u7A0B\u8868\u5355\u91CC\u627E\u5230\u540C\u540D\u5B57\u6BB5\uFF0C\u672A\u81EA\u52A8\u586B\u5199\uFF1A${unmapped.join("\u3001")}\uFF08\u8868\u5355\u73B0\u6709\u5B57\u6BB5\uFF1A${form.fields.map((field) => field.name).join("\u3001")}\uFF09`
7599
+ `\u4EE5\u4E0B\u5B57\u6BB5\u6CA1\u6709\u5728\u5EFA\u4F1A\u6D41\u7A0B\u8868\u5355\u91CC\u627E\u5230\u540C\u540D\u5B57\u6BB5\uFF0C\u672A\u81EA\u52A8\u586B\u5199\uFF1A${unmappedReal.join("\u3001")}\uFF08\u8868\u5355\u73B0\u6709\u5B57\u6BB5\uFF1A${form.fields.map((field) => field.name).join("\u3001")}\uFF09`
6020
7600
  );
6021
7601
  }
6022
7602
  if (!main[form.fields.find((field) => field.name === "\u4F1A\u8BAE\u540D\u79F0")?.id ?? ""] && !mapped.mt_name) {
6023
- warnings.push("\u5EFA\u4F1A\u6D41\u7A0B\u8868\u5355\u91CC\u6CA1\u627E\u5230\u300C\u4F1A\u8BAE\u540D\u79F0\u300D\u5B57\u6BB5\uFF0C\u8BF7\u4EBA\u5DE5\u786E\u8BA4\u6D41\u7A0B\u662F\u5426\u914D\u9519");
7603
+ warnings.push(
7604
+ "\u8BE5\u5EFA\u4F1A\u6D41\u7A0B\u7684\u8868\u5355\u6CA1\u6709\u300C\u4F1A\u8BAE\u540D\u79F0\u300D\u5B57\u6BB5\uFF08\u90E8\u5206\u6D41\u7A0B\u7528\u6D41\u7A0B\u6807\u9898\u4EE3\u66FF\u4F1A\u8BAE\u540D\u79F0\uFF0C\u5C5E\u6B63\u5E38\u914D\u7F6E\uFF0C\u4E0D\u662F\u6D41\u7A0B\u914D\u9519\uFF09\u3002\u5982\u9700\u4F1A\u8BAE\u540D\u79F0\u72EC\u7ACB\u6210\u5B57\u6BB5\uFF0C\u8BF7\u6539\u9009\u5E26\u8BE5\u5B57\u6BB5\u7684\u6D41\u7A0B\uFF08\u5982\u300C\u4F1A\u8BAE\u7533\u8BF7\u6D41\u7A0B\uFF08NEW\uFF09\u300D\uFF09\u3002"
7605
+ );
6024
7606
  }
7607
+ const crmPatchIds = (Array.isArray(shaped.values.crm_members) ? shaped.values.crm_members : []).map((row) => String(row?.id ?? "").trim()).filter((id) => id !== "");
6025
7608
  const session = await env.host.getSession();
6026
7609
  const continuation = await env.continuation.issue(
6027
7610
  {
@@ -6033,10 +7616,18 @@ var ebMeetingCreateFlowPrepare = {
6033
7616
  workflowId,
6034
7617
  requestName: String(args.mt_name),
6035
7618
  mtName: String(args.mt_name),
6036
- data: { main },
6037
- surfaceContinuation: form.continuation
7619
+ data: { main, ...detailPayload ? { details: detailPayload } : {} },
7620
+ surfaceContinuation: form.continuation,
7621
+ // 供 apply 在「会议还没生成」时按申请单信息预生成邀请函(预置函)
7622
+ invitationFields: shaped.readable,
7623
+ // 提交后回显要用「字段名 → 值」,字段清单先存下来(apply 阶段拿不到 createForm 结果)
7624
+ fieldLabels: form.fields.map((field) => ({ id: String(field.id), name: String(field.name) })),
7625
+ // 已确认的「人员 id → 姓名」,供 apply 回显时把裸 id 换成姓名
7626
+ ...autoFilledPerson.length > 0 ? { personLabels: Object.fromEntries(autoFilledPerson.map((one) => [one.id, one.name])) } : {},
7627
+ // 会议记录的客户要靠 apply 补写(表单「参会客户」是 Ebuilder,单据↔会议表不同步)
7628
+ ...crmPatchIds.length > 0 ? { crmPatch: { ids: crmPatchIds, count: crmMemberCount === void 0 ? crmPatchIds.length : crmMemberCount } } : {}
6038
7629
  },
6039
- fingerprint: JSON.stringify({ workflowId, main })
7630
+ fingerprint: JSON.stringify({ workflowId, main, ...detailPayload ? { details: detailPayload } : {} })
6040
7631
  },
6041
7632
  10 * 60 * 1e3
6042
7633
  );
@@ -6048,9 +7639,27 @@ var ebMeetingCreateFlowPrepare = {
6048
7639
  workflowId,
6049
7640
  workflowCandidates: candidates,
6050
7641
  ...probes.length > 0 ? { workflowProbes: probes } : {},
7642
+ ...flowPickNote ? { workflowAutoPicked: { workflowId, source: resolved.source, note: flowPickNote } } : {},
6051
7643
  requestName: String(args.mt_name),
6052
7644
  fieldMapping: mapped,
6053
7645
  formData: { main },
7646
+ // 人话版「即将提交的内容」—— 提交前必须先贴给用户核对(见 SKILL 建会章节)。
7647
+ formPreview: renderFormPreview(
7648
+ form.fields,
7649
+ await decoratePreviewPersonNames(
7650
+ env,
7651
+ main,
7652
+ form.fields,
7653
+ new Map(autoFilledPerson.map((one) => [one.id, one.name]))
7654
+ )
7655
+ ),
7656
+ // 召集人/联系人是不是"替用户默认填的"要说清楚,用户才知道能不能改。
7657
+ ...autoFilledPerson.length > 0 ? {
7658
+ autoFilled: Object.fromEntries(
7659
+ autoFilledPerson.map((one) => [PERSON_FIELD_LABELS[one.field], `${one.name}(${one.id})`])
7660
+ )
7661
+ } : {},
7662
+ ...detailPayload ? { detailRows: { subFormId: Object.keys(detailPayload)[0], count: crmDetailRows.length, rows: crmDetailRows } } : {},
6054
7663
  formFieldCount: form.fields.length,
6055
7664
  ...Object.keys(decisions).length > 0 ? { decisions } : {}
6056
7665
  },
@@ -6061,6 +7670,94 @@ var ebMeetingCreateFlowPrepare = {
6061
7670
  );
6062
7671
  }
6063
7672
  };
7673
+ var CRM_PATCH_SETTLE_MS = [4e3, 6e3];
7674
+ async function patchMeetingCrmMembers(env, meetingId, ids, count, settleMsList = CRM_PATCH_SETTLE_MS) {
7675
+ const list = ids.map((id) => String(id ?? "").trim()).filter((id) => id !== "");
7676
+ if (String(meetingId).trim() === "" || list.length === 0) {
7677
+ return { ok: false, error: "\u6CA1\u6709\u4F1A\u8BAE id \u6216\u5BA2\u6237 id" };
7678
+ }
7679
+ const mainTable = {
7680
+ id: String(meetingId).trim(),
7681
+ operate_type: "UPDATE",
7682
+ crm_members: list.join(",")
7683
+ };
7684
+ const total = Number(count);
7685
+ if (Number.isFinite(total) && total > 0) mainTable.crm_total_member = String(total);
7686
+ const write = async () => {
7687
+ const session = await env.host.getSession();
7688
+ await env.host.request({
7689
+ method: "POST",
7690
+ path: `${EB_BASE}/updateFormData/change_meeting`,
7691
+ query: { id: String(meetingId).trim() },
7692
+ operation: "meeting.eb.meeting.create.flow.apply",
7693
+ body: writeBody(String(session.userId), { mainTable, details: {}, operateType: {} }),
7694
+ acceptBusiness: ebBusinessSuccess
7695
+ });
7696
+ };
7697
+ const readBack = async () => {
7698
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
7699
+ const detail = getMeetingOperation2("meeting.eb.meeting.detail");
7700
+ const read = await detail.handler({ id: String(meetingId).trim() }, env);
7701
+ return String(read?.data?.data?.crm_members ?? "").trim();
7702
+ };
7703
+ let lastError = "";
7704
+ for (const settleMs of settleMsList) {
7705
+ try {
7706
+ await write();
7707
+ } catch (error) {
7708
+ return { ok: false, error: String(error?.message ?? error).slice(0, 160) };
7709
+ }
7710
+ await new Promise((resolve6) => setTimeout(resolve6, settleMs));
7711
+ try {
7712
+ const value = await readBack();
7713
+ if (value !== "") return { ok: true, value };
7714
+ lastError = "\u8865\u5199\u540E\u56DE\u8BFB crm_members \u4ECD\u4E3A\u7A7A\uFF08\u591A\u534A\u88AB\u4F1A\u8BAE\u521D\u59CB\u5316\u7684\u540E\u7EED\u5199\u5165\u8986\u76D6\u4E86\uFF09";
7715
+ } catch (error) {
7716
+ lastError = `\u8865\u5199\u6210\u529F\u4F46\u56DE\u8BFB\u5931\u8D25\uFF1A${String(error?.message ?? error).slice(0, 120)}`;
7717
+ }
7718
+ }
7719
+ return { ok: false, error: lastError };
7720
+ }
7721
+ async function renderPreInvitation(payload, env) {
7722
+ const fields = payload?.invitationFields ?? {};
7723
+ const started = Date.now();
7724
+ try {
7725
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
7726
+ const render = getMeetingOperation2("meeting.invitation.render");
7727
+ const rendered = await render.handler(
7728
+ {
7729
+ mt_name: String(fields.mt_name ?? payload?.mtName ?? "").trim(),
7730
+ begin_datetime: String(fields.begin_datetime ?? "").trim(),
7731
+ end_datetime: String(fields.end_datetime ?? "").trim(),
7732
+ address: String(fields.address ?? fields.customize_address ?? "").trim(),
7733
+ hrm_members: String(fields.hrm_members ?? "").trim(),
7734
+ mt_desc: String(fields.mt_desc ?? "").trim(),
7735
+ org_members: String(fields.org_members ?? "").trim(),
7736
+ caller: String(fields.caller ?? "").trim(),
7737
+ contacter: String(fields.contacter ?? "").trim(),
7738
+ total_member: String(fields.total_member ?? "").trim(),
7739
+ // 客户单位(邀请函开头「尊敬的 XXX」):建会时填了参会客户就直接带过去,
7740
+ // 否则整句永远是【待补充】—— 用户看到的就是"客户明明填了,邀请函里却没有"。
7741
+ extra: { guest_org: String(fields.crm_members ?? "").trim() }
7742
+ },
7743
+ env
7744
+ );
7745
+ return {
7746
+ invitation: { pending: true, preRendered: true, ...rendered?.data ?? {} },
7747
+ ms: Date.now() - started
7748
+ };
7749
+ } catch (error) {
7750
+ return {
7751
+ invitation: {
7752
+ pending: true,
7753
+ preRendered: false,
7754
+ ok: false,
7755
+ error: String(error?.message ?? error).slice(0, 200)
7756
+ },
7757
+ ms: Date.now() - started
7758
+ };
7759
+ }
7760
+ }
6064
7761
  var ebMeetingCreateFlowApply = {
6065
7762
  name: "meeting.eb.meeting.create.flow.apply",
6066
7763
  resource: "eb",
@@ -6074,6 +7771,11 @@ var ebMeetingCreateFlowApply = {
6074
7771
  {
6075
7772
  continuation: { type: "string", description: "prepare \u8FD4\u56DE\u7684\u786E\u8BA4\u7968\u636E\uFF08\u5FC5\u586B\uFF09" },
6076
7773
  confirm: { type: "boolean", description: "\u5FC5\u987B\u663E\u5F0F\u4F20 true" },
7774
+ submit_mode: {
7775
+ type: "string",
7776
+ enum: ["direct", "browser"],
7777
+ description: '\u63D0\u4EA4\u65B9\u5F0F\uFF0C\u9ED8\u8BA4 browser\uFF1A\u5728\u5C0Fe \u91CC**\u5F39\u51FA\u6D41\u7A0B\u8868\u5355**\uFF0C\u5B57\u6BB5\u5DF2\u9884\u586B\uFF0C\u7528\u6237\u6838\u5BF9/\u4FEE\u6539\u540E\u81EA\u5DF1\u70B9\u63D0\u4EA4\u3002direct = \u76F4\u63A5\u5199\u670D\u52A1\u7AEF\uFF08\u4F1A\u8BAE\u5F53\u573A\u751F\u6210\u3001\u9080\u8BF7\u51FD\u7528\u771F\u5B9E\u6570\u636E\uFF0C\u4F46\u6CA1\u6709\u4EBA\u5DE5\u6838\u5BF9\u8868\u5355\u8FD9\u4E00\u6B65\uFF09\uFF1B\u53EA\u6709\u7528\u6237\u660E\u786E\u8BF4"\u4E0D\u7528\u6211\u786E\u8BA4\uFF0C\u76F4\u63A5\u5EFA"\u65F6\u624D\u4F20 direct'
7778
+ },
6077
7779
  waitSeconds: { type: "number", description: "\u63D0\u4EA4\u540E\u7B49\u5F85\u4F1A\u8BAE\u751F\u6210\u7684\u6700\u957F\u79D2\u6570\uFF08\u9ED8\u8BA4 60\uFF0C0 = \u4E0D\u7B49\u5F85\uFF09" }
6078
7780
  },
6079
7781
  { required: ["continuation", "confirm"] }
@@ -6091,9 +7793,9 @@ var ebMeetingCreateFlowApply = {
6091
7793
  throw meetingValidationError("context_mismatch", "\u786E\u8BA4\u4E0A\u4E0B\u6587\u5DF2\u53D8\u5316\uFF08\u79DF\u6237\u6216\u767B\u5F55\u8D26\u53F7\u4E0D\u540C\uFF09\uFF0C\u8BF7\u91CD\u65B0\u6267\u884C prepare");
6092
7794
  }
6093
7795
  const payload = lease?.payload ?? {};
6094
- const { createWorkflowHost } = await import("./host-VQ6JJXGQ.js");
7796
+ const { createWorkflowHost } = await import("./host-7RXEDMAH.js");
6095
7797
  const { createWorkflowContinuationSigner } = await import("./continuation-HZIZRZBX.js");
6096
- const { getWorkflowOperation } = await import("./registry-VOGKFFTR.js");
7798
+ const { getWorkflowOperation } = await import("./registry-SG2PGVCF.js");
6097
7799
  const wfEnv = {
6098
7800
  ...workflowEnv(env),
6099
7801
  host: createWorkflowHost(env.ctx),
@@ -6101,6 +7803,7 @@ var ebMeetingCreateFlowApply = {
6101
7803
  };
6102
7804
  const submitStartedAt = Date.now();
6103
7805
  const submit = getWorkflowOperation("workflow.createRequest");
7806
+ const submitMode = String(args.submit_mode ?? "browser").trim();
6104
7807
  const submitted = await submit.handler(
6105
7808
  {
6106
7809
  // 工作流的入参名是 context(createForm 返回的 continuation 就是它的 context 句柄)
@@ -6108,7 +7811,8 @@ var ebMeetingCreateFlowApply = {
6108
7811
  data: payload.data,
6109
7812
  action: "submit",
6110
7813
  requestName: payload.requestName,
6111
- confirm: true
7814
+ confirm: true,
7815
+ ...submitMode === "browser" ? {} : { forceServerSubmit: true }
6112
7816
  },
6113
7817
  wfEnv
6114
7818
  );
@@ -6134,6 +7838,33 @@ var ebMeetingCreateFlowApply = {
6134
7838
  );
6135
7839
  }
6136
7840
  if (!requestId) {
7841
+ const submitStatus = String(submittedData.status ?? submitted?.status ?? "");
7842
+ if (submitStatus === "BROWSER_FILL") {
7843
+ const pre = await renderPreInvitation(payload, env);
7844
+ return meetingSuccess(
7845
+ "meeting.eb.meeting.create.flow.apply",
7846
+ {
7847
+ status: "BROWSER_FILL",
7848
+ workflowId: payload.workflowId,
7849
+ requestName: payload.requestName,
7850
+ requestId: null,
7851
+ meetingId: null,
7852
+ contextId: submittedData.contextId ?? null,
7853
+ // 宿主要靠这段 <browsertool> 才能把表单拉到浏览器里填,必须原样透出
7854
+ finalMarkdown: submittedData.finalMarkdown ?? null,
7855
+ message: String(submittedData.message ?? ""),
7856
+ invitation: pre.invitation,
7857
+ timings: { submitMs: Date.now() - submitStartedAt, invitationMs: pre.ms }
7858
+ },
7859
+ {
7860
+ warnings: [
7861
+ '\u5F53\u524D\u662F\u5C0Fe \u73AF\u5883\uFF1A\u672C\u6B21**\u6CA1\u6709\u5411\u670D\u52A1\u7AEF\u5199\u5165**\uFF0C\u5EFA\u4F1A\u6D41\u7A0B\u6539\u7531\u6D4F\u89C8\u5668\u9875\u9762\u5B8C\u6210\u586B\u5355\uFF08\u89C1 finalMarkdown\uFF09\u3002\u56E0\u6B64\u8FD8\u6CA1\u6709 requestId / \u4F1A\u8BAE id\uFF0C\u4E5F**\u4E0D\u80FD\u8BF4"\u6D41\u7A0B\u5DF2\u63D0\u4EA4"**\u3002',
7862
+ `\u9080\u8BF7\u51FD\u5DF2\u6309\u7533\u8BF7\u5355\u4FE1\u606F**\u9884\u751F\u6210**\uFF08\u9884\u7F6E\u51FD\uFF0C${String(pre.invitation.filePath ?? "\u89C1 filePath")}\uFF09\uFF1A\u8BF7\u4E0B\u8F7D\u540E\u4EBA\u5DE5\u6838\u5BF9\u3001\u81EA\u884C\u53D1\u9001\uFF1B\u6D41\u7A0B\u5BA1\u6279\u901A\u8FC7\u3001\u4F1A\u8BAE\u751F\u6210\u540E\u53EF\u7528 mtId \u518D\u8DD1\u4E00\u6B21 meeting.invitation.render \u62FF\u5B8C\u6574\u5B57\u6BB5\u7248\u672C\u3002`,
7863
+ '\u26D4 \u4E0D\u8981\u56E0\u4E3A"\u6CA1\u6709 requestId / \u6CA1\u6709\u4F1A\u8BAE id"\u91CD\u590D apply \u2014\u2014 \u586B\u5355\u8981\u5728\u90A3\u4E2A\u6D4F\u89C8\u5668\u9875\u9762\u91CC\u70B9\u63D0\u4EA4\u3002'
7864
+ ]
7865
+ }
7866
+ );
7867
+ }
6137
7868
  const missing = submittedData.missingFields ?? submittedData.requiredMissing ?? submittedData.missing ?? [];
6138
7869
  throw meetingValidationError(
6139
7870
  "flow_submit_incomplete",
@@ -6146,7 +7877,7 @@ var ebMeetingCreateFlowApply = {
6146
7877
  let meetingId = "";
6147
7878
  const timings = { submitMs: Date.now() - submitStartedAt };
6148
7879
  if (waitSeconds > 0) {
6149
- const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-QKMI43Q6.js");
7880
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
6150
7881
  const list = getMeetingOperation2("meeting.eb.meeting.list");
6151
7882
  const deadline = Date.now() + Math.min(waitSeconds, 30) * 1e3;
6152
7883
  const pollStart = Date.now();
@@ -6163,18 +7894,44 @@ var ebMeetingCreateFlowApply = {
6163
7894
  } catch {
6164
7895
  }
6165
7896
  if (waited >= 5e3) break;
6166
- await new Promise((resolve5) => setTimeout(resolve5, 5e3));
7897
+ await new Promise((resolve6) => setTimeout(resolve6, 5e3));
6167
7898
  waited += 5e3;
6168
7899
  }
6169
7900
  timings.pollMs = Date.now() - pollStart;
6170
7901
  }
7902
+ let crmLinked;
7903
+ const crmPatch = payload?.crmPatch;
7904
+ const crmPatchEnabled = process.env.WEAVER_MEETING_CRM_PATCH === "1";
7905
+ if (meetingId && crmPatchEnabled && Array.isArray(crmPatch?.ids) && crmPatch.ids.length > 0) {
7906
+ const patched = await patchMeetingCrmMembers(env, meetingId, crmPatch.ids, crmPatch.count);
7907
+ crmLinked = {
7908
+ ok: patched.ok,
7909
+ requested: crmPatch.ids.length,
7910
+ ...patched.value ? { readBack: patched.value } : {},
7911
+ ...patched.error ? { error: patched.error } : {}
7912
+ };
7913
+ if (patched.ok) {
7914
+ warnings.push(`\u300C\u53C2\u4F1A\u5BA2\u6237\u300D\u5DF2\u8865\u5199\u5230\u4F1A\u8BAE\u8BB0\u5F55\uFF08crm_members=${patched.value}\uFF09\u3002`);
7915
+ } else {
7916
+ warnings.push(
7917
+ `\u300C\u53C2\u4F1A\u5BA2\u6237\u300D\u6CA1\u6709\u56DE\u8BFB\u5230\uFF08${patched.error ?? "\u672A\u77E5\u539F\u56E0"}\uFF09\uFF1A\u5F53\u524D\u8D70\u7684\u662F CLI \u624B\u5DE5\u8865\u5199\uFF08WEAVER_MEETING_CRM_PATCH=1\uFF09\uFF0C\u4E0D\u4F9D\u8D56 ESB \u5B57\u6BB5\u6620\u5C04\u65F6\u624D\u9700\u8981\u5B83\u3002`
7918
+ );
7919
+ }
7920
+ }
6171
7921
  let invitation = { pending: true };
6172
7922
  if (meetingId) {
6173
7923
  const invitationStart = Date.now();
6174
7924
  try {
6175
- const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-QKMI43Q6.js");
7925
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
6176
7926
  const render = getMeetingOperation2("meeting.invitation.render");
6177
- const rendered = await render.handler({ mtId: meetingId }, env);
7927
+ const rendered = await render.handler(
7928
+ {
7929
+ mtId: meetingId,
7930
+ // 同理:会议主表不存客户名,用建会时填的参会客户补「客户单位」
7931
+ extra: { guest_org: String(payload?.invitationFields?.crm_members ?? "").trim() }
7932
+ },
7933
+ env
7934
+ );
6178
7935
  invitation = { pending: false, ...rendered?.data ?? {} };
6179
7936
  timings.invitationMs = Date.now() - invitationStart;
6180
7937
  } catch (error) {
@@ -6185,8 +7942,11 @@ var ebMeetingCreateFlowApply = {
6185
7942
  };
6186
7943
  }
6187
7944
  } else {
7945
+ const pre = await renderPreInvitation(payload, env);
7946
+ invitation = pre.invitation;
7947
+ timings.invitationMs = pre.ms;
6188
7948
  warnings.push(
6189
- `\u6D41\u7A0B\u5DF2\u63D0\u4EA4\uFF08requestId=${requestId || "\u672A\u8FD4\u56DE"}\uFF09\uFF0C\u4F46 ${waitSeconds}s \u5185\u6CA1\u5728\u4F1A\u8BAE\u8868\u91CC\u67E5\u5230\u300C${payload.mtName}\u300D\u2014\u2014\u591A\u6570\u60C5\u51B5\u662F\u8FD8\u5728\u5BA1\u6279\u4E2D\u3002\u5BA1\u6279\u901A\u8FC7\u540E\uFF08\u6216\u540E\u53F0\u5EFA\u4F1A\u5B8C\u6210\u540E\uFF09\u518D\u8DD1\u4E00\u6B21 meeting.invitation.render \u51FA\u9080\u8BF7\u51FD\uFF1B\u672C operation \u4E0D\u91CD\u590D\u63D0\u4EA4\u6D41\u7A0B\u3002`
7949
+ `\u6D41\u7A0B\u5DF2\u63D0\u4EA4\uFF08requestId=${requestId || "\u672A\u8FD4\u56DE"}\uFF09\uFF0C\u4F46 ${waitSeconds}s \u5185\u6CA1\u5728\u4F1A\u8BAE\u8868\u91CC\u67E5\u5230\u300C${payload.mtName}\u300D\u2014\u2014\u591A\u6570\u60C5\u51B5\u662F\u8FD8\u5728\u5BA1\u6279\u4E2D\u3002\u5DF2\u6309\u7533\u8BF7\u5355\u4FE1\u606F**\u9884\u751F\u6210**\u9080\u8BF7\u51FD\uFF08\u5C5E\u9884\u7F6E\u51FD\uFF0C\u8BF7\u4EBA\u5DE5\u6838\u5BF9\u540E\u81EA\u884C\u53D1\u9001\uFF09\uFF1B\u5BA1\u6279\u901A\u8FC7\u3001\u4F1A\u8BAE\u751F\u6210\u540E\u53EF\u7528 mtId \u518D\u8DD1\u4E00\u6B21 meeting.invitation.render \u62FF\u5230\u5B8C\u6574\u5B57\u6BB5\u7248\u672C\u3002\u26D4 \u672C operation \u4E0D\u91CD\u590D\u63D0\u4EA4\u6D41\u7A0B \u2014\u2014 \u4E0D\u8981\u56E0\u4E3A"\u6CA1\u6709\u4F1A\u8BAE id"\u5C31\u91CD\u590D apply\u3002`
6190
7950
  );
6191
7951
  }
6192
7952
  return meetingSuccess(
@@ -6198,6 +7958,19 @@ var ebMeetingCreateFlowApply = {
6198
7958
  requestId,
6199
7959
  requestName: payload.requestName,
6200
7960
  meetingId: meetingId || null,
7961
+ // 参会客户有没有真的写进会议记录(表单侧是 Ebuilder,需 apply 补写;回读确认才算 ok)
7962
+ ...crmLinked ? { crmLinked } : {},
7963
+ // 提交后回显「实际提交了什么」:字段名 → 值(人员/客户显示姓名,不显示 id)。
7964
+ // 用户要的就是"能看见内容",替代走不通的浏览器填单核对。
7965
+ submittedPreview: renderFormPreview(
7966
+ payload?.fieldLabels ?? [],
7967
+ await decoratePreviewPersonNames(
7968
+ env,
7969
+ payload?.data?.main ?? {},
7970
+ payload?.fieldLabels ?? [],
7971
+ new Map(Object.entries(payload?.personLabels ?? {}))
7972
+ )
7973
+ ),
6201
7974
  invitation,
6202
7975
  result: submitted?.data ?? null
6203
7976
  },
@@ -6210,22 +7983,32 @@ var ebMeetingCreateFlowQuick = {
6210
7983
  resource: "eb",
6211
7984
  method: "meeting.eb.meeting.create.flow.quick",
6212
7985
  action: "meeting.eb.meeting.create.flow.quick",
6213
- summary: "\u4E00\u6B65\u6D41\u7A0B\u5EFA\u4F1A\uFF08prepare+apply \u5408\u5E76\uFF09\uFF1A\u5185\u90E8\u5E76\u884C\u505A\u4F1A\u8BAE\u5BA4\u63A8\u8350/\u51B2\u7A81\u68C0\u6D4B/\u7A7A\u95F2\u65F6\u6BB5\uFF0C\u63D0\u4EA4\u540E\u81EA\u52A8\u51FA\u9080\u8BF7\u51FD\uFF1B\u591A\u6D41\u7A0B\u6216\u9700\u9009\u62E9\u65F6\u4E0D\u63D0\u4EA4\uFF0C\u8FD4\u56DE\u9009\u9879",
7986
+ summary: '\u4E00\u6B65\u6D41\u7A0B\u5EFA\u4F1A\uFF08prepare+apply \u5408\u5E76\uFF09\uFF1A\u5185\u90E8\u5E76\u884C\u505A\u4F1A\u8BAE\u5BA4\u63A8\u8350/\u51B2\u7A81\u68C0\u6D4B/\u7A7A\u95F2\u65F6\u6BB5\uFF0C\u9ED8\u8BA4**\u5F39\u6D4F\u89C8\u5668\u8868\u5355**\u7ED9\u7528\u6237\u6838\u5BF9\u63D0\u4EA4\uFF08submit_mode=browser\uFF0C\u5C0Fe \u91CC\u9884\u586B\u4EBA\u5458/\u5BA2\u6237\u540E\u7531\u7528\u6237\u70B9\u63D0\u4EA4\uFF1B\u975E\u5C0Fe \u73AF\u5883\u4F1A\u81EA\u52A8\u5199\u670D\u52A1\u7AEF\uFF09\uFF1B\u4F20 submit_mode=direct \u624D\u662F\u670D\u52A1\u7AEF\u76F4\u63D0\uFF08\u62FF requestId + meetingId\uFF0C\u4F1A\u8BAE\u5F53\u573A\u751F\u6210\u3001\u9080\u8BF7\u51FD\u7528\u771F\u5B9E\u6570\u636E\uFF09\u3002\u591A\u6D41\u7A0B\u6216\u9700\u9009\u62E9\u65F6\u4E0D\u63D0\u4EA4\uFF0C\u8FD4\u56DE\u9009\u9879\u3002\u26A0\uFE0F \u7528\u6237\u63D0\u5230\u5BA2\u6237\u540D\u65F6**\u5FC5\u987B\u5148** meeting.eb.crm.search {"keyword":"X"} \u62FF customerId \u518D\u4F20 crm_members:[{id,name}]\uFF1B\u53EA\u628A\u5BA2\u6237\u540D\u5199\u8FDB\u4F1A\u8BAE\u540D\u79F0/\u5185\u5BB9\u800C\u4E0D\u4F20 crm_members \u4F1A\u88AB\u76F4\u63A5\u62D2\u7EDD\uFF08crm_member_required\uFF09\u3002\u26D4 \u4E0D\u8981\u7528 submit_mode=browser \u2014\u2014 \u5C0Fe \u586B\u5355\u9875\u4E0D\u7ED9\u6D4F\u89C8\u6309\u94AE\u8D4B\u503C\uFF0C\u53C2\u4F1A\u4EBA\u5458/\u53C2\u4F1A\u5BA2\u6237\u4F1A\u6574\u6BB5\u4E22\u5931',
6214
7987
  capability: "eb.meeting",
6215
7988
  risk: "high-risk-write",
6216
7989
  requiresConfirmation: true,
6217
7990
  inputSchema: schemaOf(
6218
7991
  {
6219
7992
  mt_name: { type: "string", description: "\u4F1A\u8BAE\u540D\u79F0\uFF08\u5FC5\u586B\uFF09" },
6220
- begin_datetime: { ...SCHEMA_DATETIME_MINUTE, description: "\u5F00\u59CB\u65F6\u95F4\uFF08\u5FC5\u586B\uFF0Cyyyy-MM-dd HH:mm\uFF09" },
6221
- end_datetime: { ...SCHEMA_DATETIME_MINUTE, description: "\u7ED3\u675F\u65F6\u95F4\uFF08\u5FC5\u586B\uFF0Cyyyy-MM-dd HH:mm\uFF09" },
7993
+ begin_datetime: {
7994
+ ...SCHEMA_DATETIME_MINUTE,
7995
+ description: "\u5F00\u59CB\u65F6\u95F4\uFF08yyyy-MM-dd HH:mm\uFF09\u3002**\u53EF\u4EE5\u4E0D\u4F20**\uFF1A\u4E0D\u4F20\u4F1A\u6309\u53C2\u4F1A\u4EBA\u4E0E\u4F1A\u8BAE\u5BA4\u7A7A\u95F2\u60C5\u51B5\u63A8\u8350 3 \u4E2A\u65F6\u6BB5\u8BA9\u4F60\u9009\uFF1B\u53EA\u4F20\u5F00\u59CB\u65F6\u95F4\u3001\u4E0D\u4F20\u7ED3\u675F\u65F6\u95F4\u65F6\uFF0C\u6309 durationMinutes\uFF08\u9ED8\u8BA4 60 \u5206\u949F\uFF09\u81EA\u52A8\u7B97\u7ED3\u675F\u65F6\u95F4"
7996
+ },
7997
+ end_datetime: {
7998
+ ...SCHEMA_DATETIME_MINUTE,
7999
+ description: "\u7ED3\u675F\u65F6\u95F4\uFF08yyyy-MM-dd HH:mm\uFF09\uFF1B\u4E0D\u4F20\u5219\u6309 durationMinutes \u4ECE\u5F00\u59CB\u65F6\u95F4\u63A8\u7B97\uFF08\u9ED8\u8BA4 60 \u5206\u949F\uFF09"
8000
+ },
6222
8001
  mt_desc: { type: "string", description: "\u4F1A\u8BAE\u5185\u5BB9/\u8BF4\u660E" },
6223
8002
  address: { type: "string", description: "\u4F1A\u8BAE\u5BA4\uFF08id \u6216\u540D\u79F0\uFF1B\u4E0D\u4F20\u5219\u8FD4\u56DE\u5019\u9009\u8BA9\u7528\u6237\u9009\uFF0C\u4E0D\u4F1A\u81EA\u52A8\u51B3\u5B9A\uFF09" },
6224
8003
  customize_address: { type: "string", description: "\u81EA\u5B9A\u4E49\u4F1A\u8BAE\u5730\u70B9" },
6225
8004
  hrm_members: { type: "string", description: "\u53C2\u4F1A\u4EBA\u5458 id\uFF0C\u9017\u53F7\u5206\u9694" },
6226
8005
  total_member: { type: "number", description: "\u53C2\u4F1A\u4EBA\u6570" },
6227
- caller: { type: "string", description: "\u53EC\u96C6\u4EBA" },
6228
- contacter: { type: "string", description: "\u8054\u7CFB\u4EBA" },
8006
+ caller: { type: "string", description: "\u53EC\u96C6\u4EBA\uFF08\u5458\u5DE5 id \u6216\u59D3\u540D\uFF09\u3002\u7F3A\u7701 = \u5F53\u524D\u767B\u5F55\u4EBA\uFF0C\u4E0D\u7528\u4F20\uFF1B\u53EA\u6709\u6307\u5B9A\u522B\u4EBA\u5F53\u53EC\u96C6\u4EBA\u624D\u4F20" },
8007
+ contacter: { type: "string", description: "\u8054\u7CFB\u4EBA\uFF08\u5458\u5DE5 id \u6216\u59D3\u540D\uFF09\u3002\u7F3A\u7701 = \u5F53\u524D\u767B\u5F55\u4EBA\uFF0C\u4E0D\u7528\u4F20\uFF1B\u53EA\u6709\u6307\u5B9A\u522B\u4EBA\u5F53\u8054\u7CFB\u4EBA\u624D\u4F20" },
8008
+ mt_type: {
8009
+ type: "string",
8010
+ description: "\u4F1A\u8BAE\u7C7B\u578B\uFF1A\u53EF\u4F20\u7C7B\u578B\u540D\u79F0\u6216 id\uFF08\u5148\u7528 `meeting.eb.type.list` \u67E5\u6709\u54EA\u4E9B\uFF09\u3002**\u5F88\u591A\u79DF\u6237\u7684\u6D41\u7A0B\u8868\u5355\u628A\u300C\u4F1A\u8BAE\u7C7B\u578B\u300D\u8BBE\u4E3A\u5FC5\u586B**\uFF0C\u4E0D\u4F20\u4F1A\u88AB `workflow.createRequest` \u6253\u56DE\u7F3A\u5B57\u6BB5"
8011
+ },
6229
8012
  other_remark: { type: "string", description: "\u5907\u6CE8" },
6230
8013
  workflowId: {
6231
8014
  oneOf: [{ type: "string" }, { type: "array", items: { type: "string" } }],
@@ -6233,15 +8016,52 @@ var ebMeetingCreateFlowQuick = {
6233
8016
  },
6234
8017
  autoRoom: { type: "boolean", description: "\u9ED8\u8BA4 false\uFF1A\u4E0D\u6307\u5B9A\u4F1A\u8BAE\u5BA4\u65F6\u8FD4\u56DE\u5019\u9009\u8BA9\u7528\u6237\u9009\uFF1B\u4F20 true \u624D\u81EA\u52A8\u91C7\u7528\u63A8\u8350\u4F1A\u8BAE\u5BA4" },
6235
8018
  allowNoRoom: { type: "boolean", description: "\u786E\u5B9E\u4E0D\u9700\u8981\u4F1A\u8BAE\u5BA4\uFF08\u7EBF\u4E0A\u4F1A\u7B49\uFF09\u65F6\u4F20 true" },
8019
+ timeConfirmed: {
8020
+ type: "boolean",
8021
+ description: '\u65F6\u95F4\u7684"\u7528\u6237\u5DF2\u786E\u8BA4"\u6807\u8BB0\uFF1A\u7528\u6237\u539F\u8BDD\u660E\u786E\u7ED9\u4E86\u65F6\u95F4\u3001\u6216\u5728\u65F6\u6BB5\u9009\u9879\u91CC\u70B9\u9009\u540E\u539F\u6837\u56DE\u4F20 value\uFF08\u81EA\u5E26\u6807\u8BB0\uFF09\u65F6\u624D\u4F20 true\uFF1B\u4E0D\u4F20 = \u672A\u786E\u8BA4\uFF0CCLI \u4F1A\u8FD4\u56DE\u65F6\u6BB5\u9009\u9879\u8BA9\u7528\u6237\u70B9\u9009'
8022
+ },
8023
+ roomConfirmed: {
8024
+ type: "boolean",
8025
+ description: '\u4F1A\u8BAE\u5BA4\u7684"\u7528\u6237\u5DF2\u786E\u8BA4"\u6807\u8BB0\uFF1A\u7528\u6237\u539F\u8BDD\u660E\u786E\u6307\u5B9A\u3001\u6216\u5728\u623F\u95F4\u9009\u9879\u91CC\u70B9\u9009\u540E\u539F\u6837\u56DE\u4F20 value\uFF08\u81EA\u5E26\u6807\u8BB0\uFF09\u65F6\u624D\u4F20 true\uFF1B\u4E0D\u4F20 = \u672A\u786E\u8BA4\uFF0CCLI \u4F1A\u628A\u6307\u5B9A\u623F\u95F4\u548C\u5019\u9009\u4E00\u8D77\u5217\u51FA\u6765\u8BA9\u7528\u6237\u70B9\u9009'
8026
+ },
6236
8027
  confirm: { type: "boolean", description: "\u5FC5\u987B\u663E\u5F0F true \u624D\u4F1A\u771F\u6B63\u63D0\u4EA4" },
6237
8028
  waitSeconds: { type: "number", description: "\u63D0\u4EA4\u540E\u7B49\u5F85\u4F1A\u8BAE\u751F\u6210\u7684\u79D2\u6570\uFF08\u9ED8\u8BA4 20\uFF1B\u7B97\u6CD5\u662F\u7ACB\u5373\u67E5\u4E00\u6B21 + 5s \u518D\u67E5\u4E00\u6B21\uFF0C\u4ECD\u672A\u751F\u6210\u5C31\u8FD4\u56DE pending\uFF09" },
6238
8029
  enrich: { type: "boolean", description: "\u662F\u5426\u987A\u5E26\u505A\u4F1A\u8BAE\u5BA4\u63A8\u8350/\u51B2\u7A81\u68C0\u6D4B/\u7A7A\u95F2\u65F6\u6BB5\uFF08\u9ED8\u8BA4 true\uFF1B\u4EBA\u3001\u65F6\u95F4\u3001\u623F\u95F4\u90FD\u5DF2\u786E\u5B9A\u65F6\u4F20 false \u53EF\u663E\u8457\u52A0\u5FEB\uFF09" },
6239
8030
  extraFields: {
6240
8031
  type: "object",
6241
8032
  description: '\u6309\u6D41\u7A0B\u8868\u5355\u5B57\u6BB5\u540D\u586B\u989D\u5916\u5B57\u6BB5\uFF08\u79DF\u6237\u81EA\u5EFA\u5B57\u6BB5\u7528\u8FD9\u4E2A\u53E3\u5B50\u5E26\u8FDB\u53BB\uFF09\uFF0C\u5F62\u5982 {"\u53C2\u4F1A\u5BA2\u6237": "xx\u516C\u53F8"}'
8033
+ },
8034
+ dateFrom: {
8035
+ type: "string",
8036
+ description: "\u6CA1\u7ED9\u5177\u4F53\u65F6\u95F4\u65F6\u6309\u54EA\u4E00\u5929\u63A8\u8350\u65F6\u6BB5\uFF08yyyy-MM-dd\uFF1B\u4E0D\u4F20\u9ED8\u8BA4\u660E\u5929\uFF09"
8037
+ },
8038
+ durationMinutes: {
8039
+ type: "number",
8040
+ description: "\u4F1A\u8BAE\u65F6\u957F\uFF08\u5206\u949F\uFF0C\u9ED8\u8BA4 60\uFF09\uFF1A\u53EA\u7ED9\u4E86\u5F00\u59CB\u65F6\u95F4\u65F6\u7528\u6765\u7B97\u7ED3\u675F\u65F6\u95F4\uFF1B\u63A8\u8350\u65F6\u6BB5\u65F6\u4E5F\u6309\u8FD9\u4E2A\u65F6\u957F\u627E"
8041
+ },
8042
+ workStartHour: { type: "number", description: "\u63A8\u8350\u65F6\u6BB5\u7684\u6700\u65E9\u5C0F\u65F6\uFF08\u9ED8\u8BA4 9\uFF09" },
8043
+ workEndHour: { type: "number", description: "\u63A8\u8350\u65F6\u6BB5\u7684\u6700\u665A\u5C0F\u65F6\uFF08\u9ED8\u8BA4 18\uFF09" },
8044
+ fieldValues: {
8045
+ type: "object",
8046
+ additionalProperties: true,
8047
+ description: '\u6309**\u8868\u5355\u5B57\u6BB5 datakey\uFF08fieldId\uFF09**\u76F4\u63A5\u7ED9\u503C\uFF0C\u5F62\u5982 {"1325677252711735881": "\u7814\u8BA8\u4F1A"}\uFF1B\u4F18\u5148\u7EA7\u6700\u9AD8\uFF0C\u9002\u5408\u5B57\u6BB5\u540D\u5E26\u63D0\u793A\u8BED\u6216\u79DF\u6237\u81EA\u5EFA\u5B57\u6BB5\u3002datakey \u89C1 prepare \u7684 preview.mapped'
8048
+ },
8049
+ flowChoice: {
8050
+ type: "string",
8051
+ enum: ["auto", "ask"],
8052
+ description: "\u591A\u4E2A\u5EFA\u4F1A\u6D41\u7A0B**\u90FD\u6709\u53D1\u8D77\u6743\u9650**\u65F6\uFF1A\u9ED8\u8BA4\uFF08\u4E0D\u4F20 / ask\uFF09= \u8FD4\u56DE\u9009\u9879\u8BA9\u7528\u6237\u6311\uFF1Bauto = \u81EA\u52A8\u53D6\u4F18\u5148\u7EA7\u6700\u9AD8\u7684\u3002\u53EA\u6709 1 \u4E2A\u6709\u6743\u9650\u65F6\u76F4\u63A5\u53D1\u8D77"
8053
+ },
8054
+ crm_skip: {
8055
+ type: "boolean",
8056
+ description: "\u663E\u5F0F\u58F0\u660E\u300C\u8FD9\u6B21\u4E0D\u586B\u53C2\u4F1A\u5BA2\u6237\u300D\u3002\u4F1A\u8BAE\u540D\u79F0/\u5185\u5BB9\u91CC\u51FA\u73B0 `\u5BA2\u6237\uFF1AX`\u3001`\u5BA2\u6237\u662FX` \u8FD9\u7C7B\u5B57\u6837\u5374**\u6CA1\u4F20 `crm_members`**\u65F6\uFF0C\u4E00\u6B65\u5EFA\u4F1A\u4F1A\u76F4\u63A5\u62D2\u7EDD\uFF08crm_member_required\uFF09\u2014\u2014\u786E\u8BA4\u8FD9\u4E9B\u5B57\u6837\u4E0D\u662F\u8981\u4E0A\u4F1A\u8868\u7684\u5BA2\u6237\uFF0C\u624D\u4F20 true"
8057
+ },
8058
+ submit_mode: {
8059
+ type: "string",
8060
+ enum: ["direct", "browser"],
8061
+ description: '\u63D0\u4EA4\u65B9\u5F0F\uFF0C\u9ED8\u8BA4 browser\uFF1A\u5728\u5C0Fe \u91CC**\u5F39\u51FA\u6D41\u7A0B\u8868\u5355**\uFF08\u5B57\u6BB5\u5DF2\u9884\u586B\u542B\u4EBA\u5458/\u5BA2\u6237\uFF09\uFF0C\u7528\u6237\u6838\u5BF9\u4FEE\u6539\u540E\u81EA\u5DF1\u70B9\u63D0\u4EA4\u3002direct = \u76F4\u63A5\u5199\u670D\u52A1\u7AEF\uFF08\u4F1A\u8BAE\u5F53\u573A\u751F\u6210\u3001\u9080\u8BF7\u51FD\u7528\u771F\u5B9E\u6570\u636E\uFF0C\u4F46\u6CA1\u6709\u4EBA\u5DE5\u6838\u5BF9\u8868\u5355\u8FD9\u4E00\u6B65\uFF09\uFF1B\u53EA\u6709\u7528\u6237\u660E\u786E\u8BF4"\u4E0D\u7528\u6211\u786E\u8BA4\uFF0C\u76F4\u63A5\u5EFA"\u65F6\u624D\u4F20 direct'
6242
8062
  }
6243
8063
  },
6244
- { required: ["mt_name", "begin_datetime", "end_datetime", "confirm"] }
8064
+ { required: ["mt_name", "confirm"] }
6245
8065
  ),
6246
8066
  handler: async (args, env) => {
6247
8067
  if (args.confirm !== true) {
@@ -6250,10 +8070,20 @@ var ebMeetingCreateFlowQuick = {
6250
8070
  "\u4E00\u6B65\u5EFA\u4F1A\u4F1A\u76F4\u63A5\u63D0\u4EA4\u6D41\u7A0B\uFF0C\u5FC5\u987B\u663E\u5F0F\u4F20 confirm=true\uFF1B\u5982\u53EA\u60F3\u770B\u65B9\u6848\uFF0C\u8BF7\u7528 meeting.eb.meeting.create.flow.prepare"
6251
8071
  );
6252
8072
  }
6253
- const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-QKMI43Q6.js");
8073
+ const crmMentioned = detectCrmMentionInText(args.mt_name, args.mt_desc, args.other_remark);
8074
+ const crmSkipped = args.crm_skip === true;
8075
+ if (crmMentioned && !args.crm_members && !crmSkipped) {
8076
+ throw meetingValidationError(
8077
+ "crm_member_required",
8078
+ `\u4F1A\u8BAE\u540D\u79F0/\u5185\u5BB9\u91CC\u63D0\u5230\u5BA2\u6237\u300C${crmMentioned}\u300D\uFF0C\u4F46\u6CA1\u4F20 crm_members \u2014\u2014 \u63D0\u4EA4\u540E\u300C\u53C2\u4F1A\u5BA2\u6237\u300D\u4F1A\u662F\u7A7A\u7684\u3002\u5148 meeting.eb.crm.search {"keyword":"${crmMentioned}"} \u62FF items[].customerId\uFF0C\u518D\u628A crm_members:[{"id":"<customerId>","name":"<\u5B8C\u6574\u5BA2\u6237\u540D>"}] \u4E00\u8D77\u4F20\uFF08\u5FC5\u987B\u5E26\u540D\u79F0\uFF0C\u53EA\u7ED9 id \u4F1A\u88AB\u9759\u9ED8\u5B58\u7A7A\uFF09\uFF1B\u786E\u5B9E\u4E0D\u9700\u8981\u586B\u53C2\u4F1A\u5BA2\u6237\u5C31\u4F20 crm_skip:true\u3002`
8079
+ );
8080
+ }
8081
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
6254
8082
  const prepareStartedAt = Date.now();
8083
+ const { submit_mode: _submitMode, confirm: _confirm, ...prepareArgs } = args;
6255
8084
  const prepared = await getMeetingOperation2("meeting.eb.meeting.create.flow.prepare").handler(
6256
- { ...args, confirm: void 0 },
8085
+ // submit_mode 只属于提交阶段,prepare 的 schema 不接受它(additionalProperties:false)
8086
+ prepareArgs,
6257
8087
  env
6258
8088
  );
6259
8089
  if (prepared?.data?.status === "NEEDS_SELECTION") {
@@ -6269,6 +8099,7 @@ var ebMeetingCreateFlowQuick = {
6269
8099
  {
6270
8100
  continuation,
6271
8101
  confirm: true,
8102
+ ...args.submit_mode ? { submit_mode: args.submit_mode } : {},
6272
8103
  ...args.waitSeconds !== void 0 ? { waitSeconds: args.waitSeconds } : {}
6273
8104
  },
6274
8105
  env
@@ -6285,6 +8116,12 @@ var ebMeetingCreateFlowQuick = {
6285
8116
  requestId: applied?.data?.requestId,
6286
8117
  meetingId: applied?.data?.meetingId ?? null,
6287
8118
  invitation: applied?.data?.invitation ?? null,
8119
+ // 已提交内容回显(字段名 → 值),提交后可直接贴给用户看"到底填进去了什么"
8120
+ submittedPreview: applied?.data?.submittedPreview ?? null,
8121
+ // 小e 环境(BROWSER_FILL)不写服务端,表单靠这段 <browsertool> 在浏览器里填 —— 必须一起透出,
8122
+ // 否则宿主拿不到表单,用户看到的就是"提交了但没反应"。
8123
+ ...applied?.data?.contextId ? { contextId: (applied?.data).contextId } : {},
8124
+ ...applied?.data?.finalMarkdown ? { finalMarkdown: (applied?.data).finalMarkdown } : {},
6288
8125
  decisions: prepared?.data?.preview?.decisions ?? null,
6289
8126
  timings: {
6290
8127
  ...prepared?.data?.preview?.decisions?.timings ?? {},
@@ -6299,7 +8136,123 @@ var ebMeetingCreateFlowQuick = {
6299
8136
  );
6300
8137
  }
6301
8138
  };
8139
+ var ebMeetingCreateSuggest = {
8140
+ name: "meeting.eb.meeting.create.suggest",
8141
+ resource: "eb",
8142
+ method: "meeting.eb.meeting.create.suggest",
8143
+ action: "meeting.eb.meeting.create.suggest",
8144
+ summary: "\u5EFA\u4F1A\u5EFA\u8BAE\uFF1A\u7ED9\u5B9A\u65E5\u671F\u8303\u56F4\u4E0E\u53C2\u4F1A\u4EBA\uFF0C\u63A8\u8350\u300C\u6240\u6709\u4EBA\u90FD\u7A7A\u95F2 + \u8BE5\u65F6\u6BB5\u6709\u53EF\u7528\u4F1A\u8BAE\u5BA4\u300D\u7684\u65F6\u95F4\u5019\u9009\uFF08\u53EA\u8BFB\uFF0C\u4E0D\u5EFA\u4F1A\uFF09",
8145
+ capability: "eb.meeting",
8146
+ risk: "read",
8147
+ inputSchema: schemaOf(
8148
+ {
8149
+ dateFrom: { type: "string", description: "\u8D77\u59CB\u65E5\u671F\uFF08\u5FC5\u586B\uFF0Cyyyy-MM-dd\uFF09" },
8150
+ dateTo: { type: "string", description: "\u7ED3\u675F\u65E5\u671F\uFF08\u53EF\u9009\uFF0C\u9ED8\u8BA4\u540C dateFrom\uFF1B\u6700\u591A\u8DE8 14 \u5929\uFF09" },
8151
+ durationMinutes: { type: "number", description: "\u4F1A\u8BAE\u65F6\u957F\uFF08\u5206\u949F\uFF0C\u9ED8\u8BA4 60\uFF09" },
8152
+ hrm_members: { type: "string", description: "\u53C2\u4F1A\u4EBA\u5458\uFF08\u5FC5\u586B\uFF0C\u5458\u5DE5 id \u6216\u59D3\u540D\uFF0C\u9017\u53F7\u5206\u9694\uFF1B\u59D3\u540D\u4F1A\u81EA\u52A8\u53CD\u67E5 id\uFF09" },
8153
+ memberCount: { type: "number", description: "\u53C2\u4F1A\u4EBA\u6570\uFF08\u53EF\u9009\uFF0C\u7528\u4E8E\u4F1A\u8BAE\u5BA4\u5BB9\u91CF\u7B5B\u9009\uFF1B\u4E0D\u4F20\u5219\u6309 hrm_members \u4E2A\u6570\uFF09" },
8154
+ workStartHour: { type: "number", description: "\u53EA\u5728\u51E0\u70B9\u4EE5\u540E\u627E\uFF08\u9ED8\u8BA4 9\uFF09" },
8155
+ workEndHour: { type: "number", description: "\u53EA\u5728\u51E0\u70B9\u4EE5\u524D\u627E\uFF08\u9ED8\u8BA4 18\uFF09" },
8156
+ maxSlots: { type: "number", description: "\u6700\u591A\u7ED9\u51E0\u4E2A\u65F6\u95F4\u5019\u9009\uFF08\u9ED8\u8BA4 3\uFF0C\u6700\u5927 8\uFF09" },
8157
+ topN: { type: "number", description: "\u6BCF\u4E2A\u65F6\u95F4\u5019\u9009\u6700\u591A\u5E26\u51E0\u95F4\u4F1A\u8BAE\u5BA4\uFF08\u9ED8\u8BA4 3\uFF09" },
8158
+ includeSchedule: { type: "boolean", description: "\u662F\u5426\u628A\u65E5\u7A0B\uFF08ESB\uFF09\u4E5F\u7B97\u8FDB\u5360\u7528\uFF08\u9ED8\u8BA4 true\uFF09" }
8159
+ },
8160
+ { required: ["dateFrom", "hrm_members"] }
8161
+ ),
8162
+ handler: async (args, env) => {
8163
+ const dateFrom = String(args.dateFrom ?? "").trim().slice(0, 10);
8164
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(dateFrom)) {
8165
+ throw meetingValidationError("datetime_invalid", `dateFrom \u5FC5\u987B\u662F yyyy-MM-dd\uFF0C\u6536\u5230\u300C${String(args.dateFrom ?? "")}\u300D`);
8166
+ }
8167
+ const dateTo = String(args.dateTo ?? "").trim().slice(0, 10) || dateFrom;
8168
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(dateTo)) {
8169
+ throw meetingValidationError("datetime_invalid", `dateTo \u5FC5\u987B\u662F yyyy-MM-dd\uFF0C\u6536\u5230\u300C${String(args.dateTo ?? "")}\u300D`);
8170
+ }
8171
+ const durationMinutes = Number(args.durationMinutes) > 0 ? Number(args.durationMinutes) : 60;
8172
+ const maxSlots = Math.max(1, Math.min(8, Number(args.maxSlots) || 3));
8173
+ const topN = Math.max(1, Math.min(5, Number(args.topN) || 3));
8174
+ const rawMembers = String(args.hrm_members ?? "").split(/[,;,;\s]+/).map((item) => item.trim()).filter((item) => item !== "");
8175
+ const nameLike = rawMembers.filter((item) => !/^\d+$/.test(item));
8176
+ const nameToId = nameLike.length > 0 ? await import("./schedule-NIJBTYDO.js").then((m) => m.resolveMemberIdsByName(env, nameLike)).catch(() => /* @__PURE__ */ new Map()) : /* @__PURE__ */ new Map();
8177
+ const memberIds = rawMembers.map((item) => nameToId.get(item) ?? item);
8178
+ const unresolved = rawMembers.filter((item) => nameToId.get(item) === void 0 && !/^\d+$/.test(item));
8179
+ const selfId = await env.host.getSession().then((s) => String(s?.userId ?? "")).catch(() => "");
8180
+ const selfIncluded = selfId !== "" && memberIds.includes(selfId);
8181
+ const memberCount = Number(args.memberCount) > 0 ? Number(args.memberCount) : memberIds.length > 0 ? memberIds.length + (selfIncluded ? 0 : 1) : void 0;
8182
+ const warnings = [];
8183
+ if (unresolved.length > 0) {
8184
+ warnings.push(`\u26A0\uFE0F \u8FD9\u4E9B\u53C2\u4F1A\u4EBA\u6CA1\u67E5\u5230\u5458\u5DE5 id\uFF1A${unresolved.join("\u3001")} \u2014\u2014 \u5DF2\u8DF3\u8FC7\uFF0C\u5176\u7A7A\u95F2\u60C5\u51B5**\u672A\u6838\u9A8C**`);
8185
+ }
8186
+ const free = await findCommonFreeSlots(env, {
8187
+ rangeStart: `${dateFrom} 00:00`,
8188
+ rangeEnd: `${dateTo} 23:59`,
8189
+ durationMinutes,
8190
+ members: memberIds,
8191
+ maxSlots,
8192
+ maxDays: 14,
8193
+ includeSchedule: args.includeSchedule !== false,
8194
+ ...args.workStartHour !== void 0 ? { workStartHour: Number(args.workStartHour) } : {},
8195
+ ...args.workEndHour !== void 0 ? { workEndHour: Number(args.workEndHour) } : {}
8196
+ });
8197
+ await loadRooms(env, { refresh: true }).catch(() => null);
8198
+ const withRooms = await Promise.all((free.slots ?? []).map(async (slot) => {
8199
+ try {
8200
+ const rec = await recommendRooms(env, {
8201
+ beginDatetime: slot.begin,
8202
+ endDatetime: slot.end,
8203
+ ...memberCount ? { memberCount } : {},
8204
+ topN
8205
+ });
8206
+ return {
8207
+ begin: slot.begin,
8208
+ end: slot.end,
8209
+ rooms: rec.candidates.map((candidate) => ({
8210
+ id: candidate.id,
8211
+ name: candidate.name,
8212
+ capacityMax: candidate.capacity.max,
8213
+ reasons: candidate.reasons
8214
+ }))
8215
+ };
8216
+ } catch {
8217
+ return { begin: slot.begin, end: slot.end, rooms: [] };
8218
+ }
8219
+ }));
8220
+ const usable = withRooms.filter((slot) => slot.rooms.length > 0);
8221
+ if (usable.length === 0) {
8222
+ const why = (free.slots ?? []).length === 0 ? "\u8BE5\u8303\u56F4\u5185\u6240\u6709\u53C2\u4F1A\u4EBA\u6CA1\u6709\u5171\u540C\u7684\u7A7A\u95F2\u65F6\u6BB5" : `${(free.slots ?? []).length} \u4E2A\u300C\u4EBA\u7A7A\u95F2\u300D\u7684\u65F6\u6BB5\u91CC\uFF0C\u6CA1\u6709\u4E00\u4E2A\u8FD8\u80FD\u8BA2\u5230\u4F1A\u8BAE\u5BA4`;
8223
+ warnings.push(
8224
+ `\u6CA1\u627E\u5230\u540C\u65F6\u6EE1\u8DB3\u300C\u4EBA\u5458\u7A7A\u95F2 + \u4F1A\u8BAE\u5BA4\u7A7A\u95F2\u300D\u7684\u65F6\u95F4\u5019\u9009\uFF1A${why}\u3002\u8BF7\u5982\u5B9E\u544A\u77E5\u7528\u6237\uFF0C\u5E76\u5EFA\u8BAE\u6362\u4E2A\u65E5\u671F\u8303\u56F4\u3001\u7F29\u77ED\u65F6\u957F\uFF0C\u6216\u6539\u4E3A\u7EBF\u4E0A\u4F1A\u8BAE\uFF08allowNoRoom: true\uFF09\u3002`
8225
+ );
8226
+ if (free.blockingSummary) warnings.push(`\u5F53\u5929\u5360\u7528\u6982\u51B5\uFF1A${free.blockingSummary}`);
8227
+ }
8228
+ return meetingSuccess(
8229
+ "meeting.eb.meeting.create.suggest",
8230
+ {
8231
+ dateFrom,
8232
+ dateTo,
8233
+ durationMinutes,
8234
+ // 同样是"让用户点一下":时段候选用选项控件呈现,别写成文本列表
8235
+ ui: { component: "choice", multiple: false, recommendedKey: "T1" },
8236
+ slots: usable.map((slot, index) => ({
8237
+ key: `T${index + 1}`,
8238
+ label: `${slot.begin.slice(5, 10)} ${slot.begin.slice(11)}~${slot.end.slice(11)}`,
8239
+ value: { begin: slot.begin, end: slot.end, address: slot.rooms[0]?.id ?? null },
8240
+ reason: `${memberCount ? `${memberCount} \u4EBA\u5168\u90E8\u7A7A\u95F2\uFF08\u4F1A\u8BAE\u56DE\u6267 + \u65E5\u7A0B\u5DF2\u6838\uFF09\uFF1B` : ""}${slot.rooms.length} \u95F4\u4F1A\u8BAE\u5BA4\u53EF\u7528\uFF1A${slot.rooms.map((room) => room.name).join("\u3001")}`,
8241
+ rooms: slot.rooms
8242
+ })),
8243
+ // 把"人空闲但没房间"的时段也带出来,方便调用方解释为什么被剔除
8244
+ skippedSlots: withRooms.filter((slot) => slot.rooms.length === 0),
8245
+ memberIds,
8246
+ scheduleItems: free.scheduleItems,
8247
+ idle: free.idle,
8248
+ cost: free.cost
8249
+ },
8250
+ warnings.length > 0 ? { warnings } : {}
8251
+ );
8252
+ }
8253
+ };
6302
8254
  var meetingEbCreateFlowOperations = [
8255
+ ebMeetingCreateSuggest,
6303
8256
  ebMeetingCreateFlowPrepare,
6304
8257
  ebMeetingCreateFlowApply,
6305
8258
  ebMeetingCreateFlowQuick
@@ -6392,7 +8345,7 @@ var ebAutoInvitationAfterWrite = async (context) => {
6392
8345
  const mtId = String(context.dataIds?.[0] ?? "").trim();
6393
8346
  if (!isCreate || !enabled || !mtId) return association;
6394
8347
  try {
6395
- const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-QKMI43Q6.js");
8348
+ const { getMeetingOperation: getMeetingOperation2 } = await import("./registry-EP4VTHZS.js");
6396
8349
  const render = getMeetingOperation2("meeting.invitation.render");
6397
8350
  const result = await render.handler({ mtId }, context.env);
6398
8351
  return { ...association ?? {}, invitation: result?.data ?? null };
@@ -6415,6 +8368,7 @@ var meetingEbOperations = [
6415
8368
  ...meetingEbConflictOperations,
6416
8369
  ...meetingEbDecisionOperations,
6417
8370
  ...meetingEbScheduleOperations,
8371
+ ...meetingEbCrmOperations,
6418
8372
  ...meetingEbCreateFlowOperations,
6419
8373
  ebRoomRecommendOperation
6420
8374
  ];