dsh-side-chat-plus 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.
- package/LICENSE +21 -0
- package/README.md +317 -0
- package/README.zh.md +261 -0
- package/cordis.patch.yml +8 -0
- package/dsh.plugin.json +16 -0
- package/lib/client-registry.js +2949 -0
- package/lib/client-registry.js.map +1 -0
- package/lib/client.js +2949 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +840 -0
- package/lib/types/client/api.d.ts +218 -0
- package/lib/types/client/attachments/AttachmentRail.d.ts +39 -0
- package/lib/types/client/attachments/DropOverlay.d.ts +18 -0
- package/lib/types/client/attachments/ImageLightbox.d.ts +20 -0
- package/lib/types/client/attachments/MessageImage.d.ts +38 -0
- package/lib/types/client/attachments/index.d.ts +18 -0
- package/lib/types/client/index.d.ts +6 -0
- package/lib/types/client/locales.d.ts +178 -0
- package/lib/types/context-types.d.ts +390 -0
- package/lib/types/index.d.ts +7 -0
- package/lib/types/settings-shared.d.ts +24 -0
- package/lib/types/trust-fence.d.ts +20 -0
- package/lib/types/wire.d.ts +25 -0
- package/package.json +114 -0
- package/src/client/api.ts +112 -0
- package/src/client/attachments/AttachmentRail.module.css +89 -0
- package/src/client/attachments/AttachmentRail.tsx +173 -0
- package/src/client/attachments/DropOverlay.module.css +38 -0
- package/src/client/attachments/DropOverlay.tsx +62 -0
- package/src/client/attachments/ImageLightbox.module.css +44 -0
- package/src/client/attachments/ImageLightbox.tsx +58 -0
- package/src/client/attachments/MessageImage.module.css +61 -0
- package/src/client/attachments/MessageImage.tsx +120 -0
- package/src/client/attachments/index.ts +19 -0
- package/src/client/client.module.css +1032 -0
- package/src/client/index.tsx +1966 -0
- package/src/client/layout.css +16 -0
- package/src/client/locales.ts +181 -0
- package/src/context-types.ts +384 -0
- package/src/css-modules.d.ts +10 -0
- package/src/index.ts +840 -0
- package/src/settings-shared.ts +33 -0
- package/src/trust-fence.ts +70 -0
- package/src/wire.ts +81 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural types for the cordis services this plugin consumes, plus the
|
|
3
|
+
* Context augmentation both halves share. A third-party plugin resolves
|
|
4
|
+
* outside the DSH monorepo's single cordis instance, so the upstream
|
|
5
|
+
* `declare module 'cordis'` augmentations do not reach this Context; the
|
|
6
|
+
* members below mirror the actual runtime shapes this plugin touches.
|
|
7
|
+
*
|
|
8
|
+
* The `sessions` field is a union of the HOST store face (get/list) and the
|
|
9
|
+
* CLIENT list feed (list.getSnapshot/subscribe): each half only touches its
|
|
10
|
+
* own side, and the shared declaration keeps the two cordis instance layers
|
|
11
|
+
* from colliding under one tsconfig.
|
|
12
|
+
*/
|
|
13
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
14
|
+
import type { Context } from 'cordis';
|
|
15
|
+
/** One named webserver route. */
|
|
16
|
+
export interface SideWebRoute {
|
|
17
|
+
kind: 'exact' | 'prefix';
|
|
18
|
+
path: string;
|
|
19
|
+
handler: (req: IncomingMessage, res: ServerResponse) => void | Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
/** The webServer service face this plugin uses. */
|
|
22
|
+
export interface SideWebServer {
|
|
23
|
+
register(route: SideWebRoute): () => void;
|
|
24
|
+
}
|
|
25
|
+
/** A published session's header slice (authoritative cwd, lineage, origin). */
|
|
26
|
+
export interface SideSessionHeader {
|
|
27
|
+
cwd?: string;
|
|
28
|
+
parentSession?: string;
|
|
29
|
+
origin?: 'subagent';
|
|
30
|
+
delegationDepth?: number;
|
|
31
|
+
}
|
|
32
|
+
/** One session event (fold source for history and permission current). */
|
|
33
|
+
export interface SideSessionEvent {
|
|
34
|
+
type: string;
|
|
35
|
+
seq: number;
|
|
36
|
+
time: number;
|
|
37
|
+
data: Record<string, unknown>;
|
|
38
|
+
surfaceOp?: 'append' | 'replace' | 'remove';
|
|
39
|
+
sourceEventSeqs?: number[];
|
|
40
|
+
}
|
|
41
|
+
/** The live session face the host reads (header + append-only event log). */
|
|
42
|
+
export interface SideSession {
|
|
43
|
+
id: string;
|
|
44
|
+
header: SideSessionHeader;
|
|
45
|
+
events?: readonly SideSessionEvent[];
|
|
46
|
+
requestHeader?: () => {
|
|
47
|
+
config?: {
|
|
48
|
+
provider?: string;
|
|
49
|
+
model?: string;
|
|
50
|
+
reasoningEffort?: string;
|
|
51
|
+
maxTokens?: number;
|
|
52
|
+
};
|
|
53
|
+
} | undefined;
|
|
54
|
+
}
|
|
55
|
+
/** The host session store face (`ctx.sessions` host side). */
|
|
56
|
+
export interface SideSessionStore {
|
|
57
|
+
get(id: string): SideSession | undefined;
|
|
58
|
+
list(): SideSession[];
|
|
59
|
+
}
|
|
60
|
+
/** One session list row (client list feed). */
|
|
61
|
+
export interface SideSessionSummary {
|
|
62
|
+
id: string;
|
|
63
|
+
displayTitle: string;
|
|
64
|
+
cwd?: string;
|
|
65
|
+
origin?: 'subagent';
|
|
66
|
+
parentId?: string;
|
|
67
|
+
running: boolean;
|
|
68
|
+
}
|
|
69
|
+
/** The session list snapshot the browser half subscribes to. */
|
|
70
|
+
export interface SideSessionList {
|
|
71
|
+
current: string | undefined;
|
|
72
|
+
byId: Record<string, SideSessionSummary>;
|
|
73
|
+
}
|
|
74
|
+
/** One selectable answer in a user-question dialog. */
|
|
75
|
+
export interface SideQuestionOption {
|
|
76
|
+
label: string;
|
|
77
|
+
description?: string;
|
|
78
|
+
}
|
|
79
|
+
/** One question in a pending user-question dialog. */
|
|
80
|
+
export interface SideQuestionItem {
|
|
81
|
+
id: string;
|
|
82
|
+
question: string;
|
|
83
|
+
detail?: string;
|
|
84
|
+
header?: string;
|
|
85
|
+
options?: SideQuestionOption[];
|
|
86
|
+
multiSelect?: boolean;
|
|
87
|
+
}
|
|
88
|
+
/** A pending main-conversation interaction (question / plan-review / approval dialog). */
|
|
89
|
+
export interface SidePendingInteraction {
|
|
90
|
+
/** Presentation discriminator: the question family, or an approval. */
|
|
91
|
+
kind: 'question' | 'plan-review' | 'approval';
|
|
92
|
+
/** Opaque render identity and request key (e.g. `question:1`). */
|
|
93
|
+
key: string;
|
|
94
|
+
sessionId: string;
|
|
95
|
+
/** The request's question list (present for the question family). */
|
|
96
|
+
questions?: SideQuestionItem[];
|
|
97
|
+
}
|
|
98
|
+
/** The per-session pending-interaction source exposed by the `uiSession` service. */
|
|
99
|
+
export interface SidePendingInteractionStore {
|
|
100
|
+
getSnapshot(): ReadonlyMap<string, SidePendingInteraction>;
|
|
101
|
+
subscribe(fn: () => void): () => void;
|
|
102
|
+
}
|
|
103
|
+
/** The client sessions service face (list feed + scope resolution). */
|
|
104
|
+
export interface SideSessionsService {
|
|
105
|
+
list: {
|
|
106
|
+
getSnapshot(): SideSessionList;
|
|
107
|
+
subscribe(fn: () => void): () => void;
|
|
108
|
+
};
|
|
109
|
+
/** Resolve an Agent-scoped context view for a listed session id (use-and-discard). */
|
|
110
|
+
scope(id: string): Context | undefined;
|
|
111
|
+
}
|
|
112
|
+
/** The client UI-session service face (per-session pending interactions). */
|
|
113
|
+
export interface SideUiSessionService {
|
|
114
|
+
/** Per-session pending UI interaction (question / plan-review / approval). */
|
|
115
|
+
pendingInteractions: SidePendingInteractionStore;
|
|
116
|
+
}
|
|
117
|
+
/** Agent options (provider/model/maxTokens). */
|
|
118
|
+
export interface SideAgentOptions {
|
|
119
|
+
provider?: string;
|
|
120
|
+
model?: string;
|
|
121
|
+
maxTokens?: number;
|
|
122
|
+
}
|
|
123
|
+
/** The live agent face (`ctx.agents.get(id)` / `AgentHandle.agent`). */
|
|
124
|
+
export interface SideAgent {
|
|
125
|
+
readonly id: string;
|
|
126
|
+
readonly options: SideAgentOptions;
|
|
127
|
+
readonly session: SideSession;
|
|
128
|
+
readonly status: string;
|
|
129
|
+
readonly ctx: Context;
|
|
130
|
+
followup(message: unknown): void;
|
|
131
|
+
inject(message: unknown): void;
|
|
132
|
+
cancel(cause: {
|
|
133
|
+
kind: 'user' | 'parent' | 'disposed';
|
|
134
|
+
} | {
|
|
135
|
+
kind: 'hook';
|
|
136
|
+
reason: string;
|
|
137
|
+
}): void;
|
|
138
|
+
whenIdle(): Promise<void>;
|
|
139
|
+
}
|
|
140
|
+
/** The agent handle `ctx.agents.create` resolves (owner tears the agent down). */
|
|
141
|
+
export interface SideAgentHandle {
|
|
142
|
+
agent: SideAgent;
|
|
143
|
+
dispose(): Promise<void>;
|
|
144
|
+
}
|
|
145
|
+
/** `ctx.agents.create` options (mirror of CreateAgentOptions). */
|
|
146
|
+
export interface SideCreateAgentOptions {
|
|
147
|
+
sessionId: string;
|
|
148
|
+
meta?: {
|
|
149
|
+
cwd?: string;
|
|
150
|
+
parentSession?: string;
|
|
151
|
+
agentPreset?: string;
|
|
152
|
+
};
|
|
153
|
+
agentOptions?: SideAgentOptions;
|
|
154
|
+
setup?: (agentCtx: Context) => void | Promise<void>;
|
|
155
|
+
}
|
|
156
|
+
/** The host agent registry face. */
|
|
157
|
+
export interface SideAgentsService {
|
|
158
|
+
get(id: string): SideAgent | undefined;
|
|
159
|
+
create(options: SideCreateAgentOptions): Promise<SideAgentHandle>;
|
|
160
|
+
}
|
|
161
|
+
/** Live provider / model / reasoning-effort selection for one agent. */
|
|
162
|
+
export interface SideModelSelection {
|
|
163
|
+
provider: string;
|
|
164
|
+
model: string;
|
|
165
|
+
reasoningEffort?: string;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Mutable selection plus the value captured when the current step entered
|
|
169
|
+
* prompt assembly (`current` / `assembled`).
|
|
170
|
+
*/
|
|
171
|
+
export interface SideModelSelectionRef {
|
|
172
|
+
current: SideModelSelection | undefined;
|
|
173
|
+
assembled: SideModelSelection | undefined;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Couple a mutable selection to an agent's prompt assembly and request routing
|
|
177
|
+
* (`installModelSelection` on the agent package). Typed structurally rather
|
|
178
|
+
* than imported: this plugin resolves outside the DSH monorepo's single cordis
|
|
179
|
+
* instance, so the upstream export is reached through a loose signature.
|
|
180
|
+
*/
|
|
181
|
+
export type SideInstallModelSelection = (agentCtx: Context, selection: SideModelSelectionRef) => () => void;
|
|
182
|
+
/** The workspace registry face (archive a session durably). */
|
|
183
|
+
export interface SideWorkspaceRegistry {
|
|
184
|
+
archiveSession(sessionId: string): Promise<void>;
|
|
185
|
+
}
|
|
186
|
+
/** `sessionQuery.readSession` snapshot. */
|
|
187
|
+
export interface SideSessionLogSnapshot {
|
|
188
|
+
session: SideSessionHeader;
|
|
189
|
+
events: SideSessionEvent[];
|
|
190
|
+
}
|
|
191
|
+
/** The session query face. */
|
|
192
|
+
export interface SideSessionQuery {
|
|
193
|
+
readSession(sessionId: string): Promise<SideSessionLogSnapshot>;
|
|
194
|
+
}
|
|
195
|
+
/** Sandbox mode vocabulary (read from the session override). */
|
|
196
|
+
export type SideSandboxMode = 'read-only' | 'workspace-write' | 'danger-full-access';
|
|
197
|
+
/** The sandbox policy face. */
|
|
198
|
+
export interface SideSandboxPolicy {
|
|
199
|
+
overrideOf(session: SideSession): SideSandboxMode | undefined;
|
|
200
|
+
}
|
|
201
|
+
/** One permission preset option. */
|
|
202
|
+
export interface SidePresetOption {
|
|
203
|
+
value: string;
|
|
204
|
+
name: string;
|
|
205
|
+
description?: string;
|
|
206
|
+
}
|
|
207
|
+
/** The permission presets face. */
|
|
208
|
+
export interface SidePermissionPresets {
|
|
209
|
+
current(events: readonly SideSessionEvent[]): string;
|
|
210
|
+
set(session: SideSession, name: string): void;
|
|
211
|
+
selectFor(state: unknown): {
|
|
212
|
+
options: SidePresetOption[];
|
|
213
|
+
currentValue: string;
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/** The agent presets face (composeFrom joins a child to the parent's composition). */
|
|
217
|
+
export interface SideAgentPresets {
|
|
218
|
+
composeFrom(agentCtx: Context, parentCtx: Context): string | undefined;
|
|
219
|
+
}
|
|
220
|
+
/** One provider route. */
|
|
221
|
+
export interface SideLlmProvider {
|
|
222
|
+
id: string;
|
|
223
|
+
name: string;
|
|
224
|
+
}
|
|
225
|
+
/** One model entry. */
|
|
226
|
+
export interface SideLlmModel {
|
|
227
|
+
provider: string;
|
|
228
|
+
id: string;
|
|
229
|
+
name: string;
|
|
230
|
+
description?: string;
|
|
231
|
+
}
|
|
232
|
+
/** One reasoning-effort entry. */
|
|
233
|
+
export interface SideLlmEffort {
|
|
234
|
+
id: string;
|
|
235
|
+
name: string;
|
|
236
|
+
description?: string;
|
|
237
|
+
}
|
|
238
|
+
/** One model-request stream chunk (minimal structural view for text collection). */
|
|
239
|
+
export interface SideLlmStreamChunk {
|
|
240
|
+
type: string;
|
|
241
|
+
text?: string;
|
|
242
|
+
}
|
|
243
|
+
/** Minimal generate options the summarize route builds (a hand-built user prompt). */
|
|
244
|
+
export interface SideLlmGenerateOptions {
|
|
245
|
+
provider: string;
|
|
246
|
+
model: string;
|
|
247
|
+
reasoningEffort?: string;
|
|
248
|
+
maxTokens?: number;
|
|
249
|
+
signal?: AbortSignal;
|
|
250
|
+
messages: Array<{
|
|
251
|
+
id?: string;
|
|
252
|
+
role: 'user' | 'assistant' | 'system';
|
|
253
|
+
content: Array<{
|
|
254
|
+
type: 'text';
|
|
255
|
+
text: string;
|
|
256
|
+
}>;
|
|
257
|
+
source?: {
|
|
258
|
+
kind: string;
|
|
259
|
+
};
|
|
260
|
+
}>;
|
|
261
|
+
}
|
|
262
|
+
/** The llm face (model/effort catalog + one-shot streaming calls). */
|
|
263
|
+
export interface SideLlm {
|
|
264
|
+
listProviders(): SideLlmProvider[];
|
|
265
|
+
listModels(provider: string): Promise<SideLlmModel[]>;
|
|
266
|
+
resolveModelInfo(provider: string, model: string): Promise<{
|
|
267
|
+
provider: string;
|
|
268
|
+
id: string;
|
|
269
|
+
name: string;
|
|
270
|
+
reasoning?: {
|
|
271
|
+
efforts: readonly SideLlmEffort[];
|
|
272
|
+
defaultEffort?: string;
|
|
273
|
+
};
|
|
274
|
+
}>;
|
|
275
|
+
stream(options: SideLlmGenerateOptions): AsyncIterable<SideLlmStreamChunk>;
|
|
276
|
+
}
|
|
277
|
+
/** The client locale service face. */
|
|
278
|
+
export interface SideLocaleService {
|
|
279
|
+
getSnapshot(): {
|
|
280
|
+
active: string;
|
|
281
|
+
};
|
|
282
|
+
subscribe(fn: () => void): () => void;
|
|
283
|
+
register(ns: string, locale: string, dict: Record<string, string>): () => void;
|
|
284
|
+
}
|
|
285
|
+
/** The host settings service face (namespace registration + read/write). */
|
|
286
|
+
export interface SideSettingsService {
|
|
287
|
+
register(ns: unknown, schema: unknown): {
|
|
288
|
+
get(): unknown;
|
|
289
|
+
watch(cb: (next: unknown, prev: unknown) => void): () => void;
|
|
290
|
+
};
|
|
291
|
+
describe(opts: {
|
|
292
|
+
redactSecrets?: boolean;
|
|
293
|
+
}): Array<{
|
|
294
|
+
ns: unknown;
|
|
295
|
+
value?: unknown;
|
|
296
|
+
revision?: number;
|
|
297
|
+
}>;
|
|
298
|
+
update(ns: unknown, patch: Record<string, unknown>, expectedRevision?: number): Promise<void>;
|
|
299
|
+
}
|
|
300
|
+
/** The client slots service face (settings.section registration). */
|
|
301
|
+
export interface SideSlotsService {
|
|
302
|
+
inject(name: string, factory: () => () => void): void;
|
|
303
|
+
register(options: Record<string, unknown>, component: unknown): () => void;
|
|
304
|
+
}
|
|
305
|
+
/** One durable image reference (mirror of ImageAttachmentRef). */
|
|
306
|
+
export interface SideImageAttachmentRef {
|
|
307
|
+
attachmentId: string;
|
|
308
|
+
mediaType: 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif';
|
|
309
|
+
bytes: number;
|
|
310
|
+
width: number;
|
|
311
|
+
height: number;
|
|
312
|
+
name?: string;
|
|
313
|
+
}
|
|
314
|
+
/** The host attachment store face (`ctx.attachments`). */
|
|
315
|
+
export interface SideAttachmentStore {
|
|
316
|
+
readonly imageLimits: {
|
|
317
|
+
maxImageBytes: number;
|
|
318
|
+
maxImagesPerMessage: number;
|
|
319
|
+
maxMessageImageBytes: number;
|
|
320
|
+
maxImagePixels: number;
|
|
321
|
+
mediaTypes: readonly string[];
|
|
322
|
+
};
|
|
323
|
+
validateImage(input: {
|
|
324
|
+
data: Uint8Array;
|
|
325
|
+
mediaType: string;
|
|
326
|
+
name?: string;
|
|
327
|
+
}): Promise<void>;
|
|
328
|
+
saveImage(input: {
|
|
329
|
+
data: Uint8Array;
|
|
330
|
+
mediaType: string;
|
|
331
|
+
name?: string;
|
|
332
|
+
}): Promise<SideImageAttachmentRef>;
|
|
333
|
+
readImage(ref: SideImageAttachmentRef): Promise<{
|
|
334
|
+
ref: SideImageAttachmentRef;
|
|
335
|
+
data: Uint8Array;
|
|
336
|
+
}>;
|
|
337
|
+
}
|
|
338
|
+
/** The host command registry face (`ctx.commands`). */
|
|
339
|
+
export interface SideCommandsService {
|
|
340
|
+
list(agent: SideAgent): Array<{
|
|
341
|
+
name: string;
|
|
342
|
+
description: string;
|
|
343
|
+
}>;
|
|
344
|
+
execute(agent: SideAgent, line: string, signal: AbortSignal): Promise<unknown>;
|
|
345
|
+
}
|
|
346
|
+
/** The per-session composer input face this plugin writes to (draft-only). */
|
|
347
|
+
export interface SideSessionInput {
|
|
348
|
+
/** Replace the session composer draft (never submits). */
|
|
349
|
+
setDraft(text: string): void;
|
|
350
|
+
/** Live input state store (draft read). */
|
|
351
|
+
state: {
|
|
352
|
+
getSnapshot(): {
|
|
353
|
+
draft: string;
|
|
354
|
+
};
|
|
355
|
+
};
|
|
356
|
+
/** Surface a notice on the session composer. */
|
|
357
|
+
notify(level: 'info' | 'error', text: string): void;
|
|
358
|
+
}
|
|
359
|
+
/** The conversation service face (`ctx.conversation`) — input registry only. */
|
|
360
|
+
export interface SideConversationService {
|
|
361
|
+
input: {
|
|
362
|
+
/** Resolve the input facade for one session-scope context. */
|
|
363
|
+
for(actx: Context): SideSessionInput;
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
declare module 'cordis' {
|
|
367
|
+
interface Context {
|
|
368
|
+
webServer: SideWebServer;
|
|
369
|
+
sessions: SideSessionStore & SideSessionsService;
|
|
370
|
+
agents: SideAgentsService;
|
|
371
|
+
workspaceRegistry: SideWorkspaceRegistry;
|
|
372
|
+
sessionQuery: SideSessionQuery;
|
|
373
|
+
sandboxPolicy: SideSandboxPolicy;
|
|
374
|
+
permissionPresets: SidePermissionPresets;
|
|
375
|
+
agentPresets: SideAgentPresets;
|
|
376
|
+
llm: SideLlm;
|
|
377
|
+
locale: SideLocaleService;
|
|
378
|
+
settings: SideSettingsService;
|
|
379
|
+
slots: SideSlotsService;
|
|
380
|
+
attachments: SideAttachmentStore;
|
|
381
|
+
commands: SideCommandsService;
|
|
382
|
+
conversation: SideConversationService;
|
|
383
|
+
uiSession: SideUiSessionService;
|
|
384
|
+
inject(deps: string[], callback: (ctx: Context) => void): void;
|
|
385
|
+
get(name: string): unknown | undefined;
|
|
386
|
+
on(name: string, listener: (...args: any[]) => any): () => void;
|
|
387
|
+
effect(fn: () => void | (() => void), label?: string): void;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
export type { Context };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Context } from './context-types.ts';
|
|
2
|
+
/** Plugin identity for cordis.yml rows. */
|
|
3
|
+
export declare const name = "dsh-side-chat-plus";
|
|
4
|
+
/** Services required before mounting. */
|
|
5
|
+
export declare const inject: string[];
|
|
6
|
+
/** Host plugin body: register the /sidechat JSON API routes. */
|
|
7
|
+
export declare function apply(ctx: Context): void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared side-chat preference vocabulary (types + constants), consumed by
|
|
3
|
+
* BOTH halves: the host registers the schemastery schema over these values
|
|
4
|
+
* (index.ts) and the client reads/writes them through the plugin's own
|
|
5
|
+
* fenced /sidechat settings routes. Kept free of schemastery so the browser
|
|
6
|
+
* bundle never pulls the schema runtime in.
|
|
7
|
+
*/
|
|
8
|
+
/** The user-settings namespace holding the side-chat preferences. */
|
|
9
|
+
export declare const SUBCHAT_PREFS_NS = "dsh-side-chat";
|
|
10
|
+
/** How a brought-back reply lands in the main conversation. */
|
|
11
|
+
export type BringMode = 'draft' | 'context';
|
|
12
|
+
/** User-facing side-chat preferences. */
|
|
13
|
+
export interface SubchatPrefs {
|
|
14
|
+
/** Whether the "look up workspace / parent when needed" switch defaults on. */
|
|
15
|
+
lookupDefault: boolean;
|
|
16
|
+
/** Whether selecting text sends it immediately (true) or stages it as an attachment (false). */
|
|
17
|
+
sendImmediately: boolean;
|
|
18
|
+
/** Extra prompt appended when the selection is sent immediately (empty = none). */
|
|
19
|
+
defaultPrompt: string;
|
|
20
|
+
/** How brought-back content lands: into the composer draft, or as a collapsed context row. */
|
|
21
|
+
bringMode: BringMode;
|
|
22
|
+
}
|
|
23
|
+
/** Fallback prefs used whenever the settings document is unreachable or malformed. */
|
|
24
|
+
export declare const SUBCHAT_PREFS_DEFAULTS: SubchatPrefs;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-trust fence for the /sidechat routes, behaviorally identical to the
|
|
3
|
+
* /api gateway's fence (loopback or configured trusted authority; cross-site
|
|
4
|
+
* browser markers refuse). Self-contained copy, since the DSH connection
|
|
5
|
+
* package does not export these helpers.
|
|
6
|
+
*/
|
|
7
|
+
import type { IncomingHttpHeaders } from 'node:http';
|
|
8
|
+
interface ApiTrustRequest {
|
|
9
|
+
headers: IncomingHttpHeaders;
|
|
10
|
+
}
|
|
11
|
+
/** Whether a normalized URL hostname names the local loopback authority. */
|
|
12
|
+
export declare function isLoopbackHostname(hostname: string): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Decide whether one sidechat request may reach the plugin routes.
|
|
15
|
+
* @param request - node HTTP request facts (headers).
|
|
16
|
+
* @param trustedHosts - non-loopback authorities this deployment serves.
|
|
17
|
+
* @returns true when the Host is ours (loopback or trusted) and browser markers are same-origin.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isTrustedApiRequest(request: ApiTrustRequest, trustedHosts: readonly string[]): boolean;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire helpers for the /sidechat JSON API: bounded body reading, response
|
|
3
|
+
* writing, and the shared error envelope. Mirrors the /sidebar wire helpers
|
|
4
|
+
* (loopback + trusted-host fence), kept self-contained so the plugin never
|
|
5
|
+
* depends on the DSH gateway internals.
|
|
6
|
+
*/
|
|
7
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
8
|
+
/** One API failure with its wire code and HTTP status. */
|
|
9
|
+
export declare class SidechatError extends Error {
|
|
10
|
+
readonly code: string;
|
|
11
|
+
readonly status: number;
|
|
12
|
+
constructor(code: string, message: string, status?: number);
|
|
13
|
+
}
|
|
14
|
+
/** Read and parse the JSON request body (bounded; malformed → bad-request). */
|
|
15
|
+
export declare function readJsonBody(req: IncomingMessage): Promise<unknown>;
|
|
16
|
+
/** Write a JSON response with the given status. */
|
|
17
|
+
export declare function writeJson(res: ServerResponse, status: number, body: unknown): void;
|
|
18
|
+
/** Write the success envelope. */
|
|
19
|
+
export declare function writeOk(res: ServerResponse, value: unknown): void;
|
|
20
|
+
/** Write the failure envelope for any thrown value (unknown → internal 500). */
|
|
21
|
+
export declare function writeError(res: ServerResponse, error: unknown): void;
|
|
22
|
+
/** Narrow an unknown payload value to a string, else throw bad-request. */
|
|
23
|
+
export declare function requireString(payload: unknown, key: string): string;
|
|
24
|
+
/** Narrow an unknown payload value to a boolean (default false). */
|
|
25
|
+
export declare function optionalBoolean(payload: unknown, key: string): boolean;
|
package/package.json
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-side-chat-plus",
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "侧边聊天 Plus (Side chat Plus) — DSH web plugin: select text in a conversation and ask it in a per-session side chat (hidden ordinary session, own right-side panel, inherits model/effort/permissions, lookup toggle).",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "lib/index.js",
|
|
8
|
+
"types": "lib/types/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./lib/types/index.d.ts",
|
|
12
|
+
"default": "./lib/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./client": {
|
|
15
|
+
"types": "./lib/types/client/index.d.ts",
|
|
16
|
+
"default": "./lib/client.js"
|
|
17
|
+
},
|
|
18
|
+
"./src/*": "./src/*",
|
|
19
|
+
"./package.json": "./package.json"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=20"
|
|
23
|
+
},
|
|
24
|
+
"dsh": {
|
|
25
|
+
"bundle": {
|
|
26
|
+
"patch": "./cordis.patch.yml"
|
|
27
|
+
},
|
|
28
|
+
"client": {
|
|
29
|
+
"inject": [
|
|
30
|
+
"@deepseek-ai/dsh-api-session-controller",
|
|
31
|
+
"@deepseek-ai/dsh-client-locale",
|
|
32
|
+
"@deepseek-ai/dsh-client-ui-conversation",
|
|
33
|
+
"@deepseek-ai/dsh-client-ui-renderer",
|
|
34
|
+
"@deepseek-ai/dsh-client-ui-session",
|
|
35
|
+
"@deepseek-ai/dsh-client-ui-settings"
|
|
36
|
+
],
|
|
37
|
+
"platform": "web"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"lib/index.js",
|
|
42
|
+
"lib/client.js",
|
|
43
|
+
"lib/client-registry.js",
|
|
44
|
+
"lib/**/*.map",
|
|
45
|
+
"lib/types/**/*.d.ts",
|
|
46
|
+
"cordis.patch.yml",
|
|
47
|
+
"dsh.plugin.json",
|
|
48
|
+
"src"
|
|
49
|
+
],
|
|
50
|
+
"license": "MIT",
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"schemastery": "^3.18.0"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
56
|
+
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
57
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
58
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
59
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
60
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
61
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
62
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
63
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
64
|
+
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
65
|
+
"@deepseek-ai/dsh-session": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
66
|
+
"@deepseek-ai/dsh-session-query": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
67
|
+
"@deepseek-ai/dsh-workspace": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
68
|
+
"@deepseek-ai/dsh-permission-presets": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
69
|
+
"@deepseek-ai/dsh-sandbox-policy": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
70
|
+
"@deepseek-ai/dsh-agent-presets": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
71
|
+
"@deepseek-ai/dsh-settings": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
72
|
+
"@deepseek-ai/dsh-home-paths": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
73
|
+
"cordis": "^4.0.0-rc.7",
|
|
74
|
+
"react": "^18.2.0",
|
|
75
|
+
"react-dom": "^18.2.0"
|
|
76
|
+
},
|
|
77
|
+
"devDependencies": {
|
|
78
|
+
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
79
|
+
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
80
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
81
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
82
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
83
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
84
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
85
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
86
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
87
|
+
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
88
|
+
"@deepseek-ai/dsh-session": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
89
|
+
"@deepseek-ai/dsh-session-query": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
90
|
+
"@deepseek-ai/dsh-workspace": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
91
|
+
"@deepseek-ai/dsh-permission-presets": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
92
|
+
"@deepseek-ai/dsh-sandbox-policy": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
93
|
+
"@deepseek-ai/dsh-agent-presets": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
94
|
+
"@deepseek-ai/dsh-settings": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
95
|
+
"@deepseek-ai/dsh-home-paths": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0",
|
|
96
|
+
"@types/node": "^24.0.0",
|
|
97
|
+
"@types/react": "~18.3.1",
|
|
98
|
+
"@types/react-dom": "~18.3.1",
|
|
99
|
+
"cordis": "^4.0.0-rc.7",
|
|
100
|
+
"lightningcss": "^1.32.0",
|
|
101
|
+
"react": "^18.2.0",
|
|
102
|
+
"react-dom": "18.2.0",
|
|
103
|
+
"tsdown": "^0.22.2",
|
|
104
|
+
"typescript": "^5.6.0",
|
|
105
|
+
"vitest": "^4.1.8"
|
|
106
|
+
},
|
|
107
|
+
"scripts": {
|
|
108
|
+
"build": "node -e \"require('node:fs').rmSync('lib',{recursive:true,force:true})\" && tsc -p tsconfig.build.json && tsdown",
|
|
109
|
+
"typecheck": "tsc --noEmit",
|
|
110
|
+
"bundle": "tsdown",
|
|
111
|
+
"watch": "tsdown --watch",
|
|
112
|
+
"test": "vitest run --passWithNoTests"
|
|
113
|
+
}
|
|
114
|
+
}
|