@tomflow/proflow-execution-browser-extension 0.1.41 → 0.1.43

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.43
4
+
5
+ ### Patch Changes
6
+
7
+ - Enrich Browser OBSERVE responses with Chrome-owned tab and window identity before returning the page observation.
8
+
9
+ ## 0.1.42
10
+
11
+ ### Patch Changes
12
+
13
+ - Close Real-3 node generation identity fencing across Platform Host admission, durable Execution idempotency, and Observer/Approval resume provenance.
14
+
3
15
  ## 0.1.41
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.41";
50
+ moduleVersion: "0.1.43";
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.41";
63
+ readonly moduleVersion: "0.1.43";
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.41";
73
+ moduleVersion: "0.1.43";
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.41";
98
+ moduleVersion: "0.1.43";
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.41";
130
+ readonly moduleVersion: "0.1.43";
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.41";
137
+ moduleVersion: "0.1.43";
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.41";
170
+ moduleVersion: "0.1.43";
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.41";
183
+ readonly moduleVersion: "0.1.43";
184
184
  } | {
185
185
  contract: "deployment.result.v1";
186
186
  moduleRef: "execution-browser-extension";
187
- moduleVersion: "0.1.41";
187
+ moduleVersion: "0.1.43";
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.41";
204
+ readonly moduleVersion: "0.1.43";
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.41";
6
+ readonly moduleVersion: "0.1.43";
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.41",
6
+ moduleVersion: "0.1.43",
7
7
  kind: "browser-extension",
8
8
  templateVersion: "1.0.0",
9
9
  platformCompatibility: ">=1.0.0 <2.0.0",
@@ -14090,6 +14090,8 @@ var NODE_READY_TRIGGER = "NODE_READY";
14090
14090
  var REOPEN_TRIGGER = "REOPEN";
14091
14091
  function createTaskObserver(options) {
14092
14092
  const advance = async (taskId, resumeSignal, anomalySignal) => {
14093
+ if (resumeSignal && (typeof resumeSignal.nodeId !== "string" || resumeSignal.nodeId.length === 0 || !Number.isInteger(resumeSignal.runNo) || resumeSignal.runNo <= 0))
14094
+ return { kind: "NOOP", taskId, reason: "RESUME_GENERATION_REQUIRED" };
14093
14095
  const projection = await options.owner.getTaskDriveProjection(taskId);
14094
14096
  if (projection.terminal || projection.currentNode === null)
14095
14097
  return { kind: "STOP_DRIVING", taskId, reason: "TERMINAL" };
@@ -14119,9 +14121,11 @@ function createTaskObserver(options) {
14119
14121
  };
14120
14122
  }
14121
14123
  const binding = projection.roleBinding;
14122
- if (!projection.canDrive || !binding?.workerRef || !binding.conversationLocator)
14124
+ if (!binding?.workerRef || !binding.conversationLocator || binding.agentPackageRef !== node.requiredAgentPackageRef)
14123
14125
  return { kind: "NOOP", taskId, reason: "BINDING_NOT_READY" };
14124
14126
  if (node.status === "READY") {
14127
+ if (!projection.canDrive)
14128
+ return { kind: "NOOP", taskId, reason: "BINDING_NOT_READY" };
14125
14129
  return {
14126
14130
  kind: "WAKE",
14127
14131
  taskId,
@@ -14134,6 +14138,14 @@ function createTaskObserver(options) {
14134
14138
  };
14135
14139
  }
14136
14140
  if (resumeSignal) {
14141
+ if (projection.taskStatus !== "ACTIVE" || node.status !== "IN_PROGRESS")
14142
+ return { kind: "NOOP", taskId, reason: "BINDING_NOT_READY" };
14143
+ if (resumeSignal.nodeId !== node.nodeId || resumeSignal.runNo !== node.runNo)
14144
+ return {
14145
+ kind: "NOOP",
14146
+ taskId,
14147
+ reason: "RESUME_GENERATION_MISMATCH"
14148
+ };
14137
14149
  if (resumeSignal.targetWorkerRef !== binding.workerRef)
14138
14150
  return {
14139
14151
  kind: "NOOP",
@@ -14445,6 +14457,20 @@ var taskObserver = createTaskObserver({
14445
14457
  }
14446
14458
  }
14447
14459
  });
14460
+ async function resumeAfterApprovalDecision(approvalRef) {
14461
+ const context = await invokeObserverApplication("approval.executionContext", {
14462
+ approvalRef
14463
+ });
14464
+ if (!isRecord(context) || typeof context.executionRef !== "string" || typeof context.taskId !== "string" || typeof context.nodeId !== "string" || !Number.isInteger(context.runNo) || Number(context.runNo) <= 0 || typeof context.roleRef !== "string" || typeof context.workerRef !== "string")
14465
+ throw new Error("APPROVAL_EXECUTION_GENERATION_REQUIRED");
14466
+ return taskObserver.drive(context.taskId, {
14467
+ trigger: "RECOVERY_RESUME",
14468
+ ref: approvalRef,
14469
+ targetWorkerRef: context.workerRef,
14470
+ nodeId: context.nodeId,
14471
+ runNo: Number(context.runNo)
14472
+ });
14473
+ }
14448
14474
  var collaborationCarrier = createCollaborationCarrierApplication({
14449
14475
  task: {
14450
14476
  async getWorkerBinding(taskId, roleRef) {
@@ -14558,18 +14584,31 @@ function runObserverRecovery() {
14558
14584
  (denial) => denial.taskId === candidate.taskId && denial.workerRef === candidate.workerRef
14559
14585
  ))
14560
14586
  continue;
14561
- const decision = candidate.kind === "RECOVERY_RESUME" ? await taskObserver.drive(candidate.taskId, {
14562
- trigger: "RECOVERY_RESUME",
14563
- ref: candidate.executionRef,
14564
- targetWorkerRef: candidate.workerRef
14565
- }) : candidate.kind === "UNKNOWN_REALITY" ? await taskObserver.drive(candidate.taskId, void 0, {
14566
- kind: "UNKNOWN_REALITY",
14567
- ref: candidate.executionRef,
14568
- facts: {
14569
- executionRef: candidate.executionRef,
14570
- summary: `Execution ${candidate.executionRef} recovery remains UNKNOWN`
14587
+ let decision;
14588
+ if (candidate.kind === "RECOVERY_RESUME") {
14589
+ if (typeof candidate.nodeId !== "string" || typeof candidate.runNo !== "number") {
14590
+ await invokeObserverApplication("execution.ackSignal", {
14591
+ signalRef: candidate.signalRef
14592
+ });
14593
+ continue;
14571
14594
  }
14572
- }) : null;
14595
+ decision = await taskObserver.drive(candidate.taskId, {
14596
+ trigger: "RECOVERY_RESUME",
14597
+ ref: candidate.executionRef,
14598
+ targetWorkerRef: candidate.workerRef,
14599
+ nodeId: candidate.nodeId,
14600
+ runNo: candidate.runNo
14601
+ });
14602
+ } else if (candidate.kind === "UNKNOWN_REALITY")
14603
+ decision = await taskObserver.drive(candidate.taskId, void 0, {
14604
+ kind: "UNKNOWN_REALITY",
14605
+ ref: candidate.executionRef,
14606
+ facts: {
14607
+ executionRef: candidate.executionRef,
14608
+ summary: `Execution ${candidate.executionRef} recovery remains UNKNOWN`
14609
+ }
14610
+ });
14611
+ else decision = null;
14573
14612
  if (!decision) continue;
14574
14613
  if (decision.kind === "NOOP" && (decision.reason === "BINDING_NOT_READY" || decision.reason === "RESUME_TARGET_NOT_CURRENT_WORKER" || decision.reason === "DIAGNOSTIC_UNAVAILABLE" || decision.reason.startsWith("DIAGNOSTIC_DEFERRED:")))
14575
14614
  continue;
@@ -15029,8 +15068,13 @@ async function executeCommand(command) {
15029
15068
  );
15030
15069
  }
15031
15070
  const tabId = numeric(command.tabId, "TAB_ID");
15032
- if (command.type === "OBSERVE")
15033
- return contentCommand(tabId, { operation: "observe" });
15071
+ if (command.type === "OBSERVE") {
15072
+ const value = await contentCommand(tabId, { operation: "observe" });
15073
+ const tab = await chrome.tabs.get(tabId);
15074
+ const observed = parseSnapshotObservation(value, tab);
15075
+ if (!observed) throw new Error("CONTENT_OBSERVATION_INVALID");
15076
+ return observed;
15077
+ }
15034
15078
  if (command.type === "SUBMIT") {
15035
15079
  const before = observationFor(tabId);
15036
15080
  const fingerprint = text(command.fingerprint, "FINGERPRINT");
@@ -15462,12 +15506,10 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
15462
15506
  }
15463
15507
  void invokeApprovalApplication(message.operation, message.input).then(
15464
15508
  (value) => {
15465
- if ((message.operation === "approval.allow" || message.operation === "approval.deny" || message.operation === "approval.revoke") && isRecord(value) && typeof value.approvalRef === "string" && typeof value.taskId === "string" && typeof value.workerRef === "string")
15466
- void taskObserver.drive(value.taskId, {
15467
- trigger: "RECOVERY_RESUME",
15468
- ref: value.approvalRef,
15469
- targetWorkerRef: value.workerRef
15470
- }).catch(() => void 0);
15509
+ if ((message.operation === "approval.allow" || message.operation === "approval.deny" || message.operation === "approval.revoke") && isRecord(value) && typeof value.approvalRef === "string")
15510
+ void resumeAfterApprovalDecision(value.approvalRef).catch(
15511
+ () => void 0
15512
+ );
15471
15513
  sendResponse({ ok: true, value });
15472
15514
  },
15473
15515
  (error46) => sendResponse({
@@ -32,6 +32,8 @@ export type TaskObserverResumeSignal = {
32
32
  trigger: "EXECUTION_RESULT_READY" | "PEER_REPLY_READY" | "RECOVERY_RESUME";
33
33
  ref: string;
34
34
  targetWorkerRef: string;
35
+ nodeId: string;
36
+ runNo: number;
35
37
  };
36
38
  export type TaskObserverAnomalySignal = {
37
39
  kind: "FACT_CONFLICT" | "UNKNOWN_REALITY" | "STALLED" | "RECOVERY_FAILED";
@@ -11,6 +11,12 @@ const NODE_READY_TRIGGER = "NODE_READY";
11
11
  const REOPEN_TRIGGER = "REOPEN";
12
12
  export function createTaskObserver(options) {
13
13
  const advance = async (taskId, resumeSignal, anomalySignal) => {
14
+ if (resumeSignal &&
15
+ (typeof resumeSignal.nodeId !== "string" ||
16
+ resumeSignal.nodeId.length === 0 ||
17
+ !Number.isInteger(resumeSignal.runNo) ||
18
+ resumeSignal.runNo <= 0))
19
+ return { kind: "NOOP", taskId, reason: "RESUME_GENERATION_REQUIRED" };
14
20
  const projection = await options.owner.getTaskDriveProjection(taskId);
15
21
  // Terminal Tasks stop driving; history and bindings are retained.
16
22
  if (projection.terminal || projection.currentNode === null)
@@ -43,11 +49,13 @@ export function createTaskObserver(options) {
43
49
  };
44
50
  }
45
51
  const binding = projection.roleBinding;
46
- if (!projection.canDrive ||
47
- !binding?.workerRef ||
48
- !binding.conversationLocator)
52
+ if (!binding?.workerRef ||
53
+ !binding.conversationLocator ||
54
+ binding.agentPackageRef !== node.requiredAgentPackageRef)
49
55
  return { kind: "NOOP", taskId, reason: "BINDING_NOT_READY" };
50
56
  if (node.status === "READY") {
57
+ if (!projection.canDrive)
58
+ return { kind: "NOOP", taskId, reason: "BINDING_NOT_READY" };
51
59
  // WAKE the correct Worker with a minimal trigger; the Worker then
52
60
  // performs formal work acceptance through the Task owner. A reopened
53
61
  // run reuses the same TaskRoleBinding/Conversation but keeps the reason
@@ -64,6 +72,15 @@ export function createTaskObserver(options) {
64
72
  };
65
73
  }
66
74
  if (resumeSignal) {
75
+ if (projection.taskStatus !== "ACTIVE" || node.status !== "IN_PROGRESS")
76
+ return { kind: "NOOP", taskId, reason: "BINDING_NOT_READY" };
77
+ if (resumeSignal.nodeId !== node.nodeId ||
78
+ resumeSignal.runNo !== node.runNo)
79
+ return {
80
+ kind: "NOOP",
81
+ taskId,
82
+ reason: "RESUME_GENERATION_MISMATCH",
83
+ };
67
84
  if (resumeSignal.targetWorkerRef !== binding.workerRef)
68
85
  return {
69
86
  kind: "NOOP",
@@ -540,6 +540,30 @@ const taskObserver = createTaskObserver({
540
540
  },
541
541
  });
542
542
 
543
+ async function resumeAfterApprovalDecision(approvalRef: string) {
544
+ const context = await invokeObserverApplication("approval.executionContext", {
545
+ approvalRef,
546
+ });
547
+ if (
548
+ !isRecord(context) ||
549
+ typeof context.executionRef !== "string" ||
550
+ typeof context.taskId !== "string" ||
551
+ typeof context.nodeId !== "string" ||
552
+ !Number.isInteger(context.runNo) ||
553
+ Number(context.runNo) <= 0 ||
554
+ typeof context.roleRef !== "string" ||
555
+ typeof context.workerRef !== "string"
556
+ )
557
+ throw new Error("APPROVAL_EXECUTION_GENERATION_REQUIRED");
558
+ return taskObserver.drive(context.taskId, {
559
+ trigger: "RECOVERY_RESUME",
560
+ ref: approvalRef,
561
+ targetWorkerRef: context.workerRef,
562
+ nodeId: context.nodeId,
563
+ runNo: Number(context.runNo),
564
+ });
565
+ }
566
+
543
567
  const collaborationCarrier = createCollaborationCarrierApplication({
544
568
  task: {
545
569
  async getWorkerBinding(taskId, roleRef) {
@@ -710,23 +734,37 @@ function runObserverRecovery() {
710
734
  )
711
735
  )
712
736
  continue;
713
- const decision =
714
- candidate.kind === "RECOVERY_RESUME"
715
- ? await taskObserver.drive(candidate.taskId, {
716
- trigger: "RECOVERY_RESUME",
717
- ref: candidate.executionRef,
718
- targetWorkerRef: candidate.workerRef,
719
- })
720
- : candidate.kind === "UNKNOWN_REALITY"
721
- ? await taskObserver.drive(candidate.taskId, undefined, {
722
- kind: "UNKNOWN_REALITY",
723
- ref: candidate.executionRef,
724
- facts: {
725
- executionRef: candidate.executionRef,
726
- summary: `Execution ${candidate.executionRef} recovery remains UNKNOWN`,
727
- },
728
- })
729
- : null;
737
+ let decision;
738
+ if (candidate.kind === "RECOVERY_RESUME") {
739
+ if (
740
+ typeof candidate.nodeId !== "string" ||
741
+ typeof candidate.runNo !== "number"
742
+ ) {
743
+ // Missing generation is a permanent malformed resume intent, not a
744
+ // transient binding condition. Dispose it without ever waking a Worker
745
+ // so an old signal cannot occupy the bounded pending queue forever.
746
+ await invokeObserverApplication("execution.ackSignal", {
747
+ signalRef: candidate.signalRef,
748
+ });
749
+ continue;
750
+ }
751
+ decision = await taskObserver.drive(candidate.taskId, {
752
+ trigger: "RECOVERY_RESUME",
753
+ ref: candidate.executionRef,
754
+ targetWorkerRef: candidate.workerRef,
755
+ nodeId: candidate.nodeId,
756
+ runNo: candidate.runNo,
757
+ });
758
+ } else if (candidate.kind === "UNKNOWN_REALITY")
759
+ decision = await taskObserver.drive(candidate.taskId, undefined, {
760
+ kind: "UNKNOWN_REALITY",
761
+ ref: candidate.executionRef,
762
+ facts: {
763
+ executionRef: candidate.executionRef,
764
+ summary: `Execution ${candidate.executionRef} recovery remains UNKNOWN`,
765
+ },
766
+ });
767
+ else decision = null;
730
768
  if (!decision) continue;
731
769
  if (
732
770
  decision.kind === "NOOP" &&
@@ -1346,8 +1384,13 @@ async function executeCommand(command: BridgeCommand): Promise<unknown> {
1346
1384
  );
1347
1385
  }
1348
1386
  const tabId = numeric(command.tabId, "TAB_ID");
1349
- if (command.type === "OBSERVE")
1350
- return contentCommand(tabId, { operation: "observe" });
1387
+ if (command.type === "OBSERVE") {
1388
+ const value = await contentCommand(tabId, { operation: "observe" });
1389
+ const tab = await chrome.tabs.get(tabId);
1390
+ const observed = parseSnapshotObservation(value, tab);
1391
+ if (!observed) throw new Error("CONTENT_OBSERVATION_INVALID");
1392
+ return observed;
1393
+ }
1351
1394
  if (command.type === "SUBMIT") {
1352
1395
  const before = observationFor(tabId);
1353
1396
  const fingerprint = text(command.fingerprint, "FINGERPRINT");
@@ -1874,17 +1917,11 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
1874
1917
  message.operation === "approval.deny" ||
1875
1918
  message.operation === "approval.revoke") &&
1876
1919
  isRecord(value) &&
1877
- typeof value.approvalRef === "string" &&
1878
- typeof value.taskId === "string" &&
1879
- typeof value.workerRef === "string"
1920
+ typeof value.approvalRef === "string"
1880
1921
  )
1881
- void taskObserver
1882
- .drive(value.taskId, {
1883
- trigger: "RECOVERY_RESUME",
1884
- ref: value.approvalRef,
1885
- targetWorkerRef: value.workerRef,
1886
- })
1887
- .catch(() => undefined);
1922
+ void resumeAfterApprovalDecision(value.approvalRef).catch(
1923
+ () => undefined,
1924
+ );
1888
1925
  sendResponse({ ok: true, value });
1889
1926
  },
1890
1927
  (error: unknown) =>
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.41",
4
+ "version": "0.1.43",
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.41",
3
+ "version": "0.1.43",
4
4
  "description": "Execution-owned MV3 Browser executor, evidence provider and browser application surface.",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -31,8 +31,8 @@
31
31
  "@tomflow/proflow-module-contract": "^0.1.13"
32
32
  },
33
33
  "devDependencies": {
34
- "@tomflow/proflow-execution-runtime": "^0.1.17",
35
- "@tomflow/proflow-deployment-conformance": "^0.1.13"
34
+ "@tomflow/proflow-deployment-conformance": "^0.1.13",
35
+ "@tomflow/proflow-execution-runtime": "^0.1.18"
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.41",
6
+ "moduleVersion": "0.1.43",
7
7
  "kind": "browser-extension",
8
8
  "templateVersion": "1.0.0",
9
9
  "platformCompatibility": ">=1.0.0 <2.0.0",