mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
Move agent-control MCP config into AgentManager
This commit is contained in:
@@ -18,6 +18,7 @@ import type {
|
||||
AgentTimelineItem,
|
||||
AgentUsage,
|
||||
AgentRuntimeInfo,
|
||||
AgentControlMcpConfig,
|
||||
ListPersistedAgentsOptions,
|
||||
PersistedAgentDescriptor,
|
||||
} from "./agent-sdk-types.js";
|
||||
@@ -54,6 +55,7 @@ export type AgentManagerOptions = {
|
||||
maxTimelineItems?: number;
|
||||
idFactory?: () => string;
|
||||
registry?: AgentRegistry;
|
||||
agentControlMcp?: AgentControlMcpConfig;
|
||||
};
|
||||
|
||||
export type WaitForAgentOptions = {
|
||||
@@ -178,12 +180,14 @@ export class AgentManager {
|
||||
private readonly idFactory: () => string;
|
||||
private readonly registry?: AgentRegistry;
|
||||
private readonly previousStatuses = new Map<string, AgentLifecycleStatus>();
|
||||
private readonly agentControlMcp?: AgentControlMcpConfig;
|
||||
|
||||
constructor(options?: AgentManagerOptions) {
|
||||
this.maxTimelineItems =
|
||||
options?.maxTimelineItems ?? DEFAULT_MAX_TIMELINE_ITEMS;
|
||||
this.idFactory = options?.idFactory ?? (() => randomUUID());
|
||||
this.registry = options?.registry;
|
||||
this.agentControlMcp = options?.agentControlMcp;
|
||||
if (options?.clients) {
|
||||
for (const [provider, client] of Object.entries(options.clients)) {
|
||||
if (client) {
|
||||
@@ -982,6 +986,10 @@ export class AgentManager {
|
||||
normalized.model = trimmed.length > 0 ? trimmed : undefined;
|
||||
}
|
||||
|
||||
if (!normalized.agentControlMcp && this.agentControlMcp) {
|
||||
normalized.agentControlMcp = this.agentControlMcp;
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
|
||||
@@ -131,6 +131,11 @@ export type AgentRuntimeInfo = {
|
||||
extra?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type AgentControlMcpConfig = {
|
||||
url: string;
|
||||
headers?: Record<string, string>;
|
||||
};
|
||||
|
||||
export type ListPersistedAgentsOptions = {
|
||||
limit?: number;
|
||||
};
|
||||
@@ -156,6 +161,7 @@ export type AgentSessionConfig = {
|
||||
networkAccess?: boolean;
|
||||
webSearch?: boolean;
|
||||
reasoningEffort?: string;
|
||||
agentControlMcp?: AgentControlMcpConfig;
|
||||
extra?: {
|
||||
codex?: Record<string, unknown>;
|
||||
claude?: Partial<ClaudeAgentOptions>;
|
||||
|
||||
@@ -33,10 +33,6 @@ export interface AgentMcpServerOptions {
|
||||
* When set, create_agent will auto-inject this as parentAgentId.
|
||||
*/
|
||||
callerAgentId?: string;
|
||||
/**
|
||||
* Agent-control MCP URL to inject for Codex agents spawned via MCP.
|
||||
*/
|
||||
agentControlMcpUrl?: string;
|
||||
}
|
||||
|
||||
const AgentProviderEnum = z.enum(
|
||||
@@ -175,8 +171,7 @@ async function serializeSnapshotWithMetadata(
|
||||
export async function createAgentMcpServer(
|
||||
options: AgentMcpServerOptions
|
||||
): Promise<McpServer> {
|
||||
const { agentManager, agentRegistry, callerAgentId, agentControlMcpUrl } =
|
||||
options;
|
||||
const { agentManager, agentRegistry, callerAgentId } = options;
|
||||
const waitTracker = new WaitForAgentTracker();
|
||||
|
||||
const server = new McpServer({
|
||||
@@ -279,17 +274,12 @@ export async function createAgentMcpServer(
|
||||
const normalizedTitle = title?.trim() ?? null;
|
||||
// Use explicit parentAgentId if provided, otherwise default to caller agent ID
|
||||
const resolvedParentAgentId = parentAgentId ?? callerAgentId;
|
||||
const codexExtra =
|
||||
provider === "codex" && agentControlMcpUrl
|
||||
? { codex: { agentControlMcpUrl } }
|
||||
: undefined;
|
||||
const snapshot = await agentManager.createAgent({
|
||||
provider,
|
||||
cwd: resolvedCwd,
|
||||
modeId: initialMode,
|
||||
title: normalizedTitle ?? undefined,
|
||||
parentAgentId: resolvedParentAgentId,
|
||||
extra: codexExtra,
|
||||
});
|
||||
|
||||
if (initialPrompt) {
|
||||
|
||||
@@ -30,6 +30,7 @@ import type {
|
||||
AgentPromptInput,
|
||||
AgentRunOptions,
|
||||
AgentRunResult,
|
||||
AgentControlMcpConfig,
|
||||
AgentSession,
|
||||
AgentSessionConfig,
|
||||
AgentStreamEvent,
|
||||
@@ -83,20 +84,13 @@ type ClaudeAgentConfig = AgentSessionConfig & { provider: "claude" };
|
||||
|
||||
export type ClaudeContentChunk = { type: string; [key: string]: any };
|
||||
|
||||
type AgentControlMcpConfig = {
|
||||
url: string;
|
||||
headers?: Record<string, string>;
|
||||
};
|
||||
|
||||
type ClaudeAgentClientOptions = {
|
||||
defaults?: { agents?: Record<string, AgentDefinition> };
|
||||
agentControlMcp?: AgentControlMcpConfig;
|
||||
};
|
||||
|
||||
type ClaudeAgentSessionOptions = {
|
||||
defaults?: { agents?: Record<string, AgentDefinition> };
|
||||
handle?: AgentPersistenceHandle;
|
||||
agentControlMcp?: AgentControlMcpConfig;
|
||||
};
|
||||
|
||||
const DEFAULT_AGENT_CONTROL_MCP: AgentControlMcpConfig = {
|
||||
@@ -185,18 +179,15 @@ export class ClaudeAgentClient implements AgentClient {
|
||||
readonly capabilities = CLAUDE_CAPABILITIES;
|
||||
|
||||
private readonly defaults?: { agents?: Record<string, AgentDefinition> };
|
||||
private readonly agentControlMcp?: AgentControlMcpConfig;
|
||||
|
||||
constructor(options?: ClaudeAgentClientOptions) {
|
||||
this.defaults = options?.defaults;
|
||||
this.agentControlMcp = options?.agentControlMcp;
|
||||
}
|
||||
|
||||
async createSession(config: AgentSessionConfig): Promise<AgentSession> {
|
||||
const claudeConfig = this.assertConfig(config);
|
||||
return new ClaudeAgentSession(claudeConfig, {
|
||||
defaults: this.defaults,
|
||||
agentControlMcp: this.agentControlMcp,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -213,7 +204,6 @@ export class ClaudeAgentClient implements AgentClient {
|
||||
const claudeConfig = this.assertConfig(mergedConfig);
|
||||
return new ClaudeAgentSession(claudeConfig, {
|
||||
defaults: this.defaults,
|
||||
agentControlMcp: this.agentControlMcp,
|
||||
handle,
|
||||
});
|
||||
}
|
||||
@@ -254,7 +244,6 @@ class ClaudeAgentSession implements AgentSession {
|
||||
|
||||
private readonly config: ClaudeAgentConfig;
|
||||
private readonly defaults?: { agents?: Record<string, AgentDefinition> };
|
||||
private readonly agentControlMcp?: AgentControlMcpConfig;
|
||||
private query: Query | null = null;
|
||||
private input: Pushable<SDKUserMessage> | null = null;
|
||||
private claudeSessionId: string | null;
|
||||
@@ -283,7 +272,6 @@ class ClaudeAgentSession implements AgentSession {
|
||||
) {
|
||||
this.config = config;
|
||||
this.defaults = options?.defaults;
|
||||
this.agentControlMcp = options?.agentControlMcp;
|
||||
const handle = options?.handle;
|
||||
this.claudeSessionId = handle?.sessionId ?? handle?.nativeHandle ?? null;
|
||||
this.pendingLocalId = this.claudeSessionId ?? `claude-${randomUUID()}`;
|
||||
@@ -575,7 +563,8 @@ class ClaudeAgentSession implements AgentSession {
|
||||
};
|
||||
|
||||
// Always include the agent-control MCP server so agents can launch other agents
|
||||
const agentControlConfig = this.agentControlMcp ?? DEFAULT_AGENT_CONTROL_MCP;
|
||||
const agentControlConfig =
|
||||
this.config.agentControlMcp ?? DEFAULT_AGENT_CONTROL_MCP;
|
||||
const agentControlUrl = this.managedAgentId
|
||||
? appendCallerAgentId(agentControlConfig.url, this.managedAgentId)
|
||||
: agentControlConfig.url;
|
||||
|
||||
@@ -843,7 +843,7 @@ describe("CodexAgentClient (SDK integration)", () => {
|
||||
provider: "codex",
|
||||
cwd,
|
||||
modeId: "full-access",
|
||||
extra: { codex: { agentControlMcpUrl: mcpServer.url } },
|
||||
agentControlMcp: { url: mcpServer.url },
|
||||
};
|
||||
let session: Awaited<ReturnType<typeof client.createSession>> | null = null;
|
||||
try {
|
||||
|
||||
@@ -44,7 +44,6 @@ import { getOrchestratorModeInstructions } from "../orchestrator-instructions.js
|
||||
type CodexAgentConfig = AgentSessionConfig & { provider: "codex" };
|
||||
|
||||
type CodexExtraConfig = {
|
||||
agentControlMcpUrl?: string;
|
||||
developerInstructions?: string;
|
||||
};
|
||||
|
||||
@@ -269,11 +268,7 @@ function detectSystemCodexPath(): string | undefined {
|
||||
}
|
||||
|
||||
function resolveAgentControlMcpUrl(config: CodexAgentConfig): string | null {
|
||||
const extras = config.extra?.codex as CodexExtraConfig | undefined;
|
||||
if (!extras || typeof extras !== "object") {
|
||||
return null;
|
||||
}
|
||||
const baseUrl = extras.agentControlMcpUrl;
|
||||
const baseUrl = config.agentControlMcp?.url;
|
||||
if (!baseUrl || typeof baseUrl !== "string") {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -93,17 +93,16 @@ async function main() {
|
||||
const agentRegistry = new AgentRegistry();
|
||||
const agentManager = new AgentManager({
|
||||
clients: {
|
||||
claude: new ClaudeAgentClient({
|
||||
agentControlMcp: {
|
||||
url: agentMcpUrl,
|
||||
...(agentMcpAuthHeader
|
||||
? { headers: { Authorization: agentMcpAuthHeader } }
|
||||
: {}),
|
||||
},
|
||||
}),
|
||||
claude: new ClaudeAgentClient(),
|
||||
codex: new CodexAgentClient(),
|
||||
},
|
||||
registry: agentRegistry,
|
||||
agentControlMcp: {
|
||||
url: agentMcpUrl,
|
||||
...(agentMcpAuthHeader
|
||||
? { headers: { Authorization: agentMcpAuthHeader } }
|
||||
: {}),
|
||||
},
|
||||
});
|
||||
|
||||
attachAgentRegistryPersistence(agentManager, agentRegistry);
|
||||
@@ -123,7 +122,6 @@ async function main() {
|
||||
agentManager,
|
||||
agentRegistry,
|
||||
callerAgentId,
|
||||
agentControlMcpUrl: agentMcpUrl,
|
||||
});
|
||||
|
||||
const transport = new StreamableHTTPServerTransport({
|
||||
|
||||
@@ -71,6 +71,12 @@ const AgentSessionConfigSchema = z.object({
|
||||
networkAccess: z.boolean().optional(),
|
||||
webSearch: z.boolean().optional(),
|
||||
reasoningEffort: z.string().optional(),
|
||||
agentControlMcp: z
|
||||
.object({
|
||||
url: z.string(),
|
||||
headers: z.record(z.string()).optional(),
|
||||
})
|
||||
.optional(),
|
||||
extra: z
|
||||
.object({
|
||||
codex: z.record(z.unknown()).optional(),
|
||||
|
||||
@@ -1256,8 +1256,10 @@ export class Session {
|
||||
);
|
||||
|
||||
try {
|
||||
const sessionConfig = this.withCodexAgentControl(
|
||||
await this.buildAgentSessionConfig(config, git, worktreeName)
|
||||
const sessionConfig = await this.buildAgentSessionConfig(
|
||||
config,
|
||||
git,
|
||||
worktreeName
|
||||
);
|
||||
const snapshot = await this.agentManager.createAgent(sessionConfig);
|
||||
this.setCachedTitle(snapshot.id, null);
|
||||
@@ -1352,13 +1354,9 @@ export class Session {
|
||||
`[Session ${this.clientId}] Resuming agent ${handle.sessionId} (${handle.provider})`
|
||||
);
|
||||
try {
|
||||
const normalizedOverrides = this.withCodexAgentControl(
|
||||
overrides ?? {},
|
||||
handle.provider
|
||||
);
|
||||
const snapshot = await this.agentManager.resumeAgent(
|
||||
handle,
|
||||
normalizedOverrides
|
||||
overrides
|
||||
);
|
||||
this.setCachedTitle(snapshot.id, null);
|
||||
await this.agentManager.primeAgentHistory(snapshot.id);
|
||||
@@ -1419,11 +1417,11 @@ export class Session {
|
||||
`Agent ${agentId} cannot be refreshed because it lacks persistence`
|
||||
);
|
||||
}
|
||||
const overrides = this.withCodexAgentControl(
|
||||
snapshot = await this.agentManager.resumeAgent(
|
||||
handle,
|
||||
buildConfigOverrides(record),
|
||||
record.provider
|
||||
agentId
|
||||
);
|
||||
snapshot = await this.agentManager.resumeAgent(handle, overrides, agentId);
|
||||
this.setCachedTitle(agentId, null);
|
||||
}
|
||||
await this.agentManager.primeAgentHistory(agentId);
|
||||
@@ -1534,37 +1532,6 @@ export class Session {
|
||||
};
|
||||
}
|
||||
|
||||
private withCodexAgentControl<T extends Partial<AgentSessionConfig>>(
|
||||
config: T,
|
||||
providerOverride?: AgentProvider
|
||||
): T {
|
||||
const provider =
|
||||
providerOverride ?? (config as AgentSessionConfig).provider;
|
||||
if (provider !== "codex") {
|
||||
return config;
|
||||
}
|
||||
const agentControlMcpUrl = this.agentMcpConfig?.agentMcpUrl;
|
||||
if (!agentControlMcpUrl) {
|
||||
return config;
|
||||
}
|
||||
const extra = (config.extra ?? {}) as AgentSessionConfig["extra"];
|
||||
const codexExtra =
|
||||
extra?.codex && typeof extra.codex === "object"
|
||||
? { ...extra.codex }
|
||||
: {};
|
||||
if (typeof (codexExtra as Record<string, unknown>).agentControlMcpUrl !== "string") {
|
||||
(codexExtra as Record<string, unknown>).agentControlMcpUrl =
|
||||
agentControlMcpUrl;
|
||||
}
|
||||
return {
|
||||
...config,
|
||||
extra: {
|
||||
...extra,
|
||||
codex: codexExtra,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private async handleGitRepoInfoRequest(
|
||||
msg: Extract<SessionInboundMessage, { type: "git_repo_info_request" }>
|
||||
): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user