@tomflow/proflow-platform-host 0.1.17 → 0.1.19

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-platform-host
2
2
 
3
+ ## 0.1.19
4
+
5
+ ### Patch Changes
6
+
7
+ - Release the verified Real-3 B1 task recovery, exact execution identity, durable observer resume, bounded gateway error, and matching Agent Action contract fixes.
8
+
9
+ ## 0.1.18
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.17
4
16
 
5
17
  ### Patch Changes
@@ -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.17";
9
+ moduleVersion: "0.1.19";
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.17";
27
+ readonly moduleVersion: "0.1.19";
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.17";
37
+ moduleVersion: "0.1.19";
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.17";
58
+ moduleVersion: "0.1.19";
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.17";
76
+ moduleVersion: "0.1.19";
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.17";
89
+ moduleVersion: "0.1.19";
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.17";
100
+ moduleVersion: "0.1.19";
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.17";
117
+ readonly moduleVersion: "0.1.19";
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.17";
126
+ readonly moduleVersion: "0.1.19";
127
127
  } | {
128
128
  contract: "deployment.result.v1";
129
129
  moduleRef: "platform-host";
130
- moduleVersion: "0.1.17";
130
+ moduleVersion: "0.1.19";
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.17";
6
+ readonly moduleVersion: "0.1.19";
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.17",
6
+ moduleVersion: "0.1.19",
7
7
  kind: "service",
8
8
  templateVersion: "1.0.0",
9
9
  platformCompatibility: ">=1.0.0 <2.0.0",
@@ -192,6 +192,7 @@ export type PlatformHostTaskDriverPorts = {
192
192
  } | null;
193
193
  canDrive: boolean;
194
194
  blockedReason: string | null;
195
+ resumeSignalRef: string | null;
195
196
  }>;
196
197
  getNodeContext(taskId: string, nodeId: string): Promise<{
197
198
  task: {
package/dist/src/index.js CHANGED
@@ -324,6 +324,11 @@ function string(value, name) {
324
324
  throw new TypeError(`${name} must be a non-empty string`);
325
325
  return value;
326
326
  }
327
+ function positiveInteger(value, name) {
328
+ if (!Number.isInteger(value) || Number(value) <= 0)
329
+ throw new TypeError(`${name} must be a positive integer`);
330
+ return Number(value);
331
+ }
327
332
  function unwrap(result) {
328
333
  if (!result.ok || result.data === undefined)
329
334
  throw new Error(`OWNER_CONTRACT_FAILED:${JSON.stringify(result.error)}`);
@@ -389,6 +394,7 @@ async function constructGraph(config, executionCredential, modelCredential) {
389
394
  return {
390
395
  taskId: value.taskId,
391
396
  status: value.status,
397
+ currentNodeId: value.currentNodeId,
392
398
  roleBindings: value.roleBindings,
393
399
  };
394
400
  };
@@ -627,6 +633,37 @@ async function constructGraph(config, executionCredential, modelCredential) {
627
633
  }
628
634
  return rawRecord;
629
635
  };
636
+ const admissionError = (code, httpStatus = 403) => {
637
+ throw Object.assign(new Error(code), { httpStatus });
638
+ };
639
+ const admitExactNodeExecution = async (input, authenticatedRoleRef) => {
640
+ const taskId = string(input.taskId, "taskId");
641
+ const nodeId = string(input.nodeId, "nodeId");
642
+ const runNo = positiveInteger(input.runNo, "runNo");
643
+ const workerRef = await admitTaskParticipant(taskId, authenticatedRoleRef);
644
+ await agent.validateWorker({ authenticatedRoleRef, taskId, workerRef });
645
+ const taskFact = taskFacts(taskId);
646
+ const nodeContext = unwrap(task.queries.getNodeContext({ taskId, nodeId }));
647
+ if (taskFact.status !== "ACTIVE")
648
+ admissionError("EXECUTION_TASK_NOT_ACTIVE");
649
+ if (taskFact.currentNodeId !== nodeId)
650
+ admissionError("EXECUTION_NODE_NOT_CURRENT");
651
+ if (nodeContext.node.status !== "IN_PROGRESS")
652
+ admissionError("EXECUTION_NODE_NOT_RUNNING");
653
+ if (nodeContext.node.runNo !== runNo)
654
+ admissionError("EXECUTION_GENERATION_MISMATCH");
655
+ const nodeBinding = taskFact.roleBindings.find((binding) => binding.agentPackageRef === nodeContext.node.requiredAgentPackageRef);
656
+ if (!nodeBinding)
657
+ throw Object.assign(new Error("TASK_ROLE_BINDING_REQUIRED"), {
658
+ httpStatus: 403,
659
+ });
660
+ if (nodeBinding.roleRef !== authenticatedRoleRef)
661
+ admissionError("EXECUTION_ROLE_SCOPE_MISMATCH");
662
+ if (nodeBinding.workerRef !== workerRef ||
663
+ nodeContext.node.workerRef !== workerRef)
664
+ admissionError("EXECUTION_WORKER_SCOPE_MISMATCH");
665
+ return { taskId, nodeId, runNo, workerRef };
666
+ };
630
667
  const route = async (operationId, authenticatedRoleRef, rawInput, context) => {
631
668
  const role = agent.getRegisteredRole(authenticatedRoleRef);
632
669
  if (!roleOperations[role.agentPackageRef]?.has(operationId))
@@ -689,23 +726,16 @@ async function constructGraph(config, executionCredential, modelCredential) {
689
726
  return taskResult;
690
727
  }
691
728
  if (operationId === "executeCapability") {
692
- const taskId = typeof input.taskId === "string" ? input.taskId : undefined;
693
- let canonicalWorkerRef;
694
- if (taskId) {
695
- canonicalWorkerRef = await admitTaskParticipant(taskId, authenticatedRoleRef, typeof input.workerRef === "string" ? input.workerRef : undefined);
696
- await agent.validateWorker({
697
- authenticatedRoleRef,
698
- taskId,
699
- workerRef: canonicalWorkerRef,
700
- });
701
- }
729
+ const scope = await admitExactNodeExecution(input, authenticatedRoleRef);
730
+ const { workerRef: _suppliedWorkerRef, projectRoot: _suppliedProjectRoot, roleRef: _suppliedRoleRef, callerRef: _suppliedCallerRef, ...ownerInput } = input;
702
731
  const result = await execution.invoke(operationId, {
703
- ...input,
732
+ ...ownerInput,
704
733
  callerRef: authenticatedRoleRef,
705
734
  roleRef: authenticatedRoleRef,
706
- ...(canonicalWorkerRef !== undefined
707
- ? { workerRef: canonicalWorkerRef }
708
- : {}),
735
+ taskId: scope.taskId,
736
+ nodeId: scope.nodeId,
737
+ runNo: scope.runNo,
738
+ workerRef: scope.workerRef,
709
739
  });
710
740
  // A normal Action may complete synchronously inside the current Worker Turn.
711
741
  // Do not manufacture a Browser RESUME for every terminal Execution record.
@@ -823,7 +853,11 @@ async function constructGraph(config, executionCredential, modelCredential) {
823
853
  if (request.projectRoot &&
824
854
  resolve(request.projectRoot) !== config.workspaceRoot)
825
855
  return false;
826
- if ((request.nodeId || request.runNo) && !request.taskId)
856
+ const nodeScoped = request.nodeId !== undefined || request.runNo !== undefined;
857
+ if (nodeScoped &&
858
+ (request.nodeId === undefined || request.runNo === undefined))
859
+ return false;
860
+ if (nodeScoped && !request.taskId)
827
861
  return false;
828
862
  if (request.workerRef && !request.taskId)
829
863
  return false;
@@ -842,12 +876,30 @@ async function constructGraph(config, executionCredential, modelCredential) {
842
876
  taskId: request.taskId,
843
877
  workerRef: request.workerRef,
844
878
  });
845
- if (request.nodeId)
846
- unwrap(task.queries.getNodeContext({
879
+ if (request.nodeId) {
880
+ const nodeContext = unwrap(task.queries.getNodeContext({
847
881
  taskId: request.taskId,
848
882
  nodeId: request.nodeId,
849
- ...(request.runNo ? { runNo: request.runNo } : {}),
850
883
  }));
884
+ if (taskFact.status !== "ACTIVE")
885
+ return false;
886
+ if (taskFact.currentNodeId !== nodeContext.node.nodeId)
887
+ return false;
888
+ if (request.runNo !== nodeContext.node.runNo)
889
+ return false;
890
+ if (!browserCapability && nodeContext.node.status !== "IN_PROGRESS")
891
+ return false;
892
+ const nodeBinding = taskFact.roleBindings.find((candidate) => candidate.agentPackageRef ===
893
+ nodeContext.node.requiredAgentPackageRef);
894
+ if (!nodeBinding?.workerRef)
895
+ return false;
896
+ const scopedRoleRef = request.roleRef ??
897
+ (internalBrowserCaller ? undefined : request.callerRef);
898
+ if (scopedRoleRef !== nodeBinding.roleRef)
899
+ return false;
900
+ if (request.workerRef !== nodeBinding.workerRef)
901
+ return false;
902
+ }
851
903
  const browserInput = object(request.input, "execution input");
852
904
  if (browserCapability) {
853
905
  const targetRoleRef = string(browserInput.roleRef, "input.roleRef");
@@ -861,6 +913,30 @@ async function constructGraph(config, executionCredential, modelCredential) {
861
913
  const targetWorkerRef = string(browserInput.workerRef, "input.workerRef");
862
914
  if (binding.workerRef !== targetWorkerRef)
863
915
  return false;
916
+ if (request.capability === "worker.wake" &&
917
+ (targetRoleRef !== request.roleRef ||
918
+ targetWorkerRef !== request.workerRef ||
919
+ browserInput.taskId !== request.taskId ||
920
+ browserInput.nodeId !== request.nodeId ||
921
+ browserInput.runNo !== request.runNo))
922
+ return false;
923
+ if (request.capability === "worker.wake") {
924
+ const nodeContext = unwrap(task.queries.getNodeContext({
925
+ taskId: request.taskId,
926
+ nodeId: string(browserInput.nodeId, "input.nodeId"),
927
+ }));
928
+ const trigger = string(browserInput.trigger, "input.trigger");
929
+ const readyWake = nodeContext.node.status === "READY" &&
930
+ ((trigger === "NODE_READY" && nodeContext.node.runNo === 1) ||
931
+ (trigger === "REOPEN" && nodeContext.node.runNo > 1));
932
+ const resumeWake = nodeContext.node.status === "IN_PROGRESS" &&
933
+ (trigger === "RECOVERY_RESUME" ||
934
+ trigger === "EXECUTION_RESULT_READY" ||
935
+ trigger === "TASK_RESUMED" ||
936
+ trigger === "PEER_REPLY_READY");
937
+ if (!readyWake && !resumeWake)
938
+ return false;
939
+ }
864
940
  }
865
941
  }
866
942
  }
@@ -1111,6 +1187,22 @@ async function constructGraph(config, executionCredential, modelCredential) {
1111
1187
  actorRef: "extension:human",
1112
1188
  idempotencyKey: string(value.idempotencyKey, "idempotencyKey"),
1113
1189
  }));
1190
+ if (operation === "message.acknowledge")
1191
+ return unwrap(task.commands.acknowledgeMessage({
1192
+ messageId: string(value.messageId, "messageId"),
1193
+ ...(typeof value.resolution === "string"
1194
+ ? { resolution: value.resolution }
1195
+ : {}),
1196
+ actorRef: "extension:human",
1197
+ idempotencyKey: string(value.idempotencyKey, "idempotencyKey"),
1198
+ }));
1199
+ if (operation === "task.resume")
1200
+ return unwrap(task.commands.resumeTask({
1201
+ taskId: string(value.taskId, "taskId"),
1202
+ expectedTaskVersion: positiveInteger(value.expectedTaskVersion, "expectedTaskVersion"),
1203
+ actorRef: "extension:human",
1204
+ idempotencyKey: string(value.idempotencyKey, "idempotencyKey"),
1205
+ }));
1114
1206
  if (operation === "node.reopen")
1115
1207
  return unwrap(task.commands.reopenNode({
1116
1208
  taskId: string(value.taskId, "taskId"),
@@ -1164,6 +1256,26 @@ async function constructGraph(config, executionCredential, modelCredential) {
1164
1256
  const value = object(rawInput, "observer application input");
1165
1257
  if (operation === "task.projection")
1166
1258
  return taskDriverPorts.getTaskDriveProjection(string(value.taskId, "taskId"));
1259
+ if (operation === "approval.executionContext") {
1260
+ const approval = object(await execution.invoke("getExecutionApproval", {
1261
+ approvalRef: string(value.approvalRef, "approvalRef"),
1262
+ }), "approval fact");
1263
+ const executionFact = object(await execution.invoke("getExecution", {
1264
+ executionRef: string(approval.executionRef, "executionRef"),
1265
+ callerRef: string(approval.callerRef, "callerRef"),
1266
+ }), "execution fact");
1267
+ const runNo = Number(executionFact.runNo);
1268
+ if (!Number.isInteger(runNo) || runNo <= 0)
1269
+ throw new Error("EXECUTION_RUN_GENERATION_REQUIRED");
1270
+ return {
1271
+ executionRef: string(executionFact.executionRef, "executionRef"),
1272
+ taskId: string(executionFact.taskId, "taskId"),
1273
+ nodeId: string(executionFact.nodeId, "nodeId"),
1274
+ runNo,
1275
+ roleRef: string(executionFact.roleRef, "roleRef"),
1276
+ workerRef: string(executionFact.workerRef, "workerRef"),
1277
+ };
1278
+ }
1167
1279
  if (operation === "browser.permission.classify") {
1168
1280
  const roleRef = string(value.roleRef, "roleRef");
1169
1281
  const taskId = string(value.taskId, "taskId");
@@ -1392,15 +1504,16 @@ async function constructGraph(config, executionCredential, modelCredential) {
1392
1504
  });
1393
1505
  return admitExecutionRead(authenticatedRoleRef, record);
1394
1506
  }
1395
- const taskId = typeof value.taskId === "string" ? value.taskId : undefined;
1396
- let canonicalWorkerRef;
1397
- if (taskId)
1398
- canonicalWorkerRef = await admitTaskParticipant(taskId, authenticatedRoleRef, typeof value.workerRef === "string" ? value.workerRef : undefined);
1507
+ const scope = await admitExactNodeExecution(value, authenticatedRoleRef);
1508
+ const { workerRef: _suppliedWorkerRef, projectRoot: _suppliedProjectRoot, roleRef: _suppliedRoleRef, callerRef: _suppliedCallerRef, ...ownerInput } = value;
1399
1509
  const record = await execution.invoke("lookupExecutionIntent", {
1400
- ...value,
1510
+ ...ownerInput,
1401
1511
  callerRef: authenticatedRoleRef,
1402
1512
  roleRef: authenticatedRoleRef,
1403
- ...(canonicalWorkerRef ? { workerRef: canonicalWorkerRef } : {}),
1513
+ taskId: scope.taskId,
1514
+ nodeId: scope.nodeId,
1515
+ runNo: scope.runNo,
1516
+ workerRef: scope.workerRef,
1404
1517
  });
1405
1518
  return admitExecutionRead(authenticatedRoleRef, record);
1406
1519
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomflow/proflow-platform-host",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -24,19 +24,19 @@
24
24
  ],
25
25
  "dependencies": {
26
26
  "zod": "4.1.12",
27
- "@tomflow/proflow-task-migration-runner": "^0.1.11",
28
- "@tomflow/proflow-execution-browser-extension": "^0.1.38",
29
- "@tomflow/proflow-task-orchestration": "^0.1.10",
30
- "@tomflow/proflow-task-store-sqlite": "^0.1.11",
31
27
  "@tomflow/proflow-agent-runtime": "^0.1.13",
28
+ "@tomflow/proflow-execution-contracts": "^0.1.10",
29
+ "@tomflow/proflow-execution-browser-extension": "^0.1.45",
32
30
  "@tomflow/proflow-module-contract": "^0.1.13",
33
- "@tomflow/proflow-execution-contracts": "^0.1.10"
31
+ "@tomflow/proflow-task-migration-runner": "^0.1.11",
32
+ "@tomflow/proflow-task-orchestration": "^0.1.11",
33
+ "@tomflow/proflow-task-store-sqlite": "^0.1.11"
34
34
  },
35
35
  "devDependencies": {
36
- "@tomflow/proflow-model-runtime": "^0.1.22",
37
- "@tomflow/proflow-deployment-conformance": "^0.1.13",
38
- "@tomflow/proflow-agent-gateway": "^0.1.15",
39
- "@tomflow/proflow-execution-runtime": "^0.1.17"
36
+ "@tomflow/proflow-agent-gateway": "^0.1.16",
37
+ "@tomflow/proflow-execution-runtime": "^0.1.18",
38
+ "@tomflow/proflow-model-runtime": "^0.1.24",
39
+ "@tomflow/proflow-deployment-conformance": "^0.1.13"
40
40
  },
41
41
  "description": "Provides the ProFlow local application composition root that binds Task, Agent, Execution and Model owner transports.",
42
42
  "keywords": [
@@ -3,7 +3,7 @@
3
3
  "contractVersion": "1.0.0",
4
4
  "moduleRef": "platform-host",
5
5
  "packageName": "@tomflow/proflow-platform-host",
6
- "moduleVersion": "0.1.17",
6
+ "moduleVersion": "0.1.19",
7
7
  "kind": "service",
8
8
  "templateVersion": "1.0.0",
9
9
  "platformCompatibility": ">=1.0.0 <2.0.0",