@roaming-ai/dsh-group-chat 0.2.2 → 0.3.1

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 +91 -27
  2. package/lib/client.js +1254 -598
  3. package/lib/client.js.map +1 -1
  4. package/lib/index.js +573 -109
  5. package/lib/types/client/components/AsidePanel.d.ts +3 -19
  6. package/lib/types/client/components/Bubble.d.ts +6 -3
  7. package/lib/types/client/components/ChatPanel.d.ts +1 -0
  8. package/lib/types/client/components/ConstraintList.d.ts +11 -0
  9. package/lib/types/client/components/FailCard.d.ts +9 -0
  10. package/lib/types/client/components/Fold.d.ts +19 -0
  11. package/lib/types/client/components/HoverTip.d.ts +22 -0
  12. package/lib/types/client/components/MessageFlow.d.ts +1 -0
  13. package/lib/types/client/components/MsgActions.d.ts +14 -0
  14. package/lib/types/client/components/NavPanel.d.ts +9 -11
  15. package/lib/types/client/components/RoleDrawer.d.ts +3 -2
  16. package/lib/types/client/components/ToolRow.d.ts +2 -2
  17. package/lib/types/client/hooks/useComposer.d.ts +6 -1
  18. package/lib/types/client/hooks/useGroupChatState.d.ts +0 -16
  19. package/lib/types/client/lib/composer-draft.d.ts +17 -0
  20. package/lib/types/client/lib/model.d.ts +1 -7
  21. package/lib/types/client/utils/utils.d.ts +4 -4
  22. package/lib/types/core/constraints.d.ts +61 -0
  23. package/lib/types/core/errors.d.ts +54 -0
  24. package/lib/types/core/json.d.ts +6 -0
  25. package/lib/types/core/types.d.ts +76 -60
  26. package/lib/types/host/api/actions.d.ts +1 -1
  27. package/lib/types/host/engine/conversation.d.ts +5 -3
  28. package/lib/types/host/engine/defaults.d.ts +13 -0
  29. package/lib/types/host/engine/fold.d.ts +17 -0
  30. package/lib/types/host/engine/index.d.ts +1 -0
  31. package/lib/types/host/engine/retitle.d.ts +5 -5
  32. package/lib/types/host/service.d.ts +1 -1
  33. package/lib/types/host/state.d.ts +2 -0
  34. package/lib/types/index.d.ts +2 -0
  35. package/lib/types/shared/file-mention-grammar.d.ts +3 -12
  36. package/package.json +1 -1
  37. package/src/client/GroupChatPanel.tsx +25 -59
  38. package/src/client/components/AsidePanel.tsx +50 -23
  39. package/src/client/components/Bubble.tsx +45 -18
  40. package/src/client/components/ChatPanel.tsx +17 -12
  41. package/src/client/components/Composer.tsx +26 -20
  42. package/src/client/components/ConstraintList.tsx +64 -0
  43. package/src/client/components/FailCard.tsx +49 -0
  44. package/src/client/components/Fold.tsx +72 -0
  45. package/src/client/components/HoverTip.tsx +134 -0
  46. package/src/client/components/MessageFlow.tsx +78 -54
  47. package/src/client/components/MsgActions.tsx +85 -0
  48. package/src/client/components/NavPanel.tsx +102 -79
  49. package/src/client/components/RoleDrawer.tsx +29 -7
  50. package/src/client/components/ThinkRow.tsx +7 -3
  51. package/src/client/components/ToolRow.tsx +9 -5
  52. package/src/client/hooks/useComposer.ts +54 -56
  53. package/src/client/hooks/useGroupChatState.ts +7 -17
  54. package/src/client/lib/composer-draft.ts +47 -0
  55. package/src/client/lib/model.ts +1 -14
  56. package/src/client/lib/styles.ts +73 -12
  57. package/src/client/react-dom-shim.d.ts +2 -0
  58. package/src/client/utils/utils.ts +6 -6
  59. package/src/core/constraints.ts +204 -0
  60. package/src/core/errors.ts +174 -0
  61. package/src/core/json.ts +18 -0
  62. package/src/core/types.ts +52 -19
  63. package/src/host/api/actions.ts +49 -14
  64. package/src/host/broadcast.ts +3 -3
  65. package/src/host/engine/conversation.ts +104 -40
  66. package/src/host/engine/defaults.ts +28 -0
  67. package/src/host/engine/fold.ts +99 -0
  68. package/src/host/engine/index.ts +1 -0
  69. package/src/host/engine/retitle.ts +24 -41
  70. package/src/host/materials/materials.ts +2 -0
  71. package/src/host/persistence/persistence.ts +18 -1
  72. package/src/host/service.ts +1 -1
  73. package/src/host/state.ts +5 -4
  74. package/src/host/tools/tools.ts +2 -2
  75. package/src/index.ts +2 -0
  76. package/src/shared/file-mention-grammar.test.ts +1 -38
  77. package/src/shared/file-mention-grammar.ts +7 -33
  78. package/lib/types/client/Bubble.d.ts +0 -10
  79. package/lib/types/client/Glyph.d.ts +0 -12
  80. package/lib/types/client/GroupChatPanel.old.d.ts +0 -10
  81. package/lib/types/client/GroupChatSettingsSection.d.ts +0 -15
  82. package/lib/types/client/PermissionSelect.d.ts +0 -13
  83. package/lib/types/client/RoleDrawer.d.ts +0 -17
  84. package/lib/types/client/ThinkRow.d.ts +0 -9
  85. package/lib/types/client/ToolRow.d.ts +0 -9
  86. package/lib/types/client/api.d.ts +0 -8
  87. package/lib/types/client/components.d.ts +0 -22
  88. package/lib/types/client/drawer.d.ts +0 -17
  89. package/lib/types/client/glyph.d.ts +0 -12
  90. package/lib/types/client/model.d.ts +0 -78
  91. package/lib/types/client/panel.d.ts +0 -10
  92. package/lib/types/client/settings.d.ts +0 -15
  93. package/lib/types/client/styles.d.ts +0 -10
  94. package/lib/types/client/ui.d.ts +0 -17
  95. package/lib/types/client/utils.d.ts +0 -22
  96. package/lib/types/host/actions.d.ts +0 -34
  97. package/lib/types/host/conversation.d.ts +0 -25
  98. package/lib/types/host/http.d.ts +0 -16
  99. package/lib/types/host/materials.d.ts +0 -32
  100. package/lib/types/host/persistence.d.ts +0 -31
  101. package/lib/types/host/routes.d.ts +0 -11
  102. package/lib/types/host/store.d.ts +0 -65
  103. package/lib/types/host/tools.d.ts +0 -28
@@ -1,31 +1,15 @@
1
1
  /**
2
- * 右侧成员与工作区栏组件
2
+ * 右侧成员与工作区栏组件。工作区目录编辑态(草稿、文件浏览器)为栏内自有状态。
3
3
  * @module dsh-group-chat/client/components
4
4
  */
5
- import type { ReactNode } from 'react';
5
+ import { type ReactNode } from 'react';
6
6
  import { type ClientSnapshot, type SnapshotRole } from '../lib/model.ts';
7
7
  interface AsidePanelProps {
8
8
  snap: ClientSnapshot;
9
9
  group: ClientSnapshot['groups'][number];
10
10
  asideOpen: boolean;
11
- wsDraft: string | null;
12
- setWsDraft: (val: string | null) => void;
13
- fileBrowser: {
14
- open: boolean;
15
- loading: boolean;
16
- list: import('../../core/types.ts').BrowseResult | null;
17
- error: string;
18
- } | null;
19
- setFileBrowser: (val: {
20
- open: boolean;
21
- loading: boolean;
22
- list: import('../../core/types.ts').BrowseResult | null;
23
- error: string;
24
- } | null) => void;
25
11
  mutate: (args: Record<string, unknown>) => Promise<unknown>;
26
- openRoleEditor: (role: SnapshotRole | null) => Promise<void>;
27
- openBrowser: (path: string | undefined) => Promise<void>;
28
- selectCurrentDir: () => void;
12
+ openRoleEditor: (role: SnapshotRole | null) => void;
29
13
  }
30
14
  export declare function AsidePanel(props: AsidePanelProps): ReactNode;
31
15
  export {};
@@ -8,12 +8,15 @@
8
8
  * @module dsh-group-chat/client/Bubble
9
9
  */
10
10
  import { type ReactNode } from 'react';
11
- import { type ClientSnapshot, type SnapshotRole } from '../lib/model.ts';
11
+ import { type SnapshotRole } from '../lib/model.ts';
12
+ import type { SnapshotMessage } from '../lib/model.ts';
12
13
  export interface BubbleProps {
13
- m: ClientSnapshot['messages'][number];
14
+ m: SnapshotMessage;
14
15
  /** 父级解析好的发言角色(user/system 消息为 null)——避免 Bubble 依赖 snap identity。 */
15
16
  role: SnapshotRole | null;
17
+ busy?: boolean;
18
+ onRetry?: (messageId: string) => void;
16
19
  }
17
- declare function BubbleInner({ m, role }: BubbleProps): ReactNode;
20
+ declare function BubbleInner({ m, role, busy, onRetry }: BubbleProps): ReactNode;
18
21
  export declare const Bubble: import("react").MemoExoticComponent<typeof BubbleInner>;
19
22
  export {};
@@ -52,6 +52,7 @@ interface ChatPanelProps {
52
52
  action: (payload: Record<string, unknown>) => Promise<unknown>;
53
53
  mutate: (args: Record<string, unknown>) => Promise<unknown>;
54
54
  setMention: (val: AtToken | null) => void;
55
+ onRetrySpeak: (messageId: string) => void;
55
56
  }
56
57
  export declare function ChatPanel(props: ChatPanelProps): ReactNode;
57
58
  export {};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 会话流折点处:只读结论备忘卡(超过 4 条默认露 3 条)。
3
+ * @module dsh-group-chat/client/components
4
+ */
5
+ import { type ReactNode } from 'react';
6
+ import type { SessionConstraint } from '../../core/types.ts';
7
+ interface ConstraintListProps {
8
+ items: SessionConstraint[];
9
+ }
10
+ export declare function ConstraintList(props: ConstraintListProps): ReactNode;
11
+ export {};
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 角色发言失败卡:人话标题 + 可展开原文。操作条由 Bubble 放在气泡外下方。
3
+ * @module dsh-group-chat/client/FailCard
4
+ */
5
+ import { type ReactNode } from 'react';
6
+ export interface FailCardProps {
7
+ raw: string;
8
+ }
9
+ export declare function FailCard(props: FailCardProps): ReactNode;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 会话内折叠:高度 0fr→1fr + 溢出滚动遮罩。思考 / 工具 / 失败原文 / 结论备忘共用。
3
+ * @module dsh-group-chat/client/components
4
+ */
5
+ import { type ReactNode } from 'react';
6
+ interface FoldProps {
7
+ open: boolean;
8
+ children: ReactNode;
9
+ className?: string;
10
+ }
11
+ export declare function Fold(props: FoldProps): ReactNode;
12
+ interface ClipWellProps {
13
+ children: ReactNode;
14
+ maxHeight: number;
15
+ className?: string;
16
+ watch?: unknown;
17
+ }
18
+ export declare function ClipWell(props: ClipWellProps): ReactNode;
19
+ export {};
@@ -0,0 +1,22 @@
1
+ /**
2
+ * 对齐宿主 Tooltip 的 hover 气泡:portal 到 document.body,躲开
3
+ * `.dsgc-root` 的 container-type 把 position:fixed 按容器定位。
4
+ * @module dsh-group-chat/client/components
5
+ */
6
+ import { type ReactNode } from 'react';
7
+ export type HoverTipSide = 'top' | 'right';
8
+ interface HoverTipProps {
9
+ label: string;
10
+ side?: HoverTipSide;
11
+ delayMs?: number;
12
+ maxWidth?: number;
13
+ className?: string;
14
+ children: ReactNode;
15
+ }
16
+ /**
17
+ * @param props.label 气泡正文(pre-line)
18
+ * @param props.side 默认 right(对齐侧栏会话行);composer 轮数用 top
19
+ * @param props.delayMs hover 延迟,默认 500;键盘 focus 立即出
20
+ */
21
+ export declare function HoverTip(props: HoverTipProps): ReactNode;
22
+ export {};
@@ -10,6 +10,7 @@ interface MessageFlowProps {
10
10
  busyNow: boolean;
11
11
  msgById: Record<string, ClientSnapshot['messages'][number]>;
12
12
  action: (payload: Record<string, unknown>) => Promise<unknown>;
13
+ onRetrySpeak: (messageId: string) => void;
13
14
  }
14
15
  export declare function MessageFlow(props: MessageFlowProps): ReactNode;
15
16
  export {};
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 消息操作条:贴在气泡外下方。用户/角色消息悬停出「复制」;失败卡常驻「复制 / 重试」。
3
+ * @module dsh-group-chat/client/MsgActions
4
+ */
5
+ import { type ReactNode } from 'react';
6
+ export interface MsgActionsProps {
7
+ copyText: string;
8
+ onRetry?: () => void;
9
+ retryDisabled?: boolean;
10
+ retryTitle?: string;
11
+ /** 失败卡:不依赖悬停,始终可见。 */
12
+ always?: boolean;
13
+ }
14
+ export declare function MsgActions(props: MsgActionsProps): ReactNode;
@@ -1,9 +1,15 @@
1
1
  /**
2
- * 左侧导航栏组件
2
+ * 左侧导航栏组件(群组 → 会话目录树;群组行与会话行共用重命名/删除三件套)。
3
3
  * @module dsh-group-chat/client/components
4
4
  */
5
5
  import type { ReactNode } from 'react';
6
6
  import { type ClientSnapshot } from '../lib/model.ts';
7
+ /** 目录树节点编辑态形状(群组/会话共用)。 */
8
+ type NodeEdit = {
9
+ kind: 'group' | 'session';
10
+ id: string;
11
+ value: string;
12
+ };
7
13
  interface NavPanelProps {
8
14
  snap: ClientSnapshot;
9
15
  search: string;
@@ -15,16 +21,8 @@ interface NavPanelProps {
15
21
  setGid: (val: string) => void;
16
22
  setSid: (val: string) => void;
17
23
  setPartsSel: (val: string[] | null) => void;
18
- renameDraft: {
19
- kind: 'group' | 'session';
20
- id: string;
21
- value: string;
22
- } | null;
23
- setRenameDraft: (val: {
24
- kind: 'group' | 'session';
25
- id: string;
26
- value: string;
27
- } | null) => void;
24
+ renameDraft: NodeEdit | null;
25
+ setRenameDraft: (val: NodeEdit | null) => void;
28
26
  confirmDel: {
29
27
  kind: 'group' | 'session';
30
28
  id: string;
@@ -7,11 +7,12 @@ import { type ModelsResponse, type RoleDraft } from '../lib/model.ts';
7
7
  export interface RoleDrawerProps {
8
8
  draft: RoleDraft;
9
9
  set: (draft: RoleDraft) => void;
10
+ /** 保存目标群组(upsertRole 落库)。 */
11
+ groupId: string;
10
12
  models: ModelsResponse | null;
11
13
  modelsError: string | null;
12
14
  onRetryModels: () => void;
13
- onSave: () => void;
15
+ mutate: (args: Record<string, unknown>) => Promise<unknown>;
14
16
  onCancel: () => void;
15
- formError: string;
16
17
  }
17
18
  export declare function RoleDrawer(props: RoleDrawerProps): ReactNode;
@@ -3,7 +3,7 @@
3
3
  * @module dsh-group-chat/client/ToolRow
4
4
  */
5
5
  import { type ReactNode } from 'react';
6
- import type { ToolCallView } from '../lib/model.ts';
6
+ import type { ToolCallRecord } from '../../core/types.ts';
7
7
  export declare function ToolRow(props: {
8
- c: ToolCallView;
8
+ c: ToolCallRecord;
9
9
  }): ReactNode;
@@ -8,13 +8,18 @@ import type { SnapshotRole } from '../lib/model.ts';
8
8
  export declare function useComposerEffects(inputRef: React.RefObject<HTMLDivElement>, scrollRef: React.RefObject<HTMLDivElement>, input: string, atBottom: boolean, snap: unknown): {
9
9
  onMsgsScroll: () => boolean | undefined;
10
10
  };
11
- export declare function useComposerInput(inputRef: React.RefObject<HTMLDivElement>, setInput: (val: string) => void, setMention: (val: AtToken | null) => void, setMentionIdx: (val: number) => void): {
11
+ export declare function useComposerInput(inputRef: React.RefObject<HTMLDivElement>, setInput: (val: string) => void, setMention: (val: AtToken | null) => void, setMentionIdx: (val: number) => void, sessionId?: string | null): {
12
12
  syncFromDOM: () => void;
13
13
  onInputCE: () => void;
14
14
  onPasteCE: (e: ReactClipboardEvent<HTMLDivElement>) => void;
15
15
  onDragOverCE: (e: ReactDragEvent<HTMLDivElement>) => void;
16
16
  onDropCE: (e: ReactDragEvent<HTMLDivElement>) => void;
17
17
  };
18
+ /**
19
+ * 按会话恢复草稿:切会话时先把当前 HTML 写入旧槽,再灌入新槽;
20
+ * 面板重挂载(主会话⇄群聊)时 editor 是新节点,从模块缓存灌回。
21
+ */
22
+ export declare function useComposerDraft(sessionId: string | null | undefined, inputRef: React.RefObject<HTMLDivElement>, setInput: (val: string) => void, setMention: (val: AtToken | null) => void): void;
18
23
  export declare function useMentionChip(inputRef: React.RefObject<HTMLDivElement>, setMention: (val: AtToken | null) => void, setMentionIdx: (val: number) => void, syncFromDOM: () => void): {
19
24
  insertChip: (role: SnapshotRole) => void;
20
25
  insertFileChip: (path: string, kind: "file" | "directory") => void;
@@ -47,26 +47,10 @@ export declare function useGroupChatState(): {
47
47
  setConfirmClear: import("react").Dispatch<import("react").SetStateAction<boolean>>;
48
48
  roleDraft: RoleDraft | null;
49
49
  setRoleDraft: import("react").Dispatch<import("react").SetStateAction<RoleDraft | null>>;
50
- roleFormError: string;
51
- setRoleFormError: import("react").Dispatch<import("react").SetStateAction<string>>;
52
50
  models: ModelsResponse | null;
53
51
  setModels: import("react").Dispatch<import("react").SetStateAction<ModelsResponse | null>>;
54
52
  modelsError: string | null;
55
53
  setModelsError: import("react").Dispatch<import("react").SetStateAction<string | null>>;
56
- fileBrowser: {
57
- open: boolean;
58
- loading: boolean;
59
- list: import("../../core/types.ts").BrowseResult | null;
60
- error: string;
61
- } | null;
62
- setFileBrowser: import("react").Dispatch<import("react").SetStateAction<{
63
- open: boolean;
64
- loading: boolean;
65
- list: import("../../core/types.ts").BrowseResult | null;
66
- error: string;
67
- } | null>>;
68
- wsDraft: string | null;
69
- setWsDraft: import("react").Dispatch<import("react").SetStateAction<string | null>>;
70
54
  partsSel: string[] | null;
71
55
  setPartsSel: import("react").Dispatch<import("react").SetStateAction<string[] | null>>;
72
56
  rounds: number;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 未发送草稿:按会话 id 分槽,模块级跨挂载存活。
3
+ * 主会话⇄群聊会整页卸载 composer,contentEditable 的 HTML 必须自行记住。
4
+ * @module dsh-group-chat/client/composer-draft
5
+ */
6
+ export interface ComposerDraft {
7
+ html: string;
8
+ text: string;
9
+ }
10
+ /** 读指定会话草稿;命中则提到 LRU 最近端。 */
11
+ export declare function readComposerDraft(sessionId: string | null | undefined): ComposerDraft;
12
+ /** 写入;空草稿删槽。超出上限淘汰最旧槽。 */
13
+ export declare function writeComposerDraft(sessionId: string | null | undefined, html: string, text: string): void;
14
+ /** 发送成功或明确丢弃时摘槽。 */
15
+ export declare function clearComposerDraft(sessionId: string | null | undefined): void;
16
+ /** 测试用:清空全部槽位。 */
17
+ export declare function resetComposerDrafts(): void;
@@ -2,7 +2,7 @@
2
2
  * 浏览器半共享常量与小工具(纯函数,无 React 依赖)。
3
3
  * @module dsh-group-chat/client/model
4
4
  */
5
- import type { RoleRecord, Snapshot, ToolCallRecord } from '../../core/types.ts';
5
+ import type { Snapshot } from '../../core/types.ts';
6
6
  /** 会话列表状态派生与文案(core 纯函数,供 NavPanel 渲染)。 */
7
7
  export { sessStatus, SESS_STATUS_LABEL, type SessStatus } from '../../core/status.ts';
8
8
  /** 角色标识色调色板(与宿主半一致)。 */
@@ -72,9 +72,3 @@ export interface RoleDraft {
72
72
  }
73
73
  export declare function draftFromRole(role: SnapshotRole): RoleDraft;
74
74
  export declare function blankDraft(): RoleDraft;
75
- /** 工具调用行的展示参数。 */
76
- export type ToolCallView = ToolCallRecord;
77
- /** 草稿是否可作为角色落库(宿主侧再校验一次)。 */
78
- export declare function draftMissing(draft: RoleDraft): string;
79
- /** RoleRecord 兼容视图(快照角色行即其展示子集)。 */
80
- export type RoleView = RoleRecord;
@@ -13,14 +13,14 @@ export declare function execCommand(commandId: 'delete' | 'insertHTML' | 'insert
13
13
  * DIV/P 块前补换行(防粘贴残留的块级包裹)。
14
14
  */
15
15
  export declare function serializeInput(root: HTMLElement): string;
16
- /** @角色芯片的 HTML(原子元素:contenteditable=false + draggable,退格整删)。 */
16
+ /** @角色芯片的 HTML(原子元素:contenteditable=false + draggable,退格整删;fresh=插入后需营救选区,加 data-new 标记)。 */
17
17
  export declare function chipHtml(role: {
18
18
  id: string;
19
19
  name: string;
20
20
  color?: string;
21
- }): string;
22
- /** @文件芯片的 HTML(原子元素:contenteditable=false + draggable,退格整删)。 */
23
- export declare function fileChipHtml(path: string, kind: 'file' | 'directory'): string;
21
+ }, fresh?: boolean): string;
22
+ /** @文件芯片的 HTML(原子元素:contenteditable=false + draggable,退格整删;fresh 同 chipHtml)。 */
23
+ export declare function fileChipHtml(path: string, kind: 'file' | 'directory', fresh?: boolean): string;
24
24
  /**
25
25
  * 光标前的活跃 @token(角色或文件弹层触发判定)。
26
26
  *
@@ -0,0 +1,61 @@
1
+ /**
2
+ * 会话约束备忘纯逻辑:滑动 40 条窗口、挤出集、压行、临时原文、解析与截断。
3
+ * @module dsh-group-chat/core/constraints
4
+ */
5
+ import { type MessageRecord, type SessionConstraint, type SessionRecord } from './types.ts';
6
+ /** 当场原文窗口(含系统行)。 */
7
+ export declare const WINDOW_SIZE = 40;
8
+ /** 折叠失败时临时原文条数上限。 */
9
+ export declare const TEMP_MAX_MESSAGES = 20;
10
+ /** 折叠失败时临时原文总长上限。 */
11
+ export declare const TEMP_MAX_CHARS = 16000;
12
+ /** 单轮折叠最多消耗的挤出条数(含系统行;超出留待下一轮)。 */
13
+ export declare const FOLD_MAX_MESSAGES = 40;
14
+ /** 单轮折叠输入总长上限(格式化后)。 */
15
+ export declare const FOLD_MAX_CHARS = 16000;
16
+ /** 备忘条数上限。 */
17
+ export declare const CONSTRAINT_MAX_ITEMS = 12;
18
+ /** 备忘总长上限(条目前缀+正文)。 */
19
+ export declare const CONSTRAINT_MAX_CHARS = 1200;
20
+ /** 单条备忘正文上限。 */
21
+ export declare const CONSTRAINT_ITEM_MAX_CHARS = 160;
22
+ export declare const KIND_LABEL: Record<SessionConstraint['kind'], string>;
23
+ /** 已折入水位(缺省 0)。 */
24
+ export declare function constraintsWatermark(sess: SessionRecord): number;
25
+ /** 重试:只取失败卡之前的时间线;untilId 不在列表则原样。 */
26
+ export declare function prefixIds(ids: string[], untilId?: string): string[];
27
+ /**
28
+ * 新挤出:seq > 水位 且不在最近 40 条。只扫窗口外前缀(旧→新)。
29
+ * untilId:重试时把窗口截到该消息之前,不带上后面已经发生的发言。
30
+ */
31
+ export declare function squeezedMessages(messages: Map<string, MessageRecord>, sess: SessionRecord, untilId?: string): MessageRecord[];
32
+ /** 说话人展示名(折叠输入 / transcript 共用)。 */
33
+ export declare function speakerLabel(speaker: string, roleName?: string): string;
34
+ /** 单条消息压成 transcript 行(正文 8k + 工具一行摘要)。 */
35
+ export declare function formatTranscriptLine(m: MessageRecord, name: string): string;
36
+ /**
37
+ * 单轮折叠消耗前缀:从最旧挤出起,最多 40 条 / 16k;系统行与失败卡计入消耗但不进模型。
38
+ * 水位只能推到 consumed 的 max seq,剩余留待下一轮。
39
+ */
40
+ export declare function takeFoldBatch(squeezed: MessageRecord[], nameOf: (m: MessageRecord) => string): {
41
+ consumed: MessageRecord[];
42
+ lines: string[];
43
+ hasUser: boolean;
44
+ allSystem: boolean;
45
+ };
46
+ /**
47
+ * 未折入的挤出原文(失败缓冲):只格式化最近 20 条,再按 16k 从最旧往下丢。
48
+ */
49
+ export declare function tempTranscript(squeezed: MessageRecord[], nameOf: (m: MessageRecord) => string): string;
50
+ /** 水位 = 本批挤出的 max(seq);空批为 0。 */
51
+ export declare function squeezedMaxSeq(squeezed: MessageRecord[]): number;
52
+ /** hydrate / 模型输出:非法 kind 丢条目;空 text 丢;条数与总长截断。 */
53
+ export declare function sanitizeConstraints(raw: unknown): SessionConstraint[];
54
+ /**
55
+ * 解析折叠模型输出。null = 解析失败(水位不推);[] = 无新结论(水位推、备忘不动)。
56
+ */
57
+ export declare function parseConstraints(raw: string): SessionConstraint[] | null;
58
+ /** 本批无用户消息时,新的已定/否决降为未决。 */
59
+ export declare function downgradeWithoutUser(list: SessionConstraint[], hasUser: boolean): SessionConstraint[];
60
+ /** system 提示词「已确认约束」块;空则空串。 */
61
+ export declare function constraintBlock(list: SessionConstraint[] | undefined): string;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * 角色发言失败:供应商原文 → 人话标题/原因(core 纯函数,无 React)。
3
+ * 原始 JSON 仍落盘在消息 text 里,UI 默认只展示短因,展开才看原文。
4
+ * @module dsh-group-chat/core/errors
5
+ */
6
+ export interface SpeakFailureView {
7
+ /** 一行标题,例如「额度已用尽」。 */
8
+ title: string;
9
+ /** 可选短因:重置时间、HTTP 状态等。 */
10
+ detail?: string;
11
+ /** 复制/排障用的供应商原文(已剥「模型输出异常终止: 」前缀)。 */
12
+ raw: string;
13
+ }
14
+ /** 剥旧系统胶囊与引擎包装前缀,保留供应商原文。 */
15
+ export declare function unwrapSpeakFailure(raw: string): string;
16
+ /** 旧系统胶囊文案:角色「名」发言失败:原文。对不上则 null。 */
17
+ export declare function parseLegacyRoleFailure(text: string): {
18
+ roleName: string;
19
+ rest: string;
20
+ } | null;
21
+ export interface RoleRef {
22
+ id: string;
23
+ name: string;
24
+ provider?: string;
25
+ model?: string;
26
+ }
27
+ export interface FailedMessageRef {
28
+ speaker: string;
29
+ text: string;
30
+ error?: boolean;
31
+ failedRoleId?: string;
32
+ }
33
+ /** 发言失败卡:error 标记,或旧系统胶囊文案。 */
34
+ export declare function isSpeakFailure(m: FailedMessageRef): boolean;
35
+ /** 失败回合对应角色:failedRoleId / speaker / 旧文案里的角色名(恰好一名才命中)。 */
36
+ export declare function resolveFailedRole<T extends RoleRef>(m: FailedMessageRef, roles: T[]): T | null;
37
+ /**
38
+ * 把旧系统失败行挂到对应角色(恰好一名命中才迁)。
39
+ * 返回是否改写了记录。
40
+ */
41
+ export declare function repairFailedMessage(m: {
42
+ speaker: string;
43
+ text: string;
44
+ error?: boolean;
45
+ failedRoleId?: string;
46
+ model?: string;
47
+ }, roles: RoleRef[]): boolean;
48
+ /**
49
+ * 把供应商错误压成可扫描的标题 + 短因。
50
+ * 未知形态回退为「发言失败」,原文仍可展开。
51
+ */
52
+ export declare function classifySpeakFailure(raw: string): SpeakFailureView;
53
+ /** 失败卡默认复制内容:标题 + 短因 + 原文。 */
54
+ export declare function formatSpeakFailureCopy(view: SpeakFailureView): string;
@@ -7,3 +7,9 @@ import type { MessageRecord, RoleRecord } from './types.ts';
7
7
  export declare function messageJson(m: Partial<MessageRecord> | undefined): Record<string, unknown> | null;
8
8
  /** 角色 → 落盘 JSON(groupId 由目录归属,不再写入)。 */
9
9
  export declare function roleJson(r: Partial<RoleRecord>): Record<string, unknown>;
10
+ /**
11
+ * 宽容 JSON 提取(模型输出/供应商错误原文的统一解析入口):
12
+ * 取首个 { 至末个 } 的片段解析,仅接受普通对象;失败返回 null。
13
+ * 代码围栏剥离由调用方按需先行处理(供应商错误原文不剥)。
14
+ */
15
+ export declare function looseJson(raw: string): Record<string, unknown> | null;
@@ -29,6 +29,17 @@ export interface GroupRecord {
29
29
  roleIds: string[];
30
30
  sessionIds: string[];
31
31
  }
32
+ /** 会话约束条目类型(窗口外结论/约束备忘)。 */
33
+ export type ConstraintKind = 'decided' | 'rejected' | 'open';
34
+ /** 全部合法约束类型。 */
35
+ export declare const CONSTRAINT_KINDS: readonly ConstraintKind[];
36
+ /** 合法 kind 原样,其余 undefined。 */
37
+ export declare function asConstraintKind(value: unknown): ConstraintKind | undefined;
38
+ /** 一条无主结论/约束(已定 / 否决 / 未决)。 */
39
+ export interface SessionConstraint {
40
+ kind: ConstraintKind;
41
+ text: string;
42
+ }
32
43
  /** 会话:消息挂在会话上。 */
33
44
  export interface SessionRecord {
34
45
  id: string;
@@ -39,6 +50,10 @@ export interface SessionRecord {
39
50
  namePinned?: boolean;
40
51
  /** 主题已被手动编辑:自动整理永久跳过(隐式固定)。 */
41
52
  topicPinned?: boolean;
53
+ /** 窗口外结论/约束备忘;空则省略。 */
54
+ constraints?: SessionConstraint[];
55
+ /** 已折入备忘的最大消息 seq;0/缺省 = 尚未折过。 */
56
+ constraintsUpToSeq?: number;
42
57
  messageIds: string[];
43
58
  createdAt: number;
44
59
  }
@@ -76,6 +91,8 @@ export interface MessageRecord {
76
91
  thinkingSummary?: string;
77
92
  model?: string;
78
93
  error?: boolean;
94
+ /** 发言失败时的角色 id;刷新后仍可对该条点重试。角色消息 speaker 即角色 id,此字段冗余兼容旧系统错误行。 */
95
+ failedRoleId?: string;
79
96
  toolCalls?: ToolCallRecord[];
80
97
  ts: number;
81
98
  }
@@ -114,6 +131,8 @@ export interface RunState {
114
131
  childProc: import('node:child_process').ChildProcess | null;
115
132
  /** 最近一次 run 的结束标记:会话列表「已完成/已出错」状态的数据源。 */
116
133
  finished: RunFinished | null;
134
+ /** 原地重试时被覆盖的失败消息 id;普通 send 为 null。 */
135
+ replaceMessageId: string | null;
117
136
  }
118
137
  /** 角色发言的引擎产物。 */
119
138
  export interface SpeakResult {
@@ -174,75 +193,72 @@ export interface FileSearchResult {
174
193
  }[];
175
194
  error?: string;
176
195
  }
177
- /** 发到客户端的全量快照(wire 形态)。 */
196
+ /** 快照内的会话行(wire 形态:不含 hydrate 用的固定标记与折入水位)。 */
197
+ export type SnapshotSession = Omit<SessionRecord, 'namePinned' | 'topicPinned' | 'constraintsUpToSeq'>;
198
+ /** 快照内的消息行(wire 形态:思考全文与摘要不外发)。 */
199
+ export type SnapshotMessage = Omit<MessageRecord, 'reasoningFull' | 'thinkingSummary'>;
200
+ /** 发到客户端的全量快照(wire 形态;各表行由领域记录派生,字段增删由编译器同步)。 */
178
201
  export interface Snapshot {
179
202
  revision: number;
180
- run: {
181
- running: boolean;
182
- sessionId: string | null;
183
- currentRoleId: string | null;
184
- partial: string;
185
- partialReasoning: string;
186
- pendingConfirm: PendingConfirm | null;
187
- finished: RunFinished | null;
188
- };
203
+ run: Omit<RunState, 'stopping' | 'queue' | 'confirmSignal' | 'childProc'>;
189
204
  lastCreated: LastCreated | null;
190
- groups: {
191
- id: string;
192
- name: string;
193
- workspaceDir: string;
194
- permissionTier: PermissionTier;
195
- roleIds: string[];
196
- sessionIds: string[];
197
- }[];
198
- sessions: {
199
- id: string;
200
- groupId: string;
201
- name: string;
202
- topic: string;
203
- messageIds: string[];
204
- createdAt: number;
205
- }[];
206
- roles: {
207
- id: string;
208
- groupId: string;
209
- name: string;
210
- color?: string;
211
- persona: string;
212
- provider: string;
213
- model: string;
214
- temperature?: number;
215
- reasoningEffort?: string;
216
- enabled: boolean;
217
- thinking: boolean;
218
- }[];
219
- messages: {
220
- id: string;
221
- sessionId: string;
222
- seq: number;
223
- speaker: string;
224
- text: string;
225
- reasoning?: string;
226
- model?: string;
227
- error?: boolean;
228
- toolCalls?: ToolCallRecord[];
229
- ts: number;
230
- }[];
205
+ groups: GroupRecord[];
206
+ sessions: SnapshotSession[];
207
+ roles: RoleRecord[];
208
+ messages: SnapshotMessage[];
231
209
  error?: string;
232
210
  }
233
- /** mutate 动作的参数形态(op 分发见 host/service)。 */
234
- export interface MutateArgs {
235
- op: string;
236
- groupId?: string;
237
- sessionId?: string;
238
- roleId?: string;
239
- role?: Partial<RoleRecord>;
211
+ /** mutate 动作的参数形态(判别联合:按 op 收窄各分支字段;wire 上字段可缺,消费方各自守卫)。 */
212
+ export type MutateArgs = {
213
+ op: 'createGroup';
214
+ name?: string;
215
+ } | {
216
+ op: 'renameGroup';
217
+ groupId: string;
240
218
  name?: string;
219
+ } | {
220
+ op: 'deleteGroup';
221
+ groupId: string;
222
+ } | {
223
+ op: 'createSession';
224
+ groupId: string;
225
+ name?: string;
226
+ } | {
227
+ op: 'renameSession';
228
+ sessionId: string;
229
+ name?: string;
230
+ } | {
231
+ op: 'deleteSession';
232
+ sessionId: string;
233
+ } | {
234
+ op: 'setTopic';
235
+ sessionId: string;
241
236
  topic?: string;
237
+ } | {
238
+ op: 'upsertRole';
239
+ groupId: string;
240
+ role?: Partial<RoleRecord>;
241
+ } | {
242
+ op: 'deleteRole';
243
+ roleId: string;
244
+ } | {
245
+ op: 'setRoleEnabled';
246
+ roleId: string;
247
+ enabled?: boolean;
248
+ } | {
249
+ op: 'setWorkspaceDir';
250
+ groupId: string;
242
251
  path?: string;
252
+ } | {
253
+ op: 'setPermissionTier';
254
+ groupId: string;
243
255
  tier?: string;
244
- enabled?: boolean;
245
- }
256
+ } | {
257
+ op: 'ackFinish';
258
+ } | {
259
+ op: 'clearMessages';
260
+ sessionId: string;
261
+ };
246
262
  /** send 动作的参数形态。 */
247
263
  export interface SendArgs {
248
264
  sessionId: string;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * 动作分发(handleAction,POST /api/group-chat/action 的载荷):
3
- * mutate(12 种 CRUD/配置操作)| send | stop | confirmCommand | models | efforts。
3
+ * mutate(12 种 CRUD/配置操作)| send | retrySpeak | stop | confirmCommand | models | efforts。
4
4
  * @module dsh-group-chat/host/api/actions
5
5
  */
6
6
  import type { Snapshot } from '../../core/types.ts';