dsh-wechat-remote 1.7.12-rc.9 → 1.7.12
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 +6 -2
- package/assets/plugin.tgz +0 -0
- package/assets/release.json +28 -13
- package/lib/install-control.js +142 -9
- package/native/lib/dsh-protocol-compat.d.ts +5 -0
- package/native/lib/dsh-protocol-compat.js +39 -4
- package/native/lib/dsh-realtime-compat.js +30 -1
- package/native/lib/legacy-model-identity.d.ts +19 -0
- package/native/lib/legacy-model-identity.js +100 -0
- package/native/lib/plugin-version.d.ts +1 -1
- package/native/lib/plugin-version.js +1 -1
- package/native/lib/session-presentation.d.ts +8 -1
- package/native/lib/session-presentation.js +30 -1
- package/native/package.json +7 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agent远程管理助手 · DSH 插件安装器
|
|
2
2
|
|
|
3
|
-
命令行入口用于 DSH Web
|
|
3
|
+
命令行入口用于 DSH Web;同一个安装包也可在 Desktop 原生插件管理器中安装。当前正式版为 1.7.12。
|
|
4
4
|
|
|
5
5
|
首次安装和升级旧插件使用同一个命令:
|
|
6
6
|
|
|
@@ -24,7 +24,11 @@ DSH 的新版本、预览版本或未登记的主机架构不会仅因缺少验
|
|
|
24
24
|
|
|
25
25
|
默认使用 `web` 配置和原来的 DSH 数据目录。自定义数据目录时,在新终端设置相同的 `DSH_HOME`,或附加 `--home "数据目录"`;使用其他配置时附加 `--profile 配置名称`。WSL、容器和其他系统用户内的 DSH,需要在对应环境中执行安装,不能跨环境替换。
|
|
26
26
|
|
|
27
|
-
Desktop
|
|
27
|
+
Desktop 在原生插件管理器中添加 `dsh-wechat-remote`,不支持用 `--profile desktop` 绕过它。启用后可在本插件详情或“设置 → 微信连接”中检查更新;Desktop 的一键更新使用宿主原生安装流程。若更新后提示待生效,请在任务结束时完全退出并重新打开 Desktop,无需重新配对。
|
|
28
|
+
|
|
29
|
+
发现另一端可更新时先提示,确认后等待空闲再处理,也可选择稍后。仅处理同一系统用户、同一数据目录下已经安装并启用的插件;需要另一端原生更新能力配合,可能需要重启。未完成联动时保留另一端原状并提示,不代表两端已更新。
|
|
30
|
+
|
|
31
|
+
现有小程序的模型菜单可能未及时反映电脑上的切换;发送前请重新选择所需模型,并确认电脑已同步该选择,避免选择后立即发送。同一会话仍受 DSH 原生占用规则约束,不能由 Web 和 Desktop 同时操作。
|
|
28
32
|
|
|
29
33
|
同一个 Web 节点请勿重复添加两种微信连接插件包。若原先通过原生插件管理页安装了 `dsh-wechat-remote`,仍回到该页面更新;一行安装器会提示安装来源不匹配,不会再添加另一份。
|
|
30
34
|
|
package/assets/plugin.tgz
CHANGED
|
Binary file
|
package/assets/release.json
CHANGED
|
@@ -1,22 +1,37 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.7.12
|
|
2
|
+
"version": "1.7.12",
|
|
3
3
|
"catalog": {
|
|
4
4
|
"schemaVersion": 1,
|
|
5
|
-
"revision": "installer-1.7.12
|
|
6
|
-
"issuedAt":
|
|
7
|
-
"expiresAt":
|
|
5
|
+
"revision": "installer-1.7.12",
|
|
6
|
+
"issuedAt": 1790610305428,
|
|
7
|
+
"expiresAt": 1793029505428,
|
|
8
8
|
"releases": [
|
|
9
9
|
{
|
|
10
|
-
"version": "1.7.12
|
|
11
|
-
"channel": "
|
|
12
|
-
"dsh": [
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
"version": "1.7.12",
|
|
11
|
+
"channel": "stable",
|
|
12
|
+
"dsh": [
|
|
13
|
+
"0.1.7-rc.1"
|
|
14
|
+
],
|
|
15
|
+
"platforms": [
|
|
16
|
+
"windows"
|
|
17
|
+
],
|
|
18
|
+
"architectures": [
|
|
19
|
+
"x64"
|
|
20
|
+
],
|
|
15
21
|
"asset": {
|
|
16
|
-
"url": "https://github.com/martinbear1/dsh-wechat-remote/releases/download/v1.7.12
|
|
17
|
-
"sha256": "
|
|
18
|
-
"bytes":
|
|
19
|
-
}
|
|
22
|
+
"url": "https://github.com/martinbear1/dsh-wechat-remote/releases/download/v1.7.12/harness-remote-dsh-wechat-remote-1.7.12.tgz",
|
|
23
|
+
"sha256": "10781ff17bbc9e780391f2f753525240406926cbb2097ce641cf3eaf07809254",
|
|
24
|
+
"bytes": 10421020
|
|
25
|
+
},
|
|
26
|
+
"targets": [
|
|
27
|
+
{
|
|
28
|
+
"platform": "windows",
|
|
29
|
+
"arch": "x64",
|
|
30
|
+
"dsh": [
|
|
31
|
+
"0.1.7-rc.1"
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
]
|
|
20
35
|
}
|
|
21
36
|
],
|
|
22
37
|
"blocked": [],
|
package/lib/install-control.js
CHANGED
|
@@ -710,10 +710,29 @@ function presentationProjection(key, raw) {
|
|
|
710
710
|
}
|
|
711
711
|
return { key: `agent.${name}.v1`, value };
|
|
712
712
|
}
|
|
713
|
-
function
|
|
713
|
+
function legacyPermissionValue(value, catalog) {
|
|
714
|
+
const selection = object(value), source = object(catalog);
|
|
715
|
+
if (Array.isArray(selection.options)) return value;
|
|
716
|
+
if (typeof selection.currentValue !== "string" || !Array.isArray(source.options)) return value;
|
|
717
|
+
const seen = /* @__PURE__ */ new Set();
|
|
718
|
+
const options = [];
|
|
719
|
+
for (const raw of source.options) {
|
|
720
|
+
const option = object(raw);
|
|
721
|
+
if (typeof option.value !== "string" || !option.value || seen.has(option.value) || typeof option.name !== "string") return value;
|
|
722
|
+
seen.add(option.value);
|
|
723
|
+
options.push({
|
|
724
|
+
value: option.value,
|
|
725
|
+
name: option.name,
|
|
726
|
+
...typeof option.description === "string" ? { description: option.description } : {}
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
return { ...selection, options };
|
|
730
|
+
}
|
|
731
|
+
function withPresentationProjections(block, permissionCatalog) {
|
|
714
732
|
const source = object(block);
|
|
715
733
|
if (!source.values) return block;
|
|
716
734
|
const values = { ...object(source.values) };
|
|
735
|
+
if (Object.hasOwn(values, "permissions")) values.permissions = legacyPermissionValue(values.permissions, permissionCatalog);
|
|
717
736
|
for (const [key, value] of Object.entries(values)) {
|
|
718
737
|
const projected = presentationProjection(key, value);
|
|
719
738
|
if (projected) values[projected.key] = projected.value;
|
|
@@ -800,6 +819,94 @@ async function invokeHostRemote(ctx, gateway, request) {
|
|
|
800
819
|
return projectedSubagentCatalog(parent, projection, listing?.items);
|
|
801
820
|
}
|
|
802
821
|
|
|
822
|
+
// src/legacy-model-identity.ts
|
|
823
|
+
var LEGACY_MODEL_FORMAT = "legacy-provider-model-v1";
|
|
824
|
+
var NATIVE_MODEL_FORMAT = "native-v1";
|
|
825
|
+
var separator = " \xB7 ";
|
|
826
|
+
function record2(value) {
|
|
827
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
828
|
+
}
|
|
829
|
+
function failure(message) {
|
|
830
|
+
throw Object.assign(new Error(message), { code: "adapter/model-identity-invalid" });
|
|
831
|
+
}
|
|
832
|
+
function modelIdentityFormat(request) {
|
|
833
|
+
const format = request.modelIdentity ?? LEGACY_MODEL_FORMAT;
|
|
834
|
+
if (format !== LEGACY_MODEL_FORMAT && format !== NATIVE_MODEL_FORMAT) {
|
|
835
|
+
failure("\u4E0D\u652F\u6301\u6B64\u6A21\u578B\u6807\u8BC6\u683C\u5F0F\uFF1B\u672A\u66F4\u6539\u6A21\u578B");
|
|
836
|
+
}
|
|
837
|
+
return format;
|
|
838
|
+
}
|
|
839
|
+
function component(value) {
|
|
840
|
+
return value.replace(/%/g, "%25").replace(/·/g, "%B7").replace(/[\u0000-\u001f\u007f]/g, (char) => "%" + char.charCodeAt(0).toString(16).padStart(2, "0").toUpperCase());
|
|
841
|
+
}
|
|
842
|
+
function legacyModelAlias(provider, model) {
|
|
843
|
+
return component(provider) + separator + component(model);
|
|
844
|
+
}
|
|
845
|
+
function groupsOf(catalog) {
|
|
846
|
+
const groups = record2(catalog)?.groups;
|
|
847
|
+
if (!Array.isArray(groups)) failure("\u4E3B\u673A\u6A21\u578B\u76EE\u5F55\u65E0\u6548\uFF1B\u8BF7\u5237\u65B0\u6A21\u578B\u5217\u8868");
|
|
848
|
+
const providers = /* @__PURE__ */ new Set();
|
|
849
|
+
for (const candidate of groups) {
|
|
850
|
+
const group = record2(candidate);
|
|
851
|
+
if (!group || typeof group.id !== "string" || !group.id || providers.has(group.id) || !Array.isArray(group.models)) {
|
|
852
|
+
failure("\u4E3B\u673A\u63D0\u4F9B\u65B9\u76EE\u5F55\u65E0\u6548\uFF1B\u8BF7\u5237\u65B0\u6A21\u578B\u5217\u8868");
|
|
853
|
+
}
|
|
854
|
+
providers.add(group.id);
|
|
855
|
+
const models = /* @__PURE__ */ new Set();
|
|
856
|
+
for (const candidate2 of group.models) {
|
|
857
|
+
const model = record2(candidate2);
|
|
858
|
+
if (!model || typeof model.id !== "string" || !model.id || models.has(model.id)) {
|
|
859
|
+
failure("\u4E3B\u673A\u6A21\u578B\u76EE\u5F55\u5305\u542B\u65E0\u6548\u6216\u91CD\u590D\u6807\u8BC6\uFF1B\u672A\u731C\u6D4B\u6A21\u578B");
|
|
860
|
+
}
|
|
861
|
+
models.add(model.id);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
return groups;
|
|
865
|
+
}
|
|
866
|
+
function legacyModelSelection(value) {
|
|
867
|
+
const selection = record2(value);
|
|
868
|
+
if (!selection || typeof selection.provider !== "string" || typeof selection.model !== "string") return value;
|
|
869
|
+
return { ...selection, model: legacyModelAlias(selection.provider, selection.model), nativeModel: selection.model };
|
|
870
|
+
}
|
|
871
|
+
function presentModelCatalog(catalog, current, format) {
|
|
872
|
+
const groups = groupsOf(catalog);
|
|
873
|
+
const aliases = format === LEGACY_MODEL_FORMAT && groups.length > 1;
|
|
874
|
+
const selection = (value) => {
|
|
875
|
+
const selected = record2(value);
|
|
876
|
+
const missingProvider = groups.length === 1 && selected && selected.provider !== groups[0].id;
|
|
877
|
+
return format === LEGACY_MODEL_FORMAT && (aliases || missingProvider) ? legacyModelSelection(value) : value;
|
|
878
|
+
};
|
|
879
|
+
return {
|
|
880
|
+
...catalog,
|
|
881
|
+
groups: aliases ? groups.map((group) => ({ ...group, models: group.models.map((model) => ({
|
|
882
|
+
...model,
|
|
883
|
+
id: legacyModelAlias(group.id, model.id),
|
|
884
|
+
name: `${group.name || group.id}${separator}${model.name || model.id}`,
|
|
885
|
+
nativeModel: model.id
|
|
886
|
+
})) })) : groups,
|
|
887
|
+
default: selection(catalog.default),
|
|
888
|
+
current: selection(current),
|
|
889
|
+
// Per-response negotiation (not OS/version guessing or global state).
|
|
890
|
+
// A future Mini requests native-v1 and MUST verify this acknowledgement.
|
|
891
|
+
modelIdentity: { version: 1, format, aliases, supportedFormats: [LEGACY_MODEL_FORMAT, NATIVE_MODEL_FORMAT] }
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
function needsModelAliasResolution(request) {
|
|
895
|
+
return modelIdentityFormat(request) === LEGACY_MODEL_FORMAT && typeof request.model === "string" && request.model.includes(separator);
|
|
896
|
+
}
|
|
897
|
+
function resolveModelAlias(catalog, request) {
|
|
898
|
+
const groups = groupsOf(catalog);
|
|
899
|
+
const group = groups.find((group2) => group2.id === request.provider);
|
|
900
|
+
if (!group || typeof request.model !== "string") failure("\u8BE5\u63D0\u4F9B\u65B9\u5DF2\u4E0D\u53EF\u7528\uFF1B\u8BF7\u5237\u65B0\u6A21\u578B\u5217\u8868\u540E\u91CD\u65B0\u9009\u62E9");
|
|
901
|
+
const models = group.models;
|
|
902
|
+
const raw = models.find((model) => model.id === request.model);
|
|
903
|
+
const aliased = models.find((model) => legacyModelAlias(group.id, model.id) === request.model);
|
|
904
|
+
if (raw && aliased && raw.id !== aliased.id) failure("\u6A21\u578B\u6807\u8BC6\u6709\u6B67\u4E49\uFF1B\u8BF7\u5237\u65B0\u6A21\u578B\u5217\u8868\u540E\u91CD\u65B0\u9009\u62E9");
|
|
905
|
+
if (aliased) return { model: aliased.id, aliased: true };
|
|
906
|
+
if (raw) return { model: raw.id, aliased: false };
|
|
907
|
+
failure("\u6A21\u578B\u6216\u63D0\u4F9B\u65B9\u5DF2\u53D8\u5316\uFF1B\u8BF7\u5237\u65B0\u6A21\u578B\u5217\u8868\u540E\u91CD\u65B0\u9009\u62E9\uFF0C\u672A\u66F4\u6539\u6A21\u578B");
|
|
908
|
+
}
|
|
909
|
+
|
|
803
910
|
// src/dsh-protocol-compat.ts
|
|
804
911
|
var SINGLE_REQUEST_METHODS = /* @__PURE__ */ new Set([
|
|
805
912
|
"session.attachment",
|
|
@@ -809,7 +916,6 @@ var SINGLE_REQUEST_METHODS = /* @__PURE__ */ new Set([
|
|
|
809
916
|
"session.openWorkspacePath",
|
|
810
917
|
"session.rename",
|
|
811
918
|
"session.search",
|
|
812
|
-
"session.selectModel",
|
|
813
919
|
"session.updateQueue",
|
|
814
920
|
"workspace.archiveSession",
|
|
815
921
|
"workspace.create",
|
|
@@ -878,6 +984,7 @@ function planLegacyRpc(request) {
|
|
|
878
984
|
if (method === "session.models") {
|
|
879
985
|
return { kind: "session-models", request: payload };
|
|
880
986
|
}
|
|
987
|
+
if (method === "session.selectModel") return { kind: "session-select-model", request: payload };
|
|
881
988
|
if (method === "session.prompt") {
|
|
882
989
|
const parts = Array.isArray(payload.content) ? payload.content : [];
|
|
883
990
|
const first = recordOf(parts[0]);
|
|
@@ -1002,6 +1109,14 @@ function resolveTypertGateway(ctx) {
|
|
|
1002
1109
|
const query = ctx.get("sessionQuery");
|
|
1003
1110
|
const agents = ctx.get("agents");
|
|
1004
1111
|
return {
|
|
1112
|
+
permissionCatalog: () => {
|
|
1113
|
+
try {
|
|
1114
|
+
const service = ctx.get("permissionPresets");
|
|
1115
|
+
return typeof service?.catalog === "function" ? service.catalog() : void 0;
|
|
1116
|
+
} catch {
|
|
1117
|
+
return void 0;
|
|
1118
|
+
}
|
|
1119
|
+
},
|
|
1005
1120
|
...typeof agents?.get === "function" ? { canFollowSession: (id) => Boolean(agents.get(id)) } : {},
|
|
1006
1121
|
...typeof query?.observeSession === "function" ? { readSnapshot: async (address, maxMessages, signal) => {
|
|
1007
1122
|
const id = address.kind === "session" ? address.sessionId : address.childSessionId;
|
|
@@ -1072,8 +1187,8 @@ function errorResult(error) {
|
|
|
1072
1187
|
}
|
|
1073
1188
|
function historyEvents(records) {
|
|
1074
1189
|
if (!Array.isArray(records)) return [];
|
|
1075
|
-
return records.flatMap((
|
|
1076
|
-
const row = recordOf(
|
|
1190
|
+
return records.flatMap((record3) => {
|
|
1191
|
+
const row = recordOf(record3);
|
|
1077
1192
|
const event = recordOf(row?.event);
|
|
1078
1193
|
if (row?.type === "event" && event) return [{ event }];
|
|
1079
1194
|
if (row?.type !== "chunks" || !event) return [];
|
|
@@ -1181,7 +1296,7 @@ function createHistoryPageReader(gateway, sessionId, signal) {
|
|
|
1181
1296
|
return {
|
|
1182
1297
|
events: historyEvents(first.records),
|
|
1183
1298
|
hasMore: first.hasMore === true,
|
|
1184
|
-
projections: withPresentationProjections(first.projections),
|
|
1299
|
+
projections: withPresentationProjections(first.projections, gateway.permissionCatalog?.()),
|
|
1185
1300
|
historyEndSeq: first.cursor
|
|
1186
1301
|
};
|
|
1187
1302
|
}
|
|
@@ -1189,7 +1304,7 @@ function createHistoryPageReader(gateway, sessionId, signal) {
|
|
|
1189
1304
|
return {
|
|
1190
1305
|
...page,
|
|
1191
1306
|
...latest ? {
|
|
1192
|
-
projections: withPresentationProjections(first.projections),
|
|
1307
|
+
projections: withPresentationProjections(first.projections, gateway.permissionCatalog?.()),
|
|
1193
1308
|
historyEndSeq: first.cursor
|
|
1194
1309
|
} : {}
|
|
1195
1310
|
};
|
|
@@ -1233,6 +1348,7 @@ async function permissionCommandValue(gateway, plan, signal, readHistory, flushP
|
|
|
1233
1348
|
return plan.nativeReceipt ? command : { accepted: true, command: true, permission: current, commandId: command?.commandId };
|
|
1234
1349
|
}
|
|
1235
1350
|
async function sessionModelsValue(gateway, request, signal) {
|
|
1351
|
+
const format = modelIdentityFormat(request);
|
|
1236
1352
|
const sessionId = typeof request.sessionId === "string" ? request.sessionId : "";
|
|
1237
1353
|
if (!sessionId) throw new Error("session.models requires sessionId");
|
|
1238
1354
|
const address = await resolveDshSessionAddress(gateway, sessionId, signal);
|
|
@@ -1251,7 +1367,23 @@ async function sessionModelsValue(gateway, request, signal) {
|
|
|
1251
1367
|
}
|
|
1252
1368
|
const current = selection?.next ?? selection?.lastUsed ?? catalog.default;
|
|
1253
1369
|
if (!recordOf(current)) throw new Error("DSH returned no usable model selection");
|
|
1254
|
-
return
|
|
1370
|
+
return presentModelCatalog(catalog, current, format);
|
|
1371
|
+
}
|
|
1372
|
+
async function selectModelValue(gateway, request, signal) {
|
|
1373
|
+
signal.throwIfAborted();
|
|
1374
|
+
modelIdentityFormat(request);
|
|
1375
|
+
const { modelIdentity: _format, ...native } = request;
|
|
1376
|
+
let aliased = false;
|
|
1377
|
+
if (needsModelAliasResolution(request)) {
|
|
1378
|
+
const catalog = await gateway.invoke({ namespace: "session", method: "modelCatalog", args: {}, signal });
|
|
1379
|
+
const resolved = resolveModelAlias(catalog, request);
|
|
1380
|
+
native.model = resolved.model;
|
|
1381
|
+
aliased = resolved.aliased;
|
|
1382
|
+
}
|
|
1383
|
+
signal.throwIfAborted();
|
|
1384
|
+
const result = await gateway.invoke({ namespace: "session", method: "selectModel", args: { request: native }, signal });
|
|
1385
|
+
const receipt = recordOf(result);
|
|
1386
|
+
return aliased && receipt && recordOf(receipt.selected) ? { ...receipt, selected: legacyModelSelection(receipt.selected) } : result;
|
|
1255
1387
|
}
|
|
1256
1388
|
async function invokeLegacyRpc(gateway, request, options) {
|
|
1257
1389
|
let result;
|
|
@@ -1262,6 +1394,7 @@ async function invokeLegacyRpc(gateway, request, options) {
|
|
|
1262
1394
|
else if (plan.kind === "workspace-list") value = await workspaceValue(gateway, options.signal);
|
|
1263
1395
|
else if (plan.kind === "session-history") value = await historyValue(gateway, plan.request, options.signal);
|
|
1264
1396
|
else if (plan.kind === "session-models") value = await sessionModelsValue(gateway, plan.request, options.signal);
|
|
1397
|
+
else if (plan.kind === "session-select-model") value = await selectModelValue(gateway, plan.request, options.signal);
|
|
1265
1398
|
else if (plan.kind === "permission-command") value = await permissionCommandValue(
|
|
1266
1399
|
gateway,
|
|
1267
1400
|
plan,
|
|
@@ -1281,8 +1414,8 @@ async function invokeLegacyRpc(gateway, request, options) {
|
|
|
1281
1414
|
});
|
|
1282
1415
|
break;
|
|
1283
1416
|
} catch (error) {
|
|
1284
|
-
const
|
|
1285
|
-
if (request.method !== "session.list" || attempt >= 2 ||
|
|
1417
|
+
const failure2 = recordOf(error);
|
|
1418
|
+
if (request.method !== "session.list" || attempt >= 2 || failure2?.code !== "SESSION_QUERY_PERSISTENCE_FAILED" || typeof failure2.message !== "string" || !/ENOENT.*scandir.*[\\/]\.dsh-mkdir-[^\\/]+$/.test(failure2.message)) throw error;
|
|
1286
1419
|
await delay(25 * (attempt + 1), void 0, { signal: options.signal });
|
|
1287
1420
|
}
|
|
1288
1421
|
}
|
|
@@ -2,6 +2,8 @@ import type { Context } from '@deepseek-ai/cordis';
|
|
|
2
2
|
import { resolveDshSessionAddress } from './dsh-session-address.js';
|
|
3
3
|
type JsonRecord = Record<string, unknown>;
|
|
4
4
|
export interface TypertGatewayLike {
|
|
5
|
+
/** Public synchronous native metadata, not a session write or a guessed list. */
|
|
6
|
+
readonly permissionCatalog?: () => unknown;
|
|
5
7
|
/** Native query snapshots do not promote a cold session into a writer. */
|
|
6
8
|
readonly readSnapshot?: (address: Awaited<ReturnType<typeof resolveDshSessionAddress>>, maxMessages: number, signal: AbortSignal) => Promise<unknown>;
|
|
7
9
|
readonly canFollowSession?: (sessionId: string) => boolean;
|
|
@@ -58,6 +60,9 @@ type InvocationPlan = {
|
|
|
58
60
|
} | {
|
|
59
61
|
readonly kind: 'session-models';
|
|
60
62
|
readonly request: JsonRecord;
|
|
63
|
+
} | {
|
|
64
|
+
readonly kind: 'session-select-model';
|
|
65
|
+
readonly request: JsonRecord;
|
|
61
66
|
} | {
|
|
62
67
|
readonly kind: 'session-history';
|
|
63
68
|
readonly request: JsonRecord;
|
|
@@ -2,6 +2,7 @@ import { setTimeout as delay } from 'node:timers/promises';
|
|
|
2
2
|
import { resolveDshSessionAddress } from './dsh-session-address.js';
|
|
3
3
|
import { withPresentationProjections } from './session-presentation.js';
|
|
4
4
|
import { openHostEvents, invokeHostRemote } from './dsh-host-contract.js';
|
|
5
|
+
import { modelIdentityFormat, presentModelCatalog, needsModelAliasResolution, resolveModelAlias, legacyModelSelection } from './legacy-model-identity.js';
|
|
5
6
|
const SINGLE_REQUEST_METHODS = new Set([
|
|
6
7
|
'session.attachment',
|
|
7
8
|
'session.cancel',
|
|
@@ -10,7 +11,6 @@ const SINGLE_REQUEST_METHODS = new Set([
|
|
|
10
11
|
'session.openWorkspacePath',
|
|
11
12
|
'session.rename',
|
|
12
13
|
'session.search',
|
|
13
|
-
'session.selectModel',
|
|
14
14
|
'session.updateQueue',
|
|
15
15
|
'workspace.archiveSession',
|
|
16
16
|
'workspace.create',
|
|
@@ -88,6 +88,8 @@ export function planLegacyRpc(request) {
|
|
|
88
88
|
if (method === 'session.models') {
|
|
89
89
|
return { kind: 'session-models', request: payload };
|
|
90
90
|
}
|
|
91
|
+
if (method === 'session.selectModel')
|
|
92
|
+
return { kind: 'session-select-model', request: payload };
|
|
91
93
|
if (method === 'session.prompt') {
|
|
92
94
|
// Released clients carry the permission picker through session.prompt.
|
|
93
95
|
// DSH's native UI uses commands.execute: sending this to the model neither
|
|
@@ -217,6 +219,17 @@ export function resolveTypertGateway(ctx) {
|
|
|
217
219
|
const query = ctx.get('sessionQuery');
|
|
218
220
|
const agents = ctx.get('agents');
|
|
219
221
|
return {
|
|
222
|
+
permissionCatalog: () => {
|
|
223
|
+
// Read on demand so hot-loaded/withdrawn catalog contributions cannot
|
|
224
|
+
// leave stale choices cached in the adapter. No selection is performed.
|
|
225
|
+
try {
|
|
226
|
+
const service = ctx.get('permissionPresets');
|
|
227
|
+
return typeof service?.catalog === 'function' ? service.catalog() : undefined;
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
return undefined;
|
|
231
|
+
} // Catalog failure must not make chat/history unreadable.
|
|
232
|
+
},
|
|
220
233
|
...(typeof agents?.get === 'function' ? { canFollowSession: (id) => Boolean(agents.get(id)) } : {}),
|
|
221
234
|
...(typeof query?.observeSession === 'function' ? { readSnapshot: async (address, maxMessages, signal) => {
|
|
222
235
|
const id = address.kind === 'session' ? address.sessionId : address.childSessionId;
|
|
@@ -429,7 +442,7 @@ export function createHistoryPageReader(gateway, sessionId, signal) {
|
|
|
429
442
|
return {
|
|
430
443
|
events: historyEvents(first.records),
|
|
431
444
|
hasMore: first.hasMore === true,
|
|
432
|
-
projections: withPresentationProjections(first.projections),
|
|
445
|
+
projections: withPresentationProjections(first.projections, gateway.permissionCatalog?.()),
|
|
433
446
|
historyEndSeq: first.cursor,
|
|
434
447
|
};
|
|
435
448
|
}
|
|
@@ -437,7 +450,7 @@ export function createHistoryPageReader(gateway, sessionId, signal) {
|
|
|
437
450
|
return {
|
|
438
451
|
...page,
|
|
439
452
|
...(latest ? {
|
|
440
|
-
projections: withPresentationProjections(first.projections),
|
|
453
|
+
projections: withPresentationProjections(first.projections, gateway.permissionCatalog?.()),
|
|
441
454
|
historyEndSeq: first.cursor,
|
|
442
455
|
} : {}),
|
|
443
456
|
};
|
|
@@ -492,6 +505,7 @@ export async function invokeLegacyPermissionRpc(gateway, request, signal, readHi
|
|
|
492
505
|
return { type: 'server-response', rpcId: request.rpcId, result };
|
|
493
506
|
}
|
|
494
507
|
async function sessionModelsValue(gateway, request, signal) {
|
|
508
|
+
const format = modelIdentityFormat(request);
|
|
495
509
|
const sessionId = typeof request.sessionId === 'string' ? request.sessionId : '';
|
|
496
510
|
if (!sessionId)
|
|
497
511
|
throw new Error('session.models requires sessionId');
|
|
@@ -514,7 +528,26 @@ async function sessionModelsValue(gateway, request, signal) {
|
|
|
514
528
|
const current = selection?.next ?? selection?.lastUsed ?? catalog.default;
|
|
515
529
|
if (!recordOf(current))
|
|
516
530
|
throw new Error('DSH returned no usable model selection');
|
|
517
|
-
return
|
|
531
|
+
return presentModelCatalog(catalog, current, format);
|
|
532
|
+
}
|
|
533
|
+
async function selectModelValue(gateway, request, signal) {
|
|
534
|
+
signal.throwIfAborted();
|
|
535
|
+
modelIdentityFormat(request);
|
|
536
|
+
const { modelIdentity: _format, ...native } = request;
|
|
537
|
+
let aliased = false;
|
|
538
|
+
if (needsModelAliasResolution(request)) {
|
|
539
|
+
const catalog = await gateway.invoke({ namespace: 'session', method: 'modelCatalog', args: {}, signal });
|
|
540
|
+
const resolved = resolveModelAlias(catalog, request);
|
|
541
|
+
native.model = resolved.model;
|
|
542
|
+
aliased = resolved.aliased;
|
|
543
|
+
}
|
|
544
|
+
signal.throwIfAborted();
|
|
545
|
+
// Exactly one native mutation. DSH still validates provider, reasoning effort,
|
|
546
|
+
// session ownership and permissions. A receipt is not permission to replay.
|
|
547
|
+
const result = await gateway.invoke({ namespace: 'session', method: 'selectModel', args: { request: native }, signal });
|
|
548
|
+
const receipt = recordOf(result);
|
|
549
|
+
return aliased && receipt && recordOf(receipt.selected)
|
|
550
|
+
? { ...receipt, selected: legacyModelSelection(receipt.selected) } : result;
|
|
518
551
|
}
|
|
519
552
|
/** Execute one stable request and restore the pre-0.1.2 HTTP envelope. */
|
|
520
553
|
export async function invokeLegacyRpc(gateway, request, options) {
|
|
@@ -530,6 +563,8 @@ export async function invokeLegacyRpc(gateway, request, options) {
|
|
|
530
563
|
value = await historyValue(gateway, plan.request, options.signal);
|
|
531
564
|
else if (plan.kind === 'session-models')
|
|
532
565
|
value = await sessionModelsValue(gateway, plan.request, options.signal);
|
|
566
|
+
else if (plan.kind === 'session-select-model')
|
|
567
|
+
value = await selectModelValue(gateway, plan.request, options.signal);
|
|
533
568
|
else if (plan.kind === 'permission-command')
|
|
534
569
|
value = await permissionCommandValue(gateway, plan, options.signal, (sessionId, signal) => historyValue(gateway, { sessionId, maxMessages: 1 }, signal), options.flushPermission);
|
|
535
570
|
else {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveTypertGateway } from './dsh-protocol-compat.js';
|
|
2
2
|
import { resolveDshSessionAddress, isSessionReadError } from './dsh-session-address.js';
|
|
3
|
-
import { presentationProjection } from './session-presentation.js';
|
|
3
|
+
import { presentationProjection, legacyPermissionValue } from './session-presentation.js';
|
|
4
4
|
import { AssistantStreamCompatibility, assistantRecordPresentation } from './assistant-stream-compat.js';
|
|
5
5
|
import { resourcePresentation } from './agent-resources.js';
|
|
6
6
|
import { TurnActivityCompatibility } from './turn-activity.js';
|
|
@@ -160,6 +160,7 @@ export class DshRealtimeCompatibility {
|
|
|
160
160
|
kind: path.endsWith('.mux') ? 'mux' : 'host',
|
|
161
161
|
lifetime: new AbortController(),
|
|
162
162
|
sessionLifetimes: new Map(),
|
|
163
|
+
permissionProjections: new Map(),
|
|
163
164
|
};
|
|
164
165
|
this.sockets.add(state);
|
|
165
166
|
if (state.kind === 'host')
|
|
@@ -314,6 +315,7 @@ export class DshRealtimeCompatibility {
|
|
|
314
315
|
controller.abort(new Error('socket closed'));
|
|
315
316
|
}
|
|
316
317
|
state.sessionLifetimes.clear();
|
|
318
|
+
state.permissionProjections.clear();
|
|
317
319
|
if (state.clientId) {
|
|
318
320
|
for (const [eventId, values] of this.pending) {
|
|
319
321
|
const next = values.filter(value => value.clientId !== state.clientId);
|
|
@@ -336,6 +338,17 @@ export class DshRealtimeCompatibility {
|
|
|
336
338
|
state.socket.close(1009, 'realtime consumer is too slow');
|
|
337
339
|
return;
|
|
338
340
|
}
|
|
341
|
+
if (payload.type === 'session/projection' && payload.key === 'permissions' && typeof payload.sessionId === 'string') {
|
|
342
|
+
const previous = state.permissionProjections.get(payload.sessionId);
|
|
343
|
+
if (!previous || Number(payload.seq) >= Number(previous.seq)) {
|
|
344
|
+
state.permissionProjections.delete(payload.sessionId);
|
|
345
|
+
state.permissionProjections.set(payload.sessionId, payload);
|
|
346
|
+
if (state.permissionProjections.size > MAX_SESSION_SUBSCRIPTIONS) {
|
|
347
|
+
state.permissionProjections.delete(state.permissionProjections.keys().next().value);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
payload = { ...payload, value: legacyPermissionValue(payload.value, this.gateway().permissionCatalog?.()) };
|
|
351
|
+
}
|
|
339
352
|
state.socket.send(JSON.stringify({
|
|
340
353
|
type: 'server-event',
|
|
341
354
|
...(rpcId ? { rpcId } : {}),
|
|
@@ -376,6 +389,7 @@ export class DshRealtimeCompatibility {
|
|
|
376
389
|
if (!frame)
|
|
377
390
|
return;
|
|
378
391
|
if (frame.type === 'baseline') {
|
|
392
|
+
state.permissionProjections.clear();
|
|
379
393
|
const value = recordOf(frame.value);
|
|
380
394
|
const queues = recordOf(value?.queues) ?? {};
|
|
381
395
|
for (const [sessionId, items] of Object.entries(queues)) {
|
|
@@ -531,10 +545,25 @@ export class DshRealtimeCompatibility {
|
|
|
531
545
|
const frame = recordOf(raw);
|
|
532
546
|
if (!frame)
|
|
533
547
|
continue;
|
|
548
|
+
if (frame.type === 'emit' && frame.event === 'permission-presets/catalog-changed') {
|
|
549
|
+
// The shipped phone has no catalog listener. Re-present its known
|
|
550
|
+
// selections at their original sequence, with the new live choices.
|
|
551
|
+
// It accepts equal-seq presentation refreshes; never invent a native
|
|
552
|
+
// permission change or acquire a session merely to refresh the menu.
|
|
553
|
+
for (const target of this.sockets) {
|
|
554
|
+
for (const payload of [...target.permissionProjections.values()])
|
|
555
|
+
this.send(target, payload);
|
|
556
|
+
}
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
534
559
|
if (frame.type === 'ready') {
|
|
535
560
|
state.clientId = stringOf(frame.clientId);
|
|
536
561
|
continue;
|
|
537
562
|
}
|
|
563
|
+
if (frame.type === 'emit' && frame.event === 'api-session/removed' && Array.isArray(frame.args)) {
|
|
564
|
+
for (const target of this.sockets)
|
|
565
|
+
target.permissionProjections.delete(stringOf(frame.args[0]));
|
|
566
|
+
}
|
|
538
567
|
const conflict = this.readonlyConflict(frame);
|
|
539
568
|
// Temporary phone-notification mitigation only; see readonlyConflict.
|
|
540
569
|
// The original frame and native Desktop activation/lock stay untouched.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compatibility ONLY for the released Mini Program's model-only equality check.
|
|
3
|
+
* Native IDs, projections, defaults on disk, and slash-style Remote RPCs stay native.
|
|
4
|
+
* No mutable/global alias table: Web/Desktop, reconnect and hot reload cannot share
|
|
5
|
+
* stale identity state. Labels never determine routing; exact native IDs do.
|
|
6
|
+
*/
|
|
7
|
+
type RecordValue = Record<string, unknown>;
|
|
8
|
+
export declare const LEGACY_MODEL_FORMAT = "legacy-provider-model-v1";
|
|
9
|
+
export declare const NATIVE_MODEL_FORMAT = "native-v1";
|
|
10
|
+
export declare function modelIdentityFormat(request: RecordValue): string;
|
|
11
|
+
export declare function legacyModelAlias(provider: string, model: string): string;
|
|
12
|
+
export declare function legacyModelSelection(value: unknown): unknown;
|
|
13
|
+
export declare function presentModelCatalog(catalog: RecordValue, current: unknown, format: string): RecordValue;
|
|
14
|
+
export declare function needsModelAliasResolution(request: RecordValue): boolean;
|
|
15
|
+
export declare function resolveModelAlias(catalog: unknown, request: RecordValue): {
|
|
16
|
+
model: string;
|
|
17
|
+
aliased: boolean;
|
|
18
|
+
};
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export const LEGACY_MODEL_FORMAT = 'legacy-provider-model-v1';
|
|
2
|
+
export const NATIVE_MODEL_FORMAT = 'native-v1';
|
|
3
|
+
const separator = ' · ';
|
|
4
|
+
function record(value) {
|
|
5
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : null;
|
|
6
|
+
}
|
|
7
|
+
function failure(message) {
|
|
8
|
+
throw Object.assign(new Error(message), { code: 'adapter/model-identity-invalid' });
|
|
9
|
+
}
|
|
10
|
+
export function modelIdentityFormat(request) {
|
|
11
|
+
const format = request.modelIdentity ?? LEGACY_MODEL_FORMAT;
|
|
12
|
+
if (format !== LEGACY_MODEL_FORMAT && format !== NATIVE_MODEL_FORMAT) {
|
|
13
|
+
failure('不支持此模型标识格式;未更改模型');
|
|
14
|
+
}
|
|
15
|
+
return format;
|
|
16
|
+
}
|
|
17
|
+
function component(value) {
|
|
18
|
+
// Escape the separator character and escape character themselves, retaining
|
|
19
|
+
// readable Unicode. Thus IDs containing punctuation cannot collide.
|
|
20
|
+
return value.replace(/%/g, '%25').replace(/·/g, '%B7')
|
|
21
|
+
.replace(/[\u0000-\u001f\u007f]/g, char => '%' + char.charCodeAt(0).toString(16).padStart(2, '0').toUpperCase());
|
|
22
|
+
}
|
|
23
|
+
export function legacyModelAlias(provider, model) {
|
|
24
|
+
return component(provider) + separator + component(model);
|
|
25
|
+
}
|
|
26
|
+
function groupsOf(catalog) {
|
|
27
|
+
const groups = record(catalog)?.groups;
|
|
28
|
+
if (!Array.isArray(groups))
|
|
29
|
+
failure('主机模型目录无效;请刷新模型列表');
|
|
30
|
+
const providers = new Set();
|
|
31
|
+
for (const candidate of groups) {
|
|
32
|
+
const group = record(candidate);
|
|
33
|
+
if (!group || typeof group.id !== 'string' || !group.id || providers.has(group.id) || !Array.isArray(group.models)) {
|
|
34
|
+
failure('主机提供方目录无效;请刷新模型列表');
|
|
35
|
+
}
|
|
36
|
+
providers.add(group.id);
|
|
37
|
+
const models = new Set();
|
|
38
|
+
for (const candidate of group.models) {
|
|
39
|
+
const model = record(candidate);
|
|
40
|
+
if (!model || typeof model.id !== 'string' || !model.id || models.has(model.id)) {
|
|
41
|
+
failure('主机模型目录包含无效或重复标识;未猜测模型');
|
|
42
|
+
}
|
|
43
|
+
models.add(model.id);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return groups;
|
|
47
|
+
}
|
|
48
|
+
export function legacyModelSelection(value) {
|
|
49
|
+
const selection = record(value);
|
|
50
|
+
if (!selection || typeof selection.provider !== 'string' || typeof selection.model !== 'string')
|
|
51
|
+
return value;
|
|
52
|
+
return { ...selection, model: legacyModelAlias(selection.provider, selection.model), nativeModel: selection.model };
|
|
53
|
+
}
|
|
54
|
+
export function presentModelCatalog(catalog, current, format) {
|
|
55
|
+
const groups = groupsOf(catalog);
|
|
56
|
+
const aliases = format === LEGACY_MODEL_FORMAT && groups.length > 1;
|
|
57
|
+
const selection = (value) => {
|
|
58
|
+
const selected = record(value);
|
|
59
|
+
// A removed provider must not accidentally check the sole remaining
|
|
60
|
+
// provider's same-named model. Keep the unavailable native intent visible.
|
|
61
|
+
const missingProvider = groups.length === 1 && selected && selected.provider !== groups[0].id;
|
|
62
|
+
return format === LEGACY_MODEL_FORMAT && (aliases || missingProvider) ? legacyModelSelection(value) : value;
|
|
63
|
+
};
|
|
64
|
+
return {
|
|
65
|
+
...catalog,
|
|
66
|
+
groups: aliases ? groups.map(group => ({ ...group, models: group.models.map(model => ({
|
|
67
|
+
...model,
|
|
68
|
+
id: legacyModelAlias(group.id, model.id),
|
|
69
|
+
name: `${group.name || group.id}${separator}${model.name || model.id}`,
|
|
70
|
+
nativeModel: model.id,
|
|
71
|
+
})) })) : groups,
|
|
72
|
+
default: selection(catalog.default),
|
|
73
|
+
current: selection(current),
|
|
74
|
+
// Per-response negotiation (not OS/version guessing or global state).
|
|
75
|
+
// A future Mini requests native-v1 and MUST verify this acknowledgement.
|
|
76
|
+
modelIdentity: { version: 1, format, aliases, supportedFormats: [LEGACY_MODEL_FORMAT, NATIVE_MODEL_FORMAT] },
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export function needsModelAliasResolution(request) {
|
|
80
|
+
return modelIdentityFormat(request) === LEGACY_MODEL_FORMAT
|
|
81
|
+
&& typeof request.model === 'string' && request.model.includes(separator);
|
|
82
|
+
}
|
|
83
|
+
export function resolveModelAlias(catalog, request) {
|
|
84
|
+
const groups = groupsOf(catalog);
|
|
85
|
+
const group = groups.find(group => group.id === request.provider);
|
|
86
|
+
if (!group || typeof request.model !== 'string')
|
|
87
|
+
failure('该提供方已不可用;请刷新模型列表后重新选择');
|
|
88
|
+
const models = group.models;
|
|
89
|
+
const raw = models.find(model => model.id === request.model);
|
|
90
|
+
const aliased = models.find(model => legacyModelAlias(group.id, model.id) === request.model);
|
|
91
|
+
// Old clients can retain raw IDs over a plugin upgrade. If a real raw ID is
|
|
92
|
+
// also another model's alias, neither interpretation is safe. Require refresh.
|
|
93
|
+
if (raw && aliased && raw.id !== aliased.id)
|
|
94
|
+
failure('模型标识有歧义;请刷新模型列表后重新选择');
|
|
95
|
+
if (aliased)
|
|
96
|
+
return { model: aliased.id, aliased: true };
|
|
97
|
+
if (raw)
|
|
98
|
+
return { model: raw.id, aliased: false };
|
|
99
|
+
failure('模型或提供方已变化;请刷新模型列表后重新选择,未更改模型');
|
|
100
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Build identity stays with loaded code, even after package.json is replaced. */
|
|
2
|
-
export declare const PLUGIN_VERSION = "1.7.12
|
|
2
|
+
export declare const PLUGIN_VERSION = "1.7.12";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Build identity stays with loaded code, even after package.json is replaced. */
|
|
2
|
-
export const PLUGIN_VERSION = '1.7.12
|
|
2
|
+
export const PLUGIN_VERSION = '1.7.12';
|
|
@@ -2,4 +2,11 @@ export declare function presentationProjection(key: string, raw: unknown): {
|
|
|
2
2
|
key: string;
|
|
3
3
|
value: unknown;
|
|
4
4
|
} | null;
|
|
5
|
-
|
|
5
|
+
/** DSH 0.1.7 moved selectable permissions out of the Session projection into
|
|
6
|
+
* permissionPresets.catalog(). Released phones still read permissions.options.
|
|
7
|
+
* Join that LIVE process catalog only at our legacy transport boundary: never
|
|
8
|
+
* persist options into native history, invent default choices, or change the
|
|
9
|
+
* current permission. Older hosts already supplying options are unchanged.
|
|
10
|
+
* Retire this bridge when supported phone clients read the native catalog. */
|
|
11
|
+
export declare function legacyPermissionValue(value: unknown, catalog: unknown): unknown;
|
|
12
|
+
export declare function withPresentationProjections(block: unknown, permissionCatalog?: unknown): unknown;
|
|
@@ -40,11 +40,40 @@ export function presentationProjection(key, raw) {
|
|
|
40
40
|
}
|
|
41
41
|
return { key: `agent.${name}.v1`, value };
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
/** DSH 0.1.7 moved selectable permissions out of the Session projection into
|
|
44
|
+
* permissionPresets.catalog(). Released phones still read permissions.options.
|
|
45
|
+
* Join that LIVE process catalog only at our legacy transport boundary: never
|
|
46
|
+
* persist options into native history, invent default choices, or change the
|
|
47
|
+
* current permission. Older hosts already supplying options are unchanged.
|
|
48
|
+
* Retire this bridge when supported phone clients read the native catalog. */
|
|
49
|
+
export function legacyPermissionValue(value, catalog) {
|
|
50
|
+
const selection = object(value), source = object(catalog);
|
|
51
|
+
// A legacy Session may carry its own complete choices. They remain the
|
|
52
|
+
// authority on that host; this bridge only supplies the missing modern list.
|
|
53
|
+
if (Array.isArray(selection.options))
|
|
54
|
+
return value;
|
|
55
|
+
if (typeof selection.currentValue !== 'string' || !Array.isArray(source.options))
|
|
56
|
+
return value;
|
|
57
|
+
const seen = new Set();
|
|
58
|
+
const options = [];
|
|
59
|
+
for (const raw of source.options) {
|
|
60
|
+
const option = object(raw);
|
|
61
|
+
if (typeof option.value !== 'string' || !option.value || seen.has(option.value)
|
|
62
|
+
|| typeof option.name !== 'string')
|
|
63
|
+
return value;
|
|
64
|
+
seen.add(option.value);
|
|
65
|
+
options.push({ value: option.value, name: option.name,
|
|
66
|
+
...(typeof option.description === 'string' ? { description: option.description } : {}) });
|
|
67
|
+
}
|
|
68
|
+
return { ...selection, options };
|
|
69
|
+
}
|
|
70
|
+
export function withPresentationProjections(block, permissionCatalog) {
|
|
44
71
|
const source = object(block);
|
|
45
72
|
if (!source.values)
|
|
46
73
|
return block;
|
|
47
74
|
const values = { ...object(source.values) };
|
|
75
|
+
if (Object.hasOwn(values, 'permissions'))
|
|
76
|
+
values.permissions = legacyPermissionValue(values.permissions, permissionCatalog);
|
|
48
77
|
for (const [key, value] of Object.entries(values)) {
|
|
49
78
|
const projected = presentationProjection(key, value);
|
|
50
79
|
if (projected)
|
package/native/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@harness-remote/dsh-wechat-remote",
|
|
3
|
-
"version": "1.7.12
|
|
3
|
+
"version": "1.7.12",
|
|
4
4
|
"description": "Agent远程管理助手:支持 Windows、macOS 与 Linux 上的 DeepSeek Harness,提供微信配对、安全局域网直连及端到端加密公网连接",
|
|
5
5
|
"private": true,
|
|
6
6
|
"license": "MIT",
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"test:attachment": "node scripts/test-attachment.mjs && node scripts/test-public-object-client.mjs && node scripts/test-agent-inputs.mjs && node --test scripts/test-object-transfer-lifecycle.mjs scripts/test-object-entry-recovery.mjs",
|
|
108
108
|
"test:gate-ports": "node scripts/test-gate-ports.mjs",
|
|
109
109
|
"test:dsh-runtime": "node scripts/test-dsh-runtime.mjs && node --test scripts/test-independent-hosts.mjs scripts/test-node-storage.mjs",
|
|
110
|
-
"test:dsh-protocol-compat": "node scripts/test-dsh-protocol-compat.mjs && node scripts/test-dsh-session-address.mjs && node scripts/test-session-presentation.mjs && node --test scripts/test-preset-settings.mjs",
|
|
110
|
+
"test:dsh-protocol-compat": "node scripts/test-dsh-protocol-compat.mjs && node scripts/test-dsh-session-address.mjs && node scripts/test-session-presentation.mjs && node --test scripts/test-preset-settings.mjs scripts/test-session-controls-compat.mjs scripts/test-legacy-model-identity.mjs",
|
|
111
111
|
"test:dsh-realtime-compat": "node scripts/test-dsh-realtime-compat.mjs && node scripts/test-assistant-stream.mjs && node scripts/test-agent-resources.mjs && node --test scripts/test-session-export.mjs scripts/test-session-directory-realtime.mjs scripts/test-cold-session-readonly.mjs scripts/test-passive-writer-notification.mjs",
|
|
112
112
|
"test:dsh-compatibility-api": "node scripts/test-dsh-compatibility-api.mjs && node --test scripts/test-host-compatibility.mjs scripts/test-installer-native-restore.mjs",
|
|
113
113
|
"test:public-relay": "node --test scripts/test-tunnel-send-queue.mjs scripts/test-tunnel-request-lifecycle.mjs && node scripts/test-public-relay.mjs && node scripts/test-relay-liveness.mjs",
|
|
@@ -115,6 +115,8 @@
|
|
|
115
115
|
"test:native-pairing": "node scripts/test-native-pairing.mjs",
|
|
116
116
|
"test:native-preset-settings": "node scripts/test-native-preset-settings.mjs",
|
|
117
117
|
"test:native-control-contract": "node scripts/test-native-control-contract.mjs",
|
|
118
|
+
"test:native-session-controls": "node scripts/test-native-session-controls.mjs",
|
|
119
|
+
"test:released-mini-models": "node scripts/test-released-mini-models.mjs",
|
|
118
120
|
"test:independent-transports": "node scripts/test-independent-transports.mjs",
|
|
119
121
|
"verify:installer": "node scripts/test-hybrid-package.mjs"
|
|
120
122
|
},
|
|
@@ -226,7 +228,9 @@
|
|
|
226
228
|
"lib/typert-schema-compat.js",
|
|
227
229
|
"lib/typert-schema-compat.d.ts",
|
|
228
230
|
"lib/dsh-protocol-compat.js",
|
|
229
|
-
"lib/dsh-protocol-compat.d.ts",
|
|
231
|
+
"lib/dsh-protocol-compat.d.ts",
|
|
232
|
+
"lib/legacy-model-identity.js",
|
|
233
|
+
"lib/legacy-model-identity.d.ts",
|
|
230
234
|
"lib/dsh-session-address.js",
|
|
231
235
|
"lib/dsh-session-address.d.ts",
|
|
232
236
|
"lib/session-presentation.js",
|