dsh-side-chat-plus 0.4.2 → 0.4.3
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/CHANGELOG.md +19 -0
- package/README.md +3 -3
- package/README.zh.md +3 -3
- package/dsh.plugin.json +1 -1
- package/lib/client-registry.js +102 -92
- package/lib/client-registry.js.map +1 -1
- package/lib/client.js +105 -95
- package/lib/client.js.map +1 -1
- package/lib/index.js +27 -16
- package/package.json +20 -20
- package/src/client/index.tsx +25 -3
- package/src/index.ts +24 -6
package/lib/index.js
CHANGED
|
@@ -336,20 +336,24 @@ function buildApi(ctx, sideChats, getSettings) {
|
|
|
336
336
|
});
|
|
337
337
|
else {
|
|
338
338
|
const data = decodeBase64(part.data);
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
339
|
+
try {
|
|
340
|
+
await ctx.attachments.validateImage({
|
|
341
|
+
data,
|
|
342
|
+
mediaType: part.mediaType,
|
|
343
|
+
...part.name === void 0 ? {} : { name: part.name }
|
|
344
|
+
});
|
|
345
|
+
const attachment = await ctx.attachments.saveImage({
|
|
346
|
+
data,
|
|
347
|
+
mediaType: part.mediaType,
|
|
348
|
+
...part.name === void 0 ? {} : { name: part.name }
|
|
349
|
+
});
|
|
350
|
+
blocks.push({
|
|
351
|
+
type: "image",
|
|
352
|
+
attachment
|
|
353
|
+
});
|
|
354
|
+
} catch (error) {
|
|
355
|
+
throw new SidechatError("bad-image", error instanceof Error ? error.message : String(error), 400);
|
|
356
|
+
}
|
|
353
357
|
}
|
|
354
358
|
blocks.push({
|
|
355
359
|
type: "text",
|
|
@@ -512,6 +516,7 @@ function buildApi(ctx, sideChats, getSettings) {
|
|
|
512
516
|
const content = requireContent(payload);
|
|
513
517
|
const lookupEnabled = optionalBoolean(payload, "lookupEnabled");
|
|
514
518
|
const parent = parentOf(parentSessionId);
|
|
519
|
+
const blocks = await durableContent(content, lookupEnabled);
|
|
515
520
|
const record = payload;
|
|
516
521
|
const parentConfig = parent.session.requestHeader?.()?.config;
|
|
517
522
|
const parentProvider = parentConfig?.provider ?? parent.options.provider ?? "";
|
|
@@ -580,7 +585,7 @@ function buildApi(ctx, sideChats, getSettings) {
|
|
|
580
585
|
};
|
|
581
586
|
sideChats.set(childId, child);
|
|
582
587
|
persist();
|
|
583
|
-
await deliver(child, userMessage(
|
|
588
|
+
await deliver(child, userMessage(blocks));
|
|
584
589
|
return {
|
|
585
590
|
childId,
|
|
586
591
|
provider,
|
|
@@ -637,7 +642,13 @@ function buildApi(ctx, sideChats, getSettings) {
|
|
|
637
642
|
/** Fold one side chat's transcript. */
|
|
638
643
|
const history = async (payload) => {
|
|
639
644
|
const childId = requireString(payload, "childId");
|
|
640
|
-
|
|
645
|
+
let events;
|
|
646
|
+
try {
|
|
647
|
+
events = (await ctx.sessionQuery.readSession(childId)).events;
|
|
648
|
+
} catch (error) {
|
|
649
|
+
throw new SidechatError("not-found", error instanceof Error ? error.message : String(error), 404);
|
|
650
|
+
}
|
|
651
|
+
return { messages: foldTranscript(events) };
|
|
641
652
|
};
|
|
642
653
|
/** The deployment-resolved image policy (for client-side fast-path checks). */
|
|
643
654
|
const limits = () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-side-chat-plus",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
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
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -92,23 +92,23 @@
|
|
|
92
92
|
"@deepseek-ai/dsh-agent-preset-registry": "^0.1.7-0"
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
|
-
"@deepseek-ai/dsh-agent": "0.1.7-
|
|
96
|
-
"@deepseek-ai/dsh-attachment": "0.1.7-
|
|
97
|
-
"@deepseek-ai/dsh-client-locale": "0.1.7-
|
|
98
|
-
"@deepseek-ai/dsh-client-ui-conversation": "0.1.7-
|
|
99
|
-
"@deepseek-ai/dsh-client-ui-renderer": "0.1.7-
|
|
100
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.1.7-
|
|
101
|
-
"@deepseek-ai/dsh-client-ui-settings": "0.1.7-
|
|
102
|
-
"@deepseek-ai/dsh-client-ui-primitives": "0.1.7-
|
|
103
|
-
"@deepseek-ai/dsh-host-webserver": "0.1.7-
|
|
104
|
-
"@deepseek-ai/dsh-llm": "0.1.7-
|
|
105
|
-
"@deepseek-ai/dsh-session": "0.1.7-
|
|
106
|
-
"@deepseek-ai/dsh-session-query": "0.1.7-
|
|
107
|
-
"@deepseek-ai/dsh-workspace": "0.1.7-
|
|
108
|
-
"@deepseek-ai/dsh-permission-presets": "0.1.7-
|
|
109
|
-
"@deepseek-ai/dsh-sandbox-policy": "0.1.7-
|
|
110
|
-
"@deepseek-ai/dsh-settings": "0.1.7-
|
|
111
|
-
"@deepseek-ai/dsh-home-paths": "0.1.7-
|
|
95
|
+
"@deepseek-ai/dsh-agent": "0.1.7-rc.1",
|
|
96
|
+
"@deepseek-ai/dsh-attachment": "0.1.7-rc.1",
|
|
97
|
+
"@deepseek-ai/dsh-client-locale": "0.1.7-rc.1",
|
|
98
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.1.7-rc.1",
|
|
99
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.7-rc.1",
|
|
100
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.1",
|
|
101
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.1",
|
|
102
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.1",
|
|
103
|
+
"@deepseek-ai/dsh-host-webserver": "0.1.7-rc.1",
|
|
104
|
+
"@deepseek-ai/dsh-llm": "0.1.7-rc.1",
|
|
105
|
+
"@deepseek-ai/dsh-session": "0.1.7-rc.1",
|
|
106
|
+
"@deepseek-ai/dsh-session-query": "0.1.7-rc.1",
|
|
107
|
+
"@deepseek-ai/dsh-workspace": "0.1.7-rc.1",
|
|
108
|
+
"@deepseek-ai/dsh-permission-presets": "0.1.7-rc.1",
|
|
109
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.1.7-rc.1",
|
|
110
|
+
"@deepseek-ai/dsh-settings": "0.1.7-rc.1",
|
|
111
|
+
"@deepseek-ai/dsh-home-paths": "0.1.7-rc.1",
|
|
112
112
|
"@types/node": "^24.0.0",
|
|
113
113
|
"@types/react": "~18.3.1",
|
|
114
114
|
"@types/react-dom": "~18.3.1",
|
|
@@ -119,8 +119,8 @@
|
|
|
119
119
|
"tsdown": "^0.22.2",
|
|
120
120
|
"typescript": "^5.6.0",
|
|
121
121
|
"vitest": "^4.1.8",
|
|
122
|
-
"@deepseek-ai/dsh-agent-preset-registry": "0.1.7-
|
|
123
|
-
"@deepseek-ai/dsh-session-format-catalog": "0.1.7-
|
|
122
|
+
"@deepseek-ai/dsh-agent-preset-registry": "0.1.7-rc.1",
|
|
123
|
+
"@deepseek-ai/dsh-session-format-catalog": "0.1.7-rc.1"
|
|
124
124
|
},
|
|
125
125
|
"scripts": {
|
|
126
126
|
"build": "node -e \"require('node:fs').rmSync('lib',{recursive:true,force:true})\" && tsc -p tsconfig.build.json && tsdown && node scripts/check-seed-exports.mjs",
|
package/src/client/index.tsx
CHANGED
|
@@ -90,6 +90,12 @@ interface SidechatSnapshot {
|
|
|
90
90
|
mainQuestion: SideQuestionItem[] | null
|
|
91
91
|
/** Question ids the user deleted from the panel list. */
|
|
92
92
|
dismissedQuestionIds: string[]
|
|
93
|
+
/**
|
|
94
|
+
* Whether this deployment offers the built-in right sidebar. False means
|
|
95
|
+
* `panelHome: 'sidebar-right'` cannot be honoured and the panel falls back to
|
|
96
|
+
* the classic floating container instead of becoming unreachable.
|
|
97
|
+
*/
|
|
98
|
+
dockAvailable: boolean
|
|
93
99
|
}
|
|
94
100
|
|
|
95
101
|
/** The whole browser-side store (one per activation). */
|
|
@@ -106,6 +112,8 @@ interface SidechatStore {
|
|
|
106
112
|
closePanel(): void
|
|
107
113
|
/** Install (or clear) the dock-mode launcher; openPanel() fires it after opening. */
|
|
108
114
|
setDockLaunch(fn: (() => void) | null): void
|
|
115
|
+
/** Record whether the built-in right sidebar exists in this deployment. */
|
|
116
|
+
setDockAvailable(available: boolean): void
|
|
109
117
|
setActive(childId: string): void
|
|
110
118
|
patch(partial: Partial<PanelState>): void
|
|
111
119
|
}
|
|
@@ -143,6 +151,10 @@ function createStore(): SidechatStore {
|
|
|
143
151
|
let prefs: SubchatPrefs = { ...SUBCHAT_PREFS_DEFAULTS }
|
|
144
152
|
let mainQuestion: SideQuestionItem[] | null = null
|
|
145
153
|
let dismissedQuestionIds: string[] = []
|
|
154
|
+
// Optimistic until the dock effect has inspected the services: a deployment
|
|
155
|
+
// whose sidebar services are missing flips this to false and the panel
|
|
156
|
+
// renders floating instead of nowhere.
|
|
157
|
+
let dockAvailable = true
|
|
146
158
|
// Per-conversation panel state so switching away and back restores the side
|
|
147
159
|
// chats instead of resetting them. The side chats stay live on the host, so
|
|
148
160
|
// the client must remember each conversation's open panel + active child.
|
|
@@ -153,10 +165,10 @@ function createStore(): SidechatStore {
|
|
|
153
165
|
let dockLaunch: (() => void) | null = null
|
|
154
166
|
// Cached snapshot: useSyncExternalStore compares identity, so the object is
|
|
155
167
|
// only rebuilt on a mutation — never inside getSnapshot itself.
|
|
156
|
-
let snapshot: SidechatSnapshot = { current, panel, anchor, prefs, mainQuestion, dismissedQuestionIds }
|
|
168
|
+
let snapshot: SidechatSnapshot = { current, panel, anchor, prefs, mainQuestion, dismissedQuestionIds, dockAvailable }
|
|
157
169
|
|
|
158
170
|
const notify = (): void => {
|
|
159
|
-
snapshot = { current, panel, anchor, prefs, mainQuestion, dismissedQuestionIds }
|
|
171
|
+
snapshot = { current, panel, anchor, prefs, mainQuestion, dismissedQuestionIds, dockAvailable }
|
|
160
172
|
for (const fn of [...listeners]) fn()
|
|
161
173
|
}
|
|
162
174
|
|
|
@@ -214,6 +226,11 @@ function createStore(): SidechatStore {
|
|
|
214
226
|
setDockLaunch(fn) {
|
|
215
227
|
dockLaunch = fn
|
|
216
228
|
},
|
|
229
|
+
setDockAvailable(available) {
|
|
230
|
+
if (available === dockAvailable) return
|
|
231
|
+
dockAvailable = available
|
|
232
|
+
notify()
|
|
233
|
+
},
|
|
217
234
|
setActive(childId) {
|
|
218
235
|
panel = { ...panel, activeChildId: childId, messages: [], error: null }
|
|
219
236
|
notify()
|
|
@@ -1755,7 +1772,7 @@ function SideChatShell(props: {
|
|
|
1755
1772
|
onOpen={() => { props.store.openPanel(snap.panel.parentSessionId) }}
|
|
1756
1773
|
/>
|
|
1757
1774
|
)}
|
|
1758
|
-
{snap.prefs.panelHome === 'floating' && (
|
|
1775
|
+
{(snap.prefs.panelHome === 'floating' || !snap.dockAvailable) && (
|
|
1759
1776
|
<SidechatPanel
|
|
1760
1777
|
store={props.store}
|
|
1761
1778
|
t={props.t}
|
|
@@ -2187,9 +2204,14 @@ export function apply(ctx: Context): void {
|
|
|
2187
2204
|
?? sidebarRight?.tabs
|
|
2188
2205
|
if (sidebarRight === undefined || sidebarRightTabs === undefined) {
|
|
2189
2206
|
phase = 'unavailable'
|
|
2207
|
+
// The floating container takes over (SideChatShell renders it while
|
|
2208
|
+
// `dockAvailable` is false); the notice only explains why the dock
|
|
2209
|
+
// preference is not in effect.
|
|
2210
|
+
store.setDockAvailable(false)
|
|
2190
2211
|
store.patch({ error: translate(activeLocale, 'dock.unavailable') })
|
|
2191
2212
|
return
|
|
2192
2213
|
}
|
|
2214
|
+
store.setDockAvailable(true)
|
|
2193
2215
|
const dockT = (key: SidechatLocaleKey): string => translate(activeLocale, key)
|
|
2194
2216
|
|
|
2195
2217
|
const disposeType = sidebarRightTabs.register({
|
package/src/index.ts
CHANGED
|
@@ -270,9 +270,16 @@ function buildApi(ctx: Context, sideChats: Map<string, SidechatRecord>, getSetti
|
|
|
270
270
|
blocks.push({ type: 'text', text: part.text })
|
|
271
271
|
} else {
|
|
272
272
|
const data = decodeBase64(part.data)
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
273
|
+
try {
|
|
274
|
+
await ctx.attachments.validateImage({ data, mediaType: part.mediaType, ...(part.name === undefined ? {} : { name: part.name }) })
|
|
275
|
+
const attachment = await ctx.attachments.saveImage({ data, mediaType: part.mediaType, ...(part.name === undefined ? {} : { name: part.name }) })
|
|
276
|
+
blocks.push({ type: 'image', attachment })
|
|
277
|
+
} catch (error) {
|
|
278
|
+
// The attachment store rejects a malformed/oversized image with its
|
|
279
|
+
// own error type; report it as a client-side bad request (400) rather
|
|
280
|
+
// than an unexplained host fault (500).
|
|
281
|
+
throw new SidechatError('bad-image', error instanceof Error ? error.message : String(error), 400)
|
|
282
|
+
}
|
|
276
283
|
}
|
|
277
284
|
}
|
|
278
285
|
blocks.push({ type: 'text', text: lookupEnabled ? LOOKUP_GUIDANCE : NO_LOOKUP_GUIDANCE })
|
|
@@ -445,6 +452,10 @@ function buildApi(ctx: Context, sideChats: Map<string, SidechatRecord>, getSetti
|
|
|
445
452
|
const content = requireContent(payload)
|
|
446
453
|
const lookupEnabled = optionalBoolean(payload, 'lookupEnabled')
|
|
447
454
|
const parent = parentOf(parentSessionId)
|
|
455
|
+
// Validate and durably store the prompt's images BEFORE the side chat
|
|
456
|
+
// exists: a rejected image must not leave a live, empty side chat (and a
|
|
457
|
+
// persisted record) behind for a request that never ran.
|
|
458
|
+
const blocks = await durableContent(content, lookupEnabled)
|
|
448
459
|
|
|
449
460
|
const record = payload as { provider?: unknown; model?: unknown; reasoningEffort?: unknown; preset?: unknown }
|
|
450
461
|
const parentConfig = parent.session.requestHeader?.()?.config
|
|
@@ -539,7 +550,7 @@ function buildApi(ctx: Context, sideChats: Map<string, SidechatRecord>, getSetti
|
|
|
539
550
|
sideChats.set(childId, child)
|
|
540
551
|
persist()
|
|
541
552
|
|
|
542
|
-
await deliver(child, userMessage(
|
|
553
|
+
await deliver(child, userMessage(blocks))
|
|
543
554
|
return { childId, provider, model, ...(reasoningEffort === undefined ? {} : { reasoningEffort }) }
|
|
544
555
|
}
|
|
545
556
|
|
|
@@ -603,8 +614,15 @@ function buildApi(ctx: Context, sideChats: Map<string, SidechatRecord>, getSetti
|
|
|
603
614
|
/** Fold one side chat's transcript. */
|
|
604
615
|
const history = async (payload: unknown): Promise<{ messages: Array<{ role: 'user' | 'assistant'; blocks: TranscriptBlock[] }> }> => {
|
|
605
616
|
const childId = requireString(payload, 'childId')
|
|
606
|
-
|
|
607
|
-
|
|
617
|
+
let events: readonly SideSessionEvent[]
|
|
618
|
+
try {
|
|
619
|
+
events = (await ctx.sessionQuery.readSession(childId)).events
|
|
620
|
+
} catch (error) {
|
|
621
|
+
// A side chat whose session is gone (deleted, disposed, or an id from a
|
|
622
|
+
// previous run) is a client-side absence, not a host fault.
|
|
623
|
+
throw new SidechatError('not-found', error instanceof Error ? error.message : String(error), 404)
|
|
624
|
+
}
|
|
625
|
+
return { messages: foldTranscript(events) }
|
|
608
626
|
}
|
|
609
627
|
|
|
610
628
|
/** The deployment-resolved image policy (for client-side fast-path checks). */
|