@rezti/dsh-rez-suite 0.1.78 → 0.1.80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/style.css CHANGED
@@ -1,4 +1,4 @@
1
- .r9dlUa_wordmark {
1
+ .VSlLJG_wordmark {
2
2
  min-width: 0;
3
3
  color: inherit;
4
4
  align-items: center;
@@ -7,7 +7,7 @@
7
7
  overflow: hidden;
8
8
  }
9
9
 
10
- .r9dlUa_mark {
10
+ .VSlLJG_mark {
11
11
  object-fit: contain;
12
12
  flex: none;
13
13
  width: 28px;
@@ -15,7 +15,7 @@
15
15
  display: block;
16
16
  }
17
17
 
18
- .r9dlUa_markOnly {
18
+ .VSlLJG_markOnly {
19
19
  object-fit: contain;
20
20
  width: 100%;
21
21
  max-width: 28px;
@@ -24,7 +24,7 @@
24
24
  display: block;
25
25
  }
26
26
 
27
- .r9dlUa_name {
27
+ .VSlLJG_name {
28
28
  letter-spacing: .02em;
29
29
  white-space: nowrap;
30
30
  text-overflow: ellipsis;
@@ -33,7 +33,7 @@
33
33
  overflow: hidden;
34
34
  }
35
35
 
36
- .r9dlUa_badge {
36
+ .VSlLJG_badge {
37
37
  letter-spacing: .06em;
38
38
  opacity: .72;
39
39
  white-space: nowrap;
@@ -45,14 +45,14 @@
45
45
  font-weight: 700;
46
46
  line-height: 1.2;
47
47
  }
48
- .LOjPTW_shell {
48
+ .AQjtka_shell {
49
49
  flex-direction: column;
50
50
  gap: 12px;
51
51
  min-width: 0;
52
52
  display: flex;
53
53
  }
54
54
 
55
- .LOjPTW_card {
55
+ .AQjtka_card {
56
56
  border: 1px solid var(--dsw-alias-border-l2);
57
57
  background: var(--dsw-alias-bg-layer-3);
58
58
  border-radius: 12px;
@@ -60,16 +60,16 @@
60
60
  transition: border-color .16s, background .16s;
61
61
  }
62
62
 
63
- .LOjPTW_card:hover {
63
+ .AQjtka_card:hover {
64
64
  border-color: var(--dsw-alias-label-dimmed);
65
65
  }
66
66
 
67
- .LOjPTW_cardOpen {
67
+ .AQjtka_cardOpen {
68
68
  background: var(--dsw-alias-bg-layer-2);
69
69
  border-color: var(--dsw-alias-label-dimmed);
70
70
  }
71
71
 
72
- .LOjPTW_header {
72
+ .AQjtka_header {
73
73
  appearance: none;
74
74
  width: 100%;
75
75
  font: inherit;
@@ -85,12 +85,12 @@
85
85
  display: flex;
86
86
  }
87
87
 
88
- .LOjPTW_header:focus-visible {
88
+ .AQjtka_header:focus-visible {
89
89
  outline: 2px solid var(--dsw-alias-brand-primary);
90
90
  outline-offset: -2px;
91
91
  }
92
92
 
93
- .LOjPTW_headText {
93
+ .AQjtka_headText {
94
94
  flex-direction: column;
95
95
  flex: 1;
96
96
  gap: 4px;
@@ -98,43 +98,43 @@
98
98
  display: flex;
99
99
  }
100
100
 
101
- .LOjPTW_name {
101
+ .AQjtka_name {
102
102
  color: var(--dsw-alias-label-primary);
103
103
  font-size: 15px;
104
104
  font-weight: 600;
105
105
  line-height: 1.4;
106
106
  }
107
107
 
108
- .LOjPTW_description {
108
+ .AQjtka_description {
109
109
  color: var(--dsw-alias-label-tertiary);
110
110
  font-size: 13px;
111
111
  line-height: 1.5;
112
112
  }
113
113
 
114
- .LOjPTW_chevron {
114
+ .AQjtka_chevron {
115
115
  color: var(--dsw-alias-label-tertiary);
116
116
  flex: none;
117
117
  transition: transform .16s;
118
118
  }
119
119
 
120
- .LOjPTW_chevronOpen {
120
+ .AQjtka_chevronOpen {
121
121
  transform: rotate(180deg);
122
122
  }
123
123
 
124
- .LOjPTW_body {
124
+ .AQjtka_body {
125
125
  border-top: 1px solid var(--dsw-alias-border-l2);
126
126
  margin: 0 16px 12px;
127
127
  padding-top: 8px;
128
128
  }
129
129
 
130
- .LOjPTW_tabBar {
130
+ .AQjtka_tabBar {
131
131
  border-bottom: 1px solid var(--dsw-alias-border-l1);
132
132
  flex: none;
133
133
  gap: 2px;
134
134
  display: flex;
135
135
  }
136
136
 
137
- .LOjPTW_tab {
137
+ .AQjtka_tab {
138
138
  color: var(--dsw-alias-label-secondary);
139
139
  cursor: pointer;
140
140
  background: none;
@@ -145,18 +145,18 @@
145
145
  font-size: 13px;
146
146
  }
147
147
 
148
- .LOjPTW_tab:hover {
148
+ .AQjtka_tab:hover {
149
149
  color: var(--dsw-alias-label-primary);
150
150
  background: var(--dsw-alias-interactive-bg-hover);
151
151
  }
152
152
 
153
- .LOjPTW_tab[data-active] {
153
+ .AQjtka_tab[data-active] {
154
154
  color: var(--dsw-alias-label-primary);
155
155
  border-bottom-color: var(--dsw-alias-state-business-primary);
156
156
  font-weight: 600;
157
157
  }
158
158
 
159
- .LOjPTW_tabBody {
159
+ .AQjtka_tabBody {
160
160
  flex-direction: column;
161
161
  gap: 12px;
162
162
  min-width: 0;
@@ -164,7 +164,7 @@
164
164
  display: flex;
165
165
  }
166
166
 
167
- .LOjPTW_section {
167
+ .AQjtka_section {
168
168
  border: 1px solid var(--dsw-alias-border-l1);
169
169
  background: var(--dsw-alias-bg-layer-1, var(--dsw-alias-bg-base));
170
170
  border-radius: 10px;
@@ -174,26 +174,26 @@
174
174
  display: flex;
175
175
  }
176
176
 
177
- .LOjPTW_sectionTitle {
177
+ .AQjtka_sectionTitle {
178
178
  color: var(--dsw-alias-label-primary);
179
179
  margin: 0;
180
180
  font-size: 13px;
181
181
  font-weight: 600;
182
182
  }
183
183
 
184
- .LOjPTW_field {
184
+ .AQjtka_field {
185
185
  flex-direction: column;
186
186
  align-items: stretch;
187
187
  gap: 4px;
188
188
  display: flex;
189
189
  }
190
190
 
191
- .LOjPTW_label {
191
+ .AQjtka_label {
192
192
  color: var(--dsw-alias-label-secondary);
193
193
  font-size: 12.5px;
194
194
  }
195
195
 
196
- .LOjPTW_input, .LOjPTW_select {
196
+ .AQjtka_input, .AQjtka_select {
197
197
  color: var(--dsw-alias-label-primary);
198
198
  background: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer-3));
199
199
  border: 1px solid var(--dsw-alias-border-l2);
@@ -203,24 +203,24 @@
203
203
  font-size: 13px;
204
204
  }
205
205
 
206
- .LOjPTW_checkbox {
206
+ .AQjtka_checkbox {
207
207
  justify-self: start;
208
208
  width: 15px;
209
209
  height: 15px;
210
210
  }
211
211
 
212
- .LOjPTW_wecomRow {
212
+ .AQjtka_wecomRow {
213
213
  grid-template-columns: minmax(7rem, .8fr) minmax(8rem, 1fr) minmax(8rem, 1fr) auto;
214
214
  align-items: end;
215
215
  gap: 8px;
216
216
  display: grid;
217
217
  }
218
218
 
219
- .LOjPTW_wecomRow .LOjPTW_message {
219
+ .AQjtka_wecomRow .AQjtka_message {
220
220
  grid-column: 1 / -1;
221
221
  }
222
222
 
223
- .LOjPTW_toolbar {
223
+ .AQjtka_toolbar {
224
224
  flex-wrap: wrap;
225
225
  flex: none;
226
226
  align-items: center;
@@ -228,7 +228,7 @@
228
228
  display: flex;
229
229
  }
230
230
 
231
- .LOjPTW_button {
231
+ .AQjtka_button {
232
232
  border: 1px solid var(--dsw-alias-border-l2);
233
233
  background: var(--dsw-specific-input-major, var(--dsw-alias-bg-layer-3));
234
234
  color: var(--dsw-alias-label-primary);
@@ -238,28 +238,28 @@
238
238
  font-size: 13px;
239
239
  }
240
240
 
241
- .LOjPTW_button:hover:not(:disabled) {
241
+ .AQjtka_button:hover:not(:disabled) {
242
242
  background: var(--dsw-alias-interactive-bg-hover);
243
243
  }
244
244
 
245
- .LOjPTW_button:disabled {
245
+ .AQjtka_button:disabled {
246
246
  opacity: .4;
247
247
  cursor: default;
248
248
  }
249
249
 
250
- .LOjPTW_primaryButton {
250
+ .AQjtka_primaryButton {
251
251
  background: var(--dsw-alias-label-primary);
252
252
  border-color: var(--dsw-alias-label-primary);
253
253
  color: var(--dsw-alias-bg-layer-3);
254
254
  }
255
255
 
256
- .LOjPTW_dangerButton {
256
+ .AQjtka_dangerButton {
257
257
  background: var(--dsw-alias-state-danger, #c62828);
258
258
  border-color: var(--dsw-alias-state-danger, #c62828);
259
259
  color: #fff;
260
260
  }
261
261
 
262
- .LOjPTW_qr {
262
+ .AQjtka_qr {
263
263
  background: #fff;
264
264
  border-radius: 8px;
265
265
  width: 200px;
@@ -268,30 +268,30 @@
268
268
  display: block;
269
269
  }
270
270
 
271
- .LOjPTW_message {
271
+ .AQjtka_message {
272
272
  color: var(--dsw-alias-label-secondary);
273
273
  margin: 0;
274
274
  font-size: 12.5px;
275
275
  line-height: 1.5;
276
276
  }
277
277
 
278
- .LOjPTW_error {
278
+ .AQjtka_error {
279
279
  color: var(--dsw-alias-state-danger, #c62828);
280
280
  }
281
281
 
282
- .LOjPTW_tableWrap {
282
+ .AQjtka_tableWrap {
283
283
  border: 1px solid var(--dsw-alias-border-l1);
284
284
  border-radius: 10px;
285
285
  overflow: auto;
286
286
  }
287
287
 
288
- .LOjPTW_table {
288
+ .AQjtka_table {
289
289
  border-collapse: collapse;
290
290
  width: 100%;
291
291
  font-size: 12.5px;
292
292
  }
293
293
 
294
- .LOjPTW_table th {
294
+ .AQjtka_table th {
295
295
  z-index: 1;
296
296
  text-align: left;
297
297
  background: var(--dsw-alias-bg-layer-2, var(--dsw-alias-bg-layer-1));
@@ -300,60 +300,60 @@
300
300
  top: 0;
301
301
  }
302
302
 
303
- .LOjPTW_table td {
303
+ .AQjtka_table td {
304
304
  border-top: 1px solid var(--dsw-alias-border-l1);
305
305
  vertical-align: top;
306
306
  padding: 8px 10px;
307
307
  }
308
308
 
309
- .LOjPTW_boardTitle {
309
+ .AQjtka_boardTitle {
310
310
  color: var(--dsw-alias-label-primary);
311
311
  margin: 16px 0 4px;
312
312
  font-size: 13px;
313
313
  font-weight: 600;
314
314
  }
315
315
 
316
- .LOjPTW_badge {
316
+ .AQjtka_badge {
317
317
  border-radius: 999px;
318
318
  padding: 2px 8px;
319
319
  font-size: 11px;
320
320
  display: inline-block;
321
321
  }
322
322
 
323
- .LOjPTW_badgeOk {
323
+ .AQjtka_badgeOk {
324
324
  color: #229a55;
325
325
  background: #229a5524;
326
326
  }
327
327
 
328
- .LOjPTW_badgeFail {
328
+ .AQjtka_badgeFail {
329
329
  color: #c62828;
330
330
  background: #c6282824;
331
331
  }
332
332
 
333
- .LOjPTW_cards {
333
+ .AQjtka_cards {
334
334
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
335
335
  gap: 8px;
336
336
  display: grid;
337
337
  }
338
338
 
339
- .LOjPTW_stat {
339
+ .AQjtka_stat {
340
340
  border: 1px solid var(--dsw-alias-border-l1);
341
341
  border-radius: 10px;
342
342
  padding: 10px;
343
343
  }
344
344
 
345
- .LOjPTW_cardLabel {
345
+ .AQjtka_cardLabel {
346
346
  color: var(--dsw-alias-label-tertiary);
347
347
  font-size: 11px;
348
348
  }
349
349
 
350
- .LOjPTW_cardValue {
350
+ .AQjtka_cardValue {
351
351
  color: var(--dsw-alias-label-primary);
352
352
  font-size: 15px;
353
353
  font-weight: 600;
354
354
  }
355
355
 
356
- .LOjPTW_upgradeLayer {
356
+ .AQjtka_upgradeLayer {
357
357
  flex: none;
358
358
  align-items: center;
359
359
  width: 100%;
@@ -363,13 +363,13 @@
363
363
  position: relative;
364
364
  }
365
365
 
366
- .LOjPTW_upgradeLayerRail {
366
+ .AQjtka_upgradeLayerRail {
367
367
  width: 36px;
368
368
  height: 36px;
369
369
  margin: 0;
370
370
  }
371
371
 
372
- .LOjPTW_upgradeButton {
372
+ .AQjtka_upgradeButton {
373
373
  width: calc(100% + 4px);
374
374
  height: 42px;
375
375
  color: var(--dsw-alias-label-primary);
@@ -387,21 +387,21 @@
387
387
  overflow: hidden;
388
388
  }
389
389
 
390
- .LOjPTW_upgradeButton:hover, .LOjPTW_upgradeButton[data-outdated] {
390
+ .AQjtka_upgradeButton:hover, .AQjtka_upgradeButton[data-outdated] {
391
391
  background: var(--dsw-alias-interactive-bg-hover);
392
392
  }
393
393
 
394
- .LOjPTW_upgradeButton:disabled {
394
+ .AQjtka_upgradeButton:disabled {
395
395
  cursor: progress;
396
396
  opacity: .72;
397
397
  }
398
398
 
399
- .LOjPTW_upgradeButton:focus-visible {
399
+ .AQjtka_upgradeButton:focus-visible {
400
400
  outline: 2px solid var(--dsw-alias-brand-primary);
401
401
  outline-offset: -2px;
402
402
  }
403
403
 
404
- .LOjPTW_upgradeLayerRail .LOjPTW_upgradeButton {
404
+ .AQjtka_upgradeLayerRail .AQjtka_upgradeButton {
405
405
  border-radius: 50%;
406
406
  justify-content: center;
407
407
  gap: 0;
@@ -410,14 +410,14 @@
410
410
  padding: 0;
411
411
  }
412
412
 
413
- .LOjPTW_upgradeLabel {
413
+ .AQjtka_upgradeLabel {
414
414
  text-overflow: ellipsis;
415
415
  white-space: nowrap;
416
416
  min-width: 0;
417
417
  overflow: hidden;
418
418
  }
419
419
 
420
- .LOjPTW_upgradeMeta {
420
+ .AQjtka_upgradeMeta {
421
421
  color: var(--dsw-alias-label-tertiary);
422
422
  font-variant-numeric: tabular-nums;
423
423
  flex: none;
@@ -426,7 +426,7 @@
426
426
  line-height: 16px;
427
427
  }
428
428
 
429
- .LOjPTW_upgradeDot {
429
+ .AQjtka_upgradeDot {
430
430
  background: var(--dsw-alias-brand-primary);
431
431
  border-radius: 50%;
432
432
  flex: none;
@@ -434,26 +434,26 @@
434
434
  height: 7px;
435
435
  }
436
436
 
437
- .LOjPTW_changelogDate {
437
+ .AQjtka_changelogDate {
438
438
  font-variant-numeric: tabular-nums;
439
439
  color: var(--dsw-alias-label-tertiary);
440
440
  margin-left: 8px;
441
441
  font-weight: 500;
442
442
  }
443
443
 
444
- .LOjPTW_changelogBlock {
444
+ .AQjtka_changelogBlock {
445
445
  flex-direction: column;
446
446
  gap: 6px;
447
447
  display: flex;
448
448
  }
449
449
 
450
- .LOjPTW_changelogHeading {
450
+ .AQjtka_changelogHeading {
451
451
  color: var(--dsw-alias-label-secondary);
452
452
  font-size: 12.5px;
453
453
  font-weight: 600;
454
454
  }
455
455
 
456
- .LOjPTW_changelogList {
456
+ .AQjtka_changelogList {
457
457
  color: var(--dsw-alias-label-primary);
458
458
  flex-direction: column;
459
459
  gap: 6px;
@@ -464,11 +464,11 @@
464
464
  display: flex;
465
465
  }
466
466
 
467
- .LOjPTW_changelogList li + li {
467
+ .AQjtka_changelogList li + li {
468
468
  margin: 0;
469
469
  }
470
470
 
471
- .LOjPTW_changelogCode {
471
+ .AQjtka_changelogCode {
472
472
  background: var(--dsw-alias-bg-layer-3);
473
473
  border: 1px solid var(--dsw-alias-border-l2);
474
474
  word-break: break-all;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rezti/dsh-rez-suite",
3
- "description": "ReZ-TI one-package install for DeepSeek Harness. Update with: dsh plugin --profile web add @rezti/dsh-rez-suite@0.1.78",
4
- "version": "0.1.78",
3
+ "description": "ReZ-TI one-package install for DeepSeek Harness. Update with: dsh plugin --profile web add @rezti/dsh-rez-suite@0.1.80",
4
+ "version": "0.1.80",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": "^22.19.0 || >=24.0.0"
@@ -39,7 +39,7 @@
39
39
  "@modelcontextprotocol/sdk": "^1.30.0",
40
40
  "@deepseek-ai/dsh-schedule": "0.1.5-rc.2",
41
41
  "@deepseek-ai/dsh-time-context": "0.1.5-rc.2",
42
- "@rezti/dsh-rez-comms": "^0.1.0",
42
+ "@rezti/dsh-rez-comms": "^0.1.1",
43
43
  "@rezti/dsh-rez-gsc": "^0.1.2",
44
44
  "@rezti/dsh-rez-ha-bridge": "^0.1.10",
45
45
  "@rezti/dsh-rez-intent": "^0.1.6",
@@ -50,7 +50,7 @@
50
50
  "@rezti/dsh-rez-tapd": "^0.1.2",
51
51
  "@rezti/dsh-rez-token-manager": "^0.1.5",
52
52
  "@rezti/dsh-rez-vision": "^0.1.4",
53
- "@rezti/dsh-rez-wechat": "0.1.24",
53
+ "@rezti/dsh-rez-wechat": "0.1.25",
54
54
  "@rezti/dsh-web-file-uploader": "0.2.3",
55
55
  "@wecom/cli": "1.1.0",
56
56
  "zod": "^4.4.3"
@@ -116,4 +116,4 @@
116
116
  "typecheck": "tsc --noEmit",
117
117
  "test": "vitest run"
118
118
  }
119
- }
119
+ }
package/src/boss/mount.ts CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  type RoomRef,
10
10
  type WorkspaceRegistrar,
11
11
  } from './register.ts'
12
- import { BOSS_ROOMS, ENGINEER_ROOMS, OPS_ROOMS, STAFF_ROOMS, seedBossWorkspaces, seedEngineerWorkspaces, seedOpsWorkspaces, seedStaffWorkspaces } from './seed.ts'
12
+ import { BOSS_ROOMS, ENGINEER_ROOMS, OPS_ROOMS, SALES_ROOMS, STAFF_ROOMS, seedBossWorkspaces, seedEngineerWorkspaces, seedOpsWorkspaces, seedSalesWorkspaces, seedStaffWorkspaces } from './seed.ts'
13
13
 
14
14
  export const STAFF_GUIDANCE = [
15
15
  '侧栏已登记人事助理 / 财务助理 / 法务助理。装完、选身份后自动出现,不必手动「添加工作区」、也不必拖排序。',
@@ -28,7 +28,7 @@ export const BOSS_GUIDANCE = [
28
28
  '用户用中文就中文、用英文就英文;不要因为工作区文件是中文就强制中文。',
29
29
  ].join('')
30
30
 
31
- export const BOSS_ROLE_HINT = '搞钱 / 产品 / 运营仅角色选「老板」时出现。人事 / 财务 / 法务助理所有角色都会登记。设计 / 发布 / 电商工作房间仅「运营」身份出现。实验室仅「工程师」身份出现。'
31
+ export const BOSS_ROLE_HINT = '搞钱 / 产品 / 运营仅角色选「老板」时出现。人事 / 财务 / 法务助理所有角色都会登记。设计 / 发布 / 电商工作房间仅「运营」身份出现。实验室仅「工程师」身份出现。销售房间对「老板」「销售」「运营」身份显示。'
32
32
 
33
33
  export const ENGINEER_LAB_GUIDANCE = [
34
34
  '当前身份是工程师。侧栏第一间是实验室:对方说板型/功能就自己写 ESPHome YAML,或用回形针/粘贴。调 mcp__esphome__compile(或 rez_esphome_compile)。',
@@ -41,10 +41,17 @@ export const OPS_DESIGN_GUIDANCE = [
41
41
  '再接着是电商工作间(ec operation):一期只做阿里巴巴国际站 ListingJob。Amazon 现在没有号,看板标后续,有号再开。设置页「看板」看两室连接与 Odoo/企微权限。不要代批、不要直接写 RAW、不要做图生场景或白底。搞钱 / 产品 / 运营仅老板身份出现。',
42
42
  ].join('')
43
43
 
44
+ export const SALES_GUIDANCE = '侧栏已登记销售房间,负责客户会话分析、回复起草和 WhatsApp 发送预演。当前通讯工具使用演示数据,无真实发送能力;不得声称已读真实客户会话或已发送。'
45
+
46
+ export function hasSalesRoom(role: RezRoleId): boolean {
47
+ return role === 'boss' || role === 'sales' || role === 'operations'
48
+ }
49
+
44
50
  export function workspaceGuidanceFor(role: RezRoleId): string {
45
- if (role === 'boss') return BOSS_GUIDANCE
46
- if (role === 'operations') return STAFF_GUIDANCE + OPS_DESIGN_GUIDANCE
51
+ if (role === 'boss') return BOSS_GUIDANCE + SALES_GUIDANCE
52
+ if (role === 'operations') return STAFF_GUIDANCE + OPS_DESIGN_GUIDANCE + SALES_GUIDANCE
47
53
  if (role === 'engineer') return STAFF_GUIDANCE + ENGINEER_LAB_GUIDANCE
54
+ if (role === 'sales') return STAFF_GUIDANCE + SALES_GUIDANCE
48
55
  return STAFF_GUIDANCE + BOSS_ROLE_HINT
49
56
  }
50
57
 
@@ -80,6 +87,10 @@ export function expectedEngineerRooms(env: NodeJS.ProcessEnv = process.env): Roo
80
87
  return refsFor(ENGINEER_ROOMS, env)
81
88
  }
82
89
 
90
+ export function expectedSalesRooms(env: NodeJS.ProcessEnv = process.env): RoomRef[] {
91
+ return refsFor(SALES_ROOMS, env)
92
+ }
93
+
83
94
  export async function syncRezWorkspaces(
84
95
  registry: WorkspaceRegistrar,
85
96
  role: RezRoleId,
@@ -109,13 +120,21 @@ export async function syncRezWorkspaces(
109
120
  await unregisterWorkspaces(registry, expectedEngineerRooms(env))
110
121
  }
111
122
 
123
+ if (hasSalesRoom(role)) {
124
+ const sales = seedSalesWorkspaces({ destRoot: destRoot(env) })
125
+ await registerWorkspaces(registry, sales.rooms)
126
+ } else {
127
+ await unregisterWorkspaces(registry, expectedSalesRooms(env))
128
+ }
129
+
130
+ const sales = hasSalesRoom(role) ? expectedSalesRooms(env) : []
112
131
  const visible = role === 'boss'
113
- ? [...expectedBossRooms(env), ...expectedStaffRooms(env)]
132
+ ? [...expectedBossRooms(env), ...sales, ...expectedStaffRooms(env)]
114
133
  : role === 'operations'
115
- ? [...expectedOpsRooms(env), ...expectedStaffRooms(env)]
134
+ ? [...expectedOpsRooms(env), ...sales, ...expectedStaffRooms(env)]
116
135
  : role === 'engineer'
117
136
  ? [...expectedEngineerRooms(env), ...expectedStaffRooms(env)]
118
- : expectedStaffRooms(env)
137
+ : [...sales, ...expectedStaffRooms(env)]
119
138
  await orderWorkspaces(registry, visible)
120
139
  }
121
140
 
package/src/boss/seed.ts CHANGED
@@ -27,11 +27,15 @@ export const ENGINEER_ROOMS = [
27
27
  { id: 'lab', folder: '实验室' },
28
28
  ] as const
29
29
 
30
+ /** 客户通讯房间:老板、销售、运营可见。 */
31
+ export const SALES_ROOMS = [{ id: 'sales', folder: '销售' }] as const
32
+
33
+ export type SalesRoomId = (typeof SALES_ROOMS)[number]['id']
30
34
  export type BossRoomId = (typeof BOSS_ROOMS)[number]['id']
31
35
  export type StaffRoomId = (typeof STAFF_ROOMS)[number]['id']
32
36
  export type OpsRoomId = (typeof OPS_ROOMS)[number]['id']
33
37
  export type EngineerRoomId = (typeof ENGINEER_ROOMS)[number]['id']
34
- export type RoomId = BossRoomId | StaffRoomId | OpsRoomId | EngineerRoomId
38
+ export type RoomId = BossRoomId | StaffRoomId | OpsRoomId | EngineerRoomId | SalesRoomId
35
39
  export type RoomSpec = { id: string; folder: string }
36
40
 
37
41
  /** User-owned files. Re-seeding never overwrites these. */
@@ -127,6 +131,10 @@ export function seedEngineerWorkspaces(options: SeedOptions): SeedResult {
127
131
  )
128
132
  }
129
133
 
134
+ export function seedSalesWorkspaces(options: SeedOptions): SeedResult {
135
+ return seedRooms(options.destRoot, SALES_ROOMS, options.templatesRoot ?? defaultStaffTemplatesRoot())
136
+ }
137
+
130
138
  function seedRoom(templatesRoot: string, destRoot: string, room: RoomSpec): SeedRoomResult {
131
139
  const src = join(templatesRoot, room.id)
132
140
  const dest = join(destRoot, room.folder)
package/src/changelog.ts CHANGED
@@ -20,6 +20,17 @@ export interface ChangelogRelease {
20
20
  }
21
21
 
22
22
  export const SUITE_CHANGELOG: readonly ChangelogRelease[] = [
23
+ {
24
+ version: '0.1.80',
25
+ date: '2026-09-28',
26
+ sections: [{
27
+ title: { zh: '销售房间与 WhatsApp 发送预演', en: 'Sales room and WhatsApp dry run' },
28
+ items: [{
29
+ zh: '老板、销售、运营身份自动显示销售房间,保留已有个人资料与记忆。通讯插件升级至 0.1.1,可分析演示会话并预演 WhatsApp 回复;未接入真实客户收发。',
30
+ en: 'Automatically show Sales for boss, sales and operations roles, preserving personal files. Comms 0.1.1 analyses demo threads and plans WhatsApp replies; live customer messaging is not enabled.',
31
+ }],
32
+ }],
33
+ },
23
34
  {
24
35
  version: '0.1.78',
25
36
  date: '2026-09-18',
@@ -0,0 +1,22 @@
1
+ # 销售 · 客户通讯工作房间
2
+
3
+ 你是雷泽智能的销售助手。本室负责客户通讯:读客户会话、分析需求、起草客户语言的回复、检查 WhatsApp 发送条件。不要因为当前登录身份是运营,就把本室当作电商上架房间或拒绝客户通讯工作。
4
+
5
+ 每次会话先读 `skills/role_sales.md`;这是销售角色说明。用用户的语言解释,回复草稿使用客户的语言。
6
+
7
+ ## 当前通讯能力
8
+
9
+ - 当前通讯插件,可使用 `comms_thread_search`、`comms_thread_get`、`comms_analyze`、`comms_whatsapp_dryrun`。调用前以实际可用工具及 schema 为准;缺工具就如实说明。
10
+ - 当前 comms 数据来自 fixture 演示数据,不能称为已连接真实 Odoo 或真实客户收件箱。演示 WhatsApp 会话为 `company_id=1`、`thread_id=4102`;公司 1 只是演示范围,不是用户真实业务公司的默认值。
11
+ - 可在对话中起草回复,再调用 `comms_whatsapp_dryrun` 检查预演条件。预演不发送消息,也不写入 Odoo。没有真实发送工具,不能声称已经发出。
12
+ - `would_send=true` 只是本地预演条件判断,不代表消息已发送、网关已部署或 Meta 已批准模板。当前发送预演不核验模板审批状态,不得把任意模板名称当成已批准模板。
13
+ - 真实收发需要另行核验 Odoo 连接、WhatsApp 网关、客户映射及适用的已批准模板。不要仅凭演示数据断言所有生产系统都未安装网关。
14
+
15
+ ## 工作规则
16
+
17
+ - 每次工具调用显式携带适用的 company_id;真实业务公司不明确时先确认。
18
+ - 不编价格、不承诺未经核实的交期;缺数据时把待确认占位符写进可复制的回复正文。
19
+ - 不自动发送,不擅自创建客户映射,不改生产数据库。对外发送必须由人确认具体内容,且实际发送能力已经存在。
20
+ - 制度、素材路径等先读 Nextcloud `Documents/Standards/INDEX.yaml`,再按相对 uri 读原文,引用路径和生效日;不把记忆当标准。
21
+ - 通讯接入复用成熟插件,不自行编写 WhatsApp 或 IMAP 客户端。
22
+ - 每次汇报:结论、公司/客户/会话标识、下一步、实际写操作及是否发送。
@@ -0,0 +1,3 @@
1
+ # 销售房间初始化
2
+
3
+ 先完成用户当前任务。适当时确认称呼、负责的公司和客户范围、常用回复语言,记录到 USER.md / PRIORITIES.md。助手身份是雷泽AI助手,不是用户本人。禁止记录密码、API Key 或完整客户清单。初始化后删除本文件。
@@ -0,0 +1,3 @@
1
+ # 助手身份
2
+
3
+ 我是雷泽AI助手,负责客户通讯和销售回复起草。
@@ -0,0 +1,3 @@
1
+ # 销售工作记忆
2
+
3
+ 只记录经用户确认的长期偏好。不得记录密钥或把演示会话当真实客户资料。
@@ -0,0 +1,3 @@
1
+ # 销售工作重点
2
+
3
+ 读会话 → 分析需求 → 起草回复 → 人工核对。当前仅支持预演,不发送。
@@ -0,0 +1,3 @@
1
+ # 工作方式
2
+
3
+ 准确、简洁、可核验。回复使用客户语言,解释使用用户语言。未经核实的价格、交期、库存和认证用待确认占位符。