@roaming-ai/dsh-group-chat 0.2.1 → 0.3.0

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 (59) hide show
  1. package/README.md +3 -2
  2. package/lib/client.js +986 -298
  3. package/lib/client.js.map +1 -1
  4. package/lib/index.js +524 -52
  5. package/lib/types/client/components/Bubble.d.ts +6 -3
  6. package/lib/types/client/components/ChatPanel.d.ts +1 -0
  7. package/lib/types/client/components/ConstraintList.d.ts +11 -0
  8. package/lib/types/client/components/FailCard.d.ts +9 -0
  9. package/lib/types/client/components/Fold.d.ts +19 -0
  10. package/lib/types/client/components/HoverTip.d.ts +22 -0
  11. package/lib/types/client/components/MessageFlow.d.ts +1 -0
  12. package/lib/types/client/components/MsgActions.d.ts +14 -0
  13. package/lib/types/client/hooks/useComposer.d.ts +6 -1
  14. package/lib/types/client/lib/composer-draft.d.ts +17 -0
  15. package/lib/types/core/constraints.d.ts +61 -0
  16. package/lib/types/core/errors.d.ts +54 -0
  17. package/lib/types/core/types.d.ts +22 -0
  18. package/lib/types/host/api/actions.d.ts +1 -1
  19. package/lib/types/host/engine/conversation.d.ts +5 -3
  20. package/lib/types/host/engine/fold.d.ts +17 -0
  21. package/lib/types/host/engine/index.d.ts +1 -0
  22. package/lib/types/host/engine/retitle.d.ts +5 -5
  23. package/lib/types/host/service.d.ts +1 -1
  24. package/lib/types/host/state.d.ts +2 -0
  25. package/lib/types/index.d.ts +2 -0
  26. package/package.json +1 -1
  27. package/src/client/GroupChatPanel.tsx +18 -3
  28. package/src/client/components/AsidePanel.tsx +10 -8
  29. package/src/client/components/Bubble.tsx +45 -18
  30. package/src/client/components/ChatPanel.tsx +17 -12
  31. package/src/client/components/Composer.tsx +26 -20
  32. package/src/client/components/ConstraintList.tsx +69 -0
  33. package/src/client/components/FailCard.tsx +49 -0
  34. package/src/client/components/Fold.tsx +72 -0
  35. package/src/client/components/HoverTip.tsx +134 -0
  36. package/src/client/components/MessageFlow.tsx +77 -54
  37. package/src/client/components/MsgActions.tsx +85 -0
  38. package/src/client/components/NavPanel.tsx +6 -1
  39. package/src/client/components/ThinkRow.tsx +7 -3
  40. package/src/client/components/ToolRow.tsx +7 -3
  41. package/src/client/hooks/useComposer.ts +40 -3
  42. package/src/client/hooks/useGroupChatState.ts +5 -4
  43. package/src/client/lib/composer-draft.ts +47 -0
  44. package/src/client/lib/styles.ts +74 -13
  45. package/src/client/react-dom-shim.d.ts +2 -0
  46. package/src/core/constraints.ts +210 -0
  47. package/src/core/errors.ts +184 -0
  48. package/src/core/json.ts +1 -0
  49. package/src/core/types.ts +28 -3
  50. package/src/host/api/actions.ts +35 -1
  51. package/src/host/broadcast.ts +3 -3
  52. package/src/host/engine/conversation.ts +103 -38
  53. package/src/host/engine/fold.ts +114 -0
  54. package/src/host/engine/index.ts +1 -0
  55. package/src/host/engine/retitle.ts +16 -11
  56. package/src/host/persistence/persistence.ts +18 -1
  57. package/src/host/service.ts +1 -1
  58. package/src/host/state.ts +5 -4
  59. package/src/index.ts +2 -0
@@ -5,7 +5,9 @@
5
5
  */
6
6
 
7
7
  import { existsSync } from 'node:fs'
8
+ import { repairFailedMessage } from '../../core/errors.ts'
8
9
  import { messageJson, roleJson } from '../../core/json.ts'
10
+ import { sanitizeConstraints } from '../../core/constraints.ts'
9
11
  import { asNumber, migrateTier } from '../../core/types.ts'
10
12
  import type { GroupRecord, MessageRecord, RoleRecord, SessionRecord } from '../../core/types.ts'
11
13
  import { emptyGroup, LedgerDocument, scanGroupIds, scanSessionIds, STORE_DIR, Store } from './store.ts'
@@ -17,6 +19,8 @@ interface SessionDocument {
17
19
  topic?: string
18
20
  namePinned?: boolean
19
21
  topicPinned?: boolean
22
+ constraints?: unknown
23
+ constraintsUpToSeq?: number
20
24
  createdAt?: number
21
25
  messages?: Partial<MessageRecord>[]
22
26
  }
@@ -74,6 +78,8 @@ export function createPersistence(core: HostState): Persistence {
74
78
  topic: s.topic,
75
79
  ...(s.namePinned ? { namePinned: true } : {}),
76
80
  ...(s.topicPinned ? { topicPinned: true } : {}),
81
+ ...(s.constraints && s.constraints.length ? { constraints: s.constraints } : {}),
82
+ ...(typeof s.constraintsUpToSeq === 'number' && s.constraintsUpToSeq > 0 ? { constraintsUpToSeq: s.constraintsUpToSeq } : {}),
77
83
  createdAt: s.createdAt,
78
84
  messages: s.messageIds.map((mid) => core.messages.get(mid)).filter(Boolean).map((m) => messageJson(m)).filter(Boolean),
79
85
  })
@@ -178,6 +184,9 @@ export function createPersistence(core: HostState): Persistence {
178
184
  if (typeof doc.topic === 'string') sess.topic = doc.topic
179
185
  if (doc.namePinned === true) sess.namePinned = true
180
186
  if (doc.topicPinned === true) sess.topicPinned = true
187
+ const constraints = sanitizeConstraints(doc.constraints)
188
+ if (constraints.length) sess.constraints = constraints
189
+ if (typeof doc.constraintsUpToSeq === 'number' && doc.constraintsUpToSeq > 0) sess.constraintsUpToSeq = doc.constraintsUpToSeq
181
190
  if (typeof doc.createdAt === 'number') sess.createdAt = doc.createdAt
182
191
  let fallbackSeq = 0
183
192
  for (const m of (Array.isArray(doc.messages) ? doc.messages : [])) {
@@ -194,11 +203,19 @@ export function createPersistence(core: HostState): Persistence {
194
203
  thinkingSummary: m.thinkingSummary !== undefined ? String(m.thinkingSummary) : undefined,
195
204
  model: m.model,
196
205
  error: m.error,
206
+ failedRoleId: typeof m.failedRoleId === 'string' && m.failedRoleId ? m.failedRoleId : undefined,
197
207
  toolCalls: Array.isArray(m.toolCalls) ? m.toolCalls : undefined,
198
208
  ts: typeof m.ts === 'number' ? m.ts : Date.now(),
199
209
  })
200
210
  sess.messageIds.push(m.id)
201
211
  }
212
+ const groupRoles = g.roleIds.map((rid) => core.roles.get(rid)).filter((r): r is RoleRecord => Boolean(r))
213
+ let repaired = false
214
+ for (const mid of sess.messageIds) {
215
+ const rec = core.messages.get(mid)
216
+ if (rec && repairFailedMessage(rec, groupRoles)) repaired = true
217
+ }
218
+ if (repaired) schedulePersist({ session: sid })
202
219
  }
203
220
  core.sessions.set(sid, sess)
204
221
  g.sessionIds.push(sid)
@@ -274,7 +291,7 @@ export function createPersistence(core: HostState): Persistence {
274
291
  if (core.groups.size === 0) {
275
292
  const g: GroupRecord = { id: core.nid('grp'), name: '默认群组', workspaceDir: '', permissionTier: 'view_only', roleIds: [], sessionIds: [] }
276
293
  core.groups.set(g.id, g)
277
- const sess = core.newSession(g.id, '会话 1')
294
+ const sess = core.newSession(g.id)
278
295
  g.sessionIds.push(sess.id)
279
296
  autoSessions.push(sess.id)
280
297
  }
@@ -6,7 +6,7 @@
6
6
  * persistence/ 持久化(store 文件原语 + 脏标记合并落盘 + 启动恢复)
7
7
  * materials/ 资料读取与路径解析 + 目录浏览器
8
8
  * tools/ 工具执行(沙箱 + 确认闸门)
9
- * engine/ 对话引擎(conversation:speak/runLoop;retitle:标题整理)
9
+ * engine/ 对话引擎(conversation:speak/runLoop;retitle:标题整理;fold:窗口外约束)
10
10
  * api/ HTTP 传输(http 护栏 + routes 路由)与动作分发(actions)
11
11
  * @module dsh-group-chat/host/service
12
12
  */
package/src/host/state.ts CHANGED
@@ -28,6 +28,9 @@ declare module '@deepseek-ai/cordis' {
28
28
  /** 角色标识色板(新增角色依序取色)。 */
29
29
  export const PALETTE = ['#5b8def', '#22a06b', '#e8912d', '#c678dd', '#e05661', '#56b6c2', '#98c379', '#d19a66']
30
30
 
31
+ /** 新建会话默认名称(自动标题只在仍为此占位名时生成一次)。 */
32
+ export const DEFAULT_SESSION_NAME = '新会话'
33
+
31
34
  /** 宿主服务共享状态容器(见模块注释;可变原始值一律经 core.* 访问)。 */
32
35
  export interface HostState {
33
36
  ctx: Context
@@ -60,19 +63,17 @@ export function createHostState(ctx: Context): HostState {
60
63
  sessions: new Map(),
61
64
  roles: new Map(),
62
65
  messages: new Map(),
63
- run: { running: false, sessionId: null, currentRoleId: null, partial: '', partialReasoning: '', stopping: false, queue: [], pendingConfirm: null, confirmSignal: null, childProc: null, finished: null },
66
+ run: { running: false, sessionId: null, currentRoleId: null, partial: '', partialReasoning: '', stopping: false, queue: [], pendingConfirm: null, confirmSignal: null, childProc: null, finished: null, replaceMessageId: null },
64
67
  store: null,
65
68
  revision: 1,
66
69
  idSeq: 1,
67
70
  nid: (p: string): string => p + '-' + (core.idSeq++),
68
71
  lastCreated: null,
69
72
  newSession: (groupId: string, name?: string): SessionRecord => {
70
- let n = 0
71
- for (const s of core.sessions.values()) if (s.groupId === groupId) n++
72
73
  const s: SessionRecord = {
73
74
  id: randomUUID(),
74
75
  groupId,
75
- name: name || '会话 ' + (n + 1),
76
+ name: name || DEFAULT_SESSION_NAME,
76
77
  topic: '',
77
78
  messageIds: [],
78
79
  createdAt: Date.now(),
package/src/index.ts CHANGED
@@ -10,6 +10,8 @@
10
10
  * 数据模型:Group 1..N Session,消息挂在会话上;角色与工作区目录挂在群组上
11
11
  * - 群组与会话的增删改;群组/会话检索由客户端在快照上过滤
12
12
  * - 角色发言经 `llm` 服务流式生成,按角色绑定的 provider/model 路由
13
+ * - 整次 runLoop 结束后后台折叠被 40 条窗口挤出的消息为会话级约束备忘
14
+ * (engine/fold.ts;立刻 idle、fire-and-forget,不产生消息)
13
15
  * - 群组工作区目录经 `fs` 服务读取(根下一层文本文件,最多 20 个),
14
16
  * 以「共享资料」块注入每个角色的 system 提示词;无独立笔记/文件清单
15
17
  * - 经 `webServer` 暴露 HTTP API: