@tomflow/proflow-platform-host 0.1.16 → 0.1.18
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 +13 -0
- package/dist/deployment/adapter.d.ts +10 -10
- package/dist/deployment/descriptor.d.ts +1 -1
- package/dist/deployment/descriptor.js +1 -1
- package/dist/src/browser-permission-policy.d.ts +36 -0
- package/dist/src/browser-permission-policy.js +82 -0
- package/dist/src/index.js +147 -4
- package/package.json +7 -7
- package/proflow.module.json +1 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# @tomflow/proflow-platform-host
|
|
2
|
+
|
|
3
|
+
## 0.1.18
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Close Real-3 node generation identity fencing across Platform Host admission, durable Execution idempotency, and Observer/Approval resume provenance.
|
|
8
|
+
|
|
9
|
+
## 0.1.17
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Harden Real-3 Browser Carrier reality: controlled ChatGPT composer submission, authoritative action-permission classification, bounded binding-race deferral, Carrier Attention web relay and restart recovery, and human-deny precedence across automatic recovery.
|
|
@@ -6,7 +6,7 @@ export declare const behaviorAdapter: {
|
|
|
6
6
|
ok: true;
|
|
7
7
|
status: "SUCCEEDED";
|
|
8
8
|
moduleRef: "platform-host";
|
|
9
|
-
moduleVersion: "0.1.
|
|
9
|
+
moduleVersion: "0.1.18";
|
|
10
10
|
data: {
|
|
11
11
|
endpoint: string;
|
|
12
12
|
stateRoot: string;
|
|
@@ -24,7 +24,7 @@ export declare const behaviorAdapter: {
|
|
|
24
24
|
readonly ok: true;
|
|
25
25
|
readonly status: "SUCCEEDED";
|
|
26
26
|
readonly moduleRef: "platform-host";
|
|
27
|
-
readonly moduleVersion: "0.1.
|
|
27
|
+
readonly moduleVersion: "0.1.18";
|
|
28
28
|
};
|
|
29
29
|
observedEffects: string[];
|
|
30
30
|
}>;
|
|
@@ -34,7 +34,7 @@ export declare const behaviorAdapter: {
|
|
|
34
34
|
ok: true;
|
|
35
35
|
status: "SUCCEEDED";
|
|
36
36
|
moduleRef: "platform-host";
|
|
37
|
-
moduleVersion: "0.1.
|
|
37
|
+
moduleVersion: "0.1.18";
|
|
38
38
|
data: {
|
|
39
39
|
setupStatus: "READY";
|
|
40
40
|
runtimeStatus: "RUNNING" | "STOPPED";
|
|
@@ -55,7 +55,7 @@ export declare const behaviorAdapter: {
|
|
|
55
55
|
ok: true;
|
|
56
56
|
status: "SUCCEEDED";
|
|
57
57
|
moduleRef: "platform-host";
|
|
58
|
-
moduleVersion: "0.1.
|
|
58
|
+
moduleVersion: "0.1.18";
|
|
59
59
|
data: {
|
|
60
60
|
endpoint: string;
|
|
61
61
|
stateRoot: string;
|
|
@@ -73,7 +73,7 @@ export declare const behaviorAdapter: {
|
|
|
73
73
|
ok: true;
|
|
74
74
|
status: "SUCCEEDED";
|
|
75
75
|
moduleRef: "platform-host";
|
|
76
|
-
moduleVersion: "0.1.
|
|
76
|
+
moduleVersion: "0.1.18";
|
|
77
77
|
data: {
|
|
78
78
|
docs: string;
|
|
79
79
|
};
|
|
@@ -86,7 +86,7 @@ export declare const behaviorAdapter: {
|
|
|
86
86
|
ok: true;
|
|
87
87
|
status: "SUCCEEDED";
|
|
88
88
|
moduleRef: "platform-host";
|
|
89
|
-
moduleVersion: "0.1.
|
|
89
|
+
moduleVersion: "0.1.18";
|
|
90
90
|
data: {
|
|
91
91
|
host: string;
|
|
92
92
|
port: number;
|
|
@@ -97,7 +97,7 @@ export declare const behaviorAdapter: {
|
|
|
97
97
|
result: {
|
|
98
98
|
contract: "deployment.result.v1";
|
|
99
99
|
moduleRef: "platform-host";
|
|
100
|
-
moduleVersion: "0.1.
|
|
100
|
+
moduleVersion: "0.1.18";
|
|
101
101
|
ok: false;
|
|
102
102
|
status: "FAILED";
|
|
103
103
|
error: {
|
|
@@ -114,7 +114,7 @@ export declare const behaviorAdapter: {
|
|
|
114
114
|
readonly ok: true;
|
|
115
115
|
readonly status: "SUCCEEDED";
|
|
116
116
|
readonly moduleRef: "platform-host";
|
|
117
|
-
readonly moduleVersion: "0.1.
|
|
117
|
+
readonly moduleVersion: "0.1.18";
|
|
118
118
|
};
|
|
119
119
|
observedEffects: string[];
|
|
120
120
|
} | {
|
|
@@ -123,11 +123,11 @@ export declare const behaviorAdapter: {
|
|
|
123
123
|
readonly ok: true;
|
|
124
124
|
readonly status: "SUCCEEDED";
|
|
125
125
|
readonly moduleRef: "platform-host";
|
|
126
|
-
readonly moduleVersion: "0.1.
|
|
126
|
+
readonly moduleVersion: "0.1.18";
|
|
127
127
|
} | {
|
|
128
128
|
contract: "deployment.result.v1";
|
|
129
129
|
moduleRef: "platform-host";
|
|
130
|
-
moduleVersion: "0.1.
|
|
130
|
+
moduleVersion: "0.1.18";
|
|
131
131
|
ok: false;
|
|
132
132
|
status: "FAILED";
|
|
133
133
|
error: {
|
|
@@ -3,7 +3,7 @@ export declare const descriptor: {
|
|
|
3
3
|
readonly contractVersion: "1.0.0";
|
|
4
4
|
readonly moduleRef: "platform-host";
|
|
5
5
|
readonly packageName: "@tomflow/proflow-platform-host";
|
|
6
|
-
readonly moduleVersion: "0.1.
|
|
6
|
+
readonly moduleVersion: "0.1.18";
|
|
7
7
|
readonly kind: "service";
|
|
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: "platform-host",
|
|
5
5
|
packageName: "@tomflow/proflow-platform-host",
|
|
6
|
-
moduleVersion: "0.1.
|
|
6
|
+
moduleVersion: "0.1.18",
|
|
7
7
|
kind: "service",
|
|
8
8
|
templateVersion: "1.0.0",
|
|
9
9
|
platformCompatibility: ">=1.0.0 <2.0.0",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type RolePackageRef } from "./role-operations.ts";
|
|
2
|
+
export type BrowserPermissionRole = {
|
|
3
|
+
agentPackageRef: RolePackageRef;
|
|
4
|
+
registeredPackageVersion: string;
|
|
5
|
+
roleRef: string;
|
|
6
|
+
carrierUrl: string;
|
|
7
|
+
};
|
|
8
|
+
export type RoleCarrierValidation = {
|
|
9
|
+
agentPackageRef: string;
|
|
10
|
+
registeredPackageVersion: string;
|
|
11
|
+
roleRef: string;
|
|
12
|
+
carrierUrl: string;
|
|
13
|
+
gatewayUrl: string;
|
|
14
|
+
};
|
|
15
|
+
export type BrowserPermissionDecision = {
|
|
16
|
+
decision: "AUTO_ALLOW" | "DEFER" | "HUMAN_REQUIRED";
|
|
17
|
+
reason: "KNOWN_PROFLOW_ACTION" | "TASK_BINDING_PENDING" | "ROLE_VALIDATION_MISMATCH" | "GATEWAY_MISMATCH" | "OPERATION_NOT_AUTHORIZED" | "CONTEXT_MISMATCH";
|
|
18
|
+
};
|
|
19
|
+
export type BrowserPermissionContext = {
|
|
20
|
+
conversationLocator: string;
|
|
21
|
+
workerRef: string | null;
|
|
22
|
+
taskBinding: {
|
|
23
|
+
agentPackageRef: string;
|
|
24
|
+
roleRef: string;
|
|
25
|
+
workerRef: string | null;
|
|
26
|
+
conversationLocator: string | null;
|
|
27
|
+
} | null;
|
|
28
|
+
};
|
|
29
|
+
export declare function classifyBrowserPermission(input: {
|
|
30
|
+
role: BrowserPermissionRole;
|
|
31
|
+
validation: RoleCarrierValidation | null;
|
|
32
|
+
targetHost: string | null;
|
|
33
|
+
currentGatewayUrl: string | null;
|
|
34
|
+
operationId: string;
|
|
35
|
+
context: BrowserPermissionContext;
|
|
36
|
+
}): BrowserPermissionDecision;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { roleOperations } from "./role-operations.js";
|
|
2
|
+
const loopbackHosts = new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
|
|
3
|
+
function validWorkerConversation(role, context) {
|
|
4
|
+
const workerRef = context.workerRef;
|
|
5
|
+
if (!workerRef)
|
|
6
|
+
return false;
|
|
7
|
+
try {
|
|
8
|
+
const locator = new URL(context.conversationLocator);
|
|
9
|
+
const segments = locator.pathname.split("/").filter(Boolean);
|
|
10
|
+
return (locator.protocol === "https:" &&
|
|
11
|
+
locator.hostname === "chatgpt.com" &&
|
|
12
|
+
locator.username === "" &&
|
|
13
|
+
locator.password === "" &&
|
|
14
|
+
locator.search === "" &&
|
|
15
|
+
locator.hash === "" &&
|
|
16
|
+
segments.length === 4 &&
|
|
17
|
+
segments[0] === "g" &&
|
|
18
|
+
segments[1] === role.roleRef &&
|
|
19
|
+
segments[2] === "c" &&
|
|
20
|
+
segments[3] === workerRef);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function classifyContext(role, context) {
|
|
27
|
+
if (!validWorkerConversation(role, context))
|
|
28
|
+
return { decision: "HUMAN_REQUIRED", reason: "CONTEXT_MISMATCH" };
|
|
29
|
+
const binding = context.taskBinding;
|
|
30
|
+
if (binding === null ||
|
|
31
|
+
binding.agentPackageRef !== role.agentPackageRef ||
|
|
32
|
+
binding.roleRef !== role.roleRef)
|
|
33
|
+
return { decision: "HUMAN_REQUIRED", reason: "CONTEXT_MISMATCH" };
|
|
34
|
+
if (binding.workerRef === null && binding.conversationLocator === null)
|
|
35
|
+
return { decision: "DEFER", reason: "TASK_BINDING_PENDING" };
|
|
36
|
+
if (binding.workerRef !== context.workerRef ||
|
|
37
|
+
binding.conversationLocator !== context.conversationLocator)
|
|
38
|
+
return { decision: "HUMAN_REQUIRED", reason: "CONTEXT_MISMATCH" };
|
|
39
|
+
return { decision: "AUTO_ALLOW", reason: "KNOWN_PROFLOW_ACTION" };
|
|
40
|
+
}
|
|
41
|
+
export function classifyBrowserPermission(input) {
|
|
42
|
+
const validation = input.validation;
|
|
43
|
+
if (validation === null ||
|
|
44
|
+
validation.agentPackageRef !== input.role.agentPackageRef ||
|
|
45
|
+
validation.registeredPackageVersion !==
|
|
46
|
+
input.role.registeredPackageVersion ||
|
|
47
|
+
validation.roleRef !== input.role.roleRef ||
|
|
48
|
+
validation.carrierUrl !== input.role.carrierUrl)
|
|
49
|
+
return { decision: "HUMAN_REQUIRED", reason: "ROLE_VALIDATION_MISMATCH" };
|
|
50
|
+
let trustedHost;
|
|
51
|
+
try {
|
|
52
|
+
const gateway = new URL(validation.gatewayUrl);
|
|
53
|
+
const currentGateway = new URL(input.currentGatewayUrl ?? "");
|
|
54
|
+
const trustedTransport = gateway.protocol === "https:" ||
|
|
55
|
+
(gateway.protocol === "http:" && loopbackHosts.has(gateway.hostname));
|
|
56
|
+
if (!trustedTransport ||
|
|
57
|
+
currentGateway.origin !== gateway.origin ||
|
|
58
|
+
gateway.pathname !== "/" ||
|
|
59
|
+
gateway.search !== "" ||
|
|
60
|
+
gateway.hash !== "" ||
|
|
61
|
+
gateway.username !== "" ||
|
|
62
|
+
gateway.password !== "" ||
|
|
63
|
+
currentGateway.pathname !== "/" ||
|
|
64
|
+
currentGateway.search !== "" ||
|
|
65
|
+
currentGateway.hash !== "" ||
|
|
66
|
+
currentGateway.username !== "" ||
|
|
67
|
+
currentGateway.password !== "")
|
|
68
|
+
return { decision: "HUMAN_REQUIRED", reason: "GATEWAY_MISMATCH" };
|
|
69
|
+
trustedHost = gateway.hostname.toLowerCase();
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return { decision: "HUMAN_REQUIRED", reason: "GATEWAY_MISMATCH" };
|
|
73
|
+
}
|
|
74
|
+
if (input.targetHost?.toLowerCase() !== trustedHost)
|
|
75
|
+
return { decision: "HUMAN_REQUIRED", reason: "GATEWAY_MISMATCH" };
|
|
76
|
+
if (!roleOperations[input.role.agentPackageRef].has(input.operationId))
|
|
77
|
+
return {
|
|
78
|
+
decision: "HUMAN_REQUIRED",
|
|
79
|
+
reason: "OPERATION_NOT_AUTHORIZED",
|
|
80
|
+
};
|
|
81
|
+
return classifyContext(input.role, input.context);
|
|
82
|
+
}
|
package/dist/src/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { createTaskServices, publicOperationNames, } from "@tomflow/proflow-task
|
|
|
9
9
|
import { SqliteTaskStore } from "@tomflow/proflow-task-store-sqlite";
|
|
10
10
|
import { taskMigrations } from "@tomflow/proflow-task-store-sqlite/migrations";
|
|
11
11
|
import { z } from "zod";
|
|
12
|
+
import { classifyBrowserPermission, } from "./browser-permission-policy.js";
|
|
12
13
|
import { roleOperations, rolePackageRefs, } from "./role-operations.js";
|
|
13
14
|
const loopbackHosts = new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
|
|
14
15
|
// Business owner calls are Promise-driven. This is only a last-resort transport
|
|
@@ -388,6 +389,7 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
388
389
|
return {
|
|
389
390
|
taskId: value.taskId,
|
|
390
391
|
status: value.status,
|
|
392
|
+
currentNodeId: value.currentNodeId,
|
|
391
393
|
roleBindings: value.roleBindings,
|
|
392
394
|
};
|
|
393
395
|
};
|
|
@@ -822,7 +824,11 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
822
824
|
if (request.projectRoot &&
|
|
823
825
|
resolve(request.projectRoot) !== config.workspaceRoot)
|
|
824
826
|
return false;
|
|
825
|
-
|
|
827
|
+
const nodeScoped = request.nodeId !== undefined || request.runNo !== undefined;
|
|
828
|
+
if (nodeScoped &&
|
|
829
|
+
(request.nodeId === undefined || request.runNo === undefined))
|
|
830
|
+
return false;
|
|
831
|
+
if (nodeScoped && !request.taskId)
|
|
826
832
|
return false;
|
|
827
833
|
if (request.workerRef && !request.taskId)
|
|
828
834
|
return false;
|
|
@@ -841,12 +847,30 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
841
847
|
taskId: request.taskId,
|
|
842
848
|
workerRef: request.workerRef,
|
|
843
849
|
});
|
|
844
|
-
if (request.nodeId)
|
|
845
|
-
unwrap(task.queries.getNodeContext({
|
|
850
|
+
if (request.nodeId) {
|
|
851
|
+
const nodeContext = unwrap(task.queries.getNodeContext({
|
|
846
852
|
taskId: request.taskId,
|
|
847
853
|
nodeId: request.nodeId,
|
|
848
|
-
...(request.runNo ? { runNo: request.runNo } : {}),
|
|
849
854
|
}));
|
|
855
|
+
if (taskFact.status !== "ACTIVE")
|
|
856
|
+
return false;
|
|
857
|
+
if (taskFact.currentNodeId !== nodeContext.node.nodeId)
|
|
858
|
+
return false;
|
|
859
|
+
if (request.runNo !== nodeContext.node.runNo)
|
|
860
|
+
return false;
|
|
861
|
+
if (!browserCapability &&
|
|
862
|
+
nodeContext.node.status !== "IN_PROGRESS")
|
|
863
|
+
return false;
|
|
864
|
+
const nodeBinding = taskFact.roleBindings.find((candidate) => candidate.agentPackageRef ===
|
|
865
|
+
nodeContext.node.requiredAgentPackageRef);
|
|
866
|
+
if (!nodeBinding?.workerRef)
|
|
867
|
+
return false;
|
|
868
|
+
const scopedRoleRef = request.roleRef ?? (internalBrowserCaller ? undefined : request.callerRef);
|
|
869
|
+
if (scopedRoleRef !== nodeBinding.roleRef)
|
|
870
|
+
return false;
|
|
871
|
+
if (request.workerRef !== nodeBinding.workerRef)
|
|
872
|
+
return false;
|
|
873
|
+
}
|
|
850
874
|
const browserInput = object(request.input, "execution input");
|
|
851
875
|
if (browserCapability) {
|
|
852
876
|
const targetRoleRef = string(browserInput.roleRef, "input.roleRef");
|
|
@@ -860,6 +884,29 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
860
884
|
const targetWorkerRef = string(browserInput.workerRef, "input.workerRef");
|
|
861
885
|
if (binding.workerRef !== targetWorkerRef)
|
|
862
886
|
return false;
|
|
887
|
+
if (request.capability === "worker.wake" &&
|
|
888
|
+
(targetRoleRef !== request.roleRef ||
|
|
889
|
+
targetWorkerRef !== request.workerRef ||
|
|
890
|
+
browserInput.taskId !== request.taskId ||
|
|
891
|
+
browserInput.nodeId !== request.nodeId ||
|
|
892
|
+
browserInput.runNo !== request.runNo))
|
|
893
|
+
return false;
|
|
894
|
+
if (request.capability === "worker.wake") {
|
|
895
|
+
const nodeContext = unwrap(task.queries.getNodeContext({
|
|
896
|
+
taskId: request.taskId,
|
|
897
|
+
nodeId: string(browserInput.nodeId, "input.nodeId"),
|
|
898
|
+
}));
|
|
899
|
+
const trigger = string(browserInput.trigger, "input.trigger");
|
|
900
|
+
const readyWake = nodeContext.node.status === "READY" &&
|
|
901
|
+
((trigger === "NODE_READY" && nodeContext.node.runNo === 1) ||
|
|
902
|
+
(trigger === "REOPEN" && nodeContext.node.runNo > 1));
|
|
903
|
+
const resumeWake = nodeContext.node.status === "IN_PROGRESS" &&
|
|
904
|
+
(trigger === "RECOVERY_RESUME" ||
|
|
905
|
+
trigger === "EXECUTION_RESULT_READY" ||
|
|
906
|
+
trigger === "PEER_REPLY_READY");
|
|
907
|
+
if (!readyWake && !resumeWake)
|
|
908
|
+
return false;
|
|
909
|
+
}
|
|
863
910
|
}
|
|
864
911
|
}
|
|
865
912
|
}
|
|
@@ -916,6 +963,47 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
916
963
|
throw new Error("ROLE_NOT_FOUND");
|
|
917
964
|
return role;
|
|
918
965
|
};
|
|
966
|
+
const browserPermissionRole = (roleRef) => {
|
|
967
|
+
const role = agent
|
|
968
|
+
.listRegisteredRoles()
|
|
969
|
+
.find((candidate) => candidate.roleRef === roleRef);
|
|
970
|
+
if (!role ||
|
|
971
|
+
!rolePackageRefs.includes(role.agentPackageRef))
|
|
972
|
+
return null;
|
|
973
|
+
return { ...role, agentPackageRef: role.agentPackageRef };
|
|
974
|
+
};
|
|
975
|
+
const roleCarrierValidation = async (agentPackageRef) => {
|
|
976
|
+
try {
|
|
977
|
+
const value = object(JSON.parse(await readFile(join(config.workspaceRoot, ".proflow", "state", "agent", "role-carrier-validation", `${encodeURIComponent(agentPackageRef)}.json`), "utf8")), "role carrier validation evidence");
|
|
978
|
+
if (value.contract !== "proflow.role-carrier-validation.v1")
|
|
979
|
+
return null;
|
|
980
|
+
return {
|
|
981
|
+
agentPackageRef: string(value.agentPackageRef, "agentPackageRef"),
|
|
982
|
+
registeredPackageVersion: string(value.registeredPackageVersion, "registeredPackageVersion"),
|
|
983
|
+
roleRef: string(value.roleRef, "roleRef"),
|
|
984
|
+
carrierUrl: string(value.carrierUrl, "carrierUrl"),
|
|
985
|
+
gatewayUrl: string(value.gatewayUrl, "gatewayUrl"),
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
catch {
|
|
989
|
+
return null;
|
|
990
|
+
}
|
|
991
|
+
};
|
|
992
|
+
const currentAgentGatewayUrl = async () => {
|
|
993
|
+
try {
|
|
994
|
+
const value = object(JSON.parse(await readFile(join(config.workspaceRoot, ".proflow", "runtime", "modules", "agent-gateway", "shared-facts.json"), "utf8")), "agent gateway shared facts");
|
|
995
|
+
if (value.contract !== "proflow.module-shared-facts.v1" ||
|
|
996
|
+
value.moduleRef !== "agent-gateway")
|
|
997
|
+
return null;
|
|
998
|
+
const facts = object(value.facts, "agent gateway facts");
|
|
999
|
+
return typeof facts.publicBaseUrl === "string"
|
|
1000
|
+
? facts.publicBaseUrl
|
|
1001
|
+
: null;
|
|
1002
|
+
}
|
|
1003
|
+
catch {
|
|
1004
|
+
return null;
|
|
1005
|
+
}
|
|
1006
|
+
};
|
|
919
1007
|
const roleManagement = Object.freeze({
|
|
920
1008
|
async invoke(operation, rawInput) {
|
|
921
1009
|
const value = object(rawInput, "role management input");
|
|
@@ -1122,6 +1210,61 @@ async function constructGraph(config, executionCredential, modelCredential) {
|
|
|
1122
1210
|
const value = object(rawInput, "observer application input");
|
|
1123
1211
|
if (operation === "task.projection")
|
|
1124
1212
|
return taskDriverPorts.getTaskDriveProjection(string(value.taskId, "taskId"));
|
|
1213
|
+
if (operation === "approval.executionContext") {
|
|
1214
|
+
const approval = object(await execution.invoke("getExecutionApproval", {
|
|
1215
|
+
approvalRef: string(value.approvalRef, "approvalRef"),
|
|
1216
|
+
}), "approval fact");
|
|
1217
|
+
const executionFact = object(await execution.invoke("getExecution", {
|
|
1218
|
+
executionRef: string(approval.executionRef, "executionRef"),
|
|
1219
|
+
callerRef: string(approval.callerRef, "callerRef"),
|
|
1220
|
+
}), "execution fact");
|
|
1221
|
+
const runNo = Number(executionFact.runNo);
|
|
1222
|
+
if (!Number.isInteger(runNo) || runNo <= 0)
|
|
1223
|
+
throw new Error("EXECUTION_RUN_GENERATION_REQUIRED");
|
|
1224
|
+
return {
|
|
1225
|
+
executionRef: string(executionFact.executionRef, "executionRef"),
|
|
1226
|
+
taskId: string(executionFact.taskId, "taskId"),
|
|
1227
|
+
nodeId: string(executionFact.nodeId, "nodeId"),
|
|
1228
|
+
runNo,
|
|
1229
|
+
roleRef: string(executionFact.roleRef, "roleRef"),
|
|
1230
|
+
workerRef: string(executionFact.workerRef, "workerRef"),
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
if (operation === "browser.permission.classify") {
|
|
1234
|
+
const roleRef = string(value.roleRef, "roleRef");
|
|
1235
|
+
const taskId = string(value.taskId, "taskId");
|
|
1236
|
+
const operationId = string(value.operationId, "operationId");
|
|
1237
|
+
const conversationLocator = string(value.conversationLocator, "conversationLocator");
|
|
1238
|
+
const workerRef = typeof value.workerRef === "string" ? value.workerRef : null;
|
|
1239
|
+
const role = browserPermissionRole(roleRef);
|
|
1240
|
+
if (!role)
|
|
1241
|
+
return {
|
|
1242
|
+
decision: "HUMAN_REQUIRED",
|
|
1243
|
+
reason: "ROLE_VALIDATION_MISMATCH",
|
|
1244
|
+
};
|
|
1245
|
+
let taskBinding = null;
|
|
1246
|
+
try {
|
|
1247
|
+
const current = unwrap(task.queries.getTask({ taskId }));
|
|
1248
|
+
taskBinding =
|
|
1249
|
+
current.roleBindings.find((candidate) => candidate.roleRef === roleRef &&
|
|
1250
|
+
candidate.agentPackageRef === role.agentPackageRef) ?? null;
|
|
1251
|
+
}
|
|
1252
|
+
catch {
|
|
1253
|
+
taskBinding = null;
|
|
1254
|
+
}
|
|
1255
|
+
const [validation, currentGatewayUrl] = await Promise.all([
|
|
1256
|
+
roleCarrierValidation(role.agentPackageRef),
|
|
1257
|
+
currentAgentGatewayUrl(),
|
|
1258
|
+
]);
|
|
1259
|
+
return classifyBrowserPermission({
|
|
1260
|
+
role,
|
|
1261
|
+
validation,
|
|
1262
|
+
targetHost: typeof value.targetHost === "string" ? value.targetHost : null,
|
|
1263
|
+
currentGatewayUrl,
|
|
1264
|
+
operationId,
|
|
1265
|
+
context: { conversationLocator, workerRef, taskBinding },
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1125
1268
|
if (operation === "browser.binding")
|
|
1126
1269
|
return browserOwnerPorts.task.getWorkerBinding(string(value.taskId, "taskId"), string(value.roleRef, "roleRef"));
|
|
1127
1270
|
if (operation === "browser.bindWorker") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomflow/proflow-platform-host",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -25,18 +25,18 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"zod": "4.1.12",
|
|
27
27
|
"@tomflow/proflow-agent-runtime": "^0.1.13",
|
|
28
|
-
"@tomflow/proflow-
|
|
28
|
+
"@tomflow/proflow-execution-browser-extension": "^0.1.42",
|
|
29
29
|
"@tomflow/proflow-execution-contracts": "^0.1.10",
|
|
30
|
-
"@tomflow/proflow-
|
|
30
|
+
"@tomflow/proflow-module-contract": "^0.1.13",
|
|
31
31
|
"@tomflow/proflow-task-migration-runner": "^0.1.11",
|
|
32
|
-
"@tomflow/proflow-
|
|
33
|
-
"@tomflow/proflow-task-
|
|
32
|
+
"@tomflow/proflow-task-store-sqlite": "^0.1.11",
|
|
33
|
+
"@tomflow/proflow-task-orchestration": "^0.1.10"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@tomflow/proflow-agent-gateway": "^0.1.15",
|
|
37
37
|
"@tomflow/proflow-deployment-conformance": "^0.1.13",
|
|
38
|
-
"@tomflow/proflow-
|
|
39
|
-
"@tomflow/proflow-
|
|
38
|
+
"@tomflow/proflow-execution-runtime": "^0.1.18",
|
|
39
|
+
"@tomflow/proflow-model-runtime": "^0.1.24"
|
|
40
40
|
},
|
|
41
41
|
"description": "Provides the ProFlow local application composition root that binds Task, Agent, Execution and Model owner transports.",
|
|
42
42
|
"keywords": [
|
package/proflow.module.json
CHANGED