@onekeyfe/hwk-trezor-adapter 1.2.7 → 1.2.8-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5,6 +5,21 @@
5
5
 
6
6
 
7
7
 
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
8
23
 
9
24
 
10
25
 
@@ -762,10 +777,34 @@ function debugLog(...args) {
762
777
  emitLog("debug", ...args);
763
778
  }
764
779
 
780
+ // src/utils/queueKey.ts
781
+ function trezorQueueKey({
782
+ operationId,
783
+ connectId
784
+ }) {
785
+ return operationId || connectId || "";
786
+ }
787
+
765
788
  // src/adapter/TrezorAdapter.ts
766
789
  var KNOWN_HARDWARE_ERROR_CODES = new Set(
767
790
  Object.values(_hwkadaptercore.HardwareErrorCode).filter((value) => typeof value === "number")
768
791
  );
792
+ var ALL_NETWORK_COMMON_PARAM_KEYS = [
793
+ "autoInstallApp",
794
+ "passphraseState",
795
+ "useEmptyPassphrase",
796
+ "operationId",
797
+ "knownConnections",
798
+ "extra",
799
+ "allowDeviceSelection",
800
+ "supportedTransports"
801
+ ];
802
+ var ALL_NETWORK_ROUTING_PARAM_KEYS = [
803
+ "operationId",
804
+ "knownConnections",
805
+ "allowDeviceSelection",
806
+ "supportedTransports"
807
+ ];
769
808
  var TREZOR_BTC_NETWORK_COIN_MAP = {
770
809
  btc: "Bitcoin",
771
810
  bitcoin: "Bitcoin",
@@ -782,9 +821,24 @@ var TREZOR_BTC_NETWORK_COIN_MAP = {
782
821
  dash: "Dash"
783
822
  };
784
823
  var _TrezorAdapter = class _TrezorAdapter {
785
- constructor(connector) {
824
+ constructor(connector, options) {
786
825
  this.vendor = "trezor";
826
+ // The connector has no protocol-level cancel: the adapter stops waiting and
827
+ // drops the late response, the device carries on.
828
+ this.cancelCapability = "stops-waiting";
787
829
  this._emitter = new (0, _hwkadaptercore.TypedEventEmitter)();
830
+ this._operations = new (0, _hwkadaptercore.OperationRegistry)({
831
+ vendor: "trezor",
832
+ onEnded: (operation, reason) => {
833
+ this._emitter.emit(_hwkadaptercore.SDK.OPERATION_ENDED, {
834
+ type: _hwkadaptercore.SDK.OPERATION_ENDED,
835
+ payload: { operationId: operation.operationId, reason }
836
+ });
837
+ if (reason === "timeout") {
838
+ void this._releaseOperationConnection(operation).catch(() => void 0);
839
+ }
840
+ }
841
+ });
788
842
  this._devices = /* @__PURE__ */ new Map();
789
843
  this._sessions = /* @__PURE__ */ new Map();
790
844
  this._connectingPromises = /* @__PURE__ */ new Map();
@@ -793,12 +847,21 @@ var _TrezorAdapter = class _TrezorAdapter {
793
847
  this._uiRegistry = new (0, _hwkadaptercore.UiRequestRegistry)();
794
848
  this._passphraseRequestContextByConnectId = /* @__PURE__ */ new Map();
795
849
  this._verifiedPassphraseSessionsByConnectId = /* @__PURE__ */ new Map();
850
+ /** Raw connector calls that outlive an aborted caller, keyed by session. */
851
+ this._unsettledConnectorOperations = /* @__PURE__ */ new Map();
852
+ this._connectorIdleWaiters = /* @__PURE__ */ new Set();
853
+ this._resetPromise = null;
854
+ this._connectorTeardownTail = Promise.resolve();
855
+ this._pendingConnectorTeardowns = 0;
856
+ this._stateGeneration = 0;
857
+ this._knownDeviceConnections = /* @__PURE__ */ new Map();
796
858
  this._onDeviceConnect = (data) => {
797
859
  const device = this._connectorDeviceToDeviceInfo(data.device);
798
860
  this._devices.set(device.connectId, device);
799
861
  this._emitter.emit(_hwkadaptercore.DEVICE.CONNECT, { type: _hwkadaptercore.DEVICE.CONNECT, payload: device });
800
862
  };
801
863
  this._onDeviceDisconnect = (data) => {
864
+ this._operations.endByConnectionKey(data.connectId, "disconnect");
802
865
  this._sessions.delete(data.connectId);
803
866
  this._verifiedPassphraseSessionsByConnectId.delete(data.connectId);
804
867
  this._devices.delete(data.connectId);
@@ -818,25 +881,32 @@ var _TrezorAdapter = class _TrezorAdapter {
818
881
  payload: { device }
819
882
  });
820
883
  };
884
+ // The one outlet for every connector-raised UI request (PIN, passphrase,
885
+ // button, THP pairing), so the operation is stamped here instead of at each
886
+ // connector call site.
821
887
  this._onUiRequest = (data) => {
888
+ const operationId = this._activeOperationId();
889
+ const basePayload = data.payload && typeof data.payload === "object" ? data.payload : {};
890
+ const attribution = operationId === void 0 ? {} : { operationId };
822
891
  if (data.type === _hwkadaptercore.UI_REQUEST.REQUEST_PASSPHRASE) {
823
- const payload = data.payload && typeof data.payload === "object" ? data.payload : {};
824
- const connectId = typeof payload.connectId === "string" ? payload.connectId : void 0;
892
+ const connectId = typeof basePayload.connectId === "string" ? basePayload.connectId : void 0;
825
893
  const context = connectId ? this._passphraseRequestContextByConnectId.get(connectId) : void 0;
826
894
  this._emitter.emit(
827
895
  data.type,
828
896
  {
829
897
  ...data,
830
898
  payload: {
831
- ...payload,
899
+ ...basePayload,
832
900
  ..._optionalChain([context, 'optionalAccess', _13 => _13.passphraseState]) !== void 0 ? { passphraseState: context.passphraseState } : {},
833
- ..._optionalChain([context, 'optionalAccess', _14 => _14.useEmptyPassphrase]) !== void 0 ? { useEmptyPassphrase: context.useEmptyPassphrase } : {}
901
+ ..._optionalChain([context, 'optionalAccess', _14 => _14.useEmptyPassphrase]) !== void 0 ? { useEmptyPassphrase: context.useEmptyPassphrase } : {},
902
+ ...attribution
834
903
  }
835
904
  }
836
905
  );
837
906
  return;
838
907
  }
839
- this._emitter.emit(data.type, data);
908
+ const payload = operationId === void 0 ? data.payload : { ...basePayload, ...attribution };
909
+ this._emitter.emit(data.type, { ...data, payload });
840
910
  };
841
911
  this._onUiEvent = (data) => {
842
912
  this._emitter.emit("ui-event", data);
@@ -848,6 +918,11 @@ var _TrezorAdapter = class _TrezorAdapter {
848
918
  });
849
919
  };
850
920
  this._connector = connector;
921
+ for (const connection of _nullishCoalesce(_optionalChain([options, 'optionalAccess', _15 => _15.knownDeviceConnections]), () => ( []))) {
922
+ if (connection.deviceId) {
923
+ this._knownDeviceConnections.set(connection.deviceId, { ...connection });
924
+ }
925
+ }
851
926
  this._jobQueue = new (0, _hwkadaptercore.DeviceJobQueue)();
852
927
  this._registerConnectorEvents();
853
928
  }
@@ -872,29 +947,35 @@ var _TrezorAdapter = class _TrezorAdapter {
872
947
  const {
873
948
  passphraseState,
874
949
  autoInstallApp: _autoInstallApp,
950
+ operationId,
875
951
  useEmptyPassphrase,
952
+ knownConnections,
953
+ extra,
954
+ allowDeviceSelection,
955
+ supportedTransports,
876
956
  ...rest
877
957
  } = params;
878
958
  return {
879
959
  passphraseState: typeof passphraseState === "string" ? passphraseState : void 0,
880
960
  useEmptyPassphrase: typeof useEmptyPassphrase === "boolean" ? useEmptyPassphrase : void 0,
961
+ operationId: typeof operationId === "string" ? operationId : void 0,
962
+ connectionContext: {
963
+ knownConnections,
964
+ extra,
965
+ allowDeviceSelection: typeof allowDeviceSelection === "boolean" ? allowDeviceSelection : void 0,
966
+ supportedTransports
967
+ },
881
968
  rest
882
969
  };
883
970
  }
884
971
  return { rest: _nullishCoalesce(params, () => ( {})) };
885
972
  }
886
973
  static _withAllNetworkRequestCommonParams(item, commonParams) {
887
- const autoInstallApp = _optionalChain([commonParams, 'optionalAccess', _15 => _15.autoInstallApp]);
888
- const passphraseState = _optionalChain([commonParams, 'optionalAccess', _16 => _16.passphraseState]);
889
- const useEmptyPassphrase = _optionalChain([commonParams, 'optionalAccess', _17 => _17.useEmptyPassphrase]);
890
- if (autoInstallApp === void 0 && passphraseState === void 0 && useEmptyPassphrase === void 0) {
891
- return item;
892
- }
974
+ const defined = ALL_NETWORK_COMMON_PARAM_KEYS.filter((key) => _optionalChain([commonParams, 'optionalAccess', _16 => _16[key]]) !== void 0);
893
975
  return {
894
976
  ...item,
895
- ...autoInstallApp !== void 0 ? { autoInstallApp } : {},
896
- ...passphraseState !== void 0 ? { passphraseState } : {},
897
- ...useEmptyPassphrase !== void 0 ? { useEmptyPassphrase } : {}
977
+ ...Object.fromEntries(defined.map((key) => [key, _optionalChain([commonParams, 'optionalAccess', _17 => _17[key]])])),
978
+ ...Object.fromEntries(ALL_NETWORK_ROUTING_PARAM_KEYS.map((key) => [key, _optionalChain([commonParams, 'optionalAccess', _18 => _18[key]])]))
898
979
  };
899
980
  }
900
981
  static _normalizeLogKey(key) {
@@ -950,8 +1031,8 @@ var _TrezorAdapter = class _TrezorAdapter {
950
1031
  };
951
1032
  }
952
1033
  static _trezorFailureMessage(error) {
953
- const response = _optionalChain([error, 'optionalAccess', _18 => _18.response]);
954
- const message = _optionalChain([response, 'optionalAccess', _19 => _19.message]);
1034
+ const response = _optionalChain([error, 'optionalAccess', _19 => _19.response]);
1035
+ const message = _optionalChain([response, 'optionalAccess', _20 => _20.message]);
955
1036
  if (!message || typeof message !== "object") return {};
956
1037
  const failureMessage = message;
957
1038
  return {
@@ -960,7 +1041,7 @@ var _TrezorAdapter = class _TrezorAdapter {
960
1041
  };
961
1042
  }
962
1043
  static _mapTrezorFailureCode(error) {
963
- const directCode = _optionalChain([error, 'optionalAccess', _20 => _20.code]);
1044
+ const directCode = _optionalChain([error, 'optionalAccess', _21 => _21.code]);
964
1045
  if (directCode === "Device_InitializeFailed") {
965
1046
  return _hwkadaptercore.HardwareErrorCode.PinCancelled;
966
1047
  }
@@ -973,7 +1054,7 @@ var _TrezorAdapter = class _TrezorAdapter {
973
1054
  if (directCode === "Failure_UnexpectedMessage") {
974
1055
  return _hwkadaptercore.HardwareErrorCode.MethodNotSupported;
975
1056
  }
976
- const message = _optionalChain([error, 'optionalAccess', _21 => _21.message]);
1057
+ const message = _optionalChain([error, 'optionalAccess', _22 => _22.message]);
977
1058
  if (typeof message === "string" && message.includes("Failed to execute 'transfer") && message.includes("on 'USBDevice'")) {
978
1059
  return _hwkadaptercore.HardwareErrorCode.TransportError;
979
1060
  }
@@ -1018,13 +1099,11 @@ var _TrezorAdapter = class _TrezorAdapter {
1018
1099
  * selected is gone (the device evicted it from its limited slot pool). The
1019
1100
  * caller drops the stale cache entry and recreates from scratch.
1020
1101
  *
1021
- * NOTE: the exact eviction code still needs real-device confirmation — match
1022
- * conservatively so a genuine error isn't mistaken for an evicted session
1023
- * (which would silently re-prompt the passphrase).
1102
+ * Match only the protocol's explicit unallocated-channel code. Guessing from
1103
+ * an error message could replay a request after an unrelated business error.
1024
1104
  */
1025
1105
  static _isStaleSessionError(code) {
1026
- if (typeof code !== "string") return false;
1027
- return code === "ThpUnallocatedChannel" || /invalid.?session|unallocated.?session|session.?not.?found/i.test(code);
1106
+ return code === "ThpUnallocatedChannel";
1028
1107
  }
1029
1108
  static _errorCode(error) {
1030
1109
  return _nullishCoalesce(error.code, () => ( _TrezorAdapter._trezorFailureMessage(error).code));
@@ -1038,13 +1117,13 @@ var _TrezorAdapter = class _TrezorAdapter {
1038
1117
  return protocolError.name === "TrezorProtocolError" && protocolError.code === "Malformed protocol format" && protocolError.message === "Malformed protocol format";
1039
1118
  }
1040
1119
  /**
1041
- * Race a promise against an abort signal. Copied from LedgerAdapter:
1042
- * IConnector.call() can't actually be cancelled at the protocol level, but
1043
- * the caller gets the abort immediately and the underlying work completes
1044
- * uselessly in the background.
1120
+ * Race a promise against an abort signal. IConnector.call() can't actually
1121
+ * be cancelled at the protocol level, so the caller gets the abort while the
1122
+ * adapter keeps the transport exclusively reserved until the raw call drains.
1045
1123
  */
1046
1124
  static _abortable(signal, promise) {
1047
1125
  if (signal.aborted) {
1126
+ void promise.catch(() => void 0);
1048
1127
  return Promise.reject(
1049
1128
  _nullishCoalesce(signal.reason, () => ( new Error("Aborted")))
1050
1129
  );
@@ -1066,17 +1145,37 @@ var _TrezorAdapter = class _TrezorAdapter {
1066
1145
  );
1067
1146
  });
1068
1147
  }
1069
- async _connectSession(connectId, signal) {
1148
+ async _connectSession(connectId, signal, stateGeneration, transportType) {
1070
1149
  const connectOnce = async () => {
1071
- const connectPromise = this._connector.connect(connectId);
1072
- if (!signal) return connectPromise;
1150
+ if (_optionalChain([signal, 'optionalAccess', _23 => _23.aborted])) {
1151
+ throw _nullishCoalesce(signal.reason, () => ( new Error("Aborted")));
1152
+ }
1153
+ this._assertConnectorIdle("connectDevice");
1154
+ const releaseOperation = this._retainConnectorOperation(`connect:${connectId}`);
1155
+ let connectPromise;
1073
1156
  try {
1074
- return await _TrezorAdapter._abortable(signal, connectPromise);
1157
+ connectPromise = transportType ? this._connector.connect(connectId, { transportType }) : this._connector.connect(connectId);
1075
1158
  } catch (error) {
1076
- void connectPromise.then(
1077
- (orphan) => this._connector.disconnect(orphan.sessionId).catch(() => void 0),
1078
- () => void 0
1079
- );
1159
+ releaseOperation();
1160
+ throw error;
1161
+ }
1162
+ if (!signal) {
1163
+ try {
1164
+ return await connectPromise;
1165
+ } finally {
1166
+ releaseOperation();
1167
+ }
1168
+ }
1169
+ try {
1170
+ const session2 = await _TrezorAdapter._abortable(signal, connectPromise);
1171
+ releaseOperation();
1172
+ return session2;
1173
+ } catch (error) {
1174
+ if (signal.aborted) {
1175
+ void connectPromise.then((orphan) => this._connector.disconnect(orphan.sessionId).catch(() => void 0)).catch(() => void 0).finally(releaseOperation);
1176
+ } else {
1177
+ releaseOperation();
1178
+ }
1080
1179
  throw error;
1081
1180
  }
1082
1181
  };
@@ -1084,7 +1183,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1084
1183
  try {
1085
1184
  session = await connectOnce();
1086
1185
  } catch (error) {
1087
- if (_optionalChain([signal, 'optionalAccess', _22 => _22.aborted]) || !_TrezorAdapter._isRecoverableProtocolResidueError(error)) {
1186
+ if (_optionalChain([signal, 'optionalAccess', _24 => _24.aborted]) || !_TrezorAdapter._isRecoverableProtocolResidueError(error)) {
1088
1187
  throw error;
1089
1188
  }
1090
1189
  debugLog("[TrezorAdapter] retrying connect after draining a stale THP frame", {
@@ -1092,8 +1191,13 @@ var _TrezorAdapter = class _TrezorAdapter {
1092
1191
  });
1093
1192
  session = await connectOnce();
1094
1193
  }
1095
- if (this._disconnectRequested.delete(connectId)) {
1096
- await this._connector.disconnect(session.sessionId).catch(() => void 0);
1194
+ if (_optionalChain([signal, 'optionalAccess', _25 => _25.aborted]) || stateGeneration !== this._stateGeneration || this._disconnectRequested.delete(connectId)) {
1195
+ const releaseCleanup = this._retainConnectorOperation(`disconnect:${session.sessionId}`);
1196
+ try {
1197
+ await this._connector.disconnect(session.sessionId).catch(() => void 0);
1198
+ } finally {
1199
+ releaseCleanup();
1200
+ }
1097
1201
  throw Object.assign(new Error("Trezor connection aborted"), {
1098
1202
  code: _hwkadaptercore.HardwareErrorCode.UserAborted
1099
1203
  });
@@ -1109,14 +1213,9 @@ var _TrezorAdapter = class _TrezorAdapter {
1109
1213
  async init(_config) {
1110
1214
  }
1111
1215
  async dispose() {
1216
+ await this._resetStateAndDisconnectSessions();
1112
1217
  this._unregisterConnectorEvents();
1113
- this._uiRegistry.reset();
1114
- this._jobQueue.clear();
1115
1218
  this._connector.reset();
1116
- this._devices.clear();
1117
- this._sessions.clear();
1118
- this._verifiedPassphraseSessionsByConnectId.clear();
1119
- this._connectingPromises.clear();
1120
1219
  this._emitter.removeAllListeners();
1121
1220
  }
1122
1221
  /**
@@ -1125,12 +1224,30 @@ var _TrezorAdapter = class _TrezorAdapter {
1125
1224
  * error flows. Aborts active jobs and releases UI waits.
1126
1225
  */
1127
1226
  resetState() {
1227
+ void this._resetStateAndDisconnectSessions();
1228
+ }
1229
+ _resetStateAndDisconnectSessions() {
1230
+ if (this._resetPromise) return this._resetPromise;
1231
+ const sessionIds = new Set(this._sessions.values());
1232
+ this._stateGeneration += 1;
1233
+ this._operations.endAll("runtime-reset");
1128
1234
  this._uiRegistry.reset();
1129
1235
  this._jobQueue.clear();
1130
1236
  this._devices.clear();
1131
1237
  this._sessions.clear();
1132
1238
  this._verifiedPassphraseSessionsByConnectId.clear();
1133
1239
  this._connectingPromises.clear();
1240
+ const resetPromise = this._runConnectorTeardown(async () => {
1241
+ for (const sessionId of sessionIds) {
1242
+ await this._connector.disconnect(sessionId).catch(() => void 0);
1243
+ }
1244
+ });
1245
+ this._resetPromise = resetPromise;
1246
+ return resetPromise.finally(() => {
1247
+ if (this._resetPromise === resetPromise) {
1248
+ this._resetPromise = null;
1249
+ }
1250
+ });
1134
1251
  }
1135
1252
  getAvailableTransports() {
1136
1253
  return this.activeTransport ? [this.activeTransport] : [];
@@ -1138,32 +1255,162 @@ var _TrezorAdapter = class _TrezorAdapter {
1138
1255
  async switchTransport(_type) {
1139
1256
  }
1140
1257
  async searchDevices(options) {
1141
- await this._ensureDevicePermission();
1258
+ await this._ensureDevicePermission(void 0, void 0, _optionalChain([options, 'optionalAccess', _26 => _26.transportType]));
1259
+ return this._searchDevices(options);
1260
+ }
1261
+ async _searchDevices(options, signal) {
1262
+ if (_optionalChain([options, 'optionalAccess', _27 => _27.transportType]) && !this._availableTransports().includes(options.transportType))
1263
+ return [];
1264
+ if (_optionalChain([options, 'optionalAccess', _28 => _28.resetSession])) {
1265
+ await this._resetStateAndDisconnectSessions();
1266
+ } else {
1267
+ await this._connectorTeardownTail;
1268
+ }
1269
+ if (_optionalChain([signal, 'optionalAccess', _29 => _29.aborted])) throw signal.reason;
1270
+ const stateGeneration = this._stateGeneration;
1142
1271
  const scanned = await this._connector.searchDevices(
1143
- _optionalChain([options, 'optionalAccess', _23 => _23.waitForAllTransports]) ? { waitForAll: true } : void 0
1272
+ _optionalChain([options, 'optionalAccess', _30 => _30.waitForAllTransports]) || _optionalChain([options, 'optionalAccess', _31 => _31.transportType]) ? {
1273
+ ...options.waitForAllTransports ? { waitForAll: true } : {},
1274
+ ...options.transportType ? { transportType: options.transportType } : {}
1275
+ } : void 0
1144
1276
  );
1277
+ if (_optionalChain([signal, 'optionalAccess', _32 => _32.aborted])) throw signal.reason;
1278
+ if (stateGeneration !== this._stateGeneration) {
1279
+ throw _hwkadaptercore.createHwkError.call(void 0, {
1280
+ code: _hwkadaptercore.HardwareErrorCode.UserAborted,
1281
+ message: "Trezor discovery belongs to an ended operation"
1282
+ });
1283
+ }
1145
1284
  const scannedIds = new Set(scanned.map((d) => d.connectId));
1146
1285
  for (const connectId of [...this._devices.keys()]) {
1147
- if (!scannedIds.has(connectId) && !this._sessions.has(connectId)) {
1286
+ if ((!_optionalChain([options, 'optionalAccess', _33 => _33.transportType]) || _optionalChain([this, 'access', _34 => _34._devices, 'access', _35 => _35.get, 'call', _36 => _36(connectId), 'optionalAccess', _37 => _37.connectionType]) === options.transportType) && !scannedIds.has(connectId) && !this._sessions.has(connectId)) {
1148
1287
  this._devices.delete(connectId);
1149
1288
  }
1150
1289
  }
1151
1290
  for (const device of scanned) {
1152
1291
  const info = this._connectorDeviceToDeviceInfo(device);
1153
- this._devices.set(info.connectId, info);
1292
+ if (!this._sessions.has(info.connectId)) this._devices.set(info.connectId, info);
1293
+ }
1294
+ return Array.from(this._devices.values()).filter(
1295
+ (device) => !_optionalChain([options, 'optionalAccess', _38 => _38.transportType]) || device.connectionType === options.transportType
1296
+ );
1297
+ }
1298
+ async searchDeviceTargets(options) {
1299
+ const devices = await this.searchDevices(options);
1300
+ return devices.map((device) => ({
1301
+ searchTargetId: device.connectId,
1302
+ searchTargetReusePolicy: _hwkadaptercore.resolveSearchTargetReusePolicy.call(void 0, device),
1303
+ vendor: "trezor",
1304
+ connectionType: device.connectionType,
1305
+ kind: "physical",
1306
+ label: device.label,
1307
+ model: device.model,
1308
+ modelName: device.modelName,
1309
+ serialNumber: device.serialNumber
1310
+ }));
1311
+ }
1312
+ async listConnectionTargets(options) {
1313
+ const targets = await this.searchDeviceTargets(options);
1314
+ return targets.map(({ searchTargetId, ...target }) => ({
1315
+ ...target,
1316
+ targetId: searchTargetId
1317
+ }));
1318
+ }
1319
+ async bindBleDevice(params) {
1320
+ if (_optionalChain([params, 'access', _39 => _39.identity, 'optionalAccess', _40 => _40.vendor]) !== "trezor" || !params.identity.value) {
1321
+ return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.InvalidParams, "Trezor device identity is required");
1322
+ }
1323
+ const { value: deviceId } = params.identity;
1324
+ try {
1325
+ return await this._jobQueue.enqueue(
1326
+ deviceId,
1327
+ async (signal) => {
1328
+ const selected = await this._selectBleDeviceForBinding(
1329
+ deviceId,
1330
+ signal,
1331
+ { extra: params.extra },
1332
+ "manual-rebind"
1333
+ );
1334
+ try {
1335
+ const persisted = await this._emitVerifiedBinding(
1336
+ deviceId,
1337
+ selected.connectId,
1338
+ selected.selectionRequestId,
1339
+ { extra: params.extra },
1340
+ signal
1341
+ );
1342
+ if (!persisted) {
1343
+ throw _hwkadaptercore.createHwkError.call(void 0, {
1344
+ code: _hwkadaptercore.HardwareErrorCode.UnknownError,
1345
+ message: "Bluetooth binding could not be saved",
1346
+ origin: "host"
1347
+ });
1348
+ }
1349
+ this._rememberVerifiedConnection(deviceId, selected.connectId);
1350
+ return _hwkadaptercore.success.call(void 0, selected.connectId);
1351
+ } catch (error) {
1352
+ await this._releaseProvisionalConnection(selected.connectId, signal);
1353
+ throw error;
1354
+ }
1355
+ },
1356
+ {
1357
+ label: "bindBleDevice",
1358
+ rejectIfBusy: true,
1359
+ busyError: _TrezorAdapter._createDeviceBusyError("bindBleDevice")
1360
+ }
1361
+ );
1362
+ } catch (error) {
1363
+ return this._errorToFailure(error);
1154
1364
  }
1155
- return Array.from(this._devices.values());
1156
1365
  }
1157
- async connectDevice(connectId) {
1366
+ async connectDevice(searchTargetId) {
1367
+ const scope = this._jobQueue.createCancelScope(searchTargetId);
1158
1368
  try {
1159
- await this._ensureDevicePermission(connectId);
1160
- await this._ensureSession(connectId);
1161
- return _hwkadaptercore.success.call(void 0, connectId);
1369
+ await _TrezorAdapter._abortable(
1370
+ scope.signal,
1371
+ this._ensureDevicePermission(
1372
+ searchTargetId,
1373
+ void 0,
1374
+ _optionalChain([this, 'access', _41 => _41._devices, 'access', _42 => _42.get, 'call', _43 => _43(searchTargetId), 'optionalAccess', _44 => _44.connectionType])
1375
+ )
1376
+ );
1377
+ await this._ensureSession(searchTargetId, scope.signal);
1378
+ if (scope.signal.aborted) throw scope.signal.reason;
1379
+ const device = this._devices.get(searchTargetId);
1380
+ if (!device) {
1381
+ return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.DeviceNotFound, "Trezor device not found after connect");
1382
+ }
1383
+ this._operations.endByConnectionKey(searchTargetId, "explicit");
1384
+ const operation = this._operations.create({
1385
+ searchTargetId,
1386
+ connectId: searchTargetId,
1387
+ device,
1388
+ connectionType: device.connectionType,
1389
+ connectionKeys: [_nullishCoalesce(this._sessions.get(searchTargetId), () => ( ""))]
1390
+ });
1391
+ return _hwkadaptercore.success.call(void 0, operation.operationId);
1162
1392
  } catch (error) {
1393
+ if (scope.signal.aborted) {
1394
+ await this._waitForConnectorOperationsToDrain();
1395
+ return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.UserAborted, "Trezor connection aborted");
1396
+ }
1163
1397
  return this._errorToFailure(error);
1398
+ } finally {
1399
+ scope.release();
1400
+ }
1401
+ }
1402
+ async releaseOperation(operationId) {
1403
+ const operation = this._operations.find(operationId);
1404
+ if (!operation) {
1405
+ this._operations.resolve(operationId);
1406
+ return;
1164
1407
  }
1408
+ const endedOperation = this._operations.end(operationId, "explicit");
1409
+ if (!endedOperation) return;
1410
+ await this._releaseOperationConnection(endedOperation);
1165
1411
  }
1166
- async disconnectDevice(connectId) {
1412
+ async _releaseOperationConnection(operation) {
1413
+ const { connectId } = operation;
1167
1414
  const connecting = this._connectingPromises.has(connectId);
1168
1415
  this._connectingPromises.delete(connectId);
1169
1416
  const sessionId = this._sessions.get(connectId);
@@ -1171,90 +1418,131 @@ var _TrezorAdapter = class _TrezorAdapter {
1171
1418
  if (connecting) this._disconnectRequested.add(connectId);
1172
1419
  return;
1173
1420
  }
1174
- this._sessions.delete(connectId);
1175
- this._verifiedPassphraseSessionsByConnectId.delete(connectId);
1176
- await this._connector.disconnect(sessionId);
1421
+ await this._disconnectSession(connectId, sessionId);
1177
1422
  }
1178
- async getDeviceInfo(connectId, deviceId) {
1423
+ async getDeviceInfo(connectIdOrOperationId, deviceId) {
1424
+ let connectId;
1425
+ try {
1426
+ connectId = _hwkadaptercore.isHardwareOperationId.call(void 0, connectIdOrOperationId) ? this._operations.resolve(connectIdOrOperationId).connectId : connectIdOrOperationId;
1427
+ } catch (error) {
1428
+ return this._errorToFailure(error);
1429
+ }
1179
1430
  const device = _nullishCoalesce(this._devices.get(connectId), () => ( Array.from(this._devices.values()).find((item) => item.deviceId === deviceId)));
1180
1431
  if (!device) return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.DeviceNotFound, "Trezor device not found");
1181
1432
  return _hwkadaptercore.success.call(void 0, device);
1182
1433
  }
1183
- getSupportedChains() {
1184
- return ["btc", "evm", "sol", "tron"];
1185
- }
1186
1434
  async allNetworkGetAddress(connectId, deviceId, params) {
1435
+ const target = _hwkadaptercore.resolveHardwareOperationTarget.call(void 0, connectId, params.operationId, "trezor");
1436
+ if (!target.success) return target;
1437
+ let effectiveTargetId = _nullishCoalesce(target.payload.targetId, () => ( ""));
1438
+ let releaseOperationRetention;
1439
+ try {
1440
+ releaseOperationRetention = target.payload.operationId ? this._operations.retain(target.payload.operationId) : void 0;
1441
+ } catch (error) {
1442
+ return this._errorToFailure(error);
1443
+ }
1444
+ const cancelScope = this._jobQueue.createCancelScope(
1445
+ trezorQueueKey({ operationId: target.payload.operationId, connectId: effectiveTargetId })
1446
+ );
1187
1447
  let liveDeviceId = "";
1448
+ const bundleContext = {};
1188
1449
  const topLevelFailureIndexes = /* @__PURE__ */ new Set();
1189
1450
  const isSingleNetworkBundle = params.bundle.every(
1190
- (item) => item.network === _optionalChain([params, 'access', _24 => _24.bundle, 'access', _25 => _25[0], 'optionalAccess', _26 => _26.network])
1451
+ (item) => item.network === _optionalChain([params, 'access', _45 => _45.bundle, 'access', _46 => _46[0], 'optionalAccess', _47 => _47.network])
1191
1452
  );
1192
- const getLiveDeviceId = async () => {
1453
+ const getLiveDeviceId = async (expectedDeviceId) => {
1193
1454
  if (liveDeviceId) return _hwkadaptercore.success.call(void 0, liveDeviceId);
1194
- const deviceIdResponse = await this._getTrezorDeviceId(connectId);
1455
+ const deviceIdResponse = await this._getTrezorDeviceId(
1456
+ effectiveTargetId,
1457
+ expectedDeviceId || deviceId,
1458
+ params,
1459
+ bundleContext
1460
+ );
1195
1461
  if (deviceIdResponse.success) {
1196
1462
  liveDeviceId = deviceIdResponse.payload;
1463
+ if (!target.payload.operationId) {
1464
+ effectiveTargetId = _nullishCoalesce(_optionalChain([bundleContext, 'access', _48 => _48.connection, 'optionalAccess', _49 => _49.connectId]), () => ( effectiveTargetId));
1465
+ }
1197
1466
  }
1198
1467
  return deviceIdResponse;
1199
1468
  };
1200
- return _hwkadaptercore.runAllNetworkGetAddress.call(void 0, {
1201
- connectId,
1202
- deviceId,
1203
- params,
1204
- normalizeItem: _TrezorAdapter._normalizeAllNetworkItem,
1205
- callItem: async ({ method, item, index }) => {
1206
- const expectedDeviceId = _nullishCoalesce(_TrezorAdapter._getItemDeviceId(item), () => ( deviceId));
1207
- if (expectedDeviceId) {
1208
- const fingerprint = await getLiveDeviceId();
1209
- if (!fingerprint.success) {
1469
+ try {
1470
+ return await _hwkadaptercore.runAllNetworkGetAddress.call(void 0, {
1471
+ connectId: effectiveTargetId,
1472
+ deviceId,
1473
+ params,
1474
+ normalizeItem: _TrezorAdapter._normalizeAllNetworkItem,
1475
+ callItem: async ({ method, item, index }) => {
1476
+ if (cancelScope.signal.aborted) {
1210
1477
  topLevelFailureIndexes.add(index);
1211
- return fingerprint;
1478
+ return _TrezorAdapter._buildCancelledFailure(cancelScope.signal);
1212
1479
  }
1213
- if (expectedDeviceId !== fingerprint.payload) {
1214
- topLevelFailureIndexes.add(index);
1215
- return _hwkadaptercore.failure.call(void 0,
1216
- _hwkadaptercore.HardwareErrorCode.DeviceMismatch,
1217
- `Wrong device: expected ${expectedDeviceId}, got ${fingerprint.payload}`,
1218
- { expected: expectedDeviceId, actual: fingerprint.payload }
1219
- );
1480
+ const expectedDeviceId = _nullishCoalesce(_TrezorAdapter._getItemDeviceId(item), () => ( deviceId));
1481
+ if (expectedDeviceId) {
1482
+ const fingerprint = await getLiveDeviceId(expectedDeviceId);
1483
+ if (!fingerprint.success) {
1484
+ topLevelFailureIndexes.add(index);
1485
+ return fingerprint;
1486
+ }
1487
+ if (expectedDeviceId !== fingerprint.payload) {
1488
+ topLevelFailureIndexes.add(index);
1489
+ return _hwkadaptercore.failure.call(void 0,
1490
+ _hwkadaptercore.HardwareErrorCode.DeviceMismatch,
1491
+ `Wrong device: expected ${expectedDeviceId}, got ${fingerprint.payload}`,
1492
+ { expected: expectedDeviceId, actual: fingerprint.payload }
1493
+ );
1494
+ }
1220
1495
  }
1221
- }
1222
- const callItem = _TrezorAdapter._withAllNetworkRequestCommonParams(item, params);
1223
- return this._callAllNetworkMethod(connectId, method, callItem);
1224
- },
1225
- attachIdentity: async ({ item, chain, payload }) => {
1226
- if (!liveDeviceId) {
1227
- const fingerprint = await getLiveDeviceId();
1228
- if (!fingerprint.success) {
1229
- return { ...item, success: false, payload: fingerprint.payload };
1496
+ const callItem = _TrezorAdapter._withAllNetworkRequestCommonParams(item, params);
1497
+ return this._callAllNetworkMethod(effectiveTargetId, method, callItem, bundleContext);
1498
+ },
1499
+ attachIdentity: async ({ item, chain, payload, index }) => {
1500
+ if (!liveDeviceId) {
1501
+ if (cancelScope.signal.aborted) {
1502
+ topLevelFailureIndexes.add(index);
1503
+ const cancelled = _TrezorAdapter._buildCancelledFailure(cancelScope.signal);
1504
+ return { ...item, success: false, payload: cancelled.payload };
1505
+ }
1506
+ const fingerprint = await getLiveDeviceId(deviceId);
1507
+ if (!fingerprint.success) {
1508
+ return { ...item, success: false, payload: fingerprint.payload };
1509
+ }
1230
1510
  }
1511
+ return {
1512
+ ...item,
1513
+ success: true,
1514
+ payload: {
1515
+ ...payload,
1516
+ deviceIdentity: {
1517
+ vendor: "trezor",
1518
+ type: "deviceId",
1519
+ value: liveDeviceId
1520
+ },
1521
+ chainFingerprint: liveDeviceId,
1522
+ chainFingerprintChain: chain
1523
+ }
1524
+ };
1525
+ },
1526
+ // One named boolean per abort reason.
1527
+ shouldAbortBundle: (response, { index }) => {
1528
+ const isSessionLevelFailure = topLevelFailureIndexes.has(index);
1529
+ const isWholeChainForbidden = isSingleNetworkBundle && _optionalChain([response, 'access', _50 => _50.payload, 'optionalAccess', _51 => _51.code]) === _hwkadaptercore.HardwareErrorCode.DevicePathForbidden;
1530
+ const { code } = _nullishCoalesce(response.payload, () => ( {}));
1531
+ return isSessionLevelFailure || isWholeChainForbidden || _hwkadaptercore.isWalletSafetyFailure.call(void 0, code) || _hwkadaptercore.isUserRefusal.call(void 0, code) || _hwkadaptercore.isConnectionLost.call(void 0, code);
1231
1532
  }
1232
- return {
1233
- ...item,
1234
- success: true,
1235
- payload: {
1236
- ...payload,
1237
- deviceIdentity: {
1238
- vendor: "trezor",
1239
- type: "deviceId",
1240
- value: liveDeviceId
1241
- },
1242
- chainFingerprint: liveDeviceId,
1243
- chainFingerprintChain: chain
1244
- }
1245
- };
1246
- },
1247
- // One named boolean per abort reason.
1248
- shouldAbortBundle: (response, { index }) => {
1249
- const isSessionLevelFailure = topLevelFailureIndexes.has(index);
1250
- const isWholeChainForbidden = isSingleNetworkBundle && _optionalChain([response, 'access', _27 => _27.payload, 'optionalAccess', _28 => _28.code]) === _hwkadaptercore.HardwareErrorCode.DevicePathForbidden;
1251
- const isPassphrasePolicyFailure = _optionalChain([response, 'access', _29 => _29.payload, 'optionalAccess', _30 => _30.code]) === _hwkadaptercore.HardwareErrorCode.PassphraseAlwaysOnDevice;
1252
- return isSessionLevelFailure || isWholeChainForbidden || isPassphrasePolicyFailure;
1253
- }
1254
- });
1533
+ });
1534
+ } finally {
1535
+ cancelScope.release();
1536
+ _optionalChain([releaseOperationRetention, 'optionalCall', _52 => _52()]);
1537
+ }
1255
1538
  }
1256
- getFeatures(connectId) {
1257
- return this._callDeviceManagerMethod("getFeatures", connectId, {});
1539
+ getFeatures(connectId, operationContext) {
1540
+ return this._callDeviceManagerMethod(
1541
+ "getFeatures",
1542
+ connectId,
1543
+ {},
1544
+ operationContext
1545
+ );
1258
1546
  }
1259
1547
  /**
1260
1548
  * Device authenticity attestation. Sends a host-generated challenge, has the
@@ -1289,7 +1577,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1289
1577
  if (!internalModel) {
1290
1578
  return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.UnknownError, "Device internal_model unavailable");
1291
1579
  }
1292
- const challengeHex = _nullishCoalesce(_optionalChain([params, 'access', _31 => _31.challenge, 'optionalAccess', _32 => _32.toLowerCase, 'call', _33 => _33()]), () => ( Buffer.from(_utils.randomBytes.call(void 0, 32)).toString("hex")));
1580
+ const challengeHex = _nullishCoalesce(_optionalChain([params, 'access', _53 => _53.challenge, 'optionalAccess', _54 => _54.toLowerCase, 'call', _55 => _55()]), () => ( Buffer.from(_utils.randomBytes.call(void 0, 32)).toString("hex")));
1293
1581
  const challenge = Buffer.from(challengeHex, "hex");
1294
1582
  const proofRes = await this._callDeviceManagerMethod(
1295
1583
  "authenticateDevice",
@@ -1314,28 +1602,65 @@ var _TrezorAdapter = class _TrezorAdapter {
1314
1602
  }
1315
1603
  });
1316
1604
  }
1317
- deviceSettings(connectId, params) {
1605
+ deviceSettings(connectId, params, operationContext) {
1318
1606
  return this._callDeviceManagerMethod(
1319
1607
  "deviceSettings",
1320
1608
  connectId,
1321
- params
1609
+ params,
1610
+ operationContext
1322
1611
  );
1323
1612
  }
1324
- setBrightness(connectId, params = {}) {
1613
+ setBrightness(connectId, params = {}, operationContext) {
1325
1614
  return this._callDeviceManagerMethod(
1326
1615
  "setBrightness",
1327
1616
  connectId,
1328
- params
1617
+ params,
1618
+ operationContext
1329
1619
  );
1330
1620
  }
1331
- changePin(connectId, params = {}) {
1332
- return this._callDeviceManagerMethod("changePin", connectId, params);
1621
+ changePin(connectId, params = {}, operationContext) {
1622
+ return this._callDeviceManagerMethod(
1623
+ "changePin",
1624
+ connectId,
1625
+ params,
1626
+ operationContext
1627
+ );
1333
1628
  }
1334
- wipeDevice(connectId) {
1335
- return this._callDeviceManagerMethod("wipeDevice", connectId, {});
1629
+ wipeDevice(connectId, operationContext) {
1630
+ return this._callDeviceManagerMethod(
1631
+ "wipeDevice",
1632
+ connectId,
1633
+ {},
1634
+ operationContext
1635
+ );
1336
1636
  }
1337
- async _getTrezorDeviceId(connectId) {
1338
- const features = await this.getFeatures(connectId);
1637
+ async _getTrezorDeviceId(connectId, expectedDeviceId, commonParams, bundleContext) {
1638
+ const hasConnectionContext = _optionalChain([commonParams, 'optionalAccess', _56 => _56.knownConnections]) !== void 0 || _optionalChain([commonParams, 'optionalAccess', _57 => _57.extra]) !== void 0;
1639
+ const features = bundleContext || hasConnectionContext ? await this._callMethod(
1640
+ "getFeatures",
1641
+ connectId,
1642
+ hasConnectionContext || !connectId ? expectedDeviceId : void 0,
1643
+ hasConnectionContext ? {
1644
+ operationId: commonParams.operationId,
1645
+ knownConnections: commonParams.knownConnections,
1646
+ extra: commonParams.extra,
1647
+ allowDeviceSelection: commonParams.allowDeviceSelection,
1648
+ supportedTransports: commonParams.supportedTransports,
1649
+ passphraseState: commonParams.passphraseState,
1650
+ useEmptyPassphrase: commonParams.useEmptyPassphrase
1651
+ } : {},
1652
+ false,
1653
+ bundleContext
1654
+ ) : await this.getFeatures(
1655
+ connectId,
1656
+ !connectId && expectedDeviceId ? {
1657
+ expectedDeviceIdentity: {
1658
+ vendor: "trezor",
1659
+ type: "deviceId",
1660
+ value: expectedDeviceId
1661
+ }
1662
+ } : void 0
1663
+ );
1339
1664
  if (!features.success) return features;
1340
1665
  const liveDeviceId = features.payload.device_id;
1341
1666
  if (typeof liveDeviceId !== "string" || liveDeviceId.length === 0) {
@@ -1344,23 +1669,67 @@ var _TrezorAdapter = class _TrezorAdapter {
1344
1669
  return _hwkadaptercore.success.call(void 0, liveDeviceId);
1345
1670
  }
1346
1671
  cancel(connectId) {
1672
+ if (_hwkadaptercore.isHardwareOperationId.call(void 0, connectId)) {
1673
+ try {
1674
+ this._operations.resolve(connectId);
1675
+ } catch (e5) {
1676
+ return;
1677
+ }
1678
+ }
1347
1679
  const userAbortReason = Object.assign(new Error("User aborted operation"), {
1348
1680
  code: _hwkadaptercore.HardwareErrorCode.UserAborted
1349
1681
  });
1350
- this._uiRegistry.cancel();
1351
- this._connector.uiResponse({ type: _hwkadaptercore.UI_RESPONSE.CANCEL });
1352
- if (connectId) {
1353
- this._jobQueue.forceCancelActive(connectId, userAbortReason);
1354
- void this._connector.cancel(_nullishCoalesce(this._sessions.get(connectId), () => ( connectId)));
1682
+ const activeJobId = _optionalChain([this, 'access', _58 => _58._jobQueue, 'access', _59 => _59.getActiveJob, 'call', _60 => _60(), 'optionalAccess', _61 => _61.deviceId]);
1683
+ const targetId = _nullishCoalesce(connectId, () => ( activeJobId));
1684
+ if (targetId) {
1685
+ let resolvedConnectId = targetId;
1686
+ let operationId;
1687
+ if (_hwkadaptercore.isHardwareOperationId.call(void 0, targetId)) {
1688
+ try {
1689
+ operationId = targetId;
1690
+ resolvedConnectId = this._operations.resolve(targetId).connectId;
1691
+ } catch (e6) {
1692
+ resolvedConnectId = "";
1693
+ }
1694
+ }
1695
+ const interactionForPhysicalId = !operationId && connectId ? this._operations.findActiveByConnectionKey(connectId) : void 0;
1696
+ const pendingOperationId = _nullishCoalesce(operationId, () => ( _optionalChain([interactionForPhysicalId, 'optionalAccess', _62 => _62.operationId])));
1697
+ if (!connectId) {
1698
+ this._uiRegistry.cancel();
1699
+ } else if (pendingOperationId) {
1700
+ this._uiRegistry.cancel(void 0, void 0, pendingOperationId);
1701
+ }
1702
+ const queueKeys = /* @__PURE__ */ new Set([trezorQueueKey({ connectId: targetId })]);
1703
+ if (pendingOperationId) queueKeys.add(trezorQueueKey({ operationId: pendingOperationId }));
1704
+ const ownsConnectorInteraction = activeJobId !== void 0 ? queueKeys.has(activeJobId) || pendingOperationId !== void 0 && this._activeOperationId() === pendingOperationId : this._connectingPromises.size === 1 && this._connectingPromises.has(resolvedConnectId);
1705
+ if (connectId === void 0 || ownsConnectorInteraction) {
1706
+ this._connector.uiResponse({ type: _hwkadaptercore.UI_RESPONSE.CANCEL });
1707
+ }
1708
+ for (const key of queueKeys) {
1709
+ this._jobQueue.cancelActiveAndPending(key, userAbortReason);
1710
+ }
1711
+ if (resolvedConnectId) {
1712
+ void this._connector.cancel(_nullishCoalesce(this._sessions.get(resolvedConnectId), () => ( resolvedConnectId)));
1713
+ }
1355
1714
  return;
1356
1715
  }
1357
- for (const cid of this._sessions.keys()) {
1358
- this._jobQueue.forceCancelActive(cid, userAbortReason);
1359
- }
1716
+ this._uiRegistry.cancel();
1717
+ this._connector.uiResponse({ type: _hwkadaptercore.UI_RESPONSE.CANCEL });
1718
+ this._jobQueue.cancelActiveAndPending(void 0, userAbortReason);
1360
1719
  for (const sessionId of this._sessions.values()) {
1361
1720
  void this._connector.cancel(sessionId);
1362
1721
  }
1363
1722
  }
1723
+ /**
1724
+ * Operation owning the running job: its queue key when pinned, else the live
1725
+ * operation of its connectId, so a mid-call UI request can name it.
1726
+ */
1727
+ _activeOperationId() {
1728
+ const activeJobId = _optionalChain([this, 'access', _63 => _63._jobQueue, 'access', _64 => _64.getActiveJob, 'call', _65 => _65(), 'optionalAccess', _66 => _66.deviceId]);
1729
+ if (!activeJobId) return void 0;
1730
+ if (_hwkadaptercore.isHardwareOperationId.call(void 0, activeJobId)) return activeJobId;
1731
+ return _optionalChain([this, 'access', _67 => _67._operations, 'access', _68 => _68.findActiveByConnectionKey, 'call', _69 => _69(activeJobId), 'optionalAccess', _70 => _70.operationId]);
1732
+ }
1364
1733
  /**
1365
1734
  * Routes to BOTH the adapter's local UI registry (preemption decisions,
1366
1735
  * future SELECT_DEVICE / DEVICE_PERMISSION) AND the connector's registry
@@ -1369,6 +1738,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1369
1738
  */
1370
1739
  uiResponse(response) {
1371
1740
  this._uiRegistry.resolve(response.type, response.payload);
1741
+ if (response.type === _hwkadaptercore.UI_RESPONSE.RECEIVE_SELECT_DEVICE) return;
1372
1742
  this._connector.uiResponse(response);
1373
1743
  }
1374
1744
  /**
@@ -1380,8 +1750,8 @@ var _TrezorAdapter = class _TrezorAdapter {
1380
1750
  * Unlike Ledger (which hashes a per-chain address), it is chain-global, so
1381
1751
  * `chain` is accepted for API parity but ignored.
1382
1752
  */
1383
- async getChainFingerprint(connectId, _deviceId, _chain) {
1384
- return this._getTrezorDeviceId(connectId);
1753
+ async getChainFingerprint(connectId, deviceId, _chain) {
1754
+ return this._getTrezorDeviceId(connectId, deviceId);
1385
1755
  }
1386
1756
  /**
1387
1757
  * Resolve the active passphrase wallet identity (`passphraseState`).
@@ -1391,17 +1761,87 @@ var _TrezorAdapter = class _TrezorAdapter {
1391
1761
  * can't interleave with a chain call on the same device (which would desync
1392
1762
  * the THP session selection).
1393
1763
  */
1394
- getPassphraseState(connectId, passphraseState) {
1395
- return this._jobQueue.enqueue(
1764
+ getPassphraseState(connectId, passphraseState, operationContext) {
1765
+ const expectedIdentity = _optionalChain([operationContext, 'optionalAccess', _71 => _71.expectedDeviceIdentity]);
1766
+ if (expectedIdentity && (expectedIdentity.vendor !== "trezor" || expectedIdentity.type !== "deviceId")) {
1767
+ return Promise.resolve(
1768
+ _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.InvalidParams, "Trezor device identity is required")
1769
+ );
1770
+ }
1771
+ const target = _hwkadaptercore.resolveHardwareOperationTarget.call(void 0,
1396
1772
  connectId,
1773
+ _optionalChain([operationContext, 'optionalAccess', _72 => _72.operationId]),
1774
+ "trezor"
1775
+ );
1776
+ if (!target.success) return Promise.resolve(target);
1777
+ const { operationId } = target.payload;
1778
+ let resolvedConnectId = _nullishCoalesce(target.payload.targetId, () => ( ""));
1779
+ let releaseOperationRetention;
1780
+ if (operationId) {
1781
+ try {
1782
+ resolvedConnectId = this._operations.resolve(operationId).connectId;
1783
+ releaseOperationRetention = this._operations.retain(operationId);
1784
+ } catch (error) {
1785
+ return Promise.resolve(this._errorToFailure(error));
1786
+ }
1787
+ }
1788
+ return this._jobQueue.enqueue(
1789
+ trezorQueueKey({ operationId, connectId: resolvedConnectId }),
1397
1790
  async (signal) => {
1398
- const restorePassphraseRequestContext = this._setPassphraseRequestContext(connectId, {
1399
- passphraseState
1400
- });
1791
+ let selectionRequestId;
1792
+ let verified = false;
1401
1793
  try {
1402
- return await this._resolvePassphraseState(connectId, passphraseState, signal);
1794
+ if (!operationId && _optionalChain([expectedIdentity, 'optionalAccess', _73 => _73.value]) && (!connectId || _optionalChain([operationContext, 'optionalAccess', _74 => _74.knownConnections]) !== void 0)) {
1795
+ const resolved = await this._resolveExpectedDeviceConnectId(
1796
+ expectedIdentity.value,
1797
+ signal,
1798
+ operationContext
1799
+ );
1800
+ resolvedConnectId = resolved.connectId;
1801
+ selectionRequestId = resolved.selectionRequestId;
1802
+ }
1803
+ if (_optionalChain([expectedIdentity, 'optionalAccess', _75 => _75.value])) {
1804
+ if (!operationId) await this._ensureSession(resolvedConnectId, signal);
1805
+ if (_optionalChain([this, 'access', _76 => _76._devices, 'access', _77 => _77.get, 'call', _78 => _78(resolvedConnectId), 'optionalAccess', _79 => _79.deviceId]) !== expectedIdentity.value) {
1806
+ return _hwkadaptercore.failure.call(void 0,
1807
+ _hwkadaptercore.HardwareErrorCode.DeviceMismatch,
1808
+ "Trezor device identity does not match"
1809
+ );
1810
+ }
1811
+ }
1812
+ const restorePassphraseRequestContext = this._setPassphraseRequestContext(
1813
+ resolvedConnectId,
1814
+ {
1815
+ passphraseState
1816
+ }
1817
+ );
1818
+ try {
1819
+ const result = await this._resolvePassphraseState(
1820
+ resolvedConnectId,
1821
+ passphraseState,
1822
+ signal,
1823
+ operationId
1824
+ );
1825
+ if (result.success && _optionalChain([expectedIdentity, 'optionalAccess', _80 => _80.value])) {
1826
+ await this._emitVerifiedBinding(
1827
+ expectedIdentity.value,
1828
+ resolvedConnectId,
1829
+ selectionRequestId,
1830
+ operationContext,
1831
+ signal
1832
+ );
1833
+ verified = true;
1834
+ this._rememberVerifiedConnection(expectedIdentity.value, resolvedConnectId);
1835
+ }
1836
+ return result;
1837
+ } finally {
1838
+ restorePassphraseRequestContext();
1839
+ }
1403
1840
  } finally {
1404
- restorePassphraseRequestContext();
1841
+ if (selectionRequestId && !verified) {
1842
+ this._emitBindingStatus(selectionRequestId, signal.aborted ? "cancelled" : "failed");
1843
+ await this._releaseProvisionalConnection(resolvedConnectId, signal);
1844
+ }
1405
1845
  }
1406
1846
  },
1407
1847
  {
@@ -1409,7 +1849,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1409
1849
  rejectIfBusy: true,
1410
1850
  busyError: _TrezorAdapter._createDeviceBusyError("getPassphraseState")
1411
1851
  }
1412
- ).catch((error) => this._errorToFailure(error));
1852
+ ).catch((error) => this._errorToFailure(error)).finally(() => _optionalChain([releaseOperationRetention, 'optionalCall', _81 => _81()]));
1413
1853
  }
1414
1854
  evmGetAddress(connectId, deviceId, params) {
1415
1855
  return this._callWalletMethod("evmGetAddress", connectId, deviceId, params);
@@ -1472,11 +1912,94 @@ var _TrezorAdapter = class _TrezorAdapter {
1472
1912
  }
1473
1913
  return result;
1474
1914
  }
1475
- static async _callConnector(connector, sessionId, method, params, signal) {
1476
- const promise = connector.call(sessionId, method, params);
1915
+ async _callConnector(sessionId, method, params, signal) {
1916
+ if (_optionalChain([signal, 'optionalAccess', _82 => _82.aborted])) {
1917
+ throw _nullishCoalesce(signal.reason, () => ( new Error("Aborted")));
1918
+ }
1919
+ this._assertConnectorIdle(method);
1920
+ const releaseOperation = this._retainConnectorOperation(`call:${sessionId}`);
1921
+ let promise;
1922
+ try {
1923
+ promise = this._connector.call(sessionId, method, params).finally(() => {
1924
+ releaseOperation();
1925
+ });
1926
+ } catch (error) {
1927
+ releaseOperation();
1928
+ throw error;
1929
+ }
1477
1930
  const result = signal ? await _TrezorAdapter._abortable(signal, promise) : await promise;
1478
1931
  return _TrezorAdapter._unwrapConnectorResult(result);
1479
1932
  }
1933
+ _assertConnectorIdle(method) {
1934
+ if (this._resetPromise || this._pendingConnectorTeardowns > 0 || this._unsettledConnectorOperations.size > 0) {
1935
+ throw _TrezorAdapter._createDeviceBusyError(method);
1936
+ }
1937
+ }
1938
+ _runConnectorTeardown(task) {
1939
+ const previous = this._connectorTeardownTail;
1940
+ let releaseTail = () => void 0;
1941
+ this._connectorTeardownTail = new Promise((resolve) => {
1942
+ releaseTail = resolve;
1943
+ });
1944
+ this._pendingConnectorTeardowns += 1;
1945
+ return (async () => {
1946
+ try {
1947
+ await previous;
1948
+ await this._waitForConnectorOperationsToDrain();
1949
+ await task();
1950
+ } finally {
1951
+ this._pendingConnectorTeardowns -= 1;
1952
+ releaseTail();
1953
+ }
1954
+ })();
1955
+ }
1956
+ async _disconnectSession(connectId, sessionId = this._sessions.get(connectId)) {
1957
+ this._sessions.delete(connectId);
1958
+ this._verifiedPassphraseSessionsByConnectId.delete(connectId);
1959
+ if (sessionId) await this._runConnectorTeardown(() => this._connector.disconnect(sessionId));
1960
+ }
1961
+ _availableTransports() {
1962
+ return _nullishCoalesce(this._connector.availableTransports, () => ( [this._connector.connectionType]));
1963
+ }
1964
+ async _releaseProvisionalConnection(connectId, signal) {
1965
+ const sessionId = this._sessions.get(connectId);
1966
+ this._sessions.delete(connectId);
1967
+ this._verifiedPassphraseSessionsByConnectId.delete(connectId);
1968
+ if (!sessionId) return;
1969
+ const teardown = this._runConnectorTeardown(() => this._connector.disconnect(sessionId)).catch(
1970
+ () => void 0
1971
+ );
1972
+ if (!signal.aborted) await teardown;
1973
+ }
1974
+ _waitForConnectorOperationsToDrain() {
1975
+ if (this._unsettledConnectorOperations.size === 0) {
1976
+ return Promise.resolve();
1977
+ }
1978
+ return new Promise((resolve) => {
1979
+ this._connectorIdleWaiters.add(resolve);
1980
+ });
1981
+ }
1982
+ _retainConnectorOperation(key) {
1983
+ this._unsettledConnectorOperations.set(
1984
+ key,
1985
+ (_nullishCoalesce(this._unsettledConnectorOperations.get(key), () => ( 0))) + 1
1986
+ );
1987
+ let released = false;
1988
+ return () => {
1989
+ if (released) return;
1990
+ released = true;
1991
+ const remaining = (_nullishCoalesce(this._unsettledConnectorOperations.get(key), () => ( 1))) - 1;
1992
+ if (remaining > 0) {
1993
+ this._unsettledConnectorOperations.set(key, remaining);
1994
+ } else {
1995
+ this._unsettledConnectorOperations.delete(key);
1996
+ }
1997
+ if (this._unsettledConnectorOperations.size === 0) {
1998
+ for (const resolve of this._connectorIdleWaiters) resolve();
1999
+ this._connectorIdleWaiters.clear();
2000
+ }
2001
+ };
2002
+ }
1480
2003
  static _normalizeAllNetworkItem(method, item) {
1481
2004
  if (method !== "btcGetAddress" && method !== "btcGetPublicKey") {
1482
2005
  return item;
@@ -1495,18 +2018,21 @@ var _TrezorAdapter = class _TrezorAdapter {
1495
2018
  const { deviceId } = item;
1496
2019
  return typeof deviceId === "string" && deviceId.length > 0 ? deviceId : void 0;
1497
2020
  }
1498
- _callAllNetworkMethod(connectId, method, item) {
2021
+ /** UserAborted is a top-level abort code, so this ends the bundle. */
2022
+ static _buildCancelledFailure(signal) {
2023
+ const reason = signal.reason;
2024
+ const code = typeof _optionalChain([reason, 'optionalAccess', _83 => _83.code]) === "number" ? reason.code : _hwkadaptercore.HardwareErrorCode.UserAborted;
2025
+ const message = typeof _optionalChain([reason, 'optionalAccess', _84 => _84.message]) === "string" ? reason.message : "";
2026
+ return _hwkadaptercore.failure.call(void 0, code, message || "All-network get-address cancelled");
2027
+ }
2028
+ _callAllNetworkMethod(connectId, method, item, bundleContext) {
1499
2029
  switch (method) {
1500
2030
  case "evmGetAddress":
1501
- return this.evmGetAddress(connectId, "", item);
1502
2031
  case "btcGetAddress":
1503
- return this.btcGetAddress(connectId, "", item);
1504
2032
  case "btcGetPublicKey":
1505
- return this.btcGetPublicKey(connectId, "", item);
1506
2033
  case "solGetAddress":
1507
- return this.solGetAddress(connectId, "", item);
1508
2034
  case "tronGetAddress":
1509
- return this.tronGetAddress(connectId, "", item);
2035
+ return this._callMethod(method, connectId, "", item, true, bundleContext);
1510
2036
  default:
1511
2037
  return Promise.resolve(
1512
2038
  _hwkadaptercore.failure.call(void 0,
@@ -1525,11 +2051,51 @@ var _TrezorAdapter = class _TrezorAdapter {
1525
2051
  _callWalletMethod(methodName, connectId, deviceId, params) {
1526
2052
  return this._callMethod(methodName, connectId, deviceId, params, true);
1527
2053
  }
1528
- _callDeviceManagerMethod(methodName, connectId, params) {
1529
- return this._callMethod(methodName, connectId, void 0, params, false);
2054
+ _callDeviceManagerMethod(methodName, connectId, params, operationContext) {
2055
+ const expectedIdentity = _optionalChain([operationContext, 'optionalAccess', _85 => _85.expectedDeviceIdentity]);
2056
+ if (expectedIdentity && (expectedIdentity.vendor !== "trezor" || expectedIdentity.type !== "deviceId")) {
2057
+ return Promise.resolve(
2058
+ _hwkadaptercore.failure.call(void 0,
2059
+ _hwkadaptercore.HardwareErrorCode.InvalidParams,
2060
+ "Trezor device-manager operations require a Trezor deviceId identity"
2061
+ )
2062
+ );
2063
+ }
2064
+ return this._callMethod(
2065
+ methodName,
2066
+ connectId,
2067
+ _optionalChain([expectedIdentity, 'optionalAccess', _86 => _86.value]),
2068
+ {
2069
+ ...params && typeof params === "object" ? params : {},
2070
+ knownConnections: _optionalChain([operationContext, 'optionalAccess', _87 => _87.knownConnections]),
2071
+ extra: _optionalChain([operationContext, 'optionalAccess', _88 => _88.extra]),
2072
+ allowDeviceSelection: _optionalChain([operationContext, 'optionalAccess', _89 => _89.allowDeviceSelection]),
2073
+ supportedTransports: _optionalChain([operationContext, 'optionalAccess', _90 => _90.supportedTransports]),
2074
+ ..._optionalChain([operationContext, 'optionalAccess', _91 => _91.operationId]) ? { operationId: operationContext.operationId } : {}
2075
+ },
2076
+ false
2077
+ );
1530
2078
  }
1531
- async _callMethod(methodName, connectId, deviceId, params, requiresWalletIntent) {
2079
+ async _callMethod(methodName, connectId, deviceId, params, requiresWalletIntent, bundleContext) {
1532
2080
  const call = _TrezorAdapter._normalizeCallArgs(connectId, deviceId, params);
2081
+ const commonParams = _TrezorAdapter._splitCommonParams(call.params);
2082
+ const positionalOperationId = _hwkadaptercore.isHardwareOperationId.call(void 0, call.connectId) ? call.connectId : void 0;
2083
+ if (positionalOperationId && commonParams.operationId && positionalOperationId !== commonParams.operationId) {
2084
+ return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.InvalidParams, "Conflicting Trezor operation ids", {
2085
+ positionalOperationId,
2086
+ commonOperationId: commonParams.operationId
2087
+ });
2088
+ }
2089
+ const operationId = _nullishCoalesce(commonParams.operationId, () => ( positionalOperationId));
2090
+ let releaseOperationRetention;
2091
+ try {
2092
+ if (operationId) {
2093
+ call.connectId = this._operations.resolve(operationId).connectId;
2094
+ releaseOperationRetention = this._operations.retain(operationId);
2095
+ }
2096
+ } catch (error) {
2097
+ return this._errorToFailure(error);
2098
+ }
1533
2099
  debugLog("[TrezorAdapter][REQ]", {
1534
2100
  method: methodName,
1535
2101
  connectId: call.connectId || "(empty)",
@@ -1538,15 +2104,40 @@ var _TrezorAdapter = class _TrezorAdapter {
1538
2104
  });
1539
2105
  try {
1540
2106
  const response = await this._jobQueue.enqueue(
1541
- call.connectId,
1542
- (signal) => this._callWithRetry(
1543
- call.connectId,
1544
- methodName,
1545
- call.params,
1546
- requiresWalletIntent,
1547
- true,
1548
- signal
1549
- ),
2107
+ trezorQueueKey({ operationId, connectId: call.connectId }),
2108
+ async (signal) => {
2109
+ let effectiveConnectId = call.connectId;
2110
+ let selectionRequestId;
2111
+ try {
2112
+ if (!_optionalChain([bundleContext, 'optionalAccess', _92 => _92.connection]) && !operationId && deviceId && (!connectId || _optionalChain([commonParams, 'access', _93 => _93.connectionContext, 'optionalAccess', _94 => _94.knownConnections]) !== void 0)) {
2113
+ const resolved = await this._resolveExpectedDeviceConnectId(
2114
+ deviceId,
2115
+ signal,
2116
+ commonParams.connectionContext
2117
+ );
2118
+ effectiveConnectId = resolved.connectId;
2119
+ selectionRequestId = resolved.selectionRequestId;
2120
+ }
2121
+ } catch (error) {
2122
+ return this._errorToFailure(error);
2123
+ }
2124
+ const response2 = await this._callWithRetry(
2125
+ _nullishCoalesce(_optionalChain([bundleContext, 'optionalAccess', _95 => _95.connection, 'optionalAccess', _96 => _96.connectId]), () => ( effectiveConnectId)),
2126
+ methodName,
2127
+ call.params,
2128
+ deviceId || "",
2129
+ requiresWalletIntent,
2130
+ true,
2131
+ signal,
2132
+ operationId,
2133
+ selectionRequestId,
2134
+ bundleContext
2135
+ );
2136
+ if (!response2.success && selectionRequestId) {
2137
+ await this._releaseProvisionalConnection(effectiveConnectId, signal);
2138
+ }
2139
+ return response2;
2140
+ },
1550
2141
  {
1551
2142
  label: methodName,
1552
2143
  rejectIfBusy: true,
@@ -1567,16 +2158,53 @@ var _TrezorAdapter = class _TrezorAdapter {
1567
2158
  payload: _TrezorAdapter._sanitizeForLog(response.payload, methodName)
1568
2159
  });
1569
2160
  return response;
2161
+ } finally {
2162
+ _optionalChain([releaseOperationRetention, 'optionalCall', _97 => _97()]);
1570
2163
  }
1571
2164
  }
1572
- async _callWithRetry(connectId, methodName, params, requiresWalletIntent, allowRetry, signal) {
1573
- const { passphraseState, useEmptyPassphrase, rest } = _TrezorAdapter._splitCommonParams(params);
2165
+ async _callWithRetry(connectId, methodName, params, expectedDeviceId, requiresWalletIntent, allowRetry, signal, operationId, selectionRequestId, bundleContext) {
2166
+ const { passphraseState, useEmptyPassphrase, rest, connectionContext } = _TrezorAdapter._splitCommonParams(params);
2167
+ let pendingBindingRequestId = selectionRequestId;
2168
+ let businessCallStarted = false;
1574
2169
  const restorePassphraseRequestContext = this._setPassphraseRequestContext(connectId, {
1575
2170
  passphraseState,
1576
2171
  useEmptyPassphrase
1577
2172
  });
1578
2173
  try {
1579
- const sessionId = await this._ensureSession(connectId, signal);
2174
+ if (_optionalChain([bundleContext, 'optionalAccess', _98 => _98.connection])) {
2175
+ const pinned = bundleContext.connection;
2176
+ if (this._sessions.get(pinned.connectId) !== pinned.sessionId) {
2177
+ return _hwkadaptercore.failure.call(void 0,
2178
+ _hwkadaptercore.HardwareErrorCode.DeviceDisconnected,
2179
+ "Trezor all-network connection ended"
2180
+ );
2181
+ }
2182
+ }
2183
+ const sessionId = operationId ? this._sessions.get(this._operations.resolve(operationId).connectId) : await this._ensureSession(connectId, signal);
2184
+ if (!sessionId) {
2185
+ if (operationId) {
2186
+ this._operations.end(operationId, "disconnect");
2187
+ return _hwkadaptercore.failure.call(void 0,
2188
+ _hwkadaptercore.HardwareErrorCode.OperationEnded,
2189
+ "Trezor operation connection is no longer active",
2190
+ { operationId, reason: "disconnect" }
2191
+ );
2192
+ }
2193
+ return _hwkadaptercore.failure.call(void 0, _hwkadaptercore.HardwareErrorCode.DeviceNotFound, "Trezor session was not found");
2194
+ }
2195
+ if (bundleContext && !bundleContext.connection) {
2196
+ bundleContext.connection = { connectId, sessionId };
2197
+ }
2198
+ if (expectedDeviceId) {
2199
+ const actualDeviceId = _optionalChain([this, 'access', _99 => _99._devices, 'access', _100 => _100.get, 'call', _101 => _101(connectId), 'optionalAccess', _102 => _102.deviceId]);
2200
+ if (!actualDeviceId || actualDeviceId !== expectedDeviceId) {
2201
+ return _hwkadaptercore.failure.call(void 0,
2202
+ _hwkadaptercore.HardwareErrorCode.DeviceMismatch,
2203
+ `Wrong device: expected ${expectedDeviceId}, got ${actualDeviceId || "unknown"}`,
2204
+ { expected: expectedDeviceId, actual: actualDeviceId || "" }
2205
+ );
2206
+ }
2207
+ }
1580
2208
  if (requiresWalletIntent && !passphraseState && useEmptyPassphrase !== true) {
1581
2209
  return _hwkadaptercore.failure.call(void 0,
1582
2210
  _hwkadaptercore.HardwareErrorCode.InvalidParams,
@@ -1591,39 +2219,85 @@ var _TrezorAdapter = class _TrezorAdapter {
1591
2219
  } else if (useEmptyPassphrase === true) {
1592
2220
  await _TrezorAdapter._abortable(signal, this._createFreshAppSession(sessionId, signal));
1593
2221
  }
2222
+ if (expectedDeviceId) {
2223
+ await this._emitVerifiedBinding(
2224
+ expectedDeviceId,
2225
+ connectId,
2226
+ pendingBindingRequestId,
2227
+ connectionContext,
2228
+ signal
2229
+ );
2230
+ pendingBindingRequestId = void 0;
2231
+ this._rememberVerifiedConnection(expectedDeviceId, connectId);
2232
+ }
2233
+ businessCallStarted = true;
1594
2234
  const result = await _TrezorAdapter._abortable(
1595
2235
  signal,
1596
- _TrezorAdapter._callConnector(this._connector, sessionId, methodName, rest)
2236
+ this._callConnector(sessionId, methodName, rest, signal)
1597
2237
  );
1598
2238
  return _hwkadaptercore.success.call(void 0, result);
1599
2239
  } catch (error) {
1600
2240
  if (signal.aborted) {
2241
+ if (pendingBindingRequestId) {
2242
+ this._emitBindingStatus(pendingBindingRequestId, "cancelled");
2243
+ pendingBindingRequestId = void 0;
2244
+ }
1601
2245
  return this._errorToFailure(error);
1602
2246
  }
1603
2247
  const code = _TrezorAdapter._errorCode(error);
1604
- if (allowRetry && code === _hwkadaptercore.HardwareErrorCode.DeviceDisconnected) {
2248
+ const willRetryStaleSession = allowRetry && (passphraseState || useEmptyPassphrase === true) && _TrezorAdapter._isStaleSessionError(code);
2249
+ if (pendingBindingRequestId && !willRetryStaleSession) {
2250
+ this._emitBindingStatus(pendingBindingRequestId, "failed");
2251
+ pendingBindingRequestId = void 0;
2252
+ }
2253
+ const ambiguousTransportFailure = code === _hwkadaptercore.HardwareErrorCode.DeviceDisconnected || code === _hwkadaptercore.HardwareErrorCode.OperationTimeout || code === _hwkadaptercore.HardwareErrorCode.TransportError;
2254
+ if (ambiguousTransportFailure) {
1605
2255
  this._sessions.delete(connectId);
1606
2256
  this._verifiedPassphraseSessionsByConnectId.delete(connectId);
1607
- return await this._callWithRetry(
1608
- connectId,
1609
- methodName,
1610
- params,
1611
- requiresWalletIntent,
1612
- false,
1613
- signal
1614
- );
2257
+ const mayHaveCompleted = businessCallStarted && !_hwkadaptercore.canReplayHardwareMethodAfterTransportFailure.call(void 0, methodName);
2258
+ const mayHaveCompletedMessage = `Trezor ${methodName} may have completed before the connection was lost`;
2259
+ if (operationId) {
2260
+ this._operations.end(operationId, "disconnect");
2261
+ const endedParams = { operationId, reason: "disconnect" };
2262
+ return mayHaveCompleted ? _hwkadaptercore.failure.call(void 0,
2263
+ _hwkadaptercore.HardwareErrorCode.OperationEnded,
2264
+ mayHaveCompletedMessage,
2265
+ _hwkadaptercore.operationMayHaveCompletedParams.call(void 0, methodName, endedParams),
2266
+ void 0,
2267
+ { scope: "unknown" }
2268
+ ) : _hwkadaptercore.failure.call(void 0,
2269
+ _hwkadaptercore.HardwareErrorCode.OperationEnded,
2270
+ "Trezor operation connection was lost",
2271
+ endedParams
2272
+ );
2273
+ }
2274
+ if (mayHaveCompleted) {
2275
+ return _hwkadaptercore.failure.call(void 0,
2276
+ code,
2277
+ mayHaveCompletedMessage,
2278
+ _hwkadaptercore.operationMayHaveCompletedParams.call(void 0, methodName),
2279
+ void 0,
2280
+ { scope: "unknown" }
2281
+ );
2282
+ }
1615
2283
  }
1616
- if (allowRetry && (passphraseState || useEmptyPassphrase === true) && _TrezorAdapter._isStaleSessionError(code)) {
2284
+ if (willRetryStaleSession) {
1617
2285
  if (passphraseState) {
1618
2286
  this._forgetVerifiedPassphraseSession(connectId, passphraseState);
1619
2287
  }
2288
+ const retryBindingRequestId = pendingBindingRequestId;
2289
+ pendingBindingRequestId = void 0;
1620
2290
  return await this._callWithRetry(
1621
2291
  connectId,
1622
2292
  methodName,
1623
2293
  params,
2294
+ expectedDeviceId,
1624
2295
  requiresWalletIntent,
1625
2296
  false,
1626
- signal
2297
+ signal,
2298
+ operationId,
2299
+ retryBindingRequestId,
2300
+ bundleContext
1627
2301
  );
1628
2302
  }
1629
2303
  debugLog("[TrezorAdapter][ERROR]", {
@@ -1634,36 +2308,252 @@ var _TrezorAdapter = class _TrezorAdapter {
1634
2308
  return this._errorToFailure(error);
1635
2309
  } finally {
1636
2310
  restorePassphraseRequestContext();
2311
+ if (pendingBindingRequestId) {
2312
+ this._emitBindingStatus(pendingBindingRequestId, signal.aborted ? "cancelled" : "failed");
2313
+ pendingBindingRequestId = void 0;
2314
+ }
1637
2315
  }
1638
2316
  }
1639
- async _ensureSession(connectId, signal) {
2317
+ async _ensureSession(connectId, signal, transportType) {
1640
2318
  const existing = this._sessions.get(connectId);
1641
2319
  if (existing) return existing;
1642
2320
  const pending = this._connectingPromises.get(connectId);
1643
2321
  if (pending) return pending;
2322
+ const stateGeneration = this._stateGeneration;
1644
2323
  const promise = (async () => {
2324
+ if (_optionalChain([signal, 'optionalAccess', _103 => _103.aborted])) {
2325
+ throw Object.assign(new Error("Trezor connection aborted"), {
2326
+ code: _hwkadaptercore.HardwareErrorCode.UserAborted
2327
+ });
2328
+ }
1645
2329
  try {
1646
- if (_optionalChain([signal, 'optionalAccess', _34 => _34.aborted])) {
1647
- throw Object.assign(new Error("Trezor connection aborted"), {
1648
- code: _hwkadaptercore.HardwareErrorCode.UserAborted
1649
- });
1650
- }
1651
- try {
1652
- return await this._connectSession(connectId, signal);
1653
- } catch (error) {
1654
- if (_TrezorAdapter._isConnectionUnavailableError(error)) {
1655
- this._sessions.delete(connectId);
1656
- this._verifiedPassphraseSessionsByConnectId.delete(connectId);
1657
- }
1658
- throw error;
2330
+ return await this._connectSession(connectId, signal, stateGeneration, transportType);
2331
+ } catch (error) {
2332
+ if (_TrezorAdapter._isConnectionUnavailableError(error)) {
2333
+ this._sessions.delete(connectId);
2334
+ this._verifiedPassphraseSessionsByConnectId.delete(connectId);
1659
2335
  }
1660
- } finally {
1661
- this._connectingPromises.delete(connectId);
1662
- this._disconnectRequested.delete(connectId);
2336
+ throw error;
1663
2337
  }
1664
2338
  })();
1665
2339
  this._connectingPromises.set(connectId, promise);
1666
- return promise;
2340
+ try {
2341
+ return await promise;
2342
+ } finally {
2343
+ if (this._connectingPromises.get(connectId) === promise) {
2344
+ this._connectingPromises.delete(connectId);
2345
+ }
2346
+ this._disconnectRequested.delete(connectId);
2347
+ }
2348
+ }
2349
+ /** An identity probe tears down only a session it opened; another caller's stays up. */
2350
+ _hasSessionOrConnect(connectId) {
2351
+ return this._sessions.has(connectId) || this._connectingPromises.has(connectId);
2352
+ }
2353
+ async _resolveExpectedDeviceConnectId(expectedDeviceId, signal, context) {
2354
+ const connectedMatch = Array.from(this._devices.values()).find(
2355
+ (device) => device.deviceId === expectedDeviceId && this._sessions.has(device.connectId)
2356
+ );
2357
+ if (connectedMatch) return { connectId: connectedMatch.connectId };
2358
+ const availableTransports = this._availableTransports();
2359
+ if (availableTransports.includes("usb")) {
2360
+ await _TrezorAdapter._abortable(
2361
+ signal,
2362
+ this._ensureDevicePermission(void 0, expectedDeviceId, "usb")
2363
+ );
2364
+ }
2365
+ const candidates = await _TrezorAdapter._abortable(
2366
+ signal,
2367
+ this._searchDevices({ transportType: "usb" }, signal)
2368
+ );
2369
+ const mismatchedDeviceIds = [];
2370
+ const knownConnection = this._knownDeviceConnections.get(expectedDeviceId);
2371
+ const knownUsbIds = _optionalChain([context, 'optionalAccess', _104 => _104.knownConnections]) !== void 0 ? context.knownConnections.flatMap(
2372
+ (connection) => connection.transport === "usb" ? [connection.connectId] : []
2373
+ ) : [_optionalChain([knownConnection, 'optionalAccess', _105 => _105.usbConnectId])].filter((id) => Boolean(id));
2374
+ const suppliedBleIds = _optionalChain([context, 'optionalAccess', _106 => _106.knownConnections, 'optionalAccess', _107 => _107.flatMap, 'call', _108 => _108(
2375
+ (connection) => connection.transport === "ble" ? [connection.connectId] : []
2376
+ )]);
2377
+ const knownBleIds = _optionalChain([suppliedBleIds, 'optionalAccess', _109 => _109.length]) ? suppliedBleIds : [_optionalChain([knownConnection, 'optionalAccess', _110 => _110.bleConnectId])].filter((id) => Boolean(id));
2378
+ const usbCandidates = candidates.filter(
2379
+ (device) => device.connectionType === "usb" && (!knownUsbIds.length || knownUsbIds.includes(device.connectId))
2380
+ );
2381
+ for (const candidate of usbCandidates) {
2382
+ let sessionId;
2383
+ const probeOpensSession = !this._hasSessionOrConnect(candidate.connectId);
2384
+ try {
2385
+ sessionId = await this._ensureSession(candidate.connectId, signal, "usb");
2386
+ const actualDeviceId = _nullishCoalesce(_optionalChain([this, 'access', _111 => _111._devices, 'access', _112 => _112.get, 'call', _113 => _113(candidate.connectId), 'optionalAccess', _114 => _114.deviceId]), () => ( ""));
2387
+ if (actualDeviceId === expectedDeviceId) return { connectId: candidate.connectId };
2388
+ mismatchedDeviceIds.push(actualDeviceId || "unknown");
2389
+ } catch (error) {
2390
+ if (signal.aborted) throw error;
2391
+ if (!_TrezorAdapter._isConnectionUnavailableError(error)) throw error;
2392
+ }
2393
+ if (sessionId && probeOpensSession) {
2394
+ await this._disconnectSession(candidate.connectId, sessionId);
2395
+ }
2396
+ }
2397
+ if (mismatchedDeviceIds.length) {
2398
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2399
+ code: _hwkadaptercore.HardwareErrorCode.DeviceSearchMismatch,
2400
+ message: "The connected Trezor is not the wallet this operation needs. Unplug it and connect the right device.",
2401
+ params: { connectedDeviceIds: mismatchedDeviceIds }
2402
+ });
2403
+ }
2404
+ if (usbCandidates.length > 0) {
2405
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2406
+ code: _hwkadaptercore.HardwareErrorCode.DeviceNotFound,
2407
+ message: "Trezor devices are present but none could be opened to check its identity."
2408
+ });
2409
+ }
2410
+ const [knownBleConnectId] = knownBleIds;
2411
+ if (knownBleConnectId && availableTransports.includes("ble")) {
2412
+ await _TrezorAdapter._abortable(
2413
+ signal,
2414
+ this._ensureDevicePermission(knownBleConnectId, expectedDeviceId, "ble")
2415
+ );
2416
+ const probeOpensSession = !this._hasSessionOrConnect(knownBleConnectId);
2417
+ await this._ensureSession(knownBleConnectId, signal, "ble");
2418
+ const actualDeviceId = _optionalChain([this, 'access', _115 => _115._devices, 'access', _116 => _116.get, 'call', _117 => _117(knownBleConnectId), 'optionalAccess', _118 => _118.deviceId]);
2419
+ if (actualDeviceId === expectedDeviceId) return { connectId: knownBleConnectId };
2420
+ if (probeOpensSession) await this._disconnectSession(knownBleConnectId);
2421
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2422
+ code: _hwkadaptercore.HardwareErrorCode.DeviceMismatch,
2423
+ message: "The bound Trezor Bluetooth device has a different identity"
2424
+ });
2425
+ }
2426
+ if (_optionalChain([context, 'optionalAccess', _119 => _119.supportedTransports]) && !context.supportedTransports.includes("ble")) {
2427
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2428
+ code: _hwkadaptercore.HardwareErrorCode.DeviceNotFound,
2429
+ message: "This Trezor model has no Bluetooth transport; connect it over USB"
2430
+ });
2431
+ }
2432
+ if (_optionalChain([context, 'optionalAccess', _120 => _120.allowDeviceSelection]) === false) {
2433
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2434
+ code: _hwkadaptercore.HardwareErrorCode.DeviceNotFound,
2435
+ message: "No known Trezor connection is available"
2436
+ });
2437
+ }
2438
+ return this._selectBleDeviceForBinding(expectedDeviceId, signal, context, "missing-binding");
2439
+ }
2440
+ async _selectBleDeviceForBinding(expectedDeviceId, signal, context, reason) {
2441
+ const availableTransports = this._availableTransports();
2442
+ const allowUsbFallback = reason !== "manual-rebind" && availableTransports.includes("usb");
2443
+ const knownUsbIds = _optionalChain([context, 'optionalAccess', _121 => _121.knownConnections]) ? context.knownConnections.flatMap(
2444
+ (connection) => connection.transport === "usb" ? [connection.connectId] : []
2445
+ ) : [_optionalChain([this, 'access', _122 => _122._knownDeviceConnections, 'access', _123 => _123.get, 'call', _124 => _124(expectedDeviceId), 'optionalAccess', _125 => _125.usbConnectId])];
2446
+ if (availableTransports.includes("ble")) {
2447
+ await _TrezorAdapter._abortable(
2448
+ signal,
2449
+ this._ensureDevicePermission(void 0, expectedDeviceId, "ble")
2450
+ );
2451
+ }
2452
+ const bleCandidates = (await _TrezorAdapter._abortable(
2453
+ signal,
2454
+ this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal)
2455
+ )).filter((device) => device.connectionType === "ble");
2456
+ if (signal.aborted) throw signal.reason;
2457
+ const rejectedConnectIds = /* @__PURE__ */ new Set();
2458
+ const bindingSessionId = this._uiRegistry.createRequestId();
2459
+ let rejectedConnectId;
2460
+ while (availableTransports.includes("ble") && this._emitter.listenerCount(_hwkadaptercore.UI_REQUEST.REQUEST_SELECT_DEVICE)) {
2461
+ const { device: selected, requestId } = await _hwkadaptercore.requestBleDeviceSelection.call(void 0, {
2462
+ emitter: this._emitter,
2463
+ registry: this._uiRegistry,
2464
+ signal,
2465
+ allowUsbFallback,
2466
+ scan: async () => {
2467
+ if (allowUsbFallback) {
2468
+ const usbCandidates = (await this._searchDevices({ transportType: "usb" }, signal)).filter(
2469
+ (device) => device.connectionType === "usb" && !rejectedConnectIds.has(device.connectId)
2470
+ );
2471
+ const candidate = _nullishCoalesce(usbCandidates.find((device) => knownUsbIds.includes(device.connectId)), () => ( usbCandidates[0]));
2472
+ if (candidate) return [candidate];
2473
+ }
2474
+ return (await this._searchDevices({ transportType: "ble", waitForAllTransports: true }, signal)).filter(
2475
+ (device) => device.connectionType === "ble" && !rejectedConnectIds.has(device.connectId)
2476
+ );
2477
+ },
2478
+ request: {
2479
+ devices: bleCandidates.filter((device) => !rejectedConnectIds.has(device.connectId)),
2480
+ bindingSessionId,
2481
+ rejectedConnectId,
2482
+ context: {
2483
+ kind: "bind-connection",
2484
+ transport: "ble",
2485
+ reason
2486
+ },
2487
+ extra: _optionalChain([context, 'optionalAccess', _126 => _126.extra]),
2488
+ operationId: this._activeOperationId()
2489
+ }
2490
+ });
2491
+ let probeOpensSession = false;
2492
+ try {
2493
+ if (reason === "manual-rebind") {
2494
+ this._operations.endByConnectionKey(selected.connectId, "explicit");
2495
+ await this._releaseProvisionalConnection(selected.connectId, signal);
2496
+ }
2497
+ probeOpensSession = !this._hasSessionOrConnect(selected.connectId);
2498
+ await this._ensureSession(selected.connectId, signal, selected.connectionType);
2499
+ } catch (error) {
2500
+ this._emitBindingStatus(requestId, signal.aborted ? "cancelled" : "failed");
2501
+ throw error;
2502
+ }
2503
+ if (_optionalChain([this, 'access', _127 => _127._devices, 'access', _128 => _128.get, 'call', _129 => _129(selected.connectId), 'optionalAccess', _130 => _130.deviceId]) !== expectedDeviceId) {
2504
+ if (probeOpensSession) await this._disconnectSession(selected.connectId);
2505
+ rejectedConnectId = selected.connectId;
2506
+ rejectedConnectIds.add(selected.connectId);
2507
+ continue;
2508
+ }
2509
+ if (selected.connectionType === "usb") {
2510
+ this._emitBindingStatus(requestId, "cancelled");
2511
+ return { connectId: selected.connectId };
2512
+ }
2513
+ return { connectId: selected.connectId, selectionRequestId: requestId };
2514
+ }
2515
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2516
+ code: _hwkadaptercore.HardwareErrorCode.DeviceNotFound,
2517
+ message: "No readable Trezor device is available"
2518
+ });
2519
+ }
2520
+ _emitBindingStatus(selectionRequestId, status) {
2521
+ this._emitter.emit(_hwkadaptercore.UI_REQUEST.DEVICE_BINDING_STATUS, {
2522
+ type: _hwkadaptercore.UI_REQUEST.DEVICE_BINDING_STATUS,
2523
+ payload: { selectionRequestId, status }
2524
+ });
2525
+ }
2526
+ _rememberVerifiedConnection(deviceId, connectId) {
2527
+ const device = this._devices.get(connectId);
2528
+ if (!device || device.deviceId !== deviceId) return;
2529
+ const known = _nullishCoalesce(this._knownDeviceConnections.get(deviceId), () => ( { deviceId }));
2530
+ if (device.connectionType === "ble") {
2531
+ known.bleConnectId = connectId;
2532
+ } else if (device.connectionType === "usb" && _optionalChain([device, 'access', _131 => _131.capabilities, 'optionalAccess', _132 => _132.persistentDeviceIdentity])) {
2533
+ known.usbConnectId = connectId;
2534
+ }
2535
+ this._knownDeviceConnections.set(deviceId, known);
2536
+ }
2537
+ async _emitVerifiedBinding(deviceId, connectId, selectionRequestId, context, signal) {
2538
+ if (!selectionRequestId) return false;
2539
+ const device = this._devices.get(connectId);
2540
+ if (!device || device.deviceId !== deviceId || device.connectionType !== "ble") {
2541
+ this._emitBindingStatus(selectionRequestId, "cancelled");
2542
+ return false;
2543
+ }
2544
+ const outcome = await _hwkadaptercore.requestSaveDeviceBinding.call(void 0,
2545
+ this._emitter,
2546
+ this._uiRegistry,
2547
+ {
2548
+ selectionRequestId,
2549
+ connection: { transport: "ble", connectId },
2550
+ identity: { vendor: "trezor", type: "deviceId", value: deviceId },
2551
+ extra: _optionalChain([context, 'optionalAccess', _133 => _133.extra]),
2552
+ operationId: this._activeOperationId()
2553
+ },
2554
+ signal
2555
+ );
2556
+ return outcome.saved;
1667
2557
  }
1668
2558
  /**
1669
2559
  * Pin the device's active wallet session to `passphraseState` (the target
@@ -1685,12 +2575,21 @@ var _TrezorAdapter = class _TrezorAdapter {
1685
2575
  if (reused) return;
1686
2576
  const created = await _TrezorAdapter._abortable(
1687
2577
  signal,
1688
- _TrezorAdapter._callConnector(this._connector, sessionId, "__thpCreateSession", {
1689
- passphraseMode: "prompt"
1690
- })
2578
+ this._callConnector(
2579
+ sessionId,
2580
+ "__thpCreateSession",
2581
+ {
2582
+ passphraseMode: "prompt"
2583
+ },
2584
+ signal
2585
+ )
1691
2586
  );
1692
- if (_optionalChain([created, 'optionalAccess', _35 => _35.protocol]) === "thp" && !created.thpSessionId) return;
1693
- if (_optionalChain([created, 'optionalAccess', _36 => _36.protocol]) !== "thp" && _optionalChain([created, 'optionalAccess', _37 => _37.protocol]) !== "v1") return;
2587
+ if (_optionalChain([created, 'optionalAccess', _134 => _134.protocol]) === "thp" && !created.thpSessionId || _optionalChain([created, 'optionalAccess', _135 => _135.protocol]) !== "thp" && _optionalChain([created, 'optionalAccess', _136 => _136.protocol]) !== "v1") {
2588
+ throw _hwkadaptercore.createHwkError.call(void 0, {
2589
+ code: _hwkadaptercore.HardwareErrorCode.TransportError,
2590
+ message: "Cannot verify the requested Trezor wallet session"
2591
+ });
2592
+ }
1694
2593
  const state = await this._deriveState(sessionId, signal);
1695
2594
  if (state !== passphraseState) {
1696
2595
  throw Object.assign(
@@ -1704,13 +2603,13 @@ var _TrezorAdapter = class _TrezorAdapter {
1704
2603
  }
1705
2604
  _rememberVerifiedPassphraseSession(connectId, passphraseState, sessionId, created) {
1706
2605
  let session;
1707
- if (_optionalChain([created, 'optionalAccess', _38 => _38.protocol]) === "thp" && typeof created.thpSessionId === "string") {
2606
+ if (_optionalChain([created, 'optionalAccess', _137 => _137.protocol]) === "thp" && typeof created.thpSessionId === "string") {
1708
2607
  session = {
1709
2608
  sessionId,
1710
2609
  protocol: "thp",
1711
2610
  thpSessionId: created.thpSessionId
1712
2611
  };
1713
- } else if (_optionalChain([created, 'optionalAccess', _39 => _39.protocol]) === "v1") {
2612
+ } else if (_optionalChain([created, 'optionalAccess', _138 => _138.protocol]) === "v1") {
1714
2613
  session = {
1715
2614
  sessionId,
1716
2615
  protocol: "v1"
@@ -1730,7 +2629,7 @@ var _TrezorAdapter = class _TrezorAdapter {
1730
2629
  }
1731
2630
  }
1732
2631
  async _reuseVerifiedPassphraseSession(connectId, sessionId, passphraseState, signal) {
1733
- const cached = _optionalChain([this, 'access', _40 => _40._verifiedPassphraseSessionsByConnectId, 'access', _41 => _41.get, 'call', _42 => _42(connectId), 'optionalAccess', _43 => _43.get, 'call', _44 => _44(passphraseState)]);
2632
+ const cached = _optionalChain([this, 'access', _139 => _139._verifiedPassphraseSessionsByConnectId, 'access', _140 => _140.get, 'call', _141 => _141(connectId), 'optionalAccess', _142 => _142.get, 'call', _143 => _143(passphraseState)]);
1734
2633
  if (!cached || cached.sessionId !== sessionId) {
1735
2634
  this._forgetVerifiedPassphraseSession(connectId, passphraseState);
1736
2635
  return false;
@@ -1740,9 +2639,14 @@ var _TrezorAdapter = class _TrezorAdapter {
1740
2639
  if (!cached.thpSessionId) return false;
1741
2640
  await _TrezorAdapter._abortable(
1742
2641
  signal,
1743
- _TrezorAdapter._callConnector(this._connector, sessionId, "__thpSelectSession", {
1744
- thpSessionId: cached.thpSessionId
1745
- })
2642
+ this._callConnector(
2643
+ sessionId,
2644
+ "__thpSelectSession",
2645
+ {
2646
+ thpSessionId: cached.thpSessionId
2647
+ },
2648
+ signal
2649
+ )
1746
2650
  );
1747
2651
  }
1748
2652
  const state = await this._deriveState(sessionId, signal);
@@ -1760,9 +2664,14 @@ var _TrezorAdapter = class _TrezorAdapter {
1760
2664
  async _createFreshAppSession(sessionId, signal) {
1761
2665
  await _TrezorAdapter._abortable(
1762
2666
  signal,
1763
- _TrezorAdapter._callConnector(this._connector, sessionId, "__thpCreateSession", {
1764
- passphraseMode: "empty"
1765
- })
2667
+ this._callConnector(
2668
+ sessionId,
2669
+ "__thpCreateSession",
2670
+ {
2671
+ passphraseMode: "empty"
2672
+ },
2673
+ signal
2674
+ )
1766
2675
  );
1767
2676
  }
1768
2677
  /**
@@ -1777,12 +2686,17 @@ var _TrezorAdapter = class _TrezorAdapter {
1777
2686
  async _deriveState(sessionId, signal) {
1778
2687
  const derived = await _TrezorAdapter._abortable(
1779
2688
  signal,
1780
- _TrezorAdapter._callConnector(this._connector, sessionId, "btcGetPublicKey", {
1781
- path: "m/44'/0'/0'",
1782
- showOnDevice: false
1783
- })
2689
+ this._callConnector(
2690
+ sessionId,
2691
+ "btcGetPublicKey",
2692
+ {
2693
+ path: "m/44'/0'/0'",
2694
+ showOnDevice: false
2695
+ },
2696
+ signal
2697
+ )
1784
2698
  );
1785
- const state = _optionalChain([derived, 'optionalAccess', _45 => _45.publicKey]);
2699
+ const state = _optionalChain([derived, 'optionalAccess', _144 => _144.publicKey]);
1786
2700
  if (!state) {
1787
2701
  throw Object.assign(new Error("Failed to derive passphrase state (wallet public key)"), {
1788
2702
  code: _hwkadaptercore.HardwareErrorCode.UnknownError
@@ -1790,25 +2704,36 @@ var _TrezorAdapter = class _TrezorAdapter {
1790
2704
  }
1791
2705
  return state;
1792
2706
  }
1793
- async _resolvePassphraseState(connectId, passphraseState, signal) {
2707
+ async _resolvePassphraseState(connectId, passphraseState, signal, operationId) {
1794
2708
  try {
1795
- const sessionId = await this._ensureSession(connectId, signal);
2709
+ const sessionId = operationId ? this._sessions.get(this._operations.resolve(operationId).connectId) : await this._ensureSession(connectId, signal);
2710
+ if (!sessionId) {
2711
+ return _hwkadaptercore.failure.call(void 0,
2712
+ _hwkadaptercore.HardwareErrorCode.OperationEnded,
2713
+ "Trezor operation connection is no longer active"
2714
+ );
2715
+ }
1796
2716
  if (passphraseState) {
1797
2717
  await this._alignAppSession(connectId, sessionId, passphraseState, signal);
1798
2718
  return _hwkadaptercore.success.call(void 0, passphraseState);
1799
2719
  }
1800
2720
  const created = await _TrezorAdapter._abortable(
1801
2721
  signal,
1802
- _TrezorAdapter._callConnector(this._connector, sessionId, "__thpCreateSession", {
1803
- passphraseMode: "prompt"
1804
- })
2722
+ this._callConnector(
2723
+ sessionId,
2724
+ "__thpCreateSession",
2725
+ {
2726
+ passphraseMode: "prompt"
2727
+ },
2728
+ signal
2729
+ )
1805
2730
  );
1806
2731
  const state = await this._deriveState(sessionId, signal);
1807
2732
  const features = await _TrezorAdapter._abortable(
1808
2733
  signal,
1809
- _TrezorAdapter._callConnector(this._connector, sessionId, "getFeatures", { refresh: true })
2734
+ this._callConnector(sessionId, "getFeatures", { refresh: true }, signal)
1810
2735
  );
1811
- if (_optionalChain([features, 'optionalAccess', _46 => _46.passphrase_protection]) !== true) {
2736
+ if (_optionalChain([features, 'optionalAccess', _145 => _145.passphrase_protection]) !== true) {
1812
2737
  return _hwkadaptercore.success.call(void 0, null);
1813
2738
  }
1814
2739
  this._rememberVerifiedPassphraseSession(connectId, state, sessionId, created);
@@ -1856,7 +2781,16 @@ var _TrezorAdapter = class _TrezorAdapter {
1856
2781
  }
1857
2782
  const trustedNumericCode = typeof typed.code === "number" && KNOWN_HARDWARE_ERROR_CODES.has(typed.code) ? typed.code : void 0;
1858
2783
  const code = _nullishCoalesce(_nullishCoalesce(trustedNumericCode, () => ( _TrezorAdapter._mapTrezorFailureCode(error))), () => ( _hwkadaptercore.HardwareErrorCode.UnknownError));
1859
- return _hwkadaptercore.failure.call(void 0, code, _nullishCoalesce(typed.message, () => ( String(error))));
2784
+ const stampedOrigin = _optionalChain([error, 'optionalAccess', _146 => _146.origin]);
2785
+ const origin = stampedOrigin === "device" || stampedOrigin === "transport" || stampedOrigin === "host" ? stampedOrigin : _hwkadaptercore.defaultOriginForCode.call(void 0, code);
2786
+ const params = typed.params && typeof typed.params === "object" ? typed.params : void 0;
2787
+ return _hwkadaptercore.failure.call(void 0,
2788
+ code,
2789
+ _nullishCoalesce(typed.message, () => ( String(error))),
2790
+ params,
2791
+ origin,
2792
+ _hwkadaptercore.isHwkRecoveryHint.call(void 0, typed.recovery) ? typed.recovery : void 0
2793
+ );
1860
2794
  }
1861
2795
  /**
1862
2796
  * Emit `REQUEST_DEVICE_PERMISSION` and await the host's
@@ -1871,29 +2805,33 @@ var _TrezorAdapter = class _TrezorAdapter {
1871
2805
  * - no `connectId` → environment-level check (scan / picker)
1872
2806
  * - with `connectId` → device-level check (per-call gating)
1873
2807
  */
1874
- async _ensureDevicePermission(connectId, deviceId) {
2808
+ async _ensureDevicePermission(connectId, deviceId, requestedTransport) {
1875
2809
  if (!this._emitter.listenerCount(_hwkadaptercore.UI_REQUEST.REQUEST_DEVICE_PERMISSION)) {
1876
2810
  return;
1877
2811
  }
1878
- const transportType = _nullishCoalesce(this.activeTransport, () => ( "usb"));
2812
+ const transportType = _nullishCoalesce(_nullishCoalesce(requestedTransport, () => ( this.activeTransport)), () => ( "usb"));
2813
+ const operationId = this._activeOperationId();
1879
2814
  const waitPromise = this._uiRegistry.wait(
1880
2815
  _hwkadaptercore.UI_REQUEST.REQUEST_DEVICE_PERMISSION,
1881
- { timeoutMs: 6e4 }
2816
+ { timeoutMs: 6e4, operationId }
1882
2817
  );
1883
2818
  this._emitter.emit(_hwkadaptercore.UI_REQUEST.REQUEST_DEVICE_PERMISSION, {
1884
2819
  type: _hwkadaptercore.UI_REQUEST.REQUEST_DEVICE_PERMISSION,
1885
- payload: { transportType, connectId, deviceId }
2820
+ payload: { transportType, connectId, deviceId, operationId }
1886
2821
  });
1887
- const { granted } = await waitPromise;
2822
+ const { granted, reason } = await waitPromise;
1888
2823
  if (!granted) {
1889
2824
  throw Object.assign(new Error("Device permission denied"), {
1890
- code: _hwkadaptercore.HardwareErrorCode.DevicePermissionDenied
2825
+ code: _hwkadaptercore.HardwareErrorCode.DevicePermissionDenied,
2826
+ ...reason ? { params: { permissionDeniedReason: reason } } : {}
1891
2827
  });
1892
2828
  }
1893
2829
  }
1894
2830
  };
1895
2831
  // Keys are matched after normalizeLogKey, so snake_case/camelCase both hit.
1896
2832
  _TrezorAdapter._sensitiveLogKeys = /* @__PURE__ */ new Set([
2833
+ "extra",
2834
+ "knownconnections",
1897
2835
  "credential",
1898
2836
  "credentials",
1899
2837
  "entropy",
@@ -2012,7 +2950,7 @@ async function fetchDefinitionHex(url, fetchImpl) {
2012
2950
  }
2013
2951
  return arrayBufferToHex(await response.arrayBuffer());
2014
2952
  });
2015
- } catch (e5) {
2953
+ } catch (e7) {
2016
2954
  return void 0;
2017
2955
  }
2018
2956
  }
@@ -2041,7 +2979,7 @@ async function fetchSolanaTokenDefinition({
2041
2979
  }
2042
2980
  return Buffer.from(await response.arrayBuffer()).toString("hex");
2043
2981
  });
2044
- } catch (e6) {
2982
+ } catch (e8) {
2045
2983
  return void 0;
2046
2984
  }
2047
2985
  }