dsh-side-chat-plus 0.3.4 → 0.4.0-alpha.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/README.md +9 -1
- package/README.zh.md +7 -1
- package/dsh.plugin.json +2 -2
- package/lib/client-registry.js +143 -113
- package/lib/client-registry.js.map +1 -1
- package/lib/client.js +141 -111
- package/lib/client.js.map +1 -1
- package/lib/index.js +60 -25
- package/lib/types/client/api.d.ts +1 -0
- package/lib/types/client/locales.d.ts +2 -0
- package/lib/types/context-types.d.ts +15 -16
- package/package.json +39 -38
- package/src/client/api.ts +1 -1
- package/src/client/index.tsx +52 -21
- package/src/client/locales.ts +2 -0
- package/src/context-types.ts +13 -18
- package/src/index.ts +60 -22
package/lib/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname } from "node:path";
|
|
4
4
|
import * as agentApi from "@deepseek-ai/dsh-agent";
|
|
5
5
|
import { dshHomePath } from "@deepseek-ai/dsh-home-paths";
|
|
@@ -171,7 +171,6 @@ const inject = [
|
|
|
171
171
|
"agents",
|
|
172
172
|
"workspaceRegistry",
|
|
173
173
|
"sessionQuery",
|
|
174
|
-
"sandboxPolicy",
|
|
175
174
|
"permissionPresets",
|
|
176
175
|
"agentPresets",
|
|
177
176
|
"llm",
|
|
@@ -191,6 +190,24 @@ const RECORD_FILE = "dsh-side-chat-sessions.json";
|
|
|
191
190
|
function recordFilePath() {
|
|
192
191
|
return dshHomePath(RECORD_FILE);
|
|
193
192
|
}
|
|
193
|
+
/** Read the durable side-chat record list (absent/malformed → empty). */
|
|
194
|
+
async function readRecords() {
|
|
195
|
+
try {
|
|
196
|
+
const raw = await readFile(recordFilePath(), "utf8");
|
|
197
|
+
const parsed = JSON.parse(raw);
|
|
198
|
+
if (!Array.isArray(parsed)) return [];
|
|
199
|
+
return parsed.filter((entry) => {
|
|
200
|
+
const record = entry;
|
|
201
|
+
return typeof record?.childId === "string" && typeof record?.parentSessionId === "string";
|
|
202
|
+
}).map((entry) => ({
|
|
203
|
+
childId: entry.childId,
|
|
204
|
+
parentSessionId: entry.parentSessionId,
|
|
205
|
+
createdAt: typeof entry.createdAt === "number" ? entry.createdAt : 0
|
|
206
|
+
}));
|
|
207
|
+
} catch {
|
|
208
|
+
return [];
|
|
209
|
+
}
|
|
210
|
+
}
|
|
194
211
|
/** Persist the live side-chat record list (the "record" a future cleanup consumes). */
|
|
195
212
|
async function writeRecords(records) {
|
|
196
213
|
const path = recordFilePath();
|
|
@@ -269,14 +286,6 @@ function openTurnStart(events) {
|
|
|
269
286
|
if (lastEnd !== void 0 && lastEnd >= lastStart) return void 0;
|
|
270
287
|
return lastStart;
|
|
271
288
|
}
|
|
272
|
-
/**
|
|
273
|
-
* Read a session's event log across DSH versions: 0.1.5+ exposes
|
|
274
|
-
* `snapshotEvents()`; 0.1.2-era sessions carried a plain `events` getter.
|
|
275
|
-
*/
|
|
276
|
-
function sessionEvents(session) {
|
|
277
|
-
if (typeof session.snapshotEvents === "function") return session.snapshotEvents();
|
|
278
|
-
return session.events ?? [];
|
|
279
|
-
}
|
|
280
289
|
/** Schemastery schema for the user-facing preferences (validated by the settings service). */
|
|
281
290
|
const PrefsSchema = z.object({
|
|
282
291
|
lookupDefault: z.boolean().default(SUBCHAT_PREFS_DEFAULTS.lookupDefault),
|
|
@@ -399,10 +408,18 @@ function buildApi(ctx, sideChats, getSettings) {
|
|
|
399
408
|
const child = childOf(childId);
|
|
400
409
|
return { executed: await ctx.commands.execute(child, line, [], new AbortController().signal) !== void 0 };
|
|
401
410
|
};
|
|
411
|
+
/**
|
|
412
|
+
* Read a side chat's event log through the session-query service. DSH 0.1.6
|
|
413
|
+
* deprecated the Session's synchronous event getters (`snapshotEvents`,
|
|
414
|
+
* `eventAt`, `ownEvents`); `readSession` is the live-preferred asynchronous
|
|
415
|
+
* read that supersedes them, and the transcript routes already use it.
|
|
416
|
+
*/
|
|
417
|
+
const sessionEvents = async (sessionId) => (await ctx.sessionQuery.readSession(sessionId)).events;
|
|
402
418
|
/** Fold one side-chat agent's plan/goal state for its composer chrome. */
|
|
403
|
-
const state = (payload) => {
|
|
419
|
+
const state = async (payload) => {
|
|
404
420
|
const childId = requireString(payload, "childId");
|
|
405
|
-
|
|
421
|
+
childOf(childId);
|
|
422
|
+
const events = await sessionEvents(childId);
|
|
406
423
|
let planActive = false;
|
|
407
424
|
let planWanted = null;
|
|
408
425
|
let goal = null;
|
|
@@ -524,15 +541,14 @@ function buildApi(ctx, sideChats, getSettings) {
|
|
|
524
541
|
return { accepted: true };
|
|
525
542
|
};
|
|
526
543
|
/** List the side chats launched by one parent conversation. */
|
|
527
|
-
const list = (payload) => {
|
|
544
|
+
const list = async (payload) => {
|
|
528
545
|
const parentSessionId = requireString(payload, "parentSessionId");
|
|
529
546
|
const items = [];
|
|
530
547
|
for (const record of sideChats.values()) {
|
|
531
548
|
if (record.parentSessionId !== parentSessionId) continue;
|
|
532
|
-
const
|
|
533
|
-
const status = agent.status;
|
|
549
|
+
const status = record.handle.agent.status;
|
|
534
550
|
const statusRunning = status === "running";
|
|
535
|
-
const eventRunningSince = openTurnStart(sessionEvents(
|
|
551
|
+
const eventRunningSince = status === void 0 ? openTurnStart(await sessionEvents(record.childId)) : void 0;
|
|
536
552
|
const running = statusRunning || status === void 0 && eventRunningSince !== void 0;
|
|
537
553
|
const runningSince = running ? eventRunningSince ?? Date.now() : void 0;
|
|
538
554
|
items.push({
|
|
@@ -562,7 +578,8 @@ function buildApi(ctx, sideChats, getSettings) {
|
|
|
562
578
|
maxImageBytes: l.maxImageBytes,
|
|
563
579
|
maxImagesPerMessage: l.maxImagesPerMessage,
|
|
564
580
|
maxMessageImageBytes: l.maxMessageImageBytes,
|
|
565
|
-
maxImagePixels: l.maxImagePixels
|
|
581
|
+
maxImagePixels: l.maxImagePixels,
|
|
582
|
+
...l.maxImageDimension === void 0 ? {} : { maxImageDimension: l.maxImageDimension }
|
|
566
583
|
};
|
|
567
584
|
};
|
|
568
585
|
/** Read one durable image's bytes for transcript rendering. */
|
|
@@ -707,15 +724,26 @@ function buildApi(ctx, sideChats, getSettings) {
|
|
|
707
724
|
};
|
|
708
725
|
/** Permission-preset options for the side-chat selector. */
|
|
709
726
|
const permissions = () => {
|
|
710
|
-
const
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
727
|
+
const presets = ctx.permissionPresets;
|
|
728
|
+
const toOption = (o) => ({
|
|
729
|
+
value: o.value,
|
|
730
|
+
name: o.name,
|
|
731
|
+
...o.description === void 0 ? {} : { description: o.description }
|
|
732
|
+
});
|
|
733
|
+
const catalog = presets.catalog;
|
|
734
|
+
if (typeof catalog === "function") return {
|
|
735
|
+
options: catalog.call(presets).options.map(toOption),
|
|
736
|
+
current: presets.defaultPreset ?? "custom"
|
|
718
737
|
};
|
|
738
|
+
const selectFor = presets.selectFor;
|
|
739
|
+
if (typeof selectFor === "function") {
|
|
740
|
+
const select = selectFor.call(presets, {});
|
|
741
|
+
return {
|
|
742
|
+
options: select.options.map(toOption),
|
|
743
|
+
current: select.currentValue
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
throw new SidechatError("permissions-unavailable", "the permission-presets service exposes neither catalog() nor selectFor()", 503);
|
|
719
747
|
};
|
|
720
748
|
return {
|
|
721
749
|
"sidechat.start": start,
|
|
@@ -796,6 +824,13 @@ function apply(ctx) {
|
|
|
796
824
|
sideChats.clear();
|
|
797
825
|
};
|
|
798
826
|
}, "dsh-side-chat: dispose side chats");
|
|
827
|
+
ctx.effect(() => {
|
|
828
|
+
(async () => {
|
|
829
|
+
for (const record of await readRecords()) try {
|
|
830
|
+
await ctx.workspaceRegistry.archiveSession(record.childId);
|
|
831
|
+
} catch {}
|
|
832
|
+
})();
|
|
833
|
+
}, "dsh-side-chat: re-archive recorded side chats");
|
|
799
834
|
ctx.effect(() => ctx.webServer.register({
|
|
800
835
|
kind: "prefix",
|
|
801
836
|
path: "/sidechat/api",
|
|
@@ -52,6 +52,7 @@ export declare const zh: {
|
|
|
52
52
|
'image.unsupported': string;
|
|
53
53
|
'image.tooMany': string;
|
|
54
54
|
'image.fileTooLarge': string;
|
|
55
|
+
'image.dimensionTooLarge': string;
|
|
55
56
|
settingsNav: string;
|
|
56
57
|
'settings.lookupTitle': string;
|
|
57
58
|
'settings.lookupDesc': string;
|
|
@@ -147,6 +148,7 @@ export declare const en: {
|
|
|
147
148
|
'image.unsupported': string;
|
|
148
149
|
'image.tooMany': string;
|
|
149
150
|
'image.fileTooLarge': string;
|
|
151
|
+
'image.dimensionTooLarge': string;
|
|
150
152
|
settingsNav: string;
|
|
151
153
|
'settings.lookupTitle': string;
|
|
152
154
|
'settings.lookupDesc': string;
|
|
@@ -38,17 +38,14 @@ export interface SideSessionEvent {
|
|
|
38
38
|
surfaceOp?: 'append' | 'replace' | 'remove';
|
|
39
39
|
sourceEventSeqs?: number[];
|
|
40
40
|
}
|
|
41
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* The live session face the host reads. Event reads go through
|
|
43
|
+
* `sessionQuery.readSession` instead: DSH 0.1.6 deprecated the Session's
|
|
44
|
+
* synchronous event getters (`events` before 0.1.5, `snapshotEvents` after).
|
|
45
|
+
*/
|
|
42
46
|
export interface SideSession {
|
|
43
47
|
id: string;
|
|
44
48
|
header: SideSessionHeader;
|
|
45
|
-
/**
|
|
46
|
-
* 0.1.2-era event getter. Removed in 0.1.5 (snapshotEvents takes over), so
|
|
47
|
-
* host code reads through `sessionEvents()` which prefers the new method.
|
|
48
|
-
*/
|
|
49
|
-
events?: readonly SideSessionEvent[];
|
|
50
|
-
/** 0.1.5+ snapshot of the full event log; absent on 0.1.2-era sessions. */
|
|
51
|
-
snapshotEvents?: (fromSeq?: number, toSeqExclusive?: number) => readonly SideSessionEvent[];
|
|
52
49
|
requestHeader?: () => {
|
|
53
50
|
config?: {
|
|
54
51
|
provider?: string;
|
|
@@ -198,12 +195,6 @@ export interface SideSessionLogSnapshot {
|
|
|
198
195
|
export interface SideSessionQuery {
|
|
199
196
|
readSession(sessionId: string): Promise<SideSessionLogSnapshot>;
|
|
200
197
|
}
|
|
201
|
-
/** Sandbox mode vocabulary (read from the session override). */
|
|
202
|
-
export type SideSandboxMode = 'read-only' | 'workspace-write' | 'danger-full-access';
|
|
203
|
-
/** The sandbox policy face. */
|
|
204
|
-
export interface SideSandboxPolicy {
|
|
205
|
-
overrideOf(session: SideSession): SideSandboxMode | undefined;
|
|
206
|
-
}
|
|
207
198
|
/** One permission preset option. */
|
|
208
199
|
export interface SidePresetOption {
|
|
209
200
|
value: string;
|
|
@@ -214,7 +205,14 @@ export interface SidePresetOption {
|
|
|
214
205
|
export interface SidePermissionPresets {
|
|
215
206
|
current(session: SideSession): string;
|
|
216
207
|
set(session: SideSession, name: string): void;
|
|
217
|
-
|
|
208
|
+
/** 0.1.6+: process-level catalog of selectable presets. */
|
|
209
|
+
catalog?(): {
|
|
210
|
+
options: SidePresetOption[];
|
|
211
|
+
};
|
|
212
|
+
/** 0.1.6+: the preset selected as the default for future sessions. */
|
|
213
|
+
defaultPreset?: string;
|
|
214
|
+
/** 0.1.5 and earlier: combined catalog plus current value. Removed in 0.1.6. */
|
|
215
|
+
selectFor?(state: unknown): {
|
|
218
216
|
options: SidePresetOption[];
|
|
219
217
|
currentValue: string;
|
|
220
218
|
};
|
|
@@ -350,6 +348,8 @@ export interface SideAttachmentStore {
|
|
|
350
348
|
maxImagesPerMessage: number;
|
|
351
349
|
maxMessageImageBytes: number;
|
|
352
350
|
maxImagePixels: number;
|
|
351
|
+
/** 0.1.6+ per-image intrinsic width/height ceiling; absent on earlier lines. */
|
|
352
|
+
maxImageDimension?: number;
|
|
353
353
|
mediaTypes: readonly string[];
|
|
354
354
|
};
|
|
355
355
|
validateImage(input: {
|
|
@@ -402,7 +402,6 @@ declare module 'cordis' {
|
|
|
402
402
|
agents: SideAgentsService;
|
|
403
403
|
workspaceRegistry: SideWorkspaceRegistry;
|
|
404
404
|
sessionQuery: SideSessionQuery;
|
|
405
|
-
sandboxPolicy: SideSandboxPolicy;
|
|
406
405
|
permissionPresets: SidePermissionPresets;
|
|
407
406
|
agentPresets: SideAgentPresets;
|
|
408
407
|
llm: SideLlm;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-side-chat-plus",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-alpha.1",
|
|
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",
|
|
@@ -67,47 +67,47 @@
|
|
|
67
67
|
"schemastery": "^3.18.0"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
|
-
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1",
|
|
71
|
-
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1",
|
|
72
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1",
|
|
73
|
-
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1",
|
|
74
|
-
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1",
|
|
75
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1",
|
|
76
|
-
"@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1",
|
|
77
|
-
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1",
|
|
78
|
-
"@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1",
|
|
79
|
-
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1",
|
|
80
|
-
"@deepseek-ai/dsh-session": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1",
|
|
81
|
-
"@deepseek-ai/dsh-session-query": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1",
|
|
82
|
-
"@deepseek-ai/dsh-workspace": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1",
|
|
83
|
-
"@deepseek-ai/dsh-permission-presets": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1",
|
|
84
|
-
"@deepseek-ai/dsh-sandbox-policy": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1",
|
|
85
|
-
"@deepseek-ai/dsh-agent-presets": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1",
|
|
86
|
-
"@deepseek-ai/dsh-settings": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1",
|
|
87
|
-
"@deepseek-ai/dsh-home-paths": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1",
|
|
70
|
+
"@deepseek-ai/dsh-agent": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1 || ^0.1.6-0",
|
|
71
|
+
"@deepseek-ai/dsh-attachment": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1 || ^0.1.6-0",
|
|
72
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1 || ^0.1.6-0",
|
|
73
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1 || ^0.1.6-0",
|
|
74
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1 || ^0.1.6-0",
|
|
75
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1 || ^0.1.6-0",
|
|
76
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1 || ^0.1.6-0",
|
|
77
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1 || ^0.1.6-0",
|
|
78
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1 || ^0.1.6-0",
|
|
79
|
+
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1 || ^0.1.6-0",
|
|
80
|
+
"@deepseek-ai/dsh-session": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1 || ^0.1.6-0",
|
|
81
|
+
"@deepseek-ai/dsh-session-query": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1 || ^0.1.6-0",
|
|
82
|
+
"@deepseek-ai/dsh-workspace": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1 || ^0.1.6-0",
|
|
83
|
+
"@deepseek-ai/dsh-permission-presets": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1 || ^0.1.6-0",
|
|
84
|
+
"@deepseek-ai/dsh-sandbox-policy": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1 || ^0.1.6-0",
|
|
85
|
+
"@deepseek-ai/dsh-agent-presets": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1 || ^0.1.6-0",
|
|
86
|
+
"@deepseek-ai/dsh-settings": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1 || ^0.1.6-0",
|
|
87
|
+
"@deepseek-ai/dsh-home-paths": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.5-alpha.1 || ^0.1.6-0",
|
|
88
88
|
"cordis": "^4.0.0-rc.7",
|
|
89
89
|
"react": "^18.2.0",
|
|
90
90
|
"react-dom": "^18.2.0"
|
|
91
91
|
},
|
|
92
92
|
"devDependencies": {
|
|
93
|
-
"@deepseek-ai/dsh-agent": "^0.1.5-alpha.2",
|
|
94
|
-
"@deepseek-ai/dsh-attachment": "^0.1.5-alpha.2",
|
|
95
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.5-alpha.2",
|
|
96
|
-
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-alpha.2",
|
|
97
|
-
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-alpha.2",
|
|
98
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.5-alpha.2",
|
|
99
|
-
"@deepseek-ai/dsh-client-ui-settings": "^0.1.5-alpha.2",
|
|
100
|
-
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-alpha.2",
|
|
101
|
-
"@deepseek-ai/dsh-host-webserver": "^0.1.5-alpha.2",
|
|
102
|
-
"@deepseek-ai/dsh-llm": "^0.1.5-alpha.2",
|
|
103
|
-
"@deepseek-ai/dsh-session": "^0.1.5-alpha.2",
|
|
104
|
-
"@deepseek-ai/dsh-session-query": "^0.1.5-alpha.2",
|
|
105
|
-
"@deepseek-ai/dsh-workspace": "^0.1.5-alpha.2",
|
|
106
|
-
"@deepseek-ai/dsh-permission-presets": "^0.1.5-alpha.2",
|
|
107
|
-
"@deepseek-ai/dsh-sandbox-policy": "^0.1.5-alpha.2",
|
|
108
|
-
"@deepseek-ai/dsh-agent-presets": "^0.1.5-alpha.2",
|
|
109
|
-
"@deepseek-ai/dsh-settings": "^0.1.5-alpha.2",
|
|
110
|
-
"@deepseek-ai/dsh-home-paths": "^0.1.5-alpha.2",
|
|
93
|
+
"@deepseek-ai/dsh-agent": "^0.1.5-alpha.2 || ^0.1.6-0",
|
|
94
|
+
"@deepseek-ai/dsh-attachment": "^0.1.5-alpha.2 || ^0.1.6-0",
|
|
95
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.5-alpha.2 || ^0.1.6-0",
|
|
96
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-alpha.2 || ^0.1.6-0",
|
|
97
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-alpha.2 || ^0.1.6-0",
|
|
98
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.5-alpha.2 || ^0.1.6-0",
|
|
99
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.5-alpha.2 || ^0.1.6-0",
|
|
100
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-alpha.2 || ^0.1.6-0",
|
|
101
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.5-alpha.2 || ^0.1.6-0",
|
|
102
|
+
"@deepseek-ai/dsh-llm": "^0.1.5-alpha.2 || ^0.1.6-0",
|
|
103
|
+
"@deepseek-ai/dsh-session": "^0.1.5-alpha.2 || ^0.1.6-0",
|
|
104
|
+
"@deepseek-ai/dsh-session-query": "^0.1.5-alpha.2 || ^0.1.6-0",
|
|
105
|
+
"@deepseek-ai/dsh-workspace": "^0.1.5-alpha.2 || ^0.1.6-0",
|
|
106
|
+
"@deepseek-ai/dsh-permission-presets": "^0.1.5-alpha.2 || ^0.1.6-0",
|
|
107
|
+
"@deepseek-ai/dsh-sandbox-policy": "^0.1.5-alpha.2 || ^0.1.6-0",
|
|
108
|
+
"@deepseek-ai/dsh-agent-presets": "^0.1.5-alpha.2 || ^0.1.6-0",
|
|
109
|
+
"@deepseek-ai/dsh-settings": "^0.1.5-alpha.2 || ^0.1.6-0",
|
|
110
|
+
"@deepseek-ai/dsh-home-paths": "^0.1.5-alpha.2 || ^0.1.6-0",
|
|
111
111
|
"@types/node": "^24.0.0",
|
|
112
112
|
"@types/react": "~18.3.1",
|
|
113
113
|
"@types/react-dom": "~18.3.1",
|
|
@@ -120,7 +120,8 @@
|
|
|
120
120
|
"vitest": "^4.1.8"
|
|
121
121
|
},
|
|
122
122
|
"scripts": {
|
|
123
|
-
"build": "node -e \"require('node:fs').rmSync('lib',{recursive:true,force:true})\" && tsc -p tsconfig.build.json && tsdown",
|
|
123
|
+
"build": "node -e \"require('node:fs').rmSync('lib',{recursive:true,force:true})\" && tsc -p tsconfig.build.json && tsdown && node scripts/check-seed-exports.mjs",
|
|
124
|
+
"check:seed": "node scripts/check-seed-exports.mjs",
|
|
124
125
|
"typecheck": "tsc --noEmit",
|
|
125
126
|
"bundle": "tsdown",
|
|
126
127
|
"watch": "tsdown --watch",
|
package/src/client/api.ts
CHANGED
|
@@ -93,7 +93,7 @@ export const api = {
|
|
|
93
93
|
call<{ accepted: true }>('sidechat.inject', args),
|
|
94
94
|
directory: () => call<SidechatDirectory>('sidechat.directory', {}),
|
|
95
95
|
permissions: () => call<SidechatPermissions>('sidechat.permissions', {}),
|
|
96
|
-
limits: () => call<{ mediaTypes: string[]; maxImageBytes: number; maxImagesPerMessage: number; maxMessageImageBytes: number; maxImagePixels: number }>('sidechat.limits', {}),
|
|
96
|
+
limits: () => call<{ mediaTypes: string[]; maxImageBytes: number; maxImagesPerMessage: number; maxMessageImageBytes: number; maxImagePixels: number; maxImageDimension?: number }>('sidechat.limits', {}),
|
|
97
97
|
attachment: (args: { childId: string; attachmentId: string }) =>
|
|
98
98
|
call<{ mediaType: string; data: string }>('sidechat.attachment', args),
|
|
99
99
|
inherit: (args: { parentSessionId: string }) =>
|
package/src/client/index.tsx
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
IconChevronDownOutline14,
|
|
15
15
|
IconChevronRightOutline14,
|
|
16
16
|
IconPanelLeftOutline16,
|
|
17
|
-
|
|
17
|
+
IconSendOutline14,
|
|
18
18
|
IconStopFill16,
|
|
19
19
|
IconThinkOutline14,
|
|
20
20
|
MarkdownText,
|
|
@@ -264,6 +264,22 @@ function createDraftImages(files: readonly File[]): ComposerAttachment[] {
|
|
|
264
264
|
})
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
+
/**
|
|
268
|
+
* Read one image file's intrinsic pixel size, releasing the decoded bitmap.
|
|
269
|
+
* Returns null when the browser cannot decode it: the host's `validateImage`
|
|
270
|
+
* stays the authority either way.
|
|
271
|
+
*/
|
|
272
|
+
async function imageSize(file: File): Promise<{ width: number; height: number } | null> {
|
|
273
|
+
try {
|
|
274
|
+
const bitmap = await createImageBitmap(file)
|
|
275
|
+
const size = { width: bitmap.width, height: bitmap.height }
|
|
276
|
+
bitmap.close()
|
|
277
|
+
return size
|
|
278
|
+
} catch {
|
|
279
|
+
return null
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
267
283
|
/** Revoke one draft image's preview URL. */
|
|
268
284
|
function releaseDraftImage(attachment: ComposerAttachment): void {
|
|
269
285
|
URL.revokeObjectURL(attachment.previewUrl)
|
|
@@ -952,7 +968,7 @@ function SidechatPanel(props: {
|
|
|
952
968
|
const [now, setNow] = useState(() => Date.now())
|
|
953
969
|
const [dragActive, setDragActive] = useState(false)
|
|
954
970
|
const [lightbox, setLightbox] = useState<ComposerAttachment | null>(null)
|
|
955
|
-
const [limits, setLimits] = useState<{ mediaTypes: string[]; maxImageBytes: number; maxImagesPerMessage: number; maxMessageImageBytes: number } | null>(null)
|
|
971
|
+
const [limits, setLimits] = useState<{ mediaTypes: string[]; maxImageBytes: number; maxImagesPerMessage: number; maxMessageImageBytes: number; maxImagePixels: number; maxImageDimension?: number } | null>(null)
|
|
956
972
|
/** Index of the assistant message whose "summarize then insert" is in flight. */
|
|
957
973
|
const [summarizingIndex, setSummarizingIndex] = useState<number | null>(null)
|
|
958
974
|
/** Which question-dialog item is being brought into the side chat ('all' or an option label). */
|
|
@@ -1214,26 +1230,41 @@ function SidechatPanel(props: {
|
|
|
1214
1230
|
props.store.patch({ error: props.t('image.unsupported') })
|
|
1215
1231
|
return
|
|
1216
1232
|
}
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
if (
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1233
|
+
void (async () => {
|
|
1234
|
+
try {
|
|
1235
|
+
if (limits !== null) {
|
|
1236
|
+
if (images.some((f) => !limits.mediaTypes.includes(f.type))) {
|
|
1237
|
+
props.store.patch({ error: props.t('image.unsupported') })
|
|
1238
|
+
return
|
|
1239
|
+
}
|
|
1240
|
+
if (panel.attachments.length + images.length > limits.maxImagesPerMessage) {
|
|
1241
|
+
props.store.patch({ error: props.t('image.tooMany') })
|
|
1242
|
+
return
|
|
1243
|
+
}
|
|
1244
|
+
if (images.some((f) => f.size > limits.maxImageBytes)) {
|
|
1245
|
+
props.store.patch({ error: props.t('image.fileTooLarge') })
|
|
1246
|
+
return
|
|
1247
|
+
}
|
|
1248
|
+
// DSH 0.1.6 added a per-image intrinsic width/height ceiling; older
|
|
1249
|
+
// lines omit it. Decoding is async, so this check is awaited rather
|
|
1250
|
+
// than folded into the size-type ones above.
|
|
1251
|
+
const maxDimension = limits.maxImageDimension
|
|
1252
|
+
if (typeof maxDimension === 'number' && maxDimension > 0) {
|
|
1253
|
+
for (const file of images) {
|
|
1254
|
+
const size = await imageSize(file)
|
|
1255
|
+
if (size !== null && (size.width > maxDimension || size.height > maxDimension)) {
|
|
1256
|
+
props.store.patch({ error: props.t('image.dimensionTooLarge') })
|
|
1257
|
+
return
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1230
1261
|
}
|
|
1262
|
+
const created = createDraftImages(images)
|
|
1263
|
+
props.store.patch({ attachments: [...panel.attachments, ...created], error: null })
|
|
1264
|
+
} catch (error) {
|
|
1265
|
+
props.store.patch({ error: error instanceof Error ? error.message : String(error) })
|
|
1231
1266
|
}
|
|
1232
|
-
|
|
1233
|
-
props.store.patch({ attachments: [...panel.attachments, ...created], error: null })
|
|
1234
|
-
} catch (error) {
|
|
1235
|
-
props.store.patch({ error: error instanceof Error ? error.message : String(error) })
|
|
1236
|
-
}
|
|
1267
|
+
})()
|
|
1237
1268
|
}, [limits, panel.attachments, props.store, props.t])
|
|
1238
1269
|
|
|
1239
1270
|
// Load the deployment image policy once (fast-path checks mirror the host).
|
|
@@ -1603,7 +1634,7 @@ function SidechatPanel(props: {
|
|
|
1603
1634
|
aria-label={activeRunning ? props.t('panel.stop') : props.t('panel.send')}
|
|
1604
1635
|
onClick={activeRunning ? stop : send}
|
|
1605
1636
|
>
|
|
1606
|
-
{activeRunning ? <IconStopFill16 size={16} /> : <
|
|
1637
|
+
{activeRunning ? <IconStopFill16 size={16} /> : <IconSendOutline14 size={16} />}
|
|
1607
1638
|
</button>
|
|
1608
1639
|
</Tooltip>
|
|
1609
1640
|
</div>
|
package/src/client/locales.ts
CHANGED
|
@@ -53,6 +53,7 @@ export const zh = {
|
|
|
53
53
|
'image.unsupported': '不支持的图片格式',
|
|
54
54
|
'image.tooMany': '图片数量超出限制',
|
|
55
55
|
'image.fileTooLarge': '图片过大',
|
|
56
|
+
'image.dimensionTooLarge': '图片尺寸超出限制',
|
|
56
57
|
'settingsNav': '侧边聊天',
|
|
57
58
|
'settings.lookupTitle': '默认勾选「需要时从工作区 / 主会话查信息」',
|
|
58
59
|
'settings.lookupDesc': '新会话的侧边聊天默认打开此开关;可在每次聊天中临时更改',
|
|
@@ -149,6 +150,7 @@ export const en = {
|
|
|
149
150
|
'image.unsupported': 'Unsupported image type',
|
|
150
151
|
'image.tooMany': 'Too many images',
|
|
151
152
|
'image.fileTooLarge': 'Image too large',
|
|
153
|
+
'image.dimensionTooLarge': 'Image dimensions exceed the limit',
|
|
152
154
|
'settingsNav': 'Side chat',
|
|
153
155
|
'settings.lookupTitle': 'Default on: "look up workspace / parent when needed"',
|
|
154
156
|
'settings.lookupDesc': 'New conversations start with this switch on; you can still change it per chat',
|
package/src/context-types.ts
CHANGED
|
@@ -43,17 +43,14 @@ export interface SideSessionEvent {
|
|
|
43
43
|
sourceEventSeqs?: number[]
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* The live session face the host reads. Event reads go through
|
|
48
|
+
* `sessionQuery.readSession` instead: DSH 0.1.6 deprecated the Session's
|
|
49
|
+
* synchronous event getters (`events` before 0.1.5, `snapshotEvents` after).
|
|
50
|
+
*/
|
|
47
51
|
export interface SideSession {
|
|
48
52
|
id: string
|
|
49
53
|
header: SideSessionHeader
|
|
50
|
-
/**
|
|
51
|
-
* 0.1.2-era event getter. Removed in 0.1.5 (snapshotEvents takes over), so
|
|
52
|
-
* host code reads through `sessionEvents()` which prefers the new method.
|
|
53
|
-
*/
|
|
54
|
-
events?: readonly SideSessionEvent[]
|
|
55
|
-
/** 0.1.5+ snapshot of the full event log; absent on 0.1.2-era sessions. */
|
|
56
|
-
snapshotEvents?: (fromSeq?: number, toSeqExclusive?: number) => readonly SideSessionEvent[]
|
|
57
54
|
requestHeader?: () => { config?: { provider?: string; model?: string; reasoningEffort?: string; maxTokens?: number } } | undefined
|
|
58
55
|
}
|
|
59
56
|
|
|
@@ -212,14 +209,6 @@ export interface SideSessionQuery {
|
|
|
212
209
|
readSession(sessionId: string): Promise<SideSessionLogSnapshot>
|
|
213
210
|
}
|
|
214
211
|
|
|
215
|
-
/** Sandbox mode vocabulary (read from the session override). */
|
|
216
|
-
export type SideSandboxMode = 'read-only' | 'workspace-write' | 'danger-full-access'
|
|
217
|
-
|
|
218
|
-
/** The sandbox policy face. */
|
|
219
|
-
export interface SideSandboxPolicy {
|
|
220
|
-
overrideOf(session: SideSession): SideSandboxMode | undefined
|
|
221
|
-
}
|
|
222
|
-
|
|
223
212
|
/** One permission preset option. */
|
|
224
213
|
export interface SidePresetOption {
|
|
225
214
|
value: string
|
|
@@ -231,7 +220,12 @@ export interface SidePresetOption {
|
|
|
231
220
|
export interface SidePermissionPresets {
|
|
232
221
|
current(session: SideSession): string
|
|
233
222
|
set(session: SideSession, name: string): void
|
|
234
|
-
|
|
223
|
+
/** 0.1.6+: process-level catalog of selectable presets. */
|
|
224
|
+
catalog?(): { options: SidePresetOption[] }
|
|
225
|
+
/** 0.1.6+: the preset selected as the default for future sessions. */
|
|
226
|
+
defaultPreset?: string
|
|
227
|
+
/** 0.1.5 and earlier: combined catalog plus current value. Removed in 0.1.6. */
|
|
228
|
+
selectFor?(state: unknown): { options: SidePresetOption[]; currentValue: string }
|
|
235
229
|
}
|
|
236
230
|
|
|
237
231
|
/** The agent presets face (composeFrom joins a child to the parent's composition). */
|
|
@@ -356,6 +350,8 @@ export interface SideAttachmentStore {
|
|
|
356
350
|
maxImagesPerMessage: number
|
|
357
351
|
maxMessageImageBytes: number
|
|
358
352
|
maxImagePixels: number
|
|
353
|
+
/** 0.1.6+ per-image intrinsic width/height ceiling; absent on earlier lines. */
|
|
354
|
+
maxImageDimension?: number
|
|
359
355
|
mediaTypes: readonly string[]
|
|
360
356
|
}
|
|
361
357
|
validateImage(input: { data: Uint8Array; mediaType: string; name?: string }): Promise<void>
|
|
@@ -394,7 +390,6 @@ declare module 'cordis' {
|
|
|
394
390
|
agents: SideAgentsService
|
|
395
391
|
workspaceRegistry: SideWorkspaceRegistry
|
|
396
392
|
sessionQuery: SideSessionQuery
|
|
397
|
-
sandboxPolicy: SideSandboxPolicy
|
|
398
393
|
permissionPresets: SidePermissionPresets
|
|
399
394
|
agentPresets: SideAgentPresets
|
|
400
395
|
llm: SideLlm
|