@awiki/dsh-plugin 0.3.11 → 0.3.16

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.
Files changed (97) hide show
  1. package/README.md +24 -2
  2. package/README.zh.md +12 -2
  3. package/cordis.patch.yml +1 -0
  4. package/lib/client.js +1119 -176
  5. package/lib/client.js.map +1 -1
  6. package/lib/index.js +362 -79
  7. package/lib/provider.js +3 -2
  8. package/lib/{sdk-adapter-DpqT2en9.mjs → sdk-adapter-DGcTiEZz.mjs} +110 -9
  9. package/lib/typert.host.js +359 -68
  10. package/lib/typert.remote-client.d.ts +9 -1
  11. package/lib/typert.remote-client.d.ts.map +1 -1
  12. package/lib/typert.remote-client.js +359 -68
  13. package/lib/types/client/AwikiDeviceJoinReminder.d.ts.map +1 -1
  14. package/lib/types/client/AwikiDeviceJoinReminder.js +1 -1
  15. package/lib/types/client/AwikiDeviceJoinReminder.js.map +1 -1
  16. package/lib/types/client/AwikiDevices.d.ts +5 -1
  17. package/lib/types/client/AwikiDevices.d.ts.map +1 -1
  18. package/lib/types/client/AwikiDevices.js +2 -1
  19. package/lib/types/client/AwikiDevices.js.map +1 -1
  20. package/lib/types/client/AwikiIdentityAccess.d.ts.map +1 -1
  21. package/lib/types/client/AwikiIdentityAccess.js +72 -24
  22. package/lib/types/client/AwikiIdentityAccess.js.map +1 -1
  23. package/lib/types/client/AwikiIdentityServices.d.ts +9 -0
  24. package/lib/types/client/AwikiIdentityServices.d.ts.map +1 -0
  25. package/lib/types/client/AwikiIdentityServices.js +66 -0
  26. package/lib/types/client/AwikiIdentityServices.js.map +1 -0
  27. package/lib/types/client/AwikiOverlay.js +1 -1
  28. package/lib/types/client/AwikiOverlay.js.map +1 -1
  29. package/lib/types/client/AwikiSettingsSection.js +1 -1
  30. package/lib/types/client/AwikiSettingsSection.js.map +1 -1
  31. package/lib/types/client/AwikiUpdates.d.ts.map +1 -1
  32. package/lib/types/client/AwikiUpdates.js +21 -10
  33. package/lib/types/client/AwikiUpdates.js.map +1 -1
  34. package/lib/types/client/controller.d.ts +8 -0
  35. package/lib/types/client/controller.d.ts.map +1 -1
  36. package/lib/types/client/controller.js +21 -3
  37. package/lib/types/client/controller.js.map +1 -1
  38. package/lib/types/client/index.d.ts.map +1 -1
  39. package/lib/types/client/index.js +8 -0
  40. package/lib/types/client/index.js.map +1 -1
  41. package/lib/types/client/settings-controller.d.ts +1 -0
  42. package/lib/types/client/settings-controller.d.ts.map +1 -1
  43. package/lib/types/client/settings-controller.js +24 -10
  44. package/lib/types/client/settings-controller.js.map +1 -1
  45. package/lib/types/client/settings-locales.d.ts +1 -1
  46. package/lib/types/client/settings-locales.d.ts.map +1 -1
  47. package/lib/types/client/settings-locales.js +10 -4
  48. package/lib/types/client/settings-locales.js.map +1 -1
  49. package/lib/types/client/slots.d.ts +3 -1
  50. package/lib/types/client/slots.d.ts.map +1 -1
  51. package/lib/types/desktop-distribution.d.ts +7 -3
  52. package/lib/types/desktop-distribution.d.ts.map +1 -1
  53. package/lib/types/desktop-distribution.js +27 -8
  54. package/lib/types/desktop-distribution.js.map +1 -1
  55. package/lib/types/index.d.ts +8 -2
  56. package/lib/types/index.d.ts.map +1 -1
  57. package/lib/types/index.js +154 -47
  58. package/lib/types/index.js.map +1 -1
  59. package/lib/types/listener.js +1 -1
  60. package/lib/types/listener.js.map +1 -1
  61. package/lib/types/message-target.d.ts +4 -0
  62. package/lib/types/message-target.d.ts.map +1 -0
  63. package/lib/types/message-target.js +14 -0
  64. package/lib/types/message-target.js.map +1 -0
  65. package/lib/types/package-version.generated.d.ts +2 -2
  66. package/lib/types/package-version.generated.d.ts.map +1 -1
  67. package/lib/types/package-version.generated.js +2 -2
  68. package/lib/types/package-version.generated.js.map +1 -1
  69. package/lib/types/provider-api.d.ts +17 -1
  70. package/lib/types/provider-api.d.ts.map +1 -1
  71. package/lib/types/provider.d.ts.map +1 -1
  72. package/lib/types/provider.js +2 -1
  73. package/lib/types/provider.js.map +1 -1
  74. package/lib/types/registration-policy.d.ts +1 -1
  75. package/lib/types/registration-policy.d.ts.map +1 -1
  76. package/lib/types/registration-policy.js +1 -1
  77. package/lib/types/registration-policy.js.map +1 -1
  78. package/lib/types/sdk-adapter.d.ts +17 -1
  79. package/lib/types/sdk-adapter.d.ts.map +1 -1
  80. package/lib/types/sdk-adapter.js +90 -5
  81. package/lib/types/sdk-adapter.js.map +1 -1
  82. package/lib/types/settings-rpc-contract.d.ts +1 -0
  83. package/lib/types/settings-rpc-contract.d.ts.map +1 -1
  84. package/lib/types/settings-rpc-contract.js +2 -0
  85. package/lib/types/settings-rpc-contract.js.map +1 -1
  86. package/lib/types/types.d.ts +68 -2
  87. package/lib/types/types.d.ts.map +1 -1
  88. package/lib/types/types.js.map +1 -1
  89. package/lib/types/update-installation.d.ts +18 -0
  90. package/lib/types/update-installation.d.ts.map +1 -0
  91. package/lib/types/update-installation.js +101 -0
  92. package/lib/types/update-installation.js.map +1 -0
  93. package/lib/types/update-policy.d.ts +5 -2
  94. package/lib/types/update-policy.d.ts.map +1 -1
  95. package/lib/types/update-policy.js +14 -8
  96. package/lib/types/update-policy.js.map +1 -1
  97. package/package.json +7 -6
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as fencedMailRecoveryObservability, i as failedMailRecoveryObservability, o as successfulMailRecoveryObservability, r as downloadedAttachment, t as AwikiSdkError } from "./sdk-adapter-DpqT2en9.mjs";
1
+ import { a as fencedMailRecoveryObservability, i as failedMailRecoveryObservability, o as successfulMailRecoveryObservability, r as downloadedAttachment, s as isMessageTarget, t as AwikiSdkError } from "./sdk-adapter-DGcTiEZz.mjs";
2
2
  import "@deepseek-ai/cordis";
3
3
  import { createHash, randomUUID, timingSafeEqual } from "node:crypto";
4
4
  import { homedir } from "node:os";
@@ -13,14 +13,14 @@ import { installModelSelection } from "@deepseek-ai/dsh-agent";
13
13
  import { createUserMessage } from "@deepseek-ai/dsh-llm";
14
14
  import { fileURLToPath } from "node:url";
15
15
  import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
16
- import { satisfies } from "semver";
16
+ import { satisfies, validRange } from "semver";
17
17
  //#region lib/types/builtin-tenant-config.js
18
18
  /** Build-injected two-slot tenant catalog with a source-tree default for tests. */
19
- function record$1(value) {
19
+ function record$2(value) {
20
20
  return typeof value === "object" && value !== null && !Array.isArray(value);
21
21
  }
22
22
  function definition(value) {
23
- if (!record$1(value) || !record$1(value.display_name) || typeof value.display_name["zh-CN"] !== "string" || value.display_name["zh-CN"].trim() === "" || typeof value.display_name.en !== "string" || value.display_name.en.trim() === "" || typeof value.backend_origin !== "string" || typeof value.did_host !== "string") throw new Error("awiki: invalid built-in tenant definition");
23
+ if (!record$2(value) || !record$2(value.display_name) || typeof value.display_name["zh-CN"] !== "string" || value.display_name["zh-CN"].trim() === "" || typeof value.display_name.en !== "string" || value.display_name.en.trim() === "" || typeof value.backend_origin !== "string" || typeof value.did_host !== "string") throw new Error("awiki: invalid built-in tenant definition");
24
24
  const origin = new URL(value.backend_origin);
25
25
  const loopback = origin.protocol === "http:" && [
26
26
  "localhost",
@@ -40,7 +40,7 @@ function definition(value) {
40
40
  };
41
41
  }
42
42
  function decodeBuiltinTenantConfig(value) {
43
- if (!record$1(value) || value.schema_version !== 1 || value.default_slot !== "primary" && value.default_slot !== "secondary" || !record$1(value.tenants) || Object.keys(value.tenants).sort().join(",") !== "primary,secondary") throw new Error("awiki: invalid built-in tenant config");
43
+ if (!record$2(value) || value.schema_version !== 1 || value.default_slot !== "primary" && value.default_slot !== "secondary" || !record$2(value.tenants) || Object.keys(value.tenants).sort().join(",") !== "primary,secondary") throw new Error("awiki: invalid built-in tenant config");
44
44
  const primary = definition(value.tenants.primary);
45
45
  const secondary = definition(value.tenants.secondary);
46
46
  if (primary.backendOrigin === secondary.backendOrigin || primary.didHost === secondary.didHost) throw new Error("awiki: built-in tenant slots must use distinct endpoints");
@@ -184,18 +184,34 @@ function compareVersions(left, right) {
184
184
  function decodeDesktopDistribution(value) {
185
185
  if (typeof value !== "object" || value === null) return void 0;
186
186
  const view = value;
187
- if (view.schemaVersion !== 1 || view.distributionId !== "awiki-dsh-desktop" || typeof view.currentVersion !== "string" || view.channel !== "stable" && view.channel !== "prerelease" || typeof view.downloadPageUrl !== "string" || ![
187
+ if (view.schemaVersion !== 1 && view.schemaVersion !== 2 || view.distributionId !== "awiki-dsh-desktop" || typeof view.currentVersion !== "string" || view.channel !== "stable" && view.channel !== "prerelease" || view.downloadPageUrl !== void 0 && typeof view.downloadPageUrl !== "string" || ![
188
188
  "unchecked",
189
189
  "checking",
190
190
  "ready",
191
- "failed"
191
+ "failed",
192
+ "unavailable"
192
193
  ].includes(String(view.state)) || typeof view.updateAvailable !== "boolean" || typeof view.usedCache !== "boolean" || view.latestVersion !== void 0 && typeof view.latestVersion !== "string" || view.checkedAt !== void 0 && typeof view.checkedAt !== "string" || view.noRelease !== void 0 && typeof view.noRelease !== "boolean") return void 0;
193
194
  try {
194
195
  assertVersion(view.currentVersion);
196
+ if (view.schemaVersion === 1) return {
197
+ schemaVersion: 1,
198
+ distributionId: "awiki-dsh-desktop",
199
+ currentVersion: view.currentVersion,
200
+ channel: view.channel,
201
+ state: "unavailable",
202
+ updateAvailable: false,
203
+ usedCache: false
204
+ };
205
+ const hasTenant = view.tenantId !== void 0 || view.policyOrigin !== void 0 || view.tenantGeneration !== void 0;
206
+ if (hasTenant && (typeof view.tenantId !== "string" || view.tenantId.length === 0 || typeof view.policyOrigin !== "string" || new URL(view.policyOrigin).origin !== view.policyOrigin || new URL(view.policyOrigin).protocol !== "https:" || !Number.isSafeInteger(view.tenantGeneration) || view.tenantGeneration < 0)) return void 0;
207
+ if (!hasTenant && view.state !== "unavailable") return void 0;
208
+ if (view.policyRevision !== void 0 && (!Number.isSafeInteger(view.policyRevision) || view.policyRevision < 1)) return void 0;
195
209
  if (view.latestVersion !== void 0) assertVersion(view.latestVersion);
196
210
  if (view.checkedAt !== void 0 && !Number.isFinite(Date.parse(view.checkedAt))) return void 0;
197
- const url = new URL(view.downloadPageUrl);
198
- if (url.protocol !== "https:" || url.username !== "" || url.password !== "") return void 0;
211
+ if (typeof view.downloadPageUrl === "string") {
212
+ const url = new URL(view.downloadPageUrl);
213
+ if (url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.origin !== view.policyOrigin) return void 0;
214
+ }
199
215
  } catch {
200
216
  return;
201
217
  }
@@ -216,11 +232,17 @@ function decodeDesktopDistribution(value) {
216
232
  };
217
233
  }
218
234
  return {
219
- schemaVersion: 1,
235
+ schemaVersion: 2,
220
236
  distributionId: "awiki-dsh-desktop",
221
237
  currentVersion: view.currentVersion,
222
238
  channel: view.channel,
223
- downloadPageUrl: view.downloadPageUrl,
239
+ ...typeof view.downloadPageUrl === "string" ? { downloadPageUrl: view.downloadPageUrl } : {},
240
+ ...typeof view.tenantId === "string" ? {
241
+ tenantId: view.tenantId,
242
+ policyOrigin: view.policyOrigin,
243
+ tenantGeneration: view.tenantGeneration
244
+ } : {},
245
+ ...typeof view.policyRevision === "number" ? { policyRevision: view.policyRevision } : {},
224
246
  state: view.state,
225
247
  updateAvailable: view.updateAvailable,
226
248
  usedCache: view.usedCache,
@@ -1046,7 +1068,7 @@ const TIMEOUT_MS = 3e4;
1046
1068
  function invalid() {
1047
1069
  throw new AwikiSdkError("remote");
1048
1070
  }
1049
- function record(value) {
1071
+ function record$1(value) {
1050
1072
  if (typeof value !== "object" || value === null || Array.isArray(value)) invalid();
1051
1073
  return value;
1052
1074
  }
@@ -1189,10 +1211,10 @@ var AwikiMailAttachmentClient = class {
1189
1211
  });
1190
1212
  }, { allowAuthRetry });
1191
1213
  if (!response.ok) serviceFailure(response.status);
1192
- const envelope = record(JSON.parse(await response.text()));
1214
+ const envelope = record$1(JSON.parse(await response.text()));
1193
1215
  if (envelope.jsonrpc !== "2.0" || envelope.id !== 1) invalid();
1194
- if (envelope.error != null) serviceFailure(integer(record(envelope.error).code));
1195
- return record(envelope.result);
1216
+ if (envelope.error != null) serviceFailure(integer(record$1(envelope.error).code));
1217
+ return record$1(envelope.result);
1196
1218
  } catch (error) {
1197
1219
  if (error instanceof AwikiSdkError) throw error;
1198
1220
  if (error instanceof AwikiExternalHttpAuthError) {
@@ -2789,7 +2811,7 @@ var AwikiAgentListener = class {
2789
2811
  for (const [index, part] of parts.entries()) await this.awiki.sendText({
2790
2812
  target: {
2791
2813
  kind: "direct",
2792
- peer: conversation.peerDid
2814
+ conversationId: conversation.id
2793
2815
  },
2794
2816
  text: part,
2795
2817
  idempotencyKey: replyKey(messageId, index)
@@ -3469,10 +3491,88 @@ function legacyDocument(seed, usePreRegistryDefault) {
3469
3491
  });
3470
3492
  }
3471
3493
  //#endregion
3494
+ //#region lib/types/update-installation.js
3495
+ /** Host-only compatibility evidence for exact, tenant-recommended npm targets. */
3496
+ const IDENTITY_PACKAGE = "@agent-network-protocol/dsh-anp-identity";
3497
+ const record = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3498
+ function decodeInstallation(value) {
3499
+ if (value === void 0 || value === null) return void 0;
3500
+ if (!record(value) || !record(value.runtime_packages) || !record(value.identity) || value.identity.package_name !== "@agent-network-protocol/dsh-anp-identity" || typeof value.identity.version !== "string" || typeof value.identity.integrity !== "string" || !/^sha512-[A-Za-z0-9+/]+={0,2}$/u.test(value.identity.integrity) || typeof value.requires_identity !== "string" || value.requires_identity.length > 256 || validRange(value.requires_identity) === null) throw new Error("invalid installation requirements");
3501
+ const optional = value.optional_runtime_packages === void 0 ? {} : value.optional_runtime_packages;
3502
+ if (!record(optional)) throw new Error("invalid optional Host requirements");
3503
+ const requiredEntries = Object.entries(value.runtime_packages);
3504
+ const entries = [...requiredEntries, ...Object.entries(optional)];
3505
+ if (requiredEntries.length === 0 || entries.length > 128) throw new Error("invalid Host requirement count");
3506
+ if (Object.keys(optional).some((name) => Object.hasOwn(value.runtime_packages, name))) throw new Error("overlapping Host requirements");
3507
+ for (const [name, version] of entries) {
3508
+ if (!/^@deepseek-ai\/dsh-[a-z0-9-]+$/u.test(name) || typeof version !== "string") throw new Error("invalid Host requirement");
3509
+ assertVersion(version);
3510
+ }
3511
+ assertVersion(value.identity.version);
3512
+ if (!satisfies(value.identity.version, value.requires_identity)) throw new Error("incompatible Identity release target");
3513
+ return {
3514
+ runtime_packages: value.runtime_packages,
3515
+ requires_identity: value.requires_identity,
3516
+ ...value.optional_runtime_packages === void 0 ? {} : { optional_runtime_packages: optional },
3517
+ identity: {
3518
+ package_name: IDENTITY_PACKAGE,
3519
+ version: value.identity.version,
3520
+ integrity: value.identity.integrity
3521
+ }
3522
+ };
3523
+ }
3524
+ /** Only package manifests are read; paths and native objects never enter the public view. */
3525
+ function installedVersion(name) {
3526
+ try {
3527
+ let directory = dirname(fileURLToPath(import.meta.resolve(name)));
3528
+ for (let depth = 0; depth < 12; depth++) {
3529
+ try {
3530
+ const manifest = JSON.parse(readFileSync(join(directory, "package.json"), "utf8"));
3531
+ if (record(manifest) && manifest.name === name && typeof manifest.version === "string") {
3532
+ assertVersion(manifest.version);
3533
+ return manifest.version;
3534
+ }
3535
+ } catch {}
3536
+ const parent = dirname(directory);
3537
+ if (parent === directory) break;
3538
+ directory = parent;
3539
+ }
3540
+ } catch {}
3541
+ }
3542
+ function checkInstallation(requirements, installed) {
3543
+ if (requirements === void 0) return { blockedReason: "installation-unverified" };
3544
+ const versionOf = (name) => installed === void 0 ? installedVersion(name) : installed[name];
3545
+ for (const [name, required] of Object.entries(requirements.runtime_packages)) {
3546
+ const actual = versionOf(name);
3547
+ try {
3548
+ if (actual === void 0 || compareVersions(actual, required) !== 0) return { blockedReason: "host-incompatible" };
3549
+ } catch {
3550
+ return { blockedReason: "host-incompatible" };
3551
+ }
3552
+ }
3553
+ for (const [name, required] of Object.entries(requirements.optional_runtime_packages ?? {})) {
3554
+ const actual = versionOf(name);
3555
+ if (actual === void 0) continue;
3556
+ try {
3557
+ if (compareVersions(actual, required) !== 0) return { blockedReason: "host-incompatible" };
3558
+ } catch {
3559
+ return { blockedReason: "host-incompatible" };
3560
+ }
3561
+ }
3562
+ const identity = versionOf(IDENTITY_PACKAGE);
3563
+ try {
3564
+ if (identity === void 0 || compareVersions(identity, requirements.identity.version) < 0) return { identityTarget: `${IDENTITY_PACKAGE}@${requirements.identity.version}` };
3565
+ if (!satisfies(identity, requirements.requires_identity)) return { blockedReason: "identity-incompatible" };
3566
+ } catch {
3567
+ return { blockedReason: "identity-incompatible" };
3568
+ }
3569
+ return {};
3570
+ }
3571
+ //#endregion
3472
3572
  //#region lib/types/package-version.generated.js
3473
3573
  /** Generated from package.json by scripts/sync-package-versions.mjs. */
3474
- const DSH_AWIKI_PACKAGE_VERSION = "0.3.11";
3475
- const DSH_AWIKI_MODEL_PROXY_PACKAGE_VERSION = "0.1.7";
3574
+ const DSH_AWIKI_PACKAGE_VERSION = "0.3.16";
3575
+ const DSH_AWIKI_MODEL_PROXY_PACKAGE_VERSION = "0.1.11";
3476
3576
  //#endregion
3477
3577
  //#region lib/types/update-policy.js
3478
3578
  /** Tenant-scoped DSH AWiki plugin update policy and verified cache. */
@@ -3500,7 +3600,7 @@ function readAwikiUpdatePolicyStatus(options) {
3500
3600
  policyUnavailable: true,
3501
3601
  restricted: false,
3502
3602
  modelProxyRestricted: false
3503
- } : statusFromPolicy(base, cached.policy, cached.checkedAt, false, true);
3603
+ } : statusFromPolicy(base, cached.policy, cached.checkedAt, false, true, options.installedRuntime);
3504
3604
  }
3505
3605
  async function checkAwikiUpdatePolicy(options) {
3506
3606
  const origin = new URL(options.tenant.backendBaseUrl).origin;
@@ -3586,11 +3686,11 @@ async function checkAwikiUpdatePolicy(options) {
3586
3686
  policy
3587
3687
  });
3588
3688
  } catch {}
3589
- return statusFromPolicy(base, policy, checkedAt, false, false);
3689
+ return statusFromPolicy(base, policy, checkedAt, false, false, options.installedRuntime);
3590
3690
  } catch (error) {
3591
3691
  if (options.signal?.aborted === true) throw error;
3592
3692
  if (cached !== void 0) return {
3593
- ...statusFromPolicy(base, cached.policy, cached.checkedAt, true, true),
3693
+ ...statusFromPolicy(base, cached.policy, cached.checkedAt, true, true, options.installedRuntime),
3594
3694
  checkState: "failed"
3595
3695
  };
3596
3696
  return {
@@ -3627,16 +3727,18 @@ async function readPolicyBody(response) {
3627
3727
  }
3628
3728
  return Buffer.concat(chunks, length);
3629
3729
  }
3630
- function statusFromPolicy(base, policy, checkedAt, offline, usedCache) {
3730
+ function statusFromPolicy(base, policy, checkedAt, offline, usedCache, installedRuntime) {
3631
3731
  const plugin = policy.packages.plugin;
3632
3732
  const modelProxy = policy.packages.model_proxy;
3633
3733
  const updateAvailable = compareVersions(base.currentPluginVersion, plugin.recommended_version) < 0 || modelProxy !== void 0 && base.currentModelProxyVersion !== void 0 && compareVersions(base.currentModelProxyVersion, modelProxy.recommended_version) < 0;
3634
- const command = upgradeCommand(base, plugin, modelProxy);
3734
+ const installation = checkInstallation(policy.installation, installedRuntime);
3735
+ const command = installation.blockedReason === void 0 ? upgradeCommand(base, plugin, modelProxy, installation.identityTarget) : void 0;
3635
3736
  return {
3636
3737
  ...base,
3637
3738
  checkState: "ready",
3638
3739
  updateAvailable,
3639
3740
  ...command === void 0 ? {} : { upgradeCommand: command },
3741
+ ...!updateAvailable || installation.blockedReason === void 0 ? {} : { upgradeBlockedReason: installation.blockedReason },
3640
3742
  policyRevision: policy.policy_revision,
3641
3743
  recommendedPluginVersion: plugin.recommended_version,
3642
3744
  minimumPluginVersion: plugin.min_supported_version,
@@ -3656,14 +3758,18 @@ function statusFromPolicy(base, policy, checkedAt, offline, usedCache) {
3656
3758
  };
3657
3759
  }
3658
3760
  /** Exact published targets only; never downgrade a newer installed component. */
3659
- function upgradeCommand(current, plugin, proxy) {
3761
+ function upgradeCommand(current, plugin, proxy, identityTarget) {
3660
3762
  const pluginUpgrade = compareVersions(current.currentPluginVersion, plugin.recommended_version) < 0;
3661
3763
  const proxyUpgrade = proxy !== void 0 && current.currentModelProxyVersion !== void 0 && compareVersions(current.currentModelProxyVersion, proxy.recommended_version) < 0;
3662
3764
  if (!pluginUpgrade && !proxyUpgrade || plugin.installable === false || current.currentModelProxyVersion !== void 0 && proxy?.installable === false) return void 0;
3663
3765
  if (current.currentModelProxyVersion !== void 0 && (proxy === void 0 || compareVersions(current.currentModelProxyVersion, proxy.recommended_version) > 0)) return void 0;
3664
3766
  const pluginVersion = pluginUpgrade ? plugin.recommended_version : current.currentPluginVersion;
3665
- if (current.currentModelProxyVersion !== void 0 && proxy?.requires_plugin !== void 0 && !satisfies(pluginVersion, proxy.requires_plugin, { includePrerelease: true })) return void 0;
3666
- return `dsh plugin add ${[pluginUpgrade ? `@awiki/dsh-plugin@${pluginVersion}` : void 0, proxyUpgrade ? `@awiki/dsh-model-proxy@${proxy.recommended_version}` : void 0].filter(Boolean).join(" ")}`;
3767
+ if (current.currentModelProxyVersion !== void 0 && proxy?.requires_plugin !== void 0 && !satisfies(pluginVersion, proxy.requires_plugin)) return void 0;
3768
+ return `dsh plugin add ${[
3769
+ identityTarget,
3770
+ pluginUpgrade ? `@awiki/dsh-plugin@${pluginVersion}` : void 0,
3771
+ proxyUpgrade ? `@awiki/dsh-model-proxy@${proxy.recommended_version}` : void 0
3772
+ ].filter(Boolean).join(" ")}`;
3667
3773
  }
3668
3774
  function publicTarget(value) {
3669
3775
  return {
@@ -3680,6 +3786,7 @@ function decodePolicy(value, origin) {
3680
3786
  const releaseNotes = value.release_notes_url === "" ? void 0 : new URL(value.release_notes_url);
3681
3787
  if (releaseNotes !== void 0) assertPolicyOrigin(releaseNotes.origin, origin.startsWith("http://"));
3682
3788
  const plugin = decodePackage(value.packages.plugin, "@awiki/dsh-plugin");
3789
+ const installation = decodeInstallation(value.installation);
3683
3790
  const modelProxy = value.packages.model_proxy === void 0 ? void 0 : decodePackage(value.packages.model_proxy, "@awiki/dsh-model-proxy");
3684
3791
  if (compareVersions(plugin.min_supported_version, plugin.recommended_version) > 0 || modelProxy !== void 0 && compareVersions(modelProxy.min_supported_version, modelProxy.recommended_version) > 0) throw new Error("minimum version exceeds recommended version");
3685
3792
  return {
@@ -3689,6 +3796,7 @@ function decodePolicy(value, origin) {
3689
3796
  policy_revision: value.policy_revision,
3690
3797
  published_at: value.published_at,
3691
3798
  release_notes_url: releaseNotes?.toString() ?? "",
3799
+ ...installation === void 0 ? {} : { installation },
3692
3800
  packages: {
3693
3801
  plugin,
3694
3802
  ...modelProxy === void 0 ? {} : { model_proxy: modelProxy }
@@ -3736,6 +3844,7 @@ function decodeServerInfoPolicy(value, origin, minimumRevision = 0) {
3736
3844
  policy_revision: releases.policy_revision,
3737
3845
  published_at: releases.published_at,
3738
3846
  release_notes_url: product.release_notes_url,
3847
+ installation: product.installation,
3739
3848
  packages: {
3740
3849
  plugin,
3741
3850
  ...modelProxy === void 0 ? {} : { model_proxy: modelProxy }
@@ -3870,6 +3979,7 @@ const Config = z.object({
3870
3979
  messageServiceDid: z.string().default(DEFAULT_AWIKI_MESSAGE_SERVICE_DID),
3871
3980
  allowedAttachmentOrigins: z.array(z.string()).default([]),
3872
3981
  allowInsecureLoopbackForTesting: z.boolean().default(false),
3982
+ caBundle: z.string(),
3873
3983
  stateRoot: z.string(),
3874
3984
  attachmentMaxBytes: z.number().default(DEFAULT_ATTACHMENT_MAX_BYTES),
3875
3985
  mailAttachmentMaxCount: z.number().default(10),
@@ -4089,6 +4199,7 @@ function resolveConfig(ctx, config) {
4089
4199
  messageServiceDid: serviceDid(config.messageServiceDid ?? DEFAULT_AWIKI_MESSAGE_SERVICE_DID),
4090
4200
  allowedAttachmentOrigins: attachmentOrigins(config.allowedAttachmentOrigins, messageServicePublicUrl, allowInsecureLoopbackForTesting),
4091
4201
  allowInsecureLoopbackForTesting,
4202
+ ...config.caBundle === void 0 ? {} : { caBundle: config.caBundle },
4092
4203
  stateRoot,
4093
4204
  attachmentMaxBytes,
4094
4205
  mailAttachmentMaxCount,
@@ -4224,12 +4335,7 @@ function normalizeRecoveryPrepareRequest(request) {
4224
4335
  };
4225
4336
  }
4226
4337
  const IDENTITY_ACCESS_RESPONSE_MAX_BYTES = 65536;
4227
- const DID_URI = /^did:[a-z0-9]+:(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})+(?::(?:[A-Za-z0-9._-]|%[0-9A-Fa-f]{2})+)*$/u;
4228
- const DID_URI_MAX_CHARACTERS = 2048;
4229
4338
  const SERVER_INFO_RESPONSE_MAX_BYTES = 65536;
4230
- function isBoundedDidUri(value) {
4231
- return typeof value === "string" && value.length <= DID_URI_MAX_CHARACTERS && DID_URI.test(value);
4232
- }
4233
4339
  /** Read one untrusted discovery response without buffering beyond the fixed Host limit. */
4234
4340
  async function readBoundedResponseText(response, maxBytes) {
4235
4341
  const declaredLength = response.headers.get("content-length");
@@ -4279,15 +4385,13 @@ function identityAccessTarget(request, configuredDomain) {
4279
4385
  };
4280
4386
  }
4281
4387
  function normalizeSendTextRequest(request) {
4282
- if (typeof request?.text !== "string" || typeof request.idempotencyKey !== "string" || typeof request.target !== "object" || request.target === null) return void 0;
4388
+ if (typeof request?.text !== "string" || typeof request.idempotencyKey !== "string" || !isMessageTarget(request.target)) return void 0;
4283
4389
  const text = request.text;
4284
4390
  const codePoints = Array.from(text);
4285
4391
  if (text.trim().length === 0 || codePoints.length > MAX_MESSAGE_CHARACTERS || request.idempotencyKey.length === 0 || request.idempotencyKey.length > 512) return void 0;
4286
4392
  if (request.target.kind === "direct") {
4287
- if (typeof request.target.peer !== "string" || request.mentions !== void 0) return void 0;
4288
- } else if (request.target.kind === "group") {
4289
- if (typeof request.target.group !== "string") return void 0;
4290
- } else return void 0;
4393
+ if (request.mentions !== void 0) return void 0;
4394
+ }
4291
4395
  if (request.mentions === void 0) return request;
4292
4396
  if (!Array.isArray(request.mentions) || request.mentions.length === 0 || request.mentions.length > 100) return void 0;
4293
4397
  const ids = /* @__PURE__ */ new Set();
@@ -4459,8 +4563,12 @@ let AwikiService = (() => {
4459
4563
  let _getDeviceJoinStatus_decorators;
4460
4564
  let _cancelDeviceJoin_decorators;
4461
4565
  let _refreshDeviceManagement_decorators;
4566
+ let _getIdentityServices_decorators;
4567
+ let _updateIdentityServices_decorators;
4568
+ let _resumeIdentityServicesUpdate_decorators;
4462
4569
  let _startDeviceJoinVerification_decorators;
4463
4570
  let _approveDeviceJoin_decorators;
4571
+ let _retryDeviceManagement_decorators;
4464
4572
  let _rejectDeviceJoin_decorators;
4465
4573
  let _revokeDevice_decorators;
4466
4574
  let _prepareRootTransfer_decorators;
@@ -4524,8 +4632,12 @@ let AwikiService = (() => {
4524
4632
  _getDeviceJoinStatus_decorators = [Remote];
4525
4633
  _cancelDeviceJoin_decorators = [Remote];
4526
4634
  _refreshDeviceManagement_decorators = [Remote];
4635
+ _getIdentityServices_decorators = [Remote];
4636
+ _updateIdentityServices_decorators = [Remote];
4637
+ _resumeIdentityServicesUpdate_decorators = [Remote];
4527
4638
  _startDeviceJoinVerification_decorators = [Remote];
4528
4639
  _approveDeviceJoin_decorators = [Remote];
4640
+ _retryDeviceManagement_decorators = [Remote];
4529
4641
  _rejectDeviceJoin_decorators = [Remote];
4530
4642
  _revokeDevice_decorators = [Remote];
4531
4643
  _prepareRootTransfer_decorators = [Remote];
@@ -4786,6 +4898,39 @@ let AwikiService = (() => {
4786
4898
  },
4787
4899
  metadata: _metadata
4788
4900
  }, null, _instanceExtraInitializers);
4901
+ __esDecorate(this, null, _getIdentityServices_decorators, {
4902
+ kind: "method",
4903
+ name: "getIdentityServices",
4904
+ static: false,
4905
+ private: false,
4906
+ access: {
4907
+ has: (obj) => "getIdentityServices" in obj,
4908
+ get: (obj) => obj.getIdentityServices
4909
+ },
4910
+ metadata: _metadata
4911
+ }, null, _instanceExtraInitializers);
4912
+ __esDecorate(this, null, _updateIdentityServices_decorators, {
4913
+ kind: "method",
4914
+ name: "updateIdentityServices",
4915
+ static: false,
4916
+ private: false,
4917
+ access: {
4918
+ has: (obj) => "updateIdentityServices" in obj,
4919
+ get: (obj) => obj.updateIdentityServices
4920
+ },
4921
+ metadata: _metadata
4922
+ }, null, _instanceExtraInitializers);
4923
+ __esDecorate(this, null, _resumeIdentityServicesUpdate_decorators, {
4924
+ kind: "method",
4925
+ name: "resumeIdentityServicesUpdate",
4926
+ static: false,
4927
+ private: false,
4928
+ access: {
4929
+ has: (obj) => "resumeIdentityServicesUpdate" in obj,
4930
+ get: (obj) => obj.resumeIdentityServicesUpdate
4931
+ },
4932
+ metadata: _metadata
4933
+ }, null, _instanceExtraInitializers);
4789
4934
  __esDecorate(this, null, _startDeviceJoinVerification_decorators, {
4790
4935
  kind: "method",
4791
4936
  name: "startDeviceJoinVerification",
@@ -4808,6 +4953,17 @@ let AwikiService = (() => {
4808
4953
  },
4809
4954
  metadata: _metadata
4810
4955
  }, null, _instanceExtraInitializers);
4956
+ __esDecorate(this, null, _retryDeviceManagement_decorators, {
4957
+ kind: "method",
4958
+ name: "retryDeviceManagement",
4959
+ static: false,
4960
+ private: false,
4961
+ access: {
4962
+ has: (obj) => "retryDeviceManagement" in obj,
4963
+ get: (obj) => obj.retryDeviceManagement
4964
+ },
4965
+ metadata: _metadata
4966
+ }, null, _instanceExtraInitializers);
4811
4967
  __esDecorate(this, null, _rejectDeviceJoin_decorators, {
4812
4968
  kind: "method",
4813
4969
  name: "rejectDeviceJoin",
@@ -5428,6 +5584,7 @@ let AwikiService = (() => {
5428
5584
  allowedAttachmentOrigins: tenant.storageLayout === "legacy-base" ? this.resolved.allowedAttachmentOrigins : [messageServicePublicOrigin],
5429
5585
  attachmentMaxBytes: this.resolved.attachmentMaxBytes,
5430
5586
  allowInsecureLoopbackForTesting: this.resolved.allowInsecureLoopbackForTesting,
5587
+ ...this.resolved.caBundle === void 0 ? {} : { caBundle: this.resolved.caBundle },
5431
5588
  stateRoot: this.ensureTenantRegistry().stateRoot(tenant)
5432
5589
  };
5433
5590
  }
@@ -5477,7 +5634,10 @@ let AwikiService = (() => {
5477
5634
  }
5478
5635
  /** Read the Host-owned catalog for trusted loopback settings surfaces. */
5479
5636
  getTenantRegistryView() {
5480
- return this.ensureTenantRegistry().snapshot(this.tenantSwitching);
5637
+ return {
5638
+ ...this.ensureTenantRegistry().snapshot(this.tenantSwitching),
5639
+ generation: this.runtimeGeneration
5640
+ };
5481
5641
  }
5482
5642
  /** Browser-safe, same-process update state for Desktop and loopback settings. */
5483
5643
  getUpdatePolicyStatus() {
@@ -5491,15 +5651,27 @@ let AwikiService = (() => {
5491
5651
  ...modelProxyVersion === void 0 ? {} : { currentModelProxyVersion: modelProxyVersion }
5492
5652
  });
5493
5653
  }
5494
- /** Only the Desktop provider owns its release source; tenants cannot override it. */
5654
+ /** Project only the active tenant's Desktop state; retain legacy detection without its fixed source. */
5495
5655
  getDesktopUpdate() {
5496
- return decodeDesktopDistribution(this.desktopDistribution?.getSnapshot());
5656
+ const view = decodeDesktopDistribution(this.desktopDistribution?.getSnapshot());
5657
+ if (view === void 0 || view.schemaVersion === 1) return view;
5658
+ const tenant = this.activeTenant ?? this.ensureTenantRegistry().active();
5659
+ if (this.tenantSwitching || view.tenantId !== tenant.tenantId || view.policyOrigin !== new URL(tenant.backendBaseUrl).origin || view.tenantGeneration !== this.runtimeGeneration) return {
5660
+ schemaVersion: 2,
5661
+ distributionId: "awiki-dsh-desktop",
5662
+ currentVersion: view.currentVersion,
5663
+ channel: view.channel,
5664
+ state: "unavailable",
5665
+ updateAvailable: false,
5666
+ usedCache: false
5667
+ };
5668
+ return view;
5497
5669
  }
5498
5670
  async refreshDesktopUpdate() {
5499
5671
  const provider = this.desktopDistribution;
5500
5672
  if (provider === void 0) return void 0;
5501
- const value = await provider.check();
5502
- return provider === this.desktopDistribution ? decodeDesktopDistribution(value) : this.getDesktopUpdate();
5673
+ await provider.check();
5674
+ return this.getDesktopUpdate();
5503
5675
  }
5504
5676
  /** Refresh only the active generation; late results from old tenants are discarded. */
5505
5677
  refreshUpdatePolicy() {
@@ -5931,70 +6103,87 @@ let AwikiService = (() => {
5931
6103
  ok: false,
5932
6104
  error: failure("invalid-request")
5933
6105
  };
5934
- const endpoint = new URL(`/.well-known/handle/${encodeURIComponent(target.localPart)}`, options.userServiceUrl);
6106
+ if (request.inviteCode !== void 0 && (typeof request.inviteCode !== "string" || request.inviteCode.length > 512) || request.phone !== void 0 && (typeof request.phone !== "string" || request.phone.length > 64)) return {
6107
+ ok: false,
6108
+ error: failure("invalid-request")
6109
+ };
6110
+ const endpoint = new URL("/user-service/v1/handle/rpc", options.userServiceUrl);
5935
6111
  const abort = new AbortController();
5936
6112
  const timeout = setTimeout(() => {
5937
6113
  abort.abort();
5938
6114
  }, 1e4);
5939
- let response;
5940
6115
  try {
5941
- response = await fetch(endpoint, {
5942
- method: "GET",
6116
+ const response = await fetch(endpoint, {
6117
+ method: "POST",
5943
6118
  headers: {
5944
6119
  accept: "application/json",
6120
+ "content-type": "application/json",
5945
6121
  "cache-control": "no-store"
5946
6122
  },
6123
+ body: JSON.stringify({
6124
+ jsonrpc: "2.0",
6125
+ id: "registration-check",
6126
+ method: "registration_check",
6127
+ params: {
6128
+ handle: target.localPart,
6129
+ domain: options.userServiceDomain,
6130
+ check_invite: request.inviteCode !== void 0,
6131
+ ...request.inviteCode === void 0 ? {} : { invite_code: request.inviteCode },
6132
+ ...request.phone === void 0 ? {} : { phone: request.phone }
6133
+ }
6134
+ }),
5947
6135
  cache: "no-store",
5948
6136
  redirect: "error",
5949
6137
  signal: abort.signal
5950
6138
  });
5951
- } catch {
5952
- return {
6139
+ if (!response.ok) return {
5953
6140
  ok: false,
5954
- error: failure("network")
6141
+ error: failure("remote")
5955
6142
  };
5956
- } finally {
5957
- clearTimeout(timeout);
5958
- }
5959
- if (response.status === 404) return {
5960
- ok: true,
5961
- value: {
5962
- status: "available",
5963
- fullHandle: target.fullHandle
5964
- }
5965
- };
5966
- if (!response.ok && response.status !== 410) return {
5967
- ok: false,
5968
- error: failure("remote")
5969
- };
5970
- try {
5971
6143
  const text = await readBoundedResponseText(response, IDENTITY_ACCESS_RESPONSE_MAX_BYTES);
5972
6144
  if (text === void 0) return {
5973
6145
  ok: false,
5974
6146
  error: failure("remote")
5975
6147
  };
5976
- const value = JSON.parse(text);
5977
- if (typeof value !== "object" || value === null) return {
5978
- ok: false,
5979
- error: failure("remote")
5980
- };
5981
- const binding = value;
5982
- if (binding.handle !== target.fullHandle || !isBoundedDidUri(binding.did) || typeof binding.status !== "string" || binding.status.length === 0 || response.status === 410 && binding.status !== "revoked") return {
6148
+ let envelope;
6149
+ try {
6150
+ envelope = JSON.parse(text);
6151
+ } catch {
6152
+ return {
6153
+ ok: false,
6154
+ error: failure("remote")
6155
+ };
6156
+ }
6157
+ const value = envelope?.result;
6158
+ if (envelope?.jsonrpc !== "2.0" || envelope.id !== "registration-check" || envelope.error !== void 0 && envelope.error !== null || value?.full_handle !== target.fullHandle || ![
6159
+ "register",
6160
+ "existing",
6161
+ "unavailable"
6162
+ ].includes(value.decision) || typeof value.invite_required !== "boolean" || ![
6163
+ "not_required",
6164
+ "required",
6165
+ "valid",
6166
+ "invalid"
6167
+ ].includes(value.invite_status) || value.decision === "existing" && value.invite_required || !value.invite_required && value.invite_status !== "not_required" || value.invite_required && value.invite_status === "not_required") return {
5983
6168
  ok: false,
5984
6169
  error: failure("remote")
5985
6170
  };
5986
6171
  return {
5987
6172
  ok: true,
5988
6173
  value: {
5989
- status: "existing",
5990
- fullHandle: target.fullHandle
6174
+ status: value.decision === "register" ? "available" : value.decision,
6175
+ fullHandle: target.fullHandle,
6176
+ inviteRequired: value.invite_required,
6177
+ inviteStatus: value.invite_status
5991
6178
  }
5992
6179
  };
5993
6180
  } catch {
5994
6181
  return {
5995
6182
  ok: false,
5996
- error: failure("remote")
6183
+ error: failure("network")
5997
6184
  };
6185
+ } finally {
6186
+ clearTimeout(timeout);
5998
6187
  }
5999
6188
  }
6000
6189
  async sendRegistrationOtp(request) {
@@ -6036,7 +6225,8 @@ let AwikiService = (() => {
6036
6225
  continuationId: result.value.continuationId,
6037
6226
  fullHandle: result.value.fullHandle,
6038
6227
  mode: result.value.mode,
6039
- requiresUserPresence: result.value.requiresUserPresence
6228
+ requiresUserPresence: result.value.requiresUserPresence,
6229
+ ...result.value.methodCapabilities === void 0 ? {} : { methodCapabilities: result.value.methodCapabilities }
6040
6230
  };
6041
6231
  return {
6042
6232
  ok: true,
@@ -6044,7 +6234,8 @@ let AwikiService = (() => {
6044
6234
  status: "join-required",
6045
6235
  fullHandle: result.value.fullHandle,
6046
6236
  mode: result.value.mode,
6047
- requiresUserPresence: result.value.requiresUserPresence
6237
+ requiresUserPresence: result.value.requiresUserPresence,
6238
+ ...result.value.methodCapabilities === void 0 ? {} : { methodCapabilities: result.value.methodCapabilities }
6048
6239
  }
6049
6240
  };
6050
6241
  }
@@ -6119,6 +6310,10 @@ let AwikiService = (() => {
6119
6310
  return this.run(async (client) => {
6120
6311
  const joining = await this.selectDeviceJoinSession(client) !== null;
6121
6312
  const recoveries = await client.listPendingRecoveries();
6313
+ const pendingRegistrations = await client.pendingIdentityRegistrations();
6314
+ const creationMethods = await client.identityCreationMethods().catch(() => []);
6315
+ const identity = await client.getIdentity();
6316
+ const capabilities = identity === null ? void 0 : await client.identityMethodCapabilities(identity.did);
6122
6317
  const domain = this.activeClientOptions?.userServiceDomain;
6123
6318
  const choice = this.pendingDeviceJoin;
6124
6319
  return {
@@ -6126,9 +6321,13 @@ let AwikiService = (() => {
6126
6321
  status: "join-required",
6127
6322
  fullHandle: choice.fullHandle,
6128
6323
  mode: choice.mode,
6129
- requiresUserPresence: choice.requiresUserPresence
6324
+ requiresUserPresence: choice.requiresUserPresence,
6325
+ ...choice.methodCapabilities === void 0 ? {} : { methodCapabilities: choice.methodCapabilities }
6130
6326
  },
6131
6327
  joining,
6328
+ creationMethods,
6329
+ pendingRegistrations,
6330
+ ...capabilities === void 0 ? {} : { methodCapabilities: capabilities },
6132
6331
  recoveries: recoveries.filter((value) => domain !== void 0 && value.fullHandle.slice(value.fullHandle.indexOf(".") + 1) === domain)
6133
6332
  };
6134
6333
  }, { allowSignedOut: true });
@@ -6165,6 +6364,48 @@ let AwikiService = (() => {
6165
6364
  refreshDeviceManagement() {
6166
6365
  return this.run((client) => this.deviceManagementSnapshot(client));
6167
6366
  }
6367
+ getIdentityServices() {
6368
+ return this.run((client) => client.getIdentityServices());
6369
+ }
6370
+ updateIdentityServices(request) {
6371
+ if (request === null || !Array.isArray(request.services)) return Promise.resolve({
6372
+ ok: false,
6373
+ error: failure("invalid-request")
6374
+ });
6375
+ return this.run(async (client) => {
6376
+ const did = await this.requireIdentityServicesManager(client);
6377
+ if (request.did !== did) throw Object.assign(/* @__PURE__ */ new Error("identity changed"), {
6378
+ name: "AwikiSdkError",
6379
+ code: "conflict"
6380
+ });
6381
+ if ((await client.getIdentityServices()).pending) throw Object.assign(/* @__PURE__ */ new Error("pending service update"), {
6382
+ name: "AwikiSdkError",
6383
+ code: "conflict"
6384
+ });
6385
+ await client.updateIdentityServices(request);
6386
+ return client.getIdentityServices();
6387
+ });
6388
+ }
6389
+ resumeIdentityServicesUpdate(request) {
6390
+ return this.run(async (client) => {
6391
+ const did = await this.requireIdentityServicesManager(client);
6392
+ if (request?.did !== did) throw Object.assign(/* @__PURE__ */ new Error("identity changed"), {
6393
+ name: "AwikiSdkError",
6394
+ code: "conflict"
6395
+ });
6396
+ await client.resumeIdentityServicesUpdate();
6397
+ return client.getIdentityServices();
6398
+ });
6399
+ }
6400
+ async requireIdentityServicesManager(client) {
6401
+ await this.requireDeviceManager(client);
6402
+ const identity = await client.getIdentity();
6403
+ if (identity === null || !(await client.identityMethodCapabilities(identity.did)).servicesUpdate) throw Object.assign(/* @__PURE__ */ new Error("service update unavailable"), {
6404
+ name: "AwikiSdkError",
6405
+ code: "forbidden"
6406
+ });
6407
+ return identity.did;
6408
+ }
6168
6409
  async startDeviceJoinVerification(request) {
6169
6410
  const joinSessionId = this.requestSessions.get(request?.requestRef);
6170
6411
  if (joinSessionId === void 0) return {
@@ -6240,6 +6481,23 @@ let AwikiService = (() => {
6240
6481
  });
6241
6482
  return result.ok ? this.publicAdminJoinProgress(request.requestRef, result.value) : result;
6242
6483
  }
6484
+ async retryDeviceManagement(request) {
6485
+ const deviceId = this.deviceIds.get(request?.deviceRef);
6486
+ if (deviceId === void 0) return {
6487
+ ok: false,
6488
+ error: failure("invalid-request")
6489
+ };
6490
+ return this.run(async (client) => {
6491
+ await this.requireDeviceManager(client);
6492
+ const task = (await client.deviceJoinManagementStatus()).find((value) => value.recipientDeviceId === deviceId);
6493
+ if (task === void 0 || task.phase !== "failed" || ["root_transfer.delivery_expired", "root_transfer.delivery_invalidated"].includes(task.failureCode ?? "")) throw Object.assign(/* @__PURE__ */ new Error("management retry unavailable"), {
6494
+ name: "AwikiSdkError",
6495
+ code: "forbidden"
6496
+ });
6497
+ await client.retryDeviceJoinManagement(task.joinSessionId);
6498
+ return null;
6499
+ });
6500
+ }
6243
6501
  async rejectDeviceJoin(request) {
6244
6502
  const joinSessionId = this.requestSessions.get(request?.requestRef);
6245
6503
  if (joinSessionId === void 0 || request.reason !== "user_rejected" && request.reason !== "sas_mismatch") return {
@@ -6710,6 +6968,10 @@ let AwikiService = (() => {
6710
6968
  * @returns The accepted attachment message or a closed failure.
6711
6969
  */
6712
6970
  async sendAttachment(request) {
6971
+ if (!isMessageTarget(request?.target)) return {
6972
+ ok: false,
6973
+ error: failure("invalid-request")
6974
+ };
6713
6975
  const decoded = decodeAttachment(request.bytesBase64, this.resolved.attachmentMaxBytes);
6714
6976
  if (!decoded.ok) return decoded;
6715
6977
  return this.run(async (client) => {
@@ -7081,6 +7343,10 @@ let AwikiService = (() => {
7081
7343
  name: "AwikiSdkError",
7082
7344
  code: "not-registered"
7083
7345
  });
7346
+ if (!(await client.identityMethodCapabilities(identity.did)).rootTransfer) throw Object.assign(/* @__PURE__ */ new Error("root transfer unavailable"), {
7347
+ name: "AwikiSdkError",
7348
+ code: "forbidden"
7349
+ });
7084
7350
  const target = (await client.getDeviceRegistry()).find((device) => device.deviceId === deviceId);
7085
7351
  if (target === void 0 || target.isCurrent || target.status !== "active" || target.role !== "member" || target.managementReady) throw Object.assign(/* @__PURE__ */ new Error("root transfer recipient unavailable"), {
7086
7352
  name: "AwikiSdkError",
@@ -7107,6 +7373,9 @@ let AwikiService = (() => {
7107
7373
  async deviceManagementSnapshot(client) {
7108
7374
  await client.syncDeviceManagement();
7109
7375
  const current = await client.getCurrentDeviceSummary();
7376
+ const currentIdentity = await client.getIdentity();
7377
+ const capabilities = currentIdentity === null ? void 0 : await client.identityMethodCapabilities(currentIdentity.did);
7378
+ const rootTransferSupported = capabilities?.rootTransfer === true && client.trustedUserPresenceSupported;
7110
7379
  if (!current.canManage || current.role !== "admin" || current.readiness !== "admin_ready") {
7111
7380
  this.requestRefs.clear();
7112
7381
  this.requestSessions.clear();
@@ -7115,7 +7384,8 @@ let AwikiService = (() => {
7115
7384
  this.rootTransfers.clear();
7116
7385
  return {
7117
7386
  canManage: false,
7118
- rootTransferSupported: client.trustedUserPresenceSupported,
7387
+ rootTransferSupported,
7388
+ ...capabilities === void 0 ? {} : { methodCapabilities: capabilities },
7119
7389
  ...current.role === void 0 ? {} : { role: current.role },
7120
7390
  readiness: current.readiness,
7121
7391
  devices: [],
@@ -7124,6 +7394,7 @@ let AwikiService = (() => {
7124
7394
  }
7125
7395
  const requests = await client.listLocalDeviceJoinRequests();
7126
7396
  await Promise.all(requests.filter((request) => request.claimedByCurrentDevice).map((request) => client.getLocalDeviceJoinVerificationProgress(request.joinSessionId).catch(() => void 0)));
7397
+ const management = await client.deviceJoinManagementStatus();
7127
7398
  const devices = await client.getDeviceRegistry();
7128
7399
  const identity = await client.getIdentity();
7129
7400
  const joinedAtByDeviceId = /* @__PURE__ */ new Map();
@@ -7134,10 +7405,22 @@ let AwikiService = (() => {
7134
7405
  const initialDeviceJoinedAt = identity === null ? void 0 : new Date(identity.registeredAt).toISOString();
7135
7406
  return {
7136
7407
  canManage: true,
7137
- rootTransferSupported: client.trustedUserPresenceSupported,
7408
+ rootTransferSupported,
7409
+ ...capabilities === void 0 ? {} : { methodCapabilities: capabilities },
7138
7410
  role: "admin",
7139
7411
  readiness: "admin_ready",
7140
- devices: devices.filter((device) => device.status === "active").map((device) => this.publicDevice(device, joinedAtByDeviceId.get(device.deviceId) ?? (device.isCurrent ? initialDeviceJoinedAt : void 0))),
7412
+ devices: devices.filter((device) => device.status === "active").map((device) => {
7413
+ const task = management.find((value) => value.recipientDeviceId === device.deviceId);
7414
+ const phase = task?.phase === "failed" ? "failed" : task?.phase === "management_registered" ? "complete" : task?.phase === "waiting_for_recipient" ? "waiting" : "pending";
7415
+ return {
7416
+ ...this.publicDevice(device, joinedAtByDeviceId.get(device.deviceId) ?? (device.isCurrent ? initialDeviceJoinedAt : void 0)),
7417
+ ...task === void 0 ? {} : { provisioning: {
7418
+ phase,
7419
+ attempts: task.attempts,
7420
+ ...["root_transfer.delivery_expired", "root_transfer.delivery_invalidated"].includes(task.failureCode ?? "") ? { requiresRejoin: true } : {}
7421
+ } }
7422
+ };
7423
+ }),
7141
7424
  requests: requests.map((request) => ({
7142
7425
  requestRef: this.requestRef(request.joinSessionId),
7143
7426
  candidateKeyFingerprint: request.candidateKeyFingerprint,