@wundam/orchex 1.0.0-rc.21 → 1.0.0-rc.22
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/dist/orchestrator.js +31 -16
- package/dist/ownership.js +14 -8
- package/package.json +3 -3
package/dist/orchestrator.js
CHANGED
|
@@ -540,8 +540,9 @@ async function executeWaveInternal(projectDir, executor, options, logger) {
|
|
|
540
540
|
};
|
|
541
541
|
}
|
|
542
542
|
catch (error) {
|
|
543
|
-
const
|
|
544
|
-
const
|
|
543
|
+
const err = error;
|
|
544
|
+
const catchError = err.name && err.name !== 'Error' ? `${err.name}: ${err.message}` : err.message;
|
|
545
|
+
const catchAnalysis = analyzeError(err.stack ?? catchError, 'transport');
|
|
545
546
|
await logger.error('stream_failed', {
|
|
546
547
|
id: stream.id,
|
|
547
548
|
phase: 'execution',
|
|
@@ -568,21 +569,28 @@ async function executeWaveInternal(projectDir, executor, options, logger) {
|
|
|
568
569
|
}
|
|
569
570
|
});
|
|
570
571
|
// Wrap each promise with stream ID so rejections are identifiable
|
|
571
|
-
const taggedPromises = executePromises.map((p, i) => p.catch(err =>
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
572
|
+
const taggedPromises = executePromises.map((p, i) => p.catch(err => {
|
|
573
|
+
const errorMsg = err.message ?? String(err);
|
|
574
|
+
const errorStack = err.stack;
|
|
575
|
+
const fullError = errorStack ? `${errorMsg}\n${errorStack}` : errorMsg;
|
|
576
|
+
const analysis = analyzeError(fullError, 'transport');
|
|
577
|
+
return {
|
|
578
|
+
id: activeStreams[i]?.id ?? `wave-${targetWave.number}-idx-${i}`,
|
|
579
|
+
name: activeStreams[i]?.name ?? 'unknown',
|
|
580
|
+
status: 'failed',
|
|
581
|
+
error: errorMsg,
|
|
582
|
+
errorDetail: { category: analysis.category, retryable: analysis.retryable, selfHealable: analysis.selfHealable, suggestion: analysis.suggestion },
|
|
583
|
+
tokensUsed: { input: 0, output: 0 },
|
|
584
|
+
};
|
|
585
|
+
}));
|
|
579
586
|
// Promise.allSettled — one failure doesn't kill the wave
|
|
580
587
|
const settled = await Promise.allSettled(taggedPromises);
|
|
581
588
|
// Calculate parallel execution time (wall clock)
|
|
582
589
|
const waveEndTime = Date.now();
|
|
583
590
|
const waveCompletedAt = new Date().toISOString();
|
|
584
591
|
const parallelMs = waveEndTime - waveStartTime;
|
|
585
|
-
for (
|
|
592
|
+
for (let idx = 0; idx < settled.length; idx++) {
|
|
593
|
+
const result = settled[idx];
|
|
586
594
|
if (result.status === 'fulfilled') {
|
|
587
595
|
streamResults.push(result.value);
|
|
588
596
|
}
|
|
@@ -590,8 +598,8 @@ async function executeWaveInternal(projectDir, executor, options, logger) {
|
|
|
590
598
|
const rejectedError = result.reason?.message ?? 'Unknown error';
|
|
591
599
|
const rejectedAnalysis = analyzeError(rejectedError, 'transport');
|
|
592
600
|
streamResults.push({
|
|
593
|
-
id:
|
|
594
|
-
name: 'unknown',
|
|
601
|
+
id: activeStreams[idx]?.id ?? `wave-${targetWave.number}-idx-${idx}`,
|
|
602
|
+
name: activeStreams[idx]?.name ?? 'unknown',
|
|
595
603
|
status: 'failed',
|
|
596
604
|
error: rejectedError,
|
|
597
605
|
errorDetail: { category: rejectedAnalysis.category, retryable: rejectedAnalysis.retryable, selfHealable: rejectedAnalysis.selfHealable, suggestion: rejectedAnalysis.suggestion },
|
|
@@ -669,7 +677,7 @@ async function executeWaveInternal(projectDir, executor, options, logger) {
|
|
|
669
677
|
.filter(s => s && s.trim())
|
|
670
678
|
.join('\n')
|
|
671
679
|
|| 'unknown error';
|
|
672
|
-
|
|
680
|
+
let errorSummary = `Verify failed: ${failed.command}: ${verifyError.slice(0, 500)}`;
|
|
673
681
|
const verifyAnalysis = analyzeError(verifyError, 'verify');
|
|
674
682
|
verifyDetails.push(`${sr.id}: ${errorSummary}`);
|
|
675
683
|
await updateStreamStatus(projectDir, sr.id, 'failed', errorSummary);
|
|
@@ -681,6 +689,7 @@ async function executeWaveInternal(projectDir, executor, options, logger) {
|
|
|
681
689
|
isolated: otherBackups.length > 0,
|
|
682
690
|
});
|
|
683
691
|
// Rollback file changes for failed verify — prevents leaving orphan files on disk
|
|
692
|
+
let rollbackFailed = false;
|
|
684
693
|
const streamBackup = successBackups.find(b => b.streamId === sr.id);
|
|
685
694
|
if (streamBackup) {
|
|
686
695
|
try {
|
|
@@ -688,12 +697,18 @@ async function executeWaveInternal(projectDir, executor, options, logger) {
|
|
|
688
697
|
await logger.info('verify_rollback', { stream: sr.id, files: streamBackup.entries.length + streamBackup.createdFiles.length });
|
|
689
698
|
}
|
|
690
699
|
catch (rollbackErr) {
|
|
691
|
-
|
|
700
|
+
rollbackFailed = true;
|
|
701
|
+
const rollbackMsg = rollbackErr.message;
|
|
702
|
+
await logger.warn('verify_rollback_failed', { stream: sr.id, error: rollbackMsg });
|
|
703
|
+
errorSummary += ` [ROLLBACK FAILED: ${rollbackMsg} — files may remain on disk]`;
|
|
692
704
|
}
|
|
693
705
|
}
|
|
694
706
|
sr.status = 'failed';
|
|
695
707
|
sr.error = errorSummary;
|
|
696
|
-
|
|
708
|
+
const suggestion = rollbackFailed
|
|
709
|
+
? `${verifyAnalysis.suggestion}. WARNING: Rollback failed — manually check files owned by this stream.`
|
|
710
|
+
: verifyAnalysis.suggestion;
|
|
711
|
+
sr.errorDetail = { category: verifyAnalysis.category, retryable: verifyAnalysis.retryable, selfHealable: verifyAnalysis.selfHealable && !rollbackFailed, suggestion };
|
|
697
712
|
}
|
|
698
713
|
else {
|
|
699
714
|
verifyPassed++;
|
package/dist/ownership.js
CHANGED
|
@@ -107,8 +107,12 @@ export function checkOwnership(operations, owns, options = {}) {
|
|
|
107
107
|
}
|
|
108
108
|
return { violations, warnings, allowed };
|
|
109
109
|
}
|
|
110
|
+
function normalizePath(p) {
|
|
111
|
+
// Strip leading "./" — LLM artifacts may include it inconsistently
|
|
112
|
+
return p.startsWith('./') ? p.slice(2) : p;
|
|
113
|
+
}
|
|
110
114
|
function checkSingleOperation(op, owns, options) {
|
|
111
|
-
const filePath = op.path;
|
|
115
|
+
const filePath = normalizePath(op.path);
|
|
112
116
|
// SECURITY: Block path traversal (non-negotiable, checked first)
|
|
113
117
|
if (filePath.includes('..')) {
|
|
114
118
|
return {
|
|
@@ -141,26 +145,28 @@ function checkSingleOperation(op, owns, options) {
|
|
|
141
145
|
warning: `Common file '${filePath}' created (not in owns list)`,
|
|
142
146
|
};
|
|
143
147
|
}
|
|
144
|
-
// Not allowed
|
|
148
|
+
// Not allowed — include owns list for debuggability
|
|
145
149
|
return {
|
|
146
|
-
violation: `${op.type} on '${filePath}' is outside owned files`,
|
|
150
|
+
violation: `${op.type} on '${filePath}' is outside owned files [${owns.join(', ')}]`,
|
|
147
151
|
};
|
|
148
152
|
}
|
|
149
153
|
/**
|
|
150
154
|
* Check if a path is explicitly covered by an ownership pattern.
|
|
151
155
|
*/
|
|
152
156
|
function isExplicitlyOwned(filePath, owns) {
|
|
157
|
+
const normalizedFile = normalizePath(filePath);
|
|
153
158
|
return owns.some((pattern) => {
|
|
159
|
+
const normalizedPattern = normalizePath(pattern);
|
|
154
160
|
// Directory pattern: "src/" matches "src/foo.ts"
|
|
155
|
-
if (
|
|
156
|
-
return
|
|
161
|
+
if (normalizedPattern.endsWith('/')) {
|
|
162
|
+
return normalizedFile.startsWith(normalizedPattern);
|
|
157
163
|
}
|
|
158
164
|
// Glob pattern: "src/*.ts" matches "src/foo.ts"
|
|
159
|
-
if (
|
|
160
|
-
return matchGlobPattern(
|
|
165
|
+
if (normalizedPattern.includes('*')) {
|
|
166
|
+
return matchGlobPattern(normalizedPattern, normalizedFile);
|
|
161
167
|
}
|
|
162
168
|
// Exact match
|
|
163
|
-
return
|
|
169
|
+
return normalizedFile === normalizedPattern;
|
|
164
170
|
});
|
|
165
171
|
}
|
|
166
172
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wundam/orchex",
|
|
3
3
|
"mcpName": "io.github.wundam/orchex",
|
|
4
|
-
"version": "1.0.0-rc.
|
|
4
|
+
"version": "1.0.0-rc.22",
|
|
5
5
|
"description": "Autopilot AI orchestration — auto-plan, parallelize, and execute with ownership enforcement",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
@@ -20,11 +20,11 @@
|
|
|
20
20
|
"author": "Wundam LLC",
|
|
21
21
|
"repository": {
|
|
22
22
|
"type": "git",
|
|
23
|
-
"url": "https://orchex
|
|
23
|
+
"url": "https://github.com/wundam/orchex-community"
|
|
24
24
|
},
|
|
25
25
|
"homepage": "https://orchex.dev",
|
|
26
26
|
"bugs": {
|
|
27
|
-
"url": "https://
|
|
27
|
+
"url": "https://github.com/wundam/orchex-community/discussions"
|
|
28
28
|
},
|
|
29
29
|
"keywords": [
|
|
30
30
|
"mcp",
|