GeezerVison is showing github.com — feat: add agent-plugins experiment and Agent API v2.16 plugin messages by jscottmiller · Pull Request #29320 · coder/coder
Conversation
Linear: CODAGT-1101
tl;dr: Adds the agent-plugins experiment and the Agent API v2.16 messages that carry Agent Plugins data from the agent to coderd. First of eight stacked PRs adding read-only Agent Plugins v1.0.0 support.
A plugin is a directory with a plugin.json manifest, skills under skills/, and an optional mcp.json. Across the stack, the agent discovers plugins in plugins/ and .agents/plugins/ under its scan roots (for example ~/.coder/plugins/<name>), chatd lists their skills with plugin attribution, the agent runs the MCP servers of plugins in the workspace working directory, and the Agents page shows plugin attribution in the chat context. Hooks, commands, agents, install flows, and org-managed plugins are out of scope. Background: research/agent-plugins-standard.md (internal).
This PR adds codersdk.ExperimentAgentPlugins and a PluginBody oneof variant on ContextResource (tag 18) that carries the validated manifest fields and nests the plugin's skills and MCP servers. Manifest gains agent_plugins_enabled (field 21), which coderd sets when the experiment is enabled; the agent discovers plugins only then. coderd stores each nested resource as its own row with the plugin's name in a new plugin_name column (migration 000633), which the hydrate, MCP sync, and re-pin queries copy. With the experiment off, PushContextState drops plugin resources and everything nested in them. Otherwise it rejects pushes with plugin names that fail workspacesdk.ValidatePluginName (the spec's name grammar, shared by agent and coderd), OK plugins without a name or with a duplicate name, a non-OK plugin that nests resources, nested entries of the wrong kind, plugin skill sources outside <plugin source>/skills/, and plugin MCP servers with an empty server_name or a source other than <plugin name>/<server_name>. Nested resources count toward the 1000-resource cap.
Agents now dial v2.16, so a new agent needs a coderd that supports it, as with every Agent API minor. Turning the experiment off does not remove plugin data already pinned to existing chats.
PR map
main..4b0cfc9657
| Category |
Files |
+ |
- |
| prod (backend) |
15 |
342 |
78 |
| test (backend) |
9 |
534 |
3 |
| migrations |
2 |
14 |
0 |
| generated (backend) |
7 |
1216 |
1027 |
| generated (frontend) |
1 |
2 |
0 |
| Total |
34 |
2108 |
1108 |
coderd/agentapi/context.go (+211/-51, API): drops plugin resources when the experiment is off; otherwise validates plugin names and nesting, and stores each nested skill and MCP server as its own row with the plugin's name.
agent/proto/agent.proto (+39/-9, API): PluginBody with nested skills and mcp_servers, and Manifest.agent_plugins_enabled.
codersdk/workspacesdk/frontmatter.go (+31/-0): ValidatePluginName and MaxPluginNameLength (manifest name grammar).
coderd/database/queries/chats.sql, workspaceagentcontext.sql, migration 000633 (+28/-7, DB): the plugin_name column on both context resource tables, copied by the hydrate, MCP sync, and re-pin queries.
codersdk/agentsdk/agentsdk.go, agent/agent.go (+18/-4, API): Manifest.AgentPluginsEnabled and ConnectRPC216WithRole; the agent dials v2.16.
coderd/agentapi/api.go, manifest.go (+4): the manifest flag follows the experiment.
tailnet/proto/version.go (+8/-1, API): Agent API v2.16.
codersdk/deployment.go (+4, API): the agent-plugins experiment.
This PR was generated by Coder Agents on behalf of @jscottmiller.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Sep 15, 2026
jscottmiller
changed the title
scott/agent plugins/1 api
feat: add agent-plugins experiment and Agent API v2.13 plugin messages
Sep 15, 2026
jscottmiller
changed the title
feat: add agent-plugins experiment and Agent API v2.13 plugin messages
feat: add Agent API v2.13 plugin messages
Sep 15, 2026
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
jscottmiller
changed the title
feat: add Agent API v2.13 plugin messages
feat: add agent-plugins experiment and Agent API v2.13 plugin messages
Sep 24, 2026
coder-agents-review[bot] reacted with rocket emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
Chat: Review posted | View chat
Requested: 2026-10-07 18:56 UTC by @jscottmiller
Review history
- R1 (2026-10-01): 12 reviewers, 2 Nit, 7 P3, 8 P4, COMMENT. Review
- R2 (2026-10-02): 13 reviewers, 3 Nit, 9 P3, 12 P4, COMMENT. Review
- R3 (2026-10-02): 11 reviewers, 5 Nit, 12 P3, 16 P4, COMMENT. Review
- R4 (2026-10-03): 12 reviewers, 8 Nit, 12 P3, 18 P4, COMMENT. Review
- R5 (2026-10-03): 11 reviewers, 10 Nit, 13 P3, 24 P4, COMMENT. Review
- R6 (2026-10-06): 12 reviewers, 11 Nit, 14 P3, 32 P4, COMMENT. Review
- R7 (2026-10-06): 13 reviewers, 12 Nit, 17 P3, 39 P4, COMMENT. Review
- R8 (2026-10-06): 13 reviewers, 15 Nit, 19 P3, 45 P4, COMMENT. Review
- R9 (2026-10-07): 14 reviewers, 17 Nit, 19 P3, 53 P4, COMMENT. Review
deep-review v0.13.0 | Round 9 | b4fb790..ff177d1
Last posted: Round 9, 89 findings (19 P3, 53 P4, 17 Nit), COMMENT. Review
Finding inventory
Finding inventory: PR #29320
Findings
| # |
Sev |
Status |
Location |
Summary |
Round |
Reviewer |
Posted |
| CRF-1 |
P3 |
Author fixed (10dbcb0) |
coderd/x/chatd/context_prompt.go:403 |
chatd doc comments still call plugin kind "reserved" |
R1 |
Netero |
Yes |
| CRF-2 |
P4 |
Author fixed (10dbcb0) |
coderd/database/dump.sql:3576 |
body_kind column comments still say plugin reserved for Claude Code RFC |
R1 |
Netero |
Yes |
| CRF-3 |
P4 |
Author fixed (10dbcb0) |
codersdk/workspacesdk/frontmatter.go:26 |
ValidatePluginName has no caller; coderd stores unvalidated names |
R1 |
Netero |
Yes |
| CRF-4 |
P4 |
Author fixed (2be2785) |
codersdk/agentsdk/agentsdk.go:371 |
ConnectRPC213WithRole and DRPCAgentClient213 unused |
R1 |
Netero |
Yes |
| CRF-5 |
P4 |
Author fixed (10dbcb0) |
codersdk/agentsdk/convert.go:70 |
No round-trip test for PluginsSupported |
R1 |
Netero |
Yes |
| CRF-6 |
Note |
Note (body) |
agent/agent.go:1183 |
PR description says agents dial v2.10; they dial v2.11 |
R1 |
Netero |
No |
| CRF-7 |
Note |
Note (body) |
CI |
gen/offlinedocs failures come from base commit |
R1 |
Netero |
No |
| CRF-8 |
P3 |
Author fixed (10dbcb0) |
coderd/agentapi/context.go:396 |
PushContextState stores plugin data with experiment off; gate enforced only by agent |
R1 |
Melody P3, Ryosuke P3, Kurapika P3, Mafuuu P4, Pariston Note, Mafu-san Note |
Yes |
| CRF-9 |
P3 |
Author fixed (10dbcb0) |
agent/proto/agent.proto:558 |
ContextResource comment omits plugin kind and drops the unknown-variant rejects-whole-push constraint |
R1 |
Gon P2, Mafuuu P4, Leorio P4, Melody Nit |
Yes |
| CRF-10 |
P3 |
Author fixed (10dbcb0) |
codersdk/agentsdk/agentsdk.go:138 |
PluginsSupported doc states a false condition and omits the agent contract |
R1 |
Gon P2, Leorio P3 |
Yes |
| CRF-11 |
P3 |
Author contested; panel closed R2 (1/1 accept) |
agent/proto/agent.proto:107 |
plugins_supported names a capability but carries experiment state |
R1 |
Gon |
Yes |
| CRF-12 |
P4 |
Author fixed (10dbcb0) |
agent/proto/agent_drpc_old.go:109 |
DRPCAgentClient212 comment lists one of two v2.12 changes |
R1 |
Gon P3, Leorio P4 |
Yes |
| CRF-13 |
P3 |
Author fixed (10dbcb0) |
codersdk/workspacesdk/frontmatter.go:39 |
Invalid-character error names wrong byte for non-ASCII and omits allowed set |
R1 |
Leorio P3, Ging-go P4, Chopper P4 |
Yes |
| CRF-14 |
P3 |
Author fixed (10dbcb0) |
coderd/agentapi/api.go:159 |
No test covers Experiments wiring from Options into ManifestAPI |
R1 |
Bisky |
Yes |
| CRF-15 |
P4 |
Dropped by orchestrator (matches four sibling arms; whole-switch cleanup listed out of scope as CRF-25) |
coderd/agentapi/context.go:398 |
Dead nil-payload guard in plugin arm |
R1 |
Bisky |
No |
| CRF-16 |
P4 |
Author fixed (10dbcb0) |
codersdk/workspacesdk/frontmatter_test.go:198 |
Valid table lacks mixed separators and spec examples |
R1 |
Bisky |
Yes |
| CRF-17 |
P4 |
Dropped by orchestrator (disproved: TestGetManifest/OK/Child asserts false; verified by Bisky, Chopper, Mafu-san, Netero) |
coderd/agentapi/manifest_test.go:329 |
No test asserts PluginsSupported false |
R1 |
Ryosuke |
No |
| CRF-18 |
P4 |
Author contested; panel closed R2 (6/6 accept) |
codersdk/deployment.go:5519 |
Experiment description promises behavior this PR does not ship |
R1 |
Mafu-san P4, Ryosuke Note, Mafuuu Note, Gon Note, Leorio Note, Chopper Note |
Yes |
| CRF-19 |
P4 |
Author fixed (10dbcb0) |
agent/proto/agent.proto:656 |
source comment does not define plugin root or plugin MCP server source format; same-name servers collide on PK |
R1 |
Knuckle, Gon (OOS item merged) |
Yes |
| CRF-20 |
Note |
Note (body) |
coderd/database/queries/chats.sql:1684 |
Plugin rows re-pin only on drift, so pinned plugin_name may reference an unpinned plugin row |
R1 |
Knuckle |
No |
| CRF-21 |
Nit |
Author fixed (10dbcb0) |
agent/agentcontext/resolve.go:842 |
KindPlugin still documented as Claude Code plugin manifests |
R1 |
Mafuuu |
Yes |
| CRF-22 |
Nit |
Author fixed (10dbcb0) |
codersdk/workspacesdk/frontmatter.go:25 |
Doc comment hardcodes 64 next to MaxPluginNameLength |
R1 |
Gon |
Yes |
| CRF-23 |
Nit |
Dropped by orchestrator (squash merge uses PR title and body, not the commit body) |
commit b500f10 |
Commit body line is 110 characters |
R1 |
Leorio |
No |
| CRF-24 |
OOS |
Out of scope (body) |
coderd/x/chatd/context_prompt.go:115 |
MCP tool names built from server_name collide across plugins |
R1 |
Knuckle |
No |
| CRF-25 |
OOS |
Out of scope (body) |
coderd/agentapi/context.go:368-394 |
Unreachable nil-payload guards in existing switch arms |
R1 |
Bisky |
No |
| CRF-26 |
P3 |
Author fixed (2be2785) |
coderd/agentapi/context.go:390 |
OK plugin resource with empty name is stored; comment claims only non-OK may be empty |
R2 |
Gon P3, Bisky P3, Ryosuke P4, Leorio P4, Zoro P4 |
Yes |
| CRF-27 |
P3 |
Author fixed (2be2785) |
coderd/agentapi/api.go:289 |
No test covers Experiments wiring into ContextAPI |
R2 |
Bisky |
Yes |
| CRF-28 |
P4 |
Author fixed (2be2785) |
agent/proto/agent.proto:558 |
Wire contract does not say an invalid plugin name rejects the whole snapshot |
R2 |
Mafuuu P4, Chopper Note |
Yes |
| CRF-29 |
P4 |
Author fixed (2be2785) |
agent/proto/agent.proto:566 |
Plugin MCP source format can collide with a workspace .mcp.json server name containing "/" |
R2 |
Knuckle |
Yes |
| CRF-30 |
Nit |
Author fixed (2be2785) |
coderd/agentapi/context.go:400 |
isPlugin closure also matches plugin-attributed skills and servers |
R2 |
Gon |
Yes |
| CRF-31 |
P4 |
Author fixed (2be2785) |
coderd/agentapi/context.go:387 |
validateContextPluginName is a single-caller wrapper |
R2 |
Zoro |
Yes |
| CRF-32 |
P4 |
Author fixed (2be2785) |
coderd/agentapi/context.go:406 |
ContainsFunc fast path in withoutPluginResources is unmeasured |
R2 |
Zoro |
Yes |
| CRF-33 |
Note |
Note (body) |
coderd/agentapi/context.go:83 |
With experiment off, stored aggregate hash covers dropped plugin resources (documented) |
R2 |
Melody, Knuckle, Ryosuke |
No |
| CRF-34 |
Note |
Note (body) |
commit 10dbcb0 |
Commit body still says v2.10 and no migration; squash uses PR body |
R2 |
Mafu-san |
No |
| CRF-35 |
P3 |
Author fixed (825f9e6) |
coderd/agentapi/context.go:421 |
withoutShadowedPluginServers adds a coderd-side collision policy that the agent should own |
R3 |
Ryosuke P3, Zoro P3 |
Yes |
| CRF-36 |
P4 |
Author fixed (825f9e6) |
coderd/agentapi/context.go:433 |
Shadowed plugin MCP server is dropped with no log, row, or error |
R3 |
Chopper P4, Leorio P4, Mafuuu P4, Knuckle P4 |
Yes |
| CRF-37 |
P4 |
Author fixed (825f9e6) |
coderd/agentapi/context.go:137 |
Shadow drop runs before validation, so malformed plugin servers are dropped instead of rejected |
R3 |
Bisky P4, Mafuuu P4 |
Yes |
| CRF-38 |
P3 |
Author fixed (825f9e6) |
coderd/agentapi/context.go:405 |
No test stores a valid plugin MCP server with the experiment on |
R3 |
Bisky |
Yes |
| CRF-39 |
P3 |
Author fixed (825f9e6) |
agent/proto/agent.proto:641 |
OK plugin names are not required unique per push, so plugin_name is ambiguous |
R3 |
Mafuuu P3, Knov P4 |
Yes |
| CRF-40 |
P4 |
Author fixed (825f9e6) |
coderd/agentapi/context.go:418 |
withoutShadowedPluginServers doc says "another resource"; plugin-plugin collisions still fail the push |
R3 |
Gon P3, Netero Note |
Yes |
| CRF-41 |
P4 |
Author fixed (825f9e6) |
coderd/agentapi/context.go:391 |
"(non-OK ones may not)" reads as a prohibition; bullets duplicate the list at :294-296 |
R3 |
Gon P2, Leorio Nit, Zoro Nit |
Yes |
| CRF-42 |
Nit |
Author fixed (825f9e6) |
coderd/agentapi/context.go:412 |
isPluginMCPServer comment restates the one-line body |
R3 |
Gon P2 |
Yes |
| CRF-43 |
Nit |
Author fixed (825f9e6) |
coderd/agentapi/context.go:422 |
Map name other does not say what it holds |
R3 |
Gon |
Yes |
| CRF-44 |
Note |
Note (body) |
agent/proto/agent.proto:631 |
Proto says invalid plugin_name rejects the push, but with experiment off the resource is dropped |
R3 |
Melody |
No |
| CRF-45 |
Note |
Dropped by orchestrator (shadow drop removed in 825f9e6) |
coderd/agentapi/context.go:83 |
With experiment on, the stored aggregate hash also covers shadow-dropped servers |
R3 |
Knuckle |
No |
| CRF-46 |
OOS |
Out of scope (body) |
agent/x/agentmcp/manager.go:711 |
Agent MCP server map and chatd routing key on bare server_name, so plugin and workspace servers with the same server_name collide |
R3 |
Melody |
No |
| CRF-47 |
P4 |
Author fixed (938caef) |
coderd/agentapi/context.go:343 |
Duplicate OK plugin name error omits the first plugin's source |
R4 |
Chopper, Leorio, Mafuuu |
Yes |
| CRF-48 |
P4 |
Author fixed (938caef) |
coderd/agentapi/context.go:342 |
plugin_name need not reference an OK plugin in the push |
R4 |
Knov P4, Meruem P4, Ryosuke Note, Zoro Note |
Yes |
| CRF-49 |
Nit |
Author fixed (938caef) |
agent/proto/agent.proto:633 |
PluginBody comment repeats the source contract from ContextResource.source |
R4 |
Gon P2 |
Yes |
| CRF-50 |
Nit |
Author fixed (938caef) |
coderd/agentapi/context.go:421 |
withoutPluginResources also drops plugin-attributed resources; rename to withoutPluginData |
R4 |
Gon |
Yes |
| CRF-51 |
Nit |
Author fixed (938caef) |
coderd/agentapi/context_test.go:486 |
RejectsInvalidPluginName contains cases without an invalid name |
R4 |
Gon |
Yes |
| CRF-52 |
P2 |
Dropped by orchestrator (author applied one of the two fixes CRF-41 offered; helper list matches helper checks) |
coderd/agentapi/context.go:394 |
Helper doc list duplicates caller's list |
R4 |
Gon |
No |
| CRF-53 |
P2 |
Dropped by orchestrator (comment maps each kind to its field; no behavior consequence) |
coderd/agentapi/context.go:379 |
contextPluginName doc restates switch |
R4 |
Gon |
No |
| CRF-54 |
Nit |
Dropped by orchestrator (uppercase carried over from the base comment text) |
coderd/database/migrations/000612_context_resources_plugin_comments.up.sql:3 |
HOOK/SUBAGENT/COMMAND case in comment |
R4 |
Gon |
No |
| CRF-55 |
Note |
Note (body) |
agent/proto/agent.proto:562 |
Removing the shadow drop makes a workspace server named / fail the whole push; agent PR must resolve it |
R4 |
Leorio, Chopper, Mafuuu, Knuckle, Melody |
No |
| CRF-56 |
OOS |
Out of scope (body) |
agent/agentcontext/push.go:120 |
pushWithRetry resends a deterministically rejected push forever |
R4 |
Chopper |
No |
| CRF-57 |
OOS |
Out of scope (body) |
coderd/agentapi/context_test.go |
Tests use context.Background() instead of t.Context() (existing file pattern) |
R4 |
Ging-go |
No |
| CRF-58 |
P3 |
Author fixed (8397708) |
coderd/agentapi/context.go:364 |
Orphan skip runs before body, size, and status checks, so malformed orphans are accepted |
R5 |
Mafuuu P4, Knov P4, Meruem P4, Chopper P4, Bisky P4, Razor P4 |
Yes |
| CRF-59 |
P4 |
Author fixed (8397708) |
coderd/agentapi/context.go:325 |
First pass reports duplicate plugin name before duplicate or empty source checks |
R5 |
Melody, Mafuuu |
Yes |
| CRF-60 |
P4 |
Author fixed (8397708) |
coderd/agentapi/context.go:366 |
plugin_name matched by name only, so a non-OK plugin's skill is attributed to an OK plugin of the same name |
R5 |
Melody P4, Meruem Note, Ryosuke Note |
Yes |
| CRF-61 |
P4 |
Author fixed (8397708) |
coderd/agentapi/context_test.go:611 |
No test asserts the orphaned sources or the warning |
R5 |
Bisky, Chopper |
Yes |
| CRF-62 |
P4 |
Author fixed (8397708) |
coderd/agentapi/context_test.go:601 |
Orphan test orders plugins before skills, so first-pass order independence is untested |
R5 |
Bisky |
Yes |
| CRF-63 |
P4 |
Author fixed (8397708) |
coderd/agentapi/context.go:145 |
Warning text wrong for an absent plugin; the skip has four different names |
R5 |
Leorio P4, Gon Nit |
Yes |
| CRF-64 |
P4 |
Author fixed (8397708) |
agent/proto/agent.proto:636 |
Orphan rule is documented on PluginBody, not on the plugin_name fields it applies to |
R5 |
Gon |
Yes |
| CRF-65 |
Nit |
Author fixed (8397708) |
coderd/agentapi/context.go:305 |
"A valid skill or MCP server" reads as status OK, but the skip ignores status |
R5 |
Gon P2 |
Yes |
| CRF-66 |
Nit |
Author fixed (8397708) |
coderd/agentapi/context.go:316 |
okPlugins comment restates the assignment; rename to okPluginSources |
R5 |
Gon P2 |
Yes |
| CRF-67 |
Note |
Note (body) |
coderd/agentapi/context.go:145 |
Orphan warning logs before the version gate and carries no version field |
R5 |
Chopper |
No |
| CRF-68 |
Note |
Note (body) |
coderd/agentapi/context.go:140 |
With experiment on, stored aggregate hash covers orphan-skipped resources |
R5 |
Netero |
No |
| CRF-69 |
P4 |
Author fixed (4494299) |
coderd/agentapi/context.go:453 |
plugin_source byte cap rejects the push for input the skip already discards |
R6 |
Netero |
Yes |
| CRF-70 |
P4 |
Author fixed (4494299) |
tailnet/proto/version.go:95 |
v2.13 changelog omits plugin_source |
R6 |
Netero |
Yes |
| CRF-71 |
P4 |
Author fixed (4494299) |
PR description |
Description still describes the name-only skip rule and omits plugin_source |
R6 |
Netero |
Yes |
| CRF-72 |
P3 |
Author fixed (4494299) |
agent/proto/agent.proto:672 |
Shadowed same-name plugin with the same server_name yields duplicate source acme/echo and rejects the push before the skip |
R6 |
Mafuuu P3, Knov P3, Bisky P3, Razor P3, Melody P3, Ryosuke P3, Meruem P4 |
Yes |
| CRF-73 |
P4 |
Author fixed (4494299) |
coderd/agentapi/context.go:388 |
Skill plugin_source not checked against the skill's own source |
R6 |
Knov |
Yes |
| CRF-74 |
P4 |
Author fixed (4494299) |
coderd/agentapi/context.go:145 |
Skip warning logs only sources, not plugin_name and plugin_source |
R6 |
Chopper |
Yes |
| CRF-75 |
P4 |
Author fixed (4494299) |
coderd/agentapi/context.go:451 |
Pairing error does not say which field is set |
R6 |
Leorio |
Yes |
| CRF-76 |
P4 |
Author fixed (4494299) |
coderd/agentapi/context.go:306 |
Skip rule wording (Go doc and both plugin_source comments) reads as skipping every non-plugin resource |
R6 |
Gon |
Yes |
| CRF-77 |
Nit |
Author fixed (4494299) |
coderd/agentapi/context_test.go:1046 |
withSkillPlugin/withServerPlugin comments restate the body |
R6 |
Gon P2 |
Yes |
| CRF-78 |
P4 |
Author fixed (4494299) |
coderd/agentapi/context.go:479 |
Experiment off: resources with only plugin_source are rejected instead of dropped |
R6 |
Ryosuke |
Yes |
| CRF-79 |
P4 |
Author fixed (f64e456) |
coderd/agentapi/context.go:83 |
ContextAPI.Experiments comment omits source-only drop |
R7 |
Netero |
Yes |
| CRF-80 |
P4 |
Author fixed (f64e456) |
agent/proto/agent.proto:634 |
Proto skill prefix rule omits the accepted backslash form |
R7 |
Netero |
Yes |
| CRF-81 |
P3 |
Author fixed (f64e456) |
coderd/agentapi/context.go:396 |
Uncapped MCP server plugin_source is logged in the skip warning and quoted in errors (multi-MB lines) |
R7 |
Kurapika P3, Knov P4, Razor P4, Chopper P4 |
Yes |
| CRF-82 |
P3 |
Author fixed (f64e456) |
agent/proto/agent.proto:103 |
plugins_supported contract is not per connection; process-wide snapshot can reach an older coderd on reconnect |
R7 |
Meruem |
Yes |
| CRF-83 |
P3 |
Author fixed (f64e456) |
agent/proto/agent.proto:638 |
plugin_source duplicates okPluginSources now that agents send only OK plugin children |
R7 |
Zoro |
Yes |
| CRF-84 |
P4 |
Author fixed (f64e456) |
coderd/agentapi/context.go:389 |
Skip is a second failure policy for a contract violation that rejects elsewhere |
R7 |
Zoro |
Yes |
| CRF-85 |
P4 |
Author fixed (f64e456) |
coderd/agentapi/context.go:471 |
Backslash prefix branch has only an accept test |
R7 |
Bisky |
Yes |
| CRF-86 |
P4 |
Author fixed (f64e456) |
coderd/agentapi/context.go:485 |
Plugin MCP server with empty server_name is accepted with source "/" |
R7 |
Ryosuke |
Yes |
| CRF-87 |
P4 |
Author fixed (f64e456) |
coderd/agentapi/context.go:309 |
PR description keeps the unscoped skip-rule sentence CRF-76 fixed in code |
R7 |
Leorio |
Yes |
| CRF-88 |
P4 |
Author fixed (f64e456) |
agent/proto/agent.proto:679 |
MCPServerBody.plugin_source last sentences do not state when the collision happens or that the rule is mandatory |
R7 |
Gon |
Yes |
| CRF-89 |
Nit |
Author fixed (f64e456) |
coderd/agentapi/context_test.go:640 |
NonOKPluginServerSharingOKServerSource sits under a subtest about duplicate names |
R7 |
Gon |
Yes |
| CRF-90 |
Note |
Note (body) |
agent/proto/agent.proto:624 |
Contract does not say whether plugin skill names must be unique against workspace skills; chatd findSkill returns the first match |
R7 |
Mafuuu |
No |
| CRF-91 |
Note |
Note (body) |
coderd/agentapi/context.go:469 |
skills/ prefix check is a raw string match, not path containment |
R7 |
Kurapika |
No |
| CRF-92 |
P4 |
Author fixed (ff177d1) |
codersdk/agentsdk/agentsdk.go:138 |
SDK PluginsSupported comment lacks the per-connection rule |
R8 |
Netero |
Yes |
| CRF-93 |
P3 |
Author fixed (ff177d1) |
coderd/agentapi/context.go:360 |
Plugin validation errors still quote uncapped plugin names, server_name, and unvalidated plugin sources |
R8 |
Leorio P3, Kurapika P4, Razor P4, Chopper P4, Meruem P4, Kite P4 |
Yes |
| CRF-94 |
P4 |
Author fixed (ff177d1) |
coderd/agentapi/context.go:310 |
With two OK plugins sharing a name, a skill of the second gets a path error instead of the duplicate-name error |
R8 |
Razor |
Yes |
| CRF-95 |
P4 |
Author fixed (ff177d1) |
agent/proto/agent.proto:674 |
OK-plugins-only rule sits only on MCPServerBody, its reason is server-only, and the same-server_name rejection is no longer stated |
R8 |
Gon P2, Razor P4, Ryosuke P4 |
Yes |
| CRF-96 |
P3 |
Author fixed (ff177d1) |
agent/proto/agent.proto:104 |
plugins_supported rule uses undefined terms; version.go gates only plugin resources, not plugin_name |
R8 |
Gon P3, Leorio P4 |
Yes |
| CRF-97 |
P4 |
Author fixed (ff177d1) |
coderd/agentapi/context.go:139 |
Experiment off: index-based errors use the filtered slice index |
R8 |
Bisky, Melody |
Yes |
| CRF-98 |
P4 |
Author fixed (ff177d1) |
coderd/agentapi/context.go:467 |
isPluginData plugin-body branch untested (nameless non-OK plugin) |
R8 |
Bisky |
Yes |
| CRF-99 |
P4 |
Author fixed (ff177d1) |
coderd/agentapi/context.go:341 |
Duplicate OK name check lives in the caller while other plugin rules live in validateContextPluginFields |
R8 |
Zoro |
Yes |
| CRF-100 |
Nit |
Author fixed (ff177d1) |
coderd/agentapi/context_test.go:530 |
Three accept subtests repeat setup; one table would do |
R8 |
Zoro P4 |
Yes |
| CRF-101 |
Nit |
Author fixed (ff177d1) |
coderd/agentapi/context_test.go:494 |
Cases Plugin/Skill/MCP do not say what is invalid |
R8 |
Gon |
Yes |
| CRF-102 |
Nit |
Author fixed (ff177d1) |
agent/proto/agent.proto:560 |
"snapshot" vs "push" for the same rejection |
R8 |
Gon |
Yes |
| CRF-103 |
Note |
Note (body) |
coderd/agentapi/context.go:440 |
Separator normalization accepts mixed-separator skill sources |
R8 |
Meruem |
No |
| CRF-104 |
OOS |
Out of scope (body) |
agent/agentcontext/resolve.go:636 |
Skill name error quotes up to 64 KiB, exceeding the 4096-byte Error cap |
R8 |
Kurapika |
No |
| CRF-105 |
P4 |
Open |
coderd/database/migrations/000614_context_resources_plugin_comments.up.sql:1 |
PR description names migration 000612 and v2.11 after the rebase |
R9 |
Netero |
Yes |
| CRF-106 |
P4 |
Open |
agent/proto/agent.proto:648 |
PluginBody non-OK paragraph omits nameless non-OK children and describes a server check coderd does not make |
R9 |
Gon P2, Razor P4 |
Yes |
| CRF-107 |
P4 |
Open |
agent/proto/agent.proto:648 |
No test asserts either documented outcome for servers of a non-OK plugin |
R9 |
Melody |
Yes |
| CRF-108 |
P4 |
Open |
coderd/agentapi/context.go:414 |
collectOKPlugins indexes unvalidated plugins, so errors depend on resource order and name the wrong resource |
R9 |
Meruem, Ryosuke |
Yes |
| CRF-109 |
P4 |
Open |
coderd/agentapi/context.go:474 |
MCP source error prints a template and drops the validated plugin name |
R9 |
Leorio |
Yes |
| CRF-110 |
Nit |
Open |
coderd/agentapi/context.go:453 |
Errors write status as "ok" instead of "OK" |
R9 |
Leorio |
Yes |
| CRF-111 |
Nit |
Open |
coderd/agentapi/context.go:300 |
"Error indexes refer to resources as sent" restates the loop |
R9 |
Gon P2 |
Yes |
| CRF-112 |
P4 |
Open |
coderd/agentapi/context.go:408 |
collectOKPlugins comment says the size filter is safe, not that it is required |
R9 |
Gon |
Yes |
| CRF-113 |
P4 |
Open |
coderd/agentapi/context_test.go:510 |
huge length and 4096 bound are unexplained literals with a hidden range constraint |
R9 |
Gon |
Yes |
| CRF-114 |
P4 |
Open |
coderd/agentapi/context_test.go:633 |
EmptySource row passes without the duplicate-name logic |
R9 |
Bisky |
Yes |
Contested and acknowledged
CRF-11 (P3, agent/proto/agent.proto:107) - plugins_supported name vs experiment state
- Finding:
plugins_supported names a capability, but its value is the experiment state; a v2.13 coderd with the experiment off stored plugin resources yet sent false. Proposed renaming to plugins_enabled before release.
- Author defense: coderd now drops plugin resources and
plugin_name data when the experiment is off (CRF-8 fix), so false means this coderd will not store plugin data. That is the condition the finding itself said would make the current name accurate. The proto comment is now one condition. Name kept.
- Panel closure (R2, 1/1): Gon, the agent that raised it (same agent, resumed), verified
PushContextState drops plugin data with the experiment off (context.go:136-139, DropsPluginDataWithoutExperiment passes), so false now means coderd will not store plugin data, matching the name.
CRF-18 (P4, codersdk/deployment.go:5519) - experiment description promises unshipped behavior
- Finding: The
ExperimentAgentPlugins description says plugin skills appear in chats and plugin MCP servers run, but at this head enabling it only sets Manifest.plugins_supported. Proposed describing what this PR does, or adding the constant in the PR that adds agent discovery.
- Author defense: The seven PRs land as one stack behind the experiment; the description is what operators see once the stack is in. The experiment is off by default and not in
ExperimentsSafe, so a partial release exposes nothing unless someone opts in.
- Panel closure (R2, 6/6): Mafu-san, Ryosuke, Mafuuu, Gon, Leorio, Chopper (each the agent that raised it, resumed) accepted. Consequence of the defended text: on a release cut mid-stack, an operator who opts in by name sees no behavior change and no error. Accepted because the experiment is off by default and absent from
ExperimentsSafe, and enabling it at this head only sets a manifest flag.
CRF-4 (P4, agent/proto/agent_drpc_old.go:117) - unused 2.12/2.13 client interfaces
- Finding:
ConnectRPC213WithRole and DRPCAgentClient213 have no caller.
- Author defense: Removed
ConnectRPC213WithRole; kept DRPCAgentClient213 to match the convention that main defines DRPCAgentClient212 with no ConnectRPC212.
- Panel re-raised (R2): Netero (new agent; the R1 Netero agent was not resumed because Netero ran as the first pass before the dispute check-in) re-raised. Orchestrator verified: on base 0f78361 and origin/main,
agent_drpc_old.go ends at DRPCAgentClient211, and every interface 2.0 to 2.11 has a ConnectRPC2x caller. The PR adds DRPCAgentClient212, so the convention claim is not supported.
Law analysis
Not run. Effective additions 168 (threshold 1000).
Round log
Round 1
Full panel (round 1). Netero first pass: 1 P3, 4 P4, 2 Note, no P0, panel proceeded. Panel: ging-go, melody, ryosuke, knuckle, kurapika, pariston, mafuuu, bisky, gon, leorio, mafu-san, chopper (wildcard). Size 12: multi-domain (proto wire format, Go, coderd ingress of agent-supplied data). Result: 7 P3, 8 P4, 2 Nit, 3 Note (body), 2 OOS, 3 dropped. CRF-9 and CRF-10 downgraded from Gon P2 to P3: comment-only, the consequence depends on a future change. Keep-argument recorded: next developer adding a variant without a gate breaks every context push on older coderd. CRF-12 set to P4: the interface has no methods and version.go holds the full changelog. Reviewed against 0d12256..b500f10.
Round 2
Restructure round (new migration files 000612 added; effective additions 168 to 357). Churn guard PROCEED: 15 fixed, CRF-11 and CRF-18 contested; CRF-4 defense re-raised by Netero after orchestrator found its convention claim unsupported. Dispute check-in: gon, mafu-san, ryosuke, mafuuu, leorio, chopper resumed from R1 agents. CRF-11 closed 1/1, CRF-18 closed 6/6. Full panel: ging-go, melody, ryosuke, knuckle, kurapika, pariston, mafuuu, bisky, gon, leorio, mafu-san, chopper, plus wildcard zoro (13; chopper is a contested persona, outside the size target). Netero R2 is a new agent. New: 2 P3, 4 P4, 1 Nit, 2 Note (body). Reviewed against 0f78361..10dbcb0.
Round 3
Fix round (no files added; effective additions 357 to 489; changes address R2 findings). Churn guard PROCEED: 8 fixed (CRF-4, CRF-26 to CRF-32), none contested. Panel (new agents): gon, bisky, ryosuke, leorio, zoro, mafuuu, chopper, knuckle (raisers of findings whose code changed), ging-go, melody (specialists for changed Go and proto wire files), wildcard knov. New: 3 P3, 4 P4, 2 Nit; 2 Note (body), 1 OOS. Melody's routing-name P3 moved to out of scope (consumers in unchanged chatd and agent code; extends CRF-24). Gon P2 comment findings downgraded: CRF-41 to P4 (wording ambiguity, code is correct), CRF-42 to Nit; keep-argument recorded: duplicated rule lists drift. Most new findings come from the CRF-29 fix (withoutShadowedPluginServers). Reviewed against 0f78361..2be2785.
Round 4
Fix round (4 files changed; no files added; effective additions unchanged at 489). Churn guard PROCEED: CRF-35 to CRF-43 fixed. Panel (new agents): ryosuke, zoro, chopper, leorio, mafuuu, knuckle, bisky, knov, gon (raisers), ging-go, melody (specialists), wildcard meruem. New: 2 P4, 3 Nit, 1 Note (body), 2 OOS; 3 Gon findings dropped (CRF-52 keep-argument: two rule lists can drift; dropped because the author applied the fix offered and each list matches its function). Gon P2 on PluginBody downgraded to Nit (comment duplication, no behavior). Reviewed against 0f78361..825f9e6.
Round 5
Fix round (branch squashed; diff 825f9e6..938caef touches 4 files, no files added). Churn guard PROCEED: CRF-47 to CRF-51 fixed. Panel (new agents): chopper, leorio, mafuuu, knov, meruem, gon (raisers), ging-go, melody (specialists), ryosuke, bisky (policy layering and new tests), wildcard razor. New: 1 P3, 7 P4, 2 Nit, 2 Note (body). CRF-58 raised from six P4s to P3: consequence chain with CRF-56 (a malformed orphan is accepted, then fixing the manifest makes the same push reject, and the agent retry loop stops context sync until restart). Gon P2s on comments downgraded to Nit (wording, no behavior). Reviewed against 0f78361..938caef.
Round 6
Fix round (diff 938caef..8397708 touches 4 files; adds proto field plugin_source tag 5 as part of the CRF-60 fix; effective additions 577 to 758). Churn guard PROCEED: CRF-58 to CRF-66 fixed. Netero (advisory): 3 P4. Panel (new agents): mafuuu, knov, meruem, chopper, bisky, razor, melody, leorio, gon, ryosuke (raisers), ging-go (specialist), wildcard kite. New: 1 P3 (seven reviewers converged, reproduced), 8 P4, 1 Nit. CRF-71 (PR description) has no file line; listed in the review body. Gon P2 on test-helper comments downgraded to Nit. Reviewed against 0f78361..8397708.
Correction: CRF-71 was posted as an inline comment on tailnet/proto/version.go:95 (post requires every open finding inline), not in the review body.
Round 7
Fix round (diff 8397708..4494299; no files added). Churn guard PROCEED: CRF-69 to CRF-78 fixed. Netero advisory: 2 P4, 1 Note (not posted: fixture style). Panel (new agents): mafuuu, knov, meruem, chopper, bisky, razor, melody, ryosuke, leorio, gon (raisers), ging-go, kurapika (specialists: input bounds, path prefix), wildcard zoro. New: 3 P3, 7 P4, 1 Nit, 2 Note (body). CRF-81 is a fix-chain regression: the CRF-69 cap removal (our round-6 advice) plus the CRF-74 log fields left plugin_source unbounded; convergence of four reviewers set P3 (amplification runs before the version gate). CRF-83 (structural: delete plugin_source) would also remove CRF-81 and CRF-84. Meruem's push.go:150 out-of-scope item is CRF-56, not re-added. Reviewed against 0f78361..4494299.
Round 8
Fix round (diff 4494299..f64e456; plugin_source removed, skip replaced by rejection; effective additions 831 to 661). Churn guard PROCEED: CRF-79 to CRF-89 fixed. Netero advisory: 1 P4. Panel (new agents): kurapika, knov, razor, chopper, meruem, zoro, bisky, ryosuke, leorio, gon (raisers), ging-go, melody (specialists), wildcard kite. New: 2 P3, 7 P4, 3 Nit, 1 Note (body), 1 OOS. CRF-93 is the remaining half of CRF-81 (other uncapped fields reach errors), six reviewers, P3. Gon P2 on proto wording merged into CRF-95 at P4. Zoro P4 on test duplication downgraded to Nit. Reviewed against 0f78361..f64e456.
Round 9
Fix round (branch rebased onto b4fb790; migration renumbered to 000614; withoutPluginData replaced by in-loop drop; collectOKPlugins added). Churn guard PROCEED: CRF-92 to CRF-102 fixed. Netero advisory: 1 P4, 1 Note (DRPCAgentClient213 belongs in agent PR; not posted). Panel (new agents): leorio, kurapika, razor, chopper, meruem, kite, gon, bisky, melody, zoro, ryosuke (raisers), ging-go, knuckle (specialists; migration renumbered), wildcard takumi (distilled set exhausted). New: 8 P4, 2 Nit. No P3: CRF-93 bound verified by five reviewers and a <4096 test. Gon P2s downgraded (comment wording): CRF-106 merged with Razor at P4, CRF-111 to Nit; CRF-113 merges two Gon P4s. Reviewed against b4fb790..ff177d1.
About deep-review
CRF = Coder Review Finding (P0-P4, Nit, Note)
| Reviewer |
Focus |
| Bisky |
tests |
| Chopper |
ops/errors |
| Churn-guard |
change verification |
| Ging |
language modernization |
| Gon |
naming |
| Hisoka |
edge cases |
| Killua |
perf |
| Kite |
change integrity |
| Knov |
contracts |
| Knuckle |
SQL |
| Komugi |
flake/determinism |
| Kurapika |
security |
| Law |
decomposition |
| Leorio |
docs |
| Luffy |
product |
| Mafu-san |
process |
| Mafuuu |
contracts |
| Melody |
dispatch/pairing |
| Meruem |
structural |
| Nami |
frontend |
| Netero |
mechanical checks |
| Pariston |
premise testing |
| Pen-botter |
product gaps |
| Razor |
verification |
| Robin |
duplication |
| Ryosuke |
Go arch |
| Takumi |
concurrency |
| Zoro |
shape |
🤖 Managed by Coder Agents.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR adds the agent-plugins experiment, the v2.13 plugin context messages, and Manifest.plugins_supported. Findings: 7 P3, 8 P4, 2 Nit.
The main design question is where the experiment is enforced. GetManifest gates plugins_supported on the experiment, but PushContextState stores plugin bodies and plugin_name values unconditionally and does not call ValidatePluginName, so both the experiment and the name grammar depend on the agent. Several comment findings below change wording depending on that answer.
Out of scope (needs a ticket or explicit acceptance by a human):
coderd/x/chatd/context_prompt.go:115: pinned MCP tool names are built as server + "__" + tool from server_name, so two plugins that declare the same server name produce identical model-facing tool names even when their sources differ (not checked against the later stack PRs).
coderd/agentapi/context.go:368-394: the four existing switch arms repeat a nil-payload guard that cannot change the stored body, since protojson.Marshal on a typed nil returns {}. The new plugin arm copies it.
Notes:
- The PR description says agents keep dialing v2.10;
agent/agent.go:1183 dials v2.11 through ConnectRPC211WithRole.
- CI
gen and offlinedocs fail because the base commit lacks docs/manifest/generated/cli.json; rebasing onto main fixes both. agent.pb.go regenerates without changes. The Storybook failure was not investigated because the job log could not be read.
coderd/database/queries/chats.sql:1684: mcp_server rows are re-synced into chat pins on every push, but plugin rows change only on a drift re-pin, so a pinned server's plugin_name can name a plugin that is not pinned or is pinned at an older version.
coderd/x/chatd/context_prompt.go:403
P3 [CRF-1] The contextResourceKind and pinnedContextResources (line 335) doc comments still call plugin a reserved kind. (Netero)
After the new ContextResource_Plugin arm in coderd/agentapi/context.go, plugin rows are written to workspace_agent_context_resources and pinned into chat_context_resources, and chatd drops them because it does not handle them yet. Change both comments to say plugin rows are stored but not surfaced, and that hook, subagent, and command remain reserved.
🤖
coderd/database/dump.sql:3576
P4 [CRF-2] The body_kind column comments on workspace_agent_context_resources and chat_context_resources (line 1930) still say plugin is reserved for the Claude Code plugin RFC. (Netero)
After this PR, plugin holds Agent Plugins PluginBody JSON. The enum needs no migration, but the column comments can only be corrected with a COMMENT ON COLUMN migration, in this PR or the stacked PR that first reads plugin rows.
🤖
agent/agentcontext/resolve.go:842
Nit [CRF-21] KindPlugin is still documented as "reserved for Claude Code plugin manifests", while this PR defines the plugin kind as an Agent Plugins plugin.json manifest. (Mafuuu)
Reword it to name Agent Plugins.
🤖
🤖 This review was automatically generated with Coder Agents.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
coder-agents-review[bot] reacted with rocket emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This push makes coderd enforce the experiment and validate plugin names in PushContextState, and adds migration 000612. New findings: 2 P3, 4 P4, 1 Nit.
All 15 round-1 fixes were verified against the code. The defenses on the plugins_supported name and on the experiment description were accepted by the reviewers that raised them. The unused 2.12/2.13 client interfaces remain open; see the reply on that thread.
The test-go-pg CI failure was not attributed to this diff: the job log could not be read, and the PR's targeted tests and DB=ci go test ./coderd/database/migrations/ pass locally.
Notes:
coderd/agentapi/context.go:83: with the experiment off, the stored aggregate_hash still covers plugin resources that coderd dropped. This only happens when an agent ignores plugins_supported, and the field comment documents it.
- The commit body still says agents dial v2.10 and that no migration is needed. The squash merge uses the PR body, which is accurate.
🤖 This review was automatically generated with Coder Agents.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
coder-agents-review[bot] reacted with rocket emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This push removes plugin_source, replaces the skip with a rejection when plugin_name names no OK plugin, normalizes skill path separators, rejects empty plugin server_name, and makes plugins_supported per connection. New findings: 2 P3, 7 P4, 3 Nit.
All 11 round-7 findings were verified fixed. Removing plugin_source closed the field-mismatch class of bugs. The uncapped-input problem from round 7 remains through other fields; the first P3 lists them.
Out of scope (needs a ticket or explicit acceptance by a human):
agent/agentcontext/resolve.go:636: skill name %q is not kebab-case quotes a front-matter name of up to 64 KiB into ContextResource.Error, so one malformed SKILL.md can exceed the 4096-byte error cap and coderd rejects the whole push.
Notes:
coderd/agentapi/context.go:440: converting \ to / in both strings accepts mixed-separator sources such as /a\b/skills/x for plugin root /a/b, which the previous two-form check rejected. Only a buggy agent sends this.
🤖 This review was automatically generated with Coder Agents.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
coder-agents-review[bot] reacted with rocket emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This push bounds every quoted string in plugin validation errors, detects duplicate OK plugin names in a first pass, drops plugin data inside the indexed loop, and tightens the plugins_supported wording. New findings: 8 P4, 2 Nit.
All 11 round-8 findings were verified fixed; reviewers mutation-checked the new tests, and RejectsInvalidPluginFields now asserts each error stays under 4096 bytes with 200 KiB inputs. No P3 or higher remains. The new findings are wording, test gaps, and error clarity around collectOKPlugins and the PluginBody non-OK paragraph.
🤖 This review was automatically generated with Coder Agents.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
|
|
// enabled. When false, the agent must not discover plugins, and must
|
|
|
// not send plugin resources or set plugin_name on this connection,
|
|
|
// even if an earlier connection's manifest set it.
|
|
|
bool plugins_supported = 20;
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly I'm not in love with introducing wire changes to say "we have an experiment". I'd drop this change and figure out how to live without it.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, I don't like it. I wanted to support at least one version of bi-directional compatibility and I'm not sure of a better approach. Let's say agent plugins land in version N. I want to support the case where an operator upgrades coderd to N, starts up workspaces, then rolls coderd back to N-1. If the agent continues to publish context that includes the PluginBody message, the old/current version of coder rejects the entire push (see
), causing coderd to keep the old snapshot. We could update the agent to dial 2.13 then fall back, but that would be a new pattern and opens a can of worms.
If you can think of another approach I'm all ears; alternatively, we can accept the risk and ensure that the error is surfaced. Looking at this again, I'm realizing that restarting a workspace pulls a new agent binary; if that's the case, if someone does need to rollback they can do a mass restart. It also looks like our rollback strategy is just to restore a db snapshot, which sidesteps the stale data issue.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should be able to surface the error correctly even without that field.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated this pr to remove the field, and updated the second PR to add a generic error to the push when the plugin payload is unsupported or otherwise breaks its contract with the coderd server.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
|
|
string name = 2;
|
|
|
string description = 3;
|
|
|
// plugin_name is the owning Agent Plugin, or empty for a workspace skill.
|
|
|
string plugin_name = 4;
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do this with a (soft) reference to the plugin, instead of, for example, embedding the Skills and MCP servers directly into the PluginBody message?
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question - I have a mild preference to keep wire protocols like this flat though in this case it does lead to some additional stitching logic. I'll kick off a quick exploration on the nested version.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The thing I dislike about references like this is that they can be wrong. I.e. the plug_name doesn't match any plugin.
With the embedded structure errors like that are simply unrepresentable, which eliminates a whole class of problems, making the server code tighter with fewer error cases to check.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
jscottmiller
changed the title
feat: add agent-plugins experiment and Agent API v2.13 plugin messages
feat: add agent-plugins experiment and Agent API v2.16 plugin messages
Oct 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Adds the agent-plugins experiment and bumps the Agent API to v2.16 with
message-only changes for Agent Plugins (agent-plugins.org):
- ContextResource gains a PluginBody variant carrying validated plugin
manifest fields and the plugin's skills and MCP servers as nested
ContextResources.
- Manifest gains agent_plugins_enabled, which coderd sets when the
agent-plugins experiment is enabled.
Agents dial v2.16, so they need a coderd that supports it.
coderd stores the plugin variant under the existing plugin body kind,
without its nested resources, and each nested skill and MCP server as
its own row. Migration 000632 adds a plugin_name column to
workspace_agent_context_resources and chat_context_resources. It holds
the plugin's name on each nested row and on an OK plugin's row, and the
hydrate, MCP sync, and re-pin queries copy it. With the experiment off,
PushContextState drops plugin resources and the resources nested in
them. It rejects pushes with invalid plugin names, OK plugin resources
without a name, two OK plugin resources with the same name, a non-OK
plugin that nests resources, nested entries with the wrong body kind,
plugin skill sources outside "<plugin source>/skills/", and plugin MCP
servers with an empty server_name or a source other than
"<plugin name>/<server_name>". Nested resources count toward the
1000-resource cap.
workspacesdk.ValidatePluginName implements the spec's plugin name
grammar (1 to 64 characters of a-z, 0-9, '.', '-', alphanumeric first
and last, no repeated separators) so the agent and coderd validate
names the same way.
Reviewers
johnstcn
Awaiting requested review from johnstcn
mafredri
Awaiting requested review from mafredri
mafredri is a code owner
+1 more reviewer
Reviewers whose approvals may not affect merge requirements
Uh oh!
There was an error while loading. Please reload this page.
Labels
stale
This issue is like stale bread.
Milestone
No milestone
Development
Successfully merging this pull request may close these issues.
Uh oh!
There was an error while loading. Please reload this page.
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.
|