@tomflow/proflow-execution-browser-extension 0.1.24 → 0.1.26

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
@@ -1,5 +1,17 @@
1
1
  # @tomflow/proflow-execution-browser-extension
2
2
 
3
+ ## 0.1.26
4
+
5
+ ### Patch Changes
6
+
7
+ - Close the Real-3 source candidate and real-workspace loop: fail-closed Browser submit verification, worker-bound Task mutation and WAKE confirmation, detached platform start ownership with shell return, discoverable docs, and targeted single-package Workspace update.
8
+
9
+ ## 0.1.25
10
+
11
+ ### Patch Changes
12
+
13
+ - Preserve irreversible Custom GPT role authority and revalidate existing roles without duplicate creation
14
+
3
15
  ## 0.1.24
4
16
 
5
17
  ### Patch Changes
@@ -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.24";
50
+ moduleVersion: "0.1.26";
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.24";
63
+ readonly moduleVersion: "0.1.26";
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.24";
73
+ moduleVersion: "0.1.26";
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.24";
98
+ moduleVersion: "0.1.26";
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.24";
130
+ readonly moduleVersion: "0.1.26";
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.24";
137
+ moduleVersion: "0.1.26";
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.24";
170
+ moduleVersion: "0.1.26";
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.24";
183
+ readonly moduleVersion: "0.1.26";
184
184
  } | {
185
185
  contract: "deployment.result.v1";
186
186
  moduleRef: "execution-browser-extension";
187
- moduleVersion: "0.1.24";
187
+ moduleVersion: "0.1.26";
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.24";
204
+ readonly moduleVersion: "0.1.26";
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.24";
6
+ readonly moduleVersion: "0.1.26";
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.24",
6
+ moduleVersion: "0.1.26",
7
7
  kind: "browser-extension",
8
8
  templateVersion: "1.0.0",
9
9
  platformCompatibility: ">=1.0.0 <2.0.0",
@@ -14190,6 +14190,7 @@ var observerRecoveryRetryCount = 0;
14190
14190
  function runObserverRecovery() {
14191
14191
  if (observerRecoveryInFlight) return observerRecoveryInFlight;
14192
14192
  observerRecoveryInFlight = (async () => {
14193
+ let recoveryNeedsRetry = false;
14193
14194
  await collaborationCarrier.recoverPending(50).catch(() => void 0);
14194
14195
  const signalBatch = await invokeObserverApplication(
14195
14196
  "execution.listSignals",
@@ -14219,18 +14220,14 @@ function runObserverRecovery() {
14219
14220
  signalRef: candidate.signalRef
14220
14221
  });
14221
14222
  } catch {
14223
+ recoveryNeedsRetry = true;
14222
14224
  }
14223
14225
  }
14224
14226
  }
14225
14227
  const listed = await invokeTaskApplication("task.list", {}).catch(
14226
14228
  () => null
14227
14229
  );
14228
- if (listed === null && observerRecoveryRetryCount < 6) {
14229
- observerRecoveryRetryCount += 1;
14230
- setTimeout(() => void runObserverRecovery(), 2e3);
14231
- } else if (listed !== null) {
14232
- observerRecoveryRetryCount = 0;
14233
- }
14230
+ if (listed === null) recoveryNeedsRetry = true;
14234
14231
  if (isRecord(listed) && Array.isArray(listed.tasks)) {
14235
14232
  for (const candidate of listed.tasks.slice(0, 100)) {
14236
14233
  if (!isRecord(candidate) || typeof candidate.taskId !== "string")
@@ -14239,10 +14236,20 @@ function runObserverRecovery() {
14239
14236
  continue;
14240
14237
  await invokeTaskApplication("task.ensureWorkers", {
14241
14238
  taskId: candidate.taskId
14242
- }).catch(() => void 0);
14243
- await taskObserver.drive(candidate.taskId).catch(() => void 0);
14239
+ }).catch(() => {
14240
+ recoveryNeedsRetry = true;
14241
+ });
14242
+ await taskObserver.drive(candidate.taskId).catch(() => {
14243
+ recoveryNeedsRetry = true;
14244
+ });
14244
14245
  }
14245
14246
  }
14247
+ if (recoveryNeedsRetry && observerRecoveryRetryCount < 6) {
14248
+ observerRecoveryRetryCount += 1;
14249
+ setTimeout(() => void runObserverRecovery(), 2e3);
14250
+ } else if (!recoveryNeedsRetry) {
14251
+ observerRecoveryRetryCount = 0;
14252
+ }
14246
14253
  const previousSystemState = await loadSystemObserverState().catch(
14247
14254
  () => null
14248
14255
  );
@@ -14285,6 +14292,21 @@ async function contentCommand(tabId, command) {
14285
14292
  }
14286
14293
  return response.value;
14287
14294
  }
14295
+ async function waitForSubmittedMessage(tabId, fingerprint) {
14296
+ for (let attempt = 0; attempt < 60; attempt += 1) {
14297
+ try {
14298
+ const value = await contentCommand(tabId, {
14299
+ operation: "verify",
14300
+ fingerprint
14301
+ });
14302
+ if (isRecord(value) && value.verified === true)
14303
+ return observationFor(tabId);
14304
+ } catch {
14305
+ }
14306
+ await sleep(250);
14307
+ }
14308
+ throw new Error("MESSAGE_SUBMIT_REALITY_UNCONFIRMED");
14309
+ }
14288
14310
  function numeric(value, name) {
14289
14311
  if (!Number.isInteger(value)) throw new Error(`${name}_INVALID`);
14290
14312
  return value;
@@ -14312,11 +14334,12 @@ async function executeCommand(command) {
14312
14334
  return contentCommand(tabId, { operation: "observe" });
14313
14335
  if (command.type === "SUBMIT") {
14314
14336
  const before = observationFor(tabId);
14337
+ const fingerprint = text(command.fingerprint, "FINGERPRINT");
14315
14338
  try {
14316
14339
  await contentCommand(tabId, {
14317
14340
  operation: "submit",
14318
14341
  value: text(command.text, "TEXT"),
14319
- fingerprint: text(command.fingerprint, "FINGERPRINT")
14342
+ fingerprint
14320
14343
  });
14321
14344
  } catch (error46) {
14322
14345
  const replacement = await waitForObservation(
@@ -14325,7 +14348,7 @@ async function executeCommand(command) {
14325
14348
  ).catch(() => null);
14326
14349
  if (!replacement) throw error46;
14327
14350
  }
14328
- return waitForObservation(tabId);
14351
+ return waitForSubmittedMessage(tabId, fingerprint);
14329
14352
  }
14330
14353
  if (command.type === "VERIFY")
14331
14354
  return contentCommand(tabId, {
@@ -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 () => {
@@ -33,7 +33,6 @@ export async function createCustomGptRole(input, ports) {
33
33
  : { onlineTimeoutMs: input.onlineTimeoutMs }),
34
34
  });
35
35
  let result;
36
- let rollbackSavedRole;
37
36
  let credential = "";
38
37
  try {
39
38
  const prepared = await ports.roleRegistry.prepareCredential();
@@ -55,7 +54,6 @@ export async function createCustomGptRole(input, ports) {
55
54
  roleRef: result.gptId,
56
55
  carrierUrl: result.carrierUrl,
57
56
  }, credential);
58
- rollbackSavedRole = saved.rollback;
59
57
  if (typeof saved.credential !== "string" || saved.credential.length < 32)
60
58
  throw new Error("WORKSPACE_ROLE_CREDENTIAL_INVALID");
61
59
  if (saved.credential !== credential)
@@ -79,18 +77,10 @@ export async function createCustomGptRole(input, ports) {
79
77
  });
80
78
  return result;
81
79
  }
82
- catch (error) {
83
- if (rollbackSavedRole) {
84
- try {
85
- await rollbackSavedRole();
86
- }
87
- catch {
88
- throw new Error("WORKSPACE_ROLE_ROLLBACK_FAILED");
89
- }
90
- }
91
- throw error;
92
- }
93
80
  finally {
81
+ // Remote GPT creation is irreversible from this host. Do not roll the durable
82
+ // Role back after post-create validation failure; the caller can safely revalidate
83
+ // that same Role on the next setup instead of creating a duplicate GPT.
94
84
  credential = "";
95
85
  await host.close();
96
86
  }
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,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
+ }
@@ -587,6 +587,7 @@ let observerRecoveryRetryCount = 0;
587
587
  function runObserverRecovery() {
588
588
  if (observerRecoveryInFlight) return observerRecoveryInFlight;
589
589
  observerRecoveryInFlight = (async () => {
590
+ let recoveryNeedsRetry = false;
590
591
  await collaborationCarrier.recoverPending(50).catch(() => undefined);
591
592
  const signalBatch = await invokeObserverApplication(
592
593
  "execution.listSignals",
@@ -634,18 +635,14 @@ function runObserverRecovery() {
634
635
  });
635
636
  } catch {
636
637
  // Leave the durable signal unacknowledged for the next bounded recovery pass.
638
+ recoveryNeedsRetry = true;
637
639
  }
638
640
  }
639
641
  }
640
642
  const listed = await invokeTaskApplication("task.list", {}).catch(
641
643
  () => null,
642
644
  );
643
- if (listed === null && observerRecoveryRetryCount < 6) {
644
- observerRecoveryRetryCount += 1;
645
- setTimeout(() => void runObserverRecovery(), 2_000);
646
- } else if (listed !== null) {
647
- observerRecoveryRetryCount = 0;
648
- }
645
+ if (listed === null) recoveryNeedsRetry = true;
649
646
  if (isRecord(listed) && Array.isArray(listed.tasks)) {
650
647
  for (const candidate of listed.tasks.slice(0, 100)) {
651
648
  if (!isRecord(candidate) || typeof candidate.taskId !== "string")
@@ -663,10 +660,22 @@ function runObserverRecovery() {
663
660
  // missing roles are re-provisioned by the Host/Execution path.
664
661
  await invokeTaskApplication("task.ensureWorkers", {
665
662
  taskId: candidate.taskId,
666
- }).catch(() => undefined);
667
- await taskObserver.drive(candidate.taskId).catch(() => undefined);
663
+ }).catch(() => {
664
+ recoveryNeedsRetry = true;
665
+ });
666
+ await taskObserver.drive(candidate.taskId).catch(() => {
667
+ recoveryNeedsRetry = true;
668
+ });
668
669
  }
669
670
  }
671
+ // A bounded retry continues the same stable Execution identities. Execution
672
+ // remains the no-blind-replay authority for APPLIED/NOT_APPLIED/UNKNOWN.
673
+ if (recoveryNeedsRetry && observerRecoveryRetryCount < 6) {
674
+ observerRecoveryRetryCount += 1;
675
+ setTimeout(() => void runObserverRecovery(), 2_000);
676
+ } else if (!recoveryNeedsRetry) {
677
+ observerRecoveryRetryCount = 0;
678
+ }
670
679
  const previousSystemState = await loadSystemObserverState().catch(
671
680
  () => null,
672
681
  );
@@ -724,6 +733,27 @@ async function contentCommand(
724
733
  return response.value;
725
734
  }
726
735
 
736
+ async function waitForSubmittedMessage(
737
+ tabId: number,
738
+ fingerprint: string,
739
+ ): Promise<ContentObservation> {
740
+ for (let attempt = 0; attempt < 60; attempt += 1) {
741
+ try {
742
+ const value = await contentCommand(tabId, {
743
+ operation: "verify",
744
+ fingerprint,
745
+ });
746
+ if (isRecord(value) && value.verified === true)
747
+ return observationFor(tabId);
748
+ } catch {
749
+ // Navigation can replace the content instance after submit. The next
750
+ // bounded observation/verification pass uses the newly published session.
751
+ }
752
+ await sleep(250);
753
+ }
754
+ throw new Error("MESSAGE_SUBMIT_REALITY_UNCONFIRMED");
755
+ }
756
+
727
757
  function numeric(value: unknown, name: string): number {
728
758
  if (!Number.isInteger(value)) throw new Error(`${name}_INVALID`);
729
759
  return value as number;
@@ -757,11 +787,12 @@ async function executeCommand(command: BridgeCommand): Promise<unknown> {
757
787
  return contentCommand(tabId, { operation: "observe" });
758
788
  if (command.type === "SUBMIT") {
759
789
  const before = observationFor(tabId);
790
+ const fingerprint = text(command.fingerprint, "FINGERPRINT");
760
791
  try {
761
792
  await contentCommand(tabId, {
762
793
  operation: "submit",
763
794
  value: text(command.text, "TEXT"),
764
- fingerprint: text(command.fingerprint, "FINGERPRINT"),
795
+ fingerprint,
765
796
  });
766
797
  } catch (error) {
767
798
  const replacement = await waitForObservation(
@@ -770,7 +801,7 @@ async function executeCommand(command: BridgeCommand): Promise<unknown> {
770
801
  ).catch(() => null);
771
802
  if (!replacement) throw error;
772
803
  }
773
- return waitForObservation(tabId);
804
+ return waitForSubmittedMessage(tabId, fingerprint);
774
805
  }
775
806
  if (command.type === "VERIFY")
776
807
  return contentCommand(tabId, {
@@ -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.24",
4
+ "version": "0.1.26",
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.24",
3
+ "version": "0.1.26",
4
4
  "description": "Execution-owned MV3 Browser executor, evidence provider and browser application surface.",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -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.24",
6
+ "moduleVersion": "0.1.26",
7
7
  "kind": "browser-extension",
8
8
  "templateVersion": "1.0.0",
9
9
  "platformCompatibility": ">=1.0.0 <2.0.0",