diff --git a/admin/server/mcp.go b/admin/server/mcp.go index 1a23f1e063a9..463b181ffb7e 100644 --- a/admin/server/mcp.go +++ b/admin/server/mcp.go @@ -52,6 +52,8 @@ var mcpForwardedTools = []string{ ai.GetMetricsViewName, ai.QueryMetricsViewSummaryName, ai.QueryMetricsViewName, + ai.ListSkillsName, + ai.LoadSkillName, } // mcpSessionNamespace is a UUIDv5 namespace for the per-project session IDs derived in callRuntimeTool. diff --git a/docs/docs/developers/build/ai-configuration.md b/docs/docs/developers/build/ai-configuration.md index aaea0721fa61..f99cfd4f0a5f 100644 --- a/docs/docs/developers/build/ai-configuration.md +++ b/docs/docs/developers/build/ai-configuration.md @@ -16,6 +16,8 @@ There are two places to add `ai_instructions`: 1. **`rill.yaml`**: Project-wide instructions that apply to all queries across your entire project. 2. **`.yaml`**: Metrics view-specific instructions for individual dashboards. +For longer, structured guidance — such as step-by-step analysis playbooks — use [skills](#skills) instead, which the AI loads on demand. + ## Automatic Context Inclusion In addition to `ai_instructions`, Rill automatically includes the following in the AI context: @@ -95,6 +97,52 @@ ai_instructions: | - Weekend traffic patterns are anomalous due to our B2B focus. ``` +## Skills + +Skills teach Rill's AI project-specific practices, such as analysis playbooks (e.g. how to do root-cause analysis for a revenue drop) or business glossaries. Where `ai_instructions` is best for short guidance that always applies, skills hold longer, structured instructions that the AI loads only when they are relevant to the question at hand. Skills apply both in [AI Chat](/guide/ai/ai-chat) and to external AI clients connected via the [MCP Server](/guide/ai/mcp). + +Skills follow the [Agent Skills](https://agentskills.io) format: a skill is a directory containing a `SKILL.md` file with YAML front matter followed by markdown instructions. Rill loads skills from `skills//SKILL.md`, and also from `.agents/skills//SKILL.md` for compatibility with skills authored for other agent clients (note that the Rill Developer file explorer hides dot-directories, so prefer `skills/` for skills you edit in Rill). For example, `skills/revenue-rca/SKILL.md`: + +```markdown +--- +name: revenue-rca +description: Playbook for diagnosing revenue drops. Use when asked why revenue or bookings declined. +--- + +# Revenue root-cause analysis + +When asked why revenue declined: +1. Establish the comparison window and compute the total change. +2. Break the change down by `channel`, then `region`, then `plan_type`. +3. Account for known seasonality: B2B traffic drops on weekends. +4. State your confidence and call out data quirks that may affect the result. +``` + +The `description` is required: the AI sees the list of skills with their descriptions, and uses the description to decide when to load a skill. Phrase it as "what it does + when to use it". Always include the `name`, which the Agent Skills format requires and other clients reject when missing. It must match the skill's directory name (lowercase letters, numbers and hyphens). Rill is lenient and derives it from the directory if omitted. + +In addition to the standard Agent Skills fields, Rill supports these extension properties: + +```markdown +--- +description: Business glossary for our e-commerce metrics. +metrics_views: [orders] # Optional: the metrics views this skill is relevant to +agents: [analyst] # Optional: which agents the skill applies to; defaults to [developer] +always_apply: true # Optional: load the skill up front in every conversation instead of on demand +--- +``` + +- **`metrics_views`** tells the AI which metrics views a skill is relevant to, so for example a marketing playbook is not used during a finance analysis. It is a relevance hint, not access control. Referencing a metrics view that doesn't exist shows an error on the skill file, and the skill is not offered to the AI until the error is fixed. +- **`agents`** selects the agents the skill applies to: `analyst` for answering questions about your data, `developer` for editing the project's files. It defaults to `[developer]`, so skills written for coding agents (such as the Rill development skills that `rill init` writes to `.agents/skills/`) are not offered to the analyst. Set `agents: [analyst]` on analysis skills. +- **`always_apply`** loads the skill up front in every conversation instead of on demand, like `ai_instructions`. Use it for short, broadly applicable guidance such as glossaries. For external MCP clients, always-apply skills are also appended to the `ai_instructions` returned by `list_metrics_views`, up to 32 KiB in total; a skill that doesn't fit must be loaded with `load_skill` and a warning is logged. + +Other agent clients ignore Rill's extension fields, so a Rill skill remains a valid Agent Skill and vice versa. A skill directory may also hold supporting files (such as `references/` or `scripts/`) as the format allows; Rill ignores everything in a skill directory except `SKILL.md`, so a SQL or YAML example inside a skill is not parsed as a project resource. + +Skills are parsed into resources like the rest of your project: invalid skill files (e.g. a missing `description`) show an error on the file in Rill Developer. When the AI uses a skill, the chat response's activity trace shows a "Loaded skill" step, so you can verify a skill was applied and iterate on it: edit the file, ask a test question, and check the trace. + +:::warning Skills are visible to all AI users +Skill contents are provided to every user who can use AI features in the project, including viewers and, on a public project, anonymous visitors. Never put secrets or sensitive data in a skill. Access to the underlying data is still governed by your metrics view security policies. +::: + ## Visualization Tips When using the [Rill MCP Server](/guide/ai/mcp) with external AI clients like Claude, you can provide specific instructions on how to visualize data. Since the MCP server returns structured data, the AI client is responsible for rendering it. diff --git a/docs/docs/guide/ai/mcp.md b/docs/docs/guide/ai/mcp.md index ee1b47b7d26a..3730a34fbe28 100644 --- a/docs/docs/guide/ai/mcp.md +++ b/docs/docs/guide/ai/mcp.md @@ -243,6 +243,8 @@ There are two places to add `ai_instructions`: 1. `rill.yaml` for project-wide context, such as instructions on how to use Rill MCP Server 2. Every metrics view YAML (`.yaml`), with examples of Explore URLs for that metrics view +For longer, structured guidance — such as analysis playbooks and business glossaries — you can also define [skills](/developers/build/ai-configuration#skills) in your project. External MCP clients discover them with the `list_skills` tool and load them on demand with `load_skill`. + For detailed examples and best practices on writing effective AI instructions, see the [AI Configuration guide](/developers/build/ai-configuration). You can look at one of our [example projects](https://github.com/rilldata/rill-examples/tree/main/rill-openrtb-prog-ads) to see how these are used. Experiment with the instructions and see what works best for your requirements. @@ -258,6 +260,8 @@ You can look at one of our [example projects](https://github.com/rilldata/rill-e - __*Get metrics view spec*__ – Use `get_metrics_view` to fetch a metrics view's specification. This is important to understand all the dimensions and measures in a metrics view. - __*Query the time range*__ – Use `query_metrics_view_summary` to obtain the available time range for a metrics view. This is important to understand what time range the data spans. - __*Query the metrics*__ – Use `query_metrics_view` to run queries to get aggregated results. +- __*List skills*__ – Use `list_skills` to discover the [skills](/developers/build/ai-configuration#skills) defined in the project. The skill tools are only exposed when the project defines skills. +- __*Load a skill*__ – Use `load_skill` to fetch a skill's full instructions before doing work its description covers. ### Usage Examples diff --git a/proto/gen/rill/runtime/v1/resources.pb.go b/proto/gen/rill/runtime/v1/resources.pb.go index 20faebfb5331..5d95cf9ad043 100644 --- a/proto/gen/rill/runtime/v1/resources.pb.go +++ b/proto/gen/rill/runtime/v1/resources.pb.go @@ -470,6 +470,7 @@ type Resource struct { // *Resource_Canvas // *Resource_Api // *Resource_Connector + // *Resource_Skill Resource isResource_Resource `protobuf_oneof:"resource"` } @@ -617,6 +618,13 @@ func (x *Resource) GetConnector() *ConnectorV2 { return nil } +func (x *Resource) GetSkill() *Skill { + if x, ok := x.GetResource().(*Resource_Skill); ok { + return x.Skill + } + return nil +} + type isResource_Resource interface { isResource_Resource() } @@ -678,6 +686,10 @@ type Resource_Connector struct { Connector *ConnectorV2 `protobuf:"bytes,16,opt,name=connector,proto3,oneof"` } +type Resource_Skill struct { + Skill *Skill `protobuf:"bytes,18,opt,name=skill,proto3,oneof"` +} + func (*Resource_ProjectParser) isResource_Resource() {} func (*Resource_Source) isResource_Resource() {} @@ -706,6 +718,8 @@ func (*Resource_Api) isResource_Resource() {} func (*Resource_Connector) isResource_Resource() {} +func (*Resource_Skill) isResource_Resource() {} + type ResourceMeta struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -5121,6 +5135,185 @@ func (x *RefreshModelTrigger) GetAllSkippedPartitions() bool { return false } +type Skill struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Spec *SkillSpec `protobuf:"bytes,1,opt,name=spec,proto3" json:"spec,omitempty"` + State *SkillState `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` +} + +func (x *Skill) Reset() { + *x = Skill{} + if protoimpl.UnsafeEnabled { + mi := &file_rill_runtime_v1_resources_proto_msgTypes[46] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Skill) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Skill) ProtoMessage() {} + +func (x *Skill) ProtoReflect() protoreflect.Message { + mi := &file_rill_runtime_v1_resources_proto_msgTypes[46] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Skill.ProtoReflect.Descriptor instead. +func (*Skill) Descriptor() ([]byte, []int) { + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{46} +} + +func (x *Skill) GetSpec() *SkillSpec { + if x != nil { + return x.Spec + } + return nil +} + +func (x *Skill) GetState() *SkillState { + if x != nil { + return x.State + } + return nil +} + +// SkillSpec is parsed from a SKILL.md file that follows the Agent Skills format (https://agentskills.io). +// Skills teach AI agents project-specific practices, such as analysis playbooks and business glossaries. +type SkillSpec struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Description of what the skill does and when to use it. + Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"` + // Markdown body with the skill's full instructions. + Body string `protobuf:"bytes,2,opt,name=body,proto3" json:"body,omitempty"` + // Rill extension: metrics views the skill is relevant to. Empty means all. + MetricsViews []string `protobuf:"bytes,3,rep,name=metrics_views,json=metricsViews,proto3" json:"metrics_views,omitempty"` + // Rill extension: Rill agents the skill applies to ("analyst" and/or "developer"). + Agents []string `protobuf:"bytes,4,rep,name=agents,proto3" json:"agents,omitempty"` + // Rill extension: if true, the skill's body is always injected into the agent's context instead of being loaded on demand. + AlwaysApply bool `protobuf:"varint,5,opt,name=always_apply,json=alwaysApply,proto3" json:"always_apply,omitempty"` +} + +func (x *SkillSpec) Reset() { + *x = SkillSpec{} + if protoimpl.UnsafeEnabled { + mi := &file_rill_runtime_v1_resources_proto_msgTypes[47] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SkillSpec) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SkillSpec) ProtoMessage() {} + +func (x *SkillSpec) ProtoReflect() protoreflect.Message { + mi := &file_rill_runtime_v1_resources_proto_msgTypes[47] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SkillSpec.ProtoReflect.Descriptor instead. +func (*SkillSpec) Descriptor() ([]byte, []int) { + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{47} +} + +func (x *SkillSpec) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *SkillSpec) GetBody() string { + if x != nil { + return x.Body + } + return "" +} + +func (x *SkillSpec) GetMetricsViews() []string { + if x != nil { + return x.MetricsViews + } + return nil +} + +func (x *SkillSpec) GetAgents() []string { + if x != nil { + return x.Agents + } + return nil +} + +func (x *SkillSpec) GetAlwaysApply() bool { + if x != nil { + return x.AlwaysApply + } + return false +} + +type SkillState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *SkillState) Reset() { + *x = SkillState{} + if protoimpl.UnsafeEnabled { + mi := &file_rill_runtime_v1_resources_proto_msgTypes[48] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SkillState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SkillState) ProtoMessage() {} + +func (x *SkillState) ProtoReflect() protoreflect.Message { + mi := &file_rill_runtime_v1_resources_proto_msgTypes[48] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SkillState.ProtoReflect.Descriptor instead. +func (*SkillState) Descriptor() ([]byte, []int) { + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{48} +} + type Theme struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -5133,7 +5326,7 @@ type Theme struct { func (x *Theme) Reset() { *x = Theme{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[46] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5146,7 +5339,7 @@ func (x *Theme) String() string { func (*Theme) ProtoMessage() {} func (x *Theme) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[46] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5159,7 +5352,7 @@ func (x *Theme) ProtoReflect() protoreflect.Message { // Deprecated: Use Theme.ProtoReflect.Descriptor instead. func (*Theme) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{46} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{49} } func (x *Theme) GetSpec() *ThemeSpec { @@ -5192,7 +5385,7 @@ type ThemeSpec struct { func (x *ThemeSpec) Reset() { *x = ThemeSpec{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[47] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5205,7 +5398,7 @@ func (x *ThemeSpec) String() string { func (*ThemeSpec) ProtoMessage() {} func (x *ThemeSpec) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[47] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5218,7 +5411,7 @@ func (x *ThemeSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use ThemeSpec.ProtoReflect.Descriptor instead. func (*ThemeSpec) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{47} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{50} } func (x *ThemeSpec) GetPrimaryColor() *Color { @@ -5272,7 +5465,7 @@ type ThemeState struct { func (x *ThemeState) Reset() { *x = ThemeState{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[48] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5285,7 +5478,7 @@ func (x *ThemeState) String() string { func (*ThemeState) ProtoMessage() {} func (x *ThemeState) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[48] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5298,7 +5491,7 @@ func (x *ThemeState) ProtoReflect() protoreflect.Message { // Deprecated: Use ThemeState.ProtoReflect.Descriptor instead. func (*ThemeState) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{48} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{51} } type ThemeColors struct { @@ -5314,7 +5507,7 @@ type ThemeColors struct { func (x *ThemeColors) Reset() { *x = ThemeColors{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[49] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5327,7 +5520,7 @@ func (x *ThemeColors) String() string { func (*ThemeColors) ProtoMessage() {} func (x *ThemeColors) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[49] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5340,7 +5533,7 @@ func (x *ThemeColors) ProtoReflect() protoreflect.Message { // Deprecated: Use ThemeColors.ProtoReflect.Descriptor instead. func (*ThemeColors) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{49} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{52} } func (x *ThemeColors) GetPrimary() string { @@ -5376,7 +5569,7 @@ type Component struct { func (x *Component) Reset() { *x = Component{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[50] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5389,7 +5582,7 @@ func (x *Component) String() string { func (*Component) ProtoMessage() {} func (x *Component) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[50] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5402,7 +5595,7 @@ func (x *Component) ProtoReflect() protoreflect.Message { // Deprecated: Use Component.ProtoReflect.Descriptor instead. func (*Component) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{50} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{53} } func (x *Component) GetSpec() *ComponentSpec { @@ -5436,7 +5629,7 @@ type ComponentSpec struct { func (x *ComponentSpec) Reset() { *x = ComponentSpec{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[51] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5449,7 +5642,7 @@ func (x *ComponentSpec) String() string { func (*ComponentSpec) ProtoMessage() {} func (x *ComponentSpec) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[51] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5462,7 +5655,7 @@ func (x *ComponentSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use ComponentSpec.ProtoReflect.Descriptor instead. func (*ComponentSpec) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{51} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{54} } func (x *ComponentSpec) GetDisplayName() string { @@ -5529,7 +5722,7 @@ type ComponentState struct { func (x *ComponentState) Reset() { *x = ComponentState{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[52] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5542,7 +5735,7 @@ func (x *ComponentState) String() string { func (*ComponentState) ProtoMessage() {} func (x *ComponentState) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[52] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5555,7 +5748,7 @@ func (x *ComponentState) ProtoReflect() protoreflect.Message { // Deprecated: Use ComponentState.ProtoReflect.Descriptor instead. func (*ComponentState) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{52} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{55} } func (x *ComponentState) GetValidSpec() *ComponentSpec { @@ -5585,7 +5778,7 @@ type ComponentVariable struct { func (x *ComponentVariable) Reset() { *x = ComponentVariable{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[53] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5598,7 +5791,7 @@ func (x *ComponentVariable) String() string { func (*ComponentVariable) ProtoMessage() {} func (x *ComponentVariable) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[53] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5611,7 +5804,7 @@ func (x *ComponentVariable) ProtoReflect() protoreflect.Message { // Deprecated: Use ComponentVariable.ProtoReflect.Descriptor instead. func (*ComponentVariable) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{53} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{56} } func (x *ComponentVariable) GetName() string { @@ -5647,7 +5840,7 @@ type Canvas struct { func (x *Canvas) Reset() { *x = Canvas{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[54] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5660,7 +5853,7 @@ func (x *Canvas) String() string { func (*Canvas) ProtoMessage() {} func (x *Canvas) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[54] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5673,7 +5866,7 @@ func (x *Canvas) ProtoReflect() protoreflect.Message { // Deprecated: Use Canvas.ProtoReflect.Descriptor instead. func (*Canvas) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{54} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{57} } func (x *Canvas) GetSpec() *CanvasSpec { @@ -5741,7 +5934,7 @@ type CanvasSpec struct { func (x *CanvasSpec) Reset() { *x = CanvasSpec{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[55] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5754,7 +5947,7 @@ func (x *CanvasSpec) String() string { func (*CanvasSpec) ProtoMessage() {} func (x *CanvasSpec) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[55] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5767,7 +5960,7 @@ func (x *CanvasSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use CanvasSpec.ProtoReflect.Descriptor instead. func (*CanvasSpec) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{55} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{58} } func (x *CanvasSpec) GetDisplayName() string { @@ -5911,7 +6104,7 @@ type CanvasState struct { func (x *CanvasState) Reset() { *x = CanvasState{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[56] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5924,7 +6117,7 @@ func (x *CanvasState) String() string { func (*CanvasState) ProtoMessage() {} func (x *CanvasState) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[56] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5937,7 +6130,7 @@ func (x *CanvasState) ProtoReflect() protoreflect.Message { // Deprecated: Use CanvasState.ProtoReflect.Descriptor instead. func (*CanvasState) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{56} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{59} } func (x *CanvasState) GetValidSpec() *CanvasSpec { @@ -5973,7 +6166,7 @@ type CanvasRow struct { func (x *CanvasRow) Reset() { *x = CanvasRow{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[57] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5986,7 +6179,7 @@ func (x *CanvasRow) String() string { func (*CanvasRow) ProtoMessage() {} func (x *CanvasRow) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[57] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5999,7 +6192,7 @@ func (x *CanvasRow) ProtoReflect() protoreflect.Message { // Deprecated: Use CanvasRow.ProtoReflect.Descriptor instead. func (*CanvasRow) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{57} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{60} } func (x *CanvasRow) GetHeight() uint32 { @@ -6045,7 +6238,7 @@ type CanvasTabGroup struct { func (x *CanvasTabGroup) Reset() { *x = CanvasTabGroup{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[58] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6058,7 +6251,7 @@ func (x *CanvasTabGroup) String() string { func (*CanvasTabGroup) ProtoMessage() {} func (x *CanvasTabGroup) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[58] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6071,7 +6264,7 @@ func (x *CanvasTabGroup) ProtoReflect() protoreflect.Message { // Deprecated: Use CanvasTabGroup.ProtoReflect.Descriptor instead. func (*CanvasTabGroup) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{58} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{61} } func (x *CanvasTabGroup) GetName() string { @@ -6105,7 +6298,7 @@ type CanvasTab struct { func (x *CanvasTab) Reset() { *x = CanvasTab{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[59] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6118,7 +6311,7 @@ func (x *CanvasTab) String() string { func (*CanvasTab) ProtoMessage() {} func (x *CanvasTab) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[59] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6131,7 +6324,7 @@ func (x *CanvasTab) ProtoReflect() protoreflect.Message { // Deprecated: Use CanvasTab.ProtoReflect.Descriptor instead. func (*CanvasTab) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{59} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{62} } func (x *CanvasTab) GetName() string { @@ -6173,7 +6366,7 @@ type CanvasItem struct { func (x *CanvasItem) Reset() { *x = CanvasItem{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[60] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6186,7 +6379,7 @@ func (x *CanvasItem) String() string { func (*CanvasItem) ProtoMessage() {} func (x *CanvasItem) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[60] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6199,7 +6392,7 @@ func (x *CanvasItem) ProtoReflect() protoreflect.Message { // Deprecated: Use CanvasItem.ProtoReflect.Descriptor instead. func (*CanvasItem) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{60} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{63} } func (x *CanvasItem) GetComponent() string { @@ -6253,7 +6446,7 @@ type CanvasPreset struct { func (x *CanvasPreset) Reset() { *x = CanvasPreset{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[61] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6266,7 +6459,7 @@ func (x *CanvasPreset) String() string { func (*CanvasPreset) ProtoMessage() {} func (x *CanvasPreset) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[61] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6279,7 +6472,7 @@ func (x *CanvasPreset) ProtoReflect() protoreflect.Message { // Deprecated: Use CanvasPreset.ProtoReflect.Descriptor instead. func (*CanvasPreset) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{61} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{64} } func (x *CanvasPreset) GetTimeRange() string { @@ -6321,7 +6514,7 @@ type DefaultMetricsSQLFilter struct { func (x *DefaultMetricsSQLFilter) Reset() { *x = DefaultMetricsSQLFilter{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[62] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6334,7 +6527,7 @@ func (x *DefaultMetricsSQLFilter) String() string { func (*DefaultMetricsSQLFilter) ProtoMessage() {} func (x *DefaultMetricsSQLFilter) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[62] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[65] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6347,7 +6540,7 @@ func (x *DefaultMetricsSQLFilter) ProtoReflect() protoreflect.Message { // Deprecated: Use DefaultMetricsSQLFilter.ProtoReflect.Descriptor instead. func (*DefaultMetricsSQLFilter) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{62} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{65} } func (x *DefaultMetricsSQLFilter) GetExpression() *Expression { @@ -6370,7 +6563,7 @@ type API struct { func (x *API) Reset() { *x = API{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[63] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6383,7 +6576,7 @@ func (x *API) String() string { func (*API) ProtoMessage() {} func (x *API) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[63] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6396,7 +6589,7 @@ func (x *API) ProtoReflect() protoreflect.Message { // Deprecated: Use API.ProtoReflect.Descriptor instead. func (*API) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{63} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{66} } func (x *API) GetSpec() *APISpec { @@ -6432,7 +6625,7 @@ type APISpec struct { func (x *APISpec) Reset() { *x = APISpec{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[64] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6445,7 +6638,7 @@ func (x *APISpec) String() string { func (*APISpec) ProtoMessage() {} func (x *APISpec) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[64] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6458,7 +6651,7 @@ func (x *APISpec) ProtoReflect() protoreflect.Message { // Deprecated: Use APISpec.ProtoReflect.Descriptor instead. func (*APISpec) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{64} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{67} } func (x *APISpec) GetResolver() string { @@ -6533,7 +6726,7 @@ type APIState struct { func (x *APIState) Reset() { *x = APIState{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[65] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6546,7 +6739,7 @@ func (x *APIState) String() string { func (*APIState) ProtoMessage() {} func (x *APIState) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[65] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[68] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6559,7 +6752,7 @@ func (x *APIState) ProtoReflect() protoreflect.Message { // Deprecated: Use APIState.ProtoReflect.Descriptor instead. func (*APIState) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{65} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{68} } type Schedule struct { @@ -6577,7 +6770,7 @@ type Schedule struct { func (x *Schedule) Reset() { *x = Schedule{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[66] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6590,7 +6783,7 @@ func (x *Schedule) String() string { func (*Schedule) ProtoMessage() {} func (x *Schedule) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[66] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6603,7 +6796,7 @@ func (x *Schedule) ProtoReflect() protoreflect.Message { // Deprecated: Use Schedule.ProtoReflect.Descriptor instead. func (*Schedule) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{66} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{69} } func (x *Schedule) GetRefUpdate() bool { @@ -6656,7 +6849,7 @@ type ParseError struct { func (x *ParseError) Reset() { *x = ParseError{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[67] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6669,7 +6862,7 @@ func (x *ParseError) String() string { func (*ParseError) ProtoMessage() {} func (x *ParseError) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[67] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6682,7 +6875,7 @@ func (x *ParseError) ProtoReflect() protoreflect.Message { // Deprecated: Use ParseError.ProtoReflect.Descriptor instead. func (*ParseError) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{67} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{70} } func (x *ParseError) GetMessage() string { @@ -6732,7 +6925,7 @@ type ValidationError struct { func (x *ValidationError) Reset() { *x = ValidationError{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[68] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6745,7 +6938,7 @@ func (x *ValidationError) String() string { func (*ValidationError) ProtoMessage() {} func (x *ValidationError) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[68] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[71] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6758,7 +6951,7 @@ func (x *ValidationError) ProtoReflect() protoreflect.Message { // Deprecated: Use ValidationError.ProtoReflect.Descriptor instead. func (*ValidationError) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{68} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{71} } func (x *ValidationError) GetMessage() string { @@ -6787,7 +6980,7 @@ type DependencyError struct { func (x *DependencyError) Reset() { *x = DependencyError{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[69] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6800,7 +6993,7 @@ func (x *DependencyError) String() string { func (*DependencyError) ProtoMessage() {} func (x *DependencyError) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[69] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[72] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6813,7 +7006,7 @@ func (x *DependencyError) ProtoReflect() protoreflect.Message { // Deprecated: Use DependencyError.ProtoReflect.Descriptor instead. func (*DependencyError) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{69} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{72} } func (x *DependencyError) GetMessage() string { @@ -6841,7 +7034,7 @@ type ExecutionError struct { func (x *ExecutionError) Reset() { *x = ExecutionError{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[70] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6854,7 +7047,7 @@ func (x *ExecutionError) String() string { func (*ExecutionError) ProtoMessage() {} func (x *ExecutionError) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[70] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[73] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6867,7 +7060,7 @@ func (x *ExecutionError) ProtoReflect() protoreflect.Message { // Deprecated: Use ExecutionError.ProtoReflect.Descriptor instead. func (*ExecutionError) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{70} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{73} } func (x *ExecutionError) GetMessage() string { @@ -6888,7 +7081,7 @@ type CharLocation struct { func (x *CharLocation) Reset() { *x = CharLocation{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[71] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6901,7 +7094,7 @@ func (x *CharLocation) String() string { func (*CharLocation) ProtoMessage() {} func (x *CharLocation) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[71] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[74] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6914,7 +7107,7 @@ func (x *CharLocation) ProtoReflect() protoreflect.Message { // Deprecated: Use CharLocation.ProtoReflect.Descriptor instead. func (*CharLocation) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{71} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{74} } func (x *CharLocation) GetLine() uint32 { @@ -6936,7 +7129,7 @@ type ConnectorV2 struct { func (x *ConnectorV2) Reset() { *x = ConnectorV2{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[72] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6949,7 +7142,7 @@ func (x *ConnectorV2) String() string { func (*ConnectorV2) ProtoMessage() {} func (x *ConnectorV2) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[72] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[75] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6962,7 +7155,7 @@ func (x *ConnectorV2) ProtoReflect() protoreflect.Message { // Deprecated: Use ConnectorV2.ProtoReflect.Descriptor instead. func (*ConnectorV2) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{72} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{75} } func (x *ConnectorV2) GetSpec() *ConnectorSpec { @@ -6994,7 +7187,7 @@ type ConnectorSpec struct { func (x *ConnectorSpec) Reset() { *x = ConnectorSpec{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[73] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7007,7 +7200,7 @@ func (x *ConnectorSpec) String() string { func (*ConnectorSpec) ProtoMessage() {} func (x *ConnectorSpec) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[73] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[76] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7020,7 +7213,7 @@ func (x *ConnectorSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use ConnectorSpec.ProtoReflect.Descriptor instead. func (*ConnectorSpec) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{73} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{76} } func (x *ConnectorSpec) GetDriver() string { @@ -7069,7 +7262,7 @@ type ConnectorState struct { func (x *ConnectorState) Reset() { *x = ConnectorState{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[74] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7082,7 +7275,7 @@ func (x *ConnectorState) String() string { func (*ConnectorState) ProtoMessage() {} func (x *ConnectorState) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[74] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[77] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7095,7 +7288,7 @@ func (x *ConnectorState) ProtoReflect() protoreflect.Message { // Deprecated: Use ConnectorState.ProtoReflect.Descriptor instead. func (*ConnectorState) Descriptor() ([]byte, []int) { - return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{74} + return file_rill_runtime_v1_resources_proto_rawDescGZIP(), []int{77} } func (x *ConnectorState) GetSpecHash() string { @@ -7135,7 +7328,7 @@ type MetricsViewSpec_Dimension struct { func (x *MetricsViewSpec_Dimension) Reset() { *x = MetricsViewSpec_Dimension{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[76] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7148,7 +7341,7 @@ func (x *MetricsViewSpec_Dimension) String() string { func (*MetricsViewSpec_Dimension) ProtoMessage() {} func (x *MetricsViewSpec_Dimension) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[76] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[79] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7283,7 +7476,7 @@ type MetricsViewSpec_DimensionSelector struct { func (x *MetricsViewSpec_DimensionSelector) Reset() { *x = MetricsViewSpec_DimensionSelector{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[77] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7296,7 +7489,7 @@ func (x *MetricsViewSpec_DimensionSelector) String() string { func (*MetricsViewSpec_DimensionSelector) ProtoMessage() {} func (x *MetricsViewSpec_DimensionSelector) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[77] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[80] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7350,7 +7543,7 @@ type MetricsViewSpec_MeasureWindow struct { func (x *MetricsViewSpec_MeasureWindow) Reset() { *x = MetricsViewSpec_MeasureWindow{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[78] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7363,7 +7556,7 @@ func (x *MetricsViewSpec_MeasureWindow) String() string { func (*MetricsViewSpec_MeasureWindow) ProtoMessage() {} func (x *MetricsViewSpec_MeasureWindow) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[78] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[81] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7430,7 +7623,7 @@ type MetricsViewSpec_Measure struct { func (x *MetricsViewSpec_Measure) Reset() { *x = MetricsViewSpec_Measure{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[79] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7443,7 +7636,7 @@ func (x *MetricsViewSpec_Measure) String() string { func (*MetricsViewSpec_Measure) ProtoMessage() {} func (x *MetricsViewSpec_Measure) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[79] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[82] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7611,7 +7804,7 @@ type MetricsViewSpec_Annotation struct { func (x *MetricsViewSpec_Annotation) Reset() { *x = MetricsViewSpec_Annotation{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[80] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7624,7 +7817,7 @@ func (x *MetricsViewSpec_Annotation) String() string { func (*MetricsViewSpec_Annotation) ProtoMessage() {} func (x *MetricsViewSpec_Annotation) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[80] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[83] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7741,7 +7934,7 @@ type MetricsViewSpec_Rollup struct { func (x *MetricsViewSpec_Rollup) Reset() { *x = MetricsViewSpec_Rollup{} if protoimpl.UnsafeEnabled { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[81] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7754,7 +7947,7 @@ func (x *MetricsViewSpec_Rollup) String() string { func (*MetricsViewSpec_Rollup) ProtoMessage() {} func (x *MetricsViewSpec_Rollup) ProtoReflect() protoreflect.Message { - mi := &file_rill_runtime_v1_resources_proto_msgTypes[81] + mi := &file_rill_runtime_v1_resources_proto_msgTypes[84] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7867,7 +8060,7 @@ var file_rill_runtime_v1_resources_proto_rawDesc = []byte{ 0x69, 0x6d, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x72, 0x69, 0x6c, 0x6c, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x67, 0x72, 0x61, 0x69, 0x6e, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe0, 0x06, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x90, 0x07, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x31, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, @@ -7920,7 +8113,10 @@ var file_rill_runtime_v1_resources_proto_rawDesc = []byte{ 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x56, 0x32, 0x48, - 0x00, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x42, 0x0a, 0x0a, 0x08, + 0x00, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x2e, 0x0a, 0x05, + 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6b, + 0x69, 0x6c, 0x6c, 0x48, 0x00, 0x52, 0x05, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0xbb, 0x08, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x31, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, @@ -9149,415 +9345,433 @@ var file_rill_runtime_v1_resources_proto_rawDesc = []byte{ 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x61, 0x6c, 0x6c, 0x53, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x6a, 0x0a, - 0x05, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, + 0x05, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x12, 0x2e, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, - 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x70, 0x65, 0x63, + 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x31, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x98, 0x03, 0x0a, 0x09, 0x54, 0x68, - 0x65, 0x6d, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x40, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6d, 0x61, - 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, - 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, - 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x44, 0x0a, 0x0f, 0x73, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x48, 0x01, 0x52, 0x0e, 0x73, 0x65, - 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, - 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, - 0x5f, 0x72, 0x61, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x69, 0x6d, - 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x52, 0x61, 0x77, 0x12, 0x2e, 0x0a, 0x13, 0x73, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, - 0x61, 0x77, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, - 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x52, 0x61, 0x77, 0x12, 0x37, 0x0a, 0x05, 0x6c, - 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x69, 0x6c, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xa1, 0x01, 0x0a, 0x09, 0x53, 0x6b, + 0x69, 0x6c, 0x6c, 0x53, 0x70, 0x65, 0x63, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x23, 0x0a, + 0x0d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x76, 0x69, 0x65, 0x77, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x56, 0x69, 0x65, + 0x77, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x6c, + 0x77, 0x61, 0x79, 0x73, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0b, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x22, 0x0c, 0x0a, + 0x0a, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x6a, 0x0a, 0x05, 0x54, + 0x68, 0x65, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, + 0x73, 0x70, 0x65, 0x63, 0x12, 0x31, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x98, 0x03, 0x0a, 0x09, 0x54, 0x68, 0x65, 0x6d, + 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x40, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, + 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x72, + 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x6f, 0x6c, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x43, + 0x6f, 0x6c, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x44, 0x0a, 0x0f, 0x73, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x48, 0x01, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x6f, + 0x6e, 0x64, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x2a, 0x0a, + 0x11, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, + 0x61, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, + 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x52, 0x61, 0x77, 0x12, 0x2e, 0x0a, 0x13, 0x73, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x5f, 0x72, 0x61, 0x77, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, + 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x52, 0x61, 0x77, 0x12, 0x37, 0x0a, 0x05, 0x6c, 0x69, 0x67, + 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, + 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x48, 0x02, 0x52, 0x05, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x88, + 0x01, 0x01, 0x12, 0x35, 0x0a, 0x04, 0x64, 0x61, 0x72, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x48, 0x03, + 0x52, 0x04, 0x64, 0x61, 0x72, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x70, 0x72, + 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x42, 0x12, 0x0a, 0x10, 0x5f, + 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x42, + 0x08, 0x0a, 0x06, 0x5f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x64, 0x61, + 0x72, 0x6b, 0x22, 0x0c, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x22, 0xce, 0x01, 0x0a, 0x0b, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x73, + 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x12, 0x49, 0x0a, 0x09, 0x76, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x68, + 0x65, 0x6d, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, + 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, + 0x6c, 0x65, 0x73, 0x1a, 0x3c, 0x0a, 0x0e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x76, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x32, + 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, + 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, + 0x65, 0x63, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xdc, 0x02, 0x0a, 0x0d, 0x43, 0x6f, + 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x21, 0x0a, 0x0c, 0x64, + 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, + 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x13, + 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, + 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, + 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, + 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, + 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, + 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, + 0x12, 0x3a, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6c, 0x65, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x2a, 0x0a, 0x11, + 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, + 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, + 0x49, 0x6e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x22, 0x97, 0x01, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, + 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, + 0x09, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x70, 0x65, 0x63, 0x12, 0x46, 0x0a, 0x11, 0x64, 0x61, + 0x74, 0x61, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x65, 0x64, + 0x4f, 0x6e, 0x22, 0x78, 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, + 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x3b, 0x0a, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, + 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x6d, 0x0a, 0x06, + 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x12, 0x2f, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, + 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x32, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x8a, 0x07, 0x0a, 0x0a, + 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, + 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, + 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x77, 0x69, 0x64, + 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x57, 0x69, 0x64, + 0x74, 0x68, 0x12, 0x13, 0x0a, 0x05, 0x67, 0x61, 0x70, 0x5f, 0x78, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x04, 0x67, 0x61, 0x70, 0x58, 0x12, 0x13, 0x0a, 0x05, 0x67, 0x61, 0x70, 0x5f, 0x79, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x67, 0x61, 0x70, 0x59, 0x12, 0x14, 0x0a, 0x05, + 0x74, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x68, 0x65, + 0x6d, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x65, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, + 0x68, 0x65, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, - 0x6d, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x48, 0x02, 0x52, 0x05, 0x6c, 0x69, 0x67, 0x68, - 0x74, 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, 0x04, 0x64, 0x61, 0x72, 0x6b, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x73, - 0x48, 0x03, 0x52, 0x04, 0x64, 0x61, 0x72, 0x6b, 0x88, 0x01, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x5f, - 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x42, 0x12, 0x0a, - 0x10, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, - 0x72, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x5f, - 0x64, 0x61, 0x72, 0x6b, 0x22, 0x0c, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x22, 0xce, 0x01, 0x0a, 0x0b, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x43, 0x6f, 0x6c, 0x6f, - 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x1c, 0x0a, 0x09, - 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x12, 0x49, 0x0a, 0x09, 0x76, 0x61, - 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, + 0x6d, 0x65, 0x53, 0x70, 0x65, 0x63, 0x52, 0x0d, 0x65, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, + 0x54, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x42, 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, + 0x6e, 0x67, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x72, 0x69, 0x6c, + 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, + 0x6c, 0x6f, 0x72, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x0a, 0x74, + 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x17, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, + 0x61, 0x6e, 0x67, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, + 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x73, 0x18, 0x0c, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x12, + 0x27, 0x0a, 0x0f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x44, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x73, 0x65, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x50, 0x72, 0x65, 0x73, 0x65, 0x74, 0x52, + 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x72, 0x65, 0x73, 0x65, 0x74, 0x12, 0x40, + 0x0a, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, + 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, + 0x12, 0x2e, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, + 0x12, 0x44, 0x0a, 0x0e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x72, 0x75, 0x6c, + 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, + 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, + 0x79, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, + 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, + 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, 0x29, 0x0a, + 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, 0x4e, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x54, 0x68, 0x65, 0x6d, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x73, 0x2e, 0x56, 0x61, 0x72, 0x69, - 0x61, 0x62, 0x6c, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x76, 0x61, 0x72, 0x69, - 0x61, 0x62, 0x6c, 0x65, 0x73, 0x1a, 0x3c, 0x0a, 0x0e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, - 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0x76, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, - 0x12, 0x32, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, + 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, + 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x91, 0x01, 0x0a, 0x0b, 0x43, 0x61, 0x6e, + 0x76, 0x61, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, + 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x53, 0x70, 0x65, 0x63, 0x12, 0x46, 0x0a, 0x11, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, 0x65, 0x66, + 0x72, 0x65, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0f, 0x64, 0x61, 0x74, + 0x61, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x6e, 0x22, 0xc5, 0x01, 0x0a, + 0x09, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x52, 0x6f, 0x77, 0x12, 0x1b, 0x0a, 0x06, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, + 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, + 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x3c, 0x0a, 0x09, 0x74, + 0x61, 0x62, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, - 0x73, 0x70, 0x65, 0x63, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xdc, 0x02, 0x0a, 0x0d, - 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x21, 0x0a, - 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x12, 0x48, - 0x0a, 0x13, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, - 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x50, 0x72, - 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, - 0x74, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, - 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x05, 0x69, 0x6e, 0x70, - 0x75, 0x74, 0x12, 0x3a, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, 0x61, - 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x2a, - 0x0a, 0x11, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x63, 0x61, 0x6e, - 0x76, 0x61, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x65, 0x66, 0x69, 0x6e, - 0x65, 0x64, 0x49, 0x6e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x22, 0x97, 0x01, 0x0a, 0x0e, 0x43, - 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3d, 0x0a, - 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x53, 0x70, 0x65, - 0x63, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x53, 0x70, 0x65, 0x63, 0x12, 0x46, 0x0a, 0x11, - 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x6f, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, - 0x65, 0x64, 0x4f, 0x6e, 0x22, 0x78, 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, - 0x74, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x12, 0x3b, 0x0a, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x52, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x6d, - 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x12, 0x2f, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, - 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, - 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x32, 0x0a, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, + 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x54, 0x61, 0x62, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, + 0x08, 0x74, 0x61, 0x62, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x22, 0x54, 0x0a, 0x0e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x54, 0x61, + 0x62, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x61, + 0x62, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, - 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x8a, 0x07, - 0x0a, 0x0a, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x12, 0x21, 0x0a, 0x0c, - 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x16, 0x0a, 0x06, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x77, - 0x69, 0x64, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x57, - 0x69, 0x64, 0x74, 0x68, 0x12, 0x13, 0x0a, 0x05, 0x67, 0x61, 0x70, 0x5f, 0x78, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x04, 0x67, 0x61, 0x70, 0x58, 0x12, 0x13, 0x0a, 0x05, 0x67, 0x61, 0x70, - 0x5f, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x67, 0x61, 0x70, 0x59, 0x12, 0x14, - 0x0a, 0x05, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, - 0x68, 0x65, 0x6d, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x65, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, - 0x5f, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, - 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, - 0x68, 0x65, 0x6d, 0x65, 0x53, 0x70, 0x65, 0x63, 0x52, 0x0d, 0x65, 0x6d, 0x62, 0x65, 0x64, 0x64, - 0x65, 0x64, 0x54, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x42, 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x5f, - 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x72, - 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, - 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x52, - 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x17, 0x61, - 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x61, 0x6c, - 0x6c, 0x6f, 0x77, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, - 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x73, - 0x18, 0x0c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5a, 0x6f, 0x6e, 0x65, - 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x66, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x44, 0x0a, 0x0e, 0x64, 0x65, - 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x73, 0x65, 0x74, 0x18, 0x0f, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x50, 0x72, 0x65, 0x73, 0x65, - 0x74, 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x72, 0x65, 0x73, 0x65, 0x74, - 0x12, 0x40, 0x0a, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x56, - 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, - 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, - 0x77, 0x73, 0x12, 0x44, 0x0a, 0x0e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x72, - 0x75, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, - 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x73, 0x65, 0x63, 0x75, 0x72, - 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x69, 0x6e, 0x6e, - 0x65, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0d, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, - 0x29, 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, - 0x72, 0x65, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, 0x4e, 0x0a, 0x0b, 0x61, 0x6e, - 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2c, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x41, 0x6e, 0x6e, - 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, - 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, - 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x91, 0x01, 0x0a, 0x0b, 0x43, - 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0a, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, + 0x73, 0x54, 0x61, 0x62, 0x52, 0x04, 0x74, 0x61, 0x62, 0x73, 0x22, 0x72, 0x0a, 0x09, 0x43, 0x61, + 0x6e, 0x76, 0x61, 0x73, 0x54, 0x61, 0x62, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x64, + 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2e, + 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, + 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x61, 0x6e, 0x76, 0x61, 0x73, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x22, 0x9a, + 0x01, 0x0a, 0x0a, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1c, 0x0a, + 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x63, 0x61, 0x6e, 0x76, 0x61, 0x73, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x49, + 0x6e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x12, 0x19, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x88, + 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x0a, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x75, 0x6e, 0x69, 0x74, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x69, 0x64, 0x74, 0x68, 0x55, 0x6e, 0x69, + 0x74, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x9c, 0x03, 0x0a, 0x0c, + 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x50, 0x72, 0x65, 0x73, 0x65, 0x74, 0x12, 0x22, 0x0a, 0x0a, + 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x00, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x88, 0x01, 0x01, + 0x12, 0x4f, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x5f, 0x6d, + 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x72, 0x69, 0x6c, 0x6c, + 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x6c, + 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, + 0x65, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, + 0x65, 0x12, 0x36, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x5f, + 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x01, 0x52, 0x13, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x44, 0x69, 0x6d, + 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x4e, 0x0a, 0x0b, 0x66, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x53, 0x70, 0x65, 0x63, 0x52, 0x09, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x53, 0x70, 0x65, 0x63, 0x12, 0x46, 0x0a, 0x11, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, - 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0f, 0x64, - 0x61, 0x74, 0x61, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x6e, 0x22, 0xc5, - 0x01, 0x0a, 0x09, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x52, 0x6f, 0x77, 0x12, 0x1b, 0x0a, 0x06, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x06, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x69, 0x74, - 0x65, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, - 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, - 0x61, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x3c, 0x0a, - 0x09, 0x74, 0x61, 0x62, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x54, 0x61, 0x62, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x52, 0x08, 0x74, 0x61, 0x62, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x42, 0x09, 0x0a, 0x07, 0x5f, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x54, 0x0a, 0x0e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, - 0x54, 0x61, 0x62, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x04, - 0x74, 0x61, 0x62, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x72, 0x69, 0x6c, - 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, - 0x76, 0x61, 0x73, 0x54, 0x61, 0x62, 0x52, 0x04, 0x74, 0x61, 0x62, 0x73, 0x22, 0x72, 0x0a, 0x09, - 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x54, 0x61, 0x62, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, - 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x2e, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, - 0x22, 0x9a, 0x01, 0x0a, 0x0a, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x12, - 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x2a, 0x0a, - 0x11, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x5f, 0x63, 0x61, 0x6e, 0x76, - 0x61, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, - 0x64, 0x49, 0x6e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x12, 0x19, 0x0a, 0x05, 0x77, 0x69, 0x64, - 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, - 0x68, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x0a, 0x77, 0x69, 0x64, 0x74, 0x68, 0x5f, 0x75, 0x6e, - 0x69, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x69, 0x64, 0x74, 0x68, 0x55, - 0x6e, 0x69, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x9c, 0x03, - 0x0a, 0x0c, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x50, 0x72, 0x65, 0x73, 0x65, 0x74, 0x12, 0x22, - 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x88, - 0x01, 0x01, 0x12, 0x4f, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, - 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x72, 0x69, - 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, - 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, - 0x6f, 0x64, 0x65, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, - 0x6f, 0x64, 0x65, 0x12, 0x36, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, - 0x6e, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x01, 0x52, 0x13, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x44, - 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x4e, 0x0a, 0x0b, 0x66, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x2d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x50, 0x72, 0x65, 0x73, 0x65, 0x74, 0x2e, - 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x45, 0x78, 0x70, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x0a, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x45, 0x78, 0x70, 0x72, 0x1a, 0x67, 0x0a, 0x0f, 0x46, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x45, 0x78, 0x70, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x3e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x28, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, - 0x53, 0x51, 0x4c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, - 0x6e, 0x67, 0x65, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, - 0x6f, 0x6e, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x56, 0x0a, 0x17, - 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x53, 0x51, - 0x4c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x3b, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, - 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, - 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x64, 0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x2c, 0x0a, 0x04, 0x73, - 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x72, 0x69, 0x6c, 0x6c, - 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x53, - 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x2f, 0x0a, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, - 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xf8, 0x03, 0x0a, 0x07, 0x41, - 0x50, 0x49, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, - 0x65, 0x72, 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x5f, 0x70, - 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, - 0x65, 0x72, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, - 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x53, 0x75, - 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x36, 0x0a, 0x17, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, - 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x50, - 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, - 0x1b, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x18, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x1c, - 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x19, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, - 0x13, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x64, 0x65, 0x66, 0x73, 0x5f, 0x70, 0x72, - 0x65, 0x66, 0x69, 0x78, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x6f, 0x70, 0x65, 0x6e, - 0x61, 0x70, 0x69, 0x44, 0x65, 0x66, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x44, 0x0a, - 0x0e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, - 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, - 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x75, - 0x6c, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x6e, 0x65, 0x73, 0x74, - 0x65, 0x64, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x12, 0x73, 0x6b, 0x69, 0x70, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x53, 0x65, 0x63, - 0x75, 0x72, 0x69, 0x74, 0x79, 0x22, 0x0a, 0x0a, 0x08, 0x41, 0x50, 0x49, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x22, 0x9b, 0x01, 0x0a, 0x08, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x1d, - 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x66, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, - 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x74, - 0x69, 0x63, 0x6b, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, - 0x64, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x22, - 0xbf, 0x01, 0x0a, 0x0a, 0x50, 0x61, 0x72, 0x73, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, - 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, - 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, - 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x44, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6c, - 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, + 0x2e, 0x43, 0x61, 0x6e, 0x76, 0x61, 0x73, 0x50, 0x72, 0x65, 0x73, 0x65, 0x74, 0x2e, 0x46, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x45, 0x78, 0x70, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x45, 0x78, 0x70, 0x72, 0x1a, 0x67, 0x0a, 0x0f, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x45, 0x78, 0x70, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3e, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x68, 0x61, 0x72, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x65, - 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, - 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, - 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, - 0x67, 0x22, 0x50, 0x0a, 0x0f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, - 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x23, - 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x50, - 0x61, 0x74, 0x68, 0x22, 0x4b, 0x0a, 0x0f, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, - 0x79, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, - 0x22, 0x2a, 0x0a, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, + 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x53, 0x51, + 0x4c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, + 0x65, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, + 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x56, 0x0a, 0x17, 0x44, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x53, 0x51, 0x4c, 0x46, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x3b, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x72, 0x69, 0x6c, 0x6c, + 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x72, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x22, 0x64, 0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x2c, 0x0a, 0x04, 0x73, 0x70, 0x65, + 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, + 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x53, 0x70, 0x65, + 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x2f, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x50, 0x49, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xf8, 0x03, 0x0a, 0x07, 0x41, 0x50, 0x49, + 0x53, 0x70, 0x65, 0x63, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, + 0x12, 0x48, 0x0a, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, + 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6f, 0x70, + 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x53, 0x75, 0x6d, 0x6d, + 0x61, 0x72, 0x79, 0x12, 0x36, 0x0a, 0x17, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x70, + 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x1b, 0x6f, + 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x73, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x18, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x1c, 0x6f, 0x70, + 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x73, + 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x19, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x6f, + 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x64, 0x65, 0x66, 0x73, 0x5f, 0x70, 0x72, 0x65, 0x66, + 0x69, 0x78, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, + 0x69, 0x44, 0x65, 0x66, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x44, 0x0a, 0x0e, 0x73, + 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x75, + 0x6c, 0x65, 0x52, 0x0d, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x75, 0x6c, 0x65, + 0x73, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, + 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x12, 0x73, 0x6b, 0x69, 0x70, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x53, 0x65, 0x63, 0x75, 0x72, + 0x69, 0x74, 0x79, 0x22, 0x0a, 0x0a, 0x08, 0x41, 0x50, 0x49, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, + 0x9b, 0x01, 0x0a, 0x08, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x72, 0x65, 0x66, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x09, 0x72, 0x65, 0x66, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, + 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x69, + 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x69, 0x63, + 0x6b, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0d, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, + 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x5a, 0x6f, 0x6e, 0x65, 0x22, 0xbf, 0x01, + 0x0a, 0x0a, 0x50, 0x61, 0x72, 0x73, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, + 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, + 0x61, 0x74, 0x68, 0x12, 0x44, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, + 0x61, 0x72, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x22, + 0x50, 0x0a, 0x0f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x22, 0x0a, 0x0c, - 0x43, 0x68, 0x61, 0x72, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, - 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, - 0x22, 0x78, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x56, 0x32, 0x12, - 0x32, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, - 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, - 0x70, 0x65, 0x63, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xf1, 0x01, 0x0a, 0x0d, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x16, 0x0a, 0x06, - 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x72, - 0x69, 0x76, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, - 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, - 0x74, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x31, 0x0a, - 0x14, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, - 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, - 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3e, - 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x72, 0x67, 0x73, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x23, 0x0a, 0x0d, + 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x50, 0x61, 0x74, + 0x68, 0x22, 0x4b, 0x0a, 0x0f, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x45, + 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1e, + 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x22, 0x2a, + 0x0a, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x22, 0x0a, 0x0c, 0x43, 0x68, + 0x61, 0x72, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, + 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x78, + 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x56, 0x32, 0x12, 0x32, 0x0a, + 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x69, + 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, + 0x63, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1f, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0xf1, 0x01, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x72, + 0x69, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x72, 0x69, 0x76, + 0x65, 0x72, 0x12, 0x37, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, - 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x67, 0x73, 0x22, 0x2d, - 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, 0x48, 0x61, 0x73, 0x68, 0x2a, 0x8a, 0x01, - 0x0a, 0x0f, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, - 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, - 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, - 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x1c, - 0x0a, 0x18, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, - 0x55, 0x53, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, + 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, + 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1c, + 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x0e, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0d, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x67, 0x73, 0x22, 0x2d, 0x0a, 0x0e, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, + 0x0a, 0x09, 0x73, 0x70, 0x65, 0x63, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x73, 0x70, 0x65, 0x63, 0x48, 0x61, 0x73, 0x68, 0x2a, 0x8a, 0x01, 0x0a, 0x0f, + 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x20, 0x0a, 0x1c, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, + 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x19, 0x0a, 0x15, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, + 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, - 0x5f, 0x52, 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x2a, 0x8c, 0x01, 0x0a, 0x0f, 0x4d, - 0x6f, 0x64, 0x65, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x21, - 0x0a, 0x1d, 0x4d, 0x4f, 0x44, 0x45, 0x4c, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x5f, 0x4d, - 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, - 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x4f, 0x44, 0x45, 0x4c, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x47, - 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x45, 0x54, 0x10, 0x01, 0x12, 0x1c, - 0x0a, 0x18, 0x4d, 0x4f, 0x44, 0x45, 0x4c, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x5f, 0x4d, - 0x4f, 0x44, 0x45, 0x5f, 0x4d, 0x41, 0x4e, 0x55, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, + 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, + 0x43, 0x4f, 0x4e, 0x43, 0x49, 0x4c, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, + 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x2a, 0x8c, 0x01, 0x0a, 0x0f, 0x4d, 0x6f, 0x64, + 0x65, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x0a, 0x1d, 0x4d, 0x4f, 0x44, 0x45, 0x4c, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x5f, 0x4d, 0x4f, 0x44, - 0x45, 0x5f, 0x50, 0x41, 0x54, 0x43, 0x48, 0x10, 0x03, 0x2a, 0xab, 0x01, 0x0a, 0x15, 0x45, 0x78, - 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, - 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x23, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, - 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, - 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x20, 0x0a, 0x1c, - 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, - 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x20, - 0x0a, 0x1c, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, - 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x10, 0x02, - 0x12, 0x25, 0x0a, 0x21, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, - 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x44, 0x49, 0x4d, 0x45, - 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x2a, 0xae, 0x01, 0x0a, 0x0e, 0x45, 0x78, 0x70, 0x6c, - 0x6f, 0x72, 0x65, 0x57, 0x65, 0x62, 0x56, 0x69, 0x65, 0x77, 0x12, 0x20, 0x0a, 0x1c, 0x45, 0x58, - 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x45, 0x42, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x55, - 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, - 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x45, 0x42, 0x5f, 0x56, 0x49, 0x45, 0x57, - 0x5f, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1f, 0x45, 0x58, - 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x45, 0x42, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x54, - 0x49, 0x4d, 0x45, 0x5f, 0x44, 0x49, 0x4d, 0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, - 0x1a, 0x0a, 0x16, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x45, 0x42, 0x5f, 0x56, - 0x49, 0x45, 0x57, 0x5f, 0x50, 0x49, 0x56, 0x4f, 0x54, 0x10, 0x03, 0x12, 0x1b, 0x0a, 0x17, 0x45, - 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x45, 0x42, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, - 0x43, 0x41, 0x4e, 0x56, 0x41, 0x53, 0x10, 0x04, 0x2a, 0xdc, 0x01, 0x0a, 0x0f, 0x45, 0x78, 0x70, - 0x6c, 0x6f, 0x72, 0x65, 0x53, 0x6f, 0x72, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x1d, - 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, - 0x1b, 0x0a, 0x17, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, - 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x50, 0x45, 0x52, 0x43, 0x45, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x23, 0x0a, 0x1f, 0x45, - 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x44, 0x45, 0x4c, 0x54, 0x41, 0x5f, 0x50, 0x45, 0x52, 0x43, 0x45, 0x4e, 0x54, 0x10, 0x03, - 0x12, 0x24, 0x0a, 0x20, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x4c, 0x54, 0x41, 0x5f, 0x41, 0x42, 0x53, 0x4f, - 0x4c, 0x55, 0x54, 0x45, 0x10, 0x04, 0x12, 0x1f, 0x0a, 0x1b, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, - 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x49, 0x4d, 0x45, - 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x05, 0x2a, 0x85, 0x01, 0x0a, 0x0f, 0x41, 0x73, 0x73, 0x65, - 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x1c, 0x41, + 0x1b, 0x0a, 0x17, 0x4d, 0x4f, 0x44, 0x45, 0x4c, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x5f, + 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x45, 0x54, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, + 0x4d, 0x4f, 0x44, 0x45, 0x4c, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x5f, 0x4d, 0x4f, 0x44, + 0x45, 0x5f, 0x4d, 0x41, 0x4e, 0x55, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x4f, + 0x44, 0x45, 0x4c, 0x5f, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, + 0x50, 0x41, 0x54, 0x43, 0x48, 0x10, 0x03, 0x2a, 0xab, 0x01, 0x0a, 0x15, 0x45, 0x78, 0x70, 0x6c, + 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, + 0x65, 0x12, 0x27, 0x0a, 0x23, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, + 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, + 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x20, 0x0a, 0x1c, 0x45, 0x58, + 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, 0x4f, 0x4e, + 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, + 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, 0x49, 0x53, + 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x10, 0x02, 0x12, 0x25, + 0x0a, 0x21, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x41, 0x52, + 0x49, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x44, 0x49, 0x4d, 0x45, 0x4e, 0x53, + 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x2a, 0xae, 0x01, 0x0a, 0x0e, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x72, + 0x65, 0x57, 0x65, 0x62, 0x56, 0x69, 0x65, 0x77, 0x12, 0x20, 0x0a, 0x1c, 0x45, 0x58, 0x50, 0x4c, + 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x45, 0x42, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x55, 0x4e, 0x53, + 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x45, 0x58, + 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x45, 0x42, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x45, + 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1f, 0x45, 0x58, 0x50, 0x4c, + 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x45, 0x42, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x54, 0x49, 0x4d, + 0x45, 0x5f, 0x44, 0x49, 0x4d, 0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x1a, 0x0a, + 0x16, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x45, 0x42, 0x5f, 0x56, 0x49, 0x45, + 0x57, 0x5f, 0x50, 0x49, 0x56, 0x4f, 0x54, 0x10, 0x03, 0x12, 0x1b, 0x0a, 0x17, 0x45, 0x58, 0x50, + 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x57, 0x45, 0x42, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x5f, 0x43, 0x41, + 0x4e, 0x56, 0x41, 0x53, 0x10, 0x04, 0x2a, 0xdc, 0x01, 0x0a, 0x0f, 0x45, 0x78, 0x70, 0x6c, 0x6f, + 0x72, 0x65, 0x53, 0x6f, 0x72, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x1d, 0x45, 0x58, + 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, + 0x17, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x45, 0x58, + 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x50, 0x45, 0x52, 0x43, 0x45, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x23, 0x0a, 0x1f, 0x45, 0x58, 0x50, + 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, + 0x45, 0x4c, 0x54, 0x41, 0x5f, 0x50, 0x45, 0x52, 0x43, 0x45, 0x4e, 0x54, 0x10, 0x03, 0x12, 0x24, + 0x0a, 0x20, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x4c, 0x54, 0x41, 0x5f, 0x41, 0x42, 0x53, 0x4f, 0x4c, 0x55, + 0x54, 0x45, 0x10, 0x04, 0x12, 0x1f, 0x0a, 0x1b, 0x45, 0x58, 0x50, 0x4c, 0x4f, 0x52, 0x45, 0x5f, + 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x49, 0x4d, 0x45, 0x4e, 0x53, + 0x49, 0x4f, 0x4e, 0x10, 0x05, 0x2a, 0x85, 0x01, 0x0a, 0x0f, 0x41, 0x73, 0x73, 0x65, 0x72, 0x74, + 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x20, 0x0a, 0x1c, 0x41, 0x53, 0x53, + 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, - 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, - 0x15, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, - 0x53, 0x5f, 0x50, 0x41, 0x53, 0x53, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x53, 0x53, 0x45, - 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x41, 0x49, - 0x4c, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, - 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x42, - 0xc1, 0x01, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, - 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x69, 0x6c, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x72, - 0x69, 0x6c, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x72, 0x69, - 0x6c, 0x6c, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x72, 0x75, - 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x52, 0x52, 0x58, 0xaa, 0x02, 0x0f, - 0x52, 0x69, 0x6c, 0x6c, 0x2e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x56, 0x31, 0xca, - 0x02, 0x0f, 0x52, 0x69, 0x6c, 0x6c, 0x5c, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5c, 0x56, - 0x31, 0xe2, 0x02, 0x1b, 0x52, 0x69, 0x6c, 0x6c, 0x5c, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, - 0x02, 0x11, 0x52, 0x69, 0x6c, 0x6c, 0x3a, 0x3a, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x3a, - 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x50, 0x41, 0x53, 0x53, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x10, + 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x41, 0x53, 0x53, 0x45, 0x52, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, + 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x42, 0xc1, 0x01, + 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x72, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x2e, 0x76, 0x31, 0x42, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x69, 0x6c, 0x6c, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x72, 0x69, 0x6c, + 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x72, 0x69, 0x6c, 0x6c, + 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x72, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x52, 0x52, 0x58, 0xaa, 0x02, 0x0f, 0x52, 0x69, + 0x6c, 0x6c, 0x2e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0f, + 0x52, 0x69, 0x6c, 0x6c, 0x5c, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5c, 0x56, 0x31, 0xe2, + 0x02, 0x1b, 0x52, 0x69, 0x6c, 0x6c, 0x5c, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5c, 0x56, + 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x11, + 0x52, 0x69, 0x6c, 0x6c, 0x3a, 0x3a, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x3a, 0x3a, 0x56, + 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -9573,7 +9787,7 @@ func file_rill_runtime_v1_resources_proto_rawDescGZIP() []byte { } var file_rill_runtime_v1_resources_proto_enumTypes = make([]protoimpl.EnumInfo, 8) -var file_rill_runtime_v1_resources_proto_msgTypes = make([]protoimpl.MessageInfo, 88) +var file_rill_runtime_v1_resources_proto_msgTypes = make([]protoimpl.MessageInfo, 91) var file_rill_runtime_v1_resources_proto_goTypes = []any{ (ReconcileStatus)(0), // 0: rill.runtime.v1.ReconcileStatus (ModelChangeMode)(0), // 1: rill.runtime.v1.ModelChangeMode @@ -9629,57 +9843,60 @@ var file_rill_runtime_v1_resources_proto_goTypes = []any{ (*RefreshTriggerSpec)(nil), // 51: rill.runtime.v1.RefreshTriggerSpec (*RefreshTriggerState)(nil), // 52: rill.runtime.v1.RefreshTriggerState (*RefreshModelTrigger)(nil), // 53: rill.runtime.v1.RefreshModelTrigger - (*Theme)(nil), // 54: rill.runtime.v1.Theme - (*ThemeSpec)(nil), // 55: rill.runtime.v1.ThemeSpec - (*ThemeState)(nil), // 56: rill.runtime.v1.ThemeState - (*ThemeColors)(nil), // 57: rill.runtime.v1.ThemeColors - (*Component)(nil), // 58: rill.runtime.v1.Component - (*ComponentSpec)(nil), // 59: rill.runtime.v1.ComponentSpec - (*ComponentState)(nil), // 60: rill.runtime.v1.ComponentState - (*ComponentVariable)(nil), // 61: rill.runtime.v1.ComponentVariable - (*Canvas)(nil), // 62: rill.runtime.v1.Canvas - (*CanvasSpec)(nil), // 63: rill.runtime.v1.CanvasSpec - (*CanvasState)(nil), // 64: rill.runtime.v1.CanvasState - (*CanvasRow)(nil), // 65: rill.runtime.v1.CanvasRow - (*CanvasTabGroup)(nil), // 66: rill.runtime.v1.CanvasTabGroup - (*CanvasTab)(nil), // 67: rill.runtime.v1.CanvasTab - (*CanvasItem)(nil), // 68: rill.runtime.v1.CanvasItem - (*CanvasPreset)(nil), // 69: rill.runtime.v1.CanvasPreset - (*DefaultMetricsSQLFilter)(nil), // 70: rill.runtime.v1.DefaultMetricsSQLFilter - (*API)(nil), // 71: rill.runtime.v1.API - (*APISpec)(nil), // 72: rill.runtime.v1.APISpec - (*APIState)(nil), // 73: rill.runtime.v1.APIState - (*Schedule)(nil), // 74: rill.runtime.v1.Schedule - (*ParseError)(nil), // 75: rill.runtime.v1.ParseError - (*ValidationError)(nil), // 76: rill.runtime.v1.ValidationError - (*DependencyError)(nil), // 77: rill.runtime.v1.DependencyError - (*ExecutionError)(nil), // 78: rill.runtime.v1.ExecutionError - (*CharLocation)(nil), // 79: rill.runtime.v1.CharLocation - (*ConnectorV2)(nil), // 80: rill.runtime.v1.ConnectorV2 - (*ConnectorSpec)(nil), // 81: rill.runtime.v1.ConnectorSpec - (*ConnectorState)(nil), // 82: rill.runtime.v1.ConnectorState - nil, // 83: rill.runtime.v1.ResourceMeta.MetadataEntry - (*MetricsViewSpec_Dimension)(nil), // 84: rill.runtime.v1.MetricsViewSpec.Dimension - (*MetricsViewSpec_DimensionSelector)(nil), // 85: rill.runtime.v1.MetricsViewSpec.DimensionSelector - (*MetricsViewSpec_MeasureWindow)(nil), // 86: rill.runtime.v1.MetricsViewSpec.MeasureWindow - (*MetricsViewSpec_Measure)(nil), // 87: rill.runtime.v1.MetricsViewSpec.Measure - (*MetricsViewSpec_Annotation)(nil), // 88: rill.runtime.v1.MetricsViewSpec.Annotation - (*MetricsViewSpec_Rollup)(nil), // 89: rill.runtime.v1.MetricsViewSpec.Rollup - nil, // 90: rill.runtime.v1.MetricsViewSpec.QueryAttributesEntry - nil, // 91: rill.runtime.v1.ReportSpec.AnnotationsEntry - nil, // 92: rill.runtime.v1.AlertSpec.AnnotationsEntry - nil, // 93: rill.runtime.v1.ThemeColors.VariablesEntry - nil, // 94: rill.runtime.v1.CanvasSpec.AnnotationsEntry - nil, // 95: rill.runtime.v1.CanvasPreset.FilterExprEntry - (*timestamppb.Timestamp)(nil), // 96: google.protobuf.Timestamp - (*structpb.Struct)(nil), // 97: google.protobuf.Struct - (*StructType)(nil), // 98: rill.runtime.v1.StructType - (TimeGrain)(0), // 99: rill.runtime.v1.TimeGrain - (*Expression)(nil), // 100: rill.runtime.v1.Expression - (ExportFormat)(0), // 101: rill.runtime.v1.ExportFormat - (*Color)(nil), // 102: rill.runtime.v1.Color - (*structpb.Value)(nil), // 103: google.protobuf.Value - (*Type)(nil), // 104: rill.runtime.v1.Type + (*Skill)(nil), // 54: rill.runtime.v1.Skill + (*SkillSpec)(nil), // 55: rill.runtime.v1.SkillSpec + (*SkillState)(nil), // 56: rill.runtime.v1.SkillState + (*Theme)(nil), // 57: rill.runtime.v1.Theme + (*ThemeSpec)(nil), // 58: rill.runtime.v1.ThemeSpec + (*ThemeState)(nil), // 59: rill.runtime.v1.ThemeState + (*ThemeColors)(nil), // 60: rill.runtime.v1.ThemeColors + (*Component)(nil), // 61: rill.runtime.v1.Component + (*ComponentSpec)(nil), // 62: rill.runtime.v1.ComponentSpec + (*ComponentState)(nil), // 63: rill.runtime.v1.ComponentState + (*ComponentVariable)(nil), // 64: rill.runtime.v1.ComponentVariable + (*Canvas)(nil), // 65: rill.runtime.v1.Canvas + (*CanvasSpec)(nil), // 66: rill.runtime.v1.CanvasSpec + (*CanvasState)(nil), // 67: rill.runtime.v1.CanvasState + (*CanvasRow)(nil), // 68: rill.runtime.v1.CanvasRow + (*CanvasTabGroup)(nil), // 69: rill.runtime.v1.CanvasTabGroup + (*CanvasTab)(nil), // 70: rill.runtime.v1.CanvasTab + (*CanvasItem)(nil), // 71: rill.runtime.v1.CanvasItem + (*CanvasPreset)(nil), // 72: rill.runtime.v1.CanvasPreset + (*DefaultMetricsSQLFilter)(nil), // 73: rill.runtime.v1.DefaultMetricsSQLFilter + (*API)(nil), // 74: rill.runtime.v1.API + (*APISpec)(nil), // 75: rill.runtime.v1.APISpec + (*APIState)(nil), // 76: rill.runtime.v1.APIState + (*Schedule)(nil), // 77: rill.runtime.v1.Schedule + (*ParseError)(nil), // 78: rill.runtime.v1.ParseError + (*ValidationError)(nil), // 79: rill.runtime.v1.ValidationError + (*DependencyError)(nil), // 80: rill.runtime.v1.DependencyError + (*ExecutionError)(nil), // 81: rill.runtime.v1.ExecutionError + (*CharLocation)(nil), // 82: rill.runtime.v1.CharLocation + (*ConnectorV2)(nil), // 83: rill.runtime.v1.ConnectorV2 + (*ConnectorSpec)(nil), // 84: rill.runtime.v1.ConnectorSpec + (*ConnectorState)(nil), // 85: rill.runtime.v1.ConnectorState + nil, // 86: rill.runtime.v1.ResourceMeta.MetadataEntry + (*MetricsViewSpec_Dimension)(nil), // 87: rill.runtime.v1.MetricsViewSpec.Dimension + (*MetricsViewSpec_DimensionSelector)(nil), // 88: rill.runtime.v1.MetricsViewSpec.DimensionSelector + (*MetricsViewSpec_MeasureWindow)(nil), // 89: rill.runtime.v1.MetricsViewSpec.MeasureWindow + (*MetricsViewSpec_Measure)(nil), // 90: rill.runtime.v1.MetricsViewSpec.Measure + (*MetricsViewSpec_Annotation)(nil), // 91: rill.runtime.v1.MetricsViewSpec.Annotation + (*MetricsViewSpec_Rollup)(nil), // 92: rill.runtime.v1.MetricsViewSpec.Rollup + nil, // 93: rill.runtime.v1.MetricsViewSpec.QueryAttributesEntry + nil, // 94: rill.runtime.v1.ReportSpec.AnnotationsEntry + nil, // 95: rill.runtime.v1.AlertSpec.AnnotationsEntry + nil, // 96: rill.runtime.v1.ThemeColors.VariablesEntry + nil, // 97: rill.runtime.v1.CanvasSpec.AnnotationsEntry + nil, // 98: rill.runtime.v1.CanvasPreset.FilterExprEntry + (*timestamppb.Timestamp)(nil), // 99: google.protobuf.Timestamp + (*structpb.Struct)(nil), // 100: google.protobuf.Struct + (*StructType)(nil), // 101: rill.runtime.v1.StructType + (TimeGrain)(0), // 102: rill.runtime.v1.TimeGrain + (*Expression)(nil), // 103: rill.runtime.v1.Expression + (ExportFormat)(0), // 104: rill.runtime.v1.ExportFormat + (*Color)(nil), // 105: rill.runtime.v1.Color + (*structpb.Value)(nil), // 106: google.protobuf.Value + (*Type)(nil), // 107: rill.runtime.v1.Type } var file_rill_runtime_v1_resources_proto_depIdxs = []int32{ 9, // 0: rill.runtime.v1.Resource.meta:type_name -> rill.runtime.v1.ResourceMeta @@ -9692,186 +9909,189 @@ var file_rill_runtime_v1_resources_proto_depIdxs = []int32{ 40, // 7: rill.runtime.v1.Resource.report:type_name -> rill.runtime.v1.Report 44, // 8: rill.runtime.v1.Resource.alert:type_name -> rill.runtime.v1.Alert 50, // 9: rill.runtime.v1.Resource.refresh_trigger:type_name -> rill.runtime.v1.RefreshTrigger - 54, // 10: rill.runtime.v1.Resource.theme:type_name -> rill.runtime.v1.Theme - 58, // 11: rill.runtime.v1.Resource.component:type_name -> rill.runtime.v1.Component - 62, // 12: rill.runtime.v1.Resource.canvas:type_name -> rill.runtime.v1.Canvas - 71, // 13: rill.runtime.v1.Resource.api:type_name -> rill.runtime.v1.API - 80, // 14: rill.runtime.v1.Resource.connector:type_name -> rill.runtime.v1.ConnectorV2 - 10, // 15: rill.runtime.v1.ResourceMeta.name:type_name -> rill.runtime.v1.ResourceName - 10, // 16: rill.runtime.v1.ResourceMeta.refs:type_name -> rill.runtime.v1.ResourceName - 10, // 17: rill.runtime.v1.ResourceMeta.owner:type_name -> rill.runtime.v1.ResourceName - 83, // 18: rill.runtime.v1.ResourceMeta.metadata:type_name -> rill.runtime.v1.ResourceMeta.MetadataEntry - 96, // 19: rill.runtime.v1.ResourceMeta.created_on:type_name -> google.protobuf.Timestamp - 96, // 20: rill.runtime.v1.ResourceMeta.spec_updated_on:type_name -> google.protobuf.Timestamp - 96, // 21: rill.runtime.v1.ResourceMeta.state_updated_on:type_name -> google.protobuf.Timestamp - 96, // 22: rill.runtime.v1.ResourceMeta.deleted_on:type_name -> google.protobuf.Timestamp - 0, // 23: rill.runtime.v1.ResourceMeta.reconcile_status:type_name -> rill.runtime.v1.ReconcileStatus - 96, // 24: rill.runtime.v1.ResourceMeta.reconcile_on:type_name -> google.protobuf.Timestamp - 10, // 25: rill.runtime.v1.ResourceMeta.renamed_from:type_name -> rill.runtime.v1.ResourceName - 12, // 26: rill.runtime.v1.ProjectParser.spec:type_name -> rill.runtime.v1.ProjectParserSpec - 13, // 27: rill.runtime.v1.ProjectParser.state:type_name -> rill.runtime.v1.ProjectParserState - 75, // 28: rill.runtime.v1.ProjectParserState.parse_errors:type_name -> rill.runtime.v1.ParseError - 96, // 29: rill.runtime.v1.ProjectParserState.current_commit_on:type_name -> google.protobuf.Timestamp - 15, // 30: rill.runtime.v1.Source.spec:type_name -> rill.runtime.v1.SourceSpec - 16, // 31: rill.runtime.v1.Source.state:type_name -> rill.runtime.v1.SourceState - 97, // 32: rill.runtime.v1.SourceSpec.properties:type_name -> google.protobuf.Struct - 74, // 33: rill.runtime.v1.SourceSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule - 96, // 34: rill.runtime.v1.SourceState.refreshed_on:type_name -> google.protobuf.Timestamp - 18, // 35: rill.runtime.v1.Model.spec:type_name -> rill.runtime.v1.ModelSpec - 19, // 36: rill.runtime.v1.Model.state:type_name -> rill.runtime.v1.ModelState - 74, // 37: rill.runtime.v1.ModelSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule - 97, // 38: rill.runtime.v1.ModelSpec.incremental_state_resolver_properties:type_name -> google.protobuf.Struct - 97, // 39: rill.runtime.v1.ModelSpec.partitions_resolver_properties:type_name -> google.protobuf.Struct - 97, // 40: rill.runtime.v1.ModelSpec.input_properties:type_name -> google.protobuf.Struct - 97, // 41: rill.runtime.v1.ModelSpec.stage_properties:type_name -> google.protobuf.Struct - 97, // 42: rill.runtime.v1.ModelSpec.output_properties:type_name -> google.protobuf.Struct - 1, // 43: rill.runtime.v1.ModelSpec.change_mode:type_name -> rill.runtime.v1.ModelChangeMode - 20, // 44: rill.runtime.v1.ModelSpec.tests:type_name -> rill.runtime.v1.ModelTest - 97, // 45: rill.runtime.v1.ModelState.result_properties:type_name -> google.protobuf.Struct - 96, // 46: rill.runtime.v1.ModelState.refreshed_on:type_name -> google.protobuf.Timestamp - 97, // 47: rill.runtime.v1.ModelState.incremental_state:type_name -> google.protobuf.Struct - 98, // 48: rill.runtime.v1.ModelState.incremental_state_schema:type_name -> rill.runtime.v1.StructType - 97, // 49: rill.runtime.v1.ModelTest.resolver_properties:type_name -> google.protobuf.Struct - 22, // 50: rill.runtime.v1.MetricsView.spec:type_name -> rill.runtime.v1.MetricsViewSpec - 28, // 51: rill.runtime.v1.MetricsView.state:type_name -> rill.runtime.v1.MetricsViewState - 99, // 52: rill.runtime.v1.MetricsViewSpec.smallest_time_grain:type_name -> rill.runtime.v1.TimeGrain - 84, // 53: rill.runtime.v1.MetricsViewSpec.dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.Dimension - 87, // 54: rill.runtime.v1.MetricsViewSpec.measures:type_name -> rill.runtime.v1.MetricsViewSpec.Measure - 35, // 55: rill.runtime.v1.MetricsViewSpec.parent_dimensions:type_name -> rill.runtime.v1.FieldSelector - 35, // 56: rill.runtime.v1.MetricsViewSpec.parent_measures:type_name -> rill.runtime.v1.FieldSelector - 88, // 57: rill.runtime.v1.MetricsViewSpec.annotations:type_name -> rill.runtime.v1.MetricsViewSpec.Annotation - 23, // 58: rill.runtime.v1.MetricsViewSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule - 90, // 59: rill.runtime.v1.MetricsViewSpec.query_attributes:type_name -> rill.runtime.v1.MetricsViewSpec.QueryAttributesEntry - 89, // 60: rill.runtime.v1.MetricsViewSpec.rollups:type_name -> rill.runtime.v1.MetricsViewSpec.Rollup - 24, // 61: rill.runtime.v1.SecurityRule.access:type_name -> rill.runtime.v1.SecurityRuleAccess - 25, // 62: rill.runtime.v1.SecurityRule.field_access:type_name -> rill.runtime.v1.SecurityRuleFieldAccess - 26, // 63: rill.runtime.v1.SecurityRule.row_filter:type_name -> rill.runtime.v1.SecurityRuleRowFilter - 27, // 64: rill.runtime.v1.SecurityRule.transitive_access:type_name -> rill.runtime.v1.SecurityRuleTransitiveAccess - 10, // 65: rill.runtime.v1.SecurityRuleAccess.condition_resources:type_name -> rill.runtime.v1.ResourceName - 10, // 66: rill.runtime.v1.SecurityRuleFieldAccess.condition_resources:type_name -> rill.runtime.v1.ResourceName - 10, // 67: rill.runtime.v1.SecurityRuleRowFilter.condition_resources:type_name -> rill.runtime.v1.ResourceName - 100, // 68: rill.runtime.v1.SecurityRuleRowFilter.expression:type_name -> rill.runtime.v1.Expression - 10, // 69: rill.runtime.v1.SecurityRuleTransitiveAccess.resource:type_name -> rill.runtime.v1.ResourceName - 22, // 70: rill.runtime.v1.MetricsViewState.valid_spec:type_name -> rill.runtime.v1.MetricsViewSpec - 96, // 71: rill.runtime.v1.MetricsViewState.data_refreshed_on:type_name -> google.protobuf.Timestamp - 30, // 72: rill.runtime.v1.Explore.spec:type_name -> rill.runtime.v1.ExploreSpec - 31, // 73: rill.runtime.v1.Explore.state:type_name -> rill.runtime.v1.ExploreState - 35, // 74: rill.runtime.v1.ExploreSpec.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector - 35, // 75: rill.runtime.v1.ExploreSpec.measures_selector:type_name -> rill.runtime.v1.FieldSelector - 55, // 76: rill.runtime.v1.ExploreSpec.embedded_theme:type_name -> rill.runtime.v1.ThemeSpec - 32, // 77: rill.runtime.v1.ExploreSpec.time_ranges:type_name -> rill.runtime.v1.ExploreTimeRange - 34, // 78: rill.runtime.v1.ExploreSpec.default_preset:type_name -> rill.runtime.v1.ExplorePreset - 23, // 79: rill.runtime.v1.ExploreSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule - 30, // 80: rill.runtime.v1.ExploreState.valid_spec:type_name -> rill.runtime.v1.ExploreSpec - 96, // 81: rill.runtime.v1.ExploreState.data_refreshed_on:type_name -> google.protobuf.Timestamp - 33, // 82: rill.runtime.v1.ExploreTimeRange.comparison_time_ranges:type_name -> rill.runtime.v1.ExploreComparisonTimeRange - 35, // 83: rill.runtime.v1.ExplorePreset.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector - 35, // 84: rill.runtime.v1.ExplorePreset.measures_selector:type_name -> rill.runtime.v1.FieldSelector - 100, // 85: rill.runtime.v1.ExplorePreset.where:type_name -> rill.runtime.v1.Expression - 2, // 86: rill.runtime.v1.ExplorePreset.comparison_mode:type_name -> rill.runtime.v1.ExploreComparisonMode - 3, // 87: rill.runtime.v1.ExplorePreset.view:type_name -> rill.runtime.v1.ExploreWebView - 4, // 88: rill.runtime.v1.ExplorePreset.explore_sort_type:type_name -> rill.runtime.v1.ExploreSortType - 36, // 89: rill.runtime.v1.FieldSelector.fields:type_name -> rill.runtime.v1.StringListValue - 38, // 90: rill.runtime.v1.Migration.spec:type_name -> rill.runtime.v1.MigrationSpec - 39, // 91: rill.runtime.v1.Migration.state:type_name -> rill.runtime.v1.MigrationState - 41, // 92: rill.runtime.v1.Report.spec:type_name -> rill.runtime.v1.ReportSpec - 42, // 93: rill.runtime.v1.Report.state:type_name -> rill.runtime.v1.ReportState - 74, // 94: rill.runtime.v1.ReportSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule - 97, // 95: rill.runtime.v1.ReportSpec.resolver_properties:type_name -> google.protobuf.Struct - 101, // 96: rill.runtime.v1.ReportSpec.export_format:type_name -> rill.runtime.v1.ExportFormat - 46, // 97: rill.runtime.v1.ReportSpec.notifiers:type_name -> rill.runtime.v1.Notifier - 91, // 98: rill.runtime.v1.ReportSpec.annotations:type_name -> rill.runtime.v1.ReportSpec.AnnotationsEntry - 96, // 99: rill.runtime.v1.ReportState.next_run_on:type_name -> google.protobuf.Timestamp - 43, // 100: rill.runtime.v1.ReportState.current_execution:type_name -> rill.runtime.v1.ReportExecution - 43, // 101: rill.runtime.v1.ReportState.execution_history:type_name -> rill.runtime.v1.ReportExecution - 96, // 102: rill.runtime.v1.ReportExecution.report_time:type_name -> google.protobuf.Timestamp - 96, // 103: rill.runtime.v1.ReportExecution.started_on:type_name -> google.protobuf.Timestamp - 96, // 104: rill.runtime.v1.ReportExecution.finished_on:type_name -> google.protobuf.Timestamp - 45, // 105: rill.runtime.v1.Alert.spec:type_name -> rill.runtime.v1.AlertSpec - 47, // 106: rill.runtime.v1.Alert.state:type_name -> rill.runtime.v1.AlertState - 74, // 107: rill.runtime.v1.AlertSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule - 97, // 108: rill.runtime.v1.AlertSpec.resolver_properties:type_name -> google.protobuf.Struct - 97, // 109: rill.runtime.v1.AlertSpec.query_for_attributes:type_name -> google.protobuf.Struct - 46, // 110: rill.runtime.v1.AlertSpec.notifiers:type_name -> rill.runtime.v1.Notifier - 92, // 111: rill.runtime.v1.AlertSpec.annotations:type_name -> rill.runtime.v1.AlertSpec.AnnotationsEntry - 97, // 112: rill.runtime.v1.Notifier.properties:type_name -> google.protobuf.Struct - 96, // 113: rill.runtime.v1.AlertState.next_run_on:type_name -> google.protobuf.Timestamp - 48, // 114: rill.runtime.v1.AlertState.current_execution:type_name -> rill.runtime.v1.AlertExecution - 48, // 115: rill.runtime.v1.AlertState.execution_history:type_name -> rill.runtime.v1.AlertExecution - 49, // 116: rill.runtime.v1.AlertExecution.result:type_name -> rill.runtime.v1.AssertionResult - 96, // 117: rill.runtime.v1.AlertExecution.execution_time:type_name -> google.protobuf.Timestamp - 96, // 118: rill.runtime.v1.AlertExecution.started_on:type_name -> google.protobuf.Timestamp - 96, // 119: rill.runtime.v1.AlertExecution.finished_on:type_name -> google.protobuf.Timestamp - 96, // 120: rill.runtime.v1.AlertExecution.suppressed_since:type_name -> google.protobuf.Timestamp - 5, // 121: rill.runtime.v1.AssertionResult.status:type_name -> rill.runtime.v1.AssertionStatus - 97, // 122: rill.runtime.v1.AssertionResult.fail_row:type_name -> google.protobuf.Struct - 51, // 123: rill.runtime.v1.RefreshTrigger.spec:type_name -> rill.runtime.v1.RefreshTriggerSpec - 52, // 124: rill.runtime.v1.RefreshTrigger.state:type_name -> rill.runtime.v1.RefreshTriggerState - 10, // 125: rill.runtime.v1.RefreshTriggerSpec.resources:type_name -> rill.runtime.v1.ResourceName - 53, // 126: rill.runtime.v1.RefreshTriggerSpec.models:type_name -> rill.runtime.v1.RefreshModelTrigger - 55, // 127: rill.runtime.v1.Theme.spec:type_name -> rill.runtime.v1.ThemeSpec - 56, // 128: rill.runtime.v1.Theme.state:type_name -> rill.runtime.v1.ThemeState - 102, // 129: rill.runtime.v1.ThemeSpec.primary_color:type_name -> rill.runtime.v1.Color - 102, // 130: rill.runtime.v1.ThemeSpec.secondary_color:type_name -> rill.runtime.v1.Color - 57, // 131: rill.runtime.v1.ThemeSpec.light:type_name -> rill.runtime.v1.ThemeColors - 57, // 132: rill.runtime.v1.ThemeSpec.dark:type_name -> rill.runtime.v1.ThemeColors - 93, // 133: rill.runtime.v1.ThemeColors.variables:type_name -> rill.runtime.v1.ThemeColors.VariablesEntry - 59, // 134: rill.runtime.v1.Component.spec:type_name -> rill.runtime.v1.ComponentSpec - 60, // 135: rill.runtime.v1.Component.state:type_name -> rill.runtime.v1.ComponentState - 97, // 136: rill.runtime.v1.ComponentSpec.renderer_properties:type_name -> google.protobuf.Struct - 61, // 137: rill.runtime.v1.ComponentSpec.input:type_name -> rill.runtime.v1.ComponentVariable - 61, // 138: rill.runtime.v1.ComponentSpec.output:type_name -> rill.runtime.v1.ComponentVariable - 59, // 139: rill.runtime.v1.ComponentState.valid_spec:type_name -> rill.runtime.v1.ComponentSpec - 96, // 140: rill.runtime.v1.ComponentState.data_refreshed_on:type_name -> google.protobuf.Timestamp - 103, // 141: rill.runtime.v1.ComponentVariable.default_value:type_name -> google.protobuf.Value - 63, // 142: rill.runtime.v1.Canvas.spec:type_name -> rill.runtime.v1.CanvasSpec - 64, // 143: rill.runtime.v1.Canvas.state:type_name -> rill.runtime.v1.CanvasState - 55, // 144: rill.runtime.v1.CanvasSpec.embedded_theme:type_name -> rill.runtime.v1.ThemeSpec - 32, // 145: rill.runtime.v1.CanvasSpec.time_ranges:type_name -> rill.runtime.v1.ExploreTimeRange - 69, // 146: rill.runtime.v1.CanvasSpec.default_preset:type_name -> rill.runtime.v1.CanvasPreset - 61, // 147: rill.runtime.v1.CanvasSpec.variables:type_name -> rill.runtime.v1.ComponentVariable - 65, // 148: rill.runtime.v1.CanvasSpec.rows:type_name -> rill.runtime.v1.CanvasRow - 23, // 149: rill.runtime.v1.CanvasSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule - 94, // 150: rill.runtime.v1.CanvasSpec.annotations:type_name -> rill.runtime.v1.CanvasSpec.AnnotationsEntry - 63, // 151: rill.runtime.v1.CanvasState.valid_spec:type_name -> rill.runtime.v1.CanvasSpec - 96, // 152: rill.runtime.v1.CanvasState.data_refreshed_on:type_name -> google.protobuf.Timestamp - 68, // 153: rill.runtime.v1.CanvasRow.items:type_name -> rill.runtime.v1.CanvasItem - 66, // 154: rill.runtime.v1.CanvasRow.tab_group:type_name -> rill.runtime.v1.CanvasTabGroup - 67, // 155: rill.runtime.v1.CanvasTabGroup.tabs:type_name -> rill.runtime.v1.CanvasTab - 65, // 156: rill.runtime.v1.CanvasTab.rows:type_name -> rill.runtime.v1.CanvasRow - 2, // 157: rill.runtime.v1.CanvasPreset.comparison_mode:type_name -> rill.runtime.v1.ExploreComparisonMode - 95, // 158: rill.runtime.v1.CanvasPreset.filter_expr:type_name -> rill.runtime.v1.CanvasPreset.FilterExprEntry - 100, // 159: rill.runtime.v1.DefaultMetricsSQLFilter.expression:type_name -> rill.runtime.v1.Expression - 72, // 160: rill.runtime.v1.API.spec:type_name -> rill.runtime.v1.APISpec - 73, // 161: rill.runtime.v1.API.state:type_name -> rill.runtime.v1.APIState - 97, // 162: rill.runtime.v1.APISpec.resolver_properties:type_name -> google.protobuf.Struct - 23, // 163: rill.runtime.v1.APISpec.security_rules:type_name -> rill.runtime.v1.SecurityRule - 79, // 164: rill.runtime.v1.ParseError.start_location:type_name -> rill.runtime.v1.CharLocation - 81, // 165: rill.runtime.v1.ConnectorV2.spec:type_name -> rill.runtime.v1.ConnectorSpec - 82, // 166: rill.runtime.v1.ConnectorV2.state:type_name -> rill.runtime.v1.ConnectorState - 97, // 167: rill.runtime.v1.ConnectorSpec.properties:type_name -> google.protobuf.Struct - 97, // 168: rill.runtime.v1.ConnectorSpec.provision_args:type_name -> google.protobuf.Struct - 6, // 169: rill.runtime.v1.MetricsViewSpec.Dimension.type:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionType - 99, // 170: rill.runtime.v1.MetricsViewSpec.Dimension.smallest_time_grain:type_name -> rill.runtime.v1.TimeGrain - 104, // 171: rill.runtime.v1.MetricsViewSpec.Dimension.data_type:type_name -> rill.runtime.v1.Type - 99, // 172: rill.runtime.v1.MetricsViewSpec.DimensionSelector.time_grain:type_name -> rill.runtime.v1.TimeGrain - 85, // 173: rill.runtime.v1.MetricsViewSpec.MeasureWindow.order_by:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector - 7, // 174: rill.runtime.v1.MetricsViewSpec.Measure.type:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureType - 86, // 175: rill.runtime.v1.MetricsViewSpec.Measure.window:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureWindow - 85, // 176: rill.runtime.v1.MetricsViewSpec.Measure.per_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector - 85, // 177: rill.runtime.v1.MetricsViewSpec.Measure.required_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector - 97, // 178: rill.runtime.v1.MetricsViewSpec.Measure.format_d3_locale:type_name -> google.protobuf.Struct - 104, // 179: rill.runtime.v1.MetricsViewSpec.Measure.data_type:type_name -> rill.runtime.v1.Type - 35, // 180: rill.runtime.v1.MetricsViewSpec.Annotation.measures_selector:type_name -> rill.runtime.v1.FieldSelector - 99, // 181: rill.runtime.v1.MetricsViewSpec.Rollup.time_grain:type_name -> rill.runtime.v1.TimeGrain - 35, // 182: rill.runtime.v1.MetricsViewSpec.Rollup.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector - 35, // 183: rill.runtime.v1.MetricsViewSpec.Rollup.measures_selector:type_name -> rill.runtime.v1.FieldSelector - 70, // 184: rill.runtime.v1.CanvasPreset.FilterExprEntry.value:type_name -> rill.runtime.v1.DefaultMetricsSQLFilter - 185, // [185:185] is the sub-list for method output_type - 185, // [185:185] is the sub-list for method input_type - 185, // [185:185] is the sub-list for extension type_name - 185, // [185:185] is the sub-list for extension extendee - 0, // [0:185] is the sub-list for field type_name + 57, // 10: rill.runtime.v1.Resource.theme:type_name -> rill.runtime.v1.Theme + 61, // 11: rill.runtime.v1.Resource.component:type_name -> rill.runtime.v1.Component + 65, // 12: rill.runtime.v1.Resource.canvas:type_name -> rill.runtime.v1.Canvas + 74, // 13: rill.runtime.v1.Resource.api:type_name -> rill.runtime.v1.API + 83, // 14: rill.runtime.v1.Resource.connector:type_name -> rill.runtime.v1.ConnectorV2 + 54, // 15: rill.runtime.v1.Resource.skill:type_name -> rill.runtime.v1.Skill + 10, // 16: rill.runtime.v1.ResourceMeta.name:type_name -> rill.runtime.v1.ResourceName + 10, // 17: rill.runtime.v1.ResourceMeta.refs:type_name -> rill.runtime.v1.ResourceName + 10, // 18: rill.runtime.v1.ResourceMeta.owner:type_name -> rill.runtime.v1.ResourceName + 86, // 19: rill.runtime.v1.ResourceMeta.metadata:type_name -> rill.runtime.v1.ResourceMeta.MetadataEntry + 99, // 20: rill.runtime.v1.ResourceMeta.created_on:type_name -> google.protobuf.Timestamp + 99, // 21: rill.runtime.v1.ResourceMeta.spec_updated_on:type_name -> google.protobuf.Timestamp + 99, // 22: rill.runtime.v1.ResourceMeta.state_updated_on:type_name -> google.protobuf.Timestamp + 99, // 23: rill.runtime.v1.ResourceMeta.deleted_on:type_name -> google.protobuf.Timestamp + 0, // 24: rill.runtime.v1.ResourceMeta.reconcile_status:type_name -> rill.runtime.v1.ReconcileStatus + 99, // 25: rill.runtime.v1.ResourceMeta.reconcile_on:type_name -> google.protobuf.Timestamp + 10, // 26: rill.runtime.v1.ResourceMeta.renamed_from:type_name -> rill.runtime.v1.ResourceName + 12, // 27: rill.runtime.v1.ProjectParser.spec:type_name -> rill.runtime.v1.ProjectParserSpec + 13, // 28: rill.runtime.v1.ProjectParser.state:type_name -> rill.runtime.v1.ProjectParserState + 78, // 29: rill.runtime.v1.ProjectParserState.parse_errors:type_name -> rill.runtime.v1.ParseError + 99, // 30: rill.runtime.v1.ProjectParserState.current_commit_on:type_name -> google.protobuf.Timestamp + 15, // 31: rill.runtime.v1.Source.spec:type_name -> rill.runtime.v1.SourceSpec + 16, // 32: rill.runtime.v1.Source.state:type_name -> rill.runtime.v1.SourceState + 100, // 33: rill.runtime.v1.SourceSpec.properties:type_name -> google.protobuf.Struct + 77, // 34: rill.runtime.v1.SourceSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule + 99, // 35: rill.runtime.v1.SourceState.refreshed_on:type_name -> google.protobuf.Timestamp + 18, // 36: rill.runtime.v1.Model.spec:type_name -> rill.runtime.v1.ModelSpec + 19, // 37: rill.runtime.v1.Model.state:type_name -> rill.runtime.v1.ModelState + 77, // 38: rill.runtime.v1.ModelSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule + 100, // 39: rill.runtime.v1.ModelSpec.incremental_state_resolver_properties:type_name -> google.protobuf.Struct + 100, // 40: rill.runtime.v1.ModelSpec.partitions_resolver_properties:type_name -> google.protobuf.Struct + 100, // 41: rill.runtime.v1.ModelSpec.input_properties:type_name -> google.protobuf.Struct + 100, // 42: rill.runtime.v1.ModelSpec.stage_properties:type_name -> google.protobuf.Struct + 100, // 43: rill.runtime.v1.ModelSpec.output_properties:type_name -> google.protobuf.Struct + 1, // 44: rill.runtime.v1.ModelSpec.change_mode:type_name -> rill.runtime.v1.ModelChangeMode + 20, // 45: rill.runtime.v1.ModelSpec.tests:type_name -> rill.runtime.v1.ModelTest + 100, // 46: rill.runtime.v1.ModelState.result_properties:type_name -> google.protobuf.Struct + 99, // 47: rill.runtime.v1.ModelState.refreshed_on:type_name -> google.protobuf.Timestamp + 100, // 48: rill.runtime.v1.ModelState.incremental_state:type_name -> google.protobuf.Struct + 101, // 49: rill.runtime.v1.ModelState.incremental_state_schema:type_name -> rill.runtime.v1.StructType + 100, // 50: rill.runtime.v1.ModelTest.resolver_properties:type_name -> google.protobuf.Struct + 22, // 51: rill.runtime.v1.MetricsView.spec:type_name -> rill.runtime.v1.MetricsViewSpec + 28, // 52: rill.runtime.v1.MetricsView.state:type_name -> rill.runtime.v1.MetricsViewState + 102, // 53: rill.runtime.v1.MetricsViewSpec.smallest_time_grain:type_name -> rill.runtime.v1.TimeGrain + 87, // 54: rill.runtime.v1.MetricsViewSpec.dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.Dimension + 90, // 55: rill.runtime.v1.MetricsViewSpec.measures:type_name -> rill.runtime.v1.MetricsViewSpec.Measure + 35, // 56: rill.runtime.v1.MetricsViewSpec.parent_dimensions:type_name -> rill.runtime.v1.FieldSelector + 35, // 57: rill.runtime.v1.MetricsViewSpec.parent_measures:type_name -> rill.runtime.v1.FieldSelector + 91, // 58: rill.runtime.v1.MetricsViewSpec.annotations:type_name -> rill.runtime.v1.MetricsViewSpec.Annotation + 23, // 59: rill.runtime.v1.MetricsViewSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule + 93, // 60: rill.runtime.v1.MetricsViewSpec.query_attributes:type_name -> rill.runtime.v1.MetricsViewSpec.QueryAttributesEntry + 92, // 61: rill.runtime.v1.MetricsViewSpec.rollups:type_name -> rill.runtime.v1.MetricsViewSpec.Rollup + 24, // 62: rill.runtime.v1.SecurityRule.access:type_name -> rill.runtime.v1.SecurityRuleAccess + 25, // 63: rill.runtime.v1.SecurityRule.field_access:type_name -> rill.runtime.v1.SecurityRuleFieldAccess + 26, // 64: rill.runtime.v1.SecurityRule.row_filter:type_name -> rill.runtime.v1.SecurityRuleRowFilter + 27, // 65: rill.runtime.v1.SecurityRule.transitive_access:type_name -> rill.runtime.v1.SecurityRuleTransitiveAccess + 10, // 66: rill.runtime.v1.SecurityRuleAccess.condition_resources:type_name -> rill.runtime.v1.ResourceName + 10, // 67: rill.runtime.v1.SecurityRuleFieldAccess.condition_resources:type_name -> rill.runtime.v1.ResourceName + 10, // 68: rill.runtime.v1.SecurityRuleRowFilter.condition_resources:type_name -> rill.runtime.v1.ResourceName + 103, // 69: rill.runtime.v1.SecurityRuleRowFilter.expression:type_name -> rill.runtime.v1.Expression + 10, // 70: rill.runtime.v1.SecurityRuleTransitiveAccess.resource:type_name -> rill.runtime.v1.ResourceName + 22, // 71: rill.runtime.v1.MetricsViewState.valid_spec:type_name -> rill.runtime.v1.MetricsViewSpec + 99, // 72: rill.runtime.v1.MetricsViewState.data_refreshed_on:type_name -> google.protobuf.Timestamp + 30, // 73: rill.runtime.v1.Explore.spec:type_name -> rill.runtime.v1.ExploreSpec + 31, // 74: rill.runtime.v1.Explore.state:type_name -> rill.runtime.v1.ExploreState + 35, // 75: rill.runtime.v1.ExploreSpec.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector + 35, // 76: rill.runtime.v1.ExploreSpec.measures_selector:type_name -> rill.runtime.v1.FieldSelector + 58, // 77: rill.runtime.v1.ExploreSpec.embedded_theme:type_name -> rill.runtime.v1.ThemeSpec + 32, // 78: rill.runtime.v1.ExploreSpec.time_ranges:type_name -> rill.runtime.v1.ExploreTimeRange + 34, // 79: rill.runtime.v1.ExploreSpec.default_preset:type_name -> rill.runtime.v1.ExplorePreset + 23, // 80: rill.runtime.v1.ExploreSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule + 30, // 81: rill.runtime.v1.ExploreState.valid_spec:type_name -> rill.runtime.v1.ExploreSpec + 99, // 82: rill.runtime.v1.ExploreState.data_refreshed_on:type_name -> google.protobuf.Timestamp + 33, // 83: rill.runtime.v1.ExploreTimeRange.comparison_time_ranges:type_name -> rill.runtime.v1.ExploreComparisonTimeRange + 35, // 84: rill.runtime.v1.ExplorePreset.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector + 35, // 85: rill.runtime.v1.ExplorePreset.measures_selector:type_name -> rill.runtime.v1.FieldSelector + 103, // 86: rill.runtime.v1.ExplorePreset.where:type_name -> rill.runtime.v1.Expression + 2, // 87: rill.runtime.v1.ExplorePreset.comparison_mode:type_name -> rill.runtime.v1.ExploreComparisonMode + 3, // 88: rill.runtime.v1.ExplorePreset.view:type_name -> rill.runtime.v1.ExploreWebView + 4, // 89: rill.runtime.v1.ExplorePreset.explore_sort_type:type_name -> rill.runtime.v1.ExploreSortType + 36, // 90: rill.runtime.v1.FieldSelector.fields:type_name -> rill.runtime.v1.StringListValue + 38, // 91: rill.runtime.v1.Migration.spec:type_name -> rill.runtime.v1.MigrationSpec + 39, // 92: rill.runtime.v1.Migration.state:type_name -> rill.runtime.v1.MigrationState + 41, // 93: rill.runtime.v1.Report.spec:type_name -> rill.runtime.v1.ReportSpec + 42, // 94: rill.runtime.v1.Report.state:type_name -> rill.runtime.v1.ReportState + 77, // 95: rill.runtime.v1.ReportSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule + 100, // 96: rill.runtime.v1.ReportSpec.resolver_properties:type_name -> google.protobuf.Struct + 104, // 97: rill.runtime.v1.ReportSpec.export_format:type_name -> rill.runtime.v1.ExportFormat + 46, // 98: rill.runtime.v1.ReportSpec.notifiers:type_name -> rill.runtime.v1.Notifier + 94, // 99: rill.runtime.v1.ReportSpec.annotations:type_name -> rill.runtime.v1.ReportSpec.AnnotationsEntry + 99, // 100: rill.runtime.v1.ReportState.next_run_on:type_name -> google.protobuf.Timestamp + 43, // 101: rill.runtime.v1.ReportState.current_execution:type_name -> rill.runtime.v1.ReportExecution + 43, // 102: rill.runtime.v1.ReportState.execution_history:type_name -> rill.runtime.v1.ReportExecution + 99, // 103: rill.runtime.v1.ReportExecution.report_time:type_name -> google.protobuf.Timestamp + 99, // 104: rill.runtime.v1.ReportExecution.started_on:type_name -> google.protobuf.Timestamp + 99, // 105: rill.runtime.v1.ReportExecution.finished_on:type_name -> google.protobuf.Timestamp + 45, // 106: rill.runtime.v1.Alert.spec:type_name -> rill.runtime.v1.AlertSpec + 47, // 107: rill.runtime.v1.Alert.state:type_name -> rill.runtime.v1.AlertState + 77, // 108: rill.runtime.v1.AlertSpec.refresh_schedule:type_name -> rill.runtime.v1.Schedule + 100, // 109: rill.runtime.v1.AlertSpec.resolver_properties:type_name -> google.protobuf.Struct + 100, // 110: rill.runtime.v1.AlertSpec.query_for_attributes:type_name -> google.protobuf.Struct + 46, // 111: rill.runtime.v1.AlertSpec.notifiers:type_name -> rill.runtime.v1.Notifier + 95, // 112: rill.runtime.v1.AlertSpec.annotations:type_name -> rill.runtime.v1.AlertSpec.AnnotationsEntry + 100, // 113: rill.runtime.v1.Notifier.properties:type_name -> google.protobuf.Struct + 99, // 114: rill.runtime.v1.AlertState.next_run_on:type_name -> google.protobuf.Timestamp + 48, // 115: rill.runtime.v1.AlertState.current_execution:type_name -> rill.runtime.v1.AlertExecution + 48, // 116: rill.runtime.v1.AlertState.execution_history:type_name -> rill.runtime.v1.AlertExecution + 49, // 117: rill.runtime.v1.AlertExecution.result:type_name -> rill.runtime.v1.AssertionResult + 99, // 118: rill.runtime.v1.AlertExecution.execution_time:type_name -> google.protobuf.Timestamp + 99, // 119: rill.runtime.v1.AlertExecution.started_on:type_name -> google.protobuf.Timestamp + 99, // 120: rill.runtime.v1.AlertExecution.finished_on:type_name -> google.protobuf.Timestamp + 99, // 121: rill.runtime.v1.AlertExecution.suppressed_since:type_name -> google.protobuf.Timestamp + 5, // 122: rill.runtime.v1.AssertionResult.status:type_name -> rill.runtime.v1.AssertionStatus + 100, // 123: rill.runtime.v1.AssertionResult.fail_row:type_name -> google.protobuf.Struct + 51, // 124: rill.runtime.v1.RefreshTrigger.spec:type_name -> rill.runtime.v1.RefreshTriggerSpec + 52, // 125: rill.runtime.v1.RefreshTrigger.state:type_name -> rill.runtime.v1.RefreshTriggerState + 10, // 126: rill.runtime.v1.RefreshTriggerSpec.resources:type_name -> rill.runtime.v1.ResourceName + 53, // 127: rill.runtime.v1.RefreshTriggerSpec.models:type_name -> rill.runtime.v1.RefreshModelTrigger + 55, // 128: rill.runtime.v1.Skill.spec:type_name -> rill.runtime.v1.SkillSpec + 56, // 129: rill.runtime.v1.Skill.state:type_name -> rill.runtime.v1.SkillState + 58, // 130: rill.runtime.v1.Theme.spec:type_name -> rill.runtime.v1.ThemeSpec + 59, // 131: rill.runtime.v1.Theme.state:type_name -> rill.runtime.v1.ThemeState + 105, // 132: rill.runtime.v1.ThemeSpec.primary_color:type_name -> rill.runtime.v1.Color + 105, // 133: rill.runtime.v1.ThemeSpec.secondary_color:type_name -> rill.runtime.v1.Color + 60, // 134: rill.runtime.v1.ThemeSpec.light:type_name -> rill.runtime.v1.ThemeColors + 60, // 135: rill.runtime.v1.ThemeSpec.dark:type_name -> rill.runtime.v1.ThemeColors + 96, // 136: rill.runtime.v1.ThemeColors.variables:type_name -> rill.runtime.v1.ThemeColors.VariablesEntry + 62, // 137: rill.runtime.v1.Component.spec:type_name -> rill.runtime.v1.ComponentSpec + 63, // 138: rill.runtime.v1.Component.state:type_name -> rill.runtime.v1.ComponentState + 100, // 139: rill.runtime.v1.ComponentSpec.renderer_properties:type_name -> google.protobuf.Struct + 64, // 140: rill.runtime.v1.ComponentSpec.input:type_name -> rill.runtime.v1.ComponentVariable + 64, // 141: rill.runtime.v1.ComponentSpec.output:type_name -> rill.runtime.v1.ComponentVariable + 62, // 142: rill.runtime.v1.ComponentState.valid_spec:type_name -> rill.runtime.v1.ComponentSpec + 99, // 143: rill.runtime.v1.ComponentState.data_refreshed_on:type_name -> google.protobuf.Timestamp + 106, // 144: rill.runtime.v1.ComponentVariable.default_value:type_name -> google.protobuf.Value + 66, // 145: rill.runtime.v1.Canvas.spec:type_name -> rill.runtime.v1.CanvasSpec + 67, // 146: rill.runtime.v1.Canvas.state:type_name -> rill.runtime.v1.CanvasState + 58, // 147: rill.runtime.v1.CanvasSpec.embedded_theme:type_name -> rill.runtime.v1.ThemeSpec + 32, // 148: rill.runtime.v1.CanvasSpec.time_ranges:type_name -> rill.runtime.v1.ExploreTimeRange + 72, // 149: rill.runtime.v1.CanvasSpec.default_preset:type_name -> rill.runtime.v1.CanvasPreset + 64, // 150: rill.runtime.v1.CanvasSpec.variables:type_name -> rill.runtime.v1.ComponentVariable + 68, // 151: rill.runtime.v1.CanvasSpec.rows:type_name -> rill.runtime.v1.CanvasRow + 23, // 152: rill.runtime.v1.CanvasSpec.security_rules:type_name -> rill.runtime.v1.SecurityRule + 97, // 153: rill.runtime.v1.CanvasSpec.annotations:type_name -> rill.runtime.v1.CanvasSpec.AnnotationsEntry + 66, // 154: rill.runtime.v1.CanvasState.valid_spec:type_name -> rill.runtime.v1.CanvasSpec + 99, // 155: rill.runtime.v1.CanvasState.data_refreshed_on:type_name -> google.protobuf.Timestamp + 71, // 156: rill.runtime.v1.CanvasRow.items:type_name -> rill.runtime.v1.CanvasItem + 69, // 157: rill.runtime.v1.CanvasRow.tab_group:type_name -> rill.runtime.v1.CanvasTabGroup + 70, // 158: rill.runtime.v1.CanvasTabGroup.tabs:type_name -> rill.runtime.v1.CanvasTab + 68, // 159: rill.runtime.v1.CanvasTab.rows:type_name -> rill.runtime.v1.CanvasRow + 2, // 160: rill.runtime.v1.CanvasPreset.comparison_mode:type_name -> rill.runtime.v1.ExploreComparisonMode + 98, // 161: rill.runtime.v1.CanvasPreset.filter_expr:type_name -> rill.runtime.v1.CanvasPreset.FilterExprEntry + 103, // 162: rill.runtime.v1.DefaultMetricsSQLFilter.expression:type_name -> rill.runtime.v1.Expression + 75, // 163: rill.runtime.v1.API.spec:type_name -> rill.runtime.v1.APISpec + 76, // 164: rill.runtime.v1.API.state:type_name -> rill.runtime.v1.APIState + 100, // 165: rill.runtime.v1.APISpec.resolver_properties:type_name -> google.protobuf.Struct + 23, // 166: rill.runtime.v1.APISpec.security_rules:type_name -> rill.runtime.v1.SecurityRule + 82, // 167: rill.runtime.v1.ParseError.start_location:type_name -> rill.runtime.v1.CharLocation + 84, // 168: rill.runtime.v1.ConnectorV2.spec:type_name -> rill.runtime.v1.ConnectorSpec + 85, // 169: rill.runtime.v1.ConnectorV2.state:type_name -> rill.runtime.v1.ConnectorState + 100, // 170: rill.runtime.v1.ConnectorSpec.properties:type_name -> google.protobuf.Struct + 100, // 171: rill.runtime.v1.ConnectorSpec.provision_args:type_name -> google.protobuf.Struct + 6, // 172: rill.runtime.v1.MetricsViewSpec.Dimension.type:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionType + 102, // 173: rill.runtime.v1.MetricsViewSpec.Dimension.smallest_time_grain:type_name -> rill.runtime.v1.TimeGrain + 107, // 174: rill.runtime.v1.MetricsViewSpec.Dimension.data_type:type_name -> rill.runtime.v1.Type + 102, // 175: rill.runtime.v1.MetricsViewSpec.DimensionSelector.time_grain:type_name -> rill.runtime.v1.TimeGrain + 88, // 176: rill.runtime.v1.MetricsViewSpec.MeasureWindow.order_by:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector + 7, // 177: rill.runtime.v1.MetricsViewSpec.Measure.type:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureType + 89, // 178: rill.runtime.v1.MetricsViewSpec.Measure.window:type_name -> rill.runtime.v1.MetricsViewSpec.MeasureWindow + 88, // 179: rill.runtime.v1.MetricsViewSpec.Measure.per_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector + 88, // 180: rill.runtime.v1.MetricsViewSpec.Measure.required_dimensions:type_name -> rill.runtime.v1.MetricsViewSpec.DimensionSelector + 100, // 181: rill.runtime.v1.MetricsViewSpec.Measure.format_d3_locale:type_name -> google.protobuf.Struct + 107, // 182: rill.runtime.v1.MetricsViewSpec.Measure.data_type:type_name -> rill.runtime.v1.Type + 35, // 183: rill.runtime.v1.MetricsViewSpec.Annotation.measures_selector:type_name -> rill.runtime.v1.FieldSelector + 102, // 184: rill.runtime.v1.MetricsViewSpec.Rollup.time_grain:type_name -> rill.runtime.v1.TimeGrain + 35, // 185: rill.runtime.v1.MetricsViewSpec.Rollup.dimensions_selector:type_name -> rill.runtime.v1.FieldSelector + 35, // 186: rill.runtime.v1.MetricsViewSpec.Rollup.measures_selector:type_name -> rill.runtime.v1.FieldSelector + 73, // 187: rill.runtime.v1.CanvasPreset.FilterExprEntry.value:type_name -> rill.runtime.v1.DefaultMetricsSQLFilter + 188, // [188:188] is the sub-list for method output_type + 188, // [188:188] is the sub-list for method input_type + 188, // [188:188] is the sub-list for extension type_name + 188, // [188:188] is the sub-list for extension extendee + 0, // [0:188] is the sub-list for field type_name } func init() { file_rill_runtime_v1_resources_proto_init() } @@ -10438,7 +10658,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[46].Exporter = func(v any, i int) any { - switch v := v.(*Theme); i { + switch v := v.(*Skill); i { case 0: return &v.state case 1: @@ -10450,7 +10670,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[47].Exporter = func(v any, i int) any { - switch v := v.(*ThemeSpec); i { + switch v := v.(*SkillSpec); i { case 0: return &v.state case 1: @@ -10462,7 +10682,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[48].Exporter = func(v any, i int) any { - switch v := v.(*ThemeState); i { + switch v := v.(*SkillState); i { case 0: return &v.state case 1: @@ -10474,7 +10694,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[49].Exporter = func(v any, i int) any { - switch v := v.(*ThemeColors); i { + switch v := v.(*Theme); i { case 0: return &v.state case 1: @@ -10486,7 +10706,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[50].Exporter = func(v any, i int) any { - switch v := v.(*Component); i { + switch v := v.(*ThemeSpec); i { case 0: return &v.state case 1: @@ -10498,7 +10718,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[51].Exporter = func(v any, i int) any { - switch v := v.(*ComponentSpec); i { + switch v := v.(*ThemeState); i { case 0: return &v.state case 1: @@ -10510,7 +10730,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[52].Exporter = func(v any, i int) any { - switch v := v.(*ComponentState); i { + switch v := v.(*ThemeColors); i { case 0: return &v.state case 1: @@ -10522,7 +10742,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[53].Exporter = func(v any, i int) any { - switch v := v.(*ComponentVariable); i { + switch v := v.(*Component); i { case 0: return &v.state case 1: @@ -10534,7 +10754,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[54].Exporter = func(v any, i int) any { - switch v := v.(*Canvas); i { + switch v := v.(*ComponentSpec); i { case 0: return &v.state case 1: @@ -10546,7 +10766,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[55].Exporter = func(v any, i int) any { - switch v := v.(*CanvasSpec); i { + switch v := v.(*ComponentState); i { case 0: return &v.state case 1: @@ -10558,7 +10778,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[56].Exporter = func(v any, i int) any { - switch v := v.(*CanvasState); i { + switch v := v.(*ComponentVariable); i { case 0: return &v.state case 1: @@ -10570,7 +10790,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[57].Exporter = func(v any, i int) any { - switch v := v.(*CanvasRow); i { + switch v := v.(*Canvas); i { case 0: return &v.state case 1: @@ -10582,7 +10802,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[58].Exporter = func(v any, i int) any { - switch v := v.(*CanvasTabGroup); i { + switch v := v.(*CanvasSpec); i { case 0: return &v.state case 1: @@ -10594,7 +10814,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[59].Exporter = func(v any, i int) any { - switch v := v.(*CanvasTab); i { + switch v := v.(*CanvasState); i { case 0: return &v.state case 1: @@ -10606,7 +10826,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[60].Exporter = func(v any, i int) any { - switch v := v.(*CanvasItem); i { + switch v := v.(*CanvasRow); i { case 0: return &v.state case 1: @@ -10618,7 +10838,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[61].Exporter = func(v any, i int) any { - switch v := v.(*CanvasPreset); i { + switch v := v.(*CanvasTabGroup); i { case 0: return &v.state case 1: @@ -10630,7 +10850,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[62].Exporter = func(v any, i int) any { - switch v := v.(*DefaultMetricsSQLFilter); i { + switch v := v.(*CanvasTab); i { case 0: return &v.state case 1: @@ -10642,7 +10862,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[63].Exporter = func(v any, i int) any { - switch v := v.(*API); i { + switch v := v.(*CanvasItem); i { case 0: return &v.state case 1: @@ -10654,7 +10874,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[64].Exporter = func(v any, i int) any { - switch v := v.(*APISpec); i { + switch v := v.(*CanvasPreset); i { case 0: return &v.state case 1: @@ -10666,7 +10886,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[65].Exporter = func(v any, i int) any { - switch v := v.(*APIState); i { + switch v := v.(*DefaultMetricsSQLFilter); i { case 0: return &v.state case 1: @@ -10678,7 +10898,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[66].Exporter = func(v any, i int) any { - switch v := v.(*Schedule); i { + switch v := v.(*API); i { case 0: return &v.state case 1: @@ -10690,7 +10910,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[67].Exporter = func(v any, i int) any { - switch v := v.(*ParseError); i { + switch v := v.(*APISpec); i { case 0: return &v.state case 1: @@ -10702,7 +10922,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[68].Exporter = func(v any, i int) any { - switch v := v.(*ValidationError); i { + switch v := v.(*APIState); i { case 0: return &v.state case 1: @@ -10714,7 +10934,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[69].Exporter = func(v any, i int) any { - switch v := v.(*DependencyError); i { + switch v := v.(*Schedule); i { case 0: return &v.state case 1: @@ -10726,7 +10946,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[70].Exporter = func(v any, i int) any { - switch v := v.(*ExecutionError); i { + switch v := v.(*ParseError); i { case 0: return &v.state case 1: @@ -10738,7 +10958,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[71].Exporter = func(v any, i int) any { - switch v := v.(*CharLocation); i { + switch v := v.(*ValidationError); i { case 0: return &v.state case 1: @@ -10750,7 +10970,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[72].Exporter = func(v any, i int) any { - switch v := v.(*ConnectorV2); i { + switch v := v.(*DependencyError); i { case 0: return &v.state case 1: @@ -10762,7 +10982,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[73].Exporter = func(v any, i int) any { - switch v := v.(*ConnectorSpec); i { + switch v := v.(*ExecutionError); i { case 0: return &v.state case 1: @@ -10774,7 +10994,19 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[74].Exporter = func(v any, i int) any { - switch v := v.(*ConnectorState); i { + switch v := v.(*CharLocation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rill_runtime_v1_resources_proto_msgTypes[75].Exporter = func(v any, i int) any { + switch v := v.(*ConnectorV2); i { case 0: return &v.state case 1: @@ -10786,7 +11018,7 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[76].Exporter = func(v any, i int) any { - switch v := v.(*MetricsViewSpec_Dimension); i { + switch v := v.(*ConnectorSpec); i { case 0: return &v.state case 1: @@ -10798,6 +11030,30 @@ func file_rill_runtime_v1_resources_proto_init() { } } file_rill_runtime_v1_resources_proto_msgTypes[77].Exporter = func(v any, i int) any { + switch v := v.(*ConnectorState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rill_runtime_v1_resources_proto_msgTypes[79].Exporter = func(v any, i int) any { + switch v := v.(*MetricsViewSpec_Dimension); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_rill_runtime_v1_resources_proto_msgTypes[80].Exporter = func(v any, i int) any { switch v := v.(*MetricsViewSpec_DimensionSelector); i { case 0: return &v.state @@ -10809,7 +11065,7 @@ func file_rill_runtime_v1_resources_proto_init() { return nil } } - file_rill_runtime_v1_resources_proto_msgTypes[78].Exporter = func(v any, i int) any { + file_rill_runtime_v1_resources_proto_msgTypes[81].Exporter = func(v any, i int) any { switch v := v.(*MetricsViewSpec_MeasureWindow); i { case 0: return &v.state @@ -10821,7 +11077,7 @@ func file_rill_runtime_v1_resources_proto_init() { return nil } } - file_rill_runtime_v1_resources_proto_msgTypes[79].Exporter = func(v any, i int) any { + file_rill_runtime_v1_resources_proto_msgTypes[82].Exporter = func(v any, i int) any { switch v := v.(*MetricsViewSpec_Measure); i { case 0: return &v.state @@ -10833,7 +11089,7 @@ func file_rill_runtime_v1_resources_proto_init() { return nil } } - file_rill_runtime_v1_resources_proto_msgTypes[80].Exporter = func(v any, i int) any { + file_rill_runtime_v1_resources_proto_msgTypes[83].Exporter = func(v any, i int) any { switch v := v.(*MetricsViewSpec_Annotation); i { case 0: return &v.state @@ -10845,7 +11101,7 @@ func file_rill_runtime_v1_resources_proto_init() { return nil } } - file_rill_runtime_v1_resources_proto_msgTypes[81].Exporter = func(v any, i int) any { + file_rill_runtime_v1_resources_proto_msgTypes[84].Exporter = func(v any, i int) any { switch v := v.(*MetricsViewSpec_Rollup); i { case 0: return &v.state @@ -10873,6 +11129,7 @@ func file_rill_runtime_v1_resources_proto_init() { (*Resource_Canvas)(nil), (*Resource_Api)(nil), (*Resource_Connector)(nil), + (*Resource_Skill)(nil), } file_rill_runtime_v1_resources_proto_msgTypes[1].OneofWrappers = []any{} file_rill_runtime_v1_resources_proto_msgTypes[10].OneofWrappers = []any{} @@ -10895,17 +11152,17 @@ func file_rill_runtime_v1_resources_proto_init() { (*AlertSpec_QueryForUserEmail)(nil), (*AlertSpec_QueryForAttributes)(nil), } - file_rill_runtime_v1_resources_proto_msgTypes[47].OneofWrappers = []any{} - file_rill_runtime_v1_resources_proto_msgTypes[57].OneofWrappers = []any{} + file_rill_runtime_v1_resources_proto_msgTypes[50].OneofWrappers = []any{} file_rill_runtime_v1_resources_proto_msgTypes[60].OneofWrappers = []any{} - file_rill_runtime_v1_resources_proto_msgTypes[61].OneofWrappers = []any{} + file_rill_runtime_v1_resources_proto_msgTypes[63].OneofWrappers = []any{} + file_rill_runtime_v1_resources_proto_msgTypes[64].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_rill_runtime_v1_resources_proto_rawDesc, NumEnums: 8, - NumMessages: 88, + NumMessages: 91, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/gen/rill/runtime/v1/resources.pb.validate.go b/proto/gen/rill/runtime/v1/resources.pb.validate.go index b56b1a983dc1..3d6a9a20f0fd 100644 --- a/proto/gen/rill/runtime/v1/resources.pb.validate.go +++ b/proto/gen/rill/runtime/v1/resources.pb.validate.go @@ -661,6 +661,47 @@ func (m *Resource) validate(all bool) error { } } + case *Resource_Skill: + if v == nil { + err := ResourceValidationError{ + field: "Resource", + reason: "oneof value cannot be a typed-nil", + } + if !all { + return err + } + errors = append(errors, err) + } + + if all { + switch v := interface{}(m.GetSkill()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, ResourceValidationError{ + field: "Skill", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, ResourceValidationError{ + field: "Skill", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetSkill()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return ResourceValidationError{ + field: "Skill", + reason: "embedded message failed validation", + cause: err, + } + } + } + default: _ = v // ensures v is used } @@ -8801,6 +8842,366 @@ var _ interface { ErrorName() string } = RefreshModelTriggerValidationError{} +// Validate checks the field values on Skill with the rules defined in the +// proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *Skill) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on Skill with the rules defined in the +// proto definition for this message. If any rules are violated, the result is +// a list of violation errors wrapped in SkillMultiError, or nil if none found. +func (m *Skill) ValidateAll() error { + return m.validate(true) +} + +func (m *Skill) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if all { + switch v := interface{}(m.GetSpec()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, SkillValidationError{ + field: "Spec", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, SkillValidationError{ + field: "Spec", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetSpec()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return SkillValidationError{ + field: "Spec", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if all { + switch v := interface{}(m.GetState()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, SkillValidationError{ + field: "State", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, SkillValidationError{ + field: "State", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetState()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return SkillValidationError{ + field: "State", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if len(errors) > 0 { + return SkillMultiError(errors) + } + + return nil +} + +// SkillMultiError is an error wrapping multiple validation errors returned by +// Skill.ValidateAll() if the designated constraints aren't met. +type SkillMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m SkillMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m SkillMultiError) AllErrors() []error { return m } + +// SkillValidationError is the validation error returned by Skill.Validate if +// the designated constraints aren't met. +type SkillValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e SkillValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e SkillValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e SkillValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e SkillValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e SkillValidationError) ErrorName() string { return "SkillValidationError" } + +// Error satisfies the builtin error interface +func (e SkillValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sSkill.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = SkillValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = SkillValidationError{} + +// Validate checks the field values on SkillSpec with the rules defined in the +// proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *SkillSpec) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on SkillSpec with the rules defined in +// the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in SkillSpecMultiError, or nil +// if none found. +func (m *SkillSpec) ValidateAll() error { + return m.validate(true) +} + +func (m *SkillSpec) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for Description + + // no validation rules for Body + + // no validation rules for AlwaysApply + + if len(errors) > 0 { + return SkillSpecMultiError(errors) + } + + return nil +} + +// SkillSpecMultiError is an error wrapping multiple validation errors returned +// by SkillSpec.ValidateAll() if the designated constraints aren't met. +type SkillSpecMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m SkillSpecMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m SkillSpecMultiError) AllErrors() []error { return m } + +// SkillSpecValidationError is the validation error returned by +// SkillSpec.Validate if the designated constraints aren't met. +type SkillSpecValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e SkillSpecValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e SkillSpecValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e SkillSpecValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e SkillSpecValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e SkillSpecValidationError) ErrorName() string { return "SkillSpecValidationError" } + +// Error satisfies the builtin error interface +func (e SkillSpecValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sSkillSpec.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = SkillSpecValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = SkillSpecValidationError{} + +// Validate checks the field values on SkillState with the rules defined in the +// proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *SkillState) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on SkillState with the rules defined in +// the proto definition for this message. If any rules are violated, the +// result is a list of violation errors wrapped in SkillStateMultiError, or +// nil if none found. +func (m *SkillState) ValidateAll() error { + return m.validate(true) +} + +func (m *SkillState) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if len(errors) > 0 { + return SkillStateMultiError(errors) + } + + return nil +} + +// SkillStateMultiError is an error wrapping multiple validation errors +// returned by SkillState.ValidateAll() if the designated constraints aren't met. +type SkillStateMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m SkillStateMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m SkillStateMultiError) AllErrors() []error { return m } + +// SkillStateValidationError is the validation error returned by +// SkillState.Validate if the designated constraints aren't met. +type SkillStateValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e SkillStateValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e SkillStateValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e SkillStateValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e SkillStateValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e SkillStateValidationError) ErrorName() string { return "SkillStateValidationError" } + +// Error satisfies the builtin error interface +func (e SkillStateValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sSkillState.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = SkillStateValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = SkillStateValidationError{} + // Validate checks the field values on Theme with the rules defined in the // proto definition for this message. If any rules are violated, the first // error encountered is returned, or nil if there are no violations. diff --git a/proto/gen/rill/runtime/v1/runtime.swagger.yaml b/proto/gen/rill/runtime/v1/runtime.swagger.yaml index 39664ffbf5d5..1291913f2547 100644 --- a/proto/gen/rill/runtime/v1/runtime.swagger.yaml +++ b/proto/gen/rill/runtime/v1/runtime.swagger.yaml @@ -7872,6 +7872,8 @@ definitions: $ref: '#/definitions/v1API' connector: $ref: '#/definitions/v1ConnectorV2' + skill: + $ref: '#/definitions/v1Skill' v1ResourceEvent: type: string enum: @@ -8076,6 +8078,40 @@ definitions: v1ShareConversationResponse: type: object title: Response message for RuntimeService.ShareConversation + v1Skill: + type: object + properties: + spec: + $ref: '#/definitions/v1SkillSpec' + state: + $ref: '#/definitions/v1SkillState' + v1SkillSpec: + type: object + properties: + description: + type: string + description: Description of what the skill does and when to use it. + body: + type: string + description: Markdown body with the skill's full instructions. + metricsViews: + type: array + items: + type: string + description: 'Rill extension: metrics views the skill is relevant to. Empty means all.' + agents: + type: array + items: + type: string + description: 'Rill extension: Rill agents the skill applies to ("analyst" and/or "developer").' + alwaysApply: + type: boolean + description: 'Rill extension: if true, the skill''s body is always injected into the agent''s context instead of being loaded on demand.' + description: |- + SkillSpec is parsed from a SKILL.md file that follows the Agent Skills format (https://agentskills.io). + Skills teach AI agents project-specific practices, such as analysis playbooks and business glossaries. + v1SkillState: + type: object v1SkipModelPartitionsResponse: type: object v1Source: diff --git a/proto/rill/runtime/v1/resources.proto b/proto/rill/runtime/v1/resources.proto index e90d22c1699c..a61a35488247 100644 --- a/proto/rill/runtime/v1/resources.proto +++ b/proto/rill/runtime/v1/resources.proto @@ -34,6 +34,7 @@ message Resource { Canvas canvas = 14; API api = 15; ConnectorV2 connector = 16; + Skill skill = 18; } } @@ -800,6 +801,28 @@ message RefreshModelTrigger { bool all_skipped_partitions = 5; } +message Skill { + SkillSpec spec = 1; + SkillState state = 2; +} + +// SkillSpec is parsed from a SKILL.md file that follows the Agent Skills format (https://agentskills.io). +// Skills teach AI agents project-specific practices, such as analysis playbooks and business glossaries. +message SkillSpec { + // Description of what the skill does and when to use it. + string description = 1; + // Markdown body with the skill's full instructions. + string body = 2; + // Rill extension: metrics views the skill is relevant to. Empty means all. + repeated string metrics_views = 3; + // Rill extension: Rill agents the skill applies to ("analyst" and/or "developer"). + repeated string agents = 4; + // Rill extension: if true, the skill's body is always injected into the agent's context instead of being loaded on demand. + bool always_apply = 5; +} + +message SkillState {} + message Theme { ThemeSpec spec = 1; ThemeState state = 2; diff --git a/runtime/ai/ai.go b/runtime/ai/ai.go index d7ac889d2a88..7d072211c673 100644 --- a/runtime/ai/ai.go +++ b/runtime/ai/ai.go @@ -20,6 +20,7 @@ import ( "github.com/rilldata/rill/runtime" "github.com/rilldata/rill/runtime/drivers" "github.com/rilldata/rill/runtime/pkg/activity" + "github.com/rilldata/rill/runtime/pkg/ctxsync" "github.com/rilldata/rill/runtime/pkg/graceful" "github.com/rilldata/rill/runtime/pkg/observability" "go.opentelemetry.io/otel" @@ -61,6 +62,8 @@ func NewRunner(rt *runtime.Runtime, activity *activity.Client) *Runner { RegisterTool(r, &QueryMetricsViewSummary{Runtime: rt}) RegisterTool(r, &QueryMetricsView{Runtime: rt}) RegisterTool(r, &CreateChart{Runtime: rt}) + RegisterTool(r, &ListSkills{Runtime: rt}) + RegisterTool(r, &LoadSkill{Runtime: rt}) RegisterTool(r, &DevelopFile{Runtime: rt}) RegisterTool(r, &ListFiles{Runtime: rt}) @@ -210,6 +213,7 @@ func (r *Runner) Session(ctx context.Context, opts *SessionOptions) (res *Sessio acquireCatalog: func(ctx context.Context) (drivers.CatalogStore, func(), error) { return r.Runtime.Catalog(ctx, opts.InstanceID) }, + skillsMu: ctxsync.NewRWMutex(), dto: session, messages: messages, @@ -538,6 +542,10 @@ type BaseSession struct { messages []*Message messagesDirty bool subscribers map[chan *Message]struct{} + + skillsMu ctxsync.RWMutex + skillsLoaded bool + skills []*Skill } func (s *BaseSession) Flush(ctx context.Context) error { diff --git a/runtime/ai/analyst_agent.go b/runtime/ai/analyst_agent.go index 40bfab552f14..6f85e801807a 100644 --- a/runtime/ai/analyst_agent.go +++ b/runtime/ai/analyst_agent.go @@ -14,6 +14,8 @@ import ( "github.com/rilldata/rill/runtime" "github.com/rilldata/rill/runtime/ai/instructions" "github.com/rilldata/rill/runtime/metricsview" + "github.com/rilldata/rill/runtime/parser" + "go.uber.org/zap" ) const AnalystAgentName = "analyst_agent" @@ -105,17 +107,30 @@ func (t *AnalystAgent) Handler(ctx context.Context, args *AnalystAgentArgs) (*An // Determine if it's the first invocation of the agent in this session. first := len(s.Messages(FilterByType(MessageTypeCall), FilterByTool(AnalystAgentName))) == 1 + // Resolve the metrics views tied to the dashboard being explored, if any. + // This runs on every invocation because the metrics views are part of the prompt context; + // only the pre-invoked tool calls below are limited to the first invocation. + var metricsViewNames []string + if args.Explore != "" { + _, metricsView, err := t.getValidExploreAndMetricsView(ctx, args.Explore) + if err != nil { + return nil, err + } + metricsViewNames = append(metricsViewNames, metricsView.Meta.Name.Name) + } else if args.Canvas != "" { + _, metricsViews, err := t.getValidCanvasAndMetricsViews(ctx, args.Canvas) + if err != nil { + return nil, err + } + for _, res := range metricsViews { + metricsViewNames = append(metricsViewNames, res.Meta.Name.Name) + } + } + // If a specific dashboard is being explored, we pre-invoke some relevant tool calls for that dashboard. // TODO: This uses `first`, but that may not be safe if the user has navigated to another dashboard. We probably need some more sophisticated de-duplication here. - var metricsViewNames []string if first { - if args.Explore != "" { - _, metricsView, err := t.getValidExploreAndMetricsView(ctx, args.Explore) - if err != nil { - return nil, err - } - metricsViewNames = append(metricsViewNames, metricsView.Meta.Name.Name) - } else if args.Canvas != "" { + if args.Canvas != "" { // Pre-invoke the get_canvas tool to get the canvas definition. _, err := s.CallTool(ctx, RoleAssistant, GetCanvasName, nil, &GetCanvasArgs{ Canvas: args.Canvas, @@ -123,15 +138,6 @@ func (t *AnalystAgent) Handler(ctx context.Context, args *AnalystAgentArgs) (*An if err != nil && errors.Is(err, ctx.Err()) { // Don't exit on non-context errors return nil, err } - - _, metricsViews, err := t.getValidCanvasAndMetricsViews(ctx, args.Canvas) - if err != nil { - return nil, err - } - - for _, res := range metricsViews { - metricsViewNames = append(metricsViewNames, res.Meta.Name.Name) - } } // Pre-invoke the query_metrics_view tool for each metrics view tied to the explore/canvas. @@ -160,6 +166,22 @@ func (t *AnalystAgent) Handler(ctx context.Context, args *AnalystAgentArgs) (*An } } + // Load the project's skills. Loading failures should degrade the analysis, not fail it. + skills, err := s.Skills(ctx) + if err != nil { + s.logger.Warn("failed to load project skills", zap.Error(err)) + skills = nil + } + + // Pre-invoke the skill tools so the agent discovers the project's skills and follows the always-apply ones. + // Like the other pre-invoked calls, this is limited to the first invocation: later invocations see the calls in the conversation history. + if first && len(skills) > 0 { + err := preloadSkills(ctx, s, skills, parser.SkillAgentAnalyst) + if err != nil { + return nil, err + } + } + // Determine tools that can be used tools := []string{} if args.Explore == "" { @@ -169,6 +191,9 @@ func (t *AnalystAgent) Handler(ctx context.Context, args *AnalystAgentArgs) (*An if !args.DisableCharts { tools = append(tools, CreateChartName) } + if len(skills) > 0 { + tools = append(tools, ListSkillsName, LoadSkillName) + } // Build completion messages systemPrompt, err := t.systemPrompt() diff --git a/runtime/ai/analyst_agent_test.go b/runtime/ai/analyst_agent_test.go index cb21e17caded..6f4ccb22f03a 100644 --- a/runtime/ai/analyst_agent_test.go +++ b/runtime/ai/analyst_agent_test.go @@ -384,6 +384,75 @@ func requireValidChartSpec(t *testing.T, s *ai.Session, chartCall *ai.Message, v return spec } +func TestAnalystSkills(t *testing.T) { + // Setup the basic orders metrics view plus two project skills: + // an on-demand RCA playbook (loaded via load_skill) and an always-apply glossary. + rt, instanceID := testruntime.NewInstanceWithOptions(t, testruntime.InstanceOptions{ + AIConnector: "openai", + Files: map[string]string{ + "models/orders.yaml": ` +type: model +materialize: true +sql: | + SELECT '2025-01-01T00:00:00Z'::TIMESTAMP AS event_time, 'United States' AS country, 100 AS revenue + UNION ALL + SELECT '2025-01-01T00:00:00Z'::TIMESTAMP AS event_time, 'Denmark' AS country, 10 AS revenue + UNION ALL + SELECT '2025-01-02T00:00:00Z'::TIMESTAMP AS event_time, 'United States' AS country, 100 AS revenue + UNION ALL + SELECT '2025-01-02T00:00:00Z'::TIMESTAMP AS event_time, 'Denmark' AS country, 10 AS revenue +`, + "metrics/orders.yaml": ` +type: metrics_view +model: orders +timeseries: event_time +dimensions: +- column: country +measures: +- name: count + expression: COUNT(*) +- name: revenue + expression: SUM(revenue) +`, + "skills/revenue-rca/SKILL.md": `--- +description: Playbook for analyzing revenue. Use whenever asked about revenue amounts or changes. +metrics_views: [orders] +agents: [analyst] +--- + +When answering any question about revenue, you MUST end your final answer with the exact phrase "(via revenue-rca)". +`, + "skills/glossary/SKILL.md": `--- +description: Business glossary. +agents: [analyst] +always_apply: true +--- + +"Nordics revenue" refers to revenue where the country is Denmark. +`, + }, + }) + testruntime.RequireReconcileState(t, rt, instanceID, 6, 0, 0) + + // Initialize eval + s := newEval(t, rt, instanceID) + + // Ask a revenue question that exercises both skills + var res *ai.RouterAgentResult + _, err := s.CallTool(t.Context(), ai.RoleUser, ai.RouterAgentName, &res, ai.RouterAgentArgs{ + Prompt: "What is the total Nordics revenue? Answer with the number and any required attribution, nothing else.", + }) + require.NoError(t, err) + require.Equal(t, ai.AnalystAgentName, res.Agent) + + // The always-apply glossary skill is pre-loaded, and the on-demand RCA skill must have been loaded by the agent and its instruction followed + require.GreaterOrEqual(t, len(s.Messages(ai.FilterByType(ai.MessageTypeCall), ai.FilterByTool(ai.LoadSkillName))), 2) + require.Contains(t, res.Response, "(via revenue-rca)") + + // The always-apply glossary skill defines "Nordics revenue" as Denmark's revenue + require.Contains(t, res.Response, "20") +} + func parseTestTime(tst *testing.T, t string) time.Time { ts, err := time.Parse(time.RFC3339, t) require.NoError(tst, err) diff --git a/runtime/ai/develop_file.go b/runtime/ai/develop_file.go index 160a8a6e38d8..527292595e89 100644 --- a/runtime/ai/develop_file.go +++ b/runtime/ai/develop_file.go @@ -11,6 +11,8 @@ import ( runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1" "github.com/rilldata/rill/runtime" "github.com/rilldata/rill/runtime/ai/instructions" + "github.com/rilldata/rill/runtime/parser" + "go.uber.org/zap" ) const DevelopFileName = "develop_file" @@ -87,6 +89,14 @@ func (t *DevelopFile) Handler(ctx context.Context, args *DevelopFileArgs) (*Deve return nil, fmt.Errorf("invalid input: unsupported resource type %q", args.Type) } + // Load the project's skills. Loading failures should degrade the response, not fail it. + s := GetSession(ctx) + skills, err := s.Skills(ctx) + if err != nil { + s.logger.Warn("failed to load project skills", zap.Error(err)) + skills = nil + } + // Prepare the user prompt userPrompt, err := t.userPrompt(ctx, args) if err != nil { @@ -94,7 +104,6 @@ func (t *DevelopFile) Handler(ctx context.Context, args *DevelopFileArgs) (*Deve } // Pre-invoke some tool calls - s := GetSession(ctx) _, err = s.CallTool(ctx, RoleAssistant, ListFilesName, nil, &ListFilesArgs{}) if err != nil { return nil, err @@ -109,6 +118,13 @@ func (t *DevelopFile) Handler(ctx context.Context, args *DevelopFileArgs) (*Deve if ctx.Err() != nil { // Ignore tool error since the file may not exist return nil, ctx.Err() } + // Pre-invoke the skill tools so the sub-agent, which does not see the parent conversation, discovers the project's skills and follows the always-apply ones. + if len(skills) > 0 { + err = preloadSkills(ctx, s, skills, parser.SkillAgentDeveloper) + if err != nil { + return nil, err + } + } // Build initial completion messages messages := []*aiv1.CompletionMessage{NewTextCompletionMessage(RoleSystem, generalInstructions.Body)} @@ -118,21 +134,27 @@ func (t *DevelopFile) Handler(ctx context.Context, args *DevelopFileArgs) (*Deve messages = append(messages, NewTextCompletionMessage(RoleUser, userPrompt)) messages = append(messages, s.NewCompletionMessages(s.MessagesWithResults(FilterByParent(s.ID())))...) + // Determine tools that can be used + tools := []string{ + SearchFilesName, + ReadFileName, + WriteFileName, + GetMetricsViewName, + ListBucketsName, + ListBucketObjectsName, + ListTablesName, + ShowTableName, + QuerySQLName, + } + if len(skills) > 0 { + tools = append(tools, ListSkillsName, LoadSkillName) + } + // Run an LLM tool call loop var response string err = s.Complete(ctx, "File developer loop", &response, &CompleteOptions{ - Messages: messages, - Tools: []string{ - SearchFilesName, - ReadFileName, - WriteFileName, - GetMetricsViewName, - ListBucketsName, - ListBucketObjectsName, - ListTablesName, - ShowTableName, - QuerySQLName, - }, + Messages: messages, + Tools: tools, MaxIterations: 10, UnwrapCall: true, }) diff --git a/runtime/ai/developer_agent.go b/runtime/ai/developer_agent.go index 762a71e350a6..85ed4a2bea70 100644 --- a/runtime/ai/developer_agent.go +++ b/runtime/ai/developer_agent.go @@ -10,6 +10,8 @@ import ( aiv1 "github.com/rilldata/rill/proto/gen/rill/ai/v1" "github.com/rilldata/rill/runtime" "github.com/rilldata/rill/runtime/ai/instructions" + "github.com/rilldata/rill/runtime/parser" + "go.uber.org/zap" ) const DeveloperAgentName = "developer_agent" @@ -47,6 +49,15 @@ func (t *DeveloperAgent) CheckAccess(ctx context.Context) (bool, error) { } func (t *DeveloperAgent) Handler(ctx context.Context, args *DeveloperAgentArgs) (*DeveloperAgentResult, error) { + s := GetSession(ctx) + + // Load the project's skills. Loading failures should degrade the response, not fail it. + skills, err := s.Skills(ctx) + if err != nil { + s.logger.Warn("failed to load project skills", zap.Error(err)) + skills = nil + } + // Generate the prompts systemPrompt, err := t.systemPrompt() if err != nil { @@ -58,7 +69,6 @@ func (t *DeveloperAgent) Handler(ctx context.Context, args *DeveloperAgentArgs) } // Pre-invoke some tool calls - s := GetSession(ctx) _, err = s.CallTool(ctx, RoleAssistant, ListFilesName, nil, &ListFilesArgs{}) if err != nil { return nil, err @@ -75,6 +85,14 @@ func (t *DeveloperAgent) Handler(ctx context.Context, args *DeveloperAgentArgs) return nil, ctx.Err() } } + // Pre-invoke the skill tools so the agent discovers the project's skills and follows the always-apply ones. + // The conversation history only carries the agent's previous calls and responses, so like the calls above this runs on every invocation. + if len(skills) > 0 { + err = preloadSkills(ctx, s, skills, parser.SkillAgentDeveloper) + if err != nil { + return nil, err + } + } // Build initial completion messages messages := []*aiv1.CompletionMessage{NewTextCompletionMessage(RoleSystem, systemPrompt)} @@ -82,23 +100,29 @@ func (t *DeveloperAgent) Handler(ctx context.Context, args *DeveloperAgentArgs) messages = append(messages, NewTextCompletionMessage(RoleUser, userPrompt)) messages = append(messages, s.NewCompletionMessages(s.MessagesWithResults(FilterByParent(s.ParentID)))...) + // Determine tools that can be used + tools := []string{ + ProjectStatusName, + ListFilesName, + SearchFilesName, + ReadFileName, + ListBucketsName, + ListBucketObjectsName, + ListTablesName, + ShowTableName, + QuerySQLName, + DevelopFileName, + NavigateName, + } + if len(skills) > 0 { + tools = append(tools, ListSkillsName, LoadSkillName) + } + // Run an LLM tool call loop var response string err = s.Complete(ctx, "Developer loop", &response, &CompleteOptions{ - Messages: messages, - Tools: []string{ - ProjectStatusName, - ListFilesName, - SearchFilesName, - ReadFileName, - ListBucketsName, - ListBucketObjectsName, - ListTablesName, - ShowTableName, - QuerySQLName, - DevelopFileName, - NavigateName, - }, + Messages: messages, + Tools: tools, MaxIterations: 20, UnwrapCall: true, }) diff --git a/runtime/ai/export_test.go b/runtime/ai/export_test.go new file mode 100644 index 000000000000..0c54e05b0c41 --- /dev/null +++ b/runtime/ai/export_test.go @@ -0,0 +1,4 @@ +package ai + +// PreloadSkills exposes preloadSkills to the external test package. +var PreloadSkills = preloadSkills diff --git a/runtime/ai/instructions/data/analysis.md b/runtime/ai/instructions/data/analysis.md index 0bc046db65bb..192cc7f24cf3 100644 --- a/runtime/ai/instructions/data/analysis.md +++ b/runtime/ai/instructions/data/analysis.md @@ -76,6 +76,17 @@ Choose the appropriate chart type based on your data: - If a desired calculation cannot be achieved through the metrics tools, explicitly state this limitation - Use only the exact numbers returned by the tools in your analysis +{% if not .external %} +## Skills + +The project may define **skills**: instruction files with project-specific analysis practices, such as playbooks and business glossaries. +If a "list_skills" result is present in the conversation, the project defines skills. Then: +- Skills marked "always_apply" have already been loaded for you; treat their instructions as always in effect. +- Only use skills whose "agents" include "analyst". +- A skill's "metrics_views" lists the metrics views it is relevant to. Consider it whenever your analysis involves one of them, including metrics views beyond the dashboard you started from. +- Before doing work that a skill's description covers, you MUST call "load_skill" to retrieve it and follow its instructions before running any queries. +{% end %} + ## Guardrails You only engage in conversation that relates to the project's data. diff --git a/runtime/ai/instructions/data/development.md b/runtime/ai/instructions/data/development.md index 8f7f9ec23889..7b57af936f72 100644 --- a/runtime/ai/instructions/data/development.md +++ b/runtime/ai/instructions/data/development.md @@ -189,6 +189,20 @@ Since they repeatedly run a query, they are slightly expensive resources. They are usually found downstream of a metrics view in the DAG. Most projects don't define reports directly as files; instead, users can define reports using a UI in Rill Cloud. +### Skills + +Skills teach Rill's AI agents project-specific practices, such as analysis playbooks (e.g. how to do root-cause analysis for a revenue drop) and business glossaries. +A skill is a directory containing a `SKILL.md` file that follows the Agent Skills format (https://agentskills.io): YAML front matter followed by a markdown body with the instructions. +Rill loads skills from `skills//SKILL.md`, and also from `.agents/skills//SKILL.md` for compatibility with skills authored for other agent clients. +The front matter supports these properties: +- `description:` (required) a short summary used to decide when the skill applies; write it as "what it does + when to use it" +- `name:` always include it (the Agent Skills format requires it, even though Rill derives it from the directory when omitted); must match the directory name; lowercase letters, numbers and hyphens only +- `metrics_views:` (optional, Rill extension) list of metrics view names the skill is relevant to; the analyst uses it to decide when to load the skill +- `agents:` (optional, Rill extension) list of agents the skill applies to, `analyst` and/or `developer`; defaults to `[developer]`, so analysis skills must set `agents: [analyst]` +- `always_apply:` (optional, Rill extension) if `true`, the skill is loaded up front in every conversation instead of on demand; use for short, broadly applicable guidance such as glossaries + +Skill contents are visible to every user who can use AI features in the project, so they must never contain secrets. + ### `rill.yaml` `rill.yaml` is a required file for project-wide config found at the root directory of a Rill project. @@ -296,3 +310,13 @@ Avoid these mistakes when developing a project: - **Calling navigate too early:** Do NOT call `navigate` while iterating on changes. Only call it once, at the very end, right before your final response. - **Don't stop if there are errors:** When a file has an error after you made changes, keep looping until you have done your best to fix the error. You should not give up easily, the user expects you to try and fix errors. {% end %} + +{% if not .external %} +## Using skills + +The project may define **skills**: instruction files with project-specific development practices and conventions. +If a "list_skills" result is present in the conversation, the project defines skills. Then: +- Skills marked "always_apply" have already been loaded for you; treat their instructions as always in effect. +- Only use skills whose "agents" include "developer". +- Before doing work that a skill's description covers, you MUST call "load_skill" to retrieve it and follow its instructions. +{% end %} diff --git a/runtime/ai/instructions/instructions.go b/runtime/ai/instructions/instructions.go index 09a154827f4d..39abcbe1dbc7 100644 --- a/runtime/ai/instructions/instructions.go +++ b/runtime/ai/instructions/instructions.go @@ -96,7 +96,8 @@ func parseInstruction(path string, content []byte, opts Options) (*Instruction, name := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)) // Parse front matter - fm, body, err := parseFrontMatter(content) + var fm frontMatter + body, err := parseFrontMatter(content, &fm) if err != nil { return nil, err } @@ -114,15 +115,16 @@ func parseInstruction(path string, content []byte, opts Options) (*Instruction, }, nil } -// parseFrontMatter extracts YAML front matter from markdown content. +// parseFrontMatter extracts YAML front matter from markdown content, decoding it into the provided struct. // Front matter is expected to be delimited by "---" at the start and end. -func parseFrontMatter(content []byte) (*frontMatter, string, error) { +// If the content has no front matter, the struct is left untouched and the full content is returned as the body. +func parseFrontMatter(content []byte, into any) (string, error) { contentStr := strings.TrimSpace(string(content)) // Check for front matter delimiter at the start if !strings.HasPrefix(contentStr, "---\n") && !strings.HasPrefix(contentStr, "---\r\n") { - // No front matter, return empty front matter and full content as body - return &frontMatter{}, contentStr, nil + // No front matter, return full content as body + return contentStr, nil } // Find the closing delimiter @@ -134,7 +136,7 @@ func parseFrontMatter(content []byte) (*frontMatter, string, error) { endIdx := strings.Index(rest, "\n---") if endIdx == -1 { - return nil, "", fmt.Errorf("unclosed front matter: missing closing ---") + return "", fmt.Errorf("unclosed front matter: missing closing ---") } frontMatterContent := rest[:endIdx] @@ -144,12 +146,11 @@ func parseFrontMatter(content []byte) (*frontMatter, string, error) { body = strings.TrimSpace(body) // Parse the front matter YAML - var fm frontMatter - if err := yaml.Unmarshal([]byte(frontMatterContent), &fm); err != nil { - return nil, "", fmt.Errorf("failed to parse front matter YAML: %w", err) + if err := yaml.Unmarshal([]byte(frontMatterContent), into); err != nil { + return "", fmt.Errorf("failed to parse front matter YAML: %w", err) } - return &fm, body, nil + return body, nil } // executeTemplate applies Go's template engine to the instruction body. diff --git a/runtime/ai/instructions/instructions_test.go b/runtime/ai/instructions/instructions_test.go index 6fb005dcf5ac..8fa7acfe9c66 100644 --- a/runtime/ai/instructions/instructions_test.go +++ b/runtime/ai/instructions/instructions_test.go @@ -114,7 +114,8 @@ description: Test`, for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - fm, body, err := parseFrontMatter([]byte(tt.content)) + var fm frontMatter + body, err := parseFrontMatter([]byte(tt.content), &fm) if tt.wantErr { require.Error(t, err) return diff --git a/runtime/ai/mcp.go b/runtime/ai/mcp.go index c55390b328f9..e9c6d0e8906c 100644 --- a/runtime/ai/mcp.go +++ b/runtime/ai/mcp.go @@ -15,7 +15,9 @@ import ( ) // MCPInstructions are the instructions advertised by the MCP server. -// It is exported so the unified MCP server in the admin service can extend it instead of restating it. +// It is exported so the unified MCP server in the admin service, which serves all projects, can extend it instead of restating it. +// +//nolint:gosec // G101 false positive: long instructions text, not a credential const MCPInstructions = ` # Rill MCP Server This server exposes APIs for querying **metrics views**, which represent Rill's metrics layer. @@ -29,6 +31,12 @@ This server exposes APIs for querying **metrics views**, which represent Rill's In the workflow, do not proceed with the next step until the previous step has been completed. If the information from the previous step is already known (let's say for subsequent queries), you can skip it. If a response contains an "ai_instructions" field, you should interpret it as additional instructions for how to behave in subsequent responses that relate to that tool call. +## Skills +Projects may define **skills**: instruction files that teach agents project-specific analysis or development practices, such as analysis playbooks and business glossaries. The skill tools are only exposed when the project defines skills: +- Use "list_skills" early in a session to discover the project's skills. +- Before doing work that a skill's description covers, use "load_skill" to fetch its full instructions and follow them. +- Load any skill marked "always_apply" up front and treat its instructions as always in effect. + ## Project Development If you have edit access, the server also exposes tools for inspecting and editing the project's source code, which consists of YAML and SQL files: - **List files:** Use "list_files" to browse the files in the project. @@ -54,7 +62,8 @@ func MCPToolSpecs() map[string]*mcp.Tool { // Since it is scoped to the session, a new MCP server should be created for each client connection. // Using a separate MCP server for each client enables tailoring the server's instructions and available tools to the end user's claims. func (s *Session) MCPServer(ctx context.Context) *mcp.Server { - // Create the MCP server + // Create the MCP server. + // The instructions omit the skills section; it is added during the initialization handshake if the project defines skills (see below). srv := mcp.NewServer( &mcp.Implementation{ Name: "rill", diff --git a/runtime/ai/metrics_view_list.go b/runtime/ai/metrics_view_list.go index d7c6e6f8c5d7..03e1a0a4c458 100644 --- a/runtime/ai/metrics_view_list.go +++ b/runtime/ai/metrics_view_list.go @@ -9,6 +9,8 @@ import ( "github.com/modelcontextprotocol/go-sdk/mcp" runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1" "github.com/rilldata/rill/runtime" + "github.com/rilldata/rill/runtime/parser" + "go.uber.org/zap" ) const ListMetricsViewsName = "list_metrics_views" @@ -94,15 +96,46 @@ func (t *ListMetricsViews) Handler(ctx context.Context, args *ListMetricsViewsAr // Find instance-wide AI context and add it to the response. // NOTE: These arguably belong in the top-level instructions or other metadata, but that doesn't currently support dynamic values. - // Rill's own agents receive the project instructions directly in their prompts, - // so this is only for external MCP clients (identified by a non-rill user agent). - var aiInstructions string + // Rill's own agents receive the project instructions and always-apply skills directly in their prompts, + // so this enrichment is only for external MCP clients (identified by a non-rill user agent). + var aiInstructions strings.Builder if !strings.HasPrefix(session.CatalogSession().UserAgent, "rill") { instance, err := t.Runtime.Instance(ctx, session.InstanceID()) if err != nil { return nil, fmt.Errorf("failed to get instance %q: %w", session.InstanceID(), err) } - aiInstructions = instance.AIInstructions + aiInstructions.WriteString(instance.AIInstructions) + + // Append always-apply skills so external clients receive them without extra round-trips. + // Skill loading failures should degrade the response, not fail it. + skills, err := session.Skills(ctx) + if err != nil { + session.logger.Warn("failed to load project skills", zap.Error(err)) + } + // The skills have their own byte budget, matching the in-app agents, so long ai_instructions do not crowd them out. + // The cap applies to the rendered section, including its separator and heading, not just the body. + var skillBytes int + for _, sk := range skillsForAgent(skills, parser.SkillAgentAnalyst) { + if !sk.AlwaysApply { + continue + } + var section string + if aiInstructions.Len() > 0 { + section = "\n\n" + } + section += fmt.Sprintf("## Skill: %s\n\n", sk.Name) + // The tool has no selected metrics view, so a scoped skill states its scope. + if len(sk.MetricsViews) > 0 { + section += fmt.Sprintf("Applies to the metrics views: %s.\n\n", strings.Join(sk.MetricsViews, ", ")) + } + section += sk.Body + if skillBytes+len(section) > skillsMaxAlwaysApplyBytes { + session.logger.Warn("always-apply skill exceeds the size cap; clients must load it with load_skill", zap.String("skill", sk.Name)) + continue + } + aiInstructions.WriteString(section) + skillBytes += len(section) + } } var metricsViews []map[string]any @@ -120,7 +153,7 @@ func (t *ListMetricsViews) Handler(ctx context.Context, args *ListMetricsViewsAr } return &ListMetricsViewsResult{ - AIInstructions: aiInstructions, + AIInstructions: aiInstructions.String(), MetricsViews: metricsViews, }, nil } diff --git a/runtime/ai/project_status.go b/runtime/ai/project_status.go index 95f9a23476bb..7ffa12ba3f54 100644 --- a/runtime/ai/project_status.go +++ b/runtime/ai/project_status.go @@ -237,4 +237,5 @@ var projectStatusDefaultResourceKinds = []string{ runtime.ResourceKindExplore, runtime.ResourceKindMetricsView, runtime.ResourceKindModel, + runtime.ResourceKindSkill, } diff --git a/runtime/ai/skill_list.go b/runtime/ai/skill_list.go new file mode 100644 index 000000000000..6237a1dc62d1 --- /dev/null +++ b/runtime/ai/skill_list.go @@ -0,0 +1,75 @@ +package ai + +import ( + "context" + + "github.com/modelcontextprotocol/go-sdk/mcp" + "github.com/rilldata/rill/runtime" +) + +const ListSkillsName = "list_skills" + +type ListSkills struct { + Runtime *runtime.Runtime +} + +var _ Tool[*ListSkillsArgs, *ListSkillsResult] = (*ListSkills)(nil) + +type ListSkillsArgs struct{} + +type ListSkillsResult struct { + Skills []*SkillInfo `json:"skills"` +} + +// SkillInfo describes a skill without its body. Use load_skill to fetch the full instructions. +type SkillInfo struct { + Name string `json:"name"` + Description string `json:"description"` + MetricsViews []string `json:"metrics_views,omitempty"` + Agents []string `json:"agents"` + AlwaysApply bool `json:"always_apply"` +} + +func (t *ListSkills) Spec() *mcp.Tool { + return &mcp.Tool{ + Name: ListSkillsName, + Title: "List skills", + Description: "Lists the skills defined in the project. Skills are instruction files that teach AI agents project-specific analysis or development practices. Load a skill with the load_skill tool before doing work its description covers. Treat skills marked always_apply as standing instructions and load them up front.", + Annotations: &mcp.ToolAnnotations{ + DestructiveHint: boolPtr(false), + IdempotentHint: true, + OpenWorldHint: boolPtr(false), + ReadOnlyHint: true, + }, + Meta: map[string]any{ + "openai/toolInvocation/invoking": "Listing skills...", + "openai/toolInvocation/invoked": "Listed skills", + }, + } +} + +func (t *ListSkills) CheckAccess(ctx context.Context) (bool, error) { + return checkSkillAccess(ctx) +} + +func (t *ListSkills) Handler(ctx context.Context, args *ListSkillsArgs) (*ListSkillsResult, error) { + s := GetSession(ctx) + + skills, err := s.Skills(ctx) + if err != nil { + return nil, err + } + + infos := make([]*SkillInfo, len(skills)) + for i, sk := range skills { + infos[i] = &SkillInfo{ + Name: sk.Name, + Description: sk.Description, + MetricsViews: sk.MetricsViews, + Agents: sk.Agents, + AlwaysApply: sk.AlwaysApply, + } + } + + return &ListSkillsResult{Skills: infos}, nil +} diff --git a/runtime/ai/skill_load.go b/runtime/ai/skill_load.go new file mode 100644 index 000000000000..d4de4e6ba34b --- /dev/null +++ b/runtime/ai/skill_load.go @@ -0,0 +1,76 @@ +package ai + +import ( + "context" + "fmt" + "strings" + + "github.com/modelcontextprotocol/go-sdk/mcp" + "github.com/rilldata/rill/runtime" +) + +const LoadSkillName = "load_skill" + +type LoadSkill struct { + Runtime *runtime.Runtime +} + +var _ Tool[*LoadSkillArgs, *LoadSkillResult] = (*LoadSkill)(nil) + +type LoadSkillArgs struct { + Name string `json:"name" jsonschema:"Name of the skill to load"` +} + +type LoadSkillResult struct { + Name string `json:"name"` + Description string `json:"description"` + Body string `json:"body"` +} + +func (t *LoadSkill) Spec() *mcp.Tool { + return &mcp.Tool{ + Name: LoadSkillName, + Title: "Using skill", + Description: "Loads the full instructions for a skill defined in the project. Skills teach project-specific analysis or development practices. Call this before doing work that a skill's description covers, then follow the returned instructions.", + Annotations: &mcp.ToolAnnotations{ + DestructiveHint: boolPtr(false), + IdempotentHint: true, + OpenWorldHint: boolPtr(false), + ReadOnlyHint: true, + }, + Meta: map[string]any{ + "openai/toolInvocation/invoking": "Loading skill...", + "openai/toolInvocation/invoked": "Loaded skill", + }, + } +} + +func (t *LoadSkill) CheckAccess(ctx context.Context) (bool, error) { + return checkSkillAccess(ctx) +} + +func (t *LoadSkill) Handler(ctx context.Context, args *LoadSkillArgs) (*LoadSkillResult, error) { + s := GetSession(ctx) + + skills, err := s.Skills(ctx) + if err != nil { + return nil, err + } + + names := make([]string, len(skills)) + for i, sk := range skills { + if sk.Name == args.Name { + return &LoadSkillResult{ + Name: sk.Name, + Description: sk.Description, + Body: sk.Body, + }, nil + } + names[i] = sk.Name + } + + if len(names) == 0 { + return nil, fmt.Errorf("skill %q not found: the project does not define any skills", args.Name) + } + return nil, fmt.Errorf("skill %q not found: available skills are %s", args.Name, strings.Join(names, ", ")) +} diff --git a/runtime/ai/skills.go b/runtime/ai/skills.go new file mode 100644 index 000000000000..1b3a915347fe --- /dev/null +++ b/runtime/ai/skills.go @@ -0,0 +1,119 @@ +package ai + +import ( + "context" + "errors" + "slices" + "strings" + + "github.com/rilldata/rill/runtime" + "go.uber.org/zap" +) + +// skillsMaxAlwaysApplyBytes caps the total size of always-apply skill bodies appended to the ai_instructions returned to external MCP clients. +// Skills that exceed the cap must be loaded with the load_skill tool instead. +const skillsMaxAlwaysApplyBytes = 1 << 15 // 32kb + +// Skill is a user-defined instruction file that teaches Rill's AI agents project-specific practices, +// such as analysis playbooks, business glossaries, or development conventions. +// Skills are parsed from SKILL.md files into catalog resources; see runtime/parser/parse_skill.go. +type Skill struct { + Name string + Description string + MetricsViews []string + Agents []string + AlwaysApply bool + Body string +} + +// Skills lazily loads the project's skills from the catalog, memoizing the result for the lifetime of the session. +func (s *BaseSession) Skills(ctx context.Context) ([]*Skill, error) { + err := s.skillsMu.Lock(ctx) + if err != nil { + return nil, err + } + defer s.skillsMu.Unlock() + if s.skillsLoaded { + return s.skills, nil + } + + ctrl, err := s.runner.Runtime.Controller(ctx, s.instanceID) + if err != nil { + return nil, err + } + + rs, err := ctrl.List(ctx, runtime.ResourceKindSkill, "", false) + if err != nil { + return nil, err + } + + skills := make([]*Skill, 0, len(rs)) + for _, r := range rs { + // Skip skills that failed reconciliation (e.g. scoped to a metrics view that doesn't exist), + // so that the validation actually keeps invalid instructions away from the agents. + if r.Meta.ReconcileError != "" { + s.logger.Warn("skipping skill with reconcile error", zap.String("skill", r.Meta.Name.Name), zap.String("error", r.Meta.ReconcileError)) + continue + } + spec := r.GetSkill().Spec + skills = append(skills, &Skill{ + Name: r.Meta.Name.Name, + Description: spec.Description, + MetricsViews: spec.MetricsViews, + Agents: spec.Agents, + AlwaysApply: spec.AlwaysApply, + Body: spec.Body, + }) + } + slices.SortFunc(skills, func(a, b *Skill) int { return strings.Compare(a.Name, b.Name) }) + + s.skills = skills + s.skillsLoaded = true + return s.skills, nil +} + +// checkSkillAccess checks whether the skill tools should be available in the current session. +// They are only exposed when the project defines skills, so clients of projects without skills never see them. +func checkSkillAccess(ctx context.Context) (bool, error) { + s := GetSession(ctx) + if !s.Claims().Can(runtime.UseAI) { + return false, nil + } + skills, err := s.Skills(ctx) + if err != nil { + return false, err + } + return len(skills) > 0, nil +} + +// skillsForAgent returns the skills that apply to the given agent. +func skillsForAgent(skills []*Skill, agent string) []*Skill { + var res []*Skill + for _, sk := range skills { + if slices.Contains(sk.Agents, agent) { + res = append(res, sk) + } + } + return res +} + +// preloadSkills seeds the current call with the tool calls that make the project's skills available to an agent: +// a list_skills call so the agent can discover skills and load them on demand, and a load_skill call for each of the agent's always-apply skills. +// The seeded calls become part of the agent's context like any other pre-invoked tool call. +// Tool errors are recorded in the session and don't fail the agent; only context cancellation is returned. +func preloadSkills(ctx context.Context, s *Session, skills []*Skill, agent string) error { + _, err := s.CallTool(ctx, RoleAssistant, ListSkillsName, nil, &ListSkillsArgs{}) + if err != nil && errors.Is(err, ctx.Err()) { + return err + } + for _, sk := range skillsForAgent(skills, agent) { + if !sk.AlwaysApply { + continue + } + _, err := s.CallTool(ctx, RoleAssistant, LoadSkillName, nil, &LoadSkillArgs{Name: sk.Name}) + if err != nil && errors.Is(err, ctx.Err()) { + return err + } + } + return nil +} diff --git a/runtime/ai/skills_internal_test.go b/runtime/ai/skills_internal_test.go new file mode 100644 index 000000000000..447b84f02e6e --- /dev/null +++ b/runtime/ai/skills_internal_test.go @@ -0,0 +1,33 @@ +package ai + +import ( + "testing" + + "github.com/rilldata/rill/runtime/parser" + "github.com/stretchr/testify/require" +) + +func TestMCPInstructions(t *testing.T) { + require.Contains(t, MCPInstructions, "## Workflow Overview") + require.Contains(t, MCPInstructions, "## Skills") + require.Contains(t, MCPInstructions, "## Project Development") +} + +func TestSkillsForAgent(t *testing.T) { + skills := []*Skill{ + {Name: "rca", Agents: []string{parser.SkillAgentAnalyst}, MetricsViews: []string{"orders"}}, + {Name: "glossary", Agents: []string{parser.SkillAgentAnalyst, parser.SkillAgentDeveloper}}, + {Name: "modeling", Agents: []string{parser.SkillAgentDeveloper}}, + } + + names := func(skills []*Skill) []string { + res := make([]string, len(skills)) + for i, sk := range skills { + res[i] = sk.Name + } + return res + } + + require.Equal(t, []string{"rca", "glossary"}, names(skillsForAgent(skills, parser.SkillAgentAnalyst))) + require.Equal(t, []string{"glossary", "modeling"}, names(skillsForAgent(skills, parser.SkillAgentDeveloper))) +} diff --git a/runtime/ai/skills_test.go b/runtime/ai/skills_test.go new file mode 100644 index 000000000000..8fdac4666a28 --- /dev/null +++ b/runtime/ai/skills_test.go @@ -0,0 +1,305 @@ +package ai_test + +import ( + "strings" + "testing" + + "github.com/google/uuid" + "github.com/rilldata/rill/runtime" + "github.com/rilldata/rill/runtime/ai" + "github.com/rilldata/rill/runtime/parser" + "github.com/rilldata/rill/runtime/pkg/activity" + "github.com/rilldata/rill/runtime/testruntime" + "github.com/stretchr/testify/require" +) + +func TestSkills(t *testing.T) { + rt, instanceID := testruntime.NewInstanceWithOptions(t, testruntime.InstanceOptions{ + Files: map[string]string{ + "models/orders.sql": `SELECT 1 AS revenue`, + "metrics/orders.yaml": ` +type: metrics_view +model: orders +measures: +- name: revenue + expression: SUM(revenue) +`, + "skills/revenue-rca/SKILL.md": `--- +name: revenue-rca +description: Playbook for diagnosing revenue drops. +metrics_views: [orders] +agents: [analyst] +--- + +# Revenue RCA playbook +Always break revenue down by country first.`, + // Skills are also loaded from the generic .agents/skills directory + ".agents/skills/glossary/SKILL.md": `--- +description: Business glossary. +always_apply: true +agents: [analyst, developer] +--- + +ARPU excludes trial users.`, + // Scoped to a metrics view that doesn't exist: fails reconciliation and must not reach the agents + "skills/stale/SKILL.md": `--- +description: Scoped to a missing metrics view. +metrics_views: [missing] +--- + +Stale instructions.`, + }, + }) + testruntime.RequireReconcileState(t, rt, instanceID, 7, 1, 0) + s := newSession(t, rt, instanceID) + + // List skills: sorted by name + var listRes *ai.ListSkillsResult + _, err := s.CallTool(t.Context(), ai.RoleUser, ai.ListSkillsName, &listRes, &ai.ListSkillsArgs{}) + require.NoError(t, err) + require.Len(t, listRes.Skills, 2) + require.Equal(t, "glossary", listRes.Skills[0].Name) + require.True(t, listRes.Skills[0].AlwaysApply) + require.Equal(t, []string{"analyst", "developer"}, listRes.Skills[0].Agents) + require.Equal(t, "revenue-rca", listRes.Skills[1].Name) + require.Equal(t, []string{"orders"}, listRes.Skills[1].MetricsViews) + require.Equal(t, []string{"analyst"}, listRes.Skills[1].Agents) + + // Load a skill by name + var loadRes *ai.LoadSkillResult + _, err = s.CallTool(t.Context(), ai.RoleUser, ai.LoadSkillName, &loadRes, &ai.LoadSkillArgs{Name: "revenue-rca"}) + require.NoError(t, err) + require.Equal(t, "Playbook for diagnosing revenue drops.", loadRes.Description) + require.Contains(t, loadRes.Body, "Always break revenue down by country first.") + require.NotContains(t, loadRes.Body, "---") + + // Load an unknown skill: the error lists the available names + _, err = s.CallTool(t.Context(), ai.RoleUser, ai.LoadSkillName, &loadRes, &ai.LoadSkillArgs{Name: "nope"}) + require.ErrorContains(t, err, "glossary, revenue-rca") +} + +// TestSkillsPreload verifies that preloading seeds a list_skills call and a load_skill call +// for each of the agent's always-apply skills, and nothing else. +func TestSkillsPreload(t *testing.T) { + rt, instanceID := testruntime.NewInstanceWithOptions(t, testruntime.InstanceOptions{ + Files: map[string]string{ + "skills/glossary/SKILL.md": `--- +description: Business glossary. +agents: [analyst] +always_apply: true +--- + +ARPU excludes trial users.`, + "skills/revenue-rca/SKILL.md": `--- +description: Revenue playbook. +agents: [analyst] +--- + +Break revenue down by country.`, + "skills/conventions/SKILL.md": `--- +description: Modeling conventions. +always_apply: true +--- + +Always materialize models.`, + }, + }) + testruntime.RequireReconcileState(t, rt, instanceID, 4, 0, 0) + s := newSession(t, rt, instanceID) + + skills, err := s.Skills(t.Context()) + require.NoError(t, err) + require.NoError(t, ai.PreloadSkills(t.Context(), s, skills, parser.SkillAgentAnalyst)) + + require.Len(t, s.Messages(ai.FilterByType(ai.MessageTypeCall), ai.FilterByTool(ai.ListSkillsName)), 1) + loads := s.Messages(ai.FilterByType(ai.MessageTypeCall), ai.FilterByTool(ai.LoadSkillName)) + require.Len(t, loads, 1) + require.Contains(t, loads[0].Content, `"glossary"`) + // The developer-only always-apply skill and the on-demand skill are not loaded + for _, m := range s.Messages(ai.FilterByType(ai.MessageTypeResult), ai.FilterByTool(ai.LoadSkillName)) { + require.NotContains(t, m.Content, "Always materialize models.") + require.NotContains(t, m.Content, "Break revenue down by country.") + } +} + +// TestSkillsEmptyProject verifies that the skill tools are not available in a project without skills. +func TestSkillsEmptyProject(t *testing.T) { + rt, instanceID := testruntime.NewInstanceWithOptions(t, testruntime.InstanceOptions{}) + s := newSession(t, rt, instanceID) + + for _, name := range []string{ai.ListSkillsName, ai.LoadSkillName} { + tool, ok := s.Tool(name) + require.True(t, ok) + allowed, err := tool.CheckAccess(ai.WithSession(t.Context(), s)) + require.NoError(t, err) + require.False(t, allowed, "tool %q access", name) + } + _, err := s.CallTool(t.Context(), ai.RoleUser, ai.LoadSkillName, nil, &ai.LoadSkillArgs{Name: "anything"}) + require.ErrorContains(t, err, "access denied") +} + +// TestSkillsValidation verifies that invalid skill files surface as parse errors on the file, +// and don't affect the valid skills in the project. +func TestSkillsValidation(t *testing.T) { + rt, instanceID := testruntime.NewInstanceWithOptions(t, testruntime.InstanceOptions{ + Files: map[string]string{ + "skills/valid/SKILL.md": `--- +description: A valid skill. +--- + +Body.`, + "skills/broken/SKILL.md": `# No front matter here`, + }, + }) + + ctrl, err := rt.Controller(t.Context(), instanceID) + require.NoError(t, err) + pp, err := ctrl.Get(t.Context(), runtime.GlobalProjectParserName, false) + require.NoError(t, err) + parseErrors := pp.GetProjectParser().State.ParseErrors + require.Len(t, parseErrors, 1) + require.Equal(t, "/skills/broken/SKILL.md", parseErrors[0].FilePath) + require.Contains(t, parseErrors[0].Message, "front matter") + + s := newSession(t, rt, instanceID) + var listRes *ai.ListSkillsResult + _, err = s.CallTool(t.Context(), ai.RoleUser, ai.ListSkillsName, &listRes, &ai.ListSkillsArgs{}) + require.NoError(t, err) + require.Len(t, listRes.Skills, 1) + require.Equal(t, "valid", listRes.Skills[0].Name) +} + +// TestSkillsInListMetricsViews verifies that always-apply skills are appended to the +// ai_instructions field of list_metrics_views for external MCP clients, +// but not for Rill's own agents (which receive them directly in their prompts). +func TestSkillsInListMetricsViews(t *testing.T) { + // The project instructions alone fill the always-apply budget, which must not crowd out the skills. + longInstructions := "Revenue always refers to net revenue. " + strings.Repeat("x", 1<<15) + rt, instanceID := testruntime.NewInstanceWithOptions(t, testruntime.InstanceOptions{ + Files: map[string]string{ + "rill.yaml": "ai_instructions: " + longInstructions, + "models/orders.sql": `SELECT 1 AS revenue`, + "metrics/orders.yaml": ` +type: metrics_view +model: orders +measures: +- name: revenue + expression: SUM(revenue) +`, + "skills/glossary/SKILL.md": `--- +description: Business glossary. +agents: [analyst] +always_apply: true +--- + +ARPU excludes trial users.`, + "skills/on-demand/SKILL.md": `--- +description: An on-demand skill. +agents: [analyst] +--- + +Not injected wholesale.`, + // Scoped always-apply skill: injected with its scope, since the tool has no selected metrics view + "skills/orders-rca/SKILL.md": `--- +description: Orders playbook. +metrics_views: [orders] +agents: [analyst] +always_apply: true +--- + +Break revenue down by country.`, + // Developer skill (the default agent): not relevant to external analysis clients + "skills/dev-conventions/SKILL.md": `--- +description: Modeling conventions. +always_apply: true +--- + +Always materialize models.`, + }, + }) + testruntime.RequireReconcileState(t, rt, instanceID, 8, 0, 0) + + newSession := func(t *testing.T, userAgent string, claims *runtime.SecurityClaims) *ai.Session { + r := ai.NewRunner(rt, activity.NewNoopClient()) + s, err := r.Session(t.Context(), &ai.SessionOptions{ + InstanceID: instanceID, + Claims: claims, + UserAgent: userAgent, + }) + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, s.Flush(t.Context())) + }) + return s + } + + // External MCP client: always-apply skill bodies are included after the project instructions, on-demand skill is not + s := newSession(t, "mcp-client", &runtime.SecurityClaims{UserID: uuid.NewString(), SkipChecks: true}) + var res *ai.ListMetricsViewsResult + _, err := s.CallTool(t.Context(), ai.RoleUser, ai.ListMetricsViewsName, &res, &ai.ListMetricsViewsArgs{}) + require.NoError(t, err) + require.True(t, strings.HasPrefix(res.AIInstructions, longInstructions)) + require.Contains(t, res.AIInstructions, "## Skill: glossary\n\nARPU excludes trial users.") + require.Contains(t, res.AIInstructions, "## Skill: orders-rca\n\nApplies to the metrics views: orders.\n\nBreak revenue down by country.") + require.NotContains(t, res.AIInstructions, "Not injected wholesale.") + require.NotContains(t, res.AIInstructions, "## Skill: dev-conventions") + + // Rill's own agents: no enrichment (skills are injected into their prompts instead) + s = newSession(t, "rill-web", &runtime.SecurityClaims{UserID: uuid.NewString(), SkipChecks: true}) + res = nil + _, err = s.CallTool(t.Context(), ai.RoleUser, ai.ListMetricsViewsName, &res, &ai.ListMetricsViewsArgs{}) + require.NoError(t, err) + require.Empty(t, res.AIInstructions) +} + +// TestSkillsMCPAccess verifies that the skill tools are exposed to any principal with UseAI, +// including viewers without repo access. +func TestSkillsMCPAccess(t *testing.T) { + rt, instanceID := testruntime.NewInstanceWithOptions(t, testruntime.InstanceOptions{ + Files: map[string]string{ + "skills/glossary/SKILL.md": `--- +description: Business glossary. +--- + +ARPU excludes trial users.`, + }, + }) + + newMCPSession := func(t *testing.T, permissions ...runtime.Permission) *ai.Session { + claims := &runtime.SecurityClaims{ + UserID: uuid.NewString(), + SkipChecks: false, + Permissions: permissions, + } + r := ai.NewRunner(rt, activity.NewNoopClient()) + s, err := r.Session(t.Context(), &ai.SessionOptions{ + InstanceID: instanceID, + Claims: claims, + UserAgent: "mcp-client", + }) + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, s.Flush(t.Context())) + }) + return s + } + + assertAccess := func(t *testing.T, s *ai.Session, name string, want bool) { + t.Helper() + tool, ok := s.Tool(name) + require.True(t, ok, "tool %q should be registered", name) + allowed, err := tool.CheckAccess(ai.WithSession(t.Context(), s)) + require.NoError(t, err) + require.Equal(t, want, allowed, "tool %q access", name) + } + + // A viewer-like claim set (no ReadRepo/EditRepo) can use the skill tools + s := newMCPSession(t, runtime.UseAI, runtime.ReadMetrics, runtime.ReadObjects) + assertAccess(t, s, ai.ListSkillsName, true) + assertAccess(t, s, ai.LoadSkillName, true) + + // Without UseAI, the skill tools are not accessible + s = newMCPSession(t, runtime.ReadMetrics, runtime.ReadObjects) + assertAccess(t, s, ai.ListSkillsName, false) + assertAccess(t, s, ai.LoadSkillName, false) +} diff --git a/runtime/parser/parse_skill.go b/runtime/parser/parse_skill.go new file mode 100644 index 000000000000..8fda123078dd --- /dev/null +++ b/runtime/parser/parse_skill.go @@ -0,0 +1,190 @@ +package parser + +import ( + "context" + "errors" + "fmt" + "os" + "regexp" + "strings" + "unicode/utf8" + + runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1" + "gopkg.in/yaml.v3" +) + +// Skills are directories containing a SKILL.md file that follows the Agent Skills format (https://agentskills.io). +// They teach AI agents project-specific practices, such as analysis playbooks and business glossaries. +// Rill loads skills from the `skills` directory, and additionally from the generic `.agents/skills` directory +// for compatibility with skills authored for other agent clients. + +// Rill agents that a skill can target via the `agents` front matter field. +const ( + SkillAgentAnalyst = "analyst" + SkillAgentDeveloper = "developer" +) + +// skillYAML is the front matter of a SKILL.md file. +// The `license`, `compatibility`, `metadata` and `allowed-tools` fields are defined by the Agent Skills format; +// Rill accepts them for compatibility but does not currently use them. +type skillYAML struct { + // Fields defined by the Agent Skills format + Name string `yaml:"name"` + Description string `yaml:"description"` + License string `yaml:"license"` + Compatibility string `yaml:"compatibility"` + Metadata map[string]string `yaml:"metadata"` + AllowedTools string `yaml:"allowed-tools"` + // Rill extensions + MetricsViews []string `yaml:"metrics_views"` + Agents []string `yaml:"agents"` + AlwaysApply bool `yaml:"always_apply"` +} + +// skillNameRegexp validates skill names per the Agent Skills format: +// lowercase alphanumeric characters and single hyphens, not at the start or end. +var skillNameRegexp = regexp.MustCompile(`^[a-z0-9]+(-[a-z0-9]+)*$`) + +// parseSkill parses a SKILL.md file and adds the resulting resource to p.Resources. +func (p *Parser) parseSkill(ctx context.Context, path string) error { + name, ok := skillNameForPath(path) + if !ok { + panic(fmt.Errorf("parseSkill called with invalid skill path %q", path)) + } + + data, err := p.Repo.Get(ctx, path) + if err != nil { + if os.IsNotExist(err) { + // This is a dirty parse where a file disappeared during parsing. + // Due to the clear-and-rebuild behavior, we can safely continue parsing. + return nil + } + return err + } + if len(data) > maxFileSize { + return fmt.Errorf("size %d bytes exceeds max size of %d bytes", len(data), maxFileSize) + } + + tmp := &skillYAML{} + body, err := parseSkillFrontMatter(data, tmp) + if err != nil { + return err + } + + // Per the Agent Skills format, a `name` in the front matter must match the skill's directory name. + // Rill is lenient and derives the name from the directory when the front matter omits it. + if tmp.Name != "" && tmp.Name != name { + return fmt.Errorf("front matter name %q must match the skill's directory name %q", tmp.Name, name) + } + if len(name) > 64 || !skillNameRegexp.MatchString(name) { + return fmt.Errorf("invalid skill name %q: names must be at most 64 characters of lowercase letters, numbers and non-consecutive hyphens", name) + } + tmp.Description = strings.TrimSpace(tmp.Description) + if tmp.Description == "" { + return errors.New(`missing required front matter field "description"`) + } + if utf8.RuneCountInString(tmp.Description) > 1024 { + return fmt.Errorf(`front matter field "description" exceeds the maximum length of 1024 characters`) + } + + // Skills default to the developer agent: skills authored for other agent clients are coding skills, + // which would confuse the analyst if they were offered to it. Analysis skills opt in with `agents: [analyst]`. + agents := tmp.Agents + if len(agents) == 0 { + agents = []string{SkillAgentDeveloper} + } + for _, agent := range agents { + if agent != SkillAgentAnalyst && agent != SkillAgentDeveloper { + return fmt.Errorf("invalid agent %q: expected %q or %q", agent, SkillAgentAnalyst, SkillAgentDeveloper) + } + } + + // Reference the metrics views the skill is scoped to, so a typo surfaces as an error on the file + // and the skill is revisited when the metrics views change. + refs := make([]ResourceName, len(tmp.MetricsViews)) + for i, mv := range tmp.MetricsViews { + refs[i] = ResourceName{Kind: ResourceKindMetricsView, Name: mv} + } + + r, err := p.insertResource(ResourceKindSkill, name, []string{path}, nil, nil, refs...) + if err != nil { + return err + } + // NOTE: After calling insertResource, we can't return an error. Must call p.addParseError instead. + + r.SkillSpec = &runtimev1.SkillSpec{ + Description: tmp.Description, + Body: body, + MetricsViews: tmp.MetricsViews, + Agents: agents, + AlwaysApply: tmp.AlwaysApply, + } + + return nil +} + +// skillFrontMatterDelimiterRegexp matches a line consisting solely of "---", which delimits the front matter. +var skillFrontMatterDelimiterRegexp = regexp.MustCompile(`(?m)^---\r?$`) + +// parseSkillFrontMatter splits a SKILL.md file into YAML front matter and a markdown body, +// strictly decoding the front matter into the provided struct. +func parseSkillFrontMatter(content string, into *skillYAML) (string, error) { + content = strings.TrimSpace(content) + + // The front matter is enclosed by the first two delimiter lines, the first of which must open the file. + delims := skillFrontMatterDelimiterRegexp.FindAllStringIndex(content, 2) + if len(delims) == 0 || delims[0][0] != 0 { + return "", errors.New(`skill files must start with YAML front matter delimited by "---" lines`) + } + if len(delims) < 2 { + return "", errors.New(`unclosed front matter: missing closing "---" line`) + } + frontMatter := content[delims[0][1]:delims[1][0]] + body := strings.TrimSpace(content[delims[1][1]:]) + + if strings.TrimSpace(frontMatter) == "" { + // Empty front matter; leave the struct zero-valued so required-field validation reports the actual problem + return body, nil + } + + dec := yaml.NewDecoder(strings.NewReader(frontMatter)) + dec.KnownFields(true) + if err := dec.Decode(into); err != nil { + return "", fmt.Errorf("failed to parse front matter: %w", err) + } + + return body, nil +} + +// pathIsSkill returns true if the path declares a skill. +func pathIsSkill(path string) bool { + _, ok := skillNameForPath(path) + return ok +} + +// pathIsSkillSupportFile returns true if the path is inside a skill's directory without being its SKILL.md. +// Per the Agent Skills format, a skill directory may hold supporting files such as scripts, references and examples. +// Those are ignored by the parser, so a SQL or YAML example inside a skill does not become a project resource. +func pathIsSkillSupportFile(path string) bool { + if pathIsSkill(path) { + return false + } + parts := strings.Split(strings.TrimPrefix(path, "/"), "/") + if len(parts) >= 3 && parts[0] == "skills" { + return true + } + return len(parts) >= 4 && parts[0] == ".agents" && parts[1] == "skills" +} + +// skillNameForPath returns the skill name for a path, or false if the path does not declare a skill. +// Skills are declared by SKILL.md files at `/skills//SKILL.md` or `/.agents/skills//SKILL.md`. +func skillNameForPath(path string) (string, bool) { + parts := strings.Split(strings.TrimPrefix(path, "/"), "/") + if len(parts) == 3 && parts[0] == "skills" && parts[2] == "SKILL.md" { + return parts[1], true + } + if len(parts) == 4 && parts[0] == ".agents" && parts[1] == "skills" && parts[3] == "SKILL.md" { + return parts[2], true + } + return "", false +} diff --git a/runtime/parser/parse_skill_test.go b/runtime/parser/parse_skill_test.go new file mode 100644 index 000000000000..f8017a1d58a9 --- /dev/null +++ b/runtime/parser/parse_skill_test.go @@ -0,0 +1,236 @@ +package parser + +import ( + "context" + "strings" + "testing" + + runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1" + "github.com/stretchr/testify/require" +) + +func TestSkill(t *testing.T) { + ctx := context.Background() + repo := makeRepo(t, map[string]string{ + `rill.yaml`: ``, + // Valid skill with Rill extension fields + `skills/revenue-rca/SKILL.md`: `--- +name: revenue-rca +description: Playbook for diagnosing revenue drops. +metrics_views: [orders] +agents: [analyst, developer] +--- + +# Revenue RCA playbook +Always break revenue down by country first.`, + // Valid skill in the generic .agents/skills directory, with only the standard fields + `.agents/skills/glossary/SKILL.md`: `--- +name: glossary +description: Business glossary. +license: Apache-2.0 +metadata: + author: example-org +--- + +ARPU excludes trial users.`, + // Name defaults to the directory name; always_apply is a Rill extension + `skills/formatting/SKILL.md`: `--- +description: House formatting rules. +always_apply: true +--- + +Report percentages with one decimal.`, + // Markdown files that are not skills are ignored + `skills/revenue-rca/references/notes.md`: `Auxiliary file, not a skill.`, + `skills/loose.md`: `Not in a skill directory.`, + `README.md`: `Not a skill.`, + // Invalid: missing description + `skills/broken/SKILL.md`: `--- +name: broken +--- + +Body.`, + // Invalid: empty front matter, which must be reported as the missing description rather than as an unclosed delimiter + `skills/empty/SKILL.md`: `--- +--- + +Body.`, + // Invalid: front matter name doesn't match the directory name + `skills/mismatch/SKILL.md`: `--- +name: other-name +description: Mismatched name. +--- + +Body.`, + // Invalid: unknown front matter field (probably a typo) + `skills/typo/SKILL.md`: `--- +description: Unknown field. +metric_views: [orders] +--- + +Body.`, + // Invalid: directory name violates the naming rules + `skills/Bad_Name/SKILL.md`: `--- +description: Invalid name. +--- + +Body.`, + // Invalid: whitespace-only description + `skills/blank/SKILL.md`: `--- +description: " " +--- + +Body.`, + // Invalid: the closing delimiter must be exactly "---", so a horizontal rule doesn't close the front matter + `skills/unclosed/SKILL.md`: `--- +description: Unclosed. +---- + +Body.`, + // Valid: the description limit counts characters, not bytes + `skills/unicode/SKILL.md`: "---\ndescription: " + strings.Repeat("é", 1024) + "\n---\n\nBody.", + // Invalid: skills are only parsed from SKILL.md files, so a generic YAML file can't declare the kind + `skills/legacy.yaml`: ` +type: skill +description: Not a SKILL.md file. +`, + // Ignored: supporting files inside a skill directory are not project resources, even when they look like some + `skills/revenue-rca/example.sql`: `SELECT 1 AS revenue`, + `skills/revenue-rca/references/example.sql`: `SELECT 1 AS revenue`, + `.agents/skills/glossary/config.yaml`: `type: nonsense`, + `skills/revenue-rca/scripts/config.yaml`: "type: model\nsql: [not valid", + `.agents/skills/glossary/assets/terms.yaml`: `type: nonsense`, + // Ignored: SKILL.md files outside the skill roots are neither parsed nor reported + `.claude/skills/stray/SKILL.md`: `not a rill skill`, + `docs/SKILL.md`: `not a rill skill`, + `skills/nested/deeper/SKILL.md`: `not a rill skill`, + }) + + resources := []*Resource{ + { + Name: ResourceName{Kind: ResourceKindSkill, Name: "revenue-rca"}, + Paths: []string{"/skills/revenue-rca/SKILL.md"}, + Refs: []ResourceName{{Kind: ResourceKindMetricsView, Name: "orders"}}, + SkillSpec: &runtimev1.SkillSpec{ + Description: "Playbook for diagnosing revenue drops.", + Body: "# Revenue RCA playbook\nAlways break revenue down by country first.", + MetricsViews: []string{"orders"}, + Agents: []string{"analyst", "developer"}, + }, + }, + { + Name: ResourceName{Kind: ResourceKindSkill, Name: "unicode"}, + Paths: []string{"/skills/unicode/SKILL.md"}, + SkillSpec: &runtimev1.SkillSpec{ + Description: strings.Repeat("é", 1024), + Body: "Body.", + Agents: []string{"developer"}, + }, + }, + { + Name: ResourceName{Kind: ResourceKindSkill, Name: "glossary"}, + Paths: []string{"/.agents/skills/glossary/SKILL.md"}, + SkillSpec: &runtimev1.SkillSpec{ + Description: "Business glossary.", + Body: "ARPU excludes trial users.", + Agents: []string{"developer"}, + }, + }, + { + Name: ResourceName{Kind: ResourceKindSkill, Name: "formatting"}, + Paths: []string{"/skills/formatting/SKILL.md"}, + SkillSpec: &runtimev1.SkillSpec{ + Description: "House formatting rules.", + Body: "Report percentages with one decimal.", + Agents: []string{"developer"}, + AlwaysApply: true, + }, + }, + } + perrors := []*runtimev1.ParseError{ + {FilePath: "/skills/broken/SKILL.md", Message: `missing required front matter field "description"`}, + {FilePath: "/skills/empty/SKILL.md", Message: `missing required front matter field "description"`}, + {FilePath: "/skills/mismatch/SKILL.md", Message: `must match the skill's directory name`}, + {FilePath: "/skills/typo/SKILL.md", Message: "failed to parse front matter"}, + {FilePath: "/skills/Bad_Name/SKILL.md", Message: "invalid skill name"}, + {FilePath: "/skills/blank/SKILL.md", Message: `missing required front matter field "description"`}, + {FilePath: "/skills/unclosed/SKILL.md", Message: "unclosed front matter"}, + {FilePath: "/skills/legacy.yaml", Message: `invalid resource type "skill"`}, + } + + p, err := Parse(ctx, repo, "", "", "duckdb", true) + require.NoError(t, err) + requireResourcesAndErrors(t, p, resources, perrors) + + // Skill support files are skipped by incremental reparses too + require.True(t, p.IsSkippable("/skills/revenue-rca/references/example.sql")) + require.True(t, p.IsSkippable("/skills/revenue-rca/example.sql")) + require.True(t, p.IsSkippable("/.agents/skills/glossary/config.yaml")) + require.False(t, p.IsSkippable("/skills/revenue-rca/SKILL.md")) + require.False(t, p.IsSkippable("/skills/legacy.yaml")) + diff, err := p.Reparse(ctx, []string{"/skills/revenue-rca/references/example.sql"}) + require.NoError(t, err) + require.Empty(t, diff.Added) + require.Empty(t, diff.Modified) + + // Incremental reparse: edit a skill file + putRepo(t, repo, map[string]string{ + `skills/formatting/SKILL.md`: `--- +description: House formatting rules. +always_apply: true +--- + +Report percentages with two decimals.`, + }) + diff, err = p.Reparse(ctx, []string{"/skills/formatting/SKILL.md"}) + require.NoError(t, err) + require.Equal(t, []ResourceName{{Kind: ResourceKindSkill, Name: "formatting"}}, diff.Modified) + require.Equal(t, "Report percentages with two decimals.", p.Resources[ResourceName{Kind: ResourceKindSkill, Name: "formatting"}.Normalized()].SkillSpec.Body) + + // Incremental reparse: delete a skill file + deleteRepo(t, repo, "/skills/formatting/SKILL.md") + diff, err = p.Reparse(ctx, []string{"/skills/formatting/SKILL.md"}) + require.NoError(t, err) + require.Equal(t, []ResourceName{{Kind: ResourceKindSkill, Name: "formatting"}}, diff.Deleted) +} + +func TestPathIsSkillSupportFile(t *testing.T) { + tests := []struct { + path string + want bool + }{ + {"/skills/foo/example.sql", true}, + {"/skills/foo/references/example.sql", true}, + {"/.agents/skills/foo/config.yaml", true}, + {"/.agents/skills/foo/scripts/run.yaml", true}, + {"/skills/foo/SKILL.md", false}, + {"/.agents/skills/foo/SKILL.md", false}, + {"/skills/legacy.yaml", false}, + {"/.agents/skills/loose.yaml", false}, + {"/models/orders.sql", false}, + } + for _, tt := range tests { + require.Equal(t, tt.want, pathIsSkillSupportFile(tt.path), "path %q", tt.path) + } +} + +func TestSkillNameForPath(t *testing.T) { + tests := []struct { + path string + wantName string + wantOK bool + }{ + {"/skills/revenue-rca/SKILL.md", "revenue-rca", true}, + {"/.agents/skills/glossary/SKILL.md", "glossary", true}, + {"/skills/loose.md", "", false}, + {"/skills/glossary/notes.md", "", false}, + {"/skills/a/b/SKILL.md", "", false}, + {"/models/orders.md", "", false}, + {"/SKILL.md", "", false}, + } + for _, tt := range tests { + name, ok := skillNameForPath(tt.path) + require.Equal(t, tt.wantOK, ok, "path %q", tt.path) + require.Equal(t, tt.wantName, name, "path %q", tt.path) + } +} diff --git a/runtime/parser/parser.go b/runtime/parser/parser.go index dfde5ebb2df8..e654d5109889 100644 --- a/runtime/parser/parser.go +++ b/runtime/parser/parser.go @@ -55,6 +55,7 @@ type Resource struct { CanvasSpec *runtimev1.CanvasSpec APISpec *runtimev1.APISpec ConnectorSpec *runtimev1.ConnectorSpec + SkillSpec *runtimev1.SkillSpec } // ResourceName is a unique identifier for a resource @@ -91,6 +92,7 @@ const ( ResourceKindCanvas ResourceKindAPI ResourceKindConnector + ResourceKindSkill ) // ParseResourceKind maps a string to a ResourceKind. @@ -156,6 +158,8 @@ func (k ResourceKind) String() string { return "API" case ResourceKindConnector: return "Connector" + case ResourceKindSkill: + return "Skill" default: panic(fmt.Sprintf("unexpected resource type: %d", k)) } @@ -298,7 +302,7 @@ func (p *Parser) IsSkippable(path string) bool { if ok { return false } - return !pathIsYAML(path) && !pathIsSQL(path) && !pathIsDotEnv(path) + return !pathIsYAML(path) && !pathIsSQL(path) && !pathIsDotEnv(path) && !pathIsSkill(path) } // TrackedPathsInDir returns the paths under the given directory that the parser currently has cached results for. @@ -373,15 +377,28 @@ func (p *Parser) reload(ctx context.Context) error { p.updatedResources = nil p.deletedResources = nil - // Load entire repo + // Load entire repo. + // Skills are listed with a dedicated glob over the skill roots (see skillNameForPath) instead of adding "md" to the glob below, + // since matching every markdown file, or every SKILL.md, in the repo would count unrelated files against drivers.RepoListLimit and maxFiles. files, err := p.Repo.ListGlob(ctx, "**/*.{env,sql,yaml,yml}", true) if err != nil { return fmt.Errorf("could not list project files: %w", err) } + skillFiles, err := p.Repo.ListGlob(ctx, "{skills,.agents/skills}/*/SKILL.md", true) + if err != nil { + return fmt.Errorf("could not list project skill files: %w", err) + } // Build paths slice - paths := make([]string, 0, len(files)) + paths := make([]string, 0, len(files)+len(skillFiles)) for _, file := range files { + // Skill support files match the glob but are not project resources. + if pathIsIgnored(file.Path) { + continue + } + paths = append(paths, file.Path) + } + for _, file := range skillFiles { paths = append(paths, file.Path) } @@ -460,7 +477,8 @@ func (p *Parser) reparseExceptRillYAML(ctx context.Context, paths []string) (*Di isSQL := pathIsSQL(path) isYAML := pathIsYAML(path) isDotEnv := pathIsDotEnv(path) - if !isSQL && !isYAML && !isDotEnv { + isSkill := pathIsSkill(path) + if !isSQL && !isYAML && !isDotEnv && !isSkill { continue } @@ -642,6 +660,17 @@ func (p *Parser) parsePaths(ctx context.Context, paths []string) error { } i++ continue + } else if pathIsMarkdown(path) { + // Markdown files don't participate in the SQL/YAML stem machinery below. + // The only markdown files the parser understands are skill files; other markdown files are ignored. + if pathIsSkill(path) { + err := p.parseSkill(ctx, path) + if err != nil { + p.addParseError(path, err, false) + } + } + i++ + continue } // Identify the range of paths with the same stem as paths[i] @@ -910,6 +939,8 @@ func (p *Parser) insertResource(kind ResourceKind, name string, paths, tags []st r.APISpec = &runtimev1.APISpec{} case ResourceKindConnector: r.ConnectorSpec = &runtimev1.ConnectorSpec{} + case ResourceKindSkill: + r.SkillSpec = &runtimev1.SkillSpec{} default: panic(fmt.Errorf("unexpected resource type: %s", kind.String())) } @@ -1106,6 +1137,11 @@ func pathIsYAML(path string) bool { return strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml") } +// pathIsMarkdown returns true if the path is a markdown file +func pathIsMarkdown(path string) bool { + return strings.HasSuffix(path, ".md") +} + // pathIsRillYAML returns true if the path is rill.yaml func pathIsRillYAML(path string) bool { return path == "/rill.yaml" || path == "/rill.yml" @@ -1127,7 +1163,7 @@ func pathIsIgnored(p string) bool { return true } } - return false + return pathIsSkillSupportFile(p) } // normalizePath normalizes a user-provided path to the format returned from ListGlob. diff --git a/runtime/parser/parser_test.go b/runtime/parser/parser_test.go index 9a8362c50b4b..651c75bbdc47 100644 --- a/runtime/parser/parser_test.go +++ b/runtime/parser/parser_test.go @@ -3065,6 +3065,7 @@ func requireResourcesAndErrors(t testing.TB, p *Parser, wantResources []*Resourc require.Equal(t, want.CanvasSpec, got.CanvasSpec, "for resource %q", want.Name) require.Equal(t, want.APISpec, got.APISpec, "for resource %q", want.Name) require.Equal(t, want.ConnectorSpec, got.ConnectorSpec, "for resource %q", want.Name) + require.Equal(t, want.SkillSpec, got.SkillSpec, "for resource %q", want.Name) delete(gotResources, got.Name) found = true diff --git a/runtime/reconcilers/project_parser.go b/runtime/reconcilers/project_parser.go index 1f040b11d1c0..522a7ce3502d 100644 --- a/runtime/reconcilers/project_parser.go +++ b/runtime/reconcilers/project_parser.go @@ -736,6 +736,10 @@ func newResourceIfModified(def *parserpkg.Resource, existing *runtimev1.Resource if existing == nil || !proto.Equal(existing.GetConnector().Spec, def.ConnectorSpec) { return &runtimev1.Resource{Resource: &runtimev1.Resource_Connector{Connector: &runtimev1.ConnectorV2{Spec: def.ConnectorSpec}}} } + case parserpkg.ResourceKindSkill: + if existing == nil || !proto.Equal(existing.GetSkill().Spec, def.SkillSpec) { + return &runtimev1.Resource{Resource: &runtimev1.Resource_Skill{Skill: &runtimev1.Skill{Spec: def.SkillSpec}}} + } default: panic(fmt.Errorf("unknown resource type %q", def.Name.Kind)) } diff --git a/runtime/reconcilers/skill.go b/runtime/reconcilers/skill.go new file mode 100644 index 000000000000..562fef3996e7 --- /dev/null +++ b/runtime/reconcilers/skill.go @@ -0,0 +1,89 @@ +package reconcilers + +import ( + "context" + "errors" + "fmt" + + runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1" + "github.com/rilldata/rill/runtime" + "github.com/rilldata/rill/runtime/drivers" +) + +func init() { + runtime.RegisterReconcilerInitializer(runtime.ResourceKindSkill, newSkillReconciler) +} + +type SkillReconciler struct { + C *runtime.Controller +} + +func newSkillReconciler(ctx context.Context, c *runtime.Controller) (runtime.Reconciler, error) { + return &SkillReconciler{C: c}, nil +} + +func (r *SkillReconciler) Close(ctx context.Context) error { + return nil +} + +func (r *SkillReconciler) AssignSpec(from, to *runtimev1.Resource) error { + a := from.GetSkill() + b := to.GetSkill() + if a == nil || b == nil { + return fmt.Errorf("cannot assign spec from %T to %T", from.Resource, to.Resource) + } + b.Spec = a.Spec + return nil +} + +func (r *SkillReconciler) AssignState(from, to *runtimev1.Resource) error { + a := from.GetSkill() + b := to.GetSkill() + if a == nil || b == nil { + return fmt.Errorf("cannot assign state from %T to %T", from.Resource, to.Resource) + } + b.State = a.State + return nil +} + +func (r *SkillReconciler) ResetState(res *runtimev1.Resource) error { + return nil +} + +func (r *SkillReconciler) Reconcile(ctx context.Context, n *runtimev1.ResourceName) runtime.ReconcileResult { + self, err := r.C.Get(ctx, n, true) + if err != nil { + return runtime.ReconcileResult{Err: err} + } + s := self.GetSkill() + if s == nil { + return runtime.ReconcileResult{Err: errors.New("not a skill")} + } + + // Exit early for deletion + if self.Meta.DeletedOn != nil { + return runtime.ReconcileResult{} + } + + // Check that the metrics views the skill is scoped to exist. + // We deliberately don't use checkRefs here: scoping is a relevance filter, not a hard dependency, + // so a skill should stay valid while a referenced metrics view is refreshing or has a transient error. + for _, mv := range s.Spec.MetricsViews { + _, err := r.C.Get(ctx, &runtimev1.ResourceName{Kind: runtime.ResourceKindMetricsView, Name: mv}, false) + if err != nil { + if errors.Is(err, drivers.ErrResourceNotFound) { + return runtime.ReconcileResult{Err: fmt.Errorf("metrics view %q referenced in metrics_views not found", mv)} + } + return runtime.ReconcileResult{Err: err} + } + } + + return runtime.ReconcileResult{} +} + +func (r *SkillReconciler) ResolveTransitiveAccess(ctx context.Context, claims *runtime.SecurityClaims, res *runtimev1.Resource) ([]*runtimev1.SecurityRule, error) { + if res.GetSkill() == nil { + return nil, fmt.Errorf("not a skill resource") + } + return []*runtimev1.SecurityRule{{Rule: runtime.SelfAllowRuleAccess(res)}}, nil +} diff --git a/runtime/resources.go b/runtime/resources.go index 228ecff7f802..52bc70e48adf 100644 --- a/runtime/resources.go +++ b/runtime/resources.go @@ -26,6 +26,7 @@ const ( ResourceKindCanvas string = "rill.runtime.v1.Canvas" ResourceKindAPI string = "rill.runtime.v1.API" ResourceKindConnector string = "rill.runtime.v1.Connector" + ResourceKindSkill string = "rill.runtime.v1.Skill" ) // ResourceKindFromPretty converts a user-friendly resource kind to a runtime resource kind. @@ -60,6 +61,8 @@ func ResourceKindFromShorthand(kind string) string { return ResourceKindAPI case "connector": return ResourceKindConnector + case "skill": + return ResourceKindSkill default: return kind } @@ -80,7 +83,8 @@ func IsKnownResourceKind(kind string) bool { ResourceKindComponent, ResourceKindCanvas, ResourceKindAPI, - ResourceKindConnector: + ResourceKindConnector, + ResourceKindSkill: return true default: return false @@ -114,6 +118,8 @@ func ResourceKindFromParser(kind parser.ResourceKind) string { return ResourceKindAPI case parser.ResourceKindConnector: return ResourceKindConnector + case parser.ResourceKindSkill: + return ResourceKindSkill default: panic(fmt.Errorf("unknown parser resource type %q", kind)) } @@ -146,6 +152,8 @@ func ResourceKindToParser(kind string) parser.ResourceKind { return parser.ResourceKindAPI case ResourceKindConnector: return parser.ResourceKindConnector + case ResourceKindSkill: + return parser.ResourceKindSkill case ResourceKindProjectParser, ResourceKindRefreshTrigger: panic(fmt.Errorf("unsupported resource type %q", kind)) default: diff --git a/runtime/security.go b/runtime/security.go index 7cda3ccd74d5..e12e961262f6 100644 --- a/runtime/security.go +++ b/runtime/security.go @@ -420,6 +420,12 @@ func (p *securityEngine) resolveRules(claims *SecurityClaims, rules []*runtimev1 // Everyone can access a theme. case ResourceKindTheme: rules = append(rules, allowAccessRule) + // Everyone who can use AI features can access a skill, since skills are instructions served to the AI on their behalf. + // This means skills must never contain secrets: they are visible to viewers, not just editors. + case ResourceKindSkill: + if claims.Can(UseAI) { + rules = append(rules, allowAccessRule) + } // All other resources can only be accessed by admins. default: if claims.Admin() { diff --git a/runtime/server/mcp_test.go b/runtime/server/mcp_test.go index 5d842ab9a67e..3df36818a864 100644 --- a/runtime/server/mcp_test.go +++ b/runtime/server/mcp_test.go @@ -2,10 +2,12 @@ package server import ( "context" + "net/http" "net/http/httptest" "testing" "github.com/modelcontextprotocol/go-sdk/mcp" + "github.com/rilldata/rill/runtime" "github.com/rilldata/rill/runtime/ai" "github.com/rilldata/rill/runtime/pkg/activity" "github.com/rilldata/rill/runtime/pkg/ratelimit" @@ -67,6 +69,8 @@ explore: // jwt, err := auth.NewDevToken(nil, []runtime.Permission{runtime.ReadObjects, runtime.ReadMetrics, runtime.UseAI}) // require.NoError(t, err) + require.Contains(t, conn.InitializeResult().Instructions, "## Skills") + // Test tool listings tools, err := conn.ListTools(t.Context(), &mcp.ListToolsParams{}) require.NoError(t, err) @@ -160,3 +164,58 @@ explore: }) require.ErrorContains(t, err, `want "object"`) } + +// TestMCPSkillTools asserts that the skill tools are only advertised when the project defines skills +// (TestMCP covers a project without skills) and the caller can use AI. +func TestMCPSkillTools(t *testing.T) { + rt, instanceID := testruntime.NewInstanceWithOptions(t, testruntime.InstanceOptions{ + Files: map[string]string{ + "rill.yaml": "", + "skills/glossary/SKILL.md": `--- +description: Business glossary. +--- + +ARPU excludes trial users.`, + }, + }) + testruntime.RequireReconcileState(t, rt, instanceID, 2, 0, 0) + + srv, err := NewServer(context.Background(), &Options{}, rt, zap.NewNop(), ratelimit.NewNoop(), activity.NewNoopClient()) + require.NoError(t, err) + + httpSrv := httptest.NewServer(auth.HTTPMiddleware(srv.aud, srv.mcpHandler())) + defer httpSrv.Close() + + mcpClient := mcp.NewClient(&mcp.Implementation{Name: "mcp/test", Version: "1.0.0"}, nil) + conn, err := mcpClient.Connect(t.Context(), &mcp.StreamableClientTransport{Endpoint: httpSrv.URL}, nil) + require.NoError(t, err) + defer conn.Close() + + toolNames := func(t *testing.T, conn *mcp.ClientSession) []string { + tools, err := conn.ListTools(t.Context(), &mcp.ListToolsParams{}) + require.NoError(t, err) + var names []string + for _, tool := range tools.Tools { + names = append(names, tool.Name) + } + return names + } + require.Subset(t, toolNames(t, conn), []string{ai.ListSkillsName, ai.LoadSkillName}) + + // A client without UseAI is not offered the skill tools + token, err := auth.NewDevToken(nil, []runtime.Permission{runtime.ReadObjects, runtime.ReadMetrics}) + require.NoError(t, err) + httpClient := &http.Client{Transport: roundTripperFunc(func(req *http.Request) (*http.Response, error) { + req.Header.Set("Authorization", "Bearer "+token) + return http.DefaultTransport.RoundTrip(req) + })} + conn, err = mcpClient.Connect(t.Context(), &mcp.StreamableClientTransport{Endpoint: httpSrv.URL, HTTPClient: httpClient}, nil) + require.NoError(t, err) + defer conn.Close() + require.NotSubset(t, toolNames(t, conn), []string{ai.ListSkillsName}) + require.NotSubset(t, toolNames(t, conn), []string{ai.LoadSkillName}) +} + +type roundTripperFunc func(*http.Request) (*http.Response, error) + +func (f roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) } diff --git a/web-common/src/features/chat/core/messages/tools/tool-icons.ts b/web-common/src/features/chat/core/messages/tools/tool-icons.ts index b52803f894b4..294631a4fa35 100644 --- a/web-common/src/features/chat/core/messages/tools/tool-icons.ts +++ b/web-common/src/features/chat/core/messages/tools/tool-icons.ts @@ -9,6 +9,7 @@ import { FolderTree, Pencil, CornerDownRight, + GraduationCap, } from "lucide-svelte"; import type { ComponentType } from "svelte"; import Chart from "../../../../../components/icons/Chart.svelte"; @@ -37,6 +38,8 @@ export const TOOL_ICONS: Record = { // Common tools [ToolName.NAVIGATE]: CornerDownRight, + [ToolName.LIST_SKILLS]: GraduationCap, + [ToolName.LOAD_SKILL]: GraduationCap, }; /** diff --git a/web-common/src/features/chat/core/types.ts b/web-common/src/features/chat/core/types.ts index 17bfe399ce02..bd9e96fe5dcb 100644 --- a/web-common/src/features/chat/core/types.ts +++ b/web-common/src/features/chat/core/types.ts @@ -58,6 +58,8 @@ export const ToolName = { // Common tools NAVIGATE: "navigate", + LIST_SKILLS: "list_skills", + LOAD_SKILL: "load_skill", } as const; // ============================================================================= diff --git a/web-common/src/features/entity-management/add/AddAssetButton.svelte b/web-common/src/features/entity-management/add/AddAssetButton.svelte index cb13eab6e678..a9edd156cacd 100644 --- a/web-common/src/features/entity-management/add/AddAssetButton.svelte +++ b/web-common/src/features/entity-management/add/AddAssetButton.svelte @@ -5,6 +5,7 @@ Database, File, Folder, + GraduationCap, PlusCircleIcon, Wand, } from "lucide-svelte"; @@ -27,7 +28,7 @@ import { useRuntimeClient } from "../../../runtime-client/v2"; import { useIsModelingSupportedForDefaultOlapDriverOLAP as useIsModelingSupportedForDefaultOlapDriver } from "../../connectors/selectors.ts"; import { directoryState } from "../../file-explorer/directory-store.ts"; - import { createResourceAndNavigate } from "./new-files.ts"; + import { createResourceAndNavigate, skillFileTemplate } from "./new-files.ts"; import AddAiConnectorDialog from "../../connectors/ai/AddAiConnectorDialog.svelte"; import CreateExploreDialog from "./CreateExploreDialog.svelte"; import { removeLeadingSlash } from "../entity-mappers.ts"; @@ -73,6 +74,16 @@ currentDirectory, ); + // Skills are keyed by name across both supported roots, so a new skill must not collide with either + $: skillDirectoryNamesQuery = useDirectoryNamesInDirectory( + runtimeClient, + "skills", + ); + $: agentsSkillDirectoryNamesQuery = useDirectoryNamesInDirectory( + runtimeClient, + ".agents/skills", + ); + $: isModelingSupportedForDefaultOlapDriver = useIsModelingSupportedForDefaultOlapDriver(runtimeClient); $: isModelingSupported = $isModelingSupportedForDefaultOlapDriver.data; @@ -139,6 +150,33 @@ await navigateToFile(`/${path}`); } + + /** + * Put a skill file (markdown instructions for the AI agents) in the skills directory + */ + async function handleAddSkill() { + // Skill names only allow lowercase letters, numbers and hyphens, so we can't use getName, which appends "_N" suffixes + const existingNames = new Set( + [ + ...($skillDirectoryNamesQuery?.data ?? []), + ...($agentsSkillDirectoryNamesQuery?.data ?? []), + ].map((n) => n.toLowerCase()), + ); + let name = "my-skill"; + for (let i = 1; existingNames.has(name); i++) { + name = `my-skill-${i}`; + } + const path = `skills/${name}/SKILL.md`; + + await $createFile.mutateAsync({ + path, + blob: skillFileTemplate(name), + create: true, + createOnly: true, + }); + + await navigateToFile(`/${path}`); + } @@ -296,6 +334,11 @@ /> Theme + + + + AI Skill +