dsh-wechat-remote 1.7.12-rc.9 → 1.7.13

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Agent远程管理助手 · DSH 插件安装器
2
2
 
3
- 命令行入口用于 DSH Web;安装包内置配套插件。1.7.12-rc.9 为 Desktop 适配预览测试版,不建议用于日常节点;正式推荐版本仍为 1.7.10,以下 `latest` 命令不会安装本预览版。
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 由桌面应用的原生插件管理器负责安装和更新,不支持用 `--profile desktop` 绕过它。本预览版正在验证两端联动更新:发现另一端可更新时先提示,确认后等待空闲再处理,也可选择稍后。仅处理同一系统用户、同一数据目录下已经安装并启用的插件;需要另一端原生更新能力配合,可能需要重启。未完成联动时保留另一端原状并提示,不代表两端已更新。当前 npm `latest` 不因本预览版而获得 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
@@ -1,22 +1,37 @@
1
1
  {
2
- "version": "1.7.12-rc.9",
2
+ "version": "1.7.13",
3
3
  "catalog": {
4
4
  "schemaVersion": 1,
5
- "revision": "installer-1.7.12-rc.9",
6
- "issuedAt": 1790574684248,
7
- "expiresAt": 1792993884248,
5
+ "revision": "installer-1.7.13",
6
+ "issuedAt": 1790629914471,
7
+ "expiresAt": 1793049114471,
8
8
  "releases": [
9
9
  {
10
- "version": "1.7.12-rc.9",
11
- "channel": "preview",
12
- "dsh": [],
13
- "platforms": [],
14
- "architectures": [],
10
+ "version": "1.7.13",
11
+ "channel": "stable",
12
+ "dsh": [
13
+ "0.1.5-rc.2"
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-rc.9/harness-remote-dsh-wechat-remote-1.7.12-rc.9.tgz",
17
- "sha256": "7983d5dd34ccde43f952b6a65def8180f4b0951c412dfb6e10d3a7ca000083b5",
18
- "bytes": 10415425
19
- }
22
+ "url": "https://github.com/martinbear1/dsh-wechat-remote/releases/download/v1.7.13/harness-remote-dsh-wechat-remote-1.7.13.tgz",
23
+ "sha256": "91ded20e6805b7cf27034bc5dbda443937ad8f7d905d087366080ace080b1893",
24
+ "bytes": 10422958
25
+ },
26
+ "targets": [
27
+ {
28
+ "platform": "windows",
29
+ "arch": "x64",
30
+ "dsh": [
31
+ "0.1.5-rc.2"
32
+ ]
33
+ }
34
+ ]
20
35
  }
21
36
  ],
22
37
  "blocked": [],
@@ -4,9 +4,10 @@
4
4
  import fs from 'node:fs'
5
5
  import path from 'node:path'
6
6
  import { randomBytes } from 'node:crypto'
7
- import { attachControl, waitForJson } from './native-control.mjs'
7
+ import { attachControl, waitForJson } from './native-control.mjs'
8
+ import { verifySessionService } from '../lib/update-session-verification.js'
8
9
 
9
- export async function verifyNativeRestore(job, start, sessionIds, readableIds) {
10
+ export async function verifyNativeRestore(job, start, readableIds) {
10
11
  const directory = path.join(job.home, 'harness-remote-updates', randomBytes(16).toString('hex'))
11
12
  fs.mkdirSync(directory, { mode: 0o700 })
12
13
  fs.writeFileSync(path.join(directory, 'package.json'), '{"type":"module"}\n', { mode: 0o600 })
@@ -32,11 +33,7 @@ export async function verifyNativeRestore(job, start, sessionIds, readableIds) {
32
33
  || host.cli !== job.cli || host.dshVersion !== job.dshVersion || host.pluginVersion !== job.previousVersion) {
33
34
  throw new Error('恢复后的 DSH 身份不匹配。')
34
35
  }
35
- const items = (await request('read', { method: 'session.list' })).items
36
- if (!Array.isArray(items) || JSON.stringify(items.map(s => s.sessionId).sort()) !== JSON.stringify(sessionIds)) {
37
- throw new Error('恢复后的会话列表不一致。')
38
- }
39
- for (const sessionId of readableIds) await request('read', { method: 'session.history', payload: { sessionId, maxMessages: 1 } })
36
+ await verifySessionService((method, payload) => request('read', { method, payload }), readableIds)
40
37
  } finally {
41
38
  try { remove() }
42
39
  finally { if (ref) { try { await request('close') } catch {} } }
@@ -546,7 +546,9 @@ function offerCompanionUpdate(home, from, source, version) {
546
546
  const directory = root(home), file = path5.join(directory, `${to}.json`);
547
547
  try {
548
548
  const existing = read(file);
549
- if (existing.schema === 2 && existing.from === from && existing.version === version && existing.previous === peer.version) return existing;
549
+ if (existing.schema === 2 && existing.from === from && existing.version === version && existing.previous === peer.version) {
550
+ if (!source || existing.source || decision(home, existing) === "approve") return existing;
551
+ }
550
552
  if (versionPattern2.test(existing.version) && compareVersions(existing.version, version) > 0) return;
551
553
  } catch {
552
554
  }
@@ -710,10 +710,29 @@ function presentationProjection(key, raw) {
710
710
  }
711
711
  return { key: `agent.${name}.v1`, value };
712
712
  }
713
- function withPresentationProjections(block) {
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((record2) => {
1076
- const row = recordOf(record2);
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 { ...catalog, current };
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 failure = recordOf(error);
1285
- if (request.method !== "session.list" || attempt >= 2 || failure?.code !== "SESSION_QUERY_PERSISTENCE_FAILED" || typeof failure.message !== "string" || !/ENOENT.*scandir.*[\\/]\.dsh-mkdir-[^\\/]+$/.test(failure.message)) throw error;
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
  }
@@ -7430,8 +7430,20 @@ async function waitForJson(filename, accept, timeout = 2e4) {
7430
7430
  throw error;
7431
7431
  }
7432
7432
 
7433
+ // installer/lib/update-session-verification.js
7434
+ async function verifySessionService(read, previouslyReadable) {
7435
+ const items = (await read("session.list"))?.items;
7436
+ if (!Array.isArray(items) || items.some((item) => !item || typeof item.sessionId !== "string" || !item.sessionId || typeof item.running !== "boolean") || new Set(items.map((item) => item.sessionId)).size !== items.length) {
7437
+ throw new Error("\u4E3B\u673A\u4F1A\u8BDD\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6548\u7684\u4F1A\u8BDD\u5217\u8868");
7438
+ }
7439
+ const visible = new Set(items.map((item) => item.sessionId));
7440
+ for (const sessionId of new Set(previouslyReadable)) {
7441
+ if (visible.has(sessionId)) await read("session.history", { sessionId, maxMessages: 1 });
7442
+ }
7443
+ }
7444
+
7433
7445
  // installer/bin/native-recovery.mjs
7434
- async function verifyNativeRestore(job, start, sessionIds, readableIds) {
7446
+ async function verifyNativeRestore(job, start, readableIds) {
7435
7447
  const directory = path2.join(job.home, "harness-remote-updates", randomBytes2(16).toString("hex"));
7436
7448
  fs2.mkdirSync(directory, { mode: 448 });
7437
7449
  fs2.writeFileSync(path2.join(directory, "package.json"), '{"type":"module"}\n', { mode: 384 });
@@ -7455,11 +7467,7 @@ async function verifyNativeRestore(job, start, sessionIds, readableIds) {
7455
7467
  if (host.pid !== ref.pid || host.home !== job.home || host.profile !== job.profile || host.cli !== job.cli || host.dshVersion !== job.dshVersion || host.pluginVersion !== job.previousVersion) {
7456
7468
  throw new Error("\u6062\u590D\u540E\u7684 DSH \u8EAB\u4EFD\u4E0D\u5339\u914D\u3002");
7457
7469
  }
7458
- const items = (await request("read", { method: "session.list" })).items;
7459
- if (!Array.isArray(items) || JSON.stringify(items.map((s) => s.sessionId).sort()) !== JSON.stringify(sessionIds)) {
7460
- throw new Error("\u6062\u590D\u540E\u7684\u4F1A\u8BDD\u5217\u8868\u4E0D\u4E00\u81F4\u3002");
7461
- }
7462
- for (const sessionId of readableIds) await request("read", { method: "session.history", payload: { sessionId, maxMessages: 1 } });
7470
+ await verifySessionService((method, payload) => request("read", { method, payload }), readableIds);
7463
7471
  } finally {
7464
7472
  try {
7465
7473
  remove();
@@ -0,0 +1,16 @@
1
+ /* Generated from the shared plugin installation sources. */
2
+
3
+ // src/update-session-verification.ts
4
+ async function verifySessionService(read, previouslyReadable) {
5
+ const items = (await read("session.list"))?.items;
6
+ if (!Array.isArray(items) || items.some((item) => !item || typeof item.sessionId !== "string" || !item.sessionId || typeof item.running !== "boolean") || new Set(items.map((item) => item.sessionId)).size !== items.length) {
7
+ throw new Error("\u4E3B\u673A\u4F1A\u8BDD\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6548\u7684\u4F1A\u8BDD\u5217\u8868");
8
+ }
9
+ const visible = new Set(items.map((item) => item.sessionId));
10
+ for (const sessionId of new Set(previouslyReadable)) {
11
+ if (visible.has(sessionId)) await read("session.history", { sessionId, maxMessages: 1 });
12
+ }
13
+ }
14
+ export {
15
+ verifySessionService
16
+ };
@@ -86,6 +86,18 @@ function publishPrivateJson(file, value, replace) {
86
86
  }
87
87
  }
88
88
 
89
+ // src/update-session-verification.ts
90
+ async function verifySessionService(read, previouslyReadable) {
91
+ const items = (await read("session.list"))?.items;
92
+ if (!Array.isArray(items) || items.some((item) => !item || typeof item.sessionId !== "string" || !item.sessionId || typeof item.running !== "boolean") || new Set(items.map((item) => item.sessionId)).size !== items.length) {
93
+ throw new Error("\u4E3B\u673A\u4F1A\u8BDD\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6548\u7684\u4F1A\u8BDD\u5217\u8868");
94
+ }
95
+ const visible = new Set(items.map((item) => item.sessionId));
96
+ for (const sessionId of new Set(previouslyReadable)) {
97
+ if (visible.has(sessionId)) await read("session.history", { sessionId, maxMessages: 1 });
98
+ }
99
+ }
100
+
89
101
  // src/install-profile.ts
90
102
  import fs from "node:fs";
91
103
  import path from "node:path";
@@ -669,9 +681,7 @@ async function executeUpdate(job, progress, quiesce) {
669
681
  await healthy(job, job.targetVersion);
670
682
  await verifyFence(job);
671
683
  assertPreserved(before, durableSnapshot(job));
672
- const after = (await rpc(job, "session.list")).items.map((s) => s.sessionId).sort();
673
- if (JSON.stringify(after) !== JSON.stringify(sessionIds)) throw new Error("\u91CD\u542F\u540E\u4F1A\u8BDD\u5217\u8868\u4E0D\u4E00\u81F4");
674
- for (const id of readableIds) await rpc(job, "session.history", { sessionId: id, maxMessages: 1 });
684
+ await verifySessionService((method, payload) => rpc(job, method, payload), readableIds);
675
685
  writePrivateJsonAtomic(path4.join(job.directory, "verification-complete.json"), { id: job.id });
676
686
  return { phase: "complete", progress: 100, message: "\u63D2\u4EF6\u66F4\u65B0\u5B8C\u6210\uFF0CDSH \u5DF2\u6062\u590D\uFF1B\u539F\u8282\u70B9\u65E0\u9700\u91CD\u65B0\u914D\u5BF9\u3002", terminal: true, ok: true };
677
687
  } catch (error) {
@@ -713,7 +723,7 @@ async function executeUpdate(job, progress, quiesce) {
713
723
  }
714
724
  if (job.previousVersion === "0.0.0" || preexistingFailure) {
715
725
  const recovery = await import(pathToFileURL(path4.join(job.directory, "native-recovery.js")).href);
716
- await recovery.verifyNativeRestore(job, () => start(job), sessionIds, readableIds);
726
+ await recovery.verifyNativeRestore(job, () => start(job), readableIds);
717
727
  } else {
718
728
  start(job);
719
729
  await healthy(job, job.previousVersion);
@@ -865,6 +875,7 @@ if (process.argv[1] && path4.resolve(process.argv[1]) === fileURLToPath(import.m
865
875
  });
866
876
  }
867
877
  export {
878
+ assertPreserved,
868
879
  captureCandidateLock,
869
880
  control,
870
881
  durableSnapshot,