Give a duplicated Bot in the box the instructions it was copied from - #355
Merged
davidmckayv merged 2 commits intoSep 4, 2026
Merged
Conversation
CopilotKit#328 fixed the half of duplicate that a coworker with its own endpoint saw. This is the other half. A coworker that runs on this deployment's own Bot has no endpoint. Its configuration is `{ systemPrompt }`, and that prompt is the whole of what makes it that coworker. Duplicate rebuilt every copy from `source.endpoint` alone and wrote `type: "remote_ag_ui"` flat, so for one of these the endpoint read came back null, the copy fell through to the managed Bot, and the prompt was dropped on the floor. What comes back is the failure this repository already has a paragraph about. The copy looks identical on every screen and its entire instruction becomes standingRoleMessage - see the note above that function in copilot.ts, which names the compliance Bot that answered a filing question with invented thresholds because one sentence of role description was all that reached it. The default tenant package ships two built_in coworkers, General Assistant and Knowledge, and Knowledge's prompt is a careful do-not-fabricate instruction with the reasoning written out beside it in the package. Duplicate it and you get a coworker with the name, the title, the avatar, and none of that. The type is carried now, not only the configuration. A copy written as remote_ag_ui also could not be granted handoff for the rest of its life: agentRunsHere and botsReachableFrom both key on agents.type == "built_in", so the original could hand work on and its copy silently could not. And a built_in source no longer needs a managed Bot to fall back to. On a deployment with none, copying one used to be refused with advice to give the coworker an AG-UI endpoint it was never supposed to have - the same wrong refusal CopilotKit#328 removed for a coworker that brought its own endpoint. The decision is a pure function so it can be tested without a database. The stored row is read inside the transaction and after the access check, rather than widening AgentProfile: the DTO every surface receives should not start carrying a Bot's instructions. `auth` is still deliberately not copied. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
kevin9327
requested review from
MikeRyanDev,
davidmckayv,
guidovizoso and
tylerslaton
as code owners
September 3, 2026 22:22
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
# Conflicts: # CHANGELOG.md
davidmckayv
approved these changes
Sep 4, 2026
davidmckayv
left a comment
Contributor
There was a problem hiding this comment.
Reviewed diff against current main; resolved CHANGELOG keep-both; validated locally (format + composed test run). CI green.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
#328 fixed the half of
duplicatethat a coworker with its own endpoint saw. This is the other half,and it is the worse one.
A coworker that runs on this deployment's own Bot has no endpoint. Its
agents.configurationis{ systemPrompt }(server/src/tenant-package.ts:374-385), and that prompt is the whole of whatmakes it that coworker.
duplicaterebuilt the copy fromsource.endpointalone and wrotetype: "remote_ag_ui"flat:endpointOfreturns null for abuilt_inrow, so the copy fell through to the managed Bot and theprompt went nowhere.
What comes back is a failure this repository already has a paragraph about. The copy is
remote_ag_ui, soregisteredAgentFromRowtakes the remote branch and its entire instructionbecomes
standingRoleMessage— whose own comment inserver/src/copilot.ts:113-119says exactlywhat that costs:
This is not a corner of the product. The default tenant package —
examples/fintech, the valueTENANT_PACKAGE_DIRfalls back to — ships twobuilt_incoworkers,General AssistantandKnowledge. Both arevisibility: public, so every signed-in person can see them, and Duplicate isone item in the coworker dialog #317 just added.
Knowledge's prompt is a careful do-not-fabricateinstruction, with the reasoning for every clause written out beside it in the package. Duplicate it
and you get a coworker with the name, the title, the avatar, and none of that — and no screen says
so, which is the same "looked identical on every screen and answered from a different process"
#328's changelog describes.
Two more consequences of the type flip, both silent:
agentRunsHere(plugins/store.ts:2195-2204) andbotsReachableFrom(:2360-2372) both key onagents.type == "built_in", so the Handoff sectionreports
grantable: falseon the copy while the original could hand work on.built_incoworker was refused withThis deployment has no managed Bot. Give the coworker its own AG-UI endpoint.— advice about anendpoint it was never supposed to have. That is the same wrong refusal Give a duplicated coworker the endpoint it was copied from #328 removed for a coworker
that brought its own endpoint.
The fix.
runForDuplicatedecides what the copy runs on from what the original ran on: abuilt_insource with a prompt stays abuilt_inwith that prompt; a source with an endpoint keepsthat endpoint; only a source with neither falls back to the managed Bot, and
null— the case thatraises
ManagedAgentUnavailableError— is now reachable only when there is genuinely nothing to runthe copy on.
It is a pure function so it can be tested without a database. The stored row is read inside the same
transaction and after the access check, rather than widening
AgentProfile: the DTO every surfacereceives should not start carrying a Bot's instructions.
authis still deliberately not copied —two coworkers sharing one vault reference would mean rotating either one's key silently changed the
other's.
Where it runs
typeandconfigurationare columnson
agents, as they already were.being copied and the deployment's managed-agent configuration, both of which every replica
reads the same way.
select type, configuration from agents where id = ?inside the existing
database.transaction, on the same connection, afterfindAccessibleProfilehas already taken its read of that agent in the same transaction. Itcannot see a row the caller may not see, and it cannot see a different row from the profile
read beside it.
Boundary and audit
bot.duplicatedrow theroute already writes is unchanged. One refusal becomes less frequent — a
built_insource nolonger raises
ManagedAgentUnavailableErroron a deployment with no managed Bot — and thatrefusal was never audited on either side of this change.
from the request.
Proof
server/tests/duplicate-run.test.ts— 8 tests, no database needed. Fail-before confirmed byreplacing
runForDuplicate's body with the previous endpoint-only selection:With the fix:
Gates:
server/tests/agent-profile-store.integration.test.ts— where #328's four duplicate assertions live— needs a live Postgres and fails here with
Connection closed, so it was read and not run. It isthe right place for an end-to-end assertion that the copied
agentsrow keepstype: "built_in"andits
systemPrompt; say the word and I will add one there too.Changelog
Duplicating a Bot in the box keeps its instructions, underUnreleased.