all test cases passing

This commit is contained in:
Pedram Amini
2025-12-20 05:08:51 -06:00
parent 7842d01d07
commit 414699dd77
11 changed files with 79 additions and 6 deletions

View File

@@ -139,7 +139,12 @@ describe('BatchRunnerModal', () => {
};
(window.maestro.git as Record<string, unknown>).branches = vi.fn().mockResolvedValue({ branches: ['main', 'develop'] });
(window.maestro.git as Record<string, unknown>).checkGhCli = vi.fn().mockResolvedValue({ installed: true, authenticated: true });
(window.maestro.git as Record<string, unknown>).checkGhCli = vi.fn(() => ({
then: (cb: (value: { installed: boolean; authenticated: boolean }) => void) => {
cb({ installed: true, authenticated: true });
return Promise.resolve({ installed: true, authenticated: true });
},
}));
(window.maestro.git as Record<string, unknown>).worktreeInfo = vi.fn().mockResolvedValue({
success: true,
exists: false,

View File

@@ -300,9 +300,11 @@ describe('fileExplorer utils', () => {
const error = new Error('Permission denied');
vi.mocked(window.maestro.fs.readDir).mockRejectedValue(error);
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
await expect(loadFileTree('/restricted')).rejects.toThrow(
'Permission denied'
);
consoleSpy.mockRestore();
});
it('respects default maxDepth of 10', async () => {

View File

@@ -459,6 +459,7 @@ Binary files /dev/null and b/image.png differ`;
describe('diffText preservation', () => {
it('preserves the original diff text in the result', () => {
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
const diffText = `diff --git a/file.ts b/file.ts
--- a/file.ts
+++ b/file.ts
@@ -473,6 +474,7 @@ Binary files /dev/null and b/image.png differ`;
const result = parseGitDiff(diffText);
expect(result[0].diffText).toBe(diffText);
consoleSpy.mockRestore();
});
it('preserves individual diff sections for multiple files', () => {

View File

@@ -36,6 +36,16 @@ vi.mock('../../../web/hooks/useDeviceColorScheme', () => ({
const mockedCssCustomProperties = vi.mocked(cssCustomProperties);
const mockedUseDeviceColorScheme = vi.mocked(useDeviceColorSchemeModule.useDeviceColorScheme);
let consoleErrorSpy: ReturnType<typeof vi.spyOn> | undefined;
beforeEach(() => {
consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
});
afterEach(() => {
consoleErrorSpy?.mockRestore();
consoleErrorSpy = undefined;
});
// Test themes
const customDarkTheme: Theme = {

View File

@@ -43,6 +43,7 @@ export interface AgentConfig {
yoloModeArgs?: string[]; // Args for YOLO/full-access mode (e.g., ['--dangerously-bypass-approvals-and-sandbox'])
workingDirArgs?: (dir: string) => string[]; // Function to build working directory args (e.g., ['-C', dir])
imageArgs?: (imagePath: string) => string[]; // Function to build image attachment args (e.g., ['-i', imagePath] for Codex)
noPromptSeparator?: boolean; // If true, don't add '--' before the prompt in batch mode (OpenCode doesn't support it)
}
const AGENT_DEFINITIONS: Omit<AgentConfig, 'available' | 'path' | 'capabilities'>[] = [
@@ -126,6 +127,7 @@ const AGENT_DEFINITIONS: Omit<AgentConfig, 'available' | 'path' | 'capabilities'
modelArgs: (modelId: string) => ['--model', modelId], // Model selection (e.g., 'ollama/qwen3:8b')
yoloModeArgs: ['run'], // 'run' subcommand auto-approves all permissions (YOLO mode is implicit)
imageArgs: (imagePath: string) => ['-f', imagePath], // Image/file attachment: opencode run -f /path/to/image.png
noPromptSeparator: true, // OpenCode doesn't support '--' before prompt (breaks yargs parsing)
// Agent-specific configuration options shown in UI
configOptions: [
{

View File

@@ -200,9 +200,11 @@ export async function addParticipant(
prompt,
contextWindow: getContextWindowValue(agentConfig, agentConfigValues || {}),
customEnvVars: configResolution.effectiveCustomEnvVars ?? effectiveEnvVars,
noPromptSeparator: agentConfig?.noPromptSeparator,
});
console.log(`[GroupChat:Debug] Spawn result: ${JSON.stringify(result)}`);
console.log(`[GroupChat:Debug] noPromptSeparator: ${agentConfig?.noPromptSeparator ?? false}`);
if (!result.success) {
console.log(`[GroupChat:Debug] ERROR: Spawn failed!`);

View File

@@ -30,6 +30,7 @@ export interface IProcessManager {
prompt?: string;
customEnvVars?: Record<string, string>;
contextWindow?: number;
noPromptSeparator?: boolean;
}): { pid: number; success: boolean };
write(sessionId: string, data: string): boolean;

View File

@@ -384,6 +384,7 @@ ${message}`;
// Get the base args from the agent configuration
const args = [...agent.args];
const agentConfigValues = getAgentConfigCallback?.(chat.moderatorAgentId) || {};
console.log(`[GroupChat:Debug] agentConfigValues for ${chat.moderatorAgentId}: ${JSON.stringify(agentConfigValues)}`);
const baseArgs = buildAgentArgs(agent, {
baseArgs: args,
prompt: fullPrompt,
@@ -422,10 +423,12 @@ ${message}`;
prompt: fullPrompt,
contextWindow: getContextWindowValue(agent, agentConfigValues),
customEnvVars: configResolution.effectiveCustomEnvVars ?? getCustomEnvVarsCallback?.(chat.moderatorAgentId),
noPromptSeparator: agent.noPromptSeparator,
});
console.log(`[GroupChat:Debug] Spawn result: ${JSON.stringify(spawnResult)}`);
console.log(`[GroupChat:Debug] Moderator process spawned successfully`);
console.log(`[GroupChat:Debug] noPromptSeparator: ${agent.noPromptSeparator ?? false}`);
console.log(`[GroupChat:Debug] =================================================`);
} catch (error) {
console.error(`[GroupChat:Debug] SPAWN ERROR:`, error);
@@ -679,9 +682,11 @@ Please respond to this request.${readOnly ? ' Remember: READ-ONLY mode is active
prompt: participantPrompt,
contextWindow: getContextWindowValue(agent, agentConfigValues),
customEnvVars: configResolution.effectiveCustomEnvVars ?? getCustomEnvVarsCallback?.(participant.agentId),
noPromptSeparator: agent.noPromptSeparator,
});
console.log(`[GroupChat:Debug] Spawn result for ${participantName}: ${JSON.stringify(spawnResult)}`);
console.log(`[GroupChat:Debug] noPromptSeparator: ${agent.noPromptSeparator ?? false}`);
// Track this participant as pending response
participantsToRespond.add(participantName);
@@ -933,10 +938,12 @@ Review the agent responses above. Either:
prompt: synthesisPrompt,
contextWindow: getContextWindowValue(agent, agentConfigValues),
customEnvVars: configResolution.effectiveCustomEnvVars ?? getCustomEnvVarsCallback?.(chat.moderatorAgentId),
noPromptSeparator: agent.noPromptSeparator,
});
console.log(`[GroupChat:Debug] Synthesis spawn result: ${JSON.stringify(spawnResult)}`);
console.log(`[GroupChat:Debug] Synthesis moderator process spawned successfully`);
console.log(`[GroupChat:Debug] noPromptSeparator: ${agent.noPromptSeparator ?? false}`);
console.log(`[GroupChat:Debug] ================================================`);
} catch (error) {
console.error(`[GroupChat:Debug] SYNTHESIS SPAWN ERROR:`, error);

View File

@@ -203,6 +203,7 @@ export function registerProcessHandlers(deps: ProcessHandlerDependencies): void
contextWindow, // Pass configured context window to process manager
customEnvVars: effectiveCustomEnvVars, // Pass custom env vars (session-level or agent-level)
imageArgs: agent?.imageArgs, // Function to build image CLI args (for Codex, OpenCode)
noPromptSeparator: agent?.noPromptSeparator, // OpenCode doesn't support '--' before prompt
});
logger.info(`Process spawned successfully`, LOG_CONTEXT, {

View File

@@ -57,6 +57,7 @@ interface ProcessConfig {
imageArgs?: (imagePath: string) => string[]; // Function to build image CLI args (e.g., ['-i', path] for Codex)
contextWindow?: number; // Configured context window size (0 or undefined = not configured, hide UI)
customEnvVars?: Record<string, string>; // Custom environment variables from user configuration
noPromptSeparator?: boolean; // If true, don't add '--' before the prompt (e.g., OpenCode doesn't support it)
}
interface ManagedProcess {
@@ -194,10 +195,10 @@ export class ProcessManager extends EventEmitter {
* Spawn a new process for a session
*/
spawn(config: ProcessConfig): { pid: number; success: boolean } {
const { sessionId, toolType, cwd, command, args, requiresPty, prompt, shell, shellArgs, shellEnvVars, images, imageArgs, contextWindow, customEnvVars } = config;
const { sessionId, toolType, cwd, command, args, requiresPty, prompt, shell, shellArgs, shellEnvVars, images, imageArgs, contextWindow, customEnvVars, noPromptSeparator } = config;
// For batch mode with images, use stream-json mode and send message via stdin
// For batch mode without images, append prompt to args with -- separator
// For batch mode without images, append prompt to args with -- separator (unless noPromptSeparator is true)
const hasImages = images && images.length > 0;
const capabilities = getAgentCapabilities(toolType);
let finalArgs: string[];
@@ -219,8 +220,12 @@ export class ProcessManager extends EventEmitter {
finalArgs = [...finalArgs, ...imageArgs(tempPath)];
}
}
// Add the prompt at the end
finalArgs = [...finalArgs, '--', prompt];
// Add the prompt at the end (with or without -- separator)
if (noPromptSeparator) {
finalArgs = [...finalArgs, prompt];
} else {
finalArgs = [...finalArgs, '--', prompt];
}
logger.debug('[ProcessManager] Using file-based image args', 'ProcessManager', {
sessionId,
imageCount: images.length,
@@ -229,7 +234,12 @@ export class ProcessManager extends EventEmitter {
} else if (prompt) {
// Regular batch mode - prompt as CLI arg
// The -- ensures prompt is treated as positional arg, not a flag (even if it starts with --)
finalArgs = [...args, '--', prompt];
// Some agents (e.g., OpenCode) don't support the -- separator
if (noPromptSeparator) {
finalArgs = [...args, prompt];
} else {
finalArgs = [...args, '--', prompt];
}
} else {
finalArgs = args;
}
@@ -494,6 +504,13 @@ export class ProcessManager extends EventEmitter {
childProcess.stdout.on('data', (data: Buffer | string) => {
const output = data.toString();
// Debug: Log all stdout data for group chat sessions
if (sessionId.includes('group-chat-')) {
console.log(`[GroupChat:Debug:ProcessManager] STDOUT received for session ${sessionId}`);
console.log(`[GroupChat:Debug:ProcessManager] Raw output length: ${output.length}`);
console.log(`[GroupChat:Debug:ProcessManager] Raw output preview: "${output.substring(0, 500)}${output.length > 500 ? '...' : ''}"`);
}
if (isStreamJsonMode) {
// In stream-json mode, each line is a JSONL message
// Accumulate and process complete lines
@@ -668,6 +685,13 @@ export class ProcessManager extends EventEmitter {
const stderrData = data.toString();
logger.debug('[ProcessManager] stderr event fired', 'ProcessManager', { sessionId, dataPreview: stderrData.substring(0, 100) });
// Debug: Log all stderr data for group chat sessions
if (sessionId.includes('group-chat-')) {
console.log(`[GroupChat:Debug:ProcessManager] STDERR received for session ${sessionId}`);
console.log(`[GroupChat:Debug:ProcessManager] Stderr length: ${stderrData.length}`);
console.log(`[GroupChat:Debug:ProcessManager] Stderr preview: "${stderrData.substring(0, 500)}${stderrData.length > 500 ? '...' : ''}"`);
}
// Accumulate stderr for error detection at exit (with size limit to prevent memory exhaustion)
managedProcess.stderrBuffer = appendToBuffer(managedProcess.stderrBuffer || '', stderrData);
@@ -705,6 +729,19 @@ export class ProcessManager extends EventEmitter {
jsonBufferLength: managedProcess.jsonBuffer?.length || 0,
jsonBufferPreview: managedProcess.jsonBuffer?.substring(0, 200)
});
// Debug: Log exit details for group chat sessions
if (sessionId.includes('group-chat-')) {
console.log(`[GroupChat:Debug:ProcessManager] EXIT for session ${sessionId}`);
console.log(`[GroupChat:Debug:ProcessManager] Exit code: ${code}`);
console.log(`[GroupChat:Debug:ProcessManager] isStreamJsonMode: ${isStreamJsonMode}`);
console.log(`[GroupChat:Debug:ProcessManager] isBatchMode: ${isBatchMode}`);
console.log(`[GroupChat:Debug:ProcessManager] resultEmitted: ${managedProcess.resultEmitted}`);
console.log(`[GroupChat:Debug:ProcessManager] streamedText length: ${managedProcess.streamedText?.length || 0}`);
console.log(`[GroupChat:Debug:ProcessManager] jsonBuffer length: ${managedProcess.jsonBuffer?.length || 0}`);
console.log(`[GroupChat:Debug:ProcessManager] stderrBuffer length: ${managedProcess.stderrBuffer?.length || 0}`);
console.log(`[GroupChat:Debug:ProcessManager] stderrBuffer preview: "${(managedProcess.stderrBuffer || '').substring(0, 500)}"`);
}
if (isBatchMode && !isStreamJsonMode && managedProcess.jsonBuffer) {
// Parse JSON response from regular batch mode (not stream-json)
try {

View File

@@ -51,3 +51,7 @@ This restriction ensures:
If a user requests an operation that would write outside your assigned directory, explain the restriction and ask them to either:
1. Change to the appropriate session/agent for that directory
2. Explicitly confirm they want to override this safety measure
### Recommended Operations
Format you responses in Markdown. When referencing file paths, use backticks (ex: `path/to/file`).