diff --git a/docs/hub.md b/docs/hub.md index b4df317e76..938395c667 100644 --- a/docs/hub.md +++ b/docs/hub.md @@ -44,6 +44,15 @@ fall back to creating a fresh agent when continuation was requested. Hub owns conversation keys, trigger policy, execution records, and the mapping to workspace/agent IDs. None of these routing concepts are part of the generic daemon RPCs. Creation accepts ordinary provider configuration, exact MCP tool policy, environment, and workspace/worktree selection. + +`server_info.features.optionalMcpSupport` advertises optional MCP launch requirements. +Hub sends `toolPolicy.enforcement: "if-supported"` and +`mcpServerRequirements: { hub: "optional" }` only when this flag is true. +Without it, Hub preserves its existing strict tool-policy request. Available +providers remain selectable; providers lacking strict approval support need an +updated daemon to launch from Hub. Required external/plugin servers and explicitly +required tool policies keep their existing refusal semantics. + Private session configuration is persisted for recovery and is not exposed in agent snapshots. Provider controls remain provider-native; see [providers.md](providers.md). diff --git a/packages/server/src/server/agent/agent-manager.test.ts b/packages/server/src/server/agent/agent-manager.test.ts index 3765263b9e..da3f9bb2c2 100644 --- a/packages/server/src/server/agent/agent-manager.test.ts +++ b/packages/server/src/server/agent/agent-manager.test.ts @@ -19,6 +19,7 @@ import { type AgentManagerEvent, type ManagedAgent, } from "./agent-manager.js"; +import { buildProviderRegistry } from "./provider-registry.js"; import { AgentStorage } from "./agent-storage.js"; import { InMemoryAgentTimelineStore } from "./agent-timeline-store.js"; import { toAgentPayload } from "./agent-projections.js"; @@ -445,7 +446,7 @@ class EnvProbeAgentClient extends TestAgentClient { } class TestAgentSession implements AgentSession { - readonly provider = "codex" as const; + readonly provider: AgentProvider; readonly capabilities = TEST_CAPABILITIES; readonly id = randomUUID(); private runtimeModel: string | null = null; @@ -453,7 +454,9 @@ class TestAgentSession implements AgentSession { private turnIdCounter = 0; private interrupted = false; - constructor(private readonly config: AgentSessionConfig) {} + constructor(private readonly config: AgentSessionConfig) { + this.provider = config.provider; + } async run(): Promise { return { @@ -3822,9 +3825,13 @@ test("resumeAgentFromPersistence drops stored internal paseo MCP when runtime in expect(snapshot.config.mcpServers).toBeUndefined(); }); -test.each([true, false])( - "createAgent permits optional approval with MCP capability %s", - async (supportsMcp) => { +test.each([ + { provider: "pi", supportsMcp: true }, + { provider: "pi", supportsMcp: false }, + { provider: "codex", supportsMcp: false }, +])( + "createAgent permits optional approval for $provider with MCP capability $supportsMcp", + async ({ provider, supportsMcp }) => { const workdir = mkdtempSync(join(tmpdir(), "agent-manager-test-")); const storagePath = join(workdir, "agents"); const storage = new AgentStorage(storagePath, logger); @@ -3838,11 +3845,20 @@ test.each([true, false])( } } - const client = new CaptureClient(); + const client = new CaptureClient(provider); const manager = new AgentManager({ clients: { - codex: client, + [provider]: client, }, + providerDefinitions: + provider === "pi" + ? { + pi: { + enabled: true, + applyToolPolicy: buildProviderRegistry(logger).pi.applyToolPolicy, + }, + } + : undefined, registry: storage, logger, mcpBaseUrl: "http://127.0.0.1:6767/mcp/agents", @@ -3851,7 +3867,7 @@ test.each([true, false])( const snapshot = await manager.createAgent( { - provider: "codex", + provider, mcpServerRequirements: { paseo: "optional" }, cwd: workdir, toolPolicy: {