fix(chat): name the managed-profile CLI and the dev-only labs in agent guidance (#20236)

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agent Chat answers a person's requests with a chat agent that reads
the API catalog and the chat guidance
> - Two facts are missing from what that agent reads
> - Claude Managed Agent profiles have no web page. A board user creates
them with the `paperclipai managed-agent setup` CLI command. The catalog
does not say this, so agents tell people to use "the Paperclip web app"
> - Developer labs, such as `/<prefix>/dev/task-chat-lab`, exist only in
a development build. Nothing says this, so agents probe the production
bundle in a headless browser for many steps and then ask the person for
a "dev-mode URL"
> - This pull request adds the two facts where the agent already looks
> - The benefit is a correct next step for the person, in fewer agent
steps

## Linked Issues or Issue Description

**What happened**
An agent was asked to "Set up an Anthropic managed agent for this
company". It correctly said the action is board-only. Then it told the
board user to create the profile "in the Paperclip web app". That page
does not exist.
An agent was asked to "Open the task-chat interaction lab from this
link". The link went to a production instance. The agent used 56 steps,
mostly bash and headless Chrome, and did not tell the person that the
lab runs only under `pnpm dev` in their own checkout.

**Expected behavior**
For managed agents, the agent names the CLI command that a board user
runs. It also says that no web page exists.
For developer labs, the agent says the lab is development-only. It tells
the person to run `pnpm dev` in their own checkout and open the route
there. It does not probe the instance.

**Steps to reproduce**
1. In Agent Chat on a production build, ask "Set up an Anthropic managed
agent for this company".
2. Read the reply. It points to a web page that does not exist.
3. Attach the link `http://<host>/<prefix>/dev/task-chat-lab` and ask
"Open the task-chat interaction lab from this link".
4. Look at the run. The agent probes the bundle and does not give the
`pnpm dev` path.

## What Changed

- `server/src/routes/openapi.ts`: the GET and POST managed-agent-profile
routes now say that no web page exists. The POST description now names
`paperclipai managed-agent setup --company-id <companyId> --profile-key
<key> --display-name <name> --api-key-secret-id <secretId>
--acknowledge-retention`, the `ANTHROPIC_API_KEY` requirement, the
`--probe` option and the Secrets page.
- `server/src/services/agent-conversations.ts`: the chat directive has a
new short paragraph. It says some developer labs, including the
task-chat lab, are registered only in a development build, it tells
agents not to probe, and it gives the `pnpm dev` path with the route. It
also says the design guide and ux-lab pages exist in every build, so
agents open those normally.
- Tests for both texts in `openapi-routes.test.ts` and
`agent-conversations.test.ts`.

## Verification

- `cd server && pnpm exec vitest run
src/__tests__/openapi-routes.test.ts
src/__tests__/agent-conversations.test.ts
src/__tests__/board-access-denial.test.ts
src/__tests__/codex-local-execute.test.ts`: 142 tests pass.
- After merging master (keeping master's ready-made teams paragraph) and
the review fixes: `cd server && pnpm exec vitest run
src/__tests__/agent-conversations.test.ts
src/__tests__/openapi-routes.test.ts
src/services/native-runtime/runner-api.test.ts`: 2164 tests pass.
- The change is also cherry-picked onto the eval branch
`chat-eval/case-keyword-graders`. The live Runner E2E cells for
`agents.managed-profile` and `developer-labs.gated-lab` run there.

## Risks

- Low risk. The change is text in the API catalog and in the chat
guidance. It does not change behavior or permissions.
- The chat directive is slightly longer, so each chat prompt uses a few
more tokens.

## Model Used

- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), with tool use and code
execution in Claude Code.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [ ] I will address all Greptile and reviewer comments before
requesting merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DottaandClaude Opus 5.5 authored and GitHub committed 2026-11-09 07:15:16 -06:00
1 parent 2b461b2f58
commit ad440d4e91
4 files changed
+23 -1

No files matched your search

@@ -1058,6 +1058,15 @@ describe("chat prompt policy", () => {
expect(prompt).toContain("/<prefix>/agents/<agentId>/runs/<runId>");
});
it("says the task-chat lab exists only under pnpm dev in the person's own checkout", () => {
expect(AGENT_CHAT_DIRECTIVE).toContain("Some developer labs, including the task-chat interaction lab at /<prefix>/dev/task-chat-lab, are registered only in a development build of the UI");
expect(AGENT_CHAT_DIRECTIVE).not.toContain("Developer labs, such as");
expect(AGENT_CHAT_DIRECTIVE).toContain("the design guide and the ux-lab pages, are registered in every build");
expect(AGENT_CHAT_DIRECTIVE).toContain("running pnpm dev in their own Paperclip checkout and visiting /<prefix>/dev/task-chat-lab");
const prompt = buildPaperclipTaskMarkdown({ issue: { id: "chat", identifier: null, title: "Chat", workMode: "ask", conversationAgentId: "agent" } });
expect(prompt).toContain("/<prefix>/dev/task-chat-lab");
});
it("routes a person's own profile change to their profile settings instead of an agent write", () => {
expect(AGENT_CHAT_DIRECTIVE).toContain("only they can change them in their profile settings at /<prefix>/company/settings/instance/profile");
expect(AGENT_CHAT_DIRECTIVE).toContain("link that page instead of attempting the change");
@@ -331,6 +331,16 @@ function loadSpecRoutes() {
}
describe("openapi routes", () => {
it("tells agents managed-agent profiles are created with the CLI, not a web page", () => {
const route = buildOpenApiSpec().paths["/api/companies/{companyId}/managed-agent-profiles"];
for (const operation of [route.get, route.post]) {
expect(operation.description).toContain("Board only");
expect(operation.description).toContain("There is no web page");
}
expect(route.post.description).toContain("paperclipai managed-agent setup --company-id <companyId>");
expect(route.post.description).toContain("--acknowledge-retention");
});
it("documents the export include body so agents can request projects and skills", () => {
const spec = buildOpenApiSpec();
for (const path of ["/api/companies/{companyId}/exports", "/api/companies/{companyId}/exports/preview"]) {
+2 -1
View File
@@ -7595,6 +7595,7 @@ registry.registerPath({
path: "/api/companies/{companyId}/managed-agent-profiles",
tags: ["agents"],
summary: "List Claude Managed Agent profiles for a company",
description: "Board only: agent credentials get 403. There is no web page for Claude Managed Agent profiles; see the POST route for how a board user creates one.",
request: { params: z.object({ companyId: z.string() }) },
responses: { 200: r.ok(), 401: r.unauthorized, 403: r.forbidden },
});
@@ -7604,7 +7605,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.",
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. There is no web page for this: the board user runs the CLI command `paperclipai managed-agent setup --company-id <companyId> --profile-key <key> --display-name <name> --api-key-secret-id <secretId> --acknowledge-retention` with ANTHROPIC_API_KEY set in their shell (add --probe for a read-only qualification check first). It creates or adopts the limited Anthropic Agent and Environment, qualifies them and calls this route with board credentials. The secret itself is added on the company Secrets page, /<prefix>/company/settings/secrets.",
request: {
params: z.object({ companyId: z.string() }),
body: jsonBody(
@@ -119,6 +119,8 @@ When a reply lists many tasks, write each task identifier in full, such as RUN-1
A person's own display name and avatar belong to their account, and only they can change them in their profile settings at /<prefix>/company/settings/instance/profile. Agent credentials cannot edit a person's profile, and renaming an agent, a member record or the company does not change it. When the person asks to change their own name or avatar, link that page instead of attempting the change.
Some developer labs, including the task-chat interaction lab at /<prefix>/dev/task-chat-lab, are registered only in a development build of the UI. A production or hosted instance has no task-chat lab page and no setting turns one on, so do not probe its bundle or try to open the lab with a browser. Tell the person the task-chat lab is development-only and that they can open it by running pnpm dev in their own Paperclip checkout and visiting /<prefix>/dev/task-chat-lab on that dev server. Other lab pages, such as the design guide and the ux-lab pages, are registered in every build, so open those normally.
Ready-made teams are installable packages in Paperclip's teams catalog, not agents already in this company. When the person asks whether a ready-made team exists, or refers to one, list the catalog with GET /api/teams/catalog and choose by its descriptions and roles, since teams are not named after every kind of project. When you recommend a team, name its roles (agentSlugs), every required skill (requiredSkills, by its ref) and every configuration input (envInputs), and say that a catalog listing does not mean the team's accounts are configured. The catalog is the only source of ready-made teams: do not look for others in package registries, repositories or the web. A team chosen earlier in this chat may be recorded in its task context (get_task_context). POST /api/companies/{companyId}/teams/catalog/{catalogId}/preview shows what an install would add and creates no resources. Installing needs a board user or an agent with permission to create agents. The web app has no teams catalog page, so do not link one: name the team and its id, and when you cannot install, tell the person that a board user can run paperclipai teams install <team id> --company-id <companyId>. After an install, link the installed agents and projects at their own pages.
This conversation and your credentials belong to one company. A task identifier whose prefix differs from this conversation's prefix belongs to another company or to none, and you cannot read it here, even when the person can open it themselves: a lookup that is not found does not mean the task does not exist. Do not search for it further, guess its content or copy anything from it. Say that this chat can only work in this company, and that to use that task the person can switch to its company and open a chat there. Link the task at /<its prefix>/issues/<identifier>, taking the prefix from the identifier the person gave; this is the one link that does not use this conversation's prefix.