fix: complete MCP split implementation

- Fix socket path conflict: main server defaults to TCP (localhost:6767),
  Self-ID MCP uses Unix socket (self-id-mcp.sock)
- Remove set_title/set_branch from mcp-server.ts (they belong only in
  agent-self-id-mcp.ts which is used by coding agents)
- Rename mcpSocketPath to selfIdMcpSocketPath for consistency
- Pass --socket arg to self-id-bridge so non-default paths work
- Wire up PID lock to prevent multiple daemon instances
- Add PASEO_SELF_ID_MCP_SOCK env var for testing override
This commit is contained in:
Mohamed Boudra
2026-01-30 11:46:50 +07:00
parent eac342aa8b
commit 1670c7084b
6 changed files with 214 additions and 156 deletions

View File

@@ -59,6 +59,8 @@ export type AgentManagerOptions = {
registry?: AgentStorage; registry?: AgentStorage;
onAgentAttention?: AgentAttentionCallback; onAgentAttention?: AgentAttentionCallback;
logger: Logger; logger: Logger;
/** Path to the Self-ID MCP Unix socket for UI agent injection */
selfIdMcpSocketPath?: string;
}; };
export type WaitForAgentOptions = { export type WaitForAgentOptions = {
@@ -106,7 +108,7 @@ type ManagedAgentBase = {
/** /**
* User-defined labels for categorizing agents (e.g., { ui: "true" }). * User-defined labels for categorizing agents (e.g., { ui: "true" }).
*/ */
labels?: Record<string, string>; labels: Record<string, string>;
}; };
type ManagedAgentWithSession = ManagedAgentBase & { type ManagedAgentWithSession = ManagedAgentBase & {
@@ -190,6 +192,7 @@ export class AgentManager {
private readonly idFactory: () => string; private readonly idFactory: () => string;
private readonly registry?: AgentStorage; private readonly registry?: AgentStorage;
private readonly previousStatuses = new Map<string, AgentLifecycleStatus>(); private readonly previousStatuses = new Map<string, AgentLifecycleStatus>();
private readonly selfIdMcpSocketPath?: string;
private onAgentAttention?: AgentAttentionCallback; private onAgentAttention?: AgentAttentionCallback;
private logger: Logger; private logger: Logger;
@@ -198,6 +201,7 @@ export class AgentManager {
options?.maxTimelineItems ?? DEFAULT_MAX_TIMELINE_ITEMS; options?.maxTimelineItems ?? DEFAULT_MAX_TIMELINE_ITEMS;
this.idFactory = options?.idFactory ?? (() => randomUUID()); this.idFactory = options?.idFactory ?? (() => randomUUID());
this.registry = options?.registry; this.registry = options?.registry;
this.selfIdMcpSocketPath = options?.selfIdMcpSocketPath;
this.onAgentAttention = options?.onAgentAttention; this.onAgentAttention = options?.onAgentAttention;
this.logger = options.logger.child({ module: "agent", component: "agent-manager" }); this.logger = options.logger.child({ module: "agent", component: "agent-manager" });
if (options?.clients) { if (options?.clients) {
@@ -313,13 +317,18 @@ export class AgentManager {
agentId?: string, agentId?: string,
options?: { labels?: Record<string, string> } options?: { labels?: Record<string, string> }
): Promise<ManagedAgent> { ): Promise<ManagedAgent> {
const normalizedConfig = await this.normalizeConfig(config, { labels: options?.labels }); // Generate agent ID early so we can use it in MCP config
const resolvedAgentId = agentId ?? this.idFactory();
const normalizedConfig = await this.normalizeConfig(config, {
labels: options?.labels,
agentId: resolvedAgentId,
});
const client = this.requireClient(normalizedConfig.provider); const client = this.requireClient(normalizedConfig.provider);
const session = await client.createSession(normalizedConfig); const session = await client.createSession(normalizedConfig);
return this.registerSession( return this.registerSession(
session, session,
normalizedConfig, normalizedConfig,
agentId ?? this.idFactory(), resolvedAgentId,
{ labels: options?.labels } { labels: options?.labels }
); );
} }
@@ -835,7 +844,7 @@ export class AgentManager {
lastUserMessageAt: options?.lastUserMessageAt ?? null, lastUserMessageAt: options?.lastUserMessageAt ?? null,
attention: { requiresAttention: false }, attention: { requiresAttention: false },
internal: config.internal ?? false, internal: config.internal ?? false,
labels: options?.labels, labels: options?.labels ?? {},
} as ActiveManagedAgent; } as ActiveManagedAgent;
this.agents.set(agentId, managed); this.agents.set(agentId, managed);
@@ -1103,7 +1112,7 @@ export class AgentManager {
private async normalizeConfig( private async normalizeConfig(
config: AgentSessionConfig, config: AgentSessionConfig,
options?: { labels?: Record<string, string> } options?: { labels?: Record<string, string>; agentId?: string }
): Promise<AgentSessionConfig> { ): Promise<AgentSessionConfig> {
const normalized: AgentSessionConfig = { ...config }; const normalized: AgentSessionConfig = { ...config };
@@ -1117,12 +1126,29 @@ export class AgentManager {
normalized.model = trimmed.length > 0 ? trimmed : undefined; normalized.model = trimmed.length > 0 ? trimmed : undefined;
} }
// Inject paseoPromptInstructions for UI agents (with ui=true label) // Inject paseoPromptInstructions and MCP config for UI agents (with ui=true label)
const isUiAgent = options?.labels?.ui === "true"; const isUiAgent = options?.labels?.ui === "true";
if (isUiAgent) { if (isUiAgent) {
normalized.paseoPromptInstructions = getSelfIdentificationInstructions({ normalized.paseoPromptInstructions = getSelfIdentificationInstructions({
cwd: normalized.cwd, cwd: normalized.cwd,
}); });
// Inject Self-ID MCP server config (stdio bridge to self-id-mcp.sock)
if (this.selfIdMcpSocketPath && options?.agentId) {
const existingMcpServers = normalized.mcpServers ?? {};
normalized.mcpServers = {
...existingMcpServers,
"paseo-self-id": {
type: "stdio",
command: "paseo",
args: [
"self-id-bridge",
"--socket", this.selfIdMcpSocketPath,
"--agent-id", options.agentId,
],
},
};
}
} }
return normalized; return normalized;

View File

@@ -25,13 +25,8 @@ import { toAgentPayload } from "./agent-projections.js";
import { curateAgentActivity } from "./activity-curator.js"; import { curateAgentActivity } from "./activity-curator.js";
import { AGENT_PROVIDER_DEFINITIONS } from "./provider-registry.js"; import { AGENT_PROVIDER_DEFINITIONS } from "./provider-registry.js";
import { AgentStorage } from "./agent-storage.js"; import { AgentStorage } from "./agent-storage.js";
import { import { createWorktree } from "../../utils/worktree.js";
createWorktree,
isPaseoOwnedWorktreeCwd,
validateBranchSlug,
} from "../../utils/worktree.js";
import { WaitForAgentTracker } from "./wait-for-agent-tracker.js"; import { WaitForAgentTracker } from "./wait-for-agent-tracker.js";
import { NotGitRepoError, renameCurrentBranch } from "../../utils/checkout-git.js";
import { injectLeadingPaseoInstructionTag } from "./paseo-instructions-tag.js"; import { injectLeadingPaseoInstructionTag } from "./paseo-instructions-tag.js";
export interface AgentMcpServerOptions { export interface AgentMcpServerOptions {
@@ -112,18 +107,6 @@ function expandPath(path: string): string {
return resolve(path); return resolve(path);
} }
type ToolErrorCode = "NOT_ALLOWED" | "NOT_GIT_REPO" | "INVALID_BRANCH";
class AgentMcpToolError extends Error {
readonly code: ToolErrorCode;
constructor(code: ToolErrorCode, message: string) {
super(message);
this.name = "AgentMcpToolError";
this.code = code;
}
}
/** /**
* Wraps agentManager.waitForAgentEvent with a self-imposed timeout. * Wraps agentManager.waitForAgentEvent with a self-imposed timeout.
* Returns a friendly message when timeout occurs, rather than letting * Returns a friendly message when timeout occurs, rather than letting
@@ -903,134 +886,6 @@ export async function createAgentMcpServer(
} }
); );
server.registerTool(
"set_title",
{
title: "Set Agent Title",
description: "Update the agent's title in the registry.",
inputSchema: {
title: z
.string()
.min(1)
.max(60)
.describe("Short descriptive title (<= 60 chars)."),
},
outputSchema: {
success: z.boolean(),
title: z.string(),
},
},
async ({ title }) => {
if (!callerAgentId) {
throw new AgentMcpToolError(
"NOT_ALLOWED",
"set_title can only be called by a managed agent"
);
}
const agent = agentManager.getAgent(callerAgentId);
if (!agent) {
throw new Error(`Agent ${callerAgentId} not found`);
}
const normalizedTitle = title.trim();
if (!normalizedTitle) {
throw new AgentMcpToolError("NOT_ALLOWED", "Title cannot be empty");
}
if (normalizedTitle.length > 60) {
throw new AgentMcpToolError(
"NOT_ALLOWED",
"Title must be 60 characters or fewer"
);
}
await agentManager.setTitle(agent.id, normalizedTitle);
return {
content: [],
structuredContent: ensureValidJson({
success: true,
title: normalizedTitle,
}),
};
}
);
server.registerTool(
"set_branch",
{
title: "Set Agent Branch",
description:
"Rename the current git branch. Allowed only inside Paseo-owned worktrees.",
inputSchema: {
name: z
.string()
.min(1)
.describe("Git branch name (lowercase letters, numbers, hyphens, slashes)."),
},
outputSchema: {
success: z.boolean(),
branch: z.string(),
},
},
async ({ name }) => {
if (!callerAgentId) {
throw new AgentMcpToolError(
"NOT_ALLOWED",
"set_branch can only be called by a managed agent"
);
}
const agent = agentManager.getAgent(callerAgentId);
if (!agent) {
throw new Error(`Agent ${callerAgentId} not found`);
}
const validation = validateBranchSlug(name);
if (!validation.valid) {
throw new AgentMcpToolError(
"INVALID_BRANCH",
validation.error ?? "Invalid branch name"
);
}
let ownership;
try {
ownership = await isPaseoOwnedWorktreeCwd(agent.cwd, { paseoHome: options.paseoHome });
} catch (error) {
const notGitError =
error instanceof NotGitRepoError
? error
: new NotGitRepoError(agent.cwd);
throw new AgentMcpToolError(
"NOT_GIT_REPO",
notGitError.message
);
}
if (!ownership.allowed) {
throw new AgentMcpToolError(
"NOT_ALLOWED",
"Branch renames are only allowed inside Paseo-owned worktrees"
);
}
const result = await renameCurrentBranch(agent.cwd, name);
if (result.currentBranch !== name) {
throw new Error(
`Branch rename failed (expected ${name}, got ${result.currentBranch ?? "unknown"})`
);
}
return {
content: [],
structuredContent: ensureValidJson({
success: true,
branch: name,
}),
};
}
);
server.registerTool( server.registerTool(
"set_agent_mode", "set_agent_mode",
{ {

View File

@@ -45,6 +45,7 @@ import { AgentManager } from "./agent/agent-manager.js";
import { AgentStorage } from "./agent/agent-storage.js"; import { AgentStorage } from "./agent/agent-storage.js";
import { attachAgentStoragePersistence } from "./persistence-hooks.js"; import { attachAgentStoragePersistence } from "./persistence-hooks.js";
import { createAgentMcpServer } from "./agent/mcp-server.js"; import { createAgentMcpServer } from "./agent/mcp-server.js";
import { createAgentSelfIdMcpServer } from "./agent/agent-self-id-mcp.js";
import { createAllClients, shutdownProviders } from "./agent/provider-registry.js"; import { createAllClients, shutdownProviders } from "./agent/provider-registry.js";
import { createTerminalManager, type TerminalManager } from "../terminal/terminal-manager.js"; import { createTerminalManager, type TerminalManager } from "../terminal/terminal-manager.js";
import { import {
@@ -54,6 +55,7 @@ import {
} from "./connection-offer.js"; } from "./connection-offer.js";
import { printPairingQrIfEnabled } from "./pairing-qr.js"; import { printPairingQrIfEnabled } from "./pairing-qr.js";
import { startRelayTransport, type RelayTransportController } from "./relay-transport.js"; import { startRelayTransport, type RelayTransportController } from "./relay-transport.js";
import { acquirePidLock, releasePidLock } from "./pid-lock.js";
import type { import type {
AgentClient, AgentClient,
AgentProvider, AgentProvider,
@@ -70,6 +72,7 @@ export type PaseoOpenAIConfig = {
export type PaseoDaemonConfig = { export type PaseoDaemonConfig = {
listen: string; listen: string;
paseoHome: string; paseoHome: string;
selfIdMcpSocketPath: string;
corsAllowedOrigins: string[]; corsAllowedOrigins: string[];
agentMcpRoute: string; agentMcpRoute: string;
agentMcpAllowedHosts: string[]; agentMcpAllowedHosts: string[];
@@ -209,6 +212,7 @@ export async function createPaseoDaemon(
...config.agentClients, ...config.agentClients,
}, },
registry: agentStorage, registry: agentStorage,
selfIdMcpSocketPath: config.selfIdMcpSocketPath,
logger, logger,
}); });
@@ -221,6 +225,7 @@ export async function createPaseoDaemon(
); );
const agentMcpTransports: AgentMcpTransportMap = new Map(); const agentMcpTransports: AgentMcpTransportMap = new Map();
const selfIdMcpTransports: AgentMcpTransportMap = new Map();
const allowedHosts = config.agentMcpAllowedHosts; const allowedHosts = config.agentMcpAllowedHosts;
const createAgentMcpTransport = async (callerAgentId?: string) => { const createAgentMcpTransport = async (callerAgentId?: string) => {
@@ -328,7 +333,118 @@ export async function createPaseoDaemon(
app.post(agentMcpRoute, handleAgentMcpRequest); app.post(agentMcpRoute, handleAgentMcpRequest);
app.get(agentMcpRoute, handleAgentMcpRequest); app.get(agentMcpRoute, handleAgentMcpRequest);
app.delete(agentMcpRoute, handleAgentMcpRequest); app.delete(agentMcpRoute, handleAgentMcpRequest);
logger.info({ route: agentMcpRoute }, "Agent MCP server mounted"); logger.info({ route: agentMcpRoute }, "Agent MCP server mounted on main app");
// Create dedicated Self-ID MCP server on Unix socket for agent self-identification
// This only provides set_title and set_branch tools for coding agents
// Host validation is disabled since Unix sockets don't have HTTP hosts
const selfIdMcpSocketPath = config.selfIdMcpSocketPath;
const createSelfIdMcpTransport = async (callerAgentId: string) => {
const selfIdMcpServer = await createAgentSelfIdMcpServer({
agentManager,
paseoHome: config.paseoHome,
callerAgentId,
logger,
});
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
onsessioninitialized: (sessionId) => {
selfIdMcpTransports.set(sessionId, transport);
logger.debug({ sessionId, callerAgentId }, "Self-ID MCP session initialized");
},
onsessionclosed: (sessionId) => {
selfIdMcpTransports.delete(sessionId);
logger.debug({ sessionId }, "Self-ID MCP session closed");
},
// Disable host validation for Unix socket
enableDnsRebindingProtection: false,
});
transport.onclose = () => {
if (transport.sessionId) {
selfIdMcpTransports.delete(transport.sessionId);
}
};
transport.onerror = (err) => {
logger.error({ err }, "Self-ID MCP transport error");
};
await selfIdMcpServer.connect(transport);
return transport;
};
const handleSelfIdMcpRequest: express.RequestHandler = async (req, res) => {
if (config.mcpDebug) {
logger.debug(
{
method: req.method,
url: req.originalUrl,
sessionId: req.header("mcp-session-id"),
body: req.body,
},
"Self-ID MCP request"
);
}
try {
const sessionId = req.header("mcp-session-id");
let transport = sessionId ? selfIdMcpTransports.get(sessionId) : undefined;
if (!transport) {
if (req.method !== "POST") {
res.status(400).json({
jsonrpc: "2.0",
error: { code: -32000, message: "Missing or invalid MCP session" },
id: null,
});
return;
}
if (!isInitializeRequest(req.body)) {
res.status(400).json({
jsonrpc: "2.0",
error: { code: -32000, message: "Initialization request expected" },
id: null,
});
return;
}
const callerAgentIdRaw = req.query.callerAgentId;
const callerAgentId =
typeof callerAgentIdRaw === "string"
? callerAgentIdRaw
: Array.isArray(callerAgentIdRaw) && typeof callerAgentIdRaw[0] === "string"
? callerAgentIdRaw[0]
: undefined;
if (!callerAgentId) {
res.status(400).json({
jsonrpc: "2.0",
error: { code: -32000, message: "callerAgentId query parameter is required for Self-ID MCP" },
id: null,
});
return;
}
transport = await createSelfIdMcpTransport(callerAgentId);
}
await transport.handleRequest(req as any, res as any, req.body);
} catch (err) {
logger.error({ err }, "Failed to handle Self-ID MCP request");
if (!res.headersSent) {
res.status(500).json({
jsonrpc: "2.0",
error: { code: -32603, message: "Internal MCP server error" },
id: null,
});
}
}
};
const selfIdMcpApp = express();
selfIdMcpApp.use(express.json());
selfIdMcpApp.post("/", handleSelfIdMcpRequest);
selfIdMcpApp.get("/", handleSelfIdMcpRequest);
selfIdMcpApp.delete("/", handleSelfIdMcpRequest);
const selfIdMcpSocketServer = createHTTPServer(selfIdMcpApp);
let sttService: OpenAISTT | null = null; let sttService: OpenAISTT | null = null;
let ttsService: OpenAITTS | null = null; let ttsService: OpenAITTS | null = null;
@@ -376,12 +492,38 @@ export async function createPaseoDaemon(
downloadTokenStore, downloadTokenStore,
config.paseoHome, config.paseoHome,
agentMcpRoute, agentMcpRoute,
config.selfIdMcpSocketPath,
{ allowedOrigins }, { allowedOrigins },
{ stt: sttService, tts: ttsService }, { stt: sttService, tts: ttsService },
terminalManager terminalManager
); );
const start = async () => { const start = async () => {
// Acquire PID lock to prevent multiple daemon instances
await acquirePidLock(config.paseoHome, selfIdMcpSocketPath);
// Start Self-ID MCP socket server first
await new Promise<void>((resolve, reject) => {
const onError = (err: Error) => {
selfIdMcpSocketServer.off("listening", onListening);
reject(err);
};
const onListening = () => {
selfIdMcpSocketServer.off("error", onError);
logger.info({ path: selfIdMcpSocketPath }, `Self-ID MCP server listening on ${selfIdMcpSocketPath}`);
resolve();
};
selfIdMcpSocketServer.once("error", onError);
selfIdMcpSocketServer.once("listening", onListening);
// Remove stale socket file if it exists
if (existsSync(selfIdMcpSocketPath)) {
unlinkSync(selfIdMcpSocketPath);
}
selfIdMcpSocketServer.listen(selfIdMcpSocketPath);
});
// Start main HTTP server
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
const onError = (err: Error) => { const onError = (err: Error) => {
httpServer.off("listening", onListening); httpServer.off("listening", onListening);
@@ -456,10 +598,18 @@ export async function createPaseoDaemon(
await new Promise<void>((resolve) => { await new Promise<void>((resolve) => {
httpServer.close(() => resolve()); httpServer.close(() => resolve());
}); });
// Clean up socket file await new Promise<void>((resolve) => {
selfIdMcpSocketServer.close(() => resolve());
});
// Clean up socket files
if (listenTarget.type === "socket" && existsSync(listenTarget.path)) { if (listenTarget.type === "socket" && existsSync(listenTarget.path)) {
unlinkSync(listenTarget.path); unlinkSync(listenTarget.path);
} }
if (existsSync(selfIdMcpSocketPath)) {
unlinkSync(selfIdMcpSocketPath);
}
// Release PID lock
await releasePidLock(config.paseoHome);
}; };
return { return {

View File

@@ -5,11 +5,25 @@ import type { STTConfig } from "./agent/stt-openai.js";
import type { TTSConfig } from "./agent/tts-openai.js"; import type { TTSConfig } from "./agent/tts-openai.js";
import { loadPersistedConfig } from "./persisted-config.js"; import { loadPersistedConfig } from "./persisted-config.js";
const DEFAULT_LISTEN = "127.0.0.1:6767"; const DEFAULT_PORT = 6767;
const DEFAULT_AGENT_MCP_ROUTE = "/mcp/agents"; const DEFAULT_AGENT_MCP_ROUTE = "/mcp/agents";
const DEFAULT_RELAY_ENDPOINT = "relay.paseo.sh:443"; const DEFAULT_RELAY_ENDPOINT = "relay.paseo.sh:443";
const DEFAULT_APP_BASE_URL = "https://app.paseo.sh"; const DEFAULT_APP_BASE_URL = "https://app.paseo.sh";
function getDefaultListen(): string {
// Main HTTP server defaults to TCP
return `127.0.0.1:${DEFAULT_PORT}`;
}
function getSelfIdMcpSocketPath(paseoHome: string, env: NodeJS.ProcessEnv): string {
// Allow override via PASEO_SELF_ID_MCP_SOCK for testing
if (env.PASEO_SELF_ID_MCP_SOCK) {
return env.PASEO_SELF_ID_MCP_SOCK;
}
// Default to ${PASEO_HOME}/self-id-mcp.sock
return path.join(paseoHome, "self-id-mcp.sock");
}
function parseOpenAIConfig(env: NodeJS.ProcessEnv) { function parseOpenAIConfig(env: NodeJS.ProcessEnv) {
const apiKey = env.OPENAI_API_KEY; const apiKey = env.OPENAI_API_KEY;
if (!apiKey) return undefined; if (!apiKey) return undefined;
@@ -66,8 +80,14 @@ export function loadConfig(
): PaseoDaemonConfig { ): PaseoDaemonConfig {
const persisted = loadPersistedConfig(paseoHome); const persisted = loadPersistedConfig(paseoHome);
const listen = env.PASEO_LISTEN ?? persisted.listen ?? DEFAULT_LISTEN; // PASEO_LISTEN can be:
// - host:port (TCP)
// - /path/to/socket (Unix socket)
// - unix:///path/to/socket (Unix socket)
// Default is TCP at 127.0.0.1:6767
const listen = env.PASEO_LISTEN ?? persisted.listen ?? getDefaultListen();
const mcpListen = getListenForMcp(listen); const mcpListen = getListenForMcp(listen);
const selfIdMcpSocketPath = getSelfIdMcpSocketPath(paseoHome, env);
const envCorsOrigins = env.PASEO_CORS_ORIGINS const envCorsOrigins = env.PASEO_CORS_ORIGINS
? env.PASEO_CORS_ORIGINS.split(",").map((s) => s.trim()) ? env.PASEO_CORS_ORIGINS.split(",").map((s) => s.trim())
@@ -76,6 +96,7 @@ export function loadConfig(
return { return {
listen, listen,
paseoHome, paseoHome,
selfIdMcpSocketPath,
corsAllowedOrigins: [...persisted.cors.allowedOrigins, ...envCorsOrigins], corsAllowedOrigins: [...persisted.cors.allowedOrigins, ...envCorsOrigins],
agentMcpRoute: DEFAULT_AGENT_MCP_ROUTE, agentMcpRoute: DEFAULT_AGENT_MCP_ROUTE,
agentMcpAllowedHosts: [mcpListen, `localhost:${mcpListen.split(":")[1]}`], agentMcpAllowedHosts: [mcpListen, `localhost:${mcpListen.split(":")[1]}`],

View File

@@ -30,6 +30,7 @@ export class VoiceAssistantWebSocketServer {
downloadTokenStore: DownloadTokenStore, downloadTokenStore: DownloadTokenStore,
paseoHome: string, paseoHome: string,
agentMcpRoute: string, agentMcpRoute: string,
selfIdMcpSocketPath: string,
wsConfig: WebSocketServerConfig, wsConfig: WebSocketServerConfig,
speech?: { stt: OpenAISTT | null; tts: OpenAITTS | null }, speech?: { stt: OpenAISTT | null; tts: OpenAITTS | null },
terminalManager?: TerminalManager | null terminalManager?: TerminalManager | null
@@ -42,6 +43,7 @@ export class VoiceAssistantWebSocketServer {
downloadTokenStore, downloadTokenStore,
paseoHome, paseoHome,
agentMcpRoute, agentMcpRoute,
selfIdMcpSocketPath,
speech, speech,
terminalManager terminalManager
); );

View File

@@ -30,6 +30,7 @@ export class WebSocketSessionBridge {
private readonly pushTokenStore: PushTokenStore; private readonly pushTokenStore: PushTokenStore;
private readonly pushService: PushService; private readonly pushService: PushService;
private readonly agentMcpRoute: string; private readonly agentMcpRoute: string;
private readonly selfIdMcpSocketPath: string;
private readonly stt: OpenAISTT | null; private readonly stt: OpenAISTT | null;
private readonly tts: OpenAITTS | null; private readonly tts: OpenAITTS | null;
private readonly terminalManager: TerminalManager | null; private readonly terminalManager: TerminalManager | null;
@@ -42,6 +43,7 @@ export class WebSocketSessionBridge {
downloadTokenStore: DownloadTokenStore, downloadTokenStore: DownloadTokenStore,
paseoHome: string, paseoHome: string,
agentMcpRoute: string, agentMcpRoute: string,
selfIdMcpSocketPath: string,
speech?: { stt: OpenAISTT | null; tts: OpenAITTS | null }, speech?: { stt: OpenAISTT | null; tts: OpenAITTS | null },
terminalManager?: TerminalManager | null terminalManager?: TerminalManager | null
) { ) {
@@ -51,6 +53,7 @@ export class WebSocketSessionBridge {
this.downloadTokenStore = downloadTokenStore; this.downloadTokenStore = downloadTokenStore;
this.paseoHome = paseoHome; this.paseoHome = paseoHome;
this.agentMcpRoute = agentMcpRoute; this.agentMcpRoute = agentMcpRoute;
this.selfIdMcpSocketPath = selfIdMcpSocketPath;
this.stt = speech?.stt ?? null; this.stt = speech?.stt ?? null;
this.tts = speech?.tts ?? null; this.tts = speech?.tts ?? null;
this.terminalManager = terminalManager ?? null; this.terminalManager = terminalManager ?? null;
@@ -87,6 +90,7 @@ export class WebSocketSessionBridge {
this.agentManager, this.agentManager,
this.agentStorage, this.agentStorage,
this.agentMcpRoute, this.agentMcpRoute,
this.selfIdMcpSocketPath,
this.stt, this.stt,
this.tts, this.tts,
this.terminalManager, this.terminalManager,