@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.
@@ -540,8 +540,9 @@ async function executeWaveInternal(projectDir, executor, options, logger) {
540
540
  };
541
541
  }
542
542
  catch (error) {
543
- const catchError = error.message;
544
- const catchAnalysis = analyzeError(catchError, 'transport');
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
- id: activeStreams[i]?.id ?? `wave-${targetWave.number}-idx-${i}`,
573
- name: activeStreams[i]?.name ?? 'unknown',
574
- status: 'failed',
575
- error: err.message ?? String(err),
576
- errorDetail: { category: 'unknown', retryable: true, selfHealable: false, suggestion: 'Check orchestrator logs' },
577
- tokensUsed: { input: 0, output: 0 },
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 (const result of settled) {
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: 'unknown',
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
- const errorSummary = `Verify failed: ${failed.command}: ${verifyError.slice(0, 500)}`;
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
- await logger.warn('verify_rollback_failed', { stream: sr.id, error: rollbackErr.message });
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
- sr.errorDetail = { category: verifyAnalysis.category, retryable: verifyAnalysis.retryable, selfHealable: verifyAnalysis.selfHealable, suggestion: verifyAnalysis.suggestion };
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 (pattern.endsWith('/')) {
156
- return filePath.startsWith(pattern);
161
+ if (normalizedPattern.endsWith('/')) {
162
+ return normalizedFile.startsWith(normalizedPattern);
157
163
  }
158
164
  // Glob pattern: "src/*.ts" matches "src/foo.ts"
159
- if (pattern.includes('*')) {
160
- return matchGlobPattern(pattern, filePath);
165
+ if (normalizedPattern.includes('*')) {
166
+ return matchGlobPattern(normalizedPattern, normalizedFile);
161
167
  }
162
168
  // Exact match
163
- return filePath === pattern;
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.21",
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.dev"
23
+ "url": "https://github.com/wundam/orchex-community"
24
24
  },
25
25
  "homepage": "https://orchex.dev",
26
26
  "bugs": {
27
- "url": "https://orchex.dev/support"
27
+ "url": "https://github.com/wundam/orchex-community/discussions"
28
28
  },
29
29
  "keywords": [
30
30
  "mcp",