@roaming-ai/dsh-group-chat 0.3.0 → 0.3.2
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/README.md +93 -28
- package/lib/client.js +295 -326
- package/lib/client.js.map +1 -1
- package/lib/index.js +234 -221
- package/lib/types/client/components/AsidePanel.d.ts +3 -19
- package/lib/types/client/components/NavPanel.d.ts +9 -11
- package/lib/types/client/components/RoleDrawer.d.ts +3 -2
- package/lib/types/client/components/ToolRow.d.ts +2 -2
- package/lib/types/client/hooks/useGroupChatState.d.ts +0 -16
- package/lib/types/client/lib/model.d.ts +1 -7
- package/lib/types/client/utils/utils.d.ts +4 -4
- package/lib/types/core/json.d.ts +6 -0
- package/lib/types/core/types.d.ts +59 -64
- package/lib/types/host/engine/defaults.d.ts +13 -0
- package/lib/types/host/persistence/persistence.d.ts +2 -4
- package/lib/types/host/persistence/store.d.ts +5 -1
- package/lib/types/host/service.d.ts +2 -2
- package/lib/types/host/state.d.ts +1 -0
- package/lib/types/host/tools/tools.d.ts +2 -1
- package/lib/types/index.d.ts +3 -0
- package/lib/types/shared/file-mention-grammar.d.ts +3 -12
- package/package.json +4 -1
- package/src/client/GroupChatPanel.tsx +9 -56
- package/src/client/components/AsidePanel.tsx +40 -15
- package/src/client/components/ChatPanel.tsx +1 -1
- package/src/client/components/Composer.tsx +2 -2
- package/src/client/components/ConstraintList.tsx +1 -6
- package/src/client/components/MessageFlow.tsx +2 -1
- package/src/client/components/NavPanel.tsx +96 -78
- package/src/client/components/RoleDrawer.tsx +29 -7
- package/src/client/components/ToolRow.tsx +2 -2
- package/src/client/hooks/useComposer.ts +14 -53
- package/src/client/hooks/useGroupChatState.ts +2 -13
- package/src/client/lib/model.ts +1 -14
- package/src/client/lib/styles.ts +6 -2
- package/src/client/utils/utils.ts +6 -6
- package/src/core/constraints.ts +6 -12
- package/src/core/errors.ts +2 -12
- package/src/core/json.ts +17 -0
- package/src/core/types.ts +29 -20
- package/src/host/api/actions.ts +14 -13
- package/src/host/engine/conversation.ts +50 -51
- package/src/host/engine/defaults.ts +28 -0
- package/src/host/engine/fold.ts +2 -17
- package/src/host/engine/retitle.ts +9 -31
- package/src/host/materials/materials.ts +2 -0
- package/src/host/persistence/persistence.ts +57 -34
- package/src/host/persistence/store.ts +5 -1
- package/src/host/service.ts +4 -4
- package/src/host/state.ts +5 -2
- package/src/host/tools/tools.ts +42 -48
- package/src/index.ts +7 -5
- package/src/shared/file-mention-grammar.test.ts +1 -38
- package/src/shared/file-mention-grammar.ts +7 -33
- package/lib/types/client/Bubble.d.ts +0 -10
- package/lib/types/client/Glyph.d.ts +0 -12
- package/lib/types/client/GroupChatPanel.old.d.ts +0 -10
- package/lib/types/client/GroupChatSettingsSection.d.ts +0 -15
- package/lib/types/client/PermissionSelect.d.ts +0 -13
- package/lib/types/client/RoleDrawer.d.ts +0 -17
- package/lib/types/client/ThinkRow.d.ts +0 -9
- package/lib/types/client/ToolRow.d.ts +0 -9
- package/lib/types/client/api.d.ts +0 -8
- package/lib/types/client/components.d.ts +0 -22
- package/lib/types/client/drawer.d.ts +0 -17
- package/lib/types/client/glyph.d.ts +0 -12
- package/lib/types/client/model.d.ts +0 -78
- package/lib/types/client/panel.d.ts +0 -10
- package/lib/types/client/settings.d.ts +0 -15
- package/lib/types/client/styles.d.ts +0 -10
- package/lib/types/client/ui.d.ts +0 -17
- package/lib/types/client/utils.d.ts +0 -22
- package/lib/types/host/actions.d.ts +0 -34
- package/lib/types/host/conversation.d.ts +0 -25
- package/lib/types/host/http.d.ts +0 -16
- package/lib/types/host/materials.d.ts +0 -32
- package/lib/types/host/persistence.d.ts +0 -31
- package/lib/types/host/routes.d.ts +0 -11
- package/lib/types/host/store.d.ts +0 -65
- 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
|
|
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) =>
|
|
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 {};
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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 {
|
|
6
|
+
import type { ToolCallRecord } from '../../core/types.ts';
|
|
7
7
|
export declare function ToolRow(props: {
|
|
8
|
-
c:
|
|
8
|
+
c: ToolCallRecord;
|
|
9
9
|
}): ReactNode;
|
|
@@ -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;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* 浏览器半共享常量与小工具(纯函数,无 React 依赖)。
|
|
3
3
|
* @module dsh-group-chat/client/model
|
|
4
4
|
*/
|
|
5
|
-
import type {
|
|
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
|
*
|
package/lib/types/core/json.d.ts
CHANGED
|
@@ -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;
|
|
@@ -128,7 +128,8 @@ export interface RunState {
|
|
|
128
128
|
confirmSignal: {
|
|
129
129
|
resolve: (allowed: boolean) => void;
|
|
130
130
|
} | null;
|
|
131
|
-
|
|
131
|
+
/** 正在执行的 run_command 的中止句柄(stop/dispose 时 abort,执行器 kill 进程)。 */
|
|
132
|
+
commandAbort: AbortController | null;
|
|
132
133
|
/** 最近一次 run 的结束标记:会话列表「已完成/已出错」状态的数据源。 */
|
|
133
134
|
finished: RunFinished | null;
|
|
134
135
|
/** 原地重试时被覆盖的失败消息 id;普通 send 为 null。 */
|
|
@@ -193,78 +194,72 @@ export interface FileSearchResult {
|
|
|
193
194
|
}[];
|
|
194
195
|
error?: string;
|
|
195
196
|
}
|
|
196
|
-
/**
|
|
197
|
+
/** 快照内的会话行(wire 形态:不含 hydrate 用的固定标记与折入水位)。 */
|
|
198
|
+
export type SnapshotSession = Omit<SessionRecord, 'namePinned' | 'topicPinned' | 'constraintsUpToSeq'>;
|
|
199
|
+
/** 快照内的消息行(wire 形态:思考全文与摘要不外发)。 */
|
|
200
|
+
export type SnapshotMessage = Omit<MessageRecord, 'reasoningFull' | 'thinkingSummary'>;
|
|
201
|
+
/** 发到客户端的全量快照(wire 形态;各表行由领域记录派生,字段增删由编译器同步)。 */
|
|
197
202
|
export interface Snapshot {
|
|
198
203
|
revision: number;
|
|
199
|
-
run:
|
|
200
|
-
running: boolean;
|
|
201
|
-
sessionId: string | null;
|
|
202
|
-
currentRoleId: string | null;
|
|
203
|
-
partial: string;
|
|
204
|
-
partialReasoning: string;
|
|
205
|
-
pendingConfirm: PendingConfirm | null;
|
|
206
|
-
finished: RunFinished | null;
|
|
207
|
-
replaceMessageId: string | null;
|
|
208
|
-
};
|
|
204
|
+
run: Omit<RunState, 'stopping' | 'queue' | 'confirmSignal' | 'commandAbort'>;
|
|
209
205
|
lastCreated: LastCreated | null;
|
|
210
|
-
groups:
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
permissionTier: PermissionTier;
|
|
215
|
-
roleIds: string[];
|
|
216
|
-
sessionIds: string[];
|
|
217
|
-
}[];
|
|
218
|
-
sessions: {
|
|
219
|
-
id: string;
|
|
220
|
-
groupId: string;
|
|
221
|
-
name: string;
|
|
222
|
-
topic: string;
|
|
223
|
-
constraints?: SessionConstraint[];
|
|
224
|
-
messageIds: string[];
|
|
225
|
-
createdAt: number;
|
|
226
|
-
}[];
|
|
227
|
-
roles: {
|
|
228
|
-
id: string;
|
|
229
|
-
groupId: string;
|
|
230
|
-
name: string;
|
|
231
|
-
color?: string;
|
|
232
|
-
persona: string;
|
|
233
|
-
provider: string;
|
|
234
|
-
model: string;
|
|
235
|
-
temperature?: number;
|
|
236
|
-
reasoningEffort?: string;
|
|
237
|
-
enabled: boolean;
|
|
238
|
-
thinking: boolean;
|
|
239
|
-
}[];
|
|
240
|
-
messages: {
|
|
241
|
-
id: string;
|
|
242
|
-
sessionId: string;
|
|
243
|
-
seq: number;
|
|
244
|
-
speaker: string;
|
|
245
|
-
text: string;
|
|
246
|
-
reasoning?: string;
|
|
247
|
-
model?: string;
|
|
248
|
-
error?: boolean;
|
|
249
|
-
failedRoleId?: string;
|
|
250
|
-
toolCalls?: ToolCallRecord[];
|
|
251
|
-
ts: number;
|
|
252
|
-
}[];
|
|
206
|
+
groups: GroupRecord[];
|
|
207
|
+
sessions: SnapshotSession[];
|
|
208
|
+
roles: RoleRecord[];
|
|
209
|
+
messages: SnapshotMessage[];
|
|
253
210
|
error?: string;
|
|
254
211
|
}
|
|
255
|
-
/** mutate
|
|
256
|
-
export
|
|
257
|
-
op:
|
|
258
|
-
groupId?: string;
|
|
259
|
-
sessionId?: string;
|
|
260
|
-
roleId?: string;
|
|
261
|
-
role?: Partial<RoleRecord>;
|
|
212
|
+
/** mutate 动作的参数形态(判别联合:按 op 收窄各分支字段;wire 上字段可缺,消费方各自守卫)。 */
|
|
213
|
+
export type MutateArgs = {
|
|
214
|
+
op: 'createGroup';
|
|
262
215
|
name?: string;
|
|
216
|
+
} | {
|
|
217
|
+
op: 'renameGroup';
|
|
218
|
+
groupId: string;
|
|
219
|
+
name?: string;
|
|
220
|
+
} | {
|
|
221
|
+
op: 'deleteGroup';
|
|
222
|
+
groupId: string;
|
|
223
|
+
} | {
|
|
224
|
+
op: 'createSession';
|
|
225
|
+
groupId: string;
|
|
226
|
+
name?: string;
|
|
227
|
+
} | {
|
|
228
|
+
op: 'renameSession';
|
|
229
|
+
sessionId: string;
|
|
230
|
+
name?: string;
|
|
231
|
+
} | {
|
|
232
|
+
op: 'deleteSession';
|
|
233
|
+
sessionId: string;
|
|
234
|
+
} | {
|
|
235
|
+
op: 'setTopic';
|
|
236
|
+
sessionId: string;
|
|
263
237
|
topic?: string;
|
|
238
|
+
} | {
|
|
239
|
+
op: 'upsertRole';
|
|
240
|
+
groupId: string;
|
|
241
|
+
role?: Partial<RoleRecord>;
|
|
242
|
+
} | {
|
|
243
|
+
op: 'deleteRole';
|
|
244
|
+
roleId: string;
|
|
245
|
+
} | {
|
|
246
|
+
op: 'setRoleEnabled';
|
|
247
|
+
roleId: string;
|
|
248
|
+
enabled?: boolean;
|
|
249
|
+
} | {
|
|
250
|
+
op: 'setWorkspaceDir';
|
|
251
|
+
groupId: string;
|
|
264
252
|
path?: string;
|
|
253
|
+
} | {
|
|
254
|
+
op: 'setPermissionTier';
|
|
255
|
+
groupId: string;
|
|
265
256
|
tier?: string;
|
|
266
|
-
|
|
267
|
-
|
|
257
|
+
} | {
|
|
258
|
+
op: 'ackFinish';
|
|
259
|
+
} | {
|
|
260
|
+
op: 'clearMessages';
|
|
261
|
+
sessionId: string;
|
|
262
|
+
};
|
|
268
263
|
/** send 动作的参数形态。 */
|
|
269
264
|
export interface SendArgs {
|
|
270
265
|
sessionId: string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 引擎共享小件:DSH 默认模型读取 + 发言人展示名(retitle / fold / conversation 共用)。
|
|
3
|
+
* @module dsh-group-chat/host/engine/defaults
|
|
4
|
+
*/
|
|
5
|
+
import type { RoleRecord } from '../../core/types.ts';
|
|
6
|
+
import type { HostState } from '../state.ts';
|
|
7
|
+
/** DSH 默认模型(agentDefaultModel 服务缺位或未配置时返回 null,调用方静默跳过)。 */
|
|
8
|
+
export declare const defaultModel: (core: HostState) => {
|
|
9
|
+
provider: string;
|
|
10
|
+
model: string;
|
|
11
|
+
} | null;
|
|
12
|
+
/** 发言人展示名(roles 表内查角色名;user/system 固定文案,见 speakerLabel)。 */
|
|
13
|
+
export declare const speakerNameOf: (roles: Map<string, RoleRecord>, speaker: string) => string;
|
|
@@ -13,16 +13,14 @@ export interface Persistence {
|
|
|
13
13
|
roles?: string | null;
|
|
14
14
|
workspace?: string | null;
|
|
15
15
|
}) => void;
|
|
16
|
-
/** 同步 flush:写全部脏文件(dispose 最终落盘用)。 */
|
|
17
|
-
flushNow: () => void;
|
|
18
16
|
/** 删除群组/会话后摘除脏标记(对应文件已删/将删,flush 跳过)。 */
|
|
19
17
|
dropDirty: (targets: {
|
|
20
18
|
session?: string | null;
|
|
21
19
|
roles?: string | null;
|
|
22
20
|
workspace?: string | null;
|
|
23
21
|
}) => void;
|
|
24
|
-
/** dispose
|
|
25
|
-
release: () => void
|
|
22
|
+
/** dispose:停止新调度 → 等待挂起 flush → 最终 flush → 释放锁(异步)。 */
|
|
23
|
+
release: () => Promise<void>;
|
|
26
24
|
}
|
|
27
25
|
/**
|
|
28
26
|
* 创建持久化面:构造即完成 store 初始化 + hydrate(v1 迁移 → 残留清理 →
|
|
@@ -32,7 +32,11 @@ export declare class Store {
|
|
|
32
32
|
private acquireLock;
|
|
33
33
|
release(): void;
|
|
34
34
|
fsyncDir(dir: string): void;
|
|
35
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* 同步原子写(tmp+fsync+rename):仅供构造期 v1 迁移(migrateV1)与测试
|
|
37
|
+
* 直用;运行时 flush 走 persistence.ts 的 writeFileAtomic(异步、wx 独占
|
|
38
|
+
* 创建 + 随机后缀 + 符号链接安全,无 per-file fsync,对齐 DSH 基座标准)。
|
|
39
|
+
*/
|
|
36
40
|
atomicWrite(file: string, text: string): void;
|
|
37
41
|
quarantine(file: string): void;
|
|
38
42
|
/** 读 JSON;缺失返回 null;损坏则隔离后返回 null。 */
|
|
@@ -19,8 +19,8 @@ export interface GroupChatService {
|
|
|
19
19
|
subscribePush(push: () => void): () => void;
|
|
20
20
|
/** 设置停用时中止正在进行的群聊。 */
|
|
21
21
|
stopAll(): void;
|
|
22
|
-
/** 卸载/热重载:唤醒确认等待 + kill 子进程 →
|
|
23
|
-
dispose(): void
|
|
22
|
+
/** 卸载/热重载:唤醒确认等待 + kill 子进程 → 异步最终 flush → 释放锁(cordis 会 await)。 */
|
|
23
|
+
dispose(): Promise<void>;
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
26
|
* 创建群聊宿主服务(装配各模块;持久化锁失败时降级内存态运行)。
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 工具执行(TOOLS.md §2:沙箱 / §3:确认闸门):read_file / list_dir /
|
|
3
3
|
* run_command 三件套;realpath 硬边界 + 分隔符比较;run_command 按群组
|
|
4
|
-
*
|
|
4
|
+
* 权限档位走逐条确认或直接执行,经 `shell` 服务(ctx.shell 沙箱执行器)
|
|
5
|
+
* 以 per-call sandboxPolicy 收紧到群工作区。
|
|
5
6
|
* @module dsh-group-chat/host/tools
|
|
6
7
|
*/
|
|
7
8
|
import { TOOL_SCHEMAS } from '../../core/tools.ts';
|
package/lib/types/index.d.ts
CHANGED
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
* (engine/fold.ts;立刻 idle、fire-and-forget,不产生消息)
|
|
15
15
|
* - 群组工作区目录经 `fs` 服务读取(根下一层文本文件,最多 20 个),
|
|
16
16
|
* 以「共享资料」块注入每个角色的 system 提示词;无独立笔记/文件清单
|
|
17
|
+
* - 工具执行(read_file/list_dir/run_command)见 docs/TOOLS.md:run_command
|
|
18
|
+
* 经 `shell` 服务(ctx.shell 沙箱执行器)执行,per-call sandboxPolicy
|
|
19
|
+
* 收紧到群工作区(workspace_write 档)或免受限(full_access 档)
|
|
17
20
|
* - 经 `webServer` 暴露 HTTP API:
|
|
18
21
|
* GET /api/group-chat/state 全量快照
|
|
19
22
|
* POST /api/group-chat/action { kind: mutate|send|stop|confirmCommand|models|efforts|browse|fileSearch, ... }
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
* Sourced from @deepseek-ai/dsh-file-reference/lib/types/grammar.js
|
|
5
5
|
* Cannot import directly due to client bundle purity gate (dsh-client-bundle-purity).
|
|
6
6
|
*
|
|
7
|
+
* 只保留输入侧的 @token 识别(activeAtToken);候选的文本化(formatFileMention)
|
|
8
|
+
* 未被本插件采用——目录钻入走「插入整颗芯片」语义,见 docs/DESIGN.md Composer 契约。
|
|
9
|
+
*
|
|
7
10
|
* @module dsh-group-chat/shared
|
|
8
11
|
*/
|
|
9
12
|
export interface AtToken {
|
|
@@ -14,10 +17,6 @@ export interface AtToken {
|
|
|
14
17
|
/** Whether the token uses quoted form @"..." */
|
|
15
18
|
quoted: boolean;
|
|
16
19
|
}
|
|
17
|
-
export interface FileCandidate {
|
|
18
|
-
kind: 'file' | 'directory';
|
|
19
|
-
path: string;
|
|
20
|
-
}
|
|
21
20
|
/**
|
|
22
21
|
* Extract active @token at cursor position.
|
|
23
22
|
* Matches @ at word boundary (line start or after whitespace).
|
|
@@ -27,11 +26,3 @@ export interface FileCandidate {
|
|
|
27
26
|
* @returns Token or undefined if no active @token at cursor
|
|
28
27
|
*/
|
|
29
28
|
export declare function activeAtToken(line: string, cursor: number): AtToken | undefined;
|
|
30
|
-
/**
|
|
31
|
-
* Format file/directory candidate as @mention text.
|
|
32
|
-
*
|
|
33
|
-
* @param candidate - File or directory to format
|
|
34
|
-
* @param preserveQuote - Keep quotes even when unnecessary (for drill-down continuity)
|
|
35
|
-
* @returns Formatted @path or undefined for invalid paths
|
|
36
|
-
*/
|
|
37
|
-
export declare function formatFileMention(candidate: FileCandidate, preserveQuote: boolean): string | undefined;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roaming-ai/dsh-group-chat",
|
|
3
3
|
"description": "DSH 模型群聊:多模型角色群组对话面板。角色绑定不同 provider/model,群内共享对话记录;设置页支持启用/停用。",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"README.md"
|
|
31
31
|
],
|
|
32
32
|
"license": "MIT",
|
|
33
|
+
"author": "localSummer (https://github.com/localSummer)",
|
|
33
34
|
"repository": {
|
|
34
35
|
"type": "git",
|
|
35
36
|
"url": "https://github.com/localSummer/dsh-group-chat"
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
}
|
|
56
57
|
},
|
|
57
58
|
"dependencies": {
|
|
59
|
+
"@deepseek-ai/dsh-atomic-write": "0.1.5-rc.2",
|
|
58
60
|
"@deepseek-ai/dsh-file-reference": "0.1.5-rc.2",
|
|
59
61
|
"@deepseek-ai/dsh-file-reference-local": "0.1.5-rc.2",
|
|
60
62
|
"schemastery": "^3.18.0"
|
|
@@ -78,6 +80,7 @@
|
|
|
78
80
|
"@deepseek-ai/dsh-host-webserver": "^0.1.5-rc.1",
|
|
79
81
|
"@deepseek-ai/dsh-llm": "^0.1.5-rc.1",
|
|
80
82
|
"@deepseek-ai/dsh-settings": "^0.1.5-rc.1",
|
|
83
|
+
"@deepseek-ai/dsh-shell": "^0.1.5-rc.1",
|
|
81
84
|
"@deepseek-ai/dsh-workspace": "^0.1.5-rc.1",
|
|
82
85
|
"@types/node": "^22.20.0",
|
|
83
86
|
"@types/react": "~18.3.1",
|
|
@@ -41,16 +41,10 @@ export function GroupChatPanel(): ReactNode {
|
|
|
41
41
|
setConfirmClear,
|
|
42
42
|
roleDraft,
|
|
43
43
|
setRoleDraft,
|
|
44
|
-
roleFormError,
|
|
45
|
-
setRoleFormError,
|
|
46
44
|
models,
|
|
47
45
|
setModels,
|
|
48
46
|
modelsError,
|
|
49
47
|
setModelsError,
|
|
50
|
-
fileBrowser,
|
|
51
|
-
setFileBrowser,
|
|
52
|
-
wsDraft,
|
|
53
|
-
setWsDraft,
|
|
54
48
|
partsSel,
|
|
55
49
|
setPartsSel,
|
|
56
50
|
rounds,
|
|
@@ -127,13 +121,15 @@ export function GroupChatPanel(): ReactNode {
|
|
|
127
121
|
|
|
128
122
|
const sendMsg = useCallback(async (): Promise<void> => {
|
|
129
123
|
if (busyNow || !sess || sendingRef.current) return
|
|
124
|
+
// 空内容不可发送(按钮 disabled 之外,Enter 路径在此静默拦截)
|
|
125
|
+
if (!input.trim()) return
|
|
130
126
|
sendingRef.current = true
|
|
131
127
|
try {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (!mentionedRoles.length) return
|
|
128
|
+
if (!participants.length && !mentionedRoles.length) {
|
|
129
|
+
setErr('请至少选择一个参与角色(或在消息中 @成员)')
|
|
130
|
+
return
|
|
136
131
|
}
|
|
132
|
+
const parts = mentionedRoles.length ? mentionedRoles.map((r) => r.id) : participants
|
|
137
133
|
const res = await action({ kind: 'send', sessionId: sess.id, text: input, participantRoleIds: parts, rounds }) as ActionOk | null
|
|
138
134
|
if (res && !res.ok && res.error) setErr(res.error)
|
|
139
135
|
else if (res && res.ok) {
|
|
@@ -220,51 +216,14 @@ export function GroupChatPanel(): ReactNode {
|
|
|
220
216
|
|
|
221
217
|
const openRoleEditor = async (role: SnapshotRole | null): Promise<void> => {
|
|
222
218
|
setErr('')
|
|
223
|
-
setRoleFormError('')
|
|
224
219
|
if (!models || modelsError) await fetchModels()
|
|
225
220
|
setRoleDraft(role ? draftFromRole(role) : blankDraft())
|
|
226
221
|
}
|
|
227
222
|
|
|
228
|
-
const saveRole = async (): Promise<void> => {
|
|
229
|
-
if (!roleDraft) return
|
|
230
|
-
if (!roleDraft.name.trim()) {
|
|
231
|
-
setRoleFormError('角色名称不能为空')
|
|
232
|
-
return
|
|
233
|
-
}
|
|
234
|
-
if (!roleDraft.provider || !roleDraft.model) {
|
|
235
|
-
setRoleFormError('请选择角色绑定的模型')
|
|
236
|
-
return
|
|
237
|
-
}
|
|
238
|
-
const res = await mutate({ op: 'upsertRole', groupId: group.id, role: roleDraft })
|
|
239
|
-
if (res && res.ok && res.snapshot && !res.snapshot.error) {
|
|
240
|
-
setRoleDraft(null)
|
|
241
|
-
setRoleFormError('')
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
223
|
const stopRun = async (): Promise<void> => {
|
|
246
224
|
await action({ kind: 'stop', sessionId: sess!.id })
|
|
247
225
|
}
|
|
248
226
|
|
|
249
|
-
const openBrowser = async (path: string | undefined): Promise<void> => {
|
|
250
|
-
setFileBrowser({ open: true, loading: true, list: null, error: '' })
|
|
251
|
-
try {
|
|
252
|
-
const res = await api.action({ kind: 'browse', path: path || '' }) as import('../core/types.ts').BrowseResult
|
|
253
|
-
if (res && res.ok) setFileBrowser({ open: true, loading: false, list: res, error: '' })
|
|
254
|
-
else setFileBrowser({ open: true, loading: false, list: null, error: (res && res.error) || '浏览失败' })
|
|
255
|
-
} catch (e) {
|
|
256
|
-
setFileBrowser({ open: true, loading: false, list: null, error: String((e && (e as Error).message) || e) })
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const selectCurrentDir = (): void => {
|
|
261
|
-
if (!fileBrowser || !fileBrowser.list || !group) return
|
|
262
|
-
const path = fileBrowser.list.path!
|
|
263
|
-
void mutate({ op: 'setWorkspaceDir', groupId: group.id, path })
|
|
264
|
-
setWsDraft(null)
|
|
265
|
-
setFileBrowser(null)
|
|
266
|
-
}
|
|
267
|
-
|
|
268
227
|
return (
|
|
269
228
|
<div className="dsgc-root">
|
|
270
229
|
<NavPanel
|
|
@@ -338,14 +297,8 @@ export function GroupChatPanel(): ReactNode {
|
|
|
338
297
|
snap={snap}
|
|
339
298
|
group={group}
|
|
340
299
|
asideOpen={asideOpen}
|
|
341
|
-
wsDraft={wsDraft}
|
|
342
|
-
setWsDraft={setWsDraft}
|
|
343
|
-
fileBrowser={fileBrowser}
|
|
344
|
-
setFileBrowser={setFileBrowser}
|
|
345
300
|
mutate={mutate}
|
|
346
301
|
openRoleEditor={openRoleEditor}
|
|
347
|
-
openBrowser={openBrowser}
|
|
348
|
-
selectCurrentDir={selectCurrentDir}
|
|
349
302
|
/>
|
|
350
303
|
{sess && confirmClear
|
|
351
304
|
? (
|
|
@@ -386,12 +339,12 @@ export function GroupChatPanel(): ReactNode {
|
|
|
386
339
|
<RoleDrawer
|
|
387
340
|
draft={roleDraft}
|
|
388
341
|
set={setRoleDraft}
|
|
342
|
+
groupId={group.id}
|
|
389
343
|
models={models}
|
|
390
344
|
modelsError={modelsError}
|
|
391
345
|
onRetryModels={() => { void fetchModels() }}
|
|
392
|
-
|
|
393
|
-
onCancel={() => { setRoleDraft(null)
|
|
394
|
-
formError={roleFormError}
|
|
346
|
+
mutate={mutate}
|
|
347
|
+
onCancel={() => { setRoleDraft(null) }}
|
|
395
348
|
/>
|
|
396
349
|
)
|
|
397
350
|
: null}
|