@tomflow/proflow-task-orchestration 0.1.11 → 0.1.13
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 +14 -0
- package/dist/deployment/adapter.d.ts +3 -3
- package/dist/deployment/descriptor.d.ts +1 -1
- package/dist/deployment/descriptor.js +1 -1
- package/dist/src/contracts.js +8 -0
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.js +3 -2
- package/dist/src/phase4-continuation-services.d.ts +127 -0
- package/dist/src/phase4-continuation-services.js +296 -0
- package/dist/src/phase4-contracts.d.ts +5 -0
- package/dist/src/phase4-contracts.js +193 -0
- package/dist/src/phase4-model.d.ts +122 -0
- package/dist/src/phase4-model.js +1 -0
- package/dist/src/phase4-services.d.ts +118 -0
- package/dist/src/phase4-services.js +809 -0
- package/dist/src/product-document-path.d.ts +2 -0
- package/dist/src/product-document-path.js +24 -0
- package/dist/src/services.js +27 -2
- package/package.json +3 -3
- package/proflow.module.json +1 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { lstatSync, realpathSync } from "node:fs";
|
|
2
|
+
import { resolve, sep } from "node:path";
|
|
3
|
+
/** Only owner-generated, flat Markdown files under the workspace are accepted. */
|
|
4
|
+
export function productDocumentAbsolute(workspaceRoot, filePath) {
|
|
5
|
+
if (!/^product-requirements\/[a-f0-9]{24}\.md$/.test(filePath))
|
|
6
|
+
throw new Error("PRODUCT_DOCUMENT_PATH_INVALID");
|
|
7
|
+
const workspace = realpathSync(workspaceRoot);
|
|
8
|
+
const directory = resolve(workspace, "product-requirements");
|
|
9
|
+
const absolute = resolve(workspace, filePath);
|
|
10
|
+
for (const path of [directory, absolute]) {
|
|
11
|
+
try {
|
|
12
|
+
const stat = lstatSync(path);
|
|
13
|
+
if (stat.isSymbolicLink() || (path === directory ? !stat.isDirectory() : !stat.isFile()))
|
|
14
|
+
throw new Error("PRODUCT_DOCUMENT_PATH_INVALID");
|
|
15
|
+
if (!realpathSync(path).startsWith(`${workspace}${sep}`))
|
|
16
|
+
throw new Error("PRODUCT_DOCUMENT_PATH_INVALID");
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
if (!(error instanceof Error && "code" in error && error.code === "ENOENT"))
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return absolute;
|
|
24
|
+
}
|
package/dist/src/services.js
CHANGED
|
@@ -180,6 +180,17 @@ function recomputeReadiness(tx, task, timestamp, workspaceRoot) {
|
|
|
180
180
|
function matchesWorker(actorRef, workerRef) {
|
|
181
181
|
return workerRef !== null && actorRef === `worker:${workerRef}`;
|
|
182
182
|
}
|
|
183
|
+
function workerIdentityBoundToAnotherTask(tx, input) {
|
|
184
|
+
for (const task of tx.tasks.list()) {
|
|
185
|
+
if (task.taskId === input.taskId)
|
|
186
|
+
continue;
|
|
187
|
+
for (const binding of tx.roleBindings.listByTask(task.taskId))
|
|
188
|
+
if (binding.workerRef === input.workerRef ||
|
|
189
|
+
binding.conversationLocator === input.conversationLocator)
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
183
194
|
export function createTaskServices(options) {
|
|
184
195
|
const { store, workspaceRoot } = options;
|
|
185
196
|
const now = options.now ?? (() => new Date().toISOString());
|
|
@@ -321,13 +332,22 @@ export function createTaskServices(options) {
|
|
|
321
332
|
? taskId
|
|
322
333
|
: `id-${createHash("sha256").update(taskId).digest("hex")}`;
|
|
323
334
|
const stageDocumentPath = (taskId, type) => `.proflow/recovery/task-create/${safeTaskId(taskId)}/${type.toLowerCase().replaceAll("_", "-")}.md`;
|
|
324
|
-
const
|
|
335
|
+
const documentUpdateRecoveryTaskRoot = (taskId) => `.proflow/recovery/task-document/${safeTaskId(taskId)}`;
|
|
336
|
+
const documentUpdateRecoveryRoot = (taskId, type) => `${documentUpdateRecoveryTaskRoot(taskId)}/${type.toLowerCase().replaceAll("_", "-")}`;
|
|
325
337
|
const documentUpdateRecoveryPath = (taskId, type, idempotencyKey) => `${documentUpdateRecoveryRoot(taskId, type)}/${createHash("sha256").update(idempotencyKey).digest("hex")}`;
|
|
338
|
+
const cleanupDocumentRecoveryTaskRoot = (taskId) => {
|
|
339
|
+
const taskRootAbsolute = join(workspaceRoot, documentUpdateRecoveryTaskRoot(taskId));
|
|
340
|
+
if (existsSync(taskRootAbsolute) &&
|
|
341
|
+
readdirSync(taskRootAbsolute).length === 0)
|
|
342
|
+
rmSync(taskRootAbsolute, { recursive: true, force: true });
|
|
343
|
+
};
|
|
326
344
|
const recoverPendingDocumentUpdates = (taskId, type) => {
|
|
327
345
|
const recoveryRoot = documentUpdateRecoveryRoot(taskId, type);
|
|
328
346
|
const recoveryRootAbsolute = join(workspaceRoot, recoveryRoot);
|
|
329
|
-
if (!existsSync(recoveryRootAbsolute))
|
|
347
|
+
if (!existsSync(recoveryRootAbsolute)) {
|
|
348
|
+
cleanupDocumentRecoveryTaskRoot(taskId);
|
|
330
349
|
return;
|
|
350
|
+
}
|
|
331
351
|
const canonicalPath = relativeDocumentPath(taskId, type);
|
|
332
352
|
const canonicalAbsolute = join(workspaceRoot, canonicalPath);
|
|
333
353
|
const currentMetadata = store.read((tx) => tx.documents.get(taskId, type));
|
|
@@ -375,6 +395,7 @@ export function createTaskServices(options) {
|
|
|
375
395
|
}
|
|
376
396
|
if (readdirSync(recoveryRootAbsolute).length === 0)
|
|
377
397
|
rmSync(recoveryRootAbsolute, { recursive: true, force: true });
|
|
398
|
+
cleanupDocumentRecoveryTaskRoot(taskId);
|
|
378
399
|
};
|
|
379
400
|
const cleanupCreateStage = (taskId) => {
|
|
380
401
|
rmSync(join(workspaceRoot, `.proflow/recovery/task-create/${safeTaskId(taskId)}`), { recursive: true, force: true });
|
|
@@ -630,6 +651,8 @@ export function createTaskServices(options) {
|
|
|
630
651
|
throw new DomainError("AGENT_PACKAGE_NOT_ELIGIBLE", "Agent package is not declared by the Task.");
|
|
631
652
|
if (old.roleRef !== input.roleRef)
|
|
632
653
|
throw new DomainError("ROLE_BINDING_MISMATCH", "RoleRef does not match the declared binding.");
|
|
654
|
+
if (workerIdentityBoundToAnotherTask(tx, input))
|
|
655
|
+
throw new DomainError("TASK_ROLE_BINDING_CONFLICT", "Worker or Conversation identity is already bound to another Task.");
|
|
633
656
|
if (old.workerRef === input.workerRef &&
|
|
634
657
|
old.conversationLocator === input.conversationLocator)
|
|
635
658
|
return {
|
|
@@ -1016,11 +1039,13 @@ export function createTaskServices(options) {
|
|
|
1016
1039
|
tx.nodes.update(reopened);
|
|
1017
1040
|
for (const later of tx.nodes.listByTask(task.taskId))
|
|
1018
1041
|
if (later.sequenceNo > target.sequenceNo) {
|
|
1042
|
+
const invalidatedStartedRun = later.startedAt !== null || later.workerRef !== null;
|
|
1019
1043
|
preserveRunHistory(tx, later, later.status, timestamp);
|
|
1020
1044
|
tx.nodes.update({
|
|
1021
1045
|
...later,
|
|
1022
1046
|
status: "PENDING",
|
|
1023
1047
|
version: later.version + 1,
|
|
1048
|
+
runNo: later.runNo + (invalidatedStartedRun ? 1 : 0),
|
|
1024
1049
|
workerRef: null,
|
|
1025
1050
|
resultSummary: null,
|
|
1026
1051
|
errorCode: null,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomflow/proflow-task-orchestration",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
"@tomflow/proflow-module-contract": "^0.1.13"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@tomflow/proflow-task-migration-runner": "^0.1.11",
|
|
27
26
|
"@tomflow/proflow-deployment-conformance": "^0.1.13",
|
|
28
|
-
"@tomflow/proflow-task-store-sqlite": "^0.1.
|
|
27
|
+
"@tomflow/proflow-task-store-sqlite": "^0.1.12",
|
|
28
|
+
"@tomflow/proflow-task-migration-runner": "^0.1.11"
|
|
29
29
|
},
|
|
30
30
|
"description": "Owns the Task & Orchestration public contract, domain rules and application services.",
|
|
31
31
|
"keywords": [
|
package/proflow.module.json
CHANGED
|
@@ -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.
|
|
6
|
+
"moduleVersion": "0.1.13",
|
|
7
7
|
"kind": "library",
|
|
8
8
|
"templateVersion": "1.0.0",
|
|
9
9
|
"platformCompatibility": ">=1.0.0 <2.0.0",
|