feat(agent/proto): add v2.10 PushContextState RPC

Add the ContextResource, PushContextStateRequest, and
PushContextStateResponse messages plus a new PushContextState
RPC on the Agent API. Bumps the tailnet+agent shared
CurrentVersion to 2.10 and adds the DRPCAgentClient210
interface, plus ConnectRPC210/ConnectRPC210WithRole on
codersdk/agentsdk's Client. Reserved kinds (PLUGIN, HOOK,
SUBAGENT, COMMAND) ship in the enum but are not emitted by
v1 agents.

The wire shape matches the workspace context sources RFC.
Coderd's server-side handler arrives separately.
This commit is contained in:
Kyle Carberry
2026-06-02 14:29:25 +00:00
parent 678617ab38
commit 08ccb62f68
6 changed files with 881 additions and 247 deletions
+743 -245
View File
File diff suppressed because it is too large Load Diff
+54
View File
@@ -538,6 +538,59 @@ message UpdateAppStatusRequest {
message UpdateAppStatusResponse {} message UpdateAppStatusResponse {}
// ContextResource is a single resolved workspace context
// resource (instruction file, skill meta, MCP config, or live
// MCP server tool list) pushed from the agent to coderd as part
// of a PushContextStateRequest snapshot.
//
// Reserved kinds (PLUGIN, HOOK, SUBAGENT, COMMAND) are not
// emitted by v2.10 agents; they are forward-compatible slots
// for the Claude Code plugin RFC.
message ContextResource {
string id = 1;
enum Kind {
KIND_UNSPECIFIED = 0;
INSTRUCTION_FILE = 1;
SKILL = 2;
MCP_CONFIG = 3;
MCP_SERVER = 4;
PLUGIN = 5;
HOOK = 6;
SUBAGENT = 7;
COMMAND = 8;
}
enum Status {
STATUS_UNSPECIFIED = 0;
OK = 1;
OVERSIZE = 2;
UNREADABLE = 3;
INVALID = 4;
EXCLUDED = 5;
}
Kind kind = 2;
string source = 3;
bytes content_hash = 4;
bytes payload = 5;
optional string source_path = 6;
Status status = 7;
uint64 size_bytes = 8;
string error = 9;
string description = 10;
}
message PushContextStateRequest {
uint64 version = 1;
bytes aggregate_hash = 2;
repeated ContextResource resources = 3;
bool initial = 4;
uint64 schema_version = 5;
string snapshot_error = 6;
}
message PushContextStateResponse {
bool accepted = 1;
}
service Agent { service Agent {
rpc GetManifest(GetManifestRequest) returns (Manifest); rpc GetManifest(GetManifestRequest) returns (Manifest);
rpc GetServiceBanner(GetServiceBannerRequest) returns (ServiceBanner); rpc GetServiceBanner(GetServiceBannerRequest) returns (ServiceBanner);
@@ -557,4 +610,5 @@ service Agent {
rpc ListSubAgents(ListSubAgentsRequest) returns (ListSubAgentsResponse); rpc ListSubAgents(ListSubAgentsRequest) returns (ListSubAgentsResponse);
rpc ReportBoundaryLogs(ReportBoundaryLogsRequest) returns (ReportBoundaryLogsResponse); rpc ReportBoundaryLogs(ReportBoundaryLogsRequest) returns (ReportBoundaryLogsResponse);
rpc UpdateAppStatus(UpdateAppStatusRequest) returns (UpdateAppStatusResponse); rpc UpdateAppStatus(UpdateAppStatusRequest) returns (UpdateAppStatusResponse);
rpc PushContextState(PushContextStateRequest) returns (PushContextStateResponse);
} }
+41 -1
View File
@@ -57,6 +57,7 @@ type DRPCAgentClient interface {
ListSubAgents(ctx context.Context, in *ListSubAgentsRequest) (*ListSubAgentsResponse, error) ListSubAgents(ctx context.Context, in *ListSubAgentsRequest) (*ListSubAgentsResponse, error)
ReportBoundaryLogs(ctx context.Context, in *ReportBoundaryLogsRequest) (*ReportBoundaryLogsResponse, error) ReportBoundaryLogs(ctx context.Context, in *ReportBoundaryLogsRequest) (*ReportBoundaryLogsResponse, error)
UpdateAppStatus(ctx context.Context, in *UpdateAppStatusRequest) (*UpdateAppStatusResponse, error) UpdateAppStatus(ctx context.Context, in *UpdateAppStatusRequest) (*UpdateAppStatusResponse, error)
PushContextState(ctx context.Context, in *PushContextStateRequest) (*PushContextStateResponse, error)
} }
type drpcAgentClient struct { type drpcAgentClient struct {
@@ -231,6 +232,15 @@ func (c *drpcAgentClient) UpdateAppStatus(ctx context.Context, in *UpdateAppStat
return out, nil return out, nil
} }
func (c *drpcAgentClient) PushContextState(ctx context.Context, in *PushContextStateRequest) (*PushContextStateResponse, error) {
out := new(PushContextStateResponse)
err := c.cc.Invoke(ctx, "/coder.agent.v2.Agent/PushContextState", drpcEncoding_File_agent_proto_agent_proto{}, in, out)
if err != nil {
return nil, err
}
return out, nil
}
type DRPCAgentServer interface { type DRPCAgentServer interface {
GetManifest(context.Context, *GetManifestRequest) (*Manifest, error) GetManifest(context.Context, *GetManifestRequest) (*Manifest, error)
GetServiceBanner(context.Context, *GetServiceBannerRequest) (*ServiceBanner, error) GetServiceBanner(context.Context, *GetServiceBannerRequest) (*ServiceBanner, error)
@@ -250,6 +260,7 @@ type DRPCAgentServer interface {
ListSubAgents(context.Context, *ListSubAgentsRequest) (*ListSubAgentsResponse, error) ListSubAgents(context.Context, *ListSubAgentsRequest) (*ListSubAgentsResponse, error)
ReportBoundaryLogs(context.Context, *ReportBoundaryLogsRequest) (*ReportBoundaryLogsResponse, error) ReportBoundaryLogs(context.Context, *ReportBoundaryLogsRequest) (*ReportBoundaryLogsResponse, error)
UpdateAppStatus(context.Context, *UpdateAppStatusRequest) (*UpdateAppStatusResponse, error) UpdateAppStatus(context.Context, *UpdateAppStatusRequest) (*UpdateAppStatusResponse, error)
PushContextState(context.Context, *PushContextStateRequest) (*PushContextStateResponse, error)
} }
type DRPCAgentUnimplementedServer struct{} type DRPCAgentUnimplementedServer struct{}
@@ -326,9 +337,13 @@ func (s *DRPCAgentUnimplementedServer) UpdateAppStatus(context.Context, *UpdateA
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented) return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
} }
func (s *DRPCAgentUnimplementedServer) PushContextState(context.Context, *PushContextStateRequest) (*PushContextStateResponse, error) {
return nil, drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
}
type DRPCAgentDescription struct{} type DRPCAgentDescription struct{}
func (DRPCAgentDescription) NumMethods() int { return 18 } func (DRPCAgentDescription) NumMethods() int { return 19 }
func (DRPCAgentDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver, interface{}, bool) { func (DRPCAgentDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver, interface{}, bool) {
switch n { switch n {
@@ -494,6 +509,15 @@ func (DRPCAgentDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver,
in1.(*UpdateAppStatusRequest), in1.(*UpdateAppStatusRequest),
) )
}, DRPCAgentServer.UpdateAppStatus, true }, DRPCAgentServer.UpdateAppStatus, true
case 18:
return "/coder.agent.v2.Agent/PushContextState", drpcEncoding_File_agent_proto_agent_proto{},
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
return srv.(DRPCAgentServer).
PushContextState(
ctx,
in1.(*PushContextStateRequest),
)
}, DRPCAgentServer.PushContextState, true
default: default:
return "", nil, nil, nil, false return "", nil, nil, nil, false
} }
@@ -790,3 +814,19 @@ func (x *drpcAgent_UpdateAppStatusStream) SendAndClose(m *UpdateAppStatusRespons
} }
return x.CloseSend() return x.CloseSend()
} }
type DRPCAgent_PushContextStateStream interface {
drpc.Stream
SendAndClose(*PushContextStateResponse) error
}
type drpcAgent_PushContextStateStream struct {
drpc.Stream
}
func (x *drpcAgent_PushContextStateStream) SendAndClose(m *PushContextStateResponse) error {
if err := x.MsgSend(m, drpcEncoding_File_agent_proto_agent_proto{}); err != nil {
return err
}
return x.CloseSend()
}
+9
View File
@@ -90,3 +90,12 @@ type DRPCAgentClient28 interface {
type DRPCAgentClient29 interface { type DRPCAgentClient29 interface {
DRPCAgentClient28 DRPCAgentClient28
} }
// DRPCAgentClient210 is the Agent API at v2.10. It adds the
// PushContextState RPC used by the agent to ship resolved
// workspace context snapshots (instruction files, skills, MCP
// configs, MCP server tool lists) to coderd.
type DRPCAgentClient210 interface {
DRPCAgentClient29
PushContextState(ctx context.Context, in *PushContextStateRequest) (*PushContextStateResponse, error)
}
+26
View File
@@ -336,6 +336,32 @@ func (c *Client) ConnectRPC29WithRole(ctx context.Context, role string) (
return proto.NewDRPCAgentClient(conn), tailnetproto.NewDRPCTailnetClient(conn), nil return proto.NewDRPCAgentClient(conn), tailnetproto.NewDRPCTailnetClient(conn), nil
} }
// ConnectRPC210 returns a dRPC client to the Agent API v2.10. It is useful when
// you want to be maximally compatible with newer Coderd Release Versions that
// implement the PushContextState RPC.
func (c *Client) ConnectRPC210(ctx context.Context) (
proto.DRPCAgentClient210, tailnetproto.DRPCTailnetClient28, error,
) {
conn, err := c.connectRPCVersion(ctx, apiversion.New(2, 10), "")
if err != nil {
return nil, nil, err
}
return proto.NewDRPCAgentClient(conn), tailnetproto.NewDRPCTailnetClient(conn), nil
}
// ConnectRPC210WithRole is like ConnectRPC210 but sends an explicit role
// query parameter to the server. Use "agent" for workspace agents to
// enable connection monitoring.
func (c *Client) ConnectRPC210WithRole(ctx context.Context, role string) (
proto.DRPCAgentClient210, tailnetproto.DRPCTailnetClient28, error,
) {
conn, err := c.connectRPCVersion(ctx, apiversion.New(2, 10), role)
if err != nil {
return nil, nil, err
}
return proto.NewDRPCAgentClient(conn), tailnetproto.NewDRPCTailnetClient(conn), nil
}
// ConnectRPC connects to the workspace agent API and tailnet API. // ConnectRPC connects to the workspace agent API and tailnet API.
// It does not send a role query parameter, so the server will apply // It does not send a role query parameter, so the server will apply
// its default behavior (currently: enable connection monitoring for // its default behavior (currently: enable connection monitoring for
+8 -1
View File
@@ -69,9 +69,16 @@ import (
// - Added session_id and confined_process fields to // - Added session_id and confined_process fields to
// ReportBoundaryLogsRequest on the Agent API. // ReportBoundaryLogsRequest on the Agent API.
// - Added sequence_number field to BoundaryLog on the Agent API. // - Added sequence_number field to BoundaryLog on the Agent API.
//
// API v2.10:
// - Added PushContextState RPC on the Agent API for pushing
// resolved workspace context snapshots (instruction files,
// skills, MCP configs, MCP server tool lists) from the
// agent to coderd. Adds ContextResource, PushContextState
// Request, and PushContextStateResponse messages.
const ( const (
CurrentMajor = 2 CurrentMajor = 2
CurrentMinor = 9 CurrentMinor = 10
) )
var CurrentVersion = apiversion.New(CurrentMajor, CurrentMinor) var CurrentVersion = apiversion.New(CurrentMajor, CurrentMinor)