@tomflow/proflow-task-orchestration 0.1.10 → 0.1.11

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,11 @@
1
1
  # @tomflow/proflow-task-orchestration
2
2
 
3
+ ## 0.1.11
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
+
3
9
  ## 0.1.10
4
10
 
5
11
  ### Patch Changes
@@ -4,7 +4,7 @@ declare const success: () => {
4
4
  readonly ok: true;
5
5
  readonly status: "SUCCEEDED";
6
6
  readonly moduleRef: "task-orchestration";
7
- readonly moduleVersion: "0.1.10";
7
+ readonly moduleVersion: "0.1.11";
8
8
  };
9
9
  observedEffects: string[];
10
10
  };
@@ -17,7 +17,7 @@ export declare const behaviorAdapter: {
17
17
  ok: true;
18
18
  status: "SUCCEEDED";
19
19
  moduleRef: "task-orchestration";
20
- moduleVersion: "0.1.10";
20
+ moduleVersion: "0.1.11";
21
21
  data: {
22
22
  setupStatus: "READY";
23
23
  runtimeStatus: "NOT_APPLICABLE";
@@ -32,7 +32,7 @@ export declare const behaviorAdapter: {
32
32
  ok: true;
33
33
  status: "SUCCEEDED";
34
34
  moduleRef: "task-orchestration";
35
- moduleVersion: "0.1.10";
35
+ moduleVersion: "0.1.11";
36
36
  data: {
37
37
  docs: string;
38
38
  };
@@ -3,7 +3,7 @@ export declare const descriptor: {
3
3
  readonly contractVersion: "1.0.0";
4
4
  readonly moduleRef: "task-orchestration";
5
5
  readonly packageName: "@tomflow/proflow-task-orchestration";
6
- readonly moduleVersion: "0.1.10";
6
+ readonly moduleVersion: "0.1.11";
7
7
  readonly kind: "library";
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: "task-orchestration",
5
5
  packageName: "@tomflow/proflow-task-orchestration",
6
- moduleVersion: "0.1.10",
6
+ moduleVersion: "0.1.11",
7
7
  kind: "library",
8
8
  templateVersion: "1.0.0",
9
9
  platformCompatibility: ">=1.0.0 <2.0.0",
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { requiredTaskAgentPackageRefs } from "./model.js";
2
+ import { requiredTaskAgentPackageRefs, taskWaitTypes } from "./model.js";
3
3
  export const publicOperationNames = [
4
4
  "createTaskGroup",
5
5
  "getTaskGroup",
@@ -136,7 +136,7 @@ const schemas = {
136
136
  waitNode: z
137
137
  .object({
138
138
  ...nodeVersion,
139
- waitType: id,
139
+ waitType: z.enum(taskWaitTypes),
140
140
  reasonCode: id,
141
141
  message: id,
142
142
  relatedRef: id.optional(),
@@ -1,4 +1,6 @@
1
1
  export declare const requiredTaskAgentPackageRefs: readonly ["@tomflow/proflow-agent-product", "@tomflow/proflow-agent-controller-dev", "@tomflow/proflow-agent-test-ops"];
2
+ export declare const taskWaitTypes: readonly ["BUSINESS_CONFIRMATION", "REQUIREMENT_BLOCKED", "EXTERNAL_BUSINESS_DEPENDENCY"];
3
+ export type TaskWaitType = (typeof taskWaitTypes)[number];
2
4
  export type TaskGroupStatus = "READY" | "ACTIVE" | "SUCCEEDED";
3
5
  export type TaskStatus = "PENDING" | "READY" | "ACTIVE" | "WAITING" | "FAILED" | "PAUSED" | "SUCCEEDED" | "TERMINATED";
4
6
  export type NodeStatus = "PENDING" | "READY" | "IN_PROGRESS" | "WAITING" | "FAILED" | "SUCCEEDED" | "TERMINATED";
package/dist/src/model.js CHANGED
@@ -3,3 +3,8 @@ export const requiredTaskAgentPackageRefs = [
3
3
  "@tomflow/proflow-agent-controller-dev",
4
4
  "@tomflow/proflow-agent-test-ops",
5
5
  ];
6
+ export const taskWaitTypes = [
7
+ "BUSINESS_CONFIRMATION",
8
+ "REQUIREMENT_BLOCKED",
9
+ "EXTERNAL_BUSINESS_DEPENDENCY",
10
+ ];
@@ -144,6 +144,7 @@ export declare function createTaskServices(options: {
144
144
  } | null;
145
145
  canDrive: boolean;
146
146
  blockedReason: string | null;
147
+ resumeSignalRef: string | null;
147
148
  }>;
148
149
  getNodeContext: (raw: unknown) => TaskResult<{
149
150
  task: Pick<Task, "taskId" | "title" | "objective" | "status" | "version">;
@@ -726,15 +726,24 @@ export function createTaskServices(options) {
726
726
  const current = task.currentNodeId
727
727
  ? tx.nodes.get(task.currentNodeId)
728
728
  : undefined;
729
+ if (task.status === "WAITING" && current?.status !== "WAITING")
730
+ throw new DomainError("TASK_INVALID_STATE", "A WAITING Task must have a current WAITING Node.");
729
731
  if (current?.status === "FAILED")
730
732
  throw new DomainError("TASK_INVALID_STATE", "A failed Node must be explicitly reopened before resume.");
731
- if (current?.status === "WAITING")
733
+ if (current?.status === "WAITING") {
734
+ const unresolved = tx.messages
735
+ .listPending()
736
+ .some((message) => message.taskId === task.taskId &&
737
+ message.nodeId === current.nodeId);
738
+ if (unresolved)
739
+ throw new DomainError("TASK_BLOCKER_UNRESOLVED", "The current Node still has an unresolved business blocker.");
732
740
  tx.nodes.update({
733
741
  ...current,
734
742
  status: "IN_PROGRESS",
735
743
  version: current.version + 1,
736
744
  updatedAt: timestamp,
737
745
  });
746
+ }
738
747
  const updated = {
739
748
  ...task,
740
749
  status: "ACTIVE",
@@ -910,6 +919,8 @@ export function createTaskServices(options) {
910
919
  checkVersion(node.version, input.expectedNodeVersion, "NODE_VERSION_CONFLICT");
911
920
  if (task.status !== "ACTIVE")
912
921
  throw new DomainError("TASK_INVALID_STATE", "Task must be ACTIVE before a Node can wait.");
922
+ if (task.currentNodeId !== node.nodeId)
923
+ throw new DomainError("NODE_NOT_CURRENT", "Only the current Node can enter WAITING.");
913
924
  if (node.status !== "IN_PROGRESS" ||
914
925
  !matchesWorker(input.actorRef, node.workerRef))
915
926
  throw new DomainError(!matchesWorker(input.actorRef, node.workerRef)
@@ -950,10 +961,14 @@ export function createTaskServices(options) {
950
961
  const node = requireNode(tx, input.taskId, input.nodeId);
951
962
  checkVersion(task.version, input.expectedTaskVersion, "TASK_VERSION_CONFLICT");
952
963
  checkVersion(node.version, input.expectedNodeVersion, "NODE_VERSION_CONFLICT");
953
- if (!["ACTIVE", "WAITING"].includes(task.status))
954
- throw new DomainError("TASK_INVALID_STATE", "Task cannot accept a Node failure in its current state.");
955
- if (!["IN_PROGRESS", "WAITING"].includes(node.status))
956
- throw new DomainError("NODE_INVALID_STATE", "Node cannot fail.");
964
+ if (task.status !== "ACTIVE")
965
+ throw new DomainError("TASK_INVALID_STATE", "Task must be ACTIVE before the current run can fail.");
966
+ if (task.currentNodeId !== node.nodeId)
967
+ throw new DomainError("NODE_NOT_CURRENT", "Only the current Node can record a run failure.");
968
+ if (!matchesWorker(input.actorRef, node.workerRef))
969
+ throw new DomainError("WORKER_MISMATCH", "Only the bound running Worker may fail the current Node.");
970
+ if (node.status !== "IN_PROGRESS")
971
+ throw new DomainError("NODE_INVALID_STATE", "Only the current IN_PROGRESS run can fail.");
957
972
  const failedNode = {
958
973
  ...node,
959
974
  status: "FAILED",
@@ -1116,6 +1131,13 @@ export function createTaskServices(options) {
1116
1131
  else if (!terminal && !roleBinding?.workerRef)
1117
1132
  blockedReason = "TASK_ROLE_BINDING_REQUIRED";
1118
1133
  const canDrive = !terminal && blockedReason === null;
1134
+ let durableResumeEventId;
1135
+ if (task.status === "ACTIVE" && currentNode?.status === "IN_PROGRESS")
1136
+ for (const taskEvent of tx.events.listByTask(task.taskId))
1137
+ if (taskEvent.eventType === "TASK_RESUMED" &&
1138
+ taskEvent.taskVersion === task.version &&
1139
+ taskEvent.eventId !== undefined)
1140
+ durableResumeEventId = taskEvent.eventId;
1119
1141
  return {
1120
1142
  taskId: task.taskId,
1121
1143
  taskStatus: task.status,
@@ -1140,6 +1162,9 @@ export function createTaskServices(options) {
1140
1162
  : null,
1141
1163
  canDrive,
1142
1164
  blockedReason,
1165
+ resumeSignalRef: durableResumeEventId === undefined
1166
+ ? null
1167
+ : `task-event:${durableResumeEventId}`,
1143
1168
  };
1144
1169
  }),
1145
1170
  getNodeContext: (raw) => queryWithDocumentRecovery("getNodeContext", raw, (tx, input) => {
@@ -7,7 +7,7 @@ const taskTransitions = {
7
7
  READY: ["ACTIVE", "TERMINATED"],
8
8
  ACTIVE: ["WAITING", "PAUSED", "FAILED", "SUCCEEDED", "TERMINATED"],
9
9
  WAITING: ["ACTIVE", "PAUSED", "TERMINATED"],
10
- FAILED: ["PAUSED", "TERMINATED"],
10
+ FAILED: ["TERMINATED"],
11
11
  PAUSED: ["ACTIVE", "TERMINATED"],
12
12
  SUCCEEDED: [],
13
13
  TERMINATED: [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomflow/proflow-task-orchestration",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -23,8 +23,8 @@
23
23
  "@tomflow/proflow-module-contract": "^0.1.13"
24
24
  },
25
25
  "devDependencies": {
26
- "@tomflow/proflow-deployment-conformance": "^0.1.13",
27
26
  "@tomflow/proflow-task-migration-runner": "^0.1.11",
27
+ "@tomflow/proflow-deployment-conformance": "^0.1.13",
28
28
  "@tomflow/proflow-task-store-sqlite": "^0.1.11"
29
29
  },
30
30
  "description": "Owns the Task & Orchestration public contract, domain rules and application services.",
@@ -3,7 +3,7 @@
3
3
  "contractVersion": "1.0.0",
4
4
  "moduleRef": "task-orchestration",
5
5
  "packageName": "@tomflow/proflow-task-orchestration",
6
- "moduleVersion": "0.1.10",
6
+ "moduleVersion": "0.1.11",
7
7
  "kind": "library",
8
8
  "templateVersion": "1.0.0",
9
9
  "platformCompatibility": ">=1.0.0 <2.0.0",