agentic-workflow-manager 6.2.1 → 6.4.0
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/README.md +17 -0
- package/dist/src/commands/job/gate.js +37 -13
- package/dist/src/commands/job/index.js +48 -8
- package/dist/src/commands/job/request.js +44 -6
- package/dist/src/commands/track/emit.js +26 -0
- package/dist/src/commands/track/index.js +194 -0
- package/dist/src/commands/track/status.js +63 -0
- package/dist/src/commands/track/supervisor-wrapper.js +89 -0
- package/dist/src/commands/watch/apply.js +201 -13
- package/dist/src/commands/watch/index.js +14 -0
- package/dist/src/commands/watch/runner.js +20 -1
- package/dist/src/commands/watch/supervisor.js +251 -27
- package/dist/src/commands/watch/teardown-driver.js +189 -0
- package/dist/src/commands/watch/tracks.js +1100 -0
- package/dist/src/core/journal/adapter.js +46 -6
- package/dist/src/core/journal/paths.js +9 -0
- package/dist/src/core/journal/process.js +101 -1
- package/dist/src/core/journal/requests.js +5 -1
- package/dist/src/core/journal/store.js +34 -2
- package/dist/src/core/journal/types.js +93 -4
- package/dist/src/core/paths.js +51 -0
- package/dist/src/core/tracks/concurrency.js +85 -0
- package/dist/src/core/tracks/context.js +89 -0
- package/dist/src/core/tracks/descriptor.js +40 -0
- package/dist/src/core/tracks/git.js +318 -0
- package/dist/src/core/tracks/join.js +185 -0
- package/dist/src/core/tracks/ownership.js +100 -0
- package/dist/src/core/tracks/plan-parser.js +108 -0
- package/dist/src/core/tracks/protocol.js +466 -0
- package/dist/src/core/tracks/teardown.js +34 -0
- package/dist/src/core/tracks/types.js +14 -0
- package/dist/src/index.js +2 -0
- package/dist/tests/commands/job/gate-reconcile.test.js +267 -0
- package/dist/tests/commands/track/fixtures.js +13 -0
- package/dist/tests/commands/track/status.test.js +157 -0
- package/dist/tests/commands/track/supervisor-wrapper-cli.test.js +129 -0
- package/dist/tests/commands/track/supervisor-wrapper.test.js +131 -0
- package/dist/tests/commands/track/verbs.test.js +326 -0
- package/dist/tests/commands/watch/apply.test.js +116 -4
- package/dist/tests/commands/watch/runner.test.js +22 -0
- package/dist/tests/commands/watch/supervisor-loop.test.js +150 -0
- package/dist/tests/commands/watch/track-bootstrap-crash.test.js +342 -0
- package/dist/tests/commands/watch/track-bootstrap.test.js +350 -0
- package/dist/tests/commands/watch/track-finalize.test.js +643 -0
- package/dist/tests/commands/watch/track-freeze.test.js +591 -0
- package/dist/tests/commands/watch/track-join-crash.test.js +439 -0
- package/dist/tests/commands/watch/track-runtime-git.test.js +119 -0
- package/dist/tests/commands/watch/track-teardown-crash.test.js +426 -0
- package/dist/tests/core/journal/adapter-override.test.js +51 -0
- package/dist/tests/core/journal/process.test.js +37 -0
- package/dist/tests/core/journal/requests.test.js +21 -0
- package/dist/tests/core/journal/store.test.js +28 -0
- package/dist/tests/core/journal/types.test.js +89 -0
- package/dist/tests/core/same-existing-path.test.js +48 -0
- package/dist/tests/core/tracks/concurrency.test.js +134 -0
- package/dist/tests/core/tracks/context.test.js +177 -0
- package/dist/tests/core/tracks/descriptor.test.js +76 -0
- package/dist/tests/core/tracks/git.test.js +132 -0
- package/dist/tests/core/tracks/join-reconcile.test.js +53 -0
- package/dist/tests/core/tracks/join.test.js +197 -0
- package/dist/tests/core/tracks/ownership.test.js +96 -0
- package/dist/tests/core/tracks/plan-parser.test.js +94 -0
- package/dist/tests/core/tracks/protocol.test.js +416 -0
- package/dist/tests/core/tracks/teardown.test.js +62 -0
- package/dist/tests/helpers/git-fixture.js +35 -0
- package/dist/tests/integration/parallel-tracks.e2e.test.js +343 -0
- package/dist/tests/integration/r5-provider-evidence.test.js +67 -0
- package/dist/tests/structural/path-identity-not-string-compare.test.js +51 -0
- package/dist/tests/structural/sensor-configs-are-present.test.js +49 -0
- package/package.json +1 -1
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ownershipPrefix = ownershipPrefix;
|
|
7
|
+
exports.canonicalResource = canonicalResource;
|
|
8
|
+
exports.assessDeclaredIndependence = assessDeclaredIndependence;
|
|
9
|
+
exports.assessActualOwnership = assessActualOwnership;
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const GLOBAL = [
|
|
12
|
+
{ kind: 'lockfile', re: /(^|\/)(?:package-lock\.json|npm-shrinkwrap\.json|yarn\.lock|pnpm-lock\.yaml|Cargo\.lock|poetry\.lock)$/i },
|
|
13
|
+
{ kind: 'manifest', re: /(^|\/)(?:package\.json|Cargo\.toml|pyproject\.toml)$/i },
|
|
14
|
+
{ kind: 'migration', re: /(^|\/)(?:migrations?|schema)\//i },
|
|
15
|
+
{ kind: 'snapshot', re: /(?:^|\/)(__snapshots__\/|[^/]+\.snap$)/i },
|
|
16
|
+
{ kind: 'generated', re: /(^|\/)(?:dist|generated|coverage)\//i },
|
|
17
|
+
];
|
|
18
|
+
const canon = (p) => path_1.default.posix.normalize(p.replaceAll('\\', '/')).replace(/^\.\//, '');
|
|
19
|
+
const key = (p) => canon(p).toLocaleLowerCase('en-US');
|
|
20
|
+
const GLOB_CHARS = /[*?[\]{}]/;
|
|
21
|
+
/**
|
|
22
|
+
* Formas soportadas: `dir/sub/file.ts`, `dir/`, `dir/*`, `dir/**`. Cualquier otro glob
|
|
23
|
+
* (intermedio, `?`, clases, llaves) se rechaza: un patrón que no sabemos expandir no puede
|
|
24
|
+
* usarse para AFIRMAR que dos tracks no se pisan.
|
|
25
|
+
*/
|
|
26
|
+
function ownershipPrefix(owner) {
|
|
27
|
+
const trimmed = canon(owner).replace(/\/(?:\*\*|\*)$/, '/');
|
|
28
|
+
if (GLOB_CHARS.test(trimmed))
|
|
29
|
+
throw new Error(`ownership no soporta este glob: ${owner}`);
|
|
30
|
+
return key(trimmed);
|
|
31
|
+
}
|
|
32
|
+
const covers = (owner, actual) => {
|
|
33
|
+
const o = ownershipPrefix(owner);
|
|
34
|
+
const a = key(actual);
|
|
35
|
+
return o.endsWith('/') ? a === o.slice(0, -1) || a.startsWith(o) : a === o;
|
|
36
|
+
};
|
|
37
|
+
/** Un patrón inexpandible cuenta como intersección: nunca habilita paralelismo por ignorancia. */
|
|
38
|
+
const intersects = (left, right) => {
|
|
39
|
+
try {
|
|
40
|
+
return covers(left, right) || covers(right, left);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
/** Simétrico del anterior: un patrón inexpandible nunca prueba propiedad. */
|
|
47
|
+
const proves = (owner, actual) => {
|
|
48
|
+
try {
|
|
49
|
+
return covers(owner, actual);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
function canonicalResource(raw) {
|
|
56
|
+
const match = raw.match(/^([a-z][a-z0-9-]*):(.+)$/i);
|
|
57
|
+
if (match === null || match[2].trim().length === 0)
|
|
58
|
+
throw new Error(`recurso debe usar <clase>:<valor>: ${raw}`);
|
|
59
|
+
// Case-fold el valor igual que `key()` hace con paths: sin esto, `db:Dev`
|
|
60
|
+
// y `db:dev` producen strings distintos y el Set.has() de
|
|
61
|
+
// assessDeclaredIndependence no detecta la colision (fail-open).
|
|
62
|
+
return `${match[1].toLowerCase()}:${match[2].trim().toLocaleLowerCase('en-US')}`;
|
|
63
|
+
}
|
|
64
|
+
function assessDeclaredIndependence(tracks) {
|
|
65
|
+
const reasons = new Set();
|
|
66
|
+
for (const t of tracks) {
|
|
67
|
+
for (const owner of t.ownership) {
|
|
68
|
+
try {
|
|
69
|
+
ownershipPrefix(owner);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
reasons.add(`unsupported-glob:${canon(owner)}`);
|
|
73
|
+
}
|
|
74
|
+
const global = GLOBAL.find((g) => g.re.test(canon(owner)));
|
|
75
|
+
if (global !== undefined)
|
|
76
|
+
reasons.add(`global:${global.kind}:${canon(owner)}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
for (let i = 0; i < tracks.length; i++)
|
|
80
|
+
for (let j = i + 1; j < tracks.length; j++) {
|
|
81
|
+
for (const left of tracks[i].ownership)
|
|
82
|
+
for (const right of tracks[j].ownership) {
|
|
83
|
+
if (intersects(left, right))
|
|
84
|
+
reasons.add(`path:${canon(right)}`);
|
|
85
|
+
}
|
|
86
|
+
const rightResources = new Set(tracks[j].sharedResources.map(canonicalResource));
|
|
87
|
+
for (const resource of tracks[i].sharedResources.map(canonicalResource)) {
|
|
88
|
+
if (rightResources.has(resource))
|
|
89
|
+
reasons.add(`resource:${resource}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { parallel: reasons.size === 0, reasons: [...reasons].sort() };
|
|
93
|
+
}
|
|
94
|
+
function assessActualOwnership(track, changes) {
|
|
95
|
+
const actual = changes.flatMap((c) => c.oldPath === undefined ? [c.path] : [c.oldPath, c.path]);
|
|
96
|
+
return {
|
|
97
|
+
outsideOwnership: actual.filter((p) => !track.ownership.some((o) => proves(o, p))).sort(),
|
|
98
|
+
globalClasses: actual.flatMap((p) => GLOBAL.filter((g) => g.re.test(canon(p))).map((g) => `${g.kind}:${canon(p)}`)).sort(),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseTrackPlan = parseTrackPlan;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const taskHeading = /^### Task ([^:]+):/;
|
|
9
|
+
const trackLine = /^\*\*Track:\*\*\s*(.*)$/;
|
|
10
|
+
const fileLine = /^- (?:Create|Modify|Test|Delete):\s+`([^`]+)`/;
|
|
11
|
+
const EXACT_MATCH_ERROR = 'membresía y filas de ## Tracks requieren coincidencia exacta';
|
|
12
|
+
function jsonStrings(label, raw) {
|
|
13
|
+
if (raw === undefined)
|
|
14
|
+
throw new Error(`${label} es obligatorio para paralelismo`);
|
|
15
|
+
let value;
|
|
16
|
+
try {
|
|
17
|
+
value = JSON.parse(raw);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
throw new Error(`${label} debe ser JSON string[]`);
|
|
21
|
+
}
|
|
22
|
+
if (!Array.isArray(value) || value.length === 0 || value.some((x) => typeof x !== 'string' || x.length === 0)) {
|
|
23
|
+
throw new Error(`${label} debe ser JSON string[] no vacío`);
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
function canonicalFile(raw) {
|
|
28
|
+
const withoutLines = raw.replace(/:\d+(?:-\d+)?$/, '');
|
|
29
|
+
const posix = withoutLines.replaceAll('\\', '/');
|
|
30
|
+
const normalized = path_1.default.posix.normalize(posix);
|
|
31
|
+
if (path_1.default.posix.isAbsolute(normalized) || normalized === '..' || normalized.startsWith('../')) {
|
|
32
|
+
throw new Error(`Files path fuera del repo: ${raw}`);
|
|
33
|
+
}
|
|
34
|
+
return normalized.replace(/^\.\//, '');
|
|
35
|
+
}
|
|
36
|
+
function parseTrackPlan(source, checkRef) {
|
|
37
|
+
const lines = source.replace(/\r\n/g, '\n').split('\n');
|
|
38
|
+
const hasMembership = lines.some((line) => trackLine.test(line));
|
|
39
|
+
const tracksHeading = lines.findIndex((line) => line.trim() === '## Tracks');
|
|
40
|
+
if (!hasMembership && tracksHeading < 0)
|
|
41
|
+
return { mode: 'serial', reason: 'no-tracks' };
|
|
42
|
+
if (!hasMembership || tracksHeading < 0)
|
|
43
|
+
throw new Error('Track membership y ## Tracks deben coexistir');
|
|
44
|
+
const argvRaw = lines.find((l) => l.startsWith('**Integration argv:**'))?.slice('**Integration argv:**'.length).trim();
|
|
45
|
+
const pathsRaw = lines.find((l) => l.startsWith('**Integration paths:**'))?.slice('**Integration paths:**'.length).trim();
|
|
46
|
+
const integration = { argv: jsonStrings('Integration argv', argvRaw), paths: jsonStrings('Integration paths', pathsRaw) };
|
|
47
|
+
const declared = new Map();
|
|
48
|
+
for (const line of lines.slice(tracksHeading + 1)) {
|
|
49
|
+
if (line.startsWith('## '))
|
|
50
|
+
break;
|
|
51
|
+
const cells = line.split('|').slice(1, -1).map((x) => x.trim());
|
|
52
|
+
if (cells.length !== 3 || cells[0] === 'Track' || /^[-:]+$/.test(cells[0]))
|
|
53
|
+
continue;
|
|
54
|
+
const [id, dependsRaw, resourcesRaw] = cells;
|
|
55
|
+
if (!id || id === '.' || id === '..' || id.startsWith('-') || id.includes('/') || id.includes('\\') || !checkRef(id)) {
|
|
56
|
+
throw new Error(`track id inválido: ${JSON.stringify(id)}`);
|
|
57
|
+
}
|
|
58
|
+
if (declared.has(id))
|
|
59
|
+
throw new Error(`track duplicado: ${id}`);
|
|
60
|
+
const resources = resourcesRaw === '[]' ? [] : resourcesRaw.length === 0 ? null : resourcesRaw.split(',').map((x) => x.trim());
|
|
61
|
+
const dependsOn = dependsRaw === 'none' ? [] : dependsRaw.split(',').map((x) => x.trim());
|
|
62
|
+
declared.set(id, { trackId: id, taskIds: [], ownership: [], dependsOn, sharedResources: resources ?? [] });
|
|
63
|
+
if (resources === null)
|
|
64
|
+
return { mode: 'serial', reason: `shared-resources-missing:${id}` };
|
|
65
|
+
}
|
|
66
|
+
let taskId = null;
|
|
67
|
+
let member = null;
|
|
68
|
+
let pendingFiles = [];
|
|
69
|
+
for (const line of lines) {
|
|
70
|
+
const heading = line.match(taskHeading);
|
|
71
|
+
if (heading !== null) {
|
|
72
|
+
if (taskId !== null && pendingFiles.length > 0 && member === null)
|
|
73
|
+
throw new Error(`task ${taskId} tiene Files pero no Track`);
|
|
74
|
+
taskId = heading[1].trim();
|
|
75
|
+
member = null;
|
|
76
|
+
pendingFiles = [];
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const membership = line.match(trackLine);
|
|
80
|
+
if (membership !== null) {
|
|
81
|
+
if (taskId === null || member !== null)
|
|
82
|
+
throw new Error('cada task admite exactamente un Track');
|
|
83
|
+
member = membership[1].trim();
|
|
84
|
+
const track = declared.get(member);
|
|
85
|
+
if (track === undefined)
|
|
86
|
+
throw new Error(EXACT_MATCH_ERROR);
|
|
87
|
+
track.taskIds.push(taskId);
|
|
88
|
+
track.ownership.push(...pendingFiles);
|
|
89
|
+
}
|
|
90
|
+
const file = line.match(fileLine);
|
|
91
|
+
if (file !== null) {
|
|
92
|
+
const canonical = canonicalFile(file[1]);
|
|
93
|
+
if (member === null)
|
|
94
|
+
pendingFiles.push(canonical);
|
|
95
|
+
else
|
|
96
|
+
declared.get(member).ownership.push(canonical);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// La última task del documento no tiene un `### Task` siguiente que dispare el chequeo del loop.
|
|
100
|
+
if (taskId !== null && pendingFiles.length > 0 && member === null)
|
|
101
|
+
throw new Error(`task ${taskId} tiene Files pero no Track`);
|
|
102
|
+
const members = new Set([...declared.values()].flatMap((t) => t.taskIds.length > 0 ? [t.trackId] : []));
|
|
103
|
+
if (members.size !== declared.size)
|
|
104
|
+
throw new Error(EXACT_MATCH_ERROR);
|
|
105
|
+
if ([...declared.values()].some((t) => t.dependsOn.length > 0))
|
|
106
|
+
return { mode: 'serial', reason: 'track-dependency' };
|
|
107
|
+
return { mode: 'parallel-candidate', tracks: Object.fromEntries(declared), integration };
|
|
108
|
+
}
|