@tomflow/proflow-execution-browser-extension 0.1.25 → 0.1.27

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.
@@ -47,7 +47,7 @@ export declare const behaviorAdapter: {
47
47
  ok: true;
48
48
  status: "SUCCEEDED";
49
49
  moduleRef: "execution-browser-extension";
50
- moduleVersion: "0.1.25";
50
+ moduleVersion: "0.1.27";
51
51
  data: {
52
52
  loadDir: string;
53
53
  };
@@ -60,7 +60,7 @@ export declare const behaviorAdapter: {
60
60
  readonly ok: true;
61
61
  readonly status: "SUCCEEDED";
62
62
  readonly moduleRef: "execution-browser-extension";
63
- readonly moduleVersion: "0.1.25";
63
+ readonly moduleVersion: "0.1.27";
64
64
  };
65
65
  observedEffects: never[];
66
66
  }>;
@@ -70,7 +70,7 @@ export declare const behaviorAdapter: {
70
70
  ok: true;
71
71
  status: "SUCCEEDED";
72
72
  moduleRef: "execution-browser-extension";
73
- moduleVersion: "0.1.25";
73
+ moduleVersion: "0.1.27";
74
74
  data: {
75
75
  setupStatus: "ACTION_REQUIRED" | "READY";
76
76
  runtimeStatus: "NOT_APPLICABLE";
@@ -95,7 +95,7 @@ export declare const behaviorAdapter: {
95
95
  result: {
96
96
  contract: "deployment.result.v1";
97
97
  moduleRef: "execution-browser-extension";
98
- moduleVersion: "0.1.25";
98
+ moduleVersion: "0.1.27";
99
99
  ok: false;
100
100
  status: "ACTION_REQUIRED";
101
101
  data: {
@@ -127,14 +127,14 @@ export declare const behaviorAdapter: {
127
127
  readonly ok: true;
128
128
  readonly status: "SUCCEEDED";
129
129
  readonly moduleRef: "execution-browser-extension";
130
- readonly moduleVersion: "0.1.25";
130
+ readonly moduleVersion: "0.1.27";
131
131
  };
132
132
  observedEffects: string[];
133
133
  } | {
134
134
  result: {
135
135
  contract: "deployment.result.v1";
136
136
  moduleRef: "execution-browser-extension";
137
- moduleVersion: "0.1.25";
137
+ moduleVersion: "0.1.27";
138
138
  ok: false;
139
139
  status: "FAILED";
140
140
  error: {
@@ -167,7 +167,7 @@ export declare const behaviorAdapter: {
167
167
  ok: true;
168
168
  status: "SUCCEEDED";
169
169
  moduleRef: "execution-browser-extension";
170
- moduleVersion: "0.1.25";
170
+ moduleVersion: "0.1.27";
171
171
  data: {
172
172
  docs: string;
173
173
  };
@@ -180,11 +180,11 @@ export declare const behaviorAdapter: {
180
180
  readonly ok: true;
181
181
  readonly status: "SUCCEEDED";
182
182
  readonly moduleRef: "execution-browser-extension";
183
- readonly moduleVersion: "0.1.25";
183
+ readonly moduleVersion: "0.1.27";
184
184
  } | {
185
185
  contract: "deployment.result.v1";
186
186
  moduleRef: "execution-browser-extension";
187
- moduleVersion: "0.1.25";
187
+ moduleVersion: "0.1.27";
188
188
  ok: false;
189
189
  status: "FAILED";
190
190
  error: {
@@ -201,7 +201,7 @@ export declare const behaviorAdapter: {
201
201
  readonly ok: true;
202
202
  readonly status: "SUCCEEDED";
203
203
  readonly moduleRef: "execution-browser-extension";
204
- readonly moduleVersion: "0.1.25";
204
+ readonly moduleVersion: "0.1.27";
205
205
  };
206
206
  observedEffects: never[];
207
207
  }>;
@@ -3,7 +3,7 @@ export declare const descriptor: {
3
3
  readonly contractVersion: "1.0.0";
4
4
  readonly moduleRef: "execution-browser-extension";
5
5
  readonly packageName: "@tomflow/proflow-execution-browser-extension";
6
- readonly moduleVersion: "0.1.25";
6
+ readonly moduleVersion: "0.1.27";
7
7
  readonly kind: "browser-extension";
8
8
  readonly templateVersion: "1.0.0";
9
9
  readonly platformCompatibility: ">=1.0.0 <2.0.0";
@@ -3,7 +3,7 @@ export const descriptor = {
3
3
  contractVersion: "1.0.0",
4
4
  moduleRef: "execution-browser-extension",
5
5
  packageName: "@tomflow/proflow-execution-browser-extension",
6
- moduleVersion: "0.1.25",
6
+ moduleVersion: "0.1.27",
7
7
  kind: "browser-extension",
8
8
  templateVersion: "1.0.0",
9
9
  platformCompatibility: ">=1.0.0 <2.0.0",
@@ -13866,6 +13866,13 @@ function createTaskObserver(options) {
13866
13866
  return Object.freeze({ advance, drive });
13867
13867
  }
13868
13868
 
13869
+ // packages/execution-browser-extension/src/recovery-trigger.ts
13870
+ function shouldTriggerObserverRecovery(previous, current) {
13871
+ if (current.pageState !== "IDLE") return false;
13872
+ if (!previous) return true;
13873
+ return previous.pageState !== "IDLE" || previous.url !== current.url || previous.contentInstanceId !== current.contentInstanceId || previous.activityKind !== current.activityKind;
13874
+ }
13875
+
13869
13876
  // packages/execution-browser-extension/extension/background.ts
13870
13877
  var extensionInstanceId = `extension:${crypto.randomUUID()}`;
13871
13878
  var sessions = /* @__PURE__ */ new Map();
@@ -14190,6 +14197,7 @@ var observerRecoveryRetryCount = 0;
14190
14197
  function runObserverRecovery() {
14191
14198
  if (observerRecoveryInFlight) return observerRecoveryInFlight;
14192
14199
  observerRecoveryInFlight = (async () => {
14200
+ let recoveryNeedsRetry = false;
14193
14201
  await collaborationCarrier.recoverPending(50).catch(() => void 0);
14194
14202
  const signalBatch = await invokeObserverApplication(
14195
14203
  "execution.listSignals",
@@ -14219,18 +14227,14 @@ function runObserverRecovery() {
14219
14227
  signalRef: candidate.signalRef
14220
14228
  });
14221
14229
  } catch {
14230
+ recoveryNeedsRetry = true;
14222
14231
  }
14223
14232
  }
14224
14233
  }
14225
14234
  const listed = await invokeTaskApplication("task.list", {}).catch(
14226
14235
  () => null
14227
14236
  );
14228
- if (listed === null && observerRecoveryRetryCount < 6) {
14229
- observerRecoveryRetryCount += 1;
14230
- setTimeout(() => void runObserverRecovery(), 2e3);
14231
- } else if (listed !== null) {
14232
- observerRecoveryRetryCount = 0;
14233
- }
14237
+ if (listed === null) recoveryNeedsRetry = true;
14234
14238
  if (isRecord(listed) && Array.isArray(listed.tasks)) {
14235
14239
  for (const candidate of listed.tasks.slice(0, 100)) {
14236
14240
  if (!isRecord(candidate) || typeof candidate.taskId !== "string")
@@ -14239,10 +14243,20 @@ function runObserverRecovery() {
14239
14243
  continue;
14240
14244
  await invokeTaskApplication("task.ensureWorkers", {
14241
14245
  taskId: candidate.taskId
14242
- }).catch(() => void 0);
14243
- await taskObserver.drive(candidate.taskId).catch(() => void 0);
14246
+ }).catch(() => {
14247
+ recoveryNeedsRetry = true;
14248
+ });
14249
+ await taskObserver.drive(candidate.taskId).catch(() => {
14250
+ recoveryNeedsRetry = true;
14251
+ });
14244
14252
  }
14245
14253
  }
14254
+ if (recoveryNeedsRetry && observerRecoveryRetryCount < 6) {
14255
+ observerRecoveryRetryCount += 1;
14256
+ setTimeout(() => void runObserverRecovery(), 2e3);
14257
+ } else if (!recoveryNeedsRetry) {
14258
+ observerRecoveryRetryCount = 0;
14259
+ }
14246
14260
  const previousSystemState = await loadSystemObserverState().catch(
14247
14261
  () => null
14248
14262
  );
@@ -14285,6 +14299,21 @@ async function contentCommand(tabId, command) {
14285
14299
  }
14286
14300
  return response.value;
14287
14301
  }
14302
+ async function waitForSubmittedMessage(tabId, fingerprint) {
14303
+ for (let attempt = 0; attempt < 60; attempt += 1) {
14304
+ try {
14305
+ const value = await contentCommand(tabId, {
14306
+ operation: "verify",
14307
+ fingerprint
14308
+ });
14309
+ if (isRecord(value) && value.verified === true)
14310
+ return observationFor(tabId);
14311
+ } catch {
14312
+ }
14313
+ await sleep(250);
14314
+ }
14315
+ throw new Error("MESSAGE_SUBMIT_REALITY_UNCONFIRMED");
14316
+ }
14288
14317
  function numeric(value, name) {
14289
14318
  if (!Number.isInteger(value)) throw new Error(`${name}_INVALID`);
14290
14319
  return value;
@@ -14312,11 +14341,12 @@ async function executeCommand(command) {
14312
14341
  return contentCommand(tabId, { operation: "observe" });
14313
14342
  if (command.type === "SUBMIT") {
14314
14343
  const before = observationFor(tabId);
14344
+ const fingerprint = text(command.fingerprint, "FINGERPRINT");
14315
14345
  try {
14316
14346
  await contentCommand(tabId, {
14317
14347
  operation: "submit",
14318
14348
  value: text(command.text, "TEXT"),
14319
- fingerprint: text(command.fingerprint, "FINGERPRINT")
14349
+ fingerprint
14320
14350
  });
14321
14351
  } catch (error46) {
14322
14352
  const replacement = await waitForObservation(
@@ -14325,7 +14355,7 @@ async function executeCommand(command) {
14325
14355
  ).catch(() => null);
14326
14356
  if (!replacement) throw error46;
14327
14357
  }
14328
- return waitForObservation(tabId);
14358
+ return waitForSubmittedMessage(tabId, fingerprint);
14329
14359
  }
14330
14360
  if (command.type === "VERIFY")
14331
14361
  return contentCommand(tabId, {
@@ -14670,6 +14700,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
14670
14700
  return true;
14671
14701
  }
14672
14702
  if (message.type === "PROFLOW_CONTENT_OBSERVATION" && message.observation && sender.tab?.id !== void 0 && sender.tab.windowId !== void 0) {
14703
+ const previous = sessions.get(sender.tab.id);
14673
14704
  const observed = {
14674
14705
  ...message.observation,
14675
14706
  tabId: sender.tab.id,
@@ -14677,7 +14708,8 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
14677
14708
  };
14678
14709
  sessions.set(sender.tab.id, observed);
14679
14710
  void persistSnapshot();
14680
- if (observed.pageState === "IDLE") void runObserverRecovery();
14711
+ if (shouldTriggerObserverRecovery(previous, observed))
14712
+ void runObserverRecovery();
14681
14713
  sendResponse({ accepted: true, extensionInstanceId });
14682
14714
  return;
14683
14715
  }
@@ -1,3 +1,4 @@
1
+ import { containsSubmittedFingerprint } from "../src/submitted-message.js";
1
2
  const contentInstanceId = `content:${crypto.randomUUID()}`;
2
3
  function pageState() {
3
4
  if (document.querySelector('[role="dialog"]'))
@@ -25,7 +26,10 @@ function safeElement(selector) {
25
26
  return element;
26
27
  }
27
28
  function hasFingerprint(fingerprint) {
28
- return Boolean(fingerprint && document.body.innerText.includes(fingerprint));
29
+ return containsSubmittedFingerprint([...document.querySelectorAll('[data-message-author-role="user"]')].map((element) => ({
30
+ authorRole: element.getAttribute("data-message-author-role"),
31
+ textContent: element.textContent,
32
+ })), fingerprint);
29
33
  }
30
34
  chrome.runtime.onMessage.addListener((command, _sender, sendResponse) => {
31
35
  void (async () => {
package/dist/src/index.js CHANGED
@@ -240,7 +240,9 @@ export function createExecutionBrowserExtension(options) {
240
240
  throw new ExecutionBrowserError("PRECONDITION_FAILED", "ROLE_URL_MISMATCH");
241
241
  const precondition = await effectStarted(raw);
242
242
  const opened = await options.browser.open(request.input.roleUrl);
243
- await options.browser.submit(opened.tabId, `WORKER_BIND ${request.input.bootstrapFingerprint}`, request.input.bootstrapFingerprint);
243
+ const submitted = await options.browser.submit(opened.tabId, `WORKER_BIND ${request.input.bootstrapFingerprint}`, request.input.bootstrapFingerprint);
244
+ if (!(await options.browser.hasMessage(submitted.tabId, request.input.bootstrapFingerprint)))
245
+ throw new ExecutionBrowserError("UNKNOWN_SIDE_EFFECT", "CREATE_MESSAGE_REALITY_UNCONFIRMED");
244
246
  const observed = await options.browser.observe(opened.tabId);
245
247
  const identity = parseCarrierIdentity(observed.url);
246
248
  if (identity.roleRef !== request.input.roleRef || !identity.workerRef)
@@ -0,0 +1,8 @@
1
+ type RecoveryObservation = {
2
+ url: string;
3
+ contentInstanceId: string;
4
+ pageState: "IDLE" | "BUSY" | "BLOCKED" | "UNKNOWN";
5
+ activityKind: string | null;
6
+ };
7
+ export declare function shouldTriggerObserverRecovery(previous: RecoveryObservation | undefined, current: RecoveryObservation): boolean;
8
+ export {};
@@ -0,0 +1,10 @@
1
+ export function shouldTriggerObserverRecovery(previous, current) {
2
+ if (current.pageState !== "IDLE")
3
+ return false;
4
+ if (!previous)
5
+ return true;
6
+ return (previous.pageState !== "IDLE" ||
7
+ previous.url !== current.url ||
8
+ previous.contentInstanceId !== current.contentInstanceId ||
9
+ previous.activityKind !== current.activityKind);
10
+ }
@@ -0,0 +1,5 @@
1
+ export type SubmittedMessageCandidate = {
2
+ authorRole: string | null;
3
+ textContent: string | null;
4
+ };
5
+ export declare function containsSubmittedFingerprint(candidates: Iterable<SubmittedMessageCandidate>, fingerprint: string | undefined): boolean;
@@ -0,0 +1,10 @@
1
+ export function containsSubmittedFingerprint(candidates, fingerprint) {
2
+ if (!fingerprint)
3
+ return false;
4
+ for (const candidate of candidates) {
5
+ if (candidate.authorRole === "user" &&
6
+ candidate.textContent?.includes(fingerprint))
7
+ return true;
8
+ }
9
+ return false;
10
+ }
@@ -12,6 +12,7 @@ import {
12
12
  type TaskObserverDiagnosticAssessment,
13
13
  type TaskObserverDiagnosticFailure,
14
14
  } from "../src/task-observer.js";
15
+ import { shouldTriggerObserverRecovery } from "../src/recovery-trigger.js";
15
16
 
16
17
  type PageState = "IDLE" | "BUSY" | "BLOCKED" | "UNKNOWN";
17
18
  type ActivityKind =
@@ -587,6 +588,7 @@ let observerRecoveryRetryCount = 0;
587
588
  function runObserverRecovery() {
588
589
  if (observerRecoveryInFlight) return observerRecoveryInFlight;
589
590
  observerRecoveryInFlight = (async () => {
591
+ let recoveryNeedsRetry = false;
590
592
  await collaborationCarrier.recoverPending(50).catch(() => undefined);
591
593
  const signalBatch = await invokeObserverApplication(
592
594
  "execution.listSignals",
@@ -634,18 +636,14 @@ function runObserverRecovery() {
634
636
  });
635
637
  } catch {
636
638
  // Leave the durable signal unacknowledged for the next bounded recovery pass.
639
+ recoveryNeedsRetry = true;
637
640
  }
638
641
  }
639
642
  }
640
643
  const listed = await invokeTaskApplication("task.list", {}).catch(
641
644
  () => null,
642
645
  );
643
- if (listed === null && observerRecoveryRetryCount < 6) {
644
- observerRecoveryRetryCount += 1;
645
- setTimeout(() => void runObserverRecovery(), 2_000);
646
- } else if (listed !== null) {
647
- observerRecoveryRetryCount = 0;
648
- }
646
+ if (listed === null) recoveryNeedsRetry = true;
649
647
  if (isRecord(listed) && Array.isArray(listed.tasks)) {
650
648
  for (const candidate of listed.tasks.slice(0, 100)) {
651
649
  if (!isRecord(candidate) || typeof candidate.taskId !== "string")
@@ -663,10 +661,22 @@ function runObserverRecovery() {
663
661
  // missing roles are re-provisioned by the Host/Execution path.
664
662
  await invokeTaskApplication("task.ensureWorkers", {
665
663
  taskId: candidate.taskId,
666
- }).catch(() => undefined);
667
- await taskObserver.drive(candidate.taskId).catch(() => undefined);
664
+ }).catch(() => {
665
+ recoveryNeedsRetry = true;
666
+ });
667
+ await taskObserver.drive(candidate.taskId).catch(() => {
668
+ recoveryNeedsRetry = true;
669
+ });
668
670
  }
669
671
  }
672
+ // A bounded retry continues the same stable Execution identities. Execution
673
+ // remains the no-blind-replay authority for APPLIED/NOT_APPLIED/UNKNOWN.
674
+ if (recoveryNeedsRetry && observerRecoveryRetryCount < 6) {
675
+ observerRecoveryRetryCount += 1;
676
+ setTimeout(() => void runObserverRecovery(), 2_000);
677
+ } else if (!recoveryNeedsRetry) {
678
+ observerRecoveryRetryCount = 0;
679
+ }
670
680
  const previousSystemState = await loadSystemObserverState().catch(
671
681
  () => null,
672
682
  );
@@ -724,6 +734,27 @@ async function contentCommand(
724
734
  return response.value;
725
735
  }
726
736
 
737
+ async function waitForSubmittedMessage(
738
+ tabId: number,
739
+ fingerprint: string,
740
+ ): Promise<ContentObservation> {
741
+ for (let attempt = 0; attempt < 60; attempt += 1) {
742
+ try {
743
+ const value = await contentCommand(tabId, {
744
+ operation: "verify",
745
+ fingerprint,
746
+ });
747
+ if (isRecord(value) && value.verified === true)
748
+ return observationFor(tabId);
749
+ } catch {
750
+ // Navigation can replace the content instance after submit. The next
751
+ // bounded observation/verification pass uses the newly published session.
752
+ }
753
+ await sleep(250);
754
+ }
755
+ throw new Error("MESSAGE_SUBMIT_REALITY_UNCONFIRMED");
756
+ }
757
+
727
758
  function numeric(value: unknown, name: string): number {
728
759
  if (!Number.isInteger(value)) throw new Error(`${name}_INVALID`);
729
760
  return value as number;
@@ -757,11 +788,12 @@ async function executeCommand(command: BridgeCommand): Promise<unknown> {
757
788
  return contentCommand(tabId, { operation: "observe" });
758
789
  if (command.type === "SUBMIT") {
759
790
  const before = observationFor(tabId);
791
+ const fingerprint = text(command.fingerprint, "FINGERPRINT");
760
792
  try {
761
793
  await contentCommand(tabId, {
762
794
  operation: "submit",
763
795
  value: text(command.text, "TEXT"),
764
- fingerprint: text(command.fingerprint, "FINGERPRINT"),
796
+ fingerprint,
765
797
  });
766
798
  } catch (error) {
767
799
  const replacement = await waitForObservation(
@@ -770,7 +802,7 @@ async function executeCommand(command: BridgeCommand): Promise<unknown> {
770
802
  ).catch(() => null);
771
803
  if (!replacement) throw error;
772
804
  }
773
- return waitForObservation(tabId);
805
+ return waitForSubmittedMessage(tabId, fingerprint);
774
806
  }
775
807
  if (command.type === "VERIFY")
776
808
  return contentCommand(tabId, {
@@ -1187,6 +1219,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
1187
1219
  sender.tab?.id !== undefined &&
1188
1220
  sender.tab.windowId !== undefined
1189
1221
  ) {
1222
+ const previous = sessions.get(sender.tab.id);
1190
1223
  const observed: ContentObservation = {
1191
1224
  ...message.observation,
1192
1225
  tabId: sender.tab.id,
@@ -1194,7 +1227,8 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
1194
1227
  };
1195
1228
  sessions.set(sender.tab.id, observed);
1196
1229
  void persistSnapshot();
1197
- if (observed.pageState === "IDLE") void runObserverRecovery();
1230
+ if (shouldTriggerObserverRecovery(previous, observed))
1231
+ void runObserverRecovery();
1198
1232
  sendResponse({ accepted: true, extensionInstanceId });
1199
1233
  return;
1200
1234
  }
@@ -1,3 +1,5 @@
1
+ import { containsSubmittedFingerprint } from "../src/submitted-message.js";
2
+
1
3
  type PageState = "IDLE" | "BUSY" | "BLOCKED" | "UNKNOWN";
2
4
  type ContentCommand = {
3
5
  type: "PROFLOW_PAGE_COMMAND";
@@ -61,7 +63,15 @@ function safeElement(selector: string | undefined): HTMLElement {
61
63
  }
62
64
 
63
65
  function hasFingerprint(fingerprint: string | undefined): boolean {
64
- return Boolean(fingerprint && document.body.innerText.includes(fingerprint));
66
+ return containsSubmittedFingerprint(
67
+ [...document.querySelectorAll('[data-message-author-role="user"]')].map(
68
+ (element) => ({
69
+ authorRole: element.getAttribute("data-message-author-role"),
70
+ textContent: element.textContent,
71
+ }),
72
+ ),
73
+ fingerprint,
74
+ );
65
75
  }
66
76
 
67
77
  chrome.runtime.onMessage.addListener((command, _sender, sendResponse) => {
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "ProFlow Execution Browser",
4
- "version": "0.1.25",
4
+ "version": "0.1.27",
5
5
  "permissions": [
6
6
  "tabs",
7
7
  "storage",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomflow/proflow-execution-browser-extension",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
4
4
  "description": "Execution-owned MV3 Browser executor, evidence provider and browser application surface.",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -27,12 +27,12 @@
27
27
  "SETUP.md"
28
28
  ],
29
29
  "dependencies": {
30
- "@tomflow/proflow-execution-contracts": "^0.1.10",
31
- "@tomflow/proflow-module-contract": "^0.1.13"
30
+ "@tomflow/proflow-module-contract": "^0.1.13",
31
+ "@tomflow/proflow-execution-contracts": "^0.1.10"
32
32
  },
33
33
  "devDependencies": {
34
- "@tomflow/proflow-deployment-conformance": "^0.1.13",
35
- "@tomflow/proflow-execution-runtime": "^0.1.15"
34
+ "@tomflow/proflow-execution-runtime": "^0.1.15",
35
+ "@tomflow/proflow-deployment-conformance": "^0.1.13"
36
36
  },
37
37
  "keywords": [
38
38
  "proflow",
@@ -3,7 +3,7 @@
3
3
  "contractVersion": "1.0.0",
4
4
  "moduleRef": "execution-browser-extension",
5
5
  "packageName": "@tomflow/proflow-execution-browser-extension",
6
- "moduleVersion": "0.1.25",
6
+ "moduleVersion": "0.1.27",
7
7
  "kind": "browser-extension",
8
8
  "templateVersion": "1.0.0",
9
9
  "platformCompatibility": ">=1.0.0 <2.0.0",
package/CHANGELOG.md DELETED
@@ -1,25 +0,0 @@
1
- # @tomflow/proflow-execution-browser-extension
2
-
3
- ## 0.1.25
4
-
5
- ### Patch Changes
6
-
7
- - Preserve irreversible Custom GPT role authority and revalidate existing roles without duplicate creation
8
-
9
- ## 0.1.24
10
-
11
- ### Patch Changes
12
-
13
- - Cover Chrome's observed delayed Secure Preferences flush while waiting for unpacked-extension registration to become enabled after pairing.
14
-
15
- ## 0.1.23
16
-
17
- ### Patch Changes
18
-
19
- - Wait for Chrome unpacked-extension registration to become observably enabled after heartbeat pairing before Browser setup reports success.
20
-
21
- ## 0.1.22
22
-
23
- ### Patch Changes
24
-
25
- - Make Browser Extension static materialization version-idempotent, freeze real-user install/remove acceptance paths, and remove unsupported Reload/Disable-Enable test scenarios.