dsh-plugin-bridge 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 +10 -4
- package/README.zh.md +10 -4
- package/docs/guide.zh.md +4 -4
- package/docs/native-webui-feasibility.md +5 -3
- package/lib/client-contract.d.ts +6 -2
- package/lib/client-contract.js +34 -10
- package/lib/client.d.ts +4 -3
- package/lib/client.js +75 -11
- package/lib/client.js.map +1 -1
- package/lib/command.js +2 -2
- package/lib/dsh-alpha-host.d.ts +67 -0
- package/lib/dsh-alpha-host.js +276 -0
- package/lib/index.d.ts +2 -3
- package/lib/index.js +6 -8
- package/lib/migrate.d.ts +2 -0
- package/lib/migrate.js +11 -1
- package/package.json +20 -15
- package/reports/dsh-0.1.2-alpha.2-compat-2026-08-31.md +64 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSH v0.1.2 alpha typed-controller adapter.
|
|
3
|
+
*
|
|
4
|
+
* The alpha removed the legacy ApiProxy service. Keep that product change at
|
|
5
|
+
* this boundary: migration code continues to consume BridgeHost, while this
|
|
6
|
+
* adapter talks to the Host controllers structurally so the package can still
|
|
7
|
+
* be built against the current rc.2 SDK.
|
|
8
|
+
*/
|
|
9
|
+
import { randomUUID } from 'node:crypto';
|
|
10
|
+
import { createApiProxyHost, probeApiProxy } from './api-rpc.js';
|
|
11
|
+
import { REQUIRED_BRIDGE_CAPABILITIES, } from './host.js';
|
|
12
|
+
import { RpcError } from './rpc.js';
|
|
13
|
+
const ALPHA_METHODS = {
|
|
14
|
+
'session.list': services => services.sessionController?.list,
|
|
15
|
+
'session.create': services => services.sessionController?.create,
|
|
16
|
+
'session.history': services => services.sessionController?.inspect,
|
|
17
|
+
'session.models': services => services.sessionController?.modelCatalog,
|
|
18
|
+
'session.selectModel': services => services.sessionController?.selectModel,
|
|
19
|
+
'session.prompt': services => services.sessionController?.prompt,
|
|
20
|
+
'session.cancel': services => services.sessionController?.cancel,
|
|
21
|
+
'session.rename': services => services.sessionController?.rename,
|
|
22
|
+
'workspace.list': services => services.workspaceRegistry?.list,
|
|
23
|
+
'workspace.archiveSession': services => services.workspaceController?.archiveSession,
|
|
24
|
+
'agentPreset.list': services => services.agentPresets?.remoteExportList,
|
|
25
|
+
'goal.create': services => services.goals?.remoteExportCreate,
|
|
26
|
+
'goal.pause': services => services.goals?.pause,
|
|
27
|
+
};
|
|
28
|
+
function service(ctx, name) {
|
|
29
|
+
try {
|
|
30
|
+
const getter = ctx.get;
|
|
31
|
+
if (typeof getter === 'function') {
|
|
32
|
+
const found = getter.call(ctx, name);
|
|
33
|
+
if (found !== undefined)
|
|
34
|
+
return found;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Cordis returns undefined for an unavailable optional service; custom
|
|
39
|
+
// Context implementations may throw instead, so fall through to fixtures.
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
return ctx[name];
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function servicesOf(ctx) {
|
|
49
|
+
return {
|
|
50
|
+
apiProxy: service(ctx, 'apiProxy'),
|
|
51
|
+
sessionController: service(ctx, 'sessionController'),
|
|
52
|
+
workspaceRegistry: service(ctx, 'workspaceRegistry'),
|
|
53
|
+
workspaceController: service(ctx, 'workspaceController'),
|
|
54
|
+
agentPresets: service(ctx, 'agentPresets'),
|
|
55
|
+
goals: service(ctx, 'goals'),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function failureOf(error) {
|
|
59
|
+
const value = error;
|
|
60
|
+
return value?.failure ?? value?.rpc ?? {
|
|
61
|
+
code: value?.code,
|
|
62
|
+
message: value?.message,
|
|
63
|
+
details: value?.details,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
async function invoke(method, operation) {
|
|
67
|
+
try {
|
|
68
|
+
return await operation();
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (error instanceof RpcError)
|
|
72
|
+
throw error;
|
|
73
|
+
const failure = failureOf(error);
|
|
74
|
+
throw new RpcError(method, failure.code ?? 'internal', failure.message ?? (error instanceof Error ? error.message : String(error)), failure.details);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function required(method, value) {
|
|
78
|
+
if (value === undefined)
|
|
79
|
+
throw new RpcError(method, 'unavailable', `DSH alpha host 没有提供 ${method}`);
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
function projectionValues(row) {
|
|
83
|
+
const values = row?.projections?.values;
|
|
84
|
+
return values !== null && typeof values === 'object' ? values : {};
|
|
85
|
+
}
|
|
86
|
+
function sessionRow(row) {
|
|
87
|
+
const source = row;
|
|
88
|
+
const values = projectionValues(row);
|
|
89
|
+
const preset = typeof values.agentPreset === 'string'
|
|
90
|
+
? values.agentPreset
|
|
91
|
+
: typeof source.agentPreset === 'string' ? source.agentPreset : undefined;
|
|
92
|
+
return {
|
|
93
|
+
sessionId: String(source.sessionId ?? ''),
|
|
94
|
+
...(typeof source.running === 'boolean' ? { running: source.running } : {}),
|
|
95
|
+
...(typeof source.blank === 'boolean' ? { blank: source.blank } : {}),
|
|
96
|
+
...(typeof source.cwd === 'string' ? { cwd: source.cwd } : {}),
|
|
97
|
+
...(preset === undefined ? {} : { agentPreset: preset }),
|
|
98
|
+
...(typeof source.parentSessionId === 'string' ? { parentSessionId: source.parentSessionId } : {}),
|
|
99
|
+
...Object.keys(values).length === 0 ? {} : { projections: { values } },
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function currentModel(row) {
|
|
103
|
+
const selection = projectionValues(row).modelSelection;
|
|
104
|
+
if (selection === null || typeof selection !== 'object')
|
|
105
|
+
return undefined;
|
|
106
|
+
const record = selection;
|
|
107
|
+
const candidate = record.next ?? record.lastUsed;
|
|
108
|
+
if (candidate === null || typeof candidate !== 'object')
|
|
109
|
+
return undefined;
|
|
110
|
+
const model = candidate;
|
|
111
|
+
if (typeof model.provider !== 'string' || typeof model.model !== 'string')
|
|
112
|
+
return undefined;
|
|
113
|
+
return {
|
|
114
|
+
provider: model.provider,
|
|
115
|
+
model: model.model,
|
|
116
|
+
...(typeof model.reasoningEffort === 'string' ? { reasoningEffort: model.reasoningEffort } : {}),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function paginateHistory(events, beforeSeq, maxMessages) {
|
|
120
|
+
const visible = beforeSeq === undefined ? [...events] : events.filter(event => (event.seq ?? -1) < beforeSeq);
|
|
121
|
+
let count = 0;
|
|
122
|
+
let cut = 0;
|
|
123
|
+
for (let index = visible.length - 1; index >= 0; index -= 1) {
|
|
124
|
+
const type = visible[index]?.type;
|
|
125
|
+
if (type !== 'user/message' && type !== 'assistant/message')
|
|
126
|
+
continue;
|
|
127
|
+
count += 1;
|
|
128
|
+
if (count >= maxMessages) {
|
|
129
|
+
cut = index;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
events: visible.slice(cut).map(event => ({ event })),
|
|
135
|
+
hasMore: cut > 0,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
async function resolvedAgent(services, sessionId) {
|
|
139
|
+
const resolve = required('session.resolveAgent', services.sessionController?.resolveAgent);
|
|
140
|
+
const result = await invoke('session.resolveAgent', () => resolve.call(services.sessionController, sessionId));
|
|
141
|
+
if (result.error !== undefined) {
|
|
142
|
+
throw new RpcError('session.resolveAgent', result.error.code ?? 'internal', result.error.message ?? 'DSH alpha 无法恢复目标会话', result.error.details);
|
|
143
|
+
}
|
|
144
|
+
return required('session.resolveAgent', result.agent);
|
|
145
|
+
}
|
|
146
|
+
/** Probe the real alpha services instead of the adapter's always-present closures. */
|
|
147
|
+
export function probeDshAlphaHost(input) {
|
|
148
|
+
const services = servicesOf(input);
|
|
149
|
+
return REQUIRED_BRIDGE_CAPABILITIES.map(method => ({
|
|
150
|
+
method,
|
|
151
|
+
available: typeof ALPHA_METHODS[method]?.(services) === 'function',
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
/** Create the semantic BridgeHost over DSH v0.1.2 typed Host controllers. */
|
|
155
|
+
export function createDshAlphaHost(input, signal) {
|
|
156
|
+
const services = servicesOf(input);
|
|
157
|
+
const controller = services.sessionController;
|
|
158
|
+
const abort = signal ?? new AbortController().signal;
|
|
159
|
+
const host = {
|
|
160
|
+
descriptor: Object.freeze({ id: 'dsh-typed-controllers', version: '0.1.2-alpha', transport: 'in-process' }),
|
|
161
|
+
sessions: Object.freeze({
|
|
162
|
+
list: async (request = {}) => {
|
|
163
|
+
const list = required('session.list', controller?.list);
|
|
164
|
+
const value = await invoke('session.list', () => list.call(controller, request, abort));
|
|
165
|
+
return { items: (value.items ?? []).map(sessionRow) };
|
|
166
|
+
},
|
|
167
|
+
create: async (request) => {
|
|
168
|
+
const create = required('session.create', controller?.create);
|
|
169
|
+
return await invoke('session.create', () => create.call(controller, request));
|
|
170
|
+
},
|
|
171
|
+
history: async (request) => {
|
|
172
|
+
const inspect = required('session.history', controller?.inspect);
|
|
173
|
+
const value = await invoke('session.history', () => inspect.call(controller, request.sessionId, abort));
|
|
174
|
+
return paginateHistory(value.events, request.beforeSeq, request.maxMessages ?? 60);
|
|
175
|
+
},
|
|
176
|
+
models: async ({ sessionId }) => {
|
|
177
|
+
const list = required('session.list', controller?.list);
|
|
178
|
+
const catalog = required('session.models', controller?.modelCatalog);
|
|
179
|
+
const [listed, available] = await Promise.all([
|
|
180
|
+
invoke('session.list', () => list.call(controller, {}, abort)),
|
|
181
|
+
invoke('session.models', () => catalog.call(controller)),
|
|
182
|
+
]);
|
|
183
|
+
const row = (listed.items ?? []).find(item => item.sessionId === sessionId);
|
|
184
|
+
return {
|
|
185
|
+
...available,
|
|
186
|
+
current: currentModel(row) ?? available.default,
|
|
187
|
+
};
|
|
188
|
+
},
|
|
189
|
+
selectModel: request => {
|
|
190
|
+
const select = required('session.selectModel', controller?.selectModel);
|
|
191
|
+
return invoke('session.selectModel', () => select.call(controller, { ...request }));
|
|
192
|
+
},
|
|
193
|
+
prompt: request => {
|
|
194
|
+
const prompt = required('session.prompt', controller?.prompt);
|
|
195
|
+
return invoke('session.prompt', () => prompt.call(controller, {
|
|
196
|
+
...request,
|
|
197
|
+
requestId: `bridge-${randomUUID()}`,
|
|
198
|
+
}, abort));
|
|
199
|
+
},
|
|
200
|
+
cancel: request => {
|
|
201
|
+
const cancel = required('session.cancel', controller?.cancel);
|
|
202
|
+
return invoke('session.cancel', () => cancel.call(controller, request));
|
|
203
|
+
},
|
|
204
|
+
rename: request => {
|
|
205
|
+
const rename = required('session.rename', controller?.rename);
|
|
206
|
+
return invoke('session.rename', () => rename.call(controller, request));
|
|
207
|
+
},
|
|
208
|
+
attachment: request => {
|
|
209
|
+
const attachment = required('session.attachment', controller?.attachment);
|
|
210
|
+
return invoke('session.attachment', () => attachment.call(controller, request));
|
|
211
|
+
},
|
|
212
|
+
}),
|
|
213
|
+
workspaces: Object.freeze({
|
|
214
|
+
list: async () => {
|
|
215
|
+
const list = required('workspace.list', services.workspaceRegistry?.list);
|
|
216
|
+
const rows = await invoke('workspace.list', () => list.call(services.workspaceRegistry));
|
|
217
|
+
return {
|
|
218
|
+
items: rows.map((item) => {
|
|
219
|
+
const row = item;
|
|
220
|
+
return {
|
|
221
|
+
workspaceId: String(row.id ?? row.workspaceId ?? ''),
|
|
222
|
+
sessionIds: Array.isArray(row.sessionIds) ? row.sessionIds.map(String) : [],
|
|
223
|
+
};
|
|
224
|
+
}),
|
|
225
|
+
};
|
|
226
|
+
},
|
|
227
|
+
archiveSession: request => {
|
|
228
|
+
const archive = required('workspace.archiveSession', services.workspaceController?.archiveSession);
|
|
229
|
+
return invoke('workspace.archiveSession', () => archive.call(services.workspaceController, request));
|
|
230
|
+
},
|
|
231
|
+
}),
|
|
232
|
+
presets: Object.freeze({
|
|
233
|
+
list: async () => {
|
|
234
|
+
const list = required('agentPreset.list', services.agentPresets?.remoteExportList);
|
|
235
|
+
return await invoke('agentPreset.list', () => list.call(services.agentPresets));
|
|
236
|
+
},
|
|
237
|
+
}),
|
|
238
|
+
goals: Object.freeze({
|
|
239
|
+
create: async (request) => {
|
|
240
|
+
const agent = await resolvedAgent(services, request.sessionId);
|
|
241
|
+
const create = required('goal.create', services.goals?.remoteExportCreate);
|
|
242
|
+
return await invoke('goal.create', () => create.call(services.goals, agent, {
|
|
243
|
+
objective: request.objective,
|
|
244
|
+
maxGoalRounds: request.maxGoalRounds,
|
|
245
|
+
}));
|
|
246
|
+
},
|
|
247
|
+
pause: async (request) => {
|
|
248
|
+
const agent = await resolvedAgent(services, request.sessionId);
|
|
249
|
+
const pause = required('goal.pause', services.goals?.pause);
|
|
250
|
+
return await invoke('goal.pause', () => pause.call(services.goals, agent, request.ref));
|
|
251
|
+
},
|
|
252
|
+
clear: async (request) => {
|
|
253
|
+
const agent = await resolvedAgent(services, request.sessionId);
|
|
254
|
+
const clear = required('goal.clear', services.goals?.clear);
|
|
255
|
+
return await invoke('goal.clear', () => clear.call(services.goals, agent, request.ref));
|
|
256
|
+
},
|
|
257
|
+
}),
|
|
258
|
+
};
|
|
259
|
+
return Object.freeze(host);
|
|
260
|
+
}
|
|
261
|
+
/** Prefer the stable rc.2 path when present; otherwise use alpha controllers. */
|
|
262
|
+
export function resolveDshHost(input, signal) {
|
|
263
|
+
const services = servicesOf(input);
|
|
264
|
+
if (services.apiProxy !== undefined)
|
|
265
|
+
return createApiProxyHost(services.apiProxy, signal);
|
|
266
|
+
if (services.sessionController !== undefined)
|
|
267
|
+
return createDshAlphaHost(services, signal);
|
|
268
|
+
throw new RpcError('bridge.host', 'unavailable', '当前 DSH 没有可用的 apiProxy 或 typed sessionController');
|
|
269
|
+
}
|
|
270
|
+
/** Probe the active generation using its native surface. */
|
|
271
|
+
export function probeDshHost(input) {
|
|
272
|
+
const services = servicesOf(input);
|
|
273
|
+
if (services.apiProxy !== undefined)
|
|
274
|
+
return probeApiProxy(services.apiProxy);
|
|
275
|
+
return probeDshAlphaHost(services);
|
|
276
|
+
}
|
package/lib/index.d.ts
CHANGED
|
@@ -2,9 +2,8 @@ import type { Context } from '@deepseek-ai/cordis';
|
|
|
2
2
|
import Schema from '@deepseek-ai/schemastery';
|
|
3
3
|
export declare const name = "dsh-plugin-bridge";
|
|
4
4
|
/**
|
|
5
|
-
* `commands`
|
|
6
|
-
*
|
|
7
|
-
* 两者在官方 `web` profile 里都在(base 挂 commands,web-app 挂 api-gateway)。
|
|
5
|
+
* `commands` 是跨版本入口,唯一硬依赖。执行时优先使用 rc.2 的 `apiProxy`,
|
|
6
|
+
* alpha 则改走 typed controllers;doctor 探测实际选中的那一面并 fail closed。
|
|
8
7
|
*/
|
|
9
8
|
export declare const inject: string[];
|
|
10
9
|
export interface Config {
|
package/lib/index.js
CHANGED
|
@@ -13,16 +13,15 @@ import { mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
|
|
|
13
13
|
import { tmpdir } from 'node:os';
|
|
14
14
|
import { join } from 'node:path';
|
|
15
15
|
import Schema from '@deepseek-ai/schemastery';
|
|
16
|
-
import { createApiProxyHost, probeApiProxy } from './api-rpc.js';
|
|
17
16
|
import { createBridgeCommand } from './command.js';
|
|
18
17
|
import { SOURCE_CHAR_BUDGET, SUMMARY_CHAR_BUDGET } from './compression.js';
|
|
18
|
+
import { probeDshHost, resolveDshHost } from './dsh-alpha-host.js';
|
|
19
19
|
export const name = 'dsh-plugin-bridge';
|
|
20
20
|
/**
|
|
21
|
-
* `commands`
|
|
22
|
-
*
|
|
23
|
-
* 两者在官方 `web` profile 里都在(base 挂 commands,web-app 挂 api-gateway)。
|
|
21
|
+
* `commands` 是跨版本入口,唯一硬依赖。执行时优先使用 rc.2 的 `apiProxy`,
|
|
22
|
+
* alpha 则改走 typed controllers;doctor 探测实际选中的那一面并 fail closed。
|
|
24
23
|
*/
|
|
25
|
-
export const inject = ['commands'
|
|
24
|
+
export const inject = ['commands'];
|
|
26
25
|
/** 命令是同步返回的,等压缩工人不能等太久。 */
|
|
27
26
|
const DEFAULT_PREVIEW_TIMEOUT_MS = 180_000;
|
|
28
27
|
export const Config = Schema.object({
|
|
@@ -63,10 +62,9 @@ export function commandConfigOf(config = {}) {
|
|
|
63
62
|
};
|
|
64
63
|
}
|
|
65
64
|
export function apply(ctx, config = {}) {
|
|
66
|
-
const apiProxyOf = () => ctx.apiProxy;
|
|
67
65
|
const command = createBridgeCommand({
|
|
68
|
-
hostFor: (signal) =>
|
|
69
|
-
probe: () =>
|
|
66
|
+
hostFor: (signal) => resolveDshHost(ctx, signal),
|
|
67
|
+
probe: () => probeDshHost(ctx),
|
|
70
68
|
config: commandConfigOf(config),
|
|
71
69
|
writeSummary: writeSummaryFile,
|
|
72
70
|
readSummary: (path) => readFileSync(path, 'utf8'),
|
package/lib/migrate.d.ts
CHANGED
|
@@ -31,6 +31,8 @@ export declare function findSession(host: BridgeHostInput, sessionId: string): P
|
|
|
31
31
|
export declare function findWorkspaceId(input: BridgeHostInput, sessionId: string): Promise<string | undefined>;
|
|
32
32
|
/** 可作为迁移目标的 preset(去掉 broken 的)。 */
|
|
33
33
|
export declare function listPresets(input: BridgeHostInput): Promise<PresetRow[]>;
|
|
34
|
+
/** Map the one upstream internal rename without making either generation mandatory. */
|
|
35
|
+
export declare function resolvePresetTarget(requested: string, presets: readonly PresetRow[]): string;
|
|
34
36
|
/**
|
|
35
37
|
* 选压缩工人的模型。
|
|
36
38
|
*
|
package/lib/migrate.js
CHANGED
|
@@ -50,6 +50,16 @@ export async function listPresets(input) {
|
|
|
50
50
|
const res = await host.presets.list();
|
|
51
51
|
return (res.presets ?? []).filter((preset) => preset.broken === undefined);
|
|
52
52
|
}
|
|
53
|
+
/** Map the one upstream internal rename without making either generation mandatory. */
|
|
54
|
+
export function resolvePresetTarget(requested, presets) {
|
|
55
|
+
if (presets.some(preset => preset.id === requested))
|
|
56
|
+
return requested;
|
|
57
|
+
if (requested === 'code' && presets.some(preset => preset.id === 'ptc'))
|
|
58
|
+
return 'ptc';
|
|
59
|
+
if (requested === 'ptc' && presets.some(preset => preset.id === 'code'))
|
|
60
|
+
return 'code';
|
|
61
|
+
return requested;
|
|
62
|
+
}
|
|
53
63
|
/** 新会话的落点:优先同工作区,否则同 cwd,再否则交给 host 默认。 */
|
|
54
64
|
async function placement(host, source, sessionId) {
|
|
55
65
|
const workspaceId = await findWorkspaceId(host, sessionId).catch(() => undefined);
|
|
@@ -278,7 +288,7 @@ function imageFallbackAllowed(error) {
|
|
|
278
288
|
return false;
|
|
279
289
|
if (error.code === 'unavailable' || error.code === 'empty-image')
|
|
280
290
|
return true;
|
|
281
|
-
if (error.code !== 'attachment-error')
|
|
291
|
+
if (error.code !== 'attachment-error' && error.code !== 'session/attachment-invalid')
|
|
282
292
|
return false;
|
|
283
293
|
const reason = error.details && typeof error.details === 'object' && 'reason' in error.details
|
|
284
294
|
? String(error.details.reason)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-bridge",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Previewable cross-preset session migration for DeepSeek Harness with bounded, fixed-schema handoffs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"reports/v0.2.6-rc11-vision-report.md",
|
|
14
14
|
"reports/native-workbench-2026-08-25.md",
|
|
15
15
|
"reports/native-workbench-2026-08-25.raw.json",
|
|
16
|
+
"reports/dsh-0.1.2-alpha.2-compat-2026-08-31.md",
|
|
16
17
|
"cordis.patch.yml",
|
|
17
18
|
"README.md",
|
|
18
19
|
"README.zh.md"
|
|
@@ -48,8 +49,8 @@
|
|
|
48
49
|
"client": {
|
|
49
50
|
"inject": [
|
|
50
51
|
"@deepseek-ai/dsh-api-remotes",
|
|
51
|
-
"@deepseek-ai/dsh-client-
|
|
52
|
-
"@deepseek-ai/dsh-client-ui-
|
|
52
|
+
"@deepseek-ai/dsh-client-ui-conversation",
|
|
53
|
+
"@deepseek-ai/dsh-client-ui-chat"
|
|
53
54
|
],
|
|
54
55
|
"platform": "web"
|
|
55
56
|
}
|
|
@@ -59,16 +60,17 @@
|
|
|
59
60
|
},
|
|
60
61
|
"peerDependencies": {
|
|
61
62
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
62
|
-
"@deepseek-ai/dsh-api-remotes": "^0.1.0-rc.7 || ^0.1.1-rc.1",
|
|
63
|
-
"@deepseek-ai/dsh-client-
|
|
64
|
-
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.7 || ^0.1.1-rc.1",
|
|
65
|
-
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.7 || ^0.1.1-rc.1",
|
|
66
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.7 || ^0.1.1-rc.1",
|
|
63
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.0-rc.7 || ^0.1.1-rc.1 || ^0.1.2-alpha.1",
|
|
64
|
+
"@deepseek-ai/dsh-client-ui-chat": "^0.1.0-rc.7 || ^0.1.1-rc.1 || ^0.1.2-alpha.1",
|
|
65
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.7 || ^0.1.1-rc.1 || ^0.1.2-alpha.1",
|
|
66
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.7 || ^0.1.1-rc.1 || ^0.1.2-alpha.1",
|
|
67
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.7 || ^0.1.1-rc.1 || ^0.1.2-alpha.1",
|
|
67
68
|
"react": "^18.2.0"
|
|
68
69
|
},
|
|
69
70
|
"peerDependenciesMeta": {
|
|
71
|
+
"@deepseek-ai/cordis": { "optional": true },
|
|
70
72
|
"@deepseek-ai/dsh-api-remotes": { "optional": true },
|
|
71
|
-
"@deepseek-ai/dsh-client-
|
|
73
|
+
"@deepseek-ai/dsh-client-ui-chat": { "optional": true },
|
|
72
74
|
"@deepseek-ai/dsh-client-ui-conversation": { "optional": true },
|
|
73
75
|
"@deepseek-ai/dsh-client-ui-primitives": { "optional": true },
|
|
74
76
|
"@deepseek-ai/dsh-client-ui-slots": { "optional": true },
|
|
@@ -78,12 +80,15 @@
|
|
|
78
80
|
"@deepseek-ai/schemastery": "^3.18.1"
|
|
79
81
|
},
|
|
80
82
|
"devDependencies": {
|
|
81
|
-
"@deepseek-ai/cordis": "4.0.
|
|
82
|
-
"@deepseek-ai/dsh-api-remotes": "^0.1.
|
|
83
|
-
"@deepseek-ai/dsh-
|
|
84
|
-
"@deepseek-ai/dsh-client-ui-
|
|
85
|
-
"@deepseek-ai/dsh-client-ui-
|
|
86
|
-
"@deepseek-ai/dsh-client-ui-
|
|
83
|
+
"@deepseek-ai/cordis": "4.0.2",
|
|
84
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.2",
|
|
85
|
+
"@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.2",
|
|
86
|
+
"@deepseek-ai/dsh-client-ui-chat": "^0.1.2-alpha.2",
|
|
87
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.2",
|
|
88
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.2-alpha.2",
|
|
89
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.2",
|
|
90
|
+
"@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.2",
|
|
91
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.2-alpha.2",
|
|
87
92
|
"@types/node": "^26.2.0",
|
|
88
93
|
"@types/react": "~18.3.1",
|
|
89
94
|
"react": "^18.3.1",
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# DSH 0.1.2-alpha.2 compatibility validation
|
|
2
|
+
|
|
3
|
+
Date: 2026-08-31 (Asia/Shanghai)
|
|
4
|
+
|
|
5
|
+
## Scope
|
|
6
|
+
|
|
7
|
+
- Official host: `@deepseek-ai/dsh@0.1.2-alpha.2` from the npm registry
|
|
8
|
+
- Upstream tag: `dsh-v0.1.2-alpha.2`
|
|
9
|
+
- Upstream commit: `0a53fb55bea101816fa226bb964ae2bed71c343b`
|
|
10
|
+
- Bridge branch: `codex/dsh-alpha2-compat`
|
|
11
|
+
- Packed Bridge SHA-256: `4170fc548ad4c9f77d70bc9e47cef92da849b4fa608a6b7cf323f4ad972cb085`
|
|
12
|
+
- All profiles, credentials copied for the run, sessions, packages, and browser state were isolated from the ordinary DSH home.
|
|
13
|
+
|
|
14
|
+
## Compatibility changes covered
|
|
15
|
+
|
|
16
|
+
- Accept alpha.2's `session/attachment-invalid` image rejection while retaining rc.2's `attachment-error` behavior.
|
|
17
|
+
- Remove the alpha-deleted `@deepseek-ai/dsh-client-runtime` peer, development import, and bundler external.
|
|
18
|
+
- Build against the split alpha.2 client contracts: API remotes/session controller, Chat command-row slot, renderer, Session UI, primitives, and slots.
|
|
19
|
+
- Provide alpha.2's required localized labels to `MarkdownText` and `JsonTree`.
|
|
20
|
+
- Add `ui-chat` to client injection so the package that owns `conversation.chat.commandview` is explicit rather than assumed from the official Web profile.
|
|
21
|
+
- Treat Cordis as a host-provided optional peer while retaining its supported version range.
|
|
22
|
+
|
|
23
|
+
## Automated evidence
|
|
24
|
+
|
|
25
|
+
- Focused old/new image fallback tests passed.
|
|
26
|
+
- Alpha manifest and split-client contract tests passed.
|
|
27
|
+
- `npm run verify` passed build, typecheck, 167 tests, generated-lib drift, dataset checks, and packed-package smoke installation.
|
|
28
|
+
- Source coverage: 93.58% lines, 78.10% branches, and 88.24% functions.
|
|
29
|
+
|
|
30
|
+
## Official npm install evidence
|
|
31
|
+
|
|
32
|
+
- The clean runtime reported DSH `0.1.2-alpha.2`.
|
|
33
|
+
- Plugin add completed without a peer warning after the Cordis metadata correction.
|
|
34
|
+
- `pnpm peers check`: no peer dependency issues.
|
|
35
|
+
- The profile contained the Bridge dependency and bundle layer, and `node_modules/dsh-plugin-bridge` resolved.
|
|
36
|
+
|
|
37
|
+
## Real WebUI evidence
|
|
38
|
+
|
|
39
|
+
- Native `/bridge --doctor`: `dsh-typed-controllers`, 13/13 required methods.
|
|
40
|
+
- `code` resolved to the official `ptc` preset.
|
|
41
|
+
- Preview preserved `ORBIT-A2-731`, port `7643`, `PostgreSQL`, `src/alpha.ts`, and the `MongoDB` prohibition.
|
|
42
|
+
- Preview, Text, and Markdown modes rendered. A Next-step edit in Text mode was the exact Next step delivered to the target.
|
|
43
|
+
- At 1200x853 the card panel scrolled internally; toolbar and confirmation actions stayed outside it; the document had no overflow.
|
|
44
|
+
- Confirm automatically opened the target under the official PTC mode.
|
|
45
|
+
- The target restated all five facts and stopped for confirmation.
|
|
46
|
+
- The target goal was visibly paused and contained the reviewed summary.
|
|
47
|
+
- The original source remained separately selectable and retained its original model conversation.
|
|
48
|
+
|
|
49
|
+
The run used three authorized model requests: source seed, preview worker, and target restatement.
|
|
50
|
+
|
|
51
|
+
## Removal evidence
|
|
52
|
+
|
|
53
|
+
After `dsh plugin --profile web remove dsh-plugin-bridge` and a WebUI restart:
|
|
54
|
+
|
|
55
|
+
- the profile dependency was absent;
|
|
56
|
+
- the bundle list contained only the official base and Web app;
|
|
57
|
+
- the Bridge package was absent from profile `node_modules` and lock/profile text;
|
|
58
|
+
- the browser loaded no Bridge client asset and contained zero `.dsh-bridge-card` nodes;
|
|
59
|
+
- the fresh browser console contained zero errors;
|
|
60
|
+
- historical command outcomes remained readable through the official generic command renderer.
|
|
61
|
+
|
|
62
|
+
## Known boundary
|
|
63
|
+
|
|
64
|
+
This run did not repeat a live raw-image/VLM transfer. Alpha.2's namespaced text-model image rejection is covered by deterministic migration regression tests; the earlier rc.2 vision report remains the live attachment-transfer evidence. Installation and removal still require one WebUI restart.
|