@mrpatronz/nexusflow 2.7.0 → 2.8.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.
Files changed (135) hide show
  1. package/README.md +1 -2
  2. package/dist/agent/ClaudeSdkAdapter.d.ts.map +1 -1
  3. package/dist/agent/ClaudeSdkAdapter.js +31 -8
  4. package/dist/agent/ClaudeSdkAdapter.js.map +1 -1
  5. package/dist/agent/ClaudeSdkAdapter.test.js +18 -6
  6. package/dist/agent/ClaudeSdkAdapter.test.js.map +1 -1
  7. package/dist/agent/CodexCliAdapter.d.ts +1 -1
  8. package/dist/agent/CodexCliAdapter.d.ts.map +1 -1
  9. package/dist/agent/CodexCliAdapter.js +5 -4
  10. package/dist/agent/CodexCliAdapter.js.map +1 -1
  11. package/dist/agent/CodexCliAdapter.test.js +16 -1
  12. package/dist/agent/CodexCliAdapter.test.js.map +1 -1
  13. package/dist/agent/CodexSdkAdapter.d.ts.map +1 -1
  14. package/dist/agent/CodexSdkAdapter.js +6 -0
  15. package/dist/agent/CodexSdkAdapter.js.map +1 -1
  16. package/dist/agent/ProviderRegistry.d.ts +5 -0
  17. package/dist/agent/ProviderRegistry.d.ts.map +1 -1
  18. package/dist/agent/ProviderRegistry.js +15 -0
  19. package/dist/agent/ProviderRegistry.js.map +1 -1
  20. package/dist/agent/ProviderRegistry.test.js +19 -0
  21. package/dist/agent/ProviderRegistry.test.js.map +1 -1
  22. package/dist/agent/adapters.d.ts.map +1 -1
  23. package/dist/agent/adapters.js +5 -0
  24. package/dist/agent/adapters.js.map +1 -1
  25. package/dist/agent/mcp-config.d.ts +16 -0
  26. package/dist/agent/mcp-config.d.ts.map +1 -0
  27. package/dist/agent/mcp-config.js +28 -0
  28. package/dist/agent/mcp-config.js.map +1 -0
  29. package/dist/agent/mcp-config.test.d.ts +2 -0
  30. package/dist/agent/mcp-config.test.d.ts.map +1 -0
  31. package/dist/agent/mcp-config.test.js +22 -0
  32. package/dist/agent/mcp-config.test.js.map +1 -0
  33. package/dist/agent/models.d.ts +10 -0
  34. package/dist/agent/models.d.ts.map +1 -0
  35. package/dist/agent/models.js +35 -0
  36. package/dist/agent/models.js.map +1 -0
  37. package/dist/agent/models.test.d.ts +2 -0
  38. package/dist/agent/models.test.d.ts.map +1 -0
  39. package/dist/agent/models.test.js +40 -0
  40. package/dist/agent/models.test.js.map +1 -0
  41. package/dist/agent/orchestrator.d.ts +65 -0
  42. package/dist/agent/orchestrator.d.ts.map +1 -0
  43. package/dist/agent/orchestrator.js +252 -0
  44. package/dist/agent/orchestrator.js.map +1 -0
  45. package/dist/agent/orchestrator.test.d.ts +2 -0
  46. package/dist/agent/orchestrator.test.d.ts.map +1 -0
  47. package/dist/agent/orchestrator.test.js +284 -0
  48. package/dist/agent/orchestrator.test.js.map +1 -0
  49. package/dist/agent/session.d.ts +1 -0
  50. package/dist/agent/session.d.ts.map +1 -1
  51. package/dist/agent/session.js +3 -2
  52. package/dist/agent/session.js.map +1 -1
  53. package/dist/agent/session.test.js +9 -0
  54. package/dist/agent/session.test.js.map +1 -1
  55. package/dist/agent/smoke-chat.test.d.ts +2 -0
  56. package/dist/agent/smoke-chat.test.d.ts.map +1 -0
  57. package/dist/agent/smoke-chat.test.js +166 -0
  58. package/dist/agent/smoke-chat.test.js.map +1 -0
  59. package/dist/commands/mcp.d.ts +7 -1
  60. package/dist/commands/mcp.d.ts.map +1 -1
  61. package/dist/commands/mcp.js +7 -2
  62. package/dist/commands/mcp.js.map +1 -1
  63. package/dist/core/locks.d.ts +4 -2
  64. package/dist/core/locks.d.ts.map +1 -1
  65. package/dist/core/locks.js +55 -7
  66. package/dist/core/locks.js.map +1 -1
  67. package/dist/core/locks.test.js +48 -0
  68. package/dist/core/locks.test.js.map +1 -1
  69. package/dist/gui/assets/AgentsPage-Bfyu13gc.js +3 -0
  70. package/dist/gui/assets/{CSPContext-BG7no5S_.js → CSPContext-DBUu9tE0.js} +1 -1
  71. package/dist/gui/assets/{DashboardPage-ByGTwAQa.js → DashboardPage-O5wj99kn.js} +1 -1
  72. package/dist/gui/assets/{GettingStartedPage-Xjx3M3RH.js → GettingStartedPage-DDWBueC7.js} +1 -1
  73. package/dist/gui/assets/ProjectsPage-NHkXrDM2.js +1 -0
  74. package/dist/gui/assets/ScaffoldRepoInline-Cmqg_s94.js +1 -0
  75. package/dist/gui/assets/{SettingsPage-B9iN6hsv.js → SettingsPage-Cc77HBp7.js} +1 -1
  76. package/dist/gui/assets/SkillsPage-D4CVxwrI.js +6 -0
  77. package/dist/gui/assets/StartWorkPage-RPZTN3Ui.js +1 -0
  78. package/dist/gui/assets/{StrategiesPage-DDu3jwJx.js → StrategiesPage-Dn66xfZN.js} +1 -1
  79. package/dist/gui/assets/WorkspacesPage-BgdoM5Hs.js +6 -0
  80. package/dist/gui/assets/{alert-dialog-CdqVrvZl.js → alert-dialog-BYZ7cgWA.js} +1 -1
  81. package/dist/gui/assets/index-BmH7a-Qy.css +2 -0
  82. package/dist/gui/assets/index-CoNaATPd.js +52 -0
  83. package/dist/gui/assets/isElementDisabled-uy_EOitH.js +1 -0
  84. package/dist/gui/assets/textarea-ByfoCb54.js +1 -0
  85. package/dist/gui/assets/{useAriaLabelledBy-DfRdwH8a.js → useAriaLabelledBy-ClesgjMe.js} +1 -1
  86. package/dist/gui/assets/useValueChanged-DeYvGnKs.js +1 -0
  87. package/dist/gui/assets/{vsc-D6UPv2ii.js → vsc-BTST_4ex.js} +1 -1
  88. package/dist/gui/index.html +5 -7
  89. package/dist/harness/claude.d.ts +3 -1
  90. package/dist/harness/claude.d.ts.map +1 -1
  91. package/dist/harness/claude.js +4 -2
  92. package/dist/harness/claude.js.map +1 -1
  93. package/dist/harness/codex.d.ts +3 -1
  94. package/dist/harness/codex.d.ts.map +1 -1
  95. package/dist/harness/codex.js +64 -12
  96. package/dist/harness/codex.js.map +1 -1
  97. package/dist/harness/contract.test.d.ts +2 -0
  98. package/dist/harness/contract.test.d.ts.map +1 -0
  99. package/dist/harness/contract.test.js +426 -0
  100. package/dist/harness/contract.test.js.map +1 -0
  101. package/dist/harness/harness.test.js +16 -1
  102. package/dist/harness/harness.test.js.map +1 -1
  103. package/dist/harness/types.d.ts +7 -1
  104. package/dist/harness/types.d.ts.map +1 -1
  105. package/dist/index.js +4 -1
  106. package/dist/index.js.map +1 -1
  107. package/dist/mcp/server.d.ts +8 -1
  108. package/dist/mcp/server.d.ts.map +1 -1
  109. package/dist/mcp/server.js +11 -6
  110. package/dist/mcp/server.js.map +1 -1
  111. package/dist/mcp/tools.d.ts +6 -2
  112. package/dist/mcp/tools.d.ts.map +1 -1
  113. package/dist/mcp/tools.js +213 -6
  114. package/dist/mcp/tools.js.map +1 -1
  115. package/dist/mcp/tools.test.js +115 -6
  116. package/dist/mcp/tools.test.js.map +1 -1
  117. package/dist/server.d.ts.map +1 -1
  118. package/dist/server.js +15 -3
  119. package/dist/server.js.map +1 -1
  120. package/dist/server.test.js +12 -1
  121. package/dist/server.test.js.map +1 -1
  122. package/package.json +2 -1
  123. package/dist/gui/assets/AgentsPage-YyYRodOR.js +0 -3
  124. package/dist/gui/assets/ProjectsPage-BMdhqAGs.js +0 -1
  125. package/dist/gui/assets/ScaffoldRepoInline-gXyYSIz5.js +0 -1
  126. package/dist/gui/assets/SkillsPage-CxcsbIsQ.js +0 -6
  127. package/dist/gui/assets/StartWorkPage-DPVHcdy4.js +0 -1
  128. package/dist/gui/assets/WorkspacesPage-BWRmP0mY.js +0 -2
  129. package/dist/gui/assets/index-CZoZQkeC.css +0 -2
  130. package/dist/gui/assets/index-wy68EqfB.js +0 -50
  131. package/dist/gui/assets/isElementDisabled-D_fu2zfc.js +0 -1
  132. package/dist/gui/assets/store-D5DUHbxn.js +0 -3
  133. package/dist/gui/assets/styles-CVwNuFCx.js +0 -1
  134. package/dist/gui/assets/textarea-BNRuIXNs.js +0 -1
  135. package/dist/gui/assets/useValueChanged-CStSGSsD.js +0 -1
@@ -0,0 +1,252 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import * as path from 'node:path';
3
+ import { ClaudeSdkAdapter } from './ClaudeSdkAdapter.js';
4
+ import { CodexSdkAdapter } from './CodexSdkAdapter.js';
5
+ import { acquireLock } from '../core/locks.js';
6
+ export const MAX_OUTPUT_CAPTURE_CHARS = 16_000;
7
+ export function appendCappedOutput(existing, chunk, maxChars = MAX_OUTPUT_CAPTURE_CHARS) {
8
+ const combined = (existing ?? '') + chunk;
9
+ if (combined.length <= maxChars) {
10
+ return combined;
11
+ }
12
+ const truncated = combined.slice(combined.length - maxChars);
13
+ return `[... prior output truncated ...]\n${truncated}`;
14
+ }
15
+ /**
16
+ * Multi-Agent Orchestrator (Phase 3).
17
+ *
18
+ * Coordinates concurrent agents across Claude and Codex harnesses:
19
+ * - Claude: Programmatic subagent definitions or isolated SDK sessions with independent UUIDs.
20
+ * - Codex: Orchestrator-level thread fan-out with independent thread sessions.
21
+ * - Concurrency: Enforces workspace mutation locks (for shared workspaces) and worktree branch isolation (when worktreePath is provided).
22
+ * - Pipelines: Supports sequential pipeline execution with fail-fast skip on upstream failure.
23
+ */
24
+ export class MultiAgentOrchestrator extends EventEmitter {
25
+ workspacePath;
26
+ options;
27
+ runningHandles = new Map();
28
+ isInterrupted = false;
29
+ constructor(workspacePath, options) {
30
+ super();
31
+ this.workspacePath = workspacePath;
32
+ this.options = options;
33
+ }
34
+ async runTeam(specs, runOptions) {
35
+ if (!specs || specs.length < 2) {
36
+ throw new Error('Teamwork orchestration requires at least 2 agents (e.g. Lead Planner + Developer).');
37
+ }
38
+ // Verify unique agent IDs
39
+ const idSet = new Set();
40
+ for (const spec of specs) {
41
+ if (idSet.has(spec.id)) {
42
+ throw new Error(`Duplicate agent ID detected in teamwork spec: "${spec.id}"`);
43
+ }
44
+ idSet.add(spec.id);
45
+ }
46
+ const statuses = new Map();
47
+ for (const spec of specs) {
48
+ statuses.set(spec.id, {
49
+ id: spec.id,
50
+ name: spec.name,
51
+ role: spec.role,
52
+ vendor: spec.vendor ?? 'claude-code',
53
+ status: 'pending',
54
+ });
55
+ }
56
+ const mode = runOptions?.mode ?? 'parallel';
57
+ if (this.isInterrupted) {
58
+ for (const status of statuses.values()) {
59
+ status.status = 'cancelled';
60
+ }
61
+ return this.buildResult(statuses);
62
+ }
63
+ // Enforce workspace-level mutation lock across concurrent agents in the same workspace root
64
+ let releaseMutationLock = null;
65
+ const isSharedWorktree = specs.every(s => !s.worktreePath || s.worktreePath === this.workspacePath);
66
+ if (isSharedWorktree) {
67
+ const lockPath = path.join(this.workspacePath, '.nexusflow-mutation.lock');
68
+ try {
69
+ releaseMutationLock = await acquireLock(lockPath, {
70
+ staleMs: 60_000,
71
+ timeoutMs: 10_000,
72
+ timeoutMessage: 'Could not acquire multi-agent workspace mutation lock.',
73
+ });
74
+ }
75
+ catch (err) {
76
+ if (this.isInterrupted) {
77
+ for (const status of statuses.values()) {
78
+ status.status = 'cancelled';
79
+ }
80
+ return this.buildResult(statuses);
81
+ }
82
+ throw err;
83
+ }
84
+ }
85
+ try {
86
+ if (mode === 'pipeline') {
87
+ // Sequential pipeline execution (e.g. Planner -> Implementer -> Reviewer)
88
+ let accumulatedContext = '';
89
+ for (const spec of specs) {
90
+ if (this.isInterrupted) {
91
+ statuses.get(spec.id).status = 'cancelled';
92
+ continue;
93
+ }
94
+ // If any previous agent failed or was cancelled, skip downstream phases
95
+ const previousFailure = Array.from(statuses.values()).find(s => s.status === 'failed' || s.status === 'cancelled');
96
+ if (previousFailure) {
97
+ const status = statuses.get(spec.id);
98
+ status.status = 'cancelled';
99
+ status.error = `Skipped due to upstream phase failure in ${previousFailure.id} (${previousFailure.role})`;
100
+ continue;
101
+ }
102
+ const shouldIncludeContext = spec.includePriorContext !== false;
103
+ const effectivePrompt = (shouldIncludeContext && accumulatedContext)
104
+ ? `${spec.prompt}\n\n## Context from Prior Phases:\n${accumulatedContext.trim()}`
105
+ : spec.prompt;
106
+ await this.executeAgent({ ...spec, prompt: effectivePrompt }, statuses);
107
+ const completedStatus = statuses.get(spec.id);
108
+ if (completedStatus && completedStatus.status === 'completed' && completedStatus.lastOutput) {
109
+ accumulatedContext += `### Phase Output: ${spec.name} (${spec.role})\n${completedStatus.lastOutput.trim()}\n\n`;
110
+ }
111
+ }
112
+ }
113
+ else {
114
+ // Parallel fan-out with independent session contexts
115
+ const promises = specs.map((spec) => this.executeAgent(spec, statuses));
116
+ await Promise.allSettled(promises);
117
+ }
118
+ }
119
+ finally {
120
+ if (releaseMutationLock) {
121
+ await releaseMutationLock();
122
+ }
123
+ }
124
+ return this.buildResult(statuses);
125
+ }
126
+ buildResult(statuses) {
127
+ const results = Array.from(statuses.values());
128
+ const totalUsage = this.aggregateUsage(results);
129
+ const completedCount = results.filter(r => r.status === 'completed').length;
130
+ const success = results.length > 0 && completedCount === results.length;
131
+ const partialSuccess = completedCount > 0 && !success;
132
+ const failedAgents = results.filter(r => r.status === 'failed' || r.status === 'cancelled');
133
+ const failureReason = failedAgents.length > 0
134
+ ? failedAgents.map(a => `${a.id} (${a.role}): ${a.error || a.status}`).join('; ')
135
+ : undefined;
136
+ return {
137
+ workspacePath: this.workspacePath,
138
+ agents: results,
139
+ totalUsage,
140
+ success,
141
+ partialSuccess,
142
+ failureReason,
143
+ };
144
+ }
145
+ async interruptAll() {
146
+ this.isInterrupted = true;
147
+ const interrupts = [];
148
+ for (const handle of this.runningHandles.values()) {
149
+ if (typeof handle.interrupt === 'function') {
150
+ interrupts.push(handle.interrupt());
151
+ }
152
+ else if (typeof handle.stop === 'function') {
153
+ handle.stop();
154
+ }
155
+ }
156
+ await Promise.allSettled(interrupts);
157
+ }
158
+ async executeAgent(spec, statuses) {
159
+ const status = statuses.get(spec.id);
160
+ status.status = 'running';
161
+ const targetCwd = spec.worktreePath ?? this.workspacePath;
162
+ const vendor = spec.vendor ?? 'claude-code';
163
+ if (this.isInterrupted) {
164
+ status.status = 'cancelled';
165
+ return;
166
+ }
167
+ const adapter = vendor === 'codex'
168
+ ? (this.options?.codexAdapterFactory ? this.options.codexAdapterFactory() : new CodexSdkAdapter())
169
+ : (this.options?.claudeAdapterFactory ? this.options.claudeAdapterFactory() : new ClaudeSdkAdapter());
170
+ this.runningHandles.set(spec.id, adapter);
171
+ try {
172
+ await new Promise((resolve, reject) => {
173
+ adapter.on('session', (sessionId) => {
174
+ status.sessionId = sessionId;
175
+ this.emit('agent_session', { agentId: spec.id, sessionId });
176
+ });
177
+ adapter.on('data', (chunk) => {
178
+ status.lastOutput = appendCappedOutput(status.lastOutput, chunk);
179
+ this.emit('agent_data', { agentId: spec.id, data: chunk });
180
+ });
181
+ adapter.on('usage', (usage) => {
182
+ status.usage = usage;
183
+ this.emit('agent_usage', { agentId: spec.id, usage });
184
+ });
185
+ adapter.on('error', (err) => {
186
+ status.status = 'failed';
187
+ status.error = err.message;
188
+ });
189
+ adapter.on('idle', () => {
190
+ if (status.status === 'running') {
191
+ status.status = 'completed';
192
+ }
193
+ resolve();
194
+ });
195
+ adapter.on('close', (code) => {
196
+ if (status.status === 'running') {
197
+ if (code !== undefined && code !== 0) {
198
+ status.status = 'failed';
199
+ }
200
+ else {
201
+ status.status = this.isInterrupted ? 'cancelled' : 'completed';
202
+ }
203
+ }
204
+ resolve();
205
+ });
206
+ void (async () => {
207
+ try {
208
+ await adapter.start(targetCwd);
209
+ await adapter.send(spec.prompt, spec.executionProfile ?? 'review');
210
+ }
211
+ catch (err) {
212
+ status.status = 'failed';
213
+ status.error = err.message;
214
+ reject(err);
215
+ }
216
+ })();
217
+ });
218
+ }
219
+ catch (err) {
220
+ status.status = 'failed';
221
+ status.error = err.message;
222
+ }
223
+ finally {
224
+ this.runningHandles.delete(spec.id);
225
+ }
226
+ }
227
+ aggregateUsage(statuses) {
228
+ let inputTokens = 0;
229
+ let outputTokens = 0;
230
+ let cachedInputTokens = undefined;
231
+ let costUsdEstimate = undefined;
232
+ for (const status of statuses) {
233
+ if (status.usage) {
234
+ inputTokens += status.usage.inputTokens ?? 0;
235
+ outputTokens += status.usage.outputTokens ?? 0;
236
+ if (status.usage.cachedInputTokens !== undefined) {
237
+ cachedInputTokens = (cachedInputTokens ?? 0) + status.usage.cachedInputTokens;
238
+ }
239
+ if (status.usage.costUsdEstimate !== undefined) {
240
+ costUsdEstimate = (costUsdEstimate ?? 0) + status.usage.costUsdEstimate;
241
+ }
242
+ }
243
+ }
244
+ return {
245
+ inputTokens,
246
+ outputTokens,
247
+ cachedInputTokens,
248
+ costUsdEstimate,
249
+ };
250
+ }
251
+ }
252
+ //# sourceMappingURL=orchestrator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../src/agent/orchestrator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=orchestrator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator.test.d.ts","sourceRoot":"","sources":["../../src/agent/orchestrator.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,284 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ import * as fs from 'node:fs/promises';
5
+ import { EventEmitter } from 'node:events';
6
+ import { MultiAgentOrchestrator } from './orchestrator.js';
7
+ class MockHarness extends EventEmitter {
8
+ startedWithCwd = '';
9
+ sentPrompts = [];
10
+ stopped = false;
11
+ assignedSessionId;
12
+ assignedUsage;
13
+ autoComplete;
14
+ failWithError;
15
+ constructor(sessionId, usage, autoComplete = true, failWithError) {
16
+ super();
17
+ this.assignedSessionId = sessionId;
18
+ this.assignedUsage = usage;
19
+ this.autoComplete = autoComplete;
20
+ this.failWithError = failWithError;
21
+ }
22
+ async start(cwd) {
23
+ this.startedWithCwd = cwd;
24
+ }
25
+ async send(prompt) {
26
+ this.sentPrompts.push(prompt);
27
+ this.emit('session', this.assignedSessionId);
28
+ if (this.failWithError) {
29
+ setTimeout(() => {
30
+ this.emit('usage', this.assignedUsage);
31
+ this.emit('error', new Error(this.failWithError));
32
+ this.emit('close', 1);
33
+ }, 10);
34
+ }
35
+ else if (this.autoComplete) {
36
+ setTimeout(() => {
37
+ this.emit('data', `Done: ${prompt}`);
38
+ this.emit('usage', this.assignedUsage);
39
+ this.emit('idle');
40
+ }, 10);
41
+ }
42
+ }
43
+ stop() {
44
+ this.stopped = true;
45
+ this.emit('close', 0);
46
+ }
47
+ }
48
+ describe('MultiAgentOrchestrator (Phase 3)', () => {
49
+ it('throws when less than 2 agents are specified for teamwork', async () => {
50
+ const orchestrator = new MultiAgentOrchestrator('/dev/test-ws');
51
+ await expect(orchestrator.runTeam([
52
+ { id: 'lead', name: 'Lead', role: 'lead', prompt: 'Plan work' },
53
+ ])).rejects.toThrow(/requires at least 2 agents/);
54
+ });
55
+ it('throws when duplicate agent IDs are present in spec', async () => {
56
+ const orchestrator = new MultiAgentOrchestrator('/dev/test-ws');
57
+ await expect(orchestrator.runTeam([
58
+ { id: 'agent-1', name: 'Lead', role: 'lead', prompt: 'Plan work' },
59
+ { id: 'agent-1', name: 'Dev', role: 'developer', prompt: 'Write code' },
60
+ ])).rejects.toThrow(/Duplicate agent ID detected/);
61
+ });
62
+ it('executes >= 2 agents concurrently with independent session contexts and usage aggregation', async () => {
63
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'nexusflow-orch-test-'));
64
+ try {
65
+ const claudeMock = new MockHarness('claude-session-uuid-1', {
66
+ inputTokens: 150,
67
+ outputTokens: 50,
68
+ cachedInputTokens: 30,
69
+ });
70
+ const codexMock = new MockHarness('codex-session-uuid-2', {
71
+ inputTokens: 200,
72
+ outputTokens: 75,
73
+ cachedInputTokens: 60,
74
+ });
75
+ const orchestrator = new MultiAgentOrchestrator(tmpDir, {
76
+ claudeAdapterFactory: () => claudeMock,
77
+ codexAdapterFactory: () => codexMock,
78
+ });
79
+ const specs = [
80
+ {
81
+ id: 'planner',
82
+ name: 'Lead Planner',
83
+ role: 'lead',
84
+ vendor: 'claude-code',
85
+ prompt: 'Analyze repos and create implementation plan',
86
+ },
87
+ {
88
+ id: 'coder',
89
+ name: 'Code Implementer',
90
+ role: 'developer',
91
+ vendor: 'codex',
92
+ prompt: 'Implement test and features in repo',
93
+ },
94
+ ];
95
+ const result = await orchestrator.runTeam(specs);
96
+ expect(result.workspacePath).toBe(tmpDir);
97
+ expect(result.agents).toHaveLength(2);
98
+ expect(result.success).toBe(true);
99
+ expect(result.partialSuccess).toBe(false);
100
+ // Verify independent session IDs
101
+ expect(result.agents[0].id).toBe('planner');
102
+ expect(result.agents[0].sessionId).toBe('claude-session-uuid-1');
103
+ expect(result.agents[0].status).toBe('completed');
104
+ expect(result.agents[1].id).toBe('coder');
105
+ expect(result.agents[1].sessionId).toBe('codex-session-uuid-2');
106
+ expect(result.agents[1].status).toBe('completed');
107
+ // Verify total usage aggregation
108
+ expect(result.totalUsage).toEqual({
109
+ inputTokens: 350,
110
+ outputTokens: 125,
111
+ cachedInputTokens: 90,
112
+ costUsdEstimate: undefined,
113
+ });
114
+ // Verify lock was released after run
115
+ const lockPath = path.join(tmpDir, '.nexusflow-mutation.lock');
116
+ await expect(fs.stat(lockPath)).rejects.toThrow();
117
+ }
118
+ finally {
119
+ await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => { });
120
+ }
121
+ });
122
+ it('interrupts all running agents on interruptAll()', async () => {
123
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'nexusflow-orch-abort-'));
124
+ try {
125
+ const mockHarness1 = new MockHarness('agent-1-uuid', { inputTokens: 10, outputTokens: 10 }, false);
126
+ const mockHarness2 = new MockHarness('agent-2-uuid', { inputTokens: 10, outputTokens: 10 }, false);
127
+ const orchestrator = new MultiAgentOrchestrator(tmpDir, {
128
+ claudeAdapterFactory: () => mockHarness1,
129
+ codexAdapterFactory: () => mockHarness2,
130
+ });
131
+ const specs = [
132
+ {
133
+ id: 'agent-1',
134
+ name: 'Agent 1',
135
+ role: 'lead',
136
+ vendor: 'claude-code',
137
+ prompt: 'Task 1',
138
+ },
139
+ {
140
+ id: 'agent-2',
141
+ name: 'Agent 2',
142
+ role: 'developer',
143
+ vendor: 'codex',
144
+ prompt: 'Task 2',
145
+ },
146
+ ];
147
+ const runPromise = orchestrator.runTeam(specs);
148
+ await vi.waitFor(() => {
149
+ if (mockHarness1.sentPrompts.length === 0 || mockHarness2.sentPrompts.length === 0) {
150
+ throw new Error('Waiting for agents to start');
151
+ }
152
+ });
153
+ await orchestrator.interruptAll();
154
+ const result = await runPromise;
155
+ expect(result.agents).toHaveLength(2);
156
+ expect(mockHarness1.stopped).toBe(true);
157
+ expect(mockHarness2.stopped).toBe(true);
158
+ expect(result.agents[0].status).toBe('cancelled');
159
+ expect(result.agents[1].status).toBe('cancelled');
160
+ expect(result.success).toBe(false);
161
+ // Verify lock is released and releasable
162
+ const lockPath = path.join(tmpDir, '.nexusflow-mutation.lock');
163
+ await expect(fs.stat(lockPath)).rejects.toThrow();
164
+ }
165
+ finally {
166
+ await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => { });
167
+ }
168
+ });
169
+ it('runs sequential pipeline mode and skips downstream phases if upstream fails', async () => {
170
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'nexusflow-orch-pipe-'));
171
+ try {
172
+ const plannerMock = new MockHarness('planner-uuid', { inputTokens: 50, outputTokens: 20 }, true, 'Compilation syntax error in plan');
173
+ const coderMock = new MockHarness('coder-uuid', { inputTokens: 50, outputTokens: 20 });
174
+ const reviewerMock = new MockHarness('reviewer-uuid', { inputTokens: 50, outputTokens: 20 });
175
+ let callCount = 0;
176
+ const orchestrator = new MultiAgentOrchestrator(tmpDir, {
177
+ claudeAdapterFactory: () => {
178
+ callCount++;
179
+ if (callCount === 1)
180
+ return plannerMock;
181
+ return reviewerMock;
182
+ },
183
+ codexAdapterFactory: () => coderMock,
184
+ });
185
+ const specs = [
186
+ { id: 'plan', name: 'Planner', role: 'lead', vendor: 'claude-code', prompt: 'Plan task' },
187
+ { id: 'impl', name: 'Implementer', role: 'developer', vendor: 'codex', prompt: 'Implement task' },
188
+ { id: 'rev', name: 'Reviewer', role: 'reviewer', vendor: 'claude-code', prompt: 'Review task' },
189
+ ];
190
+ const result = await orchestrator.runTeam(specs, { mode: 'pipeline' });
191
+ expect(result.success).toBe(false);
192
+ expect(result.partialSuccess).toBe(false);
193
+ expect(result.agents[0].status).toBe('failed');
194
+ expect(result.agents[0].error).toContain('Compilation syntax error');
195
+ // Downstream phases skipped safely
196
+ expect(result.agents[1].status).toBe('cancelled');
197
+ expect(result.agents[1].error).toContain('Skipped due to upstream phase failure');
198
+ expect(result.agents[2].status).toBe('cancelled');
199
+ expect(result.agents[2].error).toContain('Skipped due to upstream phase failure');
200
+ expect(coderMock.sentPrompts).toHaveLength(0);
201
+ expect(result.failureReason).toContain('Compilation syntax error');
202
+ }
203
+ finally {
204
+ await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => { });
205
+ }
206
+ });
207
+ it('reports partialSuccess when some agents succeed and others fail', async () => {
208
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'nexusflow-orch-partial-'));
209
+ try {
210
+ const agent1Mock = new MockHarness('agent1-uuid', { inputTokens: 100, outputTokens: 20 }, true);
211
+ const agent2Mock = new MockHarness('agent2-uuid', { inputTokens: 50, outputTokens: 10 }, true, 'Agent 2 network timeout');
212
+ const orchestrator = new MultiAgentOrchestrator(tmpDir, {
213
+ claudeAdapterFactory: () => agent1Mock,
214
+ codexAdapterFactory: () => agent2Mock,
215
+ });
216
+ const specs = [
217
+ { id: 'agent-1', name: 'Agent 1', role: 'lead', vendor: 'claude-code', prompt: 'Task 1' },
218
+ { id: 'agent-2', name: 'Agent 2', role: 'developer', vendor: 'codex', prompt: 'Task 2' },
219
+ ];
220
+ const result = await orchestrator.runTeam(specs, { mode: 'parallel' });
221
+ expect(result.success).toBe(false);
222
+ expect(result.partialSuccess).toBe(true);
223
+ expect(result.agents[0].status).toBe('completed');
224
+ expect(result.agents[1].status).toBe('failed');
225
+ expect(result.failureReason).toContain('network timeout');
226
+ expect(result.totalUsage.inputTokens).toBe(150);
227
+ }
228
+ finally {
229
+ await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => { });
230
+ }
231
+ });
232
+ it('threads prior phase output into subsequent agent prompts in pipeline mode', async () => {
233
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'nexusflow-orch-pipe-thread-'));
234
+ try {
235
+ const plannerMock = new MockHarness('planner-uuid', { inputTokens: 50, outputTokens: 20 });
236
+ const coderMock = new MockHarness('coder-uuid', { inputTokens: 50, outputTokens: 20 });
237
+ const reviewerMock = new MockHarness('reviewer-uuid', { inputTokens: 50, outputTokens: 20 });
238
+ let claudeCount = 0;
239
+ const orchestrator = new MultiAgentOrchestrator(tmpDir, {
240
+ claudeAdapterFactory: () => {
241
+ claudeCount++;
242
+ if (claudeCount === 1)
243
+ return plannerMock;
244
+ return reviewerMock;
245
+ },
246
+ codexAdapterFactory: () => coderMock,
247
+ });
248
+ const specs = [
249
+ { id: 'plan', name: 'Planner', role: 'lead', vendor: 'claude-code', prompt: 'Create plan for auth API' },
250
+ { id: 'impl', name: 'Implementer', role: 'developer', vendor: 'codex', prompt: 'Implement the planned endpoints' },
251
+ { id: 'rev', name: 'Reviewer', role: 'reviewer', vendor: 'claude-code', prompt: 'Review implementation for security vulnerabilities' },
252
+ ];
253
+ const result = await orchestrator.runTeam(specs, { mode: 'pipeline' });
254
+ expect(result.success).toBe(true);
255
+ expect(result.agents).toHaveLength(3);
256
+ // Verify planner output was captured
257
+ expect(result.agents[0].lastOutput).toContain('Done: Create plan for auth API');
258
+ // Verify coder prompt received planner context
259
+ expect(coderMock.sentPrompts[0]).toContain('Implement the planned endpoints');
260
+ expect(coderMock.sentPrompts[0]).toContain('Context from Prior Phases:');
261
+ expect(coderMock.sentPrompts[0]).toContain('Planner (lead)');
262
+ expect(coderMock.sentPrompts[0]).toContain('Done: Create plan for auth API');
263
+ // Verify reviewer prompt received planner + coder context
264
+ expect(reviewerMock.sentPrompts[0]).toContain('Review implementation for security vulnerabilities');
265
+ expect(reviewerMock.sentPrompts[0]).toContain('Context from Prior Phases:');
266
+ expect(reviewerMock.sentPrompts[0]).toContain('Planner (lead)');
267
+ expect(reviewerMock.sentPrompts[0]).toContain('Implementer (developer)');
268
+ }
269
+ finally {
270
+ await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => { });
271
+ }
272
+ });
273
+ it('caps accumulated stream output and inserts truncation marker when exceeding MAX_OUTPUT_CAPTURE_CHARS', async () => {
274
+ const { appendCappedOutput } = await import('./orchestrator.js');
275
+ const smallChunk = 'Hello world\n';
276
+ expect(appendCappedOutput(undefined, smallChunk, 50)).toBe('Hello world\n');
277
+ // Overflow max chars
278
+ const largeChunk = 'A'.repeat(60);
279
+ const capped = appendCappedOutput('initial ', largeChunk, 20);
280
+ expect(capped).toContain('[... prior output truncated ...]');
281
+ expect(capped.endsWith('A'.repeat(20))).toBe(true);
282
+ });
283
+ });
284
+ //# sourceMappingURL=orchestrator.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator.test.js","sourceRoot":"","sources":["../../src/agent/orchestrator.test.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,7 @@ import type { AgentExecutionProfile } from './ProviderRegistry.js';
7
7
  export interface AgentSession {
8
8
  id: string;
9
9
  resume: boolean;
10
+ model?: string;
10
11
  }
11
12
  /**
12
13
  * Strict UUID check. Session ids come from the client and end up in the argv of a
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/agent/session.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAEnE,gGAAgG;AAChG,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,OAAO,CAAC;CACjB;AAID;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,MAAM,CAE5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,OAAO,EACpB,OAAO,CAAC,EAAE,YAAY,EACtB,gBAAgB,GAAE,qBAAgC,GACjD,MAAM,EAAE,CAmBV"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/agent/session.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAEnE,gGAAgG;AAChG,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAID;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,OAAO,GAAG,EAAE,IAAI,MAAM,CAE5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,OAAO,EACpB,OAAO,CAAC,EAAE,YAAY,EACtB,gBAAgB,GAAE,qBAAgC,GACjD,MAAM,EAAE,CAoBV"}
@@ -29,14 +29,15 @@ export function buildClaudeTurnArgs(isFirstTurn, session, executionProfile = 're
29
29
  '--permission-mode',
30
30
  executionProfile === 'workspace-write' ? 'acceptEdits' : 'plan',
31
31
  ];
32
+ const modelArgs = session?.model ? ['--model', session.model] : [];
32
33
  if (!session) {
33
34
  return isFirstTurn
34
35
  ? ['-p', ...outputArgs, ...permissionArgs]
35
36
  : ['-c', '-p', ...outputArgs, ...permissionArgs];
36
37
  }
37
38
  if (!session.resume && isFirstTurn) {
38
- return ['-p', ...outputArgs, ...permissionArgs, '--session-id', session.id];
39
+ return ['-p', ...outputArgs, ...permissionArgs, ...modelArgs, '--session-id', session.id];
39
40
  }
40
- return ['-p', ...outputArgs, ...permissionArgs, '--resume', session.id];
41
+ return ['-p', ...outputArgs, ...permissionArgs, ...modelArgs, '--resume', session.id];
41
42
  }
42
43
  //# sourceMappingURL=session.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/agent/session.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,MAAM,OAAO,GAAG,iEAAiE,CAAC;AAElF;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAAW;IAC5C,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACpD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CACjC,WAAoB,EACpB,OAAsB,EACtB,mBAA0C,QAAQ;IAElD,MAAM,UAAU,GAAG;QACjB,iBAAiB,EAAE,aAAa;QAChC,WAAW;QACX,4BAA4B;KAC7B,CAAC;IACF,MAAM,cAAc,GAAG;QACrB,mBAAmB;QACnB,gBAAgB,KAAK,iBAAiB,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM;KAChE,CAAC;IACF,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,WAAW;YAChB,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,UAAU,EAAE,GAAG,cAAc,CAAC;YAC1C,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,UAAU,EAAE,GAAG,cAAc,CAAC,CAAC;IACrD,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,WAAW,EAAE,CAAC;QACnC,OAAO,CAAC,IAAI,EAAE,GAAG,UAAU,EAAE,GAAG,cAAc,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,CAAC,IAAI,EAAE,GAAG,UAAU,EAAE,GAAG,cAAc,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;AAC1E,CAAC"}
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/agent/session.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,MAAM,OAAO,GAAG,iEAAiE,CAAC;AAElF;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAAW;IAC5C,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACpD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CACjC,WAAoB,EACpB,OAAsB,EACtB,mBAA0C,QAAQ;IAElD,MAAM,UAAU,GAAG;QACjB,iBAAiB,EAAE,aAAa;QAChC,WAAW;QACX,4BAA4B;KAC7B,CAAC;IACF,MAAM,cAAc,GAAG;QACrB,mBAAmB;QACnB,gBAAgB,KAAK,iBAAiB,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM;KAChE,CAAC;IACF,MAAM,SAAS,GAAG,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,WAAW;YAChB,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,UAAU,EAAE,GAAG,cAAc,CAAC;YAC1C,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,UAAU,EAAE,GAAG,cAAc,CAAC,CAAC;IACrD,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,WAAW,EAAE,CAAC;QACnC,OAAO,CAAC,IAAI,EAAE,GAAG,UAAU,EAAE,GAAG,cAAc,EAAE,GAAG,SAAS,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IAC5F,CAAC;IACD,OAAO,CAAC,IAAI,EAAE,GAAG,UAAU,EAAE,GAAG,cAAc,EAAE,GAAG,SAAS,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;AACxF,CAAC"}
@@ -41,6 +41,15 @@ describe('buildClaudeTurnArgs', () => {
41
41
  '-p', ...STREAM_ARGS, '--permission-mode', 'acceptEdits', '--resume', ID,
42
42
  ]);
43
43
  });
44
+ it('passes the selected model on new and resumed Claude turns', () => {
45
+ const session = { id: ID, resume: false, model: 'sonnet' };
46
+ expect(buildClaudeTurnArgs(true, session)).toEqual([
47
+ '-p', ...STREAM_ARGS, '--permission-mode', 'plan', '--model', 'sonnet', '--session-id', ID,
48
+ ]);
49
+ expect(buildClaudeTurnArgs(false, session)).toEqual([
50
+ '-p', ...STREAM_ARGS, '--permission-mode', 'plan', '--model', 'sonnet', '--resume', ID,
51
+ ]);
52
+ });
44
53
  });
45
54
  describe('isValidSessionUuid', () => {
46
55
  it('accepts a lowercase uuid', () => {