@measey/mycoder-agent
Version:
Agent module for mycoder - an AI-powered software development assistant
140 lines • 6.76 kB
JavaScript
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';
import { errorToString } from '../../utils/errorToString.js';
import { sleep } from '../../utils/sleep.js';
import { detectBrowsers } from './lib/browserDetectors.js';
import { filterPageContent } from './lib/filterPageContent.js';
import { SessionStatus } from './SessionTracker.js';
const parameterSchema = z.object({
url: z.string().url().optional().describe('Initial URL to navigate to'),
timeout: z
.number()
.optional()
.describe('Default timeout in milliseconds (default: 30000)'),
contentFilter: z
.enum(['raw', 'smartMarkdown'])
.optional()
.describe('Content filter method to use when retrieving page content'),
description: z
.string()
.describe('The reason for starting this browser session (max 80 chars)'),
});
const returnSchema = z.object({
sessionId: z.string(),
status: z.string(),
content: z.string().optional(),
error: z.string().optional(),
});
export const sessionStartTool = {
name: 'sessionStart',
logPrefix: '🏄',
description: 'Starts a new browser session with optional initial URL',
parameters: parameterSchema,
parametersJsonSchema: zodToJsonSchema(parameterSchema),
returns: returnSchema,
returnsJsonSchema: zodToJsonSchema(returnSchema),
execute: async ({ url, timeout = 30000, contentFilter }, context) => {
const { logger, headless, userSession, browserTracker, ...otherContext } = context;
// Use provided contentFilter or default to 'raw'mycoder
const effectiveContentFilter = contentFilter || 'raw';
// Get config from context if available
const config = otherContext.config || {};
logger.debug(`Starting browser session${url ? ` at ${url}` : ''}`);
logger.debug(`User session mode: ${userSession ? 'enabled' : 'disabled'}`);
logger.debug(`Webpage processing mode: ${effectiveContentFilter}`);
try {
// Get browser configuration from config
const browserConfig = config.browser || {};
// Create browser configuration
const sessionConfig = {
headless,
defaultTimeout: timeout,
useSystemBrowsers: browserConfig.useSystemBrowsers !== false,
preferredType: browserConfig.preferredType || 'chromium',
executablePath: browserConfig.executablePath,
};
// If userSession is true, use system Chrome
if (userSession) {
logger.debug('User session mode enabled, forcing system Chrome');
sessionConfig.useSystemBrowsers = true;
sessionConfig.preferredType = 'chromium';
// Try to detect Chrome browser
const browsers = await detectBrowsers(logger);
const chrome = browsers.find((b) => b.name.toLowerCase().includes('chrome'));
if (chrome) {
logger.debug(`Found system Chrome at ${chrome.path}`);
sessionConfig.executablePath = chrome.path;
}
}
logger.debug(`Browser config: ${JSON.stringify(sessionConfig)}`);
// Create a session directly using the browserTracker
const sessionId = await browserTracker.createSession(sessionConfig);
// Get reference to the page
const page = browserTracker.getSessionPage(sessionId);
// Navigate to URL if provided
let content = '';
if (url) {
try {
// Try with 'domcontentloaded' first which is more reliable than 'networkidle'
logger.debug(`Navigating to ${url} with 'domcontentloaded' waitUntil`);
await page.goto(url, { waitUntil: 'domcontentloaded', timeout });
await sleep(3000);
content = await filterPageContent(page, effectiveContentFilter, context);
logger.debug(`Content: ${content}`);
logger.debug('Navigation completed with domcontentloaded strategy');
}
catch (error) {
// If that fails, try with no waitUntil option at all (most basic)
logger.warn(`Failed with domcontentloaded strategy: ${errorToString(error)}`);
logger.debug(`Retrying navigation to ${url} with no waitUntil option`);
try {
await page.goto(url, { timeout });
await sleep(3000);
content = await filterPageContent(page, effectiveContentFilter, context);
logger.debug(`Content: ${content}`);
logger.debug('Navigation completed with basic strategy');
}
catch (innerError) {
logger.error(`Failed with basic navigation strategy: ${errorToString(innerError)}`);
throw innerError; // Re-throw to be caught by outer catch block
}
}
}
logger.debug('Browser session started successfully');
logger.debug(`Content length: ${content.length} characters`);
// Update browser tracker with running status
browserTracker.updateSessionStatus(sessionId, SessionStatus.RUNNING, {
url: url || 'about:blank',
contentLength: content.length,
});
return {
sessionId,
status: 'initialized',
content: content || undefined,
};
}
catch (error) {
logger.error(`Failed to start browser: ${errorToString(error)}`);
// No need to update browser tracker here as we don't have a valid sessionId
// when an error occurs before the browser is properly initialized
return {
sessionId: '',
status: 'error',
error: errorToString(error),
};
}
},
logParameters: ({ url, description, contentFilter }, { logger }) => {
const effectiveContentFilter = contentFilter || 'raw';
logger.log(`Starting browser session${url ? ` at ${url}` : ''} with ${effectiveContentFilter} processing, ${description}`);
},
logReturns: (output, { logger }) => {
if (output.error) {
logger.error(`Browser start failed: ${output.error}`);
}
else {
logger.log(`Browser session started with ID: ${output.sessionId}`);
}
},
};
//# sourceMappingURL=sessionStart.js.map