diff --git a/server/src/__tests__/board-access-denial.test.ts b/server/src/__tests__/board-access-denial.test.ts index 98b20c19e37..e217af86064 100644 --- a/server/src/__tests__/board-access-denial.test.ts +++ b/server/src/__tests__/board-access-denial.test.ts @@ -5,6 +5,7 @@ import { describe, expect, it } from "vitest"; import { errorHandler } from "../middleware/index.js"; import { assertBoard, assertBoardOrgAccess, BOARD_ACCESS_REQUIRED_CODE } from "../routes/authz.js"; import { AGENT_CHAT_DIRECTIVE } from "../services/agent-conversations.js"; +import { buildOpenApiDocument } from "../routes/openapi.js"; const COMPANY_ID = "10000000-0000-4000-8000-000000000001"; const AGENT_ID = "20000000-0000-4000-8000-000000000002"; @@ -71,4 +72,11 @@ describe("board-only denial", () => { expect(AGENT_CHAT_DIRECTIVE).toContain("tell the person which control in the Paperclip web app to use, link its page when you know the route"); expect(AGENT_CHAT_DIRECTIVE).not.toContain("use permitted discovery to identify the supported operator path"); }); + + it("says in the API catalog who sets up a Claude Managed Agent profile", () => { + const post = buildOpenApiDocument().paths["/api/companies/{companyId}/managed-agent-profiles"].post; + expect(post["x-paperclip-authorization"]).toEqual({ actor: "board" }); + expect(post.description).toContain("Board only: agent credentials get 403"); + expect(post.description).toContain("explicitly acknowledges stateful retention"); + }); }); diff --git a/server/src/adapters/registry.ts b/server/src/adapters/registry.ts index d5b38bea0b7..a6f822946fc 100644 --- a/server/src/adapters/registry.ts +++ b/server/src/adapters/registry.ts @@ -644,7 +644,7 @@ const paperclipRunnerAdapter: ServerAdapterModule = { ) : buildNpmRuntimeCommandSpec(config, "codex", "@openai/codex@0.160.0"), agentConfigurationDoc: - "# Paperclip Runner\n\nAdapter: paperclip_runner\n\nRuns Codex, OpenCode, Claude Managed, AWS AgentCore, or ACPX Claude/Grok Build/Cursor/Pi through the Rust Paperclip runner and authenticated PRP transport. Pi accepts an explicit provider/model ID and uses the company credentials bound to the agent environment. GitHub Copilot requires a saved GitHub token and an explicitly selected available model. Managed providers use company-scoped qualified profiles, explicit retention acknowledgement, and spend limits.\n", + "# Paperclip Runner\n\nAdapter: paperclip_runner\n\nRuns Codex, OpenCode, Claude Managed, AWS AgentCore, or ACPX Claude/Grok Build/Cursor/Pi through the Rust Paperclip runner and authenticated PRP transport. Pi accepts an explicit provider/model ID and uses the company credentials bound to the agent environment. GitHub Copilot requires a saved GitHub token and an explicitly selected available model. Managed providers use company-scoped qualified profiles, explicit retention acknowledgement, and spend limits. A board operator creates each Claude Managed (Anthropic managed agent) profile through POST /api/companies/{companyId}/managed-agent-profiles; agent credentials cannot create, qualify or list one.\n", getConfigSchema: () => ({ fields: [ { diff --git a/server/src/routes/openapi.ts b/server/src/routes/openapi.ts index e4c2f54bb37..068495bba5e 100644 --- a/server/src/routes/openapi.ts +++ b/server/src/routes/openapi.ts @@ -7570,6 +7570,7 @@ registry.registerPath({ path: "/api/companies/{companyId}/managed-agent-profiles", tags: ["agents"], summary: "Create or operator-attest a Claude Managed Agent profile", + description: "Board only: agent credentials get 403. A Claude Managed Agent runs on Anthropic's managed-agents beta. A board operator creates the company profile with an Anthropic API-key secret and a limited environment (no hosts, no packages), attests its qualification and explicitly acknowledges stateful retention before it can be enabled.", request: { params: z.object({ companyId: z.string() }), body: jsonBody(