weaver-work-cli 0.1.39 → 0.1.40

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 (103) hide show
  1. package/README.md +4 -2
  2. package/dist/chunks/{archive-ENZYBBME.js → archive-JHTONRST.js} +2 -2
  3. package/dist/chunks/{archive-oa-YWLEC5V4.js → archive-oa-FCHLCPHC.js} +2 -2
  4. package/dist/chunks/{asset-OOV6DWPA.js → asset-RLIHC7DB.js} +2 -2
  5. package/dist/chunks/{asset-idle-RE632MY5.js → asset-idle-KVOFTLCP.js} +2 -2
  6. package/dist/chunks/{attachment-content-RWN2FCLL.js → attachment-content-RZYDB3MB.js} +3 -3
  7. package/dist/chunks/{blog-PEPOJAL7.js → blog-42OZTJNP.js} +2 -2
  8. package/dist/chunks/{caizhilian-WYIQDVPJ.js → caizhilian-KRK547K5.js} +2 -2
  9. package/dist/chunks/{calendar-CIARBAOX.js → calendar-RTWAMKHD.js} +2 -2
  10. package/dist/chunks/{chunk-L4N7B7ZD.js → chunk-4CTESSKI.js} +4 -4
  11. package/dist/chunks/{chunk-XXJOSZTQ.js → chunk-4TEDNCON.js} +43 -5
  12. package/dist/chunks/chunk-57IECMWX.js +487 -0
  13. package/dist/chunks/{chunk-CQHGNCK2.js → chunk-7BRU4EFH.js} +13 -7
  14. package/dist/chunks/{chunk-KTCB5R5H.js → chunk-7E6MUOEK.js} +2 -2
  15. package/dist/chunks/{chunk-I2HB737U.js → chunk-7U577Q7U.js} +2 -2
  16. package/dist/chunks/{chunk-BJGXMFGD.js → chunk-CAQNBBQA.js} +2 -2
  17. package/dist/chunks/{chunk-MUQUHKPG.js → chunk-CXRQWOZ5.js} +2 -2
  18. package/dist/chunks/{chunk-T7JDJJ7H.js → chunk-FLS4DPGP.js} +110 -16
  19. package/dist/chunks/{chunk-P2DAISYW.js → chunk-I566ZYNF.js} +1 -1
  20. package/dist/chunks/{chunk-36DIFZAH.js → chunk-IZSSVYAG.js} +2 -2
  21. package/dist/chunks/{chunk-EINRJHQ3.js → chunk-JWRZM2H2.js} +3 -3
  22. package/dist/chunks/{chunk-F2EQXUEE.js → chunk-LFMSDBZN.js} +97 -14
  23. package/dist/chunks/{chunk-GVPJZXJ2.js → chunk-LN447PY2.js} +91 -50
  24. package/dist/chunks/{chunk-K3RUEEOL.js → chunk-NETCCARD.js} +2 -2
  25. package/dist/chunks/{chunk-YS5DPN4T.js → chunk-NJKREDA3.js} +1 -1
  26. package/dist/chunks/{chunk-ZXRNHZTE.js → chunk-SPRHHSVC.js} +2 -2
  27. package/dist/chunks/{chunk-HJYVHXUQ.js → chunk-T6EGVBKU.js} +1 -1
  28. package/dist/chunks/{chunk-NLK2RZKG.js → chunk-VNBU55SL.js} +428 -145
  29. package/dist/chunks/{chunk-EZGFHJFW.js → chunk-WFFNM65B.js} +26 -7
  30. package/dist/chunks/{conflict-DRMDOL56.js → conflict-VR55AOOO.js} +2 -2
  31. package/dist/chunks/{contract-73MERZWN.js → contract-HI4FNPYE.js} +2 -2
  32. package/dist/chunks/{cowork-HNGBPD7A.js → cowork-HJLI353D.js} +12 -5
  33. package/dist/chunks/{ebuilder-form-OY6DVNRK.js → ebuilder-form-HTGI63UU.js} +3 -3
  34. package/dist/chunks/{ehr-RAQNEQO7.js → ehr-GYXIYH3A.js} +2 -2
  35. package/dist/chunks/env-DW3G4HUO.js +19 -0
  36. package/dist/chunks/{esb-6XV7C7YV.js → esb-I5INCEFE.js} +4 -4
  37. package/dist/chunks/factory-TKXGXS6L.js +30 -0
  38. package/dist/chunks/{fna-XTSRB7G7.js → fna-K2MHTQ24.js} +2 -2
  39. package/dist/chunks/group-chat-VYBNMJLG.js +18 -0
  40. package/dist/chunks/{host-VGPVP7YF.js → host-256GVCAH.js} +3 -3
  41. package/dist/chunks/{host-A7J6GDUP.js → host-D5SERMAJ.js} +3 -3
  42. package/dist/chunks/{host-3F5UM5GN.js → host-JM4YVHFM.js} +3 -3
  43. package/dist/chunks/{host-YAQAPYNP.js → host-L3VLITMT.js} +3 -3
  44. package/dist/chunks/{host-YMBXWFWX.js → host-ZUQTJYYK.js} +3 -3
  45. package/dist/chunks/{hrm-TLHKC545.js → hrm-VLRBKTZB.js} +3 -3
  46. package/dist/chunks/{im-YOUCFMUK.js → im-QK3VTLXK.js} +8 -8
  47. package/dist/chunks/{invoice-WNBS2B4C.js → invoice-T5JTA32H.js} +3 -3
  48. package/dist/chunks/{jiuchuanhui-XABWS5RN.js → jiuchuanhui-P2FNEQVZ.js} +2 -2
  49. package/dist/chunks/{lazy-dispatch-NLZ5UQQI.js → lazy-dispatch-XRRI46M6.js} +4 -4
  50. package/dist/chunks/{mail-LRYMBFNL.js → mail-3Y7MJ6PS.js} +9 -4
  51. package/dist/chunks/{meeting-ESXL6MFI.js → meeting-M7LTXGJE.js} +11 -10
  52. package/dist/chunks/{okr-EOV65SSF.js → okr-OBC6S67L.js} +2 -2
  53. package/dist/chunks/{plan-YFPVKW6Y.js → plan-5H6I53SK.js} +2 -2
  54. package/dist/chunks/{project-MDVZUYQW.js → project-L75QZV4S.js} +2 -2
  55. package/dist/chunks/{registry-QCLZEDAP.js → registry-KMHCPAAD.js} +9 -8
  56. package/dist/chunks/{registry-OZSMDVPP.js → registry-QGHFNUWS.js} +2 -2
  57. package/dist/chunks/{registry-VT5Y2HLA.js → registry-RXPN6BBT.js} +4 -4
  58. package/dist/chunks/{registry-YX4CSEMD.js → registry-UGRBCBHO.js} +2 -2
  59. package/dist/chunks/{room-recommend-L3MWH4JW.js → room-recommend-A3UDC3CA.js} +4 -4
  60. package/dist/chunks/{schedule-UASAVDVD.js → schedule-5GCACCK7.js} +4 -4
  61. package/dist/chunks/{workflow-KDGIUCNW.js → workflow-M5ZJ7DWF.js} +6 -6
  62. package/dist/index.js +28 -28
  63. package/dist/internal/e10/auth/commands.js +109 -56
  64. package/dist/internal/e10/auth/session.js +11 -1
  65. package/dist/internal/e10/auth/xiaoe.js +29 -4
  66. package/dist/internal/e10/client.js +13 -0
  67. package/dist/internal/e10/context.js +14 -6
  68. package/dist/internal/e10/request-runtime.js +9 -0
  69. package/dist/shortcuts/cowork/operations/item-write.js +10 -2
  70. package/dist/shortcuts/mail/operations/mail-send.js +8 -2
  71. package/dist/shortcuts/meeting/operations/eb/catalog.js +5 -1
  72. package/dist/shortcuts/meeting/operations/eb/create-flow.js +381 -104
  73. package/dist/shortcuts/meeting/operations/eb/crm-search.js +87 -5
  74. package/dist/shortcuts/meeting/operations/eb/flow-memory.js +49 -0
  75. package/dist/shortcuts/meeting/operations/eb/group-chat.js +583 -0
  76. package/dist/shortcuts/meeting/operations/eb/index.js +2 -0
  77. package/dist/shortcuts/meeting/operations/eb/room-recommend.js +19 -3
  78. package/dist/shortcuts/meeting/operations/eb/room-summary.js +2 -0
  79. package/dist/shortcuts/meeting/operations/eb/usage-cache.js +99 -0
  80. package/dist/shortcuts/meeting/operations/from-group-chat.js +35 -12
  81. package/dist/shortcuts/workflow/operations/form.js +21 -2
  82. package/dist/shortcuts/workflow/operations/todoSmartConvert/aigroupRules.js +52 -1
  83. package/dist/shortcuts/workflow/operations/todoSmartConvert/summaryCache.js +10 -9
  84. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.collect.js +28 -2
  85. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.core.js +1 -0
  86. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.group.js +17 -9
  87. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.js +1 -0
  88. package/dist/shortcuts/workflow/operations/todoSmartConvert/todoSmartConvert.summary.js +14 -2
  89. package/docs/e10-auth.md +15 -0
  90. package/package.json +1 -1
  91. package/scripts/mcp-meeting.mjs +234 -0
  92. package/skills/weaver-e10-cowork/references/cowork-write.md +1 -1
  93. package/skills/weaver-e10-mail/references/mail-send.md +2 -0
  94. package/skills/weaver-e10-meeting/SKILL.md +88 -25
  95. package/skills/weaver-e10-meeting/references/meeting-minute-file.md +25 -11
  96. package/skills/weaver-e10-meeting.zip +0 -0
  97. package/skills/weaver-e10-shared/SKILL.md +1 -1
  98. package/skills/weaver-e10-shared/references/e10-auth-and-session.md +1 -1
  99. package/skills/weaver-e10-workflow/SKILL.md +4 -0
  100. package/skills/weaver-e10-workflow/aigroup/default.json +12 -0
  101. package/skills/weaver-e10-workflow/references/todo-smart-convert.md +17 -11
  102. package/skills/weaver-e10-workflow/tools/todo-summary.cjs +73 -23
  103. package/dist/chunks/{chunk-2LV74IWP.js → chunk-E7OK5WD5.js} +5 -5
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * weaver-work-cli 会议 MCP 服务器(stdio 传输,零依赖)。
4
+ *
5
+ * 把 CLI 的会议 operation 封装成 MCP tools,供小E / WorkBuddy 等宿主以 MCP 方式调用:
6
+ * {"mcpServers": {"weaver-meeting": {"command": "node", "args": ["<本文件路径>"]}}}
7
+ *
8
+ * 设计原则(2026-11-03 与用户对齐):
9
+ * - **不重新实现业务**:tools/call 内部直接 spawn 现有 CLI(`meeting run <op> --input-json`),
10
+ * 防呆闸门(timeConfirmed/roomConfirmed/confirm)、登录态、占用缓存、流程记忆全部原样继承;
11
+ * - **工具少而清晰**:MCP 工具发现靠模型读 tool schema,工具越多越乱 —— 只暴露 3 个高频动作;
12
+ * - **零依赖**:MCP stdio 就是 JSON-RPC 2.0(每行一个 JSON),手写握手即可,不引 SDK
13
+ * (私服有坏包前科,bundle 链路也不动)。
14
+ *
15
+ * 手工自测:
16
+ * printf '%s\n%s\n%s\n' \
17
+ * '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"t","version":"0"}}}' \
18
+ * '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
19
+ * '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' | node scripts/mcp-meeting.mjs
20
+ */
21
+
22
+ import { spawn } from 'node:child_process';
23
+ import { createInterface } from 'node:readline';
24
+ import { fileURLToPath } from 'node:url';
25
+ import path from 'node:path';
26
+ import { existsSync } from 'node:fs';
27
+
28
+ const SERVER_INFO = { name: 'weaver-meeting', version: '1.0.0' };
29
+ const PROTOCOL_VERSION = '2024-11-05';
30
+
31
+ // CLI 入口:优先同目录的 dist(打包后 scripts/ 与 dist/ 同级),找不到再试仓库根
32
+ const here = path.dirname(fileURLToPath(import.meta.url));
33
+ const candidates = [
34
+ path.resolve(here, '..', 'dist', 'index.js'),
35
+ path.resolve(here, '..', '..', 'dist', 'index.js'),
36
+ ];
37
+ const CLI_ENTRY = candidates.find((p) => existsSync(p));
38
+ if (!CLI_ENTRY) {
39
+ console.error(JSON.stringify({ error: `找不到 CLI 入口,试过:${candidates.join(', ')}` }));
40
+ process.exit(1);
41
+ }
42
+
43
+ /** 调用现有 CLI:一行 JSON 进,一行 JSON 出。 */
44
+ async function callCli(operation, input) {
45
+ return new Promise((resolve) => {
46
+ const child = spawn(process.execPath, [
47
+ CLI_ENTRY, '--json', 'meeting', 'run', operation, '--input-json', JSON.stringify(input ?? {}),
48
+ ], { windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] });
49
+ let stdout = '';
50
+ let stderr = '';
51
+ child.stdout.on('data', (chunk) => { stdout += String(chunk); });
52
+ child.stderr.on('data', (chunk) => { stderr += String(chunk); });
53
+ child.on('close', () => {
54
+ try {
55
+ resolve(JSON.parse(stdout));
56
+ } catch {
57
+ resolve({
58
+ ok: false,
59
+ error: { type: 'mcp', message: `CLI 输出不是 JSON(stderr: ${stderr.slice(0, 200)};stdout: ${stdout.slice(0, 200)})` },
60
+ });
61
+ }
62
+ });
63
+ child.on('error', (error) => {
64
+ resolve({ ok: false, error: { type: 'mcp', message: `CLI 启动失败:${error.message}` } });
65
+ });
66
+ });
67
+ }
68
+
69
+ /** 把 CLI 的返回压成 MCP text content(大 JSON 直接塞给模型会撑上下文,这里挑关键域)。 */
70
+ function summarize(result) {
71
+ const data = result?.data ?? {};
72
+ const brief = {
73
+ ok: result?.ok === true,
74
+ ...(result?.ok ? {} : { error: result?.error }),
75
+ status: data.status,
76
+ meetingId: data.meetingId ?? undefined,
77
+ requestId: data.requestId ?? undefined,
78
+ workflowPending: data.workflowPending,
79
+ message: typeof data.message === 'string' ? data.message.slice(0, 400) : undefined,
80
+ // 选项类返回(时段/流程/会议室挑选)必须原样带给模型 —— 用户要在选项里点选
81
+ options: Array.isArray(data.options) ? data.options.slice(0, 6) : undefined,
82
+ timings: data.timings,
83
+ };
84
+ return { content: [{ type: 'text', text: JSON.stringify(brief, null, 1) }] };
85
+ }
86
+
87
+ // ---------------------------------------------------------------------------
88
+ // 工具清单:3 个高频动作,schema 直接搬 CLI 入参的精简版(描述要自足,模型只看得到这里)
89
+ // ---------------------------------------------------------------------------
90
+ const TOOLS = [
91
+ {
92
+ name: 'meeting_book_room',
93
+ description: '预订会议室(泛微 E10 EB 版):发起建会流程并**服务端直提**,不弹流程表单。'
94
+ + '时间/会议室必须来自用户原话或在候选里点选的结果——'
95
+ + '时间要传 timeConfirmed: true、会议室要传 roomConfirmed: true 才会真正提交,否则返回候选让用户挑。'
96
+ + 'submit=false 时只做预检与推荐(不提交、不弹表单);submit=true 直接提交流程(同样不弹表单)。',
97
+ inputSchema: {
98
+ type: 'object',
99
+ properties: {
100
+ mt_subject: { type: 'string', description: '会议名称/主题(必填)' },
101
+ begin_datetime: { type: 'string', description: '开始时间 yyyy-MM-dd HH:mm(必填,须来自用户确认)' },
102
+ end_datetime: { type: 'string', description: '结束时间 yyyy-MM-dd HH:mm' },
103
+ timeConfirmed: { type: 'boolean', description: '用户原话明确了时间、或在候选中点选后才传 true' },
104
+ address: { type: 'string', description: '会议室 id 或名称' },
105
+ roomConfirmed: { type: 'boolean', description: '用户明确指定该会议室、或点选后才传 true' },
106
+ mt_type: { type: 'string', description: '会议类型名称(流程表单必填项)' },
107
+ extraFields: { type: 'object', description: '流程表单的其他必填字段(按字段标签传,如 {"会议内容":"..."})' },
108
+ submit: { type: 'boolean', description: 'false=只出方案不提交(默认);true=直接提交流程(不弹表单)' },
109
+ },
110
+ required: ['mt_subject', 'begin_datetime'],
111
+ },
112
+ },
113
+ {
114
+ name: 'meeting_room_list',
115
+ description: '查询会议室列表(EB 版,含状态)。可按人数/关键词过滤。',
116
+ inputSchema: {
117
+ type: 'object',
118
+ properties: {
119
+ keyword: { type: 'string', description: '会议室名称关键词' },
120
+ pageSize: { type: 'number', description: '每页条数,默认 20' },
121
+ },
122
+ },
123
+ },
124
+ {
125
+ name: 'meeting_room_usage',
126
+ description: '查询某天某会议室(或不指定则全部会议室)的占用明细,用于回答"这个会议室明天有没有空"。',
127
+ inputSchema: {
128
+ type: 'object',
129
+ properties: {
130
+ day: { type: 'string', description: '日期 yyyy-MM-dd(必填)' },
131
+ address: { type: 'string', description: '会议室 id;不传 = 返回当天全部会议室占用' },
132
+ },
133
+ required: ['day'],
134
+ },
135
+ },
136
+ ];
137
+
138
+ async function handleToolCall(name, args) {
139
+ if (name === 'meeting_book_room') {
140
+ const input = {
141
+ // ⛔ CLI 的语义键是 mt_name(FIELD_ALIASES 里「会议名称/会议主题/主题」都挂在它下面),
142
+ // mt_subject 不是合法入参 —— MCP 层做一次转换,别把模型给的名称弄丢。
143
+ mt_name: args.mt_subject ?? args.mt_name,
144
+ begin_datetime: args.begin_datetime,
145
+ ...(args.end_datetime ? { end_datetime: args.end_datetime } : {}),
146
+ ...(args.timeConfirmed ? { timeConfirmed: true } : {}),
147
+ ...(args.address ? { address: args.address } : {}),
148
+ ...(args.roomConfirmed ? { roomConfirmed: true } : {}),
149
+ ...(args.mt_type ? { mt_type: args.mt_type } : {}),
150
+ ...(args.extraFields && typeof args.extraFields === 'object' ? { extraFields: args.extraFields } : {}),
151
+ // MCP 口径(2026-11-03 用户确认):预约会议 = 直提流程,**永不弹流程表单**;
152
+ // 提交必须 confirm + submit_mode:direct,预检则不带 confirm(CLI 返回方案,同样不弹表单)。
153
+ ...(args.submit === true ? { confirm: true, submit_mode: 'direct' } : {}),
154
+ };
155
+ return summarize(await callCli('meeting.eb.meeting.create.flow.quick', input));
156
+ }
157
+ if (name === 'meeting_room_list') {
158
+ return summarize(await callCli('meeting.eb.room.list', {
159
+ ...(args.keyword ? { keyword: args.keyword } : {}),
160
+ ...(args.pageSize ? { pageSize: args.pageSize } : {}),
161
+ }));
162
+ }
163
+ if (name === 'meeting_room_usage') {
164
+ const day = String(args.day ?? '').slice(0, 10);
165
+ const input = args.address
166
+ ? { beginDatetime: `${day} 00:00`, endDatetime: `${day} 23:59`, address: args.address }
167
+ : { begindatetime: `${day} 00:00`, enddatetime: `${day} 23:59`, pageSize: 200 };
168
+ const op = args.address ? 'meeting.eb.conflict.room' : 'meeting.eb.roomUsage.list';
169
+ return summarize(await callCli(op, input));
170
+ }
171
+ return { content: [{ type: 'text', text: `未知工具:${name}` }], isError: true };
172
+ }
173
+
174
+ // ---------------------------------------------------------------------------
175
+ // JSON-RPC over stdio(每行一个消息)
176
+ // ---------------------------------------------------------------------------
177
+ const rl = createInterface({ input: process.stdin });
178
+ const send = (message) => process.stdout.write(`${JSON.stringify(message)}\n`);
179
+ // stdin 关闭(宿主不再发消息)时要等在途的 tools/call 跑完再退——CLI 调用要十几秒,不能被杀
180
+ let pendingCalls = 0;
181
+ let stdinClosed = false;
182
+
183
+ rl.on('line', (line) => {
184
+ const text = line.trim();
185
+ if (text === '') return;
186
+ let message;
187
+ try {
188
+ message = JSON.parse(text);
189
+ } catch {
190
+ return; // 非 JSON 行(人话日志)直接忽略
191
+ }
192
+ const { id, method, params } = message ?? {};
193
+ if (method === 'initialize') {
194
+ send({
195
+ jsonrpc: '2.0', id,
196
+ result: {
197
+ protocolVersion: PROTOCOL_VERSION,
198
+ capabilities: { tools: { listChanged: false } },
199
+ serverInfo: SERVER_INFO,
200
+ },
201
+ });
202
+ return;
203
+ }
204
+ if (method === 'notifications/initialized' || (method ?? '').startsWith('notifications/')) return;
205
+ if (method === 'tools/list') {
206
+ send({ jsonrpc: '2.0', id, result: { tools: TOOLS } });
207
+ return;
208
+ }
209
+ if (method === 'tools/call') {
210
+ pendingCalls += 1;
211
+ handleToolCall(String(params?.name ?? ''), params?.arguments ?? {})
212
+ .then((result) => send({ jsonrpc: '2.0', id, result }))
213
+ .catch((error) => send({
214
+ jsonrpc: '2.0', id,
215
+ result: { content: [{ type: 'text', text: `工具执行异常:${error.message}` }], isError: true },
216
+ }))
217
+ .finally(() => {
218
+ pendingCalls -= 1;
219
+ if (stdinClosed && pendingCalls === 0) process.exit(0);
220
+ });
221
+ return;
222
+ }
223
+ if (method === 'ping') {
224
+ send({ jsonrpc: '2.0', id, result: {} });
225
+ return;
226
+ }
227
+ if (id !== undefined) {
228
+ send({ jsonrpc: '2.0', id, error: { code: -32601, message: `method not found: ${method}` } });
229
+ }
230
+ });
231
+ rl.on('close', () => {
232
+ if (pendingCalls === 0) process.exit(0);
233
+ stdinClosed = true; // 在途调用完成后自行退出(见 tools/call 的 finally)
234
+ });
@@ -33,7 +33,7 @@ weaver-work-cli cowork run cowork.item.create.prepare --input-json '{"itemName":
33
33
  weaver-work-cli cowork run cowork.item.create.apply --input-json '{"itemName":"XX项目协作","sectionId":"<选项id>","itemContent":"目标与分工","confirm":true,"continuation":"<continuation>"}'
34
34
  ```
35
35
 
36
- prepare 会做三件事:版块 id 存在性校验(不存在直接报错并列可选版块)、负责人默认当前用户、开关默认全开。apply 成功后返回新协作 `id`。
36
+ prepare 会做三件事:版块 id 存在性校验(不存在直接报错并列可选版块)、负责人默认当前用户、开关默认全开。apply 成功后返回新协作 `id`,以及 `viewUrl`(PC 端 `/sp/cowork/view/{id}`)与 `mobileViewUrl`(移动端 `/mobile/cowork/view/{id}`)——`{id}` 为协作主键,链接可直接打开该协作。其余写操作(action/flag)的 apply 回执同样带 `viewUrl`。
37
37
 
38
38
  ## 2. 协作级动作:`cowork.item.action.prepare` → `apply`(`action: "end"`)
39
39
 
@@ -31,6 +31,8 @@ weaver-work-cli --json mail run mail.send.prepare --input-json '{"flag":"-1","to
31
31
  weaver-work-cli --json mail run mail.send.apply --input-json '{"continuation":"<PREPARE_CONTINUATION>","confirm":true}'
32
32
  ```
33
33
 
34
+ `apply` 成功后返回 `subject` 与 `mailResourceId`,并附 `viewUrl`(PC 端 `/sp/mail/view?mailResourceId=<id>`,可直接打开该邮件);存草稿分支同样返回 `mailResourceId` + `viewUrl`。`viewUrl` 为空时说明接口未回传邮件 id(极少数环境),此时提示用户到「已发送」手动确认即可。
35
+
34
36
  Windows PowerShell 与 macOS/Linux 命令一致(JSON 用单引号包裹)。
35
37
 
36
38
  ## 要点
@@ -5,7 +5,7 @@ display_name_en: Weaver E10 Meeting Management
5
5
  description: "泛微 E10 会议管理:查会议、预约建会(推荐会议室、查冲突与空闲时段)、改期/改地点/改参会人、取消删除、流程建会、会议室查询与推荐、生成邀请函、读取会议附件、回执签到、纪要与决议任务、参会二维码。详见 description_zh。"
6
6
  description_zh: "泛微 E10 会议管理,帮您处理会议日常事务:[会议]-查询今天/本周/我的会议与会议详情,预约建会(自动推荐会议室、检测参会人会议冲突与日程空闲、冲突时给出当天可选时段),改期/换地点/改参会人、取消、删除;受限环境可走审批流程建会,提交后必出邀请函;[会议室]-查询会议室与占用,按人数/设备/常用程度智能推荐;[邀请函]-生成邀请函文档(有会议 id 用真实数据;没有也能按会议名+时间出预置函);[资料]-读取会议与议题的附件、抽取文档正文;参会回执、签到与补签、纪要、议题、决议与决议任务;[二维码]-把文本渲染成参会码/签到码图片。认证与会话由 weaver-e10-shared 提供。"
7
7
  description_en: "Weaver E10 meeting management: [Meetings] query today/week/my meetings and details, book meetings with automatic room recommendation, attendee conflict and calendar-availability checks (plus same-day alternative slots), reschedule / change room / change attendees, cancel, delete; flow-based booking with automatic invitation when the app only allows workflow creation; [Rooms] occupancy queries and smart recommendation by capacity, equipment and personal usage; [Invitation] generate invitation docx from real meeting data; [Content] list and read meeting/topic attachments and extract document text; receipts, sign-in, minutes, topics, decisions and decision tasks; [QR] render text into attendee/sign-in QR images. Auth and session are provided by weaver-e10-shared."
8
- version: 2.45.8
8
+ version: 2.48.1
9
9
  author: 泛微网络科技股份有限公司
10
10
  agent_created: true
11
11
  requires:
@@ -29,6 +29,10 @@ cliHelp: "weaver-work-cli meeting --help"
29
29
 
30
30
  用户说「帮我约个会 / 预约明天和 XX 开会」时,**先按这个顺序做,不要一上来就问一堆**:
31
31
 
32
+ **⛔ 参会人员不许收集**:用户没点名要请谁时,**不要用任何弹框/表单/输入框问『参会人员』**,直接调用 CLI 且**不传 `hrm_members`** —— CLI 会自动填当前登录人(实测表单里就是 `| 参会人员 | 卜伟领 |`)。用户主动说"叫上张三"才传。
33
+ 这条优先于下面所有步骤:先弹『参会人员(可选)』再建会 = 多问一轮,用户明确投诉过。
34
+
35
+
32
36
  0. **不确定 CLI 有没有某个能力 → `weaver-work-cli meeting ops`(纯文本)**,查具体能力用
33
37
  `meeting ops --grep 关键词`(如 `--grep suggest`、`--grep 建会`)。
34
38
  ⛔ **不准用 `meeting schema --json` 判断能力存在性**:完整 308KB、stdout 只回 20KB 预览,
@@ -41,8 +45,9 @@ cliHelp: "weaver-work-cli meeting --help"
41
45
  - ✅ **只有这两种情况传 `true`**:
42
46
  ① 用户**原话明确**给出了("明天 9 点到 10 点"、"用 502 茶室");
43
47
  ② 用户**在选项里点选**后,把 `options[].value` **整体原样回传**(value 里自带该标记,别手动拼)。
44
- - ⛔ **其他一切情况都不传**:用户只说"明天/上午/下午"、你从上下文推断、你觉得 9 点最合适、
48
+ - ⛔ **其他一切情况都不传**:用户只说"明天"(没给时段词)、你从上下文推断、你觉得 9 点最合适、
45
49
  你觉得这间房最合适 —— **一律不传**。
50
+ (用户说了"上午 / 下午 / 晚上"**不算猜**:按第 2 条固定区间传 `time_slot`,CLI 自己展开并置标记。)
46
51
  不传时**即使你填了** `begin_datetime` / `address`,CLI 也不会当成已定,而是返回时段/房间选项让用户点选。
47
52
  - ⛔ **禁止把候选写成定论**。`NEEDS_SELECTION` 返回的 `options` 是**候选**,不是已选:
48
53
  不许说「时间:09:00–10:00」「地点:测试会议室1」这种既定语气,
@@ -50,14 +55,24 @@ cliHelp: "weaver-work-cli meeting --help"
50
55
  - ⛔ **禁止绕过**:不许先自己调 `room.list` / `room.recommend` 挑一间,再带 `roomConfirmed: true` 硬塞;
51
56
  也不许因为"懒得再多问一轮"就直接传标记。用户没说过,就是没说过。
52
57
 
53
- 1. **确认顺序固定:名称 → 时间 → 参会人 → 地点 → 流程**。
58
+ 1. **确认顺序固定:名称 → 时间 →(参会人,可省略)→ 地点 → 流程**。
54
59
  流程只是审批载体,**不许在四要素定下来之前就问"用哪条流程"**。CLI 也是这个顺序:
55
60
  时间没确认先回 `slot`,地点没确认再回 `room`,四个都定了才回 `flow`;
56
61
  `flow` 的 `options[].value` 里已带 `begin_datetime`/`end_datetime`/`address` 和两个确认标记,
57
62
  用户点选流程后**原样回传 value** 就直接进确认,不要再问一遍时间/地点。
58
63
 
59
- 2. **没给具体几点 → 直接走 ① 的 `prepare` 且不传 `begin_datetime`**,它会返回「所有参会人空闲 **且**
60
- 该时段有可用会议室」的时段选项,直接做成选项让用户挑;禁止问"安排在几点"。
64
+ 2. **时间只有粗粒度说法时,按固定口径直接定**(2026-11-02 用户确认,不要再弹候选):
65
+ - 说了「上午 / 早上」→ **09:00–12:00**;「下午」→ **13:00–18:00**;「晚上」→ **18:00–21:00**;
66
+ - **正确传法(优先用这个)**:`{"dateFrom":"2026-11-03","time_slot":"上午"}` —— CLI 会自己展开成
67
+ `2026-11-03 09:00`~`12:00` 并视为已确认;
68
+ 也可以直接写 `begin_datetime: "2026-11-03 上午"`,两种都认;
69
+ - ⛔ **不要只传 `dateFrom` 就等 CLI 返回时段候选** —— 用户已经说了"上午",再弹一次"选时段"就是重复问
70
+ (2026-11-02 用户投诉的就是这个);
71
+ - 只给了一个**开始时间** → 结束 = 开始 + 1 小时(`durationMinutes` 可覆盖);
72
+ - 给了**完整区间**("9 点到 11 点")→ 按给的,不做映射;
73
+ - 以上都算用户拍过板,CLI 会自动置 `timeConfirmed`,**不要再返回时段选项让他挑**。
74
+ 2.1 **连日期都没有时才返回候选**:不传 `begin_datetime`,`prepare` 会返回「所有参会人空闲 **且**
75
+ 该时段有可用会议室」的时段选项,做成选项让用户挑;禁止问"安排在几点"。
61
76
  只有"只想看空闲、还不建会"时才单独调 `meeting.eb.meeting.create.suggest`。
62
77
  - 每个时段选项的 `reason` 会写明「N 人全部空闲(会议回执 + 日程已核);M 间会议室可用:…」——
63
78
  这就是"人和房都空闲"的凭据,`options[].rooms` 是该时段真实的可用房间清单。
@@ -66,13 +81,19 @@ cliHelp: "weaver-work-cli meeting --help"
66
81
 
67
82
  3. **没指定会议室 → 不要"问",要"给选项"**。`suggest` 返回里的 `rooms`、`prepare` 返回里的
68
83
  `roomOptions` / `options` 自带候选(已按人数/常用/时段占用排序,默认 3 个)。
84
+ **带了客户(`crm_members` 非空)时,接待类会议室会排前面** —— 判据是会议室描述(`room_desc`,
85
+ 如「接待重要客户使用」)或名称含 接待/招待/客户/商务/贵宾,`reasons` 里会写明「带客户优先:…」。
86
+ 这一层是 CLI 自动做的,**不需要你额外传参、也不要自己按名字挑**。
69
87
  - ⛔ **禁止开放式提问**:不要说「会议地点是必填项,需要你指定一间 / 你想用哪间会议室」——
70
88
  这是把选择题变成填空题,等于没推荐。
71
89
  - ✅ **必须做成选项控件**:`options[].label` 作标题、`reason` 作副标题,第一个标「推荐」。
72
90
  - ⛔ **不要自己再去调 `meeting.eb.room.list` 拼候选** —— 排好序的 `options` 就在返回值里,
73
91
  另拉列表既丢排序又会把不合适(容量不够/已被占用)的房间混进来。
74
92
  - 候选为空(该时段全被占用)→ 给「改时段 / 线上会议 / 地点待定」三个选项,仍不要让用户手填房间名。
75
- 4. **不要问「参会人数」**。CLI 自动算 = `hrm_members` 人数 **+ 召集人自己**(已在上线账号里就不重复加),
93
+ 4. **没给参会人 → 不要弹窗问,CLI 默认就是当前登录人**(2026-11-02 用户要求):
94
+ `hrm_members` 不传时 CLI 自动填当前登录人并写进表单;用户后面想加人会说,
95
+ 所以**不要**为了"请选择参会人"打断他。
96
+ 4.1 **不要问「参会人数」**。CLI 自动算 = `hrm_members` 人数 **+ 召集人自己**(已在上线账号里就不重复加),
76
97
  用于会议室容量过滤;`total_member` / `memberCount` 是可选参数,**只有用户明确说了"几个人"才传**。
77
98
  ⛔ 不要自己推算人数、也不要因为"看起来只有 1 个参会人"就降级给人少的房间。
78
99
  5. **不要逐个问必填项**。会议名称没给就从上下文拟一个(如"十一月开发进度沟通会")让用户一次确认;
@@ -112,6 +133,11 @@ cliHelp: "weaver-work-cli meeting --help"
112
133
  不许因为"名字不完整"就跳过查询、改成弹「客户怎么写」的选择框 —— 2026-10-31 用户投诉的就是这个:
113
134
  小E 弹了「填入参会客户字段 / 仅写入会议内容备注」两个选项,**"仅写入备注"正是明令禁止的做法**。
114
135
  先查,再谈确认;不存在"先问怎么填"这一步。
136
+ - **`items` 可能有多条**(动作流是 like 模糊查:搜「中国民航」能命中"中国民航信息网络股份有限公司--…"、"中航信数智…"等一票近名客户)。
137
+ → **把前 3–5 个客户做成选项让用户选**(标题用客户全名),不要默认取第一个、也不要自己合并;
138
+ - 用户选定后,`crm_members` 传 **该客户的 `{id, name}`**(就是 `items[]` 里的那一项)。
139
+ CLI 会**直接用这个客户自己的联系人明细**(`contacts`),**不会再回查 ESB** —— 所以选中谁就只有谁的联系人。
140
+ 如果 `items[]` 里带了 `contacts`,原样一起传更稳(省掉一次查询)。
115
141
  - `exact: false`(命中名 ≠ 关键词)→ **把命中的全名原样念出来问"是这家吗"**,用户认了就往下走。
116
142
  ⛔ 不要问成选择题("填客户字段 / 只写备注"),也不要拿首个匹配当定论。
117
143
  - `items: []`(真没查到)→ 才问用户:「CRM 里没搜到「X」,能给我完整的公司名吗?或这家先不关联客户?」
@@ -262,7 +288,11 @@ cliHelp: "weaver-work-cli meeting --help"
262
288
  ```
263
289
  把 `label` 做成选项让用户挑一个(`value.begin/end/address` 直接可回填),选定后带时间走 prepare。
264
290
  - 没候选时它会说明原因(人没共同空闲 / 人空闲但订不到会议室),**如实转达**,别自己编一个时间;
265
- - 用户说了"上午/下午"这种粗粒度,也走它(`workStartHour`/`workEndHour` 约束),不要直接默认 9 点。
291
+ - ⛔ **用户说了"上午/下午/晚上"就不要走 suggest**:直接按固定时段定下来
292
+ (上午 09:00–12:00 / 下午 13:00–18:00 / 晚上 18:00–21:00),
293
+ 建会时传 `{"dateFrom":"<日期>","time_slot":"上午"}` 给 `create.flow.prepare/quick`;
294
+ **不要**再传 `workStartHour`/`workEndHour` 让 CLI 给候选 —— 那会二次追问用户时间
295
+ (2026-11-03 真实翻车:用户说"明天上午",小E 还是弹了 08:00~09:00 / 09:00~10:00 让选)。
266
296
  - ⛔ 这个操作调不通(返回 `unknown operation`)时**先跑 `meeting ops --grep suggest` 自查**,
267
297
  确认 CLI 装的是不是最新版;**不要**直接对用户说"没有这个能力" —— 建会主链路本来就不需要它。
268
298
  - ⛔ **仅限「会议建会」这一件事:不要自己手搓 `workflow.*`**
@@ -356,9 +386,9 @@ cliHelp: "weaver-work-cli meeting --help"
356
386
  没证据。正确说法:"这项没核验到(原因 X)",并原样转达原因。
357
387
  以前这些失败被 `.catch(() => null)` 静默吞掉,"没查"和"没冲突"长得一模一样,用户以为查过了
358
388
  (会议室查不到就卡在选房 → 冲突检测不跑 → 却答复"流程表单本身不做冲突检测",就是这么错的)。
359
- - **会议室台账缓存 24 小时,但「建会」链路强制拉最新**:`create.flow.prepare` / `create.flow.quick` /
360
- `create.suggest` 内部都会强制刷新台账,所以刚导入/新建的会议室在建会时一定看得到。
361
- 只有**纯查询**(`meeting.eb.room.recommend`)会走 24h 缓存(快);缓存超过 4 小时会在 warnings 提示
389
+ - **会议室台账缓存 24 小时(2026-11-03 松绑:建会链路也走缓存)**:`create.flow.prepare` / `create.flow.quick` /
390
+ 不再每次强制重拉台账(省 ~1s),靠「候选为空自动强制重拉」兜底;代价是**刚导入的会议室在缓存期可能按名称查不到**
391
+ (按 id 指定不受影响)。只有**纯查询**(`meeting.eb.room.recommend`)同样走 24h 缓存(快);缓存超过 4 小时会在 warnings 提示
362
392
  "数据可能过时",最近改过会议室就带 `refresh: true` 重查。候选为空时还会自动强制重拉一次
363
393
  (`meta.cacheRetried: true`)。
364
394
  ⛔ **不要因为"只有 1 间/查不到"就去断言系统里没有会议室** —— 先看 `meta.roomsTotal` 与 `cacheRetried`,
@@ -412,13 +442,17 @@ cliHelp: "weaver-work-cli meeting --help"
412
442
  - 取表单一律用 `meeting.eb.meeting.create.flow.prepare`(内部按固化流程候选、未命中回落 `mt_new_wf` 调 `workflow.createForm`),
413
443
  **不要**自己拼 `workflow.createRequestDirect` / `workflow.createForm`。
414
444
  3. **能自动推的绝不问用户;剩下要问的一次问全**
415
- - **时间没说具体几点**(只说"明天/下周三/上午")→ **不要问,直接调 ① 的 `prepare` 且不传 `begin_datetime`**,
416
- 它会返回「所有人都空闲 + 该时段有会议室」的时段选项(带会议室名,`value` 可直接回填)。
417
- 这是 2026-10-30 用户明确要求的:没填时间就要推荐时间,不许弹空输入框让人手填。
445
+ - **时间只说了日期**("明天 / 下周三",**且话里没有任何时段词**)→ 不要问,直接调 ① 的 `prepare`
446
+ 且不传 `begin_datetime`,它会返回「所有人都空闲 + 该时段有会议室」的时段选项(`value` 可直接回填)。
447
+ ⛔ **只要话里出现"上午/早上/下午/晚上",就不许走这条路**:必须按第 2 条传
448
+ `{"dateFrom":"<日期>","time_slot":"上午"}`,让 CLI 直接定成 09:00–12:00。
449
+ 2026-11-03 用户第二次投诉的就是这个:说了"明天上午",还是弹「09:00~10:00 / 10:00~11:00」让他选。
418
450
  只有"只想看空闲、不建会"才单独用 `meeting.eb.meeting.create.suggest`。
419
451
  - **会议室没指定** → **不要问,要给选项**(`prepare` 返回的 `options` / `roomOptions`,
420
452
  已排好序)。呈现规则见本节第 2 条:做成选项控件,禁止列成文本 + "需要你指定一间"。
421
- - 其余真正缺的(会议名称 / 参会人 / 会议类型)**一次列完问**,不要"先问人 → 再问类型"。
453
+ - 其余真正缺的(会议名称 / 会议类型)**一次列完问**,不要"先问人 → 再问类型"。
454
+ ⛔ **参会人不算"缺"**:不传 = 当前登录人(CLI 自动补进表单),**不许弹框问"参会人员是谁"**
455
+ (2026-11-03 用户投诉:明明可以默认自己,却又弹了一次输入框)。用户后面想加人会说。
422
456
  每多一轮追问就多一次模型推理(20–40 秒),用户体感"建个会要 2 分钟"基本都是这么来的(实测 CLI 全程只要 3 秒)。
423
457
  - 用户一句话里已经给全的要素**不要再复述确认**,直接进下一步。
424
458
  4. **不要在 prepare 之前自己先去查会议室/类型/冲突** —— `prepare`/`quick` 内部已并行跑完(见上面「会议室不能默默留空」)。
@@ -540,7 +574,10 @@ weaver-work-cli --json meeting run meeting.minute.fromGroupChat --input-json '{"
540
574
  `attachment.upload` → `minute.create` 挂附件 → ESB「解析附件」(文档同步、可能 60s 超时报 `write_uncertain` 但**不代表没执行**;音频异步约 9s 返回、原文约 2 分钟就绪)→ 等 `video_taskResult=1` / `minutes_status=4` → ESB「生成智能纪要文本」(写 `parse_content`)→ ESB「保存解析并生成文档」。
541
575
  ⛔ 不要自己拟纪要文案(提示词在 ESB 里,自己写会和小E 现有格式不一致)。
542
576
  ⛔ 不要调「获取解析纪要音频结果」:它是 1 分钟一轮的定时任务、会自己写表单,重复调可能覆盖;查进度只用 `meeting.minute.parseStatus`。
543
- ⚠️ 流程 id / 页面 id 是**租户级配置**,文中 `<…>` 均为占位符。
577
+ ✅ **三个动作流 id 与智能纪要页面 id 已内置**(本租户 primary_key),`meeting.minute.fromGroupChat` 默认就用它们,
578
+ 你**不用传** `flowGenerateMinutes` / `flowGenerateDocument` / `aiPageId`;换租户才需要覆盖。
579
+ ⛔ 手工触发时用 `uniqueIndent: "<32位 primary_key>"`,**不要塞进 `esbFlowId`** —— 那个入参只认 19 位数字 id,
580
+ 传 GUID 会报 `can not cast to long` / `系统错误`。
544
581
  ## 读附件内容(会议附件 / 项目文件 / 议题资料)
545
582
 
546
583
  ⛔ **旧结论作废**:以前 skill 里写"附件回读只有文件名、拿不到 fileId ⇒ 下载不了",**是错的**。
@@ -643,6 +680,12 @@ weaver-work-cli --json meeting run meeting.attachment.extract --input-json '{"mt
643
680
  - **不要**在建会前自己先跑 `hrm.employee.query`、`room.list`、`conflict.member`、`schedule.free` —— prepare/quick 内部已经并行做完了
644
681
  (只有在需要给用户**换人/换房/换时间**时才单独查);
645
682
  - 需要让用户挑选时(多流程 / 有冲突要改期 / 换会议室),用 `quick`/`prepare` 返回的 `options` 做**一次**选择,选定后再 `quick` 一次即可收口。
683
+ - ✅ **两个省时机制已内置(2.48.0),不用做任何事**:
684
+ ① **全天占用缓存**——quick/prepare 第一次跑某天会一次拉全天全部会议室占用(~2s,5 分钟内有效),
685
+ 同一天内改时间/换会议室/再推荐**不再打占用接口**;提交成功后当天缓存自动作废,不会拿旧台账放行冲突。
686
+ ② **流程记忆**——多个建会流程都可发起时,自动沿用**这个人上次成功用过的流程**直接发起
687
+ (留痕说明"沿用了你上次选的"),不再弹流程选项;要强制回到"每次都问"传 `flowChoice: "ask"`。
688
+ 权限过滤照做:记忆只是默认值,不是绕过权限。
646
689
 
647
690
  ## 流程建会(`meeting.eb.meeting.create.flow`)
648
691
 
@@ -679,21 +722,41 @@ weaver-work-cli --json meeting run meeting.eb.meeting.create.flow.apply \
679
722
  - **刷新为完整版**:审批通过后 `meeting.eb.meeting.list` 找到该会议 id →
680
723
  `meeting.invitation.render --mtId <id>`(只生成、不发送)。
681
724
  - ⛔ **不要因为"会议还在审批"就重复 `apply`** —— 会重复建单/重复建会。
682
- - **只有 `submit_mode=browser` 才可能返回 `status=BROWSER_FILL`**(2026-10-31 起默认不再进这一档,
683
- 见下方「提交方式」)。万一真进到这一档:CLI 会返回 `contextId` + `finalMarkdown`(那段 `<browsertool>` 表单)
684
- + `invitation.filePath`(按申请单预生成的预置函)。正确做法:把 `finalMarkdown` 内容原样作为本轮回复正文
685
- (从 `<browsertool>` 到 `</browsertool>`、不包代码块),并告诉用户邀请函已生成在哪个路径;
725
+ - **`status=BROWSER_FILL` 时**:CLI 返回 `contextId` + `finalMarkdown`(那段 `<browsertool>` 表单)
726
+ + `invitation`(按申请单预生成的预置函)。具体做法见下方「提交方式」第一条;
686
727
  ⛔ 不要说"流程没提交成功 / 没有邀请函",也不要因为"没有 requestId"重复 `apply`。
687
728
 
688
729
  - **✅ 提交方式默认「弹表单」**(2026-10-31 用户纠正;之前默认直提是错的,别再改回去):
689
- `create.flow.quick/apply` 默认 `submit_mode=browser` —— 在小e 里会**打开流程表单**,会议名称/时间/地点/参会人员/参会客户都已预填
690
- (浏览按钮传的是 id,页面能落值),用户核对、修改后**自己点提交**。
691
- - `browser` 下 CLI **不向服务端写入**,返回 `status=BROWSER_FILL` + `finalMarkdown`(那段 `<browsertool>`),
692
- 没有 `requestId` / `meetingId` —— ⛔ **不能说"流程已提交"**,要说"表单已打开,您在页面上核对后点提交";
693
- - 邀请函照给(预置函);审批通过、会议生成后再用 mtId 跑一次 `meeting.invitation.render` 拿完整版;
730
+ `create.flow.quick/apply` 默认 `submit_mode=browser` —— CLI **只把表单块返回给你,不会自己打开页面**。
731
+ 会议名称/时间/地点/参会人员/参会客户都已预填(浏览按钮传的是 id,页面能落值),用户核对、修改后自己点提交。
732
+ - **打开表单的唯一动作 = 把 `finalMarkdown` 原样贴进回复**(从 `<browsertool>` 到 `</browsertool>`):
733
+ - 放在回复**最后**、单独成段;⛔ 不包代码块、不省略、不转述、不改缩进;
734
+ - **发送前自检**:正文里必须能搜到 `<browsertool>` 和 `"toolId": "fill_form"`;搜不到就补上再发;
735
+ - ⛔ 没贴这段却写"表单已在右侧打开"=骗人,用户看到的就是没有表单(2026-11-02 就是这么翻车的)。
736
+ - `browser` 下 CLI **不向服务端写入**,返回 `status=BROWSER_FILL` + `finalMarkdown`,没有 `requestId` / `meetingId` ——
737
+ ⛔ **不能说"流程已提交"**,要说"表单已打开(贴在下方),您在页面上核对后点提交"。
738
+ - **邀请函照给,且 `invitation.text` 必须全文贴在回复里**(预置函:按申请单已知信息生成,字段可能不全)。
739
+ ⛔ 不要只丢一个 `filePath`;也**不要因为"流程还没提交成功"就不给邀请函** —— 这是两件独立的事。
694
740
  - 只有用户明确说"不用我确认,直接建"时才传 `submit_mode=direct`:直接写服务端 → 拿 `requestId` + `meetingId`,
695
- 会议当场生成;判断落库看这两个 id,并用 `meeting.eb.meeting.detail --id <meetingId>` 回读核对;
741
+ 会议当场生成;判断落库看这两个 id,并用 `meeting.eb.meeting.detail --id <meetingId>` 回读核对。
742
+ ⚠️ 小e 环境里即使传 `direct` 也可能仍返回 `BROWSER_FILL`(流程侧没有强制提交开关)——
743
+ **一切以返回的 `status` 为准**:只有 `status=COMPLETE` 且拿到 `meetingId` 才算建好,
744
+ 否则就是"表单已给出、等用户点提交",⛔ 不要说"已提交/已建好"。
696
745
  - ⛔ 不管哪种模式,`quick` / `apply` 都是真提交,必须已拿到用户对时间/地点/人/客户的确认。
746
+ - **群聊随建会同步创建**(2026-11-02 用户要求;CLI 自动做,不用你另外调):
747
+ `quick` / `apply` 的返回里带 `groupChat: {ok, groupId, groupName, created, bound}`。
748
+ - `groupChat.ok=true` → 群已按「会议名称」建好,并在群里发过一条会议信息;回复里说一句"会议群已建好(群名=会议名称)"即可;
749
+ - `bound=true` → 群 id 已回写会议记录 `imchat_groupid`;`bound=false`(browser 档必然如此,因为还没有会议 id)→
750
+ 告诉用户"群已建好,会议生成后再绑定";
751
+ **用户说"我提交了/审批过了"之后**,跑 `meeting.eb.meeting.group.ensure`:
752
+ - 有流程单号 → 传 `{"requestId":"<单据id>"}`(**首选**,会议记录的 `request_id` 是唯一凭据);
753
+ - 没单号 → 传 `{"lookupPending":true}`:CLI 读本机「表单已发出」记录,用建群时记的基线单号
754
+ 找出之后新提交的那张,再按它反查会议。
755
+ ⛔ **不要用会议名称反查**(用户可能在表单里改名、也可能有同名会议);
756
+ 返回 `requestCandidates` 说明同时有多张新单 —— 让用户确认是哪一张,别猜;
757
+ 返回 `meetingLookup` 就是找到的会议;查不到会明说"可能还没提交/还在审批",别反复重试;
758
+ - `groupChat.ok=false` → 如实说"群没建成功"+原因;⛔ 不许编群 id、不许说群已建好;
759
+ - ⛔ 不要重复建群:重试前先看已有返回里有没有 `groupChat.groupId`。
697
760
  - **⛔ 提交前必须把 `formPreview` 贴给用户过一眼**(2.44.0 起,2026-10-31 用户要求
698
761
  「为什么没有直接发起流程表单让我确认重填后让我点击提交」):
699
762
  浏览器填单那条路走不通(浏览按钮不赋值),所以用"把要提交的内容原样列出来"替代核对环节。
@@ -155,22 +155,36 @@ video_taskResult == '2' → 生成失败
155
155
 
156
156
  ## ESB 解析链路
157
157
 
158
- 三个动作流都可用 `esbFlowId` 或 `uniqueIndent` 触发,走 `esb.trigger.prepare → apply`。
158
+ 三个动作流走 `esb.trigger.prepare → apply`,但**目标入参按 id 形态二选一,不能混**:
159
159
 
160
- > ⚠️ **表里的 id / primary_key 都是租户级配置**,各环境不同,换租户必须替换。
161
- > `meeting.minute.fromGroupChat` 内置了参考租户的默认值(生成纪要 / 生成文档两条),
162
- > 其他租户用入参 `flowGenerateMinutes` / `flowGenerateDocument` 覆盖;解析附件那条由调用方显式传 `esbFlowId`。
160
+ | id 形态 | 用哪个入参 | 服务端行为 |
161
+ | --- | --- | --- |
162
+ | 19 位数字(如 `1333212605456498718`) | `esbFlowId` | 正常 |
163
+ | 32 位十六进制 primary_key(如 `7204936B2ACC4B12A1CECD1DAB60DD97`) | **`uniqueIndent`** | 传进 `esbFlowId` 会报 `can not cast to long, value : 7204…`(查模板)或 `{"code":400,"msg":"系统错误"}`(触发) |
163
164
 
164
- | 用途 | esbFlowId | primary_key | mainTable |
165
- | --- | --- | --- | --- |
166
- | **解析附件**(文档同步 / 音视频异步) | `<解析附件flowId>` | `<解析附件uniqueIndent>` | `mtAIMinutePageID*`、`minuteId*`、`mt_id*`;可选 `doc_file` / `video_file` / `file_type` |
167
- | **保存解析并生成文档** | `<生成文档flowId>` | `<生成文档uniqueIndent>` | `parse_content*`、`mt_minute_id*` |
168
- | 解析文本生成智能纪要(备用) | `<生成纪要flowId>` | `<生成纪要uniqueIndent>` | `minuteId*` |
165
+ > ⛔ **本租户(e-cology / 泛微网络)后台只给 primary_key,没有数字 id** —— 所以一律走 `uniqueIndent`。
166
+ > `uniqueIndent` 触发时 prepare 会提示"无法按 applicationId 回查模板",这是**正常**的,不影响执行。
167
+
168
+ | 用途 | primary_key(本租户) | mainTable |
169
+ | --- | --- | --- |
170
+ | **解析附件**(文档同步 / 音视频异步) | `AD8EE1B345FD4570AE1D0882FD1B5927` | `mtAIMinutePageID*`、`minuteId*`、`mt_id*`;可选 `doc_file` / `video_file` / `file_type` |
171
+ | **保存解析并生成文档** | `1F638AE31BC746C28A1D9DCF26D91037` | `parse_content*`、`mt_minute_id*` |
172
+ | **解析文本生成智能纪要** | `7204936B2ACC4B12A1CECD1DAB60DD97` | `minuteId*` |
173
+ | 获取解析纪要音频结果 | `1DB38C10699A47E89DD7CEFDAA76B8A1` | **由后台解析任务自己触发,不要手动调**(见下节) |
169
174
 
170
- 调用示例(解析附件):
175
+ 智能纪要页面 id(`mt_minutes_ai_page`,租户级、无接口可查):**`1250830691712172036`**。
176
+ `meeting.minute.fromGroupChat` 已内置上述三个 primary_key 与页面 id 作默认值,
177
+ 换租户用入参 `flowGenerateMinutes` / `flowGenerateDocument` / `aiPageId` 覆盖。
178
+
179
+ 调用示例(生成纪要文案 → 生成文档,实测 resultCode=200):
171
180
 
172
181
  ```bash
173
- weaver-work-cli esb run esb.trigger.prepare --input-json '{"employeeId":"<员工id>","esbFlowId":"<解析附件flowId>","customParams":{"mainTable":{"minuteId":"<纪要id>","mt_id":"<会议id>","file_type":"1","video_file":"<附件id>","mtAIMinutePageID":""}}}'
182
+ # ① 解析文本 → 智能纪要文本(写 parse_content)
183
+ weaver-work-cli esb run esb.trigger.prepare --input-json '{"employeeId":"<员工id>","uniqueIndent":"7204936B2ACC4B12A1CECD1DAB60DD97","customParams":{"mainTable":{"minuteId":"<纪要id>"}}}'
184
+ weaver-work-cli esb run esb.trigger.apply --input-json '{"continuation":"<上一步返回值>","confirm":true}'
185
+
186
+ # ② 保存解析并生成文档(回写 mt_minutes,返回 customData.mainTable.wdID)
187
+ weaver-work-cli esb run esb.trigger.prepare --input-json '{"employeeId":"<员工id>","uniqueIndent":"1F638AE31BC746C28A1D9DCF26D91037","customParams":{"mainTable":{"parse_content":"<①产出的正文>","mt_minute_id":"<纪要id>"}}}'
174
188
  weaver-work-cli esb run esb.trigger.apply --input-json '{"continuation":"<上一步返回值>","confirm":true}'
175
189
  ```
176
190
 
Binary file
@@ -67,7 +67,7 @@ Token、Cookie、ETEAMSID 和业务凭证**只能由 `weaver-work-cli` 内部读
67
67
 
68
68
  下述 `auth xiaoe` 流程仅适用于上述已确认的 XiaoE 智能体环境;其他智能体不执行宿主脚本检测,直接进入浏览器 OAuth 流程。`auth xiaoe` 命令本身就是认证方式的显式选择;`hasXiaoEEnv` 使用桌面的 `--runtime-agent xiaoe` 或任意 `XIAOE_*` 环境变量识别运行环境。
69
69
 
70
- 首次缺少本地会话时,已识别为 XiaoE 的业务命令会在 CLI 公共会话加载层自动获取登录信息;如需提前登录,只运行 `weaver-work-cli auth xiaoe`。CLI 内部按「平台凭证 Node 换票 → CLI 自带 `xiaoe_env.py` 兼容换票 → 宿主 `tokens.py` → 桌面 XiaoE 客户端会话 → `WEAVER_XIAOE_*` 环境映射」顺序取凭据;其中 `XIAOE_DOMAIN` 是 XiaoE/erpa 换票服务地址,不是 E10 地址。`xiaoe_env.py` 属于 CLI npm 包,不放入 shared Skill,且只能由 CLI 内部执行和解析。已有 XiaoE 会话被接口明确拒绝为失效时,CLI 根据会话元数据在公共请求层静默恢复并重放一次;这个流程绕开 shared Skill,Agent 不参与,也不得再补跑登录或原业务命令。失败时 CLI 返回最终结构化错误。
70
+ 首次缺少本地会话时,已识别为 XiaoE 的业务命令会在 CLI 公共会话加载层自动获取登录信息;如需提前登录,只运行 `weaver-work-cli auth xiaoe`。CLI 内部按「平台凭证 Node 换票 → CLI 自带 `xiaoe_env.py` 兼容换票 → 宿主 `tokens.py` → 桌面 XiaoE 客户端会话 → `WEAVER_XIAOE_*` 环境映射」顺序取凭据;其中 `XIAOE_DOMAIN` 是 XiaoE/erpa 换票服务地址,不是 E10 地址。`xiaoe_env.py` 属于 CLI npm 包,不放入 shared Skill,且只能由 CLI 内部执行和解析。已有 XiaoE 会话达到换票结果的 `expiryTimes` 时,CLI 会在业务请求发出前静默换票;已有 XiaoE 会话被接口明确拒绝为失效时,CLI 根据会话元数据在公共请求层静默恢复并重放一次。这个流程绕开 shared Skill,Agent 不参与,也不得再补跑登录或原业务命令。失败时 CLI 返回最终结构化错误。
71
71
 
72
72
  1. 首次无本地登录态、需要在业务命令之前显式建立 E10 会话时,可运行:
73
73
  ```text
@@ -29,7 +29,7 @@ weaver-work-cli auth whoami
29
29
  - 业务命令失败,stderr JSON 中 `error.type === "authentication"`(如 `E10 auth not found`)
30
30
  - 业务命令返回 `error.subtype === "session_expired"`(E10 登录态 401/302 失效)
31
31
 
32
- CLI 的认证错误只是状态信号,不是登录命令。已有 XiaoE 会话过期时,公共请求层会根据持久化会话的 `agentType` / `loginSource` 自动恢复,不依赖 `XIAOE_*`、`--runtime-agent xiaoe`、shared Skill 或 Agent。业务命令无可读取的本地会话时,若当前 `XIAOE_*`、`--runtime-agent xiaoe` 等标记已识别为 XiaoE,公共会话加载层也会先静默获取登录信息。失败时返回 `xiaoe_credential_unavailable`,提示已尝试自动获取,Agent 只反馈并停止;不要再次登录或重跑。需要在业务命令之前主动建立会话时,可按下述流程显式登录。
32
+ CLI 的认证错误只是状态信号,不是登录命令。已有 XiaoE 会话达到换票结果的 `expiryTimes` 时,CLI 会在业务请求发出前静默换票;被接口判定过期时,公共请求层会根据持久化会话的 `agentType` / `loginSource` 自动恢复,不依赖 `XIAOE_*`、`--runtime-agent xiaoe`、shared Skill 或 Agent。业务命令无可读取的本地会话时,若当前 `XIAOE_*`、`--runtime-agent xiaoe` 等标记已识别为 XiaoE,公共会话加载层也会先静默获取登录信息。失败时返回 `xiaoe_credential_unavailable`,提示已尝试自动获取,Agent 只反馈并停止;不要再次登录或重跑。需要在业务命令之前主动建立会话时,可按下述流程显式登录。
33
33
 
34
34
  此时**禁止**:
35
35
 
@@ -100,6 +100,8 @@ cliHelp: "weaver-work-cli workflow --help"
100
100
  - 两类词**同时出现** → 按 `workflow.todoSmartConvert`(归拢优先;`todoStat` 只服务纯统计诉求)。
101
101
  - 三类词**都没有**(只是"有哪些待办 / 列出来 / 多少条")→ `workflow.search`,不要调 `workflow.todoStat`。
102
102
  - **例外**:「批量处理待办」后面跟了明确写动作(提交 / 置已读 / 催办 / 共享 / 转发 / 退回)时,仍走 `workflow.batch*`,不要因为出现"批量处理"就转成汇总。
103
+ - **汇总范围的小分类(2026-11-02)**:用户原话出现小分类词(待处理 / 待阅 / 超时 / 未读 / 被退回 / 草稿 / 抄送 / 转发…)时,`prep`/`run` 要传 `--sub-category <词>`(或 `--tabid <tabid>`,显式 tabid 优先;todo 词表:待处理=7、待阅=5、超时=6、被退回=8、草稿=14、全部=0),否则「汇总下我的待处理」会静默取回全部(tabid=0)。未表达小分类时不传。仅影响汇总取数,`workflow.search` 不受影响。
104
+ - **默认排除抄送类身份(2026-11-03,generalRules)**:汇总取数默认带 `--isremarks`(排除抄送类身份 30/31/33/70/71,见 default.json `generalRules.identityFilter`),取数 warning 会提示。**用户提示词显式提到抄送/包含抄送/只要抄送/指定身份时,必须显式传 `--isremarks` 覆盖默认值**(全量=全部 23 个身份码;只要抄送=`30,31,33,70,71`)。
103
105
  - **不要**为了"先看一眼有哪些"去调 `workflow.todoStat` / `workflow.search` 再回头走汇总链路——那会多花一轮,且交付形态会被带偏。
104
106
 
105
107
 
@@ -209,6 +211,8 @@ weaver-work-cli --json workflow aigroup set --input-json '{"workflowName":["费
209
211
  ### 5) 命令输出读取纪律(禁止截断、禁止重复取数)
210
212
 
211
213
  - **禁止对命令输出做管道截断**:不要用 `| head`、`| tail`、`| more` 等处理 `weaver-work-cli --json ...` 的 stdout。截断会破坏 JSON,并且必然导致重复执行同一条命令(实测最常见的一条命令被执行 2 次,就是 `| head` 引起的)。
214
+ - **`tools/` 脚本(`todo-summary.cjs` 等)的输出同样禁止截断、禁止重跑**(2026-11-02 用户定稿,实测踩坑):这类输出是**纯文本素材**而不是 JSON——截断不会报错,只会让素材**悄悄缺条**。实测 `build` 的「待写摘要」一节共 17.6 KB / 9 条,用 `| head -c 9000` 只看得到前 5 条,且此时缓存已经写过、重跑 `build` 也补不回被跳过的那几条,最后只能把整套链路重跑一遍(同一任务 12 轮 → 1 轮,脚本耗时 71.9s → 3.0s)。因此:**脚本输出一律整段直读,不 `head` / `head -c` / `tail` / `grep` 过滤正文**;确需留存时用 `> <out>/xxx.log` 一次落盘、再用 Read **一次**读完(只允许这种"整段落盘",不许分段看、不许先截断看一眼再重跑)。`prep` / `build` / `finish` 的 stdout 已包含下一步所需的全部内容(组/行/序号、待写摘要素材、最终结构、自检、载荷),**不需要、也不允许**为了"确认一下"再去翻中间产物。
215
+ - **中间产物不要重复探查**:`<out>/` 下的 `items.json` / `state.json` / `smartgroup.json` / `build.log` / `finish.log` 是脚本自己的中间态;不要 `ls` 输出目录、不要读 `state.json` 核对、不要翻日志找已完成的信息——每次探查≈1 轮≈20s(实测一次任务这样白花 3 轮、约 25s)。只有 stdout 里确实缺、且下一步**必须**用的字段(如某条 `requestId`)才去读,读就一次读完。
212
216
  - **一律直读 stdout(失败时读 stderr,见下条),不要落盘、不要写脚本回读**:`--json` 输出是**多行 pretty JSON**,且已按体积做过控制(`workflow.todoStat` 约 20 KB/400 余行;`workflow.search` 的条目已收窄到必要字段、泛化待办查询实测约 **14 KB**;`workflow.detail` 只有几 KB;`workflow.createForm` / `editLoad` 遇到大表单会落盘、stdout 只回提示)——都在宿主单次输出上限以内——**直接读即可**。注意 `workflow.createForm` 的 `fields` / `historyRequestData` / `systemFields` 三个键是**压成单行**输出的(只去缩进、内容一字不改),其中 `fields` 那一行字段多时可能上万字符——**这属正常,不是被截断**,照常按 JSON 解析。**禁止**把 stdout 重定向到文件(`> /tmp/xxx.json`)再写脚本解析回读:它不会带来任何新信息,只会白白多出一次工具往返(实测多一轮约 +7s,占整轮耗时可观)。
213
217
  - **`workflow.search` 的体积纪律(别等被截断)**:泛化待办按要求走 `planJson` 分组(各组独立渲染、各 10 条,体积天然分散);要看更多用 `current` 分页续查;**不要靠加大 `pageSize` 一次拉全**。CLI 侧已按 **24 KB(美化口径)** 设体积守卫:超线时自动把完整结果落盘,并在结果**最前面**给出 `resultFile`(结构化 JSON)+ `resultMdFile`(纯 `finalMarkdown`),**stdout 仍保留全量 `finalMarkdown`** ⇒ 正常渲染**直接用 stdout 即可、不需要读文件**;只有需要完整结构化明细(如逐条 `requestId`)时才从 `resultFile` 按需提取,并一次读完(不要 `| head`/`| tail`)。宿主把输出截成一小段预览时:**不要重跑同一条命令**(结果一样、白烧一轮),改用更小页或 `planJson` 分组重取;宿主提示的落盘副本(`~/.workbuddy/projects/**/tool-results/**`)**不能用 Read 工具读**(会拿 `Permission … has been denied`),确实要用时以 shell 命令读取并只提取需要的字段。
214
218
  - **失败出口在 stderr(stdout 为空即失败)**:命令失败时退出码非 0、**错误 JSON 只出现在 stderr**(`{ok:false, error:{type,subtype,message,retryable}}`),**stdout 是空的**。因此:**stdout 为空 → 一律视为失败并去读 stderr**,按 `error.subtype` 决定下一步(如 `todo_stat_requires_smart_mode` → 不要重试 todoStat,改走 `workflow.search`;`plan_requires_smart_mode` → 去掉 `planJson` 重查);判断成败**以 `ok` 字段为准**,宿主界面把命令显示成"运行成功"只代表进程结束,不代表业务成功。
@@ -1,5 +1,17 @@
1
1
  {
2
2
  "schemaVersion": 3,
3
+ "generalRules": {
4
+ "description": "通用规则区(2026-11-03):不与任何工作流绑定的全局行为定义;旧版本 CLI 忽略本区块。后续新的通用规则以新键扩展(预留)。",
5
+ "priorityGroup": {
6
+ "description": "第一分组「建议优先处理的流程」的入选规则(叠加视图,入选条目仍保留在原工作流分组)。",
7
+ "title": "建议优先处理的流程",
8
+ "rules": [
9
+ "overdue",
10
+ "levelFallback"
11
+ ],
12
+ "rulesNote": "overdue=已超时(overTime/userOverTime 判定,与紧急程度无关);levelFallback=紧急程度降级链(有 2 取 2,无 2 取 1,全 0 不因等级入选)。预留:expiringToday(今天到期)等后续扩展,未启用。"
13
+ }
14
+ },
3
15
  "defaults": {
4
16
  "weight": 999,
5
17
  "needSummary": true,