From c7d34591a8f7292b4845f8c68aa32b1d78f597ce Mon Sep 17 00:00:00 2001 From: mintaka Date: Tue, 6 Oct 2026 23:33:53 -0400 Subject: [PATCH] feat(store): store pull requests and their issue links (RIG-4034) Adds PullRequestIssueLink to CreatePullRequestRequest, migration 0003 (pull_requests, pull_request_issue_links, prs_backfilled_at), and the store API the projection reads: CreatePullRequestWithLink, UpsertPullRequest, PullRequestsForIssues and FallbackIssuesForTarget. Spec-impact: none. Refs RIG-4034 Co-authored-by: Matt Wilkinson --- .../gen/compass/v1/agent_gateway.pb.go | 436 +++++++++++------- go/internal/store/db/models.go | 47 +- go/internal/store/db/pull_requests.sql.go | 392 ++++++++++++++++ go/internal/store/db/querier.go | 12 + go/internal/store/forge_authored.go | 8 +- go/internal/store/migrations/0001_init.sql | 42 ++ go/internal/store/pull_requests.go | 324 +++++++++++++ .../store/pull_requests_pgtest_test.go | 303 ++++++++++++ go/internal/store/queries/pull_requests.sql | 93 ++++ go/internal/store/rls_pgtest_test.go | 1 + go/internal/store/updated_at_pgtest_test.go | 1 + .../src/gen/compass/v1/agent_gateway_pb.ts | 81 +++- proto/compass/v1/agent_gateway.proto | 8 + 13 files changed, 1538 insertions(+), 210 deletions(-) create mode 100644 go/internal/store/db/pull_requests.sql.go create mode 100644 go/internal/store/pull_requests.go create mode 100644 go/internal/store/pull_requests_pgtest_test.go create mode 100644 go/internal/store/queries/pull_requests.sql diff --git a/go/internal/gen/compass/v1/agent_gateway.pb.go b/go/internal/gen/compass/v1/agent_gateway.pb.go index a684b1eb2..23ec3ffb4 100644 --- a/go/internal/gen/compass/v1/agent_gateway.pb.go +++ b/go/internal/gen/compass/v1/agent_gateway.pb.go @@ -2058,13 +2058,16 @@ func (x *ListIssuesResponse) GetIssues() []*v1.Issue { } type CreatePullRequestRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` - Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"` - Body string `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"` // WITHOUT the owner header - HeadRef string `protobuf:"bytes,4,opt,name=head_ref,json=headRef,proto3" json:"head_ref,omitempty"` // the branch the agent already pushed with ITS OWN git creds - BaseRef string `protobuf:"bytes,5,opt,name=base_ref,json=baseRef,proto3" json:"base_ref,omitempty"` // empty = the repo default branch - Draft bool `protobuf:"varint,6,opt,name=draft,proto3" json:"draft,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` + Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"` + Body string `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"` // WITHOUT the owner header + HeadRef string `protobuf:"bytes,4,opt,name=head_ref,json=headRef,proto3" json:"head_ref,omitempty"` // the branch the agent already pushed with ITS OWN git creds + BaseRef string `protobuf:"bytes,5,opt,name=base_ref,json=baseRef,proto3" json:"base_ref,omitempty"` // empty = the repo default branch + Draft bool `protobuf:"varint,6,opt,name=draft,proto3" json:"draft,omitempty"` + // The issue this PR works on. Unset = no explicit link; the server falls + // back to the forge's closing references. + Issue *PullRequestIssueLink `protobuf:"bytes,7,opt,name=issue,proto3" json:"issue,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -2141,6 +2144,73 @@ func (x *CreatePullRequestRequest) GetDraft() bool { return false } +func (x *CreatePullRequestRequest) GetIssue() *PullRequestIssueLink { + if x != nil { + return x.Issue + } + return nil +} + +type PullRequestIssueLink struct { + state protoimpl.MessageState `protogen:"open.v1"` + Forge *v1.ForgeRef `protobuf:"bytes,1,opt,name=forge,proto3" json:"forge,omitempty"` // unset = the PR's forge + Repo string `protobuf:"bytes,2,opt,name=repo,proto3" json:"repo,omitempty"` // owner/name; the team key on Linear + Number uint64 `protobuf:"varint,3,opt,name=number,proto3" json:"number,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PullRequestIssueLink) Reset() { + *x = PullRequestIssueLink{} + mi := &file_compass_v1_agent_gateway_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PullRequestIssueLink) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PullRequestIssueLink) ProtoMessage() {} + +func (x *PullRequestIssueLink) ProtoReflect() protoreflect.Message { + mi := &file_compass_v1_agent_gateway_proto_msgTypes[21] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PullRequestIssueLink.ProtoReflect.Descriptor instead. +func (*PullRequestIssueLink) Descriptor() ([]byte, []int) { + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{21} +} + +func (x *PullRequestIssueLink) GetForge() *v1.ForgeRef { + if x != nil { + return x.Forge + } + return nil +} + +func (x *PullRequestIssueLink) GetRepo() string { + if x != nil { + return x.Repo + } + return "" +} + +func (x *PullRequestIssueLink) GetNumber() uint64 { + if x != nil { + return x.Number + } + return 0 +} + type CommentOnPullRequestRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Repo string `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"` @@ -2152,7 +2222,7 @@ type CommentOnPullRequestRequest struct { func (x *CommentOnPullRequestRequest) Reset() { *x = CommentOnPullRequestRequest{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[21] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2164,7 +2234,7 @@ func (x *CommentOnPullRequestRequest) String() string { func (*CommentOnPullRequestRequest) ProtoMessage() {} func (x *CommentOnPullRequestRequest) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[21] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2177,7 +2247,7 @@ func (x *CommentOnPullRequestRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CommentOnPullRequestRequest.ProtoReflect.Descriptor instead. func (*CommentOnPullRequestRequest) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{21} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{22} } func (x *CommentOnPullRequestRequest) GetRepo() string { @@ -2211,7 +2281,7 @@ type GetPullRequestRequest struct { func (x *GetPullRequestRequest) Reset() { *x = GetPullRequestRequest{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[22] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2223,7 +2293,7 @@ func (x *GetPullRequestRequest) String() string { func (*GetPullRequestRequest) ProtoMessage() {} func (x *GetPullRequestRequest) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[22] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2236,7 +2306,7 @@ func (x *GetPullRequestRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetPullRequestRequest.ProtoReflect.Descriptor instead. func (*GetPullRequestRequest) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{22} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{23} } func (x *GetPullRequestRequest) GetRepo() string { @@ -2266,7 +2336,7 @@ type SubmitReviewRequest struct { func (x *SubmitReviewRequest) Reset() { *x = SubmitReviewRequest{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[23] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2278,7 +2348,7 @@ func (x *SubmitReviewRequest) String() string { func (*SubmitReviewRequest) ProtoMessage() {} func (x *SubmitReviewRequest) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[23] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2291,7 +2361,7 @@ func (x *SubmitReviewRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SubmitReviewRequest.ProtoReflect.Descriptor instead. func (*SubmitReviewRequest) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{23} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{24} } func (x *SubmitReviewRequest) GetRepo() string { @@ -2341,7 +2411,7 @@ type ReviewCommentInput struct { func (x *ReviewCommentInput) Reset() { *x = ReviewCommentInput{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[24] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2353,7 +2423,7 @@ func (x *ReviewCommentInput) String() string { func (*ReviewCommentInput) ProtoMessage() {} func (x *ReviewCommentInput) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[24] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2366,7 +2436,7 @@ func (x *ReviewCommentInput) ProtoReflect() protoreflect.Message { // Deprecated: Use ReviewCommentInput.ProtoReflect.Descriptor instead. func (*ReviewCommentInput) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{24} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{25} } func (x *ReviewCommentInput) GetPath() string { @@ -2422,7 +2492,7 @@ type TransitionIssueStateRequest struct { func (x *TransitionIssueStateRequest) Reset() { *x = TransitionIssueStateRequest{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[25] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2434,7 +2504,7 @@ func (x *TransitionIssueStateRequest) String() string { func (*TransitionIssueStateRequest) ProtoMessage() {} func (x *TransitionIssueStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[25] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2447,7 +2517,7 @@ func (x *TransitionIssueStateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use TransitionIssueStateRequest.ProtoReflect.Descriptor instead. func (*TransitionIssueStateRequest) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{25} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{26} } func (x *TransitionIssueStateRequest) GetRepo() string { @@ -2498,7 +2568,7 @@ type TransitionPullRequestStateRequest struct { func (x *TransitionPullRequestStateRequest) Reset() { *x = TransitionPullRequestStateRequest{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[26] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2510,7 +2580,7 @@ func (x *TransitionPullRequestStateRequest) String() string { func (*TransitionPullRequestStateRequest) ProtoMessage() {} func (x *TransitionPullRequestStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[26] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2523,7 +2593,7 @@ func (x *TransitionPullRequestStateRequest) ProtoReflect() protoreflect.Message // Deprecated: Use TransitionPullRequestStateRequest.ProtoReflect.Descriptor instead. func (*TransitionPullRequestStateRequest) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{26} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{27} } func (x *TransitionPullRequestStateRequest) GetRepo() string { @@ -2560,7 +2630,7 @@ type SubscribeForgeRequest struct { func (x *SubscribeForgeRequest) Reset() { *x = SubscribeForgeRequest{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[27] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2572,7 +2642,7 @@ func (x *SubscribeForgeRequest) String() string { func (*SubscribeForgeRequest) ProtoMessage() {} func (x *SubscribeForgeRequest) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[27] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2585,7 +2655,7 @@ func (x *SubscribeForgeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SubscribeForgeRequest.ProtoReflect.Descriptor instead. func (*SubscribeForgeRequest) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{27} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{28} } func (x *SubscribeForgeRequest) GetRepo() string { @@ -2632,7 +2702,7 @@ type SubscribeForgeResponse struct { func (x *SubscribeForgeResponse) Reset() { *x = SubscribeForgeResponse{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[28] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2644,7 +2714,7 @@ func (x *SubscribeForgeResponse) String() string { func (*SubscribeForgeResponse) ProtoMessage() {} func (x *SubscribeForgeResponse) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[28] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[29] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2657,7 +2727,7 @@ func (x *SubscribeForgeResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SubscribeForgeResponse.ProtoReflect.Descriptor instead. func (*SubscribeForgeResponse) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{28} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{29} } func (x *SubscribeForgeResponse) GetSubscriptionId() string { @@ -2676,7 +2746,7 @@ type UnsubscribeForgeRequest struct { func (x *UnsubscribeForgeRequest) Reset() { *x = UnsubscribeForgeRequest{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[29] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2688,7 +2758,7 @@ func (x *UnsubscribeForgeRequest) String() string { func (*UnsubscribeForgeRequest) ProtoMessage() {} func (x *UnsubscribeForgeRequest) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[29] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[30] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2701,7 +2771,7 @@ func (x *UnsubscribeForgeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UnsubscribeForgeRequest.ProtoReflect.Descriptor instead. func (*UnsubscribeForgeRequest) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{29} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{30} } func (x *UnsubscribeForgeRequest) GetSubscriptionId() string { @@ -2719,7 +2789,7 @@ type UnsubscribeForgeResponse struct { func (x *UnsubscribeForgeResponse) Reset() { *x = UnsubscribeForgeResponse{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[30] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2731,7 +2801,7 @@ func (x *UnsubscribeForgeResponse) String() string { func (*UnsubscribeForgeResponse) ProtoMessage() {} func (x *UnsubscribeForgeResponse) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[30] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[31] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2744,7 +2814,7 @@ func (x *UnsubscribeForgeResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UnsubscribeForgeResponse.ProtoReflect.Descriptor instead. func (*UnsubscribeForgeResponse) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{30} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{31} } // One agent-initiated board call. `call_id` is the agent-minted correlation id @@ -2765,7 +2835,7 @@ type BoardCallRequest struct { func (x *BoardCallRequest) Reset() { *x = BoardCallRequest{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[31] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2777,7 +2847,7 @@ func (x *BoardCallRequest) String() string { func (*BoardCallRequest) ProtoMessage() {} func (x *BoardCallRequest) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[31] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[32] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2790,7 +2860,7 @@ func (x *BoardCallRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BoardCallRequest.ProtoReflect.Descriptor instead. func (*BoardCallRequest) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{31} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{32} } func (x *BoardCallRequest) GetCallId() string { @@ -2843,7 +2913,7 @@ type SetIssueStateRequest struct { func (x *SetIssueStateRequest) Reset() { *x = SetIssueStateRequest{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[32] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2855,7 +2925,7 @@ func (x *SetIssueStateRequest) String() string { func (*SetIssueStateRequest) ProtoMessage() {} func (x *SetIssueStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[32] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[33] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2868,7 +2938,7 @@ func (x *SetIssueStateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SetIssueStateRequest.ProtoReflect.Descriptor instead. func (*SetIssueStateRequest) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{32} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{33} } func (x *SetIssueStateRequest) GetIssueId() string { @@ -2895,7 +2965,7 @@ type SetIssueStateResponse struct { func (x *SetIssueStateResponse) Reset() { *x = SetIssueStateResponse{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[33] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2907,7 +2977,7 @@ func (x *SetIssueStateResponse) String() string { func (*SetIssueStateResponse) ProtoMessage() {} func (x *SetIssueStateResponse) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[33] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[34] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2920,7 +2990,7 @@ func (x *SetIssueStateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SetIssueStateResponse.ProtoReflect.Descriptor instead. func (*SetIssueStateResponse) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{33} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{34} } func (x *SetIssueStateResponse) GetIssue() *v1.Issue { @@ -2948,7 +3018,7 @@ type BoardCallResult struct { func (x *BoardCallResult) Reset() { *x = BoardCallResult{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[34] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2960,7 +3030,7 @@ func (x *BoardCallResult) String() string { func (*BoardCallResult) ProtoMessage() {} func (x *BoardCallResult) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[34] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[35] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2973,7 +3043,7 @@ func (x *BoardCallResult) ProtoReflect() protoreflect.Message { // Deprecated: Use BoardCallResult.ProtoReflect.Descriptor instead. func (*BoardCallResult) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{34} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{35} } func (x *BoardCallResult) GetCallId() string { @@ -3037,7 +3107,7 @@ type BoardCallError struct { func (x *BoardCallError) Reset() { *x = BoardCallError{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[35] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3049,7 +3119,7 @@ func (x *BoardCallError) String() string { func (*BoardCallError) ProtoMessage() {} func (x *BoardCallError) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[35] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[36] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3062,7 +3132,7 @@ func (x *BoardCallError) ProtoReflect() protoreflect.Message { // Deprecated: Use BoardCallError.ProtoReflect.Descriptor instead. func (*BoardCallError) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{35} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{36} } func (x *BoardCallError) GetCode() string { @@ -3092,7 +3162,7 @@ type PublishFrameRequest struct { func (x *PublishFrameRequest) Reset() { *x = PublishFrameRequest{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[36] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3104,7 +3174,7 @@ func (x *PublishFrameRequest) String() string { func (*PublishFrameRequest) ProtoMessage() {} func (x *PublishFrameRequest) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[36] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[37] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3117,7 +3187,7 @@ func (x *PublishFrameRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PublishFrameRequest.ProtoReflect.Descriptor instead. func (*PublishFrameRequest) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{36} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{37} } func (x *PublishFrameRequest) GetFrame() *AgentFrame { @@ -3136,7 +3206,7 @@ type PublishFrameResponse struct { func (x *PublishFrameResponse) Reset() { *x = PublishFrameResponse{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[37] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3148,7 +3218,7 @@ func (x *PublishFrameResponse) String() string { func (*PublishFrameResponse) ProtoMessage() {} func (x *PublishFrameResponse) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[37] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[38] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3161,7 +3231,7 @@ func (x *PublishFrameResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PublishFrameResponse.ProtoReflect.Descriptor instead. func (*PublishFrameResponse) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{37} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{38} } // The durable-frame unary carries the SAME AgentFrame message, constrained by @@ -3183,7 +3253,7 @@ type PostConversationFrameRequest struct { func (x *PostConversationFrameRequest) Reset() { *x = PostConversationFrameRequest{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[38] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3195,7 +3265,7 @@ func (x *PostConversationFrameRequest) String() string { func (*PostConversationFrameRequest) ProtoMessage() {} func (x *PostConversationFrameRequest) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[38] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[39] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3208,7 +3278,7 @@ func (x *PostConversationFrameRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PostConversationFrameRequest.ProtoReflect.Descriptor instead. func (*PostConversationFrameRequest) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{38} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{39} } func (x *PostConversationFrameRequest) GetFrame() *AgentFrame { @@ -3234,7 +3304,7 @@ type PostConversationFrameResponse struct { func (x *PostConversationFrameResponse) Reset() { *x = PostConversationFrameResponse{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[39] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3246,7 +3316,7 @@ func (x *PostConversationFrameResponse) String() string { func (*PostConversationFrameResponse) ProtoMessage() {} func (x *PostConversationFrameResponse) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[39] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[40] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3259,7 +3329,7 @@ func (x *PostConversationFrameResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PostConversationFrameResponse.ProtoReflect.Descriptor instead. func (*PostConversationFrameResponse) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{39} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{40} } // The Control subscribe request carries no session id: the per-container socket @@ -3272,7 +3342,7 @@ type ControlSubscribeRequest struct { func (x *ControlSubscribeRequest) Reset() { *x = ControlSubscribeRequest{} - mi := &file_compass_v1_agent_gateway_proto_msgTypes[40] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3284,7 +3354,7 @@ func (x *ControlSubscribeRequest) String() string { func (*ControlSubscribeRequest) ProtoMessage() {} func (x *ControlSubscribeRequest) ProtoReflect() protoreflect.Message { - mi := &file_compass_v1_agent_gateway_proto_msgTypes[40] + mi := &file_compass_v1_agent_gateway_proto_msgTypes[41] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3297,7 +3367,7 @@ func (x *ControlSubscribeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ControlSubscribeRequest.ProtoReflect.Descriptor instead. func (*ControlSubscribeRequest) Descriptor() ([]byte, []int) { - return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{40} + return file_compass_v1_agent_gateway_proto_rawDescGZIP(), []int{41} } var File_compass_v1_agent_gateway_proto protoreflect.FileDescriptor @@ -3428,14 +3498,19 @@ const file_compass_v1_agent_gateway_proto_rawDesc = "" + "\x06labels\x18\x03 \x03(\tR\x06labels\x12\x14\n" + "\x05limit\x18\x04 \x01(\rR\x05limit\"?\n" + "\x12ListIssuesResponse\x12)\n" + - "\x06issues\x18\x01 \x03(\v2\x11.compass.v1.IssueR\x06issues\"\xa4\x01\n" + + "\x06issues\x18\x01 \x03(\v2\x11.compass.v1.IssueR\x06issues\"\xdc\x01\n" + "\x18CreatePullRequestRequest\x12\x12\n" + "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x14\n" + "\x05title\x18\x02 \x01(\tR\x05title\x12\x12\n" + "\x04body\x18\x03 \x01(\tR\x04body\x12\x19\n" + "\bhead_ref\x18\x04 \x01(\tR\aheadRef\x12\x19\n" + "\bbase_ref\x18\x05 \x01(\tR\abaseRef\x12\x14\n" + - "\x05draft\x18\x06 \x01(\bR\x05draft\"b\n" + + "\x05draft\x18\x06 \x01(\bR\x05draft\x126\n" + + "\x05issue\x18\a \x01(\v2 .compass.v1.PullRequestIssueLinkR\x05issue\"n\n" + + "\x14PullRequestIssueLink\x12*\n" + + "\x05forge\x18\x01 \x01(\v2\x14.compass.v1.ForgeRefR\x05forge\x12\x12\n" + + "\x04repo\x18\x02 \x01(\tR\x04repo\x12\x16\n" + + "\x06number\x18\x03 \x01(\x04R\x06number\"b\n" + "\x1bCommentOnPullRequestRequest\x12\x12\n" + "\x04repo\x18\x01 \x01(\tR\x04repo\x12\x1b\n" + "\tpr_number\x18\x02 \x01(\x04R\bprNumber\x12\x12\n" + @@ -3526,7 +3601,7 @@ func file_compass_v1_agent_gateway_proto_rawDescGZIP() []byte { } var file_compass_v1_agent_gateway_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_compass_v1_agent_gateway_proto_msgTypes = make([]protoimpl.MessageInfo, 41) +var file_compass_v1_agent_gateway_proto_msgTypes = make([]protoimpl.MessageInfo, 42) var file_compass_v1_agent_gateway_proto_goTypes = []any{ (ForgeSubscriptionScope)(0), // 0: compass.v1.ForgeSubscriptionScope (*CommsCallRequest)(nil), // 1: compass.v1.CommsCallRequest @@ -3550,72 +3625,73 @@ var file_compass_v1_agent_gateway_proto_goTypes = []any{ (*ListIssuesRequest)(nil), // 19: compass.v1.ListIssuesRequest (*ListIssuesResponse)(nil), // 20: compass.v1.ListIssuesResponse (*CreatePullRequestRequest)(nil), // 21: compass.v1.CreatePullRequestRequest - (*CommentOnPullRequestRequest)(nil), // 22: compass.v1.CommentOnPullRequestRequest - (*GetPullRequestRequest)(nil), // 23: compass.v1.GetPullRequestRequest - (*SubmitReviewRequest)(nil), // 24: compass.v1.SubmitReviewRequest - (*ReviewCommentInput)(nil), // 25: compass.v1.ReviewCommentInput - (*TransitionIssueStateRequest)(nil), // 26: compass.v1.TransitionIssueStateRequest - (*TransitionPullRequestStateRequest)(nil), // 27: compass.v1.TransitionPullRequestStateRequest - (*SubscribeForgeRequest)(nil), // 28: compass.v1.SubscribeForgeRequest - (*SubscribeForgeResponse)(nil), // 29: compass.v1.SubscribeForgeResponse - (*UnsubscribeForgeRequest)(nil), // 30: compass.v1.UnsubscribeForgeRequest - (*UnsubscribeForgeResponse)(nil), // 31: compass.v1.UnsubscribeForgeResponse - (*BoardCallRequest)(nil), // 32: compass.v1.BoardCallRequest - (*SetIssueStateRequest)(nil), // 33: compass.v1.SetIssueStateRequest - (*SetIssueStateResponse)(nil), // 34: compass.v1.SetIssueStateResponse - (*BoardCallResult)(nil), // 35: compass.v1.BoardCallResult - (*BoardCallError)(nil), // 36: compass.v1.BoardCallError - (*PublishFrameRequest)(nil), // 37: compass.v1.PublishFrameRequest - (*PublishFrameResponse)(nil), // 38: compass.v1.PublishFrameResponse - (*PostConversationFrameRequest)(nil), // 39: compass.v1.PostConversationFrameRequest - (*PostConversationFrameResponse)(nil), // 40: compass.v1.PostConversationFrameResponse - (*ControlSubscribeRequest)(nil), // 41: compass.v1.ControlSubscribeRequest - (*v1.PostMessageRequest)(nil), // 42: compass.v1.PostMessageRequest - (*v1.ListMessagesRequest)(nil), // 43: compass.v1.ListMessagesRequest - (*v1.GetRosterRequest)(nil), // 44: compass.v1.GetRosterRequest - (*v1.UpdatePinnedBoardRequest)(nil), // 45: compass.v1.UpdatePinnedBoardRequest - (*v1.CreateChannelRequest)(nil), // 46: compass.v1.CreateChannelRequest - (*v1.UpdateChannelMembersRequest)(nil), // 47: compass.v1.UpdateChannelMembersRequest - (*v1.CreateChannelGroupRequest)(nil), // 48: compass.v1.CreateChannelGroupRequest - (*v1.OpenDMRequest)(nil), // 49: compass.v1.OpenDMRequest - (*v1.PostMessageResponse)(nil), // 50: compass.v1.PostMessageResponse - (*v1.ListMessagesResponse)(nil), // 51: compass.v1.ListMessagesResponse - (*v1.GetRosterResponse)(nil), // 52: compass.v1.GetRosterResponse - (*v1.UpdatePinnedBoardResponse)(nil), // 53: compass.v1.UpdatePinnedBoardResponse - (*v1.CreateChannelResponse)(nil), // 54: compass.v1.CreateChannelResponse - (*v1.UpdateChannelMembersResponse)(nil), // 55: compass.v1.UpdateChannelMembersResponse - (*v1.CreateChannelGroupResponse)(nil), // 56: compass.v1.CreateChannelGroupResponse - (*v1.OpenDMResponse)(nil), // 57: compass.v1.OpenDMResponse - (*v1.ForgeRef)(nil), // 58: compass.v1.ForgeRef - (*v1.Issue)(nil), // 59: compass.v1.Issue - (*CommentRef)(nil), // 60: compass.v1.CommentRef - (*v1.PullRequest)(nil), // 61: compass.v1.PullRequest - (*ReviewRef)(nil), // 62: compass.v1.ReviewRef - (ForgeArtifactKind)(0), // 63: compass.v1.ForgeArtifactKind - (v1.IssueState)(0), // 64: compass.v1.IssueState - (*AgentFrame)(nil), // 65: compass.v1.AgentFrame - (*AgentControl)(nil), // 66: compass.v1.AgentControl + (*PullRequestIssueLink)(nil), // 22: compass.v1.PullRequestIssueLink + (*CommentOnPullRequestRequest)(nil), // 23: compass.v1.CommentOnPullRequestRequest + (*GetPullRequestRequest)(nil), // 24: compass.v1.GetPullRequestRequest + (*SubmitReviewRequest)(nil), // 25: compass.v1.SubmitReviewRequest + (*ReviewCommentInput)(nil), // 26: compass.v1.ReviewCommentInput + (*TransitionIssueStateRequest)(nil), // 27: compass.v1.TransitionIssueStateRequest + (*TransitionPullRequestStateRequest)(nil), // 28: compass.v1.TransitionPullRequestStateRequest + (*SubscribeForgeRequest)(nil), // 29: compass.v1.SubscribeForgeRequest + (*SubscribeForgeResponse)(nil), // 30: compass.v1.SubscribeForgeResponse + (*UnsubscribeForgeRequest)(nil), // 31: compass.v1.UnsubscribeForgeRequest + (*UnsubscribeForgeResponse)(nil), // 32: compass.v1.UnsubscribeForgeResponse + (*BoardCallRequest)(nil), // 33: compass.v1.BoardCallRequest + (*SetIssueStateRequest)(nil), // 34: compass.v1.SetIssueStateRequest + (*SetIssueStateResponse)(nil), // 35: compass.v1.SetIssueStateResponse + (*BoardCallResult)(nil), // 36: compass.v1.BoardCallResult + (*BoardCallError)(nil), // 37: compass.v1.BoardCallError + (*PublishFrameRequest)(nil), // 38: compass.v1.PublishFrameRequest + (*PublishFrameResponse)(nil), // 39: compass.v1.PublishFrameResponse + (*PostConversationFrameRequest)(nil), // 40: compass.v1.PostConversationFrameRequest + (*PostConversationFrameResponse)(nil), // 41: compass.v1.PostConversationFrameResponse + (*ControlSubscribeRequest)(nil), // 42: compass.v1.ControlSubscribeRequest + (*v1.PostMessageRequest)(nil), // 43: compass.v1.PostMessageRequest + (*v1.ListMessagesRequest)(nil), // 44: compass.v1.ListMessagesRequest + (*v1.GetRosterRequest)(nil), // 45: compass.v1.GetRosterRequest + (*v1.UpdatePinnedBoardRequest)(nil), // 46: compass.v1.UpdatePinnedBoardRequest + (*v1.CreateChannelRequest)(nil), // 47: compass.v1.CreateChannelRequest + (*v1.UpdateChannelMembersRequest)(nil), // 48: compass.v1.UpdateChannelMembersRequest + (*v1.CreateChannelGroupRequest)(nil), // 49: compass.v1.CreateChannelGroupRequest + (*v1.OpenDMRequest)(nil), // 50: compass.v1.OpenDMRequest + (*v1.PostMessageResponse)(nil), // 51: compass.v1.PostMessageResponse + (*v1.ListMessagesResponse)(nil), // 52: compass.v1.ListMessagesResponse + (*v1.GetRosterResponse)(nil), // 53: compass.v1.GetRosterResponse + (*v1.UpdatePinnedBoardResponse)(nil), // 54: compass.v1.UpdatePinnedBoardResponse + (*v1.CreateChannelResponse)(nil), // 55: compass.v1.CreateChannelResponse + (*v1.UpdateChannelMembersResponse)(nil), // 56: compass.v1.UpdateChannelMembersResponse + (*v1.CreateChannelGroupResponse)(nil), // 57: compass.v1.CreateChannelGroupResponse + (*v1.OpenDMResponse)(nil), // 58: compass.v1.OpenDMResponse + (*v1.ForgeRef)(nil), // 59: compass.v1.ForgeRef + (*v1.Issue)(nil), // 60: compass.v1.Issue + (*CommentRef)(nil), // 61: compass.v1.CommentRef + (*v1.PullRequest)(nil), // 62: compass.v1.PullRequest + (*ReviewRef)(nil), // 63: compass.v1.ReviewRef + (ForgeArtifactKind)(0), // 64: compass.v1.ForgeArtifactKind + (v1.IssueState)(0), // 65: compass.v1.IssueState + (*AgentFrame)(nil), // 66: compass.v1.AgentFrame + (*AgentControl)(nil), // 67: compass.v1.AgentControl } var file_compass_v1_agent_gateway_proto_depIdxs = []int32{ - 42, // 0: compass.v1.CommsCallRequest.post:type_name -> compass.v1.PostMessageRequest - 43, // 1: compass.v1.CommsCallRequest.list:type_name -> compass.v1.ListMessagesRequest - 44, // 2: compass.v1.CommsCallRequest.roster:type_name -> compass.v1.GetRosterRequest + 43, // 0: compass.v1.CommsCallRequest.post:type_name -> compass.v1.PostMessageRequest + 44, // 1: compass.v1.CommsCallRequest.list:type_name -> compass.v1.ListMessagesRequest + 45, // 2: compass.v1.CommsCallRequest.roster:type_name -> compass.v1.GetRosterRequest 4, // 3: compass.v1.CommsCallRequest.set_status:type_name -> compass.v1.SetAgentStatusRequest - 45, // 4: compass.v1.CommsCallRequest.pin:type_name -> compass.v1.UpdatePinnedBoardRequest - 46, // 5: compass.v1.CommsCallRequest.create_channel:type_name -> compass.v1.CreateChannelRequest - 47, // 6: compass.v1.CommsCallRequest.update_members:type_name -> compass.v1.UpdateChannelMembersRequest - 48, // 7: compass.v1.CommsCallRequest.create_channel_group:type_name -> compass.v1.CreateChannelGroupRequest - 49, // 8: compass.v1.CommsCallRequest.open_dm:type_name -> compass.v1.OpenDMRequest - 50, // 9: compass.v1.CommsCallResult.post:type_name -> compass.v1.PostMessageResponse - 51, // 10: compass.v1.CommsCallResult.list:type_name -> compass.v1.ListMessagesResponse + 46, // 4: compass.v1.CommsCallRequest.pin:type_name -> compass.v1.UpdatePinnedBoardRequest + 47, // 5: compass.v1.CommsCallRequest.create_channel:type_name -> compass.v1.CreateChannelRequest + 48, // 6: compass.v1.CommsCallRequest.update_members:type_name -> compass.v1.UpdateChannelMembersRequest + 49, // 7: compass.v1.CommsCallRequest.create_channel_group:type_name -> compass.v1.CreateChannelGroupRequest + 50, // 8: compass.v1.CommsCallRequest.open_dm:type_name -> compass.v1.OpenDMRequest + 51, // 9: compass.v1.CommsCallResult.post:type_name -> compass.v1.PostMessageResponse + 52, // 10: compass.v1.CommsCallResult.list:type_name -> compass.v1.ListMessagesResponse 3, // 11: compass.v1.CommsCallResult.error:type_name -> compass.v1.CommsCallError - 52, // 12: compass.v1.CommsCallResult.roster:type_name -> compass.v1.GetRosterResponse + 53, // 12: compass.v1.CommsCallResult.roster:type_name -> compass.v1.GetRosterResponse 5, // 13: compass.v1.CommsCallResult.set_status:type_name -> compass.v1.SetAgentStatusResponse - 53, // 14: compass.v1.CommsCallResult.pin:type_name -> compass.v1.UpdatePinnedBoardResponse - 54, // 15: compass.v1.CommsCallResult.create_channel:type_name -> compass.v1.CreateChannelResponse - 55, // 16: compass.v1.CommsCallResult.update_members:type_name -> compass.v1.UpdateChannelMembersResponse - 56, // 17: compass.v1.CommsCallResult.create_channel_group:type_name -> compass.v1.CreateChannelGroupResponse - 57, // 18: compass.v1.CommsCallResult.open_dm:type_name -> compass.v1.OpenDMResponse + 54, // 14: compass.v1.CommsCallResult.pin:type_name -> compass.v1.UpdatePinnedBoardResponse + 55, // 15: compass.v1.CommsCallResult.create_channel:type_name -> compass.v1.CreateChannelResponse + 56, // 16: compass.v1.CommsCallResult.update_members:type_name -> compass.v1.UpdateChannelMembersResponse + 57, // 17: compass.v1.CommsCallResult.create_channel_group:type_name -> compass.v1.CreateChannelGroupResponse + 58, // 18: compass.v1.CommsCallResult.open_dm:type_name -> compass.v1.OpenDMResponse 7, // 19: compass.v1.LifecycleCallRequest.spawn:type_name -> compass.v1.SpawnPeerRequest 9, // 20: compass.v1.LifecycleCallRequest.despawn:type_name -> compass.v1.DespawnPeerRequest 8, // 21: compass.v1.LifecycleCallResult.spawn:type_name -> compass.v1.SpawnPeerResponse @@ -3626,53 +3702,55 @@ var file_compass_v1_agent_gateway_proto_depIdxs = []int32{ 18, // 26: compass.v1.ForgeCallRequest.get_issue:type_name -> compass.v1.GetIssueRequest 19, // 27: compass.v1.ForgeCallRequest.list_issues:type_name -> compass.v1.ListIssuesRequest 21, // 28: compass.v1.ForgeCallRequest.create_pull_request:type_name -> compass.v1.CreatePullRequestRequest - 22, // 29: compass.v1.ForgeCallRequest.comment_on_pull_request:type_name -> compass.v1.CommentOnPullRequestRequest - 23, // 30: compass.v1.ForgeCallRequest.get_pull_request:type_name -> compass.v1.GetPullRequestRequest - 28, // 31: compass.v1.ForgeCallRequest.subscribe:type_name -> compass.v1.SubscribeForgeRequest - 30, // 32: compass.v1.ForgeCallRequest.unsubscribe:type_name -> compass.v1.UnsubscribeForgeRequest - 24, // 33: compass.v1.ForgeCallRequest.submit_review:type_name -> compass.v1.SubmitReviewRequest - 26, // 34: compass.v1.ForgeCallRequest.transition_issue_state:type_name -> compass.v1.TransitionIssueStateRequest - 27, // 35: compass.v1.ForgeCallRequest.transition_pull_request_state:type_name -> compass.v1.TransitionPullRequestStateRequest - 58, // 36: compass.v1.ForgeCallRequest.forge:type_name -> compass.v1.ForgeRef - 59, // 37: compass.v1.ForgeCallResult.issue:type_name -> compass.v1.Issue - 60, // 38: compass.v1.ForgeCallResult.issue_comment:type_name -> compass.v1.CommentRef + 23, // 29: compass.v1.ForgeCallRequest.comment_on_pull_request:type_name -> compass.v1.CommentOnPullRequestRequest + 24, // 30: compass.v1.ForgeCallRequest.get_pull_request:type_name -> compass.v1.GetPullRequestRequest + 29, // 31: compass.v1.ForgeCallRequest.subscribe:type_name -> compass.v1.SubscribeForgeRequest + 31, // 32: compass.v1.ForgeCallRequest.unsubscribe:type_name -> compass.v1.UnsubscribeForgeRequest + 25, // 33: compass.v1.ForgeCallRequest.submit_review:type_name -> compass.v1.SubmitReviewRequest + 27, // 34: compass.v1.ForgeCallRequest.transition_issue_state:type_name -> compass.v1.TransitionIssueStateRequest + 28, // 35: compass.v1.ForgeCallRequest.transition_pull_request_state:type_name -> compass.v1.TransitionPullRequestStateRequest + 59, // 36: compass.v1.ForgeCallRequest.forge:type_name -> compass.v1.ForgeRef + 60, // 37: compass.v1.ForgeCallResult.issue:type_name -> compass.v1.Issue + 61, // 38: compass.v1.ForgeCallResult.issue_comment:type_name -> compass.v1.CommentRef 20, // 39: compass.v1.ForgeCallResult.issues:type_name -> compass.v1.ListIssuesResponse - 61, // 40: compass.v1.ForgeCallResult.pull_request:type_name -> compass.v1.PullRequest - 60, // 41: compass.v1.ForgeCallResult.pr_comment:type_name -> compass.v1.CommentRef - 29, // 42: compass.v1.ForgeCallResult.subscribed:type_name -> compass.v1.SubscribeForgeResponse - 31, // 43: compass.v1.ForgeCallResult.unsubscribed:type_name -> compass.v1.UnsubscribeForgeResponse + 62, // 40: compass.v1.ForgeCallResult.pull_request:type_name -> compass.v1.PullRequest + 61, // 41: compass.v1.ForgeCallResult.pr_comment:type_name -> compass.v1.CommentRef + 30, // 42: compass.v1.ForgeCallResult.subscribed:type_name -> compass.v1.SubscribeForgeResponse + 32, // 43: compass.v1.ForgeCallResult.unsubscribed:type_name -> compass.v1.UnsubscribeForgeResponse 15, // 44: compass.v1.ForgeCallResult.error:type_name -> compass.v1.ForgeCallError - 62, // 45: compass.v1.ForgeCallResult.review:type_name -> compass.v1.ReviewRef - 59, // 46: compass.v1.ListIssuesResponse.issues:type_name -> compass.v1.Issue - 25, // 47: compass.v1.SubmitReviewRequest.comments:type_name -> compass.v1.ReviewCommentInput - 63, // 48: compass.v1.SubscribeForgeRequest.kind:type_name -> compass.v1.ForgeArtifactKind - 0, // 49: compass.v1.SubscribeForgeRequest.scope:type_name -> compass.v1.ForgeSubscriptionScope - 33, // 50: compass.v1.BoardCallRequest.set_issue_state:type_name -> compass.v1.SetIssueStateRequest - 64, // 51: compass.v1.SetIssueStateRequest.state:type_name -> compass.v1.IssueState - 59, // 52: compass.v1.SetIssueStateResponse.issue:type_name -> compass.v1.Issue - 34, // 53: compass.v1.BoardCallResult.set_issue_state:type_name -> compass.v1.SetIssueStateResponse - 36, // 54: compass.v1.BoardCallResult.error:type_name -> compass.v1.BoardCallError - 65, // 55: compass.v1.PublishFrameRequest.frame:type_name -> compass.v1.AgentFrame - 65, // 56: compass.v1.PostConversationFrameRequest.frame:type_name -> compass.v1.AgentFrame - 1, // 57: compass.v1.AgentGateway.Comms:input_type -> compass.v1.CommsCallRequest - 6, // 58: compass.v1.AgentGateway.Lifecycle:input_type -> compass.v1.LifecycleCallRequest - 37, // 59: compass.v1.AgentGateway.Publish:input_type -> compass.v1.PublishFrameRequest - 39, // 60: compass.v1.AgentGateway.PostConversationFrame:input_type -> compass.v1.PostConversationFrameRequest - 41, // 61: compass.v1.AgentGateway.Control:input_type -> compass.v1.ControlSubscribeRequest - 13, // 62: compass.v1.AgentGateway.Forge:input_type -> compass.v1.ForgeCallRequest - 32, // 63: compass.v1.AgentGateway.Board:input_type -> compass.v1.BoardCallRequest - 2, // 64: compass.v1.AgentGateway.Comms:output_type -> compass.v1.CommsCallResult - 11, // 65: compass.v1.AgentGateway.Lifecycle:output_type -> compass.v1.LifecycleCallResult - 38, // 66: compass.v1.AgentGateway.Publish:output_type -> compass.v1.PublishFrameResponse - 40, // 67: compass.v1.AgentGateway.PostConversationFrame:output_type -> compass.v1.PostConversationFrameResponse - 66, // 68: compass.v1.AgentGateway.Control:output_type -> compass.v1.AgentControl - 14, // 69: compass.v1.AgentGateway.Forge:output_type -> compass.v1.ForgeCallResult - 35, // 70: compass.v1.AgentGateway.Board:output_type -> compass.v1.BoardCallResult - 64, // [64:71] is the sub-list for method output_type - 57, // [57:64] is the sub-list for method input_type - 57, // [57:57] is the sub-list for extension type_name - 57, // [57:57] is the sub-list for extension extendee - 0, // [0:57] is the sub-list for field type_name + 63, // 45: compass.v1.ForgeCallResult.review:type_name -> compass.v1.ReviewRef + 60, // 46: compass.v1.ListIssuesResponse.issues:type_name -> compass.v1.Issue + 22, // 47: compass.v1.CreatePullRequestRequest.issue:type_name -> compass.v1.PullRequestIssueLink + 59, // 48: compass.v1.PullRequestIssueLink.forge:type_name -> compass.v1.ForgeRef + 26, // 49: compass.v1.SubmitReviewRequest.comments:type_name -> compass.v1.ReviewCommentInput + 64, // 50: compass.v1.SubscribeForgeRequest.kind:type_name -> compass.v1.ForgeArtifactKind + 0, // 51: compass.v1.SubscribeForgeRequest.scope:type_name -> compass.v1.ForgeSubscriptionScope + 34, // 52: compass.v1.BoardCallRequest.set_issue_state:type_name -> compass.v1.SetIssueStateRequest + 65, // 53: compass.v1.SetIssueStateRequest.state:type_name -> compass.v1.IssueState + 60, // 54: compass.v1.SetIssueStateResponse.issue:type_name -> compass.v1.Issue + 35, // 55: compass.v1.BoardCallResult.set_issue_state:type_name -> compass.v1.SetIssueStateResponse + 37, // 56: compass.v1.BoardCallResult.error:type_name -> compass.v1.BoardCallError + 66, // 57: compass.v1.PublishFrameRequest.frame:type_name -> compass.v1.AgentFrame + 66, // 58: compass.v1.PostConversationFrameRequest.frame:type_name -> compass.v1.AgentFrame + 1, // 59: compass.v1.AgentGateway.Comms:input_type -> compass.v1.CommsCallRequest + 6, // 60: compass.v1.AgentGateway.Lifecycle:input_type -> compass.v1.LifecycleCallRequest + 38, // 61: compass.v1.AgentGateway.Publish:input_type -> compass.v1.PublishFrameRequest + 40, // 62: compass.v1.AgentGateway.PostConversationFrame:input_type -> compass.v1.PostConversationFrameRequest + 42, // 63: compass.v1.AgentGateway.Control:input_type -> compass.v1.ControlSubscribeRequest + 13, // 64: compass.v1.AgentGateway.Forge:input_type -> compass.v1.ForgeCallRequest + 33, // 65: compass.v1.AgentGateway.Board:input_type -> compass.v1.BoardCallRequest + 2, // 66: compass.v1.AgentGateway.Comms:output_type -> compass.v1.CommsCallResult + 11, // 67: compass.v1.AgentGateway.Lifecycle:output_type -> compass.v1.LifecycleCallResult + 39, // 68: compass.v1.AgentGateway.Publish:output_type -> compass.v1.PublishFrameResponse + 41, // 69: compass.v1.AgentGateway.PostConversationFrame:output_type -> compass.v1.PostConversationFrameResponse + 67, // 70: compass.v1.AgentGateway.Control:output_type -> compass.v1.AgentControl + 14, // 71: compass.v1.AgentGateway.Forge:output_type -> compass.v1.ForgeCallResult + 36, // 72: compass.v1.AgentGateway.Board:output_type -> compass.v1.BoardCallResult + 66, // [66:73] is the sub-list for method output_type + 59, // [59:66] is the sub-list for method input_type + 59, // [59:59] is the sub-list for extension type_name + 59, // [59:59] is the sub-list for extension extendee + 0, // [0:59] is the sub-list for field type_name } func init() { file_compass_v1_agent_gateway_proto_init() } @@ -3739,10 +3817,10 @@ func file_compass_v1_agent_gateway_proto_init() { (*ForgeCallResult_Error)(nil), (*ForgeCallResult_Review)(nil), } - file_compass_v1_agent_gateway_proto_msgTypes[31].OneofWrappers = []any{ + file_compass_v1_agent_gateway_proto_msgTypes[32].OneofWrappers = []any{ (*BoardCallRequest_SetIssueState)(nil), } - file_compass_v1_agent_gateway_proto_msgTypes[34].OneofWrappers = []any{ + file_compass_v1_agent_gateway_proto_msgTypes[35].OneofWrappers = []any{ (*BoardCallResult_SetIssueState)(nil), (*BoardCallResult_Error)(nil), } @@ -3752,7 +3830,7 @@ func file_compass_v1_agent_gateway_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_compass_v1_agent_gateway_proto_rawDesc), len(file_compass_v1_agent_gateway_proto_rawDesc)), NumEnums: 1, - NumMessages: 41, + NumMessages: 42, NumExtensions: 0, NumServices: 1, }, diff --git a/go/internal/store/db/models.go b/go/internal/store/db/models.go index 0c1129f06..fbc5c6ac6 100644 --- a/go/internal/store/db/models.go +++ b/go/internal/store/db/models.go @@ -242,15 +242,16 @@ type ForgeAuthoredArtifact struct { } type ForgeRepoSubscription struct { - ForgeProvider int16 - ForgeHost string - Repo string - Enabled bool - SweptUpdatedAt pgtype.Timestamptz - ListEtag string - CreatedAt pgtype.Timestamptz - UpdatedAt pgtype.Timestamptz - TenantID string + ForgeProvider int16 + ForgeHost string + Repo string + Enabled bool + SweptUpdatedAt pgtype.Timestamptz + ListEtag string + PrsBackfilledAt pgtype.Timestamptz + CreatedAt pgtype.Timestamptz + UpdatedAt pgtype.Timestamptz + TenantID string } type ForgeStateTransition struct { @@ -342,6 +343,34 @@ type OwedMention struct { TenantID string } +type PullRequest struct { + ForgeProvider int16 + ForgeHost string + Repo string + Number int64 + ForgeState string + ForgeCreatedAt pgtype.Timestamptz + ForgeUpdatedAt pgtype.Timestamptz + Pr []byte + CreatedAt pgtype.Timestamptz + UpdatedAt pgtype.Timestamptz + TenantID string +} + +type PullRequestIssueLink struct { + PrForgeProvider int16 + PrForgeHost string + PrRepo string + PrNumber int64 + IssueForgeProvider int16 + IssueForgeHost string + IssueRepo string + IssueNumber int64 + Source int16 + CreatedAt pgtype.Timestamptz + TenantID string +} + type Secret struct { Name string ScopeKind int16 diff --git a/go/internal/store/db/pull_requests.sql.go b/go/internal/store/db/pull_requests.sql.go new file mode 100644 index 000000000..2149fb749 --- /dev/null +++ b/go/internal/store/db/pull_requests.sql.go @@ -0,0 +1,392 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 +// source: pull_requests.sql + +package db + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const deleteClosingRefLink = `-- name: DeleteClosingRefLink :exec +DELETE FROM pull_request_issue_links + WHERE pr_forge_provider = $1 AND pr_forge_host = $2 AND pr_repo = $3 AND pr_number = $4 + AND issue_forge_provider = $5 AND issue_forge_host = $6 AND issue_repo = $7 + AND issue_number = $8 AND source = 2 +` + +type DeleteClosingRefLinkParams struct { + PrForgeProvider int16 + PrForgeHost string + PrRepo string + PrNumber int64 + IssueForgeProvider int16 + IssueForgeHost string + IssueRepo string + IssueNumber int64 +} + +func (q *Queries) DeleteClosingRefLink(ctx context.Context, arg DeleteClosingRefLinkParams) error { + _, err := q.db.Exec(ctx, deleteClosingRefLink, + arg.PrForgeProvider, + arg.PrForgeHost, + arg.PrRepo, + arg.PrNumber, + arg.IssueForgeProvider, + arg.IssueForgeHost, + arg.IssueRepo, + arg.IssueNumber, + ) + return err +} + +const fallbackIssuesForTarget = `-- name: FallbackIssuesForTarget :many +SELECT DISTINCT c.issue_forge_provider, c.issue_forge_host, c.issue_repo, c.issue_number + FROM pull_request_issue_links e + JOIN pull_request_issue_links c + ON c.tenant_id = e.tenant_id AND c.pr_forge_provider = e.pr_forge_provider + AND c.pr_forge_host = e.pr_forge_host AND c.pr_repo = e.pr_repo AND c.pr_number = e.pr_number + WHERE e.source = 1 AND c.source = 2 + AND e.issue_forge_provider = $1 AND e.issue_forge_host = $2 + AND e.issue_repo = $3 AND e.issue_number = $4 + ORDER BY c.issue_forge_provider, c.issue_forge_host, c.issue_repo, c.issue_number +` + +type FallbackIssuesForTargetParams struct { + IssueForgeProvider int16 + IssueForgeHost string + IssueRepo string + IssueNumber int64 +} + +type FallbackIssuesForTargetRow struct { + IssueForgeProvider int16 + IssueForgeHost string + IssueRepo string + IssueNumber int64 +} + +func (q *Queries) FallbackIssuesForTarget(ctx context.Context, arg FallbackIssuesForTargetParams) ([]FallbackIssuesForTargetRow, error) { + rows, err := q.db.Query(ctx, fallbackIssuesForTarget, + arg.IssueForgeProvider, + arg.IssueForgeHost, + arg.IssueRepo, + arg.IssueNumber, + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []FallbackIssuesForTargetRow + for rows.Next() { + var i FallbackIssuesForTargetRow + if err := rows.Scan( + &i.IssueForgeProvider, + &i.IssueForgeHost, + &i.IssueRepo, + &i.IssueNumber, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const insertClosingRefLink = `-- name: InsertClosingRefLink :exec +INSERT INTO pull_request_issue_links + (pr_forge_provider, pr_forge_host, pr_repo, pr_number, + issue_forge_provider, issue_forge_host, issue_repo, issue_number, source) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 2) +ON CONFLICT (tenant_id, pr_forge_provider, pr_forge_host, pr_repo, pr_number, + issue_forge_provider, issue_forge_host, issue_repo, issue_number) +DO NOTHING +` + +type InsertClosingRefLinkParams struct { + PrForgeProvider int16 + PrForgeHost string + PrRepo string + PrNumber int64 + IssueForgeProvider int16 + IssueForgeHost string + IssueRepo string + IssueNumber int64 +} + +func (q *Queries) InsertClosingRefLink(ctx context.Context, arg InsertClosingRefLinkParams) error { + _, err := q.db.Exec(ctx, insertClosingRefLink, + arg.PrForgeProvider, + arg.PrForgeHost, + arg.PrRepo, + arg.PrNumber, + arg.IssueForgeProvider, + arg.IssueForgeHost, + arg.IssueRepo, + arg.IssueNumber, + ) + return err +} + +const insertPullRequestIfAbsent = `-- name: InsertPullRequestIfAbsent :exec +INSERT INTO pull_requests + (forge_provider, forge_host, repo, number, forge_state, + forge_created_at, forge_updated_at, pr) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8) +ON CONFLICT (tenant_id, forge_provider, forge_host, repo, number) DO NOTHING +` + +type InsertPullRequestIfAbsentParams struct { + ForgeProvider int16 + ForgeHost string + Repo string + Number int64 + ForgeState string + ForgeCreatedAt pgtype.Timestamptz + ForgeUpdatedAt pgtype.Timestamptz + Pr []byte +} + +// The create path's write: a webhook-hydrated row already present always wins. +func (q *Queries) InsertPullRequestIfAbsent(ctx context.Context, arg InsertPullRequestIfAbsentParams) error { + _, err := q.db.Exec(ctx, insertPullRequestIfAbsent, + arg.ForgeProvider, + arg.ForgeHost, + arg.Repo, + arg.Number, + arg.ForgeState, + arg.ForgeCreatedAt, + arg.ForgeUpdatedAt, + arg.Pr, + ) + return err +} + +const listPullRequestLinks = `-- name: ListPullRequestLinks :many +SELECT issue_forge_provider, issue_forge_host, issue_repo, issue_number, source + FROM pull_request_issue_links + WHERE pr_forge_provider = $1 AND pr_forge_host = $2 AND pr_repo = $3 AND pr_number = $4 + ORDER BY issue_forge_provider, issue_forge_host, issue_repo, issue_number +` + +type ListPullRequestLinksParams struct { + PrForgeProvider int16 + PrForgeHost string + PrRepo string + PrNumber int64 +} + +type ListPullRequestLinksRow struct { + IssueForgeProvider int16 + IssueForgeHost string + IssueRepo string + IssueNumber int64 + Source int16 +} + +func (q *Queries) ListPullRequestLinks(ctx context.Context, arg ListPullRequestLinksParams) ([]ListPullRequestLinksRow, error) { + rows, err := q.db.Query(ctx, listPullRequestLinks, + arg.PrForgeProvider, + arg.PrForgeHost, + arg.PrRepo, + arg.PrNumber, + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ListPullRequestLinksRow + for rows.Next() { + var i ListPullRequestLinksRow + if err := rows.Scan( + &i.IssueForgeProvider, + &i.IssueForgeHost, + &i.IssueRepo, + &i.IssueNumber, + &i.Source, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const pullRequestsForIssues = `-- name: PullRequestsForIssues :many +WITH want AS ( + SELECT unnest($1::smallint[]) AS provider, + unnest($2::text[]) AS host, + unnest($3::text[]) AS repo, + unnest($4::bigint[]) AS number +) +SELECT l.issue_forge_provider, l.issue_forge_host, l.issue_repo, l.issue_number, + p.forge_provider, p.forge_host, p.repo, p.number, p.forge_state, + p.forge_created_at, p.forge_updated_at, p.pr + FROM pull_request_issue_links l + JOIN want w + ON l.issue_forge_provider = w.provider AND l.issue_forge_host = w.host + AND l.issue_repo = w.repo AND l.issue_number = w.number + JOIN pull_requests p + ON p.tenant_id = l.tenant_id AND p.forge_provider = l.pr_forge_provider + AND p.forge_host = l.pr_forge_host AND p.repo = l.pr_repo AND p.number = l.pr_number + WHERE l.source = 1 + OR NOT EXISTS ( + SELECT 1 + FROM pull_request_issue_links e + JOIN issues i + ON i.tenant_id = e.tenant_id AND i.forge_provider = e.issue_forge_provider + AND i.forge_host = e.issue_forge_host AND i.repo = e.issue_repo + AND i.number = e.issue_number + WHERE e.source = 1 AND e.tenant_id = l.tenant_id + AND e.pr_forge_provider = l.pr_forge_provider AND e.pr_forge_host = l.pr_forge_host + AND e.pr_repo = l.pr_repo AND e.pr_number = l.pr_number) + ORDER BY p.forge_created_at, p.forge_provider, p.forge_host, p.repo, p.number +` + +type PullRequestsForIssuesParams struct { + IssueProviders []int16 + IssueHosts []string + IssueRepos []string + IssueNumbers []int64 +} + +type PullRequestsForIssuesRow struct { + IssueForgeProvider int16 + IssueForgeHost string + IssueRepo string + IssueNumber int64 + ForgeProvider int16 + ForgeHost string + Repo string + Number int64 + ForgeState string + ForgeCreatedAt pgtype.Timestamptz + ForgeUpdatedAt pgtype.Timestamptz + Pr []byte +} + +// Explicit links attach directly; a closing reference attaches only when none +// of the PR's explicit targets is an issue on the board. +func (q *Queries) PullRequestsForIssues(ctx context.Context, arg PullRequestsForIssuesParams) ([]PullRequestsForIssuesRow, error) { + rows, err := q.db.Query(ctx, pullRequestsForIssues, + arg.IssueProviders, + arg.IssueHosts, + arg.IssueRepos, + arg.IssueNumbers, + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []PullRequestsForIssuesRow + for rows.Next() { + var i PullRequestsForIssuesRow + if err := rows.Scan( + &i.IssueForgeProvider, + &i.IssueForgeHost, + &i.IssueRepo, + &i.IssueNumber, + &i.ForgeProvider, + &i.ForgeHost, + &i.Repo, + &i.Number, + &i.ForgeState, + &i.ForgeCreatedAt, + &i.ForgeUpdatedAt, + &i.Pr, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const upsertExplicitPullRequestLink = `-- name: UpsertExplicitPullRequestLink :exec +INSERT INTO pull_request_issue_links + (pr_forge_provider, pr_forge_host, pr_repo, pr_number, + issue_forge_provider, issue_forge_host, issue_repo, issue_number, source) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 1) +ON CONFLICT (tenant_id, pr_forge_provider, pr_forge_host, pr_repo, pr_number, + issue_forge_provider, issue_forge_host, issue_repo, issue_number) +DO UPDATE SET source = 1 +` + +type UpsertExplicitPullRequestLinkParams struct { + PrForgeProvider int16 + PrForgeHost string + PrRepo string + PrNumber int64 + IssueForgeProvider int16 + IssueForgeHost string + IssueRepo string + IssueNumber int64 +} + +func (q *Queries) UpsertExplicitPullRequestLink(ctx context.Context, arg UpsertExplicitPullRequestLinkParams) error { + _, err := q.db.Exec(ctx, upsertExplicitPullRequestLink, + arg.PrForgeProvider, + arg.PrForgeHost, + arg.PrRepo, + arg.PrNumber, + arg.IssueForgeProvider, + arg.IssueForgeHost, + arg.IssueRepo, + arg.IssueNumber, + ) + return err +} + +const upsertPullRequestGuarded = `-- name: UpsertPullRequestGuarded :execrows +INSERT INTO pull_requests + (forge_provider, forge_host, repo, number, forge_state, + forge_created_at, forge_updated_at, pr) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8) +ON CONFLICT (tenant_id, forge_provider, forge_host, repo, number) DO UPDATE + SET forge_state = EXCLUDED.forge_state, + forge_created_at = EXCLUDED.forge_created_at, + forge_updated_at = EXCLUDED.forge_updated_at, + pr = EXCLUDED.pr + WHERE EXCLUDED.forge_updated_at >= pull_requests.forge_updated_at +` + +type UpsertPullRequestGuardedParams struct { + ForgeProvider int16 + ForgeHost string + Repo string + Number int64 + ForgeState string + ForgeCreatedAt pgtype.Timestamptz + ForgeUpdatedAt pgtype.Timestamptz + Pr []byte +} + +// Zero rows affected means the stored row is newer and the write was skipped. +func (q *Queries) UpsertPullRequestGuarded(ctx context.Context, arg UpsertPullRequestGuardedParams) (int64, error) { + result, err := q.db.Exec(ctx, upsertPullRequestGuarded, + arg.ForgeProvider, + arg.ForgeHost, + arg.Repo, + arg.Number, + arg.ForgeState, + arg.ForgeCreatedAt, + arg.ForgeUpdatedAt, + arg.Pr, + ) + if err != nil { + return 0, err + } + return result.RowsAffected(), nil +} diff --git a/go/internal/store/db/querier.go b/go/internal/store/db/querier.go index abb45f854..79c3014ba 100644 --- a/go/internal/store/db/querier.go +++ b/go/internal/store/db/querier.go @@ -128,6 +128,7 @@ type Querier interface { DeleteChannelMember(ctx context.Context, arg DeleteChannelMemberParams) (int64, error) DeleteChannelPin(ctx context.Context, arg DeleteChannelPinParams) error DeleteChannelPinReturningPosition(ctx context.Context, arg DeleteChannelPinReturningPositionParams) (int32, error) + DeleteClosingRefLink(ctx context.Context, arg DeleteClosingRefLinkParams) error // DeleteComputeUsageIntervalsBefore removes both events only when the end is old. // It runs as the system role, so the tenant predicate is the only tenant scope. DeleteComputeUsageIntervalsBefore(ctx context.Context, arg DeleteComputeUsageIntervalsBeforeParams) (int64, error) @@ -180,6 +181,7 @@ type Querier interface { // Its created_at is the best start we hold, so the start is marked estimated. EnsureComputeUsageIntervalStart(ctx context.Context, agentAccountID string) error EnsureForgeRepoSubscription(ctx context.Context, arg EnsureForgeRepoSubscriptionParams) error + FallbackIssuesForTarget(ctx context.Context, arg FallbackIssuesForTargetParams) ([]FallbackIssuesForTargetRow, error) FindAskMessage(ctx context.Context, arg FindAskMessageParams) ([]FindAskMessageRow, error) ForgeScopeUserExists(ctx context.Context, accountID string) (bool, error) // Collects the coordinate's cursor IFF no subscription for it remains (the NOT @@ -300,6 +302,7 @@ type Querier interface { // An agent's group lives in its owner's namespace, which keys top-level names. InsertChannelGroup(ctx context.Context, arg InsertChannelGroupParams) (string, error) InsertChannelPin(ctx context.Context, arg InsertChannelPinParams) error + InsertClosingRefLink(ctx context.Context, arg InsertClosingRefLinkParams) error InsertCoordinationChannel(ctx context.Context, arg InsertCoordinationChannelParams) (string, error) InsertCoordinationGroup(ctx context.Context, arg InsertCoordinationGroupParams) error // Born kind=DM, zero-value policy (OPEN, ownerless) + mandatory; poison-free via @@ -318,6 +321,8 @@ type Querier interface { // RETURNING) rather than clobbering the winner. The seeded version is 1. InsertModelRegistry(ctx context.Context, registry []byte) (int64, error) InsertOwnerDMGroup(ctx context.Context, arg InsertOwnerDMGroupParams) error + // The create path's write: a webhook-hydrated row already present always wins. + InsertPullRequestIfAbsent(ctx context.Context, arg InsertPullRequestIfAbsentParams) error InsertServerKeyState(ctx context.Context, arg InsertServerKeyStateParams) error // Server-secrets registry queries (design record T0, mechanism C1/D6). The // SERVER-owned half of the names-only secret registry, physically separate from @@ -378,6 +383,7 @@ type Querier interface { ListForgeScopeRepos(ctx context.Context, arg ListForgeScopeReposParams) ([]string, error) ListIssues(ctx context.Context) ([]ListIssuesRow, error) ListMessages(ctx context.Context, arg ListMessagesParams) ([]ListMessagesRow, error) + ListPullRequestLinks(ctx context.Context, arg ListPullRequestLinksParams) ([]ListPullRequestLinksRow, error) // ListTenantIDs lists every tenant. tenants has no row-level security, so the // app role sees them all without the BYPASSRLS system role. ListTenantIDs(ctx context.Context) ([]string, error) @@ -492,6 +498,9 @@ type Querier interface { PinnedEntries(ctx context.Context, channelID string) ([]PinnedEntriesRow, error) PlacementForAgent(ctx context.Context, agentAccountID string) (PlacementForAgentRow, error) PruneTranscriptEntries(ctx context.Context, arg PruneTranscriptEntriesParams) error + // Explicit links attach directly; a closing reference attaches only when none + // of the PR's explicit targets is an issue on the board. + PullRequestsForIssues(ctx context.Context, arg PullRequestsForIssuesParams) ([]PullRequestsForIssuesRow, error) // Agent config-bundle queries (sqlc adoption T5, RIG-3034). These replace the // inline SQL literals in internal/store/agent_config.go; the hand-written Store // methods keep their signatures and own the bundle validation/hash and the @@ -669,6 +678,7 @@ type Querier interface { UpdateModelRegistry(ctx context.Context, arg UpdateModelRegistryParams) (int64, error) UpdateTopicLastSeq(ctx context.Context, arg UpdateTopicLastSeqParams) error UpsertChannelMember(ctx context.Context, arg UpsertChannelMemberParams) error + UpsertExplicitPullRequestLink(ctx context.Context, arg UpsertExplicitPullRequestLinkParams) error UpsertForgeArtifactCursor(ctx context.Context, arg UpsertForgeArtifactCursorParams) error // Issue-domain queries (sqlc adoption T6, RIG-3034). These replace the inline // SQL literals in internal/store/issues.go; the hand-written Store methods keep @@ -688,6 +698,8 @@ type Querier interface { // ErrNotFound/ErrInvalidArgument mapping. The LinearAgentSession read maps the // generated nullable fields back to LinearAgentSessionRow inline. UpsertLinearAgentSession(ctx context.Context, arg UpsertLinearAgentSessionParams) (int64, error) + // Zero rows affected means the stored row is newer and the write was skipped. + UpsertPullRequestGuarded(ctx context.Context, arg UpsertPullRequestGuardedParams) (int64, error) // UpsertSecret writes declaration+value in one row and, on a re-write of an // existing (name, scope_kind, scope_id), rewrites value/nonce/key_version and the // routing metadata. updated_at is maintained by the set_updated_at trigger, which diff --git a/go/internal/store/forge_authored.go b/go/internal/store/forge_authored.go index 25eeb086e..17c198016 100644 --- a/go/internal/store/forge_authored.go +++ b/go/internal/store/forge_authored.go @@ -83,7 +83,13 @@ func (s *Store) RecordAuthoredArtifact(ctx context.Context, a AuthoredArtifact) if err := a.valid(); err != nil { return err } - if err := s.q.RecordAuthoredArtifact(ctx, db.RecordAuthoredArtifactParams{ + return recordAuthoredArtifact(ctx, s.q, a) +} + +// recordAuthoredArtifact writes a validated row through q, so a caller's +// transaction can include it. +func recordAuthoredArtifact(ctx context.Context, q *db.Queries, a AuthoredArtifact) error { + if err := q.RecordAuthoredArtifact(ctx, db.RecordAuthoredArtifactParams{ ForgeProvider: int16(a.Provider), //nolint:gosec // G115: ForgeProvider is a CHECK-constrained 1..4 enum (forge_authored_artifacts.forge_provider), always within int16 ForgeHost: a.Host, Repo: a.Repo, diff --git a/go/internal/store/migrations/0001_init.sql b/go/internal/store/migrations/0001_init.sql index f13a75a63..ad7af0541 100644 --- a/go/internal/store/migrations/0001_init.sql +++ b/go/internal/store/migrations/0001_init.sql @@ -982,12 +982,50 @@ CREATE TABLE forge_repo_subscriptions ( enabled BOOLEAN NOT NULL DEFAULT TRUE, swept_updated_at TIMESTAMPTZ, -- last swept forge updated_at watermark; NULL = never swept list_etag TEXT NOT NULL DEFAULT '', -- conditional-GET etag for the repo LIST walk + prs_backfilled_at TIMESTAMPTZ, -- PR backfill pass done; NULL = not yet run created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), tenant_id TEXT NOT NULL DEFAULT current_setting('compass.tenant_id', TRUE), PRIMARY KEY (tenant_id, forge_provider, forge_host, repo) ); +-- pull_requests holds each ingested PR whole; pull_request_issue_links maps PRs +-- to the issues they work on (source 1 explicit, 2 closing reference). +CREATE TABLE pull_requests ( + forge_provider SMALLINT NOT NULL CHECK (forge_provider IN (1, 2, 3)), + forge_host TEXT NOT NULL, + repo TEXT NOT NULL CHECK (repo <> ''), + number BIGINT NOT NULL, + forge_state TEXT NOT NULL, + forge_created_at TIMESTAMPTZ NOT NULL, + forge_updated_at TIMESTAMPTZ NOT NULL, + pr JSONB NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + tenant_id TEXT NOT NULL DEFAULT current_setting('compass.tenant_id', TRUE) REFERENCES tenants (id) ON DELETE RESTRICT, + PRIMARY KEY (tenant_id, forge_provider, forge_host, repo, number) +); + +-- No foreign keys: the issue may live on Linear or not be ingested yet. +CREATE TABLE pull_request_issue_links ( + pr_forge_provider SMALLINT NOT NULL CHECK (pr_forge_provider IN (1, 2, 3)), + pr_forge_host TEXT NOT NULL, + pr_repo TEXT NOT NULL CHECK (pr_repo <> ''), + pr_number BIGINT NOT NULL, + issue_forge_provider SMALLINT NOT NULL CHECK (issue_forge_provider IN (1, 2, 3, 4)), + issue_forge_host TEXT NOT NULL, + issue_repo TEXT NOT NULL CHECK (issue_repo <> ''), + issue_number BIGINT NOT NULL, + source SMALLINT NOT NULL CHECK (source IN (1, 2)), + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + tenant_id TEXT NOT NULL DEFAULT current_setting('compass.tenant_id', TRUE) REFERENCES tenants (id) ON DELETE RESTRICT, + PRIMARY KEY (tenant_id, pr_forge_provider, pr_forge_host, pr_repo, pr_number, + issue_forge_provider, issue_forge_host, issue_repo, issue_number) +); + +CREATE INDEX pull_request_issue_links_issue_idx ON pull_request_issue_links + (tenant_id, issue_forge_provider, issue_forge_host, issue_repo, issue_number); + -- Owner-managed allowlist of forge coordinates an account may write. CREATE TABLE account_forge_scopes ( tenant_id TEXT NOT NULL DEFAULT current_setting('compass.tenant_id', TRUE) REFERENCES tenants (id) ON DELETE RESTRICT, @@ -1488,6 +1526,8 @@ GRANT SELECT, INSERT, UPDATE, DELETE compute_usage_prune_horizon TO compass_app, compass_system; GRANT SELECT, INSERT, DELETE ON account_forge_scopes TO compass_app, compass_system; +-- A PR row is never deleted; its links are replaced through closing-ref sync. +REVOKE DELETE ON pull_requests FROM compass_app, compass_system; REVOKE UPDATE ON account_forge_scopes FROM compass_app, compass_system; -- server_key_state withholds DELETE, revoked here because the schema-wide grant -- above hands it out with everything else. The tripwire row is written once at @@ -1529,6 +1569,7 @@ DECLARE 'linear_agent_sessions', 'issues', 'forge_repo_subscriptions', 'forge_artifact_cursors', 'forge_state_transitions', 'account_forge_scopes', + 'pull_requests', 'pull_request_issue_links', 'token_usage_events', 'token_usage_rollups_hourly', 'token_usage_rollups_daily', 'compute_usage_events', 'compute_usage_rollups_hourly', 'compute_usage_rollups_daily', 'account_tour_state' ]; @@ -1611,6 +1652,7 @@ DECLARE 'model_registry', 'forge_repo_subscriptions', 'forge_state_transitions', + 'pull_requests', 'server_secrets', 'server_key_state', 'compute_usage_rollups_hourly', diff --git a/go/internal/store/pull_requests.go b/go/internal/store/pull_requests.go new file mode 100644 index 000000000..7cf9421dd --- /dev/null +++ b/go/internal/store/pull_requests.go @@ -0,0 +1,324 @@ +package store + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/jackc/pgx/v5/pgtype" + + "github.com/RigelBuild/compass/go/internal/store/db" +) + +// ForgeCoord names one forge issue or PR. It is comparable, so it keys maps. +type ForgeCoord struct { + Provider ForgeProvider + Host string + Repo string + Number uint64 +} + +// PullRequestRow is one stored PR: the scalars the store reads plus the whole +// PR as protojson bytes. +type PullRequestRow struct { + Coord ForgeCoord + State string + CreatedAt time.Time + UpdatedAt time.Time + PR []byte +} + +const ( + linkSourceExplicit int16 = 1 + linkSourceClosingRef int16 = 2 +) + +// normalized trims and, on GitHub, lowercases the repo so board joins never miss on case. +func (c ForgeCoord) normalized() ForgeCoord { + c.Repo = strings.TrimSpace(c.Repo) + if c.Provider == ForgeProviderGitHub { + c.Repo = strings.ToLower(c.Repo) + } + return c +} + +func (c ForgeCoord) valid() error { + if err := validCoordinate(c.Provider, c.Host, c.Repo); err != nil { + return err + } + if c.Number == 0 { + return fmt.Errorf("%w: number is required", ErrInvalidArgument) + } + return nil +} + +// forgeCoordDB is a coordinate in its column types. +type forgeCoordDB struct { + provider int16 + host string + repo string + number int64 +} + +func (c ForgeCoord) db() forgeCoordDB { + return forgeCoordDB{ + provider: int16(c.Provider), //nolint:gosec // G115: ForgeProvider is a CHECK-constrained 1..4 enum, always within int16 + host: c.Host, + repo: c.Repo, + number: int64(c.Number), //nolint:gosec // G115: a canonical forge issue/PR number, well within int64 + } +} + +func coordFromDB(provider int16, host, repo string, number int64) ForgeCoord { + return ForgeCoord{ + Provider: ForgeProvider(provider), + Host: host, + Repo: repo, + Number: uint64(number), //nolint:gosec // G115: written only from a uint64 forge number + } +} + +func (pr PullRequestRow) normalized() (PullRequestRow, error) { + pr.Coord = pr.Coord.normalized() + if err := pr.Coord.valid(); err != nil { + return PullRequestRow{}, err + } + if len(pr.PR) == 0 { + return PullRequestRow{}, fmt.Errorf("%w: pull request body is required", ErrInvalidArgument) + } + if pr.CreatedAt.IsZero() || pr.UpdatedAt.IsZero() { + return PullRequestRow{}, fmt.Errorf("%w: forge created and updated times are required", ErrInvalidArgument) + } + return pr, nil +} + +// normalizeCoords validates and normalizes coordinates, dropping duplicates. +func normalizeCoords(in []ForgeCoord) ([]ForgeCoord, error) { + out := make([]ForgeCoord, 0, len(in)) + seen := make(map[ForgeCoord]bool, len(in)) + for _, c := range in { + c = c.normalized() + if err := c.valid(); err != nil { + return nil, err + } + if !seen[c] { + seen[c] = true + out = append(out, c) + } + } + return out, nil +} + +func timestamptz(t time.Time) pgtype.Timestamptz { + return pgtype.Timestamptz{Time: t, Valid: true} +} + +// CreatePullRequestWithLink records an agent-created PR in one transaction: the +// ownership row, the PR row (an existing hydrated row wins) and, when issue is +// set, the explicit link. The ownership row keeps the caller's repo casing. +func (s *Store) CreatePullRequestWithLink(ctx context.Context, a AuthoredArtifact, pr PullRequestRow, issue *ForgeCoord) error { + pr, err := pr.normalized() + if err != nil { + return err + } + if err := a.valid(); err != nil { + return err + } + authored := ForgeCoord{Provider: a.Provider, Host: a.Host, Repo: a.Repo, Number: a.Number}.normalized() + if a.Kind != ForgeArtifactKindPullRequest || authored != pr.Coord { + return fmt.Errorf("%w: authored artifact does not name the pull request", ErrInvalidArgument) + } + var target ForgeCoord + if issue != nil { + target = issue.normalized() + if err := target.valid(); err != nil { + return err + } + } + + tx, err := s.beginTenantTx(ctx) + if err != nil { + return fmt.Errorf("store: begin create pull request: %w", err) + } + defer func() { _ = tx.Rollback(ctx) }() + qtx := s.q.WithTx(tx) + + if err := recordAuthoredArtifact(ctx, qtx, a); err != nil { + return err + } + p := pr.Coord.db() + if err := qtx.InsertPullRequestIfAbsent(ctx, db.InsertPullRequestIfAbsentParams{ + ForgeProvider: p.provider, ForgeHost: p.host, Repo: p.repo, Number: p.number, + ForgeState: pr.State, ForgeCreatedAt: timestamptz(pr.CreatedAt), + ForgeUpdatedAt: timestamptz(pr.UpdatedAt), Pr: pr.PR, + }); err != nil { + return fmt.Errorf("store: insert pull request: %w", err) + } + if issue != nil { + i := target.db() + if err := qtx.UpsertExplicitPullRequestLink(ctx, db.UpsertExplicitPullRequestLinkParams{ + PrForgeProvider: p.provider, PrForgeHost: p.host, PrRepo: p.repo, PrNumber: p.number, + IssueForgeProvider: i.provider, IssueForgeHost: i.host, IssueRepo: i.repo, IssueNumber: i.number, + }); err != nil { + return fmt.Errorf("store: link pull request: %w", err) + } + } + if err := tx.Commit(ctx); err != nil { + return fmt.Errorf("store: commit create pull request: %w", err) + } + return nil +} + +// UpsertPullRequest writes a hydrated PR and replaces its closing-reference +// links; explicit links are never removed. It returns every issue linked before +// or after the write. An update older than the stored row is skipped and +// affects nothing. +func (s *Store) UpsertPullRequest(ctx context.Context, pr PullRequestRow, closingRefs []ForgeCoord) ([]ForgeCoord, error) { + pr, err := pr.normalized() + if err != nil { + return nil, err + } + refs, err := normalizeCoords(closingRefs) + if err != nil { + return nil, err + } + + tx, err := s.beginTenantTx(ctx) + if err != nil { + return nil, fmt.Errorf("store: begin upsert pull request: %w", err) + } + defer func() { _ = tx.Rollback(ctx) }() + qtx := s.q.WithTx(tx) + + p := pr.Coord.db() + written, err := qtx.UpsertPullRequestGuarded(ctx, db.UpsertPullRequestGuardedParams{ + ForgeProvider: p.provider, ForgeHost: p.host, Repo: p.repo, Number: p.number, + ForgeState: pr.State, ForgeCreatedAt: timestamptz(pr.CreatedAt), + ForgeUpdatedAt: timestamptz(pr.UpdatedAt), Pr: pr.PR, + }) + if err != nil { + return nil, fmt.Errorf("store: upsert pull request: %w", err) + } + if written == 0 { + return nil, nil + } + affected, err := replaceClosingRefs(ctx, qtx, p, refs) + if err != nil { + return nil, err + } + if err := tx.Commit(ctx); err != nil { + return nil, fmt.Errorf("store: commit upsert pull request: %w", err) + } + return affected, nil +} + +// replaceClosingRefs makes the PR's closing-reference set equal refs and returns +// the union of the issues linked before and after. +func replaceClosingRefs(ctx context.Context, qtx *db.Queries, p forgeCoordDB, refs []ForgeCoord) ([]ForgeCoord, error) { + old, err := qtx.ListPullRequestLinks(ctx, db.ListPullRequestLinksParams{ + PrForgeProvider: p.provider, PrForgeHost: p.host, PrRepo: p.repo, PrNumber: p.number, + }) + if err != nil { + return nil, fmt.Errorf("store: list pull request links: %w", err) + } + keep := make(map[ForgeCoord]bool, len(refs)) + for _, r := range refs { + keep[r] = true + } + seen := make(map[ForgeCoord]bool, len(old)+len(refs)) + affected := make([]ForgeCoord, 0, len(old)+len(refs)) + for _, l := range old { + c := coordFromDB(l.IssueForgeProvider, l.IssueForgeHost, l.IssueRepo, l.IssueNumber) + seen[c] = true + affected = append(affected, c) + if l.Source != linkSourceClosingRef || keep[c] { + continue + } + if err := qtx.DeleteClosingRefLink(ctx, db.DeleteClosingRefLinkParams{ + PrForgeProvider: p.provider, PrForgeHost: p.host, PrRepo: p.repo, PrNumber: p.number, + IssueForgeProvider: l.IssueForgeProvider, IssueForgeHost: l.IssueForgeHost, + IssueRepo: l.IssueRepo, IssueNumber: l.IssueNumber, + }); err != nil { + return nil, fmt.Errorf("store: unlink closing reference: %w", err) + } + } + for _, r := range refs { + i := r.db() + if err := qtx.InsertClosingRefLink(ctx, db.InsertClosingRefLinkParams{ + PrForgeProvider: p.provider, PrForgeHost: p.host, PrRepo: p.repo, PrNumber: p.number, + IssueForgeProvider: i.provider, IssueForgeHost: i.host, IssueRepo: i.repo, IssueNumber: i.number, + }); err != nil { + return nil, fmt.Errorf("store: link closing reference: %w", err) + } + if !seen[r] { + seen[r] = true + affected = append(affected, r) + } + } + return affected, nil +} + +// PullRequestsForIssues returns the PRs attached to each issue, oldest forge +// creation first, keyed by the normalized issue coordinate. A closing reference attaches a PR only when none of its +// explicit targets is a board issue. +func (s *Store) PullRequestsForIssues(ctx context.Context, issues []ForgeCoord) (map[ForgeCoord][]PullRequestRow, error) { + want, err := normalizeCoords(issues) + if err != nil { + return nil, err + } + out := make(map[ForgeCoord][]PullRequestRow, len(want)) + if len(want) == 0 { + return out, nil + } + arg := db.PullRequestsForIssuesParams{ + IssueProviders: make([]int16, 0, len(want)), + IssueHosts: make([]string, 0, len(want)), + IssueRepos: make([]string, 0, len(want)), + IssueNumbers: make([]int64, 0, len(want)), + } + for _, c := range want { + d := c.db() + arg.IssueProviders = append(arg.IssueProviders, d.provider) + arg.IssueHosts = append(arg.IssueHosts, d.host) + arg.IssueRepos = append(arg.IssueRepos, d.repo) + arg.IssueNumbers = append(arg.IssueNumbers, d.number) + } + rows, err := s.q.PullRequestsForIssues(ctx, arg) + if err != nil { + return nil, fmt.Errorf("store: pull requests for issues: %w", err) + } + for _, r := range rows { + issue := coordFromDB(r.IssueForgeProvider, r.IssueForgeHost, r.IssueRepo, r.IssueNumber) + out[issue] = append(out[issue], PullRequestRow{ + Coord: coordFromDB(r.ForgeProvider, r.ForgeHost, r.Repo, r.Number), + State: r.ForgeState, + CreatedAt: r.ForgeCreatedAt.Time, + UpdatedAt: r.ForgeUpdatedAt.Time, + PR: r.Pr, + }) + } + return out, nil +} + +// FallbackIssuesForTarget returns the closing-reference issues of every PR whose +// explicit target is issue: the issues that gain or lose the PR when issue +// enters or leaves the board. +func (s *Store) FallbackIssuesForTarget(ctx context.Context, issue ForgeCoord) ([]ForgeCoord, error) { + issue = issue.normalized() + if err := issue.valid(); err != nil { + return nil, err + } + i := issue.db() + rows, err := s.q.FallbackIssuesForTarget(ctx, db.FallbackIssuesForTargetParams{ + IssueForgeProvider: i.provider, IssueForgeHost: i.host, IssueRepo: i.repo, IssueNumber: i.number, + }) + if err != nil { + return nil, fmt.Errorf("store: fallback issues for target: %w", err) + } + out := make([]ForgeCoord, 0, len(rows)) + for _, r := range rows { + out = append(out, coordFromDB(r.IssueForgeProvider, r.IssueForgeHost, r.IssueRepo, r.IssueNumber)) + } + return out, nil +} diff --git a/go/internal/store/pull_requests_pgtest_test.go b/go/internal/store/pull_requests_pgtest_test.go new file mode 100644 index 000000000..8188032a1 --- /dev/null +++ b/go/internal/store/pull_requests_pgtest_test.go @@ -0,0 +1,303 @@ +//go:build pgtest + +package store + +// PR-to-issue link store contracts: explicit links outrank closing references, +// the forge_updated_at guard, read-time fallback, ordering, RLS and repo case. +// context.Background is the test root (the pgtest-suite convention). + +import ( + "context" + "errors" + "slices" + "testing" + "time" +) + +var prBase = time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC) + +func ghCoord(repo string, n uint64) ForgeCoord { + return ForgeCoord{Provider: ForgeProviderGitHub, Host: "github.com", Repo: repo, Number: n} +} + +func prRow(repo string, n uint64, state string, created, updated time.Time) PullRequestRow { + return PullRequestRow{ + Coord: ghCoord(repo, n), + State: state, + CreatedAt: created, + UpdatedAt: updated, + PR: []byte(`{"state":"` + state + `"}`), + } +} + +func seedBoardIssue(t *testing.T, ctx context.Context, s *Store, c ForgeCoord) { + t.Helper() + if _, err := s.UpsertIssueForgeFields(ctx, IssueForgeFields{ + ForgeProvider: c.Provider, ForgeHost: c.Host, Repo: c.Repo, + Number: uint32(c.Number), Title: "issue", + }); err != nil { + t.Fatalf("seed board issue %v: %v", c, err) + } +} + +func authoredPR(agent, owner AccountID, pr PullRequestRow) AuthoredArtifact { + return AuthoredArtifact{ + Provider: pr.Coord.Provider, Host: pr.Coord.Host, Repo: pr.Coord.Repo, + Kind: ForgeArtifactKindPullRequest, Number: pr.Coord.Number, + AgentAccountID: agent, OwnerUserID: owner, + SessionID: "sess", CreatedAtUnixMS: prBase.UnixMilli(), + } +} + +// prsFor returns the PR numbers attached to issue, in read order. +func prsFor(t *testing.T, ctx context.Context, s *Store, issue ForgeCoord) []uint64 { + t.Helper() + got, err := s.PullRequestsForIssues(ctx, []ForgeCoord{issue}) + if err != nil { + t.Fatalf("PullRequestsForIssues: %v", err) + } + out := make([]uint64, 0, len(got[issue])) + for _, pr := range got[issue] { + out = append(out, pr.Coord.Number) + } + return out +} + +func mustUpsertPR(t *testing.T, ctx context.Context, s *Store, pr PullRequestRow, refs ...ForgeCoord) []ForgeCoord { + t.Helper() + affected, err := s.UpsertPullRequest(ctx, pr, refs) + if err != nil { + t.Fatalf("UpsertPullRequest: %v", err) + } + return affected +} + +func TestPullRequestExplicitLinkSurvivesUpsert(t *testing.T) { + ctx := context.Background() + s := newTestStore(t) + agent, owner := seedAgent(t, s, "prl1") + issue := ghCoord("a/b", 1) + seedBoardIssue(t, ctx, s, issue) + + pr := prRow("a/b", 10, "open", prBase, time.Unix(0, 0)) + if err := s.CreatePullRequestWithLink(ctx, authoredPR(agent, owner, pr), pr, &issue); err != nil { + t.Fatalf("CreatePullRequestWithLink: %v", err) + } + mustUpsertPR(t, ctx, s, prRow("a/b", 10, "open", prBase, prBase.Add(time.Hour))) + + if got := prsFor(t, ctx, s, issue); !slices.Equal(got, []uint64{10}) { + t.Fatalf("explicit link lost after upsert: got %v", got) + } +} + +func TestPullRequestExplicitTargetSurvivesClosingRefRemoval(t *testing.T) { + ctx := context.Background() + s := newTestStore(t) + agent, owner := seedAgent(t, s, "prl2") + issue := ghCoord("a/b", 1) + seedBoardIssue(t, ctx, s, issue) + + pr := prRow("a/b", 10, "open", prBase, time.Unix(0, 0)) + if err := s.CreatePullRequestWithLink(ctx, authoredPR(agent, owner, pr), pr, &issue); err != nil { + t.Fatalf("CreatePullRequestWithLink: %v", err) + } + mustUpsertPR(t, ctx, s, prRow("a/b", 10, "open", prBase, prBase.Add(time.Hour)), issue) + mustUpsertPR(t, ctx, s, prRow("a/b", 10, "open", prBase, prBase.Add(2*time.Hour))) + + if got := prsFor(t, ctx, s, issue); !slices.Equal(got, []uint64{10}) { + t.Fatalf("explicit target unlinked by closing-ref removal: got %v", got) + } +} + +func TestPullRequestUnresolvableExplicitLinkFallsBack(t *testing.T) { + ctx := context.Background() + s := newTestStore(t) + agent, owner := seedAgent(t, s, "prl3") + offBoard := ghCoord("a/b", 99) + closing := ghCoord("a/b", 2) + seedBoardIssue(t, ctx, s, closing) + + pr := prRow("a/b", 10, "open", prBase, time.Unix(0, 0)) + if err := s.CreatePullRequestWithLink(ctx, authoredPR(agent, owner, pr), pr, &offBoard); err != nil { + t.Fatalf("CreatePullRequestWithLink: %v", err) + } + mustUpsertPR(t, ctx, s, prRow("a/b", 10, "open", prBase, prBase.Add(time.Hour)), closing) + + if got := prsFor(t, ctx, s, closing); !slices.Equal(got, []uint64{10}) { + t.Fatalf("off-board explicit link did not fall back: got %v", got) + } + fb, err := s.FallbackIssuesForTarget(ctx, offBoard) + if err != nil { + t.Fatalf("FallbackIssuesForTarget: %v", err) + } + if !slices.Equal(fb, []ForgeCoord{closing}) { + t.Fatalf("FallbackIssuesForTarget = %v, want [%v]", fb, closing) + } + + // Once the explicit target is on the board, the closing reference stops attaching. + seedBoardIssue(t, ctx, s, offBoard) + if got := prsFor(t, ctx, s, closing); len(got) != 0 { + t.Fatalf("closing ref attached despite a resolvable explicit link: got %v", got) + } + if got := prsFor(t, ctx, s, offBoard); !slices.Equal(got, []uint64{10}) { + t.Fatalf("resolvable explicit link not attached: got %v", got) + } +} + +func TestPullRequestRemovedClosingRefsAreAffected(t *testing.T) { + ctx := context.Background() + s := newTestStore(t) + i2, i3, i4 := ghCoord("a/b", 2), ghCoord("a/b", 3), ghCoord("a/b", 4) + + first := mustUpsertPR(t, ctx, s, prRow("a/b", 10, "open", prBase, prBase.Add(time.Hour)), i2, i3) + if !sameCoords(first, []ForgeCoord{i2, i3}) { + t.Fatalf("first affected = %v, want [%v %v]", first, i2, i3) + } + second := mustUpsertPR(t, ctx, s, prRow("a/b", 10, "open", prBase, prBase.Add(2*time.Hour)), i3, i4) + if !sameCoords(second, []ForgeCoord{i2, i3, i4}) { + t.Fatalf("second affected = %v, want old+new [%v %v %v]", second, i2, i3, i4) + } + seedBoardIssue(t, ctx, s, i2) + if got := prsFor(t, ctx, s, i2); len(got) != 0 { + t.Fatalf("removed closing ref still linked: %v", got) + } +} + +func TestPullRequestOlderUpdateSkipped(t *testing.T) { + ctx := context.Background() + s := newTestStore(t) + issue := ghCoord("a/b", 1) + seedBoardIssue(t, ctx, s, issue) + + mustUpsertPR(t, ctx, s, prRow("a/b", 10, "merged", prBase, prBase.Add(2*time.Hour)), issue) + affected := mustUpsertPR(t, ctx, s, prRow("a/b", 10, "open", prBase, prBase.Add(time.Hour))) + if len(affected) != 0 { + t.Fatalf("older update reported affected %v", affected) + } + got, err := s.PullRequestsForIssues(ctx, []ForgeCoord{issue}) + if err != nil { + t.Fatalf("PullRequestsForIssues: %v", err) + } + if prs := got[issue]; len(prs) != 1 || prs[0].State != "merged" { + t.Fatalf("older update overwrote the row or its links: %+v", prs) + } +} + +func TestPullRequestCreateAfterHydrateKeepsHydratedRow(t *testing.T) { + ctx := context.Background() + s := newTestStore(t) + agent, owner := seedAgent(t, s, "prl6") + issue := ghCoord("a/b", 1) + seedBoardIssue(t, ctx, s, issue) + + mustUpsertPR(t, ctx, s, prRow("a/b", 10, "hydrated", prBase, prBase.Add(time.Hour))) + created := prRow("a/b", 10, "created", prBase, time.Unix(0, 0)) + if err := s.CreatePullRequestWithLink(ctx, authoredPR(agent, owner, created), created, &issue); err != nil { + t.Fatalf("CreatePullRequestWithLink: %v", err) + } + got, err := s.PullRequestsForIssues(ctx, []ForgeCoord{issue}) + if err != nil { + t.Fatalf("PullRequestsForIssues: %v", err) + } + prs := got[issue] + if len(prs) != 1 || prs[0].State != "hydrated" || !prs[0].UpdatedAt.Equal(prBase.Add(time.Hour)) { + t.Fatalf("create clobbered the hydrated row: %+v", prs) + } + if string(prs[0].PR) != `{"state": "hydrated"}` { + t.Fatalf("PR JSON = %s, want the hydrated body", prs[0].PR) + } +} + +func TestPullRequestOrderFollowsForgeCreatedAt(t *testing.T) { + ctx := context.Background() + s := newTestStore(t) + issue := ghCoord("a/b", 1) + seedBoardIssue(t, ctx, s, issue) + + for _, c := range []struct { + n uint64 + age time.Duration + }{{30, 3 * time.Hour}, {10, time.Hour}, {20, 2 * time.Hour}} { + mustUpsertPR(t, ctx, s, prRow("a/b", c.n, "open", prBase.Add(c.age), prBase.Add(c.age)), issue) + } + if got := prsFor(t, ctx, s, issue); !slices.Equal(got, []uint64{10, 20, 30}) { + t.Fatalf("order = %v, want by forge_created_at [10 20 30]", got) + } +} + +func TestPullRequestTenantIsolation(t *testing.T) { + ctx := context.Background() + s := newTestStore(t) + agent, owner := seedAgent(t, s, "prl8") + issue := ghCoord("a/b", 1) + seedBoardIssue(t, ctx, s, issue) + + created := prRow("a/b", 10, "created", prBase, time.Unix(0, 0)) + if err := s.CreatePullRequestWithLink(ctx, authoredPR(agent, owner, created), created, &issue); err != nil { + t.Fatalf("CreatePullRequestWithLink: %v", err) + } + mustUpsertPR(t, ctx, s, prRow("a/b", 10, "open", prBase, prBase.Add(time.Hour)), issue) + got, err := s.PullRequestsForIssues(ctx, []ForgeCoord{issue}) + if err != nil { + t.Fatalf("PullRequestsForIssues: %v", err) + } + if prs := got[issue]; len(prs) != 1 || prs[0].State != "open" { + t.Fatalf("create and ingest did not share one tenant row: %+v", prs) + } + + ctxB := WithTenant(context.Background(), seedTenant(t, s, "prl-tenant-b")) + if got := prsFor(t, ctxB, s, issue); len(got) != 0 { + t.Fatalf("foreign tenant read PRs %v", got) + } + if fb, err := s.FallbackIssuesForTarget(ctxB, issue); err != nil || len(fb) != 0 { + t.Fatalf("foreign tenant FallbackIssuesForTarget = %v, %v", fb, err) + } +} + +func TestPullRequestMixedCaseReposJoin(t *testing.T) { + ctx := context.Background() + s := newTestStore(t) + agent, owner := seedAgent(t, s, "prl9") + seedBoardIssue(t, ctx, s, ghCoord("owner/repo", 1)) + + issue := ghCoord("Owner/Repo", 1) + pr := prRow("OWNER/repo", 10, "open", prBase, time.Unix(0, 0)) + if err := s.CreatePullRequestWithLink(ctx, authoredPR(agent, owner, pr), pr, &issue); err != nil { + t.Fatalf("CreatePullRequestWithLink: %v", err) + } + mustUpsertPR(t, ctx, s, prRow("owner/REPO", 10, "open", prBase, prBase.Add(time.Hour)), ghCoord("Owner/repo", 1)) + + got, err := s.PullRequestsForIssues(ctx, []ForgeCoord{ghCoord("OWNER/REPO", 1)}) + if err != nil { + t.Fatalf("PullRequestsForIssues: %v", err) + } + prs := got[ghCoord("owner/repo", 1)] + if len(prs) != 1 || prs[0].Coord != ghCoord("owner/repo", 10) { + t.Fatalf("mixed-case repos did not join to one lowercased PR: %+v", got) + } + // The ownership row keeps the webhook's casing for the self-origin lookup. + if _, err := s.AuthoredArtifactByCoordinate(ctx, ForgeProviderGitHub, "github.com", "OWNER/repo", ForgeArtifactKindPullRequest, 10); err != nil { + t.Fatalf("authored row lost its repo casing: %v", err) + } +} + +func TestCreatePullRequestRejectsMismatchedArtifact(t *testing.T) { + ctx := context.Background() + s := newTestStore(t) + agent, owner := seedAgent(t, s, "prl10") + pr := prRow("a/b", 10, "open", prBase, time.Unix(0, 0)) + a := authoredPR(agent, owner, pr) + a.Number = 11 + if err := s.CreatePullRequestWithLink(ctx, a, pr, nil); !errors.Is(err, ErrInvalidArgument) { + t.Fatalf("mismatched artifact err = %v, want ErrInvalidArgument", err) + } + if got := prsFor(t, ctx, s, ghCoord("a/b", 1)); len(got) != 0 { + t.Fatalf("rejected create wrote rows: %v", got) + } +} + +func sameCoords(got, want []ForgeCoord) bool { + return len(got) == len(want) && !slices.ContainsFunc(want, func(c ForgeCoord) bool { + return !slices.Contains(got, c) + }) +} diff --git a/go/internal/store/queries/pull_requests.sql b/go/internal/store/queries/pull_requests.sql new file mode 100644 index 000000000..3dc3cf8d0 --- /dev/null +++ b/go/internal/store/queries/pull_requests.sql @@ -0,0 +1,93 @@ +-- name: InsertPullRequestIfAbsent :exec +-- The create path's write: a webhook-hydrated row already present always wins. +INSERT INTO pull_requests + (forge_provider, forge_host, repo, number, forge_state, + forge_created_at, forge_updated_at, pr) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8) +ON CONFLICT (tenant_id, forge_provider, forge_host, repo, number) DO NOTHING; + +-- name: UpsertPullRequestGuarded :execrows +-- Zero rows affected means the stored row is newer and the write was skipped. +INSERT INTO pull_requests + (forge_provider, forge_host, repo, number, forge_state, + forge_created_at, forge_updated_at, pr) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8) +ON CONFLICT (tenant_id, forge_provider, forge_host, repo, number) DO UPDATE + SET forge_state = EXCLUDED.forge_state, + forge_created_at = EXCLUDED.forge_created_at, + forge_updated_at = EXCLUDED.forge_updated_at, + pr = EXCLUDED.pr + WHERE EXCLUDED.forge_updated_at >= pull_requests.forge_updated_at; + +-- name: UpsertExplicitPullRequestLink :exec +INSERT INTO pull_request_issue_links + (pr_forge_provider, pr_forge_host, pr_repo, pr_number, + issue_forge_provider, issue_forge_host, issue_repo, issue_number, source) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 1) +ON CONFLICT (tenant_id, pr_forge_provider, pr_forge_host, pr_repo, pr_number, + issue_forge_provider, issue_forge_host, issue_repo, issue_number) +DO UPDATE SET source = 1; + +-- name: InsertClosingRefLink :exec +INSERT INTO pull_request_issue_links + (pr_forge_provider, pr_forge_host, pr_repo, pr_number, + issue_forge_provider, issue_forge_host, issue_repo, issue_number, source) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 2) +ON CONFLICT (tenant_id, pr_forge_provider, pr_forge_host, pr_repo, pr_number, + issue_forge_provider, issue_forge_host, issue_repo, issue_number) +DO NOTHING; + +-- name: DeleteClosingRefLink :exec +DELETE FROM pull_request_issue_links + WHERE pr_forge_provider = $1 AND pr_forge_host = $2 AND pr_repo = $3 AND pr_number = $4 + AND issue_forge_provider = $5 AND issue_forge_host = $6 AND issue_repo = $7 + AND issue_number = $8 AND source = 2; + +-- name: ListPullRequestLinks :many +SELECT issue_forge_provider, issue_forge_host, issue_repo, issue_number, source + FROM pull_request_issue_links + WHERE pr_forge_provider = $1 AND pr_forge_host = $2 AND pr_repo = $3 AND pr_number = $4 + ORDER BY issue_forge_provider, issue_forge_host, issue_repo, issue_number; + +-- name: PullRequestsForIssues :many +-- Explicit links attach directly; a closing reference attaches only when none +-- of the PR's explicit targets is an issue on the board. +WITH want AS ( + SELECT unnest(@issue_providers::smallint[]) AS provider, + unnest(@issue_hosts::text[]) AS host, + unnest(@issue_repos::text[]) AS repo, + unnest(@issue_numbers::bigint[]) AS number +) +SELECT l.issue_forge_provider, l.issue_forge_host, l.issue_repo, l.issue_number, + p.forge_provider, p.forge_host, p.repo, p.number, p.forge_state, + p.forge_created_at, p.forge_updated_at, p.pr + FROM pull_request_issue_links l + JOIN want w + ON l.issue_forge_provider = w.provider AND l.issue_forge_host = w.host + AND l.issue_repo = w.repo AND l.issue_number = w.number + JOIN pull_requests p + ON p.tenant_id = l.tenant_id AND p.forge_provider = l.pr_forge_provider + AND p.forge_host = l.pr_forge_host AND p.repo = l.pr_repo AND p.number = l.pr_number + WHERE l.source = 1 + OR NOT EXISTS ( + SELECT 1 + FROM pull_request_issue_links e + JOIN issues i + ON i.tenant_id = e.tenant_id AND i.forge_provider = e.issue_forge_provider + AND i.forge_host = e.issue_forge_host AND i.repo = e.issue_repo + AND i.number = e.issue_number + WHERE e.source = 1 AND e.tenant_id = l.tenant_id + AND e.pr_forge_provider = l.pr_forge_provider AND e.pr_forge_host = l.pr_forge_host + AND e.pr_repo = l.pr_repo AND e.pr_number = l.pr_number) + ORDER BY p.forge_created_at, p.forge_provider, p.forge_host, p.repo, p.number; + +-- name: FallbackIssuesForTarget :many +SELECT DISTINCT c.issue_forge_provider, c.issue_forge_host, c.issue_repo, c.issue_number + FROM pull_request_issue_links e + JOIN pull_request_issue_links c + ON c.tenant_id = e.tenant_id AND c.pr_forge_provider = e.pr_forge_provider + AND c.pr_forge_host = e.pr_forge_host AND c.pr_repo = e.pr_repo AND c.pr_number = e.pr_number + WHERE e.source = 1 AND c.source = 2 + AND e.issue_forge_provider = $1 AND e.issue_forge_host = $2 + AND e.issue_repo = $3 AND e.issue_number = $4 + ORDER BY c.issue_forge_provider, c.issue_forge_host, c.issue_repo, c.issue_number; diff --git a/go/internal/store/rls_pgtest_test.go b/go/internal/store/rls_pgtest_test.go index 172eb19e8..ad63801b8 100644 --- a/go/internal/store/rls_pgtest_test.go +++ b/go/internal/store/rls_pgtest_test.go @@ -608,6 +608,7 @@ func TestRLSCatalogEnabledAndForced(t *testing.T) { "forge_state_transitions", "token_usage_events", "token_usage_rollups_hourly", "token_usage_rollups_daily", "compute_usage_events", "compute_usage_rollups_hourly", "compute_usage_rollups_daily", + "pull_requests", "pull_request_issue_links", } for _, tbl := range tenantOwned { if !enumerated[tbl] { diff --git a/go/internal/store/updated_at_pgtest_test.go b/go/internal/store/updated_at_pgtest_test.go index 30f546f74..74b56991b 100644 --- a/go/internal/store/updated_at_pgtest_test.go +++ b/go/internal/store/updated_at_pgtest_test.go @@ -203,6 +203,7 @@ func TestUpdatedAtTriggerCatalogFloor(t *testing.T) { for _, tbl := range []string{ "secrets", "agent_placements", "session_bindings", "agent_config_bundle", "model_registry", "forge_repo_subscriptions", + "pull_requests", } { var n int if err := s.pool.QueryRow(ctx, diff --git a/packages/compass-agent/src/gen/compass/v1/agent_gateway_pb.ts b/packages/compass-agent/src/gen/compass/v1/agent_gateway_pb.ts index eced6a07a..98b0e8c83 100644 --- a/packages/compass-agent/src/gen/compass/v1/agent_gateway_pb.ts +++ b/packages/compass-agent/src/gen/compass/v1/agent_gateway_pb.ts @@ -48,7 +48,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file compass/v1/agent_gateway.proto. */ export const file_compass_v1_agent_gateway: GenFile = /*@__PURE__*/ - fileDesc("Ch5jb21wYXNzL3YxL2FnZW50X2dhdGV3YXkucHJvdG8SCmNvbXBhc3MudjEiuwQKEENvbW1zQ2FsbFJlcXVlc3QSDwoHY2FsbF9pZBgBIAEoCRIuCgRwb3N0GAIgASgLMh4uY29tcGFzcy52MS5Qb3N0TWVzc2FnZVJlcXVlc3RIABIvCgRsaXN0GAMgASgLMh8uY29tcGFzcy52MS5MaXN0TWVzc2FnZXNSZXF1ZXN0SAASLgoGcm9zdGVyGAQgASgLMhwuY29tcGFzcy52MS5HZXRSb3N0ZXJSZXF1ZXN0SAASNwoKc2V0X3N0YXR1cxgFIAEoCzIhLmNvbXBhc3MudjEuU2V0QWdlbnRTdGF0dXNSZXF1ZXN0SAASMwoDcGluGAYgASgLMiQuY29tcGFzcy52MS5VcGRhdGVQaW5uZWRCb2FyZFJlcXVlc3RIABI6Cg5jcmVhdGVfY2hhbm5lbBgHIAEoCzIgLmNvbXBhc3MudjEuQ3JlYXRlQ2hhbm5lbFJlcXVlc3RIABJBCg51cGRhdGVfbWVtYmVycxgIIAEoCzInLmNvbXBhc3MudjEuVXBkYXRlQ2hhbm5lbE1lbWJlcnNSZXF1ZXN0SAASRQoUY3JlYXRlX2NoYW5uZWxfZ3JvdXAYCSABKAsyJS5jb21wYXNzLnYxLkNyZWF0ZUNoYW5uZWxHcm91cFJlcXVlc3RIABIsCgdvcGVuX2RtGAsgASgLMhkuY29tcGFzcy52MS5PcGVuRE1SZXF1ZXN0SAASGwoTdHJpZ2dlcl90cmFjZXBhcmVudBgKIAEoCUIGCgRjYWxsItUECg9Db21tc0NhbGxSZXN1bHQSDwoHY2FsbF9pZBgBIAEoCRIvCgRwb3N0GAIgASgLMh8uY29tcGFzcy52MS5Qb3N0TWVzc2FnZVJlc3BvbnNlSAASMAoEbGlzdBgDIAEoCzIgLmNvbXBhc3MudjEuTGlzdE1lc3NhZ2VzUmVzcG9uc2VIABIrCgVlcnJvchgEIAEoCzIaLmNvbXBhc3MudjEuQ29tbXNDYWxsRXJyb3JIABIvCgZyb3N0ZXIYBSABKAsyHS5jb21wYXNzLnYxLkdldFJvc3RlclJlc3BvbnNlSAASOAoKc2V0X3N0YXR1cxgGIAEoCzIiLmNvbXBhc3MudjEuU2V0QWdlbnRTdGF0dXNSZXNwb25zZUgAEjQKA3BpbhgHIAEoCzIlLmNvbXBhc3MudjEuVXBkYXRlUGlubmVkQm9hcmRSZXNwb25zZUgAEjsKDmNyZWF0ZV9jaGFubmVsGAggASgLMiEuY29tcGFzcy52MS5DcmVhdGVDaGFubmVsUmVzcG9uc2VIABJCCg51cGRhdGVfbWVtYmVycxgJIAEoCzIoLmNvbXBhc3MudjEuVXBkYXRlQ2hhbm5lbE1lbWJlcnNSZXNwb25zZUgAEkYKFGNyZWF0ZV9jaGFubmVsX2dyb3VwGAogASgLMiYuY29tcGFzcy52MS5DcmVhdGVDaGFubmVsR3JvdXBSZXNwb25zZUgAEi0KB29wZW5fZG0YCyABKAsyGi5jb21wYXNzLnYxLk9wZW5ETVJlc3BvbnNlSABCCAoGcmVzdWx0Ii8KDkNvbW1zQ2FsbEVycm9yEgwKBGNvZGUYASABKAkSDwoHbWVzc2FnZRgCIAEoCSIpChVTZXRBZ2VudFN0YXR1c1JlcXVlc3QSEAoIYWN0aXZpdHkYASABKAkiGAoWU2V0QWdlbnRTdGF0dXNSZXNwb25zZSKRAQoUTGlmZWN5Y2xlQ2FsbFJlcXVlc3QSDwoHY2FsbF9pZBgBIAEoCRItCgVzcGF3bhgCIAEoCzIcLmNvbXBhc3MudjEuU3Bhd25QZWVyUmVxdWVzdEgAEjEKB2Rlc3Bhd24YAyABKAsyHi5jb21wYXNzLnYxLkRlc3Bhd25QZWVyUmVxdWVzdEgAQgYKBGNhbGwiiAEKEFNwYXduUGVlclJlcXVlc3QSDgoGaGFuZGxlGAEgASgJEhQKDGRpc3BsYXlfbmFtZRgCIAEoCRIZChFjbGllbnRfcmVxdWVzdF9pZBgEIAEoCRIMCgRyb2xlGAUgASgJEg8KB3BlcnNvbmEYBiABKAlKBAgDEARSDmluaXRpYWxfcHJvbXB0InIKEVNwYXduUGVlclJlc3BvbnNlEhgKEGFnZW50X2FjY291bnRfaWQYASABKAkSFgoOY29udGFpbmVyX25hbWUYAiABKAkSEgoKc2Vzc2lvbl9pZBgDIAEoCRIXCg9kbV9jaGFubmVsX25hbWUYBCABKAkiKgoSRGVzcGF3blBlZXJSZXF1ZXN0EhQKDGFnZW50X2hhbmRsZRgBIAEoCSIVChNEZXNwYXduUGVlclJlc3BvbnNlIsUBChNMaWZlY3ljbGVDYWxsUmVzdWx0Eg8KB2NhbGxfaWQYASABKAkSLgoFc3Bhd24YAiABKAsyHS5jb21wYXNzLnYxLlNwYXduUGVlclJlc3BvbnNlSAASMgoHZGVzcGF3bhgDIAEoCzIfLmNvbXBhc3MudjEuRGVzcGF3blBlZXJSZXNwb25zZUgAEi8KBWVycm9yGAQgASgLMh4uY29tcGFzcy52MS5MaWZlY3ljbGVDYWxsRXJyb3JIAEIICgZyZXN1bHQiMwoSTGlmZWN5Y2xlQ2FsbEVycm9yEgwKBGNvZGUYASABKAkSDwoHbWVzc2FnZRgCIAEoCSLrBgoQRm9yZ2VDYWxsUmVxdWVzdBIPCgdjYWxsX2lkGAEgASgJEjYKDGNyZWF0ZV9pc3N1ZRgCIAEoCzIeLmNvbXBhc3MudjEuQ3JlYXRlSXNzdWVSZXF1ZXN0SAASPQoQY29tbWVudF9vbl9pc3N1ZRgDIAEoCzIhLmNvbXBhc3MudjEuQ29tbWVudE9uSXNzdWVSZXF1ZXN0SAASMAoJZ2V0X2lzc3VlGAQgASgLMhsuY29tcGFzcy52MS5HZXRJc3N1ZVJlcXVlc3RIABI0CgtsaXN0X2lzc3VlcxgFIAEoCzIdLmNvbXBhc3MudjEuTGlzdElzc3Vlc1JlcXVlc3RIABJDChNjcmVhdGVfcHVsbF9yZXF1ZXN0GAYgASgLMiQuY29tcGFzcy52MS5DcmVhdGVQdWxsUmVxdWVzdFJlcXVlc3RIABJKChdjb21tZW50X29uX3B1bGxfcmVxdWVzdBgHIAEoCzInLmNvbXBhc3MudjEuQ29tbWVudE9uUHVsbFJlcXVlc3RSZXF1ZXN0SAASPQoQZ2V0X3B1bGxfcmVxdWVzdBgIIAEoCzIhLmNvbXBhc3MudjEuR2V0UHVsbFJlcXVlc3RSZXF1ZXN0SAASNgoJc3Vic2NyaWJlGAkgASgLMiEuY29tcGFzcy52MS5TdWJzY3JpYmVGb3JnZVJlcXVlc3RIABI6Cgt1bnN1YnNjcmliZRgKIAEoCzIjLmNvbXBhc3MudjEuVW5zdWJzY3JpYmVGb3JnZVJlcXVlc3RIABI4Cg1zdWJtaXRfcmV2aWV3GAsgASgLMh8uY29tcGFzcy52MS5TdWJtaXRSZXZpZXdSZXF1ZXN0SAASSQoWdHJhbnNpdGlvbl9pc3N1ZV9zdGF0ZRgOIAEoCzInLmNvbXBhc3MudjEuVHJhbnNpdGlvbklzc3VlU3RhdGVSZXF1ZXN0SAASVgoddHJhbnNpdGlvbl9wdWxsX3JlcXVlc3Rfc3RhdGUYDyABKAsyLS5jb21wYXNzLnYxLlRyYW5zaXRpb25QdWxsUmVxdWVzdFN0YXRlUmVxdWVzdEgAEiMKBWZvcmdlGAwgASgLMhQuY29tcGFzcy52MS5Gb3JnZVJlZhIZChFjbGllbnRfcmVxdWVzdF9pZBgNIAEoCUIGCgRjYWxsIuADCg9Gb3JnZUNhbGxSZXN1bHQSDwoHY2FsbF9pZBgBIAEoCRIiCgVpc3N1ZRgCIAEoCzIRLmNvbXBhc3MudjEuSXNzdWVIABIvCg1pc3N1ZV9jb21tZW50GAMgASgLMhYuY29tcGFzcy52MS5Db21tZW50UmVmSAASMAoGaXNzdWVzGAQgASgLMh4uY29tcGFzcy52MS5MaXN0SXNzdWVzUmVzcG9uc2VIABIvCgxwdWxsX3JlcXVlc3QYBSABKAsyFy5jb21wYXNzLnYxLlB1bGxSZXF1ZXN0SAASLAoKcHJfY29tbWVudBgGIAEoCzIWLmNvbXBhc3MudjEuQ29tbWVudFJlZkgAEjgKCnN1YnNjcmliZWQYByABKAsyIi5jb21wYXNzLnYxLlN1YnNjcmliZUZvcmdlUmVzcG9uc2VIABI8Cgx1bnN1YnNjcmliZWQYCCABKAsyJC5jb21wYXNzLnYxLlVuc3Vic2NyaWJlRm9yZ2VSZXNwb25zZUgAEisKBWVycm9yGAkgASgLMhouY29tcGFzcy52MS5Gb3JnZUNhbGxFcnJvckgAEicKBnJldmlldxgKIAEoCzIVLmNvbXBhc3MudjEuUmV2aWV3UmVmSABCCAoGcmVzdWx0IkcKDkZvcmdlQ2FsbEVycm9yEgwKBGNvZGUYASABKAkSDwoHbWVzc2FnZRgCIAEoCRIWCg5yZXRyeV9hZnRlcl9tcxgDIAEoDSJPChJDcmVhdGVJc3N1ZVJlcXVlc3QSDAoEcmVwbxgBIAEoCRINCgV0aXRsZRgCIAEoCRIMCgRib2R5GAMgASgJEg4KBmxhYmVscxgEIAMoCSJJChVDb21tZW50T25Jc3N1ZVJlcXVlc3QSDAoEcmVwbxgBIAEoCRIUCgxpc3N1ZV9udW1iZXIYAiABKAQSDAoEYm9keRgDIAEoCSI1Cg9HZXRJc3N1ZVJlcXVlc3QSDAoEcmVwbxgBIAEoCRIUCgxpc3N1ZV9udW1iZXIYAiABKAQiTwoRTGlzdElzc3Vlc1JlcXVlc3QSDAoEcmVwbxgBIAEoCRINCgVzdGF0ZRgCIAEoCRIOCgZsYWJlbHMYAyADKAkSDQoFbGltaXQYBCABKA0iNwoSTGlzdElzc3Vlc1Jlc3BvbnNlEiEKBmlzc3VlcxgBIAMoCzIRLmNvbXBhc3MudjEuSXNzdWUieAoYQ3JlYXRlUHVsbFJlcXVlc3RSZXF1ZXN0EgwKBHJlcG8YASABKAkSDQoFdGl0bGUYAiABKAkSDAoEYm9keRgDIAEoCRIQCghoZWFkX3JlZhgEIAEoCRIQCghiYXNlX3JlZhgFIAEoCRINCgVkcmFmdBgGIAEoCCJMChtDb21tZW50T25QdWxsUmVxdWVzdFJlcXVlc3QSDAoEcmVwbxgBIAEoCRIRCglwcl9udW1iZXIYAiABKAQSDAoEYm9keRgDIAEoCSI4ChVHZXRQdWxsUmVxdWVzdFJlcXVlc3QSDAoEcmVwbxgBIAEoCRIRCglwcl9udW1iZXIYAiABKAQihwEKE1N1Ym1pdFJldmlld1JlcXVlc3QSDAoEcmVwbxgBIAEoCRIRCglwcl9udW1iZXIYAiABKAQSDwoHdmVyZGljdBgDIAEoCRIMCgRib2R5GAQgASgJEjAKCGNvbW1lbnRzGAUgAygLMh4uY29tcGFzcy52MS5SZXZpZXdDb21tZW50SW5wdXQiTAoSUmV2aWV3Q29tbWVudElucHV0EgwKBHBhdGgYASABKAkSDAoEbGluZRgCIAEoDRIMCgRzaWRlGAMgASgJEgwKBGJvZHkYBCABKAkifgobVHJhbnNpdGlvbklzc3VlU3RhdGVSZXF1ZXN0EgwKBHJlcG8YASABKAkSFAoMaXNzdWVfbnVtYmVyGAIgASgEEg0KBXN0YXRlGAMgASgJEhQKDGNsb3NlX3JlYXNvbhgEIAEoCRIWCg53b3JrZmxvd19zdGF0ZRgFIAEoCSJTCiFUcmFuc2l0aW9uUHVsbFJlcXVlc3RTdGF0ZVJlcXVlc3QSDAoEcmVwbxgBIAEoCRIRCglwcl9udW1iZXIYAiABKAQSDQoFc3RhdGUYAyABKAkipgEKFVN1YnNjcmliZUZvcmdlUmVxdWVzdBIMCgRyZXBvGAEgASgJEisKBGtpbmQYAiABKA4yHS5jb21wYXNzLnYxLkZvcmdlQXJ0aWZhY3RLaW5kEg4KBm51bWJlchgDIAEoBBIxCgVzY29wZRgEIAEoDjIiLmNvbXBhc3MudjEuRm9yZ2VTdWJzY3JpcHRpb25TY29wZRIPCgdwcm9qZWN0GAUgASgJIjEKFlN1YnNjcmliZUZvcmdlUmVzcG9uc2USFwoPc3Vic2NyaXB0aW9uX2lkGAEgASgJIjIKF1Vuc3Vic2NyaWJlRm9yZ2VSZXF1ZXN0EhcKD3N1YnNjcmlwdGlvbl9pZBgBIAEoCSIaChhVbnN1YnNjcmliZUZvcmdlUmVzcG9uc2UiaAoQQm9hcmRDYWxsUmVxdWVzdBIPCgdjYWxsX2lkGAEgASgJEjsKD3NldF9pc3N1ZV9zdGF0ZRgCIAEoCzIgLmNvbXBhc3MudjEuU2V0SXNzdWVTdGF0ZVJlcXVlc3RIAEIGCgRjYWxsIk8KFFNldElzc3VlU3RhdGVSZXF1ZXN0EhAKCGlzc3VlX2lkGAEgASgJEiUKBXN0YXRlGAIgASgOMhYuY29tcGFzcy52MS5Jc3N1ZVN0YXRlIjkKFVNldElzc3VlU3RhdGVSZXNwb25zZRIgCgVpc3N1ZRgBIAEoCzIRLmNvbXBhc3MudjEuSXNzdWUilwEKD0JvYXJkQ2FsbFJlc3VsdBIPCgdjYWxsX2lkGAEgASgJEjwKD3NldF9pc3N1ZV9zdGF0ZRgCIAEoCzIhLmNvbXBhc3MudjEuU2V0SXNzdWVTdGF0ZVJlc3BvbnNlSAASKwoFZXJyb3IYAyABKAsyGi5jb21wYXNzLnYxLkJvYXJkQ2FsbEVycm9ySABCCAoGcmVzdWx0Ii8KDkJvYXJkQ2FsbEVycm9yEgwKBGNvZGUYASABKAkSDwoHbWVzc2FnZRgCIAEoCSI8ChNQdWJsaXNoRnJhbWVSZXF1ZXN0EiUKBWZyYW1lGAEgASgLMhYuY29tcGFzcy52MS5BZ2VudEZyYW1lIhYKFFB1Ymxpc2hGcmFtZVJlc3BvbnNlIl4KHFBvc3RDb252ZXJzYXRpb25GcmFtZVJlcXVlc3QSJQoFZnJhbWUYASABKAsyFi5jb21wYXNzLnYxLkFnZW50RnJhbWUSFwoPaWRlbXBvdGVuY3lfa2V5GAIgASgJIh8KHVBvc3RDb252ZXJzYXRpb25GcmFtZVJlc3BvbnNlIhkKF0NvbnRyb2xTdWJzY3JpYmVSZXF1ZXN0KpEBChZGb3JnZVN1YnNjcmlwdGlvblNjb3BlEigKJEZPUkdFX1NVQlNDUklQVElPTl9TQ09QRV9VTlNQRUNJRklFRBAAEiUKIUZPUkdFX1NVQlNDUklQVElPTl9TQ09QRV9BUlRJRkFDVBABEiYKIkZPUkdFX1NVQlNDUklQVElPTl9TQ09QRV9DT05UQUlORVIQAjK0BAoMQWdlbnRHYXRld2F5EkIKBUNvbW1zEhwuY29tcGFzcy52MS5Db21tc0NhbGxSZXF1ZXN0GhsuY29tcGFzcy52MS5Db21tc0NhbGxSZXN1bHQSTgoJTGlmZWN5Y2xlEiAuY29tcGFzcy52MS5MaWZlY3ljbGVDYWxsUmVxdWVzdBofLmNvbXBhc3MudjEuTGlmZWN5Y2xlQ2FsbFJlc3VsdBJOCgdQdWJsaXNoEh8uY29tcGFzcy52MS5QdWJsaXNoRnJhbWVSZXF1ZXN0GiAuY29tcGFzcy52MS5QdWJsaXNoRnJhbWVSZXNwb25zZSgBEmwKFVBvc3RDb252ZXJzYXRpb25GcmFtZRIoLmNvbXBhc3MudjEuUG9zdENvbnZlcnNhdGlvbkZyYW1lUmVxdWVzdBopLmNvbXBhc3MudjEuUG9zdENvbnZlcnNhdGlvbkZyYW1lUmVzcG9uc2USSgoHQ29udHJvbBIjLmNvbXBhc3MudjEuQ29udHJvbFN1YnNjcmliZVJlcXVlc3QaGC5jb21wYXNzLnYxLkFnZW50Q29udHJvbDABEkIKBUZvcmdlEhwuY29tcGFzcy52MS5Gb3JnZUNhbGxSZXF1ZXN0GhsuY29tcGFzcy52MS5Gb3JnZUNhbGxSZXN1bHQSQgoFQm9hcmQSHC5jb21wYXNzLnYxLkJvYXJkQ2FsbFJlcXVlc3QaGy5jb21wYXNzLnYxLkJvYXJkQ2FsbFJlc3VsdGIGcHJvdG8z", [file_compass_v1_comms, file_compass_v1_agent, file_compass_v1_compass, file_compass_v1_forge]); + fileDesc("Ch5jb21wYXNzL3YxL2FnZW50X2dhdGV3YXkucHJvdG8SCmNvbXBhc3MudjEiuwQKEENvbW1zQ2FsbFJlcXVlc3QSDwoHY2FsbF9pZBgBIAEoCRIuCgRwb3N0GAIgASgLMh4uY29tcGFzcy52MS5Qb3N0TWVzc2FnZVJlcXVlc3RIABIvCgRsaXN0GAMgASgLMh8uY29tcGFzcy52MS5MaXN0TWVzc2FnZXNSZXF1ZXN0SAASLgoGcm9zdGVyGAQgASgLMhwuY29tcGFzcy52MS5HZXRSb3N0ZXJSZXF1ZXN0SAASNwoKc2V0X3N0YXR1cxgFIAEoCzIhLmNvbXBhc3MudjEuU2V0QWdlbnRTdGF0dXNSZXF1ZXN0SAASMwoDcGluGAYgASgLMiQuY29tcGFzcy52MS5VcGRhdGVQaW5uZWRCb2FyZFJlcXVlc3RIABI6Cg5jcmVhdGVfY2hhbm5lbBgHIAEoCzIgLmNvbXBhc3MudjEuQ3JlYXRlQ2hhbm5lbFJlcXVlc3RIABJBCg51cGRhdGVfbWVtYmVycxgIIAEoCzInLmNvbXBhc3MudjEuVXBkYXRlQ2hhbm5lbE1lbWJlcnNSZXF1ZXN0SAASRQoUY3JlYXRlX2NoYW5uZWxfZ3JvdXAYCSABKAsyJS5jb21wYXNzLnYxLkNyZWF0ZUNoYW5uZWxHcm91cFJlcXVlc3RIABIsCgdvcGVuX2RtGAsgASgLMhkuY29tcGFzcy52MS5PcGVuRE1SZXF1ZXN0SAASGwoTdHJpZ2dlcl90cmFjZXBhcmVudBgKIAEoCUIGCgRjYWxsItUECg9Db21tc0NhbGxSZXN1bHQSDwoHY2FsbF9pZBgBIAEoCRIvCgRwb3N0GAIgASgLMh8uY29tcGFzcy52MS5Qb3N0TWVzc2FnZVJlc3BvbnNlSAASMAoEbGlzdBgDIAEoCzIgLmNvbXBhc3MudjEuTGlzdE1lc3NhZ2VzUmVzcG9uc2VIABIrCgVlcnJvchgEIAEoCzIaLmNvbXBhc3MudjEuQ29tbXNDYWxsRXJyb3JIABIvCgZyb3N0ZXIYBSABKAsyHS5jb21wYXNzLnYxLkdldFJvc3RlclJlc3BvbnNlSAASOAoKc2V0X3N0YXR1cxgGIAEoCzIiLmNvbXBhc3MudjEuU2V0QWdlbnRTdGF0dXNSZXNwb25zZUgAEjQKA3BpbhgHIAEoCzIlLmNvbXBhc3MudjEuVXBkYXRlUGlubmVkQm9hcmRSZXNwb25zZUgAEjsKDmNyZWF0ZV9jaGFubmVsGAggASgLMiEuY29tcGFzcy52MS5DcmVhdGVDaGFubmVsUmVzcG9uc2VIABJCCg51cGRhdGVfbWVtYmVycxgJIAEoCzIoLmNvbXBhc3MudjEuVXBkYXRlQ2hhbm5lbE1lbWJlcnNSZXNwb25zZUgAEkYKFGNyZWF0ZV9jaGFubmVsX2dyb3VwGAogASgLMiYuY29tcGFzcy52MS5DcmVhdGVDaGFubmVsR3JvdXBSZXNwb25zZUgAEi0KB29wZW5fZG0YCyABKAsyGi5jb21wYXNzLnYxLk9wZW5ETVJlc3BvbnNlSABCCAoGcmVzdWx0Ii8KDkNvbW1zQ2FsbEVycm9yEgwKBGNvZGUYASABKAkSDwoHbWVzc2FnZRgCIAEoCSIpChVTZXRBZ2VudFN0YXR1c1JlcXVlc3QSEAoIYWN0aXZpdHkYASABKAkiGAoWU2V0QWdlbnRTdGF0dXNSZXNwb25zZSKRAQoUTGlmZWN5Y2xlQ2FsbFJlcXVlc3QSDwoHY2FsbF9pZBgBIAEoCRItCgVzcGF3bhgCIAEoCzIcLmNvbXBhc3MudjEuU3Bhd25QZWVyUmVxdWVzdEgAEjEKB2Rlc3Bhd24YAyABKAsyHi5jb21wYXNzLnYxLkRlc3Bhd25QZWVyUmVxdWVzdEgAQgYKBGNhbGwiiAEKEFNwYXduUGVlclJlcXVlc3QSDgoGaGFuZGxlGAEgASgJEhQKDGRpc3BsYXlfbmFtZRgCIAEoCRIZChFjbGllbnRfcmVxdWVzdF9pZBgEIAEoCRIMCgRyb2xlGAUgASgJEg8KB3BlcnNvbmEYBiABKAlKBAgDEARSDmluaXRpYWxfcHJvbXB0InIKEVNwYXduUGVlclJlc3BvbnNlEhgKEGFnZW50X2FjY291bnRfaWQYASABKAkSFgoOY29udGFpbmVyX25hbWUYAiABKAkSEgoKc2Vzc2lvbl9pZBgDIAEoCRIXCg9kbV9jaGFubmVsX25hbWUYBCABKAkiKgoSRGVzcGF3blBlZXJSZXF1ZXN0EhQKDGFnZW50X2hhbmRsZRgBIAEoCSIVChNEZXNwYXduUGVlclJlc3BvbnNlIsUBChNMaWZlY3ljbGVDYWxsUmVzdWx0Eg8KB2NhbGxfaWQYASABKAkSLgoFc3Bhd24YAiABKAsyHS5jb21wYXNzLnYxLlNwYXduUGVlclJlc3BvbnNlSAASMgoHZGVzcGF3bhgDIAEoCzIfLmNvbXBhc3MudjEuRGVzcGF3blBlZXJSZXNwb25zZUgAEi8KBWVycm9yGAQgASgLMh4uY29tcGFzcy52MS5MaWZlY3ljbGVDYWxsRXJyb3JIAEIICgZyZXN1bHQiMwoSTGlmZWN5Y2xlQ2FsbEVycm9yEgwKBGNvZGUYASABKAkSDwoHbWVzc2FnZRgCIAEoCSLrBgoQRm9yZ2VDYWxsUmVxdWVzdBIPCgdjYWxsX2lkGAEgASgJEjYKDGNyZWF0ZV9pc3N1ZRgCIAEoCzIeLmNvbXBhc3MudjEuQ3JlYXRlSXNzdWVSZXF1ZXN0SAASPQoQY29tbWVudF9vbl9pc3N1ZRgDIAEoCzIhLmNvbXBhc3MudjEuQ29tbWVudE9uSXNzdWVSZXF1ZXN0SAASMAoJZ2V0X2lzc3VlGAQgASgLMhsuY29tcGFzcy52MS5HZXRJc3N1ZVJlcXVlc3RIABI0CgtsaXN0X2lzc3VlcxgFIAEoCzIdLmNvbXBhc3MudjEuTGlzdElzc3Vlc1JlcXVlc3RIABJDChNjcmVhdGVfcHVsbF9yZXF1ZXN0GAYgASgLMiQuY29tcGFzcy52MS5DcmVhdGVQdWxsUmVxdWVzdFJlcXVlc3RIABJKChdjb21tZW50X29uX3B1bGxfcmVxdWVzdBgHIAEoCzInLmNvbXBhc3MudjEuQ29tbWVudE9uUHVsbFJlcXVlc3RSZXF1ZXN0SAASPQoQZ2V0X3B1bGxfcmVxdWVzdBgIIAEoCzIhLmNvbXBhc3MudjEuR2V0UHVsbFJlcXVlc3RSZXF1ZXN0SAASNgoJc3Vic2NyaWJlGAkgASgLMiEuY29tcGFzcy52MS5TdWJzY3JpYmVGb3JnZVJlcXVlc3RIABI6Cgt1bnN1YnNjcmliZRgKIAEoCzIjLmNvbXBhc3MudjEuVW5zdWJzY3JpYmVGb3JnZVJlcXVlc3RIABI4Cg1zdWJtaXRfcmV2aWV3GAsgASgLMh8uY29tcGFzcy52MS5TdWJtaXRSZXZpZXdSZXF1ZXN0SAASSQoWdHJhbnNpdGlvbl9pc3N1ZV9zdGF0ZRgOIAEoCzInLmNvbXBhc3MudjEuVHJhbnNpdGlvbklzc3VlU3RhdGVSZXF1ZXN0SAASVgoddHJhbnNpdGlvbl9wdWxsX3JlcXVlc3Rfc3RhdGUYDyABKAsyLS5jb21wYXNzLnYxLlRyYW5zaXRpb25QdWxsUmVxdWVzdFN0YXRlUmVxdWVzdEgAEiMKBWZvcmdlGAwgASgLMhQuY29tcGFzcy52MS5Gb3JnZVJlZhIZChFjbGllbnRfcmVxdWVzdF9pZBgNIAEoCUIGCgRjYWxsIuADCg9Gb3JnZUNhbGxSZXN1bHQSDwoHY2FsbF9pZBgBIAEoCRIiCgVpc3N1ZRgCIAEoCzIRLmNvbXBhc3MudjEuSXNzdWVIABIvCg1pc3N1ZV9jb21tZW50GAMgASgLMhYuY29tcGFzcy52MS5Db21tZW50UmVmSAASMAoGaXNzdWVzGAQgASgLMh4uY29tcGFzcy52MS5MaXN0SXNzdWVzUmVzcG9uc2VIABIvCgxwdWxsX3JlcXVlc3QYBSABKAsyFy5jb21wYXNzLnYxLlB1bGxSZXF1ZXN0SAASLAoKcHJfY29tbWVudBgGIAEoCzIWLmNvbXBhc3MudjEuQ29tbWVudFJlZkgAEjgKCnN1YnNjcmliZWQYByABKAsyIi5jb21wYXNzLnYxLlN1YnNjcmliZUZvcmdlUmVzcG9uc2VIABI8Cgx1bnN1YnNjcmliZWQYCCABKAsyJC5jb21wYXNzLnYxLlVuc3Vic2NyaWJlRm9yZ2VSZXNwb25zZUgAEisKBWVycm9yGAkgASgLMhouY29tcGFzcy52MS5Gb3JnZUNhbGxFcnJvckgAEicKBnJldmlldxgKIAEoCzIVLmNvbXBhc3MudjEuUmV2aWV3UmVmSABCCAoGcmVzdWx0IkcKDkZvcmdlQ2FsbEVycm9yEgwKBGNvZGUYASABKAkSDwoHbWVzc2FnZRgCIAEoCRIWCg5yZXRyeV9hZnRlcl9tcxgDIAEoDSJPChJDcmVhdGVJc3N1ZVJlcXVlc3QSDAoEcmVwbxgBIAEoCRINCgV0aXRsZRgCIAEoCRIMCgRib2R5GAMgASgJEg4KBmxhYmVscxgEIAMoCSJJChVDb21tZW50T25Jc3N1ZVJlcXVlc3QSDAoEcmVwbxgBIAEoCRIUCgxpc3N1ZV9udW1iZXIYAiABKAQSDAoEYm9keRgDIAEoCSI1Cg9HZXRJc3N1ZVJlcXVlc3QSDAoEcmVwbxgBIAEoCRIUCgxpc3N1ZV9udW1iZXIYAiABKAQiTwoRTGlzdElzc3Vlc1JlcXVlc3QSDAoEcmVwbxgBIAEoCRINCgVzdGF0ZRgCIAEoCRIOCgZsYWJlbHMYAyADKAkSDQoFbGltaXQYBCABKA0iNwoSTGlzdElzc3Vlc1Jlc3BvbnNlEiEKBmlzc3VlcxgBIAMoCzIRLmNvbXBhc3MudjEuSXNzdWUiqQEKGENyZWF0ZVB1bGxSZXF1ZXN0UmVxdWVzdBIMCgRyZXBvGAEgASgJEg0KBXRpdGxlGAIgASgJEgwKBGJvZHkYAyABKAkSEAoIaGVhZF9yZWYYBCABKAkSEAoIYmFzZV9yZWYYBSABKAkSDQoFZHJhZnQYBiABKAgSLwoFaXNzdWUYByABKAsyIC5jb21wYXNzLnYxLlB1bGxSZXF1ZXN0SXNzdWVMaW5rIlkKFFB1bGxSZXF1ZXN0SXNzdWVMaW5rEiMKBWZvcmdlGAEgASgLMhQuY29tcGFzcy52MS5Gb3JnZVJlZhIMCgRyZXBvGAIgASgJEg4KBm51bWJlchgDIAEoBCJMChtDb21tZW50T25QdWxsUmVxdWVzdFJlcXVlc3QSDAoEcmVwbxgBIAEoCRIRCglwcl9udW1iZXIYAiABKAQSDAoEYm9keRgDIAEoCSI4ChVHZXRQdWxsUmVxdWVzdFJlcXVlc3QSDAoEcmVwbxgBIAEoCRIRCglwcl9udW1iZXIYAiABKAQihwEKE1N1Ym1pdFJldmlld1JlcXVlc3QSDAoEcmVwbxgBIAEoCRIRCglwcl9udW1iZXIYAiABKAQSDwoHdmVyZGljdBgDIAEoCRIMCgRib2R5GAQgASgJEjAKCGNvbW1lbnRzGAUgAygLMh4uY29tcGFzcy52MS5SZXZpZXdDb21tZW50SW5wdXQiTAoSUmV2aWV3Q29tbWVudElucHV0EgwKBHBhdGgYASABKAkSDAoEbGluZRgCIAEoDRIMCgRzaWRlGAMgASgJEgwKBGJvZHkYBCABKAkifgobVHJhbnNpdGlvbklzc3VlU3RhdGVSZXF1ZXN0EgwKBHJlcG8YASABKAkSFAoMaXNzdWVfbnVtYmVyGAIgASgEEg0KBXN0YXRlGAMgASgJEhQKDGNsb3NlX3JlYXNvbhgEIAEoCRIWCg53b3JrZmxvd19zdGF0ZRgFIAEoCSJTCiFUcmFuc2l0aW9uUHVsbFJlcXVlc3RTdGF0ZVJlcXVlc3QSDAoEcmVwbxgBIAEoCRIRCglwcl9udW1iZXIYAiABKAQSDQoFc3RhdGUYAyABKAkipgEKFVN1YnNjcmliZUZvcmdlUmVxdWVzdBIMCgRyZXBvGAEgASgJEisKBGtpbmQYAiABKA4yHS5jb21wYXNzLnYxLkZvcmdlQXJ0aWZhY3RLaW5kEg4KBm51bWJlchgDIAEoBBIxCgVzY29wZRgEIAEoDjIiLmNvbXBhc3MudjEuRm9yZ2VTdWJzY3JpcHRpb25TY29wZRIPCgdwcm9qZWN0GAUgASgJIjEKFlN1YnNjcmliZUZvcmdlUmVzcG9uc2USFwoPc3Vic2NyaXB0aW9uX2lkGAEgASgJIjIKF1Vuc3Vic2NyaWJlRm9yZ2VSZXF1ZXN0EhcKD3N1YnNjcmlwdGlvbl9pZBgBIAEoCSIaChhVbnN1YnNjcmliZUZvcmdlUmVzcG9uc2UiaAoQQm9hcmRDYWxsUmVxdWVzdBIPCgdjYWxsX2lkGAEgASgJEjsKD3NldF9pc3N1ZV9zdGF0ZRgCIAEoCzIgLmNvbXBhc3MudjEuU2V0SXNzdWVTdGF0ZVJlcXVlc3RIAEIGCgRjYWxsIk8KFFNldElzc3VlU3RhdGVSZXF1ZXN0EhAKCGlzc3VlX2lkGAEgASgJEiUKBXN0YXRlGAIgASgOMhYuY29tcGFzcy52MS5Jc3N1ZVN0YXRlIjkKFVNldElzc3VlU3RhdGVSZXNwb25zZRIgCgVpc3N1ZRgBIAEoCzIRLmNvbXBhc3MudjEuSXNzdWUilwEKD0JvYXJkQ2FsbFJlc3VsdBIPCgdjYWxsX2lkGAEgASgJEjwKD3NldF9pc3N1ZV9zdGF0ZRgCIAEoCzIhLmNvbXBhc3MudjEuU2V0SXNzdWVTdGF0ZVJlc3BvbnNlSAASKwoFZXJyb3IYAyABKAsyGi5jb21wYXNzLnYxLkJvYXJkQ2FsbEVycm9ySABCCAoGcmVzdWx0Ii8KDkJvYXJkQ2FsbEVycm9yEgwKBGNvZGUYASABKAkSDwoHbWVzc2FnZRgCIAEoCSI8ChNQdWJsaXNoRnJhbWVSZXF1ZXN0EiUKBWZyYW1lGAEgASgLMhYuY29tcGFzcy52MS5BZ2VudEZyYW1lIhYKFFB1Ymxpc2hGcmFtZVJlc3BvbnNlIl4KHFBvc3RDb252ZXJzYXRpb25GcmFtZVJlcXVlc3QSJQoFZnJhbWUYASABKAsyFi5jb21wYXNzLnYxLkFnZW50RnJhbWUSFwoPaWRlbXBvdGVuY3lfa2V5GAIgASgJIh8KHVBvc3RDb252ZXJzYXRpb25GcmFtZVJlc3BvbnNlIhkKF0NvbnRyb2xTdWJzY3JpYmVSZXF1ZXN0KpEBChZGb3JnZVN1YnNjcmlwdGlvblNjb3BlEigKJEZPUkdFX1NVQlNDUklQVElPTl9TQ09QRV9VTlNQRUNJRklFRBAAEiUKIUZPUkdFX1NVQlNDUklQVElPTl9TQ09QRV9BUlRJRkFDVBABEiYKIkZPUkdFX1NVQlNDUklQVElPTl9TQ09QRV9DT05UQUlORVIQAjK0BAoMQWdlbnRHYXRld2F5EkIKBUNvbW1zEhwuY29tcGFzcy52MS5Db21tc0NhbGxSZXF1ZXN0GhsuY29tcGFzcy52MS5Db21tc0NhbGxSZXN1bHQSTgoJTGlmZWN5Y2xlEiAuY29tcGFzcy52MS5MaWZlY3ljbGVDYWxsUmVxdWVzdBofLmNvbXBhc3MudjEuTGlmZWN5Y2xlQ2FsbFJlc3VsdBJOCgdQdWJsaXNoEh8uY29tcGFzcy52MS5QdWJsaXNoRnJhbWVSZXF1ZXN0GiAuY29tcGFzcy52MS5QdWJsaXNoRnJhbWVSZXNwb25zZSgBEmwKFVBvc3RDb252ZXJzYXRpb25GcmFtZRIoLmNvbXBhc3MudjEuUG9zdENvbnZlcnNhdGlvbkZyYW1lUmVxdWVzdBopLmNvbXBhc3MudjEuUG9zdENvbnZlcnNhdGlvbkZyYW1lUmVzcG9uc2USSgoHQ29udHJvbBIjLmNvbXBhc3MudjEuQ29udHJvbFN1YnNjcmliZVJlcXVlc3QaGC5jb21wYXNzLnYxLkFnZW50Q29udHJvbDABEkIKBUZvcmdlEhwuY29tcGFzcy52MS5Gb3JnZUNhbGxSZXF1ZXN0GhsuY29tcGFzcy52MS5Gb3JnZUNhbGxSZXN1bHQSQgoFQm9hcmQSHC5jb21wYXNzLnYxLkJvYXJkQ2FsbFJlcXVlc3QaGy5jb21wYXNzLnYxLkJvYXJkQ2FsbFJlc3VsdGIGcHJvdG8z", [file_compass_v1_comms, file_compass_v1_agent, file_compass_v1_compass, file_compass_v1_forge]); /** * One agent-initiated comms call. `call_id` is the agent-minted correlation id @@ -990,6 +990,14 @@ export type CreatePullRequestRequest = Message<"compass.v1.CreatePullRequestRequ * @generated from field: bool draft = 6; */ draft: boolean; + + /** + * The issue this PR works on. Unset = no explicit link; the server falls + * back to the forge's closing references. + * + * @generated from field: compass.v1.PullRequestIssueLink issue = 7; + */ + issue?: PullRequestIssueLink | undefined; }; /** @@ -999,6 +1007,37 @@ export type CreatePullRequestRequest = Message<"compass.v1.CreatePullRequestRequ export const CreatePullRequestRequestSchema: GenMessage = /*@__PURE__*/ messageDesc(file_compass_v1_agent_gateway, 20); +/** + * @generated from message compass.v1.PullRequestIssueLink + */ +export type PullRequestIssueLink = Message<"compass.v1.PullRequestIssueLink"> & { + /** + * unset = the PR's forge + * + * @generated from field: compass.v1.ForgeRef forge = 1; + */ + forge?: ForgeRef | undefined; + + /** + * owner/name; the team key on Linear + * + * @generated from field: string repo = 2; + */ + repo: string; + + /** + * @generated from field: uint64 number = 3; + */ + number: bigint; +}; + +/** + * Describes the message compass.v1.PullRequestIssueLink. + * Use `create(PullRequestIssueLinkSchema)` to create a new message. + */ +export const PullRequestIssueLinkSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_compass_v1_agent_gateway, 21); + /** * @generated from message compass.v1.CommentOnPullRequestRequest */ @@ -1026,7 +1065,7 @@ export type CommentOnPullRequestRequest = Message<"compass.v1.CommentOnPullReque * Use `create(CommentOnPullRequestRequestSchema)` to create a new message. */ export const CommentOnPullRequestRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 21); + messageDesc(file_compass_v1_agent_gateway, 22); /** * @generated from message compass.v1.GetPullRequestRequest @@ -1048,7 +1087,7 @@ export type GetPullRequestRequest = Message<"compass.v1.GetPullRequestRequest"> * Use `create(GetPullRequestRequestSchema)` to create a new message. */ export const GetPullRequestRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 22); + messageDesc(file_compass_v1_agent_gateway, 23); /** * @generated from message compass.v1.SubmitReviewRequest @@ -1093,7 +1132,7 @@ export type SubmitReviewRequest = Message<"compass.v1.SubmitReviewRequest"> & { * Use `create(SubmitReviewRequestSchema)` to create a new message. */ export const SubmitReviewRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 23); + messageDesc(file_compass_v1_agent_gateway, 24); /** * @generated from message compass.v1.ReviewCommentInput @@ -1133,7 +1172,7 @@ export type ReviewCommentInput = Message<"compass.v1.ReviewCommentInput"> & { * Use `create(ReviewCommentInputSchema)` to create a new message. */ export const ReviewCommentInputSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 24); + messageDesc(file_compass_v1_agent_gateway, 25); /** * Move an existing artifact between forge states. Mutates a coordinate and @@ -1191,7 +1230,7 @@ export type TransitionIssueStateRequest = Message<"compass.v1.TransitionIssueSta * Use `create(TransitionIssueStateRequestSchema)` to create a new message. */ export const TransitionIssueStateRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 25); + messageDesc(file_compass_v1_agent_gateway, 26); /** * The PR twin. No refinement fields: `close_reason` is a GitHub *issue* @@ -1225,7 +1264,7 @@ export type TransitionPullRequestStateRequest = Message<"compass.v1.TransitionPu * Use `create(TransitionPullRequestStateRequestSchema)` to create a new message. */ export const TransitionPullRequestStateRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 26); + messageDesc(file_compass_v1_agent_gateway, 27); /** * @generated from message compass.v1.SubscribeForgeRequest @@ -1270,7 +1309,7 @@ export type SubscribeForgeRequest = Message<"compass.v1.SubscribeForgeRequest"> * Use `create(SubscribeForgeRequestSchema)` to create a new message. */ export const SubscribeForgeRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 27); + messageDesc(file_compass_v1_agent_gateway, 28); /** * @generated from message compass.v1.SubscribeForgeResponse @@ -1287,7 +1326,7 @@ export type SubscribeForgeResponse = Message<"compass.v1.SubscribeForgeResponse" * Use `create(SubscribeForgeResponseSchema)` to create a new message. */ export const SubscribeForgeResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 28); + messageDesc(file_compass_v1_agent_gateway, 29); /** * @generated from message compass.v1.UnsubscribeForgeRequest @@ -1304,7 +1343,7 @@ export type UnsubscribeForgeRequest = Message<"compass.v1.UnsubscribeForgeReques * Use `create(UnsubscribeForgeRequestSchema)` to create a new message. */ export const UnsubscribeForgeRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 29); + messageDesc(file_compass_v1_agent_gateway, 30); /** * @generated from message compass.v1.UnsubscribeForgeResponse @@ -1317,7 +1356,7 @@ export type UnsubscribeForgeResponse = Message<"compass.v1.UnsubscribeForgeRespo * Use `create(UnsubscribeForgeResponseSchema)` to create a new message. */ export const UnsubscribeForgeResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 30); + messageDesc(file_compass_v1_agent_gateway, 31); /** * One agent-initiated board call. `call_id` is the agent-minted correlation id @@ -1351,7 +1390,7 @@ export type BoardCallRequest = Message<"compass.v1.BoardCallRequest"> & { * Use `create(BoardCallRequestSchema)` to create a new message. */ export const BoardCallRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 31); + messageDesc(file_compass_v1_agent_gateway, 32); /** * Set an issue's canonical lifecycle state. Carries the full @@ -1385,7 +1424,7 @@ export type SetIssueStateRequest = Message<"compass.v1.SetIssueStateRequest"> & * Use `create(SetIssueStateRequestSchema)` to create a new message. */ export const SetIssueStateRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 32); + messageDesc(file_compass_v1_agent_gateway, 33); /** * The post-transition truth, correlated in BoardCallResult. Unchanged on a no-op. @@ -1404,7 +1443,7 @@ export type SetIssueStateResponse = Message<"compass.v1.SetIssueStateResponse"> * Use `create(SetIssueStateResponseSchema)` to create a new message. */ export const SetIssueStateResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 33); + messageDesc(file_compass_v1_agent_gateway, 34); /** * The result of one board call, correlated by `call_id`. A successful call sets @@ -1443,7 +1482,7 @@ export type BoardCallResult = Message<"compass.v1.BoardCallResult"> & { * Use `create(BoardCallResultSchema)` to create a new message. */ export const BoardCallResultSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 34); + messageDesc(file_compass_v1_agent_gateway, 35); /** * An in-band board-call failure: a tool error the agent renders to the model, @@ -1469,7 +1508,7 @@ export type BoardCallError = Message<"compass.v1.BoardCallError"> & { * Use `create(BoardCallErrorSchema)` to create a new message. */ export const BoardCallErrorSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 35); + messageDesc(file_compass_v1_agent_gateway, 36); /** * Publish stream element: one trace/session AgentFrame, in emission order. No @@ -1493,7 +1532,7 @@ export type PublishFrameRequest = Message<"compass.v1.PublishFrameRequest"> & { * Use `create(PublishFrameRequestSchema)` to create a new message. */ export const PublishFrameRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 36); + messageDesc(file_compass_v1_agent_gateway, 37); /** * Acked at stream close, mirroring RunnerService.PublishEvents' PublishEventsResponse. @@ -1508,7 +1547,7 @@ export type PublishFrameResponse = Message<"compass.v1.PublishFrameResponse"> & * Use `create(PublishFrameResponseSchema)` to create a new message. */ export const PublishFrameResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 37); + messageDesc(file_compass_v1_agent_gateway, 38); /** * The durable-frame unary carries the SAME AgentFrame message, constrained by @@ -1541,7 +1580,7 @@ export type PostConversationFrameRequest = Message<"compass.v1.PostConversationF * Use `create(PostConversationFrameRequestSchema)` to create a new message. */ export const PostConversationFrameRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 38); + messageDesc(file_compass_v1_agent_gateway, 39); /** * Returned only after the upstream PublishEvents forward is accepted. @@ -1556,7 +1595,7 @@ export type PostConversationFrameResponse = Message<"compass.v1.PostConversation * Use `create(PostConversationFrameResponseSchema)` to create a new message. */ export const PostConversationFrameResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 39); + messageDesc(file_compass_v1_agent_gateway, 40); /** * The Control subscribe request carries no session id: the per-container socket @@ -1572,7 +1611,7 @@ export type ControlSubscribeRequest = Message<"compass.v1.ControlSubscribeReques * Use `create(ControlSubscribeRequestSchema)` to create a new message. */ export const ControlSubscribeRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_compass_v1_agent_gateway, 40); + messageDesc(file_compass_v1_agent_gateway, 41); /** * Subscribe/unsubscribe a forge artifact for change notifications (DL-053). The diff --git a/proto/compass/v1/agent_gateway.proto b/proto/compass/v1/agent_gateway.proto index a758e9859..8f0109aeb 100644 --- a/proto/compass/v1/agent_gateway.proto +++ b/proto/compass/v1/agent_gateway.proto @@ -354,6 +354,14 @@ message CreatePullRequestRequest { string head_ref = 4; // the branch the agent already pushed with ITS OWN git creds string base_ref = 5; // empty = the repo default branch bool draft = 6; + // The issue this PR works on. Unset = no explicit link; the server falls + // back to the forge's closing references. + PullRequestIssueLink issue = 7; +} +message PullRequestIssueLink { + compass.v1.ForgeRef forge = 1; // unset = the PR's forge + string repo = 2; // owner/name; the team key on Linear + uint64 number = 3; } message CommentOnPullRequestRequest { string repo = 1;