From 583e3b54053cea5febb8920ee69de40812f0f78d Mon Sep 17 00:00:00 2001 From: mintaka Date: Mon, 31 Aug 2026 13:59:42 -0400 Subject: [PATCH 1/4] docs(server): ManagerResolver nearest-ancestor contract + proto role-comment sweep (RIG-3080) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Taxonomy T7 from the frozen manager-role-taxonomy record (RIG-3066). Two doc/spec surfaces drift under the three-role taxonomy and are corrected here before an implementer codes against them; both are comment-only and regen-neutral. - `go/internal/linearagent/routing.go`: the `ManagerResolver` seam doc said the tree walk goes "up parent_agent_id to a role=\"manager\" agent". Under the taxonomy that is wrong twice — a `manager`-only filter skips an `owner` parent (routing a domain-owned artifact past its owning tier), and every tree node is now Manager-class so the filter is vestigial. Reworded to the nearest tree ancestor (any Manager-class role). Only the interface + test fakes exist today (`NewResolver` is called only from tests), so this prevents a future implementer hardcoding `role==\"manager\"`. - `proto/compass/v1/compass.proto`: the `role` field comment said "operator-set role". After T1 the label is spawner-selected from the closed taxonomy and server-validated; the provision field stays server-authoritative (populated from the stored role, overwrites client-supplied). Swept the comment to match the T1 invariant. Verified: gofmt clean; go build ./internal/linearagent/... exit 0; go test ./internal/linearagent/... ok (routing fakes green); proto diff is comment-only (regen-neutral, confirmed no non-comment line changed). Refs RIG-3080 Co-authored-by: Matt Wilkinson jj-hooks: autofixes for compass-server/rig-3080-managerresolver-contract --- go/gen/compass/v1/compass.pb.go | 21 ++++++++++++------- go/internal/linearagent/routing.go | 4 +++- .../src/gen/compass/v1/compass_pb.ts | 21 ++++++++++++------- .../src/gen/compass/v1/compass_pb.ts | 21 ++++++++++++------- proto/compass/v1/compass.proto | 21 ++++++++++++------- 5 files changed, 55 insertions(+), 33 deletions(-) diff --git a/go/gen/compass/v1/compass.pb.go b/go/gen/compass/v1/compass.pb.go index 166f14dc8..99897e31c 100644 --- a/go/gen/compass/v1/compass.pb.go +++ b/go/gen/compass/v1/compass.pb.go @@ -2704,14 +2704,19 @@ type ProvisionAgentWorkspaceRequest struct { // it into the container // (compass-runner consumer). Empty = no persona baked (default). Persona string `protobuf:"bytes,3,opt,name=persona,proto3" json:"persona,omitempty"` - // The agent's operator-set role, selecting the container's block-0 system - // prompt at provision so it survives compaction (a system-prompt config block - // is not part of the message history a snapcompact archives). SERVER- - // AUTHORITATIVE: the Server is expected to populate this by reading - // AgentAccount.role from the store on the provision path and to overwrite any - // client-supplied value, so a caller cannot inject a role prompt — an - // invariant enforced by the server provision path (not by this wire-settable - // field). Where persona (field 3) is an APPEND overlay, role REPLACES block-0: + // The agent's role, selected at spawn from the closed taxonomy + // (supervisor/owner/manager) and server-validated, selecting the container's + // block-0 system prompt at provision so it survives compaction (a + // system-prompt config block is not part of the message history a snapcompact + // archives). The label's ORIGIN is spawner-selected: the spawn request + // carries a required label from the closed taxonomy, validated against the + // server's spawnableRoles set (CodeInvalidArgument on an + // unknown label) before it is stored. This provision field stays SERVER- + // AUTHORITATIVE: the Server populates it by reading the stored + // AgentAccount.role (the provision source of record) and overwrites any + // client-supplied value, so a caller cannot inject a role prompt via + // provision — prompt TEXT ships only in the operator-published config bundle. + // Where persona (field 3) is an APPEND overlay, role REPLACES block-0: // the label selects config/prompts//SYSTEM.md, materialized by the // Runner into the container's customSystemPrompt (compass-runner consumer). // Empty = no role (default OMP block-0). diff --git a/go/internal/linearagent/routing.go b/go/internal/linearagent/routing.go index 390c73217..9a184c446 100644 --- a/go/internal/linearagent/routing.go +++ b/go/internal/linearagent/routing.go @@ -46,7 +46,9 @@ type OwnershipIndex interface { // ManagerResolver walks a recorded authoring agent (possibly a peer) to its // owning Manager and returns that Manager's account id and home channel id. // It is its own narrow seam because no single store method spans the tree walk -// (up parent_agent_id to a role="manager" agent) AND the home-channel read; the +// (up parent_agent_id to the nearest tree ancestor — any Manager-class role, +// not a role=="manager" filter, since every tree node is now Manager-class and +// an owner parent must not be skipped) AND the home-channel read; the // driver backs it with the store's agent-tree + account reads at assembly. // store.ErrNotFound when the agent (or a walk ancestor) does not resolve. type ManagerResolver interface { diff --git a/packages/compass-agent/src/gen/compass/v1/compass_pb.ts b/packages/compass-agent/src/gen/compass/v1/compass_pb.ts index 810bbd111..d5e6b8475 100644 --- a/packages/compass-agent/src/gen/compass/v1/compass_pb.ts +++ b/packages/compass-agent/src/gen/compass/v1/compass_pb.ts @@ -1100,14 +1100,19 @@ export type ProvisionAgentWorkspaceRequest = Message<"compass.v1.ProvisionAgentW persona: string; /** - * The agent's operator-set role, selecting the container's block-0 system - * prompt at provision so it survives compaction (a system-prompt config block - * is not part of the message history a snapcompact archives). SERVER- - * AUTHORITATIVE: the Server is expected to populate this by reading - * AgentAccount.role from the store on the provision path and to overwrite any - * client-supplied value, so a caller cannot inject a role prompt — an - * invariant enforced by the server provision path (not by this wire-settable - * field). Where persona (field 3) is an APPEND overlay, role REPLACES block-0: + * The agent's role, selected at spawn from the closed taxonomy + * (supervisor/owner/manager) and server-validated, selecting the container's + * block-0 system prompt at provision so it survives compaction (a + * system-prompt config block is not part of the message history a snapcompact + * archives). The label's ORIGIN is spawner-selected: the spawn request + * carries a required label from the closed taxonomy, validated against the + * server's spawnableRoles set (CodeInvalidArgument on an + * unknown label) before it is stored. This provision field stays SERVER- + * AUTHORITATIVE: the Server populates it by reading the stored + * AgentAccount.role (the provision source of record) and overwrites any + * client-supplied value, so a caller cannot inject a role prompt via + * provision — prompt TEXT ships only in the operator-published config bundle. + * Where persona (field 3) is an APPEND overlay, role REPLACES block-0: * the label selects config/prompts//SYSTEM.md, materialized by the * Runner into the container's customSystemPrompt (compass-runner consumer). * Empty = no role (default OMP block-0). diff --git a/packages/compass-client/src/gen/compass/v1/compass_pb.ts b/packages/compass-client/src/gen/compass/v1/compass_pb.ts index 810bbd111..d5e6b8475 100644 --- a/packages/compass-client/src/gen/compass/v1/compass_pb.ts +++ b/packages/compass-client/src/gen/compass/v1/compass_pb.ts @@ -1100,14 +1100,19 @@ export type ProvisionAgentWorkspaceRequest = Message<"compass.v1.ProvisionAgentW persona: string; /** - * The agent's operator-set role, selecting the container's block-0 system - * prompt at provision so it survives compaction (a system-prompt config block - * is not part of the message history a snapcompact archives). SERVER- - * AUTHORITATIVE: the Server is expected to populate this by reading - * AgentAccount.role from the store on the provision path and to overwrite any - * client-supplied value, so a caller cannot inject a role prompt — an - * invariant enforced by the server provision path (not by this wire-settable - * field). Where persona (field 3) is an APPEND overlay, role REPLACES block-0: + * The agent's role, selected at spawn from the closed taxonomy + * (supervisor/owner/manager) and server-validated, selecting the container's + * block-0 system prompt at provision so it survives compaction (a + * system-prompt config block is not part of the message history a snapcompact + * archives). The label's ORIGIN is spawner-selected: the spawn request + * carries a required label from the closed taxonomy, validated against the + * server's spawnableRoles set (CodeInvalidArgument on an + * unknown label) before it is stored. This provision field stays SERVER- + * AUTHORITATIVE: the Server populates it by reading the stored + * AgentAccount.role (the provision source of record) and overwrites any + * client-supplied value, so a caller cannot inject a role prompt via + * provision — prompt TEXT ships only in the operator-published config bundle. + * Where persona (field 3) is an APPEND overlay, role REPLACES block-0: * the label selects config/prompts//SYSTEM.md, materialized by the * Runner into the container's customSystemPrompt (compass-runner consumer). * Empty = no role (default OMP block-0). diff --git a/proto/compass/v1/compass.proto b/proto/compass/v1/compass.proto index 129bdc962..6e02c8e70 100644 --- a/proto/compass/v1/compass.proto +++ b/proto/compass/v1/compass.proto @@ -596,14 +596,19 @@ message ProvisionAgentWorkspaceRequest { // it into the container // (compass-runner consumer). Empty = no persona baked (default). string persona = 3; - // The agent's operator-set role, selecting the container's block-0 system - // prompt at provision so it survives compaction (a system-prompt config block - // is not part of the message history a snapcompact archives). SERVER- - // AUTHORITATIVE: the Server is expected to populate this by reading - // AgentAccount.role from the store on the provision path and to overwrite any - // client-supplied value, so a caller cannot inject a role prompt — an - // invariant enforced by the server provision path (not by this wire-settable - // field). Where persona (field 3) is an APPEND overlay, role REPLACES block-0: + // The agent's role, selected at spawn from the closed taxonomy + // (supervisor/owner/manager) and server-validated, selecting the container's + // block-0 system prompt at provision so it survives compaction (a + // system-prompt config block is not part of the message history a snapcompact + // archives). The label's ORIGIN is spawner-selected: the spawn request + // carries a required label from the closed taxonomy, validated against the + // server's spawnableRoles set (CodeInvalidArgument on an + // unknown label) before it is stored. This provision field stays SERVER- + // AUTHORITATIVE: the Server populates it by reading the stored + // AgentAccount.role (the provision source of record) and overwrites any + // client-supplied value, so a caller cannot inject a role prompt via + // provision — prompt TEXT ships only in the operator-published config bundle. + // Where persona (field 3) is an APPEND overlay, role REPLACES block-0: // the label selects config/prompts//SYSTEM.md, materialized by the // Runner into the container's customSystemPrompt (compass-runner consumer). // Empty = no role (default OMP block-0). From 48a26e3f3acc4172fee11f087d0bcbcdb27b76ce Mon Sep 17 00:00:00 2001 From: mintaka Date: Mon, 31 Aug 2026 15:57:27 -0400 Subject: [PATCH 2/4] docs(proto): polish role field comment wording (RIG-3080) Review-loop polish on the ProvisionAgentWorkspaceRequest.role doc comment (three low-severity findings): reflow so "INVALID_ARGUMENT on an unknown label" is not split mid-phrase; use the Connect-canonical INVALID_ARGUMENT spelling instead of the Go-flavored CodeInvalidArgument (the comment generates into both Go and TS clients); and spell out that a role is required at spawn while the provision wire field stays optional (an empty stored role yields the default block-0), reconciling with the trailing "empty" clause. Comment-only + regen across all gen trees; no wire/field change. Refs RIG-3066 Co-authored-by: Matt Wilkinson --- go/gen/compass/v1/compass.pb.go | 21 ++++++++++--------- .../src/gen/compass/v1/compass_pb.ts | 21 ++++++++++--------- .../src/gen/compass/v1/compass_pb.ts | 21 ++++++++++--------- proto/compass/v1/compass.proto | 21 ++++++++++--------- 4 files changed, 44 insertions(+), 40 deletions(-) diff --git a/go/gen/compass/v1/compass.pb.go b/go/gen/compass/v1/compass.pb.go index 99897e31c..ec5dbf5e4 100644 --- a/go/gen/compass/v1/compass.pb.go +++ b/go/gen/compass/v1/compass.pb.go @@ -2710,16 +2710,17 @@ type ProvisionAgentWorkspaceRequest struct { // system-prompt config block is not part of the message history a snapcompact // archives). The label's ORIGIN is spawner-selected: the spawn request // carries a required label from the closed taxonomy, validated against the - // server's spawnableRoles set (CodeInvalidArgument on an - // unknown label) before it is stored. This provision field stays SERVER- - // AUTHORITATIVE: the Server populates it by reading the stored - // AgentAccount.role (the provision source of record) and overwrites any - // client-supplied value, so a caller cannot inject a role prompt via - // provision — prompt TEXT ships only in the operator-published config bundle. - // Where persona (field 3) is an APPEND overlay, role REPLACES block-0: - // the label selects config/prompts//SYSTEM.md, materialized by the - // Runner into the container's customSystemPrompt (compass-runner consumer). - // Empty = no role (default OMP block-0). + // server's spawnableRoles set (INVALID_ARGUMENT on an unknown label) before + // it is stored. This provision field stays SERVER-AUTHORITATIVE: the Server + // populates it by reading the stored AgentAccount.role (the provision source + // of record) and overwrites any client-supplied value, so a caller cannot + // inject a role prompt via provision — prompt TEXT ships only in the + // operator-published config bundle. Where persona (field 3) is an APPEND + // overlay, role REPLACES block-0: the label selects + // config/prompts//SYSTEM.md, materialized by the Runner into the + // container's customSystemPrompt (compass-runner consumer). A role is + // required at spawn; this provision field is optional and an empty stored + // role yields the default OMP block-0. Role string `protobuf:"bytes,4,opt,name=role,proto3" json:"role,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache diff --git a/packages/compass-agent/src/gen/compass/v1/compass_pb.ts b/packages/compass-agent/src/gen/compass/v1/compass_pb.ts index d5e6b8475..932b67f33 100644 --- a/packages/compass-agent/src/gen/compass/v1/compass_pb.ts +++ b/packages/compass-agent/src/gen/compass/v1/compass_pb.ts @@ -1106,16 +1106,17 @@ export type ProvisionAgentWorkspaceRequest = Message<"compass.v1.ProvisionAgentW * system-prompt config block is not part of the message history a snapcompact * archives). The label's ORIGIN is spawner-selected: the spawn request * carries a required label from the closed taxonomy, validated against the - * server's spawnableRoles set (CodeInvalidArgument on an - * unknown label) before it is stored. This provision field stays SERVER- - * AUTHORITATIVE: the Server populates it by reading the stored - * AgentAccount.role (the provision source of record) and overwrites any - * client-supplied value, so a caller cannot inject a role prompt via - * provision — prompt TEXT ships only in the operator-published config bundle. - * Where persona (field 3) is an APPEND overlay, role REPLACES block-0: - * the label selects config/prompts//SYSTEM.md, materialized by the - * Runner into the container's customSystemPrompt (compass-runner consumer). - * Empty = no role (default OMP block-0). + * server's spawnableRoles set (INVALID_ARGUMENT on an unknown label) before + * it is stored. This provision field stays SERVER-AUTHORITATIVE: the Server + * populates it by reading the stored AgentAccount.role (the provision source + * of record) and overwrites any client-supplied value, so a caller cannot + * inject a role prompt via provision — prompt TEXT ships only in the + * operator-published config bundle. Where persona (field 3) is an APPEND + * overlay, role REPLACES block-0: the label selects + * config/prompts//SYSTEM.md, materialized by the Runner into the + * container's customSystemPrompt (compass-runner consumer). A role is + * required at spawn; this provision field is optional and an empty stored + * role yields the default OMP block-0. * * @generated from field: string role = 4; */ diff --git a/packages/compass-client/src/gen/compass/v1/compass_pb.ts b/packages/compass-client/src/gen/compass/v1/compass_pb.ts index d5e6b8475..932b67f33 100644 --- a/packages/compass-client/src/gen/compass/v1/compass_pb.ts +++ b/packages/compass-client/src/gen/compass/v1/compass_pb.ts @@ -1106,16 +1106,17 @@ export type ProvisionAgentWorkspaceRequest = Message<"compass.v1.ProvisionAgentW * system-prompt config block is not part of the message history a snapcompact * archives). The label's ORIGIN is spawner-selected: the spawn request * carries a required label from the closed taxonomy, validated against the - * server's spawnableRoles set (CodeInvalidArgument on an - * unknown label) before it is stored. This provision field stays SERVER- - * AUTHORITATIVE: the Server populates it by reading the stored - * AgentAccount.role (the provision source of record) and overwrites any - * client-supplied value, so a caller cannot inject a role prompt via - * provision — prompt TEXT ships only in the operator-published config bundle. - * Where persona (field 3) is an APPEND overlay, role REPLACES block-0: - * the label selects config/prompts//SYSTEM.md, materialized by the - * Runner into the container's customSystemPrompt (compass-runner consumer). - * Empty = no role (default OMP block-0). + * server's spawnableRoles set (INVALID_ARGUMENT on an unknown label) before + * it is stored. This provision field stays SERVER-AUTHORITATIVE: the Server + * populates it by reading the stored AgentAccount.role (the provision source + * of record) and overwrites any client-supplied value, so a caller cannot + * inject a role prompt via provision — prompt TEXT ships only in the + * operator-published config bundle. Where persona (field 3) is an APPEND + * overlay, role REPLACES block-0: the label selects + * config/prompts//SYSTEM.md, materialized by the Runner into the + * container's customSystemPrompt (compass-runner consumer). A role is + * required at spawn; this provision field is optional and an empty stored + * role yields the default OMP block-0. * * @generated from field: string role = 4; */ diff --git a/proto/compass/v1/compass.proto b/proto/compass/v1/compass.proto index 6e02c8e70..e9906343b 100644 --- a/proto/compass/v1/compass.proto +++ b/proto/compass/v1/compass.proto @@ -602,16 +602,17 @@ message ProvisionAgentWorkspaceRequest { // system-prompt config block is not part of the message history a snapcompact // archives). The label's ORIGIN is spawner-selected: the spawn request // carries a required label from the closed taxonomy, validated against the - // server's spawnableRoles set (CodeInvalidArgument on an - // unknown label) before it is stored. This provision field stays SERVER- - // AUTHORITATIVE: the Server populates it by reading the stored - // AgentAccount.role (the provision source of record) and overwrites any - // client-supplied value, so a caller cannot inject a role prompt via - // provision — prompt TEXT ships only in the operator-published config bundle. - // Where persona (field 3) is an APPEND overlay, role REPLACES block-0: - // the label selects config/prompts//SYSTEM.md, materialized by the - // Runner into the container's customSystemPrompt (compass-runner consumer). - // Empty = no role (default OMP block-0). + // server's spawnableRoles set (INVALID_ARGUMENT on an unknown label) before + // it is stored. This provision field stays SERVER-AUTHORITATIVE: the Server + // populates it by reading the stored AgentAccount.role (the provision source + // of record) and overwrites any client-supplied value, so a caller cannot + // inject a role prompt via provision — prompt TEXT ships only in the + // operator-published config bundle. Where persona (field 3) is an APPEND + // overlay, role REPLACES block-0: the label selects + // config/prompts//SYSTEM.md, materialized by the Runner into the + // container's customSystemPrompt (compass-runner consumer). A role is + // required at spawn; this provision field is optional and an empty stored + // role yields the default OMP block-0. string role = 4; } From d3be19db541faddb158c77708e95f8167de955cb Mon Sep 17 00:00:00 2001 From: mintaka Date: Mon, 31 Aug 2026 14:18:49 -0400 Subject: [PATCH 3/4] feat(config): admit prompts//SYSTEM.md + expose Prompts info bucket (RIG-3075) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `prompts/` as a whitelisted config-bundle top dir at all three admission doors — store (`internal/store/agent_config.go`), runner unpack twin (`internal/runner/config_materialize.go`), and CLI builder (`cmd/compass/bundle.go`) — with a dedicated strict grammar: exactly `prompts//SYSTEM.md` (three path components, `` under the safe-name regex, filename exactly `SYSTEM.md`). The case precedes the skills/extensions fall-through at each door because that fall-through admits arbitrary depth under ``, which would wrongly accept a too-deep or wrong-filename prompt member. Expose shipped role prompts by name via a `Prompts` info bucket: new `repeated string prompts = 10` on `GetAgentConfigInfoResponse` (+ regen across the go, compass-client, and compass-agent gen lanes), the service mapping in `agent_config_service.go`, and the `agent-config info` CLI render bucket. This lets an operator verify a role prompt is bundle-delivered before the seed flips the root to that role (T6). Tests: prompts accept/reject matrices at store + runner + CLI doors, the member-name walk buckets ``, the service e2e pgtest round-trips Put -> GetAgentConfigInfo -> Prompts, and the CLI render surfaces the bucket. Red-green verified: dropping the strict store-door case admits the too-deep and wrong-filename members the reject matrix pins. Refs RIG-3075 Co-authored-by: Matt Wilkinson --- go/cmd/compass/agent_config.go | 1 + go/cmd/compass/bundle.go | 17 +++++++++- go/cmd/compass/bundle_test.go | 26 ++++++++++------ go/cmd/compass/cli_test.go | 3 +- go/gen/compass/v1/compass.pb.go | 19 ++++++++++-- go/internal/runner/config_materialize.go | 16 ++++++++++ go/internal/runner/config_materialize_test.go | 31 +++++++++++-------- go/internal/store/agent_config.go | 28 +++++++++++++++++ go/internal/store/agent_config_test.go | 12 ++++++- go/server/agent_config_service.go | 1 + go/server/agent_config_service_pgtest_test.go | 13 +++++--- .../src/gen/compass/v1/compass_pb.ts | 11 ++++++- .../src/gen/compass/v1/compass_pb.ts | 11 ++++++- proto/compass/v1/compass.proto | 4 +++ 14 files changed, 158 insertions(+), 35 deletions(-) diff --git a/go/cmd/compass/agent_config.go b/go/cmd/compass/agent_config.go index e7de33229..0c0f76acd 100644 --- a/go/cmd/compass/agent_config.go +++ b/go/cmd/compass/agent_config.go @@ -143,6 +143,7 @@ func renderConfigInfo(out io.Writer, msg *compassv1.GetAgentConfigInfoResponse) {"mcp", msg.GetMcpServers()}, {"rules", msg.GetRules()}, {"subagents", msg.GetSubagents()}, + {"prompts", msg.GetPrompts()}, } for _, b := range buckets { if _, err := fmt.Fprintf(out, "%s (%d):\n", b.label, len(b.names)); err != nil { diff --git a/go/cmd/compass/bundle.go b/go/cmd/compass/bundle.go index 8740b503a..d3dd0ba19 100644 --- a/go/cmd/compass/bundle.go +++ b/go/cmd/compass/bundle.go @@ -33,6 +33,7 @@ const ( topDirSettings = "settings" topDirRules = "rules" topDirAgents = "agents" + topDirPrompts = "prompts" ) // Top-level regular-file members admitted by exact filename, not under a top dir @@ -42,6 +43,9 @@ const ( memberAgentsMD = "AGENTS.md" memberModels = "models.yml" settingsMember = "settings/config.yml" + // memberSystemMD is the only filename admitted under prompts// + // (store door: RIG-3075 T2). + memberSystemMD = "SYSTEM.md" ) // maxBundleFileCount and maxBundleContentBytes are a fail-fast client-side check @@ -67,6 +71,7 @@ var bundleTopDirs = map[string]bool{ topDirSettings: true, topDirRules: true, topDirAgents: true, + topDirPrompts: true, } // bundleNamePattern is the grammar for a member's segment (store door: @@ -165,7 +170,7 @@ func validateDirMember(name string) error { if !bundleTopDirs[parts[0]] { return errNotWhitelisted(name) } - if (parts[0] == topDirSkills || parts[0] == topDirExtensions) && len(parts) >= 2 && !bundleNamePattern.MatchString(parts[1]) { + if (parts[0] == topDirSkills || parts[0] == topDirExtensions || parts[0] == topDirPrompts) && len(parts) >= 2 && !bundleNamePattern.MatchString(parts[1]) { return fmt.Errorf("bundle member name %q must match %s", parts[1], bundleNamePattern.String()) } return nil @@ -277,6 +282,16 @@ func validateMemberGrammar(parts []string, name string, content []byte) error { return fmt.Errorf("agents member %q must be flat agents/.md", name) } return validateFlatNamedMember(topDirAgents, parts[1], name, ".md") + case topDirPrompts: + // Exactly prompts//SYSTEM.md — three components, grammar-valid + // , filename exactly SYSTEM.md (store door: RIG-3075 T2). + if len(parts) != 3 || parts[2] != memberSystemMD { + return fmt.Errorf("prompts member %q must be prompts//%s", name, memberSystemMD) + } + if !bundleNamePattern.MatchString(parts[1]) { + return fmt.Errorf("prompts role name %q must match %s", parts[1], bundleNamePattern.String()) + } + return nil default: // skills/ or extensions/: the second component must match. if len(parts) < 2 { diff --git a/go/cmd/compass/bundle_test.go b/go/cmd/compass/bundle_test.go index 697784f17..a5dbb827b 100644 --- a/go/cmd/compass/bundle_test.go +++ b/go/cmd/compass/bundle_test.go @@ -99,6 +99,10 @@ func TestBuildBundleRejects(t *testing.T) { {"bad agents ext", map[string]string{"agents/foo.txt": "x"}, "must end in one of .md"}, {"bad agents name", map[string]string{"agents/bad name.md": "x"}, "must match"}, {"nested agents", map[string]string{"agents/sub/foo.md": "x"}, "must be flat"}, + {"prompts wrong filename", map[string]string{"prompts/supervisor/other.md": "x"}, "must be prompts//SYSTEM.md"}, + {"prompts too deep", map[string]string{"prompts/supervisor/sub/SYSTEM.md": "x"}, "must be prompts//SYSTEM.md"}, + {"prompts too shallow", map[string]string{"prompts/SYSTEM.md": "x"}, "must be prompts//SYSTEM.md"}, + {"prompts bad role name", map[string]string{"prompts/bad name/SYSTEM.md": "x"}, "must match"}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { @@ -234,16 +238,17 @@ func TestBundleCaps(t *testing.T) { // between the builder's output and the door's grammar is caught here. func TestBuildBundleDoorParity(t *testing.T) { root := writeBundleDir(t, map[string]string{ - "skills/alpha/SKILL.md": "# alpha", - "skills/alpha/ref/notes.md": "notes", - "extensions/beta/main.go": "package beta", - "mcp/gamma.json": `{"ok":true}`, - "settings/config.yml": "autoCompact: true\n", - "rules/delta.md": "# delta rule", - "rules/epsilon.mdc": "# epsilon rule", - "agents/zeta.md": "# zeta agent", - "AGENTS.md": "# fleet context", - "models.yml": "models:\n main: anthropic/claude\n", + "skills/alpha/SKILL.md": "# alpha", + "skills/alpha/ref/notes.md": "notes", + "extensions/beta/main.go": "package beta", + "mcp/gamma.json": `{"ok":true}`, + "settings/config.yml": "autoCompact: true\n", + "rules/delta.md": "# delta rule", + "rules/epsilon.mdc": "# epsilon rule", + "agents/zeta.md": "# zeta agent", + "AGENTS.md": "# fleet context", + "prompts/supervisor/SYSTEM.md": "# supervisor prompt", + "models.yml": "models:\n main: anthropic/claude\n", }) bundle, err := buildBundle(root) if err != nil { @@ -269,6 +274,7 @@ func TestBuildBundleDoorParity(t *testing.T) { "extensions/beta/main.go", "mcp/gamma.json", "models.yml", + "prompts/supervisor/SYSTEM.md", "rules/delta.md", "rules/epsilon.mdc", "settings/config.yml", diff --git a/go/cmd/compass/cli_test.go b/go/cmd/compass/cli_test.go index bb62b28a3..b15bf3607 100644 --- a/go/cmd/compass/cli_test.go +++ b/go/cmd/compass/cli_test.go @@ -385,6 +385,7 @@ func TestRenderConfigInfoAllBuckets(t *testing.T) { McpServers: []string{"gamma"}, Rules: []string{"delta", "epsilon"}, Subagents: []string{"zeta"}, + Prompts: []string{"supervisor"}, HasSettings: true, HasAgentsMd: true, HasModels: true, @@ -393,7 +394,7 @@ func TestRenderConfigInfoAllBuckets(t *testing.T) { if err := renderConfigInfo(&out, msg); err != nil { t.Fatalf("renderConfigInfo: %v", err) } - for _, want := range []string{"v9", "alpha", "beta", "gamma", "delta", "epsilon", "zeta"} { + for _, want := range []string{"v9", "alpha", "beta", "gamma", "delta", "epsilon", "zeta", "supervisor"} { if !strings.Contains(out.String(), want) { t.Errorf("render output %q missing %q", out.String(), want) } diff --git a/go/gen/compass/v1/compass.pb.go b/go/gen/compass/v1/compass.pb.go index ec5dbf5e4..5d9be39f2 100644 --- a/go/gen/compass/v1/compass.pb.go +++ b/go/gen/compass/v1/compass.pb.go @@ -3742,7 +3742,11 @@ type GetAgentConfigInfoResponse struct { // Names of the bundle's subagent definitions (agents/.md), names only. Subagents []string `protobuf:"bytes,8,rep,name=subagents,proto3" json:"subagents,omitempty"` // Whether the bundle carries a fleet models.yml. - HasModels bool `protobuf:"varint,9,opt,name=has_models,json=hasModels,proto3" json:"has_models,omitempty"` + HasModels bool `protobuf:"varint,9,opt,name=has_models,json=hasModels,proto3" json:"has_models,omitempty"` + // The declared role-prompt names (the in prompts//SYSTEM.md), + // names only. Lets the operator verify a shipped role prompt before the seed + // flips the root to that role (RIG-3075 T2). + Prompts []string `protobuf:"bytes,10,rep,name=prompts,proto3" json:"prompts,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -3840,6 +3844,13 @@ func (x *GetAgentConfigInfoResponse) GetHasModels() bool { return false } +func (x *GetAgentConfigInfoResponse) GetPrompts() []string { + if x != nil { + return x.Prompts + } + return nil +} + type DeleteAgentConfigRequest struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields @@ -5031,7 +5042,7 @@ const file_compass_v1_compass_proto_rawDesc = "" + "\x06bundle\x18\x01 \x01(\fR\x06bundle\"2\n" + "\x16PutAgentConfigResponse\x12\x18\n" + "\aversion\x18\x01 \x01(\tR\aversion\"\x1b\n" + - "\x19GetAgentConfigInfoRequest\"\xa9\x02\n" + + "\x19GetAgentConfigInfoRequest\"\xc3\x02\n" + "\x1aGetAgentConfigInfoResponse\x12\x18\n" + "\aversion\x18\x01 \x01(\tR\aversion\x12\x16\n" + "\x06skills\x18\x02 \x03(\tR\x06skills\x12\x1e\n" + @@ -5045,7 +5056,9 @@ const file_compass_v1_compass_proto_rawDesc = "" + "\x05rules\x18\a \x03(\tR\x05rules\x12\x1c\n" + "\tsubagents\x18\b \x03(\tR\tsubagents\x12\x1d\n" + "\n" + - "has_models\x18\t \x01(\bR\thasModels\"\x1a\n" + + "has_models\x18\t \x01(\bR\thasModels\x12\x18\n" + + "\aprompts\x18\n" + + " \x03(\tR\aprompts\"\x1a\n" + "\x18DeleteAgentConfigRequest\"\x1b\n" + "\x19DeleteAgentConfigResponse\"5\n" + "\x10AgentAttribution\x12!\n" + diff --git a/go/internal/runner/config_materialize.go b/go/internal/runner/config_materialize.go index cb6d190a2..35cb3b31e 100644 --- a/go/internal/runner/config_materialize.go +++ b/go/internal/runner/config_materialize.go @@ -65,6 +65,7 @@ const ( topDirSettings = "settings" topDirRules = "rules" topDirAgents = "agents" + topDirPrompts = "prompts" ) // configTopDirs are the only permitted top-level directories in a config bundle. @@ -75,6 +76,7 @@ var configTopDirs = map[string]struct{}{ topDirSettings: {}, topDirRules: {}, topDirAgents: {}, + topDirPrompts: {}, } // Top-level regular-file members admitted by exact filename (RIG-1678 T2), and @@ -85,6 +87,9 @@ const ( memberAgentsMD = "AGENTS.md" memberModels = "models.yml" settingsMember = "settings/config.yml" + // memberSystemMD is the only filename admitted under prompts// + // (RIG-3075 T2); structural twin of the store door. + memberSystemMD = "SYSTEM.md" ) // configFetcher is the T3 fetch seam the materializer pulls through. *ServerLink @@ -538,6 +543,17 @@ func validateNestedMember(name, clean, top string, parts []string, typeflag byte return "", fmt.Errorf("config bundle mcp member name %q is not a safe name", entry) } return clean, nil + case topDirPrompts: + // Exactly prompts//SYSTEM.md — three components, safe , + // filename exactly SYSTEM.md (RIG-3075 T2). Stricter than + // skills/extensions, so it needs its own arm, not the fall-through. + if len(parts) != 3 || parts[2] != memberSystemMD { + return "", fmt.Errorf("config bundle prompts member %q must be prompts//%s", name, memberSystemMD) + } + if !configTopLevelName.MatchString(parts[1]) { + return "", fmt.Errorf("config bundle prompts role name %q is not a safe name", parts[1]) + } + return clean, nil } } diff --git a/go/internal/runner/config_materialize_test.go b/go/internal/runner/config_materialize_test.go index d3c6ddd7a..121093a23 100644 --- a/go/internal/runner/config_materialize_test.go +++ b/go/internal/runner/config_materialize_test.go @@ -621,12 +621,13 @@ var yamlMap = []byte("compaction:\n enabled: true\n") func TestConfigMaterializeLandsNewMembers(t *testing.T) { root := t.TempDir() tarball := buildConfigTarball(t, map[string][]byte{ - "settings/config.yml": yamlMap, - "AGENTS.md": []byte("# fleet conventions\n"), - "models.yml": []byte("providers:\n x:\n baseUrl: https://y\n"), - "rules/red-green.md": []byte("# red-green\n"), - "rules/hold-lane.mdc": []byte("# hold lane\n"), - "agents/design.md": []byte("# design agent\n"), + "settings/config.yml": yamlMap, + "AGENTS.md": []byte("# fleet conventions\n"), + "models.yml": []byte("providers:\n x:\n baseUrl: https://y\n"), + "rules/red-green.md": []byte("# red-green\n"), + "rules/hold-lane.mdc": []byte("# hold lane\n"), + "agents/design.md": []byte("# design agent\n"), + "prompts/supervisor/SYSTEM.md": []byte("# supervisor prompt\n"), }) f := &fakeConfigFetcher{bundle: AgentConfigBundle{Version: "v1", Tarball: tarball}} m := NewConfigMaterializer(root, f, nil) @@ -636,12 +637,13 @@ func TestConfigMaterializeLandsNewMembers(t *testing.T) { versionDir := filepath.Join(root, "v1") files := map[string]string{ - filepath.Join("settings", "config.yml"): string(yamlMap), - "AGENTS.md": "# fleet conventions\n", - "models.yml": "providers:\n x:\n baseUrl: https://y\n", - filepath.Join("rules", "red-green.md"): "# red-green\n", - filepath.Join("rules", "hold-lane.mdc"): "# hold lane\n", - filepath.Join("agents", "design.md"): "# design agent\n", + filepath.Join("settings", "config.yml"): string(yamlMap), + "AGENTS.md": "# fleet conventions\n", + "models.yml": "providers:\n x:\n baseUrl: https://y\n", + filepath.Join("rules", "red-green.md"): "# red-green\n", + filepath.Join("rules", "hold-lane.mdc"): "# hold lane\n", + filepath.Join("agents", "design.md"): "# design agent\n", + filepath.Join("prompts", "supervisor", "SYSTEM.md"): "# supervisor prompt\n", } for rel, want := range files { p := filepath.Join(versionDir, rel) @@ -654,7 +656,7 @@ func TestConfigMaterializeLandsNewMembers(t *testing.T) { t.Fatalf("%s mode = %v err=%v, want 0644", rel, fi.Mode().Perm(), err) } } - for _, dir := range []string{"settings", "rules", "agents"} { + for _, dir := range []string{"settings", "rules", "agents", "prompts"} { di, err := os.Stat(filepath.Join(versionDir, dir)) if err != nil || di.Mode().Perm() != 0o755 { t.Fatalf("%s dir mode = %v err=%v, want 0755", dir, di.Mode().Perm(), err) @@ -679,6 +681,9 @@ func TestConfigMaterializeRejectsNewMemberStructure(t *testing.T) { "agents nested": buildConfigTarball(t, map[string][]byte{"agents/sub/a.md": []byte("x")}), "top-level other file": buildConfigTarball(t, map[string][]byte{"README.md": []byte("x")}), "models non-mapping": buildConfigTarball(t, map[string][]byte{"models.yml": []byte("- a\n")}), + "prompts wrong filename": buildConfigTarball(t, map[string][]byte{"prompts/supervisor/other.md": []byte("x")}), + "prompts too deep": buildConfigTarball(t, map[string][]byte{"prompts/supervisor/sub/SYSTEM.md": []byte("x")}), + "prompts too shallow": buildConfigTarball(t, map[string][]byte{"prompts/SYSTEM.md": []byte("x")}), } for name, tarball := range cases { t.Run(name, func(t *testing.T) { diff --git a/go/internal/store/agent_config.go b/go/internal/store/agent_config.go index 896778b3c..8ec9a1395 100644 --- a/go/internal/store/agent_config.go +++ b/go/internal/store/agent_config.go @@ -39,6 +39,7 @@ const ( topDirSettings = "settings" topDirRules = "rules" topDirAgents = "agents" + topDirPrompts = "prompts" ) // Top-level regular-file members admitted by exact filename (not under a top @@ -50,6 +51,9 @@ const ( // settingsMember is the ONLY file admitted under settings/ — yml-only per // OQ-1 (no .yaml/.json variant, no other name). settingsMember = "settings/config.yml" + // memberSystemMD is the ONLY filename admitted under prompts// — the + // role prompt is exactly prompts//SYSTEM.md (RIG-3075 T2). + memberSystemMD = "SYSTEM.md" ) // configBundleTopDirs is the whitelist as a set, for the O(1) membership check in @@ -61,6 +65,7 @@ var configBundleTopDirs = map[string]bool{ topDirSettings: true, topDirRules: true, topDirAgents: true, + topDirPrompts: true, } // configNamePattern is the grammar for a config entry's segment — @@ -227,6 +232,7 @@ type AgentConfigInfoResult struct { McpServers []string Rules []string Subagents []string + Prompts []string HasSettings bool HasAgentsMD bool HasModels bool @@ -254,6 +260,7 @@ func configBundleMemberNames(bundle []byte) (AgentConfigInfoResult, error) { mcpSet := make(map[string]bool) ruleSet := make(map[string]bool) agentSet := make(map[string]bool) + promptSet := make(map[string]bool) var info AgentConfigInfoResult tr := tar.NewReader(&cappedReader{r: gz}) @@ -299,6 +306,12 @@ func configBundleMemberNames(bundle []byte) (AgentConfigInfoResult, error) { ruleSet[trimAnySuffix(parts[1], ".md", ".mdc")] = true case topDirAgents: agentSet[strings.TrimSuffix(parts[1], ".md")] = true + case topDirPrompts: + // A role prompt is exactly prompts//SYSTEM.md; count the + // name, matching the door grammar (validateRegularMember). + if len(parts) == 3 && parts[2] == memberSystemMD { + promptSet[parts[1]] = true + } } } info.Skills = sortedKeys(skillSet) @@ -306,6 +319,7 @@ func configBundleMemberNames(bundle []byte) (AgentConfigInfoResult, error) { info.McpServers = sortedKeys(mcpSet) info.Rules = sortedKeys(ruleSet) info.Subagents = sortedKeys(agentSet) + info.Prompts = sortedKeys(promptSet) return info, nil } @@ -504,6 +518,8 @@ func configMemberParts(name string) ([]string, error) { // parses as a YAML mapping and sets no credential-denylisted key. // - rules/.md|.mdc — flat, grammar-valid ; prose, no content check. // - agents/.md — flat, grammar-valid ; prose, no content check. +// - prompts//SYSTEM.md — grammar-valid , filename exactly +// SYSTEM.md; prose, no content check (RIG-3075 T2). // - top-level AGENTS.md — prose, no content check beyond the read. // - top-level models.yml — content parses as a YAML mapping and sets no // credentialed provider surface (apiKey, or a headers.* literal secret). @@ -533,6 +549,18 @@ func validateRegularMember(parts []string, r io.Reader) ([]byte, error) { return nil, err } return io.ReadAll(r) + case topDirPrompts: + // Exactly prompts//SYSTEM.md: three components, grammar-valid + // , filename exactly SYSTEM.md. Stricter than skills/extensions + // (which admit arbitrary depth under ), so it needs its own case + // rather than the fall-through below. + if len(parts) != 3 || parts[2] != memberSystemMD { + return nil, fmt.Errorf("%w: prompts member %q must be prompts//%s", ErrInvalidArgument, joined, memberSystemMD) + } + if !configNamePattern.MatchString(parts[1]) { + return nil, fmt.Errorf("%w: prompts role name %q must match %s", ErrInvalidArgument, parts[1], configNamePattern.String()) + } + return io.ReadAll(r) } // Top-level single-component files (configMemberParts admits only the two diff --git a/go/internal/store/agent_config_test.go b/go/internal/store/agent_config_test.go index 2dec87553..c43281aa5 100644 --- a/go/internal/store/agent_config_test.go +++ b/go/internal/store/agent_config_test.go @@ -370,7 +370,7 @@ func TestConfigBundleMemberNamesEmpty(t *testing.T) { t.Fatalf("configBundleMemberNames(empty): %v", err) } if len(info.Skills) != 0 || len(info.Extensions) != 0 || len(info.McpServers) != 0 || - len(info.Rules) != 0 || len(info.Subagents) != 0 { + len(info.Rules) != 0 || len(info.Subagents) != 0 || len(info.Prompts) != 0 { t.Fatalf("empty bundle names = %+v, want all empty", info) } if info.HasSettings || info.HasAgentsMD || info.HasModels { @@ -408,6 +408,7 @@ func TestValidateConfigBundleAcceptsNewMembers(t *testing.T) { {"rules .md", []tarEntry{{name: "rules/a.md", content: "# rule a"}}}, {"rules .mdc", []tarEntry{{name: "rules/b.mdc", content: "# rule b"}}}, {"agents .md", []tarEntry{{name: "agents/design.md", content: "# design agent"}}}, + {"prompts//SYSTEM.md", []tarEntry{{name: "prompts/supervisor/SYSTEM.md", content: "# supervisor"}}}, {"top-level AGENTS.md", []tarEntry{{name: "AGENTS.md", content: "# fleet conventions"}}}, {"top-level models.yml mapping", []tarEntry{{name: "models.yml", content: "providers:\n x:\n baseUrl: https://y\n"}}}, {"models.yml headers env reference", []tarEntry{{name: "models.yml", content: "providers:\n x:\n headers:\n X-Org: MY_ORG_ENV\n"}}}, @@ -443,6 +444,10 @@ func TestValidateConfigBundleRejectsNewMembers(t *testing.T) { {"agents nested", []tarEntry{{name: "agents/sub/a.md", content: "x"}}}, {"top-level other file", []tarEntry{{name: "README.md", content: "x"}}}, {"models non-mapping", []tarEntry{{name: "models.yml", content: "- a\n"}}}, + {"prompts wrong filename", []tarEntry{{name: "prompts/supervisor/other.md", content: "x"}}}, + {"prompts nested too deep", []tarEntry{{name: "prompts/supervisor/sub/SYSTEM.md", content: "x"}}}, + {"prompts flat too shallow", []tarEntry{{name: "prompts/SYSTEM.md", content: "x"}}}, + {"prompts bad role name", []tarEntry{{name: "prompts/bad name/SYSTEM.md", content: "x"}}}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { @@ -531,6 +536,8 @@ func TestConfigBundleMemberNamesNewMembers(t *testing.T) { tarEntry{name: "rules/hold-lane.mdc", content: "x"}, tarEntry{name: "agents/design.md", content: "x"}, tarEntry{name: "agents/review.md", content: "x"}, + tarEntry{name: "prompts/supervisor/SYSTEM.md", content: "# sup"}, + tarEntry{name: "prompts/owner/SYSTEM.md", content: "# own"}, ) info, err := configBundleMemberNames(b) if err != nil { @@ -545,4 +552,7 @@ func TestConfigBundleMemberNamesNewMembers(t *testing.T) { if got, want := strings.Join(info.Subagents, ","), "design,review"; got != want { t.Errorf("subagents = %q, want %q", got, want) } + if got, want := strings.Join(info.Prompts, ","), "owner,supervisor"; got != want { + t.Errorf("prompts = %q, want %q", got, want) + } } diff --git a/go/server/agent_config_service.go b/go/server/agent_config_service.go index e8380d5ab..666ef1d93 100644 --- a/go/server/agent_config_service.go +++ b/go/server/agent_config_service.go @@ -120,6 +120,7 @@ func (s *service) GetAgentConfigInfo( Rules: info.Rules, Subagents: info.Subagents, HasModels: info.HasModels, + Prompts: info.Prompts, }), nil } diff --git a/go/server/agent_config_service_pgtest_test.go b/go/server/agent_config_service_pgtest_test.go index 47cbf9ff1..24fd7ef86 100644 --- a/go/server/agent_config_service_pgtest_test.go +++ b/go/server/agent_config_service_pgtest_test.go @@ -238,10 +238,12 @@ func TestGetAgentConfigInfoUnconfiguredIsEmpty(t *testing.T) { func TestGetAgentConfigInfoReturnsBucketedNames(t *testing.T) { f := newConfigFixture(t) bundle := mkConfigBundle(t, map[string]string{ - "skills/review/SKILL.md": "# review", - "skills/triage/SKILL.md": "# triage", - "extensions/cotal/main.js": "x", - "mcp/linear.json": `{"a":1}`, + "skills/review/SKILL.md": "# review", + "skills/triage/SKILL.md": "# triage", + "extensions/cotal/main.js": "x", + "mcp/linear.json": `{"a":1}`, + "prompts/supervisor/SYSTEM.md": "# supervisor", + "prompts/owner/SYSTEM.md": "# owner", }) put, err := f.client.PutAgentConfig(context.Background(), authReq(f, &compassv1.PutAgentConfigRequest{Bundle: bundle})) @@ -266,6 +268,9 @@ func TestGetAgentConfigInfoReturnsBucketedNames(t *testing.T) { if got := join(resp.Msg.GetMcpServers()); got != "linear" { t.Errorf("mcp_servers = %q, want linear", got) } + if got := join(resp.Msg.GetPrompts()); got != "owner,supervisor" { + t.Errorf("prompts = %q, want owner,supervisor", got) + } } // TestDeleteAgentConfigClearsAndSignalsEmpty: Delete clears the fleet (a diff --git a/packages/compass-agent/src/gen/compass/v1/compass_pb.ts b/packages/compass-agent/src/gen/compass/v1/compass_pb.ts index 932b67f33..c53a6f0d5 100644 --- a/packages/compass-agent/src/gen/compass/v1/compass_pb.ts +++ b/packages/compass-agent/src/gen/compass/v1/compass_pb.ts @@ -20,7 +20,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file compass/v1/compass.proto. */ export const file_compass_v1_compass: GenFile = /*@__PURE__*/ - fileDesc("Chhjb21wYXNzL3YxL2NvbXBhc3MucHJvdG8SCmNvbXBhc3MudjEiqAEKEFNldFNlY3JldFJlcXVlc3QSDAoEbmFtZRgBIAEoCRISCgV2YWx1ZRgCIAEoCUIDgAEBEiwKCGRlbGl2ZXJ5GAMgASgOMhouY29tcGFzcy52MS5TZWNyZXREZWxpdmVyeRIkCgRraW5kGAQgASgOMhYuY29tcGFzcy52MS5TZWNyZXRLaW5kEhAKCHByb3ZpZGVyGAUgASgJEgwKBGhvc3QYBiABKAkiEwoRU2V0U2VjcmV0UmVzcG9uc2UiFAoSTGlzdFNlY3JldHNSZXF1ZXN0IkAKE0xpc3RTZWNyZXRzUmVzcG9uc2USKQoHc2VjcmV0cxgBIAMoCzIYLmNvbXBhc3MudjEuU2VjcmV0U3RhdHVzIqABCgxTZWNyZXRTdGF0dXMSDAoEbmFtZRgBIAEoCRIOCgZpc19zZXQYAiABKAgSLAoIZGVsaXZlcnkYAyABKA4yGi5jb21wYXNzLnYxLlNlY3JldERlbGl2ZXJ5EiQKBGtpbmQYBCABKA4yFi5jb21wYXNzLnYxLlNlY3JldEtpbmQSEAoIcHJvdmlkZXIYBSABKAkSDAoEaG9zdBgGIAEoCSIjChNEZWxldGVTZWNyZXRSZXF1ZXN0EgwKBG5hbWUYASABKAkiFgoURGVsZXRlU2VjcmV0UmVzcG9uc2UiFgoUR2V0U2VydmVySW5mb1JlcXVlc3QiPQoVR2V0U2VydmVySW5mb1Jlc3BvbnNlEg8KB3ZlcnNpb24YASABKAkSEwoLYXBpX3ZlcnNpb24YAiABKAkiDwoNV2hvQW1JUmVxdWVzdCIkCg5XaG9BbUlSZXNwb25zZRISCgphY2NvdW50X2lkGAEgASgJIkMKFlN1YnNjcmliZUV2ZW50c1JlcXVlc3QSEQoJc2luY2Vfc2VxGAEgASgEEhYKDmluc3RhbmNlX2Vwb2NoGAIgASgEIuIDChdTdWJzY3JpYmVFdmVudHNSZXNwb25zZRILCgNzZXEYASABKAQSEgoKYXRfdW5peF9tcxgCIAEoAxIWCg5pbnN0YW5jZV9lcG9jaBgDIAEoBBIUCgxzbmFwc2hvdF9zZXEYBCABKAQSMQoNc2VydmVyX3N0YXR1cxgKIAEoCzIYLmNvbXBhc3MudjEuU2VydmVyU3RhdHVzSAASNQoPcmVzeW5jX3JlcXVpcmVkGAsgASgLMhouY29tcGFzcy52MS5SZXN5bmNSZXF1aXJlZEgAEj4KFGFnZW50X3Nlc3Npb25fc3RhdHVzGAwgASgLMh4uY29tcGFzcy52MS5BZ2VudFNlc3Npb25TdGF0dXNIABI8ChNhZ2VudF9tZXNzYWdlX2NodW5rGA0gASgLMh0uY29tcGFzcy52MS5BZ2VudE1lc3NhZ2VDaHVua0gAEjQKD2FnZW50X3Rvb2xfY2FsbBgOIAEoCzIZLmNvbXBhc3MudjEuQWdlbnRUb29sQ2FsbEgAEisKCmFnZW50X3BsYW4YDyABKAsyFS5jb21wYXNzLnYxLkFnZW50UGxhbkgAEiIKBWlzc3VlGBAgASgLMhEuY29tcGFzcy52MS5Jc3N1ZUgAQgkKB3BheWxvYWQiLgoWTGlzdEJvYXJkSXNzdWVzUmVxdWVzdBIUCgxzbmFwc2hvdF9zZXEYASABKAQiPAoXTGlzdEJvYXJkSXNzdWVzUmVzcG9uc2USIQoGaXNzdWVzGAEgAygLMhEuY29tcGFzcy52MS5Jc3N1ZSI2CgxTZXJ2ZXJTdGF0dXMSJgoFc3RhdGUYASABKA4yFy5jb21wYXNzLnYxLlNlcnZlclN0YXRlIhAKDlJlc3luY1JlcXVpcmVkInAKEkFnZW50U2Vzc2lvblN0YXR1cxISCgpzZXNzaW9uX2lkGAEgASgJEiwKBXN0YXRlGAIgASgOMh0uY29tcGFzcy52MS5BZ2VudFNlc3Npb25TdGF0ZRIYChBhZ2VudF9hY2NvdW50X2lkGAMgASgJIkkKEUFnZW50TWVzc2FnZUNodW5rEhIKCnNlc3Npb25faWQYASABKAkSDAoEdGV4dBgCIAEoCRISCgppc190aG91Z2h0GAMgASgIInkKDUFnZW50VG9vbENhbGwSEgoKc2Vzc2lvbl9pZBgBIAEoCRIUCgx0b29sX2NhbGxfaWQYAiABKAkSDQoFdGl0bGUYAyABKAkSLwoGc3RhdHVzGAQgASgOMh8uY29tcGFzcy52MS5BZ2VudFRvb2xDYWxsU3RhdHVzIkwKCUFnZW50UGxhbhISCgpzZXNzaW9uX2lkGAEgASgJEisKB2VudHJpZXMYAiADKAsyGi5jb21wYXNzLnYxLkFnZW50UGxhbkVudHJ5IlMKDkFnZW50UGxhbkVudHJ5Eg8KB2NvbnRlbnQYASABKAkSMAoGc3RhdHVzGAIgASgOMiAuY29tcGFzcy52MS5BZ2VudFBsYW5FbnRyeVN0YXR1cyKsAwoMU2Vzc2lvbkV2ZW50EhAKCGV2ZW50X2lkGAEgASgJEhIKCmF0X3VuaXhfbXMYAiABKAMSOgoOYXNzaXN0YW50X3RleHQYAyABKAsyIC5jb21wYXNzLnYxLlNlc3Npb25Bc3Npc3RhbnRUZXh0SAASLwoIdGhpbmtpbmcYBCABKAsyGy5jb21wYXNzLnYxLlNlc3Npb25UaGlua2luZ0gAEjAKCXRvb2xfY2FsbBgFIAEoCzIbLmNvbXBhc3MudjEuU2Vzc2lvblRvb2xDYWxsSAASPQoQdG9vbF9jYWxsX3VwZGF0ZRgGIAEoCzIhLmNvbXBhc3MudjEuU2Vzc2lvblRvb2xDYWxsVXBkYXRlSAASJwoEcGxhbhgHIAEoCzIXLmNvbXBhc3MudjEuU2Vzc2lvblBsYW5IABIrCgZub3RpY2UYCCABKAsyGS5jb21wYXNzLnYxLlNlc3Npb25Ob3RpY2VIABI5ChFzZXNzaW9uX2luamVjdGlvbhgJIAEoCzIcLmNvbXBhc3MudjEuU2Vzc2lvbkluamVjdGlvbkgAQgcKBWV2ZW50IjgKFFNlc3Npb25Bc3Npc3RhbnRUZXh0EgwKBHRleHQYASABKAkSEgoKbWVzc2FnZV9pZBgCIAEoCSIzCg9TZXNzaW9uVGhpbmtpbmcSDAoEdGV4dBgBIAEoCRISCgptZXNzYWdlX2lkGAIgASgJImcKD1Nlc3Npb25Ub29sQ2FsbBIUCgx0b29sX2NhbGxfaWQYASABKAkSDQoFdGl0bGUYAiABKAkSLwoGc3RhdHVzGAMgASgOMh8uY29tcGFzcy52MS5BZ2VudFRvb2xDYWxsU3RhdHVzIpoBChVTZXNzaW9uVG9vbENhbGxVcGRhdGUSFAoMdG9vbF9jYWxsX2lkGAEgASgJEi8KBnN0YXR1cxgCIAEoDjIfLmNvbXBhc3MudjEuQWdlbnRUb29sQ2FsbFN0YXR1cxIOCgZvdXRwdXQYAyABKAkSKgoFZGlmZnMYBCADKAsyGy5jb21wYXNzLnYxLlNlc3Npb25GaWxlRGlmZiJVCg9TZXNzaW9uRmlsZURpZmYSDAoEcGF0aBgBIAEoCRIVCghvbGRfdGV4dBgCIAEoCUgAiAEBEhAKCG5ld190ZXh0GAMgASgJQgsKCV9vbGRfdGV4dCI6CgtTZXNzaW9uUGxhbhIrCgdlbnRyaWVzGAEgAygLMhouY29tcGFzcy52MS5BZ2VudFBsYW5FbnRyeSI5Cg1TZXNzaW9uTm90aWNlEgwKBHRleHQYASABKAkSEQoEbGluaxgCIAEoCUgAiAEBQgcKBV9saW5rIoMBChBTZXNzaW9uSW5qZWN0aW9uEjEKB29wX2tpbmQYASABKA4yIC5jb21wYXNzLnYxLlNlc3Npb25JbmplY3Rpb25LaW5kEhIKCm1lc3NhZ2VfaWQYAiABKAkSEwoLZnJvbV9oYW5kbGUYAyABKAkSEwoLdHJhY2VwYXJlbnQYBCABKAkiMgocU3Vic2NyaWJlQWdlbnRTZXNzaW9uUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJIn4KEUFnZW50U2Vzc2lvbkZyYW1lEhIKCnNlc3Npb25faWQYASABKAkSJwoFZXZlbnQYAiABKAsyGC5jb21wYXNzLnYxLlNlc3Npb25FdmVudBIsCgVzdGF0ZRgDIAEoDjIdLmNvbXBhc3MudjEuQWdlbnRTZXNzaW9uU3RhdGUicAoeUHJvdmlzaW9uQWdlbnRXb3Jrc3BhY2VSZXF1ZXN0EhQKDGFnZW50X2hhbmRsZRgBIAEoCRIZChFjbGllbnRfcmVxdWVzdF9pZBgCIAEoCRIPCgdwZXJzb25hGAMgASgJEgwKBHJvbGUYBCABKAkiOQofUHJvdmlzaW9uQWdlbnRXb3Jrc3BhY2VSZXNwb25zZRIWCg5jb250YWluZXJfbmFtZRgBIAEoCSJQChtSZW1vdmVBZ2VudFdvcmtzcGFjZVJlcXVlc3QSFgoOY29udGFpbmVyX25hbWUYASABKAkSGQoRY2xpZW50X3JlcXVlc3RfaWQYAiABKAkiHgocUmVtb3ZlQWdlbnRXb3Jrc3BhY2VSZXNwb25zZSJjChhTdGFydEFnZW50U2Vzc2lvblJlcXVlc3QSFgoOY29udGFpbmVyX25hbWUYASABKAkSGQoRcmVzdW1lX3Nlc3Npb25faWQYAyABKAlKBAgCEANSDmluaXRpYWxfcHJvbXB0Ii8KGVN0YXJ0QWdlbnRTZXNzaW9uUmVzcG9uc2USEgoKc2Vzc2lvbl9pZBgBIAEoCSJaChFTcGF3bkFnZW50UmVxdWVzdBIUCgxhZ2VudF9oYW5kbGUYASABKAkSGQoRY2xpZW50X3JlcXVlc3RfaWQYAyABKAlKBAgCEANSDmluaXRpYWxfcHJvbXB0IkAKElNwYXduQWdlbnRSZXNwb25zZRISCgpzZXNzaW9uX2lkGAEgASgJEhYKDmNvbnRhaW5lcl9uYW1lGAIgASgJIi0KF1N0b3BBZ2VudFNlc3Npb25SZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkiGgoYU3RvcEFnZW50U2Vzc2lvblJlc3BvbnNlIi8KGVJlbG9hZEFnZW50U2Vzc2lvblJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCSIwChpSZWxvYWRBZ2VudFNlc3Npb25SZXNwb25zZRISCgpzZXNzaW9uX2lkGAEgASgJIisKFUdldEFnZW50U3RhdHVzUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJIkoKFkdldEFnZW50U3RhdHVzUmVzcG9uc2USMAoIc3RhdHVzZXMYASADKAsyHi5jb21wYXNzLnYxLkFnZW50U2Vzc2lvblN0YXR1cyIrChFJc3N1ZVRva2VuUmVxdWVzdBIWCg5hY2NvdW50X2hhbmRsZRgBIAEoCSIjChJJc3N1ZVRva2VuUmVzcG9uc2USDQoFdG9rZW4YASABKAkiKAoSUmV2b2tlVG9rZW5SZXF1ZXN0EhIKBXRva2VuGAEgASgJQgOAAQEiFQoTUmV2b2tlVG9rZW5SZXNwb25zZSInChVQdXRBZ2VudENvbmZpZ1JlcXVlc3QSDgoGYnVuZGxlGAEgASgMIikKFlB1dEFnZW50Q29uZmlnUmVzcG9uc2USDwoHdmVyc2lvbhgBIAEoCSIbChlHZXRBZ2VudENvbmZpZ0luZm9SZXF1ZXN0IskBChpHZXRBZ2VudENvbmZpZ0luZm9SZXNwb25zZRIPCgd2ZXJzaW9uGAEgASgJEg4KBnNraWxscxgCIAMoCRISCgpleHRlbnNpb25zGAMgAygJEhMKC21jcF9zZXJ2ZXJzGAQgAygJEhQKDGhhc19zZXR0aW5ncxgFIAEoCBIVCg1oYXNfYWdlbnRzX21kGAYgASgIEg0KBXJ1bGVzGAcgAygJEhEKCXN1YmFnZW50cxgIIAMoCRISCgpoYXNfbW9kZWxzGAkgASgIIhoKGERlbGV0ZUFnZW50Q29uZmlnUmVxdWVzdCIbChlEZWxldGVBZ2VudENvbmZpZ1Jlc3BvbnNlIigKEEFnZW50QXR0cmlidXRpb24SFAoMYWdlbnRfaGFuZGxlGAEgASgJIkUKCEZvcmdlUmVmEisKCHByb3ZpZGVyGAEgASgOMhkuY29tcGFzcy52MS5Gb3JnZVByb3ZpZGVyEgwKBGhvc3QYAiABKAki1AMKBUlzc3VlEgoKAmlkGAEgASgJEiMKBWZvcmdlGAIgASgLMhQuY29tcGFzcy52MS5Gb3JnZVJlZhIMCgRyZXBvGAMgASgJEg4KBm51bWJlchgEIAEoDRINCgV0aXRsZRgFIAEoCRIMCgRib2R5GAYgASgJEhMKC2ZvcmdlX3N0YXRlGAcgASgJEgsKA3VybBgIIAEoCRIrCgVhZ2VudBgJIAEoCzIcLmNvbXBhc3MudjEuQWdlbnRBdHRyaWJ1dGlvbhIVCg1mb3JnZV9hY2NvdW50GAogASgJEg4KBmxhYmVscxgLIAMoCRIuCgp1cGRhdGVkX2F0GBMgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIlCgVzdGF0ZRgMIAEoDjIWLmNvbXBhc3MudjEuSXNzdWVTdGF0ZRIQCghwcmlvcml0eRgNIAEoCRIQCghhc3NpZ25lZRgOIAEoCRIPCgdzdW1tYXJ5GA8gASgJEg4KBmJyYW5jaBgQIAEoCRIkCgNwcnMYESADKAsyFy5jb21wYXNzLnYxLlB1bGxSZXF1ZXN0EicKB3RyYWNrZXIYEiABKAsyFi5jb21wYXNzLnYxLlRyYWNrZXJSZWYingMKC1B1bGxSZXF1ZXN0EiMKBWZvcmdlGAEgASgLMhQuY29tcGFzcy52MS5Gb3JnZVJlZhIMCgRyZXBvGAIgASgJEg4KBm51bWJlchgDIAEoDRINCgV0aXRsZRgEIAEoCRITCgtmb3JnZV9zdGF0ZRgFIAEoCRILCgN1cmwYBiABKAkSEAoIaGVhZF9yZWYYByABKAkSEAoIYmFzZV9yZWYYCCABKAkSKwoFYWdlbnQYCSABKAsyHC5jb21wYXNzLnYxLkFnZW50QXR0cmlidXRpb24SFQoNZm9yZ2VfYWNjb3VudBgKIAEoCRINCgVkcmFmdBgLIAEoCBIpCgdjaGFuZ2VkGAwgASgLMhguY29tcGFzcy52MS5DaGFuZ2VkU3RhdHMSKQoGY2hlY2tzGA0gASgLMhkuY29tcGFzcy52MS5DaGVja3NTdW1tYXJ5EiMKB3Jldmlld3MYDiADKAsyEi5jb21wYXNzLnYxLlJldmlldxIpCgd0aHJlYWRzGA8gAygLMhguY29tcGFzcy52MS5SZXZpZXdUaHJlYWQiUwoNQ2hlY2tzU3VtbWFyeRIQCghoZWFkX3NoYRgBIAEoCRINCgVzdGF0ZRgCIAEoCRIhCgZjaGVja3MYAyADKAsyES5jb21wYXNzLnYxLkNoZWNrIkMKBUNoZWNrEgwKBG5hbWUYASABKAkSDQoFc3RhdGUYAiABKAkSCwoDdXJsGAMgASgJEhAKCHJlcXVpcmVkGAQgASgIIkMKDENoYW5nZWRTdGF0cxINCgVmaWxlcxgBIAEoDRIRCglhZGRpdGlvbnMYAiABKA0SEQoJZGVsZXRpb25zGAMgASgNIkMKClRyYWNrZXJSZWYSDAoEa2luZBgBIAEoCRIKCgJpZBgCIAEoCRIOCgZzdGF0dXMYAyABKAkSCwoDdXJsGAQgASgJIkcKBlJldmlldxIOCgZhdXRob3IYASABKAkSDgoGaXNfYm90GAIgASgIEg8KB3ZlcmRpY3QYAyABKAkSDAoEYm9keRgEIAEoCSJVCgxSZXZpZXdUaHJlYWQSDAoEcGF0aBgBIAEoCRIQCghyZXNvbHZlZBgCIAEoCBIlCghjb21tZW50cxgDIAMoCzITLmNvbXBhc3MudjEuQ29tbWVudCI3CgdDb21tZW50Eg4KBmF1dGhvchgBIAEoCRIOCgZpc19ib3QYAiABKAgSDAoEYm9keRgDIAEoCSpkCg5TZWNyZXREZWxpdmVyeRIfChtTRUNSRVRfREVMSVZFUllfVU5TUEVDSUZJRUQQABIYChRTRUNSRVRfREVMSVZFUllfRklMRRABEhcKE1NFQ1JFVF9ERUxJVkVSWV9FTlYQAipwCgpTZWNyZXRLaW5kEhsKF1NFQ1JFVF9LSU5EX1VOU1BFQ0lGSUVEEAASFwoTU0VDUkVUX0tJTkRfR0VORVJJQxABEhgKFFNFQ1JFVF9LSU5EX1BST1ZJREVSEAISEgoOU0VDUkVUX0tJTkRfR0gQAypDCgtTZXJ2ZXJTdGF0ZRIcChhTRVJWRVJfU1RBVEVfVU5TUEVDSUZJRUQQABIWChJTRVJWRVJfU1RBVEVfUkVBRFkQASqCAgoRQWdlbnRTZXNzaW9uU3RhdGUSIwofQUdFTlRfU0VTU0lPTl9TVEFURV9VTlNQRUNJRklFRBAAEiAKHEFHRU5UX1NFU1NJT05fU1RBVEVfU1RBUlRJTkcQARIdChlBR0VOVF9TRVNTSU9OX1NUQVRFX1JFQURZEAISHwobQUdFTlRfU0VTU0lPTl9TVEFURV9XT1JLSU5HEAMSHwobQUdFTlRfU0VTU0lPTl9TVEFURV9TVE9QUEVEEAQSHwobQUdFTlRfU0VTU0lPTl9TVEFURV9FUlJPUkVEEAUSJAogQUdFTlRfU0VTU0lPTl9TVEFURV9ESVNDT05ORUNURUQQBirSAQoTQWdlbnRUb29sQ2FsbFN0YXR1cxImCiJBR0VOVF9UT09MX0NBTExfU1RBVFVTX1VOU1BFQ0lGSUVEEAASIgoeQUdFTlRfVE9PTF9DQUxMX1NUQVRVU19QRU5ESU5HEAESJgoiQUdFTlRfVE9PTF9DQUxMX1NUQVRVU19JTl9QUk9HUkVTUxACEiQKIEFHRU5UX1RPT0xfQ0FMTF9TVEFUVVNfQ09NUExFVEVEEAMSIQodQUdFTlRfVE9PTF9DQUxMX1NUQVRVU19GQUlMRUQQBCq0AQoUQWdlbnRQbGFuRW50cnlTdGF0dXMSJwojQUdFTlRfUExBTl9FTlRSWV9TVEFUVVNfVU5TUEVDSUZJRUQQABIjCh9BR0VOVF9QTEFOX0VOVFJZX1NUQVRVU19QRU5ESU5HEAESJwojQUdFTlRfUExBTl9FTlRSWV9TVEFUVVNfSU5fUFJPR1JFU1MQAhIlCiFBR0VOVF9QTEFOX0VOVFJZX1NUQVRVU19DT01QTEVURUQQAyqEAQoUU2Vzc2lvbkluamVjdGlvbktpbmQSJgoiU0VTU0lPTl9JTkpFQ1RJT05fS0lORF9VTlNQRUNJRklFRBAAEiAKHFNFU1NJT05fSU5KRUNUSU9OX0tJTkRfU1RFRVIQARIiCh5TRVNTSU9OX0lOSkVDVElPTl9LSU5EX0RFTElWRVIQAirxAQoKSXNzdWVTdGF0ZRIbChdJU1NVRV9TVEFURV9VTlNQRUNJRklFRBAAEhcKE0lTU1VFX1NUQVRFX0JBQ0tMT0cQARIUChBJU1NVRV9TVEFURV9UT0RPEAISFgoSSVNTVUVfU1RBVEVfUVVFVUVEEAMSFwoTSVNTVUVfU1RBVEVfQkxPQ0tFRBAEEhsKF0lTU1VFX1NUQVRFX0lOX1BST0dSRVNTEAUSGQoVSVNTVUVfU1RBVEVfSU5fUkVWSUVXEAYSFAoQSVNTVUVfU1RBVEVfRE9ORRAHEhgKFElTU1VFX1NUQVRFX0FSQ0hJVkVEEAgqnAEKDUZvcmdlUHJvdmlkZXISHgoaRk9SR0VfUFJPVklERVJfVU5TUEVDSUZJRUQQABIZChVGT1JHRV9QUk9WSURFUl9HSVRIVUIQARIZChVGT1JHRV9QUk9WSURFUl9HSVRMQUIQAhIaChZGT1JHRV9QUk9WSURFUl9GT1JHRUpPEAMSGQoVRk9SR0VfUFJPVklERVJfTElORUFSEAQyrQwKDkNvbXBhc3NTZXJ2aWNlElQKDUdldFNlcnZlckluZm8SIC5jb21wYXNzLnYxLkdldFNlcnZlckluZm9SZXF1ZXN0GiEuY29tcGFzcy52MS5HZXRTZXJ2ZXJJbmZvUmVzcG9uc2USPwoGV2hvQW1JEhkuY29tcGFzcy52MS5XaG9BbUlSZXF1ZXN0GhouY29tcGFzcy52MS5XaG9BbUlSZXNwb25zZRJcCg9TdWJzY3JpYmVFdmVudHMSIi5jb21wYXNzLnYxLlN1YnNjcmliZUV2ZW50c1JlcXVlc3QaIy5jb21wYXNzLnYxLlN1YnNjcmliZUV2ZW50c1Jlc3BvbnNlMAESWgoPTGlzdEJvYXJkSXNzdWVzEiIuY29tcGFzcy52MS5MaXN0Qm9hcmRJc3N1ZXNSZXF1ZXN0GiMuY29tcGFzcy52MS5MaXN0Qm9hcmRJc3N1ZXNSZXNwb25zZRJyChdQcm92aXNpb25BZ2VudFdvcmtzcGFjZRIqLmNvbXBhc3MudjEuUHJvdmlzaW9uQWdlbnRXb3Jrc3BhY2VSZXF1ZXN0GisuY29tcGFzcy52MS5Qcm92aXNpb25BZ2VudFdvcmtzcGFjZVJlc3BvbnNlEmAKEVN0YXJ0QWdlbnRTZXNzaW9uEiQuY29tcGFzcy52MS5TdGFydEFnZW50U2Vzc2lvblJlcXVlc3QaJS5jb21wYXNzLnYxLlN0YXJ0QWdlbnRTZXNzaW9uUmVzcG9uc2USSwoKU3Bhd25BZ2VudBIdLmNvbXBhc3MudjEuU3Bhd25BZ2VudFJlcXVlc3QaHi5jb21wYXNzLnYxLlNwYXduQWdlbnRSZXNwb25zZRJdChBTdG9wQWdlbnRTZXNzaW9uEiMuY29tcGFzcy52MS5TdG9wQWdlbnRTZXNzaW9uUmVxdWVzdBokLmNvbXBhc3MudjEuU3RvcEFnZW50U2Vzc2lvblJlc3BvbnNlEmkKFFJlbW92ZUFnZW50V29ya3NwYWNlEicuY29tcGFzcy52MS5SZW1vdmVBZ2VudFdvcmtzcGFjZVJlcXVlc3QaKC5jb21wYXNzLnYxLlJlbW92ZUFnZW50V29ya3NwYWNlUmVzcG9uc2USYwoSUmVsb2FkQWdlbnRTZXNzaW9uEiUuY29tcGFzcy52MS5SZWxvYWRBZ2VudFNlc3Npb25SZXF1ZXN0GiYuY29tcGFzcy52MS5SZWxvYWRBZ2VudFNlc3Npb25SZXNwb25zZRJXCg5HZXRBZ2VudFN0YXR1cxIhLmNvbXBhc3MudjEuR2V0QWdlbnRTdGF0dXNSZXF1ZXN0GiIuY29tcGFzcy52MS5HZXRBZ2VudFN0YXR1c1Jlc3BvbnNlEmIKFVN1YnNjcmliZUFnZW50U2Vzc2lvbhIoLmNvbXBhc3MudjEuU3Vic2NyaWJlQWdlbnRTZXNzaW9uUmVxdWVzdBodLmNvbXBhc3MudjEuQWdlbnRTZXNzaW9uRnJhbWUwARJLCgpJc3N1ZVRva2VuEh0uY29tcGFzcy52MS5Jc3N1ZVRva2VuUmVxdWVzdBoeLmNvbXBhc3MudjEuSXNzdWVUb2tlblJlc3BvbnNlEk4KC1Jldm9rZVRva2VuEh4uY29tcGFzcy52MS5SZXZva2VUb2tlblJlcXVlc3QaHy5jb21wYXNzLnYxLlJldm9rZVRva2VuUmVzcG9uc2USVwoOUHV0QWdlbnRDb25maWcSIS5jb21wYXNzLnYxLlB1dEFnZW50Q29uZmlnUmVxdWVzdBoiLmNvbXBhc3MudjEuUHV0QWdlbnRDb25maWdSZXNwb25zZRJjChJHZXRBZ2VudENvbmZpZ0luZm8SJS5jb21wYXNzLnYxLkdldEFnZW50Q29uZmlnSW5mb1JlcXVlc3QaJi5jb21wYXNzLnYxLkdldEFnZW50Q29uZmlnSW5mb1Jlc3BvbnNlEmAKEURlbGV0ZUFnZW50Q29uZmlnEiQuY29tcGFzcy52MS5EZWxldGVBZ2VudENvbmZpZ1JlcXVlc3QaJS5jb21wYXNzLnYxLkRlbGV0ZUFnZW50Q29uZmlnUmVzcG9uc2Uy/QEKDlNlY3JldHNTZXJ2aWNlEkgKCVNldFNlY3JldBIcLmNvbXBhc3MudjEuU2V0U2VjcmV0UmVxdWVzdBodLmNvbXBhc3MudjEuU2V0U2VjcmV0UmVzcG9uc2USTgoLTGlzdFNlY3JldHMSHi5jb21wYXNzLnYxLkxpc3RTZWNyZXRzUmVxdWVzdBofLmNvbXBhc3MudjEuTGlzdFNlY3JldHNSZXNwb25zZRJRCgxEZWxldGVTZWNyZXQSHy5jb21wYXNzLnYxLkRlbGV0ZVNlY3JldFJlcXVlc3QaIC5jb21wYXNzLnYxLkRlbGV0ZVNlY3JldFJlc3BvbnNlYgZwcm90bzM", [file_google_protobuf_timestamp]); + fileDesc("Chhjb21wYXNzL3YxL2NvbXBhc3MucHJvdG8SCmNvbXBhc3MudjEiqAEKEFNldFNlY3JldFJlcXVlc3QSDAoEbmFtZRgBIAEoCRISCgV2YWx1ZRgCIAEoCUIDgAEBEiwKCGRlbGl2ZXJ5GAMgASgOMhouY29tcGFzcy52MS5TZWNyZXREZWxpdmVyeRIkCgRraW5kGAQgASgOMhYuY29tcGFzcy52MS5TZWNyZXRLaW5kEhAKCHByb3ZpZGVyGAUgASgJEgwKBGhvc3QYBiABKAkiEwoRU2V0U2VjcmV0UmVzcG9uc2UiFAoSTGlzdFNlY3JldHNSZXF1ZXN0IkAKE0xpc3RTZWNyZXRzUmVzcG9uc2USKQoHc2VjcmV0cxgBIAMoCzIYLmNvbXBhc3MudjEuU2VjcmV0U3RhdHVzIqABCgxTZWNyZXRTdGF0dXMSDAoEbmFtZRgBIAEoCRIOCgZpc19zZXQYAiABKAgSLAoIZGVsaXZlcnkYAyABKA4yGi5jb21wYXNzLnYxLlNlY3JldERlbGl2ZXJ5EiQKBGtpbmQYBCABKA4yFi5jb21wYXNzLnYxLlNlY3JldEtpbmQSEAoIcHJvdmlkZXIYBSABKAkSDAoEaG9zdBgGIAEoCSIjChNEZWxldGVTZWNyZXRSZXF1ZXN0EgwKBG5hbWUYASABKAkiFgoURGVsZXRlU2VjcmV0UmVzcG9uc2UiFgoUR2V0U2VydmVySW5mb1JlcXVlc3QiPQoVR2V0U2VydmVySW5mb1Jlc3BvbnNlEg8KB3ZlcnNpb24YASABKAkSEwoLYXBpX3ZlcnNpb24YAiABKAkiDwoNV2hvQW1JUmVxdWVzdCIkCg5XaG9BbUlSZXNwb25zZRISCgphY2NvdW50X2lkGAEgASgJIkMKFlN1YnNjcmliZUV2ZW50c1JlcXVlc3QSEQoJc2luY2Vfc2VxGAEgASgEEhYKDmluc3RhbmNlX2Vwb2NoGAIgASgEIuIDChdTdWJzY3JpYmVFdmVudHNSZXNwb25zZRILCgNzZXEYASABKAQSEgoKYXRfdW5peF9tcxgCIAEoAxIWCg5pbnN0YW5jZV9lcG9jaBgDIAEoBBIUCgxzbmFwc2hvdF9zZXEYBCABKAQSMQoNc2VydmVyX3N0YXR1cxgKIAEoCzIYLmNvbXBhc3MudjEuU2VydmVyU3RhdHVzSAASNQoPcmVzeW5jX3JlcXVpcmVkGAsgASgLMhouY29tcGFzcy52MS5SZXN5bmNSZXF1aXJlZEgAEj4KFGFnZW50X3Nlc3Npb25fc3RhdHVzGAwgASgLMh4uY29tcGFzcy52MS5BZ2VudFNlc3Npb25TdGF0dXNIABI8ChNhZ2VudF9tZXNzYWdlX2NodW5rGA0gASgLMh0uY29tcGFzcy52MS5BZ2VudE1lc3NhZ2VDaHVua0gAEjQKD2FnZW50X3Rvb2xfY2FsbBgOIAEoCzIZLmNvbXBhc3MudjEuQWdlbnRUb29sQ2FsbEgAEisKCmFnZW50X3BsYW4YDyABKAsyFS5jb21wYXNzLnYxLkFnZW50UGxhbkgAEiIKBWlzc3VlGBAgASgLMhEuY29tcGFzcy52MS5Jc3N1ZUgAQgkKB3BheWxvYWQiLgoWTGlzdEJvYXJkSXNzdWVzUmVxdWVzdBIUCgxzbmFwc2hvdF9zZXEYASABKAQiPAoXTGlzdEJvYXJkSXNzdWVzUmVzcG9uc2USIQoGaXNzdWVzGAEgAygLMhEuY29tcGFzcy52MS5Jc3N1ZSI2CgxTZXJ2ZXJTdGF0dXMSJgoFc3RhdGUYASABKA4yFy5jb21wYXNzLnYxLlNlcnZlclN0YXRlIhAKDlJlc3luY1JlcXVpcmVkInAKEkFnZW50U2Vzc2lvblN0YXR1cxISCgpzZXNzaW9uX2lkGAEgASgJEiwKBXN0YXRlGAIgASgOMh0uY29tcGFzcy52MS5BZ2VudFNlc3Npb25TdGF0ZRIYChBhZ2VudF9hY2NvdW50X2lkGAMgASgJIkkKEUFnZW50TWVzc2FnZUNodW5rEhIKCnNlc3Npb25faWQYASABKAkSDAoEdGV4dBgCIAEoCRISCgppc190aG91Z2h0GAMgASgIInkKDUFnZW50VG9vbENhbGwSEgoKc2Vzc2lvbl9pZBgBIAEoCRIUCgx0b29sX2NhbGxfaWQYAiABKAkSDQoFdGl0bGUYAyABKAkSLwoGc3RhdHVzGAQgASgOMh8uY29tcGFzcy52MS5BZ2VudFRvb2xDYWxsU3RhdHVzIkwKCUFnZW50UGxhbhISCgpzZXNzaW9uX2lkGAEgASgJEisKB2VudHJpZXMYAiADKAsyGi5jb21wYXNzLnYxLkFnZW50UGxhbkVudHJ5IlMKDkFnZW50UGxhbkVudHJ5Eg8KB2NvbnRlbnQYASABKAkSMAoGc3RhdHVzGAIgASgOMiAuY29tcGFzcy52MS5BZ2VudFBsYW5FbnRyeVN0YXR1cyKsAwoMU2Vzc2lvbkV2ZW50EhAKCGV2ZW50X2lkGAEgASgJEhIKCmF0X3VuaXhfbXMYAiABKAMSOgoOYXNzaXN0YW50X3RleHQYAyABKAsyIC5jb21wYXNzLnYxLlNlc3Npb25Bc3Npc3RhbnRUZXh0SAASLwoIdGhpbmtpbmcYBCABKAsyGy5jb21wYXNzLnYxLlNlc3Npb25UaGlua2luZ0gAEjAKCXRvb2xfY2FsbBgFIAEoCzIbLmNvbXBhc3MudjEuU2Vzc2lvblRvb2xDYWxsSAASPQoQdG9vbF9jYWxsX3VwZGF0ZRgGIAEoCzIhLmNvbXBhc3MudjEuU2Vzc2lvblRvb2xDYWxsVXBkYXRlSAASJwoEcGxhbhgHIAEoCzIXLmNvbXBhc3MudjEuU2Vzc2lvblBsYW5IABIrCgZub3RpY2UYCCABKAsyGS5jb21wYXNzLnYxLlNlc3Npb25Ob3RpY2VIABI5ChFzZXNzaW9uX2luamVjdGlvbhgJIAEoCzIcLmNvbXBhc3MudjEuU2Vzc2lvbkluamVjdGlvbkgAQgcKBWV2ZW50IjgKFFNlc3Npb25Bc3Npc3RhbnRUZXh0EgwKBHRleHQYASABKAkSEgoKbWVzc2FnZV9pZBgCIAEoCSIzCg9TZXNzaW9uVGhpbmtpbmcSDAoEdGV4dBgBIAEoCRISCgptZXNzYWdlX2lkGAIgASgJImcKD1Nlc3Npb25Ub29sQ2FsbBIUCgx0b29sX2NhbGxfaWQYASABKAkSDQoFdGl0bGUYAiABKAkSLwoGc3RhdHVzGAMgASgOMh8uY29tcGFzcy52MS5BZ2VudFRvb2xDYWxsU3RhdHVzIpoBChVTZXNzaW9uVG9vbENhbGxVcGRhdGUSFAoMdG9vbF9jYWxsX2lkGAEgASgJEi8KBnN0YXR1cxgCIAEoDjIfLmNvbXBhc3MudjEuQWdlbnRUb29sQ2FsbFN0YXR1cxIOCgZvdXRwdXQYAyABKAkSKgoFZGlmZnMYBCADKAsyGy5jb21wYXNzLnYxLlNlc3Npb25GaWxlRGlmZiJVCg9TZXNzaW9uRmlsZURpZmYSDAoEcGF0aBgBIAEoCRIVCghvbGRfdGV4dBgCIAEoCUgAiAEBEhAKCG5ld190ZXh0GAMgASgJQgsKCV9vbGRfdGV4dCI6CgtTZXNzaW9uUGxhbhIrCgdlbnRyaWVzGAEgAygLMhouY29tcGFzcy52MS5BZ2VudFBsYW5FbnRyeSI5Cg1TZXNzaW9uTm90aWNlEgwKBHRleHQYASABKAkSEQoEbGluaxgCIAEoCUgAiAEBQgcKBV9saW5rIoMBChBTZXNzaW9uSW5qZWN0aW9uEjEKB29wX2tpbmQYASABKA4yIC5jb21wYXNzLnYxLlNlc3Npb25JbmplY3Rpb25LaW5kEhIKCm1lc3NhZ2VfaWQYAiABKAkSEwoLZnJvbV9oYW5kbGUYAyABKAkSEwoLdHJhY2VwYXJlbnQYBCABKAkiMgocU3Vic2NyaWJlQWdlbnRTZXNzaW9uUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJIn4KEUFnZW50U2Vzc2lvbkZyYW1lEhIKCnNlc3Npb25faWQYASABKAkSJwoFZXZlbnQYAiABKAsyGC5jb21wYXNzLnYxLlNlc3Npb25FdmVudBIsCgVzdGF0ZRgDIAEoDjIdLmNvbXBhc3MudjEuQWdlbnRTZXNzaW9uU3RhdGUicAoeUHJvdmlzaW9uQWdlbnRXb3Jrc3BhY2VSZXF1ZXN0EhQKDGFnZW50X2hhbmRsZRgBIAEoCRIZChFjbGllbnRfcmVxdWVzdF9pZBgCIAEoCRIPCgdwZXJzb25hGAMgASgJEgwKBHJvbGUYBCABKAkiOQofUHJvdmlzaW9uQWdlbnRXb3Jrc3BhY2VSZXNwb25zZRIWCg5jb250YWluZXJfbmFtZRgBIAEoCSJQChtSZW1vdmVBZ2VudFdvcmtzcGFjZVJlcXVlc3QSFgoOY29udGFpbmVyX25hbWUYASABKAkSGQoRY2xpZW50X3JlcXVlc3RfaWQYAiABKAkiHgocUmVtb3ZlQWdlbnRXb3Jrc3BhY2VSZXNwb25zZSJjChhTdGFydEFnZW50U2Vzc2lvblJlcXVlc3QSFgoOY29udGFpbmVyX25hbWUYASABKAkSGQoRcmVzdW1lX3Nlc3Npb25faWQYAyABKAlKBAgCEANSDmluaXRpYWxfcHJvbXB0Ii8KGVN0YXJ0QWdlbnRTZXNzaW9uUmVzcG9uc2USEgoKc2Vzc2lvbl9pZBgBIAEoCSJaChFTcGF3bkFnZW50UmVxdWVzdBIUCgxhZ2VudF9oYW5kbGUYASABKAkSGQoRY2xpZW50X3JlcXVlc3RfaWQYAyABKAlKBAgCEANSDmluaXRpYWxfcHJvbXB0IkAKElNwYXduQWdlbnRSZXNwb25zZRISCgpzZXNzaW9uX2lkGAEgASgJEhYKDmNvbnRhaW5lcl9uYW1lGAIgASgJIi0KF1N0b3BBZ2VudFNlc3Npb25SZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkiGgoYU3RvcEFnZW50U2Vzc2lvblJlc3BvbnNlIi8KGVJlbG9hZEFnZW50U2Vzc2lvblJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCSIwChpSZWxvYWRBZ2VudFNlc3Npb25SZXNwb25zZRISCgpzZXNzaW9uX2lkGAEgASgJIisKFUdldEFnZW50U3RhdHVzUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJIkoKFkdldEFnZW50U3RhdHVzUmVzcG9uc2USMAoIc3RhdHVzZXMYASADKAsyHi5jb21wYXNzLnYxLkFnZW50U2Vzc2lvblN0YXR1cyIrChFJc3N1ZVRva2VuUmVxdWVzdBIWCg5hY2NvdW50X2hhbmRsZRgBIAEoCSIjChJJc3N1ZVRva2VuUmVzcG9uc2USDQoFdG9rZW4YASABKAkiKAoSUmV2b2tlVG9rZW5SZXF1ZXN0EhIKBXRva2VuGAEgASgJQgOAAQEiFQoTUmV2b2tlVG9rZW5SZXNwb25zZSInChVQdXRBZ2VudENvbmZpZ1JlcXVlc3QSDgoGYnVuZGxlGAEgASgMIikKFlB1dEFnZW50Q29uZmlnUmVzcG9uc2USDwoHdmVyc2lvbhgBIAEoCSIbChlHZXRBZ2VudENvbmZpZ0luZm9SZXF1ZXN0ItoBChpHZXRBZ2VudENvbmZpZ0luZm9SZXNwb25zZRIPCgd2ZXJzaW9uGAEgASgJEg4KBnNraWxscxgCIAMoCRISCgpleHRlbnNpb25zGAMgAygJEhMKC21jcF9zZXJ2ZXJzGAQgAygJEhQKDGhhc19zZXR0aW5ncxgFIAEoCBIVCg1oYXNfYWdlbnRzX21kGAYgASgIEg0KBXJ1bGVzGAcgAygJEhEKCXN1YmFnZW50cxgIIAMoCRISCgpoYXNfbW9kZWxzGAkgASgIEg8KB3Byb21wdHMYCiADKAkiGgoYRGVsZXRlQWdlbnRDb25maWdSZXF1ZXN0IhsKGURlbGV0ZUFnZW50Q29uZmlnUmVzcG9uc2UiKAoQQWdlbnRBdHRyaWJ1dGlvbhIUCgxhZ2VudF9oYW5kbGUYASABKAkiRQoIRm9yZ2VSZWYSKwoIcHJvdmlkZXIYASABKA4yGS5jb21wYXNzLnYxLkZvcmdlUHJvdmlkZXISDAoEaG9zdBgCIAEoCSLUAwoFSXNzdWUSCgoCaWQYASABKAkSIwoFZm9yZ2UYAiABKAsyFC5jb21wYXNzLnYxLkZvcmdlUmVmEgwKBHJlcG8YAyABKAkSDgoGbnVtYmVyGAQgASgNEg0KBXRpdGxlGAUgASgJEgwKBGJvZHkYBiABKAkSEwoLZm9yZ2Vfc3RhdGUYByABKAkSCwoDdXJsGAggASgJEisKBWFnZW50GAkgASgLMhwuY29tcGFzcy52MS5BZ2VudEF0dHJpYnV0aW9uEhUKDWZvcmdlX2FjY291bnQYCiABKAkSDgoGbGFiZWxzGAsgAygJEi4KCnVwZGF0ZWRfYXQYEyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEiUKBXN0YXRlGAwgASgOMhYuY29tcGFzcy52MS5Jc3N1ZVN0YXRlEhAKCHByaW9yaXR5GA0gASgJEhAKCGFzc2lnbmVlGA4gASgJEg8KB3N1bW1hcnkYDyABKAkSDgoGYnJhbmNoGBAgASgJEiQKA3BycxgRIAMoCzIXLmNvbXBhc3MudjEuUHVsbFJlcXVlc3QSJwoHdHJhY2tlchgSIAEoCzIWLmNvbXBhc3MudjEuVHJhY2tlclJlZiKeAwoLUHVsbFJlcXVlc3QSIwoFZm9yZ2UYASABKAsyFC5jb21wYXNzLnYxLkZvcmdlUmVmEgwKBHJlcG8YAiABKAkSDgoGbnVtYmVyGAMgASgNEg0KBXRpdGxlGAQgASgJEhMKC2ZvcmdlX3N0YXRlGAUgASgJEgsKA3VybBgGIAEoCRIQCghoZWFkX3JlZhgHIAEoCRIQCghiYXNlX3JlZhgIIAEoCRIrCgVhZ2VudBgJIAEoCzIcLmNvbXBhc3MudjEuQWdlbnRBdHRyaWJ1dGlvbhIVCg1mb3JnZV9hY2NvdW50GAogASgJEg0KBWRyYWZ0GAsgASgIEikKB2NoYW5nZWQYDCABKAsyGC5jb21wYXNzLnYxLkNoYW5nZWRTdGF0cxIpCgZjaGVja3MYDSABKAsyGS5jb21wYXNzLnYxLkNoZWNrc1N1bW1hcnkSIwoHcmV2aWV3cxgOIAMoCzISLmNvbXBhc3MudjEuUmV2aWV3EikKB3RocmVhZHMYDyADKAsyGC5jb21wYXNzLnYxLlJldmlld1RocmVhZCJTCg1DaGVja3NTdW1tYXJ5EhAKCGhlYWRfc2hhGAEgASgJEg0KBXN0YXRlGAIgASgJEiEKBmNoZWNrcxgDIAMoCzIRLmNvbXBhc3MudjEuQ2hlY2siQwoFQ2hlY2sSDAoEbmFtZRgBIAEoCRINCgVzdGF0ZRgCIAEoCRILCgN1cmwYAyABKAkSEAoIcmVxdWlyZWQYBCABKAgiQwoMQ2hhbmdlZFN0YXRzEg0KBWZpbGVzGAEgASgNEhEKCWFkZGl0aW9ucxgCIAEoDRIRCglkZWxldGlvbnMYAyABKA0iQwoKVHJhY2tlclJlZhIMCgRraW5kGAEgASgJEgoKAmlkGAIgASgJEg4KBnN0YXR1cxgDIAEoCRILCgN1cmwYBCABKAkiRwoGUmV2aWV3Eg4KBmF1dGhvchgBIAEoCRIOCgZpc19ib3QYAiABKAgSDwoHdmVyZGljdBgDIAEoCRIMCgRib2R5GAQgASgJIlUKDFJldmlld1RocmVhZBIMCgRwYXRoGAEgASgJEhAKCHJlc29sdmVkGAIgASgIEiUKCGNvbW1lbnRzGAMgAygLMhMuY29tcGFzcy52MS5Db21tZW50IjcKB0NvbW1lbnQSDgoGYXV0aG9yGAEgASgJEg4KBmlzX2JvdBgCIAEoCBIMCgRib2R5GAMgASgJKmQKDlNlY3JldERlbGl2ZXJ5Eh8KG1NFQ1JFVF9ERUxJVkVSWV9VTlNQRUNJRklFRBAAEhgKFFNFQ1JFVF9ERUxJVkVSWV9GSUxFEAESFwoTU0VDUkVUX0RFTElWRVJZX0VOVhACKnAKClNlY3JldEtpbmQSGwoXU0VDUkVUX0tJTkRfVU5TUEVDSUZJRUQQABIXChNTRUNSRVRfS0lORF9HRU5FUklDEAESGAoUU0VDUkVUX0tJTkRfUFJPVklERVIQAhISCg5TRUNSRVRfS0lORF9HSBADKkMKC1NlcnZlclN0YXRlEhwKGFNFUlZFUl9TVEFURV9VTlNQRUNJRklFRBAAEhYKElNFUlZFUl9TVEFURV9SRUFEWRABKoICChFBZ2VudFNlc3Npb25TdGF0ZRIjCh9BR0VOVF9TRVNTSU9OX1NUQVRFX1VOU1BFQ0lGSUVEEAASIAocQUdFTlRfU0VTU0lPTl9TVEFURV9TVEFSVElORxABEh0KGUFHRU5UX1NFU1NJT05fU1RBVEVfUkVBRFkQAhIfChtBR0VOVF9TRVNTSU9OX1NUQVRFX1dPUktJTkcQAxIfChtBR0VOVF9TRVNTSU9OX1NUQVRFX1NUT1BQRUQQBBIfChtBR0VOVF9TRVNTSU9OX1NUQVRFX0VSUk9SRUQQBRIkCiBBR0VOVF9TRVNTSU9OX1NUQVRFX0RJU0NPTk5FQ1RFRBAGKtIBChNBZ2VudFRvb2xDYWxsU3RhdHVzEiYKIkFHRU5UX1RPT0xfQ0FMTF9TVEFUVVNfVU5TUEVDSUZJRUQQABIiCh5BR0VOVF9UT09MX0NBTExfU1RBVFVTX1BFTkRJTkcQARImCiJBR0VOVF9UT09MX0NBTExfU1RBVFVTX0lOX1BST0dSRVNTEAISJAogQUdFTlRfVE9PTF9DQUxMX1NUQVRVU19DT01QTEVURUQQAxIhCh1BR0VOVF9UT09MX0NBTExfU1RBVFVTX0ZBSUxFRBAEKrQBChRBZ2VudFBsYW5FbnRyeVN0YXR1cxInCiNBR0VOVF9QTEFOX0VOVFJZX1NUQVRVU19VTlNQRUNJRklFRBAAEiMKH0FHRU5UX1BMQU5fRU5UUllfU1RBVFVTX1BFTkRJTkcQARInCiNBR0VOVF9QTEFOX0VOVFJZX1NUQVRVU19JTl9QUk9HUkVTUxACEiUKIUFHRU5UX1BMQU5fRU5UUllfU1RBVFVTX0NPTVBMRVRFRBADKoQBChRTZXNzaW9uSW5qZWN0aW9uS2luZBImCiJTRVNTSU9OX0lOSkVDVElPTl9LSU5EX1VOU1BFQ0lGSUVEEAASIAocU0VTU0lPTl9JTkpFQ1RJT05fS0lORF9TVEVFUhABEiIKHlNFU1NJT05fSU5KRUNUSU9OX0tJTkRfREVMSVZFUhACKvEBCgpJc3N1ZVN0YXRlEhsKF0lTU1VFX1NUQVRFX1VOU1BFQ0lGSUVEEAASFwoTSVNTVUVfU1RBVEVfQkFDS0xPRxABEhQKEElTU1VFX1NUQVRFX1RPRE8QAhIWChJJU1NVRV9TVEFURV9RVUVVRUQQAxIXChNJU1NVRV9TVEFURV9CTE9DS0VEEAQSGwoXSVNTVUVfU1RBVEVfSU5fUFJPR1JFU1MQBRIZChVJU1NVRV9TVEFURV9JTl9SRVZJRVcQBhIUChBJU1NVRV9TVEFURV9ET05FEAcSGAoUSVNTVUVfU1RBVEVfQVJDSElWRUQQCCqcAQoNRm9yZ2VQcm92aWRlchIeChpGT1JHRV9QUk9WSURFUl9VTlNQRUNJRklFRBAAEhkKFUZPUkdFX1BST1ZJREVSX0dJVEhVQhABEhkKFUZPUkdFX1BST1ZJREVSX0dJVExBQhACEhoKFkZPUkdFX1BST1ZJREVSX0ZPUkdFSk8QAxIZChVGT1JHRV9QUk9WSURFUl9MSU5FQVIQBDKtDAoOQ29tcGFzc1NlcnZpY2USVAoNR2V0U2VydmVySW5mbxIgLmNvbXBhc3MudjEuR2V0U2VydmVySW5mb1JlcXVlc3QaIS5jb21wYXNzLnYxLkdldFNlcnZlckluZm9SZXNwb25zZRI/CgZXaG9BbUkSGS5jb21wYXNzLnYxLldob0FtSVJlcXVlc3QaGi5jb21wYXNzLnYxLldob0FtSVJlc3BvbnNlElwKD1N1YnNjcmliZUV2ZW50cxIiLmNvbXBhc3MudjEuU3Vic2NyaWJlRXZlbnRzUmVxdWVzdBojLmNvbXBhc3MudjEuU3Vic2NyaWJlRXZlbnRzUmVzcG9uc2UwARJaCg9MaXN0Qm9hcmRJc3N1ZXMSIi5jb21wYXNzLnYxLkxpc3RCb2FyZElzc3Vlc1JlcXVlc3QaIy5jb21wYXNzLnYxLkxpc3RCb2FyZElzc3Vlc1Jlc3BvbnNlEnIKF1Byb3Zpc2lvbkFnZW50V29ya3NwYWNlEiouY29tcGFzcy52MS5Qcm92aXNpb25BZ2VudFdvcmtzcGFjZVJlcXVlc3QaKy5jb21wYXNzLnYxLlByb3Zpc2lvbkFnZW50V29ya3NwYWNlUmVzcG9uc2USYAoRU3RhcnRBZ2VudFNlc3Npb24SJC5jb21wYXNzLnYxLlN0YXJ0QWdlbnRTZXNzaW9uUmVxdWVzdBolLmNvbXBhc3MudjEuU3RhcnRBZ2VudFNlc3Npb25SZXNwb25zZRJLCgpTcGF3bkFnZW50Eh0uY29tcGFzcy52MS5TcGF3bkFnZW50UmVxdWVzdBoeLmNvbXBhc3MudjEuU3Bhd25BZ2VudFJlc3BvbnNlEl0KEFN0b3BBZ2VudFNlc3Npb24SIy5jb21wYXNzLnYxLlN0b3BBZ2VudFNlc3Npb25SZXF1ZXN0GiQuY29tcGFzcy52MS5TdG9wQWdlbnRTZXNzaW9uUmVzcG9uc2USaQoUUmVtb3ZlQWdlbnRXb3Jrc3BhY2USJy5jb21wYXNzLnYxLlJlbW92ZUFnZW50V29ya3NwYWNlUmVxdWVzdBooLmNvbXBhc3MudjEuUmVtb3ZlQWdlbnRXb3Jrc3BhY2VSZXNwb25zZRJjChJSZWxvYWRBZ2VudFNlc3Npb24SJS5jb21wYXNzLnYxLlJlbG9hZEFnZW50U2Vzc2lvblJlcXVlc3QaJi5jb21wYXNzLnYxLlJlbG9hZEFnZW50U2Vzc2lvblJlc3BvbnNlElcKDkdldEFnZW50U3RhdHVzEiEuY29tcGFzcy52MS5HZXRBZ2VudFN0YXR1c1JlcXVlc3QaIi5jb21wYXNzLnYxLkdldEFnZW50U3RhdHVzUmVzcG9uc2USYgoVU3Vic2NyaWJlQWdlbnRTZXNzaW9uEiguY29tcGFzcy52MS5TdWJzY3JpYmVBZ2VudFNlc3Npb25SZXF1ZXN0Gh0uY29tcGFzcy52MS5BZ2VudFNlc3Npb25GcmFtZTABEksKCklzc3VlVG9rZW4SHS5jb21wYXNzLnYxLklzc3VlVG9rZW5SZXF1ZXN0Gh4uY29tcGFzcy52MS5Jc3N1ZVRva2VuUmVzcG9uc2USTgoLUmV2b2tlVG9rZW4SHi5jb21wYXNzLnYxLlJldm9rZVRva2VuUmVxdWVzdBofLmNvbXBhc3MudjEuUmV2b2tlVG9rZW5SZXNwb25zZRJXCg5QdXRBZ2VudENvbmZpZxIhLmNvbXBhc3MudjEuUHV0QWdlbnRDb25maWdSZXF1ZXN0GiIuY29tcGFzcy52MS5QdXRBZ2VudENvbmZpZ1Jlc3BvbnNlEmMKEkdldEFnZW50Q29uZmlnSW5mbxIlLmNvbXBhc3MudjEuR2V0QWdlbnRDb25maWdJbmZvUmVxdWVzdBomLmNvbXBhc3MudjEuR2V0QWdlbnRDb25maWdJbmZvUmVzcG9uc2USYAoRRGVsZXRlQWdlbnRDb25maWcSJC5jb21wYXNzLnYxLkRlbGV0ZUFnZW50Q29uZmlnUmVxdWVzdBolLmNvbXBhc3MudjEuRGVsZXRlQWdlbnRDb25maWdSZXNwb25zZTL9AQoOU2VjcmV0c1NlcnZpY2USSAoJU2V0U2VjcmV0EhwuY29tcGFzcy52MS5TZXRTZWNyZXRSZXF1ZXN0Gh0uY29tcGFzcy52MS5TZXRTZWNyZXRSZXNwb25zZRJOCgtMaXN0U2VjcmV0cxIeLmNvbXBhc3MudjEuTGlzdFNlY3JldHNSZXF1ZXN0Gh8uY29tcGFzcy52MS5MaXN0U2VjcmV0c1Jlc3BvbnNlElEKDERlbGV0ZVNlY3JldBIfLmNvbXBhc3MudjEuRGVsZXRlU2VjcmV0UmVxdWVzdBogLmNvbXBhc3MudjEuRGVsZXRlU2VjcmV0UmVzcG9uc2ViBnByb3RvMw", [file_google_protobuf_timestamp]); /** * @generated from message compass.v1.SetSecretRequest @@ -1624,6 +1624,15 @@ export type GetAgentConfigInfoResponse = Message<"compass.v1.GetAgentConfigInfoR * @generated from field: bool has_models = 9; */ hasModels: boolean; + + /** + * The declared role-prompt names (the in prompts//SYSTEM.md), + * names only. Lets the operator verify a shipped role prompt before the seed + * flips the root to that role (RIG-3075 T2). + * + * @generated from field: repeated string prompts = 10; + */ + prompts: string[]; }; /** diff --git a/packages/compass-client/src/gen/compass/v1/compass_pb.ts b/packages/compass-client/src/gen/compass/v1/compass_pb.ts index 932b67f33..c53a6f0d5 100644 --- a/packages/compass-client/src/gen/compass/v1/compass_pb.ts +++ b/packages/compass-client/src/gen/compass/v1/compass_pb.ts @@ -20,7 +20,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file compass/v1/compass.proto. */ export const file_compass_v1_compass: GenFile = /*@__PURE__*/ - fileDesc("Chhjb21wYXNzL3YxL2NvbXBhc3MucHJvdG8SCmNvbXBhc3MudjEiqAEKEFNldFNlY3JldFJlcXVlc3QSDAoEbmFtZRgBIAEoCRISCgV2YWx1ZRgCIAEoCUIDgAEBEiwKCGRlbGl2ZXJ5GAMgASgOMhouY29tcGFzcy52MS5TZWNyZXREZWxpdmVyeRIkCgRraW5kGAQgASgOMhYuY29tcGFzcy52MS5TZWNyZXRLaW5kEhAKCHByb3ZpZGVyGAUgASgJEgwKBGhvc3QYBiABKAkiEwoRU2V0U2VjcmV0UmVzcG9uc2UiFAoSTGlzdFNlY3JldHNSZXF1ZXN0IkAKE0xpc3RTZWNyZXRzUmVzcG9uc2USKQoHc2VjcmV0cxgBIAMoCzIYLmNvbXBhc3MudjEuU2VjcmV0U3RhdHVzIqABCgxTZWNyZXRTdGF0dXMSDAoEbmFtZRgBIAEoCRIOCgZpc19zZXQYAiABKAgSLAoIZGVsaXZlcnkYAyABKA4yGi5jb21wYXNzLnYxLlNlY3JldERlbGl2ZXJ5EiQKBGtpbmQYBCABKA4yFi5jb21wYXNzLnYxLlNlY3JldEtpbmQSEAoIcHJvdmlkZXIYBSABKAkSDAoEaG9zdBgGIAEoCSIjChNEZWxldGVTZWNyZXRSZXF1ZXN0EgwKBG5hbWUYASABKAkiFgoURGVsZXRlU2VjcmV0UmVzcG9uc2UiFgoUR2V0U2VydmVySW5mb1JlcXVlc3QiPQoVR2V0U2VydmVySW5mb1Jlc3BvbnNlEg8KB3ZlcnNpb24YASABKAkSEwoLYXBpX3ZlcnNpb24YAiABKAkiDwoNV2hvQW1JUmVxdWVzdCIkCg5XaG9BbUlSZXNwb25zZRISCgphY2NvdW50X2lkGAEgASgJIkMKFlN1YnNjcmliZUV2ZW50c1JlcXVlc3QSEQoJc2luY2Vfc2VxGAEgASgEEhYKDmluc3RhbmNlX2Vwb2NoGAIgASgEIuIDChdTdWJzY3JpYmVFdmVudHNSZXNwb25zZRILCgNzZXEYASABKAQSEgoKYXRfdW5peF9tcxgCIAEoAxIWCg5pbnN0YW5jZV9lcG9jaBgDIAEoBBIUCgxzbmFwc2hvdF9zZXEYBCABKAQSMQoNc2VydmVyX3N0YXR1cxgKIAEoCzIYLmNvbXBhc3MudjEuU2VydmVyU3RhdHVzSAASNQoPcmVzeW5jX3JlcXVpcmVkGAsgASgLMhouY29tcGFzcy52MS5SZXN5bmNSZXF1aXJlZEgAEj4KFGFnZW50X3Nlc3Npb25fc3RhdHVzGAwgASgLMh4uY29tcGFzcy52MS5BZ2VudFNlc3Npb25TdGF0dXNIABI8ChNhZ2VudF9tZXNzYWdlX2NodW5rGA0gASgLMh0uY29tcGFzcy52MS5BZ2VudE1lc3NhZ2VDaHVua0gAEjQKD2FnZW50X3Rvb2xfY2FsbBgOIAEoCzIZLmNvbXBhc3MudjEuQWdlbnRUb29sQ2FsbEgAEisKCmFnZW50X3BsYW4YDyABKAsyFS5jb21wYXNzLnYxLkFnZW50UGxhbkgAEiIKBWlzc3VlGBAgASgLMhEuY29tcGFzcy52MS5Jc3N1ZUgAQgkKB3BheWxvYWQiLgoWTGlzdEJvYXJkSXNzdWVzUmVxdWVzdBIUCgxzbmFwc2hvdF9zZXEYASABKAQiPAoXTGlzdEJvYXJkSXNzdWVzUmVzcG9uc2USIQoGaXNzdWVzGAEgAygLMhEuY29tcGFzcy52MS5Jc3N1ZSI2CgxTZXJ2ZXJTdGF0dXMSJgoFc3RhdGUYASABKA4yFy5jb21wYXNzLnYxLlNlcnZlclN0YXRlIhAKDlJlc3luY1JlcXVpcmVkInAKEkFnZW50U2Vzc2lvblN0YXR1cxISCgpzZXNzaW9uX2lkGAEgASgJEiwKBXN0YXRlGAIgASgOMh0uY29tcGFzcy52MS5BZ2VudFNlc3Npb25TdGF0ZRIYChBhZ2VudF9hY2NvdW50X2lkGAMgASgJIkkKEUFnZW50TWVzc2FnZUNodW5rEhIKCnNlc3Npb25faWQYASABKAkSDAoEdGV4dBgCIAEoCRISCgppc190aG91Z2h0GAMgASgIInkKDUFnZW50VG9vbENhbGwSEgoKc2Vzc2lvbl9pZBgBIAEoCRIUCgx0b29sX2NhbGxfaWQYAiABKAkSDQoFdGl0bGUYAyABKAkSLwoGc3RhdHVzGAQgASgOMh8uY29tcGFzcy52MS5BZ2VudFRvb2xDYWxsU3RhdHVzIkwKCUFnZW50UGxhbhISCgpzZXNzaW9uX2lkGAEgASgJEisKB2VudHJpZXMYAiADKAsyGi5jb21wYXNzLnYxLkFnZW50UGxhbkVudHJ5IlMKDkFnZW50UGxhbkVudHJ5Eg8KB2NvbnRlbnQYASABKAkSMAoGc3RhdHVzGAIgASgOMiAuY29tcGFzcy52MS5BZ2VudFBsYW5FbnRyeVN0YXR1cyKsAwoMU2Vzc2lvbkV2ZW50EhAKCGV2ZW50X2lkGAEgASgJEhIKCmF0X3VuaXhfbXMYAiABKAMSOgoOYXNzaXN0YW50X3RleHQYAyABKAsyIC5jb21wYXNzLnYxLlNlc3Npb25Bc3Npc3RhbnRUZXh0SAASLwoIdGhpbmtpbmcYBCABKAsyGy5jb21wYXNzLnYxLlNlc3Npb25UaGlua2luZ0gAEjAKCXRvb2xfY2FsbBgFIAEoCzIbLmNvbXBhc3MudjEuU2Vzc2lvblRvb2xDYWxsSAASPQoQdG9vbF9jYWxsX3VwZGF0ZRgGIAEoCzIhLmNvbXBhc3MudjEuU2Vzc2lvblRvb2xDYWxsVXBkYXRlSAASJwoEcGxhbhgHIAEoCzIXLmNvbXBhc3MudjEuU2Vzc2lvblBsYW5IABIrCgZub3RpY2UYCCABKAsyGS5jb21wYXNzLnYxLlNlc3Npb25Ob3RpY2VIABI5ChFzZXNzaW9uX2luamVjdGlvbhgJIAEoCzIcLmNvbXBhc3MudjEuU2Vzc2lvbkluamVjdGlvbkgAQgcKBWV2ZW50IjgKFFNlc3Npb25Bc3Npc3RhbnRUZXh0EgwKBHRleHQYASABKAkSEgoKbWVzc2FnZV9pZBgCIAEoCSIzCg9TZXNzaW9uVGhpbmtpbmcSDAoEdGV4dBgBIAEoCRISCgptZXNzYWdlX2lkGAIgASgJImcKD1Nlc3Npb25Ub29sQ2FsbBIUCgx0b29sX2NhbGxfaWQYASABKAkSDQoFdGl0bGUYAiABKAkSLwoGc3RhdHVzGAMgASgOMh8uY29tcGFzcy52MS5BZ2VudFRvb2xDYWxsU3RhdHVzIpoBChVTZXNzaW9uVG9vbENhbGxVcGRhdGUSFAoMdG9vbF9jYWxsX2lkGAEgASgJEi8KBnN0YXR1cxgCIAEoDjIfLmNvbXBhc3MudjEuQWdlbnRUb29sQ2FsbFN0YXR1cxIOCgZvdXRwdXQYAyABKAkSKgoFZGlmZnMYBCADKAsyGy5jb21wYXNzLnYxLlNlc3Npb25GaWxlRGlmZiJVCg9TZXNzaW9uRmlsZURpZmYSDAoEcGF0aBgBIAEoCRIVCghvbGRfdGV4dBgCIAEoCUgAiAEBEhAKCG5ld190ZXh0GAMgASgJQgsKCV9vbGRfdGV4dCI6CgtTZXNzaW9uUGxhbhIrCgdlbnRyaWVzGAEgAygLMhouY29tcGFzcy52MS5BZ2VudFBsYW5FbnRyeSI5Cg1TZXNzaW9uTm90aWNlEgwKBHRleHQYASABKAkSEQoEbGluaxgCIAEoCUgAiAEBQgcKBV9saW5rIoMBChBTZXNzaW9uSW5qZWN0aW9uEjEKB29wX2tpbmQYASABKA4yIC5jb21wYXNzLnYxLlNlc3Npb25JbmplY3Rpb25LaW5kEhIKCm1lc3NhZ2VfaWQYAiABKAkSEwoLZnJvbV9oYW5kbGUYAyABKAkSEwoLdHJhY2VwYXJlbnQYBCABKAkiMgocU3Vic2NyaWJlQWdlbnRTZXNzaW9uUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJIn4KEUFnZW50U2Vzc2lvbkZyYW1lEhIKCnNlc3Npb25faWQYASABKAkSJwoFZXZlbnQYAiABKAsyGC5jb21wYXNzLnYxLlNlc3Npb25FdmVudBIsCgVzdGF0ZRgDIAEoDjIdLmNvbXBhc3MudjEuQWdlbnRTZXNzaW9uU3RhdGUicAoeUHJvdmlzaW9uQWdlbnRXb3Jrc3BhY2VSZXF1ZXN0EhQKDGFnZW50X2hhbmRsZRgBIAEoCRIZChFjbGllbnRfcmVxdWVzdF9pZBgCIAEoCRIPCgdwZXJzb25hGAMgASgJEgwKBHJvbGUYBCABKAkiOQofUHJvdmlzaW9uQWdlbnRXb3Jrc3BhY2VSZXNwb25zZRIWCg5jb250YWluZXJfbmFtZRgBIAEoCSJQChtSZW1vdmVBZ2VudFdvcmtzcGFjZVJlcXVlc3QSFgoOY29udGFpbmVyX25hbWUYASABKAkSGQoRY2xpZW50X3JlcXVlc3RfaWQYAiABKAkiHgocUmVtb3ZlQWdlbnRXb3Jrc3BhY2VSZXNwb25zZSJjChhTdGFydEFnZW50U2Vzc2lvblJlcXVlc3QSFgoOY29udGFpbmVyX25hbWUYASABKAkSGQoRcmVzdW1lX3Nlc3Npb25faWQYAyABKAlKBAgCEANSDmluaXRpYWxfcHJvbXB0Ii8KGVN0YXJ0QWdlbnRTZXNzaW9uUmVzcG9uc2USEgoKc2Vzc2lvbl9pZBgBIAEoCSJaChFTcGF3bkFnZW50UmVxdWVzdBIUCgxhZ2VudF9oYW5kbGUYASABKAkSGQoRY2xpZW50X3JlcXVlc3RfaWQYAyABKAlKBAgCEANSDmluaXRpYWxfcHJvbXB0IkAKElNwYXduQWdlbnRSZXNwb25zZRISCgpzZXNzaW9uX2lkGAEgASgJEhYKDmNvbnRhaW5lcl9uYW1lGAIgASgJIi0KF1N0b3BBZ2VudFNlc3Npb25SZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkiGgoYU3RvcEFnZW50U2Vzc2lvblJlc3BvbnNlIi8KGVJlbG9hZEFnZW50U2Vzc2lvblJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCSIwChpSZWxvYWRBZ2VudFNlc3Npb25SZXNwb25zZRISCgpzZXNzaW9uX2lkGAEgASgJIisKFUdldEFnZW50U3RhdHVzUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJIkoKFkdldEFnZW50U3RhdHVzUmVzcG9uc2USMAoIc3RhdHVzZXMYASADKAsyHi5jb21wYXNzLnYxLkFnZW50U2Vzc2lvblN0YXR1cyIrChFJc3N1ZVRva2VuUmVxdWVzdBIWCg5hY2NvdW50X2hhbmRsZRgBIAEoCSIjChJJc3N1ZVRva2VuUmVzcG9uc2USDQoFdG9rZW4YASABKAkiKAoSUmV2b2tlVG9rZW5SZXF1ZXN0EhIKBXRva2VuGAEgASgJQgOAAQEiFQoTUmV2b2tlVG9rZW5SZXNwb25zZSInChVQdXRBZ2VudENvbmZpZ1JlcXVlc3QSDgoGYnVuZGxlGAEgASgMIikKFlB1dEFnZW50Q29uZmlnUmVzcG9uc2USDwoHdmVyc2lvbhgBIAEoCSIbChlHZXRBZ2VudENvbmZpZ0luZm9SZXF1ZXN0IskBChpHZXRBZ2VudENvbmZpZ0luZm9SZXNwb25zZRIPCgd2ZXJzaW9uGAEgASgJEg4KBnNraWxscxgCIAMoCRISCgpleHRlbnNpb25zGAMgAygJEhMKC21jcF9zZXJ2ZXJzGAQgAygJEhQKDGhhc19zZXR0aW5ncxgFIAEoCBIVCg1oYXNfYWdlbnRzX21kGAYgASgIEg0KBXJ1bGVzGAcgAygJEhEKCXN1YmFnZW50cxgIIAMoCRISCgpoYXNfbW9kZWxzGAkgASgIIhoKGERlbGV0ZUFnZW50Q29uZmlnUmVxdWVzdCIbChlEZWxldGVBZ2VudENvbmZpZ1Jlc3BvbnNlIigKEEFnZW50QXR0cmlidXRpb24SFAoMYWdlbnRfaGFuZGxlGAEgASgJIkUKCEZvcmdlUmVmEisKCHByb3ZpZGVyGAEgASgOMhkuY29tcGFzcy52MS5Gb3JnZVByb3ZpZGVyEgwKBGhvc3QYAiABKAki1AMKBUlzc3VlEgoKAmlkGAEgASgJEiMKBWZvcmdlGAIgASgLMhQuY29tcGFzcy52MS5Gb3JnZVJlZhIMCgRyZXBvGAMgASgJEg4KBm51bWJlchgEIAEoDRINCgV0aXRsZRgFIAEoCRIMCgRib2R5GAYgASgJEhMKC2ZvcmdlX3N0YXRlGAcgASgJEgsKA3VybBgIIAEoCRIrCgVhZ2VudBgJIAEoCzIcLmNvbXBhc3MudjEuQWdlbnRBdHRyaWJ1dGlvbhIVCg1mb3JnZV9hY2NvdW50GAogASgJEg4KBmxhYmVscxgLIAMoCRIuCgp1cGRhdGVkX2F0GBMgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIlCgVzdGF0ZRgMIAEoDjIWLmNvbXBhc3MudjEuSXNzdWVTdGF0ZRIQCghwcmlvcml0eRgNIAEoCRIQCghhc3NpZ25lZRgOIAEoCRIPCgdzdW1tYXJ5GA8gASgJEg4KBmJyYW5jaBgQIAEoCRIkCgNwcnMYESADKAsyFy5jb21wYXNzLnYxLlB1bGxSZXF1ZXN0EicKB3RyYWNrZXIYEiABKAsyFi5jb21wYXNzLnYxLlRyYWNrZXJSZWYingMKC1B1bGxSZXF1ZXN0EiMKBWZvcmdlGAEgASgLMhQuY29tcGFzcy52MS5Gb3JnZVJlZhIMCgRyZXBvGAIgASgJEg4KBm51bWJlchgDIAEoDRINCgV0aXRsZRgEIAEoCRITCgtmb3JnZV9zdGF0ZRgFIAEoCRILCgN1cmwYBiABKAkSEAoIaGVhZF9yZWYYByABKAkSEAoIYmFzZV9yZWYYCCABKAkSKwoFYWdlbnQYCSABKAsyHC5jb21wYXNzLnYxLkFnZW50QXR0cmlidXRpb24SFQoNZm9yZ2VfYWNjb3VudBgKIAEoCRINCgVkcmFmdBgLIAEoCBIpCgdjaGFuZ2VkGAwgASgLMhguY29tcGFzcy52MS5DaGFuZ2VkU3RhdHMSKQoGY2hlY2tzGA0gASgLMhkuY29tcGFzcy52MS5DaGVja3NTdW1tYXJ5EiMKB3Jldmlld3MYDiADKAsyEi5jb21wYXNzLnYxLlJldmlldxIpCgd0aHJlYWRzGA8gAygLMhguY29tcGFzcy52MS5SZXZpZXdUaHJlYWQiUwoNQ2hlY2tzU3VtbWFyeRIQCghoZWFkX3NoYRgBIAEoCRINCgVzdGF0ZRgCIAEoCRIhCgZjaGVja3MYAyADKAsyES5jb21wYXNzLnYxLkNoZWNrIkMKBUNoZWNrEgwKBG5hbWUYASABKAkSDQoFc3RhdGUYAiABKAkSCwoDdXJsGAMgASgJEhAKCHJlcXVpcmVkGAQgASgIIkMKDENoYW5nZWRTdGF0cxINCgVmaWxlcxgBIAEoDRIRCglhZGRpdGlvbnMYAiABKA0SEQoJZGVsZXRpb25zGAMgASgNIkMKClRyYWNrZXJSZWYSDAoEa2luZBgBIAEoCRIKCgJpZBgCIAEoCRIOCgZzdGF0dXMYAyABKAkSCwoDdXJsGAQgASgJIkcKBlJldmlldxIOCgZhdXRob3IYASABKAkSDgoGaXNfYm90GAIgASgIEg8KB3ZlcmRpY3QYAyABKAkSDAoEYm9keRgEIAEoCSJVCgxSZXZpZXdUaHJlYWQSDAoEcGF0aBgBIAEoCRIQCghyZXNvbHZlZBgCIAEoCBIlCghjb21tZW50cxgDIAMoCzITLmNvbXBhc3MudjEuQ29tbWVudCI3CgdDb21tZW50Eg4KBmF1dGhvchgBIAEoCRIOCgZpc19ib3QYAiABKAgSDAoEYm9keRgDIAEoCSpkCg5TZWNyZXREZWxpdmVyeRIfChtTRUNSRVRfREVMSVZFUllfVU5TUEVDSUZJRUQQABIYChRTRUNSRVRfREVMSVZFUllfRklMRRABEhcKE1NFQ1JFVF9ERUxJVkVSWV9FTlYQAipwCgpTZWNyZXRLaW5kEhsKF1NFQ1JFVF9LSU5EX1VOU1BFQ0lGSUVEEAASFwoTU0VDUkVUX0tJTkRfR0VORVJJQxABEhgKFFNFQ1JFVF9LSU5EX1BST1ZJREVSEAISEgoOU0VDUkVUX0tJTkRfR0gQAypDCgtTZXJ2ZXJTdGF0ZRIcChhTRVJWRVJfU1RBVEVfVU5TUEVDSUZJRUQQABIWChJTRVJWRVJfU1RBVEVfUkVBRFkQASqCAgoRQWdlbnRTZXNzaW9uU3RhdGUSIwofQUdFTlRfU0VTU0lPTl9TVEFURV9VTlNQRUNJRklFRBAAEiAKHEFHRU5UX1NFU1NJT05fU1RBVEVfU1RBUlRJTkcQARIdChlBR0VOVF9TRVNTSU9OX1NUQVRFX1JFQURZEAISHwobQUdFTlRfU0VTU0lPTl9TVEFURV9XT1JLSU5HEAMSHwobQUdFTlRfU0VTU0lPTl9TVEFURV9TVE9QUEVEEAQSHwobQUdFTlRfU0VTU0lPTl9TVEFURV9FUlJPUkVEEAUSJAogQUdFTlRfU0VTU0lPTl9TVEFURV9ESVNDT05ORUNURUQQBirSAQoTQWdlbnRUb29sQ2FsbFN0YXR1cxImCiJBR0VOVF9UT09MX0NBTExfU1RBVFVTX1VOU1BFQ0lGSUVEEAASIgoeQUdFTlRfVE9PTF9DQUxMX1NUQVRVU19QRU5ESU5HEAESJgoiQUdFTlRfVE9PTF9DQUxMX1NUQVRVU19JTl9QUk9HUkVTUxACEiQKIEFHRU5UX1RPT0xfQ0FMTF9TVEFUVVNfQ09NUExFVEVEEAMSIQodQUdFTlRfVE9PTF9DQUxMX1NUQVRVU19GQUlMRUQQBCq0AQoUQWdlbnRQbGFuRW50cnlTdGF0dXMSJwojQUdFTlRfUExBTl9FTlRSWV9TVEFUVVNfVU5TUEVDSUZJRUQQABIjCh9BR0VOVF9QTEFOX0VOVFJZX1NUQVRVU19QRU5ESU5HEAESJwojQUdFTlRfUExBTl9FTlRSWV9TVEFUVVNfSU5fUFJPR1JFU1MQAhIlCiFBR0VOVF9QTEFOX0VOVFJZX1NUQVRVU19DT01QTEVURUQQAyqEAQoUU2Vzc2lvbkluamVjdGlvbktpbmQSJgoiU0VTU0lPTl9JTkpFQ1RJT05fS0lORF9VTlNQRUNJRklFRBAAEiAKHFNFU1NJT05fSU5KRUNUSU9OX0tJTkRfU1RFRVIQARIiCh5TRVNTSU9OX0lOSkVDVElPTl9LSU5EX0RFTElWRVIQAirxAQoKSXNzdWVTdGF0ZRIbChdJU1NVRV9TVEFURV9VTlNQRUNJRklFRBAAEhcKE0lTU1VFX1NUQVRFX0JBQ0tMT0cQARIUChBJU1NVRV9TVEFURV9UT0RPEAISFgoSSVNTVUVfU1RBVEVfUVVFVUVEEAMSFwoTSVNTVUVfU1RBVEVfQkxPQ0tFRBAEEhsKF0lTU1VFX1NUQVRFX0lOX1BST0dSRVNTEAUSGQoVSVNTVUVfU1RBVEVfSU5fUkVWSUVXEAYSFAoQSVNTVUVfU1RBVEVfRE9ORRAHEhgKFElTU1VFX1NUQVRFX0FSQ0hJVkVEEAgqnAEKDUZvcmdlUHJvdmlkZXISHgoaRk9SR0VfUFJPVklERVJfVU5TUEVDSUZJRUQQABIZChVGT1JHRV9QUk9WSURFUl9HSVRIVUIQARIZChVGT1JHRV9QUk9WSURFUl9HSVRMQUIQAhIaChZGT1JHRV9QUk9WSURFUl9GT1JHRUpPEAMSGQoVRk9SR0VfUFJPVklERVJfTElORUFSEAQyrQwKDkNvbXBhc3NTZXJ2aWNlElQKDUdldFNlcnZlckluZm8SIC5jb21wYXNzLnYxLkdldFNlcnZlckluZm9SZXF1ZXN0GiEuY29tcGFzcy52MS5HZXRTZXJ2ZXJJbmZvUmVzcG9uc2USPwoGV2hvQW1JEhkuY29tcGFzcy52MS5XaG9BbUlSZXF1ZXN0GhouY29tcGFzcy52MS5XaG9BbUlSZXNwb25zZRJcCg9TdWJzY3JpYmVFdmVudHMSIi5jb21wYXNzLnYxLlN1YnNjcmliZUV2ZW50c1JlcXVlc3QaIy5jb21wYXNzLnYxLlN1YnNjcmliZUV2ZW50c1Jlc3BvbnNlMAESWgoPTGlzdEJvYXJkSXNzdWVzEiIuY29tcGFzcy52MS5MaXN0Qm9hcmRJc3N1ZXNSZXF1ZXN0GiMuY29tcGFzcy52MS5MaXN0Qm9hcmRJc3N1ZXNSZXNwb25zZRJyChdQcm92aXNpb25BZ2VudFdvcmtzcGFjZRIqLmNvbXBhc3MudjEuUHJvdmlzaW9uQWdlbnRXb3Jrc3BhY2VSZXF1ZXN0GisuY29tcGFzcy52MS5Qcm92aXNpb25BZ2VudFdvcmtzcGFjZVJlc3BvbnNlEmAKEVN0YXJ0QWdlbnRTZXNzaW9uEiQuY29tcGFzcy52MS5TdGFydEFnZW50U2Vzc2lvblJlcXVlc3QaJS5jb21wYXNzLnYxLlN0YXJ0QWdlbnRTZXNzaW9uUmVzcG9uc2USSwoKU3Bhd25BZ2VudBIdLmNvbXBhc3MudjEuU3Bhd25BZ2VudFJlcXVlc3QaHi5jb21wYXNzLnYxLlNwYXduQWdlbnRSZXNwb25zZRJdChBTdG9wQWdlbnRTZXNzaW9uEiMuY29tcGFzcy52MS5TdG9wQWdlbnRTZXNzaW9uUmVxdWVzdBokLmNvbXBhc3MudjEuU3RvcEFnZW50U2Vzc2lvblJlc3BvbnNlEmkKFFJlbW92ZUFnZW50V29ya3NwYWNlEicuY29tcGFzcy52MS5SZW1vdmVBZ2VudFdvcmtzcGFjZVJlcXVlc3QaKC5jb21wYXNzLnYxLlJlbW92ZUFnZW50V29ya3NwYWNlUmVzcG9uc2USYwoSUmVsb2FkQWdlbnRTZXNzaW9uEiUuY29tcGFzcy52MS5SZWxvYWRBZ2VudFNlc3Npb25SZXF1ZXN0GiYuY29tcGFzcy52MS5SZWxvYWRBZ2VudFNlc3Npb25SZXNwb25zZRJXCg5HZXRBZ2VudFN0YXR1cxIhLmNvbXBhc3MudjEuR2V0QWdlbnRTdGF0dXNSZXF1ZXN0GiIuY29tcGFzcy52MS5HZXRBZ2VudFN0YXR1c1Jlc3BvbnNlEmIKFVN1YnNjcmliZUFnZW50U2Vzc2lvbhIoLmNvbXBhc3MudjEuU3Vic2NyaWJlQWdlbnRTZXNzaW9uUmVxdWVzdBodLmNvbXBhc3MudjEuQWdlbnRTZXNzaW9uRnJhbWUwARJLCgpJc3N1ZVRva2VuEh0uY29tcGFzcy52MS5Jc3N1ZVRva2VuUmVxdWVzdBoeLmNvbXBhc3MudjEuSXNzdWVUb2tlblJlc3BvbnNlEk4KC1Jldm9rZVRva2VuEh4uY29tcGFzcy52MS5SZXZva2VUb2tlblJlcXVlc3QaHy5jb21wYXNzLnYxLlJldm9rZVRva2VuUmVzcG9uc2USVwoOUHV0QWdlbnRDb25maWcSIS5jb21wYXNzLnYxLlB1dEFnZW50Q29uZmlnUmVxdWVzdBoiLmNvbXBhc3MudjEuUHV0QWdlbnRDb25maWdSZXNwb25zZRJjChJHZXRBZ2VudENvbmZpZ0luZm8SJS5jb21wYXNzLnYxLkdldEFnZW50Q29uZmlnSW5mb1JlcXVlc3QaJi5jb21wYXNzLnYxLkdldEFnZW50Q29uZmlnSW5mb1Jlc3BvbnNlEmAKEURlbGV0ZUFnZW50Q29uZmlnEiQuY29tcGFzcy52MS5EZWxldGVBZ2VudENvbmZpZ1JlcXVlc3QaJS5jb21wYXNzLnYxLkRlbGV0ZUFnZW50Q29uZmlnUmVzcG9uc2Uy/QEKDlNlY3JldHNTZXJ2aWNlEkgKCVNldFNlY3JldBIcLmNvbXBhc3MudjEuU2V0U2VjcmV0UmVxdWVzdBodLmNvbXBhc3MudjEuU2V0U2VjcmV0UmVzcG9uc2USTgoLTGlzdFNlY3JldHMSHi5jb21wYXNzLnYxLkxpc3RTZWNyZXRzUmVxdWVzdBofLmNvbXBhc3MudjEuTGlzdFNlY3JldHNSZXNwb25zZRJRCgxEZWxldGVTZWNyZXQSHy5jb21wYXNzLnYxLkRlbGV0ZVNlY3JldFJlcXVlc3QaIC5jb21wYXNzLnYxLkRlbGV0ZVNlY3JldFJlc3BvbnNlYgZwcm90bzM", [file_google_protobuf_timestamp]); + fileDesc("Chhjb21wYXNzL3YxL2NvbXBhc3MucHJvdG8SCmNvbXBhc3MudjEiqAEKEFNldFNlY3JldFJlcXVlc3QSDAoEbmFtZRgBIAEoCRISCgV2YWx1ZRgCIAEoCUIDgAEBEiwKCGRlbGl2ZXJ5GAMgASgOMhouY29tcGFzcy52MS5TZWNyZXREZWxpdmVyeRIkCgRraW5kGAQgASgOMhYuY29tcGFzcy52MS5TZWNyZXRLaW5kEhAKCHByb3ZpZGVyGAUgASgJEgwKBGhvc3QYBiABKAkiEwoRU2V0U2VjcmV0UmVzcG9uc2UiFAoSTGlzdFNlY3JldHNSZXF1ZXN0IkAKE0xpc3RTZWNyZXRzUmVzcG9uc2USKQoHc2VjcmV0cxgBIAMoCzIYLmNvbXBhc3MudjEuU2VjcmV0U3RhdHVzIqABCgxTZWNyZXRTdGF0dXMSDAoEbmFtZRgBIAEoCRIOCgZpc19zZXQYAiABKAgSLAoIZGVsaXZlcnkYAyABKA4yGi5jb21wYXNzLnYxLlNlY3JldERlbGl2ZXJ5EiQKBGtpbmQYBCABKA4yFi5jb21wYXNzLnYxLlNlY3JldEtpbmQSEAoIcHJvdmlkZXIYBSABKAkSDAoEaG9zdBgGIAEoCSIjChNEZWxldGVTZWNyZXRSZXF1ZXN0EgwKBG5hbWUYASABKAkiFgoURGVsZXRlU2VjcmV0UmVzcG9uc2UiFgoUR2V0U2VydmVySW5mb1JlcXVlc3QiPQoVR2V0U2VydmVySW5mb1Jlc3BvbnNlEg8KB3ZlcnNpb24YASABKAkSEwoLYXBpX3ZlcnNpb24YAiABKAkiDwoNV2hvQW1JUmVxdWVzdCIkCg5XaG9BbUlSZXNwb25zZRISCgphY2NvdW50X2lkGAEgASgJIkMKFlN1YnNjcmliZUV2ZW50c1JlcXVlc3QSEQoJc2luY2Vfc2VxGAEgASgEEhYKDmluc3RhbmNlX2Vwb2NoGAIgASgEIuIDChdTdWJzY3JpYmVFdmVudHNSZXNwb25zZRILCgNzZXEYASABKAQSEgoKYXRfdW5peF9tcxgCIAEoAxIWCg5pbnN0YW5jZV9lcG9jaBgDIAEoBBIUCgxzbmFwc2hvdF9zZXEYBCABKAQSMQoNc2VydmVyX3N0YXR1cxgKIAEoCzIYLmNvbXBhc3MudjEuU2VydmVyU3RhdHVzSAASNQoPcmVzeW5jX3JlcXVpcmVkGAsgASgLMhouY29tcGFzcy52MS5SZXN5bmNSZXF1aXJlZEgAEj4KFGFnZW50X3Nlc3Npb25fc3RhdHVzGAwgASgLMh4uY29tcGFzcy52MS5BZ2VudFNlc3Npb25TdGF0dXNIABI8ChNhZ2VudF9tZXNzYWdlX2NodW5rGA0gASgLMh0uY29tcGFzcy52MS5BZ2VudE1lc3NhZ2VDaHVua0gAEjQKD2FnZW50X3Rvb2xfY2FsbBgOIAEoCzIZLmNvbXBhc3MudjEuQWdlbnRUb29sQ2FsbEgAEisKCmFnZW50X3BsYW4YDyABKAsyFS5jb21wYXNzLnYxLkFnZW50UGxhbkgAEiIKBWlzc3VlGBAgASgLMhEuY29tcGFzcy52MS5Jc3N1ZUgAQgkKB3BheWxvYWQiLgoWTGlzdEJvYXJkSXNzdWVzUmVxdWVzdBIUCgxzbmFwc2hvdF9zZXEYASABKAQiPAoXTGlzdEJvYXJkSXNzdWVzUmVzcG9uc2USIQoGaXNzdWVzGAEgAygLMhEuY29tcGFzcy52MS5Jc3N1ZSI2CgxTZXJ2ZXJTdGF0dXMSJgoFc3RhdGUYASABKA4yFy5jb21wYXNzLnYxLlNlcnZlclN0YXRlIhAKDlJlc3luY1JlcXVpcmVkInAKEkFnZW50U2Vzc2lvblN0YXR1cxISCgpzZXNzaW9uX2lkGAEgASgJEiwKBXN0YXRlGAIgASgOMh0uY29tcGFzcy52MS5BZ2VudFNlc3Npb25TdGF0ZRIYChBhZ2VudF9hY2NvdW50X2lkGAMgASgJIkkKEUFnZW50TWVzc2FnZUNodW5rEhIKCnNlc3Npb25faWQYASABKAkSDAoEdGV4dBgCIAEoCRISCgppc190aG91Z2h0GAMgASgIInkKDUFnZW50VG9vbENhbGwSEgoKc2Vzc2lvbl9pZBgBIAEoCRIUCgx0b29sX2NhbGxfaWQYAiABKAkSDQoFdGl0bGUYAyABKAkSLwoGc3RhdHVzGAQgASgOMh8uY29tcGFzcy52MS5BZ2VudFRvb2xDYWxsU3RhdHVzIkwKCUFnZW50UGxhbhISCgpzZXNzaW9uX2lkGAEgASgJEisKB2VudHJpZXMYAiADKAsyGi5jb21wYXNzLnYxLkFnZW50UGxhbkVudHJ5IlMKDkFnZW50UGxhbkVudHJ5Eg8KB2NvbnRlbnQYASABKAkSMAoGc3RhdHVzGAIgASgOMiAuY29tcGFzcy52MS5BZ2VudFBsYW5FbnRyeVN0YXR1cyKsAwoMU2Vzc2lvbkV2ZW50EhAKCGV2ZW50X2lkGAEgASgJEhIKCmF0X3VuaXhfbXMYAiABKAMSOgoOYXNzaXN0YW50X3RleHQYAyABKAsyIC5jb21wYXNzLnYxLlNlc3Npb25Bc3Npc3RhbnRUZXh0SAASLwoIdGhpbmtpbmcYBCABKAsyGy5jb21wYXNzLnYxLlNlc3Npb25UaGlua2luZ0gAEjAKCXRvb2xfY2FsbBgFIAEoCzIbLmNvbXBhc3MudjEuU2Vzc2lvblRvb2xDYWxsSAASPQoQdG9vbF9jYWxsX3VwZGF0ZRgGIAEoCzIhLmNvbXBhc3MudjEuU2Vzc2lvblRvb2xDYWxsVXBkYXRlSAASJwoEcGxhbhgHIAEoCzIXLmNvbXBhc3MudjEuU2Vzc2lvblBsYW5IABIrCgZub3RpY2UYCCABKAsyGS5jb21wYXNzLnYxLlNlc3Npb25Ob3RpY2VIABI5ChFzZXNzaW9uX2luamVjdGlvbhgJIAEoCzIcLmNvbXBhc3MudjEuU2Vzc2lvbkluamVjdGlvbkgAQgcKBWV2ZW50IjgKFFNlc3Npb25Bc3Npc3RhbnRUZXh0EgwKBHRleHQYASABKAkSEgoKbWVzc2FnZV9pZBgCIAEoCSIzCg9TZXNzaW9uVGhpbmtpbmcSDAoEdGV4dBgBIAEoCRISCgptZXNzYWdlX2lkGAIgASgJImcKD1Nlc3Npb25Ub29sQ2FsbBIUCgx0b29sX2NhbGxfaWQYASABKAkSDQoFdGl0bGUYAiABKAkSLwoGc3RhdHVzGAMgASgOMh8uY29tcGFzcy52MS5BZ2VudFRvb2xDYWxsU3RhdHVzIpoBChVTZXNzaW9uVG9vbENhbGxVcGRhdGUSFAoMdG9vbF9jYWxsX2lkGAEgASgJEi8KBnN0YXR1cxgCIAEoDjIfLmNvbXBhc3MudjEuQWdlbnRUb29sQ2FsbFN0YXR1cxIOCgZvdXRwdXQYAyABKAkSKgoFZGlmZnMYBCADKAsyGy5jb21wYXNzLnYxLlNlc3Npb25GaWxlRGlmZiJVCg9TZXNzaW9uRmlsZURpZmYSDAoEcGF0aBgBIAEoCRIVCghvbGRfdGV4dBgCIAEoCUgAiAEBEhAKCG5ld190ZXh0GAMgASgJQgsKCV9vbGRfdGV4dCI6CgtTZXNzaW9uUGxhbhIrCgdlbnRyaWVzGAEgAygLMhouY29tcGFzcy52MS5BZ2VudFBsYW5FbnRyeSI5Cg1TZXNzaW9uTm90aWNlEgwKBHRleHQYASABKAkSEQoEbGluaxgCIAEoCUgAiAEBQgcKBV9saW5rIoMBChBTZXNzaW9uSW5qZWN0aW9uEjEKB29wX2tpbmQYASABKA4yIC5jb21wYXNzLnYxLlNlc3Npb25JbmplY3Rpb25LaW5kEhIKCm1lc3NhZ2VfaWQYAiABKAkSEwoLZnJvbV9oYW5kbGUYAyABKAkSEwoLdHJhY2VwYXJlbnQYBCABKAkiMgocU3Vic2NyaWJlQWdlbnRTZXNzaW9uUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJIn4KEUFnZW50U2Vzc2lvbkZyYW1lEhIKCnNlc3Npb25faWQYASABKAkSJwoFZXZlbnQYAiABKAsyGC5jb21wYXNzLnYxLlNlc3Npb25FdmVudBIsCgVzdGF0ZRgDIAEoDjIdLmNvbXBhc3MudjEuQWdlbnRTZXNzaW9uU3RhdGUicAoeUHJvdmlzaW9uQWdlbnRXb3Jrc3BhY2VSZXF1ZXN0EhQKDGFnZW50X2hhbmRsZRgBIAEoCRIZChFjbGllbnRfcmVxdWVzdF9pZBgCIAEoCRIPCgdwZXJzb25hGAMgASgJEgwKBHJvbGUYBCABKAkiOQofUHJvdmlzaW9uQWdlbnRXb3Jrc3BhY2VSZXNwb25zZRIWCg5jb250YWluZXJfbmFtZRgBIAEoCSJQChtSZW1vdmVBZ2VudFdvcmtzcGFjZVJlcXVlc3QSFgoOY29udGFpbmVyX25hbWUYASABKAkSGQoRY2xpZW50X3JlcXVlc3RfaWQYAiABKAkiHgocUmVtb3ZlQWdlbnRXb3Jrc3BhY2VSZXNwb25zZSJjChhTdGFydEFnZW50U2Vzc2lvblJlcXVlc3QSFgoOY29udGFpbmVyX25hbWUYASABKAkSGQoRcmVzdW1lX3Nlc3Npb25faWQYAyABKAlKBAgCEANSDmluaXRpYWxfcHJvbXB0Ii8KGVN0YXJ0QWdlbnRTZXNzaW9uUmVzcG9uc2USEgoKc2Vzc2lvbl9pZBgBIAEoCSJaChFTcGF3bkFnZW50UmVxdWVzdBIUCgxhZ2VudF9oYW5kbGUYASABKAkSGQoRY2xpZW50X3JlcXVlc3RfaWQYAyABKAlKBAgCEANSDmluaXRpYWxfcHJvbXB0IkAKElNwYXduQWdlbnRSZXNwb25zZRISCgpzZXNzaW9uX2lkGAEgASgJEhYKDmNvbnRhaW5lcl9uYW1lGAIgASgJIi0KF1N0b3BBZ2VudFNlc3Npb25SZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkiGgoYU3RvcEFnZW50U2Vzc2lvblJlc3BvbnNlIi8KGVJlbG9hZEFnZW50U2Vzc2lvblJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCSIwChpSZWxvYWRBZ2VudFNlc3Npb25SZXNwb25zZRISCgpzZXNzaW9uX2lkGAEgASgJIisKFUdldEFnZW50U3RhdHVzUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJIkoKFkdldEFnZW50U3RhdHVzUmVzcG9uc2USMAoIc3RhdHVzZXMYASADKAsyHi5jb21wYXNzLnYxLkFnZW50U2Vzc2lvblN0YXR1cyIrChFJc3N1ZVRva2VuUmVxdWVzdBIWCg5hY2NvdW50X2hhbmRsZRgBIAEoCSIjChJJc3N1ZVRva2VuUmVzcG9uc2USDQoFdG9rZW4YASABKAkiKAoSUmV2b2tlVG9rZW5SZXF1ZXN0EhIKBXRva2VuGAEgASgJQgOAAQEiFQoTUmV2b2tlVG9rZW5SZXNwb25zZSInChVQdXRBZ2VudENvbmZpZ1JlcXVlc3QSDgoGYnVuZGxlGAEgASgMIikKFlB1dEFnZW50Q29uZmlnUmVzcG9uc2USDwoHdmVyc2lvbhgBIAEoCSIbChlHZXRBZ2VudENvbmZpZ0luZm9SZXF1ZXN0ItoBChpHZXRBZ2VudENvbmZpZ0luZm9SZXNwb25zZRIPCgd2ZXJzaW9uGAEgASgJEg4KBnNraWxscxgCIAMoCRISCgpleHRlbnNpb25zGAMgAygJEhMKC21jcF9zZXJ2ZXJzGAQgAygJEhQKDGhhc19zZXR0aW5ncxgFIAEoCBIVCg1oYXNfYWdlbnRzX21kGAYgASgIEg0KBXJ1bGVzGAcgAygJEhEKCXN1YmFnZW50cxgIIAMoCRISCgpoYXNfbW9kZWxzGAkgASgIEg8KB3Byb21wdHMYCiADKAkiGgoYRGVsZXRlQWdlbnRDb25maWdSZXF1ZXN0IhsKGURlbGV0ZUFnZW50Q29uZmlnUmVzcG9uc2UiKAoQQWdlbnRBdHRyaWJ1dGlvbhIUCgxhZ2VudF9oYW5kbGUYASABKAkiRQoIRm9yZ2VSZWYSKwoIcHJvdmlkZXIYASABKA4yGS5jb21wYXNzLnYxLkZvcmdlUHJvdmlkZXISDAoEaG9zdBgCIAEoCSLUAwoFSXNzdWUSCgoCaWQYASABKAkSIwoFZm9yZ2UYAiABKAsyFC5jb21wYXNzLnYxLkZvcmdlUmVmEgwKBHJlcG8YAyABKAkSDgoGbnVtYmVyGAQgASgNEg0KBXRpdGxlGAUgASgJEgwKBGJvZHkYBiABKAkSEwoLZm9yZ2Vfc3RhdGUYByABKAkSCwoDdXJsGAggASgJEisKBWFnZW50GAkgASgLMhwuY29tcGFzcy52MS5BZ2VudEF0dHJpYnV0aW9uEhUKDWZvcmdlX2FjY291bnQYCiABKAkSDgoGbGFiZWxzGAsgAygJEi4KCnVwZGF0ZWRfYXQYEyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEiUKBXN0YXRlGAwgASgOMhYuY29tcGFzcy52MS5Jc3N1ZVN0YXRlEhAKCHByaW9yaXR5GA0gASgJEhAKCGFzc2lnbmVlGA4gASgJEg8KB3N1bW1hcnkYDyABKAkSDgoGYnJhbmNoGBAgASgJEiQKA3BycxgRIAMoCzIXLmNvbXBhc3MudjEuUHVsbFJlcXVlc3QSJwoHdHJhY2tlchgSIAEoCzIWLmNvbXBhc3MudjEuVHJhY2tlclJlZiKeAwoLUHVsbFJlcXVlc3QSIwoFZm9yZ2UYASABKAsyFC5jb21wYXNzLnYxLkZvcmdlUmVmEgwKBHJlcG8YAiABKAkSDgoGbnVtYmVyGAMgASgNEg0KBXRpdGxlGAQgASgJEhMKC2ZvcmdlX3N0YXRlGAUgASgJEgsKA3VybBgGIAEoCRIQCghoZWFkX3JlZhgHIAEoCRIQCghiYXNlX3JlZhgIIAEoCRIrCgVhZ2VudBgJIAEoCzIcLmNvbXBhc3MudjEuQWdlbnRBdHRyaWJ1dGlvbhIVCg1mb3JnZV9hY2NvdW50GAogASgJEg0KBWRyYWZ0GAsgASgIEikKB2NoYW5nZWQYDCABKAsyGC5jb21wYXNzLnYxLkNoYW5nZWRTdGF0cxIpCgZjaGVja3MYDSABKAsyGS5jb21wYXNzLnYxLkNoZWNrc1N1bW1hcnkSIwoHcmV2aWV3cxgOIAMoCzISLmNvbXBhc3MudjEuUmV2aWV3EikKB3RocmVhZHMYDyADKAsyGC5jb21wYXNzLnYxLlJldmlld1RocmVhZCJTCg1DaGVja3NTdW1tYXJ5EhAKCGhlYWRfc2hhGAEgASgJEg0KBXN0YXRlGAIgASgJEiEKBmNoZWNrcxgDIAMoCzIRLmNvbXBhc3MudjEuQ2hlY2siQwoFQ2hlY2sSDAoEbmFtZRgBIAEoCRINCgVzdGF0ZRgCIAEoCRILCgN1cmwYAyABKAkSEAoIcmVxdWlyZWQYBCABKAgiQwoMQ2hhbmdlZFN0YXRzEg0KBWZpbGVzGAEgASgNEhEKCWFkZGl0aW9ucxgCIAEoDRIRCglkZWxldGlvbnMYAyABKA0iQwoKVHJhY2tlclJlZhIMCgRraW5kGAEgASgJEgoKAmlkGAIgASgJEg4KBnN0YXR1cxgDIAEoCRILCgN1cmwYBCABKAkiRwoGUmV2aWV3Eg4KBmF1dGhvchgBIAEoCRIOCgZpc19ib3QYAiABKAgSDwoHdmVyZGljdBgDIAEoCRIMCgRib2R5GAQgASgJIlUKDFJldmlld1RocmVhZBIMCgRwYXRoGAEgASgJEhAKCHJlc29sdmVkGAIgASgIEiUKCGNvbW1lbnRzGAMgAygLMhMuY29tcGFzcy52MS5Db21tZW50IjcKB0NvbW1lbnQSDgoGYXV0aG9yGAEgASgJEg4KBmlzX2JvdBgCIAEoCBIMCgRib2R5GAMgASgJKmQKDlNlY3JldERlbGl2ZXJ5Eh8KG1NFQ1JFVF9ERUxJVkVSWV9VTlNQRUNJRklFRBAAEhgKFFNFQ1JFVF9ERUxJVkVSWV9GSUxFEAESFwoTU0VDUkVUX0RFTElWRVJZX0VOVhACKnAKClNlY3JldEtpbmQSGwoXU0VDUkVUX0tJTkRfVU5TUEVDSUZJRUQQABIXChNTRUNSRVRfS0lORF9HRU5FUklDEAESGAoUU0VDUkVUX0tJTkRfUFJPVklERVIQAhISCg5TRUNSRVRfS0lORF9HSBADKkMKC1NlcnZlclN0YXRlEhwKGFNFUlZFUl9TVEFURV9VTlNQRUNJRklFRBAAEhYKElNFUlZFUl9TVEFURV9SRUFEWRABKoICChFBZ2VudFNlc3Npb25TdGF0ZRIjCh9BR0VOVF9TRVNTSU9OX1NUQVRFX1VOU1BFQ0lGSUVEEAASIAocQUdFTlRfU0VTU0lPTl9TVEFURV9TVEFSVElORxABEh0KGUFHRU5UX1NFU1NJT05fU1RBVEVfUkVBRFkQAhIfChtBR0VOVF9TRVNTSU9OX1NUQVRFX1dPUktJTkcQAxIfChtBR0VOVF9TRVNTSU9OX1NUQVRFX1NUT1BQRUQQBBIfChtBR0VOVF9TRVNTSU9OX1NUQVRFX0VSUk9SRUQQBRIkCiBBR0VOVF9TRVNTSU9OX1NUQVRFX0RJU0NPTk5FQ1RFRBAGKtIBChNBZ2VudFRvb2xDYWxsU3RhdHVzEiYKIkFHRU5UX1RPT0xfQ0FMTF9TVEFUVVNfVU5TUEVDSUZJRUQQABIiCh5BR0VOVF9UT09MX0NBTExfU1RBVFVTX1BFTkRJTkcQARImCiJBR0VOVF9UT09MX0NBTExfU1RBVFVTX0lOX1BST0dSRVNTEAISJAogQUdFTlRfVE9PTF9DQUxMX1NUQVRVU19DT01QTEVURUQQAxIhCh1BR0VOVF9UT09MX0NBTExfU1RBVFVTX0ZBSUxFRBAEKrQBChRBZ2VudFBsYW5FbnRyeVN0YXR1cxInCiNBR0VOVF9QTEFOX0VOVFJZX1NUQVRVU19VTlNQRUNJRklFRBAAEiMKH0FHRU5UX1BMQU5fRU5UUllfU1RBVFVTX1BFTkRJTkcQARInCiNBR0VOVF9QTEFOX0VOVFJZX1NUQVRVU19JTl9QUk9HUkVTUxACEiUKIUFHRU5UX1BMQU5fRU5UUllfU1RBVFVTX0NPTVBMRVRFRBADKoQBChRTZXNzaW9uSW5qZWN0aW9uS2luZBImCiJTRVNTSU9OX0lOSkVDVElPTl9LSU5EX1VOU1BFQ0lGSUVEEAASIAocU0VTU0lPTl9JTkpFQ1RJT05fS0lORF9TVEVFUhABEiIKHlNFU1NJT05fSU5KRUNUSU9OX0tJTkRfREVMSVZFUhACKvEBCgpJc3N1ZVN0YXRlEhsKF0lTU1VFX1NUQVRFX1VOU1BFQ0lGSUVEEAASFwoTSVNTVUVfU1RBVEVfQkFDS0xPRxABEhQKEElTU1VFX1NUQVRFX1RPRE8QAhIWChJJU1NVRV9TVEFURV9RVUVVRUQQAxIXChNJU1NVRV9TVEFURV9CTE9DS0VEEAQSGwoXSVNTVUVfU1RBVEVfSU5fUFJPR1JFU1MQBRIZChVJU1NVRV9TVEFURV9JTl9SRVZJRVcQBhIUChBJU1NVRV9TVEFURV9ET05FEAcSGAoUSVNTVUVfU1RBVEVfQVJDSElWRUQQCCqcAQoNRm9yZ2VQcm92aWRlchIeChpGT1JHRV9QUk9WSURFUl9VTlNQRUNJRklFRBAAEhkKFUZPUkdFX1BST1ZJREVSX0dJVEhVQhABEhkKFUZPUkdFX1BST1ZJREVSX0dJVExBQhACEhoKFkZPUkdFX1BST1ZJREVSX0ZPUkdFSk8QAxIZChVGT1JHRV9QUk9WSURFUl9MSU5FQVIQBDKtDAoOQ29tcGFzc1NlcnZpY2USVAoNR2V0U2VydmVySW5mbxIgLmNvbXBhc3MudjEuR2V0U2VydmVySW5mb1JlcXVlc3QaIS5jb21wYXNzLnYxLkdldFNlcnZlckluZm9SZXNwb25zZRI/CgZXaG9BbUkSGS5jb21wYXNzLnYxLldob0FtSVJlcXVlc3QaGi5jb21wYXNzLnYxLldob0FtSVJlc3BvbnNlElwKD1N1YnNjcmliZUV2ZW50cxIiLmNvbXBhc3MudjEuU3Vic2NyaWJlRXZlbnRzUmVxdWVzdBojLmNvbXBhc3MudjEuU3Vic2NyaWJlRXZlbnRzUmVzcG9uc2UwARJaCg9MaXN0Qm9hcmRJc3N1ZXMSIi5jb21wYXNzLnYxLkxpc3RCb2FyZElzc3Vlc1JlcXVlc3QaIy5jb21wYXNzLnYxLkxpc3RCb2FyZElzc3Vlc1Jlc3BvbnNlEnIKF1Byb3Zpc2lvbkFnZW50V29ya3NwYWNlEiouY29tcGFzcy52MS5Qcm92aXNpb25BZ2VudFdvcmtzcGFjZVJlcXVlc3QaKy5jb21wYXNzLnYxLlByb3Zpc2lvbkFnZW50V29ya3NwYWNlUmVzcG9uc2USYAoRU3RhcnRBZ2VudFNlc3Npb24SJC5jb21wYXNzLnYxLlN0YXJ0QWdlbnRTZXNzaW9uUmVxdWVzdBolLmNvbXBhc3MudjEuU3RhcnRBZ2VudFNlc3Npb25SZXNwb25zZRJLCgpTcGF3bkFnZW50Eh0uY29tcGFzcy52MS5TcGF3bkFnZW50UmVxdWVzdBoeLmNvbXBhc3MudjEuU3Bhd25BZ2VudFJlc3BvbnNlEl0KEFN0b3BBZ2VudFNlc3Npb24SIy5jb21wYXNzLnYxLlN0b3BBZ2VudFNlc3Npb25SZXF1ZXN0GiQuY29tcGFzcy52MS5TdG9wQWdlbnRTZXNzaW9uUmVzcG9uc2USaQoUUmVtb3ZlQWdlbnRXb3Jrc3BhY2USJy5jb21wYXNzLnYxLlJlbW92ZUFnZW50V29ya3NwYWNlUmVxdWVzdBooLmNvbXBhc3MudjEuUmVtb3ZlQWdlbnRXb3Jrc3BhY2VSZXNwb25zZRJjChJSZWxvYWRBZ2VudFNlc3Npb24SJS5jb21wYXNzLnYxLlJlbG9hZEFnZW50U2Vzc2lvblJlcXVlc3QaJi5jb21wYXNzLnYxLlJlbG9hZEFnZW50U2Vzc2lvblJlc3BvbnNlElcKDkdldEFnZW50U3RhdHVzEiEuY29tcGFzcy52MS5HZXRBZ2VudFN0YXR1c1JlcXVlc3QaIi5jb21wYXNzLnYxLkdldEFnZW50U3RhdHVzUmVzcG9uc2USYgoVU3Vic2NyaWJlQWdlbnRTZXNzaW9uEiguY29tcGFzcy52MS5TdWJzY3JpYmVBZ2VudFNlc3Npb25SZXF1ZXN0Gh0uY29tcGFzcy52MS5BZ2VudFNlc3Npb25GcmFtZTABEksKCklzc3VlVG9rZW4SHS5jb21wYXNzLnYxLklzc3VlVG9rZW5SZXF1ZXN0Gh4uY29tcGFzcy52MS5Jc3N1ZVRva2VuUmVzcG9uc2USTgoLUmV2b2tlVG9rZW4SHi5jb21wYXNzLnYxLlJldm9rZVRva2VuUmVxdWVzdBofLmNvbXBhc3MudjEuUmV2b2tlVG9rZW5SZXNwb25zZRJXCg5QdXRBZ2VudENvbmZpZxIhLmNvbXBhc3MudjEuUHV0QWdlbnRDb25maWdSZXF1ZXN0GiIuY29tcGFzcy52MS5QdXRBZ2VudENvbmZpZ1Jlc3BvbnNlEmMKEkdldEFnZW50Q29uZmlnSW5mbxIlLmNvbXBhc3MudjEuR2V0QWdlbnRDb25maWdJbmZvUmVxdWVzdBomLmNvbXBhc3MudjEuR2V0QWdlbnRDb25maWdJbmZvUmVzcG9uc2USYAoRRGVsZXRlQWdlbnRDb25maWcSJC5jb21wYXNzLnYxLkRlbGV0ZUFnZW50Q29uZmlnUmVxdWVzdBolLmNvbXBhc3MudjEuRGVsZXRlQWdlbnRDb25maWdSZXNwb25zZTL9AQoOU2VjcmV0c1NlcnZpY2USSAoJU2V0U2VjcmV0EhwuY29tcGFzcy52MS5TZXRTZWNyZXRSZXF1ZXN0Gh0uY29tcGFzcy52MS5TZXRTZWNyZXRSZXNwb25zZRJOCgtMaXN0U2VjcmV0cxIeLmNvbXBhc3MudjEuTGlzdFNlY3JldHNSZXF1ZXN0Gh8uY29tcGFzcy52MS5MaXN0U2VjcmV0c1Jlc3BvbnNlElEKDERlbGV0ZVNlY3JldBIfLmNvbXBhc3MudjEuRGVsZXRlU2VjcmV0UmVxdWVzdBogLmNvbXBhc3MudjEuRGVsZXRlU2VjcmV0UmVzcG9uc2ViBnByb3RvMw", [file_google_protobuf_timestamp]); /** * @generated from message compass.v1.SetSecretRequest @@ -1624,6 +1624,15 @@ export type GetAgentConfigInfoResponse = Message<"compass.v1.GetAgentConfigInfoR * @generated from field: bool has_models = 9; */ hasModels: boolean; + + /** + * The declared role-prompt names (the in prompts//SYSTEM.md), + * names only. Lets the operator verify a shipped role prompt before the seed + * flips the root to that role (RIG-3075 T2). + * + * @generated from field: repeated string prompts = 10; + */ + prompts: string[]; }; /** diff --git a/proto/compass/v1/compass.proto b/proto/compass/v1/compass.proto index e9906343b..21ea8419d 100644 --- a/proto/compass/v1/compass.proto +++ b/proto/compass/v1/compass.proto @@ -774,6 +774,10 @@ message GetAgentConfigInfoResponse { repeated string subagents = 8; // Whether the bundle carries a fleet models.yml. bool has_models = 9; + // The declared role-prompt names (the in prompts//SYSTEM.md), + // names only. Lets the operator verify a shipped role prompt before the seed + // flips the root to that role (RIG-3075 T2). + repeated string prompts = 10; } message DeleteAgentConfigRequest {} From 83f8c33a78713ba1ca667229952b0f1e0fb9d6a3 Mon Sep 17 00:00:00 2001 From: mintaka Date: Mon, 31 Aug 2026 16:16:15 -0400 Subject: [PATCH 4/4] test(config): cover runner role-name guard + refresh admitted-dir enumeration (RIG-3075) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review-loop fixes on the prompts//SYSTEM.md admission (one medium, one low). MEDIUM — door-parity test-coverage gap: the store and CLI reject-sets both cover a bad prompts role name, but the runner reject-set (TestConfigMaterializeRejectsNewMemberStructure) did not, leaving validateNestedMember's configTopLevelName role-name guard undefended — deleting that line kept every runner prompts test green. Added the 'prompts bad role name' reject case; red-green proven (neuter the guard → the new case reddens with 'expected rejection, got nil error'). LOW — stale enumeration strings: admitting prompts/ made four operator-facing rejection/help messages wrong (they listed skills/extensions/mcp/settings/rules/agents but omitted prompts/), so a top-dir typo got an error whose list omitted the now-valid prompts/. Refreshed all four (store configMemberParts, runner validateMemberPath, bundle.go empty-dir + errNotWhitelisted) and the two coupled TestBuildBundleEmpty substring assertions. Refs RIG-3066 Co-authored-by: Matt Wilkinson --- go/cmd/compass/bundle.go | 4 ++-- go/cmd/compass/bundle_test.go | 4 ++-- go/internal/runner/config_materialize.go | 2 +- go/internal/runner/config_materialize_test.go | 1 + go/internal/store/agent_config.go | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/go/cmd/compass/bundle.go b/go/cmd/compass/bundle.go index d3dd0ba19..3d0a8516b 100644 --- a/go/cmd/compass/bundle.go +++ b/go/cmd/compass/bundle.go @@ -133,7 +133,7 @@ func buildBundle(dir string) ([]byte, error) { return nil, walkErr } if fileCount == 0 { - return nil, fmt.Errorf("bundle directory %q contains no members under skills/, extensions/, mcp/, settings/, rules/, or agents/ and no top-level %s or %s; use `agent-config delete` to clear the fleet config", dir, memberAgentsMD, memberModels) + return nil, fmt.Errorf("bundle directory %q contains no members under skills/, extensions/, mcp/, settings/, rules/, agents/, or prompts/ and no top-level %s or %s; use `agent-config delete` to clear the fleet config", dir, memberAgentsMD, memberModels) } if err := tw.Close(); err != nil { return nil, fmt.Errorf("finalizing tar: %w", err) @@ -180,7 +180,7 @@ func validateDirMember(name string) error { // neither under a whitelisted top dir nor one of the two top-level singletons // (store door: configMemberParts). func errNotWhitelisted(name string) error { - return fmt.Errorf("bundle member %q is not under skills/, extensions/, mcp/, settings/, rules/, or agents/ and is not a top-level %s or %s", name, memberAgentsMD, memberModels) + return fmt.Errorf("bundle member %q is not under skills/, extensions/, mcp/, settings/, rules/, agents/, or prompts/ and is not a top-level %s or %s", name, memberAgentsMD, memberModels) } // addRegularMember validates a regular file against the door grammar and writes diff --git a/go/cmd/compass/bundle_test.go b/go/cmd/compass/bundle_test.go index a5dbb827b..caf824e5d 100644 --- a/go/cmd/compass/bundle_test.go +++ b/go/cmd/compass/bundle_test.go @@ -192,7 +192,7 @@ func TestBuildBundleEmpty(t *testing.T) { if err == nil { t.Fatal("buildBundle(empty dir) = nil error, want rejection") } - if !strings.Contains(err.Error(), "contains no members under skills/, extensions/, mcp/, settings/, rules/, or agents/") { + if !strings.Contains(err.Error(), "contains no members under skills/, extensions/, mcp/, settings/, rules/, agents/, or prompts/") { t.Errorf("buildBundle(empty) error %q does not name the no-members condition", err) } }) @@ -206,7 +206,7 @@ func TestBuildBundleEmpty(t *testing.T) { if err == nil { t.Fatal("buildBundle(only empty subdir) = nil error, want rejection") } - if !strings.Contains(err.Error(), "contains no members under skills/, extensions/, mcp/, settings/, rules/, or agents/") { + if !strings.Contains(err.Error(), "contains no members under skills/, extensions/, mcp/, settings/, rules/, agents/, or prompts/") { t.Errorf("buildBundle(empty subdir) error %q does not name the no-members condition", err) } }) diff --git a/go/internal/runner/config_materialize.go b/go/internal/runner/config_materialize.go index 35cb3b31e..b3522a42b 100644 --- a/go/internal/runner/config_materialize.go +++ b/go/internal/runner/config_materialize.go @@ -486,7 +486,7 @@ func validateMemberPath(name string, typeflag byte) (string, error) { return validateTopLevelMember(name, clean, top, typeflag) } if _, ok := configTopDirs[top]; !ok { - return "", fmt.Errorf("config bundle member %q is not under skills/, extensions/, mcp/, settings/, rules/, or agents/", name) + return "", fmt.Errorf("config bundle member %q is not under skills/, extensions/, mcp/, settings/, rules/, agents/, or prompts/", name) } return validateNestedMember(name, clean, top, parts, typeflag) } diff --git a/go/internal/runner/config_materialize_test.go b/go/internal/runner/config_materialize_test.go index 121093a23..175035eae 100644 --- a/go/internal/runner/config_materialize_test.go +++ b/go/internal/runner/config_materialize_test.go @@ -684,6 +684,7 @@ func TestConfigMaterializeRejectsNewMemberStructure(t *testing.T) { "prompts wrong filename": buildConfigTarball(t, map[string][]byte{"prompts/supervisor/other.md": []byte("x")}), "prompts too deep": buildConfigTarball(t, map[string][]byte{"prompts/supervisor/sub/SYSTEM.md": []byte("x")}), "prompts too shallow": buildConfigTarball(t, map[string][]byte{"prompts/SYSTEM.md": []byte("x")}), + "prompts bad role name": buildConfigTarball(t, map[string][]byte{"prompts/bad name/SYSTEM.md": []byte("x")}), } for name, tarball := range cases { t.Run(name, func(t *testing.T) { diff --git a/go/internal/store/agent_config.go b/go/internal/store/agent_config.go index 8ec9a1395..6fdd49c56 100644 --- a/go/internal/store/agent_config.go +++ b/go/internal/store/agent_config.go @@ -504,7 +504,7 @@ func configMemberParts(name string) ([]string, error) { return parts, nil } if !configBundleTopDirs[parts[0]] { - return nil, fmt.Errorf("%w: bundle member %q is not under skills/, extensions/, mcp/, settings/, rules/, or agents/ and is not a top-level %s or %s", ErrInvalidArgument, name, memberAgentsMD, memberModels) + return nil, fmt.Errorf("%w: bundle member %q is not under skills/, extensions/, mcp/, settings/, rules/, agents/, or prompts/ and is not a top-level %s or %s", ErrInvalidArgument, name, memberAgentsMD, memberModels) } return parts, nil }