import { controllerMaxDraftTextBytes, controllerMcpMethods, controllerMcpWorkspaceMethods, controllerMcpBrowserMaximumImageBytes, controllerMcpBrowserOperationTimeoutMs, type IControllerMcpWireCaller, type IControllerRuntimeId, type TControllerMcpMethod, type TControllerMcpTypedRequest, type TControllerModelChoice, type TControllerMcpWorkspaceMethod, type TControllerMcpWorkspaceRequest, } from '../ts_interfaces/index.js'; import * as validation from './classes.validation.js'; import * as plugins from './plugins.js'; const maximumIdBytes = 1024; const maximumModelPartBytes = 512; const wireIdPattern = /^[A-Za-z0-9_-]{16,128}$/; const callerCredentialPattern = /^[A-Za-z0-9_-]{43}$/; const isRecord = (valueArg: unknown): valueArg is Record => ( valueArg !== null && typeof valueArg === 'object' && !Array.isArray(valueArg) ); const assertExactKeys = ( valueArg: Record, requiredArg: readonly string[], optionalArg: readonly string[] = [], ): void => { const keys = Object.keys(valueArg); const allowed = new Set([...requiredArg, ...optionalArg]); if ( requiredArg.some((keyArg) => !Object.hasOwn(valueArg, keyArg)) || keys.some((keyArg) => !allowed.has(keyArg)) ) throw new Error('Invalid private controller request.'); }; const assertBoundedString = ( valueArg: unknown, maximumBytesArg: number, allowBlankArg = false, ): string => { if ( typeof valueArg !== 'string' || (!allowBlankArg && valueArg.trim().length === 0) || Buffer.byteLength(valueArg, 'utf8') > maximumBytesArg ) throw new Error('Invalid private controller request.'); return valueArg; }; export const assertControllerMcpRuntimeId = (valueArg: unknown): IControllerRuntimeId => { if (!isRecord(valueArg)) throw new Error('Invalid private controller request.'); assertExactKeys(valueArg, ['harnessId', 'nativeId']); if (valueArg.harnessId !== 'opencode' && valueArg.harnessId !== 'flex' && valueArg.harnessId !== 'codex') { throw new Error('Invalid private controller request.'); } return { harnessId: valueArg.harnessId, nativeId: assertBoundedString(valueArg.nativeId, maximumIdBytes), }; }; export const assertControllerMcpModelChoice = ( valueArg: unknown, ): TControllerModelChoice => { if (!isRecord(valueArg)) throw new Error('Invalid private controller request.'); if (valueArg.harnessId === 'codex') { assertExactKeys(valueArg, ['harnessId', 'providerID', 'modelID'], ['variant']); if (valueArg.providerID !== 'codex') throw new Error('Invalid Codex model namespace.'); return { harnessId: 'codex', providerID: 'codex', modelID: assertBoundedString(valueArg.modelID, maximumModelPartBytes), ...(valueArg.variant === undefined ? {} : { variant: assertBoundedString(valueArg.variant, maximumModelPartBytes) }) }; } if (valueArg.harnessId === 'opencode') { assertExactKeys(valueArg, ['harnessId', 'providerID', 'modelID'], ['variant']); return { harnessId: 'opencode', providerID: assertBoundedString(valueArg.providerID, maximumModelPartBytes), modelID: assertBoundedString(valueArg.modelID, maximumModelPartBytes), ...(valueArg.variant === undefined ? {} : { variant: assertBoundedString(valueArg.variant, maximumModelPartBytes) }), }; } if (valueArg.harnessId === 'flex') { assertExactKeys(valueArg, ['harnessId', 'providerID', 'modelID'], ['variant']); return { harnessId: 'flex', providerID: assertBoundedString(valueArg.providerID, maximumModelPartBytes), modelID: assertBoundedString(valueArg.modelID, maximumModelPartBytes), ...(valueArg.variant === undefined ? {} : { variant: assertBoundedString(valueArg.variant, maximumModelPartBytes) }), }; } throw new Error('Invalid private controller request.'); }; const assertMethodRequest = ( methodArg: TControllerMcpMethod, requestArg: unknown, ): Record => { if (!isRecord(requestArg)) throw new Error('Invalid private controller request.'); if (methodArg === 'agl.mcp.authswitch.request') { plugins.authswitch.assertAuthSwitchServiceRequest(requestArg); return requestArg; } if (isControllerMcpWorkspaceMethod(methodArg)) { return assertControllerMcpWorkspaceRequest(methodArg, requestArg); } if (methodArg === 'agl.mcp.controller.settings.update') { // The settings `agl settings` administers. Every key is optional; the shared validator // decides each value, and an update that names nothing is refused. assertExactKeys(requestArg, [], ['browserVideoBackend', 'standardProjectDirectories']); if (Object.keys(requestArg).length === 0) { throw new Error('A settings update must change at least one setting.'); } return validation.assertSettingsUpdateRequest(requestArg); } if (methodArg === 'agl.mcp.controller.settings.get' || methodArg === 'agl.mcp.controller.status' || methodArg === 'agl.mcp.projects.list') { assertExactKeys(requestArg, []); return {}; } if (methodArg === 'agl.mcp.sessions.list') { assertExactKeys(requestArg, ['projectId']); return { projectId: assertBoundedString(requestArg.projectId, maximumIdBytes) }; } if (methodArg === 'agl.mcp.session.read') { assertExactKeys(requestArg, ['projectId', 'sessionId']); return { projectId: assertBoundedString(requestArg.projectId, maximumIdBytes), sessionId: assertControllerMcpRuntimeId(requestArg.sessionId), }; } if (methodArg === 'agl.mcp.session.scratchpad.read') { assertExactKeys(requestArg, ['projectId', 'sessionId']); return { projectId: assertBoundedString(requestArg.projectId, maximumIdBytes), sessionId: assertControllerMcpRuntimeId(requestArg.sessionId), }; } if (methodArg === 'agl.mcp.session.scratchpad.update') { assertExactKeys(requestArg, ['projectId', 'sessionId', 'text', 'expectedRevision']); const text = assertBoundedString(requestArg.text, 128 * 1024, true); if ( text.length > 32_768 || !Number.isSafeInteger(requestArg.expectedRevision) || (requestArg.expectedRevision as number) < 0 ) throw new Error('Invalid private controller request.'); return { projectId: assertBoundedString(requestArg.projectId, maximumIdBytes), sessionId: assertControllerMcpRuntimeId(requestArg.sessionId), text, expectedRevision: requestArg.expectedRevision as number, }; } assertExactKeys( requestArg, ['projectId', 'sessionId', 'text'], ['model', 'providerConnectionId'], ); const text = assertBoundedString(requestArg.text, controllerMaxDraftTextBytes); const sessionId = assertControllerMcpRuntimeId(requestArg.sessionId); const model = requestArg.model === undefined ? undefined : assertControllerMcpModelChoice(requestArg.model); const providerConnectionId = requestArg.providerConnectionId === undefined ? undefined : assertBoundedString(requestArg.providerConnectionId, maximumModelPartBytes); if (providerConnectionId !== undefined && model?.harnessId !== 'flex') { throw new Error('Invalid private controller request.'); } return { projectId: assertBoundedString(requestArg.projectId, maximumIdBytes), sessionId, text, ...(model === undefined ? {} : { model }), ...(providerConnectionId === undefined ? {} : { providerConnectionId }), }; }; export const assertControllerMcpWireRequest = ( valueArg: unknown, ): TControllerMcpTypedRequest => { if (!isRecord(valueArg)) throw new Error('Invalid private controller request.'); assertExactKeys( valueArg, ['requestInstanceId', 'method', 'request', 'response', 'correlation'], ['caller'], ); assertControllerMcpWireCaller(valueArg.caller); if ( typeof valueArg.requestInstanceId !== 'string' || !wireIdPattern.test(valueArg.requestInstanceId) || typeof valueArg.method !== 'string' || !(controllerMcpMethods as readonly string[]).includes(valueArg.method) || !isRecord(valueArg.response) || Object.keys(valueArg.response).length !== 0 || !isRecord(valueArg.correlation) ) throw new Error('Invalid private controller request.'); assertExactKeys(valueArg.correlation, ['id', 'phase']); if ( typeof valueArg.correlation.id !== 'string' || !wireIdPattern.test(valueArg.correlation.id) || valueArg.correlation.phase !== 'request' ) throw new Error('Invalid private controller request.'); const method = valueArg.method as TControllerMcpMethod; const request = assertMethodRequest(method, valueArg.request); return { requestInstanceId: valueArg.requestInstanceId, method, request, response: {}, correlation: { id: valueArg.correlation.id, phase: 'request', }, } as unknown as TControllerMcpTypedRequest; }; /** * The optional caller credential travels on the envelope rather than inside each request, so one * validation covers every method. The value itself is never echoed, logged or surfaced. */ export const assertControllerMcpWireCaller = ( valueArg: unknown, ): IControllerMcpWireCaller | undefined => { if (valueArg === undefined) return undefined; if (!isRecord(valueArg)) throw new Error('Invalid private controller request.'); assertExactKeys(valueArg, ['credential']); if ( typeof valueArg.credential !== 'string' || !callerCredentialPattern.test(valueArg.credential) ) throw new Error('Invalid private controller request.'); return { credential: valueArg.credential }; }; export const controllerMcpValueIsRecord = isRecord; export const assertControllerMcpExactKeys = assertExactKeys; export const isControllerMcpWorkspaceMethod = ( methodArg: string, ): methodArg is TControllerMcpWorkspaceMethod => ( (controllerMcpWorkspaceMethods as readonly string[]).includes(methodArg) ); const workspaceRequestValidators = { 'agl.mcp.browser.action': (valueArg: unknown) => { if (!isRecord(valueArg)) throw new Error('Invalid browser action.'); assertExactKeys(valueArg, ['projectId', 'resourceId', 'expectedAttachmentRevision', 'action']); const { action: rawAction, ...target } = valueArg; const request = validation.assertResourceAttachmentFenceRequest(target, 'browser action'); const action = plugins.browserRuntime.validateAgentAction(rawAction); if ('timeoutMs' in action && action.timeoutMs !== undefined && action.timeoutMs > controllerMcpBrowserOperationTimeoutMs) { throw new Error('Browser actions must complete within 20 seconds.'); } return { ...request, action }; }, 'agl.mcp.project.create': validation.assertProjectCreateRequest, 'agl.mcp.model.list': (valueArg: unknown) => { if (!isRecord(valueArg)) throw new Error('Invalid model context.'); assertExactKeys(valueArg, [], ['projectId', 'sessionId']); if (valueArg.sessionId !== undefined) return validation.assertSessionGetRequest(valueArg); return valueArg.projectId === undefined ? {} : validation.assertSessionListRequest(valueArg); }, 'agl.mcp.session.create': validation.assertSessionCreateRequest, 'agl.mcp.session.model.update': validation.assertSessionModelUpdateRequest, 'agl.mcp.session.rename': validation.assertSessionRenameRequest, 'agl.mcp.session.archive': validation.assertSessionArchiveRequest, 'agl.mcp.session.abort': validation.assertSessionAbortRequest, // MCP resource listing stays project-scoped: the caller-identity filter is defined against // one project, so the cross-project browser form is deliberately not reachable here. 'agl.mcp.resource.list': (valueArg: unknown) => { const request = validation.assertResourceListRequest(valueArg); if (request.projectId === undefined) throw new Error('Missing request field: projectId'); return request; }, 'agl.mcp.resource.create': validation.assertResourceCreateRequest, 'agl.mcp.resource.rename': validation.assertResourceRenameRequest, 'agl.mcp.resource.attach': validation.assertMcpResourceAttachRequest, 'agl.mcp.resource.detach': validation.assertMcpResourceDetachRequest, 'agl.mcp.resource.start': (valueArg: unknown) => validation.assertResourceTargetRequest(valueArg, 'resource start'), 'agl.mcp.resource.stop': (valueArg: unknown) => validation.assertResourceTargetRequest(valueArg, 'resource stop'), 'agl.mcp.resource.retire': (valueArg: unknown) => validation.assertResourceTargetRequest(valueArg, 'resource retire'), 'agl.mcp.context.resolve': (valueArg: unknown) => { if (!isRecord(valueArg)) throw new Error('Invalid context.'); assertExactKeys(valueArg, ['directory'], ['sessionId']); const directory = assertBoundedString(valueArg.directory, 4096); // The caller's cwd is not the controller's cwd. Resolve only explicit // absolute paths; canonical filesystem identity is checked by the controller. if (!directory.startsWith('/') || /[\x00-\x1f\x7f\\]/.test(directory)) { throw new Error('Context directory must be an absolute path.'); } return { directory, ...(valueArg.sessionId === undefined ? {} : { sessionId: validation.assertControllerRuntimeId(valueArg.sessionId, 'sessionId', ['opencode', 'flex', 'codex']), }), }; }, }; export const assertControllerMcpWorkspaceRequest = ( methodArg: TMethod, valueArg: unknown, ): TControllerMcpWorkspaceRequest => ( workspaceRequestValidators[methodArg](valueArg) as TControllerMcpWorkspaceRequest ); export const assertControllerMcpWorkspaceResponse = ( methodArg: TControllerMcpWorkspaceMethod, valueArg: unknown, ): void => { if (!isRecord(valueArg)) throw new Error('Invalid workspace response.'); if (methodArg === 'agl.mcp.browser.action') { assertExactKeys(valueArg, ['result']); if (!isRecord(valueArg.result)) throw new Error('Invalid browser result.'); if (valueArg.result.action === 'screenshot') { assertExactKeys(valueArg.result, ['action', 'image']); const image = valueArg.result.image; if (!isRecord(image)) throw new Error('Invalid browser image.'); assertExactKeys(image, ['mimeType', 'data']); if (!['image/jpeg', 'image/png'].includes(String(image.mimeType)) || typeof image.data !== 'string' || image.data.length === 0 || image.data.length > 4 * Math.ceil(controllerMcpBrowserMaximumImageBytes / 3) || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(image.data)) { throw new Error('Invalid browser image.'); } const bytes = Buffer.from(image.data, 'base64'); if (bytes.length > controllerMcpBrowserMaximumImageBytes || bytes.toString('base64') !== image.data) { throw new Error('Invalid browser image.'); } } else { plugins.browserRuntime.validateAgentActionResult(valueArg.result); } } else if (methodArg === 'agl.mcp.context.resolve' || methodArg === 'agl.mcp.project.create') { assertExactKeys(valueArg, ['project'], methodArg === 'agl.mcp.context.resolve' ? ['session'] : []); if (!isRecord(valueArg.project) || typeof valueArg.project.id !== 'string' || typeof valueArg.project.directory !== 'string') throw new Error('Invalid project response.'); if (valueArg.session !== undefined) { if (!isRecord(valueArg.session)) throw new Error('Invalid session response.'); assertControllerMcpRuntimeId(valueArg.session.id); } } else if (methodArg === 'agl.mcp.model.list') { assertExactKeys(valueArg, ['models'], ['codexContext']); if (!Array.isArray(valueArg.models) || (valueArg.codexContext !== undefined && !isRecord(valueArg.codexContext))) { throw new Error('Invalid model response.'); } } else if (methodArg === 'agl.mcp.session.model.update') { assertExactKeys(valueArg, ['model'], ['providerConnectionId']); const model = assertControllerMcpModelChoice(valueArg.model); if (valueArg.providerConnectionId !== undefined) { assertBoundedString(valueArg.providerConnectionId, maximumModelPartBytes); if (model.harnessId !== 'flex') throw new Error('Invalid provider connection.'); } } else if (methodArg === 'agl.mcp.session.abort') { assertExactKeys(valueArg, ['success']); if (typeof valueArg.success !== 'boolean') throw new Error('Invalid abort response.'); } else if (methodArg.startsWith('agl.mcp.session.')) { assertExactKeys(valueArg, ['session'], methodArg === 'agl.mcp.session.create' ? ['creationPending'] : []); if (!isRecord(valueArg.session)) throw new Error('Invalid session response.'); assertControllerMcpRuntimeId(valueArg.session.id); if (valueArg.creationPending !== undefined && valueArg.creationPending !== true) { throw new Error('Invalid creation response.'); } } else if (methodArg === 'agl.mcp.resource.list') { assertExactKeys(valueArg, ['resources']); if (!Array.isArray(valueArg.resources)) throw new Error('Invalid resource response.'); } else if (methodArg === 'agl.mcp.resource.retire') { assertExactKeys(valueArg, ['retired']); if (valueArg.retired !== true) throw new Error('Invalid retirement response.'); } else { assertExactKeys(valueArg, ['resource']); if (!isRecord(valueArg.resource) || typeof valueArg.resource.id !== 'string' || typeof valueArg.resource.projectId !== 'string' || !['browser', 'terminal'].includes(String(valueArg.resource.kind))) { throw new Error('Invalid resource response.'); } } };