@deeeed/metamask-harness 0.44.0 → 0.44.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/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.44.1 - 2026-08-27
6
+
7
+ ### Fixed
8
+
9
+ - Accept retained market context as lifecycle-valid Homepage evidence during account-only switches.
10
+ - Require controller-backed Mobile unlock proof before continuing a recipe after startup or restart.
11
+
5
12
  ## 0.44.0 - 2026-08-25
6
13
 
7
14
  ### Fixed
@@ -418,8 +418,15 @@ const COMMANDS = {
418
418
  var agenticPresent = typeof globalThis.__AGENTIC__ !== 'undefined';
419
419
  var route = globalThis.__AGENTIC__?.getRoute() || null;
420
420
  var account = null;
421
+ var keyringUnlocked = false;
421
422
  try { account = globalThis.__AGENTIC__?.getSelectedAccount() || null; } catch(e) {}
422
- return { route: route, account: account, agenticPresent: agenticPresent };
423
+ try { keyringUnlocked = globalThis.Engine?.context?.KeyringController?.isUnlocked?.() === true; } catch(e) {}
424
+ return {
425
+ route: route,
426
+ account: account,
427
+ agenticPresent: agenticPresent,
428
+ keyringUnlocked: keyringUnlocked,
429
+ };
423
430
  })()`;
424
431
  const snapshot = await cdpEval(client, expr);
425
432
  return { ...snapshot, deviceName: deviceName || '', platform: platform || '' };
@@ -680,6 +680,13 @@ function frameTime(record) {
680
680
 
681
681
  function frameIsFresh(record) {
682
682
  if (record.fresh_for_lifecycle === false) return false;
683
+ if (record.source === "retained_market_context") {
684
+ return (
685
+ record.lifecycle === "account_switch" &&
686
+ !isAccountContentVariant(record.content_variant) &&
687
+ record.fresh_for_lifecycle === true
688
+ );
689
+ }
683
690
  return (
684
691
  FRESH_SOURCES.has(record.source) ||
685
692
  ((record.source === "resident_state" || record.source === "memory_cache") &&
@@ -94,7 +94,7 @@ async function waitForWalletState(input, initialStatus, timeoutMs) {
94
94
  const deadline = Date.now() + timeoutMs;
95
95
  let last = initialStatus;
96
96
  while (true) {
97
- if (selectedAccount(last, input) || routeName(last, input) === 'Login') return last;
97
+ if (isUnlockedStatus(last, input) || routeName(last, input) === 'Login') return last;
98
98
  if (Date.now() >= deadline) break;
99
99
  await new Promise((resolve) => setTimeout(resolve, 250));
100
100
  try {
@@ -104,6 +104,11 @@ async function waitForWalletState(input, initialStatus, timeoutMs) {
104
104
  // still bounds this loop and the final error reports the last wallet state.
105
105
  }
106
106
  }
107
+ if (selectedAccount(last, input)) {
108
+ throw new Error(
109
+ `Mobile wallet route and controller unlock state did not settle within the remaining ${timeoutMs}ms target budget (route ${routeName(last, input) || 'unknown'}).`,
110
+ );
111
+ }
107
112
  throw new Error(
108
113
  `No wallet onboarded on this device (route ${routeName(last, input) || 'unknown'}, empty wallet status) — unlock has nothing to unlock.\n Next: mm-harness fixtures set # applies the fixture wallet, then re-run: mm-harness call ensure_unlocked`,
109
114
  );
@@ -116,7 +121,7 @@ async function waitForUnlocked(input, timeoutMs = 30000) {
116
121
  while (Date.now() < deadline) {
117
122
  try {
118
123
  last = await statusBeforeDeadline(input, deadline);
119
- if (selectedAccount(last, input) && routeName(last, input) !== 'Login') return last;
124
+ if (isUnlockedStatus(last, input)) return last;
120
125
  } catch (error) {
121
126
  lastError = error;
122
127
  }
@@ -126,7 +131,14 @@ async function waitForUnlocked(input, timeoutMs = 30000) {
126
131
  }
127
132
 
128
133
  function isUnlockedStatus(status, input) {
129
- return Boolean(selectedAccount(status, input)) && routeName(status, input) !== 'Login';
134
+ const selected = selectedStatus(status, input);
135
+ const route = routeName(status, input);
136
+ return (
137
+ Boolean(selectedAccount(status, input)) &&
138
+ selected?.keyringUnlocked === true &&
139
+ route.length > 0 &&
140
+ route !== 'Login'
141
+ );
130
142
  }
131
143
 
132
144
  async function waitForStableUnlocked(input, initialStatus, stableMs = 750) {
@@ -188,6 +200,8 @@ export async function ensureUnlocked(input) {
188
200
  action: input.action,
189
201
  unlocked: true,
190
202
  alreadyUnlocked: true,
203
+ keyringUnlocked:
204
+ selectedStatus(stableBefore, input)?.keyringUnlocked === true,
191
205
  account: selectedAccount(stableBefore, input),
192
206
  deviceName: selectedStatus(stableBefore, input)?.deviceName ?? null,
193
207
  platform: selectedStatus(stableBefore, input)?.platform ?? null,
@@ -209,11 +223,13 @@ export async function ensureUnlocked(input) {
209
223
  }
210
224
  try {
211
225
  const current = await statusBeforeDeadline(input, unlockDeadline);
212
- if (selectedAccount(current, input) && routeName(current, input) !== 'Login') {
226
+ if (isUnlockedStatus(current, input)) {
213
227
  return {
214
228
  action: input.action,
215
229
  unlocked: true,
216
230
  alreadyUnlocked: true,
231
+ keyringUnlocked:
232
+ selectedStatus(current, input)?.keyringUnlocked === true,
217
233
  account: selectedAccount(current, input),
218
234
  deviceName: selectedStatus(current, input)?.deviceName ?? null,
219
235
  platform: selectedStatus(current, input)?.platform ?? null,
@@ -246,6 +262,8 @@ export async function ensureUnlocked(input) {
246
262
  return {
247
263
  action: input.action,
248
264
  unlocked: Boolean(result?.ok ?? result?.unlocked ?? true),
265
+ keyringUnlocked:
266
+ selectedStatus(stableAfter, input)?.keyringUnlocked === true,
249
267
  account: selectedAccount(stableAfter, input),
250
268
  route: selectedStatus(stableAfter, input)?.route ?? null,
251
269
  deviceName: selectedStatus(stableAfter, input)?.deviceName ?? null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deeeed/metamask-harness",
3
- "version": "0.44.0",
3
+ "version": "0.44.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "mm-harness": "bin/mm-harness"