diff --git a/.github/actions/mecatequi/action.yml b/.github/actions/mecatequi/action.yml index d3ac0ff2a1..f38ddaae20 100644 --- a/.github/actions/mecatequi/action.yml +++ b/.github/actions/mecatequi/action.yml @@ -46,7 +46,7 @@ # version — there is no version input (matlatl has none either). # # OMITTED INPUT — the ask-reviewer policy file. --subagent-ask-reviewer is surfaced (see -# the input) so a posture:auto run can adjudicate a child's read-only substitution instead +# the input) so an auto-mode run can adjudicate a child's read-only substitution instead # of silently auto-denying it, but --subagent-ask-reviewer-policy (a TRUSTED rubric file # whose CONTENT replaces the built-in rubric) is NOT surfaced: it grants the reviewer a # rule set the operator would have to author and check into the repo, which is a deliberate @@ -124,13 +124,26 @@ inputs: description: Session workspace root (must be a git repository so the diff can be computed). required: false default: ${{ github.workspace }} + permission-mode: + description: >- + Permission mode → --permission-mode: one of plan, default, accept-edits, auto, + yolo. Empty (the input default) resolves to "auto", the documented CI mode + (allow-all, child injection-defence ON), unless the deprecated `posture` input is + set. auto refuses to start without an ENFORCING guardrails checker, inherited from + operator settings or named with `guardrails-model`. yolo is the explicit choice to + run allow-all with no checker. "default" would CANCEL the run on the first mutate + because a headless run has no approver. Setting both this and `posture` fails the + step. + required: false + default: "" posture: description: >- - Operator posture ladder (strict < trusted < auto < yolo). Default "auto" — the - documented CI posture (allow-all, child injection-defence ON). "strict" would - CANCEL the run on the first mutate because a headless run has no approver. + DEPRECATED alias for `permission-mode`, kept for one release → --posture (the + binary WARNs). Used only when set non-empty; setting it together with + `permission-mode` fails the step. Migrate `posture: "auto"` to + `permission-mode: "auto"`. required: false - default: "auto" + default: "" timeout: description: >- Wall-clock bound on the whole run (e.g. 40m); the primary cost ceiling for a @@ -185,17 +198,25 @@ inputs: guardrails-model: description: >- GUARDRAILS (issue #27) checker model id / alias inspecting outbound args + inbound - results. Empty disables guardrails. + results. Empty (default) passes no guardrails flag, so the binary uses the checker + configured in operator settings (a `guardrails:` model or the `guardrail` model + slot), if any. The literal "off" passes --guardrails off, the kill-switch, which + also disables a checker configured in settings. Any other value passes + --guardrails-model . The auto permission mode refuses to start without an + enforcing checker, so pair "off" or an unconfigured empty value with + `permission-mode: yolo`. required: false default: "" subagent-ask-reviewer: description: >- - OPT-IN headless ask reviewer (issue #31) → --subagent-ask-reviewer: model id / alias - of a tool-less one-turn reviewer that adjudicates a CHILD permission ask the headless - auto-deny would otherwise reject (so a posture:auto run whose subagent wants a - read-only $(...) substitution is reviewed rather than silently denied). Empty (default) - = no reviewer (children auto-deny). The TRUSTED policy-rubric file is NOT an input — - see the action header. + Headless ask reviewer (issue #31) → --subagent-ask-reviewer: model id / alias of a + tool-less one-turn reviewer that adjudicates a CHILD permission ask the headless + auto-deny would otherwise reject (so an auto-mode run whose subagent wants a + read-only $(...) substitution is reviewed rather than silently denied). Empty + (default) omits the flag: in the auto and yolo modes the reviewer is then ON by + default (ask-reviewer model slot, else the session model); in other modes children + auto-deny. "off" disables it explicitly. The TRUSTED policy-rubric file is NOT an + input — see the action header. required: false default: "" subagent-ask-reviewer-max-denies: @@ -365,6 +386,7 @@ runs: MQ_UNTRUSTED: ${{ inputs.untrusted }} MQ_INSTRUCTIONS: ${{ inputs.instructions }} MQ_WORKSPACE: ${{ inputs.workspace }} + MQ_PERMISSION_MODE: ${{ inputs.permission-mode }} MQ_POSTURE: ${{ inputs.posture }} MQ_TIMEOUT: ${{ inputs.timeout }} MQ_MAX_RUN_TOKENS: ${{ inputs.max-run-tokens }} @@ -407,10 +429,23 @@ runs: # Build the argv as an ARRAY from env values — never string-interpolated. Each # event-derived value crosses as a discrete token, so no value can be re-parsed # as a flag or a shell metacharacter. + # Permission mode: `permission-mode` replaces the deprecated `posture` input. Both + # set is ambiguous, so fail rather than guess; neither set is the documented CI mode. + if [ -n "${MQ_PERMISSION_MODE}" ] && [ -n "${MQ_POSTURE}" ]; then + echo "::error::both permission-mode and the deprecated posture input are set; pass only permission-mode." + exit 1 + fi + if [ -n "${MQ_POSTURE}" ]; then + echo "::warning::the posture input is deprecated; use permission-mode instead." + permission_args=( --posture "${MQ_POSTURE}" ) + else + permission_args=( --permission-mode "${MQ_PERMISSION_MODE:-auto}" ) + fi + args=( --prompt-file "${MQ_PROMPT_FILE}" --workspace "${MQ_WORKSPACE}" - --posture "${MQ_POSTURE}" + "${permission_args[@]}" --timeout "${MQ_TIMEOUT}" --headless=true --out-diff "${MQ_OUT_DIFF}" @@ -426,7 +461,13 @@ runs: [ -n "${MQ_DEFAULT_MODEL}" ] && args+=( --default-model "${MQ_DEFAULT_MODEL}" ) [ "${openai_on}" = "true" ] && args+=( --openai ) [ -n "${MQ_OPENAI_BASE_URL}" ] && args+=( --openai-base-url "${MQ_OPENAI_BASE_URL}" ) - [ -n "${MQ_GUARDRAILS_MODEL}" ] && args+=( --guardrails-model "${MQ_GUARDRAILS_MODEL}" ) + # Checker choice: empty inherits the operator-settings checker (no flag), "off" + # is the explicit kill-switch, anything else names the checker model. + if [ "${MQ_GUARDRAILS_MODEL}" = "off" ]; then + args+=( --guardrails off ) + elif [ -n "${MQ_GUARDRAILS_MODEL}" ]; then + args+=( --guardrails-model "${MQ_GUARDRAILS_MODEL}" ) + fi [ -n "${MQ_ASK_REVIEWER}" ] && args+=( --subagent-ask-reviewer "${MQ_ASK_REVIEWER}" ) [ -n "${MQ_ASK_REVIEWER_MAX_DENIES}" ] && args+=( --subagent-ask-reviewer-max-denies "${MQ_ASK_REVIEWER_MAX_DENIES}" ) diff --git a/.github/workflows/mecatequi-reusable.yml b/.github/workflows/mecatequi-reusable.yml index 40b9ff7285..4b8ee4980e 100644 --- a/.github/workflows/mecatequi-reusable.yml +++ b/.github/workflows/mecatequi-reusable.yml @@ -13,9 +13,11 @@ on: # Inputs are the per-session passthrough surface of the mecatequi action. The # escape-hatch-only knobs are deliberately NOT exposed here — `default-model` (catalog- # validated, rejects an uncatalogued id — use `model` instead), `openai` (provider is - # auto-detected from the present key), and `subagent-ask-reviewer` (an autonomous-approval - # capability that is a deployment decision better made by editing the action) — so a - # consumer needing them vendors the escape-hatch template rather than the reusable workflow. + # auto-detected from the present key), and `subagent-ask-reviewer` (the action default + # applies: under its default auto permission mode the reviewer is ON, using the ask-reviewer + # model slot or else the session model; choosing another reviewer or "off" is a deployment + # decision better made by editing the action) — so a consumer needing them vendors the + # escape-hatch template rather than the reusable workflow. inputs: label: description: >- @@ -47,8 +49,11 @@ on: default: "" posture: description: >- - Operator posture ladder (strict < trusted < auto < yolo). Default "auto" — the - documented CI posture (allow-all, child injection-defence ON). + Passed to the action's DEPRECATED `posture` input (the binary WARNs): strict, + trusted, auto, or yolo. Default "auto" resolves to permission mode auto (allow-all, + child injection-defence ON), which refuses to start without an ENFORCING + guardrails checker; use "yolo" to run allow-all with no checker. It never grants + project trust. type: string required: false default: auto @@ -84,7 +89,10 @@ on: guardrails-model: description: >- Guardrails (issue #27) checker model id / alias that inspects outbound tool args + - inbound results. Empty (default) disables guardrails. + inbound results. Empty (default) passes no guardrails flag, so the checker + configured in operator settings applies, if any. "off" is the kill-switch. Any + other value names the checker model. With posture "auto", an empty value and no + settings checker, or "off", refuses to start. type: string required: false default: "" diff --git a/.github/workflows/mecatequi.yml b/.github/workflows/mecatequi.yml index 6595efdecf..251616a237 100644 --- a/.github/workflows/mecatequi.yml +++ b/.github/workflows/mecatequi.yml @@ -2,7 +2,7 @@ # mecatequi — the LIVE workflow for THIS repo (mecatl). Private repo, internal posture. # # To run it: label an issue `mecatequi`, OR comment `@mecatequi` on an issue. The agent -# runs a single-shot mecatequi pass over the issue text (untrusted, posture:auto) against +# runs a single-shot mecatequi pass over the issue text (untrusted, permission-mode auto) against # OpenRouter, and the publish job opens a PR (or posts an honest comment) with the result. # # Unlike .github/workflows/mecatequi-example.yml (the inert template), this file is REAL: @@ -221,7 +221,10 @@ jobs: with: prompt-file: ${{ steps.prompt.outputs.prompt-file }} untrusted: "true" - posture: "auto" + # yolo: a deliberate unchecked choice for this private, split-privilege CI + # run. auto would refuse to start because no guardrails checker is configured. + permission-mode: "yolo" + guardrails-model: "off" # Use `model` (per-session passthrough), NOT `default-model`: a deployment # default is validated against the embedded models.dev catalog at startup, and # claude-sonnet-4.6 is newer than that snapshot (it would fail "not catalogued"). diff --git a/Taskfile.yml b/Taskfile.yml index 95e7d4d373..ae659cb5e9 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -817,7 +817,7 @@ tasks: set -eu -o pipefail missing="" rendered=$(helm template production deploy/helm/mecak8s --set '{{.SAFE_SET}}') - for arg in --grpc-addr --http-addr --redis-url --session-lease-k8s-namespace --headless --posture --oidc-issuer --oidc-audience --oidc-max-jwks-staleness=1h; do + for arg in --grpc-addr --http-addr --redis-url --session-lease-k8s-namespace --headless --permission-mode --oidc-issuer --oidc-audience --oidc-max-jwks-staleness=1h; do echo "$rendered" | grep -q -- "$arg" || missing="$missing $arg" done if [ -n "$missing" ]; then diff --git a/apps/contracts/openapi.json b/apps/contracts/openapi.json index f36d7a18d4..c306b5858c 100644 --- a/apps/contracts/openapi.json +++ b/apps/contracts/openapi.json @@ -1814,7 +1814,9 @@ "enum": [ "default", "plan", - "acceptEdits" + "acceptEdits", + "auto", + "yolo" ] }, "model": { @@ -2991,7 +2993,9 @@ "enum": [ "default", "plan", - "acceptEdits" + "acceptEdits", + "auto", + "yolo" ] } }, @@ -5527,7 +5531,10 @@ "enum": [ "default", "plan", - "acceptEdits" + "acceptEdits", + "auto", + "yolo", + "serverDefault" ] }, "modelId": { @@ -5882,7 +5889,10 @@ "enum": [ "default", "plan", - "acceptEdits" + "acceptEdits", + "auto", + "yolo", + "serverDefault" ] }, "modelId": { @@ -6190,8 +6200,7 @@ "default", "plan", "acceptEdits" - ], - "default": "plan" + ] }, "mutating": { "type": "boolean", @@ -6306,7 +6315,10 @@ "enum": [ "default", "plan", - "acceptEdits" + "acceptEdits", + "auto", + "yolo", + "serverDefault" ] }, "modelId": { diff --git a/apps/contracts/src/generated/types.gen.ts b/apps/contracts/src/generated/types.gen.ts index 21bdb3ad16..9f9d942d8e 100644 --- a/apps/contracts/src/generated/types.gen.ts +++ b/apps/contracts/src/generated/types.gen.ts @@ -702,7 +702,7 @@ export type GetSessionDetailResponses = { }; id: string; kind: string; - mode: 'default' | 'plan' | 'acceptEdits'; + mode: 'default' | 'plan' | 'acceptEdits' | 'auto' | 'yolo'; model?: { id: string; providerId: string; @@ -1033,7 +1033,7 @@ export type SetSessionModeResponses = { * The authoritative session permission mode. */ 200: { - mode: 'default' | 'plan' | 'acceptEdits'; + mode: 'default' | 'plan' | 'acceptEdits' | 'auto' | 'yolo'; }; }; @@ -1854,7 +1854,7 @@ export type ListSchedulesResponses = { lastFireAt: string; lastFireSessionId: string; maxFires: number; - mode: 'default' | 'plan' | 'acceptEdits'; + mode: 'default' | 'plan' | 'acceptEdits' | 'auto' | 'yolo' | 'serverDefault'; modelId: string; mutating: boolean; name: string; @@ -1965,7 +1965,7 @@ export type CreateScheduleResponses = { lastFireAt: string; lastFireSessionId: string; maxFires: number; - mode: 'default' | 'plan' | 'acceptEdits'; + mode: 'default' | 'plan' | 'acceptEdits' | 'auto' | 'yolo' | 'serverDefault'; modelId: string; mutating: boolean; name: string; @@ -2141,7 +2141,7 @@ export type UpdateScheduleResponses = { lastFireAt: string; lastFireSessionId: string; maxFires: number; - mode: 'default' | 'plan' | 'acceptEdits'; + mode: 'default' | 'plan' | 'acceptEdits' | 'auto' | 'yolo' | 'serverDefault'; modelId: string; mutating: boolean; name: string; diff --git a/apps/contracts/src/schemas/chat.ts b/apps/contracts/src/schemas/chat.ts index 75737fddc8..aa391e5d06 100644 --- a/apps/contracts/src/schemas/chat.ts +++ b/apps/contracts/src/schemas/chat.ts @@ -4,6 +4,12 @@ import { z } from "zod"; export const sessionModeSchema = z.enum(["default", "plan", "acceptEdits"]); +/** + * A session's reported mode. It adds the autonomous modes a session created + * outside Studio can run in; Studio reads them but never requests them. + */ +export const sessionReportedModeSchema = z.enum(["default", "plan", "acceptEdits", "auto", "yolo"]); + export const sessionToolAccessSchema = z.enum(["all", "noFilesystem"]); export const reasoningEffortSchema = z.enum(["default", "low", "medium", "high", "xhigh", "max"]); @@ -123,7 +129,7 @@ export const sessionDetailResponseSchema = z.object({ }), id: z.string(), kind: z.string(), - mode: sessionModeSchema, + mode: sessionReportedModeSchema, model: sessionModelSelectionSchema .extend({ contextWindow: z.string(), @@ -155,7 +161,7 @@ export const setSessionModeRequestSchema = z.object({ }); export const setSessionModeResponseSchema = z.object({ - mode: sessionModeSchema, + mode: sessionReportedModeSchema, }); export const forkSessionRequestSchema = z.object({ diff --git a/apps/contracts/src/schemas/schedules.ts b/apps/contracts/src/schemas/schedules.ts index 255e82bea3..b430851d73 100644 --- a/apps/contracts/src/schemas/schedules.ts +++ b/apps/contracts/src/schemas/schedules.ts @@ -12,6 +12,21 @@ export const maximumInt32 = 2_147_483_647; export const scheduleModeSchema = z.enum(["default", "plan", "acceptEdits"]); +/** + * A schedule's reported mode. It adds what a schedule created outside Studio + * can run in: `auto` and `yolo` (an in-chat schedule records its creating + * session's mode), and `serverDefault` (no mode was set, so each fire uses the + * server's default session mode). Studio reads these but never requests them. + */ +export const scheduleReportedModeSchema = z.enum([ + "default", + "plan", + "acceptEdits", + "auto", + "yolo", + "serverDefault", +]); + /** Each segment of an IANA zone name starts with an uppercase letter. */ const ianaZoneName = /^[A-Z][A-Za-z0-9_+-]*(?:\/[A-Z][A-Za-z0-9_+-]*)*$/u; @@ -72,7 +87,14 @@ export const createScheduleRequestSchema = scheduleWriteSchema.extend({ name: z.string().trim().min(1).max(160), }); -export const updateScheduleRequestSchema = scheduleWriteSchema; +/** + * An update may omit `mode` to keep the stored one, which is how a writing + * schedule whose mode Studio cannot set (`auto`, `yolo`, `serverDefault`) is + * edited without changing it. A sent mode is still one Studio can create. + */ +export const updateScheduleRequestSchema = scheduleWriteSchema.extend({ + mode: scheduleModeSchema.optional(), +}); export const scheduleSchema = z.object({ enabled: z.boolean(), @@ -80,7 +102,7 @@ export const scheduleSchema = z.object({ lastFireAt: z.string().nullable(), lastFireSessionId: z.string(), maxFires: z.number().int().nonnegative(), - mode: scheduleModeSchema, + mode: scheduleReportedModeSchema, modelId: z.string(), mutating: z.boolean(), name: z.string(), diff --git a/apps/server/src/mecatl/chat.test.ts b/apps/server/src/mecatl/chat.test.ts index c6a1fe8996..7a8b7f6bc3 100644 --- a/apps/server/src/mecatl/chat.test.ts +++ b/apps/server/src/mecatl/chat.test.ts @@ -517,6 +517,30 @@ describe("Mecatl chat sessions", () => { }); }); + it("reports an autonomous session's mode instead of folding it into default", async () => { + // A session created elsewhere in auto or yolo must not read as Manual. + for (const [mode, want] of [ + [SessionMode.Auto, "auto"], + [SessionMode.Yolo, "yolo"], + ] as const) { + const snapshot = vi.fn().mockResolvedValue({ + sessionId: "session-1", + state: "idle", + mode, + tokenUsage: {}, + }); + const service = createMecatlChatService( + { sessions: { get: vi.fn().mockResolvedValue({ snapshot }) } } as unknown as Client, + () => + ({ + capabilities: { manualCompaction: false, modelSelection: true }, + }) as ServerCompatibility, + ); + + await expect(service.detail("session-1")).resolves.toMatchObject({ mode: want }); + } + }); + it("keeps the resolved model's fields for a provider with no display name", async () => { // Pins the session model served before provider display names (#2260). const snapshot = vi.fn().mockResolvedValue({ diff --git a/apps/server/src/mecatl/chat.ts b/apps/server/src/mecatl/chat.ts index 8be1abb6e7..cbe6fe6b7e 100644 --- a/apps/server/src/mecatl/chat.ts +++ b/apps/server/src/mecatl/chat.ts @@ -112,7 +112,7 @@ export interface ChatService { setMode( sessionId: string, mode: CreateSessionRequest["mode"], - ): Promise<{ mode: CreateSessionRequest["mode"] }>; + ): Promise<{ mode: SessionDetailResponse["mode"] }>; transcript(sessionId: string, signal?: AbortSignal): Promise; } @@ -609,9 +609,13 @@ function toSessionMode(mode: CreateSessionRequest["mode"]) { return SessionMode.Default; } -function fromSessionMode(mode: SessionMode): CreateSessionRequest["mode"] { +// Auto and yolo are kept, not folded into default: a session created elsewhere +// in an autonomous mode must not read as one that asks before every change. +function fromSessionMode(mode: SessionMode): SessionDetailResponse["mode"] { if (mode === SessionMode.Plan) return "plan"; if (mode === SessionMode.AcceptEdits) return "acceptEdits"; + if (mode === SessionMode.Auto) return "auto"; + if (mode === SessionMode.Yolo) return "yolo"; return "default"; } diff --git a/apps/server/src/mecatl/schedules.test.ts b/apps/server/src/mecatl/schedules.test.ts index e0d081504d..ad154af202 100644 --- a/apps/server/src/mecatl/schedules.test.ts +++ b/apps/server/src/mecatl/schedules.test.ts @@ -243,6 +243,47 @@ describe("Mecatl schedule spec mapping", () => { expect([a.mode, b.mode, c.mode]).toEqual(["default", "acceptEdits", "plan"]); }); + it("reports modes set outside Studio instead of folding them into plan", async () => { + // An in-chat schedule records its creator's mode (auto or yolo), and one + // created without a mode fires in the server's default session mode. + for (const [mode, want] of [ + [PermissionMode.AUTO, "auto"], + [PermissionMode.YOLO, "yolo"], + [PermissionMode.UNSPECIFIED, "serverDefault"], + ] as const) { + const list = vi.fn().mockResolvedValue({ + schedules: [spec({ mode, mutating: true }).schedule], + }); + const service = createMecatlScheduleService( + { schedules: { list } } as unknown as Client, + true, + ); + + const response = await service.list(); + expect(response.items[0]?.mode, String(mode)).toBe(want); + } + }); + + it("keeps the stored mode when an update omits it, and plan when writes are off", async () => { + for (const stored of [PermissionMode.AUTO, PermissionMode.YOLO, PermissionMode.UNSPECIFIED]) { + const current = spec({ mode: stored, mutating: true, timezone: "Europe/Rome" }); + const get = vi.fn().mockResolvedValue(current); + const update = vi.fn().mockResolvedValue(current); + const service = createMecatlScheduleService( + { schedules: { get, update } } as unknown as Client, + true, + ); + + await service.update("s", { ...baseWriteRequest(), mode: undefined, mutating: true }); + await service.update("s", { ...baseWriteRequest(), mode: undefined, mutating: false }); + await service.update("s", { ...baseWriteRequest(), mode: "acceptEdits", mutating: true }); + expect( + update.mock.calls.map((call) => call[0].spec.mode), + String(stored), + ).toEqual([stored, PermissionMode.PLAN, PermissionMode.ACCEPT_EDITS]); + } + }); + it("derives schedule status from enabled, in-flight, and pending claims", async () => { const rows = [ { name: "paused", state: { enabled: false } }, diff --git a/apps/server/src/mecatl/schedules.ts b/apps/server/src/mecatl/schedules.ts index 0fbe5601bd..09b7debecf 100644 --- a/apps/server/src/mecatl/schedules.ts +++ b/apps/server/src/mecatl/schedules.ts @@ -148,7 +148,7 @@ function toSdkSpec( }, maxFires: cron ? request.maxFires : 0, misfire: current?.misfire ?? 0, - mode: modeToSdk(request.mode), + mode: writeModeToSdk(request, current), mutating: request.mutating, name, oneShotMaxRetries: once && request.oneShotRetry ? request.oneShotMaxRetries : 0, @@ -282,10 +282,27 @@ function modeToSdk(mode: UpdateScheduleRequest["mode"]): PermissionMode { return PermissionMode.PLAN; } +/** + * The mode an update sends. An omitted mode on a writing schedule keeps the + * stored one verbatim, including an unspecified mode (the server default) and + * the autonomous modes Studio cannot request; a read-only schedule is plan. + */ +function writeModeToSdk(request: UpdateScheduleRequest, current?: ScheduleSpec): PermissionMode { + if (request.mode !== undefined) return modeToSdk(request.mode); + if (!request.mutating) return PermissionMode.PLAN; + return current?.mode ?? PermissionMode.PLAN; +} + +// Auto, yolo, and an unspecified mode are reported as such, not folded into +// plan: an in-chat schedule records its creator's mode, and a schedule created +// without a mode fires in the server's default session mode. function modeFromSdk(mode: PermissionMode): ScheduleResponse["mode"] { + if (mode === PermissionMode.PLAN) return "plan"; if (mode === PermissionMode.DEFAULT) return "default"; if (mode === PermissionMode.ACCEPT_EDITS) return "acceptEdits"; - return "plan"; + if (mode === PermissionMode.AUTO) return "auto"; + if (mode === PermissionMode.YOLO) return "yolo"; + return "serverDefault"; } function profileToSdk(profile: UpdateScheduleRequest["profile"]): string { diff --git a/apps/web/src/features/chat/chat-composer.alignment.test.tsx b/apps/web/src/features/chat/chat-composer.alignment.test.tsx index f2eae5dd7b..768e720f77 100644 --- a/apps/web/src/features/chat/chat-composer.alignment.test.tsx +++ b/apps/web/src/features/chat/chat-composer.alignment.test.tsx @@ -10,6 +10,7 @@ import { reconcileAccount, } from "../../lib/account-storage"; import { readDraft, writeDraft } from "../../lib/draft-store"; +import { isSelectableMode } from "../../lib/permission-mode"; import { ShortcutProvider, useShortcut } from "../shortcuts/shortcut-provider"; import { ChatComposer, type DraftChatConfiguration } from "./chat-composer"; import { composerFrameClass } from "./composer-frame"; @@ -801,6 +802,24 @@ describe("PermissionModeBadge", () => { view.rerender(); expect(view.container.textContent).toBe("Permission mode: Plan"); }); + + it("names an autonomous mode set outside Studio instead of hiding it as Manual", () => { + const view = render(); + expect(view.container.textContent).toBe("Permission mode: Auto"); + view.rerender(); + expect(view.container.textContent).toBe("Permission mode: YOLO"); + }); +}); + +describe("isSelectableMode", () => { + it("offers the Mode control only for the modes Studio can set", () => { + expect( + ["default", "plan", "acceptEdits"].every((mode) => isSelectableMode(mode as never)), + ).toBe(true); + expect(isSelectableMode("auto")).toBe(false); + expect(isSelectableMode("yolo")).toBe(false); + expect(isSelectableMode(undefined)).toBe(false); + }); }); describe("QueuedMessageStrip", () => { diff --git a/apps/web/src/features/chat/chat-workspace.tsx b/apps/web/src/features/chat/chat-workspace.tsx index 0102fa8ca7..d45b8d4b18 100644 --- a/apps/web/src/features/chat/chat-workspace.tsx +++ b/apps/web/src/features/chat/chat-workspace.tsx @@ -103,6 +103,7 @@ import { notifyRunCompletion } from "../../lib/browser-notifications"; import { copyToClipboard } from "../../lib/clipboard"; import { errorMessage } from "../../lib/error-message"; import { modelPreferenceId, useDisabledModels } from "../../lib/model-preferences"; +import { isSelectableMode, modeTitle } from "../../lib/permission-mode"; import { defaultAgentName, useAgentAvatar, @@ -1004,7 +1005,9 @@ export function ChatWorkspace({ sessionId }: { sessionId?: string }) { ? "Plan" : seedMode === "acceptEdits" ? "Accept edits" - : "Loading permission mode", + : seedMode === "auto" || seedMode === "yolo" + ? `${modeTitle(seedMode)} (set outside Studio)` + : "Loading permission mode", execution: sessionId ? displayedDetail ? `Files: ${displayedDetail.capabilities.executionFiles ? "available" : "unavailable"}; built-in Shell: ${displayedDetail.capabilities.builtInShell ? "available" : "unavailable"}` @@ -3492,7 +3495,11 @@ export function ChatWorkspace({ sessionId }: { sessionId?: string }) { draftKey={sessionId ?? "new"} imageAttachmentsSupported={imageAttachmentsSupported} liveControlsDisabled={liveControlsDisabled} - liveMode={sessionId ? displayedDetail?.mode : undefined} + liveMode={ + sessionId && isSelectableMode(displayedDetail?.mode) + ? displayedDetail.mode + : undefined + } liveModel={sessionId ? displayedDetail?.model : undefined} models={models} onConfigurationChange={setDraftConfiguration} diff --git a/apps/web/src/features/chat/permission-mode-badge.tsx b/apps/web/src/features/chat/permission-mode-badge.tsx index ebdc9bbee2..5358a82715 100644 --- a/apps/web/src/features/chat/permission-mode-badge.tsx +++ b/apps/web/src/features/chat/permission-mode-badge.tsx @@ -1,21 +1,29 @@ // SPDX-License-Identifier: Apache-2.0 import { Badge } from "../../components/ui/badge"; -import { modeTitle, type SessionPermissionMode } from "../../lib/permission-mode"; +import { modeTitle, type SessionReportedMode } from "../../lib/permission-mode"; /** * The chat header's permission-mode badge, from the prototype (`stack-08`). * It is silent for Manual, the everyday posture, so a badge in the header * always means the chat does not ask before every change. */ -export function PermissionModeBadge({ mode }: { mode?: SessionPermissionMode }) { +export function PermissionModeBadge({ mode }: { mode?: SessionReportedMode }) { if (!mode || mode === "default") return null; const label = modeTitle(mode); return ( Permission mode: {label} diff --git a/apps/web/src/features/chat/session-details-dialog.tsx b/apps/web/src/features/chat/session-details-dialog.tsx index 39678378a5..ceed82ced0 100644 --- a/apps/web/src/features/chat/session-details-dialog.tsx +++ b/apps/web/src/features/chat/session-details-dialog.tsx @@ -51,8 +51,10 @@ const PROVENANCE_LABEL: Record = { const MODE_LABEL: Record = { acceptEdits: "Accept edits", + auto: "Auto", default: "Default", plan: "Plan", + yolo: "YOLO", }; /** A no-filesystem session's placement kinds. */ diff --git a/apps/web/src/features/schedules/schedule-detail.tsx b/apps/web/src/features/schedules/schedule-detail.tsx index 1ceb1fbf6b..9597d3e241 100644 --- a/apps/web/src/features/schedules/schedule-detail.tsx +++ b/apps/web/src/features/schedules/schedule-detail.tsx @@ -60,6 +60,7 @@ import { pageTitleClass } from "../../lib/typography"; import { useShortcutSuppression } from "../shortcuts/shortcut-provider"; import { describeCron } from "./describe-cron"; import { canEditSchedule, ScheduleForm } from "./schedule-form"; +import { scheduleModeLabel } from "./schedule-form-value"; import { ScheduleStatusBadge } from "./schedule-status"; type Schedule = ListSchedulesResponse["items"][number]; @@ -247,7 +248,17 @@ export function ScheduleDetail({ scheduleName }: { scheduleName: string }) {
- {modeLabel(schedule.mode)} + + {scheduleModeLabel(schedule.mode)} + {schedule.mutating && writes enabled} {schedule.profile === "noFilesystem" && no filesystem}
@@ -264,7 +275,7 @@ export function ScheduleDetail({ scheduleName }: { scheduleName: string }) {
- + ); } - -function modeLabel(mode: Schedule["mode"]) { - return mode === "acceptEdits" ? "Accept edits" : mode === "plan" ? "Plan" : "Default"; -} diff --git a/apps/web/src/features/schedules/schedule-form-value.test.ts b/apps/web/src/features/schedules/schedule-form-value.test.ts index e69797d7b7..3117423196 100644 --- a/apps/web/src/features/schedules/schedule-form-value.test.ts +++ b/apps/web/src/features/schedules/schedule-form-value.test.ts @@ -7,6 +7,7 @@ import { isTriggerEditable, phraseTriggerPatch, type Schedule, + scheduleModeLabel, TRIGGER_FIELDS, type TriggerPatch, triggerFromValue, @@ -56,6 +57,34 @@ describe("schedule form value mapping", () => { } }); + it("keeps a mode set outside Studio by omitting it, and plan when writes go off", () => { + // Studio cannot send auto, yolo, or the server default; an edit must not + // replace them with a mode it can. + for (const mode of ["auto", "yolo", "serverDefault"] as const) { + const existing = schedule({ mode, mutating: true }); + const value = valueFromSchedule(existing); + expect(bodyFromValue(value, existing), mode).toMatchObject({ + mode: undefined, + mutating: true, + }); + expect(bodyFromValue({ ...value, allowWrites: false }, existing), mode).toMatchObject({ + mode: "plan", + mutating: false, + }); + expect(bodyFromValue({ ...value, writeMode: "acceptEdits" }, existing), mode).toMatchObject({ + mode: "acceptEdits", + mutating: true, + }); + } + }); + + it("labels the modes set outside Studio honestly", () => { + expect(scheduleModeLabel("auto")).toBe("Auto (set outside Studio)"); + expect(scheduleModeLabel("yolo")).toBe("YOLO (set outside Studio)"); + expect(scheduleModeLabel("serverDefault")).toBe("Server default"); + expect(scheduleModeLabel("default")).toBe("Default"); + }); + it("sends an existing schedule's trigger exactly as stored", () => { for (const trigger of [ { expression: "0 9 * * *", kind: "cron", timezone: "" }, diff --git a/apps/web/src/features/schedules/schedule-form-value.ts b/apps/web/src/features/schedules/schedule-form-value.ts index f2fb86d4fc..3e9f699a4d 100644 --- a/apps/web/src/features/schedules/schedule-form-value.ts +++ b/apps/web/src/features/schedules/schedule-form-value.ts @@ -11,6 +11,35 @@ import { export type Schedule = ListSchedulesResponse["items"][number]; export type ScheduleBody = CreateScheduleData["body"]; +/** A mode Studio can send: `scheduleModeSchema` in `contracts/src/schemas/schedules.ts`. */ +export type RequestableScheduleMode = NonNullable; + +/** + * Whether Studio can send this mode. `auto` and `yolo` (recorded by an in-chat + * schedule) and `serverDefault` (no mode set) come from outside Studio; an + * edit keeps them by omitting the mode rather than replacing them. + */ +export function isRequestableScheduleMode(mode: Schedule["mode"]): mode is RequestableScheduleMode { + return mode === "default" || mode === "plan" || mode === "acceptEdits"; +} + +/** The label for a schedule's mode, naming the ones set outside Studio as such. */ +export function scheduleModeLabel(mode: Schedule["mode"]): string { + switch (mode) { + case "acceptEdits": + return "Accept edits"; + case "plan": + return "Plan"; + case "auto": + return "Auto (set outside Studio)"; + case "yolo": + return "YOLO (set outside Studio)"; + case "serverDefault": + return "Server default"; + default: + return "Default"; + } +} export interface FormValue { allowWrites: boolean; @@ -27,8 +56,9 @@ export interface FormValue { timezone: string; triggerKind: "cron" | "once"; /** - * The permission mode a writing schedule runs in. `plan` is only ever - * loaded from a stored writing schedule, so saving it unchanged keeps it. + * The permission mode a writing schedule runs in. `plan` and the modes set + * outside Studio are only ever loaded from a stored writing schedule, so + * saving it unchanged keeps them. */ writeMode: Schedule["mode"]; } @@ -127,7 +157,13 @@ export function bodyFromValue(value: FormValue, schedule?: Schedule): ScheduleBo const trigger = triggerFromValue(value, schedule); return { maxFires: Math.max(0, Number(value.maxFires) || 0), - mode: value.allowWrites ? value.writeMode : "plan", + // A mode Studio cannot send is kept by omitting it (the BFF keeps the + // stored mode), never replaced with one Studio can. + mode: !value.allowWrites + ? "plan" + : isRequestableScheduleMode(value.writeMode) + ? value.writeMode + : undefined, mutating: value.allowWrites, name: value.name.trim(), oneShotMaxRetries: value.oneShotRetry ? Math.max(0, Number(value.oneShotMaxRetries) || 0) : 0, diff --git a/apps/web/src/features/schedules/schedule-form.tsx b/apps/web/src/features/schedules/schedule-form.tsx index 31b0c84ca9..cf31083b19 100644 --- a/apps/web/src/features/schedules/schedule-form.tsx +++ b/apps/web/src/features/schedules/schedule-form.tsx @@ -52,6 +52,18 @@ const TRIGGER_LOCKED_NOTE = const NO_PHRASE: PhraseOutcome = { kind: "none" }; +/** + * The write-mode options offered only to keep a stored writing schedule's mode + * as it is: modes Studio never offers for a new schedule. Accept edits and + * default are always offered, so they have no entry here. + */ +const KEPT_MODE_OPTIONS: Partial> = { + auto: "Keep Auto, set outside Studio", + plan: "Plan only, changes are refused", + serverDefault: "Keep the server's default mode", + yolo: "Keep YOLO, set outside Studio", +}; + const PHRASE_PLACEHOLDER = "every 30 minutes · daily at 9am · next monday 3pm · in 2 hours"; const PHRASE_HINT = 'Not recognised — try "every weekday at 9am" or a cron expression'; @@ -140,8 +152,9 @@ export function ScheduleForm({ } } - // `plan` is offered only to keep a stored writing schedule's mode as it is. - const offerPlanMode = schedule?.mutating === true && schedule.mode === "plan"; + // `plan` and the modes set outside Studio are offered only to keep a stored + // writing schedule's mode as it is. + const keptMode = schedule?.mutating === true ? KEPT_MODE_OPTIONS[schedule.mode] : undefined; const phrasePreview = describePhraseOutcome(phraseOutcome); const phraseNote = phrasePreview ?? (phrase.trim() ? PHRASE_HINT : null); // Formatted from the very trigger that will be sent, never from the inputs. @@ -284,9 +297,7 @@ export function ScheduleForm({ options={[ { label: "Accept edits automatically", value: "acceptEdits" }, { label: "Ask when approval is needed", value: "default" }, - ...(offerPlanMode - ? [{ label: "Plan only, changes are refused", value: "plan" }] - : []), + ...(keptMode && schedule ? [{ label: keptMode, value: schedule.mode }] : []), ]} value={value.writeMode} /> diff --git a/apps/web/src/lib/permission-mode.ts b/apps/web/src/lib/permission-mode.ts index 241553b387..5ea3ba684c 100644 --- a/apps/web/src/lib/permission-mode.ts +++ b/apps/web/src/lib/permission-mode.ts @@ -3,6 +3,20 @@ /** Mirrors `sessionModeSchema` in `contracts/src/schemas/chat.ts`. */ export type SessionPermissionMode = "acceptEdits" | "default" | "plan"; +/** + * Mirrors `sessionReportedModeSchema`: what a live chat reports. Auto and yolo + * come from a chat created outside Studio. Studio shows them but cannot set + * them, so a chat in either has no Mode control here. + */ +export type SessionReportedMode = SessionPermissionMode | "auto" | "yolo"; + +/** Whether Studio's Mode control can show and change this mode. */ +export function isSelectableMode( + mode: SessionReportedMode | undefined, +): mode is SessionPermissionMode { + return mode === "default" || mode === "plan" || mode === "acceptEdits"; +} + export interface PermissionModeOption { description: string; /** The posture dot before the title: none for Manual, green for Accept edits, blue for Plan. */ @@ -39,5 +53,7 @@ export const MODE_OPTIONS: PermissionModeOption[] = [ ]; export function modeTitle(mode: string | undefined): string { + if (mode === "auto") return "Auto"; + if (mode === "yolo") return "YOLO"; return MODE_OPTIONS.find((option) => option.value === mode)?.title ?? "Manual"; } diff --git a/cmd/mecak8s/flags.go b/cmd/mecak8s/flags.go index 17b5fe6fea..574ecd1503 100644 --- a/cmd/mecak8s/flags.go +++ b/cmd/mecak8s/flags.go @@ -240,6 +240,12 @@ type config struct { // lets CLI out-rank the operator-global settings.yaml posture: key. posture string postureFlagSet bool + // permissionMode is the raw --permission-mode token, validated at + // parse time. Empty (the default) leaves the deprecated --posture default + // "auto" in force, so an unconfigured pod keeps posture auto with the default + // session mode and operator YAML can still override it. + permissionMode string + permissionModeFlagSet bool // Reasoning-effort tier: operator-tier reasoning-effort default ("" = // unset, the provider's own default applies). reasoningEffortFlagSet records an @@ -444,7 +450,7 @@ func parseFlags(argv []string) (config, error) { fs.BoolVar(&cfg.headless, "headless", true, "Run without an interactive permission approver. Child permission requests are denied unless --subagent-ask-reviewer handles them") // Headless ask reviewer (issue #31). - fs.StringVar(&cfg.subagentAskReviewer, "subagent-ask-reviewer", "", "Model ID or alias for reviewing child permission requests in headless mode. Empty disables the reviewer") + fs.StringVar(&cfg.subagentAskReviewer, "subagent-ask-reviewer", "", "Model ID or alias for reviewing child permission requests in headless mode. Empty leaves the reviewer off, except that the auto and yolo permission modes turn it on by default (ask-reviewer model slot, else the session model); off disables it explicitly") fs.IntVar(&cfg.subagentAskReviewerMaxDenies, "subagent-ask-reviewer-max-denies", agent.DefaultAskReviewMaxDenies, "Consecutive non-allow outcomes that disable the reviewer for the run. Values less than or equal to zero use the default") fs.StringVar(&cfg.subagentAskReviewerPolicyFile, "subagent-ask-reviewer-policy", "", "Path to a trusted policy rubric for --subagent-ask-reviewer. Replaces the built-in rubric; an unreadable file prevents startup") @@ -457,7 +463,8 @@ func parseFlags(argv []string) (config, error) { fs.StringVar(&cfg.subagentModel, "subagent-model", "", "Default model for child agents without their own model. Empty inherits --model") // Posture: DEFAULT "auto" (the recommended UNATTENDED single-tenant tier). - fs.StringVar(&cfg.posture, "posture", "auto", "Permission posture: strict prompts for mutations; trusted honors project allow rules; auto allows tools by default and relaxes main shell substitutions while child injection defenses remain enabled; yolo also runs child command substitutions automatically. Deny rules and configured ask rules still apply. auto and yolo require MECATL_SANDBOX when running as root") + fs.StringVar(&cfg.permissionMode, "permission-mode", "", permissionModeHelp) + fs.StringVar(&cfg.posture, "posture", "auto", "DEPRECATED: use --permission-mode. Permission posture: strict prompts for mutations; trusted honors project allow rules; auto allows tools by default and relaxes main shell substitutions while child injection defenses remain enabled; yolo also runs child command substitutions automatically. Deny rules and configured ask rules still apply. auto and yolo require MECATL_SANDBOX when running as root") // Reasoning-effort tier: operator-tier only; help text verbatim from mecated. fs.StringVar(&cfg.reasoningEffort, "reasoning-effort", "", @@ -494,7 +501,7 @@ func parseFlags(argv []string) (config, error) { fs.Var(&cfg.permissionConfigs, "permission-config", "Trusted permission YAML file. Repeatable") fs.BoolVar(&cfg.permissionsConventional, "permissions-conventional", true, "Discover project and user permission settings for each session") fs.BoolVar(&cfg.importClaudePermissions, "import-claude-permissions", false, "Import compatible Claude Code permission settings") - fs.BoolVar(&cfg.trustProject, "trust-project", false, "Honor a discovered project's allow rules. Deny and ask rules are always honored; equivalent to --posture=trusted") + fs.BoolVar(&cfg.trustProject, "trust-project", false, "Honor a discovered project's allow rules. Deny and ask rules are always honored; raises the posture to at least trusted and combines with --permission-mode") // Fan-out / teams toggles. fs.BoolVar(&cfg.enableParallel, "enable-parallel", false, "Enable the Parallel tool for isolated child branches") @@ -546,6 +553,8 @@ func parseFlags(argv []string) (config, error) { switch fl.Name { case "posture": cfg.postureFlagSet = true + case "permission-mode": + cfg.permissionModeFlagSet = true case "shell": cfg.shellFlagSet = true case "reasoning-effort": @@ -563,6 +572,9 @@ func parseFlags(argv []string) (config, error) { cfg.defaultProviderFlagSet = true } }) + if err := validatePermissionModeFlags(cfg); err != nil { + return config{}, err + } resolvedShell, err := cliconfig.ResolveCommandRunnerConfig(cfg.shell, cfg.shellFlagSet, cfg.permissionsConventional, cfg.permissionConfigs) if err != nil { return config{}, fmt.Errorf("command runner configuration: %w", err) @@ -815,6 +827,10 @@ func appConfig(cfg config, diag port.Diagnostics, obs observability) app.Config PermissionConfigs: cfg.permissionConfigs, Posture: app.ParsePosture(cfg.posture), PostureFlagSet: cfg.postureFlagSet, + // Permission mode: an explicit token out-ranks the deprecated + // --posture (default auto) in app.Build's foldPermissionMode. + PermissionMode: cfg.permissionMode, + PermissionModeFlagSet: cfg.permissionModeFlagSet, // Reasoning-effort tier: operator-tier only; reasoningEffortFlagSet // lets CLI out-rank the operator-global settings.yaml reasoning-effort: key // (folded by foldOperatorReasoningEffort in app.Build, like posture). diff --git a/cmd/mecak8s/main.go b/cmd/mecak8s/main.go index f37eb3046c..74b8be00c4 100644 --- a/cmd/mecak8s/main.go +++ b/cmd/mecak8s/main.go @@ -75,6 +75,7 @@ func run() error { diag = slogdiag.New(os.Stderr, true, port.LevelDebug) } cfg.diagnostics = diag + warnDeprecatedPermissionFlags(diag, cfg) ctx, stop := signalCtx() defer stop() diff --git a/cmd/mecak8s/main_test.go b/cmd/mecak8s/main_test.go index 7549e76456..ce47bca437 100644 --- a/cmd/mecak8s/main_test.go +++ b/cmd/mecak8s/main_test.go @@ -452,7 +452,8 @@ func TestBuildOverRedisDrivesRunToCompletion(t *testing.T) { RedisURL: mr.Addr(), RedisAllowPlaintext: true, Interactive: false, // mecak8s headless default (Interactive=!headless) - Posture: app.PostureAuto, + Posture: app.PostureYolo, + GuardrailsDisabled: true, // unchecked allow-all is yolo PermissionsConventional: false, AgentsConventional: false, Diagnostics: port.NopDiagnostics{}, @@ -626,6 +627,7 @@ func TestDrainHTTPRouting(t *testing.T) { func TestServeWiresSeparateDrainListener(t *testing.T) { cfg, err := parseFlags([]string{ "--mock", + "--permission-mode=yolo", "--guardrails=off", // unchecked allow-all is yolo "--grpc-addr", freeLoopbackPort(t), "--http-addr", freeLoopbackPort(t), "--drain-addr", freeLoopbackPort(t), diff --git a/cmd/mecak8s/mcp_test.go b/cmd/mecak8s/mcp_test.go index e4b1e1e16b..5a83aef7e1 100644 --- a/cmd/mecak8s/mcp_test.go +++ b/cmd/mecak8s/mcp_test.go @@ -60,7 +60,7 @@ func TestMecak8sBuildDiscoversOperatorMCPSettings(t *testing.T) { if err := os.WriteFile(explicit, []byte("mcp:\n mode: global\n servers:\n - name: explicit\n url: https://mcp.example/mcp\n auth: {mode: none}\n"), 0o600); err != nil { t.Fatal(err) } - cfg, err = parseFlags([]string{"--permission-config", explicit}) + cfg, err = parseFlags([]string{"--permission-config", explicit, "--permission-mode=yolo", "--guardrails=off"}) if err != nil { t.Fatal(err) } diff --git a/cmd/mecak8s/permission_mode.go b/cmd/mecak8s/permission_mode.go new file mode 100644 index 0000000000..8a3977ce7a --- /dev/null +++ b/cmd/mecak8s/permission_mode.go @@ -0,0 +1,45 @@ +package main + +import ( + "context" + "fmt" + "strings" + + "github.com/stacklok/mecatl/engine/port" + "github.com/stacklok/mecatl/internal/app" +) + +// permissionModeHelp is the --permission-mode usage line. It names +// both halves a token sets and their lifetimes, and lists every valid token from +// the composition table so help and parsing cannot drift. +var permissionModeHelp = "Permission mode, one of: " + strings.Join(app.PermissionModeNames(), ", ") + + ". Sets two things: the mode new sessions start in, and the process-wide server ceiling, fixed at startup, which is the most permissive mode a session may switch to (accept-edits for plan, default, and accept-edits). Default: auto. " + + "auto and yolo allow tools without asking and require MECATL_SANDBOX when running as root. auto needs an enforcing guardrails checker (--guardrails-model or the guardrail model slot) and refuses to start without one, including with --guardrails off; yolo runs without one. " + + "Deny rules and configured ask rules still apply. A mode never grants project trust; use --trust-project. Replaces the deprecated --posture" + +// validatePermissionModeFlags fails fast on an unknown --permission-mode token +// and on a --permission-mode combined with the deprecated --posture alias. +func validatePermissionModeFlags(cfg config) error { + if !cfg.permissionModeFlagSet { + return nil + } + if _, err := app.ParsePermissionMode(cfg.permissionMode); err != nil { + return fmt.Errorf("--permission-mode: %w", err) + } + if cfg.postureFlagSet { + return fmt.Errorf("--permission-mode and --posture are both set: pass only --permission-mode (--posture is its deprecated alias)") + } + return nil +} + +// warnDeprecatedPermissionFlags emits exactly one deprecation WARN when the +// operator passed the deprecated --posture explicitly. The --posture DEFAULT +// (auto) is not a use of the alias and stays silent. +func warnDeprecatedPermissionFlags(diag port.Diagnostics, cfg config) { + if diag == nil || !cfg.postureFlagSet { + return + } + diag.Log(context.Background(), port.LevelWarn, + "--posture is DEPRECATED; use --permission-mode instead", + "posture", cfg.posture, "replacement", "--permission-mode "+app.PostureReplacementToken(app.ParsePosture(cfg.posture))) +} diff --git a/cmd/mecak8s/permission_mode_test.go b/cmd/mecak8s/permission_mode_test.go new file mode 100644 index 0000000000..9a28c359a8 --- /dev/null +++ b/cmd/mecak8s/permission_mode_test.go @@ -0,0 +1,224 @@ +package main + +import ( + "bytes" + "context" + "log/slog" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/stacklok/mecatl/engine/adapter/mockllm" + "github.com/stacklok/mecatl/engine/port" + "github.com/stacklok/mecatl/engine/session" + "github.com/stacklok/mecatl/internal/adapter/slogdiag" + "github.com/stacklok/mecatl/internal/app" +) + +// k8sPair returns the (posture, default session mode) pair and the app.Config +// that a mecak8s argv hands to app.Build. +func k8sPair(t *testing.T, argv ...string) (app.Posture, session.PermissionMode, app.Config) { + t.Helper() + cfg, err := parseFlags(argv) + if err != nil { + t.Fatalf("parseFlags(%q): %v", argv, err) + } + ac := appConfig(cfg, port.NopDiagnostics{}, observability{}) + if ac.PermissionModeFlagSet { + tok, err := app.ParsePermissionMode(ac.PermissionMode) + if err != nil { + t.Fatalf("appConfig carried an invalid token %q: %v", ac.PermissionMode, err) + } + return app.ResolveAliasPosture(tok.Posture, ac.AllowAllTools, ac.TrustProject), tok.SessionMode, ac + } + mode := ac.DefaultSessionMode + if mode == "" { + mode = session.ModeDefault + } + return app.ResolveAuthoritativePosture(ac), mode, ac +} + +func k8sDeprecationWarnings(t *testing.T, argv ...string) []string { + t.Helper() + cfg, err := parseFlags(argv) + if err != nil { + t.Fatalf("parseFlags(%q): %v", argv, err) + } + var buf bytes.Buffer + warnDeprecatedPermissionFlags(slogdiag.NewFromLogger(slog.New(slog.NewTextHandler(&buf, nil))), cfg) + var out []string + for _, line := range strings.Split(buf.String(), "\n") { + if strings.Contains(line, "level=WARN") && strings.Contains(line, "DEPRECATED") { + out = append(out, line) + } + } + return out +} + +func TestDeprecatedAliasesStillResolve(t *testing.T) { + // Each deprecated --posture alias must resolve to the correct posture and emit + // exactly one deprecation WARN naming the replacement --permission-mode token. + // Session mode for deprecated paths is set inside Build (not at flag-parse time). + for _, tc := range []struct { + posture string + wantPosture app.Posture + token string + }{ + {"strict", app.PostureStrict, "default"}, + {"auto", app.PostureAuto, "auto"}, + {"yolo", app.PostureYolo, "yolo"}, + } { + t.Run(tc.posture, func(t *testing.T) { + aliasPosture, _, ac := k8sPair(t, "--posture", tc.posture) + if aliasPosture != tc.wantPosture { + t.Fatalf("--posture %s posture = %s, want %s", tc.posture, aliasPosture, tc.wantPosture) + } + if ac.DefaultSessionMode != "" { + t.Fatalf("--posture %s: cmd layer must not set DefaultSessionMode (got %q); Build sets it", tc.posture, ac.DefaultSessionMode) + } + warns := k8sDeprecationWarnings(t, "--posture", tc.posture) + if len(warns) != 1 || !strings.Contains(warns[0], "--permission-mode "+tc.token) { + t.Fatalf("--posture %s deprecation WARNs = %q, want exactly one naming --permission-mode %s", tc.posture, warns, tc.token) + } + }) + } + // The --posture DEFAULT is not a use of the alias. + if warns := k8sDeprecationWarnings(t); len(warns) != 0 { + t.Fatalf("default mecak8s emitted deprecation WARNs: %q", warns) + } + if warns := k8sDeprecationWarnings(t, "--permission-mode", "auto"); len(warns) != 0 { + t.Fatalf("--permission-mode emitted deprecation WARNs: %q", warns) + } +} + +func TestAliasCombinationsStillResolve(t *testing.T) { + // Headless root: no token grants project trust, so strict plus explicit trust + // (today: trusted posture, TrustProject) must keep resolving as it does today. + posture, mode, ac := k8sPair(t, "--posture", "strict", "--trust-project") + if posture != app.PostureTrusted || mode != session.ModeDefault || !ac.TrustProject { + t.Fatalf("--posture strict --trust-project = (%s, %s, trust=%v), want (trusted, default, trust=true)", posture, mode, ac.TrustProject) + } + posture, _, ac = k8sPair(t, "--posture", "yolo", "--trust-project") + if posture != app.PostureYolo || !ac.TrustProject { + t.Fatalf("--posture yolo --trust-project = (%s, trust=%v), want (yolo, trust=true)", posture, ac.TrustProject) + } + posture, _, ac = k8sPair(t, "--permission-mode", "auto", "--trust-project") + if posture != app.PostureAuto || !ac.TrustProject { + t.Fatalf("--permission-mode auto --trust-project = (%s, trust=%v), want (auto, trust=true)", posture, ac.TrustProject) + } +} + +func TestDefaultsReproduceCurrentBehaviour(t *testing.T) { + posture, mode, ac := k8sPair(t) + if posture != app.PostureAuto || mode != session.ModeDefault { + t.Fatalf("default mecak8s = (%s, %s), want (auto, default)", posture, mode) + } + // The auto default stays a non-explicit --posture default, so operator YAML + // can still override it exactly as today. + if ac.Posture != app.PostureAuto || ac.PostureFlagSet || ac.PermissionModeFlagSet || ac.DefaultSessionMode != "" { + t.Fatalf("default app.Config posture=%s postureSet=%v modeSet=%v default=%q, want auto/false/false/empty", + ac.Posture, ac.PostureFlagSet, ac.PermissionModeFlagSet, ac.DefaultSessionMode) + } + if !ac.Headless || ac.Interactive || ac.TrustProject { + t.Fatalf("default mecak8s headless=%v interactive=%v trust=%v, want true/false/false", ac.Headless, ac.Interactive, ac.TrustProject) + } +} + +func TestBareMecak8sRefusesWithoutCheckerChoice(t *testing.T) { + // The bare default is auto, which requires an enforcing checker: no checker + // and the explicit kill-switch both refuse; unchecked autonomy is yolo. + build := func(t *testing.T, args ...string) error { + t.Helper() + cfg, err := parseFlags(args) + if err != nil { + t.Fatalf("parseFlags %v: %v", args, err) + } + ac := appConfig(cfg, port.NopDiagnostics{}, observability{}) + ac.RedisURL = "" + ac.SchedulerEnabled = false + ac.SessionLeaseK8sNamespace = "" + ac.Workspace = t.TempDir() + ac.MockProvider = mockllm.New() + built, err := buildIsolated(t, context.Background(), ac) + if err == nil { + built.Close() + } + return err + } + for _, args := range [][]string{nil, {"--guardrails=off"}} { + if err := build(t, args...); err == nil || !strings.Contains(err.Error(), "requires an enforcing guardrails checker") { + t.Fatalf("mecak8s %v: err = %v, want the enforcing-checker refusal", args, err) + } + } + if err := build(t, "--permission-mode=yolo", "--guardrails=off"); err != nil { + t.Fatalf("mecak8s --permission-mode=yolo --guardrails=off must start: %v", err) + } +} + +// TestChartAutoInheritsMountedSettingsChecker covers the chart's default argv +// shape (--permission-mode=auto with no guardrails flag, because guardrails.model +// and guardrails.disabled are unset) composed with mounted operator settings: +// a checker configured there lets auto start, none refuses, and the explicit +// kill-switch (guardrails.disabled) wins over the settings checker. +func TestChartAutoInheritsMountedSettingsChecker(t *testing.T) { + settings := filepath.Join(t.TempDir(), "settings.yaml") + if err := os.WriteFile(settings, []byte("guardrails:\n model: mock-checker\n"), 0o600); err != nil { + t.Fatal(err) + } + slot := filepath.Join(t.TempDir(), "settings.yaml") + if err := os.WriteFile(slot, []byte("models:\n slots:\n guardrail: mock-checker\n"), 0o600); err != nil { + t.Fatal(err) + } + build := func(argv ...string) error { + t.Helper() + cfg, err := parseFlags(append([]string{"--mock", "--headless=true", "--permission-mode=auto"}, argv...)) + if err != nil { + t.Fatalf("parseFlags: %v", err) + } + ac := appConfig(cfg, port.NopDiagnostics{}, observability{}) + ac.RedisURL = "" + ac.SchedulerEnabled = false + ac.SessionLeaseK8sNamespace = "" + ac.Workspace = t.TempDir() + built, err := buildIsolated(t, context.Background(), ac) + if err == nil { + built.Close() + } + return err + } + for name, path := range map[string]string{"guardrails model": settings, "guardrail slot": slot} { + if err := build("--permission-config=" + path); err != nil { + t.Fatalf("auto with a mounted %s checker must start: %v", name, err) + } + } + for name, argv := range map[string][]string{ + "no inherited checker": nil, + "explicit kill-switch": {"--guardrails=off", "--permission-config=" + settings}, + } { + if err := build(argv...); err == nil || !strings.Contains(err.Error(), "requires an enforcing guardrails checker") { + t.Fatalf("auto with %s err = %v, want the enforcing-checker refusal", name, err) + } + } +} + +func TestPermissionModeFlagConflictsAndUnknownTokens(t *testing.T) { + _, err := parseFlags([]string{"--permission-mode", "auto", "--posture", "auto"}) + if err == nil || !strings.Contains(err.Error(), "pass only --permission-mode") { + t.Fatalf("--permission-mode with --posture err = %v, want a pass-one startup error", err) + } + _, err = parseFlags([]string{"--permission-mode", "bogus"}) + if err == nil { + t.Fatal("unknown --permission-mode token parsed without error") + } + for _, name := range app.PermissionModeNames() { + if !strings.Contains(err.Error(), name) { + t.Fatalf("unknown-token error %q does not name %q", err, name) + } + } + for _, want := range append(app.PermissionModeNames(), "process-wide", "new sessions") { + if !strings.Contains(permissionModeHelp, want) { + t.Fatalf("--permission-mode help lacks %q", want) + } + } +} diff --git a/cmd/mecak8s/telemetry_test.go b/cmd/mecak8s/telemetry_test.go index 08aef9faae..34beecdc31 100644 --- a/cmd/mecak8s/telemetry_test.go +++ b/cmd/mecak8s/telemetry_test.go @@ -227,7 +227,8 @@ func TestTelemetryMetricsAddrServesPrometheus(t *testing.T) { // A fixed free loopback port so the scrape target is deterministic. free := freeLoopbackPort(t) cfg, err := parseFlags([]string{ - "--mock", // offline: no provider key in CI + "--mock", // offline: no provider key in CI + "--permission-mode=yolo", "--guardrails=off", // unchecked allow-all is yolo "--redis-url", mr.Addr(), "--redis-allow-plaintext", // disposable miniredis fixture "--metrics-addr", free, @@ -339,6 +340,7 @@ func TestTelemetryPushesRunMetricsOnExit(t *testing.T) { cfg, err := parseFlags([]string{ "--mock", + "--permission-mode=yolo", "--guardrails=off", // unchecked allow-all is yolo "--redis-url", mr.Addr(), "--redis-allow-plaintext", // disposable miniredis fixture "--grpc-addr", "127.0.0.1:0", diff --git a/cmd/mecated/helpmeta.go b/cmd/mecated/helpmeta.go index 42a445bff4..a9a71a8c10 100644 --- a/cmd/mecated/helpmeta.go +++ b/cmd/mecated/helpmeta.go @@ -186,10 +186,11 @@ var flagMetaByFlag = map[string]flagMeta{ "no-shell": {group: groupTools, common: true, acp: acpInclude}, // ── Posture & permissions (both) ───────────────────────────────────── - "posture": {group: groupPermissions, common: true, acp: acpInclude}, + "permission-mode": {group: groupPermissions, common: true, acp: acpInclude}, + "posture": {group: groupPermissions, common: false, acp: acpInclude}, "deployment-id": {group: groupServer, common: false, acp: acpInclude}, "cors-origins": {group: groupSecurity, common: false, acp: acpExclude}, - "yolo": {group: groupPermissions, common: true, acp: acpInclude}, + "yolo": {group: groupPermissions, common: false, acp: acpInclude}, "trust-project": {group: groupPermissions, common: true, acp: acpInclude}, "permissions-conventional": {group: groupPermissions, common: true, acp: acpInclude}, "import-claude-permissions": {group: groupPermissions, common: false, acp: acpInclude}, diff --git a/cmd/mecated/main.go b/cmd/mecated/main.go index 1edcf9a450..8c41a7edbb 100644 --- a/cmd/mecated/main.go +++ b/cmd/mecated/main.go @@ -512,6 +512,15 @@ type config struct { // postureFlagSet is true when --posture was passed explicitly (set after parse via // fs.Visit), so composition lets CLI out-rank the settings.yaml posture: key. postureFlagSet bool + // permissionMode is the raw --permission-mode token, validated at + // parse time; permissionModeFlagSet records that it was passed explicitly, so + // composition (app.Build's foldPermissionMode) lets it out-rank every other + // posture source. --posture and --yolo are its deprecated aliases. + permissionMode string + permissionModeFlagSet bool + // yoloFlagSet records an explicit --yolo, for the deprecation WARN and the + // --permission-mode conflict check. + yoloFlagSet bool // reasoningEffort is the operator-tier reasoning-effort default: "" // or "auto" (unset → the provider default) or low/medium/high/xhigh/max. // Operator-tier only: the operator-global settings.yaml reasoning-effort: key @@ -1333,7 +1342,12 @@ func appConfig(cfg config, sink port.EventSink, recorder port.ToolCallRecorder, // YAML-only allow-all tier cannot escape it. Posture: app.ParsePosture(cfg.posture), PostureFlagSet: cfg.postureFlagSet, - DeploymentID: cfg.deploymentID, + // Permission mode: the named token writes both the posture and + // the default session mode; app.Build's foldPermissionMode resolves it + // ahead of the deprecated --posture/--yolo surface. + PermissionMode: cfg.permissionMode, + PermissionModeFlagSet: cfg.permissionModeFlagSet, + DeploymentID: cfg.deploymentID, // Reasoning-effort tier: operator-tier only; reasoningEffortFlagSet // lets CLI out-rank the operator-global settings.yaml reasoning-effort: key. ReasoningEffort: cfg.reasoningEffort, @@ -1387,6 +1401,7 @@ func appConfig(cfg config, sink port.EventSink, recorder port.ToolCallRecorder, // cyclomatic complexity bounded. A non-nil err is the root/no-sandbox refusal (a // fast path — app.Build re-checks it authoritatively as the fail-closed backstop). func applyPostureCLI(cfg config, diag port.Diagnostics) error { + warnDeprecatedPermissionFlags(diag, cfg) effPosture := app.ResolveAuthoritativePosture(posturePreCheckConfig(cfg, diag)) if !app.IsKnownPostureToken(cfg.posture) { slog.Warn("unknown --posture value; failing closed to strict", "value", cfg.posture) @@ -1414,13 +1429,15 @@ func applyPostureCLI(cfg config, diag port.Diagnostics) error { // --trust-project inputs. It does NOT need the provider/engine fields — app.Build owns // the authoritative resolution + the engine; this is only the early read. func posturePreCheckConfig(cfg config, diag port.Diagnostics) app.Config { - return app.Config{ + out := app.Config{ Workspace: cfg.workspace, PermissionsConventional: cfg.permissionsConventional, ImportClaudePermissions: cfg.importClaudePermissions, PermissionConfigs: cfg.permissionConfigs, Posture: app.ParsePosture(cfg.posture), PostureFlagSet: cfg.postureFlagSet, + PermissionMode: cfg.permissionMode, + PermissionModeFlagSet: cfg.permissionModeFlagSet, DeploymentID: cfg.deploymentID, AllowAllTools: cfg.allowAllTools, TrustProject: cfg.trustProject, @@ -1428,6 +1445,14 @@ func posturePreCheckConfig(cfg config, diag port.Diagnostics) app.Config { Interactive: !cfg.headless, Diagnostics: diag, } + // An explicit --permission-mode token's posture half is the explicit tier the + // fast path must see, so `--permission-mode yolo` as bare root hits the same + // refusal `--yolo` does. The token was validated at parse time. + if tok, err := app.ParsePermissionMode(cfg.permissionMode); cfg.permissionModeFlagSet && err == nil { + out.Posture = tok.Posture + out.PostureFlagSet = true + } + return out } // privilegedProcess reports the cmd-computed "root WITHOUT a declared sandbox" @@ -1828,7 +1853,7 @@ func parseFlagsModeOut(mode commandMode, argv []string, out io.Writer) (*flag.Fl // Shared model alias/slot flags (cliconfig): mecated uses the default help // wording, so a zero ModelFlagHelp is enough. cfg.modelAliases, cfg.modelAliasProviders, cfg.modelSlots = cliconfig.RegisterModelFlags(fs, cliconfig.ModelFlagHelp{}) - fs.StringVar(&cfg.subagentAskReviewer, "subagent-ask-reviewer", "", "model identifier or --model-alias for reviewing child permission requests in headless mode. An allow applies only to that request; denied or failed reviews deny it. Empty disables reviews.") + fs.StringVar(&cfg.subagentAskReviewer, "subagent-ask-reviewer", "", "model identifier or --model-alias for reviewing child permission requests in headless mode. An allow applies only to that request; denied or failed reviews deny it. Empty leaves reviews off, except that a headless auto or yolo permission mode turns them on by default (ask-reviewer model slot, else the session model); off disables reviews explicitly.") fs.IntVar(&cfg.subagentAskReviewerMaxDenies, "subagent-ask-reviewer-max-denies", agent.DefaultAskReviewMaxDenies, "maximum consecutive denied, failed, or timed-out child-permission reviews before later requests are automatically denied. An allowed review resets the count. Values at or below 0 use the default of 3.") fs.StringVar(&cfg.subagentAskReviewerPolicyFile, "subagent-ask-reviewer-policy", "", "path to a trusted policy file that replaces the built-in child-permission review rubric. Startup fails if the file is unreadable.") fs.BoolVar(&cfg.subagentModelRouter, "subagent-model-router", false, "set explicitly to false to disable the configured subagent model router. The router selects a model for eligible plain subagent delegations; failures use the default model.") @@ -1861,10 +1886,11 @@ func parseFlagsModeOut(mode commandMode, argv []string, out io.Writer) (*flag.Fl fs.BoolVar(&cfg.permissionsConventional, "permissions-conventional", true, "load project and user permission settings for each session. Enabled by default. Project allow rules require --trust-project; project deny and ask rules always apply.") fs.BoolVar(&cfg.importClaudePermissions, "import-claude-permissions", false, "also load supported Claude Code permission settings when --permissions-conventional is enabled. Unsupported or invalid rules are ignored with a log entry.") fs.BoolVar(&cfg.trustProject, "trust-project", false, "apply allow rules from a discovered project permission configuration. Project deny and ask rules always apply. Disabled by default because project allow rules can approve tool calls.") + fs.StringVar(&cfg.permissionMode, "permission-mode", "", permissionModeHelp) fs.BoolVar(&cfg.allowAllTools, "yolo", false, - "alias for --posture=yolo. Allows tools server-wide and lets child command substitutions run automatically. Deny rules and configured ask rules still apply. Intended only for isolated, ephemeral, single-tenant environments. Refused for root unless MECATL_SANDBOX=1 or IS_SANDBOX=1.") + "DEPRECATED: use --permission-mode yolo. Alias for --posture=yolo. Allows tools server-wide and lets child command substitutions run automatically. Deny rules and configured ask rules still apply. Intended only for isolated, ephemeral, single-tenant environments. Refused for root unless MECATL_SANDBOX=1 or IS_SANDBOX=1.") fs.StringVar(&cfg.posture, "posture", "", - "permission posture: strict prompts mutations; trusted applies project allow rules; auto allows tools and relaxes main shell substitutions; yolo also runs child command substitutions. Deny rules and configured ask rules still apply. strict is the default. --trust-project and --yolo select trusted and yolo. auto and yolo are refused for root outside MECATL_SANDBOX.") + "DEPRECATED: use --permission-mode. Permission posture: strict prompts mutations; trusted applies project allow rules; auto allows tools and relaxes main shell substitutions; yolo also runs child command substitutions. Deny rules and configured ask rules still apply. strict is the default. --trust-project and --yolo select trusted and yolo. auto and yolo are refused for root outside MECATL_SANDBOX.") fs.StringVar(&cfg.deploymentID, "deployment-id", "", fmt.Sprintf("optional opaque deployment label returned by GetCompatibilityInfo. Mecatl never derives it from host or environment values. Printable single-line text only, up to %d bytes.", maxDeploymentIDLen)) @@ -1942,6 +1968,9 @@ func parseFlagsModeOut(mode commandMode, argv []string, out io.Writer) (*flag.Fl // composition can let CLI out-rank the operator-global settings.yaml posture: key // and WARN if an alias raised above an explicit lower --posture. recordExplicitFlags(fs, &cfg) + if err := validatePermissionModeFlags(cfg); err != nil { + return fs, config{}, err + } resolvedShell, err := cliconfig.ResolveCommandRunnerConfig(cfg.shell, cfg.shellFlagSet, cfg.permissionsConventional, cfg.permissionConfigs) if err != nil { return fs, config{}, fmt.Errorf("command runner configuration: %w", err) @@ -2034,6 +2063,10 @@ func recordExplicitFlags(fs *flag.FlagSet, cfg *config) { switch f.Name { case "posture": cfg.postureFlagSet = true + case "permission-mode": + cfg.permissionModeFlagSet = true + case "yolo": + cfg.yoloFlagSet = true case "shell": cfg.shellFlagSet = true case "subagent-model-router": diff --git a/cmd/mecated/permission_mode.go b/cmd/mecated/permission_mode.go new file mode 100644 index 0000000000..bea9a609e3 --- /dev/null +++ b/cmd/mecated/permission_mode.go @@ -0,0 +1,56 @@ +package main + +import ( + "context" + "fmt" + "strings" + + "github.com/stacklok/mecatl/engine/port" + "github.com/stacklok/mecatl/internal/app" +) + +// permissionModeHelp is the --permission-mode usage line. It names +// both halves a token sets and their lifetimes, and lists every valid token from +// the composition table so help and parsing cannot drift. +var permissionModeHelp = "permission mode, one of: " + strings.Join(app.PermissionModeNames(), ", ") + + ". Sets two things: the mode new sessions start in, and the process-wide server ceiling, fixed at startup, which is the most permissive mode a session may switch to (accept-edits for plan, default, and accept-edits). Default: default. " + + "auto and yolo allow tools without asking. auto needs an enforcing guardrails checker (--guardrails-model or the guardrail model slot) and refuses to start without one; yolo runs without one. Both are refused for root unless MECATL_SANDBOX=1 or IS_SANDBOX=1. " + + "Deny rules and configured ask rules still apply. A mode never grants project trust; use --trust-project. Replaces the deprecated --posture and --yolo." + +// validatePermissionModeFlags fails fast on an unknown --permission-mode token +// and on a --permission-mode combined with one of its deprecated aliases, so the +// operator never has to guess which of two permission sources won. +func validatePermissionModeFlags(cfg config) error { + if !cfg.permissionModeFlagSet { + return nil + } + if _, err := app.ParsePermissionMode(cfg.permissionMode); err != nil { + return fmt.Errorf("--permission-mode: %w", err) + } + if cfg.postureFlagSet { + return fmt.Errorf("--permission-mode and --posture are both set: pass only --permission-mode (--posture is its deprecated alias)") + } + if cfg.yoloFlagSet { + return fmt.Errorf("--permission-mode and --yolo are both set: pass only --permission-mode (--yolo is a deprecated alias for --permission-mode yolo)") + } + return nil +} + +// warnDeprecatedPermissionFlags emits exactly one deprecation WARN per +// deprecated alias the operator passed, naming the --permission-mode token that +// replaces it. The aliases still resolve exactly as before. +func warnDeprecatedPermissionFlags(diag port.Diagnostics, cfg config) { + if diag == nil { + return + } + if cfg.postureFlagSet { + diag.Log(context.Background(), port.LevelWarn, + "--posture is DEPRECATED; use --permission-mode instead", + "posture", cfg.posture, "replacement", "--permission-mode "+app.PostureReplacementToken(app.ParsePosture(cfg.posture))) + } + if cfg.yoloFlagSet { + diag.Log(context.Background(), port.LevelWarn, + "--yolo is DEPRECATED; use --permission-mode yolo instead", + "replacement", "--permission-mode yolo") + } +} diff --git a/cmd/mecated/permission_mode_test.go b/cmd/mecated/permission_mode_test.go new file mode 100644 index 0000000000..7b2f3435f3 --- /dev/null +++ b/cmd/mecated/permission_mode_test.go @@ -0,0 +1,325 @@ +package main + +import ( + "context" + "log/slog" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/stacklok/mecatl/engine/port" + "github.com/stacklok/mecatl/engine/session" + "github.com/stacklok/mecatl/internal/adapter/slogdiag" + "github.com/stacklok/mecatl/internal/app" +) + +// resolvedPair is the (posture, default session mode) pair a mecated argv +// produces, read through the same fast-path resolver applyPostureCLI uses and +// the app.Config the root hands to app.Build. +func resolvedPair(t *testing.T, argv ...string) (app.Posture, session.PermissionMode, app.Config) { + t.Helper() + cfg, err := parseFlags(argv) + if err != nil { + t.Fatalf("parseFlags(%q): %v", argv, err) + } + posture := app.ResolveAuthoritativePosture(posturePreCheckConfig(cfg, port.NopDiagnostics{})) + mode := session.ModeDefault + ac := appConfig(cfg, nil, nil, nil, nil, nil) + if ac.PermissionModeFlagSet { + tok, err := app.ParsePermissionMode(ac.PermissionMode) + if err != nil { + t.Fatalf("appConfig carried an invalid token %q: %v", ac.PermissionMode, err) + } + mode = tok.SessionMode + } else if ac.DefaultSessionMode != "" { + mode = ac.DefaultSessionMode + } + return posture, mode, ac +} + +// deprecationWarnings runs the real startup posture surface (applyPostureCLI) +// and returns the deprecation WARN lines it logged. +func deprecationWarnings(t *testing.T, argv ...string) []string { + t.Helper() + cfg, err := parseFlags(argv) + if err != nil { + t.Fatalf("parseFlags(%q): %v", argv, err) + } + logs := captureLogs(t) + if err := applyPostureCLI(cfg, slogdiag.NewFromLogger(slog.Default())); err != nil { + t.Fatalf("applyPostureCLI(%q): %v", argv, err) + } + var out []string + for _, line := range strings.Split(logs(), "\n") { + if strings.Contains(line, "level=WARN") && strings.Contains(line, "DEPRECATED") { + out = append(out, line) + } + } + return out +} + +func TestDeprecatedAliasesStillResolve(t *testing.T) { + // Each deprecated alias must resolve to the correct posture and emit exactly + // one deprecation WARN naming the replacement --permission-mode token. + // Session mode is set inside Build (via foldPermissionMode), not at flag-parse + // time, so the pre-Build resolvedPair returns ModeDefault for deprecated paths. + cases := []struct { + name string + alias []string + wantPosture app.Posture + wantToken string + }{ + {"posture strict", []string{"--posture", "strict"}, app.PostureStrict, "default"}, + {"posture auto", []string{"--posture", "auto"}, app.PostureAuto, "auto"}, + {"posture yolo", []string{"--posture", "yolo"}, app.PostureYolo, "yolo"}, + {"yolo", []string{"--yolo"}, app.PostureYolo, "yolo"}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + aliasPosture, _, _ := resolvedPair(t, tc.alias...) + if aliasPosture != tc.wantPosture { + t.Fatalf("%q posture = %s, want %s", tc.alias, aliasPosture, tc.wantPosture) + } + warns := deprecationWarnings(t, tc.alias...) + if len(warns) != 1 { + t.Fatalf("%q emitted %d deprecation WARNs, want exactly 1: %q", tc.alias, len(warns), warns) + } + if !strings.Contains(warns[0], "--permission-mode") { + t.Fatalf("deprecation WARN does not name --permission-mode: %s", warns[0]) + } + if !strings.Contains(warns[0], "--permission-mode "+tc.wantToken) { + t.Fatalf("deprecation WARN does not name the replacement token %q: %s", tc.wantToken, warns[0]) + } + }) + } + // The replacement itself is not deprecated. + if warns := deprecationWarnings(t, "--permission-mode", "auto"); len(warns) != 0 { + t.Fatalf("--permission-mode emitted deprecation WARNs: %q", warns) + } +} + +func TestAliasCombinationsStillResolve(t *testing.T) { + cases := []struct { + name string + argv []string + wantPosture app.Posture + wantMode session.PermissionMode + wantTrust bool + wantAllow bool + }{ + // Interactive root: --trust-project raises an explicit strict to trusted, today's MAX fold. + {"posture strict trust-project interactive", []string{"--posture", "strict", "--trust-project"}, app.PostureTrusted, session.ModeDefault, true, false}, + // Headless root: no token grants trust on headless, so yolo plus explicit trust has no single-token spelling. + {"yolo trust-project headless", []string{"--headless", "--yolo", "--trust-project"}, app.PostureYolo, session.ModeDefault, true, true}, + {"posture auto yolo", []string{"--posture", "auto", "--yolo"}, app.PostureYolo, session.ModeDefault, false, true}, + // --trust-project is not deprecated and still combines with the new flag. + // --permission-mode auto sets session mode to ModeAuto via the token table. + {"permission-mode auto trust-project headless", []string{"--headless", "--permission-mode", "auto", "--trust-project"}, app.PostureAuto, session.ModeAuto, true, false}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + posture, mode, ac := resolvedPair(t, tc.argv...) + if posture != tc.wantPosture { + t.Fatalf("%q posture = %s, want %s", tc.argv, posture, tc.wantPosture) + } + if mode != tc.wantMode { + t.Fatalf("%q session mode = %s, want %s", tc.argv, mode, tc.wantMode) + } + if ac.TrustProject != tc.wantTrust { + t.Fatalf("%q TrustProject = %v, want %v", tc.argv, ac.TrustProject, tc.wantTrust) + } + if ac.AllowAllTools != tc.wantAllow { + t.Fatalf("%q AllowAllTools = %v, want %v", tc.argv, ac.AllowAllTools, tc.wantAllow) + } + }) + } +} + +// TestCombinedLegacyAliasesBuildRaisedPair drives the real flag, appConfig, and +// app.Build path: a deprecated --posture combined with --yolo must land on the +// session mode and ceiling of the raised tier, exactly as --yolo alone does, +// not on the pair the lower --posture value implies. +func TestCombinedLegacyAliasesBuildRaisedPair(t *testing.T) { + t.Setenv("XDG_CONFIG_HOME", t.TempDir()) + for _, argv := range [][]string{ + {"--yolo"}, + {"--posture", "strict", "--yolo"}, + {"--posture", "auto", "--yolo"}, + } { + t.Run(strings.Join(argv, " "), func(t *testing.T) { + cfg, err := parseFlags(append([]string{"--workspace", t.TempDir(), "--guardrails", "off"}, argv...)) + if err != nil { + t.Fatalf("parseFlags: %v", err) + } + ac := appConfig(cfg, nil, nil, nil, nil, port.NopDiagnostics{}) + ac.UseMock, ac.NoSoul, ac.Privileged = true, true, false + built, err := buildIsolated(t, context.Background(), ac) + if err != nil { + t.Fatalf("app.Build: %v", err) + } + defer built.Close() + sess, err := built.Service.CreateSession(context.Background(), "", session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + if sess.Mode != session.ModeYolo { + t.Fatalf("%q: unspecified-mode session started in %q, want yolo", argv, sess.Mode) + } + if _, err := built.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{}); err != nil { + t.Fatalf("%q: the yolo ceiling must admit a yolo session: %v", argv, err) + } + }) + } +} + +// TestSettingsPermissionModeRefusesDeprecatedFlags drives parseFlags, +// appConfig, and app.Build with an operator settings.yaml carrying +// permissionMode: plan. The deprecated --yolo and --posture flags cannot be +// combined with it, exactly as they cannot be combined with --permission-mode; +// the token alone, the deprecated flag alone, --permission-mode over the key, +// and the non-deprecated --trust-project still build. +func TestSettingsPermissionModeRefusesDeprecatedFlags(t *testing.T) { + t.Setenv("XDG_CONFIG_HOME", t.TempDir()) + settings := filepath.Join(t.TempDir(), "settings.yaml") + if err := os.WriteFile(settings, []byte("permissionMode: plan\n"), 0o600); err != nil { + t.Fatal(err) + } + build := func(t *testing.T, argv ...string) (*app.Built, error) { + t.Helper() + cfg, err := parseFlags(append([]string{"--workspace", t.TempDir(), "--guardrails", "off"}, argv...)) + if err != nil { + t.Fatalf("parseFlags: %v", err) + } + ac := appConfig(cfg, nil, nil, nil, nil, port.NopDiagnostics{}) + ac.UseMock, ac.NoSoul, ac.Privileged = true, true, false + return buildIsolated(t, context.Background(), ac) + } + for name, tc := range map[string]struct { + argv []string + want string + }{ + "yolo": {[]string{"--permission-config", settings, "--yolo"}, "--permission-mode yolo"}, + "posture": {[]string{"--permission-config", settings, "--posture", "auto"}, "--permission-mode auto"}, + "posture strict": {[]string{"--permission-config", settings, "--posture", "strict"}, "--permission-mode default"}, + } { + t.Run("refuses "+name, func(t *testing.T) { + built, err := build(t, tc.argv...) + if err == nil { + built.Close() + t.Fatalf("%q: settings permissionMode plus a deprecated flag must refuse to build", tc.argv) + } + for _, want := range []string{"permissionMode: plan", "cannot be combined", "keep permissionMode: plan", tc.want} { + if !strings.Contains(err.Error(), want) { + t.Errorf("%q: refusal must name %q; got %v", tc.argv, want, err) + } + } + }) + } + for name, tc := range map[string]struct { + argv []string + want session.PermissionMode + }{ + "settings token alone": {[]string{"--permission-config", settings}, session.ModePlan}, + "settings token with trust": {[]string{"--permission-config", settings, "--trust-project"}, session.ModePlan}, + "permission-mode over the key": {[]string{"--permission-config", settings, "--permission-mode", "yolo"}, session.ModeYolo}, + "yolo alone": {[]string{"--yolo"}, session.ModeYolo}, + "posture alone": {[]string{"--posture", "strict"}, session.ModeDefault}, + } { + t.Run("builds "+name, func(t *testing.T) { + built, err := build(t, tc.argv...) + if err != nil { + t.Fatalf("%q: app.Build: %v", tc.argv, err) + } + defer built.Close() + sess, err := built.Service.CreateSession(context.Background(), "", session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + if sess.Mode != tc.want { + t.Fatalf("%q: unspecified-mode session started in %q, want %q", tc.argv, sess.Mode, tc.want) + } + }) + } +} + +func TestDefaultsReproduceCurrentBehaviour(t *testing.T) { + for _, tc := range []struct { + name string + argv []string + }{ + {"interactive", nil}, + {"headless", []string{"--headless"}}, + } { + t.Run(tc.name, func(t *testing.T) { + posture, mode, ac := resolvedPair(t, tc.argv...) + if posture != app.PostureStrict || mode != session.ModeDefault { + t.Fatalf("unconfigured %s mecated = (%s, %s), want (strict, default)", tc.name, posture, mode) + } + if ac.Posture != app.PostureStrict || ac.PostureFlagSet || ac.PermissionModeFlagSet || ac.PermissionMode != "" || ac.DefaultSessionMode != "" { + t.Fatalf("unconfigured %s app.Config carries a permission choice: posture=%s postureSet=%v modeSet=%v mode=%q default=%q", + tc.name, ac.Posture, ac.PostureFlagSet, ac.PermissionModeFlagSet, ac.PermissionMode, ac.DefaultSessionMode) + } + if ac.AllowAllTools || ac.TrustProject { + t.Fatalf("unconfigured %s mecated raised allow-all=%v trust=%v", tc.name, ac.AllowAllTools, ac.TrustProject) + } + if warns := deprecationWarnings(t, tc.argv...); len(warns) != 0 { + t.Fatalf("unconfigured %s mecated emitted deprecation WARNs: %q", tc.name, warns) + } + }) + } +} + +func TestPermissionModeFlagConflictsAndUnknownTokens(t *testing.T) { + for _, argv := range [][]string{ + {"--permission-mode", "auto", "--posture", "auto"}, + {"--posture", "strict", "--permission-mode", "default"}, + {"--permission-mode", "yolo", "--yolo"}, + } { + _, err := parseFlags(argv) + if err == nil || !strings.Contains(err.Error(), "pass only --permission-mode") { + t.Fatalf("parseFlags(%q) err = %v, want a pass-one startup error", argv, err) + } + } + _, err := parseFlags([]string{"--permission-mode", "bogus"}) + if err == nil { + t.Fatal("unknown --permission-mode token parsed without error") + } + for _, name := range app.PermissionModeNames() { + if !strings.Contains(err.Error(), name) { + t.Fatalf("unknown-token error %q does not name valid token %q", err, name) + } + } + // --trust-project is not an alias and combines freely. + if _, err := parseFlags([]string{"--permission-mode", "default", "--trust-project"}); err != nil { + t.Fatalf("--permission-mode with --trust-project: %v", err) + } +} + +func TestPermissionModeHitsRootRefusalFastPath(t *testing.T) { + for _, tok := range []string{"auto", "yolo"} { + cfg, err := parseFlags([]string{"--permission-mode", tok}) + if err != nil { + t.Fatalf("parseFlags: %v", err) + } + eff := app.ResolveAuthoritativePosture(posturePreCheckConfig(cfg, port.NopDiagnostics{})) + if err := app.PostureRefusalReason(eff, true); err == nil { + t.Fatalf("--permission-mode %s as unsandboxed root was not refused on the fast path (posture %s)", tok, eff) + } + } + yolo, err := parseFlags([]string{"--yolo"}) + if err != nil { + t.Fatalf("parseFlags: %v", err) + } + if err := app.PostureRefusalReason(app.ResolveAuthoritativePosture(posturePreCheckConfig(yolo, port.NopDiagnostics{})), true); err == nil { + t.Fatal("--yolo as unsandboxed root was not refused on the fast path") + } +} + +func TestPermissionModeHelpNamesBothHalvesAndEveryToken(t *testing.T) { + for _, want := range append(app.PermissionModeNames(), "process-wide", "new sessions") { + if !strings.Contains(permissionModeHelp, want) { + t.Fatalf("--permission-mode help lacks %q: %s", want, permissionModeHelp) + } + } +} diff --git a/cmd/mecatequi/flags.go b/cmd/mecatequi/flags.go index 60c4d3bd1b..c4be97aeb6 100644 --- a/cmd/mecatequi/flags.go +++ b/cmd/mecatequi/flags.go @@ -143,6 +143,10 @@ type flags struct { // explicit --posture so composition lets CLI out-rank the settings.yaml key. posture string postureFlagSet bool + // permissionMode is the raw --permission-mode token, validated at + // parse time; permissionModeFlagSet records it was passed explicitly. + permissionMode string + permissionModeFlagSet bool // Reasoning-effort tier. reasoningEffortFlagSet records an explicit // --reasoning-effort so composition lets CLI out-rank the settings.yaml key. reasoningEffort string @@ -201,33 +205,10 @@ func parseFlags(argv []string) (flags, error) { return flags{}, err } - // Record whether --posture was set EXPLICITLY (vs the empty default) so - // composition lets CLI out-rank the operator-global settings.yaml posture: key. - fs.Visit(func(fl *flag.Flag) { - switch fl.Name { - case "posture": - f.postureFlagSet = true - case "shell": - f.shellFlagSet = true - case "subagent-model-router": - // Tri-state kill-switch: record that the flag was given so - // appConfig can distinguish unset / =false (kill-switch) / =true (inert). - f.subagentModelRouterSet = true - case "out-summary": - // Stdout-compact summary mode (issue #341): ONLY an EXPLICIT - // --out-summary=- selects it. The unset default also resolves to "-" - // but keeps the indented JSON — default behavior unchanged. - f.summaryCompact = f.outSummary == "-" - case "product-metrics": - f.productMetricsSet = true - } - if fl.Name == "reasoning-effort" { - f.reasoningEffortFlagSet = true - } - if fl.Name == "default-provider" { - f.defaultProviderFlagSet = true - } - }) + recordExplicitFlags(fs, &f) + if err := validatePermissionModeFlags(f); err != nil { + return flags{}, err + } resolvedShell, err := cliconfig.ResolveCommandRunnerConfig(f.shell, f.shellFlagSet, true, f.permissionConfigs) if err != nil { return flags{}, fmt.Errorf("command runner configuration: %w", err) @@ -281,6 +262,39 @@ func parseFlags(argv []string) (flags, error) { return f, nil } +// recordExplicitFlags records which flags were set EXPLICITLY (vs their +// defaults), e.g. so composition lets CLI out-rank the operator-global +// settings.yaml posture: and permissionMode: keys. +func recordExplicitFlags(fs *flag.FlagSet, f *flags) { + fs.Visit(func(fl *flag.Flag) { + switch fl.Name { + case "posture": + f.postureFlagSet = true + case "permission-mode": + f.permissionModeFlagSet = true + case "shell": + f.shellFlagSet = true + case "subagent-model-router": + // Tri-state kill-switch: record that the flag was given so + // appConfig can distinguish unset / =false (kill-switch) / =true (inert). + f.subagentModelRouterSet = true + case "out-summary": + // Stdout-compact summary mode (issue #341): ONLY an EXPLICIT + // --out-summary=- selects it. The unset default also resolves to "-" + // but keeps the indented JSON — default behavior unchanged. + f.summaryCompact = f.outSummary == "-" + case "product-metrics": + f.productMetricsSet = true + } + if fl.Name == "reasoning-effort" { + f.reasoningEffortFlagSet = true + } + if fl.Name == "default-provider" { + f.defaultProviderFlagSet = true + } + }) +} + // configureFlags registers mecatequi's flags on fs. Keeping registration separate // lets help-output tests exercise the same flag descriptions that users see. func configureFlags(fs *flag.FlagSet, f *flags) { @@ -331,12 +345,13 @@ func configureFlags(fs *flag.FlagSet, f *flags) { fs.StringVar(&f.guardrailsModel, "guardrails-model", "", "Model identifier or alias for the tool-free guardrails checker. Setting this flag enables guardrails unless --guardrails=off. A guardrail model slot takes precedence. Default: empty.") fs.StringVar(&f.guardrailsMode, "guardrails", "", "Set to off to disable guardrails, including configured checker models. Other values leave guardrails controlled by the configured checker model.") - fs.StringVar(&f.subagentAskReviewer, "subagent-ask-reviewer", "", "Model identifier or alias for the tool-free reviewer of child permission requests in headless runs. Default: empty, which disables the reviewer.") + fs.StringVar(&f.subagentAskReviewer, "subagent-ask-reviewer", "", "Model identifier or alias for the tool-free reviewer of child permission requests in headless runs. Default: empty, which leaves the reviewer off except in a headless auto or yolo permission mode, where it is on by default (ask-reviewer model slot, else the session model). Set off to disable it explicitly.") fs.BoolVar(&f.subagentModelRouter, "subagent-model-router", false, "Set false to disable configured subagent model routing. A configured models.router taxonomy enables routing; this flag does not enable it.") fs.IntVar(&f.subagentAskReviewerMaxDenies, "subagent-ask-reviewer-max-denies", agent.DefaultAskReviewMaxDenies, "Consecutive non-allow reviewer outcomes before the reviewer is disabled for the rest of the run. Values less than or equal to 0 use the default: 3.") fs.StringVar(&f.subagentAskReviewerPolicyFile, "subagent-ask-reviewer-policy", "", "Trusted policy rubric file for --subagent-ask-reviewer. Its contents replace the built-in rubric. An unreadable file fails startup.") - fs.StringVar(&f.posture, "posture", "", "Permission posture: strict (default), trusted, auto, or yolo. Headless runs need an explicit trust source such as --trust-project to admit project content. Deny and configured ask rules still apply. Unknown values use strict.") + fs.StringVar(&f.permissionMode, "permission-mode", "", permissionModeHelp) + fs.StringVar(&f.posture, "posture", "", "DEPRECATED: use --permission-mode. Permission posture: strict (default), trusted, auto, or yolo. Headless runs need an explicit trust source such as --trust-project to admit project content. Deny and configured ask rules still apply. Unknown values use strict.") fs.StringVar(&f.reasoningEffort, "reasoning-effort", "", "Reasoning effort: auto, low, medium, high, xhigh, or max. Empty uses the provider or operator setting. OpenAI maps xhigh and max to high. Unknown values use the provider or operator setting.") fs.BoolVar(&f.trustProject, "trust-project", false, "Allow workspace content to provide project instructions, rules, agents, skills, souls, commands, Git snapshots, and the read-only child worktree shell. Default: false. Enable only for a repository and Git metadata you trust.") @@ -478,6 +493,10 @@ func appConfig(f flags, diag port.Diagnostics, obs observability) app.Config { Posture: app.ParsePosture(f.posture), PostureFlagSet: f.postureFlagSet, + // Permission mode: an explicit token out-ranks the deprecated + // --posture in app.Build's foldPermissionMode. + PermissionMode: f.permissionMode, + PermissionModeFlagSet: f.permissionModeFlagSet, // Explicit workspace trust: on this HEADLESS root the posture ladder never // raises TrustProject, so --trust-project is the one-shot opt-in that admits // both project steering and the read-only worktree shell. diff --git a/cmd/mecatequi/main.go b/cmd/mecatequi/main.go index c2a27e9994..18d234fcc7 100644 --- a/cmd/mecatequi/main.go +++ b/cmd/mecatequi/main.go @@ -24,7 +24,7 @@ // (strict), the main engine asks on every mutate — so a strict + headless run that // reaches a mutate is CANCELLED on that ask (run.Cancel, drain-to-close) and exits 1 // with an actionable "no approver" message naming the fix. The intended CI posture is -// therefore --posture auto (allow-all, child injection-defense ON) or trusted/yolo, +// therefore --permission-mode auto (allow-all, child injection-defense ON) or yolo, // where the main engine does not ask. --timeout is the orthogonal wall-clock backstop. // See run()'s cancel-on-ask path and flags.posture. package main @@ -69,6 +69,7 @@ func realMain(argv []string, stdout, stderr io.Writer) int { } emitAuthFileWarning(stderr, f.providerCredentials.AuthFileWarning) diag := newDiagnostics() + warnDeprecatedPermissionFlags(diag, f) // Observability (issue #343): OPT-IN OTLP push. Built right after // flag parse so the flush-on-exit defer covers EVERY exit path (setup-failure @@ -188,7 +189,8 @@ func verdictLine(sum Summary, noApprover, timedOut bool) string { return fmt.Sprintf("mecatequi: TIMED OUT — stop=%s, %s; the run exceeded --timeout and was cancelled", stop, files) case noApprover: return "mecatequi: NO APPROVER — a permission approval was requested but none is attached (--headless); " + - "re-run with --posture auto|trusted|yolo or add allow rules. stop=" + stop + "re-run with --permission-mode auto (with an enforcing --guardrails-model ) or yolo (no checker), " + + "or add allow rules (--trust-project honours the repo's). stop=" + stop default: return fmt.Sprintf("mecatequi: done — stop=%s, %s", stop, files) } diff --git a/cmd/mecatequi/permission_mode.go b/cmd/mecatequi/permission_mode.go new file mode 100644 index 0000000000..b59e5bde2f --- /dev/null +++ b/cmd/mecatequi/permission_mode.go @@ -0,0 +1,44 @@ +package main + +import ( + "context" + "fmt" + "strings" + + "github.com/stacklok/mecatl/engine/port" + "github.com/stacklok/mecatl/internal/app" +) + +// permissionModeHelp is the --permission-mode usage line. It names +// both halves a token sets and their lifetimes, and lists every valid token from +// the composition table so help and parsing cannot drift. +var permissionModeHelp = "Permission mode, one of: " + strings.Join(app.PermissionModeNames(), ", ") + + ". Sets the mode the run's session starts in and the process-wide ceiling on the modes it may enter. Default: default. " + + "auto and yolo allow tools without asking. auto needs an enforcing guardrails checker (--guardrails-model or the guardrail model slot) and refuses to start without one; yolo runs without one. " + + "Deny and configured ask rules still apply. A mode never grants project trust; use --trust-project. Replaces the deprecated --posture." + +// validatePermissionModeFlags fails fast on an unknown --permission-mode token +// and on a --permission-mode combined with the deprecated --posture alias. +func validatePermissionModeFlags(f flags) error { + if !f.permissionModeFlagSet { + return nil + } + if _, err := app.ParsePermissionMode(f.permissionMode); err != nil { + return fmt.Errorf("--permission-mode: %w", err) + } + if f.postureFlagSet { + return fmt.Errorf("--permission-mode and --posture are both set: pass only --permission-mode (--posture is its deprecated alias)") + } + return nil +} + +// warnDeprecatedPermissionFlags emits exactly one deprecation WARN when the +// operator passed the deprecated --posture explicitly. +func warnDeprecatedPermissionFlags(diag port.Diagnostics, f flags) { + if diag == nil || !f.postureFlagSet { + return + } + diag.Log(context.Background(), port.LevelWarn, + "--posture is DEPRECATED; use --permission-mode instead", + "posture", f.posture, "replacement", "--permission-mode "+app.PostureReplacementToken(app.ParsePosture(f.posture))) +} diff --git a/cmd/mecatequi/permission_mode_test.go b/cmd/mecatequi/permission_mode_test.go new file mode 100644 index 0000000000..f75c5a69ac --- /dev/null +++ b/cmd/mecatequi/permission_mode_test.go @@ -0,0 +1,441 @@ +package main + +import ( + "bytes" + "context" + "fmt" + "io" + "log/slog" + "os" + "os/exec" + "path/filepath" + "regexp" + "slices" + "strings" + "testing" + + "sigs.k8s.io/yaml" + + "github.com/stacklok/mecatl/engine/port" + "github.com/stacklok/mecatl/engine/session" + "github.com/stacklok/mecatl/internal/adapter/slogdiag" + "github.com/stacklok/mecatl/internal/app" +) + +func TestDeprecatedAliasesStillResolve(t *testing.T) { + for _, tc := range []struct{ posture, token string }{ + {"strict", "default"}, {"auto", "auto"}, {"yolo", "yolo"}, + } { + t.Run(tc.posture, func(t *testing.T) { + alias, err := parseFlags([]string{"--prompt", "x", "--posture", tc.posture}) + if err != nil { + t.Fatalf("parseFlags: %v", err) + } + ac := appConfig(alias, port.NopDiagnostics{}, observability{}) + tok, err := app.ParsePermissionMode(tc.token) + if err != nil { + t.Fatal(err) + } + if got := app.ResolveAuthoritativePosture(ac); got != tok.Posture || ac.DefaultSessionMode != "" { + t.Fatalf("--posture %s = (%s, %q), want the %q token's (%s, default)", tc.posture, got, ac.DefaultSessionMode, tc.token, tok.Posture) + } + var buf bytes.Buffer + warnDeprecatedPermissionFlags(slogdiag.NewFromLogger(slog.New(slog.NewTextHandler(&buf, nil))), alias) + if n := strings.Count(buf.String(), "DEPRECATED"); n != 1 || !strings.Contains(buf.String(), "--permission-mode "+tc.token) { + t.Fatalf("--posture %s logged %d deprecation WARNs, want exactly one naming --permission-mode %s: %s", tc.posture, n, tc.token, buf.String()) + } + }) + } + f, err := parseFlags([]string{"--prompt", "x", "--permission-mode", "auto"}) + if err != nil { + t.Fatalf("parseFlags: %v", err) + } + var buf bytes.Buffer + warnDeprecatedPermissionFlags(slogdiag.NewFromLogger(slog.New(slog.NewTextHandler(&buf, nil))), f) + if buf.Len() != 0 { + t.Fatalf("--permission-mode emitted a deprecation WARN: %s", buf.String()) + } + if ac := appConfig(f, port.NopDiagnostics{}, observability{}); !ac.PermissionModeFlagSet || ac.PermissionMode != "auto" { + t.Fatalf("appConfig did not carry --permission-mode: set=%v mode=%q", ac.PermissionModeFlagSet, ac.PermissionMode) + } +} + +func TestPermissionModeFlagConflictsAndUnknownTokens(t *testing.T) { + _, err := parseFlags([]string{"--prompt", "x", "--permission-mode", "auto", "--posture", "auto"}) + if err == nil || !strings.Contains(err.Error(), "pass only --permission-mode") { + t.Fatalf("--permission-mode with --posture err = %v, want a pass-one startup error", err) + } + _, err = parseFlags([]string{"--prompt", "x", "--permission-mode", "bogus"}) + if err == nil { + t.Fatal("unknown --permission-mode token parsed without error") + } + for _, name := range app.PermissionModeNames() { + if !strings.Contains(err.Error(), name) || !strings.Contains(permissionModeHelp, name) { + t.Fatalf("unknown-token error %q or help does not name %q", err, name) + } + } + if !strings.Contains(permissionModeHelp, "process-wide") { + t.Fatal("--permission-mode help does not say which half is process-wide") + } +} + +// actionFile is the subset of the composite action.yml the checker-choice test reads. +type actionFile struct { + Inputs map[string]struct { + Default string `json:"default"` + } `json:"inputs"` + Runs struct { + Steps []struct { + Name string `json:"name"` + Env map[string]string `json:"env"` + Run string `json:"run"` + } `json:"steps"` + } `json:"runs"` +} + +type workflowFile struct { + Jobs map[string]struct { + Steps []struct { + Uses string `json:"uses"` + With map[string]any `json:"with"` + } `json:"steps"` + } `json:"jobs"` +} + +var inputRef = regexp.MustCompile(`^\$\{\{\s*inputs\.([a-z0-9-]+)\s*\}\}$`) + +// runActionScript executes the action's real "Run mecatequi" bash body with +// the given inputs (defaults fill the rest) against a fake binary that records +// its argv, and returns that argv plus the script's combined output. +func runActionScript(t *testing.T, action actionFile, with map[string]string) ([]string, string, error) { + t.Helper() + idx := slices.IndexFunc(action.Runs.Steps, func(s struct { + Name string `json:"name"` + Env map[string]string `json:"env"` + Run string `json:"run"` + }) bool { + return s.Name == "Run mecatequi" + }) + if idx < 0 { + t.Fatal("action.yml has no \"Run mecatequi\" step") + } + step := action.Runs.Steps[idx] + dir := t.TempDir() + argsOut := filepath.Join(dir, "argv") + // NUL-separated, so a multi-line value (the default instructions) stays one arg. + fake := "#!/bin/sh\nprintf '%s\\0' \"$@\" > \"$ARGS_OUT\"\n" + if err := os.WriteFile(filepath.Join(dir, "mecatequi"), []byte(fake), 0o700); err != nil { + t.Fatal(err) + } + prompt := filepath.Join(dir, "prompt.md") + if err := os.WriteFile(prompt, []byte("task"), 0o600); err != nil { + t.Fatal(err) + } + overrides := map[string]string{ + "prompt-file": prompt, + "workspace": dir, + "out-diff": filepath.Join(dir, "p.patch"), + "out-summary": filepath.Join(dir, "s.json"), + "out-events": filepath.Join(dir, "e.jsonl"), + } + for k, v := range with { + overrides[k] = v + } + env := []string{ + "PATH=" + os.Getenv("PATH"), + "HOME=" + dir, + "RUNNER_TEMP=" + dir, + "GITHUB_OUTPUT=" + filepath.Join(dir, "github_output"), + "ARGS_OUT=" + argsOut, + } + for name, expr := range step.Env { + m := inputRef.FindStringSubmatch(expr) + if m == nil { + t.Fatalf("step env %s=%q is not an inputs reference", name, expr) + } + input, ok := action.Inputs[m[1]] + if !ok { + t.Fatalf("step env %s references undeclared input %q", name, m[1]) + } + value := input.Default + if v, ok := overrides[m[1]]; ok { + value = v + } + env = append(env, name+"="+value) + } + cmd := exec.Command("bash", "-c", step.Run) + cmd.Env = env + cmd.Dir = dir + out, err := cmd.CombinedOutput() + if err != nil { + return nil, string(out), err + } + raw, rerr := os.ReadFile(argsOut) + if rerr != nil { + t.Fatalf("fake mecatequi was not invoked: %v\n%s", rerr, out) + } + return strings.Split(strings.TrimSuffix(string(raw), "\x00"), "\x00"), string(out), nil +} + +// hasPair reports whether argv carries flag immediately followed by value. +func hasPair(argv []string, flag, value string) bool { + for i := 0; i+1 < len(argv); i++ { + if argv[i] == flag && argv[i+1] == value { + return true + } + } + return false +} + +// hasGuardrailsFlag reports whether argv carries any guardrails checker flag. +func hasGuardrailsFlag(argv []string) bool { + return slices.ContainsFunc(argv, func(a string) bool { return strings.HasPrefix(a, "--guardrails") }) +} + +// loadAction parses the composite action, skipping when bash is unavailable. +func loadAction(t *testing.T) actionFile { + t.Helper() + if _, err := exec.LookPath("bash"); err != nil { + t.Skip("bash is required to execute the composite action body") + } + raw, err := os.ReadFile("../../.github/actions/mecatequi/action.yml") + if err != nil { + t.Fatal(err) + } + var action actionFile + if err := yaml.Unmarshal(raw, &action); err != nil { + t.Fatalf("parse action.yml: %v", err) + } + return action +} + +// buildFromActionArgv runs the action body with the given inputs against a fake +// binary, then feeds the argv it produced (plus extra flags, for example a +// mounted operator settings file) through the real CLI composition with the +// mock provider. The workspace input is pointed at a hermetic git repo. +func buildFromActionArgv(t *testing.T, action actionFile, with map[string]string, extra ...string) (*app.Built, error) { + t.Helper() + repo := t.TempDir() + initTestRepo(t, repo) + inputs := map[string]string{"workspace": repo} + for k, v := range with { + inputs[k] = v + } + argv, out, err := runActionScript(t, action, inputs) + if err != nil { + t.Fatalf("action body failed: %v\n%s", err, out) + } + f, err := parseFlags(append(argv, extra...)) + if err != nil { + t.Fatalf("parseFlags(%q): %v", argv, err) + } + cfg := appConfig(f, port.NopDiagnostics{}, observability{}) + cfg.UseMock, cfg.NoSoul = true, true + return buildIsolated(t, context.Background(), cfg) +} + +// TestOmittedGuardrailsInputInheritsSettingsChecker covers the default action +// input (no guardrails flag) composed with operator settings: a checker +// configured there lets auto start enforcing, and with none auto refuses. +func TestOmittedGuardrailsInputInheritsSettingsChecker(t *testing.T) { + action := loadAction(t) + for name, settings := range map[string]string{ + "guardrails model": "guardrails:\n model: mock-checker\n", + "guardrail slot": "models:\n slots:\n guardrail: mock-checker\n", + } { + t.Run(name, func(t *testing.T) { + path := filepath.Join(t.TempDir(), "settings.yaml") + if err := os.WriteFile(path, []byte(settings), 0o600); err != nil { + t.Fatal(err) + } + built, err := buildFromActionArgv(t, action, nil, "--permission-config", path) + if err != nil { + t.Fatalf("auto with an inherited %s checker must start: %v", name, err) + } + defer built.Close() + ctx := context.Background() + outcome, err := run(ctx, built.Service, session.Limits{}, "summarise the repo", io.Discard) + if err != nil { + t.Fatalf("run: %v", err) + } + sess, err := built.Service.GetSession(ctx, session.SessionID(outcome.Summary.SessionID)) + if err != nil { + t.Fatalf("GetSession: %v", err) + } + if sess.Mode != session.ModeAuto { + t.Fatalf("session mode = %q, want auto", sess.Mode) + } + }) + } + t.Run("no inherited checker refuses", func(t *testing.T) { + built, err := buildFromActionArgv(t, action, nil) + if err == nil { + built.Close() + t.Fatal("auto with no inherited checker must refuse to start") + } + if !strings.Contains(err.Error(), "requires an enforcing guardrails checker") { + t.Fatalf("refusal = %v, want the enforcing-checker error", err) + } + }) + t.Run("explicit off overrides the settings checker", func(t *testing.T) { + path := filepath.Join(t.TempDir(), "settings.yaml") + if err := os.WriteFile(path, []byte("guardrails:\n model: mock-checker\n"), 0o600); err != nil { + t.Fatal(err) + } + built, err := buildFromActionArgv(t, action, map[string]string{"guardrails-model": "off"}, "--permission-config", path) + if err == nil { + built.Close() + t.Fatal("auto with the explicit kill-switch must refuse to start") + } + if !strings.Contains(err.Error(), "requires an enforcing guardrails checker") { + t.Fatalf("refusal = %v, want the enforcing-checker error", err) + } + }) +} + +// TestShippedAllowAllDefaultsDeclareCheckerChoice pins the tri-state checker +// input of the composite action and this repo's live workflow: an empty +// guardrails-model passes no flag (the binary inherits the settings checker), +// "off" passes the kill-switch, and any other value names the checker. It +// executes the action's real bash body against a fake binary rather than +// grepping it. +func TestShippedAllowAllDefaultsDeclareCheckerChoice(t *testing.T) { + action := loadAction(t) + + t.Run("action defaults inherit the checker", func(t *testing.T) { + argv, out, err := runActionScript(t, action, nil) + if err != nil { + t.Fatalf("action body failed: %v\n%s", err, out) + } + if !hasPair(argv, "--permission-mode", "auto") || hasGuardrailsFlag(argv) || slices.Contains(argv, "--posture") { + t.Fatalf("default action argv = %q, want --permission-mode auto with no guardrails flag and no --posture", argv) + } + }) + t.Run("checker model", func(t *testing.T) { + argv, out, err := runActionScript(t, action, map[string]string{"guardrails-model": "checker-model"}) + if err != nil { + t.Fatalf("action body failed: %v\n%s", err, out) + } + if !hasPair(argv, "--guardrails-model", "checker-model") || slices.Contains(argv, "--guardrails") { + t.Fatalf("argv = %q, want --guardrails-model checker-model and no kill-switch", argv) + } + }) + t.Run("explicit off", func(t *testing.T) { + argv, out, err := runActionScript(t, action, map[string]string{"guardrails-model": "off"}) + if err != nil { + t.Fatalf("action body failed: %v\n%s", err, out) + } + if !hasPair(argv, "--guardrails", "off") || slices.Contains(argv, "--guardrails-model") { + t.Fatalf("argv = %q, want --guardrails off and no --guardrails-model", argv) + } + }) + t.Run("deprecated posture input", func(t *testing.T) { + argv, out, err := runActionScript(t, action, map[string]string{"posture": "auto"}) + if err != nil { + t.Fatalf("action body failed: %v\n%s", err, out) + } + if !hasPair(argv, "--posture", "auto") || slices.Contains(argv, "--permission-mode") || hasGuardrailsFlag(argv) { + t.Fatalf("argv = %q, want the deprecated --posture auto alone with no guardrails flag", argv) + } + if !strings.Contains(out, "::warning::") || !strings.Contains(out, "permission-mode") { + t.Fatalf("deprecated posture input did not warn naming permission-mode: %s", out) + } + }) + t.Run("both inputs fail", func(t *testing.T) { + _, out, err := runActionScript(t, action, map[string]string{"posture": "auto", "permission-mode": "auto"}) + if err == nil || !strings.Contains(out, "pass only permission-mode") { + t.Fatalf("both inputs err = %v, want a failed step naming permission-mode: %s", err, out) + } + }) + + t.Run("live workflow", func(t *testing.T) { + raw, err := os.ReadFile("../../.github/workflows/mecatequi.yml") + if err != nil { + t.Fatal(err) + } + var wf workflowFile + if err := yaml.Unmarshal(raw, &wf); err != nil { + t.Fatalf("parse mecatequi.yml: %v", err) + } + found := 0 + for _, job := range wf.Jobs { + for _, step := range job.Steps { + if step.Uses != "./.github/actions/mecatequi" { + continue + } + found++ + if _, ok := step.With["posture"]; ok { + t.Fatalf("workflow still passes the deprecated posture input: %v", step.With) + } + with := map[string]string{} + for _, k := range []string{"permission-mode", "guardrails-model", "subagent-ask-reviewer"} { + if v, ok := step.With[k]; ok { + with[k] = fmt.Sprint(v) + } + } + // The live workflow mounts no operator settings, so its argv alone must + // start: the real composition, not a flag-shape check, decides. + if built, err := buildFromActionArgv(t, action, with); err != nil { + t.Fatalf("workflow inputs do not start mecatequi: %v", err) + } else { + built.Close() + } + } + } + if found == 0 { + t.Fatal("mecatequi.yml does not use ./.github/actions/mecatequi") + } + }) +} + +// TestSettingsPermissionModeRefusesDeprecatedPosture drives parseFlags, +// appConfig, and app.Build with an operator settings.yaml carrying +// permissionMode: plan. The deprecated --posture cannot be combined with it; +// the token alone and --posture alone still build. +func TestSettingsPermissionModeRefusesDeprecatedPosture(t *testing.T) { + t.Setenv("XDG_CONFIG_HOME", t.TempDir()) + settings := filepath.Join(t.TempDir(), "settings.yaml") + if err := os.WriteFile(settings, []byte("permissionMode: plan\n"), 0o600); err != nil { + t.Fatal(err) + } + build := func(t *testing.T, args ...string) (*app.Built, error) { + t.Helper() + repo := t.TempDir() + initTestRepo(t, repo) + f, err := parseFlags(append([]string{"--prompt", "x", "--workspace", repo}, args...)) + if err != nil { + t.Fatalf("parseFlags: %v", err) + } + cfg := appConfig(f, port.NopDiagnostics{}, observability{}) + cfg.UseMock, cfg.NoSoul = true, true + return buildIsolated(t, context.Background(), cfg) + } + built, err := build(t, "--permission-config", settings, "--posture", "strict") + if err == nil { + built.Close() + t.Fatal("settings permissionMode plus --posture must refuse to build") + } + if !strings.Contains(err.Error(), "permissionMode: plan cannot be combined") || !strings.Contains(err.Error(), "--permission-mode default") { + t.Fatalf("refusal must name the conflict and the migration; got %v", err) + } + for _, tc := range []struct { + args []string + want session.PermissionMode + }{ + {[]string{"--permission-config", settings}, session.ModePlan}, + {[]string{"--posture", "strict"}, session.ModeDefault}, + } { + built, err := build(t, tc.args...) + if err != nil { + t.Fatalf("%q: app.Build: %v", tc.args, err) + } + sess, err := built.Service.CreateSession(context.Background(), "", session.Limits{}) + built.Close() + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + if sess.Mode != tc.want { + t.Fatalf("%q: unspecified-mode session started in %q, want %q", tc.args, sess.Mode, tc.want) + } + } +} diff --git a/cmd/mecatequi/run.go b/cmd/mecatequi/run.go index 3365f995d5..b12f208d9d 100644 --- a/cmd/mecatequi/run.go +++ b/cmd/mecatequi/run.go @@ -118,7 +118,7 @@ type SummaryUsage struct { // runOutcome is the result of run(): the Summary, the captured event stream (the // durable-log source), and NoApprover — set when the run was cancelled because the MAIN // engine asked for a permission approval and mecatequi has no approver attached. The -// caller uses NoApprover to print the actionable "re-run with --posture …" guidance and +// caller uses NoApprover to print the actionable "re-run with --permission-mode …" guidance and // to distinguish a no-approver cancel from an ordinary cancel in the operator message // (the StopReason is "cancelled" in both, honestly). type runOutcome struct { @@ -162,8 +162,11 @@ type runOutcome struct { // filled from the Service's DefaultLimits inside CreateSession, so passing the zero // value preserves the deployment defaults, and passing only MaxTurns caps turns while // the tool-call / failure caps stay at their defaults (never silently disabled). +// +// The session mode is left unspecified so the Service's DefaultMode — the mode +// resolved from --permission-mode — applies. func run(ctx context.Context, svc *server.Service, limits session.Limits, prompt string, w io.Writer) (runOutcome, error) { - sess, err := svc.CreateSession(ctx, session.ModeDefault, limits) + sess, err := svc.CreateSession(ctx, "", limits) if err != nil { return runOutcome{}, fmt.Errorf("create session: %w", err) } diff --git a/cmd/mecatequi/run_test.go b/cmd/mecatequi/run_test.go index 1123132855..f47fc134d9 100644 --- a/cmd/mecatequi/run_test.go +++ b/cmd/mecatequi/run_test.go @@ -73,6 +73,51 @@ func gitInRepo(t *testing.T, dir string, args ...string) { } } +// TestRunUsesPermissionModeFromFlags drives the real CLI boundary — parseFlags, +// appConfig, app.Build, run — and proves the run's session starts in the mode +// --permission-mode selected rather than a hard-coded default. +func TestRunUsesPermissionModeFromFlags(t *testing.T) { + ctx := context.Background() + for token, want := range map[string]session.PermissionMode{ + "plan": session.ModePlan, + "default": session.ModeDefault, + "accept-edits": session.ModeAccept, + "yolo": session.ModeYolo, + } { + t.Run(token, func(t *testing.T) { + repo := t.TempDir() + initTestRepo(t, repo) + args := []string{"--prompt", "x", "--workspace", repo, "--permission-mode", token} + if token == "yolo" { + args = append(args, "--guardrails", "off") + } + f, err := parseFlags(args) + if err != nil { + t.Fatalf("parseFlags: %v", err) + } + cfg := appConfig(f, port.NopDiagnostics{}, observability{}) + cfg.UseMock, cfg.NoSoul = true, true + built, err := buildIsolated(t, ctx, cfg) + if err != nil { + t.Fatalf("app.Build: %v", err) + } + defer built.Close() + var human bytes.Buffer + outcome, err := run(ctx, built.Service, session.Limits{}, "summarise the repo", &human) + if err != nil { + t.Fatalf("run: %v", err) + } + sess, err := built.Service.GetSession(ctx, session.SessionID(outcome.Summary.SessionID)) + if err != nil { + t.Fatalf("GetSession: %v", err) + } + if sess.Mode != want { + t.Fatalf("--permission-mode %s ran in session mode %q, want %q", token, sess.Mode, want) + } + }) + } +} + // TestRunEndToEndMockProvider is the model-facing e2e: a full app.Build over a // hermetic git repo with UseMock, then run() against the real Service. It asserts the // run completes cleanly (end_turn), leaves no diff, carries a usage struct, writes a diff --git a/cmd/mecatui/client/capabilities.go b/cmd/mecatui/client/capabilities.go index 0eb3a962ac..f0419ed4c5 100644 --- a/cmd/mecatui/client/capabilities.go +++ b/cmd/mecatui/client/capabilities.go @@ -1,6 +1,8 @@ package client import ( + "slices" + mecatlv1 "github.com/stacklok/mecatl/contracts/gen/go/mecatl/v1" ) @@ -43,10 +45,21 @@ type Capabilities struct { Image bool Audio bool // Posture is the SERVER-WIDE operator posture tier ("strict"/"trusted"/"auto"/ - // "yolo"), CHROME ONLY: the ui renders a "⚠ auto"/"⚠ yolo" badge so an operator - // sees the daemon's automation posture. NOT session state. Empty (an older server, - // or strict/trusted) → no badge. + // "yolo"). It bounds which modes the ui's mode cycle may switch to. NOT + // session state. Empty on an older server. Posture string + // SessionPermissionModes reports the session_permission_modes feature: the + // five modes are session-scoped (auto and yolo are session modes, and a lower + // session mode drops the posture's allow-all). An older server without it + // applies its auto/yolo posture process-wide whatever the session mode, so the + // ui must not present a session mode as the effective permission mode there. + SessionPermissionModes bool + // ProjectTrust is "trusted" or "untrusted" for the server's launch + // workspace, CHROME ONLY: the ui's header badge. Empty on an older server. + ProjectTrust string + // SubagentAskReviewer is the server's subagent ask reviewer state ("on", + // "off", "inert"), CHROME ONLY: shown by /posture. Empty on an older server. + SubagentAskReviewer string // Worktrees is true when a WorktreeLister is wired (ListWorktrees may return a // non-empty list for a real git repo). It gates the /worktrees overlay — the // first-class operator workflow for binding a session to an EXISTING sibling @@ -107,6 +120,18 @@ func capabilitiesWithSessionMediaFrom(caps Capabilities, sessionCaps *mecatlv1.S return caps } +// SessionPermissionModesFeature is the compatibility feature identifier for +// session-scoped permission modes. +const SessionPermissionModesFeature = "session_permission_modes" + +// compatibilityCapabilitiesFrom maps a compatibility response: the capability +// message plus the feature bits the ui consumes. +func compatibilityCapabilitiesFrom(resp *mecatlv1.GetCompatibilityInfoResponse) Capabilities { + caps := capabilitiesFrom(resp.GetCapabilities()) + caps.SessionPermissionModes = slices.Contains(resp.GetFeatures(), SessionPermissionModesFeature) + return caps +} + // capabilitiesFrom maps a proto ServerCapabilities (nil-safe) to the plain // struct. A nil message (older server) yields the all-false zero value. func capabilitiesFrom(c *mecatlv1.ServerCapabilities) Capabilities { @@ -130,6 +155,8 @@ func capabilitiesFrom(c *mecatlv1.ServerCapabilities) Capabilities { Image: c.GetImage(), Audio: c.GetAudio(), Posture: c.GetPosture(), + ProjectTrust: projectTrustFrom(c), + SubagentAskReviewer: c.GetSubagentAskReviewer(), Worktrees: c.GetWorktrees(), Scheduling: c.GetScheduling(), Reflection: c.GetReflection(), @@ -146,6 +173,25 @@ func capabilitiesFrom(c *mecatlv1.ServerCapabilities) Capabilities { } } +// Project-trust spellings for Capabilities.ProjectTrust. +const ( + ProjectTrusted = "trusted" + ProjectUntrusted = "untrusted" +) + +// projectTrustFrom maps the optional project_trusted echo; unset (an older +// server) stays "" so the client shows no trust indicator. +func projectTrustFrom(c *mecatlv1.ServerCapabilities) string { + switch { + case c.ProjectTrusted == nil: + return "" + case c.GetProjectTrusted(): + return ProjectTrusted + default: + return ProjectUntrusted + } +} + func manualDreamCapabilitiesFrom(c *mecatlv1.ManualDreamCapabilities) *ManualDreamCapabilities { if c == nil { return nil diff --git a/cmd/mecatui/client/capabilities_test.go b/cmd/mecatui/client/capabilities_test.go index 968d503fe4..548930f6c3 100644 --- a/cmd/mecatui/client/capabilities_test.go +++ b/cmd/mecatui/client/capabilities_test.go @@ -78,6 +78,11 @@ func TestCapabilitiesFrom(t *testing.T) { in: &mecatlv1.ServerCapabilities{ManualCompaction: true}, want: Capabilities{ManualCompaction: true}, }, + { + name: "subagent_ask_reviewer maps independently", + in: &mecatlv1.ServerCapabilities{SubagentAskReviewer: new("inert")}, + want: Capabilities{SubagentAskReviewer: "inert"}, + }, { name: "model_selection maps independently", in: &mecatlv1.ServerCapabilities{ModelSelection: true}, diff --git a/cmd/mecatui/client/client.go b/cmd/mecatui/client/client.go index 44be01357b..94853e979f 100644 --- a/cmd/mecatui/client/client.go +++ b/cmd/mecatui/client/client.go @@ -473,7 +473,7 @@ func (c *Client) compatibilityCapabilities(ctx context.Context) (Capabilities, e if err != nil { return Capabilities{}, fmt.Errorf("get compatibility info: %w", err) } - return capabilitiesFrom(resp.GetCapabilities()), nil + return compatibilityCapabilitiesFrom(resp), nil } // createSession is the shared CreateSession proto call and response unwrap body. @@ -714,11 +714,26 @@ func ModeFromString(s string) mecatlv1.PermissionMode { return mecatlv1.PermissionMode_PERMISSION_MODE_ACCEPT_EDITS case ModeDefaultString, "": return mecatlv1.PermissionMode_PERMISSION_MODE_DEFAULT + case "auto": + return mecatlv1.PermissionMode_PERMISSION_MODE_AUTO + case "yolo": + return mecatlv1.PermissionMode_PERMISSION_MODE_YOLO default: return mecatlv1.PermissionMode_PERMISSION_MODE_UNSPECIFIED } } +// RequestModeFromString maps the mode a client REQUESTS for a new session. It is +// ModeFromString except that an empty mode means "no preference" and goes out +// UNSPECIFIED, so the server applies its own configured default mode +// instead of receiving an explicit DEFAULT. +func RequestModeFromString(s string) mecatlv1.PermissionMode { + if s == "" { + return mecatlv1.PermissionMode_PERMISSION_MODE_UNSPECIFIED + } + return ModeFromString(s) +} + // ModeString maps the proto enum to the CLI/UI spelling. Unknown/unspecified values // degrade to "default", matching the server boundary. func ModeString(m mecatlv1.PermissionMode) string { @@ -727,6 +742,10 @@ func ModeString(m mecatlv1.PermissionMode) string { return modePlanString case mecatlv1.PermissionMode_PERMISSION_MODE_ACCEPT_EDITS: return "accept-edits" + case mecatlv1.PermissionMode_PERMISSION_MODE_AUTO: + return "auto" + case mecatlv1.PermissionMode_PERMISSION_MODE_YOLO: + return "yolo" case mecatlv1.PermissionMode_PERMISSION_MODE_DEFAULT, mecatlv1.PermissionMode_PERMISSION_MODE_UNSPECIFIED: return ModeDefaultString default: @@ -734,18 +753,6 @@ func ModeString(m mecatlv1.PermissionMode) string { } } -// NextMode returns the next mode in the TUI's cycle order. -func NextMode(mode string) string { - switch ModeString(ModeFromString(mode)) { - case ModeDefaultString: - return modePlanString - case modePlanString: - return "accept-edits" - default: - return ModeDefaultString - } -} - // loadCAPool reads a PEM CA bundle into a cert pool for server verification. func loadCAPool(path string) (*x509.CertPool, error) { pem, err := os.ReadFile(path) //nolint:gosec // operator-supplied CA path diff --git a/cmd/mecatui/client/models_test.go b/cmd/mecatui/client/models_test.go index ab0100092c..6518b1008e 100644 --- a/cmd/mecatui/client/models_test.go +++ b/cmd/mecatui/client/models_test.go @@ -256,6 +256,32 @@ func TestCreateSessionUsesSessionMediaCapabilities(t *testing.T) { } } +// TestCreateSessionReadsSessionPermissionModesFeature pins that the +// session_permission_modes feature, not the legacy posture capability, decides +// whether the ui may present the session-scoped mode semantics. An API-major-1 +// server that advertises yolo posture without the feature is a legacy server. +func TestCreateSessionReadsSessionPermissionModesFeature(t *testing.T) { + for _, tc := range []struct { + name string + features []string + want bool + }{ + {name: "older yolo server without the feature", features: []string{"server_info"}, want: false}, + {name: "server advertising the feature", features: []string{"server_info", SessionPermissionModesFeature}, want: true}, + } { + t.Run(tc.name, func(t *testing.T) { + fake := &fakeModelsClient{caps: &mecatlv1.ServerCapabilities{Posture: "yolo"}, features: tc.features} + _, caps, _, err := newFakeClient(fake).CreateSession(context.Background(), mecatlv1.PermissionMode_PERMISSION_MODE_DEFAULT, ModelSelection{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + if caps.Posture != "yolo" || caps.SessionPermissionModes != tc.want { + t.Fatalf("capabilities posture=%q sessionPermissionModes=%v, want yolo/%v", caps.Posture, caps.SessionPermissionModes, tc.want) + } + }) + } +} + // TestReasoningEffortIsZero asserts an effort-only selection is NOT zero (the client // must still send it) while a fully-empty selection is. func TestReasoningEffortIsZero(t *testing.T) { @@ -297,18 +323,4 @@ func TestModeHelpers(t *testing.T) { if got := ModeString(ModeFromString("accept-edits")); got != "accept-edits" { t.Fatalf("accept-edits round trip = %q", got) } - cases := []struct { - in string - want string - }{ - {"default", "plan"}, - {"plan", "accept-edits"}, - {"accept-edits", "default"}, - {"", "plan"}, - } - for _, tc := range cases { - if got := NextMode(tc.in); got != tc.want { - t.Errorf("NextMode(%q) = %q, want %q", tc.in, got, tc.want) - } - } } diff --git a/cmd/mecatui/client/schedule.go b/cmd/mecatui/client/schedule.go index 8cbc1e08b7..d367d8b3b7 100644 --- a/cmd/mecatui/client/schedule.go +++ b/cmd/mecatui/client/schedule.go @@ -492,16 +492,24 @@ func misfireToProtoFromString(s string) mecatlv1.MisfirePolicy { } } -// modeStringFromProto maps the proto PermissionMode enum to the canonical UI -// string, reusing ModeString (the harness-mode discipline). Unspecified defaults -// to "default". +// modeStringFromProto maps a schedule's proto PermissionMode to the canonical UI +// string, reusing ModeString (the harness-mode discipline). UNSPECIFIED maps to +// "": the schedule fires in the server's default session mode, which the client +// does not know, so it must not be shown as "default". func modeStringFromProto(m mecatlv1.PermissionMode) string { + if m == mecatlv1.PermissionMode_PERMISSION_MODE_UNSPECIFIED { + return "" + } return ModeString(m) } -// modeToProtoFromString maps a UI mode string to the proto enum, reusing -// ModeFromString. Unknown/empty maps to UNSPECIFIED (the server defaults that to -// DEFAULT). +// modeToProtoFromString maps a schedule's UI mode string to the proto enum. An +// empty mode goes out as UNSPECIFIED so the server's default session mode +// applies; it is deliberately not ModeFromString(""), which other callers rely +// on to mean DEFAULT. Any other value reuses ModeFromString. func modeToProtoFromString(s string) mecatlv1.PermissionMode { + if s == "" { + return mecatlv1.PermissionMode_PERMISSION_MODE_UNSPECIFIED + } return ModeFromString(s) } diff --git a/cmd/mecatui/client/schedule_test.go b/cmd/mecatui/client/schedule_test.go index 8d31d79f2b..d542fafd99 100644 --- a/cmd/mecatui/client/schedule_test.go +++ b/cmd/mecatui/client/schedule_test.go @@ -420,3 +420,42 @@ func TestScheduleCmdsErrorMsgs(t *testing.T) { t.Fatalf("ListFiresCmd error msg = %+v", m) } } + +// TestScheduleModeUnsetMapsToUnspecified pins the schedule-only mode mapping. +// An unset mode goes out as UNSPECIFIED so the server's default session mode +// applies at fire (ModeFromString("") still means DEFAULT for other callers), +// and an UNSPECIFIED read-back stays unset instead of claiming "default". +func TestScheduleModeUnsetMapsToUnspecified(t *testing.T) { + for _, tc := range []struct { + mode string + want mecatlv1.PermissionMode + }{ + {mode: "", want: mecatlv1.PermissionMode_PERMISSION_MODE_UNSPECIFIED}, + {mode: "plan", want: mecatlv1.PermissionMode_PERMISSION_MODE_PLAN}, + {mode: "default", want: mecatlv1.PermissionMode_PERMISSION_MODE_DEFAULT}, + } { + fake := &fakeScheduleClient{createResp: &mecatlv1.CreateScheduleResponse{Schedule: sampleProtoSchedule("m")}} + if _, err := newScheduleFakeClient(fake).CreateSchedule(context.Background(), ScheduleSpec{ + Name: "m", Prompt: "p", Trigger: ScheduleTrigger{Cron: "@every 1h"}, Mode: tc.mode, Mutating: true, + }); err != nil { + t.Fatalf("CreateSchedule(mode %q): %v", tc.mode, err) + } + if got := fake.lastCreate.GetSpec().GetMode(); got != tc.want { + t.Errorf("mode %q sent as %v, want %v", tc.mode, got, tc.want) + } + } + if ModeFromString("") != mecatlv1.PermissionMode_PERMISSION_MODE_DEFAULT { + t.Fatal(`ModeFromString("") must keep meaning DEFAULT for non-schedule callers`) + } + + unset := sampleProtoSchedule("u") + unset.Spec.Mode = mecatlv1.PermissionMode_PERMISSION_MODE_UNSPECIFIED + fake := &fakeScheduleClient{getResp: &mecatlv1.GetScheduleResponse{Schedule: unset}} + s, err := newScheduleFakeClient(fake).GetSchedule(context.Background(), "u") + if err != nil { + t.Fatalf("GetSchedule: %v", err) + } + if s.Spec.Mode != "" { + t.Fatalf("UNSPECIFIED read back as %q, want empty (server default)", s.Spec.Mode) + } +} diff --git a/cmd/mecatui/client/session.go b/cmd/mecatui/client/session.go index 278b2c29c7..32170c609f 100644 --- a/cmd/mecatui/client/session.go +++ b/cmd/mecatui/client/session.go @@ -2,9 +2,11 @@ package client import ( "context" + "errors" "fmt" tea "charm.land/bubbletea/v2" + grpcstatus "google.golang.org/grpc/status" mecatlv1 "github.com/stacklok/mecatl/contracts/gen/go/mecatl/v1" ) @@ -144,6 +146,16 @@ func (c *Client) SetMode(ctx context.Context, id, mode string) (string, error) { return snapshotFrom(resp.GetSession()).Mode, nil } +// ModeErrorText is the server's reason for a failed SetMode, without the +// transport framing, falling back to the whole error. +func ModeErrorText(err error) string { + var se interface{ GRPCStatus() *grpcstatus.Status } + if errors.As(err, &se) { + return se.GRPCStatus().Message() + } + return err.Error() +} + // ResolvedModelMsg carries the result of a GetSession refetch (the footer // context-meter heal, issue #66, the plan-approval mode+model refresh, // issue #206, and the caps-heal path for /sessions continue + /effort fork, diff --git a/cmd/mecatui/config.go b/cmd/mecatui/config.go index 75b4cf1d70..b3e09ed3f8 100644 --- a/cmd/mecatui/config.go +++ b/cmd/mecatui/config.go @@ -238,6 +238,17 @@ type config struct { // key. Mapped onto app.Config.Posture/PostureFlagSet in embeddedConfig. posture string postureFlagSet bool + // permissionMode is the named token (--permission-mode). It writes the + // embedded server's posture half (app.Config.PermissionMode) and this client's + // requested session mode. modeFlagSet/yoloFlagSet record the deprecated aliases + // so the combination error and the one-per-alias deprecation WARN key on what + // the operator actually typed. + permissionMode string + permissionModeFlagSet bool + modeFlagSet bool + yoloFlagSet bool + // permissionModeToken is the parsed --permission-mode token (zero when unset). + permissionModeToken app.PermissionModeToken // reasoningEffort is the operator-tier reasoning-effort default for // the EMBEDDED server. reasoningEffortFlagSet records an explicit // --reasoning-effort so CLI out-ranks the operator-global settings.yaml @@ -386,7 +397,8 @@ func parseTransportFlags(mode transportMode, out io.Writer, args []string, brows fs.SetOutput(out) fs.StringVar(&cfg.workspace, "workspace", "", "embedded server only: absolute deployment workspace root (default: cwd); not accepted by connect") registerMicroVMDevelopmentFlags(fs, &cfg.microVMDevRelease, &cfg.microVMDevAcknowledge) - fs.StringVar(&cfg.mode, "mode", "default", "start sessions in permission mode: default, plan, or accept-edits") + fs.StringVar(&cfg.mode, "mode", "default", "deprecated, use --permission-mode: start sessions in permission mode: default, plan, or accept-edits") + fs.StringVar(&cfg.permissionMode, "permission-mode", "", "set the permission mode: "+strings.Join(app.PermissionModeNames(), ", ")+"; sets the mode new sessions start in and, for the embedded server, the most permissive mode a session may switch to. It never grants project trust; use --trust-project") fs.Func("debug-mcp", "attach configured streaming-HTTP MCP server NAME to debug sessions (repeatable)", func(value string) error { cfg.debugMCP = append(cfg.debugMCP, value) return nil @@ -452,9 +464,9 @@ func parseTransportFlags(mode transportMode, out io.Writer, args []string, brows fs.StringVar(&cfg.anthropicCacheTTL, "anthropic-cache-ttl", "", "set Anthropic prompt-cache lifetime to 5m or 1h (default: 1h on Anthropic, OpenRouter's Anthropic endpoint and the ToolHive gateway); other values are ignored with a warning") fs.BoolVar(&cfg.trustProject, "trust-project", false, "enable project instructions, persona, skills, commands, and allow rules; use only with projects you trust") fs.BoolVar(&cfg.allowAllTools, "yolo", false, - "use yolo posture: allow tools by default and disable delegated-agent command-injection safeguards; explicit deny and ask rules still apply") + "deprecated, use --permission-mode yolo: allow tools by default and disable delegated-agent command-injection safeguards; explicit deny and ask rules still apply") fs.StringVar(&cfg.posture, "posture", "", - "set the permission posture: strict, trusted, auto, or yolo (default: strict); auto and yolo reduce safeguards and are refused as root outside a sandbox") + "deprecated, use --permission-mode: set the permission posture: strict, trusted, auto, or yolo (default: strict); auto and yolo reduce safeguards and are refused as root outside a sandbox") fs.StringVar(&cfg.reasoningEffort, "reasoning-effort", "", "set the default reasoning effort: auto, low, medium, high, xhigh, or max") fs.BoolVar(&cfg.quiet, "quiet", false, @@ -687,6 +699,12 @@ func recordExplicitFlag(f *flag.Flag, cfg *config) { cfg.debugFlagSet = true case "posture": cfg.postureFlagSet = true + case "permission-mode": + cfg.permissionModeFlagSet = true + case "mode": + cfg.modeFlagSet = true + case "yolo": + cfg.yoloFlagSet = true case "shell": cfg.shellFlagSet = true case "subagent-model-router": @@ -734,6 +752,9 @@ func finalizeParsedConfig(fs *flag.FlagSet, cfg *config) error { // settings.yaml keys (mirrors mecated). Extracted to recordExplicitFlag to keep // this function under the cyclomatic-complexity bound. fs.Visit(func(f *flag.Flag) { recordExplicitFlag(f, cfg) }) + if err := parsePermissionModeFlag(cfg); err != nil { + return err + } if cfg.authToken == "" { cfg.authToken = os.Getenv("MECATL_AUTH_TOKEN") @@ -935,6 +956,9 @@ func (c config) validate() error { default: return fmt.Errorf("invalid --mode %q (want default|plan|accept-edits)", c.mode) } + if err := validatePermissionModeFlags(c); err != nil { + return err + } // Provider/posture checks apply ONLY to paths that may embed; the // predicate + its rationale live once on config.mayEmbed. if c.mayEmbed() { @@ -1014,13 +1038,24 @@ func embeddedAuthoritativePosture(c config) app.Posture { Workspace: c.workspace, PermissionsConventional: true, ImportClaudePermissions: true, - Posture: app.ParsePosture(c.posture), - PostureFlagSet: c.postureFlagSet, + Posture: embeddedFlagPosture(c), + PostureFlagSet: c.postureFlagSet || c.permissionModeFlagSet, AllowAllTools: c.allowAllTools, TrustProject: c.trustProject, }) } +// embeddedFlagPosture is the posture the CLI flags request for the pre-TUI checks: +// an explicit --permission-mode token's posture half, else the deprecated --posture. +// Build applies the token through app.Config.PermissionMode itself; this only keeps +// the pre-launch refusal and WARN in step with it. +func embeddedFlagPosture(c config) app.Posture { + if c.permissionModeFlagSet { + return c.permissionModeToken.Posture + } + return app.ParsePosture(c.posture) +} + // embeddedPrivileged is the "root && !sandbox" predicate fed to the posture refusal // (the cmd owns the os/env reads; internal/app takes the bool). It is the SAME value // embeddedConfig threads onto app.Config.Privileged so the fast-path and Build agree. diff --git a/cmd/mecatui/customization/command_test.go b/cmd/mecatui/customization/command_test.go index 297879df43..77aa284be8 100644 --- a/cmd/mecatui/customization/command_test.go +++ b/cmd/mecatui/customization/command_test.go @@ -251,7 +251,7 @@ func TestStatusLineCommandDoesNotTrimNonASCIIOutputBoundary(t *testing.T) { } } -func TestStatusInputProtocolV5WorkspacePathNameAndContext(t *testing.T) { +func TestStatusInputProtocolV6WorkspacePathNameAndContext(t *testing.T) { input := Input{ Version: ProtocolVersion, Context: Context{ @@ -263,14 +263,14 @@ func TestStatusInputProtocolV5WorkspacePathNameAndContext(t *testing.T) { }, Model: Model{ProviderID: "openai", ID: "gpt-5", ProviderLabel: "openai/gpt-5", FriendlyName: "GPT-5"}, Workspace: Workspace{Location: "local", Name: "provider label", Path: "/eligible/root"}, - Terminal: Terminal{FooterAvailCols: 80}, + Terminal: Terminal{FooterAvailCols: 80, Emoji: true}, } wire, err := json.Marshal(input) if err != nil { t.Fatalf("marshal status input: %v", err) } - if ProtocolVersion != 5 || !strings.Contains(string(wire), `"Model":{"ProviderID":"openai","ID":"gpt-5","ProviderLabel":"openai/gpt-5","FriendlyName":"GPT-5","Route":""`) || strings.Contains(string(wire), "DisplayName") || !strings.Contains(string(wire), `"Name":"provider label"`) || !strings.Contains(string(wire), `"Path":"/eligible/root"`) || !strings.Contains(string(wire), `"Context":{"Used":{"Raw":75,"Human":"75"},"Window":{"Raw":100,"Human":"100"},"Percent":75,"Known":true,"Estimated":true}`) || strings.Contains(string(wire), "Basename") { - t.Fatalf("status input v5 workspace/context projection = %s", wire) + if ProtocolVersion != 6 || !strings.Contains(string(wire), `"FooterAvailCols":80,"Emoji":true`) || !strings.Contains(string(wire), `"Model":{"ProviderID":"openai","ID":"gpt-5","ProviderLabel":"openai/gpt-5","FriendlyName":"GPT-5","Route":""`) || strings.Contains(string(wire), "DisplayName") || !strings.Contains(string(wire), `"Name":"provider label"`) || !strings.Contains(string(wire), `"Path":"/eligible/root"`) || !strings.Contains(string(wire), `"Context":{"Used":{"Raw":75,"Human":"75"},"Window":{"Raw":100,"Human":"100"},"Percent":75,"Known":true,"Estimated":true}`) || strings.Contains(string(wire), "Basename") { + t.Fatalf("status input v6 workspace/context/terminal projection = %s", wire) } templates := NewTemplateSource(TemplateSet{Footer: SurfaceTemplates{Full: `
[{{.Workspace.Path}}]
`}}, 0) t.Cleanup(func() { _ = templates.Close(context.Background()) }) diff --git a/cmd/mecatui/customization/defaults.go b/cmd/mecatui/customization/defaults.go index 11b54a1602..daa20794ee 100644 --- a/cmd/mecatui/customization/defaults.go +++ b/cmd/mecatui/customization/defaults.go @@ -1,13 +1,20 @@ package customization +// modeSegmentTemplate is the header's permission-mode segment. The yolo mode is +// marked with the alarm pill (the danger token, its text carrying the pill's +// padding), preceded by a ⚡️ bolt outside the pill on an emoji-capable +// terminal; every other mode, including a pending yolo, is plain +// warning text. +const modeSegmentTemplate = `{{if .Session.Mode}} · mode {{if eq .Session.Mode "yolo"}}{{if .Terminal.Emoji}}⚡️ {{end}} YOLO {{else}}{{.Session.Mode}}{{end}}{{end}}` + // defaultTemplateSet contains the status and terminal-title templates shipped // with mecatui. Keep these defaults together so every presentation surface has // one canonical source. func defaultTemplateSet() TemplateSet { return TemplateSet{ Header: SurfaceTemplates{ - Full: `
mecatui{{if .Session.Title}} · {{elide 32 .Session.Title}}{{end}}{{if .Model.ProviderLabel}} · {{.Model.ProviderLabel}}{{if .Model.Route}}/{{.Model.Route}}{{end}}{{end}}{{if .Session.Mode}} · mode {{.Session.Mode}}{{end}}{{if and (eq .Server.ConnectionMode "connect") .Server.DisplayTarget}} · {{.Server.DisplayTarget}}{{end}}
`, - Compact: `
mecatui{{if .Session.Title}} · {{elide 24 .Session.Title}}{{end}}{{if .Model.ProviderLabel}} · {{.Model.ProviderLabel}}{{end}}{{if .Session.Mode}} · mode {{.Session.Mode}}{{end}}
`, + Full: `
mecatui{{if .Session.Title}} · {{elide 32 .Session.Title}}{{end}}{{if .Model.ProviderLabel}} · {{.Model.ProviderLabel}}{{if .Model.Route}}/{{.Model.Route}}{{end}}{{end}}` + modeSegmentTemplate + `{{if and (eq .Server.ConnectionMode "connect") .Server.DisplayTarget}} · {{.Server.DisplayTarget}}{{end}}
`, + Compact: `
mecatui{{if .Session.Title}} · {{elide 24 .Session.Title}}{{end}}{{if .Model.ProviderLabel}} · {{.Model.ProviderLabel}}{{end}}` + modeSegmentTemplate + `
`, Minimal: `
mecatui{{if .Session.Title}} · {{elide 12 .Session.Title}}{{end}}
`, }, Footer: SurfaceTemplates{ diff --git a/cmd/mecatui/customization/input.go b/cmd/mecatui/customization/input.go index 25b9505b2a..c204152513 100644 --- a/cmd/mecatui/customization/input.go +++ b/cmd/mecatui/customization/input.go @@ -130,12 +130,16 @@ type Workspace struct { // Terminal provides measured dimensions and independently reserved columns for // status surfaces. Cols is the full terminal width. HeaderAvailCols and // FooterAvailCols are the widths remaining after the renderer reserves mandatory -// header safety/navigation and footer activity lanes, respectively. +// header safety/navigation and footer activity lanes, respectively. Emoji +// reports whether the terminal is likely to render emoji presentation (for +// example ⚡️) at its expected width; it is a conservative, environment-based +// guess fixed for the process. type Terminal struct { Rows int Cols int HeaderAvailCols int FooterAvailCols int + Emoji bool } // MainAgent carries the main agent's display-safe activity. State is one of diff --git a/cmd/mecatui/customization/statusml.go b/cmd/mecatui/customization/statusml.go index dff7a2dce1..1c7e65035f 100644 --- a/cmd/mecatui/customization/statusml.go +++ b/cmd/mecatui/customization/statusml.go @@ -12,7 +12,7 @@ import ( const ( // ProtocolVersion is the current Input wire-independent contract version. - ProtocolVersion uint8 = 5 + ProtocolVersion uint8 = 6 maxMarkupBytes = 16 << 10 maxSpanRunes = 4 << 10 @@ -34,6 +34,9 @@ const ( TokenWarning Token = "warning" TokenError Token = "error" TokenInfo Token = "info" + // TokenDanger renders as the filled, theme-independent alarm pill (the yolo + // mode marker) rather than a foreground colour. + TokenDanger Token = "danger" ) // Palette resolves a StatusML semantic token through the active theme. It @@ -267,7 +270,7 @@ func validName(name string) bool { func isToken(name string) bool { switch Token(name) { - case TokenText, TokenMuted, TokenPrimary, TokenSecondary, TokenAccent, TokenSuccess, TokenWarning, TokenError, TokenInfo: + case TokenText, TokenMuted, TokenPrimary, TokenSecondary, TokenAccent, TokenSuccess, TokenWarning, TokenError, TokenInfo, TokenDanger: return true default: return false diff --git a/cmd/mecatui/helpmeta.go b/cmd/mecatui/helpmeta.go index 7428c8bfc1..e9542aacd1 100644 --- a/cmd/mecatui/helpmeta.go +++ b/cmd/mecatui/helpmeta.go @@ -118,9 +118,13 @@ var flagApplicabilityByFlag = map[string]flagApplicability{ "context-window-override": {group: groupLLMResilience, common: false, local: true, connect: false}, // ── Permissions (embedded-only) ─────────────────────────────────────── - "posture": {group: groupPermissions, common: true, local: true, connect: false}, - "yolo": {group: groupPermissions, common: true, local: true, connect: false}, - "trust-project": {group: groupPermissions, common: true, local: true, connect: false}, + // --permission-mode applies under connect too: its session half sets this + // client's new sessions, and a non-strict posture half is refused there + // (validatePermissionModeFlags) because the posture is the remote server's. + "permission-mode": {group: groupPermissions, common: true, local: true, connect: true}, + "posture": {group: groupPermissions, common: true, local: true, connect: false}, + "yolo": {group: groupPermissions, common: true, local: true, connect: false}, + "trust-project": {group: groupPermissions, common: true, local: true, connect: false}, // ── Storage (embedded-only) ─────────────────────────────────────────── "store-dir": {group: groupStorage, common: true, local: true, connect: false}, diff --git a/cmd/mecatui/main.go b/cmd/mecatui/main.go index dca377578c..65ceba1040 100644 --- a/cmd/mecatui/main.go +++ b/cmd/mecatui/main.go @@ -253,6 +253,7 @@ func runWithOptions(argv []string, options runOptions) error { // mecatui.log file writer. installBaselineSlog(cfg.quiet) + warnDeprecatedPermissionFlags(os.Stderr, cfg) warnEmbeddedPosture(cfg) reg := buildRegistry(cfg.workspace, cfg.themeDir) @@ -385,12 +386,13 @@ func runWithOptions(argv []string, options runOptions) error { initialSel, wsDefault, wsDefaultSet, globalDefault := launchSelections(store, uiWorkspace, cfg.debugTarget) connectionMode := resolveConnectionMode(cfg) + requestedMode, modeServerDefault := cfg.requestedSessionMode() // Held concretely (not just as the ui interface) because this run OWNS its // shutdown: a /connect restart re-enters runWithOptions and builds a fresh // notifier, so this one must be settled first (see closeAgentLifecycleHook). agentHook := agenthook.New(os.Environ()) deps := applyLaunchIntent(cfg, ui.Deps{ - Session: &sessionAdapter{cl: cl, mode: cfg.mode, debugTarget: cfg.debugTarget, debugMCP: cfg.debugMCP}, + Session: &sessionAdapter{cl: cl, mode: requestedMode, debugTarget: cfg.debugTarget, debugMCP: cfg.debugMCP}, AgentHook: agentLifecycleHook(agentHook), Conv: cl, MCP: cl, @@ -453,11 +455,12 @@ func runWithOptions(argv []string, options runOptions) error { // live/config-first server-side — never recomputed by the client. Embedded mode's // --context-window-override and an external mecated's flag both move the engine // trigger and this echoed denominator. - Model: cfg.model, - Workspace: uiWorkspace, - Mode: cfg.mode, - Resume: resume, - Ctx: ctx, + Model: cfg.model, + Workspace: uiWorkspace, + Mode: requestedMode, + ModeServerDefault: modeServerDefault, + Resume: resume, + Ctx: ctx, // Build identity for the welcome splash (explicit linker stamp, or a // VCS-derived source-build ID when embedded metadata is available). Version: buildinfo.BuildID, @@ -1589,6 +1592,11 @@ func embeddedConfig(cfg config, diag port.Diagnostics) app.Config { // escape it. Posture: app.ParsePosture(cfg.posture), PostureFlagSet: cfg.postureFlagSet, + // The named permission-mode token: Build resolves its posture half and puts its + // session half on the server's DefaultMode. An explicit flag out-ranks the + // deprecated --posture and both YAML keys. + PermissionMode: cfg.permissionMode, + PermissionModeFlagSet: cfg.permissionModeFlagSet, // Reasoning-effort tier: operator-tier only; reasoningEffortFlagSet // lets CLI out-rank the operator-global settings.yaml reasoning-effort: key. ReasoningEffort: cfg.reasoningEffort, @@ -1831,13 +1839,13 @@ func (s *sessionAdapter) CreateSession(ctx context.Context, sel client.ModelSele if mode == "" { mode = s.mode } - id, target, caps, resolved, err := s.cl.CreateDebugSession(ctx, s.debugTarget, client.ModeFromString(mode), sel, s.debugMCP...) + id, target, caps, resolved, err := s.cl.CreateDebugSession(ctx, s.debugTarget, client.RequestModeFromString(mode), sel, s.debugMCP...) if target != "" { s.debugTarget = target } return id, caps, resolved, err } - return s.cl.CreateSession(ctx, client.ModeFromString(mode), sel) + return s.cl.CreateSession(ctx, client.RequestModeFromString(mode), sel) } func (s *sessionAdapter) CreateSessionWithExecution(ctx context.Context, sel client.ModelSelection, mode string, choice client.ExecutionChoice) (string, client.Capabilities, client.ResolvedModel, error) { diff --git a/cmd/mecatui/main_test.go b/cmd/mecatui/main_test.go index 22a66a1008..af45910d04 100644 --- a/cmd/mecatui/main_test.go +++ b/cmd/mecatui/main_test.go @@ -114,8 +114,10 @@ func TestEmbeddedGuardrailsAdvisoryWarning(t *testing.T) { wantFact: "guardrails: OFF", }, { + // yolo with no checker refuses to start unless the kill switch declares + // the choice; with it, no checker runs and nothing is advisory. name: "no checker yolo from operator settings", - settings: "execution:\n default_placement: host-local\nposture: yolo\n", + settings: "execution:\n default_placement: host-local\nposture: yolo\nguardrails:\n disabled: true\n", wantFact: "guardrails: OFF", }, { diff --git a/cmd/mecatui/permission_mode.go b/cmd/mecatui/permission_mode.go new file mode 100644 index 0000000000..303ecae370 --- /dev/null +++ b/cmd/mecatui/permission_mode.go @@ -0,0 +1,162 @@ +package main + +import ( + "errors" + "fmt" + "io" + + "github.com/stacklok/mecatl/cmd/mecatui/client" + "github.com/stacklok/mecatl/engine/session" + "github.com/stacklok/mecatl/internal/app" +) + +// permission_mode.go is mecatui's half of the --permission-mode flag. +// A token names an exact (posture, session mode) pair. The posture half goes to +// the embedded server's composition (app.Config.PermissionMode); the session half +// is the mode this client requests for the sessions it creates. The deprecated +// --mode, --posture, and --yolo keep resolving exactly as before and each emit one +// pre-launch deprecation WARN. + +// parsePermissionModeFlag parses an explicit --permission-mode into cfg and +// rejects a combination with the deprecated aliases. It runs at parse time so an +// unknown token fails fast on every transport. +func parsePermissionModeFlag(cfg *config) error { + if cfg.permissionModeFlagSet { + tok, err := app.ParsePermissionMode(cfg.permissionMode) + if err != nil { + return fmt.Errorf("--permission-mode: %w", err) + } + cfg.permissionModeToken = tok + } + return validatePermissionModeFlags(*cfg) +} + +// validatePermissionModeFlags enforces the flag-surface rules: --permission-mode +// replaces --mode, --posture, and --yolo rather than combining with them, and +// under `mecatui connect` a token whose posture half is not strict is refused, +// because the posture belongs to the remote server (mirroring --posture, which +// connect rejects outright). --trust-project is not deprecated and still folds. +func validatePermissionModeFlags(c config) error { + if !c.permissionModeFlagSet { + return nil + } + var aliases []string + if c.modeFlagSet { + aliases = append(aliases, "--mode") + } + if c.postureFlagSet { + aliases = append(aliases, "--posture") + } + if c.yoloFlagSet { + aliases = append(aliases, "--yolo") + } + if len(aliases) > 0 { + return fmt.Errorf("--permission-mode cannot be combined with %s; pass one: --permission-mode alone replaces the deprecated flags", joinFlagNames(aliases)) + } + tok := c.permissionModeToken + if c.transportMode == modeConnect && tok.Name != "" && tok.Posture != app.PostureStrict { + return errors.New("--permission-mode " + tok.Name + " sets posture " + tok.Posture.String() + + ", which belongs to the remote server; mecatui connect cannot change it. The server operator must change mecated's configuration and restart it. " + + "To choose the mode of this client's new sessions, pass plan, default, or accept-edits") + } + return nil +} + +func joinFlagNames(names []string) string { + switch len(names) { + case 1: + return names[0] + case 2: + return names[0] + " or " + names[1] + default: + out := "" + for i, n := range names { + switch { + case i == len(names)-1: + out += ", or " + n + case i > 0: + out += ", " + n + default: + out += n + } + } + return out + } +} + +// requestedSessionMode is the session mode this client asks for when it creates a +// session, in the TUI spelling. serverDefault is true when neither +// --permission-mode nor --mode was given: the create then carries no mode, so the +// server's own default applies (embedded: the operator's permissionMode: key; connect: +// the remote server's default). Without that key, and on an older server, the +// server's default is "default", which is exactly what mecatui requested before. +func (c config) requestedSessionMode() (mode string, serverDefault bool) { + switch { + case c.permissionModeFlagSet: + return client.ModeString(client.ModeFromString(string(c.permissionModeToken.SessionMode))), false + case c.modeFlagSet: + return c.mode, false + default: + return "", true + } +} + +// deprecatedPermissionFlagWarnings returns one WARN line per deprecated alias the +// operator typed (--mode, --posture, --yolo), each naming --permission-mode and, +// when one exists, the single token that reproduces the combination. +func deprecatedPermissionFlagWarnings(c config) []string { + if c.permissionModeFlagSet { + // Combined use is a startup error, so no deprecation line is needed. + return nil + } + if !c.modeFlagSet && !c.postureFlagSet && !c.yoloFlagSet { + return nil + } + hint := "no single token names this combination; it still resolves as before" + posture := app.ResolveAliasPosture(app.ParsePosture(c.posture), c.allowAllTools, c.trustProject) + if !c.modeFlagSet { + // No explicit session mode: the replacement token by posture alone. + hint = "use --permission-mode " + app.PostureReplacementToken(posture) + } else { + mode := c.mode + if tok, ok := app.PermissionModeForPair(posture, sessionModeFromTUI(mode)); ok { + hint = "use --permission-mode " + tok.Name + } + } + var out []string + add := func(flagName string) { + out = append(out, "mecatui: WARNING: "+flagName+" is deprecated and will be removed; use --permission-mode instead ("+hint+")") + } + if c.modeFlagSet { + add("--mode") + } + if c.postureFlagSet { + add("--posture") + } + if c.yoloFlagSet { + add("--yolo") + } + return out +} + +// warnDeprecatedPermissionFlags prints the deprecation WARNs on the pre-launch +// stderr path (before the alternate screen starts), like warnEmbeddedPosture. +func warnDeprecatedPermissionFlags(w io.Writer, c config) { + for _, line := range deprecatedPermissionFlagWarnings(c) { + _, _ = fmt.Fprintln(w, line) + } +} + +// sessionModeFromTUI maps a TUI mode spelling to the session value app's table +// uses. It goes through the client's spelling grammar first, so every accepted +// --mode spelling maps the same way it does on the wire. +func sessionModeFromTUI(mode string) session.PermissionMode { + switch client.ModeString(client.ModeFromString(mode)) { + case "plan": + return session.ModePlan + case "accept-edits": + return session.ModeAccept + default: + return session.ModeDefault + } +} diff --git a/cmd/mecatui/permission_mode_test.go b/cmd/mecatui/permission_mode_test.go new file mode 100644 index 0000000000..08141c5f0c --- /dev/null +++ b/cmd/mecatui/permission_mode_test.go @@ -0,0 +1,292 @@ +package main + +import ( + "context" + "io" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/stacklok/mecatl/cmd/mecatui/client" + "github.com/stacklok/mecatl/cmd/mecatui/embed" + "github.com/stacklok/mecatl/cmd/mecatui/ui" + "github.com/stacklok/mecatl/engine/adapter/mockllm" + "github.com/stacklok/mecatl/engine/port" + "github.com/stacklok/mecatl/engine/session" + "github.com/stacklok/mecatl/internal/app" +) + +// deprecationLines runs the real pre-launch warning path and returns its lines. +func deprecationLines(cfg config) []string { + var b strings.Builder + warnDeprecatedPermissionFlags(&b, cfg) + out := strings.TrimSpace(b.String()) + if out == "" { + return nil + } + return strings.Split(out, "\n") +} + +// TestDeprecatedAliasesStillResolve pins that, in mecatui, --mode, +// --posture, and --yolo resolve exactly as before and each emits exactly one +// deprecation WARN naming --permission-mode. --trust-project is not deprecated. +func TestDeprecatedAliasesStillResolve(t *testing.T) { + cases := []struct { + name string + args []string + wantPosture app.Posture + wantMode string + wantToken string + }{ + {name: "mode", args: []string{"--mode", "plan"}, wantPosture: app.PostureStrict, wantMode: "plan", wantToken: "plan"}, + {name: "posture", args: []string{"--posture", "auto"}, wantPosture: app.PostureAuto, wantMode: "", wantToken: "auto"}, + {name: "yolo", args: []string{"--yolo"}, wantPosture: app.PostureYolo, wantMode: "", wantToken: "yolo"}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + _, cfg, err := parseTransportFlagsTest(t, modeLocal, append([]string{"--mock", "--workspace", t.TempDir()}, tc.args...)) + if err != nil { + t.Fatalf("parse %v: %v", tc.args, err) + } + if got := embeddedAuthoritativePosture(cfg); got != tc.wantPosture { + t.Errorf("posture = %v, want %v", got, tc.wantPosture) + } + if mode, _ := cfg.requestedSessionMode(); mode != tc.wantMode { + t.Errorf("requested session mode = %q, want %q", mode, tc.wantMode) + } + ac := embeddedConfig(cfg, nil) + if ac.PermissionModeFlagSet { + t.Error("a deprecated alias must not set PermissionModeFlagSet") + } + lines := deprecationLines(cfg) + if len(lines) != 1 { + t.Fatalf("deprecation WARNs = %d (%q), want exactly 1", len(lines), lines) + } + for _, want := range []string{"WARNING", tc.args[0], "--permission-mode " + tc.wantToken} { + if !strings.Contains(lines[0], want) { + t.Errorf("WARN %q missing %q", lines[0], want) + } + } + }) + } + t.Run("each alias warns once", func(t *testing.T) { + _, cfg, err := parseTransportFlagsTest(t, modeLocal, []string{"--mock", "--workspace", t.TempDir(), "--mode", "plan", "--posture", "auto", "--yolo"}) + if err != nil { + t.Fatalf("parse: %v", err) + } + lines := deprecationLines(cfg) + if len(lines) != 3 { + t.Fatalf("deprecation WARNs = %q, want one per alias", lines) + } + for i, flagName := range []string{"--mode", "--posture", "--yolo"} { + if !strings.Contains(lines[i], flagName+" is deprecated") || !strings.Contains(lines[i], "--permission-mode") { + t.Errorf("WARN %d = %q, want %s deprecation naming --permission-mode", i, lines[i], flagName) + } + } + }) + t.Run("trust-project and the new flag do not warn", func(t *testing.T) { + for _, args := range [][]string{{"--trust-project"}, {"--permission-mode", "auto"}, nil} { + _, cfg, err := parseTransportFlagsTest(t, modeLocal, append([]string{"--mock", "--workspace", t.TempDir()}, args...)) + if err != nil { + t.Fatalf("parse %v: %v", args, err) + } + if lines := deprecationLines(cfg); len(lines) != 0 { + t.Errorf("%v emitted deprecation WARNs %q", args, lines) + } + } + }) +} + +// TestPermissionModeConflictsWithAliases pins that --permission-mode +// combined with any deprecated alias is a startup error telling the operator to +// pass one, and that an unknown token fails fast naming the valid set. +func TestPermissionModeConflictsWithAliases(t *testing.T) { + for _, alias := range [][]string{{"--mode", "plan"}, {"--posture", "auto"}, {"--yolo"}} { + args := append([]string{"--mock", "--workspace", "/abs", "--permission-mode", "plan"}, alias...) + _, _, err := parseTransportFlagsTest(t, modeLocal, args) + if err == nil { + t.Fatalf("%v: want a startup error", args) + } + if !strings.Contains(err.Error(), "--permission-mode cannot be combined with "+alias[0]) || !strings.Contains(err.Error(), "pass one") { + t.Errorf("%v: error %q must name the conflict and say to pass one", args, err) + } + } + _, _, err := parseTransportFlagsTest(t, modeLocal, []string{"--mock", "--workspace", "/abs", "--permission-mode", "accept-all"}) + if err == nil || !strings.Contains(err.Error(), "accept-edits") { + t.Fatalf("unknown token error = %v, want the valid token list", err) + } + if _, _, err := parseTransportFlagsTest(t, modeLocal, []string{"--mock", "--workspace", "/abs", "--permission-mode", "plan", "--trust-project"}); err != nil { + t.Errorf("--trust-project is not deprecated and must combine: %v", err) + } +} + +// TestConnectRefusesPostureHalf pins the connect behaviour: the session +// half of a token applies to this client's sessions, while a non-strict posture +// half is refused because the posture belongs to the remote server. +func TestConnectRefusesPostureHalf(t *testing.T) { + _, cfg, err := parseTransportFlagsTest(t, modeConnect, []string{"--permission-mode", "accept-edits"}) + if err != nil { + t.Fatalf("session-only token under connect: %v", err) + } + if mode, serverDefault := cfg.requestedSessionMode(); mode != "accept-edits" || serverDefault { + t.Errorf("connect session half = %q (server default %v), want accept-edits", mode, serverDefault) + } + for _, tok := range []string{"auto", "yolo"} { + _, _, err := parseTransportFlagsTest(t, modeConnect, []string{"--permission-mode", tok}) + if err == nil { + t.Fatalf("connect --permission-mode %s: want an error", tok) + } + if !strings.Contains(err.Error(), "belongs to the remote server") || !strings.Contains(err.Error(), "mecated") { + t.Errorf("connect --permission-mode %s error %q must name the server's ownership", tok, err) + } + } +} + +// TestVocabularyMatchesComposition keeps the TUI's display copy of the +// token table identical to the composition layer's contract. +func TestVocabularyMatchesComposition(t *testing.T) { + vocab := ui.PermissionModeVocabulary() + names := app.PermissionModeNames() + if len(vocab) != len(names) { + t.Fatalf("TUI lists %d tokens, composition has %d", len(vocab), len(names)) + } + for i, name := range names { + tok, err := app.ParsePermissionMode(name) + if err != nil { + t.Fatal(err) + } + want := ui.PermissionModeEntry{ + Name: tok.Name, + Posture: tok.Posture.String(), + SessionMode: client.ModeString(client.ModeFromString(string(tok.SessionMode))), + } + if vocab[i] != want { + t.Errorf("row %d = %+v, want %+v", i, vocab[i], want) + } + } +} + +// TestDefaultsReproduceCurrentBehaviour pins, for mecatui end to end, that the +// parsed flags build the real embedded server, the real client creates a session +// through the real sessionAdapter, and the server-reported posture and session +// mode match what mecatui produced before the permission-mode vocabulary. +func TestDefaultsReproduceCurrentBehaviour(t *testing.T) { + cases := []struct { + name string + args []string + wantPosture string + wantMode string + }{ + {name: "unconfigured", args: nil, wantPosture: "strict", wantMode: "default"}, + {name: "mode plan", args: []string{"--mode", "plan"}, wantPosture: "strict", wantMode: "plan"}, + {name: "permission-mode plan", args: []string{"--permission-mode", "plan"}, wantPosture: "strict", wantMode: "plan"}, + {name: "permission-mode accept-edits", args: []string{"--permission-mode", "accept-edits"}, wantPosture: "strict", wantMode: "accept-edits"}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + ctx, cancel := context.WithTimeout(t.Context(), 15*time.Second) + defer cancel() + workspace := t.TempDir() + _, cfg, err := parseTransportFlagsTest(t, modeLocal, append([]string{"--mock", "--workspace", workspace}, tc.args...)) + if err != nil { + t.Fatalf("parse: %v", err) + } + ac := embeddedConfig(cfg, nil) + ac.MockProvider = mockllm.New() + ac.StoreDir = "" + ac.MemoryDir = t.TempDir() + ac.UserModelDir = t.TempDir() + ac.ToolHiveEnabled = false + srv, err := embed.Start(ctx, ac, embed.PerfConfig{}) + if err != nil { + t.Fatalf("start embedded server: %v", err) + } + defer func() { _ = srv.Close() }() + cl, err := client.Dial(client.DialConfig{Server: srv.Target()}) + if err != nil { + t.Fatalf("dial: %v", err) + } + defer func() { _ = cl.Close() }() + + requested, _ := cfg.requestedSessionMode() + adapter := &sessionAdapter{cl: cl, mode: requested} + id, caps, _, err := adapter.CreateSession(ctx, client.ModelSelection{}, requested) + if err != nil { + t.Fatalf("create session: %v", err) + } + snap, err := adapter.GetSession(ctx, id) + if err != nil { + t.Fatalf("get session: %v", err) + } + if caps.Posture != tc.wantPosture { + t.Errorf("posture = %q, want %q", caps.Posture, tc.wantPosture) + } + if snap.Mode != tc.wantMode { + t.Errorf("session mode = %q, want %q", snap.Mode, tc.wantMode) + } + }) + } +} + +// TestEmbeddedSettingsPermissionModeRefusesDeprecatedFlags drives the embedded +// server's real path (flags, embeddedConfig, app.Build) with a conventional +// user-global settings.yaml carrying permissionMode: plan. The deprecated +// --yolo and --posture flags cannot be combined with it; the token alone and +// the deprecated flag alone still build. +func TestEmbeddedSettingsPermissionModeRefusesDeprecatedFlags(t *testing.T) { + storage := isolatedEmbeddedTestStorage(t) + settings := filepath.Join(os.Getenv("XDG_CONFIG_HOME"), "mecatl", "settings.yaml") + build := func(t *testing.T, withSettings bool, args ...string) (*app.Built, error) { + t.Helper() + if withSettings { + if err := os.MkdirAll(filepath.Dir(settings), 0o700); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(settings, []byte("permissionMode: plan\n"), 0o600); err != nil { + t.Fatal(err) + } + } else if err := os.Remove(settings); err != nil && !os.IsNotExist(err) { + t.Fatal(err) + } + _, cfg, err := parseTransportFlags(modeLocal, io.Discard, append([]string{"--mock", "--workspace", t.TempDir()}, args...)) + if err != nil { + t.Fatalf("parse %v: %v", args, err) + } + ac := embeddedConfig(storage.apply(cfg), port.NopDiagnostics{}) + ac.NoSoul, ac.Privileged = true, false + return app.Build(t.Context(), ac) + } + for _, args := range [][]string{{"--yolo"}, {"--posture", "auto"}} { + built, err := build(t, true, args...) + if err == nil { + built.Close() + t.Fatalf("%q: settings permissionMode plus a deprecated flag must refuse to build", args) + } + if !strings.Contains(err.Error(), "permissionMode: plan cannot be combined") || !strings.Contains(err.Error(), "--permission-mode") { + t.Fatalf("%q: refusal must name the conflict and the migration; got %v", args, err) + } + } + for _, tc := range []struct { + withSettings bool + args []string + want session.PermissionMode + }{ + {true, nil, session.ModePlan}, + {false, []string{"--yolo"}, session.ModeYolo}, + } { + built, err := build(t, tc.withSettings, tc.args...) + if err != nil { + t.Fatalf("settings=%v %q: app.Build: %v", tc.withSettings, tc.args, err) + } + sess, err := built.Service.CreateSession(context.Background(), "", session.Limits{}) + built.Close() + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + if sess.Mode != tc.want { + t.Fatalf("settings=%v %q: unspecified-mode session started in %q, want %q", tc.withSettings, tc.args, sess.Mode, tc.want) + } + } +} diff --git a/cmd/mecatui/startup_compatibility_test.go b/cmd/mecatui/startup_compatibility_test.go index f251003594..752720726e 100644 --- a/cmd/mecatui/startup_compatibility_test.go +++ b/cmd/mecatui/startup_compatibility_test.go @@ -188,7 +188,7 @@ func TestStartupCompatibilityFreshAccepted(t *testing.T) { if err != nil || !ran { t.Fatalf("startup error = %v, normal UI ran = %v", err, ran) } - if calls := service.observedCalls(); !reflect.DeepEqual(calls, []string{"GetCompatibilityInfo", "ListModels", "GetCompatibilityInfo", "CreateSession"}) { + if calls := service.observedCalls(); !reflect.DeepEqual(calls, []string{"GetCompatibilityInfo", "ListModels", "GetCompatibilityInfo", "CreateSession", "GetSession"}) { t.Fatalf("fresh startup RPCs = %v", calls) } } diff --git a/cmd/mecatui/ui/builtins.go b/cmd/mecatui/ui/builtins.go index f8009afc86..fae23f6dd2 100644 --- a/cmd/mecatui/ui/builtins.go +++ b/cmd/mecatui/ui/builtins.go @@ -452,7 +452,7 @@ func (m Model) runHelp() (tea.Model, tea.Cmd) { return m, nil } deps := (&m).surfaceDeps() - m.modal = &helpState{deps: deps, lines: strings.Split(helpBody(deps.theme, deps.caps, deps.marks), "\n")} + m.modal = &helpState{deps: deps, lines: strings.Split(m.helpBody(), "\n")} m.prompt.Blur() return m, nil } @@ -571,7 +571,7 @@ func (m Model) runLearningSensitivity() (tea.Model, tea.Cmd) { // non-empty. The summary names the four defenses the posture controls so an operator // can confirm, e.g., that the child prompt-injection defense is OFF under yolo. func (m Model) runPosture() (tea.Model, tea.Cmd) { - base := postureSummary(m.caps.Posture) + base := postureSummary(m.caps.Posture, m.caps.ProjectTrust, m.caps.SubagentAskReviewer) if m.deps.Guardrails == nil || m.sessionID == "" { m.statusMsg = m.deps.Theme.Style("muted").Render(base + "; checker unknown (server does not expose guardrail coverage)") return m, nil @@ -622,13 +622,24 @@ func (m Model) runDebugAsk() (tea.Model, tea.Cmd) { } // postureSummary renders the one-line /posture summary for a posture token. It is -// pure (no Model) so it is directly testable. allow-all + main-substitution are on at -// auto+yolo; the CHILD substitution (prompt-injection defense OFF) is yolo-only; -// project-trust is on at trusted+. An empty/unknown token degrades to a bare label. -func postureSummary(p string) string { - allowAll := p == postureAuto || p == postureYolo - childDefenseOff := p == postureYolo +// pure (no Model) so it is directly testable. +// +// Posture sets the server ceiling (which modes a session may enter) and the +// project-trust floor. Allow-all and substitution loosening are now session-mode +// behaviors: auto/yolo sessions inject the allow-all rule per-evaluate, so the +// description names the ceiling rather than claiming allow-all is always on. +// Project-trust is the server-reported trust when known (trust), else inferred +// from the posture, which raises it for trusted+ on interactive roots. An +// empty/unknown token degrades to a bare label. The subagent ask reviewer is +// named only when the server reported it. +func postureSummary(p, projectTrust, reviewer string) string { + ceilingAuto := p == postureAuto + ceilingYolo := p == postureYolo + childDefenseOff := p == postureYolo // yolo session mode loosens child substitution trust := p == postureTrusted || p == postureAuto || p == postureYolo + if projectTrust != "" { + trust = projectTrust == client.ProjectTrusted + } onoff := func(b bool) string { if b { return "on" @@ -639,12 +650,37 @@ func postureSummary(p string) string { if label == "" { label = unknownLabel } + ceiling := "default/plan/accept-edits" + if ceilingYolo { + ceiling = "all modes including yolo" + } else if ceilingAuto { + ceiling = "up to and including auto" + } return "posture " + label + - " — allow-all " + onoff(allowAll) + - "; main $()/heredoc auto-run " + onoff(allowAll) + - "; child $()/heredoc auto-run (injection-defense off) " + onoff(childDefenseOff) + + " — ceiling " + ceiling + + "; child $()/heredoc auto-run when in yolo mode (injection-defense off) " + onoff(childDefenseOff) + "; project-trust " + onoff(trust) + - " (Deny & configured Ask always apply)" + " (allow-all is mode-driven: auto/yolo sessions inject it; Deny & configured Ask always apply)" + + reviewerSummary(reviewer) +} + +// reviewerSummary renders the /posture segment for the server-reported subagent +// ask reviewer state. Empty (an older server) renders nothing; an unrecognized +// value is never echoed raw. +func reviewerSummary(state string) string { + const prefix = "; subagent ask reviewer " + switch state { + case "": + return "" + case "on": + return prefix + "on (a model may approve subagent permission requests no human can answer)" + case "off": + return prefix + "off" + case "inert": + return prefix + "inert (configured, but you answer subagent permission requests)" + default: + return prefix + unknownLabel + } } type builtinName struct { diff --git a/cmd/mecatui/ui/canonical_status_test.go b/cmd/mecatui/ui/canonical_status_test.go index aaff471425..fe90dd844e 100644 --- a/cmd/mecatui/ui/canonical_status_test.go +++ b/cmd/mecatui/ui/canonical_status_test.go @@ -57,7 +57,7 @@ func TestCanonicalStatus_Scenario1_StockSourceOwnsSurfaces(t *testing.T) { func TestCanonicalStatus_Scenario1_EmptySourceDoesNotFallBack(t *testing.T) { source := &statusSourceFake{changed: make(chan struct{}, 1)} m := canonicalFrame(source, 110) - m.caps.Posture = postureAuto + m.caps.ProjectTrust = client.ProjectUntrusted m.generatedStatusLine = customization.Result{Header: customization.Surface{Present: true, Spans: []customization.Span{{Text: strings.Repeat("oversize", 100)}}}, Footer: customization.Surface{Present: true, Spans: []customization.Span{{Text: strings.Repeat("oversize", 100)}}}} for _, result := range []customization.Result{m.generatedStatusLine, {}, {Header: customization.Surface{Present: true}, Footer: customization.Surface{Present: true}}} { m.generatedStatusLine = result @@ -65,7 +65,7 @@ func TestCanonicalStatus_Scenario1_EmptySourceDoesNotFallBack(t *testing.T) { if strings.Contains(header, "session") || strings.Contains(header, "canonical-model") || strings.Contains(header, "oversize") || strings.Contains(header, "·") || strings.Contains(footer, "ctx") || strings.Contains(footer, "oversize") { t.Fatalf("source fallback: %q / %q", header, footer) } - if !strings.Contains(header, "auto") || !strings.Contains(footer, "ready") || !strings.Contains(footer, "help") { + if !strings.Contains(header, client.ProjectUntrusted) || !strings.Contains(footer, "ready") || !strings.Contains(footer, "help") { t.Fatalf("lost mandatory chrome: %q / %q", header, footer) } } @@ -73,7 +73,7 @@ func TestCanonicalStatus_Scenario1_EmptySourceDoesNotFallBack(t *testing.T) { func TestCanonicalStatus_Scenario1_NoSourceMinimalIdentity(t *testing.T) { m := canonicalFrame(nil, 110) - m.caps.Posture = postureAuto + m.caps.ProjectTrust = client.ProjectUntrusted for _, id := range []string{"", "safe-1234567890", "hostile\x1b[31m\nmore-1234567890", "\xff"} { m.sessionID = id header, footer := stripANSIstr(m.renderHeader()), stripANSIstr(m.renderFooter()) @@ -84,7 +84,7 @@ func TestCanonicalStatus_Scenario1_NoSourceMinimalIdentity(t *testing.T) { } else if !strings.Contains(header, "session "+handle) { t.Fatalf("known identity: %q", header) } - if strings.Contains(header, "canonical-model") || strings.Contains(header, "[31m") || strings.Contains(footer, "ctx") || strings.Contains(footer, "↑") || !strings.Contains(header, "auto") || !strings.Contains(footer, "ready") || !strings.Contains(footer, "help") { + if strings.Contains(header, "canonical-model") || strings.Contains(header, "[31m") || strings.Contains(footer, "ctx") || strings.Contains(footer, "↑") || !strings.Contains(header, client.ProjectUntrusted) || !strings.Contains(footer, "ready") || !strings.Contains(footer, "help") { t.Fatalf("unsafe/legacy identity or lost chrome: %q / %q", header, footer) } } @@ -196,10 +196,10 @@ func TestCanonicalStatus_Scenario2_DebugDisclosureSurvivesMissingSourceAndFatal( func TestCanonicalStatus_Scenario2_DebugCommandSourceReceivesReservedWidth(t *testing.T) { m := canonicalFrame(nil, 120) m.deps.DebugTarget = "target-1234567890" - m.caps.Posture = postureAuto + m.caps.ProjectTrust = client.ProjectUntrusted m.conv.recordFileChange("changed.go") cue := "⚠ DEBUG target " + client.SessionHandle(m.deps.DebugTarget) - budget := m.width - 2 - lipgloss.Width(cue) - 5 - lipgloss.Width(autoBadgeText) - 2 - lipgloss.Width("✎ 1 file") - headerGapPad + budget := m.width - 2 - lipgloss.Width(cue) - 5 - lipgloss.Width(client.ProjectUntrusted) - 2 - lipgloss.Width("✎ 1 file") - headerGapPad if budget <= 0 { t.Fatalf("no command header budget: %d", budget) } @@ -214,7 +214,7 @@ func TestCanonicalStatus_Scenario2_DebugCommandSourceReceivesReservedWidth(t *te m = updated.(Model) header := stripANSIstr(m.renderHeader()) first := strings.Split(header, "\n")[0] - if i, j := strings.Index(first, cue), strings.Index(first, identity); i < 0 || j <= i || !strings.Contains(first, autoBadgeText) || !strings.Contains(first, "✎ 1 file") || strings.Contains(header, "wrong-budget") || lipgloss.Width(first) > m.width { + if i, j := strings.Index(first, cue), strings.Index(first, identity); i < 0 || j <= i || !strings.Contains(first, client.ProjectUntrusted) || !strings.Contains(first, "✎ 1 file") || strings.Contains(header, "wrong-budget") || lipgloss.Width(first) > m.width { t.Fatalf("command source width %d displaced debug warning or indicator: %q (surface %#v)", budget, header, m.generatedStatusLine.Header) } if !m.generatedStatusLine.Header.Present || !statusSurfaceFits(m.generatedStatusLine.Header, budget) || !strings.Contains(header, "PRIVACY:") { @@ -236,10 +236,10 @@ func TestCanonicalStatus_Scenario2_DebugReservationReachesSource(t *testing.T) { if first.Terminal.HeaderAvailCols != m.statusLineGeometry().headerAvailable || first.Terminal.HeaderAvailCols > 120-2-warningWidth { t.Fatalf("warning not reserved: %#v", first.Terminal) } - m = applyAll(m, client.SessionReadyMsg{SessionID: m.sessionID, Capabilities: client.Capabilities{Posture: postureAuto}}) + m = applyAll(m, client.SessionReadyMsg{SessionID: m.sessionID, Capabilities: client.Capabilities{Posture: postureAuto, ProjectTrust: client.ProjectTrusted}}) second, _ := source.lastInput() if second.Terminal.HeaderAvailCols >= first.Terminal.HeaderAvailCols { - t.Fatalf("posture did not reserve: %d -> %d", first.Terminal.HeaderAvailCols, second.Terminal.HeaderAvailCols) + t.Fatalf("trust badge did not reserve: %d -> %d", first.Terminal.HeaderAvailCols, second.Terminal.HeaderAvailCols) } m = applyAll(m, client.ToolCallMsg{ID: "write-1", Name: "Write", Args: `{"path":"changed.go","content":"x"}`}) withNavigation, _ := source.lastInput() diff --git a/cmd/mecatui/ui/help.go b/cmd/mecatui/ui/help.go index 78ae43e854..532eca4453 100644 --- a/cmd/mecatui/ui/help.go +++ b/cmd/mecatui/ui/help.go @@ -154,6 +154,20 @@ func helpScrollIndicator(hk helpKeys, start, end, total int) string { // hk carries the LIVE key markings from the model's keyMap, so a rebinding // propagates here. func helpBody(th theme.Theme, caps client.Capabilities, hk helpKeys) string { + return helpBodyFor(th, caps, hk, true) +} + +// helpBody renders the overlay body for this model's transport: the permission +// mode section's restart guidance differs between the embedded server and +// `mecatui connect`. +func (m Model) helpBody() string { + return helpBodyFor(m.deps.Theme, m.caps, m.helpKeyMarkings(), m.deps.Embedded) +} + +// helpBodyFor is helpBody with an explicit transport. embedded selects whose +// restart a posture change needs: the operator's own mecatui relaunch, or the +// remote server operator's mecated restart. +func helpBodyFor(th theme.Theme, caps client.Capabilities, hk helpKeys, embedded bool) string { heading := th.Style("toolName") body := th.Style("toolArgs") var b strings.Builder @@ -219,6 +233,8 @@ func helpBody(th theme.Theme, caps client.Capabilities, hk helpKeys) string { ) writeHelpRows(&b, th, inspectRows) + writeHelpPermissionModes(&b, th, hk, embedded) + b.WriteString("\n" + heading.Render("Conversation and navigation") + "\n") writeHelpRows(&b, th, []helpRow{ {key: hk.selectAll, action: "select all prompt text"}, @@ -290,6 +306,58 @@ func helpBody(th theme.Theme, caps client.Capabilities, hk helpKeys) string { return b.String() } +// PermissionModeEntry is one row of the permission-mode vocabulary as +// the TUI displays it: the token, the process-wide posture it sets, and the +// session mode new sessions start in (the TUI spelling). +type PermissionModeEntry struct { + Name string + Posture string + SessionMode string +} + +// PermissionModeVocabulary is the token table the help overlay lists. The ui +// cannot import the composition layer, so this is a display copy; a test in the +// mecatui main package pins it to app.ParsePermissionMode so the two never drift. +func PermissionModeVocabulary() []PermissionModeEntry { + // auto and yolo each have their own session mode on the wire. + return []PermissionModeEntry{ + {Name: "plan", Posture: postureStrict, SessionMode: "plan"}, + {Name: "default", Posture: postureStrict, SessionMode: client.ModeDefaultString}, + {Name: "accept-edits", Posture: postureStrict, SessionMode: "accept-edits"}, + {Name: "auto", Posture: postureAuto, SessionMode: "auto"}, + {Name: "yolo", Posture: postureYolo, SessionMode: "yolo"}, + } +} + +// writeHelpPermissionModes lists every permission-mode token with its two +// halves, states that the posture half is process-wide while the session half is +// only a new-session default, that cycling reaches every mode but cannot raise +// the posture, and whose restart a posture change needs. +func writeHelpPermissionModes(b *strings.Builder, th theme.Theme, hk helpKeys, embedded bool) { + b.WriteString("\n" + th.Style("toolName").Render("Permission modes (--permission-mode, or cycle with "+hk.modeSwitch+")") + "\n") + vocab := PermissionModeVocabulary() + rows := make([]helpRow, 0, len(vocab)) + for _, e := range vocab { + rows = append(rows, helpRow{key: e.Name, action: "posture " + e.Posture + " (process-wide) · new sessions start in " + e.SessionMode}) + } + writeHelpRows(b, th, rows) + writeHelpBodyLines(b, th, + "The posture half applies to the whole server and is fixed when it starts.", + "The session half is only the default for new sessions.", + hk.modeSwitch+" cycles every mode. A mode that raises the posture needs a restart, and auto also an enforcing guardrails checker;", + "cycling onto one keeps the current mode and your draft, and the footer says what makes it available.", + "A mode at or below the posture applies its session half live.", + ) + if embedded { + writeHelpBodyLines(b, th, + "To change the posture, quit and relaunch: mecatui --permission-mode ") + return + } + writeHelpBodyLines(b, th, + "This client is connected to a remote server, which owns the posture.", + "To change it, the server operator must change mecated's configuration and restart it.") +} + // writeHelpBodyLines renders each line independently so helpRenderedLines can // safely window the ANSI output without severing a style sequence. func writeHelpBodyLines(b *strings.Builder, th theme.Theme, lines ...string) { diff --git a/cmd/mecatui/ui/keys.go b/cmd/mecatui/ui/keys.go index e7bff3e704..8159590e83 100644 --- a/cmd/mecatui/ui/keys.go +++ b/cmd/mecatui/ui/keys.go @@ -63,8 +63,9 @@ type keyMap struct { ScrollTop key.Binding ScrollBottom key.Binding - // ModeSwitch (shift+tab) cycles the session permission mode: default → plan → - // accept-edits → default. Unlike Alt+M, Shift+Tab does not depend on a terminal + // ModeSwitch (shift+tab) cycles every permission mode: default → plan → + // accept-edits → auto → yolo → default (see cycleMode for modes above the + // server ceiling). Unlike Alt+M, Shift+Tab does not depend on a terminal // mapping macOS Option to Meta, and it never collides with prose input. ModeSwitch key.Binding diff --git a/cmd/mecatui/ui/mode_cycle.go b/cmd/mecatui/ui/mode_cycle.go new file mode 100644 index 0000000000..4f5f6b7dc5 --- /dev/null +++ b/cmd/mecatui/ui/mode_cycle.go @@ -0,0 +1,221 @@ +package ui + +import ( + tea "charm.land/bubbletea/v2" +) + +// modeCycleOrder is the ModeSwitch order over every permission mode: the +// vocabulary with default and plan swapped, keeping the established +// default → plan → accept-edits start before the posture-raising modes. A +// legacy server (see legacyPermissionModes) has no auto or yolo session mode, +// so its cycle stops at the three session modes it understands. +func modeCycleOrder(legacy bool) []string { + vocab := PermissionModeVocabulary() + names := make([]string, 0, len(vocab)) + for _, e := range vocab { + if legacy && e.Posture != postureStrict { + continue + } + names = append(names, e.Name) + } + names[0], names[1] = names[1], names[0] + return names +} + +// legacyPermissionModes reports a server without the session_permission_modes +// feature. Its auto or yolo posture is process-wide: it applies to every +// session whatever the session mode, and SetMode cannot remove it. The ui then +// cycles only the legacy session modes and labels the header with the server +// posture, so a session mode is never presented as the effective permission mode. +func (m Model) legacyPermissionModes() bool { + return !m.caps.SessionPermissionModes +} + +// legacyAllowAllPosture is the process-wide allow-all posture a legacy server +// applies over every session mode, or "" when there is none. +func (m Model) legacyAllowAllPosture() string { + if !m.legacyPermissionModes() { + return "" + } + switch m.caps.Posture { + case postureAuto, postureYolo: + return m.caps.Posture + default: + return "" + } +} + +// modeCursor is the permission mode the cycle last landed on, valid only while +// the session's desired mode is still the one it recorded. Any other mode change +// (server read-back, new session) invalidates it, so the cycle re-derives its +// position from the live session. +type modeCursor struct { + token string + session string +} + +// postureRank orders the posture tiers. An unknown posture (an older server +// omits it) ranks as strict. +func postureRank(p string) int { + switch p { + case postureTrusted: + return 1 + case postureAuto: + return 2 + case postureYolo: + return 3 + default: + return 0 + } +} + +func permissionModeEntry(name string) PermissionModeEntry { + for _, e := range PermissionModeVocabulary() { + if e.Name == name { + return e + } + } + return PermissionModeEntry{} +} + +// currentModeToken is the cycle position: the mode the cycle last landed on, or +// the mode matching the running posture and the session's desired mode. +func (m Model) currentModeToken() string { + if m.modeCursor.token != "" && m.modeCursor.session == m.desiredMode() { + return m.modeCursor.token + } + return m.acceptedModeToken() +} + +// acceptedModeToken is the mode the session is actually running: the one +// matching the running posture and the session's desired mode, else the +// session mode itself. +func (m Model) acceptedModeToken() string { + session := m.desiredMode() + if m.legacyPermissionModes() { + return session + } + for _, e := range PermissionModeVocabulary() { + if e.Posture == m.caps.Posture && e.SessionMode == session { + return e.Name + } + } + return session +} + +func nextModeToken(cur string, legacy bool) string { + order := modeCycleOrder(legacy) + for i, name := range order { + if name == cur { + return order[(i+1)%len(order)] + } + } + // No position yet (the server default is still unknown): start after default. + return order[1] +} + +// cycleMode lands on the next permission mode. A mode whose posture is at or +// below the running one applies its session half live. One that would raise the +// posture cannot apply, because the posture (the server ceiling) is fixed when +// the server starts: the cycle only parks its cursor there so the next press +// moves on, while the session keeps its confirmed mode, the draft stays usable, +// and the footer says what would make the mode available. +func (m Model) cycleMode() (tea.Model, tea.Cmd) { + cur := m.currentModeToken() + if m.modeRefused != "" { + cur, m.modeRefused = m.modeRefused, "" + } + next := permissionModeEntry(nextModeToken(cur, m.legacyPermissionModes())) + if postureRank(next.Posture) > postureRank(m.caps.Posture) { + m.modeCursor = modeCursor{token: next.Name, session: m.desiredMode()} + m.statusMsg = m.deps.Theme.Style("warning").Render(m.modeUnavailableStatus(next)) + (&m).relayout() + return m, nil + } + mm, cmd := m.switchMode(next) + m = mm.(Model) + m.modeCursor = modeCursor{token: next.Name, session: m.desiredMode()} + (&m).relayout() + return m, cmd +} + +// headerMode is the session mode the header shows: the confirmed mode, or the +// requested one marked pending. A mode the cycle parked on because it is +// unavailable never shows here: the session is not running it. +// +// "pending" means a SetMode round-trip is in flight. When the pending mode +// equals the server-confirmed active mode (m.pendingMode == m.activeMode) the +// session IS already in the target mode; the in-flight call is a no-op +// confirmation. Showing "pending" in that case is misleading — e.g. cycling +// through other modes and back to the starting auto mode would flash +// "auto pending" even though the session never left auto from the user's +// perspective. Skip the suffix in that case. +func (m Model) headerMode() string { + mode := m.activeMode + if mode == "" { + mode = m.deps.Mode + } + if m.pendingMode != "" { + mode = m.pendingMode + if m.pendingMode != m.activeMode { + // Only show "pending" when the in-flight mode differs from what the + // server has already confirmed — a genuine transition, not a no-op. + mode += " pending" + } + } + if posture := m.legacyAllowAllPosture(); posture != "" && mode != "" { + // An older server's allow-all posture overrides every session mode. + mode += " (server " + posture + ")" + } + return mode +} + +// modeUnavailableStatus is the footer notice for a mode above the server +// ceiling: the session stays in its confirmed mode, what would make the mode +// available, and that the next press moves on. +func (m Model) modeUnavailableStatus(e PermissionModeEntry) string { + return "mode " + e.Name + " unavailable, staying in " + m.acceptedModeToken() + " — " + + m.modeRestartHint(e) + " · " + firstKey(m.keys.ModeSwitch, "shift+tab") + " next mode" +} + +// modeLabel names a landed mode for the footer. All modes at or below the +// server ceiling are fully effective — allow-all and loose-substitution are +// session-mode-driven, not posture-driven, so switching to a lower mode +// actually changes behavior and nothing "stays until restart". +// +// On a legacy server with an allow-all posture the label says that posture +// still applies, because the session mode cannot lower it there. +func (m Model) modeLabel(e PermissionModeEntry) string { + if posture := m.legacyAllowAllPosture(); posture != "" { + return e.Name + " (the server's " + posture + " posture still applies to every session)" + } + return e.Name +} + +// modeRestartHint says what makes a mode above the server ceiling available: a +// restart with a higher --permission-mode and, for auto only, an enforcing +// guardrails checker on the server. +func (m Model) modeRestartHint(e PermissionModeEntry) string { + checker := e.Posture == postureAuto + if !m.deps.Embedded { + hint := "the remote server's operator must restart mecated with --permission-mode " + e.Name + if checker { + hint += " and an enforcing guardrails checker" + } + return hint + } + hint := "quit and relaunch with mecatui --permission-mode " + e.Name + if checker { + hint += "; auto also needs an enforcing guardrails checker (set guardrails.model in settings.yaml)" + } + return hint +} + +// modeRefusedHint adds what the server needs before it accepts a refused mode. +// Only auto has a prerequisite beyond the ceiling. +func modeRefusedHint(mode string) string { + if mode == "auto" { + return " — auto needs an enforcing guardrails checker on the server" + } + return "" +} diff --git a/cmd/mecatui/ui/mode_test.go b/cmd/mecatui/ui/mode_test.go index 1590e76bce..73964414c4 100644 --- a/cmd/mecatui/ui/mode_test.go +++ b/cmd/mecatui/ui/mode_test.go @@ -124,6 +124,97 @@ func TestModeSwitchFailureDefersToNextPrompt(t *testing.T) { } } +// TestModeRefusedAtIdleClearsPending pins that a SetMode error with no turn in +// flight is the server's final refusal: the header must not stay "pending" +// and the status names the server's reason. +func TestModeRefusedAtIdleClearsPending(t *testing.T) { + conv := &fakeConv{recv: &fakeRecver{}, send: &fakeSender{}, mode: "default", setModeErr: errors.New("set mode: mode \"plan\" exceeds the server ceiling")} + m := modeTestModel(t, conv) + + mm, cmd := m.Update(modeKey()) + m = mm.(Model) + if cmd == nil { + t.Fatal("shift+tab should issue SetMode command") + } + m = applyAll(m, cmd()) + + if m.activeMode != "default" || m.pendingMode != "" { + t.Fatalf("active/pending mode = %q/%q, want default/empty", m.activeMode, m.pendingMode) + } + status := stripANSIstr(m.statusMsg) + if !strings.Contains(status, "mode plan refused") || !strings.Contains(status, "exceeds the server ceiling") { + t.Fatalf("status = %q, want refusal naming the server reason", status) + } + if mode := m.statusLineSnapshot().Session.Mode; mode != "default" { + t.Fatalf("status snapshot mode = %q, want default", mode) + } + if m.retryPendingModeCmd() != nil { + t.Fatal("a refused mode must not be retried at the next turn boundary") + } +} + +// TestRefusedModeDoesNotTrapCycle pins that after the server refuses a mode +// the next ModeSwitch continues past it instead of re-requesting it forever: +// on a yolo server an auto refusal still lets the cycle reach yolo. +func TestRefusedModeDoesNotTrapCycle(t *testing.T) { + conv := &fakeConv{recv: &fakeRecver{}, send: &fakeSender{}, mode: "accept-edits"} + m := newTestModelFromDeps(Deps{Session: conv, Conv: conv, Theme: theme.New("aztec", theme.AztecPalette()), Mode: "accept-edits", Ctx: context.Background(), Embedded: true}) + m = applyAll(m, tea.WindowSizeMsg{Width: 120, Height: 30}, client.SessionReadyMsg{SessionID: "sess-test-0001", Mode: "accept-edits"}) + m.caps.Posture = postureYolo + m.caps.SessionPermissionModes = true + + press := func() { + t.Helper() + mm, cmd := m.Update(modeKey()) + m = mm.(Model) + if cmd != nil { + m = applyAll(m, cmd()) + } + } + conv.setModeErr = errors.New(`mode "auto" requires an enforcing guardrails checker`) + press() + status := stripANSIstr(m.statusMsg) + if m.activeMode != "accept-edits" || m.headerMode() != "accept-edits" || + !strings.Contains(status, "mode auto refused, staying in accept-edits") || + !strings.Contains(status, "auto needs an enforcing guardrails checker on the server") { + t.Fatalf("after auto refusal: active=%q header=%q status=%q", m.activeMode, m.headerMode(), status) + } + // The refusal freezes nothing: the draft stays editable. + m = applyAll(m, tea.KeyPressMsg{Code: 'x', Text: "x"}) + if got := m.prompt.Value(); got != "x" { + t.Fatalf("prompt after refusal = %q, want editable", got) + } + m.prompt.Rewrite("") + conv.setModeErr = nil + press() + if got := conv.setModes(); len(got) != 2 || got[1] != "yolo" { + t.Fatalf("SetMode calls = %v, want [auto yolo]", got) + } + if m.activeMode != "yolo" || m.headerMode() != "yolo" { + t.Fatalf("active/header = %q/%q, want yolo", m.activeMode, m.headerMode()) + } +} + +// TestModeMismatchAdoptsServerMode pins that a SetMode reply naming a +// different mode than requested is adopted, not swallowed into a permanent +// "pending" header. +func TestModeMismatchAdoptsServerMode(t *testing.T) { + conv := &fakeConv{recv: &fakeRecver{}, send: &fakeSender{}, mode: "default"} + m := modeTestModel(t, conv) + m.pendingMode = "auto" + + m = applyAll(m, client.ModeChangedMsg{SessionID: "sess-test-0001", Requested: "auto", Mode: "default"}) + if m.activeMode != "default" || m.pendingMode != "" { + t.Fatalf("active/pending mode = %q/%q, want default/empty", m.activeMode, m.pendingMode) + } + if status := stripANSIstr(m.statusMsg); !strings.Contains(status, "mode auto not applied") || !strings.Contains(status, "kept default") { + t.Fatalf("status = %q, want mismatch notice", status) + } + if strings.Contains(m.headerMode(), "pending") { + t.Fatalf("header mode = %q, must not stay pending", m.headerMode()) + } +} + func TestStaleModeResponseDoesNotClobberNewerRequest(t *testing.T) { conv := &fakeConv{recv: &fakeRecver{}, send: &fakeSender{}, mode: "default"} m := modeTestModel(t, conv) diff --git a/cmd/mecatui/ui/model.go b/cmd/mecatui/ui/model.go index 91c40ddea8..a26f58e2d2 100644 --- a/cmd/mecatui/ui/model.go +++ b/cmd/mecatui/ui/model.go @@ -294,6 +294,13 @@ type Deps struct { Workspace string Mode string Model string + // ModeServerDefault asks the server to choose the first session's permission + // mode: no --permission-mode or --mode was given, so the create + // carries an unspecified mode and the server's configured default applies + // (embedded: the operator's permissionMode: key; connect: the remote server's + // own default). The created session's mode is read back with GetSession, so the + // header never guesses it. Mode is then only the pre-session display fallback. + ModeServerDefault bool // Resume is a statically validated existing chat selected before Bubble Tea // starts. Its authoritative transcript is adopted without CreateSession; nil // preserves the new-session default. @@ -806,6 +813,12 @@ type Model struct { // immediate switch. activeMode string pendingMode string + // modeCursor is where the ModeSwitch cycle over every permission mode last + // landed, including a mode that needs a restart and so changed nothing. + modeCursor modeCursor + // modeRefused is the mode the server just refused. The next ModeSwitch + // continues the cycle after it, so a refused mode does not trap the cycle. + modeRefused string // fullColor is true on a truecolor terminal, derived from the tea.ColorProfileMsg // (msg.Profile == colorprofile.TrueColor) in the reducer. It gates the welcome @@ -1105,7 +1118,7 @@ func New(deps Deps) Model { // rejects the create, createSessionCmd's fallback leg retries on the default and // surfaces a loud warning (connectFallbackMsg) — connect still completes. createModelSelection: deps.InitialModel, - activeMode: client.ModeString(client.ModeFromString(deps.Mode)), + activeMode: initialActiveMode(deps), modelCatalog: modelCatalog{active: deps.InitialModel, globalDefault: deps.GlobalDefault}, // Seed the CLI-supplied seed prompt (-p/--prompt + --prompt-file) for // one-shot auto-submit on the FIRST session ready. diff --git a/cmd/mecatui/ui/models.go b/cmd/mecatui/ui/models.go index 6fd2b675e5..9736e126b9 100644 --- a/cmd/mecatui/ui/models.go +++ b/cmd/mecatui/ui/models.go @@ -125,7 +125,7 @@ func (m Model) restartOnModelCmd(oldID string, sel client.ModelSelection) tea.Cm if err != nil { return restartFailedMsg{err: err, model: modelSelLabel(sel)} } - return client.SessionReadyMsg{SessionID: id, Capabilities: caps, ResolvedModel: resolved, Mode: m.desiredMode()} + return client.SessionReadyMsg{SessionID: id, Capabilities: caps, ResolvedModel: resolved, Mode: createdSessionMode(deps, id, m.desiredMode())} } } diff --git a/cmd/mecatui/ui/permission_mode_vocabulary_test.go b/cmd/mecatui/ui/permission_mode_vocabulary_test.go new file mode 100644 index 0000000000..d32b780339 --- /dev/null +++ b/cmd/mecatui/ui/permission_mode_vocabulary_test.go @@ -0,0 +1,530 @@ +package ui + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + tea "charm.land/bubbletea/v2" + + "github.com/stacklok/mecatl/cmd/mecatui/client" + "github.com/stacklok/mecatl/cmd/mecatui/theme" +) + +// permissionModeTestModel is a live model bound to a session whose server reports posture. +func permissionModeTestModel(t *testing.T, conv *fakeConv, posture string, embedded bool) Model { + t.Helper() + m := newTestModelFromDeps(Deps{ + Session: conv, + Conv: conv, + Theme: theme.New("aztec", theme.AztecPalette()), + Mode: "default", + Embedded: embedded, + Ctx: context.Background(), + }) + return applyAll(m, + tea.WindowSizeMsg{Width: 140, Height: 30}, + client.SessionReadyMsg{SessionID: "sess-test-0001", Mode: "default", Capabilities: client.Capabilities{Posture: posture, SessionPermissionModes: true}}, + ) +} + +// statusMode is the session mode the header's status surface renders as "mode X". +func statusMode(m Model) string { + return m.statusLineInput(time.Now()).Session.Mode +} + +// pressModeSwitch presses the real ModeSwitch chord, feeds any resulting SetMode +// command back through Update as the Bubble Tea runtime would, and reports +// whether a command was issued. +func pressModeSwitch(t *testing.T, m Model) (Model, bool) { + t.Helper() + mm, cmd := m.Update(modeKey()) + m = mm.(Model) + if cmd == nil { + return m, false + } + return applyAll(m, cmd()), true +} + +// modeCycleStep is one ModeSwitch press: the mode the cycle cursor lands on, +// whether it applies a session half (issues SetMode), and phrases the footer +// status must carry. stays is set when the mode is unavailable (above the server +// ceiling): the confirmed mode the header must keep showing. +type modeCycleStep struct { + token string + setMode bool + stays string + status []string +} + +// visibleModeText is the footer status with wrapping collapsed so a phrase can +// be matched across a wrapped line. +func visibleModeText(m Model) string { + return strings.Join(strings.Fields(stripANSIstr(m.statusMsg)), " ") +} + +func runModeCycle(t *testing.T, m Model, steps []modeCycleStep) Model { + t.Helper() + for i, s := range steps { + var issued bool + m, issued = pressModeSwitch(t, m) + if issued != s.setMode { + t.Fatalf("step %d (%s): SetMode issued = %v, want %v", i, s.token, issued, s.setMode) + } + if got := m.currentModeToken(); got != s.token { + t.Fatalf("step %d: landed on %q, want %q", i, got, s.token) + } + wantHeader := s.token + if s.stays != "" { + wantHeader = s.stays + } + if got := statusMode(m); got != wantHeader { + t.Fatalf("step %d: header mode = %q, want %q", i, got, wantHeader) + } + text := visibleModeText(m) + if s.stays != "" { + want := "mode " + s.token + " unavailable, staying in " + s.stays + if !strings.Contains(text, want) { + t.Fatalf("step %d (%s): status %q missing %q", i, s.token, text, want) + } + } + for _, want := range s.status { + if !strings.Contains(text, want) { + t.Fatalf("step %d (%s): status %q missing %q", i, s.token, text, want) + } + } + } + return m +} + +// TestModeSwitchCyclesEveryMode pins that under strict the cycle lands on all +// five modes. The posture-raising ones (auto, yolo) change nothing and say what +// to configure and restart; the strict ones apply live. +func TestModeSwitchCyclesEveryMode(t *testing.T) { + conv := &fakeConv{recv: &fakeRecver{}, send: &fakeSender{}, mode: "default"} + m := permissionModeTestModel(t, conv, postureStrict, true) + m = runModeCycle(t, m, []modeCycleStep{ + {token: "plan", setMode: true, status: []string{"mode plan"}}, + {token: "accept-edits", setMode: true, status: []string{"mode accept-edits"}}, + {token: "auto", stays: "accept-edits", status: []string{"enforcing guardrails checker", "guardrails.model", "mecatui --permission-mode auto", "shift+tab next mode"}}, + {token: "yolo", stays: "accept-edits", status: []string{"mecatui --permission-mode yolo"}}, + {token: "default", setMode: true, status: []string{"mode default"}}, + {token: "plan", setMode: true}, + }) + want := []string{"plan", "accept-edits", "default", "plan"} + if got := conv.setModes(); strings.Join(got, ",") != strings.Join(want, ",") { + t.Fatalf("SetMode calls = %v, want %v", got, want) + } + if m.activeMode != "plan" { + t.Fatalf("active mode after cycle = %q, want plan", m.activeMode) + } +} + +// TestModeSwitchUnderTrustedPosture pins that a server running with the legacy +// trusted posture (reachable via deprecated --posture trusted) handles the +// 5-token vocabulary correctly: strict-posture tokens apply their session half, +// posture-raising tokens (auto, yolo) are unavailable. +// Since "trusted" is no longer a named token, the start position is the session +// mode itself ("default"), and cycling visits all five tokens. +func TestModeSwitchUnderTrustedPosture(t *testing.T) { + conv := &fakeConv{recv: &fakeRecver{}, send: &fakeSender{}, mode: "default"} + m := permissionModeTestModel(t, conv, postureTrusted, true) + // No vocabulary entry for posture=trusted; acceptedModeToken falls back to + // the session mode string. + if got := m.currentModeToken(); got != "default" { + t.Fatalf("start position = %q, want default (no trusted token in vocabulary)", got) + } + runModeCycle(t, m, []modeCycleStep{ + {token: "plan", setMode: true}, + {token: "accept-edits", setMode: true}, + // auto and yolo raise posture above trusted → unavailable (need restart). + {token: "auto", stays: "accept-edits", status: []string{"auto also needs an enforcing guardrails checker"}}, + {token: "yolo", stays: "accept-edits", status: []string{"mecatui --permission-mode yolo"}}, + // In the new architecture, switching to a lower-ceiling mode is fully effective: + // the session mode drives allow-all, so "posture stays" no longer applies. + {token: "default", setMode: true, status: []string{"mode default"}}, + {token: "plan", setMode: true}, + }) + want := []string{"plan", "accept-edits", "default", "plan"} + if got := conv.setModes(); strings.Join(got, ",") != strings.Join(want, ",") { + t.Fatalf("SetMode calls = %v, want %v", got, want) + } +} + +// TestModeSwitchConnectNamesServerRestart pins that under `mecatui connect` a +// posture-raising mode points at the remote server's operator, not a relaunch. +func TestModeSwitchConnectNamesServerRestart(t *testing.T) { + conv := &fakeConv{recv: &fakeRecver{}, send: &fakeSender{}, mode: "accept-edits"} + m := permissionModeTestModel(t, conv, postureStrict, false) + m.activeMode = "accept-edits" + m = runModeCycle(t, m, []modeCycleStep{ + {token: "auto", stays: "accept-edits", status: []string{"remote server's operator must restart mecated with --permission-mode auto and an enforcing guardrails checker"}}, + {token: "yolo", stays: "accept-edits", status: []string{"remote server's operator must restart mecated with --permission-mode yolo"}}, + }) + if text := visibleModeText(m); strings.Contains(text, "mecatui --permission-mode") { + t.Fatalf("connect hint offers a local relaunch: %q", text) + } + if got := conv.setModes(); len(got) != 0 { + t.Fatalf("SetMode calls = %v, want none", got) + } +} + +// TestUnavailableModeKeepsComposerUsable pins that landing on a mode above the +// server ceiling freezes nothing: at idle or mid-run, typing and paste still edit +// the draft, the input area still shows it, enter still reaches the send/queue +// path, the header keeps the confirmed mode, and the next press moves on. +func TestUnavailableModeKeepsComposerUsable(t *testing.T) { + for _, phase := range []phase{phaseIdle, phaseRunning} { + t.Run(fmt.Sprint(phase), func(t *testing.T) { + conv := &fakeConv{recv: &fakeRecver{}, send: &fakeSender{}, mode: "default"} + m := permissionModeTestModel(t, conv, postureStrict, true) + m.prompt.Rewrite("keep this draft") + m = runModeCycle(t, m, []modeCycleStep{ + {token: "plan", setMode: true}, + {token: "accept-edits", setMode: true}, + {token: "auto", stays: "accept-edits"}, + }) + m.phase = phase + + m = applyAll(m, tea.KeyPressMsg{Code: 'x', Text: "x"}, tea.PasteMsg{Content: " pasted"}) + if got := m.prompt.Value(); got != "keep this draftx pasted" { + t.Fatalf("prompt = %q, want typing and paste to reach the composer", got) + } + if !strings.Contains(stripANSIstr(m.renderInput()), "keep this draftx pasted") { + t.Fatalf("input area does not show the draft: %q", stripANSIstr(m.renderInput())) + } + if got := statusMode(m); got != "accept-edits" { + t.Fatalf("header mode = %q, want the confirmed accept-edits", got) + } + + mm, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyEnter}) + m = mm.(Model) + if m.prompt.Value() != "" || (cmd == nil && len(m.queued) == 0) { + t.Fatalf("enter did not send or queue the draft: prompt=%q queued=%v cmd=%v", m.prompt.Value(), m.queued, cmd != nil) + } + + m = runModeCycle(t, m, []modeCycleStep{{token: "yolo", stays: "accept-edits"}}) + if got := conv.setModes(); strings.Join(got, ",") != "plan,accept-edits" { + t.Fatalf("SetMode calls = %v, want only the two applied modes", got) + } + }) + } +} + +// TestUnavailableModeDoesNotHoldQueuedPrompt pins that a run ending while the +// cycle sits on an unavailable mode still auto-sends the queued follow-up. +func TestUnavailableModeDoesNotHoldQueuedPrompt(t *testing.T) { + conv := &fakeConv{recv: &fakeRecver{}, send: &fakeSender{}, mode: "default"} + m := permissionModeTestModel(t, conv, postureStrict, true) + m = runModeCycle(t, m, []modeCycleStep{ + {token: "plan", setMode: true}, + {token: "accept-edits", setMode: true}, + {token: "auto", stays: "accept-edits"}, + }) + m.queued = []string{"queued follow-up"} + mm, cmd := m.drainQueue("end_turn") + m = mm.(Model) + if cmd == nil || m.queuePaused != "" || m.phase != phaseRunning { + t.Fatalf("drain: cmd=%v paused=%q phase=%v, want the follow-up sent", cmd != nil, m.queuePaused, m.phase) + } +} + +// TestUnavailableModeHintNamesCheckerOnlyForAuto pins that only auto's +// unavailable hint mentions the guardrails checker; yolo needs only the restart. +func TestUnavailableModeHintNamesCheckerOnlyForAuto(t *testing.T) { + for _, embedded := range []bool{true, false} { + t.Run(fmt.Sprintf("embedded=%v", embedded), func(t *testing.T) { + conv := &fakeConv{recv: &fakeRecver{}, send: &fakeSender{}, mode: "accept-edits"} + m := permissionModeTestModel(t, conv, postureStrict, embedded) + m.activeMode = "accept-edits" + m, _ = pressModeSwitch(t, m) + if auto := visibleModeText(m); !strings.Contains(auto, "mode auto unavailable") || !strings.Contains(auto, "guardrails checker") { + t.Fatalf("auto hint = %q, want it to name the guardrails checker", auto) + } + m, _ = pressModeSwitch(t, m) + yolo := visibleModeText(m) + if !strings.Contains(yolo, "mode yolo unavailable") || !strings.Contains(yolo, "--permission-mode yolo") { + t.Fatalf("yolo hint = %q, want the restart with --permission-mode yolo", yolo) + } + if strings.Contains(yolo, "checker") || strings.Contains(yolo, "guardrails") { + t.Fatalf("yolo hint = %q, must not mention a guardrails checker", yolo) + } + }) + } +} + +// TestHelpOverlayShowsVocabularyAndScopeSplit pins that every token is +// listed with its posture half marked process-wide and its session half marked as +// the new-session default, with the exact invocation and the statement that +// cycling reaches every mode but a posture-raising one needs a restart. +func TestHelpOverlayShowsVocabularyAndScopeSplit(t *testing.T) { + conv := &fakeConv{recv: &fakeRecver{}, send: &fakeSender{}, mode: "default"} + m := permissionModeTestModel(t, conv, postureStrict, true) + body := stripANSIstr(m.helpBody()) + + // The token table, written out independently of PermissionModeVocabulary. + // auto and yolo sessions start in their own named modes, not "default". + want := []struct{ token, posture, session string }{ + {"plan", "strict", "plan"}, + {"default", "strict", "default"}, + {"accept-edits", "strict", "accept-edits"}, + {"auto", "auto", "auto"}, + {"yolo", "yolo", "yolo"}, + } + for _, w := range want { + row := "posture " + w.posture + " (process-wide) · new sessions start in " + w.session + found := false + for _, line := range strings.Split(body, "\n") { + fields := strings.Fields(strings.Trim(line, "┃ ")) + if len(fields) > 0 && fields[0] == w.token && strings.Contains(line, row) { + found = true + break + } + } + if !found { + t.Errorf("help overlay has no row for token %q with %q", w.token, row) + } + } + for _, phrase := range []string{ + "--permission-mode", + "The posture half applies to the whole server and is fixed when it starts.", + "The session half is only the default for new sessions.", + m.helpKeyMarkings().modeSwitch + " cycles every mode. A mode that raises the posture needs a restart", + "and auto also an enforcing guardrails checker", + "keeps the current mode and your draft", + "mecatui --permission-mode ", + } { + if !strings.Contains(body, phrase) { + t.Errorf("help overlay missing %q", phrase) + } + } + for _, stale := range []string{"auto or yolo", "blocked", "holds prompts", "trusted-accept-edits"} { + if strings.Contains(body, stale) { + t.Errorf("help overlay carries stale %q", stale) + } + } +} + +// TestHeaderShowsModeNotPosture pins that the header shows the session mode and +// no posture badge at any tier: the posture is not a header axis, only the +// mode the cycle lands on and (separately) project trust. +func TestHeaderShowsModeNotPosture(t *testing.T) { + for _, posture := range []string{postureStrict, postureTrusted, postureAuto, postureYolo} { + t.Run(posture, func(t *testing.T) { + conv := &fakeConv{recv: &fakeRecver{}, send: &fakeSender{}, mode: "default"} + m := permissionModeTestModel(t, conv, posture, true) + header := stripANSIstr(m.renderHeader()) + if got := statusMode(m); got != "default" { + t.Errorf("status mode = %q, want default", got) + } + if badge, _ := m.postureBadgeRender(); badge != "" { + t.Errorf("posture %s rendered a badge without reported trust: %q", posture, badge) + } + for _, gone := range []string{"YOLO", "⚠ auto", "trusted"} { + if strings.Contains(header, gone) { + t.Errorf("posture %s: header carries posture badge %q: %q", posture, gone, header) + } + } + }) + } +} + +// TestHeaderKeepsPostureAcrossModeCycle pins that under auto, cycling through +// yolo (unavailable: above the auto ceiling), then through lower modes keeps the +// trust badge on every frame, including the pending frames before the server +// confirms each switch. +// +// A session on an auto-posture server starts in "auto" mode. The cycle order +// from "auto" is: yolo (unavailable) → default → plan → accept-edits → auto → … +// Yolo is the only mode that needs a restart; every other landing sends a +// SetMode and shows "pending" until the server confirms. +func TestHeaderKeepsPostureAcrossModeCycle(t *testing.T) { + // Session starts in "auto" mode — the natural default on an auto-posture server. + conv := &fakeConv{recv: &fakeRecver{}, send: &fakeSender{}, mode: "auto"} + m := newTestModelFromDeps(Deps{ + Session: conv, + Conv: conv, + Theme: theme.New("aztec", theme.AztecPalette()), + Mode: "auto", + Embedded: true, + Ctx: context.Background(), + }) + m = applyAll(m, + tea.WindowSizeMsg{Width: 140, Height: 30}, + client.SessionReadyMsg{SessionID: "sess-auto-cycle", Mode: "auto", Capabilities: client.Capabilities{Posture: postureAuto, ProjectTrust: client.ProjectTrusted, SessionPermissionModes: true}}, + ) + + assertAuto := func(step, wantMode string) { + t.Helper() + header := stripANSIstr(m.renderHeader()) + if !strings.Contains(header, client.ProjectTrusted) { + t.Fatalf("%s: header lost the trust badge: %q", step, header) + } + if got := statusMode(m); got != wantMode { + t.Fatalf("%s: status mode = %q, want %q", step, got, wantMode) + } + } + assertAuto("start", "auto") + + // yolo is above the auto ceiling → unavailable, no server round trip, and + // the header keeps the confirmed auto. + mm, cmd := m.Update(modeKey()) + m = mm.(Model) + if cmd != nil { + t.Fatalf("landing on yolo issued a command: should be unavailable, not switched") + } + assertAuto("landed yolo", "auto") + + // Switching to modes below auto (default, plan, accept-edits) all require + // a SetMode round trip: the session is in "auto", switching to anything lower + // is a genuine change. + for _, next := range []string{"default", "plan", "accept-edits"} { + mm, cmd := m.Update(modeKey()) + m = mm.(Model) + if cmd == nil { + t.Fatalf("switch to %s issued no command", next) + } + assertAuto("pending "+next, next+" pending") + m = applyAll(m, cmd()) + assertAuto("confirmed "+next, next) + } + + // Cycling back to auto from accept-edits: pendingMode="auto" != activeMode="accept-edits" + // → genuine transition, shows "auto pending" until server confirms. + mm, cmd = m.Update(modeKey()) + m = mm.(Model) + if cmd == nil { + t.Fatalf("cycling back to auto issued no command") + } + assertAuto("pending auto", "auto pending") + m = applyAll(m, cmd()) + assertAuto("confirmed auto", "auto") +} + +// TestHelpOverlayNamesWhoseRestart pins that the embedded server's +// help gives the mecatui relaunch invocation; under `mecatui connect` it names +// the server operator's mecated restart and offers no local invocation. +func TestHelpOverlayNamesWhoseRestart(t *testing.T) { + render := func(embedded bool) string { + conv := &fakeConv{recv: &fakeRecver{}, send: &fakeSender{}, mode: "default"} + m := permissionModeTestModel(t, conv, postureAuto, embedded) + return stripANSIstr(m.helpBody()) + } + embedded := render(true) + if !strings.Contains(embedded, "quit and relaunch: mecatui --permission-mode ") { + t.Errorf("embedded help missing the mecatui relaunch invocation:\n%s", embedded) + } + if strings.Contains(embedded, "server operator") { + t.Errorf("embedded help must not defer to a server operator:\n%s", embedded) + } + connect := render(false) + if !strings.Contains(connect, "the server operator must change mecated's configuration and restart it") { + t.Errorf("connect help missing the server-operator guidance:\n%s", connect) + } + if strings.Contains(connect, "mecatui --permission-mode") || strings.Contains(connect, "relaunch") { + t.Errorf("connect help must offer no local invocation:\n%s", connect) + } +} + +// TestModeServerDefaultRequestsUnspecifiedAndReadsBack pins the session +// half under ModeServerDefault: the first create requests no mode, so the server's +// default applies, and the header shows the mode the server reports rather than +// guessing "default". +func TestModeServerDefaultRequestsUnspecifiedAndReadsBack(t *testing.T) { + // The fake keeps its current mode when a create requests none, so "plan" here + // stands for a server whose configured default is plan. + conv := &fakeConv{recv: &fakeRecver{}, send: &fakeSender{}, mode: "plan"} + m := newTestModelFromDeps(Deps{ + Session: conv, + Conv: conv, + Theme: theme.New("aztec", theme.AztecPalette()), + ModeServerDefault: true, + Ctx: context.Background(), + }) + if got := m.desiredMode(); got != "" { + t.Fatalf("desiredMode before any session = %q, want empty (server default)", got) + } + msg := m.createSessionCmd()() + ready, ok := msg.(client.SessionReadyMsg) + if !ok { + t.Fatalf("create returned %T, want SessionReadyMsg", msg) + } + if ready.Mode != "plan" { + t.Fatalf("ready mode = %q, want the server-reported plan", ready.Mode) + } + if conv.mode != "plan" { + t.Fatalf("create overrode the server default with %q", conv.mode) + } + m = applyAll(m, tea.WindowSizeMsg{Width: 120, Height: 30}, ready) + if got := statusMode(m); got != "plan" { + t.Fatalf("status mode = %q, want plan", got) + } + // Once known, later creates carry the current mode explicitly, as before. + if got := m.desiredMode(); got != "plan" { + t.Fatalf("desiredMode after ready = %q, want plan", got) + } +} + +// legacyYoloModel is a session on an API-major-1 server that predates the +// session_permission_modes feature but advertises a yolo posture: its allow-all +// is process-wide and no session mode removes it. +func legacyYoloModel(t *testing.T, conv *fakeConv) Model { + t.Helper() + m := newTestModelFromDeps(Deps{ + Session: conv, + Conv: conv, + Theme: theme.New("aztec", theme.AztecPalette()), + Mode: "default", + Ctx: context.Background(), + }) + return applyAll(m, + tea.WindowSizeMsg{Width: 140, Height: 30}, + client.SessionReadyMsg{SessionID: "sess-legacy-yolo", Mode: "default", Capabilities: client.Capabilities{Posture: postureYolo}}, + ) +} + +// TestLegacyYoloServerNeverPresentsSessionModeAsEffective pins the legacy +// presentation: against an older yolo server the header names the server's +// posture next to every session mode, the cycle offers only the session modes +// that server understands (never auto or yolo), and landing on one says the +// server's yolo still applies. +func TestLegacyYoloServerNeverPresentsSessionModeAsEffective(t *testing.T) { + conv := &fakeConv{recv: &fakeRecver{}, send: &fakeSender{}, mode: "default"} + m := legacyYoloModel(t, conv) + if got := statusMode(m); got != "default (server yolo)" { + t.Fatalf("start: status mode = %q, want the server posture named", got) + } + want := []string{"plan", "accept-edits", "default", "plan"} + for i, mode := range want { + var issued bool + m, issued = pressModeSwitch(t, m) + if !issued { + t.Fatalf("step %d: no SetMode issued for %s", i, mode) + } + if got := statusMode(m); got != mode+" (server yolo)" { + t.Fatalf("step %d: status mode = %q, want %q", i, got, mode+" (server yolo)") + } + if status := visibleModeText(m); !strings.Contains(status, "yolo posture still applies") { + t.Fatalf("step %d: status = %q, want the posture caveat", i, status) + } + } + for _, sent := range conv.setModes() { + if sent == "auto" || sent == "yolo" { + t.Fatalf("SetMode calls = %v: a legacy server has no %s session mode", conv.setModes(), sent) + } + } +} + +// TestLegacyStrictServerShowsPlainSessionMode pins that the legacy label is +// only added for an allow-all posture: a strict older server's session mode is +// its effective mode. +func TestLegacyStrictServerShowsPlainSessionMode(t *testing.T) { + conv := &fakeConv{recv: &fakeRecver{}, send: &fakeSender{}, mode: "default"} + m := legacyYoloModel(t, conv) + m.caps.Posture = postureStrict + if got := statusMode(m); got != "default" { + t.Fatalf("status mode = %q, want default", got) + } +} diff --git a/cmd/mecatui/ui/posture_badge_test.go b/cmd/mecatui/ui/posture_badge_test.go index 9e3d086925..29aadf2f13 100644 --- a/cmd/mecatui/ui/posture_badge_test.go +++ b/cmd/mecatui/ui/posture_badge_test.go @@ -11,240 +11,84 @@ import ( "github.com/stacklok/mecatl/cmd/mecatui/theme" ) -// TestPostureBadgeShownForAutoYolo asserts the header renders the auto/yolo chrome -// badge when the server reports an allow-all posture, and NO badge for strict/trusted -// (and an empty/older-server posture) — the goldens-stability guarantee. Under -// MECATUI_NO_EMOJI the yolo badge is the clean " YOLO " pill with NO lightning bolt and -// NO VS16; auto is "⚠ auto". The badge is sourced from caps.Posture (SessionReadyMsg), -// NOT the per-session mode segment. -func TestPostureBadgeShownForAutoYolo(t *testing.T) { - cases := []struct { - posture string - wantBadge string // "" = no badge - }{ - {"", ""}, - {"strict", ""}, - {"trusted", ""}, - {"auto", "⚠ auto"}, - {"yolo", "YOLO"}, - } - for _, tc := range cases { - m, _, _ := newTestModel(t, theme.New("aztec", theme.AztecPalette())) - m = applyAll(m, tea.WindowSizeMsg{Width: 100, Height: 30}, - client.SessionReadyMsg{SessionID: "sess-test-0001", Capabilities: client.Capabilities{Posture: tc.posture}}) - - raw := m.renderHeader() - header := stripANSIstr(raw) - if tc.wantBadge == "" { - // No badge for strict/trusted/unset: no posture glyph or word may appear. - if strings.Contains(header, "⚠") || strings.Contains(header, "⚡") || strings.Contains(header, "YOLO") { - t.Errorf("posture %q: header must show NO badge, got %q", tc.posture, header) - } - continue - } - if !strings.Contains(header, tc.wantBadge) { - t.Errorf("posture %q: header missing %q badge, got %q", tc.posture, tc.wantBadge, header) - } - if tc.posture == "yolo" { - // No-emoji yolo: a CLEAN pill — no bolt, no VS16. - if strings.Contains(header, "⚡") { - t.Errorf("no-emoji yolo badge must carry NO lightning bolt, got %q", header) - } - if strings.Contains(raw, vs16) { - t.Errorf("no-emoji yolo badge must carry NO VS16, got %q", header) - } - } - } -} - -// TestPostureBadgeCarriesWarningStyle asserts the badge is rendered in the CORRECT -// per-tier style — auto in the THEME's "warning" style (inline coloured text), and the -// yolo pill in the "dangerPill" style (the filled alarm-red chip) — and NEVER the muted -// style the benign scroll/changed-files cues use. The one persistent in-session danger -// cue must READ as danger. It checks the RAW (un-stripped) header for the exact styled -// substring (so a regression to the empty-fallback or muted style trips it) and confirms -// the not-muted guard. -func TestPostureBadgeCarriesWarningStyle(t *testing.T) { +// TestTrustBadgeShowsProjectTrust asserts the header's right-aligned badge shows +// the server-reported project trust, independent of posture and permission mode: +// "trusted" in the warning style, "untrusted" muted, and no badge from an older +// server that does not report trust. No posture tier renders a badge of its own. +func TestTrustBadgeShowsProjectTrust(t *testing.T) { th := theme.New("aztec", theme.AztecPalette()) cases := []struct { + trust string posture string - text string // the styled segment's content - slot string // the style slot it must be rendered in + slot string // "" = no badge }{ - {"auto", autoBadgeText, "warning"}, - {"yolo", yoloPillText, "dangerPill"}, + {"", postureYolo, ""}, + {client.ProjectUntrusted, postureStrict, "muted"}, + {client.ProjectUntrusted, postureYolo, "muted"}, + {client.ProjectTrusted, postureStrict, "warning"}, + {client.ProjectTrusted, postureAuto, "warning"}, } for _, tc := range cases { m, _, _ := newTestModel(t, th) m = applyAll(m, tea.WindowSizeMsg{Width: 100, Height: 30}, - client.SessionReadyMsg{SessionID: "sess-test-0001", Capabilities: client.Capabilities{Posture: tc.posture}}) - + client.SessionReadyMsg{SessionID: "sess-test-0001", Capabilities: client.Capabilities{Posture: tc.posture, ProjectTrust: tc.trust}}) raw := m.renderHeader() - wantStyled := th.Style(tc.slot).Render(tc.text) - // The styled segment MUST be non-empty (guards against an empty-fallback slot). - if stripANSIstr(wantStyled) == "" { - t.Fatalf("posture %q: the %q style render is empty — the slot is missing", tc.posture, tc.slot) - } - if !strings.Contains(raw, wantStyled) { - t.Errorf("posture %q: header must render the badge in the %q style; want substring %q in %q", tc.posture, tc.slot, wantStyled, raw) + header := stripANSIstr(raw) + for _, gone := range []string{"YOLO", "⚠ auto", "⚡"} { + if strings.Contains(header, gone) { + t.Errorf("trust %q posture %q: header must not carry a posture badge %q: %q", tc.trust, tc.posture, gone, header) + } } - // Guard against a regression to the muted style (the benign-cue weight). - mutedBadge := th.Style("muted").Render(tc.text) - if mutedBadge != wantStyled && strings.Contains(raw, mutedBadge) { - t.Errorf("posture %q: badge must NOT be muted-styled (it is a danger cue); found muted render %q", tc.posture, mutedBadge) + if tc.slot == "" { + if strings.Contains(header, "trusted") { + t.Errorf("unreported trust must render no badge: %q", header) + } + continue } - } -} - -// TestPostureBadgeYoloIsRedPill asserts the yolo pill is the FILLED, FIXED-COLOUR alarm -// chip — distinct from the auto badge's inline warning text and from the theme's Error -// colour — the loudest posture must read loudest and identically across themes. It pins -// the dangerPill's theme-independent alarm-red background + near-white foreground. -func TestPostureBadgeYoloIsRedPill(t *testing.T) { - th := theme.New("aztec", theme.AztecPalette()) - m, _, _ := newTestModel(t, th) - m = applyAll(m, tea.WindowSizeMsg{Width: 100, Height: 30}, - client.SessionReadyMsg{SessionID: "sess-test-0001", Capabilities: client.Capabilities{Posture: "yolo"}}) - raw := m.renderHeader() - - pill := th.Style("dangerPill").Render(yoloPillText) - warn := th.Style("warning").Render(yoloPillText) - if pill == warn { - t.Fatal("dangerPill and warning must render differently (the pill is filled, the warning is inline)") - } - if !strings.Contains(raw, pill) { - t.Errorf("yolo header must render the danger PILL; want %q in %q", pill, raw) - } - // The pill carries the FIXED alarm-red background + near-white foreground RGB SGRs, - // NOT the theme Error / Bg pair — danger is danger across every theme. - for _, want := range []string{"\x1b[", "224;49;49", "245;245;245"} { // #E03131 / #F5F5F5 as RGB - if !strings.Contains(pill, want) { - t.Errorf("dangerPill render missing fixed alarm colour %q: %q", want, pill) + if want := th.Style(tc.slot).Render(tc.trust); !strings.Contains(raw, want) { + t.Errorf("trust %q: header must render %q in the %q style; got %q", tc.trust, tc.trust, tc.slot, raw) } } } -// TestPostureBadgeIsNotModeSegment guards that the badge is DISTINCT from the per-session -// `mode` segment: with posture auto and a default permission mode, the header carries -// the auto badge but no "mode auto" text (the mode segment renders the PermissionMode, -// here unset, never the posture). -func TestPostureBadgeIsNotModeSegment(t *testing.T) { +// TestTrustBadgeIsNotModeSegment guards that the badge is DISTINCT from the +// per-session mode segment: on an auto server the header carries the trust badge +// and the mode segment carries the session mode, never the posture. +func TestTrustBadgeIsNotModeSegment(t *testing.T) { m, _, _ := newTestModel(t, theme.New("aztec", theme.AztecPalette())) m = applyAll(m, tea.WindowSizeMsg{Width: 120, Height: 30}, - client.SessionReadyMsg{SessionID: "sess-test-0001", Capabilities: client.Capabilities{Posture: "auto"}}) + client.SessionReadyMsg{SessionID: "sess-test-0001", Capabilities: client.Capabilities{Posture: postureAuto, ProjectTrust: client.ProjectTrusted}}) header := stripANSIstr(m.renderHeader()) - if !strings.Contains(header, "⚠ auto") { - t.Fatalf("expected the auto posture badge; got %q", header) + if !strings.Contains(header, "trusted") { + t.Fatalf("expected the trust badge; got %q", header) } if strings.Contains(header, "mode auto") { t.Fatalf("posture must not leak into the mode segment; got %q", header) } } -// vs16 is the U+FE0F variation selector — the byte whose PRESENCE distinguishes the -// emoji-presentation yolo badge ("⚡️ YOLO") from the width-stable text one ("⚡ YOLO"). -const vs16 = "️" - -// TestPostureBadgeEmojiVariant pins the yolo badge variants: an emoji-capable -// model renders a PLAIN ⚡️ bolt (with VS16) OUTSIDE the danger pill; an incapable -// model renders just the clean " YOLO " pill. The capability is injected before -// New because detection is evaluated once at construction. -func TestPostureBadgeEmojiVariant(t *testing.T) { - cases := []struct { - name string - emojiOK bool - wantBolt bool - }{ - {"emoji forced", true, true}, - {"emoji off", false, false}, - } - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - th := theme.New("aztec", theme.AztecPalette()) - m, _, _ := newTestModel(t, th, func(deps *Deps) { - deps.emojiCapable = func() bool { return tc.emojiOK } - }) - m = applyAll(m, tea.WindowSizeMsg{Width: 100, Height: 30}, - client.SessionReadyMsg{SessionID: "sess-test-0001", Capabilities: client.Capabilities{Posture: "yolo"}}) - - raw := m.renderHeader() - header := stripANSIstr(raw) - - // The clean pill is present (dangerPill-styled " YOLO ") in BOTH variants. - pill := th.Style("dangerPill").Render(yoloPillText) - if !strings.Contains(raw, pill) { - t.Errorf("yolo header must render the clean danger pill %q in %q", pill, raw) - } - // The pill itself must NOT contain the bolt — the emoji is decoration OUTSIDE. - if strings.Contains(pill, "⚡") { - t.Fatal("the dangerPill render must not contain the lightning bolt") - } - if !strings.Contains(header, "YOLO") { - t.Errorf("yolo header missing YOLO, got %q", header) - } - - gotBolt := strings.Contains(raw, "⚡") - gotVS16 := strings.Contains(raw, vs16) - if gotBolt != tc.wantBolt || gotVS16 != tc.wantBolt { - t.Errorf("%s: bolt present=%v VS16 present=%v, want both %v", tc.name, gotBolt, gotVS16, tc.wantBolt) - } - if tc.wantBolt { - // The PLAIN bolt prefix appears IMMEDIATELY BEFORE the styled pill — i.e. - // the raw header contains "" with the bolt NOT - // inside the pill's SGR run. - if !strings.Contains(raw, yoloBoltPrefix+pill) { - t.Errorf("emoji yolo: plain bolt prefix must immediately precede the pill; want %q before pill in %q", yoloBoltPrefix, raw) - } - } - }) - } -} - -// TestYoloBadgeWithTailFitsOneRow is the fitHeader pill-width-math guard (the one -// genuinely tricky arithmetic): with posture=yolo AND a non-empty right-aligned tail -// (a changed-files cue), at a band of narrow-but-fitting widths, appending the YOLO -// danger pill beside the tail must NOT add a header row beyond what the identity line -// alone already produces, and must never exceed the terminal width. The baseline is the -// SAME model at strict posture (no badge): fitHeader only appends the indicator when it -// FITS within the gap, so a correctly-compensated pill leaves the row count identical to -// the no-badge baseline (it never forces a wrap) and the width within w. This isolates -// the pill arithmetic from the pre-existing identity-line wrap (which is width-driven and -// independent of the badge). Both glyph variants are covered because ⚡️ (width-2) and ⚡ -// (text, width-1) make the pill — and thus the +2-padded gap math — different widths. -func TestYoloBadgeWithTailFitsOneRow(t *testing.T) { - cases := []struct { - name string - emojiOK bool - }{ - {"text glyph", false}, - {"emoji glyph", true}, - } - mkHeader := func(t *testing.T, th theme.Theme, posture string, w int, emojiOK bool) string { - m, _, _ := newTestModel(t, th, func(deps *Deps) { - deps.emojiCapable = func() bool { return emojiOK } - }) +// TestTrustBadgeWithTailFitsOneRow is the fitHeader width-math guard: with the +// widest badge ("untrusted") AND a right-aligned changed-files tail, the header +// must not add a row beyond the no-badge baseline nor exceed the terminal width. +func TestTrustBadgeWithTailFitsOneRow(t *testing.T) { + th := theme.New("aztec", theme.AztecPalette()) + mkHeader := func(trust string, w int) string { + m, _, _ := newTestModel(t, th) m = applyAll(m, tea.WindowSizeMsg{Width: w, Height: 30}, - client.SessionReadyMsg{SessionID: "sess-test-0001", Capabilities: client.Capabilities{Posture: posture}}) - // A changed-files cue gives a non-empty right-aligned tail beside the badge. + client.SessionReadyMsg{SessionID: "sess-test-0001", Capabilities: client.Capabilities{ProjectTrust: trust}}) m.conv.recordFileChange("a.go") m.conv.recordFileChange("b.go") return m.renderHeader() } - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - th := theme.New("aztec", theme.AztecPalette()) - - for w := 60; w <= 120; w += 5 { - baseRows := lipgloss.Height(mkHeader(t, th, "strict", w, tc.emojiOK)) // no badge - header := mkHeader(t, th, "yolo", w, tc.emojiOK) // pill + tail - if rows := lipgloss.Height(header); rows != baseRows { - t.Errorf("%s width %d: yolo header is %d rows, want %d (the no-badge baseline) — the pill must not force an extra wrap", tc.name, w, rows, baseRows) - } - if gotW := lipgloss.Width(header); gotW > w { - t.Errorf("%s width %d: yolo header rendered %d cols, want ≤ %d (pill+tail must not overflow)", tc.name, w, gotW, w) - } - } - }) + for w := 60; w <= 120; w += 5 { + baseRows := lipgloss.Height(mkHeader("", w)) + header := mkHeader(client.ProjectUntrusted, w) + if rows := lipgloss.Height(header); rows != baseRows { + t.Errorf("width %d: header is %d rows, want %d (the no-badge baseline)", w, rows, baseRows) + } + if gotW := lipgloss.Width(header); gotW > w { + t.Errorf("width %d: header rendered %d cols, want ≤ %d", w, gotW, w) + } } } @@ -256,13 +100,15 @@ func TestPostureSummary(t *testing.T) { posture string want []string // substrings that MUST be present }{ - {"strict", []string{"posture strict", "allow-all off", "child $()/heredoc auto-run (injection-defense off) off", "project-trust off"}}, - {"trusted", []string{"posture trusted", "allow-all off", "project-trust on"}}, - {"auto", []string{"posture auto", "allow-all on", "main $()/heredoc auto-run on", "child $()/heredoc auto-run (injection-defense off) off", "project-trust on"}}, - {"yolo", []string{"posture yolo", "allow-all on", "child $()/heredoc auto-run (injection-defense off) on", "project-trust on"}}, + // allow-all is now session-mode-driven (not posture-driven), so the summary + // names the ceiling and trust, not an always-on allow-all flag. + {"strict", []string{"posture strict", "ceiling default/plan/accept-edits", "project-trust off", "allow-all is mode-driven"}}, + {"trusted", []string{"posture trusted", "project-trust on"}}, + {"auto", []string{"posture auto", "ceiling up to and including auto", "child $()/heredoc auto-run when in yolo mode (injection-defense off) off", "project-trust on"}}, + {"yolo", []string{"posture yolo", "ceiling all modes including yolo", "child $()/heredoc auto-run when in yolo mode (injection-defense off) on", "project-trust on"}}, } for _, tc := range cases { - got := postureSummary(tc.posture) + got := postureSummary(tc.posture, "", "") for _, sub := range tc.want { if !strings.Contains(got, sub) { t.Errorf("postureSummary(%q) missing %q; got %q", tc.posture, sub, got) @@ -270,3 +116,38 @@ func TestPostureSummary(t *testing.T) { } } } + +// TestPostureShowsSubagentAskReviewer pins that /posture names the +// server-reported subagent ask reviewer state, says nothing about it when an +// older server omits it, and never echoes an unrecognized value raw. +func TestPostureShowsSubagentAskReviewer(t *testing.T) { + cases := []struct { + reviewer string + want string // "" means the reviewer must not be mentioned + }{ + {"", ""}, + {"on", "; subagent ask reviewer on (a model may approve subagent permission requests no human can answer)"}, + {"off", "; subagent ask reviewer off"}, + {"inert", "; subagent ask reviewer inert (configured, but you answer subagent permission requests)"}, + {"\x1b[31mfuture", "; subagent ask reviewer unknown"}, + } + for _, tc := range cases { + m, _, _ := newTestModel(t, theme.New("aztec", theme.AztecPalette())) + m = applyAll(m, tea.WindowSizeMsg{Width: 400, Height: 30}, + client.SessionReadyMsg{SessionID: "sess-test-0001", Capabilities: client.Capabilities{Posture: postureAuto, SubagentAskReviewer: tc.reviewer}}) + next, _ := m.runPosture() + got := stripANSIstr(next.(Model).statusMsg) + if tc.want == "" { + if strings.Contains(got, "subagent ask reviewer") { + t.Errorf("reviewer %q: /posture must not mention the reviewer; got %q", tc.reviewer, got) + } + continue + } + if !strings.Contains(got, tc.want) { + t.Errorf("reviewer %q: /posture missing %q; got %q", tc.reviewer, tc.want, got) + } + if strings.Contains(got, "future") { + t.Errorf("reviewer %q: unrecognized value echoed raw; got %q", tc.reviewer, got) + } + } +} diff --git a/cmd/mecatui/ui/schedule.go b/cmd/mecatui/ui/schedule.go index b3a4342feb..0f15e63a0a 100644 --- a/cmd/mecatui/ui/schedule.go +++ b/cmd/mecatui/ui/schedule.go @@ -68,9 +68,10 @@ type scheduleState struct { // keeps it SIMPLE — name, prompt, trigger (cron OR NL), mutating. The trigger // field accepts EITHER a raw cron expression OR a natural-language phrase; on // submit, schedparse.Compile is tried first (compile to cron or one-shot), and -// only on no-match is the value treated verbatim as raw cron. Mode defaults to -// plan for non-mutating (the server enforces the mode↔mutating invariant); -// singleton defaults true. focusIdx is the cursor over the fields. +// only on no-match is the value treated verbatim as raw cron. The form has no +// mode field: a non-mutating schedule is sent as plan (the server requires it), +// and a mutating one leaves mode unset so its fires use the server's default +// session mode. Singleton defaults true. focusIdx is the cursor over the fields. type scheduleForm struct { name textinput.Model prompt textinput.Model @@ -384,6 +385,9 @@ func (m Model) submitScheduleCreate() (tea.Model, tea.Cmd, bool) { Singleton: true, Timezone: "UTC", } + if !f.mutating { + spec.Mode = "plan" + } // Return to the panel; the ScheduleMsg reducer appends the new row. m.schedule.view = schedulePanel m.schedule.form = scheduleForm{} diff --git a/cmd/mecatui/ui/schedule_test.go b/cmd/mecatui/ui/schedule_test.go index e392fc2a22..f89824de57 100644 --- a/cmd/mecatui/ui/schedule_test.go +++ b/cmd/mecatui/ui/schedule_test.go @@ -37,6 +37,7 @@ type fakeScheduleLister struct { lastFireName string lastFiresName string lastGetname string + lastCreateSpec client.ScheduleSpec } func (f *fakeScheduleLister) ListSchedules(_ context.Context) ([]client.Schedule, error) { @@ -56,8 +57,9 @@ func (f *fakeScheduleLister) GetSchedule(_ context.Context, name string) (client return f.sched, nil } -func (f *fakeScheduleLister) CreateSchedule(_ context.Context, _ client.ScheduleSpec) (client.Schedule, error) { +func (f *fakeScheduleLister) CreateSchedule(_ context.Context, spec client.ScheduleSpec) (client.Schedule, error) { f.createCalls++ + f.lastCreateSpec = spec if f.err != nil { return client.Schedule{}, f.err } @@ -1183,3 +1185,55 @@ func TestScheduleInspectRendersNeverFired(t *testing.T) { t.Errorf("never-fired inspect rendered \"in-flight\" (a never-claimed schedule must NOT render in-flight):\n%s", out) } } + +// TestScheduleCreateFormModeFollowsMutating pins the mode the create form +// sends. A read-leaning schedule must name plan, which the server requires; an +// empty mode there was refused. A mutating one leaves mode unset so its fires +// use the server's default session mode rather than an explicit default that +// asks with no approver on an allow-all server. +func TestScheduleCreateFormModeFollowsMutating(t *testing.T) { + for _, tc := range []struct { + toggle string + mutating bool + wantMode string + }{ + {toggle: "y", mutating: true, wantMode: ""}, + {toggle: "n", mutating: false, wantMode: "plan"}, + } { + t.Run("mutating="+tc.toggle, func(t *testing.T) { + fs := &fakeScheduleLister{sched: sampleSchedule("s")} + m := newScheduleModel(t, newScheduleConv(scheduleCaps()), fs, scheduleCaps()) + mm, _ := m.openSchedule() + m = applyAll(mm.(Model), client.SchedulesMsg{Schedules: nil}) + press := func(k tea.KeyPressMsg) tea.Cmd { + mm, cmd, _ := m.onScheduleKey(k) + m = mm.(Model) + return cmd + } + typeText := func(s string) { + for _, r := range s { + press(tea.KeyPressMsg{Code: r, Text: string(r)}) + } + } + tab := tea.KeyPressMsg{Code: tea.KeyTab, Text: "tab"} + press(tea.KeyPressMsg{Code: 'c', Text: "c"}) + typeText("s") + press(tab) + typeText("run it") + press(tab) + typeText("0 9 * * *") + press(tab) + press(tea.KeyPressMsg{Code: rune(tc.toggle[0]), Text: tc.toggle}) + cmd := press(tea.KeyPressMsg{Code: tea.KeyEnter}) + if cmd == nil { + t.Fatalf("submit returned no command (actionErr=%q)", m.schedule.actionErr) + } + m = feedCmd(t, m, cmd) + got := fs.lastCreateSpec + if fs.createCalls != 1 || got.Mutating != tc.mutating || got.Mode != tc.wantMode { + t.Fatalf("create sent (mutating %t, mode %q) in %d calls, want (mutating %t, mode %q) once", + got.Mutating, got.Mode, fs.createCalls, tc.mutating, tc.wantMode) + } + }) + } +} diff --git a/cmd/mecatui/ui/statusline.go b/cmd/mecatui/ui/statusline.go index 63f47855fe..08d180593b 100644 --- a/cmd/mecatui/ui/statusline.go +++ b/cmd/mecatui/ui/statusline.go @@ -40,6 +40,13 @@ func renderStatusSpans(th theme.Theme, spans []customization.Span) string { continue } style := lipgloss.NewStyle() + if span.Token == customization.TokenDanger { + // The yolo alarm pill: the dangerPill fill without its padding, so the + // span's own text (which carries the pill's spaces) is its exact width + // and the plain-text fit math stays correct. + b.WriteString(th.Style("dangerPill").UnsetPadding().Render(text)) + continue + } if span.Href != "" { b.WriteString(style.Foreground(lipgloss.Color(th.Palette.MdLink)).Underline(true).Hyperlink(span.Href).Render(text)) continue diff --git a/cmd/mecatui/ui/statusline_source.go b/cmd/mecatui/ui/statusline_source.go index 3295297689..b90352f84f 100644 --- a/cmd/mecatui/ui/statusline_source.go +++ b/cmd/mecatui/ui/statusline_source.go @@ -184,13 +184,7 @@ func (m Model) statusLineInput(now time.Time) customization.Input { workspace.Path = m.statusContextRoot } state, activity, approval := "idle", "", "none" - mode := m.activeMode - if mode == "" { - mode = m.deps.Mode - } - if m.pendingMode != "" { - mode = m.pendingMode + " pending" - } + mode := m.headerMode() handle := client.SessionHandle(m.sessionID) switch m.phase { case phaseConnecting: @@ -220,7 +214,7 @@ func (m Model) statusLineInput(now time.Time) customization.Input { MainAgent: customization.MainAgent{State: state, Activity: activity, Approval: approval}, Delegation: m.statusDelegation(), Clock: customization.Clock{Now: now}, - Terminal: customization.Terminal{Rows: m.height, Cols: m.widthOr(), HeaderAvailCols: geometry.headerAvailable, FooterAvailCols: geometry.footerAvailable}, + Terminal: customization.Terminal{Rows: m.height, Cols: m.widthOr(), HeaderAvailCols: geometry.headerAvailable, FooterAvailCols: geometry.footerAvailable, Emoji: m.emojiOK}, } } diff --git a/cmd/mecatui/ui/statusline_test.go b/cmd/mecatui/ui/statusline_test.go index e5c2b6a30f..0ae96a1e3b 100644 --- a/cmd/mecatui/ui/statusline_test.go +++ b/cmd/mecatui/ui/statusline_test.go @@ -9,6 +9,7 @@ import ( tea "charm.land/bubbletea/v2" "charm.land/lipgloss/v2" + "github.com/stacklok/mecatl/cmd/mecatui/client" customization "github.com/stacklok/mecatl/cmd/mecatui/customization" "github.com/stacklok/mecatl/cmd/mecatui/theme" ) @@ -42,6 +43,62 @@ func TestStatusLine_Scenario2_DefaultTemplatesPreserveChrome(t *testing.T) { } } } + +// TestStatusLine_YoloModeRendersAlarmPill pins that the shipped header marks +// the yolo mode with the filled alarm pill (the theme-independent dangerPill +// colours around "YOLO"), and every other mode — including a pending yolo — as +// plain "mode " text with no pill. On an emoji-capable terminal a ⚡️ +// bolt (VS16 intact) precedes the pill, outside its fill; otherwise no bolt. +func TestStatusLine_YoloModeRendersAlarmPill(t *testing.T) { + th := theme.New("aztec", theme.AztecPalette()) + renderWith := func(mode string, emoji bool) string { + t.Helper() + s := customization.NewDefaultSource(0) + t.Cleanup(func() { _ = s.Close(context.Background()) }) + m, _, _ := newTestModel(t, th, func(deps *Deps) { + deps.emojiCapable = func() bool { return emoji } + }) + m.deps.StatusSource = s + m.sessionID = "session-yolo-pill" + m.phase = phaseIdle + m.activeMode = mode + m.caps.Posture = postureYolo + m.caps.SessionPermissionModes = true + updated, _ := m.Update(tea.WindowSizeMsg{Width: 100, Height: 24}) + m = updated.(Model) + updated, _ = m.update(waitStatusMessage(t, m.statusLineWaitCmd())) + return updated.(Model).renderHeader() + } + render := func(mode string) string { return renderWith(mode, false) } + pill := th.Style("dangerPill").UnsetPadding().Render(" YOLO ") + if want := th.Style("dangerPill").Render(" YOLO "); stripANSIstr(pill) != stripANSIstr(want) || lipgloss.Width(pill) != lipgloss.Width(want) { + t.Fatalf("mode pill %q must match the posture pill %q cell for cell", pill, want) + } + for _, sgr := range []string{"224;49;49", "245;245;245"} { // #E03131 / #F5F5F5 + if !strings.Contains(pill, sgr) { + t.Fatalf("mode pill missing the fixed alarm colour %q: %q", sgr, pill) + } + } + raw := render("yolo") + if !strings.Contains(raw, pill) || !strings.Contains(stripANSIstr(raw), "mode YOLO") || strings.Contains(raw, "⚡") { + t.Fatalf("no-emoji yolo header must carry the bare alarm pill %q: %q", pill, raw) + } + emoji := renderWith("yolo", true) + bolt := strings.Index(emoji, "⚡️ ") + if bolt < 0 || !strings.Contains(emoji, pill) || bolt > strings.Index(emoji, pill) || strings.Contains(pill, "⚡") { + t.Fatalf("emoji yolo header must put the ⚡️ bolt (with VS16) before the pill, outside its fill: %q", emoji) + } + if !strings.Contains(stripANSIstr(emoji), "mode ⚡️ YOLO") { + t.Fatalf("emoji yolo header text = %q", stripANSIstr(emoji)) + } + for _, mode := range []string{"auto", "default"} { + raw := render(mode) + if strings.Contains(raw, "224;49;49") || !strings.Contains(stripANSIstr(raw), "mode "+mode) { + t.Fatalf("%s header must be plain mode text without the pill: %q", mode, raw) + } + } +} + func TestStatusLine_StaleGeneratedSurfaceNeverWraps(t *testing.T) { m, _, _ := newTestModel(t, theme.New("aztec", theme.AztecPalette())) m.deps.StatusSource = &statusSourceFake{changed: make(chan struct{})} @@ -95,10 +152,10 @@ func TestStatusLine_Scenario2_HeaderSystemIndicatorsSurviveOverride(t *testing.T m.width = 100 m.generatedStatusLine.Header = customization.Render(`
CUSTOM
`, nil).Header m.deps.StatusSource = &statusSourceFake{changed: make(chan struct{})} - m.caps.Posture = postureAuto + m.caps.ProjectTrust = client.ProjectUntrusted m.conv.recordFileChange("changed.go") header := stripANSIstr(m.renderHeader()) - for _, want := range []string{"CUSTOM", autoBadgeText, "✎ 1 file"} { + for _, want := range []string{"CUSTOM", client.ProjectUntrusted, "✎ 1 file"} { if !strings.Contains(header, want) { t.Errorf("missing %q in %q", want, header) } diff --git a/cmd/mecatui/ui/testdata/help_all_on.golden b/cmd/mecatui/ui/testdata/help_all_on.golden index 70b17c7091..6e7a65814a 100644 --- a/cmd/mecatui/ui/testdata/help_all_on.golden +++ b/cmd/mecatui/ui/testdata/help_all_on.golden @@ -16,7 +16,7 @@ ┃ also clears attachments, large pasted text, and pending media ┃ ┃ requires a terminal with enhanced key-event support ┃ ┃ fixed shortcut; repeats do not count, and other views handle esc first ┃ - ┃ lines 1–14 of 71 · esc or ? close · ↑/↓ scroll · pgup/pgdn page · home/end jump ┃ + ┃ lines 1–14 of 86 · esc or ? close · ↑/↓ scroll · pgup/pgdn page · home/end jump ┃ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ diff --git a/cmd/mecatui/ui/testdata/help_embedded.golden b/cmd/mecatui/ui/testdata/help_embedded.golden index 1ac09b963b..45cecf3b13 100644 --- a/cmd/mecatui/ui/testdata/help_embedded.golden +++ b/cmd/mecatui/ui/testdata/help_embedded.golden @@ -16,7 +16,7 @@ ┃ also clears attachments, large pasted text, and pending media ┃ ┃ requires a terminal with enhanced key-event support ┃ ┃ fixed shortcut; repeats do not count, and other views handle esc first ┃ - ┃ lines 1–14 of 70 · esc or ? close · ↑/↓ scroll · pgup/pgdn page · home/end jump ┃ + ┃ lines 1–14 of 85 · esc or ? close · ↑/↓ scroll · pgup/pgdn page · home/end jump ┃ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ diff --git a/cmd/mecatui/ui/update.go b/cmd/mecatui/ui/update.go index 48d8491b11..d7558ff026 100644 --- a/cmd/mecatui/ui/update.go +++ b/cmd/mecatui/ui/update.go @@ -1122,7 +1122,7 @@ func (m Model) updateLifecycle(msg tea.Msg) (tea.Model, tea.Cmd, bool) { if msg.Err == nil { checker = guardrailPostureSummary(msg.Coverage) } - m.statusMsg = m.deps.Theme.Style("muted").Render(postureSummary(m.caps.Posture) + "; " + checker) + m.statusMsg = m.deps.Theme.Style("muted").Render(postureSummary(m.caps.Posture, m.caps.ProjectTrust, m.caps.SubagentAskReviewer) + "; " + checker) } else if msg.Err != nil { m.conv.addNotice("Guardrails status unavailable; checker state is unknown, not off or healthy.") } else { @@ -2311,7 +2311,7 @@ func (m Model) onKey(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) { // for idle + running so the key never feeds the textarea. It also avoids relying // on macOS Option being configured as Meta. if m.deps.DebugTarget == "" && key.Matches(msg, m.keys.ModeSwitch) && (m.phase == phaseIdle || m.phase == phaseRunning) { - return m.switchMode(client.NextMode(m.desiredMode())) + return m.cycleMode() } return m.dispatchPhaseKey(msg) @@ -2631,21 +2631,52 @@ func (m Model) desiredMode() string { if m.activeMode != "" { return m.activeMode } + if m.deps.ModeServerDefault { + // No session mode known yet and none requested: let the server's default apply. + return "" + } return client.ModeString(client.ModeFromString(m.deps.Mode)) } -func (m Model) switchMode(mode string) (tea.Model, tea.Cmd) { - mode = client.ModeString(client.ModeFromString(mode)) +// initialActiveMode seeds the model's active mode. Under ModeServerDefault it is +// unknown until the server reports the created session's mode. +func initialActiveMode(deps Deps) string { + if deps.ModeServerDefault { + return "" + } + return client.ModeString(client.ModeFromString(deps.Mode)) +} + +// createdSessionMode is the mode a freshly created session is in. An explicit +// request is what the server applied; an unspecified request (ModeServerDefault) +// is read back from the server, because CreateSessionResponse carries no mode. A +// failed read-back reports "" so the header keeps its fallback rather than guess. +func createdSessionMode(deps Deps, id, requested string) string { + if requested != "" || deps.Session == nil { + return requested + } + snap, err := deps.Session.GetSession(deps.Ctx, id) + if err != nil { + return "" + } + return snap.Mode +} + +// switchMode applies a permission mode's session half to this session. +func (m Model) switchMode(e PermissionModeEntry) (tea.Model, tea.Cmd) { + mode := client.ModeString(client.ModeFromString(e.SessionMode)) if m.sessionID == "" { m.statusMsg = m.deps.Theme.Style("warning").Render("no active session — mode switch unavailable") return m, nil } if mode == m.activeMode && m.pendingMode == "" { - m.statusMsg = m.deps.Theme.Style("muted").Render("mode already " + mode) + // The session is already in this half (e.g. trusted after default under + // a trusted posture): the landing needs no server round trip. + m.statusMsg = m.deps.Theme.Style("success").Render("mode " + m.modeLabel(e)) return m, nil } m.pendingMode = mode - m.statusMsg = m.deps.Theme.Style("muted").Render("switching mode to " + mode + "…") + m.statusMsg = m.deps.Theme.Style("muted").Render("switching mode to " + m.modeLabel(e) + "…") return m, client.SetModeCmd(m.deps.Ctx, m.deps.Session, m.sessionID, mode) } @@ -2658,17 +2689,38 @@ func (m Model) onModeChanged(msg client.ModeChangedMsg) tea.Model { return m } if msg.Err != nil { + if m.phase == phaseIdle { + // No turn is in flight, so this is the server's final refusal (ceiling, + // missing checker), not the mid-turn deferral: retrying cannot succeed. + m.pendingMode = "" + m.modeCursor = modeCursor{} + m.modeRefused = requested + m.statusMsg = m.deps.Theme.Style("warning").Render("mode " + requested + " refused, staying in " + m.acceptedModeToken() + + ": " + client.ModeErrorText(msg.Err) + modeRefusedHint(requested)) + return m + } m.pendingMode = requested m.statusMsg = m.deps.Theme.Style("warning").Render("mode " + m.pendingMode + " will apply on the next prompt") return m } mode := client.ModeString(client.ModeFromString(msg.Mode)) if mode != requested { + // The server applied a different mode than requested (e.g. an older server + // that does not know it). Adopt what it reports instead of staying pending. + m.activeMode = mode + m.pendingMode = "" + m.modeCursor = modeCursor{} + m.statusMsg = m.deps.Theme.Style("warning").Render("mode " + requested + " not applied: the server kept " + mode) return m } m.activeMode = mode m.pendingMode = "" - m.statusMsg = m.deps.Theme.Style("success").Render("mode " + mode) + m.modeRefused = "" + label := mode + if m.modeCursor.session == mode { + label = m.modeLabel(permissionModeEntry(m.modeCursor.token)) + } + m.statusMsg = m.deps.Theme.Style("success").Render("mode " + label) return m } @@ -5051,7 +5103,7 @@ func (m Model) createSessionCmd() tea.Cmd { return func() tea.Msg { id, caps, resolved, err := deps.Session.CreateSession(deps.Ctx, sel, m.desiredMode()) if err == nil { - return client.SessionReadyMsg{SessionID: id, Capabilities: caps, ResolvedModel: resolved, Mode: m.desiredMode()} + return client.SessionReadyMsg{SessionID: id, Capabilities: caps, ResolvedModel: resolved, Mode: createdSessionMode(deps, id, m.desiredMode())} } if sel.IsZero() || !client.IsInvalidArgument(err) { reason, _ := client.AuthFailure(err, deps.BearerBacked) @@ -5068,7 +5120,7 @@ func (m Model) createSessionCmd() tea.Cmd { return client.ConnectErrMsg{Err: err} } return connectFallbackMsg{ - ready: client.SessionReadyMsg{SessionID: id, Capabilities: caps, ResolvedModel: resolved, Mode: m.desiredMode()}, + ready: client.SessionReadyMsg{SessionID: id, Capabilities: caps, ResolvedModel: resolved, Mode: createdSessionMode(deps, id, m.desiredMode())}, rejected: sel, err: err, } diff --git a/cmd/mecatui/ui/view.go b/cmd/mecatui/ui/view.go index 8d459073fa..76fed39298 100644 --- a/cmd/mecatui/ui/view.go +++ b/cmd/mecatui/ui/view.go @@ -182,60 +182,20 @@ const ( postureYolo = "yolo" ) -// autoBadgeText is the auto-posture badge: amber inline WARNING text (no pill). -const autoBadgeText = "⚠ auto" - -// yoloPillText is the YOLO pill's CONTENT — just the word, padded by a space each side -// so the filled dangerPill chip has visual breathing room around "YOLO". The pill never -// contains the lightning bolt: the emoji (when shown) rides OUTSIDE the pill as plain -// decoration (see postureBadgeRender) — VTE renders ⚡️ in its own multicolour glyph and -// IGNORES a foreground, which clashed inside the dark-on-red pill. -const yoloPillText = " YOLO " - -// yoloBoltPrefix is the PLAIN (unstyled) emoji bolt shown immediately BEFORE the YOLO -// pill on an emoji-capable terminal — ⚡ + U+FE0F (VS16) for the width-2 emoji -// presentation, plus a trailing space. It is decoration OUTSIDE the pill, so the -// terminal shows the bolt's natural colour and the pill stays a clean dark-on-red chip. -// The VS16 MUST survive to the screen — the header path renders via lipgloss styles only -// and never routes the badge through normalizeEmojiWidth/wrapStyled (which strip VS16). -const yoloBoltPrefix = "⚡️ " - -// postureBadgeRender builds the right-aligned operator-posture chrome badge — the STYLED -// string ready to drop into the header AND its visible (plain) cell width for the -// fit/shed math. It surfaces the SERVER-WIDE automation posture (m.caps.Posture) for the -// allow-all tiers ONLY — strict/trusted/unknown render NO badge, so the -// steady-state frame and the goldens stay byte-identical. It is DISTINCT from the -// per-session `mode` segment. -// -// Tiers: -// - auto → amber inline WARNING text "⚠ auto". -// - yolo → a clean filled DANGER PILL (dangerPill style) of " YOLO ", optionally -// PRECEDED by a PLAIN ⚡️ emoji bolt when m.emojiOK (seeded once at New). The bolt is -// pure decoration OUTSIDE the pill; the pill is always a clean dark-on-red chip. -// -// The plain width is computed to match the ACTUAL rendered glyphs: the dangerPill's -// Padding(0,1) adds 2 cells lipgloss.Width(yoloPillText) does count (the text already -// carries its spaces), so the pill's visible width is lipgloss.Width(yoloPillText)+2; the -// optional bolt prefix adds lipgloss.Width(yoloBoltPrefix) (⚡️ width-2 + space). +// postureBadgeRender builds the right-aligned project-trust chrome badge — the +// STYLED string ready to drop into the header AND its visible (plain) cell width +// for the fit/shed math. It shows the server-reported project trust +// (m.caps.ProjectTrust), an axis independent of the permission mode: "trusted" +// in the warning style (the project's rules and instructions are honoured), or +// "untrusted" muted. The permission mode, including auto and yolo, is shown only +// by the cycling mode segment. An older server reports no trust, so no badge. func (m Model) postureBadgeRender() (styled string, plainWidth int) { - switch m.caps.Posture { - case postureAuto: - return m.deps.Theme.Style("warning").Render(autoBadgeText), lipgloss.Width(autoBadgeText) - case postureYolo: - pill := m.deps.Theme.Style("dangerPill").Render(yoloPillText) - // The pill's visible width = its text cells + the 2 padding cells the style adds. - w := lipgloss.Width(yoloPillText) + 2 - if m.emojiOK { - // PLAIN bolt prefix (no style) so VTE shows its natural emoji colour. - return yoloBoltPrefix + pill, lipgloss.Width(yoloBoltPrefix) + w - } - return pill, w - case postureStrict, postureTrusted: - // The non-allow-all tiers carry no badge — the steady-state frame stays - // byte-identical (the goldens are captured at strict). - return "", 0 + switch t := m.caps.ProjectTrust; t { + case client.ProjectTrusted: + return m.deps.Theme.Style("warning").Render(t), lipgloss.Width(t) + case client.ProjectUntrusted: + return m.deps.Theme.Style("muted").Render(t), lipgloss.Width(t) default: - // Unknown / older-server posture: no badge. return "", 0 } } diff --git a/contracts/gen/go/mecatl/v1/harness.pb.go b/contracts/gen/go/mecatl/v1/harness.pb.go index 9041b26df2..059a071c20 100644 --- a/contracts/gen/go/mecatl/v1/harness.pb.go +++ b/contracts/gen/go/mecatl/v1/harness.pb.go @@ -48,7 +48,8 @@ const ( // PermissionMode is the session-wide permission posture, mirroring // session.PermissionMode. Plan mode exposes a read-only toolset; acceptEdits -// auto-accepts edits. +// auto-accepts edits; auto runs autonomously with an enforcing guardrails +// checker; yolo runs gate-free with no enforcing checker. type PermissionMode int32 const ( @@ -60,6 +61,13 @@ const ( PermissionMode_PERMISSION_MODE_PLAN PermissionMode = 2 // PERMISSION_MODE_ACCEPT_EDITS auto-accepts edits. PermissionMode_PERMISSION_MODE_ACCEPT_EDITS PermissionMode = 3 + // PERMISSION_MODE_AUTO runs autonomously using an enforcing guardrails checker. + // Entering this mode requires a checker; the server rejects SetMode to AUTO + // without one. + PermissionMode_PERMISSION_MODE_AUTO PermissionMode = 4 + // PERMISSION_MODE_YOLO runs gate-free with no enforcing checker. Advisory + // monitoring may remain. Configured deny rules still apply. + PermissionMode_PERMISSION_MODE_YOLO PermissionMode = 5 ) // Enum value maps for PermissionMode. @@ -69,12 +77,16 @@ var ( 1: "PERMISSION_MODE_DEFAULT", 2: "PERMISSION_MODE_PLAN", 3: "PERMISSION_MODE_ACCEPT_EDITS", + 4: "PERMISSION_MODE_AUTO", + 5: "PERMISSION_MODE_YOLO", } PermissionMode_value = map[string]int32{ "PERMISSION_MODE_UNSPECIFIED": 0, "PERMISSION_MODE_DEFAULT": 1, "PERMISSION_MODE_PLAN": 2, "PERMISSION_MODE_ACCEPT_EDITS": 3, + "PERMISSION_MODE_AUTO": 4, + "PERMISSION_MODE_YOLO": 5, } ) @@ -999,7 +1011,9 @@ func (x *Limits) GetMaxConsecutiveFailures() int32 { // CreateSessionRequest opens a new session. type CreateSessionRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - // mode is the permission posture; unspecified defaults to DEFAULT. + // mode is the permission posture; unspecified uses the server's configured + // default session mode, which is DEFAULT unless the operator chose a + // permission mode that starts sessions elsewhere. Mode PermissionMode `protobuf:"varint,2,opt,name=mode,proto3,enum=mecatl.v1.PermissionMode" json:"mode,omitempty"` // limits are the optional stop conditions. Limits *Limits `protobuf:"bytes,3,opt,name=limits,proto3" json:"limits,omitempty"` @@ -1886,12 +1900,10 @@ type ServerCapabilities struct { // (multi-provider Phase 0, S3.) ModelSelection bool `protobuf:"varint,12,opt,name=model_selection,json=modelSelection,proto3" json:"model_selection,omitempty"` // posture is the SERVER-WIDE operator posture-ladder tier ("strict"/"trusted"/ - // "auto"/"yolo"). It is CHROME ONLY — a client renders an honest "⚠ auto"/"⚠ yolo" - // badge so an operator can see the daemon's automation posture at a glance — and is + // "auto"/"yolo"). It bounds which session modes a client may cycle to and is // NEVER session state (it does not change per session; permission MODE is the // per-session knob). String passthrough, no enum on the wire (the EvNoProgress / - // StopBudget discipline). Additive: an older server leaves it empty and the client - // shows no badge (strict/trusted also show no badge). + // StopBudget discipline). Additive: an older server leaves it empty. Posture string `protobuf:"bytes,13,opt,name=posture,proto3" json:"posture,omitempty"` // worktrees is true when a WorktreeLister is wired (ListWorktrees may return a // non-empty list for a real git repo). Gates the client's /worktrees overlay — @@ -1952,8 +1964,20 @@ type ServerCapabilities struct { // execution_templates is true only when an authenticated template catalog is // wired; an eligible inventory may still be empty. ExecutionTemplates bool `protobuf:"varint,31,opt,name=execution_templates,json=executionTemplates,proto3" json:"execution_templates,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // project_trusted reports whether the server trusts its launch workspace's + // project content (rules, instructions, soul). It is an axis separate from + // the permission mode. CHROME ONLY. Additive: an older server leaves it + // unset and the client shows no trust indicator. + ProjectTrusted *bool `protobuf:"varint,32,opt,name=project_trusted,json=projectTrusted,proto3,oneof" json:"project_trusted,omitempty"` + // subagent_ask_reviewer reports the subagent ask reviewer, a model that may + // approve a subagent's, team member's, or parallel branch's permission + // request that no human can answer: "on" (enabled), "off" (not enabled), or + // "inert" (configured, but an interactive approver answers instead). Clients + // treat other values as unknown. CHROME ONLY. Additive: an older server + // leaves it unset and the client shows nothing. + SubagentAskReviewer *string `protobuf:"bytes,33,opt,name=subagent_ask_reviewer,json=subagentAskReviewer,proto3,oneof" json:"subagent_ask_reviewer,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ServerCapabilities) Reset() { @@ -2189,6 +2213,20 @@ func (x *ServerCapabilities) GetExecutionTemplates() bool { return false } +func (x *ServerCapabilities) GetProjectTrusted() bool { + if x != nil && x.ProjectTrusted != nil { + return *x.ProjectTrusted + } + return false +} + +func (x *ServerCapabilities) GetSubagentAskReviewer() string { + if x != nil && x.SubagentAskReviewer != nil { + return *x.SubagentAskReviewer + } + return "" +} + // ListSessionMcpConnectorsRequest names an owned, broker-bound session. type ListSessionMcpConnectorsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -4982,6 +5020,9 @@ type SetModeRequest struct { // session_id is the session to update. SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` // mode is the desired permission posture; unspecified defaults to DEFAULT. + // AUTO requires an enforcing guardrails checker; the server rejects SetMode + // to AUTO without one. The server may also enforce a MaxAllowedMode ceiling; + // requesting a mode above it is rejected with INVALID_ARGUMENT. Mode PermissionMode `protobuf:"varint,2,opt,name=mode,proto3,enum=mecatl.v1.PermissionMode" json:"mode,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -19746,7 +19787,7 @@ var file_mecatl_v1_harness_proto_rawDesc = string([]byte{ 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, - 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x9a, 0x08, 0x0a, 0x12, 0x53, 0x65, + 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0xaf, 0x09, 0x0a, 0x12, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x63, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x6d, 0x63, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, @@ -19809,933 +19850,923 @@ var file_mecatl_v1_harness_proto_rawDesc = string([]byte{ 0x66, 0x72, 0x65, 0x73, 0x68, 0x12, 0x2f, 0x0a, 0x13, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x4a, 0x04, 0x08, 0x14, 0x10, 0x15, 0x4a, 0x04, 0x08, 0x15, - 0x10, 0x16, 0x52, 0x0f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x61, 0x64, 0x6f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x11, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6d, 0x69, 0x67, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x40, 0x0a, 0x1f, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x63, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, - 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0xf9, 0x01, 0x0a, 0x20, 0x4c, 0x69, 0x73, - 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x63, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, - 0x0c, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, - 0x79, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x5f, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x65, 0x6e, 0x72, - 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3d, 0x0a, 0x0a, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x63, 0x70, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x74, - 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, - 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x74, 0x72, 0x75, 0x6e, 0x63, - 0x61, 0x74, 0x65, 0x64, 0x22, 0x70, 0x0a, 0x12, 0x4d, 0x63, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x27, - 0x0a, 0x0f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x75, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, - 0x75, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x74, 0x6f, 0x6f, - 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x9a, 0x02, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, - 0x51, 0x0a, 0x14, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x61, 0x70, 0x61, 0x62, - 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x13, 0x73, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x18, 0x20, 0x20, 0x01, 0x28, 0x08, 0x48, + 0x00, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, + 0x64, 0x88, 0x01, 0x01, 0x12, 0x37, 0x0a, 0x15, 0x73, 0x75, 0x62, 0x61, 0x67, 0x65, 0x6e, 0x74, + 0x5f, 0x61, 0x73, 0x6b, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x65, 0x72, 0x18, 0x21, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x13, 0x73, 0x75, 0x62, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x41, + 0x73, 0x6b, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x65, 0x72, 0x88, 0x01, 0x01, 0x42, 0x12, 0x0a, + 0x10, 0x5f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, + 0x64, 0x42, 0x18, 0x0a, 0x16, 0x5f, 0x73, 0x75, 0x62, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x61, + 0x73, 0x6b, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x65, 0x72, 0x4a, 0x04, 0x08, 0x14, 0x10, + 0x15, 0x4a, 0x04, 0x08, 0x15, 0x10, 0x16, 0x52, 0x0f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, + 0x61, 0x64, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x11, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x5f, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x40, 0x0a, 0x1f, 0x4c, + 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x63, 0x70, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, + 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0xf9, 0x01, + 0x0a, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x63, 0x70, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, + 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0f, 0x65, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x3d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x4d, 0x63, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, + 0x12, 0x29, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, + 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x22, 0x70, 0x0a, 0x12, 0x4d, 0x63, 0x70, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x75, 0x65, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x61, + 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x75, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x09, 0x74, 0x6f, 0x6f, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x9a, 0x02, 0x0a, 0x15, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x51, 0x0a, 0x14, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, - 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x6d, - 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x4d, - 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x4d, 0x6f, - 0x64, 0x65, 0x6c, 0x12, 0x3a, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4a, - 0x04, 0x08, 0x02, 0x10, 0x03, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, - 0x69, 0x65, 0x73, 0x22, 0x71, 0x0a, 0x11, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, - 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, - 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x9d, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f, 0x6c, - 0x76, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x6f, 0x64, - 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x6f, 0x64, - 0x65, 0x6c, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, - 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x29, 0x0a, 0x10, 0x72, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x66, 0x66, 0x6f, 0x72, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x69, 0x6e, 0x67, - 0x45, 0x66, 0x66, 0x6f, 0x72, 0x74, 0x22, 0x90, 0x01, 0x0a, 0x13, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x14, - 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x69, - 0x6d, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, - 0x6c, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x5f, 0x69, 0x6e, 0x5f, - 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x62, 0x75, 0x69, - 0x6c, 0x74, 0x49, 0x6e, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x22, 0x3b, 0x0a, 0x11, 0x47, 0x65, 0x74, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, + 0x65, 0x73, 0x52, 0x13, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x61, 0x70, 0x61, 0x62, + 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x6c, + 0x76, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x18, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x6c, + 0x76, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x3a, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x63, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x63, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22, 0x71, 0x0a, 0x11, 0x50, 0x6c, 0x61, 0x63, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, + 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, + 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, + 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, + 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x9d, 0x01, 0x0a, 0x0d, + 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x1f, 0x0a, + 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x19, + 0x0a, 0x08, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, + 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x66, + 0x66, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x66, 0x66, 0x6f, 0x72, 0x74, 0x22, 0x90, 0x01, 0x0a, 0x13, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x69, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x75, 0x64, + 0x69, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x12, + 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x6c, + 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x62, 0x75, 0x69, 0x6c, + 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x49, 0x6e, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x22, 0x3b, + 0x0a, 0x11, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, + 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x42, 0x0a, 0x12, 0x47, + 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x2c, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, + 0x45, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x42, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x07, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, + 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x76, 0x0a, 0x14, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, + 0x74, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, + 0x0a, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, + 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, 0x22, 0xaa, + 0x02, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x3a, + 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, + 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x6f, + 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x3b, 0x0a, 0x08, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, + 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x52, 0x65, 0x70, + 0x6c, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x08, 0x61, 0x63, 0x74, 0x69, 0x76, + 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x42, 0x0a, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x45, 0x0a, 0x1b, 0x47, 0x65, - 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, + 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x52, 0x0c, 0x72, + 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x22, 0x44, 0x0a, 0x1a, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, - 0x64, 0x22, 0x76, 0x0a, 0x14, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x52, 0x65, 0x70, - 0x6c, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x76, 0x61, - 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x76, - 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, - 0x65, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, - 0x65, 0x74, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x61, - 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, 0x22, 0xaa, 0x02, 0x0a, 0x1c, 0x47, 0x65, - 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x3a, 0x0a, 0x08, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, - 0x65, 0x12, 0x3b, 0x0a, 0x08, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x08, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x12, 0x12, - 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, - 0x6e, 0x64, 0x12, 0x42, 0x0a, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, - 0x69, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x22, 0x44, 0x0a, 0x1a, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, - 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x42, 0x0a, 0x18, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, - 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, - 0x22, 0x72, 0x0a, 0x19, 0x57, 0x61, 0x74, 0x63, 0x68, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, + 0x64, 0x22, 0x42, 0x0a, 0x18, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x15, 0x0a, - 0x06, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, - 0x75, 0x6e, 0x49, 0x64, 0x22, 0x72, 0x0a, 0x1a, 0x57, 0x61, 0x74, 0x63, 0x68, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x26, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x75, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x72, 0x0a, 0x19, 0x57, 0x61, 0x74, 0x63, 0x68, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, + 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x75, 0x72, 0x73, - 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x22, 0x4a, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x16, 0x0a, 0x06, - 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x75, - 0x72, 0x73, 0x6f, 0x72, 0x22, 0x70, 0x0a, 0x09, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, - 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xb5, 0x06, 0x0a, 0x0e, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x6f, 0x64, 0x69, - 0x66, 0x69, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x41, 0x74, 0x55, 0x6e, - 0x69, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x75, 0x72, 0x6e, - 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x12, 0x19, - 0x0a, 0x08, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x55, 0x6e, 0x69, - 0x78, 0x12, 0x2a, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x14, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x69, - 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x12, 0x0a, - 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, - 0x64, 0x12, 0x42, 0x0a, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, - 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x68, 0x69, 0x70, 0x12, 0x4b, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, - 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, - 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, - 0x74, 0x69, 0x65, 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, - 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x64, - 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x43, - 0x6f, 0x64, 0x65, 0x12, 0x3a, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, - 0x3e, 0x0a, 0x0e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x74, 0x6c, 0x65, - 0x52, 0x0d, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x4a, 0x0a, 0x0b, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x11, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x2e, - 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x61, - 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x12, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x1a, 0x54, 0x0a, 0x0f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x73, 0x61, 0x67, 0x65, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, - 0x08, 0x0d, 0x10, 0x0e, 0x4a, 0x04, 0x08, 0x0e, 0x10, 0x0f, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, - 0x65, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x10, 0x74, 0x69, - 0x74, 0x6c, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x22, 0xd5, - 0x02, 0x0a, 0x13, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, - 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0c, 0x62, - 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x05, 0x48, 0x00, 0x52, 0x0b, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x63, 0x68, 0x65, - 0x64, 0x75, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x6f, 0x72, 0x69, 0x67, - 0x69, 0x6e, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1f, 0x0a, - 0x0b, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x35, - 0x0a, 0x17, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x14, 0x64, 0x65, 0x62, 0x75, 0x67, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, - 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x87, 0x03, 0x0a, 0x1c, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x43, 0x61, 0x70, 0x61, 0x62, - 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x5f, 0x63, 0x68, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x70, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x43, 0x68, 0x61, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x6e, 0x73, 0x70, - 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x6e, 0x73, 0x70, 0x65, - 0x63, 0x74, 0x12, 0x39, 0x0a, 0x18, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x61, 0x74, - 0x69, 0x76, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x61, 0x74, - 0x69, 0x76, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x27, 0x0a, - 0x0f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, - 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x69, - 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x63, 0x6f, 0x70, 0x79, 0x49, 0x64, 0x12, - 0x27, 0x0a, 0x0f, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x76, 0x69, 0x65, 0x77, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6f, 0x72, 0x6b, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x66, 0x6f, 0x72, 0x6b, 0x12, 0x16, 0x0a, 0x06, - 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x42, 0x0a, 0x07, - 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x73, - 0x22, 0xe0, 0x01, 0x0a, 0x1d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x76, 0x65, - 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, 0x6f, - 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x63, 0x68, 0x61, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x43, - 0x68, 0x61, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x12, 0x17, 0x0a, - 0x07, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x63, 0x6f, 0x70, 0x79, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x74, - 0x72, 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x76, 0x69, 0x65, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x66, 0x6f, 0x72, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, - 0x6f, 0x72, 0x6b, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x22, 0x8f, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x08, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x63, 0x75, 0x72, 0x73, - 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x65, 0x78, 0x74, 0x43, 0x75, - 0x72, 0x73, 0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, - 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x19, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, - 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x22, 0xe2, 0x02, 0x0a, 0x0f, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x12, 0x2f, 0x0a, 0x14, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x78, - 0x5f, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x11, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x78, 0x41, 0x67, 0x65, 0x53, 0x65, - 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x6d, 0x61, - 0x78, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, - 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x78, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x31, 0x0a, 0x15, 0x63, - 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x63, 0x68, 0x69, 0x6c, - 0x64, 0x4d, 0x61, 0x78, 0x41, 0x67, 0x65, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x26, - 0x0a, 0x0f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x4d, 0x61, - 0x78, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x19, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, - 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x6f, - 0x6e, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x16, 0x73, 0x63, 0x68, 0x65, 0x64, - 0x75, 0x6c, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x41, 0x67, 0x65, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, - 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x6d, - 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, - 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x43, 0x6f, 0x75, 0x6e, - 0x74, 0x12, 0x32, 0x0a, 0x15, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x63, 0x61, 0x64, 0x65, 0x6e, - 0x63, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x13, 0x73, 0x77, 0x65, 0x65, 0x70, 0x43, 0x61, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x53, 0x65, - 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x22, 0xaa, 0x0c, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, - 0x72, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, - 0x12, 0x2d, 0x0a, 0x12, 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, - 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x75, 0x6e, - 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, - 0x23, 0x0a, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x42, - 0x79, 0x74, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x79, - 0x74, 0x65, 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2b, 0x0a, 0x11, - 0x72, 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x72, 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, - 0x61, 0x62, 0x6c, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x1b, 0x72, 0x65, 0x63, - 0x6c, 0x61, 0x69, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x61, - 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, - 0x72, 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, - 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1d, - 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, 0x0a, - 0x08, 0x76, 0x31, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x07, 0x76, 0x31, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x76, 0x32, 0x5f, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x76, 0x32, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x61, 0x69, 0x6e, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, - 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x73, 0x63, - 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, - 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0e, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0c, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x43, 0x6f, 0x75, 0x6e, - 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x75, 0x70, 0x74, 0x5f, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x6f, 0x72, 0x72, 0x75, 0x70, - 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x52, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x26, 0x0a, 0x0f, 0x6c, 0x61, - 0x73, 0x74, 0x5f, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x18, 0x11, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x77, 0x65, 0x65, 0x70, 0x55, 0x6e, - 0x69, 0x78, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x77, 0x65, 0x65, 0x70, - 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x77, 0x65, 0x65, 0x70, 0x41, 0x76, 0x61, 0x69, 0x6c, - 0x61, 0x62, 0x6c, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x77, 0x65, - 0x65, 0x70, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x18, 0x13, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6e, - 0x65, 0x78, 0x74, 0x53, 0x77, 0x65, 0x65, 0x70, 0x55, 0x6e, 0x69, 0x78, 0x12, 0x30, 0x0a, 0x14, - 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, - 0x61, 0x62, 0x6c, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x6e, 0x65, 0x78, 0x74, - 0x53, 0x77, 0x65, 0x65, 0x70, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1d, - 0x0a, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6a, 0x6f, 0x62, 0x18, 0x15, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x21, 0x0a, - 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, 0x16, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, - 0x12, 0x40, 0x0a, 0x1c, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, - 0x18, 0x17, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, - 0x73, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, - 0x6c, 0x65, 0x12, 0x51, 0x0a, 0x25, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x5f, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, - 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x18, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x22, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x52, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, - 0x73, 0x73, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x19, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, - 0x73, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x32, 0x0a, - 0x15, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x1a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x13, 0x6f, 0x77, - 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, - 0x73, 0x12, 0x45, 0x0a, 0x1f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x5f, 0x74, 0x72, 0x75, 0x6e, 0x63, - 0x61, 0x74, 0x65, 0x64, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1c, 0x6f, 0x77, 0x6e, 0x65, - 0x72, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x54, - 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x42, 0x0a, 0x1d, 0x6f, 0x77, 0x6e, 0x65, - 0x72, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x5f, - 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x1b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, - 0x6c, 0x65, 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x53, 0x0a, 0x26, - 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, - 0x6c, 0x65, 0x73, 0x5f, 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, - 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x23, 0x6f, 0x77, - 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x73, - 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, - 0x6e, 0x12, 0x38, 0x0a, 0x18, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x73, - 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x1e, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x16, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x63, - 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x38, 0x0a, 0x18, 0x6f, - 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, - 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x1f, 0x20, 0x03, 0x28, 0x09, 0x52, 0x16, 0x6f, - 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x4b, 0x0a, 0x22, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, - 0x73, 0x73, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x73, 0x5f, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x20, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x1f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x63, 0x68, 0x65, - 0x64, 0x75, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x54, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, - 0x65, 0x64, 0x22, 0x9f, 0x03, 0x0a, 0x0d, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x3d, 0x0a, 0x07, 0x62, 0x79, - 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x42, 0x79, 0x4b, 0x69, 0x6e, 0x64, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x06, 0x62, 0x79, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x40, 0x0a, 0x08, 0x62, 0x79, 0x5f, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x42, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x07, 0x62, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x43, 0x0a, 0x09, 0x62, - 0x79, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, - 0x75, 0x70, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x42, 0x79, 0x52, 0x65, 0x61, 0x73, 0x6f, - 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x62, 0x79, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, - 0x1a, 0x39, 0x0a, 0x0b, 0x42, 0x79, 0x4b, 0x69, 0x6e, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3a, 0x0a, 0x0c, 0x42, - 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3b, 0x0a, 0x0d, 0x42, 0x79, 0x52, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc6, 0x01, 0x0a, 0x10, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, - 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x6f, - 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x41, 0x74, - 0x55, 0x6e, 0x69, 0x78, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, - 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x65, - 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0x31, 0x0a, - 0x19, 0x50, 0x6c, 0x61, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, 0x61, - 0x6e, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6b, 0x69, - 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x6b, 0x69, 0x6e, 0x64, 0x73, - 0x22, 0xe2, 0x03, 0x0a, 0x1a, 0x50, 0x6c, 0x61, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x2d, 0x0a, 0x12, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1c, - 0x0a, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2d, 0x0a, 0x12, - 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, - 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, - 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x67, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x08, 0x65, 0x6c, 0x69, 0x67, 0x69, 0x62, 0x6c, 0x65, 0x18, 0x06, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, - 0x65, 0x52, 0x08, 0x65, 0x6c, 0x69, 0x67, 0x69, 0x62, 0x6c, 0x65, 0x12, 0x36, 0x0a, 0x09, 0x70, - 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, - 0x75, 0x70, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, - 0x74, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, - 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x65, 0x73, - 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x0e, - 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x4a, 0x6f, 0x62, - 0x49, 0x64, 0x12, 0x41, 0x0a, 0x0f, 0x65, 0x6c, 0x69, 0x67, 0x69, 0x62, 0x6c, 0x65, 0x5f, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x0e, 0x65, 0x6c, 0x69, 0x67, 0x69, 0x62, 0x6c, 0x65, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x22, 0x54, 0x0a, 0x1a, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x12, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, - 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x3d, 0x0a, 0x1b, 0x43, - 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, 0x61, - 0x6e, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x06, 0x6a, 0x6f, - 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, - 0x02, 0x10, 0x01, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x22, 0x3d, 0x0a, 0x1b, 0x47, 0x65, - 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x4a, - 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x06, 0x6a, 0x6f, 0x62, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, - 0x10, 0x01, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x22, 0x6e, 0x0a, 0x10, 0x43, 0x6c, 0x65, - 0x61, 0x6e, 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1f, 0x0a, - 0x0b, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x69, 0x74, 0x65, 0x6d, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x1f, - 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x12, - 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xee, 0x01, 0x0a, 0x0a, 0x43, 0x6c, - 0x65, 0x61, 0x6e, 0x75, 0x70, 0x4a, 0x6f, 0x62, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, - 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, - 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, - 0x73, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, - 0x07, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, - 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x6c, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x73, 0x74, 0x61, 0x6c, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x66, - 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x33, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, - 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, 0x45, 0x72, 0x72, - 0x6f, 0x72, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x67, 0x0a, 0x0e, 0x53, 0x65, - 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, - 0x6f, 0x64, 0x65, 0x22, 0x3f, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3d, 0x0a, 0x13, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x49, 0x64, 0x22, 0x16, 0x0a, 0x14, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5d, 0x0a, 0x14, 0x52, - 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, - 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x05, 0x74, - 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, - 0x02, 0x10, 0x01, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x22, 0x45, 0x0a, 0x15, 0x52, 0x65, - 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x22, 0x3e, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, - 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, - 0x64, 0x22, 0x17, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x92, 0x01, 0x0a, 0x13, 0x43, - 0x6c, 0x65, 0x61, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x33, 0x0a, 0x11, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, - 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x74, - 0x72, 0x65, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x00, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x53, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x77, 0x6f, - 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, - 0x71, 0x0a, 0x14, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x3a, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x22, 0x8e, 0x02, 0x0a, 0x12, 0x46, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x33, 0x0a, 0x11, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14, - 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, - 0x69, 0x74, 0x6c, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x69, 0x6e, - 0x67, 0x5f, 0x65, 0x66, 0x66, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, - 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x66, 0x66, 0x6f, 0x72, 0x74, 0x12, - 0x30, 0x0a, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x10, 0x77, 0x6f, - 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, - 0x01, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x49, 0x64, 0x42, 0x14, 0x0a, - 0x12, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x22, 0x70, 0x0a, 0x13, 0x46, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x3a, 0x0a, 0x09, 0x70, 0x6c, 0x61, - 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, - 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x63, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x53, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, - 0x4f, 0x63, 0x63, 0x75, 0x70, 0x61, 0x6e, 0x63, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x70, - 0x75, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0b, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x1c, 0x0a, 0x09, - 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x09, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x22, 0x8f, 0x08, 0x0a, 0x07, 0x53, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2d, 0x0a, 0x04, 0x6d, - 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x29, 0x0a, 0x06, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x52, 0x06, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, - 0x6f, 0x6f, 0x6c, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x09, 0x74, 0x6f, 0x6f, 0x6c, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x55, 0x6e, - 0x69, 0x78, 0x12, 0x3f, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x6d, - 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x4d, - 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x4d, 0x6f, - 0x64, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x42, 0x0a, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x52, 0x0c, 0x72, - 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x12, 0x2a, 0x0a, 0x11, 0x64, - 0x65, 0x62, 0x75, 0x67, 0x5f, 0x6d, 0x63, 0x70, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, - 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x4d, 0x63, 0x70, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x64, 0x65, 0x62, 0x75, 0x67, - 0x5f, 0x6d, 0x63, 0x70, 0x5f, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x0d, 0x64, 0x65, 0x62, 0x75, 0x67, 0x4d, 0x63, 0x70, 0x54, 0x6f, 0x6f, 0x6c, 0x73, 0x12, - 0x3a, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x12, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, - 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x52, 0x09, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x3e, 0x0a, 0x0e, 0x74, - 0x69, 0x74, 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x13, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x52, 0x0d, 0x74, 0x69, - 0x74, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x43, 0x0a, 0x0b, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x22, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x45, + 0x6f, 0x72, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x72, 0x75, 0x6e, 0x49, 0x64, 0x22, 0x72, 0x0a, 0x1a, 0x57, 0x61, 0x74, + 0x63, 0x68, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x22, 0x4a, 0x0a, + 0x13, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, 0x70, 0x0a, 0x09, 0x50, 0x72, 0x69, + 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65, 0x72, 0x12, 0x18, + 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x72, 0x61, 0x6e, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x72, + 0x61, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xb5, 0x06, 0x0a, 0x0e, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x1d, + 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x28, 0x0a, + 0x10, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x75, 0x6e, 0x69, + 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, + 0x64, 0x41, 0x74, 0x55, 0x6e, 0x69, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x74, 0x75, + 0x72, 0x6e, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x26, + 0x0a, 0x0f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x75, 0x6e, 0x69, + 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x41, 0x74, 0x55, 0x6e, 0x69, 0x78, 0x12, 0x2a, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x52, 0x05, 0x6f, 0x77, 0x6e, + 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x42, 0x0a, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x52, 0x0c, 0x72, 0x65, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x12, 0x4b, 0x0a, 0x0c, 0x63, 0x61, + 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x27, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x43, 0x61, 0x70, + 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, + 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x3a, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x63, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x63, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x3e, 0x0a, 0x0e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x5f, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x54, 0x69, 0x74, 0x6c, 0x65, 0x52, 0x0d, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x12, 0x4a, 0x0a, 0x0b, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x75, 0x73, + 0x61, 0x67, 0x65, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x73, 0x61, 0x67, 0x65, - 0x12, 0x51, 0x0a, 0x14, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x61, 0x70, 0x61, - 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x13, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, - 0x69, 0x65, 0x73, 0x12, 0x55, 0x0a, 0x18, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x6f, 0x63, 0x63, 0x75, 0x70, 0x61, 0x6e, 0x63, 0x79, 0x18, - 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4f, 0x63, 0x63, 0x75, 0x70, 0x61, 0x6e, - 0x63, 0x79, 0x52, 0x16, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, - 0x74, 0x4f, 0x63, 0x63, 0x75, 0x70, 0x61, 0x6e, 0x63, 0x79, 0x1a, 0x54, 0x0a, 0x0f, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, + 0x74, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x54, 0x0a, 0x0f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x55, 0x73, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x73, 0x61, + 0x67, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, + 0x07, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x0d, 0x10, 0x0e, 0x4a, 0x04, 0x08, 0x0e, 0x10, 0x0f, 0x52, + 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x52, 0x10, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, + 0x6e, 0x63, 0x65, 0x22, 0xd5, 0x02, 0x0a, 0x13, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x12, 0x2a, 0x0a, 0x11, 0x70, + 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x61, 0x6c, 0x6c, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x61, 0x6c, 0x6c, 0x49, 0x64, + 0x12, 0x26, 0x0a, 0x0c, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x0b, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x63, 0x68, 0x65, + 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, + 0x11, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, + 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, + 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x17, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x74, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x64, 0x65, 0x62, 0x75, 0x67, 0x54, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x62, + 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x87, 0x03, 0x0a, 0x1c, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, + 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, + 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x63, 0x68, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x43, 0x68, 0x61, 0x74, 0x12, 0x18, 0x0a, + 0x07, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x12, 0x39, 0x0a, 0x18, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x5f, 0x72, + 0x65, 0x70, 0x6c, 0x61, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x61, 0x63, 0x74, + 0x69, 0x76, 0x69, 0x74, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x63, + 0x6f, 0x70, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x63, 0x6f, + 0x70, 0x79, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x76, + 0x69, 0x65, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x66, 0x6f, 0x72, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x66, 0x6f, 0x72, + 0x6b, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x06, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x12, 0x42, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x73, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x72, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x73, 0x22, 0xe0, 0x01, 0x0a, 0x1d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x5f, 0x63, 0x68, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x43, 0x68, 0x61, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x6e, 0x73, 0x70, + 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x69, 0x6e, 0x73, 0x70, 0x65, + 0x63, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x6f, 0x70, 0x79, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x76, + 0x69, 0x65, 0x77, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x76, 0x69, 0x65, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6f, 0x72, 0x6b, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x66, 0x6f, 0x72, 0x6b, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x22, 0x8f, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, + 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x35, 0x0a, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x08, + 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, + 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, + 0x65, 0x78, 0x74, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, + 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x19, 0x0a, 0x17, 0x47, 0x65, + 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xe2, 0x02, 0x0a, 0x0f, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x2f, 0x0a, 0x14, 0x6d, 0x61, 0x69, + 0x6e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x78, + 0x41, 0x67, 0x65, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, + 0x69, 0x6e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x78, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x31, 0x0a, 0x15, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x67, + 0x65, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x12, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x4d, 0x61, 0x78, 0x41, 0x67, 0x65, 0x53, 0x65, 0x63, 0x6f, + 0x6e, 0x64, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x78, + 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x63, 0x68, + 0x69, 0x6c, 0x64, 0x4d, 0x61, 0x78, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x19, 0x73, + 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x67, 0x65, + 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x16, + 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x4d, 0x61, 0x78, 0x41, 0x67, 0x65, 0x53, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x2e, 0x0a, 0x13, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, + 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x11, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x4d, 0x61, + 0x78, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, + 0x63, 0x61, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x73, 0x77, 0x65, 0x65, 0x70, 0x43, 0x61, 0x64, 0x65, + 0x6e, 0x63, 0x65, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x22, 0xaa, 0x0c, 0x0a, 0x18, 0x47, + 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x76, 0x61, 0x69, + 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x11, 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, + 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x61, 0x62, 0x6c, 0x65, + 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x72, 0x65, + 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x3e, + 0x0a, 0x1b, 0x72, 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x79, + 0x74, 0x65, 0x73, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x19, 0x72, 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x61, 0x62, 0x6c, 0x65, + 0x42, 0x79, 0x74, 0x65, 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x23, + 0x0a, 0x0d, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x76, 0x31, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x76, 0x31, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, 0x0a, + 0x08, 0x76, 0x32, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x07, 0x76, 0x32, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x69, 0x6e, + 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x61, + 0x69, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, + 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x63, 0x68, + 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x63, 0x68, 0x65, + 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0e, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x75, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, + 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x75, 0x70, + 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, + 0x6f, 0x72, 0x72, 0x75, 0x70, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x06, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, + 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, + 0x26, 0x0a, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x75, 0x6e, + 0x69, 0x78, 0x18, 0x11, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x77, + 0x65, 0x65, 0x70, 0x55, 0x6e, 0x69, 0x78, 0x12, 0x30, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, + 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, + 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x77, 0x65, 0x65, 0x70, + 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, + 0x74, 0x5f, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x18, 0x13, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x53, 0x77, 0x65, 0x65, 0x70, 0x55, 0x6e, 0x69, + 0x78, 0x12, 0x30, 0x0a, 0x14, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x77, 0x65, 0x65, 0x70, 0x5f, + 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x12, 0x6e, 0x65, 0x78, 0x74, 0x53, 0x77, 0x65, 0x65, 0x70, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6a, 0x6f, + 0x62, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x4a, + 0x6f, 0x62, 0x12, 0x21, 0x0a, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, + 0x72, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x46, 0x61, + 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x40, 0x0a, 0x1c, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, + 0x73, 0x73, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x61, 0x76, 0x61, 0x69, + 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x17, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x6f, 0x77, 0x6e, + 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x41, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x51, 0x0a, 0x25, 0x6f, 0x77, 0x6e, 0x65, 0x72, + 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x75, 0x6e, + 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, + 0x18, 0x18, 0x20, 0x01, 0x28, 0x09, 0x52, 0x22, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, + 0x73, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x17, 0x6f, 0x77, + 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x19, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x6f, 0x77, 0x6e, + 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x5f, + 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x1a, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x13, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x45, 0x0a, 0x1f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, + 0x65, 0x73, 0x73, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x5f, + 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x1c, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x49, 0x64, 0x73, 0x54, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x42, 0x0a, + 0x1d, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, + 0x75, 0x6c, 0x65, 0x73, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x1c, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x53, + 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, + 0x65, 0x12, 0x53, 0x0a, 0x26, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x73, + 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x5f, 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x1d, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x23, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x63, 0x68, 0x65, + 0x64, 0x75, 0x6c, 0x65, 0x73, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, + 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x18, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, + 0x65, 0x73, 0x73, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x16, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, + 0x65, 0x73, 0x73, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x38, 0x0a, 0x18, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x63, + 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x1f, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x16, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x53, 0x63, 0x68, + 0x65, 0x64, 0x75, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x4b, 0x0a, 0x22, 0x6f, 0x77, + 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x5f, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, + 0x18, 0x20, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x6c, 0x65, 0x73, + 0x73, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x54, 0x72, + 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x22, 0x9f, 0x03, 0x0a, 0x0d, 0x43, 0x6c, 0x65, 0x61, + 0x6e, 0x75, 0x70, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, + 0x3d, 0x0a, 0x07, 0x62, 0x79, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x24, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, + 0x61, 0x6e, 0x75, 0x70, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x42, 0x79, 0x4b, 0x69, 0x6e, + 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x62, 0x79, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x40, + 0x0a, 0x08, 0x62, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x25, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, + 0x61, 0x6e, 0x75, 0x70, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x42, 0x79, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x62, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x43, 0x0a, 0x09, 0x62, 0x79, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x42, 0x79, + 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x62, 0x79, 0x52, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x1a, 0x39, 0x0a, 0x0b, 0x42, 0x79, 0x4b, 0x69, 0x6e, 0x64, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x1a, 0x3a, 0x0a, 0x0c, 0x42, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3b, 0x0a, 0x0d, + 0x42, 0x79, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, 0x4a, 0x04, 0x08, 0x0b, - 0x10, 0x0c, 0x4a, 0x04, 0x08, 0x0c, 0x10, 0x0d, 0x4a, 0x04, 0x08, 0x0d, 0x10, 0x0e, 0x52, 0x09, - 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, - 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x10, - 0x74, 0x69, 0x74, 0x6c, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, - 0x52, 0x1a, 0x61, 0x64, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x22, 0xd2, 0x01, 0x0a, - 0x0c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, - 0x74, 0x6c, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, - 0x6e, 0x63, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x67, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x45, - 0x0a, 0x0e, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x53, - 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0d, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x41, 0x74, - 0x74, 0x65, 0x6d, 0x70, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x22, 0x45, 0x0a, 0x13, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, - 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x63, - 0x6f, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x63, 0x6f, - 0x6d, 0x65, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0xbc, 0x01, 0x0a, 0x0a, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, - 0x39, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x21, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x1a, 0x4b, 0x0a, 0x0b, 0x4d, 0x6f, - 0x64, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8c, 0x03, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x06, 0x70, - 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x00, - 0x52, 0x06, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x12, 0x2d, 0x0a, 0x05, 0x72, 0x65, 0x74, 0x72, - 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x79, 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, 0x00, - 0x52, 0x05, 0x72, 0x65, 0x74, 0x72, 0x79, 0x12, 0x44, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x75, 0x6d, - 0x65, 0x5f, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, - 0x75, 0x6d, 0x65, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x48, 0x00, 0x52, 0x0e, 0x72, - 0x65, 0x73, 0x75, 0x6d, 0x65, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x12, 0x2b, 0x0a, - 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x12, 0x3b, 0x0a, 0x0c, 0x63, 0x61, - 0x6e, 0x63, 0x65, 0x6c, 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x16, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x61, 0x6e, 0x63, - 0x65, 0x6c, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x12, 0x28, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x65, 0x72, - 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x74, 0x65, 0x65, 0x72, 0x48, 0x00, 0x52, 0x05, 0x73, 0x74, 0x65, 0x65, - 0x72, 0x12, 0x3b, 0x0a, 0x0c, 0x73, 0x74, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x61, 0x6e, 0x63, 0x65, - 0x6c, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x65, 0x65, 0x72, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x48, - 0x00, 0x52, 0x0b, 0x73, 0x74, 0x65, 0x65, 0x72, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x06, - 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0x3a, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, - 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x05, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x22, 0xb7, 0x01, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x2b, - 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6d, - 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x2e, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6d, - 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x6d, 0x69, 0x6d, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, - 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x3c, - 0x0a, 0x04, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x10, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, - 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, - 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, - 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x41, 0x55, 0x44, 0x49, 0x4f, 0x10, 0x02, 0x22, 0xf3, 0x03, 0x0a, - 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x30, 0x0a, - 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, - 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, - 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, - 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, - 0x74, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, - 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, - 0x63, 0x65, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x61, 0x75, 0x64, 0x69, 0x65, 0x6e, - 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x0b, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x23, - 0x0a, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, - 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, - 0x69, 0x65, 0x64, 0x22, 0x9c, 0x01, 0x0a, 0x04, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x10, - 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, - 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x10, - 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x10, - 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x41, 0x55, 0x44, 0x49, 0x4f, 0x10, - 0x03, 0x12, 0x16, 0x0a, 0x12, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, - 0x43, 0x45, 0x5f, 0x4c, 0x49, 0x4e, 0x4b, 0x10, 0x04, 0x12, 0x1a, 0x0a, 0x16, 0x4b, 0x49, 0x4e, - 0x44, 0x5f, 0x45, 0x4d, 0x42, 0x45, 0x44, 0x44, 0x45, 0x44, 0x5f, 0x52, 0x45, 0x53, 0x4f, 0x55, - 0x52, 0x43, 0x45, 0x10, 0x05, 0x12, 0x1b, 0x0a, 0x17, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x53, 0x54, - 0x52, 0x55, 0x43, 0x54, 0x55, 0x52, 0x45, 0x44, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x45, 0x4e, 0x54, - 0x10, 0x06, 0x22, 0x34, 0x0a, 0x0a, 0x52, 0x65, 0x74, 0x72, 0x79, 0x53, 0x74, 0x61, 0x72, 0x74, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc6, 0x01, 0x0a, 0x10, 0x43, 0x6c, + 0x65, 0x61, 0x6e, 0x75, 0x70, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1d, + 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, + 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, + 0x28, 0x0a, 0x10, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x75, + 0x6e, 0x69, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x6d, 0x6f, 0x64, 0x69, 0x66, + 0x69, 0x65, 0x64, 0x41, 0x74, 0x55, 0x6e, 0x69, 0x78, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x73, 0x74, + 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0e, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x74, + 0x65, 0x73, 0x22, 0x31, 0x0a, 0x19, 0x50, 0x6c, 0x61, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x14, 0x0a, 0x05, 0x6b, 0x69, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, + 0x6b, 0x69, 0x6e, 0x64, 0x73, 0x22, 0xe2, 0x03, 0x0a, 0x1a, 0x50, 0x6c, 0x61, 0x6e, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, + 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, + 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x75, + 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, + 0x12, 0x1e, 0x0a, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x08, 0x65, 0x6c, 0x69, 0x67, 0x69, + 0x62, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x43, 0x61, 0x6e, + 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x08, 0x65, 0x6c, 0x69, 0x67, 0x69, 0x62, 0x6c, 0x65, + 0x12, 0x36, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x09, 0x70, + 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x73, 0x74, 0x69, + 0x6d, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0e, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, + 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x6a, 0x6f, 0x62, + 0x5f, 0x69, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x6c, 0x61, 0x6e, 0x6e, + 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x41, 0x0a, 0x0f, 0x65, 0x6c, 0x69, 0x67, 0x69, + 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x18, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, + 0x61, 0x6e, 0x75, 0x70, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x0e, 0x65, 0x6c, 0x69, 0x67, + 0x69, 0x62, 0x6c, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x22, 0x54, 0x0a, 0x1a, 0x41, 0x70, + 0x70, 0x6c, 0x79, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, + 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x12, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x11, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x22, 0x3d, 0x0a, 0x1b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1e, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x22, + 0x3d, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, + 0x61, 0x6e, 0x75, 0x70, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, + 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, + 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x22, 0x6e, + 0x0a, 0x10, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x49, 0x74, 0x65, 0x6d, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x74, 0x65, 0x6d, 0x48, 0x61, 0x6e, + 0x64, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, + 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, + 0x43, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xee, + 0x01, 0x0a, 0x0a, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x4a, 0x6f, 0x62, 0x12, 0x15, 0x0a, + 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, + 0x6f, 0x62, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, + 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x70, + 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x07, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, + 0x73, 0x74, 0x61, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x33, 0x0a, 0x06, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, + 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x49, 0x74, + 0x65, 0x6d, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, + 0x67, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, + 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x04, 0x6d, 0x6f, 0x64, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, + 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0x3f, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x4d, + 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x07, 0x73, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x52, 0x07, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3d, 0x0a, 0x13, 0x43, 0x6c, 0x6f, + 0x73, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0xb3, 0x01, 0x0a, 0x06, 0x50, 0x72, 0x6f, - 0x6d, 0x70, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, - 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, - 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, - 0x28, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x52, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x12, 0x43, 0x0a, 0x1e, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x1b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4f, 0x77, 0x6e, 0x65, 0x64, 0x50, 0x6c, - 0x61, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x8f, - 0x02, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, - 0x6c, 0x12, 0x1e, 0x0a, 0x06, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x16, 0x0a, 0x14, 0x43, 0x6c, 0x6f, 0x73, + 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x5d, 0x0a, 0x14, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, + 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, + 0x12, 0x1d, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x22, + 0x45, 0x0a, 0x15, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x07, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3e, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, + 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x17, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x92, 0x01, 0x0a, 0x13, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x33, 0x0a, 0x11, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x11, + 0x77, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x74, + 0x72, 0x65, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x42, 0x14, + 0x0a, 0x12, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x73, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x22, 0x71, 0x0a, 0x14, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x3a, 0x0a, 0x09, 0x70, + 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, + 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x09, 0x70, 0x6c, + 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x8e, 0x02, 0x0a, 0x12, 0x46, 0x6f, 0x72, 0x6b, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x33, + 0x0a, 0x11, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, + 0x10, 0x01, 0x52, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x66, 0x66, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x66, + 0x66, 0x6f, 0x72, 0x74, 0x12, 0x30, 0x0a, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, + 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x00, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x6f, 0x64, 0x65, 0x6c, + 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x65, 0x6c, + 0x49, 0x64, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x5f, + 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x70, 0x0a, 0x13, 0x46, 0x6f, 0x72, 0x6b, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x3a, + 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, + 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, + 0x09, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x53, 0x0a, 0x10, 0x43, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4f, 0x63, 0x63, 0x75, 0x70, 0x61, 0x6e, 0x63, 0x79, 0x12, 0x21, + 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x22, + 0x8f, 0x08, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x73, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x2d, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, + 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, + 0x29, 0x0a, 0x06, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x6d, 0x69, + 0x74, 0x73, 0x52, 0x06, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x75, + 0x72, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x74, 0x75, 0x72, 0x6e, 0x73, + 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x6f, 0x6f, 0x6c, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x12, + 0x26, 0x0a, 0x0f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x75, 0x6e, + 0x69, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x64, 0x41, 0x74, 0x55, 0x6e, 0x69, 0x78, 0x12, 0x3f, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x6c, + 0x76, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x18, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x6c, 0x76, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x6c, + 0x76, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, + 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x42, 0x0a, 0x0c, + 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, 0x18, 0x0f, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, + 0x69, 0x70, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69, 0x70, + 0x12, 0x2a, 0x0a, 0x11, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x6d, 0x63, 0x70, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x64, 0x65, 0x62, + 0x75, 0x67, 0x4d, 0x63, 0x70, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x26, 0x0a, 0x0f, + 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x6d, 0x63, 0x70, 0x5f, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x18, + 0x11, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x65, 0x62, 0x75, 0x67, 0x4d, 0x63, 0x70, 0x54, + 0x6f, 0x6f, 0x6c, 0x73, 0x12, 0x3a, 0x0a, 0x09, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x12, 0x3e, 0x0a, 0x0e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x74, 0x6c, + 0x65, 0x52, 0x0d, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x12, 0x43, 0x0a, 0x0b, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x55, + 0x73, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x51, 0x0a, 0x14, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x5f, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x15, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x69, 0x65, 0x73, 0x52, 0x13, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x61, 0x70, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x55, 0x0a, 0x18, 0x6c, 0x61, 0x74, 0x65, + 0x73, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x6f, 0x63, 0x63, 0x75, 0x70, + 0x61, 0x6e, 0x63, 0x79, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, + 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4f, 0x63, + 0x63, 0x75, 0x70, 0x61, 0x6e, 0x63, 0x79, 0x52, 0x16, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4f, 0x63, 0x63, 0x75, 0x70, 0x61, 0x6e, 0x63, 0x79, 0x1a, + 0x54, 0x0a, 0x0f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x0a, 0x10, + 0x0b, 0x4a, 0x04, 0x08, 0x0b, 0x10, 0x0c, 0x4a, 0x04, 0x08, 0x0c, 0x10, 0x0d, 0x4a, 0x04, 0x08, + 0x0d, 0x10, 0x0e, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x05, + 0x74, 0x69, 0x74, 0x6c, 0x65, 0x52, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x69, 0x65, 0x73, 0x52, 0x10, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x65, + 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x1a, 0x61, 0x64, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x64, 0x22, 0xd2, 0x01, 0x0a, 0x0c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x74, + 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x76, + 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, + 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x67, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x45, 0x0a, 0x0e, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x74, + 0x74, 0x65, 0x6d, 0x70, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x41, 0x74, 0x74, + 0x65, 0x6d, 0x70, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x0d, 0x6c, 0x61, 0x74, + 0x65, 0x73, 0x74, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x45, 0x0a, 0x13, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x41, + 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, + 0x07, 0x6f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0xbc, 0x01, + 0x0a, 0x0a, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x05, + 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x39, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x4d, 0x6f, 0x64, 0x65, + 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x1a, + 0x4b, 0x0a, 0x0b, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x26, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8c, 0x03, 0x0a, + 0x0f, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x2b, 0x0a, 0x06, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x11, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, + 0x6d, 0x70, 0x74, 0x48, 0x00, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x12, 0x2d, 0x0a, + 0x05, 0x72, 0x65, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x79, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x48, 0x00, 0x52, 0x05, 0x72, 0x65, 0x74, 0x72, 0x79, 0x12, 0x44, 0x0a, 0x0f, + 0x72, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x5f, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, + 0x48, 0x00, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, + 0x61, 0x6c, 0x12, 0x2b, 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x12, + 0x3b, 0x0a, 0x0c, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x48, 0x00, 0x52, + 0x0b, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x12, 0x28, 0x0a, 0x05, + 0x73, 0x74, 0x65, 0x65, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x65, 0x65, 0x72, 0x48, 0x00, 0x52, + 0x05, 0x73, 0x74, 0x65, 0x65, 0x72, 0x12, 0x3b, 0x0a, 0x0c, 0x73, 0x74, 0x65, 0x65, 0x72, 0x5f, + 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x65, 0x65, 0x72, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x74, 0x65, 0x65, 0x72, 0x43, 0x61, 0x6e, + 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0x3a, 0x0a, 0x10, 0x43, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x26, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, + 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0xb7, 0x01, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, + 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x75, 0x72, 0x6c, 0x22, 0x3c, 0x0a, 0x04, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x10, 0x4b, + 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x49, 0x4d, 0x41, 0x47, 0x45, 0x10, + 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x41, 0x55, 0x44, 0x49, 0x4f, 0x10, + 0x02, 0x22, 0xf3, 0x03, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x12, 0x30, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, + 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x61, + 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x61, + 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x69, + 0x66, 0x69, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, + 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x22, 0x9c, 0x01, 0x0a, 0x04, 0x4b, 0x69, 0x6e, + 0x64, 0x12, 0x14, 0x0a, 0x10, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4b, 0x49, 0x4e, 0x44, 0x5f, + 0x54, 0x45, 0x58, 0x54, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x49, + 0x4d, 0x41, 0x47, 0x45, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x41, + 0x55, 0x44, 0x49, 0x4f, 0x10, 0x03, 0x12, 0x16, 0x0a, 0x12, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x52, + 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x4c, 0x49, 0x4e, 0x4b, 0x10, 0x04, 0x12, 0x1a, + 0x0a, 0x16, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x45, 0x4d, 0x42, 0x45, 0x44, 0x44, 0x45, 0x44, 0x5f, + 0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x10, 0x05, 0x12, 0x1b, 0x0a, 0x17, 0x4b, 0x49, + 0x4e, 0x44, 0x5f, 0x53, 0x54, 0x52, 0x55, 0x43, 0x54, 0x55, 0x52, 0x45, 0x44, 0x5f, 0x43, 0x4f, + 0x4e, 0x54, 0x45, 0x4e, 0x54, 0x10, 0x06, 0x22, 0x34, 0x0a, 0x0a, 0x52, 0x65, 0x74, 0x72, 0x79, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, + 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0xb3, 0x01, + 0x0a, 0x06, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, + 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x74, 0x65, 0x78, 0x74, 0x12, 0x28, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x12, 0x43, + 0x0a, 0x1e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x64, 0x5f, 0x70, + 0x6c, 0x61, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4f, 0x77, + 0x6e, 0x65, 0x64, 0x50, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0x8f, 0x02, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x41, 0x70, + 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x12, 0x1e, 0x0a, 0x06, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, + 0x05, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x42, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x64, + 0x69, 0x63, 0x74, 0x42, 0x0c, 0xba, 0x48, 0x09, 0x82, 0x01, 0x06, 0x18, 0x01, 0x18, 0x02, 0x18, + 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x78, + 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x75, 0x6e, + 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x69, 0x64, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x49, 0x64, 0x12, + 0x47, 0x0a, 0x0e, 0x67, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x5f, 0x6b, 0x69, 0x6e, + 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x41, 0x70, 0x70, + 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x0d, 0x67, 0x75, 0x61, 0x72, 0x64, + 0x72, 0x61, 0x69, 0x6c, 0x4b, 0x69, 0x6e, 0x64, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x52, 0x05, + 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x22, 0x30, 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x12, + 0x26, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x22, 0x31, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x12, 0x22, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, + 0x01, 0x52, 0x07, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, 0x64, 0x22, 0x8c, 0x01, 0x0a, 0x05, 0x53, + 0x74, 0x65, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x70, 0x61, 0x72, 0x74, + 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x75, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, + 0x63, 0x74, 0x65, 0x64, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x22, 0x54, 0x0a, 0x0b, 0x53, 0x74, 0x65, + 0x65, 0x72, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x22, + 0xb9, 0x02, 0x0a, 0x14, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x75, 0x6e, 0x41, 0x73, + 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, + 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, + 0x12, 0x2f, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, + 0x10, 0x01, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x75, 0x6e, 0x49, + 0x64, 0x12, 0x1e, 0x0a, 0x06, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05, 0x61, 0x73, 0x6b, 0x49, - 0x64, 0x12, 0x42, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x64, 0x12, 0x42, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x42, 0x0c, 0xba, 0x48, 0x09, 0x82, 0x01, 0x06, 0x18, 0x01, 0x18, 0x02, 0x18, 0x03, 0x52, 0x07, 0x76, 0x65, - 0x72, 0x64, 0x69, 0x63, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x1b, 0x0a, - 0x09, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x49, 0x64, 0x12, 0x47, 0x0a, 0x0e, 0x67, 0x75, - 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, - 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x0d, 0x67, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x4b, - 0x69, 0x6e, 0x64, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x52, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x22, 0x30, 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x78, - 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x75, 0x6e, - 0x49, 0x64, 0x22, 0x31, 0x0a, 0x0b, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x43, 0x68, 0x69, 0x6c, - 0x64, 0x12, 0x22, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x63, 0x68, - 0x69, 0x6c, 0x64, 0x49, 0x64, 0x22, 0x8c, 0x01, 0x0a, 0x05, 0x53, 0x74, 0x65, 0x65, 0x72, 0x12, - 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, - 0x65, 0x78, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x49, 0x64, 0x12, 0x28, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x12, 0x26, 0x0a, 0x0f, - 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, - 0x75, 0x6e, 0x49, 0x64, 0x22, 0x54, 0x0a, 0x0b, 0x53, 0x74, 0x65, 0x65, 0x72, 0x43, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, - 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x78, 0x70, - 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x22, 0xb9, 0x02, 0x0a, 0x14, 0x52, - 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x75, 0x6e, 0x41, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, - 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x0f, 0x65, - 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0d, 0x65, - 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x06, - 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, - 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x42, 0x0a, 0x07, - 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, - 0x61, 0x6c, 0x56, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x42, 0x0c, 0xba, 0x48, 0x09, 0x82, 0x01, - 0x06, 0x18, 0x01, 0x18, 0x02, 0x18, 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, - 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x49, 0x64, 0x12, 0x47, 0x0a, - 0x0e, 0x67, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x41, 0x70, 0x70, 0x72, 0x6f, - 0x76, 0x61, 0x6c, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x0d, 0x67, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, - 0x69, 0x6c, 0x4b, 0x69, 0x6e, 0x64, 0x22, 0x45, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, - 0x65, 0x52, 0x75, 0x6e, 0x41, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x15, 0x0a, 0x06, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x72, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x22, 0xd4, 0x01, - 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x6e, 0x41, 0x73, 0x6b, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, - 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, - 0x2f, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, 0x5f, + 0x72, 0x64, 0x69, 0x63, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x5f, + 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, + 0x49, 0x64, 0x12, 0x47, 0x0a, 0x0e, 0x67, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x5f, + 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x6d, 0x65, 0x63, + 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, + 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x0d, 0x67, 0x75, + 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x4b, 0x69, 0x6e, 0x64, 0x22, 0x45, 0x0a, 0x15, 0x52, + 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x75, 0x6e, 0x41, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x61, + 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x6b, + 0x49, 0x64, 0x22, 0xd4, 0x01, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x50, 0x6c, + 0x61, 0x6e, 0x41, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, + 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, + 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x52, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x06, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05, + 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x42, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x64, 0x69, + 0x63, 0x74, 0x42, 0x0c, 0xba, 0x48, 0x09, 0x82, 0x01, 0x06, 0x18, 0x01, 0x18, 0x02, 0x18, 0x03, + 0x52, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x22, 0x46, 0x0a, 0x16, 0x52, 0x65, 0x73, + 0x6f, 0x6c, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x6e, 0x41, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x73, + 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x6b, 0x49, + 0x64, 0x22, 0x6b, 0x0a, 0x10, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, + 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2f, 0x0a, + 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, + 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x22, 0x2a, + 0x0a, 0x11, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x75, 0x6e, 0x49, 0x64, 0x22, 0xc2, 0x02, 0x0a, 0x0f, 0x53, + 0x74, 0x65, 0x65, 0x72, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, + 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, + 0x65, 0x64, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x32, 0x0a, 0x05, 0x70, + 0x61, 0x72, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x65, 0x63, + 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x42, 0x08, + 0xba, 0x48, 0x05, 0x92, 0x01, 0x02, 0x10, 0x10, 0x52, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x12, + 0x26, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x18, 0x40, 0x52, 0x09, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x64, 0x3a, 0x66, 0xba, 0x48, 0x63, 0x1a, 0x61, 0x0a, 0x11, + 0x73, 0x74, 0x65, 0x65, 0x72, 0x5f, 0x72, 0x75, 0x6e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x12, 0x23, 0x74, 0x65, 0x78, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x61, 0x72, 0x74, + 0x73, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x6f, 0x74, 0x68, 0x20, 0x62, 0x65, + 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x27, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x74, 0x65, 0x78, + 0x74, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x20, 0x7c, 0x7c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x28, + 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x61, 0x72, 0x74, 0x73, 0x29, 0x20, 0x3e, 0x20, 0x30, 0x22, + 0x7b, 0x0a, 0x10, 0x53, 0x74, 0x65, 0x65, 0x72, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x74, 0x65, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x52, 0x07, 0x6f, + 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, + 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x64, 0x22, 0x98, 0x01, 0x0a, + 0x15, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x65, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, + 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2f, + 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, + 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x12, + 0x26, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x18, 0x40, 0x52, 0x09, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x64, 0x22, 0x81, 0x01, 0x0a, 0x16, 0x43, 0x61, 0x6e, 0x63, + 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x31, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x74, 0x65, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x52, 0x07, 0x6f, 0x75, + 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x64, 0x22, 0x84, 0x01, 0x0a, 0x26, + 0x47, 0x65, 0x74, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, + 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x32, + 0x0a, 0x10, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, - 0x01, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x75, 0x6e, 0x49, 0x64, - 0x12, 0x1e, 0x0a, 0x06, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05, 0x61, 0x73, 0x6b, 0x49, 0x64, - 0x12, 0x42, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, - 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x42, 0x0c, 0xba, - 0x48, 0x09, 0x82, 0x01, 0x06, 0x18, 0x01, 0x18, 0x02, 0x18, 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, - 0x64, 0x69, 0x63, 0x74, 0x22, 0x46, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x50, - 0x6c, 0x61, 0x6e, 0x41, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x15, - 0x0a, 0x06, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x72, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x22, 0x6b, 0x0a, 0x10, - 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x65, - 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, - 0x63, 0x74, 0x65, 0x64, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x22, 0x2a, 0x0a, 0x11, 0x43, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x15, - 0x0a, 0x06, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x72, 0x75, 0x6e, 0x49, 0x64, 0x22, 0xc2, 0x02, 0x0a, 0x0f, 0x53, 0x74, 0x65, 0x65, 0x72, 0x52, - 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, - 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, - 0x64, 0x12, 0x2f, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x75, - 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, - 0x02, 0x10, 0x01, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x75, 0x6e, - 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x32, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x42, 0x08, 0xba, 0x48, 0x05, 0x92, 0x01, - 0x02, 0x10, 0x10, 0x52, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x12, 0x26, 0x0a, 0x0a, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, - 0xba, 0x48, 0x04, 0x72, 0x02, 0x18, 0x40, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x49, 0x64, 0x3a, 0x66, 0xba, 0x48, 0x63, 0x1a, 0x61, 0x0a, 0x11, 0x73, 0x74, 0x65, 0x65, 0x72, - 0x5f, 0x72, 0x75, 0x6e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x74, 0x65, - 0x78, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x61, 0x72, 0x74, 0x73, 0x20, 0x63, 0x61, 0x6e, - 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x6f, 0x74, 0x68, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, - 0x79, 0x1a, 0x27, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x21, 0x3d, 0x20, - 0x27, 0x27, 0x20, 0x7c, 0x7c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, - 0x70, 0x61, 0x72, 0x74, 0x73, 0x29, 0x20, 0x3e, 0x20, 0x30, 0x22, 0x7b, 0x0a, 0x10, 0x53, 0x74, - 0x65, 0x65, 0x72, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, - 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x65, 0x65, - 0x72, 0x4f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x63, 0x6f, 0x6d, - 0x65, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x72, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x64, 0x22, 0x98, 0x01, 0x0a, 0x15, 0x43, 0x61, 0x6e, 0x63, - 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x0f, 0x65, 0x78, 0x70, - 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0d, 0x65, 0x78, 0x70, - 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0a, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, - 0xba, 0x48, 0x04, 0x72, 0x02, 0x18, 0x40, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x49, 0x64, 0x22, 0x81, 0x01, 0x0a, 0x16, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x75, 0x6e, - 0x53, 0x74, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, - 0x07, 0x6f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x65, 0x65, 0x72, - 0x4f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, - 0x12, 0x15, 0x0a, 0x06, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x72, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x49, 0x64, 0x22, 0x84, 0x01, 0x0a, 0x26, 0x47, 0x65, 0x74, 0x4d, 0x63, - 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, - 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x10, 0x61, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x61, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x3b, 0x0a, - 0x27, 0x47, 0x65, 0x74, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0xe8, 0x01, 0x0a, 0x1e, 0x52, - 0x65, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, - 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, - 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x10, - 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x44, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x75, 0x6d, - 0x65, 0x5f, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, - 0x75, 0x6d, 0x65, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x48, 0x00, 0x52, 0x0e, 0x72, - 0x65, 0x73, 0x75, 0x6d, 0x65, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x12, 0x2b, 0x0a, - 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x22, 0x49, 0x0a, 0x1f, 0x52, 0x65, 0x63, 0x68, 0x65, 0x63, 0x6b, - 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x22, 0xe7, 0x01, 0x0a, 0x1d, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4d, 0x63, 0x70, 0x41, 0x75, + 0x01, 0x52, 0x0f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x64, 0x22, 0x3b, 0x0a, 0x27, 0x47, 0x65, 0x74, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, + 0xe8, 0x01, 0x0a, 0x1e, 0x52, 0x65, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, @@ -20749,2258 +20780,2281 @@ var file_mecatl_v1_harness_proto_rawDesc = string([]byte{ 0x61, 0x6c, 0x12, 0x2b, 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, - 0x09, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x22, 0x48, 0x0a, 0x1e, 0x43, 0x61, - 0x6e, 0x63, 0x65, 0x6c, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x05, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x22, 0xc9, 0x01, 0x0a, 0x0d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, - 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x09, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x22, 0x49, 0x0a, 0x1f, 0x52, 0x65, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, + 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0xe7, 0x01, 0x0a, 0x1d, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, + 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x64, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x63, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, - 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, - 0x72, 0x65, 0x73, 0x41, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x21, 0x0a, - 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, - 0x22, 0x50, 0x0a, 0x17, 0x50, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x1e, 0x0a, 0x0b, 0x70, - 0x6c, 0x61, 0x6e, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x70, 0x6c, 0x61, 0x6e, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x61, - 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x6b, - 0x49, 0x64, 0x22, 0xb4, 0x09, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x71, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x73, - 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x75, 0x72, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x04, 0x74, 0x75, 0x72, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x30, 0x0a, 0x09, 0x74, 0x6f, - 0x6f, 0x6c, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6f, 0x6c, 0x43, 0x61, - 0x6c, 0x6c, 0x52, 0x08, 0x74, 0x6f, 0x6f, 0x6c, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x36, 0x0a, 0x0b, - 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, - 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0a, 0x74, 0x6f, 0x6f, 0x6c, 0x52, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x12, 0x2a, 0x0a, 0x03, 0x61, 0x73, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, - 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x6b, 0x52, 0x03, 0x61, 0x73, 0x6b, - 0x12, 0x29, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x11, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x2d, 0x0a, 0x08, 0x74, - 0x75, 0x72, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x75, 0x72, 0x6e, 0x45, 0x6e, - 0x64, 0x52, 0x07, 0x74, 0x75, 0x72, 0x6e, 0x45, 0x6e, 0x64, 0x12, 0x23, 0x0a, 0x04, 0x68, 0x6f, - 0x6f, 0x6b, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x6f, 0x6b, 0x52, 0x04, 0x68, 0x6f, 0x6f, 0x6b, 0x12, - 0x2f, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, - 0x62, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x73, 0x75, 0x62, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x12, 0x23, 0x0a, 0x04, 0x74, 0x65, 0x61, 0x6d, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x52, - 0x04, 0x74, 0x65, 0x61, 0x6d, 0x12, 0x2f, 0x0a, 0x08, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, - 0x6c, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x52, 0x08, 0x70, 0x61, - 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x12, 0x36, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, - 0x6c, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x50, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x2f, - 0x0a, 0x08, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, - 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x52, 0x08, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x12, - 0x36, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x18, 0x11, + 0x64, 0x12, 0x44, 0x0a, 0x0f, 0x72, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x5f, 0x61, 0x70, 0x70, 0x72, + 0x6f, 0x76, 0x61, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, + 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x41, 0x70, 0x70, + 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x48, 0x00, 0x52, 0x0e, 0x72, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x41, + 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x12, 0x2b, 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x22, + 0x48, 0x0a, 0x1e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x26, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0xc9, 0x01, 0x0a, 0x0d, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x12, + 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, + 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x50, 0x0a, 0x17, 0x50, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6e, + 0x74, 0x69, 0x6e, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, + 0x12, 0x1e, 0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x6e, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x6e, 0x52, 0x75, 0x6e, 0x49, 0x64, + 0x12, 0x15, 0x0a, 0x06, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x22, 0xb4, 0x09, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x65, 0x71, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x03, 0x73, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x75, 0x72, 0x6e, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x74, 0x75, 0x72, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, + 0x30, 0x0a, 0x09, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, + 0x6f, 0x6f, 0x6c, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x08, 0x74, 0x6f, 0x6f, 0x6c, 0x43, 0x61, 0x6c, + 0x6c, 0x12, 0x36, 0x0a, 0x0b, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0a, 0x74, + 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x2a, 0x0a, 0x03, 0x61, 0x73, 0x6b, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x6b, + 0x52, 0x03, 0x61, 0x73, 0x6b, 0x12, 0x29, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x12, 0x2d, 0x0a, 0x08, 0x74, 0x75, 0x72, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, + 0x75, 0x72, 0x6e, 0x45, 0x6e, 0x64, 0x52, 0x07, 0x74, 0x75, 0x72, 0x6e, 0x45, 0x6e, 0x64, 0x12, + 0x23, 0x0a, 0x04, 0x68, 0x6f, 0x6f, 0x6b, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x6f, 0x6b, 0x52, 0x04, + 0x68, 0x6f, 0x6f, 0x6b, 0x12, 0x2f, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x61, 0x67, 0x65, 0x6e, 0x74, + 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x73, 0x75, 0x62, + 0x61, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x04, 0x74, 0x65, 0x61, 0x6d, 0x18, 0x0d, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x54, 0x65, 0x61, 0x6d, 0x52, 0x04, 0x74, 0x65, 0x61, 0x6d, 0x12, 0x2f, 0x0a, 0x08, 0x70, 0x61, + 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, + 0x6c, 0x52, 0x08, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x12, 0x36, 0x0a, 0x08, 0x73, + 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, + 0x6c, 0x65, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, + 0x75, 0x6c, 0x65, 0x12, 0x2f, 0x0a, 0x08, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x18, + 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x52, 0x08, 0x61, 0x70, 0x70, 0x72, + 0x6f, 0x76, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, + 0x6d, 0x70, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, + 0x52, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x12, 0x4b, 0x0a, 0x12, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, + 0x76, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, + 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x52, 0x11, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x73, 0x74, 0x65, + 0x65, 0x72, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x65, 0x65, 0x72, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x05, + 0x73, 0x74, 0x65, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x0d, 0x73, 0x74, 0x65, 0x65, 0x72, 0x5f, 0x6f, + 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x65, 0x65, 0x72, 0x41, 0x63, + 0x6b, 0x52, 0x0c, 0x73, 0x74, 0x65, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x12, + 0x36, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x79, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x55, 0x73, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x52, 0x0a, 0x75, 0x73, 0x65, - 0x72, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x12, 0x4b, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x18, 0x12, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, - 0x65, 0x52, 0x11, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x63, - 0x68, 0x69, 0x76, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x65, 0x72, 0x18, 0x13, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x74, 0x65, 0x65, 0x72, 0x45, 0x63, 0x68, 0x6f, 0x52, 0x05, 0x73, 0x74, 0x65, 0x65, 0x72, - 0x12, 0x38, 0x0a, 0x0d, 0x73, 0x74, 0x65, 0x65, 0x72, 0x5f, 0x6f, 0x75, 0x74, 0x63, 0x6f, 0x6d, - 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x65, 0x65, 0x72, 0x41, 0x63, 0x6b, 0x52, 0x0c, 0x73, 0x74, - 0x65, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x0b, 0x6d, 0x6f, - 0x64, 0x65, 0x6c, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x79, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x65, - 0x6c, 0x52, 0x65, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x74, - 0x72, 0x79, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x16, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x72, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x05, 0x74, 0x69, 0x74, - 0x6c, 0x65, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x74, 0x6c, - 0x65, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x3e, 0x0a, 0x0d, 0x61, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x74, - 0x72, 0x6f, 0x6c, 0x5f, 0x72, 0x65, 0x66, 0x75, 0x73, 0x65, 0x64, 0x18, 0x19, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x66, 0x75, 0x73, 0x65, 0x64, 0x52, 0x0e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x66, 0x75, 0x73, 0x65, 0x64, 0x12, 0x5e, 0x0a, 0x19, - 0x70, 0x6c, 0x61, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x22, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x6e, - 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x69, 0x6c, - 0x75, 0x72, 0x65, 0x52, 0x17, 0x70, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x4a, 0x04, 0x08, 0x09, - 0x10, 0x0a, 0x52, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x22, 0x43, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, - 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x66, 0x75, 0x73, 0x65, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x61, - 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x6b, - 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x22, 0x9a, - 0x01, 0x0a, 0x0a, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x48, 0x0a, - 0x11, 0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x79, 0x44, 0x69, 0x73, 0x70, 0x6f, 0x73, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x72, 0x65, 0x74, 0x72, 0x79, 0x44, 0x69, 0x73, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x0a, 0x0f, 0x73, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x5f, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x0e, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x22, 0x68, 0x0a, 0x09, 0x53, - 0x74, 0x65, 0x65, 0x72, 0x45, 0x63, 0x68, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1d, 0x0a, 0x0a, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x05, 0x70, - 0x61, 0x72, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x05, - 0x70, 0x61, 0x72, 0x74, 0x73, 0x22, 0x8c, 0x01, 0x0a, 0x08, 0x53, 0x74, 0x65, 0x65, 0x72, 0x41, - 0x63, 0x6b, 0x12, 0x31, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x53, 0x74, 0x65, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x52, 0x07, 0x6f, 0x75, - 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, - 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x70, 0x72, 0x6f, - 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x49, 0x64, 0x22, 0x3c, 0x0a, 0x0c, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, - 0x6c, 0x52, 0x65, 0x66, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, - 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, - 0x72, 0x79, 0x22, 0xb9, 0x04, 0x0a, 0x0f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, - 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x65, - 0x77, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x61, - 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x4a, 0x6f, 0x62, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x12, 0x3e, - 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x73, 0x73, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x41, 0x73, 0x73, 0x65, 0x73, 0x73, 0x6d, 0x65, - 0x6e, 0x74, 0x52, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x73, 0x73, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x3e, - 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x41, - 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x44, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x64, 0x65, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x43, 0x6f, - 0x64, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x75, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x72, - 0x75, 0x6c, 0x65, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x72, 0x75, 0x6c, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x12, 0x2e, 0x0a, 0x13, - 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x68, 0x65, 0x63, 0x6b, - 0x65, 0x72, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, - 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x69, 0x64, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x4d, - 0x6f, 0x64, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x33, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x63, 0x65, 0x72, - 0x6e, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x52, 0x65, - 0x66, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x63, 0x65, 0x72, 0x6e, 0x73, 0x12, 0x31, 0x0a, 0x07, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, - 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, - 0x69, 0x6c, 0x52, 0x65, 0x66, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x22, 0xe2, - 0x01, 0x0a, 0x16, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x41, 0x70, 0x70, 0x72, - 0x6f, 0x76, 0x61, 0x6c, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x76, - 0x69, 0x65, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, - 0x76, 0x69, 0x65, 0x77, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, - 0x61, 0x6c, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x21, 0x0a, 0x0c, - 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, - 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4f, 0x6e, - 0x6c, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x5f, 0x61, 0x76, 0x61, - 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x72, 0x65, - 0x70, 0x65, 0x61, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x4a, 0x04, 0x08, - 0x06, 0x10, 0x07, 0x22, 0x3d, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x75, 0x61, 0x72, 0x64, - 0x72, 0x61, 0x69, 0x6c, 0x43, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x6d, 0x6f, 0x64, + 0x65, 0x6c, 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x75, 0x6e, 0x5f, 0x69, + 0x64, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x2d, + 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x3e, 0x0a, + 0x0d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x18, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, + 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x0a, + 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x72, 0x65, 0x66, 0x75, 0x73, 0x65, 0x64, + 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x66, 0x75, 0x73, 0x65, + 0x64, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x66, 0x75, 0x73, 0x65, + 0x64, 0x12, 0x5e, 0x0a, 0x19, 0x70, 0x6c, 0x61, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, + 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, 0x1a, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x52, 0x17, 0x70, 0x6c, 0x61, 0x6e, 0x43, 0x6f, + 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, + 0x65, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x52, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x22, 0x43, + 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x66, 0x75, 0x73, 0x65, 0x64, + 0x12, 0x15, 0x0a, 0x06, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, + 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, + 0x6f, 0x72, 0x79, 0x22, 0x9a, 0x01, 0x0a, 0x0a, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x74, + 0x72, 0x79, 0x12, 0x48, 0x0a, 0x11, 0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x64, 0x69, 0x73, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x79, 0x44, + 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x72, 0x65, 0x74, 0x72, + 0x79, 0x44, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x0a, 0x0f, + 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, + 0x52, 0x0e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, + 0x22, 0x68, 0x0a, 0x09, 0x53, 0x74, 0x65, 0x65, 0x72, 0x45, 0x63, 0x68, 0x6f, 0x12, 0x12, 0x0a, + 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, + 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x64, + 0x12, 0x28, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x52, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x22, 0x8c, 0x01, 0x0a, 0x08, 0x53, + 0x74, 0x65, 0x65, 0x72, 0x41, 0x63, 0x6b, 0x12, 0x31, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x63, 0x6f, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x65, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x63, 0x6f, 0x6d, + 0x65, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, + 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x08, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x64, 0x22, 0x3c, 0x0a, 0x0c, 0x47, 0x75, 0x61, + 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x66, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12, 0x1a, 0x0a, 0x08, 0x63, + 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, + 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x22, 0xb9, 0x04, 0x0a, 0x0f, 0x47, 0x75, 0x61, 0x72, + 0x64, 0x72, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x12, 0x1b, 0x0a, 0x09, 0x72, + 0x65, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x4a, 0x6f, 0x62, 0x52, 0x03, + 0x6a, 0x6f, 0x62, 0x12, 0x3e, 0x0a, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x73, 0x73, 0x6d, 0x65, 0x6e, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x41, 0x73, 0x73, + 0x65, 0x73, 0x73, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0a, 0x61, 0x73, 0x73, 0x65, 0x73, 0x73, 0x6d, + 0x65, 0x6e, 0x74, 0x12, 0x3e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x49, 0x6e, 0x73, + 0x70, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x41, 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x44, 0x69, + 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, + 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x75, 0x6c, 0x65, 0x49, 0x64, + 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x75, 0x6c, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, + 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, + 0x64, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x5f, 0x6d, 0x6f, 0x64, + 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x68, 0x65, + 0x63, 0x6b, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x33, 0x0a, 0x08, 0x63, + 0x6f, 0x6e, 0x63, 0x65, 0x72, 0x6e, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, + 0x61, 0x69, 0x6c, 0x52, 0x65, 0x66, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x63, 0x65, 0x72, 0x6e, 0x73, + 0x12, 0x31, 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, + 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x66, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x22, 0xe2, 0x01, 0x0a, 0x16, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, + 0x6c, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x1b, + 0x0a, 0x09, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x04, 0x6b, + 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x41, + 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, + 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x64, 0x69, 0x67, 0x65, 0x73, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x44, 0x69, + 0x67, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x70, 0x65, 0x61, + 0x74, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x22, 0x3d, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, + 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x43, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x67, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x93, 0x02, 0x0a, 0x16, 0x47, 0x75, 0x61, 0x72, + 0x64, 0x72, 0x61, 0x69, 0x6c, 0x43, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x6f, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x74, 0x6f, 0x6f, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x03, + 0x6a, 0x6f, 0x62, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x4a, + 0x6f, 0x62, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x72, + 0x75, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x75, + 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x6f, 0x72, 0x69, + 0x67, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x75, 0x6c, 0x65, 0x4f, + 0x72, 0x69, 0x67, 0x69, 0x6e, 0x12, 0x3e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x49, + 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x70, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0xd0, 0x01, + 0x0a, 0x1d, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x43, + 0x6f, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x68, 0x65, + 0x63, 0x6b, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x68, 0x65, + 0x63, 0x6b, 0x65, 0x72, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, + 0x6c, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x43, 0x6f, 0x76, 0x65, 0x72, 0x61, + 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, + 0x22, 0x5d, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, + 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x49, 0x64, 0x22, 0x93, 0x02, 0x0a, 0x16, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, - 0x43, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x12, 0x0a, - 0x04, 0x74, 0x6f, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x6f, 0x6f, - 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x4a, 0x6f, 0x62, 0x52, 0x03, 0x6a, - 0x6f, 0x62, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x69, - 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x75, 0x6c, 0x65, 0x49, 0x64, 0x12, - 0x1f, 0x0a, 0x0b, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x75, 0x6c, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, - 0x12, 0x3e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0xd0, 0x01, 0x0a, 0x1d, 0x4c, 0x69, 0x73, - 0x74, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x43, 0x6f, 0x76, 0x65, 0x72, 0x61, - 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x5f, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x11, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x5f, - 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, - 0x63, 0x68, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x3b, - 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x21, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x61, 0x72, - 0x64, 0x72, 0x61, 0x69, 0x6c, 0x43, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0x5d, 0x0a, 0x1f, 0x47, - 0x65, 0x74, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x76, 0x69, 0x65, - 0x77, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, - 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1b, 0x0a, - 0x09, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x49, 0x64, 0x22, 0xa1, 0x01, 0x0a, 0x20, 0x47, - 0x65, 0x74, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x76, 0x69, 0x65, - 0x77, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, - 0x63, 0x6f, 0x6e, 0x63, 0x65, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, - 0x6f, 0x6e, 0x63, 0x65, 0x72, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x1f, 0x0a, - 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x65, 0x78, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xb6, - 0x01, 0x0a, 0x08, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x12, 0x15, 0x0a, 0x06, 0x61, - 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, 0x6b, - 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x6f, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x74, 0x6f, 0x6f, 0x6c, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, - 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x12, - 0x34, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, - 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x52, 0x07, 0x76, 0x65, - 0x72, 0x64, 0x69, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x4a, 0x04, 0x08, - 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x5f, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x22, 0x68, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x50, - 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x28, 0x0a, 0x05, 0x70, 0x61, 0x72, - 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x70, 0x61, - 0x72, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x79, 0x6e, 0x74, 0x68, 0x65, 0x74, 0x69, 0x63, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x79, 0x6e, 0x74, 0x68, 0x65, 0x74, 0x69, - 0x63, 0x22, 0x4f, 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, - 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x3a, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, - 0x65, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, - 0x65, 0x64, 0x22, 0xc4, 0x02, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, - 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, - 0x78, 0x74, 0x12, 0x32, 0x0a, 0x0a, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6f, 0x6c, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x09, 0x74, 0x6f, 0x6f, - 0x6c, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x12, 0x36, 0x0a, 0x0b, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x52, 0x0a, 0x74, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1c, - 0x0a, 0x09, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x25, 0x0a, 0x0e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x50, 0x68, - 0x61, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x12, 0x2a, 0x0a, - 0x11, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x5f, - 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, - 0x69, 0x6e, 0x67, 0x49, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x22, 0xa8, 0x01, 0x0a, 0x0f, 0x53, 0x63, - 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x23, 0x0a, - 0x0d, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x69, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x72, 0x65, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, - 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, - 0x0a, 0x04, 0x73, 0x74, 0x6f, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x74, - 0x6f, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x65, 0x72, 0x72, 0x22, 0xb8, 0x01, 0x0a, 0x04, 0x48, 0x6f, 0x6f, 0x6b, 0x12, 0x14, 0x0a, - 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x68, - 0x61, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x6f, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x74, 0x6f, 0x6f, 0x6c, 0x12, 0x33, 0x0a, 0x08, 0x64, 0x65, 0x63, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x6f, 0x6b, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, - 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, - 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x67, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, - 0x69, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x52, 0x65, - 0x76, 0x69, 0x65, 0x77, 0x52, 0x09, 0x67, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x22, - 0xb8, 0x03, 0x0a, 0x0f, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x63, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x29, 0x0a, - 0x10, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x69, 0x66, 0x69, 0x65, 0x72, 0x5f, 0x6d, 0x6f, 0x64, 0x65, - 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x69, 0x66, - 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x61, 0x6e, 0x64, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x43, - 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x61, 0x6e, 0x64, 0x69, - 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x63, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x6c, - 0x12, 0x23, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, - 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x32, 0x0a, 0x12, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, - 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x01, 0x48, 0x01, 0x52, 0x11, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x75, 0x74, - 0x63, 0x6f, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x63, - 0x6f, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x74, 0x69, - 0x76, 0x65, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x11, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x74, 0x69, 0x76, 0x65, 0x4d, 0x69, 0x73, 0x73, - 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x69, 0x73, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6d, 0x69, 0x73, 0x73, 0x4c, 0x69, 0x6d, 0x69, - 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x65, 0x72, 0x5f, 0x6f, 0x70, 0x65, - 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x65, 0x72, - 0x4f, 0x70, 0x65, 0x6e, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, - 0x6e, 0x63, 0x65, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x81, 0x06, 0x0a, 0x08, 0x53, - 0x75, 0x62, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x61, 0x72, 0x65, 0x6e, - 0x74, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x19, 0x0a, - 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x6f, 0x61, 0x6c, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x67, 0x6f, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x09, - 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x74, 0x6f, 0x6f, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x45, - 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x6f, 0x6f, 0x6c, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, - 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, - 0x74, 0x6f, 0x70, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x74, 0x6f, 0x70, 0x12, - 0x1f, 0x0a, 0x0b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x73, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, - 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x0a, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x6e, 0x64, - 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x74, 0x65, 0x67, - 0x6f, 0x72, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x6f, 0x75, 0x74, 0x65, - 0x64, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, - 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x6f, 0x64, - 0x65, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x5f, 0x6b, 0x69, 0x6e, 0x64, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x4b, 0x69, 0x6e, - 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x18, - 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x14, 0x0a, - 0x05, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x61, - 0x75, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x6f, 0x75, - 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x4a, 0x0a, 0x10, 0x72, 0x6f, - 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x13, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x48, 0x00, 0x52, 0x0f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x63, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x2b, 0x0a, 0x12, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, - 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x54, 0x6f, 0x6f, 0x6c, 0x43, 0x61, 0x6c, - 0x6c, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, - 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, + 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x49, 0x64, 0x22, + 0xa1, 0x01, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, + 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x49, + 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x63, 0x65, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x63, 0x65, 0x72, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x69, 0x73, 0x70, 0x6c, + 0x61, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x65, 0x78, 0x74, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0xb6, 0x01, 0x0a, 0x08, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, + 0x12, 0x15, 0x0a, 0x06, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x6f, 0x6f, 0x6c, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x6f, 0x6f, 0x6c, 0x12, 0x17, 0x0a, 0x07, 0x63, + 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x61, + 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x64, 0x69, 0x63, + 0x74, 0x52, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x72, + 0x69, 0x67, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x72, 0x69, 0x67, + 0x69, 0x6e, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x52, 0x0c, + 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x22, 0x68, 0x0a, 0x0a, + 0x55, 0x73, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, + 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x28, + 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x52, 0x05, 0x70, 0x61, 0x72, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x79, 0x6e, 0x74, + 0x68, 0x65, 0x74, 0x69, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x79, 0x6e, + 0x74, 0x68, 0x65, 0x74, 0x69, 0x63, 0x22, 0x4f, 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x3a, 0x0a, 0x08, 0x72, + 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x72, + 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x22, 0xc4, 0x02, 0x0a, 0x13, 0x43, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, + 0x6f, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x32, 0x0a, 0x0a, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, + 0x63, 0x61, 0x6c, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6f, 0x6c, 0x43, 0x61, 0x6c, 0x6c, + 0x52, 0x09, 0x74, 0x6f, 0x6f, 0x6c, 0x43, 0x61, 0x6c, 0x6c, 0x73, 0x12, 0x36, 0x0a, 0x0b, 0x74, + 0x6f, 0x6f, 0x6c, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x6f, + 0x6c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0a, 0x74, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x69, 0x6e, 0x67, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x69, 0x6e, + 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x68, + 0x61, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x50, 0x68, 0x61, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x74, + 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x70, 0x61, 0x72, + 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x5f, + 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x22, 0xa8, + 0x01, 0x0a, 0x0f, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x50, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x63, 0x68, 0x65, 0x64, + 0x75, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x69, 0x72, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x72, 0x65, 0x49, 0x64, + 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, + 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x74, 0x6f, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x73, 0x74, 0x6f, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x65, 0x72, 0x72, 0x22, 0xb8, 0x01, 0x0a, 0x04, 0x48, 0x6f, + 0x6f, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x6f, 0x6f, 0x6c, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x6f, 0x6f, 0x6c, 0x12, 0x33, 0x0a, 0x08, + 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, + 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x6f, 0x6b, 0x44, + 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x63, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x67, 0x75, + 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, + 0x61, 0x69, 0x6c, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x09, 0x67, 0x75, 0x61, 0x72, 0x64, + 0x72, 0x61, 0x69, 0x6c, 0x22, 0xb8, 0x03, 0x0a, 0x0f, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, + 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, + 0x65, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, + 0x6e, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6c, + 0x61, 0x73, 0x73, 0x69, 0x66, 0x69, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x2d, 0x0a, + 0x12, 0x63, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x61, 0x74, 0x65, 0x67, + 0x6f, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x61, 0x6e, 0x64, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x27, 0x0a, 0x0f, + 0x63, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, + 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x23, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, + 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x32, 0x0a, 0x12, 0x6d, 0x69, + 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x48, 0x01, 0x52, 0x11, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, + 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x88, 0x01, 0x01, 0x12, 0x18, + 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x6f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x6f, 0x6e, 0x73, + 0x65, 0x63, 0x75, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x65, 0x73, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x74, 0x69, 0x76, + 0x65, 0x4d, 0x69, 0x73, 0x73, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x69, 0x73, 0x73, 0x5f, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6d, 0x69, 0x73, + 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x65, + 0x72, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x62, 0x72, + 0x65, 0x61, 0x6b, 0x65, 0x72, 0x4f, 0x70, 0x65, 0x6e, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x6d, 0x69, 0x6e, + 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x22, + 0x81, 0x06, 0x0a, 0x08, 0x53, 0x75, 0x62, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0e, + 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x61, 0x6c, 0x6c, + 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x67, 0x6f, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x67, 0x6f, 0x61, + 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x6f, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, + 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x07, 0x69, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x6f, + 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, + 0x6f, 0x6f, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x05, 0x75, 0x73, 0x61, 0x67, + 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x73, 0x74, 0x6f, 0x70, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x73, 0x74, 0x6f, 0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x6d, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x4d, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, + 0x75, 0x6e, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x62, 0x61, 0x63, 0x6b, 0x67, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, 0x5f, + 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x21, + 0x0a, 0x0c, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x65, + 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x6e, 0x6e, 0x65, 0x72, + 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6e, 0x6e, + 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x0f, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, + 0x74, 0x61, 0x69, 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x74, 0x61, + 0x69, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x63, 0x61, 0x75, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x6f, 0x75, 0x74, + 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, + 0x4a, 0x0a, 0x10, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x63, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x63, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, + 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x2b, 0x0a, 0x12, 0x63, + 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, + 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x54, 0x6f, + 0x6f, 0x6c, 0x43, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x18, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, + 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, + 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x42, 0x13, + 0x0a, 0x11, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x63, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x22, 0xa8, 0x03, 0x0a, 0x0e, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, + 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, + 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x1a, + 0x0a, 0x08, 0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x08, 0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x65, + 0x61, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x6c, 0x65, 0x61, 0x64, 0x12, 0x27, + 0x0a, 0x0f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, + 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, 0x43, + 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x6f, 0x75, 0x74, 0x65, + 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, + 0x64, 0x65, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, + 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, + 0x67, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x4a, 0x0a, 0x10, 0x72, 0x6f, 0x75, 0x74, 0x69, + 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, + 0x75, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, + 0x0f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x88, 0x01, 0x01, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x18, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x72, 0x5f, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x16, 0x20, 0x01, 0x28, + 0x65, 0x72, 0x5f, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x72, 0x6f, - 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xa8, - 0x03, 0x0a, 0x0e, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x70, 0x65, - 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x75, 0x74, - 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6d, 0x75, 0x74, - 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x65, 0x61, 0x64, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x04, 0x6c, 0x65, 0x61, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, - 0x72, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, - 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, - 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x25, 0x0a, 0x0e, 0x72, - 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x73, - 0x6f, 0x6e, 0x12, 0x4a, 0x0a, 0x10, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, - 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, - 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, - 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0f, 0x72, 0x6f, 0x75, 0x74, - 0x69, 0x6e, 0x67, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1a, - 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x18, 0x65, 0x78, - 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x61, - 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x65, 0x78, - 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x61, 0x74, 0x65, - 0x67, 0x6f, 0x72, 0x79, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, - 0x5f, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xc5, 0x05, 0x0a, 0x04, 0x54, 0x65, - 0x61, 0x6d, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x61, 0x6c, - 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x65, - 0x6e, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, - 0x64, 0x12, 0x31, 0x0a, 0x06, 0x72, 0x6f, 0x73, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, - 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x70, 0x65, 0x63, 0x52, 0x06, 0x72, 0x6f, - 0x73, 0x74, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, - 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, - 0x65, 0x78, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, - 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x6f, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, - 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, - 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x06, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x74, 0x6f, 0x70, 0x18, - 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x74, 0x6f, 0x70, 0x12, 0x26, 0x0a, 0x05, 0x75, - 0x73, 0x61, 0x67, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x75, 0x73, - 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x75, - 0x73, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x55, 0x73, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, - 0x74, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x29, 0x0a, - 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, - 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x54, 0x61, 0x73, - 0x6b, 0x52, 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x32, 0x0a, 0x08, 0x66, 0x69, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x46, 0x69, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x44, 0x0a, 0x0c, - 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x11, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, - 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x44, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x14, - 0x0a, 0x05, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, - 0x61, 0x75, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x12, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x6f, - 0x6f, 0x6c, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x54, 0x6f, 0x6f, 0x6c, 0x43, 0x61, 0x6c, 0x6c, 0x49, - 0x64, 0x22, 0xb5, 0x07, 0x0a, 0x08, 0x50, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x12, 0x24, - 0x0a, 0x0e, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x61, - 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6a, 0x6f, 0x69, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6a, 0x6f, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, - 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0b, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, - 0x21, 0x0a, 0x0c, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x6c, 0x61, 0x62, - 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, - 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x6f, 0x61, 0x6c, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x67, 0x6f, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x6f, - 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, - 0x6f, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x6f, 0x6f, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, - 0x12, 0x16, 0x0a, 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x74, 0x6f, 0x70, - 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x74, 0x6f, 0x70, 0x12, 0x26, 0x0a, 0x05, - 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x75, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x6d, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x4d, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x18, - 0x10, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x77, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x0a, - 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x64, 0x5f, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x13, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, - 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x65, - 0x6c, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, 0x4d, - 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x15, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x6e, - 0x6e, 0x65, 0x72, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, - 0x74, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x16, 0x0a, - 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x18, 0x18, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, - 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x19, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x72, - 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x4a, 0x0a, 0x10, - 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x63, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x2b, 0x0a, 0x12, 0x63, 0x68, 0x69, 0x6c, - 0x64, 0x5f, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x1b, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x54, 0x6f, 0x6f, 0x6c, 0x43, - 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x12, 0x38, 0x0a, 0x18, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x72, 0x6f, - 0x75, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x1d, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x16, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x42, 0x13, 0x0a, 0x11, 0x5f, - 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x4a, 0x04, 0x08, 0x0c, 0x10, 0x0d, 0x4a, 0x04, 0x08, 0x11, 0x10, 0x12, 0x52, 0x09, 0x77, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x10, 0x77, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x5f, - 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x42, 0x0a, 0x08, 0x54, 0x6f, 0x6f, - 0x6c, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x67, - 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x22, 0xba, 0x01, - 0x0a, 0x0a, 0x54, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x17, 0x0a, 0x07, - 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, - 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, - 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x07, 0x69, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x2f, 0x0a, 0x06, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x73, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, - 0x72, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0xd1, 0x01, 0x0a, 0x0d, 0x50, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x73, 0x6b, 0x12, 0x15, 0x0a, 0x06, - 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x73, - 0x6b, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x6f, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x74, 0x6f, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x09, 0x67, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x41, 0x70, 0x70, 0x72, - 0x6f, 0x76, 0x61, 0x6c, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x52, 0x09, 0x67, 0x75, 0x61, 0x72, 0x64, - 0x72, 0x61, 0x69, 0x6c, 0x12, 0x1c, 0x0a, 0x07, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x88, - 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x22, 0xc1, - 0x02, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x74, 0x6f, - 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x74, 0x6f, 0x70, 0x12, 0x12, 0x0a, - 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, - 0x74, 0x12, 0x26, 0x0a, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xc5, + 0x05, 0x0a, 0x04, 0x54, 0x65, 0x61, 0x6d, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x61, 0x72, 0x65, 0x6e, + 0x74, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x17, 0x0a, + 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x06, 0x72, 0x6f, 0x73, 0x74, 0x65, 0x72, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x70, 0x65, + 0x63, 0x52, 0x06, 0x72, 0x6f, 0x73, 0x74, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x74, 0x65, 0x78, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x6f, 0x6c, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x45, + 0x72, 0x72, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x12, 0x0a, 0x04, + 0x73, 0x74, 0x6f, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x74, 0x6f, 0x70, + 0x12, 0x26, 0x0a, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x73, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x0e, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x57, 0x69, 0x6e, 0x64, + 0x6f, 0x77, 0x12, 0x29, 0x0a, 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, + 0x61, 0x6d, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x32, 0x0a, + 0x08, 0x66, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, + 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x73, 0x12, 0x44, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x44, 0x69, + 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x12, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0f, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x13, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x63, 0x61, 0x75, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x12, 0x63, 0x68, 0x69, + 0x6c, 0x64, 0x5f, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, + 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x54, 0x6f, 0x6f, 0x6c, + 0x43, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x22, 0xb5, 0x07, 0x0a, 0x08, 0x50, 0x61, 0x72, 0x61, 0x6c, + 0x6c, 0x65, 0x6c, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x61, + 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x61, 0x72, + 0x65, 0x6e, 0x74, 0x43, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x6a, 0x6f, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6a, 0x6f, 0x69, + 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x62, 0x72, 0x61, 0x6e, + 0x63, 0x68, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x72, 0x61, 0x6e, 0x63, + 0x68, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, + 0x72, 0x61, 0x6e, 0x63, 0x68, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x6f, + 0x61, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x67, 0x6f, 0x61, 0x6c, 0x12, 0x1b, + 0x0a, 0x09, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x74, 0x6f, 0x6f, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x69, + 0x73, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, + 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x6f, 0x6f, 0x6c, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x73, 0x74, 0x6f, 0x70, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x74, 0x6f, + 0x70, 0x12, 0x26, 0x0a, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x61, - 0x67, 0x65, 0x52, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, - 0x4d, 0x0a, 0x11, 0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x74, 0x72, 0x79, 0x44, 0x69, 0x73, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x10, 0x72, 0x65, 0x74, 0x72, 0x79, - 0x44, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x47, - 0x0a, 0x0f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x48, 0x01, 0x52, 0x0e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x72, 0x6f, 0x67, - 0x72, 0x65, 0x73, 0x73, 0x88, 0x01, 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x72, 0x65, 0x74, 0x72, - 0x79, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x12, 0x0a, - 0x10, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, - 0x73, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x52, 0x09, 0x70, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, - 0x6e, 0x74, 0x22, 0x70, 0x0a, 0x07, 0x54, 0x75, 0x72, 0x6e, 0x45, 0x6e, 0x64, 0x12, 0x26, 0x0a, - 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, - 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, - 0x75, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, - 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x73, 0x74, 0x69, 0x6d, - 0x61, 0x74, 0x65, 0x64, 0x22, 0xd4, 0x01, 0x0a, 0x05, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21, - 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, - 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, - 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, - 0x72, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x0f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x52, 0x65, 0x61, 0x64, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x77, 0x72, 0x69, 0x74, - 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, - 0x63, 0x61, 0x63, 0x68, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, - 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x6f, - 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x72, 0x65, 0x61, 0x73, - 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x22, 0xd1, 0x01, 0x0a, 0x0b, - 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, - 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, - 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, + 0x67, 0x65, 0x52, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x69, + 0x6e, 0x6e, 0x65, 0x72, 0x18, 0x10, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x77, 0x69, 0x6e, 0x6e, + 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x12, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, 0x64, 0x12, 0x27, 0x0a, + 0x0f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, + 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, 0x43, 0x61, + 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x64, + 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, + 0x65, 0x6c, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, + 0x1d, 0x0a, 0x0a, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x16, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x12, + 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, + 0x78, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x18, 0x18, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x6f, + 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x19, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x73, 0x6f, + 0x6e, 0x12, 0x4a, 0x0a, 0x10, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x63, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x44, + 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0f, 0x72, 0x6f, 0x75, 0x74, 0x69, + 0x6e, 0x67, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x2b, 0x0a, + 0x12, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x63, 0x61, 0x6c, 0x6c, + 0x5f, 0x69, 0x64, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x68, 0x69, 0x6c, 0x64, + 0x54, 0x6f, 0x6f, 0x6c, 0x43, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x18, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, + 0x69, 0x74, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, + 0x72, 0x79, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, + 0x69, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, + 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x63, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x4a, 0x04, 0x08, 0x0c, 0x10, 0x0d, 0x4a, 0x04, 0x08, 0x11, 0x10, + 0x12, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x10, 0x77, 0x69, + 0x6e, 0x6e, 0x65, 0x72, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x42, + 0x0a, 0x08, 0x54, 0x6f, 0x6f, 0x6c, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x61, 0x72, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, + 0x67, 0x73, 0x22, 0xba, 0x01, 0x0a, 0x0a, 0x54, 0x6f, 0x6f, 0x6c, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x63, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, + 0x2f, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, + 0x12, 0x2d, 0x0a, 0x12, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x64, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x73, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, + 0xd1, 0x01, 0x0a, 0x0d, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x73, + 0x6b, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x61, 0x73, 0x6b, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x6f, 0x6f, 0x6c, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x6f, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, + 0x61, 0x72, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, + 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x09, 0x67, 0x75, 0x61, 0x72, + 0x64, 0x72, 0x61, 0x69, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, + 0x6c, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x52, 0x09, + 0x67, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x12, 0x1c, 0x0a, 0x07, 0x63, 0x61, 0x6c, + 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, + 0x6c, 0x6c, 0x49, 0x64, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x63, 0x61, 0x6c, 0x6c, + 0x5f, 0x69, 0x64, 0x22, 0xc1, 0x02, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x73, 0x74, 0x6f, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x74, + 0x6f, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x26, 0x0a, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x12, 0x4d, 0x0a, 0x11, 0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x64, 0x69, + 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x74, 0x72, + 0x79, 0x44, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x10, + 0x72, 0x65, 0x74, 0x72, 0x79, 0x44, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x88, 0x01, 0x01, 0x12, 0x47, 0x0a, 0x0f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x70, 0x72, + 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x50, + 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x48, 0x01, 0x52, 0x0e, 0x73, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x88, 0x01, 0x01, 0x42, 0x14, 0x0a, 0x12, + 0x5f, 0x72, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x70, 0x72, + 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x52, 0x09, 0x70, 0x65, + 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x22, 0x70, 0x0a, 0x07, 0x54, 0x75, 0x72, 0x6e, 0x45, + 0x6e, 0x64, 0x12, 0x26, 0x0a, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, + 0x61, 0x67, 0x65, 0x52, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0a, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x65, + 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x65, 0x73, 0x74, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x64, 0x22, 0xd4, 0x01, 0x0a, 0x05, 0x55, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x69, 0x6e, 0x70, 0x75, 0x74, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6f, + 0x75, 0x74, 0x70, 0x75, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x52, 0x65, 0x61, + 0x64, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x10, 0x63, 0x61, 0x63, 0x68, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x69, + 0x6e, 0x67, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, + 0x22, 0xd1, 0x01, 0x0a, 0x0b, 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, + 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, + 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6d, 0x65, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, + 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, + 0x4f, 0x6e, 0x6c, 0x79, 0x22, 0x31, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x50, 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, 0x4d, + 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x22, 0x6c, 0x0a, 0x13, 0x4d, 0x63, 0x70, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, + 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, + 0x72, 0x69, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, + 0x65, 0x78, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6c, 0x6f, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x04, 0x62, 0x6c, 0x6f, 0x62, 0x22, 0x54, 0x0a, 0x16, 0x52, 0x65, 0x61, 0x64, 0x4d, + 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1f, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x12, 0x19, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, 0x75, 0x72, 0x69, 0x22, 0x55, 0x0a, + 0x17, 0x52, 0x65, 0x61, 0x64, 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, + 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7b, 0x0a, 0x11, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, + 0x74, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, + 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x64, 0x22, 0xab, 0x01, 0x0a, 0x09, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, + 0x69, 0x74, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, + 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x0a, 0x09, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x41, 0x72, 0x67, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x09, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, + 0x2f, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x22, 0x48, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, + 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x70, 0x72, + 0x6f, 0x6d, 0x70, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, + 0x74, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x73, 0x22, 0xde, 0x01, 0x0a, 0x13, 0x47, + 0x65, 0x74, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x4b, 0x0a, 0x09, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x2e, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x09, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x3c, 0x0a, + 0x0e, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3a, 0x0a, 0x10, 0x4d, + 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, + 0x6f, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0x71, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4d, 0x63, + 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, - 0x7a, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x22, - 0x31, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x22, 0x50, 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, - 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x63, - 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x22, 0x6c, 0x0a, 0x13, 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x75, - 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x1b, 0x0a, - 0x09, 0x6d, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, - 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x12, - 0x0a, 0x04, 0x62, 0x6c, 0x6f, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6c, - 0x6f, 0x62, 0x22, 0x54, 0x0a, 0x16, 0x52, 0x65, 0x61, 0x64, 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x06, - 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, - 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x19, 0x0a, - 0x03, 0x75, 0x72, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, - 0x02, 0x10, 0x01, 0x52, 0x03, 0x75, 0x72, 0x69, 0x22, 0x55, 0x0a, 0x17, 0x52, 0x65, 0x61, 0x64, - 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, - 0x7b, 0x0a, 0x11, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x41, 0x72, 0x67, 0x75, - 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, - 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x22, 0xab, 0x01, 0x0a, - 0x09, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3a, - 0x0a, 0x09, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x63, - 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, - 0x09, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x2f, 0x0a, 0x15, 0x4c, 0x69, - 0x73, 0x74, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x48, 0x0a, 0x16, 0x4c, - 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x73, + 0x6e, 0x12, 0x37, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x22, 0x69, 0x0a, 0x0d, 0x4d, 0x63, + 0x70, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, + 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, + 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x22, 0xb9, 0x01, 0x0a, 0x09, 0x4d, 0x63, 0x70, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x32, 0x0a, 0x07, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x63, 0x70, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, + 0x20, 0x0a, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x06, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, + 0x73, 0x22, 0x17, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x9c, 0x01, 0x0a, 0x16, 0x4c, + 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x52, 0x07, 0x70, 0x72, - 0x6f, 0x6d, 0x70, 0x74, 0x73, 0x22, 0xde, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4d, 0x63, 0x70, - 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, - 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, - 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x1b, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, - 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x09, 0x61, - 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x63, - 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x41, - 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x61, - 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x3c, 0x0a, 0x0e, 0x41, 0x72, 0x67, 0x75, - 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3a, 0x0a, 0x10, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, - 0x6d, 0x70, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, - 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, - 0x78, 0x74, 0x22, 0x71, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, - 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x08, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x63, 0x70, 0x50, 0x72, - 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x73, 0x22, 0x69, 0x0a, 0x0d, 0x4d, 0x63, 0x70, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, - 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1c, 0x0a, 0x09, - 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x22, 0xb9, 0x01, 0x0a, 0x09, 0x4d, 0x63, 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, - 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x32, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x63, 0x70, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x69, - 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0b, 0x64, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x22, 0x17, 0x0a, 0x15, - 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x9c, 0x01, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, - 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x2e, 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x63, - 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, - 0x73, 0x74, 0x61, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x73, 0x74, 0x61, - 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x69, 0x6e, - 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, - 0x6c, 0x69, 0x6e, 0x67, 0x22, 0x42, 0x0a, 0x18, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4d, - 0x63, 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x51, 0x0a, 0x19, 0x52, 0x65, 0x66, 0x72, - 0x65, 0x73, 0x68, 0x4d, 0x63, 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x22, 0x1b, 0x0a, 0x19, 0x4c, - 0x69, 0x73, 0x74, 0x54, 0x6f, 0x6f, 0x6c, 0x48, 0x69, 0x76, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x34, 0x0a, 0x1a, 0x4c, 0x69, 0x73, 0x74, - 0x54, 0x6f, 0x6f, 0x6c, 0x48, 0x69, 0x76, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x22, 0xac, - 0x01, 0x0a, 0x09, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6f, 0x6c, - 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x12, 0x27, - 0x0a, 0x0f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x6f, 0x64, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x22, 0x13, 0x0a, - 0x11, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x22, 0x42, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x61, 0x67, 0x65, 0x6e, - 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, - 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3f, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x76, 0x31, 0x2e, 0x4d, 0x63, 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x07, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x72, 0x65, 0x63, 0x6f, 0x6e, + 0x63, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x72, 0x65, + 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x22, 0x42, 0x0a, 0x18, 0x52, 0x65, 0x66, + 0x72, 0x65, 0x73, 0x68, 0x4d, 0x63, 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, + 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x51, 0x0a, + 0x19, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4d, 0x63, 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, + 0x22, 0x1b, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x6f, 0x6c, 0x48, 0x69, 0x76, 0x65, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x34, 0x0a, + 0x1a, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x6f, 0x6c, 0x48, 0x69, 0x76, 0x65, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x73, 0x22, 0xac, 0x01, 0x0a, 0x09, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3d, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x43, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, - 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x46, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, - 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x12, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, - 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x22, 0x98, - 0x01, 0x0a, 0x08, 0x57, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73, - 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, - 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, - 0x61, 0x62, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, - 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x61, 0x72, 0x65, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x04, 0x62, 0x61, 0x72, 0x65, 0x22, 0x3e, 0x0a, 0x14, 0x4c, 0x69, 0x73, - 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x4a, 0x0a, 0x15, 0x4c, 0x69, 0x73, - 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x31, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, - 0x74, 0x72, 0x65, 0x65, 0x73, 0x22, 0xaa, 0x01, 0x0a, 0x09, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x49, - 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x67, 0x65, - 0x6e, 0x74, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, - 0x61, 0x67, 0x65, 0x6e, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x77, - 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x61, - 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x22, 0x13, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x42, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x53, - 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, - 0x06, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x22, 0x5b, 0x0a, 0x0d, 0x53, - 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, - 0x73, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x34, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, - 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x48, - 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x50, 0x0a, 0x14, 0x52, 0x65, 0x61, 0x64, - 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, - 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, - 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, - 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x31, 0x0a, 0x15, 0x52, 0x65, - 0x61, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0xe4, 0x01, - 0x0a, 0x08, 0x53, 0x6f, 0x75, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, - 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x79, - 0x74, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x18, 0x0a, 0x07, 0x70, - 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, - 0x65, 0x73, 0x65, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, - 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x6c, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x6e, - 0x61, 0x6e, 0x63, 0x65, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x07, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x72, - 0x69, 0x66, 0x74, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x72, 0x69, - 0x66, 0x74, 0x65, 0x64, 0x22, 0x10, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x6f, 0x75, 0x6c, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x53, 0x6f, 0x75, - 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x04, 0x73, 0x6f, 0x75, - 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x73, 0x6f, - 0x75, 0x6c, 0x22, 0x44, 0x0a, 0x0e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x27, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x55, - 0x73, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x22, 0xfe, 0x02, 0x0a, 0x11, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, - 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, - 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x6f, - 0x72, 0x69, 0x67, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x72, 0x69, - 0x67, 0x69, 0x6e, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, - 0x2c, 0x0a, 0x12, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, - 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x39, 0x0a, - 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x14, 0x0a, + 0x05, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, + 0x6f, 0x6c, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x70, 0x65, + 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6f, 0x6c, + 0x6f, 0x72, 0x22, 0x13, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x42, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x41, + 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, + 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3f, 0x0a, 0x07, 0x43, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3d, 0x0a, 0x13, + 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, + 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x46, 0x0a, 0x14, 0x4c, + 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x6e, 0x64, 0x73, 0x22, 0x98, 0x01, 0x0a, 0x08, 0x57, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, + 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, + 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1a, + 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x61, + 0x72, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x62, 0x61, 0x72, 0x65, 0x22, 0x3e, + 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, + 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x4a, + 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x74, + 0x72, 0x65, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x65, 0x63, + 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x52, + 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x73, 0x22, 0xaa, 0x01, 0x0a, 0x09, 0x53, + 0x6b, 0x69, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, + 0x0a, 0x0b, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0a, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x4f, 0x77, 0x6e, 0x65, 0x64, 0x12, + 0x1f, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, + 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x13, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x53, + 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x42, 0x0a, 0x12, + 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x6b, 0x69, 0x6c, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x73, + 0x22, 0x5b, 0x0a, 0x0d, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x73, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x34, 0x0a, + 0x15, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x22, 0x48, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, + 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, + 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, + 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x50, 0x0a, + 0x14, 0x52, 0x65, 0x61, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x22, + 0x31, 0x0a, 0x15, 0x52, 0x65, 0x61, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x22, 0xe4, 0x01, 0x0a, 0x08, 0x53, 0x6f, 0x75, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x7a, + 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, + 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x32, + 0x35, 0x36, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, + 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x70, 0x72, + 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, + 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x6c, 0x50, + 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x65, + 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x12, + 0x18, 0x0a, 0x07, 0x64, 0x72, 0x69, 0x66, 0x74, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x07, 0x64, 0x72, 0x69, 0x66, 0x74, 0x65, 0x64, 0x22, 0x10, 0x0a, 0x0e, 0x47, 0x65, 0x74, + 0x53, 0x6f, 0x75, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x0a, 0x0f, 0x47, + 0x65, 0x74, 0x53, 0x6f, 0x75, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, + 0x0a, 0x04, 0x73, 0x6f, 0x75, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x6c, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x04, 0x73, 0x6f, 0x75, 0x6c, 0x22, 0x44, 0x0a, 0x0e, 0x55, 0x73, 0x65, 0x72, 0x4d, + 0x6f, 0x64, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x27, 0x0a, + 0x13, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0xfe, 0x02, 0x0a, 0x11, 0x55, 0x73, 0x65, 0x72, 0x4d, + 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x72, 0x69, 0x74, + 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x77, 0x72, 0x69, 0x74, 0x65, 0x72, + 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, + 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, + 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x4a, 0x04, 0x08, + 0x0a, 0x10, 0x0b, 0x52, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x5f, 0x69, 0x64, 0x52, 0x11, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x22, 0xae, 0x01, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, + 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x36, 0x0a, 0x07, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x6f, 0x64, + 0x65, 0x6c, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x12, 0x36, 0x0a, 0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x52, 0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x2b, 0x0a, 0x11, 0x68, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x41, + 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x22, 0xb6, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, + 0x55, 0x73, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x33, 0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, + 0x73, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, + 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x69, 0x7a, 0x65, + 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x32, 0x0a, + 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x6f, + 0x64, 0x65, 0x6c, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x22, 0xdd, 0x01, 0x0a, 0x09, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x64, + 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, + 0x61, 0x73, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x23, 0x0a, 0x0d, + 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, + 0x64, 0x22, 0x13, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xef, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x68, 0x69, 0x6e, 0x74, 0x12, 0x3d, 0x0a, 0x1b, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, + 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x64, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x41, 0x75, 0x74, 0x6f, 0x53, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, + 0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x13, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x6f, + 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x22, 0x86, 0x01, 0x0a, 0x12, 0x4c, 0x69, 0x73, + 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x2c, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x65, + 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x12, 0x42, 0x0a, + 0x0f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x22, 0x4c, 0x0a, 0x15, 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, + 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x49, 0x64, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x52, 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x22, + 0x96, 0x02, 0x0a, 0x11, 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x63, 0x65, 0x69, 0x70, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, + 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x69, + 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, + 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x71, 0x75, + 0x65, 0x75, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x62, 0x73, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x62, 0x73, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x67, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61, 0x67, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, + 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, + 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x6c, 0x69, + 0x63, 0x74, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, + 0x6c, 0x69, 0x63, 0x74, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, + 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x50, 0x0a, 0x16, 0x52, 0x65, 0x66, 0x6c, + 0x65, 0x63, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x36, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, + 0x74, 0x52, 0x07, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x8b, 0x04, 0x0a, 0x0f, 0x4c, + 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, + 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x6f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x61, 0x69, 0x6c, + 0x75, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x61, + 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6c, + 0x61, 0x69, 0x6d, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x47, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x10, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x65, + 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x63, 0x6c, 0x61, + 0x69, 0x6d, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, + 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, + 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x6b, 0x69, 0x6c, 0x6c, + 0x5f, 0x69, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x6b, 0x69, 0x6c, 0x6c, + 0x49, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, + 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, 0x52, 0x0f, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x52, - 0x11, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, - 0x69, 0x64, 0x22, 0xae, 0x01, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, - 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x36, 0x0a, 0x07, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x36, - 0x0a, 0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, - 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x68, - 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x2b, 0x0a, 0x11, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, - 0x79, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x10, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, - 0x62, 0x6c, 0x65, 0x22, 0xb6, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4d, - 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x07, - 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x6f, - 0x64, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, - 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, - 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x32, 0x0a, 0x06, 0x64, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x44, 0x65, - 0x74, 0x61, 0x69, 0x6c, 0x52, 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x22, 0xdd, 0x01, 0x0a, - 0x09, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x64, - 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x69, - 0x6d, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x69, 0x6e, - 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x69, - 0x6e, 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x6d, 0x70, - 0x74, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, - 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x43, 0x61, 0x63, 0x68, 0x65, 0x64, 0x22, 0x13, 0x0a, 0x11, - 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x22, 0xef, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x68, - 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x69, 0x6e, 0x74, 0x12, - 0x3d, 0x0a, 0x1b, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, - 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4d, 0x6f, 0x64, - 0x65, 0x6c, 0x41, 0x75, 0x74, 0x6f, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x1f, - 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, - 0x32, 0x0a, 0x15, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x6f, 0x74, - 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, - 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x6f, 0x74, 0x44, 0x65, 0x66, 0x61, - 0x75, 0x6c, 0x74, 0x22, 0x86, 0x01, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x65, - 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x6d, 0x6f, - 0x64, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x12, 0x42, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0e, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x4c, 0x0a, 0x15, - 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, - 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x4a, 0x04, 0x08, - 0x02, 0x10, 0x03, 0x52, 0x05, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x22, 0x96, 0x02, 0x0a, 0x11, 0x52, - 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, - 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x71, 0x75, 0x65, 0x75, 0x65, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x71, 0x75, 0x65, 0x75, 0x65, 0x64, 0x12, - 0x1c, 0x0a, 0x09, 0x61, 0x62, 0x73, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x09, 0x61, 0x62, 0x73, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x67, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x67, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, - 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, - 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x65, 0x64, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x65, - 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x22, 0x50, 0x0a, 0x16, 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, - 0x07, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x6c, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x52, 0x07, 0x72, 0x65, - 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x8b, 0x04, 0x0a, 0x0f, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, - 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x75, 0x74, - 0x63, 0x6f, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x63, - 0x6f, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x63, - 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x61, 0x69, 0x6c, 0x75, - 0x72, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, - 0x74, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x11, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x67, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x44, 0x0a, 0x10, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, - 0x73, 0x5f, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x45, 0x78, 0x70, - 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x67, - 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x69, 0x64, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, - 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x0b, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x39, 0x0a, - 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x34, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x4c, + 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x69, 0x64, 0x22, 0x52, + 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, + 0x65, 0x6d, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x07, + 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, + 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x52, 0x07, 0x61, 0x74, 0x74, 0x65, 0x6d, + 0x70, 0x74, 0x22, 0x61, 0x0a, 0x1b, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, + 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, + 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0x77, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, + 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x08, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, + 0x6d, 0x70, 0x74, 0x52, 0x08, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x12, 0x1f, 0x0a, + 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x65, 0x78, 0x74, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, 0x6b, + 0x0a, 0x1c, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, + 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, + 0x02, 0x10, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x32, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x65, 0x78, 0x70, 0x65, + 0x63, 0x74, 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x55, 0x0a, 0x1d, 0x4d, + 0x75, 0x74, 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, + 0x65, 0x6d, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x07, + 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, + 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x52, 0x07, 0x61, 0x74, 0x74, 0x65, 0x6d, + 0x70, 0x74, 0x22, 0x7e, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, + 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x75, + 0x72, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x75, 0x72, 0x73, + 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x22, 0x9b, 0x02, 0x0a, 0x13, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x45, + 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x66, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x6f, 0x63, + 0x61, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x63, 0x61, + 0x74, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x1b, 0x0a, + 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x71, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0c, 0x74, 0x6f, + 0x6f, 0x6c, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x74, 0x6f, 0x6f, 0x6c, 0x43, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, + 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x69, + 0x67, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, + 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, + 0x74, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, + 0x77, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, + 0x22, 0x80, 0x01, 0x0a, 0x10, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x63, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x63, 0x74, + 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x12, + 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x02, 0x61, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x02, 0x61, 0x74, 0x22, 0xb4, 0x01, 0x0a, 0x18, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, + 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, + 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x12, + 0x27, 0x0a, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x78, 0x69, 0x73, + 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, + 0x75, 0x73, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, 0x65, 0x76, + 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xec, 0x05, 0x0a, 0x10, 0x4c, + 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x20, 0x0a, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x76, 0x69, + 0x64, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, + 0x45, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x66, 0x52, 0x08, 0x65, 0x76, 0x69, + 0x64, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, + 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, + 0x73, 0x12, 0x39, 0x0a, 0x09, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0c, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x09, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, 0x09, + 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x23, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, + 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x63, + 0x65, 0x69, 0x70, 0x74, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0e, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x12, 0x2f, 0x0a, 0x13, + 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x6c, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x70, 0x72, 0x6f, 0x6d, 0x6f, + 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x40, 0x0a, + 0x1c, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x6e, 0x61, 0x76, 0x61, + 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x12, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x1a, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, + 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, + 0x28, 0x0a, 0x10, 0x6c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x6b, 0x69, 0x6c, 0x6c, + 0x5f, 0x69, 0x64, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6c, 0x65, 0x61, 0x72, 0x6e, + 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x49, 0x64, 0x22, 0x7b, 0x0a, 0x1d, 0x4c, 0x69, 0x73, + 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, + 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x09, 0x70, 0x72, + 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, + 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x70, + 0x6f, 0x73, 0x61, 0x6c, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x63, 0x75, + 0x72, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x65, 0x78, 0x74, + 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, 0x4f, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, + 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, + 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x56, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x4c, 0x65, + 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, + 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x22, + 0xc3, 0x01, 0x0a, 0x1d, 0x44, 0x65, 0x63, 0x69, 0x64, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, + 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, + 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x32, 0x0a, 0x10, 0x65, 0x78, + 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x65, + 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, + 0x0a, 0x08, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x64, 0x65, 0x63, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x59, 0x0a, 0x1e, 0x44, 0x65, 0x63, 0x69, 0x64, 0x65, 0x4c, + 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, + 0x73, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, + 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, + 0x22, 0x85, 0x01, 0x0a, 0x1c, 0x55, 0x6e, 0x64, 0x6f, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, + 0x67, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, + 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x32, 0x0a, 0x10, 0x65, 0x78, + 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x65, + 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, + 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x58, 0x0a, 0x1d, 0x55, 0x6e, 0x64, 0x6f, + 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x70, 0x72, 0x6f, + 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, + 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x61, 0x6c, 0x22, 0xa2, 0x05, 0x0a, 0x13, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, + 0x69, 0x6c, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x77, + 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, + 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x70, 0x65, 0x72, 0x73, 0x65, 0x64, 0x65, 0x73, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x70, 0x65, 0x72, 0x73, 0x65, 0x64, 0x65, + 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x65, 0x76, 0x69, 0x64, 0x65, + 0x6e, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x76, 0x69, 0x64, + 0x65, 0x6e, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, + 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x45, + 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x66, 0x52, 0x08, 0x65, 0x76, 0x69, 0x64, + 0x65, 0x6e, 0x63, 0x65, 0x12, 0x3c, 0x0a, 0x0b, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x45, 0x76, 0x61, 0x6c, 0x75, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x39, 0x0a, 0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x73, 0x18, 0x0d, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, 0x65, + 0x69, 0x70, 0x74, 0x52, 0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x73, 0x12, 0x39, 0x0a, + 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x64, 0x41, 0x74, 0x22, 0x34, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, - 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, - 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x69, 0x64, 0x22, 0x52, 0x0a, 0x1a, 0x47, 0x65, 0x74, - 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x07, 0x61, 0x74, 0x74, 0x65, 0x6d, - 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, - 0x65, 0x6d, 0x70, 0x74, 0x52, 0x07, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x22, 0x61, 0x0a, - 0x1b, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, - 0x65, 0x6d, 0x70, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, - 0x22, 0x77, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, - 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x36, 0x0a, 0x08, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, - 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x52, 0x08, - 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, - 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, - 0x65, 0x78, 0x74, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x22, 0x6b, 0x0a, 0x1c, 0x4d, 0x75, 0x74, - 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, - 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x32, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, - 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x55, 0x0a, 0x1d, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x65, - 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x07, 0x61, 0x74, 0x74, 0x65, 0x6d, - 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, - 0x65, 0x6d, 0x70, 0x74, 0x52, 0x07, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x22, 0x7e, 0x0a, - 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x14, 0x0a, - 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, - 0x6d, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x9b, 0x02, - 0x0a, 0x13, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x69, 0x64, 0x65, 0x6e, - 0x63, 0x65, 0x52, 0x65, 0x66, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x18, - 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x07, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x5f, 0x73, 0x65, 0x71, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x53, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0c, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x63, 0x61, - 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x6f, 0x6f, - 0x6c, 0x43, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, - 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, - 0x1c, 0x0a, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x22, 0x0a, - 0x0c, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, - 0x79, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x22, 0x80, 0x01, 0x0a, 0x10, - 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, - 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, - 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x02, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x02, 0x61, 0x74, 0x22, 0xb4, - 0x01, 0x0a, 0x18, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x6d, 0x6f, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, - 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x4b, 0x65, 0x79, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x72, - 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0e, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, 0x78, 0x69, - 0x73, 0x74, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, - 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, - 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xec, 0x05, 0x0a, 0x10, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, - 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, - 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, - 0x74, 0x6c, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x62, 0x6f, 0x64, 0x79, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, - 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x69, 0x64, 0x65, - 0x6e, 0x63, 0x65, 0x52, 0x65, 0x66, 0x52, 0x08, 0x65, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, - 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x18, 0x0b, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x08, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x12, 0x39, 0x0a, 0x09, - 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, - 0x6e, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x64, 0x65, - 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x6d, 0x6f, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, - 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x52, - 0x09, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, - 0x5f, 0x61, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, - 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x73, 0x63, 0x6f, 0x70, - 0x65, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x64, 0x12, 0x2f, 0x0a, 0x13, 0x70, 0x72, 0x6f, 0x6d, 0x6f, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x11, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x41, - 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x40, 0x0a, 0x1c, 0x70, 0x72, 0x6f, 0x6d, - 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, - 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, - 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, - 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x6c, 0x65, - 0x61, 0x72, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x13, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, - 0x6c, 0x6c, 0x49, 0x64, 0x22, 0x7b, 0x0a, 0x1d, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, - 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, - 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x73, - 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x65, 0x78, 0x74, 0x43, 0x75, 0x72, 0x73, 0x6f, - 0x72, 0x22, 0x4f, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, - 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x17, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, - 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x22, 0x56, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, - 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x37, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, - 0x52, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x22, 0xc3, 0x01, 0x0a, 0x1d, 0x44, - 0x65, 0x63, 0x69, 0x64, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, - 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x32, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, - 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x08, 0x64, 0x65, 0x63, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, - 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, - 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x22, 0x59, 0x0a, 0x1e, 0x44, 0x65, 0x63, 0x69, 0x64, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, - 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, - 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x22, 0x85, 0x01, 0x0a, 0x1c, - 0x55, 0x6e, 0x64, 0x6f, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x6d, - 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, - 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x32, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, - 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, - 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x22, 0x58, 0x0a, 0x1d, 0x55, 0x6e, 0x64, 0x6f, 0x4c, 0x65, 0x61, 0x72, 0x6e, - 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, - 0x73, 0x61, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x22, 0xa2, 0x05, - 0x0a, 0x13, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x77, 0x6e, 0x65, - 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x1e, 0x0a, 0x0a, - 0x73, 0x75, 0x70, 0x65, 0x72, 0x73, 0x65, 0x64, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x73, 0x75, 0x70, 0x65, 0x72, 0x73, 0x65, 0x64, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, - 0x65, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0a, + 0x64, 0x41, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x5f, 0x61, + 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, + 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, + 0x12, 0x25, 0x0a, 0x0e, 0x75, 0x6e, 0x64, 0x6f, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x75, 0x6e, 0x64, 0x6f, 0x41, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x22, 0xca, 0x01, 0x0a, 0x0f, 0x53, 0x6b, 0x69, 0x6c, + 0x6c, 0x45, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, + 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, + 0x72, 0x64, 0x69, 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x78, 0x74, 0x75, 0x72, 0x65, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x69, 0x78, 0x74, + 0x75, 0x72, 0x65, 0x49, 0x64, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, + 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, + 0x6e, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x72, 0x65, 0x61, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x72, 0x65, 0x61, 0x74, 0x6d, 0x65, 0x6e, 0x74, + 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x02, 0x61, 0x74, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x02, 0x61, 0x74, 0x22, 0xc0, 0x04, 0x0a, 0x12, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, + 0x6b, 0x69, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, + 0x6b, 0x69, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x66, 0x72, 0x6f, 0x6d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x25, 0x0a, 0x0e, + 0x65, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x65, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x3a, 0x0a, 0x08, 0x65, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x18, - 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x69, 0x64, 0x65, 0x6e, - 0x63, 0x65, 0x52, 0x65, 0x66, 0x52, 0x08, 0x65, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x12, - 0x3c, 0x0a, 0x0b, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0c, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x45, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x0b, 0x65, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x39, 0x0a, - 0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6b, 0x69, 0x6c, - 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x52, 0x08, - 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, - 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x2b, - 0x0a, 0x11, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, - 0x62, 0x6c, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x6e, 0x73, 0x70, 0x65, - 0x63, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x75, - 0x6e, 0x64, 0x6f, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x11, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0d, 0x75, 0x6e, 0x64, 0x6f, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, - 0x6c, 0x65, 0x22, 0xca, 0x01, 0x0a, 0x0f, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x45, 0x76, 0x61, 0x6c, - 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, - 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x69, 0x78, 0x74, 0x75, 0x72, 0x65, 0x49, 0x64, - 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1c, 0x0a, - 0x09, 0x74, 0x72, 0x65, 0x61, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x74, 0x72, 0x65, 0x61, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x02, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x02, 0x61, 0x74, 0x22, - 0xc0, 0x04, 0x0a, 0x12, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, - 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x49, - 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x1c, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, - 0x0a, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x66, 0x72, 0x6f, 0x6d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, - 0x74, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x74, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x76, 0x69, 0x64, 0x65, - 0x6e, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x0d, 0x65, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2c, - 0x0a, 0x12, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x2f, 0x0a, 0x14, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x6f, 0x6f, 0x6c, 0x5f, 0x63, 0x61, 0x6c, 0x6c, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x54, 0x6f, 0x6f, 0x6c, 0x43, 0x61, 0x6c, 0x6c, 0x49, 0x64, 0x73, 0x12, 0x18, 0x0a, - 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x78, 0x74, 0x75, - 0x72, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x69, - 0x78, 0x74, 0x75, 0x72, 0x65, 0x49, 0x64, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65, - 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x61, 0x73, 0x65, - 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x72, 0x65, 0x61, 0x74, 0x6d, 0x65, 0x6e, - 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x72, 0x65, 0x61, 0x74, 0x6d, 0x65, - 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x02, 0x61, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x02, 0x61, 0x74, 0x12, 0x2b, - 0x0a, 0x11, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, - 0x62, 0x6c, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, 0x6e, 0x73, 0x70, 0x65, - 0x63, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x75, - 0x6e, 0x64, 0x6f, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x11, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0d, 0x75, 0x6e, 0x64, 0x6f, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, - 0x6c, 0x65, 0x22, 0x99, 0x01, 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, - 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x75, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, + 0x73, 0x12, 0x2f, 0x0a, 0x14, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x6f, 0x6f, 0x6c, + 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x11, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x6f, 0x6f, 0x6c, 0x43, 0x61, 0x6c, 0x6c, 0x49, + 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x0b, + 0x66, 0x69, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0a, 0x66, 0x69, 0x78, 0x74, 0x75, 0x72, 0x65, 0x49, 0x64, 0x73, 0x12, 0x1a, 0x0a, + 0x08, 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x62, 0x61, 0x73, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x72, 0x65, + 0x61, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x72, + 0x65, 0x61, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x02, 0x61, 0x74, 0x18, 0x0f, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x02, 0x61, 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x5f, 0x61, + 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, + 0x69, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, + 0x12, 0x25, 0x0a, 0x0e, 0x75, 0x6e, 0x64, 0x6f, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x75, 0x6e, 0x64, 0x6f, 0x41, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x99, 0x01, 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, + 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x16, + 0x0a, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x14, 0x0a, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x41, 0x67, + 0x65, 0x6e, 0x74, 0x22, 0xae, 0x01, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, + 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x36, 0x0a, 0x06, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, + 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x06, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x78, + 0x74, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x6e, 0x65, 0x78, 0x74, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x67, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x22, 0x8f, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, + 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x75, 0x72, - 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, - 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, - 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x22, 0xae, - 0x01, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, - 0x69, 0x6c, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x06, - 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, - 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, - 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x73, 0x6b, - 0x69, 0x6c, 0x6c, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x63, 0x75, 0x72, - 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x65, 0x78, 0x74, 0x43, - 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, - 0x8f, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, - 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x12, 0x28, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x67, - 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, - 0x10, 0x01, 0x52, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x17, - 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, - 0x02, 0x10, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x22, 0x89, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, - 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, - 0x05, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, - 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, - 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x73, 0x6b, - 0x69, 0x6c, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0xd2, 0x01, - 0x0a, 0x1f, 0x44, 0x69, 0x66, 0x66, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, - 0x6c, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x28, 0x0a, 0x0b, 0x6f, - 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, - 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2a, - 0x0a, 0x0c, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x66, - 0x72, 0x6f, 0x6d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0a, 0x74, 0x6f, - 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, - 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x74, 0x6f, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x22, 0xcd, 0x01, 0x0a, 0x20, 0x44, 0x69, 0x66, 0x66, 0x4c, 0x65, 0x61, 0x72, 0x6e, + 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x28, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, + 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, + 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x41, 0x67, + 0x65, 0x6e, 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x89, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4c, 0x65, + 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x34, 0x0a, 0x05, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, + 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x05, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x67, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x67, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x22, 0xd2, 0x01, 0x0a, 0x1f, 0x44, 0x69, 0x66, 0x66, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x69, 0x66, 0x66, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x69, 0x66, 0x66, 0x12, 0x1e, 0x0a, 0x0a, 0x67, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x5f, 0x69, - 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x49, 0x64, - 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x22, 0xd1, 0x01, 0x0a, 0x19, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, - 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x28, 0x0a, 0x0b, 0x6f, 0x77, - 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x41, - 0x67, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, - 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, - 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x34, 0x0a, 0x11, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, - 0x72, 0x02, 0x10, 0x01, 0x52, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xe8, 0x01, 0x0a, 0x1a, 0x4d, 0x75, 0x74, 0x61, 0x74, - 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x05, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x73, 0x6b, 0x69, 0x6c, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x67, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x11, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x10, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x22, 0xe0, 0x01, 0x0a, 0x1b, 0x52, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x4c, 0x65, - 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x28, 0x0a, 0x0b, 0x6f, - 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x6f, 0x77, 0x6e, 0x65, 0x72, - 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, - 0x0a, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, - 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x34, - 0x0a, 0x11, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, - 0x10, 0x01, 0x52, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x52, 0x65, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x61, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, - 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x75, 0x72, - 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, - 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0xae, 0x01, 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, - 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x63, - 0x65, 0x69, 0x70, 0x74, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x1f, 0x0a, - 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x65, 0x78, 0x74, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x1e, - 0x0a, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, - 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0xbe, 0x01, 0x0a, 0x11, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, - 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x07, 0x6d, 0x65, - 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, - 0x53, 0x70, 0x65, 0x63, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, - 0x04, 0x67, 0x6f, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x67, 0x6f, 0x61, - 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x74, 0x6f, - 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x54, - 0x65, 0x61, 0x6d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x22, 0x5e, 0x0a, 0x12, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x65, 0x63, 0x61, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x22, 0xa1, 0x01, 0x0a, 0x0c, 0x54, 0x65, - 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, - 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x67, 0x65, - 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x65, 0x61, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x6c, 0x65, 0x61, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x75, - 0x74, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6d, 0x75, - 0x74, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, - 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x22, 0xcb, 0x01, - 0x0a, 0x14, 0x53, 0x70, 0x61, 0x77, 0x6e, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, - 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x65, 0x61, 0x64, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x04, 0x6c, 0x65, 0x61, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x75, 0x74, 0x61, - 0x74, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6d, 0x75, 0x74, 0x61, - 0x74, 0x69, 0x6e, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, - 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x69, 0x6e, - 0x69, 0x74, 0x69, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x22, 0x46, 0x0a, 0x15, 0x53, - 0x70, 0x61, 0x77, 0x6e, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x06, 0x6d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x22, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x61, 0x6d, - 0x6d, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x20, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x74, 0x65, - 0x61, 0x6d, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x12, 0x0a, - 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, - 0x6d, 0x12, 0x1b, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x1d, - 0x0a, 0x1b, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5a, 0x0a, - 0x15, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x12, 0x28, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, + 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x2a, 0x0a, 0x0c, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, + 0x10, 0x01, 0x52, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x26, 0x0a, 0x0a, 0x74, 0x6f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x74, 0x6f, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xcd, 0x01, 0x0a, 0x20, 0x44, 0x69, 0x66, 0x66, + 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x64, 0x69, 0x66, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x69, 0x66, 0x66, + 0x12, 0x1e, 0x0a, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x6b, + 0x69, 0x6c, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x6b, + 0x69, 0x6c, 0x6c, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x72, 0x6f, + 0x6d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x5f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xd1, 0x01, 0x0a, 0x19, 0x4d, 0x75, 0x74, 0x61, + 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, + 0x28, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x6f, + 0x77, 0x6e, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x21, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x11, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, + 0x64, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xe8, 0x01, 0x0a, 0x1a, + 0x4d, 0x75, 0x74, 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, + 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x05, 0x73, 0x6b, + 0x69, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, + 0x6c, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x73, 0x6b, 0x69, 0x6c, 0x6c, + 0x12, 0x1e, 0x0a, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xe0, 0x01, 0x0a, 0x1b, 0x52, 0x6f, 0x6c, 0x6c, 0x62, + 0x61, 0x63, 0x6b, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x12, 0x28, 0x0a, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, + 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, + 0x72, 0x02, 0x10, 0x01, 0x52, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x11, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, + 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, + 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, + 0x64, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x61, 0x0a, 0x17, 0x4c, 0x69, 0x73, + 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x16, + 0x0a, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x22, 0xae, 0x01, 0x0a, + 0x18, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x65, 0x63, + 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x65, 0x78, 0x74, 0x43, 0x75, 0x72, + 0x73, 0x6f, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0xbe, 0x01, + 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, - 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x62, - 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, - 0x01, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x18, 0x0a, 0x16, 0x43, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x32, 0x0a, 0x0e, 0x52, 0x75, 0x6e, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, + 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x31, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, + 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x53, 0x70, 0x65, 0x63, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x6f, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x67, 0x6f, 0x61, 0x6c, 0x12, 0x26, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x74, 0x65, + 0x61, 0x6d, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0d, 0x6d, 0x61, 0x78, 0x54, 0x65, 0x61, 0x6d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x22, 0x5e, + 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x2f, 0x0a, + 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x4d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x22, 0xa1, + 0x01, 0x0a, 0x0c, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, + 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, + 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, + 0x65, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x6c, 0x65, 0x61, 0x64, 0x12, + 0x1a, 0x0a, 0x08, 0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x08, 0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x69, + 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x6d, + 0x70, 0x74, 0x22, 0xcb, 0x01, 0x0a, 0x14, 0x53, 0x70, 0x61, 0x77, 0x6e, 0x54, 0x65, 0x61, 0x6d, + 0x6d, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x07, 0x74, + 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, + 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1b, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, + 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x67, + 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x61, 0x67, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x65, 0x61, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x6c, 0x65, 0x61, 0x64, 0x12, 0x1a, 0x0a, + 0x08, 0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x08, 0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x69, + 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, + 0x22, 0x46, 0x0a, 0x15, 0x53, 0x70, 0x61, 0x77, 0x6e, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x06, 0x6d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, + 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x88, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x6e, + 0x64, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, + 0x01, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x02, 0x74, 0x6f, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, + 0x74, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x1b, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x62, + 0x6f, 0x64, 0x79, 0x22, 0x1d, 0x0a, 0x1b, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x61, 0x6d, 0x6d, + 0x61, 0x74, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x5a, 0x0a, 0x15, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x54, 0x65, 0x61, 0x6d, + 0x6d, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x07, 0x74, + 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, + 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x1f, 0x0a, + 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, + 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x18, + 0x0a, 0x16, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x32, 0x0a, 0x0e, 0x52, 0x75, 0x6e, 0x54, + 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x07, 0x74, 0x65, + 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, + 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x22, 0x7d, 0x0a, 0x09, + 0x54, 0x65, 0x61, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x6d, + 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, + 0x72, 0x12, 0x26, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x07, 0x6f, 0x75, 0x74, + 0x63, 0x6f, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x65, 0x63, + 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x4f, 0x75, 0x74, 0x63, 0x6f, + 0x6d, 0x65, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x22, 0xa4, 0x02, 0x0a, 0x0b, + 0x54, 0x65, 0x61, 0x6d, 0x4f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, + 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x72, 0x6f, 0x75, + 0x6e, 0x64, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x71, 0x75, 0x69, 0x65, 0x73, 0x63, 0x65, 0x6e, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x71, 0x75, 0x69, 0x65, 0x73, 0x63, 0x65, 0x6e, + 0x74, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x5f, 0x65, 0x78, 0x68, 0x61, + 0x75, 0x73, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x62, 0x75, 0x64, + 0x67, 0x65, 0x74, 0x45, 0x78, 0x68, 0x61, 0x75, 0x73, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, + 0x73, 0x74, 0x6f, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x74, 0x6f, 0x70, + 0x12, 0x26, 0x0a, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x12, 0x44, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, + 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x4d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x44, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x32, + 0x0a, 0x08, 0x66, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, + 0x6d, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x73, 0x22, 0x33, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, - 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x22, 0x7d, 0x0a, 0x09, 0x54, 0x65, 0x61, 0x6d, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x05, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x4f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x52, 0x07, 0x6f, - 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x22, 0xa4, 0x02, 0x0a, 0x0b, 0x54, 0x65, 0x61, 0x6d, 0x4f, - 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x12, 0x1c, - 0x0a, 0x09, 0x71, 0x75, 0x69, 0x65, 0x73, 0x63, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x09, 0x71, 0x75, 0x69, 0x65, 0x73, 0x63, 0x65, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, - 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x5f, 0x65, 0x78, 0x68, 0x61, 0x75, 0x73, 0x74, 0x65, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x45, 0x78, - 0x68, 0x61, 0x75, 0x73, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x74, 0x6f, 0x70, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x74, 0x6f, 0x70, 0x12, 0x26, 0x0a, 0x05, 0x75, - 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x75, 0x73, - 0x61, 0x67, 0x65, 0x12, 0x44, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x44, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x64, 0x69, 0x73, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x32, 0x0a, 0x08, 0x66, 0x69, 0x6e, - 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x46, 0x69, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x33, 0x0a, - 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x20, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, - 0x49, 0x64, 0x22, 0x8c, 0x01, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, - 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x74, 0x61, 0x73, 0x6b, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x05, 0x74, 0x61, - 0x73, 0x6b, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x71, 0x75, 0x69, 0x65, 0x73, 0x63, 0x65, 0x6e, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x71, 0x75, 0x69, 0x65, 0x73, 0x63, 0x65, 0x6e, - 0x74, 0x22, 0x74, 0x0a, 0x0a, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x82, 0x01, 0x0a, 0x08, 0x54, 0x65, 0x61, 0x6d, - 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, - 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x70, 0x73, - 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x70, 0x73, 0x22, 0x39, 0x0a, 0x0b, - 0x54, 0x65, 0x61, 0x6d, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x6d, - 0x65, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xa1, 0x01, 0x0a, 0x15, 0x54, 0x65, 0x61, 0x6d, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x44, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x74, 0x6f, 0x70, 0x70, 0x65, 0x64, 0x12, - 0x37, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, - 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, - 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x22, 0x36, 0x0a, 0x12, 0x43, - 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x20, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x74, 0x65, 0x61, - 0x6d, 0x49, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x65, - 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x8c, 0x01, 0x0a, 0x12, 0x41, - 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x3a, 0x0a, 0x0b, 0x74, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x22, 0xa3, 0x01, 0x0a, 0x17, 0x4d, 0x61, - 0x6e, 0x75, 0x61, 0x6c, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, - 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x47, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, - 0x0d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x3f, - 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, + 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x22, 0x8c, 0x01, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, + 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, + 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x29, 0x0a, + 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x54, 0x61, 0x73, + 0x6b, 0x52, 0x05, 0x74, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x71, 0x75, 0x69, 0x65, + 0x73, 0x63, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x71, 0x75, 0x69, + 0x65, 0x73, 0x63, 0x65, 0x6e, 0x74, 0x22, 0x74, 0x0a, 0x0a, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, + 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x67, 0x65, 0x6e, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x67, + 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, + 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x82, 0x01, 0x0a, + 0x08, 0x54, 0x65, 0x61, 0x6d, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x64, 0x65, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x70, + 0x73, 0x22, 0x39, 0x0a, 0x0b, 0x54, 0x65, 0x61, 0x6d, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, + 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xa1, 0x01, 0x0a, + 0x15, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x44, 0x69, 0x73, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x74, + 0x6f, 0x70, 0x70, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x74, 0x6f, + 0x70, 0x70, 0x65, 0x64, 0x12, 0x37, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x74, 0x6f, 0x70, 0x52, + 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x21, 0x0a, + 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0b, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x73, + 0x22, 0x36, 0x0a, 0x12, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x65, 0x61, 0x6d, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, + 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x43, 0x6c, 0x65, 0x61, + 0x6e, 0x75, 0x70, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x8c, 0x01, 0x0a, 0x12, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, + 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x3a, + 0x0a, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x0a, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, + 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x74, 0x65, 0x22, 0xa3, + 0x01, 0x0a, 0x17, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x61, + 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x47, 0x0a, 0x0e, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, - 0x6c, 0x69, 0x74, 0x79, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x22, - 0x7a, 0x0a, 0x15, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x61, - 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x67, 0x65, 0x6e, 0x65, - 0x72, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x67, 0x65, 0x6e, 0x65, - 0x72, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x63, 0x69, 0x64, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x65, 0x63, 0x69, 0x64, 0x65, 0x12, 0x2d, 0x0a, 0x12, - 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x73, - 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, - 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x22, 0x32, 0x0a, 0x18, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x6c, 0x61, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, - 0x4b, 0x0a, 0x19, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x72, 0x65, 0x61, 0x6d, - 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x04, - 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x76, 0x69, - 0x65, 0x77, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x22, 0x4d, 0x0a, 0x16, - 0x44, 0x65, 0x63, 0x69, 0x64, 0x65, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x6c, 0x61, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x6e, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x61, 0x6e, 0x49, 0x64, 0x12, - 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x4c, 0x0a, 0x17, 0x44, - 0x65, 0x63, 0x69, 0x64, 0x65, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x07, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, - 0x52, 0x07, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x99, 0x02, 0x0a, 0x0f, 0x44, 0x72, - 0x65, 0x61, 0x6d, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x0e, 0x0a, + 0x6c, 0x69, 0x74, 0x79, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, + 0x6f, 0x72, 0x79, 0x12, 0x3f, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6d, 0x6f, 0x64, 0x65, + 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, + 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x4d, + 0x6f, 0x64, 0x65, 0x6c, 0x22, 0x7a, 0x0a, 0x15, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x1a, 0x0a, + 0x08, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x08, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x63, + 0x69, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x65, 0x63, 0x69, 0x64, + 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, + 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x75, + 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, + 0x22, 0x32, 0x0a, 0x18, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x72, 0x65, 0x61, + 0x6d, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x22, 0x4b, 0x0a, 0x19, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x72, 0x65, 0x61, + 0x6d, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x04, 0x70, 0x6c, 0x61, + 0x6e, 0x22, 0x4d, 0x0a, 0x16, 0x44, 0x65, 0x63, 0x69, 0x64, 0x65, 0x44, 0x72, 0x65, 0x61, 0x6d, + 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x70, + 0x6c, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, + 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x22, 0x4c, 0x0a, 0x17, 0x44, 0x65, 0x63, 0x69, 0x64, 0x65, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, + 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x07, 0x72, + 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, + 0x63, 0x65, 0x69, 0x70, 0x74, 0x52, 0x07, 0x72, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x22, 0x99, + 0x02, 0x0a, 0x0f, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x50, 0x6c, + 0x61, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, + 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, + 0x72, 0x65, 0x73, 0x41, 0x74, 0x12, 0x36, 0x0a, 0x17, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, + 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x15, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x4f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, + 0x14, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x70, 0x6c, 0x61, + 0x6e, 0x6e, 0x65, 0x64, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, + 0x39, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x44, 0x72, 0x65, 0x61, 0x6d, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, + 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xa5, 0x02, 0x0a, 0x0e, 0x44, + 0x72, 0x65, 0x61, 0x6d, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, + 0x64, 0x12, 0x37, 0x0a, 0x08, 0x73, 0x75, 0x72, 0x76, 0x69, 0x76, 0x6f, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, + 0x52, 0x08, 0x73, 0x75, 0x72, 0x76, 0x69, 0x76, 0x6f, 0x72, 0x12, 0x35, 0x0a, 0x07, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x61, 0x72, + 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x12, 0x3d, 0x0a, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x18, 0x65, 0x78, 0x61, 0x63, + 0x74, 0x5f, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x6c, 0x69, 0x67, + 0x69, 0x62, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x65, 0x78, 0x61, 0x63, + 0x74, 0x44, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x45, 0x6c, 0x69, 0x67, 0x69, 0x62, + 0x6c, 0x65, 0x22, 0x5c, 0x0a, 0x10, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x61, 0x72, 0x74, 0x69, + 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x20, + 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0x4a, 0x0a, 0x10, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xd6, 0x02, 0x0a, + 0x0c, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, - 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, - 0x12, 0x36, 0x0a, 0x17, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x65, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x15, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x70, 0x6c, 0x61, 0x6e, - 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x6f, 0x70, - 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x72, 0x65, 0x61, 0x6d, - 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xa5, 0x02, 0x0a, 0x0e, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x4f, - 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x37, 0x0a, 0x08, - 0x73, 0x75, 0x72, 0x76, 0x69, 0x76, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x72, 0x65, 0x61, 0x6d, - 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x52, 0x08, 0x73, 0x75, 0x72, - 0x76, 0x69, 0x76, 0x6f, 0x72, 0x12, 0x35, 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, - 0x61, 0x6e, 0x74, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3d, 0x0a, 0x0b, - 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x72, - 0x65, 0x61, 0x6d, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0b, - 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, - 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, - 0x73, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x18, 0x65, 0x78, 0x61, 0x63, 0x74, 0x5f, 0x64, 0x75, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x6c, 0x69, 0x67, 0x69, 0x62, 0x6c, 0x65, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x65, 0x78, 0x61, 0x63, 0x74, 0x44, 0x75, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x65, 0x45, 0x6c, 0x69, 0x67, 0x69, 0x62, 0x6c, 0x65, 0x22, 0x5c, 0x0a, - 0x10, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, - 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x4a, 0x0a, 0x10, 0x44, - 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xd6, 0x02, 0x0a, 0x0c, 0x44, 0x72, 0x65, 0x61, - 0x6d, 0x52, 0x65, 0x63, 0x65, 0x69, 0x70, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, - 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x14, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x12, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x5f, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x12, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x6f, 0x6e, 0x66, 0x6c, 0x69, - 0x63, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x15, 0x63, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, - 0x74, 0x65, 0x64, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, - 0x0a, 0x14, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x73, 0x6b, - 0x69, 0x70, 0x70, 0x65, 0x64, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, - 0x12, 0x2e, 0x0a, 0x13, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x66, - 0x61, 0x69, 0x6c, 0x65, 0x64, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, - 0x22, 0x3f, 0x0a, 0x15, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, - 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, - 0x64, 0x22, 0x36, 0x0a, 0x16, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x65, 0x64, 0x22, 0x42, 0x0a, 0x21, 0x57, 0x6f, 0x72, - 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x14, 0x70, 0x6c, 0x61, 0x6e, 0x6e, + 0x65, 0x64, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x70, 0x6c, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x61, 0x70, 0x70, + 0x6c, 0x69, 0x65, 0x64, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x12, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x36, 0x0a, 0x17, 0x63, + 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x15, 0x63, 0x6f, + 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x65, 0x64, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x12, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x11, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3f, 0x0a, 0x15, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x67, 0x0a, - 0x21, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, - 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, - 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, - 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0xaa, 0x01, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x23, - 0x0a, 0x0d, 0x65, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, - 0x74, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x72, - 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, 0x65, 0x73, - 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x55, 0x72, 0x6c, 0x2a, 0x8a, 0x01, 0x0a, 0x0e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, - 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x50, 0x45, 0x52, 0x4d, 0x49, - 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, - 0x4c, 0x54, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, - 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x4c, 0x41, 0x4e, 0x10, 0x02, 0x12, 0x20, - 0x0a, 0x1c, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, - 0x45, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x5f, 0x45, 0x44, 0x49, 0x54, 0x53, 0x10, 0x03, - 0x2a, 0x92, 0x01, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x56, 0x65, 0x72, - 0x64, 0x69, 0x63, 0x74, 0x12, 0x20, 0x0a, 0x1c, 0x41, 0x50, 0x50, 0x52, 0x4f, 0x56, 0x41, 0x4c, - 0x5f, 0x56, 0x45, 0x52, 0x44, 0x49, 0x43, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, - 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x50, 0x50, 0x52, 0x4f, 0x56, - 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x44, 0x49, 0x43, 0x54, 0x5f, 0x44, 0x45, 0x4e, 0x59, 0x10, - 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x41, 0x50, 0x50, 0x52, 0x4f, 0x56, 0x41, 0x4c, 0x5f, 0x56, 0x45, - 0x52, 0x44, 0x49, 0x43, 0x54, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x5f, 0x4f, 0x4e, 0x43, 0x45, - 0x10, 0x02, 0x12, 0x21, 0x0a, 0x1d, 0x41, 0x50, 0x50, 0x52, 0x4f, 0x56, 0x41, 0x4c, 0x5f, 0x56, - 0x45, 0x52, 0x44, 0x49, 0x43, 0x54, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x5f, 0x41, 0x4c, 0x57, - 0x41, 0x59, 0x53, 0x10, 0x03, 0x2a, 0xbe, 0x01, 0x0a, 0x0c, 0x53, 0x74, 0x65, 0x65, 0x72, 0x4f, - 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x53, 0x54, 0x45, 0x45, 0x52, 0x5f, - 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, - 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x53, 0x54, 0x45, 0x45, 0x52, 0x5f, 0x4f, - 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x45, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, - 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x53, 0x54, 0x45, 0x45, 0x52, 0x5f, 0x4f, 0x55, 0x54, 0x43, 0x4f, - 0x4d, 0x45, 0x5f, 0x41, 0x50, 0x50, 0x45, 0x4e, 0x44, 0x45, 0x44, 0x10, 0x02, 0x12, 0x1b, 0x0a, - 0x17, 0x53, 0x54, 0x45, 0x45, 0x52, 0x5f, 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x45, 0x5f, 0x52, - 0x45, 0x54, 0x52, 0x41, 0x43, 0x54, 0x45, 0x44, 0x10, 0x03, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x54, - 0x45, 0x45, 0x52, 0x5f, 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, - 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x04, 0x12, 0x1a, 0x0a, 0x16, 0x53, 0x54, - 0x45, 0x45, 0x52, 0x5f, 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x45, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, - 0x4c, 0x41, 0x54, 0x45, 0x10, 0x05, 0x2a, 0x80, 0x01, 0x0a, 0x0c, 0x47, 0x75, 0x61, 0x72, 0x64, - 0x72, 0x61, 0x69, 0x6c, 0x4a, 0x6f, 0x62, 0x12, 0x1d, 0x0a, 0x19, 0x47, 0x55, 0x41, 0x52, 0x44, - 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x4a, 0x4f, 0x42, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, - 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, - 0x41, 0x49, 0x4c, 0x5f, 0x4a, 0x4f, 0x42, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, - 0x12, 0x19, 0x0a, 0x15, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x4a, 0x4f, - 0x42, 0x5f, 0x49, 0x4e, 0x42, 0x4f, 0x55, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x47, - 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x4a, 0x4f, 0x42, 0x5f, 0x50, 0x45, 0x52, - 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x2a, 0xaa, 0x01, 0x0a, 0x13, 0x47, 0x75, - 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x41, 0x73, 0x73, 0x65, 0x73, 0x73, 0x6d, 0x65, 0x6e, - 0x74, 0x12, 0x24, 0x0a, 0x20, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x41, - 0x53, 0x53, 0x45, 0x53, 0x53, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x23, 0x0a, 0x1f, 0x47, 0x55, 0x41, 0x52, 0x44, - 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x41, 0x53, 0x53, 0x45, 0x53, 0x53, 0x4d, 0x45, 0x4e, 0x54, 0x5f, - 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1f, - 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x41, 0x53, 0x53, 0x45, 0x53, 0x53, - 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x50, 0x52, 0x4f, 0x48, 0x49, 0x42, 0x49, 0x54, 0x45, 0x44, 0x10, - 0x02, 0x12, 0x23, 0x0a, 0x1f, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x41, - 0x53, 0x53, 0x45, 0x53, 0x53, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x55, 0x4e, 0x52, 0x45, 0x53, 0x4f, - 0x4c, 0x56, 0x45, 0x44, 0x10, 0x03, 0x2a, 0x8c, 0x01, 0x0a, 0x13, 0x47, 0x75, 0x61, 0x72, 0x64, - 0x72, 0x61, 0x69, 0x6c, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, - 0x0a, 0x20, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x49, 0x4e, 0x53, 0x50, - 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, - 0x4c, 0x5f, 0x49, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, - 0x50, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x01, 0x12, 0x2c, 0x0a, 0x28, 0x47, 0x55, 0x41, 0x52, 0x44, + 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x73, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x36, 0x0a, 0x16, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, + 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x65, 0x64, 0x22, 0x42, + 0x0a, 0x21, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, + 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x49, 0x64, 0x22, 0x67, 0x0a, 0x21, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x45, + 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, + 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0xaa, 0x01, 0x0a, 0x13, + 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, + 0x65, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x6e, 0x72, 0x6f, + 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x72, 0x65, 0x71, + 0x75, 0x69, 0x72, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x29, 0x0a, + 0x10, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x72, + 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x2a, 0xbe, 0x01, 0x0a, 0x0e, 0x50, 0x65, 0x72, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x50, + 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, + 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, + 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, + 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x50, 0x45, 0x52, + 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x4c, 0x41, + 0x4e, 0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, + 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x5f, 0x45, 0x44, + 0x49, 0x54, 0x53, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, + 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x10, 0x04, 0x12, + 0x18, 0x0a, 0x14, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, + 0x44, 0x45, 0x5f, 0x59, 0x4f, 0x4c, 0x4f, 0x10, 0x05, 0x2a, 0x92, 0x01, 0x0a, 0x0f, 0x41, 0x70, + 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x12, 0x20, 0x0a, + 0x1c, 0x41, 0x50, 0x50, 0x52, 0x4f, 0x56, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x44, 0x49, 0x43, + 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x19, 0x0a, 0x15, 0x41, 0x50, 0x50, 0x52, 0x4f, 0x56, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x44, + 0x49, 0x43, 0x54, 0x5f, 0x44, 0x45, 0x4e, 0x59, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x41, 0x50, + 0x50, 0x52, 0x4f, 0x56, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x44, 0x49, 0x43, 0x54, 0x5f, 0x41, + 0x4c, 0x4c, 0x4f, 0x57, 0x5f, 0x4f, 0x4e, 0x43, 0x45, 0x10, 0x02, 0x12, 0x21, 0x0a, 0x1d, 0x41, + 0x50, 0x50, 0x52, 0x4f, 0x56, 0x41, 0x4c, 0x5f, 0x56, 0x45, 0x52, 0x44, 0x49, 0x43, 0x54, 0x5f, + 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x5f, 0x41, 0x4c, 0x57, 0x41, 0x59, 0x53, 0x10, 0x03, 0x2a, 0xbe, + 0x01, 0x0a, 0x0c, 0x53, 0x74, 0x65, 0x65, 0x72, 0x4f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x12, + 0x1d, 0x0a, 0x19, 0x53, 0x54, 0x45, 0x45, 0x52, 0x5f, 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x45, + 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1a, + 0x0a, 0x16, 0x53, 0x54, 0x45, 0x45, 0x52, 0x5f, 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x45, 0x5f, + 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x53, 0x54, + 0x45, 0x45, 0x52, 0x5f, 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x45, 0x5f, 0x41, 0x50, 0x50, 0x45, + 0x4e, 0x44, 0x45, 0x44, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x54, 0x45, 0x45, 0x52, 0x5f, + 0x4f, 0x55, 0x54, 0x43, 0x4f, 0x4d, 0x45, 0x5f, 0x52, 0x45, 0x54, 0x52, 0x41, 0x43, 0x54, 0x45, + 0x44, 0x10, 0x03, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x54, 0x45, 0x45, 0x52, 0x5f, 0x4f, 0x55, 0x54, + 0x43, 0x4f, 0x4d, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, + 0x47, 0x10, 0x04, 0x12, 0x1a, 0x0a, 0x16, 0x53, 0x54, 0x45, 0x45, 0x52, 0x5f, 0x4f, 0x55, 0x54, + 0x43, 0x4f, 0x4d, 0x45, 0x5f, 0x54, 0x4f, 0x4f, 0x5f, 0x4c, 0x41, 0x54, 0x45, 0x10, 0x05, 0x2a, + 0x80, 0x01, 0x0a, 0x0c, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x4a, 0x6f, 0x62, + 0x12, 0x1d, 0x0a, 0x19, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x4a, 0x4f, + 0x42, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x18, 0x0a, 0x14, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x4a, 0x4f, 0x42, + 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x47, 0x55, 0x41, + 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x4a, 0x4f, 0x42, 0x5f, 0x49, 0x4e, 0x42, 0x4f, 0x55, + 0x4e, 0x44, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, + 0x4c, 0x5f, 0x4a, 0x4f, 0x42, 0x5f, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, + 0x10, 0x03, 0x2a, 0xaa, 0x01, 0x0a, 0x13, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, + 0x41, 0x73, 0x73, 0x65, 0x73, 0x73, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x20, 0x47, 0x55, + 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x41, 0x53, 0x53, 0x45, 0x53, 0x53, 0x4d, 0x45, + 0x4e, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x23, 0x0a, 0x1f, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x41, 0x53, + 0x53, 0x45, 0x53, 0x53, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x41, + 0x42, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x23, 0x0a, 0x1f, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, + 0x49, 0x4c, 0x5f, 0x41, 0x53, 0x53, 0x45, 0x53, 0x53, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x50, 0x52, + 0x4f, 0x48, 0x49, 0x42, 0x49, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x23, 0x0a, 0x1f, 0x47, 0x55, + 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x41, 0x53, 0x53, 0x45, 0x53, 0x53, 0x4d, 0x45, + 0x4e, 0x54, 0x5f, 0x55, 0x4e, 0x52, 0x45, 0x53, 0x4f, 0x4c, 0x56, 0x45, 0x44, 0x10, 0x03, 0x2a, + 0x8c, 0x01, 0x0a, 0x13, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x49, 0x6e, 0x73, + 0x70, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x20, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x49, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, - 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x41, 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, - 0x55, 0x52, 0x45, 0x10, 0x02, 0x2a, 0xd0, 0x02, 0x0a, 0x14, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, - 0x61, 0x69, 0x6c, 0x44, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, - 0x0a, 0x21, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x44, 0x49, 0x53, 0x50, - 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, - 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, - 0x49, 0x4c, 0x5f, 0x44, 0x49, 0x53, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x45, - 0x58, 0x45, 0x43, 0x55, 0x54, 0x45, 0x10, 0x01, 0x12, 0x24, 0x0a, 0x20, 0x47, 0x55, 0x41, 0x52, - 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x44, 0x49, 0x53, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, - 0x4e, 0x5f, 0x41, 0x53, 0x4b, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x29, - 0x0a, 0x25, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x44, 0x49, 0x53, 0x50, - 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x57, 0x49, 0x54, 0x48, 0x48, 0x4f, 0x4c, 0x44, - 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x10, 0x03, 0x12, 0x28, 0x0a, 0x24, 0x47, 0x55, 0x41, - 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x44, 0x49, 0x53, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, - 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x4c, 0x45, 0x41, 0x53, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, - 0x54, 0x10, 0x04, 0x12, 0x1e, 0x0a, 0x1a, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, - 0x5f, 0x44, 0x49, 0x53, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x4e, - 0x59, 0x10, 0x05, 0x12, 0x27, 0x0a, 0x23, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, - 0x5f, 0x44, 0x49, 0x53, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x41, 0x53, - 0x53, 0x5f, 0x41, 0x44, 0x56, 0x49, 0x53, 0x4f, 0x52, 0x59, 0x10, 0x06, 0x12, 0x2a, 0x0a, 0x26, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a, + 0x1d, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x49, 0x4e, 0x53, 0x50, 0x45, + 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x01, + 0x12, 0x2c, 0x0a, 0x28, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x49, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4f, 0x50, 0x45, 0x52, 0x41, 0x54, 0x49, + 0x4f, 0x4e, 0x41, 0x4c, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x02, 0x2a, 0xd0, + 0x02, 0x0a, 0x14, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x44, 0x69, 0x73, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x21, 0x47, 0x55, 0x41, 0x52, 0x44, + 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x44, 0x49, 0x53, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, + 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, + 0x0a, 0x1d, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x44, 0x49, 0x53, 0x50, + 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x45, 0x10, + 0x01, 0x12, 0x24, 0x0a, 0x20, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x44, + 0x49, 0x53, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x53, 0x4b, 0x5f, 0x41, + 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x29, 0x0a, 0x25, 0x47, 0x55, 0x41, 0x52, 0x44, + 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x44, 0x49, 0x53, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, + 0x5f, 0x57, 0x49, 0x54, 0x48, 0x48, 0x4f, 0x4c, 0x44, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, + 0x10, 0x03, 0x12, 0x28, 0x0a, 0x24, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, + 0x44, 0x49, 0x53, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x4c, 0x45, + 0x41, 0x53, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x10, 0x04, 0x12, 0x1e, 0x0a, 0x1a, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x44, 0x49, 0x53, 0x50, 0x4f, 0x53, - 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, 0x4f, 0x4e, 0x54, 0x49, 0x4e, 0x55, 0x45, 0x5f, 0x57, - 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x07, 0x2a, 0x90, 0x01, 0x0a, 0x15, 0x47, 0x75, 0x61, - 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x4b, 0x69, - 0x6e, 0x64, 0x12, 0x27, 0x0a, 0x23, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, - 0x41, 0x50, 0x50, 0x52, 0x4f, 0x56, 0x41, 0x4c, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, - 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x22, 0x0a, 0x1e, 0x47, + 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x4e, 0x59, 0x10, 0x05, 0x12, 0x27, 0x0a, 0x23, + 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x44, 0x49, 0x53, 0x50, 0x4f, 0x53, + 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x41, 0x53, 0x53, 0x5f, 0x41, 0x44, 0x56, 0x49, 0x53, + 0x4f, 0x52, 0x59, 0x10, 0x06, 0x12, 0x2a, 0x0a, 0x26, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, + 0x49, 0x4c, 0x5f, 0x44, 0x49, 0x53, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x43, + 0x4f, 0x4e, 0x54, 0x49, 0x4e, 0x55, 0x45, 0x5f, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, + 0x07, 0x2a, 0x90, 0x01, 0x0a, 0x15, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x41, + 0x70, 0x70, 0x72, 0x6f, 0x76, 0x61, 0x6c, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x27, 0x0a, 0x23, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x41, 0x50, 0x50, 0x52, 0x4f, 0x56, 0x41, - 0x4c, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, - 0x2a, 0x0a, 0x26, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x41, 0x50, 0x50, - 0x52, 0x4f, 0x56, 0x41, 0x4c, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, - 0x54, 0x5f, 0x52, 0x45, 0x4c, 0x45, 0x41, 0x53, 0x45, 0x10, 0x02, 0x2a, 0x98, 0x01, 0x0a, 0x0c, - 0x48, 0x6f, 0x6f, 0x6b, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x19, - 0x48, 0x4f, 0x4f, 0x4b, 0x5f, 0x44, 0x45, 0x43, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, - 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x48, - 0x4f, 0x4f, 0x4b, 0x5f, 0x44, 0x45, 0x43, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x46, - 0x4f, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x48, 0x4f, 0x4f, 0x4b, 0x5f, 0x44, 0x45, 0x43, 0x49, - 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x45, 0x44, 0x10, 0x02, 0x12, 0x1a, - 0x0a, 0x16, 0x48, 0x4f, 0x4f, 0x4b, 0x5f, 0x44, 0x45, 0x43, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x5f, - 0x4d, 0x4f, 0x44, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x03, 0x12, 0x1a, 0x0a, 0x16, 0x48, 0x4f, - 0x4f, 0x4b, 0x5f, 0x44, 0x45, 0x43, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x44, 0x56, 0x49, - 0x53, 0x4f, 0x52, 0x59, 0x10, 0x04, 0x2a, 0x96, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x74, 0x72, 0x79, - 0x44, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x1d, 0x52, - 0x45, 0x54, 0x52, 0x59, 0x5f, 0x44, 0x49, 0x53, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, - 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1d, - 0x0a, 0x19, 0x52, 0x45, 0x54, 0x52, 0x59, 0x5f, 0x44, 0x49, 0x53, 0x50, 0x4f, 0x53, 0x49, 0x54, - 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x01, 0x12, 0x1f, 0x0a, - 0x1b, 0x52, 0x45, 0x54, 0x52, 0x59, 0x5f, 0x44, 0x49, 0x53, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, - 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x54, 0x52, 0x59, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x02, 0x12, 0x1f, - 0x0a, 0x1b, 0x52, 0x45, 0x54, 0x52, 0x59, 0x5f, 0x44, 0x49, 0x53, 0x50, 0x4f, 0x53, 0x49, 0x54, - 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x45, 0x52, 0x4d, 0x41, 0x4e, 0x45, 0x4e, 0x54, 0x10, 0x03, 0x2a, - 0xa8, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x50, 0x52, 0x4f, - 0x47, 0x52, 0x45, 0x53, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, - 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x50, 0x52, - 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x01, - 0x12, 0x1d, 0x0a, 0x19, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, - 0x45, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x45, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x02, 0x12, - 0x1b, 0x0a, 0x17, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, - 0x53, 0x53, 0x5f, 0x56, 0x49, 0x53, 0x49, 0x42, 0x4c, 0x45, 0x10, 0x03, 0x12, 0x1c, 0x0a, 0x18, + 0x4c, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, + 0x45, 0x44, 0x10, 0x00, 0x12, 0x22, 0x0a, 0x1e, 0x47, 0x55, 0x41, 0x52, 0x44, 0x52, 0x41, 0x49, + 0x4c, 0x5f, 0x41, 0x50, 0x50, 0x52, 0x4f, 0x56, 0x41, 0x4c, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, + 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x2a, 0x0a, 0x26, 0x47, 0x55, 0x41, 0x52, + 0x44, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x41, 0x50, 0x50, 0x52, 0x4f, 0x56, 0x41, 0x4c, 0x5f, 0x4b, + 0x49, 0x4e, 0x44, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x52, 0x45, 0x4c, 0x45, 0x41, + 0x53, 0x45, 0x10, 0x02, 0x2a, 0x98, 0x01, 0x0a, 0x0c, 0x48, 0x6f, 0x6f, 0x6b, 0x44, 0x65, 0x63, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x19, 0x48, 0x4f, 0x4f, 0x4b, 0x5f, 0x44, 0x45, + 0x43, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, + 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x48, 0x4f, 0x4f, 0x4b, 0x5f, 0x44, 0x45, 0x43, + 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, + 0x48, 0x4f, 0x4f, 0x4b, 0x5f, 0x44, 0x45, 0x43, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x42, 0x4c, + 0x4f, 0x43, 0x4b, 0x45, 0x44, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x48, 0x4f, 0x4f, 0x4b, 0x5f, + 0x44, 0x45, 0x43, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x4f, 0x44, 0x49, 0x46, 0x49, 0x45, + 0x44, 0x10, 0x03, 0x12, 0x1a, 0x0a, 0x16, 0x48, 0x4f, 0x4f, 0x4b, 0x5f, 0x44, 0x45, 0x43, 0x49, + 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x44, 0x56, 0x49, 0x53, 0x4f, 0x52, 0x59, 0x10, 0x04, 0x2a, + 0x96, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x74, 0x72, 0x79, 0x44, 0x69, 0x73, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x1d, 0x52, 0x45, 0x54, 0x52, 0x59, 0x5f, 0x44, 0x49, + 0x53, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x54, 0x52, 0x59, + 0x5f, 0x44, 0x49, 0x53, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, + 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x54, 0x52, 0x59, 0x5f, + 0x44, 0x49, 0x53, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x45, 0x54, 0x52, + 0x59, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x54, 0x52, 0x59, + 0x5f, 0x44, 0x49, 0x53, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x45, 0x52, + 0x4d, 0x41, 0x4e, 0x45, 0x4e, 0x54, 0x10, 0x03, 0x2a, 0xa8, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x1b, 0x53, + 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x5f, 0x55, + 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x5f, - 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x04, 0x2a, 0x84, 0x01, 0x0a, 0x0e, 0x53, - 0x6f, 0x75, 0x6c, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1f, 0x0a, - 0x1b, 0x53, 0x4f, 0x55, 0x4c, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x45, 0x4e, 0x41, 0x4e, 0x43, 0x45, - 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, - 0x0a, 0x14, 0x53, 0x4f, 0x55, 0x4c, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x45, 0x4e, 0x41, 0x4e, 0x43, - 0x45, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x4f, 0x55, 0x4c, - 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x45, 0x4e, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x4a, - 0x45, 0x43, 0x54, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x53, 0x4f, 0x55, 0x4c, 0x5f, 0x50, 0x52, - 0x4f, 0x56, 0x45, 0x4e, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x44, 0x52, 0x49, 0x56, 0x45, 0x52, 0x10, - 0x03, 0x2a, 0xad, 0x01, 0x0a, 0x14, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x23, 0x54, 0x45, - 0x41, 0x4d, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x5f, 0x52, - 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, - 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x45, 0x4d, 0x42, - 0x45, 0x52, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45, - 0x52, 0x52, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x25, 0x0a, 0x21, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, + 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x01, 0x12, 0x1d, 0x0a, 0x19, 0x53, 0x54, 0x52, + 0x45, 0x41, 0x4d, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x45, + 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x54, 0x52, 0x45, + 0x41, 0x4d, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x5f, 0x56, 0x49, 0x53, 0x49, + 0x42, 0x4c, 0x45, 0x10, 0x03, 0x12, 0x1c, 0x0a, 0x18, 0x53, 0x54, 0x52, 0x45, 0x41, 0x4d, 0x5f, + 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, + 0x45, 0x10, 0x04, 0x2a, 0x84, 0x01, 0x0a, 0x0e, 0x53, 0x6f, 0x75, 0x6c, 0x50, 0x72, 0x6f, 0x76, + 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x4f, 0x55, 0x4c, 0x5f, 0x50, + 0x52, 0x4f, 0x56, 0x45, 0x4e, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x4f, 0x55, 0x4c, 0x5f, + 0x50, 0x52, 0x4f, 0x56, 0x45, 0x4e, 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x10, + 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x4f, 0x55, 0x4c, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x45, 0x4e, + 0x41, 0x4e, 0x43, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x4a, 0x45, 0x43, 0x54, 0x10, 0x02, 0x12, 0x1a, + 0x0a, 0x16, 0x53, 0x4f, 0x55, 0x4c, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x45, 0x4e, 0x41, 0x4e, 0x43, + 0x45, 0x5f, 0x44, 0x52, 0x49, 0x56, 0x45, 0x52, 0x10, 0x03, 0x2a, 0xad, 0x01, 0x0a, 0x14, 0x54, + 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x23, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x45, 0x4d, 0x42, + 0x45, 0x52, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x55, + 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, + 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x4f, 0x50, + 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x01, 0x12, + 0x25, 0x0a, 0x21, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x5f, 0x53, + 0x54, 0x4f, 0x50, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, + 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x12, 0x22, 0x0a, 0x1e, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, - 0x4e, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x12, 0x22, 0x0a, - 0x1e, 0x54, 0x45, 0x41, 0x4d, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x5f, 0x53, 0x54, 0x4f, - 0x50, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x10, - 0x03, 0x32, 0x9c, 0x39, 0x0a, 0x0e, 0x48, 0x61, 0x72, 0x6e, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x67, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x61, - 0x74, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x26, 0x2e, 0x6d, - 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x70, - 0x61, 0x74, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, - 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, - 0x16, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0x28, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x29, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, - 0x73, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0d, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x52, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x6a, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, - 0x43, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x12, 0x27, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, - 0x69, 0x6c, 0x43, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x28, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, + 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x10, 0x03, 0x32, 0x9c, 0x39, 0x0a, 0x0e, 0x48, + 0x61, 0x72, 0x6e, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x67, 0x0a, + 0x14, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, 0x6c, 0x69, + 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, + 0x70, 0x61, 0x74, 0x69, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, + 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, + 0x12, 0x28, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6d, 0x65, 0x63, + 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x47, 0x65, 0x74, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x63, + 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, + 0x0a, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, + 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x43, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x67, + 0x65, 0x12, 0x27, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x43, 0x6f, 0x76, 0x65, 0x72, - 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x73, 0x0a, 0x18, 0x47, - 0x65, 0x74, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x76, 0x69, 0x65, - 0x77, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x12, 0x2a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, - 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x76, 0x69, - 0x65, 0x77, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x67, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, - 0x72, 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x27, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6d, 0x65, 0x63, + 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x47, 0x75, 0x61, 0x72, 0x64, + 0x72, 0x61, 0x69, 0x6c, 0x43, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x73, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x47, 0x75, 0x61, 0x72, 0x64, + 0x72, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, + 0x12, 0x2a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x47, 0x75, 0x61, 0x72, 0x64, 0x72, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x75, 0x61, 0x72, + 0x64, 0x72, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x44, 0x65, 0x74, 0x61, 0x69, + 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x07, 0x53, 0x65, 0x74, - 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4d, - 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x43, - 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0d, - 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6e, 0x61, 0x6d, - 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x52, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x43, - 0x6c, 0x65, 0x61, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, - 0x46, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x08, 0x43, 0x6f, - 0x6e, 0x76, 0x65, 0x72, 0x73, 0x65, 0x12, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, - 0x01, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x75, - 0x6e, 0x41, 0x73, 0x6b, 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x75, 0x6e, 0x41, 0x73, 0x6b, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x75, 0x6e, 0x41, 0x73, 0x6b, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x6c, - 0x76, 0x65, 0x50, 0x6c, 0x61, 0x6e, 0x41, 0x73, 0x6b, 0x12, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x50, 0x6c, 0x61, - 0x6e, 0x41, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x50, - 0x6c, 0x61, 0x6e, 0x41, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, - 0x0a, 0x09, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x12, 0x1b, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x75, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x08, 0x53, 0x74, 0x65, 0x65, 0x72, 0x52, - 0x75, 0x6e, 0x12, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x74, 0x65, 0x65, 0x72, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x65, 0x65, 0x72, - 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x0e, 0x43, - 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x65, 0x65, 0x72, 0x12, 0x20, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x52, 0x75, 0x6e, 0x53, 0x74, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x21, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, - 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x58, 0x0a, 0x0f, 0x52, 0x65, 0x61, 0x64, 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x12, 0x21, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x65, 0x61, 0x64, 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x0e, 0x4c, 0x69, 0x73, - 0x74, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x50, - 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, - 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x4f, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, - 0x12, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x55, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x53, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x11, 0x52, 0x65, 0x66, 0x72, - 0x65, 0x73, 0x68, 0x4d, 0x63, 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x23, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, - 0x68, 0x4d, 0x63, 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, - 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4d, 0x63, 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x73, 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x63, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x73, 0x12, 0x2a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x63, 0x70, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x63, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, - 0x12, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x6f, 0x6c, 0x48, 0x69, 0x76, 0x65, 0x47, 0x72, 0x6f, - 0x75, 0x70, 0x73, 0x12, 0x24, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x6f, 0x6c, 0x48, 0x69, 0x76, 0x65, 0x47, 0x72, 0x6f, 0x75, - 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6d, 0x65, 0x63, 0x61, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x6f, 0x6c, 0x48, 0x69, - 0x76, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x49, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, - 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, - 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x67, 0x65, - 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x4c, - 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x12, 0x1e, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0d, - 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x73, 0x12, 0x1f, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, - 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, - 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x50, 0x0a, 0x13, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x25, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x30, 0x01, 0x12, 0x4c, 0x0a, 0x11, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x12, 0x23, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x6d, - 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, - 0x12, 0x63, 0x0a, 0x12, 0x57, 0x61, 0x74, 0x63, 0x68, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x24, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6d, - 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x88, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x4d, 0x63, 0x70, - 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, - 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x31, 0x2e, 0x6d, 0x65, 0x63, 0x61, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x6d, - 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x63, 0x70, 0x41, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x73, - 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x74, 0x0a, 0x17, 0x52, 0x65, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x63, 0x70, 0x41, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x4d, - 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x65, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x12, 0x71, 0x0a, 0x16, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, - 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x28, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4d, 0x63, 0x70, - 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x12, 0x4f, 0x0a, 0x0c, 0x4c, 0x69, 0x73, - 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x40, 0x0a, 0x07, 0x53, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x19, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x6f, 0x64, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x63, + 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, + 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x70, + 0x61, 0x63, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, + 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x46, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x46, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x46, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x08, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x65, 0x12, + 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0d, + 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x75, 0x6e, 0x41, 0x73, 0x6b, 0x12, 0x1f, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, + 0x65, 0x52, 0x75, 0x6e, 0x41, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, + 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, + 0x76, 0x65, 0x52, 0x75, 0x6e, 0x41, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x55, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x6e, 0x41, + 0x73, 0x6b, 0x12, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x6e, 0x41, 0x73, 0x6b, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x6e, 0x41, 0x73, 0x6b, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x09, 0x43, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x52, 0x75, 0x6e, 0x12, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x43, 0x0a, 0x08, 0x53, 0x74, 0x65, 0x65, 0x72, 0x52, 0x75, 0x6e, 0x12, 0x1a, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x65, 0x65, 0x72, 0x52, 0x75, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x65, 0x65, 0x72, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x0e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x75, + 0x6e, 0x53, 0x74, 0x65, 0x65, 0x72, 0x12, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x65, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x53, 0x74, + 0x65, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x10, 0x4c, + 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, + 0x22, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x52, 0x65, 0x61, 0x64, + 0x4d, 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x21, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4d, 0x63, 0x70, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, + 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4d, + 0x63, 0x70, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x55, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, + 0x6d, 0x70, 0x74, 0x73, 0x12, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x47, 0x65, 0x74, + 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x12, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, + 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x63, 0x70, 0x50, 0x72, 0x6f, 0x6d, + 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x0e, 0x4c, 0x69, + 0x73, 0x74, 0x4d, 0x63, 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x63, 0x70, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, + 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, + 0x63, 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x5e, 0x0a, 0x11, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4d, 0x63, 0x70, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4d, 0x63, 0x70, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x4d, + 0x63, 0x70, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x73, 0x0a, 0x18, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x4d, 0x63, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x2a, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x63, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x10, 0x47, 0x65, - 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x22, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, - 0x6f, 0x72, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x12, 0x50, 0x6c, 0x61, 0x6e, 0x53, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x12, 0x24, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x50, 0x6c, 0x61, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, - 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x13, 0x41, 0x70, - 0x70, 0x6c, 0x79, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, - 0x70, 0x12, 0x25, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, - 0x70, 0x6c, 0x79, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, + 0x6e, 0x4d, 0x63, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, + 0x6f, 0x6c, 0x48, 0x69, 0x76, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x24, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x6f, 0x6f, + 0x6c, 0x48, 0x69, 0x76, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x54, 0x6f, 0x6f, 0x6c, 0x48, 0x69, 0x76, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0a, 0x4c, 0x69, 0x73, + 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x73, 0x12, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, + 0x6b, 0x74, 0x72, 0x65, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x13, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x12, 0x25, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x4c, 0x0a, 0x11, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, + 0x12, 0x23, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x76, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x63, 0x0a, 0x12, 0x57, 0x61, 0x74, + 0x63, 0x68, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, + 0x24, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, + 0x68, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x88, + 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x31, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x74, 0x0a, 0x17, 0x52, 0x65, 0x63, + 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x63, 0x68, + 0x65, 0x63, 0x6b, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x12, + 0x71, 0x0a, 0x16, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4d, 0x63, 0x70, 0x41, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x4d, 0x63, 0x70, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, + 0x30, 0x01, 0x12, 0x4f, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x22, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x48, 0x65, + 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x61, 0x0a, 0x12, 0x50, 0x6c, 0x61, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, + 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x12, 0x24, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, + 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x13, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x12, 0x25, 0x2e, 0x6d, 0x65, 0x63, + 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x15, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, + 0x65, 0x61, 0x6e, 0x75, 0x70, 0x4a, 0x6f, 0x62, 0x12, 0x55, 0x0a, 0x14, 0x43, 0x61, 0x6e, 0x63, + 0x65, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, + 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, + 0x63, 0x65, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x4a, 0x6f, 0x62, 0x12, - 0x55, 0x0a, 0x14, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x55, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, + 0x61, 0x6e, 0x75, 0x70, 0x4a, 0x6f, 0x62, 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, + 0x65, 0x61, 0x6e, 0x75, 0x70, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, - 0x6e, 0x75, 0x70, 0x4a, 0x6f, 0x62, 0x12, 0x55, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x4a, 0x6f, 0x62, 0x12, 0x26, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x4a, 0x6f, 0x62, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x4a, 0x6f, 0x62, 0x12, 0x49, 0x0a, - 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x12, 0x1c, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, - 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, - 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, - 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, - 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, - 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x52, 0x0a, 0x0d, 0x52, 0x65, 0x61, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, - 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, - 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x61, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x53, 0x6f, 0x75, 0x6c, 0x12, 0x19, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x6f, - 0x75, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x6f, 0x75, 0x6c, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, - 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x0e, 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, - 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, - 0x12, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, - 0x6d, 0x70, 0x74, 0x12, 0x24, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, - 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6d, 0x65, 0x63, 0x61, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, - 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x67, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, - 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, - 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x27, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, + 0x6e, 0x75, 0x70, 0x4a, 0x6f, 0x62, 0x12, 0x49, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, + 0x69, 0x6c, 0x6c, 0x73, 0x12, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x55, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, + 0x6c, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x52, 0x65, 0x61, 0x64, + 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x46, + 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x65, 0x63, + 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, + 0x46, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x07, + 0x47, 0x65, 0x74, 0x53, 0x6f, 0x75, 0x6c, 0x12, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x6f, 0x75, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x53, 0x6f, 0x75, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, + 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x12, 0x1e, + 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, + 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, + 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, + 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x55, 0x0a, 0x0e, 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x66, 0x6c, 0x65, 0x63, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, + 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x12, 0x24, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, + 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, 0x14, 0x52, 0x65, 0x74, - 0x72, 0x79, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, - 0x74, 0x12, 0x27, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, - 0x74, 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, - 0x6d, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6d, 0x65, 0x63, + 0x73, 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, + 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x69, 0x0a, 0x14, 0x52, 0x65, 0x74, 0x72, 0x79, 0x4c, 0x65, 0x61, 0x72, 0x6e, + 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x12, 0x27, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, - 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6b, 0x0a, 0x16, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x4c, - 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x12, 0x27, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x74, 0x61, 0x74, - 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x4d, 0x75, 0x74, 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, + 0x74, 0x65, 0x6d, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6b, 0x0a, + 0x16, 0x41, 0x62, 0x61, 0x6e, 0x64, 0x6f, 0x6e, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, + 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x12, 0x27, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, - 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x5e, 0x0a, 0x11, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x72, 0x65, - 0x61, 0x6d, 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x23, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x72, 0x65, 0x61, 0x6d, - 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, - 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x44, 0x65, 0x63, 0x69, 0x64, 0x65, 0x44, 0x72, 0x65, 0x61, 0x6d, - 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x21, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x44, 0x65, 0x63, 0x69, 0x64, 0x65, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x6c, 0x61, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x69, 0x64, 0x65, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, - 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x15, 0x4c, - 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, - 0x73, 0x61, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, + 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x28, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x74, + 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x74, 0x74, 0x65, 0x6d, + 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x11, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x6c, 0x61, 0x6e, 0x12, + 0x23, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x6c, + 0x61, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x44, 0x65, + 0x63, 0x69, 0x64, 0x65, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x21, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x69, 0x64, 0x65, + 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x22, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x63, + 0x69, 0x64, 0x65, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, + 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x73, 0x12, 0x27, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4c, 0x65, - 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x12, 0x25, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, + 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x64, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, + 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x12, 0x25, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, + 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, - 0x16, 0x44, 0x65, 0x63, 0x69, 0x64, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, - 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x12, 0x28, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x69, 0x64, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, - 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x29, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, - 0x63, 0x69, 0x64, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, - 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x15, - 0x55, 0x6e, 0x64, 0x6f, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x6d, - 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x55, 0x6e, 0x64, 0x6f, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, - 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6d, 0x0a, 0x16, 0x44, 0x65, 0x63, 0x69, 0x64, 0x65, + 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, + 0x12, 0x28, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x63, + 0x69, 0x64, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, + 0x73, 0x61, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6d, 0x65, 0x63, + 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x69, 0x64, 0x65, 0x4c, 0x65, 0x61, + 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x15, 0x55, 0x6e, 0x64, 0x6f, 0x4c, 0x65, 0x61, + 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x64, 0x6f, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5e, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, - 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x12, 0x23, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, - 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4c, - 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x12, 0x21, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, - 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x65, - 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x73, 0x0a, 0x18, 0x44, 0x69, 0x66, 0x66, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, - 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2a, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x66, 0x66, 0x4c, - 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x66, 0x66, 0x4c, 0x65, 0x61, 0x72, 0x6e, - 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x14, 0x41, 0x63, 0x74, 0x69, 0x76, - 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x12, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x64, 0x6f, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, + 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x5e, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, + 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x12, 0x23, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, + 0x69, 0x6c, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x65, 0x61, 0x72, + 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, + 0x6b, 0x69, 0x6c, 0x6c, 0x12, 0x21, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, + 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x73, 0x0a, 0x18, 0x44, + 0x69, 0x66, 0x66, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x66, 0x66, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, + 0x6b, 0x69, 0x6c, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x44, 0x69, 0x66, 0x66, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x63, 0x0a, 0x14, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, + 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x12, 0x24, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, + 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, + 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x74, 0x61, 0x74, + 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x12, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x4c, + 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x12, 0x24, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x65, 0x4c, 0x65, + 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x25, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, + 0x74, 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x62, 0x0a, 0x13, 0x41, 0x72, 0x63, 0x68, + 0x69, 0x76, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x12, 0x24, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, - 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x12, - 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, - 0x6c, 0x6c, 0x12, 0x24, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, - 0x75, 0x74, 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, - 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, - 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x62, 0x0a, 0x13, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, - 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x12, 0x24, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, + 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x14, + 0x52, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, + 0x6b, 0x69, 0x6c, 0x6c, 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x65, 0x0a, 0x14, 0x52, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x4c, - 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x12, 0x26, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, - 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x4d, 0x75, 0x74, 0x61, 0x74, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x53, 0x6b, 0x69, - 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x10, 0x4c, 0x69, - 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x22, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, - 0x6b, 0x69, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x4d, - 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x12, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x61, 0x6d, - 0x12, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, - 0x0d, 0x53, 0x70, 0x61, 0x77, 0x6e, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x12, 0x1f, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, 0x77, 0x6e, - 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, 0x77, - 0x6e, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x64, 0x0a, 0x13, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, - 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x25, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, + 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x22, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, 0x6c, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6b, 0x69, 0x6c, + 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x49, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x12, 0x1c, + 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, + 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, + 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x6f, 0x64, + 0x65, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0a, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x12, 0x1c, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x61, 0x6d, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52, 0x0a, 0x0d, 0x53, 0x70, 0x61, 0x77, 0x6e, 0x54, + 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, 0x77, 0x6e, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x70, 0x61, 0x77, 0x6e, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x13, 0x53, 0x65, + 0x6e, 0x64, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x25, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, + 0x6e, 0x64, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, - 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x26, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, - 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x0e, 0x43, 0x61, 0x6e, 0x63, 0x65, - 0x6c, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x12, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x54, 0x65, 0x61, 0x6d, - 0x6d, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x54, 0x65, - 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, - 0x0a, 0x07, 0x52, 0x75, 0x6e, 0x54, 0x65, 0x61, 0x6d, 0x12, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x43, 0x0a, 0x08, - 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x12, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x65, 0x61, 0x6d, - 0x12, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, - 0x61, 0x6e, 0x75, 0x70, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, - 0x6e, 0x75, 0x70, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x40, 0x0a, 0x0b, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x1d, - 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x6f, - 0x76, 0x65, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, - 0x01, 0x12, 0x68, 0x0a, 0x18, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x57, 0x6f, 0x72, 0x6b, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x2c, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x65, - 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x68, 0x0a, 0x18, 0x52, - 0x65, 0x74, 0x72, 0x79, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x72, - 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x2c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x72, - 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, - 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x69, 0x0a, 0x19, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x57, - 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, - 0x6e, 0x74, 0x12, 0x2c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x57, - 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, - 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, + 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x55, 0x0a, 0x0e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, + 0x74, 0x65, 0x12, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x54, 0x65, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x07, 0x52, 0x75, 0x6e, 0x54, 0x65, + 0x61, 0x6d, 0x12, 0x19, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, + 0x75, 0x6e, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x43, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, 0x61, + 0x6d, 0x12, 0x1a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x65, + 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0b, 0x43, 0x6c, + 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x65, 0x61, + 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 0x54, 0x65, 0x61, 0x6d, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x41, 0x70, 0x70, 0x72, + 0x6f, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x68, 0x0a, 0x18, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x2c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, + 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, + 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, + 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x68, 0x0a, 0x18, 0x52, 0x65, 0x74, 0x72, 0x79, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, - 0x42, 0xa2, 0x01, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x42, 0x0c, 0x48, 0x61, 0x72, 0x6e, 0x65, 0x73, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x50, 0x01, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, - 0x74, 0x61, 0x63, 0x6b, 0x6c, 0x6f, 0x6b, 0x2f, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2f, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2f, 0x76, 0x31, 0x3b, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x76, 0x31, 0xa2, 0x02, 0x03, 0x4d, 0x58, 0x58, 0xaa, 0x02, 0x09, 0x4d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x09, 0x4d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x5c, 0x56, 0x31, - 0xe2, 0x02, 0x15, 0x4d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0a, 0x4d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x2c, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, + 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, + 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x69, + 0x0a, 0x19, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x2c, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x45, + 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0xa2, 0x01, 0x0a, 0x0d, 0x63, 0x6f, + 0x6d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x48, 0x61, 0x72, + 0x6e, 0x65, 0x73, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3e, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x6c, 0x6f, 0x6b, + 0x2f, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, + 0x73, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2f, + 0x76, 0x31, 0x3b, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x4d, 0x58, + 0x58, 0xaa, 0x02, 0x09, 0x4d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x09, + 0x4d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x15, 0x4d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0xea, 0x02, 0x0a, 0x4d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, }) var ( @@ -23678,6 +23732,7 @@ func file_mecatl_v1_harness_proto_init() { if File_mecatl_v1_harness_proto != nil { return } + file_mecatl_v1_harness_proto_msgTypes[13].OneofWrappers = []any{} file_mecatl_v1_harness_proto_msgTypes[33].OneofWrappers = []any{} file_mecatl_v1_harness_proto_msgTypes[57].OneofWrappers = []any{} file_mecatl_v1_harness_proto_msgTypes[59].OneofWrappers = []any{} diff --git a/contracts/gen/go/mecatl/v1/harness_grpc.pb.go b/contracts/gen/go/mecatl/v1/harness_grpc.pb.go index 39137ce521..bf91c54326 100644 --- a/contracts/gen/go/mecatl/v1/harness_grpc.pb.go +++ b/contracts/gen/go/mecatl/v1/harness_grpc.pb.go @@ -169,6 +169,10 @@ type HarnessServiceClient interface { // SetMode changes an existing session's permission posture. The session aggregate // remains authoritative: a mid-turn change is rejected with InvalidArgument, so // clients that want "next prompt" semantics must defer and retry once idle. + // One exception, advertised by the refused_mode_recovery feature: for a stored + // session awaiting an approval in a mode the server no longer admits, a change + // to an admitted, strictly lower mode cancels the pending turn without running + // it and applies the mode. SetMode(ctx context.Context, in *SetModeRequest, opts ...grpc.CallOption) (*SetModeResponse, error) // CloseSession ends a session and releases its server-side resources (learned // permission rules, bound placement, and any per-session engine). Idempotent: @@ -1447,6 +1451,10 @@ type HarnessServiceServer interface { // SetMode changes an existing session's permission posture. The session aggregate // remains authoritative: a mid-turn change is rejected with InvalidArgument, so // clients that want "next prompt" semantics must defer and retry once idle. + // One exception, advertised by the refused_mode_recovery feature: for a stored + // session awaiting an approval in a mode the server no longer admits, a change + // to an admitted, strictly lower mode cancels the pending turn without running + // it and applies the mode. SetMode(context.Context, *SetModeRequest) (*SetModeResponse, error) // CloseSession ends a session and releases its server-side resources (learned // permission rules, bound placement, and any per-session engine). Idempotent: diff --git a/contracts/gen/go/mecatl/v1/schedule.pb.go b/contracts/gen/go/mecatl/v1/schedule.pb.go index f7d38ff3f0..399644fc82 100644 --- a/contracts/gen/go/mecatl/v1/schedule.pb.go +++ b/contracts/gen/go/mecatl/v1/schedule.pb.go @@ -260,6 +260,10 @@ type ScheduleSpec struct { Profile string `protobuf:"bytes,6,opt,name=profile,proto3" json:"profile,omitempty"` // mode is the session permission posture. Reuses the PermissionMode enum from // harness.proto (same package — proto allows cross-file refs within a package). + // A non-mutating schedule must use PLAN. For a mutating schedule, unspecified + // means each fire uses the server's configured default session mode at fire + // time; it reads back as unspecified. Every fire checks the mode against the + // server's mode ceiling and fails if the ceiling no longer allows it. Mode PermissionMode `protobuf:"varint,8,opt,name=mode,proto3,enum=mecatl.v1.PermissionMode" json:"mode,omitempty"` // limits are the bounded per-fire budgets. Reuses the Limits message from // harness.proto. They are PER-FIRE: each fire gets a fresh session with these diff --git a/contracts/proto/mecatl/v1/harness.proto b/contracts/proto/mecatl/v1/harness.proto index 5d6da006f9..47b948e813 100644 --- a/contracts/proto/mecatl/v1/harness.proto +++ b/contracts/proto/mecatl/v1/harness.proto @@ -76,6 +76,10 @@ service HarnessService { // SetMode changes an existing session's permission posture. The session aggregate // remains authoritative: a mid-turn change is rejected with InvalidArgument, so // clients that want "next prompt" semantics must defer and retry once idle. + // One exception, advertised by the refused_mode_recovery feature: for a stored + // session awaiting an approval in a mode the server no longer admits, a change + // to an admitted, strictly lower mode cancels the pending turn without running + // it and applies the mode. rpc SetMode(SetModeRequest) returns (SetModeResponse); // CloseSession ends a session and releases its server-side resources (learned @@ -513,7 +517,8 @@ service HarnessService { // PermissionMode is the session-wide permission posture, mirroring // session.PermissionMode. Plan mode exposes a read-only toolset; acceptEdits -// auto-accepts edits. +// auto-accepts edits; auto runs autonomously with an enforcing guardrails +// checker; yolo runs gate-free with no enforcing checker. enum PermissionMode { // PERMISSION_MODE_UNSPECIFIED defaults to DEFAULT at the server boundary. PERMISSION_MODE_UNSPECIFIED = 0; @@ -523,6 +528,13 @@ enum PermissionMode { PERMISSION_MODE_PLAN = 2; // PERMISSION_MODE_ACCEPT_EDITS auto-accepts edits. PERMISSION_MODE_ACCEPT_EDITS = 3; + // PERMISSION_MODE_AUTO runs autonomously using an enforcing guardrails checker. + // Entering this mode requires a checker; the server rejects SetMode to AUTO + // without one. + PERMISSION_MODE_AUTO = 4; + // PERMISSION_MODE_YOLO runs gate-free with no enforcing checker. Advisory + // monitoring may remain. Configured deny rules still apply. + PERMISSION_MODE_YOLO = 5; } // Limits are the configured stop conditions for a session. A zero value in @@ -540,7 +552,9 @@ message Limits { message CreateSessionRequest { reserved 1, 8; reserved "workspace", "source_session_id"; - // mode is the permission posture; unspecified defaults to DEFAULT. + // mode is the permission posture; unspecified uses the server's configured + // default session mode, which is DEFAULT unless the operator chose a + // permission mode that starts sessions elsewhere. PermissionMode mode = 2; // limits are the optional stop conditions. Limits limits = 3; @@ -788,12 +802,10 @@ message ServerCapabilities { // (multi-provider Phase 0, S3.) bool model_selection = 12; // posture is the SERVER-WIDE operator posture-ladder tier ("strict"/"trusted"/ - // "auto"/"yolo"). It is CHROME ONLY — a client renders an honest "⚠ auto"/"⚠ yolo" - // badge so an operator can see the daemon's automation posture at a glance — and is + // "auto"/"yolo"). It bounds which session modes a client may cycle to and is // NEVER session state (it does not change per session; permission MODE is the // per-session knob). String passthrough, no enum on the wire (the EvNoProgress / - // StopBudget discipline). Additive: an older server leaves it empty and the client - // shows no badge (strict/trusted also show no badge). + // StopBudget discipline). Additive: an older server leaves it empty. string posture = 13; // worktrees is true when a WorktreeLister is wired (ListWorktrees may return a // non-empty list for a real git repo). Gates the client's /worktrees overlay — @@ -856,6 +868,18 @@ message ServerCapabilities { // execution_templates is true only when an authenticated template catalog is // wired; an eligible inventory may still be empty. bool execution_templates = 31; + // project_trusted reports whether the server trusts its launch workspace's + // project content (rules, instructions, soul). It is an axis separate from + // the permission mode. CHROME ONLY. Additive: an older server leaves it + // unset and the client shows no trust indicator. + optional bool project_trusted = 32; + // subagent_ask_reviewer reports the subagent ask reviewer, a model that may + // approve a subagent's, team member's, or parallel branch's permission + // request that no human can answer: "on" (enabled), "off" (not enabled), or + // "inert" (configured, but an interactive approver answers instead). Clients + // treat other values as unknown. CHROME ONLY. Additive: an older server + // leaves it unset and the client shows nothing. + optional string subagent_ask_reviewer = 33; } // ListSessionMcpConnectorsRequest names an owned, broker-bound session. @@ -1319,6 +1343,9 @@ message SetModeRequest { // session_id is the session to update. string session_id = 1 [(buf.validate.field).string.min_len = 1]; // mode is the desired permission posture; unspecified defaults to DEFAULT. + // AUTO requires an enforcing guardrails checker; the server rejects SetMode + // to AUTO without one. The server may also enforce a MaxAllowedMode ceiling; + // requesting a mode above it is rejected with INVALID_ARGUMENT. PermissionMode mode = 2; } diff --git a/contracts/proto/mecatl/v1/schedule.proto b/contracts/proto/mecatl/v1/schedule.proto index 952eeabe9b..0addddba50 100644 --- a/contracts/proto/mecatl/v1/schedule.proto +++ b/contracts/proto/mecatl/v1/schedule.proto @@ -160,6 +160,10 @@ message ScheduleSpec { reserved "workspace"; // mode is the session permission posture. Reuses the PermissionMode enum from // harness.proto (same package — proto allows cross-file refs within a package). + // A non-mutating schedule must use PLAN. For a mutating schedule, unspecified + // means each fire uses the server's configured default session mode at fire + // time; it reads back as unspecified. Every fire checks the mode against the + // server's mode ceiling and fails if the ceiling no longer allows it. PermissionMode mode = 8; // limits are the bounded per-fire budgets. Reuses the Limits message from // harness.proto. They are PER-FIRE: each fire gets a fresh session with these diff --git a/deploy/helm/mecak8s/chart_test.go b/deploy/helm/mecak8s/chart_test.go index 18dc9d9433..1d98c9ac86 100644 --- a/deploy/helm/mecak8s/chart_test.go +++ b/deploy/helm/mecak8s/chart_test.go @@ -923,7 +923,7 @@ func TestMecak8sHelmChart_DeployCheckProductionFixtureRuntimeAndSpread(t *testin "--redis-max-followers=32", "--session-lease-k8s-namespace=default", "--headless=true", - "--posture=auto", + "--permission-mode=auto", "--default-provider=openrouter", "--model=anthropic/claude-sonnet-4-6", "--max-run-tokens=200000", @@ -3072,3 +3072,90 @@ mcp: t.Fatalf("OAuth profile checksums = %q and %q; profile change must roll pods", firstSum, secondSum) } } + +// TestShippedAllowAllDefaultsDeclareCheckerChoice pins the chart's tri-state +// checker choice: with neither guardrails.model nor guardrails.disabled set the +// chart passes no guardrails flag, so mecak8s inherits the checker from mounted +// settings; a model renders --guardrails-model; disabled renders the +// --guardrails=off kill-switch. permissionMode renders --permission-mode. +func TestShippedAllowAllDefaultsDeclareCheckerChoice(t *testing.T) { + cases := []struct { + name string + extra []string + mode string + guardArg string // the single expected --guardrails* arg; empty means none + }{ + {name: "default inherits the settings checker", mode: "auto"}, + {name: "model declares checker", extra: []string{"--set-string", "guardrails.model=openrouter/checker"}, mode: "auto", guardArg: "--guardrails-model=openrouter/checker"}, + {name: "disabled declares the kill-switch", extra: []string{"--set", "permissionMode=yolo,guardrails.disabled=true"}, mode: "yolo", guardArg: "--guardrails=off"}, + {name: "yolo inherits", extra: []string{"--set", "permissionMode=yolo"}, mode: "yolo"}, + {name: "default mode", extra: []string{"--set", "permissionMode=default"}, mode: "default"}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + rendered, err := helm(t, append(secureProductionArgs(), tc.extra...)...) + if err != nil { + t.Fatalf("render: %v\n%s", err, rendered) + } + args := deploymentFromRender(t, rendered).Spec.Template.Spec.Containers[0].Args + if !slices.Contains(args, "--permission-mode="+tc.mode) { + t.Fatalf("args lack --permission-mode=%s: %q", tc.mode, args) + } + var guard []string + for _, a := range args { + if strings.HasPrefix(a, "--posture") || a == "--yolo" { + t.Fatalf("args still use deprecated alias %q: %q", a, args) + } + if strings.HasPrefix(a, "--guardrails") { + guard = append(guard, a) + } + } + var want []string + if tc.guardArg != "" { + want = []string{tc.guardArg} + } + if !slices.Equal(guard, want) { + t.Fatalf("guardrails args = %q, want %q", guard, want) + } + }) + } +} + +// TestPermissionModeValuesAreValidated pins the chart-owned permission mode: +// an unknown permissionMode, a mode flag in extraArgs, or both checker choices +// fail rendering with a message naming the fix. +func TestPermissionModeValuesAreValidated(t *testing.T) { + cases := []struct { + name string + extra []string + want []string + }{ + // The schema enum rejects it first; its message format varies by Helm version. + {name: "unknown mode", extra: []string{"--set", "permissionMode=trusted"}, want: []string{"permissionMode", "accept-edits"}}, + {name: "posture in extraArgs", extra: []string{"--set", "extraArgs[0]=--posture=strict"}, want: []string{"--posture=strict", "permissionMode"}}, + {name: "split posture in extraArgs", extra: []string{"--set", "extraArgs[0]=--posture,extraArgs[1]=auto"}, want: []string{"--posture", "permissionMode"}}, + {name: "permission-mode in extraArgs", extra: []string{"--set", "extraArgs[0]=--permission-mode=yolo"}, want: []string{"--permission-mode=yolo", "permissionMode"}}, + {name: "yolo in extraArgs", extra: []string{"--set", "extraArgs[0]=--yolo"}, want: []string{"--yolo", "permissionMode"}}, + // Go's flag parser also accepts single-dash long flags. + {name: "single-dash posture in extraArgs", extra: []string{"--set", "extraArgs[0]=-posture=strict"}, want: []string{"-posture=strict", "permissionMode"}}, + {name: "single-dash split posture in extraArgs", extra: []string{"--set", "extraArgs[0]=-posture,extraArgs[1]=strict"}, want: []string{"-posture", "permissionMode"}}, + {name: "single-dash permission-mode in extraArgs", extra: []string{"--set", "extraArgs[0]=-permission-mode=yolo"}, want: []string{"-permission-mode=yolo", "permissionMode"}}, + {name: "single-dash split permission-mode in extraArgs", extra: []string{"--set", "extraArgs[0]=-permission-mode,extraArgs[1]=yolo"}, want: []string{"-permission-mode", "permissionMode"}}, + {name: "split permission-mode in extraArgs", extra: []string{"--set", "extraArgs[0]=--permission-mode,extraArgs[1]=yolo"}, want: []string{"--permission-mode", "permissionMode"}}, + {name: "single-dash yolo in extraArgs", extra: []string{"--set", "extraArgs[0]=-yolo"}, want: []string{"-yolo", "permissionMode"}}, + {name: "model and disabled", extra: []string{"--set", "guardrails.model=checker,guardrails.disabled=true"}, want: []string{"guardrails.model", "guardrails.disabled"}}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + rendered, err := helm(t, append(secureProductionArgs(), tc.extra...)...) + if err == nil { + t.Fatalf("render succeeded, want failure:\n%s", rendered) + } + for _, w := range tc.want { + if !strings.Contains(rendered, w) { + t.Fatalf("render error lacks %q:\n%s", w, rendered) + } + } + }) + } +} diff --git a/deploy/helm/mecak8s/templates/_helpers.tpl b/deploy/helm/mecak8s/templates/_helpers.tpl index f6a1a08649..638bf3ae5f 100644 --- a/deploy/helm/mecak8s/templates/_helpers.tpl +++ b/deploy/helm/mecak8s/templates/_helpers.tpl @@ -155,6 +155,24 @@ mounted {{- end -}} {{- end -}} +{{/* Validate the permission mode and guardrails checker choice. */}} +{{- define "mecak8s.validatePermissionMode" -}} +{{- $modes := list "plan" "default" "accept-edits" "auto" "yolo" -}} +{{- if not (has .Values.permissionMode $modes) -}} +{{- fail (printf "permissionMode %q is invalid: valid values are %s" (toString .Values.permissionMode) (join ", " $modes)) -}} +{{- end -}} +{{- range $arg := .Values.extraArgs -}} +{{- $a := toString $arg -}} +{{- /* Go's flag parser accepts one or two leading dashes, with the value after "=" or in the next argument. */ -}} +{{- if regexMatch "^--?(posture|permission-mode|yolo)(=|$)" $a -}} +{{- fail (printf "extraArgs entry %q sets the permission mode, which the chart owns; remove it and set permissionMode instead (--posture=strict becomes permissionMode: default, --posture=auto becomes permissionMode: auto, --posture=yolo or --yolo becomes permissionMode: yolo)" $a) -}} +{{- end -}} +{{- end -}} +{{- if and .Values.guardrails.model .Values.guardrails.disabled -}} +{{- fail "set at most one of guardrails.model or guardrails.disabled" -}} +{{- end -}} +{{- end -}} + {{/* Validate cross-entry MCP invariants that JSON Schema cannot express. */}} {{- define "mecak8s.validateMCP" -}} {{- $seen := dict -}} diff --git a/deploy/helm/mecak8s/templates/deployment.yaml b/deploy/helm/mecak8s/templates/deployment.yaml index b7c5ae9d1d..efd97cf386 100644 --- a/deploy/helm/mecak8s/templates/deployment.yaml +++ b/deploy/helm/mecak8s/templates/deployment.yaml @@ -4,6 +4,7 @@ {{- include "mecak8s.validateTLS" . -}} {{- include "mecak8s.validateLearningStore" . -}} {{- include "mecak8s.validateProviderSecurity" . -}} +{{- include "mecak8s.validatePermissionMode" . -}} {{- $learningStoreEnabled := ne .Values.learning.store.endpoint "" -}} {{- $learningStoreTokenConfigured := and .Values.learning.store.tokenSecret .Values.learning.store.tokenKey -}} {{- include "mecak8s.validateMCP" . -}} @@ -111,7 +112,12 @@ spec: {{- end }} - {{ printf "--session-lease-k8s-namespace=%s" .Release.Namespace | quote }} - --headless=true - - --posture=auto + - {{ printf "--permission-mode=%s" .Values.permissionMode | quote }} + {{- if .Values.guardrails.model }} + - {{ printf "--guardrails-model=%s" .Values.guardrails.model | quote }} + {{- else if .Values.guardrails.disabled }} + - --guardrails=off + {{- end }} {{- if .Values.logging.level }} - {{ printf "--log-level=%s" .Values.logging.level | quote }} {{- end }} diff --git a/deploy/helm/mecak8s/values-kind.yaml b/deploy/helm/mecak8s/values-kind.yaml index 9e059a904d..d77d4acf62 100644 --- a/deploy/helm/mecak8s/values-kind.yaml +++ b/deploy/helm/mecak8s/values-kind.yaml @@ -15,6 +15,9 @@ image: pullPolicy: IfNotPresent mockProvider: true +# The mock provider has no guardrails checker, and auto refuses to start +# without one; yolo keeps the fixture's unchecked autonomous behavior. +permissionMode: yolo redis: endpoint: redis:6379 diff --git a/deploy/helm/mecak8s/values.schema.json b/deploy/helm/mecak8s/values.schema.json index aff20c8435..677e264b1f 100644 --- a/deploy/helm/mecak8s/values.schema.json +++ b/deploy/helm/mecak8s/values.schema.json @@ -92,6 +92,22 @@ }, "maxRunTokens": {"type": ["integer", "null"], "minimum": 1}, "maxTeamTokens": {"type": ["integer", "null"], "minimum": 1}, + "guardrails": { + "type": "object", + "additionalProperties": false, + "required": ["model"], + "properties": { + "model": { + "type": "string", + "anyOf": [ + {"const": ""}, + {"pattern": "^\\S(?:.*\\S)?$"} + ] + }, + "disabled": {"type": "boolean"} + } + }, + "permissionMode": {"type": "string", "enum": ["plan", "default", "accept-edits", "auto", "yolo"]}, "telemetry": { "type": "object", "additionalProperties": false, diff --git a/deploy/helm/mecak8s/values.yaml b/deploy/helm/mecak8s/values.yaml index e7bd132089..8720b0f7f5 100644 --- a/deploy/helm/mecak8s/values.yaml +++ b/deploy/helm/mecak8s/values.yaml @@ -36,7 +36,9 @@ logging: # extraArgs appends command-line arguments to the mecak8s agent container. It remains # the escape hatch for flags not modeled by the chart; when it contains --log-level, -# it is appended after logging.level and therefore takes precedence. +# it is appended after logging.level and therefore takes precedence. The chart owns +# the permission mode: rendering fails if extraArgs sets --posture, +# --permission-mode, or --yolo (with one or two dashes); set permissionMode instead. extraArgs: [] # First-class remote Streamable HTTP MCP connections. Authentication is an @@ -110,6 +112,29 @@ model: "" maxRunTokens: null maxTeamTokens: null +# permissionMode renders --permission-mode=: one of plan, default, +# accept-edits, auto, or yolo. auto (the default) runs every tool without asking; +# only deny rules and configured ask rules still apply. mecak8s refuses to start +# in auto without an ENFORCING guardrails checker: set guardrails.model, or bind +# a checker in mounted settings (a `guardrails:` model or the `guardrail` model +# slot). yolo is the explicit choice to run every tool without a checker. +# Migration: earlier charts always ran --posture=auto with --guardrails=off. Such +# a deployment must now configure a checker or set permissionMode: yolo. Move +# any --posture, --permission-mode, or --yolo entry from extraArgs to this +# value (--posture=strict is permissionMode: default). +permissionMode: auto + +# guardrails selects the checker for tool arguments and results. Set at most one: +# - neither (the default): the chart passes no guardrails flag, so mecak8s uses +# the checker configured in mounted settings, if any. +# - model: --guardrails-model=, a tool-free checker model that inspects +# outbound tool arguments and inbound results. +# - disabled: true: --guardrails=off, the kill-switch. It also disables a +# checker configured in mounted settings, and auto refuses to start with it. +guardrails: + model: "" + disabled: false + # Stable, non-secret identity exported on mecak8s OTel resources. Empty is safe # only for live Helm install/upgrade: Helm creates a UUID on install and preserves # the live ConfigMap value on ordinary upgrades. Offline/template-based GitOps must diff --git a/deploy/mecatl-execution-kind/mecak8s-values.yaml b/deploy/mecatl-execution-kind/mecak8s-values.yaml index 6eadc297d0..14c368e242 100644 --- a/deploy/mecatl-execution-kind/mecak8s-values.yaml +++ b/deploy/mecatl-execution-kind/mecak8s-values.yaml @@ -1,6 +1,9 @@ fullnameOverride: mecak8s replicaCount: 1 mockProvider: true +# The mock provider has no guardrails checker, and auto refuses to start +# without one; yolo keeps the fixture's unchecked autonomous behavior. +permissionMode: yolo redis: endpoint: redis:6379 credentialsSecret: "" diff --git a/docs/architecture/api-surface.md b/docs/architecture/api-surface.md index 99adaf5475..551d7e2511 100644 --- a/docs/architecture/api-surface.md +++ b/docs/architecture/api-surface.md @@ -154,7 +154,10 @@ the source untouched, with one exception: clearing a running source cancels it f a later failure publishes no successor but the source stays cancelled. `SetMode` is rejected mid-turn, because changing posture would race permission checks -already in flight; clients defer it to the next prompt. `CompactSession` likewise runs +already in flight; clients defer it to the next prompt. The exception is a stored session +awaiting an approval in a mode the server no longer admits: a switch to an admitted, lower +mode cancels the pending turn without running it, then applies the mode +(`refused_mode_recovery`). `CompactSession` likewise runs only at an idle or terminal boundary. ## Scheduled tasks @@ -186,7 +189,10 @@ entry; `engine/agent` knows nothing about it. explanation and no session id: no execution was admitted. It does not leave an uncertain pending owner blocking the next fresh slot. - Each fire creates a fresh `sched--` session owned by the schedule's captured owner, - with bounded limits. A schedule not marked mutating runs in plan mode. The fire + with bounded limits. A schedule not marked mutating runs in plan mode. A mutating + schedule runs in its recorded mode, or the server's default session mode when the + mode is unset (UNSPECIFIED on the wire); the fire's session creation admits that + mode against the current ceiling. The fire reattaches the exact placement persisted at creation, and fails before creating a session if that placement has drifted (`internal/app/scheduler_fire.go`). diff --git a/docs/architecture/governance.md b/docs/architecture/governance.md index e789f67239..1268003dbd 100644 --- a/docs/architecture/governance.md +++ b/docs/architecture/governance.md @@ -51,29 +51,58 @@ One Shell line can hold many programs, so `engine/governance/shell.go` decompose Plan mode uses the separate `ReadOnlyShell` classifier. Children add approval steps for floored segments; see [subagents and teams](subagents-and-teams.md). -## Posture ladder - -Posture (`internal/app/posture.go`) is the one operator knob for how much the harness -approves on its own. It composes rules and evaluator options; it never bypasses the -evaluator. - -| Posture | Allow-all rule | Substitution floor loosened | Raises project trust | -| --- | --- | --- | --- | -| `strict` (default) | No | No | No | -| `trusted` | No | No | Unless headless | -| `auto` | Main and children | Main engine | Unless headless | -| `yolo` | Main and children | Main and children | Unless headless | - -`trusted` changes no permission rule; its only effect is raising project trust. Headless -is a per-binary setting, not inferred from the environment: `mecatui` is always -interactive, `mecated` is interactive unless started with `--headless`, and `mecak8s` and -`mecatequi` default to headless. +## Permission modes + +The operator's `--permission-mode` token or `permissionMode:` key +(`internal/app/permission_mode.go`) sets two values: the mode new sessions start in and +the server ceiling (`MaxAllowedMode`), the most permissive mode a session may enter. +`plan`, `default`, and `accept-edits` share the ceiling `accept-edits`; `auto` and `yolo` +are their own ceilings. Allow-all is driven by each session's current mode at +evaluation time, not by a process-wide posture, so switching a session from `yolo` to +`default` restores ordinary asks. Modes compose rules and evaluator options; they never +bypass the evaluator. + +| Session mode | Allow-all rule | Substitution floor loosened | +| --- | --- | --- | +| `plan`, `default`, `accept-edits` | No | No | +| `auto` | Main and children | Main engine | +| `yolo` | Main and children | Main and children | The allow-all rule is one ordinary `ScopeCLI` Allow, pinned per audience, so it loosens only the built-in floor: every Deny and configured Ask still applies at `yolo`. Only `yolo` lets children auto-run substitutions, which turns off the child prompt-injection -defense. A project file's `posture:` is ignored with a warning, an unknown value fails -closed to `strict`, and `auto` or `yolo` are refused as root without a declared sandbox. +defense. + +`Service.admitMode` (`internal/adapter/server/service.go`) refuses a created or switched +mode above the ceiling, and `auto` without an enforcing guardrails checker; a server +whose default mode is `auto` refuses to start without one. Unchecked autonomy is the +explicit `yolo` choice, where a configured checker is advisory. `admitStoredMode` +re-checks a persisted session's mode at run, approval resume, fork, and clear, so a +mode admitted under a looser deployment fails with `FailedPrecondition` until the owner +switches it; history is untouched. A session parked awaiting an approval in such a mode +recovers through `SetMode` to an admitted, lower mode, which cancels the pending turn +without running it (`cancelRefusedAwaiting`). A project file's `permissionMode:` is ignored with a +warning, and `auto` or `yolo` are refused as root without a declared sandbox. + +Delegated children receive the parent session's mode through `parentCaps.parentMode`: +`effectiveChildMode` (`engine/agent/subagent.go`) runs a default-configured Subagent +child, Parallel branch, or Team member in its parent's mode when that mode is `auto` or +`yolo`, and keeps an explicitly configured child mode or a member definition's +`permissionMode`. `Service.AdmittedModes` lists the modes `admitMode` accepts, which the +ACP adapter offers as its mode picker. + +A mode change is a session-aggregate transition that `session.SetMode` refuses while the +session is `StateAwaiting`: a pending ask is resolved under the mode it was raised in, +and the new mode applies from the next run. Re-evaluating a parked ask on a mode switch +is deliberately not supported; a mode change never interrupts a command that has +already started. + +A mode never grants project trust. The deprecated `--posture` ladder +(`strict < trusted < auto < yolo`, `internal/app/posture.go`) still raises project +trust on an interactive root; combining it with `--permission-mode` is an error. +Headless is a per-binary setting, not inferred from the environment: `mecatui` is always +interactive, `mecated` is interactive unless started with `--headless`, and `mecak8s` and +`mecatequi` default to headless. ## Workspace trust diff --git a/e2e/k8s/caller_identity_test.go b/e2e/k8s/caller_identity_test.go index 899f466c7f..0c520c32fd 100644 --- a/e2e/k8s/caller_identity_test.go +++ b/e2e/k8s/caller_identity_test.go @@ -40,9 +40,10 @@ import ( // the unauthenticated base. Caller identity is inherently MULTI-caller, so these // take the bearer per call rather than reading one shared credential. +// defaultSessionCreateBody omits the mode so the session starts in the +// deployment's default permission mode (the Kind values select yolo). func defaultSessionCreateBody() []byte { - body, _ := json.Marshal(map[string]any{"mode": "default"}) - return body + return []byte(`{}`) } func createSessionAs(ctx context.Context, addr, bearer string) (int, string) { diff --git a/e2e/k8s_execution/live_qualification_test.go b/e2e/k8s_execution/live_qualification_test.go index c1681bee16..5b4b2213e2 100644 --- a/e2e/k8s_execution/live_qualification_test.go +++ b/e2e/k8s_execution/live_qualification_test.go @@ -854,7 +854,7 @@ func createLiveSession(t *testing.T, ctx context.Context, addr, token string) li t.Fatal("exact live execution template revision unavailable") } bodyJSON, err := json.Marshal(map[string]any{ - "mode": "default", "limits": map[string]int{"max_turns": 8, "max_tool_calls": 20, "max_consecutive_failures": 3}, + "limits": map[string]int{"max_turns": 8, "max_tool_calls": 20, "max_consecutive_failures": 3}, "execution": map[string]any{"template": map[string]string{"id": "go", "revision": revision}}, }) if err != nil { diff --git a/e2e/k8s_execution/production_qualification_test.go b/e2e/k8s_execution/production_qualification_test.go index e7a0a9d6f2..af32ee610b 100644 --- a/e2e/k8s_execution/production_qualification_test.go +++ b/e2e/k8s_execution/production_qualification_test.go @@ -1329,7 +1329,7 @@ func TestKindExecutionProductionPendingDeleteOutageRecovery(t *testing.T) { func createSessionAfterProviderReload(t *testing.T, ctx context.Context, addr, token string) string { t.Helper() - body := []byte(`{"mode":"default","limits":{"max_turns":16,"max_tool_calls":24,"max_consecutive_failures":3}}`) + body := []byte(`{"limits":{"max_turns":16,"max_tool_calls":24,"max_consecutive_failures":3}}`) deadline := time.Now().Add(90 * time.Second) for { status, response := request(t, ctx, http.MethodPost, "http://"+addr+"/v1/sessions", token, body) diff --git a/e2e/k8s_execution/qualification_test.go b/e2e/k8s_execution/qualification_test.go index 35ca3d09f6..129f558d0d 100644 --- a/e2e/k8s_execution/qualification_test.go +++ b/e2e/k8s_execution/qualification_test.go @@ -844,7 +844,7 @@ func fixtureToken(t *testing.T, ctx context.Context, addr, pki, sub string) stri } func createSessionTemplate(t *testing.T, ctx context.Context, addr, token, id, revision string) string { t.Helper() - body, err := json.Marshal(map[string]any{"mode": "default", "execution": map[string]any{"template": map[string]string{"id": id, "revision": revision}}}) + body, err := json.Marshal(map[string]any{"execution": map[string]any{"template": map[string]string{"id": id, "revision": revision}}}) if err != nil { t.Fatal(err) } @@ -863,7 +863,8 @@ func createSessionTemplate(t *testing.T, ctx context.Context, addr, token, id, r func createSession(t *testing.T, ctx context.Context, addr, token string, templateRevision ...string) string { t.Helper() - body := map[string]any{"mode": "default", "limits": map[string]int{"max_turns": 16, "max_tool_calls": 24, "max_consecutive_failures": 3}} + // No mode: sessions start in the deployment default (the fixture selects yolo). + body := map[string]any{"limits": map[string]int{"max_turns": 16, "max_tool_calls": 24, "max_consecutive_failures": 3}} if len(templateRevision) > 0 { body["execution"] = map[string]any{"template": map[string]string{"id": "go", "revision": templateRevision[0]}} } diff --git a/engine/CHANGELOG.md b/engine/CHANGELOG.md index 197f92e348..967f8b4576 100644 --- a/engine/CHANGELOG.md +++ b/engine/CHANGELOG.md @@ -13,6 +13,13 @@ The covered surface is the eight core packages (`session`, `governance`, `learni ### Added +- **`ModeAuto` and `ModeYolo` permission modes** — adds `session.ModeAuto = "auto"` + and `session.ModeYolo = "yolo"` to `session.PermissionMode`. These complete the + five-mode vocabulary (`plan`, `default`, `acceptEdits`, `auto`, `yolo`) that the + unified permission-mode architecture requires; `Policy.Evaluate` now injects + allow-all and loose-substitution at evaluation time based on the session's current + mode, so mode switches are fully effective at runtime. Added (minor). + - **Schedule-store claim and retry conformance** — adds `scheduleconformance.RunClaimAndRetryConformance` with the complete shared suite plus unresolved prior-claim rejection, late-result isolation, and @@ -419,6 +426,34 @@ The covered surface is the eight core packages (`session`, `governance`, `learni ### Changed +- **Delegated children inherit an auto or yolo parent's mode** — a Subagent + child, Parallel branch, or Team member with no configured mode now runs in its + parent session's mode when that mode is `ModeAuto` or `ModeYolo`, so the + parent's mode-driven authority reaches its delegates. Any explicitly + configured mode (`WithChildMode`, `WithParallelChildMode`, `WithTeamMode`, or + a member definition's `permissionMode`), `ModeDefault` included, is kept. A + resumed Subagent child's persisted mode is re-admitted against the current + parent: it runs in the narrower of its stored mode and the mode a fresh child + would get. No signature changes. + +- **Mutating in-chat schedules fire in their creator's mode** — a + `mutating: true` schedule created through the `Schedule` tool now records the + executing session's permission mode as `ScheduleSpec.Mode` instead of always + `ModeDefault`. A yolo session's schedule therefore fires in yolo rather than + in a default session whose writes ask with no approver. Read-leaning + schedules still record `ModePlan`, and an unbound context still records + `ModeDefault`. The host re-admits the recorded mode at every fire. No + signature changes. + +- **Contextual review enforcement follows the executing session's mode** — + the engine now demotes a `ReviewPolicyProvider`'s enforcement to advisory + for an executing `ModeYolo` session, and never consults + `PermissionReviewPolicyProvider` for a yolo child. A reviewer shared with + delegated children should report the operator's configured enforcement: + a yolo parent stays advisory while its narrower child keeps an enforcing + checker. A reviewer that already demoted for yolo is unaffected. Changed + (breaking, pre-v1 minor); no signature changes. + - **Atomic one-shot retry eligibility (issue #2273)** — `port.ScheduleOneShotReArmer.ReArmOneShot` now requires store-side atomic validation of current disabled/settled-failure state, one-shot retry policy, diff --git a/engine/adapter/permpolicy/permpolicy.go b/engine/adapter/permpolicy/permpolicy.go index aacf92c3fa..46f8631d8c 100644 --- a/engine/adapter/permpolicy/permpolicy.go +++ b/engine/adapter/permpolicy/permpolicy.go @@ -63,9 +63,21 @@ type RuleResolver interface { // when nil, Evaluate consults only the static rules + learned rules (today's // behaviour), which is what the allow-all demo/forked-member policies want. type Policy struct { - eval *governance.Evaluator - store port.PermissionStore - resolver RuleResolver + eval *governance.Evaluator // standard evaluator (no loose substitution) + evalLoose *governance.Evaluator // yolo evaluator (with loose substitution for ModeYolo) + store port.PermissionStore + resolver RuleResolver +} + +// allowAllCLIRules returns the mode-driven allow-all rules injected by ModeAuto +// and ModeYolo at evaluation time (not at startup). ScopeCLI is above the +// built-in floor (ScopeBuiltinDefault), so a configured Deny or Ask in any +// scope above the floor still wins — the same guarantee the startup yoloAllowAllRule +// provided. It uses no Audience tag (AudienceAll), so it matches both main and +// child evaluators; for child engines the child floor already covers mutates, so +// this is harmless redundancy. +func allowAllCLIRules() []governance.Rule { + return []governance.Rule{{Scope: governance.ScopeCLI, Effect: governance.Allow}} } // AllowAllFloorRules returns the CANONICAL "default child posture" ruleset: a @@ -90,11 +102,17 @@ func AllowAllFloorRules() []governance.Rule { // per call by the underlying Evaluator. A nil store disables rule learning: Learn // is a no-op and Evaluate consults only the static rules. // -// Optional governance.EvaluatorOptions (e.g. WithLooseSubstitution for the --yolo -// posture) are forwarded to the underlying Evaluator. The resolver (file-based config) +// Optional governance.EvaluatorOptions are forwarded to the underlying Evaluator. +// A second evalLoose evaluator is built with WithLooseSubstitution(true) added; +// it is selected by ModeYolo at Evaluate time. The resolver (file-based config) // is unset; use NewPolicyWithResolver to wire it. func NewPolicy(rules []governance.Rule, store port.PermissionStore, evalOpts ...governance.EvaluatorOption) *Policy { - return &Policy{eval: governance.NewEvaluator(rules, evalOpts...), store: store} + looseOpts := append(append([]governance.EvaluatorOption{}, evalOpts...), governance.WithLooseSubstitution(true)) + return &Policy{ + eval: governance.NewEvaluator(rules, evalOpts...), + evalLoose: governance.NewEvaluator(rules, looseOpts...), + store: store, + } } // NewPolicyWithResolver is NewPolicy plus a RuleResolver that supplies file-based @@ -102,16 +120,20 @@ func NewPolicy(rules []governance.Rule, store port.PermissionStore, evalOpts ... // (issue #13). A nil resolver behaves exactly like NewPolicy. Optional // governance.EvaluatorOptions are forwarded to the underlying Evaluator. func NewPolicyWithResolver(rules []governance.Rule, store port.PermissionStore, resolver RuleResolver, evalOpts ...governance.EvaluatorOption) *Policy { - return &Policy{eval: governance.NewEvaluator(rules, evalOpts...), store: store, resolver: resolver} + looseOpts := append(append([]governance.EvaluatorOption{}, evalOpts...), governance.WithLooseSubstitution(true)) + return &Policy{ + eval: governance.NewEvaluator(rules, evalOpts...), + evalLoose: governance.NewEvaluator(rules, looseOpts...), + store: store, + resolver: resolver, + } } // acceptEditsRules are the floor-loosening ALLOW rules session.ModeAccept -// ("accept-edits") contributes: Edit and Write at ScopeCLI, the same scope the -// --yolo allow-all posture rule uses (issue #32's childRules AllowAllTools -// injection) for a runtime/session-level policy decision. Being scoped ABOVE -// ScopeBuiltinDefault, it can only ever LOOSEN the built-in mutate-ask floor +// ("accept-edits") contributes: Edit and Write at ScopeCLI. Being scoped ABOVE +// ScopeBuiltinDefault, they can only ever LOOSEN the built-in mutate-ask floor // (resolveSimpleRule's existing issue-#13 mechanic: a higher-precedence Allow -// may loosen ONLY an Ask scoped at ScopeBuiltinDefault) — it can never suppress +// may loosen ONLY an Ask scoped at ScopeBuiltinDefault) — they can never suppress // a CONFIGURED deny/ask for Edit/Write from any scope above the floor, and Shell // is untouched (accept-edits auto-accepts file edits only, mirroring the // client-visible "accept edits" contract; it is not a broader auto-run mode). @@ -121,20 +143,34 @@ var acceptEditsRules = []governance.Rule{ } // Evaluate returns the permission decision for tool call c under mode, scoped to -// sessionID and the session workspace ws. Plan mode (session.ModePlan) forces a -// deny for mutating tools (Edit/Write and non read-only Shell) BEFORE any extra -// rule is consulted. Accept-edits mode (session.ModeAccept) contributes the -// floor-loosening acceptEditsRules into the extra set, so Edit/Write auto-allow -// UNLESS a configured (above-floor) deny/ask says otherwise. All other modes -// evaluate against the static rule set PLUS this session's learned allows (when -// a store is configured) PLUS the file-based config rules that apply to ws (when -// a resolver is configured). Because resolution is deny-dominant across the -// whole merged set, an extra allow can never override a static (or config) -// deny/ask. +// sessionID and the session workspace ws. +// +// Mode drives the full effective policy at evaluation time: +// - ModePlan: hard-deny of mutating tools BEFORE any rule (plan-mode gate). +// - ModeAccept: injects acceptEditsRules (Edit/Write auto-allow). +// - ModeAuto: injects allowAllCLIRules() — a ScopeCLI allow-all that loosens +// the built-in mutate-ask floor. Configured deny/ask in any scope still win. +// - ModeYolo: same as ModeAuto plus loose substitution (the built-in +// substitution Ask floor is lifted so $()/backtick commands auto-run). +// - ModeDefault: static rules + learned allows only. +// +// Allow-all behavior is NOT baked into the evaluator's base rules at startup; +// it is injected here based on the session's CURRENT mode. Switching from +// yolo→default actually removes the allow-all floor. A configured Deny in any +// scope and a deliberately configured Ask always win. func (p *Policy) Evaluate(ctx context.Context, sessionID session.SessionID, mode session.PermissionMode, c session.ToolCall, ws tool.WorkspaceReader) port.PermissionResult { var extra []governance.Rule - if mode == session.ModeAccept { + planMode := mode == session.ModePlan + eval := p.eval // default: standard evaluator, no loose substitution + + switch mode { + case session.ModeAccept: extra = append(extra, acceptEditsRules...) + case session.ModeAuto: + extra = append(extra, allowAllCLIRules()...) + case session.ModeYolo: + extra = append(extra, allowAllCLIRules()...) + eval = p.evalLoose // yolo: loose substitution enabled } if p.store != nil { extra = append(extra, p.store.Rules(sessionID)...) @@ -142,7 +178,7 @@ func (p *Policy) Evaluate(ctx context.Context, sessionID session.SessionID, mode if p.resolver != nil { extra = append(extra, p.resolver.Resolve(ctx, ws)...) } - return port.PermissionResult{Decision: p.eval.EvaluateWith(c.Name, c.Args, mode == session.ModePlan, extra)} + return port.PermissionResult{Decision: eval.EvaluateWith(c.Name, c.Args, planMode, extra)} } // Learn records a per-session allow rule for tool call c (the model's "allow diff --git a/engine/agent/actionreview.go b/engine/agent/actionreview.go index 1857d8905a..6c22d99a97 100644 --- a/engine/agent/actionreview.go +++ b/engine/agent/actionreview.go @@ -686,7 +686,7 @@ func (e *Engine) prepareActionAssessment(ctx context.Context, r *Run, sess *sess func (e *Engine) reviewChildPermissionAsk(ctx context.Context, r *Run, sess *session.Session, env tool.Environment, ask session.PendingAsk) *uint64 { if e.deps.Role == "" || r == nil || r.reviewRoot == nil || r.reviewRoot.reviewer == nil || ask.AskProvenance != governance.AskProvenanceBuiltinSubstitutionFloor || - ask.Tool != tool.ShellToolName || sess.Mode == session.ModePlan { + ask.Tool != tool.ShellToolName || sess.Mode == session.ModePlan || sess.Mode == session.ModeYolo { return nil } call := session.NewToolCall(ask.Call, ask.Tool, append(json.RawMessage(nil), ask.Args...)) @@ -817,7 +817,7 @@ func (e *Engine) resolveActionAssessment(ctx context.Context, r *Run, sess *sess decision = "unresolved" result.Assessment = ReviewUnresolved } - _, enforce := reviewPolicy(r.reviewRoot.reviewer, call.Name, ReviewJobAction, assessment.err != nil) + _, enforce := reviewPolicy(r.reviewRoot.reviewer, sess.Mode, call.Name, ReviewJobAction, assessment.err != nil) if assessment.err != nil { var terminal GuardrailReviewTerminalFailure if errors.As(assessment.err, &terminal) && terminal.GuardrailReviewTerminalFailure() { @@ -938,7 +938,7 @@ func (e *Engine) reviewActionWithTail(ctx context.Context, r *Run, sess *session if r.reviewRoot == nil || r.reviewRoot.reviewer == nil { return session.ToolResult{}, nil, false, true } - if applies, _ := reviewPolicy(r.reviewRoot.reviewer, call.Name, ReviewJobAction, false); !applies { + if applies, _ := reviewPolicy(r.reviewRoot.reviewer, sess.Mode, call.Name, ReviewJobAction, false); !applies { if !r.reviewRoot.admitPrincipalRevisions(nil, auth.permissionReviewRevision) { res := session.NewToolError(call.ID, "contextual guardrail review context changed before execution; retry the action for a fresh assessment") e.publishToolResult(r, session.Event{Type: session.EvToolResult, Turn: turnIdx, ToolResult: ptr(res)}) diff --git a/engine/agent/child_mode_inherit_test.go b/engine/agent/child_mode_inherit_test.go new file mode 100644 index 0000000000..f582682278 --- /dev/null +++ b/engine/agent/child_mode_inherit_test.go @@ -0,0 +1,311 @@ +package agent_test + +import ( + "context" + "strings" + "sync/atomic" + "testing" + "time" + + "github.com/stacklok/mecatl/engine/adapter/memstore" + "github.com/stacklok/mecatl/engine/adapter/mockllm" + "github.com/stacklok/mecatl/engine/adapter/permpolicy" + "github.com/stacklok/mecatl/engine/agent" + "github.com/stacklok/mecatl/engine/session" + "github.com/stacklok/mecatl/engine/tool" +) + +// TestEffectiveChildMode pins the inheritance table: an unconfigured child +// inherits an auto or yolo parent and otherwise runs in default; every +// explicitly configured child mode, default included, is kept. +func TestEffectiveChildMode(t *testing.T) { + for _, tc := range []struct { + configured, parent, want session.PermissionMode + }{ + {"", session.ModeYolo, session.ModeYolo}, + {"", session.ModeAuto, session.ModeAuto}, + {"", session.ModeAccept, session.ModeDefault}, + {"", session.ModePlan, session.ModeDefault}, + {"", "", session.ModeDefault}, + {session.ModeDefault, session.ModeYolo, session.ModeDefault}, + {session.ModeDefault, session.ModeAuto, session.ModeDefault}, + {session.ModePlan, session.ModeYolo, session.ModePlan}, + {session.ModeAccept, session.ModeAuto, session.ModeAccept}, + } { + if got := agent.EffectiveChildModeForTest(tc.configured, tc.parent); got != tc.want { + t.Errorf("effectiveChildMode(%q, parent %q) = %q, want %q", tc.configured, tc.parent, got, tc.want) + } + } +} + +// TestSubagentInheritsYoloParentMode drives a real parent run: a child tool +// that asks under the default mode (no rules, headless → denied) runs when the +// parent session is in yolo, because the child inherits the parent's mode. +func TestSubagentInheritsYoloParentMode(t *testing.T) { + for _, tc := range []struct { + parent session.PermissionMode + want bool + }{ + {session.ModeDefault, false}, + {session.ModeYolo, true}, + } { + var executed atomic.Bool + childTool := &fakeTool{name: "Mutate", readOnly: false, + exec: func(_ context.Context, in session.ToolCall, _ tool.Workspace) (session.ToolResult, error) { + executed.Store(true) + return session.NewToolResult(in.ID, "ran"), nil + }} + childEngine := agent.NewEngine(agent.Deps{ + LLM: mockllm.New( + mockllm.ToolCallTurn(toolCall("k1", "Mutate", `{}`)), + mockllm.TextTurn("child done"), + ), + Catalog: catalogWith(t, childTool), + Policy: permpolicy.NewPolicy(nil, nil), + Model: "child-model", + }) + task := agent.NewSubagentTool(childEngine) + e := newEngine(agent.Deps{LLM: mockllm.New( + mockllm.ToolCallTurn(toolCall("p1", "Subagent", `{"prompt":"go"}`)), + mockllm.TextTurn("ok"), + ), Catalog: catalogWith(t, task)}) + sess := newSession(t, session.Limits{}) + if err := sess.SetMode(tc.parent); err != nil { + t.Fatal(err) + } + done := make(chan struct{}) + go func() { + drain(e.Run(context.Background(), sess, agent.MemEnv("/ws"), agent.RunRequest{Text: "go"})) + close(done) + }() + select { + case <-done: + case <-time.After(5 * time.Second): + t.Fatal("parent run did not complete") + } + if executed.Load() != tc.want { + t.Errorf("parent %s: child mutating tool executed=%t, want %t", tc.parent, executed.Load(), tc.want) + } + } +} + +// mutatingChildEngine builds a child engine whose only tool mutates and asks +// under the default mode (no rules, headless → denied), counting executions. +func mutatingChildEngine(t *testing.T, executed *atomic.Int32, turns ...mockllm.Turn) *agent.Engine { + t.Helper() + childTool := &fakeTool{name: "Mutate", readOnly: false, + exec: func(_ context.Context, in session.ToolCall, _ tool.Workspace) (session.ToolResult, error) { + executed.Add(1) + return session.NewToolResult(in.ID, "ran"), nil + }} + return agent.NewEngine(agent.Deps{ + LLM: mockllm.New(turns...), + Catalog: catalogWith(t, childTool), + Policy: permpolicy.NewPolicy(nil, nil), + Model: "child-model", + }) +} + +// runParent drives one parent run in mode to completion and returns its events. +func runParent(t *testing.T, sess *session.Session, mode session.PermissionMode, parentLLM *mockllm.Provider, tools ...tool.Tool) []session.Event { + t.Helper() + if sess.State == session.StateCompleted { + if err := sess.Reopen(); err != nil { + t.Fatal(err) + } + } + if err := sess.SetMode(mode); err != nil { + t.Fatal(err) + } + e := newEngine(agent.Deps{LLM: parentLLM, Catalog: catalogWith(t, tools...)}) + return drainObserving(t, e.Run(context.Background(), sess, agent.MemEnv("/ws"), agent.RunRequest{Text: "go"}), nil) +} + +// TestExplicitDefaultChildModeIsKeptUnderYoloParent proves an explicitly +// configured default mode is a restriction, not "inherit": fresh foreground +// and background Subagent children and Parallel branches keep default's ask +// (headless → denied) under a yolo parent, while unconfigured ones inherit yolo. +func TestExplicitDefaultChildModeIsKeptUnderYoloParent(t *testing.T) { + childTurns := func() []mockllm.Turn { + return []mockllm.Turn{mockllm.ToolCallTurn(toolCall("k1", "Mutate", `{}`)), mockllm.TextTurn("child done")} + } + for _, tc := range []struct { + name string + explicit bool + build func(*agent.Engine) tool.Tool + parent []mockllm.Turn + }{ + {"subagent", true, func(e *agent.Engine) tool.Tool { + return agent.NewSubagentTool(e, agent.WithChildMode(session.ModeDefault)) + }, + []mockllm.Turn{mockllm.ToolCallTurn(toolCall("p1", "Subagent", `{"prompt":"go"}`)), mockllm.TextTurn("ok")}}, + {"subagent-inherit", false, func(e *agent.Engine) tool.Tool { return agent.NewSubagentTool(e) }, + []mockllm.Turn{mockllm.ToolCallTurn(toolCall("p1", "Subagent", `{"prompt":"go"}`)), mockllm.TextTurn("ok")}}, + {"subagent-background", true, func(e *agent.Engine) tool.Tool { + return agent.NewSubagentTool(e, agent.WithChildMode(session.ModeDefault)) + }, + []mockllm.Turn{ + mockllm.ToolCallTurn(toolCall("p1", "Subagent", `{"prompt":"go","background":true}`)), + mockllm.ToolCallTurn(toolCall("p2", "SubagentStatus", `{"agent_id":"subagent-s1-p1","wait_ms":30000}`)), + mockllm.TextTurn("ok"), + }}, + {"parallel", true, func(e *agent.Engine) tool.Tool { + return agent.NewParallelTool(e, &memForker{}, agent.WithParallelChildMode(session.ModeDefault)) + }, []mockllm.Turn{mockllm.ToolCallTurn(toolCall("p1", "Parallel", `{"tasks":["A"]}`)), mockllm.TextTurn("ok")}}, + {"parallel-inherit", false, func(e *agent.Engine) tool.Tool { return agent.NewParallelTool(e, &memForker{}) }, + []mockllm.Turn{mockllm.ToolCallTurn(toolCall("p1", "Parallel", `{"tasks":["A"]}`)), mockllm.TextTurn("ok")}}, + } { + t.Run(tc.name, func(t *testing.T) { + var executed atomic.Int32 + delegate := tc.build(mutatingChildEngine(t, &executed, childTurns()...)) + runParent(t, newSession(t, session.Limits{}), session.ModeYolo, mockllm.New(tc.parent...), delegate, agent.NewSubagentStatusTool()) + if got, want := executed.Load() == 1, !tc.explicit; got != want { + t.Errorf("child mutating tool executed=%t under a yolo parent, want %t", got, want) + } + }) + } +} + +// TestResumedSubagentReadmitsModeAfterParentDowngrade proves a child persisted +// under a yolo parent does not keep yolo's authority when it is resumed, after a +// restart, by a parent that has since been downgraded to default. Both the +// foreground and the background resume paths re-admit the stored mode before +// the child runs; the child's history is preserved. +func TestResumedSubagentReadmitsModeAfterParentDowngrade(t *testing.T) { + for _, background := range []bool{false, true} { + name := "foreground" + if background { + name = "background" + } + t.Run(name, func(t *testing.T) { + store := memstore.New() + var executed atomic.Int32 + sess := newSession(t, session.Limits{}) + + // Phase 1: the yolo parent creates the child, whose mutation runs. + first := agent.NewSubagentTool(mutatingChildEngine(t, &executed, + mockllm.ToolCallTurn(toolCall("k1", "Mutate", `{}`)), mockllm.TextTurn("FIRST_PASS")), + agent.WithSubagentStore(store)) + runParent(t, sess, session.ModeYolo, mockllm.New( + mockllm.ToolCallTurn(toolCall("p1", "Subagent", `{"prompt":"go"}`)), mockllm.TextTurn("ok")), first) + if executed.Load() != 1 { + t.Fatalf("yolo parent: child mutation executed %d times, want 1", executed.Load()) + } + if stored, err := store.Load(context.Background(), "subagent-s1-p1"); err != nil || stored.Mode != session.ModeYolo { + t.Fatalf("persisted child = %v, %v; want mode yolo", stored, err) + } + + // Phase 2: a restarted harness (fresh tool and engines, same store) + // resumes the child from a parent downgraded to default. + second := agent.NewSubagentTool(mutatingChildEngine(t, &executed, + mockllm.ToolCallTurn(toolCall("k2", "Mutate", `{}`)), mockllm.TextTurn("SECOND_PASS")), + agent.WithSubagentStore(store)) + parentTurns := []mockllm.Turn{mockllm.ToolCallTurn(toolCall("p2", "Subagent", `{"prompt":"again","resume":"subagent-s1-p1"}`)), mockllm.TextTurn("ok")} + collect := session.ToolCallID("p2") + if background { + parentTurns = []mockllm.Turn{ + mockllm.ToolCallTurn(toolCall("p2", "Subagent", `{"prompt":"again","resume":"subagent-s1-p1","background":true}`)), + mockllm.ToolCallTurn(toolCall("p3", "SubagentStatus", `{"agent_id":"subagent-s1-p1","wait_ms":30000}`)), + mockllm.TextTurn("ok"), + } + collect = "p3" + } + evs := runParent(t, sess, session.ModeDefault, mockllm.New(parentTurns...), second, agent.NewSubagentStatusTool()) + if res := resultByCallID(evs)[collect]; res == nil || !strings.Contains(res.Content, "SECOND_PASS") { + t.Fatalf("resumed child did not complete: %+v; events %v; state %s", res, typesOf(evs), sess.State) + } + if executed.Load() != 1 { + t.Errorf("downgraded parent: resumed child kept yolo authority (mutation executed %d times, want 1)", executed.Load()) + } + stored, err := store.Load(context.Background(), "subagent-s1-p1") + if err != nil { + t.Fatal(err) + } + if stored.Mode != session.ModeDefault { + t.Errorf("re-persisted child mode = %q, want %q", stored.Mode, session.ModeDefault) + } + var kept bool + for _, m := range stored.Conversation.Messages { + kept = kept || strings.Contains(m.Text, "FIRST_PASS") + } + if !kept { + t.Error("resumed child lost its prior history") + } + }) + } +} + +// TestResumedChildMode pins the re-admission table: the narrower of the stored +// mode and what a fresh child of the current parent would get. +func TestResumedChildMode(t *testing.T) { + for _, tc := range []struct { + stored, configured, parent, want session.PermissionMode + }{ + {session.ModeYolo, "", session.ModeDefault, session.ModeDefault}, + {session.ModeYolo, "", session.ModeAuto, session.ModeAuto}, + {session.ModeYolo, "", session.ModeYolo, session.ModeYolo}, + {session.ModeAuto, "", session.ModeYolo, session.ModeAuto}, + {session.ModeDefault, "", session.ModeYolo, session.ModeDefault}, + {session.ModePlan, "", session.ModeYolo, session.ModePlan}, + {session.ModeYolo, session.ModeAccept, session.ModeYolo, session.ModeAccept}, + } { + if got := agent.ResumedChildModeForTest(tc.stored, tc.configured, tc.parent); got != tc.want { + t.Errorf("resumedChildMode(stored %q, configured %q, parent %q) = %q, want %q", tc.stored, tc.configured, tc.parent, got, tc.want) + } + } +} + +// rejectingActionReviewer prohibits every action and enforces its finding. +type rejectingActionReviewer struct{} + +func (rejectingActionReviewer) Review(context.Context, agent.ToolReviewRequest, agent.ReviewEvidenceSource) (agent.ToolReviewResult, session.AuxiliaryUsage, error) { + return agent.ToolReviewResult{Assessment: agent.ReviewProhibited}, session.AuxiliaryUsage{}, nil +} + +func (rejectingActionReviewer) GuardrailReviewPolicy(_ string, job agent.ReviewJob, _ bool) (bool, bool) { + return job == agent.ReviewJobAction, true +} + +// TestSharedReviewerEnforcesForNarrowerChildOfYoloParent proves the reviewer a +// yolo parent shares with its Subagent child selects enforcement from the +// CHILD's mode: an explicitly default child's prohibited action is blocked, +// while an inheriting yolo child (like its parent) treats it as advisory. +func TestSharedReviewerEnforcesForNarrowerChildOfYoloParent(t *testing.T) { + for _, tc := range []struct { + name string + opts []agent.SubagentOption + want bool + }{ + {"explicit-default", []agent.SubagentOption{agent.WithChildMode(session.ModeDefault)}, false}, + {"inherit", nil, true}, + } { + t.Run(tc.name, func(t *testing.T) { + var executed atomic.Bool + probe := &fakeTool{name: "Probe", readOnly: true, + exec: func(_ context.Context, in session.ToolCall, _ tool.Workspace) (session.ToolResult, error) { + executed.Store(true) + return session.NewToolResult(in.ID, "probed"), nil + }} + childEngine := agent.NewEngine(agent.Deps{ + LLM: mockllm.New(mockllm.ToolCallTurn(toolCall("k1", "Probe", `{}`)), mockllm.TextTurn("child done")), + Catalog: catalogWith(t, probe), + Policy: permpolicy.NewPolicy(permpolicy.AllowAllFloorRules(), nil), + Model: "child-model", + }) + task := agent.NewSubagentTool(childEngine, tc.opts...) + sess := newSession(t, session.Limits{}) + if err := sess.SetMode(session.ModeYolo); err != nil { + t.Fatal(err) + } + e := newEngine(agent.Deps{ + LLM: mockllm.New(mockllm.ToolCallTurn(toolCall("p1", "Subagent", `{"prompt":"go"}`)), mockllm.TextTurn("ok")), + Catalog: catalogWith(t, task), + ToolReviewer: rejectingActionReviewer{}, + }) + drainObserving(t, e.Run(context.Background(), sess, agent.MemEnv("/ws"), agent.RunRequest{Text: "go"}), nil) + if executed.Load() != tc.want { + t.Errorf("child Probe executed=%t under a yolo parent with a rejecting reviewer, want %t", executed.Load(), tc.want) + } + }) + } +} diff --git a/engine/agent/dispatch.go b/engine/agent/dispatch.go index 88c57ea6cb..5ffad3848d 100644 --- a/engine/agent/dispatch.go +++ b/engine/agent/dispatch.go @@ -432,7 +432,7 @@ func (e *Engine) fanOutReadBatchActionAssessments(ctx context.Context, r *Run, s if r.reviewRoot == nil || r.reviewRoot.reviewer == nil { continue } - if applies, _ := reviewPolicy(r.reviewRoot.reviewer, p.call.Name, ReviewJobAction, false); applies { + if applies, _ := reviewPolicy(r.reviewRoot.reviewer, sess.Mode, p.call.Name, ReviewJobAction, false); applies { assessment := e.prepareActionAssessment(ctx, r, sess, env, p.call) p.assessment = &assessment reviewWG.Add(1) @@ -2058,6 +2058,11 @@ func (e *Engine) timeExecute(ctx context.Context, r *Run, sess *session.Session, ev.Turn = turnIdx r.children.safeEmit(ev) } + if sess != nil { + // Read at execution, like parentCaps' parentMode, so a schedule created + // after a mid-session mode change records the mode it was created under. + ctx = withSessionMode(ctx, sess.Mode) + } switch ct := t.(type) { case childCapableTool: // Child-capable tools may execute in read-batch workers. Keep their returned @@ -2140,6 +2145,7 @@ func (e *Engine) parentCaps(r *Run, sess *session.Session, turnIdx int, reporter // parentCaps.parentSessionID. caps.parentSessionID = sess.ID caps.parentIncarnation = sess.Incarnation() + caps.parentMode = sess.Mode } // The OPT-IN headless ask reviewer: bind the engine's reviewer + THIS run's // breaker + the timeout into a closure resolveChildAsk consults on the headless diff --git a/engine/agent/export_test.go b/engine/agent/export_test.go index 7571960550..c4dadf23e4 100644 --- a/engine/agent/export_test.go +++ b/engine/agent/export_test.go @@ -11,6 +11,11 @@ import "github.com/stacklok/mecatl/engine/session" // test-only on purpose — see the withSessionOrigin doc-comment. var WithSessionOrigin = withSessionOrigin +// WithSessionMode exposes withSessionMode so the external tests can pin the +// schedule tool's handling of a context the dispatch stamp cannot produce for +// it (a plan-mode session holding the non-plan tool). +var WithSessionMode = withSessionMode + // EnqueueSteerForTest exposes the canonical Run steer entry point to external // drain tests. var EnqueueSteerForTest = (*Run).EnqueueSteer @@ -36,3 +41,9 @@ func RefreshReviewTasksForTest(r *Run, messages []session.Message) { func WithParallelBranchRegisteredForTest(fn func(int)) ParallelOption { return func(t *ParallelTool) { t.onBranchRegistered = fn } } + +// EffectiveChildModeForTest exposes the delegated-child mode rule. +var EffectiveChildModeForTest = effectiveChildMode + +// ResumedChildModeForTest exposes the resumed-child mode re-admission rule. +var ResumedChildModeForTest = resumedChildMode diff --git a/engine/agent/inboundreview.go b/engine/agent/inboundreview.go index 1351fd1776..365966f221 100644 --- a/engine/agent/inboundreview.go +++ b/engine/agent/inboundreview.go @@ -42,25 +42,32 @@ type inboundAssessment struct { err error applies bool enforce bool + mode session.PermissionMode principalRevision uint64 } -func reviewPolicy(reviewer ToolReviewer, toolName string, job ReviewJob, operationalFailure bool) (bool, bool) { +// reviewPolicy resolves the reviewer's configured applicability and enforcement +// for the EXECUTING session's mode. A shared reviewRoot carries the operator's +// undemoted rules, so a yolo session (no enforcing checker; advisory monitoring +// only) demotes enforcement here while a narrower delegated child of that yolo +// parent keeps it. +func reviewPolicy(reviewer ToolReviewer, mode session.PermissionMode, toolName string, job ReviewJob, operationalFailure bool) (bool, bool) { + applies, enforce := job == ReviewJobAction, true if policy, ok := reviewer.(ReviewPolicyProvider); ok { - return policy.GuardrailReviewPolicy(toolName, job, operationalFailure) + applies, enforce = policy.GuardrailReviewPolicy(toolName, job, operationalFailure) } // ToolReviewer predated inbound escrow. Reviewers that do not explicitly // advertise policy retain action-only behavior rather than unexpectedly // inspecting and holding every tool result. - return job == ReviewJobAction, true + return applies, enforce && mode != session.ModeYolo } func (e *Engine) prepareInboundAssessment(ctx context.Context, r *Run, sess *session.Session, env tool.Environment, call session.ToolCall, result session.ToolResult) inboundAssessment { if r.reviewRoot == nil || r.reviewRoot.reviewer == nil { return inboundAssessment{} } - applies, enforce := reviewPolicy(r.reviewRoot.reviewer, call.Name, ReviewJobInbound, false) - assessment := inboundAssessment{applies: applies, enforce: enforce} + applies, enforce := reviewPolicy(r.reviewRoot.reviewer, sess.Mode, call.Name, ReviewJobInbound, false) + assessment := inboundAssessment{applies: applies, enforce: enforce, mode: sess.Mode} if !applies { return assessment } @@ -115,7 +122,7 @@ func assessInbound(parent context.Context, r *Run, assessment *inboundAssessment if assessment.err != nil { assessment.result.Assessment = ReviewUnresolved recordReviewFailure(r.reviewRoot.reviewer, assessment.result, assessment.err) - _, assessment.enforce = reviewPolicy(r.reviewRoot.reviewer, assessment.request.EffectiveCall.Name, ReviewJobInbound, true) + _, assessment.enforce = reviewPolicy(r.reviewRoot.reviewer, assessment.mode, assessment.request.EffectiveCall.Name, ReviewJobInbound, true) var terminal GuardrailReviewTerminalFailure if errors.As(assessment.err, &terminal) && terminal.GuardrailReviewTerminalFailure() { assessment.enforce = true @@ -136,7 +143,7 @@ func assessInbound(parent context.Context, r *Run, assessment *inboundAssessment assessment.err = reviewFailure(ReviewFailureInvalidAssessment) } if assessment.err != nil { - _, assessment.enforce = reviewPolicy(r.reviewRoot.reviewer, assessment.request.EffectiveCall.Name, ReviewJobInbound, true) + _, assessment.enforce = reviewPolicy(r.reviewRoot.reviewer, assessment.mode, assessment.request.EffectiveCall.Name, ReviewJobInbound, true) var terminal GuardrailReviewTerminalFailure if errors.As(assessment.err, &terminal) && terminal.GuardrailReviewTerminalFailure() { assessment.enforce = true diff --git a/engine/agent/parallel.go b/engine/agent/parallel.go index 0386f74ab8..5245b0a490 100644 --- a/engine/agent/parallel.go +++ b/engine/agent/parallel.go @@ -139,8 +139,8 @@ type ParallelTool struct { // limits bound a single child branch run. Defaults to defaultChildLimits. limits session.Limits - // childMode is the permission mode each child session runs under. Defaults to - // session.ModeDefault. + // childMode is the permission mode each child session runs under. Empty (the + // default) inherits the parent's mode; see effectiveChildMode. childMode session.PermissionMode // maxBranches caps the fan-out per Parallel call. @@ -210,8 +210,10 @@ func WithParallelChildLimits(l session.Limits) ParallelOption { return func(t *ParallelTool) { t.limits = l } } -// WithParallelChildMode sets the permission mode each child branch session runs under -// (default session.ModeDefault). +// WithParallelChildMode sets the permission mode each child branch session runs under. +// Without it a branch inherits an auto or yolo parent's mode and otherwise runs in +// session.ModeDefault (see effectiveChildMode); an explicit session.ModeDefault keeps +// default even under an auto or yolo parent. func WithParallelChildMode(m session.PermissionMode) ParallelOption { return func(t *ParallelTool) { t.childMode = m } } @@ -334,7 +336,6 @@ func NewParallelTool(childEngine *Engine, forker tool.EnvironmentForker, opts .. childEngine: childEngine, forker: forker, limits: defaultChildLimits, - childMode: session.ModeDefault, maxBranches: defaultMaxBranches, concurrency: defaultParallelConcurrency, idPrefix: strings.TrimSuffix(ParallelSessionPrefix, "-"), // the exported convention is the source @@ -1179,12 +1180,12 @@ func (t *ParallelTool) runBranch(ctx context.Context, callID session.ToolCallID, if caps.parentSessionID == "" { // Direct Tool.Execute has no parent aggregate identity. Classify that // custom-host path unknown rather than fabricating lineage or granting main. - childSess = newChildSessionInEnvironment(t.childSessionID("", callID, i), t.childMode, + childSess = newChildSessionInEnvironment(t.childSessionID("", callID, i), effectiveChildMode(t.childMode, caps.parentMode), childEnv, t.limits, branchEngine.now()) err = childSess.RestoreSessionMetadata(session.SessionKindUnknown, session.SessionRelationship{}) } else { childSess, err = newParallelBranchSessionInEnvironment( - t.childSessionID(caps.parentSessionID, callID, i), t.childMode, + t.childSessionID(caps.parentSessionID, callID, i), effectiveChildMode(t.childMode, caps.parentMode), childEnv, t.limits, branchEngine.now(), caps.parentSessionID, caps.parentIncarnation, callID, i) } diff --git a/engine/agent/review.go b/engine/agent/review.go index f77b547275..5c37057719 100644 --- a/engine/agent/review.go +++ b/engine/agent/review.go @@ -157,14 +157,18 @@ type ReviewEvidencePreparer interface { // toolName and job identify the candidate rule; operationalFailure selects its // checker-down posture. The returns report whether review applies and whether a // finding or unresolved assessment is enforced. A ToolReviewer without this -// capability retains action-only enforcement. +// capability retains action-only enforcement. Report the operator's configured +// enforcement independent of permission mode: the engine demotes it to advisory +// for each executing yolo-mode session, including delegated children that share +// the reviewer. type ReviewPolicyProvider interface { GuardrailReviewPolicy(toolName string, job ReviewJob, operationalFailure bool) (applies, enforce bool) } // PermissionReviewPolicyProvider reports whether an exact child Shell call has // enabled, enforcing, matching action-review coverage. Advisory, skipped, and -// unmatched rules must return false. +// unmatched rules must return false. The engine never consults it for a +// yolo-mode child. type PermissionReviewPolicyProvider interface { GuardrailPermissionReviewEligible(session.ToolCall) bool } diff --git a/engine/agent/scheduletool.go b/engine/agent/scheduletool.go index 84fef0678c..bfbcb66265 100644 --- a/engine/agent/scheduletool.go +++ b/engine/agent/scheduletool.go @@ -370,9 +370,22 @@ func (t *ScheduleTool) create(ctx context.Context, call session.ToolCall, args s if err != nil { return session.NewToolError(call.ID, "Schedule create: "+err.Error()) } - mode := session.ModeDefault - if !args.Mutating { - mode = session.ModePlan + // A read-leaning schedule always fires in plan (the Mutating/Mode + // invariant). A mutating one fires in the CREATING session's mode, read + // from the run context and never from args: a yolo session's schedule must + // not fire into a default session whose writes ask with no approver. The + // fire re-admits that mode against the server's ceiling, so a server + // tightened after creation refuses the fire rather than escalating it. An + // unbound context (no executing session) keeps the conservative default. + mode := session.ModePlan + if args.Mutating { + mode = sessionModeFromContext(ctx) + if mode == "" { + mode = session.ModeDefault + } + if mode == session.ModePlan { + return session.NewToolError(call.ID, "Schedule create: plan mode is active: a mutating Schedule create is not permitted; present a plan and exit plan mode first") + } } origin := sessionOriginFromContext(ctx) spec := port.ScheduleSpec{ diff --git a/engine/agent/scheduletool_test.go b/engine/agent/scheduletool_test.go index 5c1df23eca..96e24a4519 100644 --- a/engine/agent/scheduletool_test.go +++ b/engine/agent/scheduletool_test.go @@ -3,14 +3,18 @@ package agent_test import ( "context" "errors" + "fmt" "strings" "testing" "time" "github.com/stacklok/mecatl/engine/adapter/memfs" + "github.com/stacklok/mecatl/engine/adapter/mockllm" + "github.com/stacklok/mecatl/engine/adapter/permpolicy" "github.com/stacklok/mecatl/engine/agent" "github.com/stacklok/mecatl/engine/port" "github.com/stacklok/mecatl/engine/session" + "github.com/stacklok/mecatl/engine/tool" ) // scheduletool_test.go pins the ScheduleTool's in-engine behaviour: the verb @@ -647,3 +651,85 @@ func TestScheduleTool_InspectRendersTerminalFire(t *testing.T) { t.Fatalf("terminal inspect rendered \"in-flight\" = %q (a terminal fire must NOT render the in-flight marker)", res.Content) } } + +// TestScheduleTool_MutatingCreateRecordsCreatingSessionMode pins that a mutating +// in-chat schedule fires in the mode of the session that created it, stamped by +// dispatch from the executing session through a real Engine.Run, while a +// read-leaning schedule records plan whatever the creating mode. Recording a +// fixed default instead made a yolo session's schedule fire into a default +// session whose writes ask with no approver, hanging the fire. +func TestScheduleTool_MutatingCreateRecordsCreatingSessionMode(t *testing.T) { + t.Parallel() + cases := []struct { + name string + mode session.PermissionMode + mutating bool + want session.PermissionMode + }{ + {name: "default mutating", mode: session.ModeDefault, mutating: true, want: session.ModeDefault}, + {name: "accept-edits mutating", mode: session.ModeAccept, mutating: true, want: session.ModeAccept}, + {name: "yolo mutating", mode: session.ModeYolo, mutating: true, want: session.ModeYolo}, + {name: "yolo read-leaning", mode: session.ModeYolo, mutating: false, want: session.ModePlan}, + {name: "default read-leaning", mode: session.ModeDefault, mutating: false, want: session.ModePlan}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + args := fmt.Sprintf(`{"verb":"create","name":"s","prompt":"p","cron":"@every 1h","mutating":%t}`, tc.mutating) + llm := mockllm.New( + mockllm.ToolCallTurn(toolCall("c1", agent.ScheduleToolName, args)), + mockllm.TextTurn("done"), + ) + mgr := newStubScheduleManager() + cat := tool.NewCatalog() + cat.MustRegister(agent.NewScheduleTool(mgr)) + eng := agent.NewEngine(agent.Deps{ + LLM: llm, + Catalog: cat, + Policy: permpolicy.NewPolicy(permpolicy.AllowAllFloorRules(), nil), + }) + sess := session.New("creator", tc.mode, session.EnvironmentRef{Kind: session.EnvKindLocal, ID: "/ws", Revision: "in-tree-v1"}, session.Limits{}, time.Unix(0, 0)) + drain(eng.Run(context.Background(), sess, agent.MemEnv("/ws"), agent.RunRequest{Text: "schedule it"})) + + if len(mgr.created) != 1 { + t.Fatalf("created %d schedules, want 1", len(mgr.created)) + } + got := mgr.created[0] + if got.Mode != tc.want || got.Mutating != tc.mutating { + t.Fatalf("recorded (mode %q, mutating %t), want (mode %q, mutating %t)", got.Mode, got.Mutating, tc.want, tc.mutating) + } + }) + } +} + +// TestScheduleTool_MutatingCreateModeFallbacks pins the two contexts the +// dispatch stamp does not cover: an unbound context (no executing session) +// keeps the conservative default, and a plan-mode context holding the non-plan +// tool refuses the mutating create rather than recording a mutating plan-mode +// schedule that could never write. +func TestScheduleTool_MutatingCreateModeFallbacks(t *testing.T) { + t.Parallel() + create := `{"verb":"create","name":"s","prompt":"p","cron":"@every 1h","mutating":true}` + + mgr := newStubScheduleManager() + res, err := agent.NewScheduleTool(mgr).Execute(context.Background(), scheduleCall(t, create), agent.MemEnv("/ws")) + if err != nil || res.IsError { + t.Fatalf("unbound mutating create = (%v, %q), want allowed", err, res.Content) + } + if len(mgr.created) != 1 || mgr.created[0].Mode != session.ModeDefault { + t.Fatalf("unbound mutating create recorded %+v, want mode %q", mgr.created, session.ModeDefault) + } + + mgr = newStubScheduleManager() + ctx := agent.WithSessionMode(context.Background(), session.ModePlan) + res, err = agent.NewScheduleTool(mgr).Execute(ctx, scheduleCall(t, create), agent.MemEnv("/ws")) + if err != nil { + t.Fatalf("plan-context Execute: %v", err) + } + if !res.IsError || !strings.Contains(res.Content, "plan mode") { + t.Fatalf("plan-context mutating create = %q, want the plan-mode refusal", res.Content) + } + if len(mgr.created) != 0 { + t.Fatalf("plan-context mutating create reached the manager: %+v", mgr.created) + } +} diff --git a/engine/agent/sessionorigin.go b/engine/agent/sessionorigin.go index f3511ff5ec..143ae3dbb8 100644 --- a/engine/agent/sessionorigin.go +++ b/engine/agent/sessionorigin.go @@ -37,3 +37,24 @@ func sessionOriginFromContext(ctx context.Context) session.SessionID { id, _ := ctx.Value(sessionOriginContextKey{}).(session.SessionID) return id } + +type sessionModeContextKey struct{} + +// withSessionMode returns a child context carrying the executing session's +// permission mode at the moment a tool call executes. Its one writer is +// timeExecute, which stamps sess.Mode per call (not once per run) because the +// mode can change between dispatches; its one reader is ScheduleTool.create, +// which records it as a mutating schedule's fire mode. Unexported for the same +// reason as withSessionOrigin: the mode must come from the run, never from an +// embedder or from model arguments, or a schedule could name a mode its +// creating session never held. +func withSessionMode(ctx context.Context, mode session.PermissionMode) context.Context { + return context.WithValue(ctx, sessionModeContextKey{}, mode) +} + +// sessionModeFromContext reads the executing session's mode, returning the +// empty mode when the context did not come from timeExecute. +func sessionModeFromContext(ctx context.Context) session.PermissionMode { + mode, _ := ctx.Value(sessionModeContextKey{}).(session.PermissionMode) + return mode +} diff --git a/engine/agent/subagent.go b/engine/agent/subagent.go index 27abf6439b..3838b500fe 100644 --- a/engine/agent/subagent.go +++ b/engine/agent/subagent.go @@ -80,6 +80,10 @@ type observableTool interface { // adapter/server/proto type crosses. A tool that does not implement childCapableTool // (or a nil caps) gets the legacy headless auto-deny posture, unchanged. type parentCaps struct { + // parentMode is the parent session's permission mode at dispatch. A child of + // an auto or yolo parent inherits that mode (effectiveChildMode) so the + // parent's mode-driven authority carries to its delegates. + parentMode session.PermissionMode // reviewRoot shares only harness-owned trajectory/reviewer state with workers. // It never carries a parent conversation or model-authored summary. reviewRoot *reviewRoot @@ -679,8 +683,8 @@ type SubagentTool struct { // limits bound a single child run. Defaults to defaultChildLimits. limits session.Limits - // childMode is the permission mode the child session runs under. Defaults to - // session.ModeDefault. + // childMode is the permission mode the child session runs under. Empty (the + // default) inherits the parent's mode; see effectiveChildMode. childMode session.PermissionMode // hooks fires the SubagentStop lifecycle hook when a child finishes @@ -1012,13 +1016,64 @@ func WithChildLimits(l session.Limits) SubagentOption { return func(t *SubagentTool) { t.limits = l } } -// WithChildMode sets the permission mode the child session runs under (default -// session.ModeDefault). session.ModePlan additionally hides any non-read-only -// tools from the child at the catalog level. +// WithChildMode sets the permission mode the child session runs under. Without +// it the child inherits an auto or yolo parent's mode and otherwise runs in +// session.ModeDefault (see effectiveChildMode); an explicit session.ModeDefault +// keeps default even under an auto or yolo parent. session.ModePlan additionally +// hides any non-read-only tools from the child at the catalog level. func WithChildMode(m session.PermissionMode) SubagentOption { return func(t *SubagentTool) { t.childMode = m } } +// effectiveChildMode is the mode a delegated child session runs in. A child +// with no configured mode ("") inherits an auto or yolo parent's mode, so the +// parent's mode-driven authority (allow-all, and in yolo the loosened +// substitution floor) carries to its delegates, and runs in session.ModeDefault +// under any other parent. Any explicitly configured mode, session.ModeDefault +// included, is kept. +func effectiveChildMode(configured, parent session.PermissionMode) session.PermissionMode { + if configured != "" { + return configured + } + if parent == session.ModeAuto || parent == session.ModeYolo { + return parent + } + return session.ModeDefault +} + +// resumedChildMode re-admits a persisted child's mode against the mode a fresh +// child of the CURRENT parent would get: the narrower of the two in the +// plan < default < acceptEdits < auto < yolo order. A child created under a +// yolo parent therefore cannot keep yolo's authority after the parent (or the +// deployment ceiling that admitted the parent) tightens, while a stored mode +// that is already narrower is preserved. +func resumedChildMode(stored, configured, parent session.PermissionMode) session.PermissionMode { + admitted := effectiveChildMode(configured, parent) + if childModeRank(stored) <= childModeRank(admitted) { + return stored + } + return admitted +} + +// childModeRank orders permission modes by authority. An unknown mode ranks +// below plan, so it is never replaced by a wider one. +func childModeRank(mode session.PermissionMode) int { + switch mode { + case session.ModePlan: + return 1 + case session.ModeDefault: + return 2 + case session.ModeAccept: + return 3 + case session.ModeAuto: + return 4 + case session.ModeYolo: + return 5 + default: + return 0 + } +} + // WithSubagentStopHook injects the HookRunner that fires the SubagentStop hook // when a child run finishes. It is best-effort: a hook error or block never fails // the Subagent call. Passing nil disables the hook. @@ -1334,7 +1389,6 @@ func NewSubagentTool(childEngine *Engine, opts ...SubagentOption) tool.Tool { t := &SubagentTool{ childEngine: childEngine, limits: defaultChildLimits, - childMode: session.ModeDefault, idPrefix: strings.TrimSuffix(SubagentSessionPrefix, "-"), // the exported convention is the source inFlight: make(map[session.SessionID]struct{}), } @@ -2261,7 +2315,7 @@ func (t *SubagentTool) resolveEngineAndLimits(callID session.ToolCallID, args su // still on disk — because this is the only place that sees the resumed session's // PERSISTED workspace before buildChildSession re-homes it. See editsSurvived's // doc-comment on the named result below and resumeWritableNote. -func (t *SubagentTool) prepareChildSession(ctx context.Context, call session.ToolCall, env tool.Environment, args subagentArgs, resuming, writable bool, childID, parentID session.SessionID, parentIncarnation session.IncarnationID, limits session.Limits, forkHistory []session.Message, forkInstructions session.InstructionSnapshot) (child *session.Session, runEnv tool.Environment, cleanup func() error, advisory string, editsSurvived bool, errResult session.ToolResult, ok bool) { +func (t *SubagentTool) prepareChildSession(ctx context.Context, call session.ToolCall, env tool.Environment, args subagentArgs, resuming, writable bool, childID, parentID session.SessionID, parentIncarnation session.IncarnationID, parentMode session.PermissionMode, limits session.Limits, forkHistory []session.Message, forkInstructions session.InstructionSnapshot) (child *session.Session, runEnv tool.Environment, cleanup func() error, advisory string, editsSurvived bool, errResult session.ToolResult, ok bool) { noop := func() error { return nil } var resumedChild *session.Session priorRef := session.EnvironmentRef{} @@ -2299,7 +2353,7 @@ func (t *SubagentTool) prepareChildSession(ctx context.Context, call session.Too // otherwise is the exact falsehood resumeWritableNote exists to prevent, inverted. // The path comparison is the honest test and needs no new persisted field. editsSurvived = writable && priorRef.Valid() && priorRef == env.Ref() - child, errRes, bok := t.buildChildSession(call.ID, childID, parentID, parentIncarnation, resumedChild, runEnv, limits, forkHistory, forkInstructions) + child, errRes, bok := t.buildChildSession(call.ID, childID, parentID, parentIncarnation, parentMode, resumedChild, runEnv, limits, forkHistory, forkInstructions) if !bok { _ = cleanupWS() return nil, tool.Environment{}, noop, "", false, errRes, false @@ -2580,7 +2634,7 @@ func (t *SubagentTool) run(ctx context.Context, call session.ToolCall, env tool. // Resume load + fork + session build (see prepareChildSession). The cleanup is // always non-nil and tears the worktree down after the child fully drains (the // run is drained below in this call), so a deferred cleanup is correct. - child, runEnv, cleanupWS, forkAdvisory, editsSurvived, errResult, ok := t.prepareChildSession(ctx, call, env, args, resuming, writable, childID, caps.parentSessionID, caps.parentIncarnation, limits, forkHistory, forkInstructions) + child, runEnv, cleanupWS, forkAdvisory, editsSurvived, errResult, ok := t.prepareChildSession(ctx, call, env, args, resuming, writable, childID, caps.parentSessionID, caps.parentIncarnation, caps.parentMode, limits, forkHistory, forkInstructions) if !ok { return errResult, nil } @@ -2967,7 +3021,7 @@ func (t *SubagentTool) driveBackground(ctx context.Context, b backgroundChild) { return } defer func() { _ = cleanupWS() }() - child, errResult, ok := t.buildChildSession(b.call.ID, b.childID, b.caps.parentSessionID, b.caps.parentIncarnation, b.resumed, runEnv, b.limits, b.forkHistory, b.forkInstructions) + child, errResult, ok := t.buildChildSession(b.call.ID, b.childID, b.caps.parentSessionID, b.caps.parentIncarnation, b.caps.parentMode, b.resumed, runEnv, b.limits, b.forkHistory, b.forkInstructions) if !ok { endOnError(errResult) return @@ -4185,8 +4239,9 @@ func (t *SubagentTool) forkChildEnvironment(ctx context.Context, callID session. // stays consistent with where this run actually executes — the original worktree is // torn down, and without the re-home the re-persisted snapshot would record a dead // path. (The child's prompt cwd is independently sourced from the engine's PromptConfig -// and is NOT affected by this field.) -func (t *SubagentTool) buildChildSession(callID session.ToolCallID, childID, parentID session.SessionID, parentIncarnation session.IncarnationID, resumedChild *session.Session, env tool.Environment, limits session.Limits, forkHistory []session.Message, forkInstructions session.InstructionSnapshot) (*session.Session, session.ToolResult, bool) { +// and is NOT affected by this field.) A resumed child's persisted mode is also +// re-admitted against the current parent (resumedChildMode). +func (t *SubagentTool) buildChildSession(callID session.ToolCallID, childID, parentID session.SessionID, parentIncarnation session.IncarnationID, parentMode session.PermissionMode, resumedChild *session.Session, env tool.Environment, limits session.Limits, forkHistory []session.Message, forkInstructions session.InstructionSnapshot) (*session.Session, session.ToolResult, bool) { if resumedChild == nil { // When a named agent def pins limits, the child runs under THOSE; otherwise it uses // the Subagent tool's default limits. @@ -4195,10 +4250,10 @@ func (t *SubagentTool) buildChildSession(callID session.ToolCallID, childID, par if parentID == "" { // Direct Tool.Execute has no parent aggregate identity. Classify that // custom-host path unknown rather than fabricating lineage or granting main. - child = newChildSessionInEnvironment(childID, t.childMode, env, limits, t.childEngine.now()) + child = newChildSessionInEnvironment(childID, effectiveChildMode(t.childMode, parentMode), env, limits, t.childEngine.now()) err = child.RestoreSessionMetadata(session.SessionKindUnknown, session.SessionRelationship{}) } else { - child, err = newSubagentSessionInEnvironment(childID, t.childMode, env, limits, t.childEngine.now(), parentID, parentIncarnation, callID) + child, err = newSubagentSessionInEnvironment(childID, effectiveChildMode(t.childMode, parentMode), env, limits, t.childEngine.now(), parentID, parentIncarnation, callID) } if err != nil { return nil, session.NewToolError(callID, @@ -4224,6 +4279,14 @@ func (t *SubagentTool) buildChildSession(callID session.ToolCallID, childID, par return nil, session.NewToolError(callID, fmt.Sprintf("Subagent: failed to re-home resumed subagent %q: %v", childID, err)), false } + // Re-admit the persisted mode before the resumed child runs or is re-persisted + // (foreground and background resume both land here); history is untouched. + if mode := resumedChildMode(resumedChild.Mode, t.childMode, parentMode); mode != resumedChild.Mode { + if err := resumedChild.SetMode(mode); err != nil { + return nil, session.NewToolError(callID, + fmt.Sprintf("Subagent: failed to re-admit resumed subagent %q permission mode: %v", childID, err)), false + } + } return resumedChild, session.ToolResult{}, true } diff --git a/engine/agent/teamtool.go b/engine/agent/teamtool.go index 7d43f6bfb6..09735a1b83 100644 --- a/engine/agent/teamtool.go +++ b/engine/agent/teamtool.go @@ -403,6 +403,12 @@ func (t *TeamTool) run(ctx context.Context, call session.ToolCall, env tool.Envi // ask can surface and each member's durable relationship is correlated before it // is registered, persisted, or projected. opts = append(opts, withParentCaps(caps), withParentTeamCall(call.ID)) + // Members of an auto or yolo parent inherit its mode (effectiveChildMode); a + // member definition's own permissionMode, default included, still overrides + // the team default. + if mode := effectiveChildMode("", caps.parentMode); mode != session.ModeDefault { + opts = append(opts, WithTeamMode(mode)) + } sup := NewSupervisor(tm, env, factory, opts...) roster := teamRoster(args.Members) diff --git a/engine/api/session.txt b/engine/api/session.txt index add4aa269b..2f63034beb 100644 --- a/engine/api/session.txt +++ b/engine/api/session.txt @@ -183,8 +183,10 @@ const MaxToolResultTextBytes untyped int = 25600 const MediaAudio MediaKind = "audio" const MediaImage MediaKind = "image" const ModeAccept PermissionMode = "acceptEdits" +const ModeAuto PermissionMode = "auto" const ModeDefault PermissionMode = "default" const ModePlan PermissionMode = "plan" +const ModeYolo PermissionMode = "yolo" const NoProgressExtractiveNudgeText untyped string = "Stop investigating now and do not run any more commands or tools. Using only the information you have already gathered, write your best final answer to the original task as a direct message now, even if it is incomplete or uncertain — note any gaps briefly. Do not plan further steps; deliver what you have." const NoProgressNudgeText untyped string = "Please continue. Make concrete progress on the task using your tools, or — if you are blocked or believe the task is complete — say so explicitly in a short message." const ParallelBranchEnd ParallelEventKind = "branch_end" diff --git a/engine/session/session.go b/engine/session/session.go index 0b7f7cd1c9..4f41741e3d 100644 --- a/engine/session/session.go +++ b/engine/session/session.go @@ -75,6 +75,16 @@ const ( ModePlan PermissionMode = "plan" // ModeAccept auto-accepts edits (acceptEdits). ModeAccept PermissionMode = "acceptEdits" + // ModeAuto runs autonomously using an enforcing guardrails checker in place of + // human approval: every tool call is approved by the checker instead of asking. + // A configured Deny in any scope and deliberately configured human-required asks + // still apply. Requires an enforcing checker; switching to this mode without one + // is refused by the server. + ModeAuto PermissionMode = "auto" + // ModeYolo runs gate-free: no routine prompts and no enforcing checker. Advisory + // monitoring may remain. Configured deny rules and deliberately required human + // asks still apply. + ModeYolo PermissionMode = "yolo" ) // ApprovalVerdict is the client's resolution of a permission.ask. It widens an diff --git a/internal/adapter/acp/agent.go b/internal/adapter/acp/agent.go index ebe3cc08ad..ff1c287336 100644 --- a/internal/adapter/acp/agent.go +++ b/internal/adapter/acp/agent.go @@ -265,8 +265,8 @@ func (a *Agent) handleInitialize(params json.RawMessage) (any, error) { // Client-provided streaming-HTTP MCP servers are validated and mounted PER-SESSION (so // their tools and auth never leak into other sessions). A stdio (command-shaped) // entry and an sse entry are hard-rejected (CLAUDE.md: no stdio MCP, ever; mecatl -// never spawns a server process). The session mode is always default this phase; -// the available modes are reflected so the editor can show them. +// never spawns a server process). The session starts in the server's default +// mode; the admitted modes are reflected so the editor can show them. func (a *Agent) handleSessionNew(ctx context.Context, params json.RawMessage) (any, error) { var req newSessionRequest if err := json.Unmarshal(params, &req); err != nil { @@ -293,7 +293,9 @@ func (a *Agent) handleSessionNew(ctx context.Context, params json.RawMessage) (a return tool.NewEnvironment(base.Ref(), ws, base.ReadLedger(), nil) } } - sess, err := a.svc.CreateACPSession(ctx, req.Cwd, session.ModeDefault, session.Limits{}, specs, overlay) + // An empty mode applies the server's configured default mode, exactly as an + // unspecified CreateSession does on gRPC and HTTP. + sess, err := a.svc.CreateACPSession(ctx, req.Cwd, "", session.Limits{}, specs, overlay) if err != nil { return nil, newMethodErr(codeInvalidParams, "acp: session/new: "+err.Error()) } @@ -308,22 +310,30 @@ func (a *Agent) handleSessionNew(ctx context.Context, params json.RawMessage) (a return newSessionResponse{ SessionID: string(sess.ID), - Modes: modeStateFor(sess.Mode), + Modes: a.modeStateFor(sess.Mode), }, nil } -// modeStateFor builds the ACP sessionModeState advertising mecatl's three -// permission modes with current as the session's CURRENT mode. It is shared by -// session/new and session/load so both seed the editor's mode picker identically. -func modeStateFor(current session.PermissionMode) *sessionModeState { - return &sessionModeState{ - CurrentModeID: string(current), - AvailableModes: []sessionMode{ - {ID: string(session.ModeDefault), Name: "Default", Description: "Standard deny/ask/allow permissions"}, - {ID: string(session.ModePlan), Name: "Plan", Description: "Read-only planning (no mutations)"}, - {ID: string(session.ModeAccept), Name: "Accept Edits", Description: "Auto-accept edits"}, - }, +// acpModes names and describes every mecatl permission mode an editor may be +// offered, in plan → yolo order. +var acpModes = map[session.PermissionMode]sessionMode{ + session.ModePlan: {ID: string(session.ModePlan), Name: "Plan", Description: "Read-only planning (no mutations)"}, + session.ModeDefault: {ID: string(session.ModeDefault), Name: "Default", Description: "Standard deny/ask/allow permissions"}, + session.ModeAccept: {ID: string(session.ModeAccept), Name: "Accept Edits", Description: "Auto-accept edits"}, + session.ModeAuto: {ID: string(session.ModeAuto), Name: "Auto", Description: "Run tools without asking, with an enforcing guardrails checker"}, + session.ModeYolo: {ID: string(session.ModeYolo), Name: "YOLO", Description: "Run tools without asking and without a preventive checker"}, +} + +// modeStateFor builds the ACP sessionModeState with current as the session's +// CURRENT mode, advertising only the modes the server admits now (its ceiling +// and the auto checker requirement), so the editor's picker never offers a +// mode session/set_mode would refuse. Shared by session/new and session/load. +func (a *Agent) modeStateFor(current session.PermissionMode) *sessionModeState { + state := &sessionModeState{CurrentModeID: string(current)} + for _, mode := range a.svc.AdmittedModes() { + state.AvailableModes = append(state.AvailableModes, acpModes[mode]) } + return state } // notifyAvailableCommands lists the slash commands for the workspace via the @@ -419,7 +429,7 @@ func (a *Agent) handleSessionLoad(ctx context.Context, params json.RawMessage) ( // Rebuild the editor's transcript from the persisted history BEFORE returning, // so a re-attaching editor sees the prior turns rather than an empty session. a.replayHistory(ctx, req.SessionID, sess.Conversation) - return loadSessionResponse{Modes: modeStateFor(sess.Mode)}, nil + return loadSessionResponse{Modes: a.modeStateFor(sess.Mode)}, nil } // handleSetMode applies a session/set_mode by mapping the ACP modeId to a mecatl @@ -467,6 +477,10 @@ func modeFromACP(modeID string) (session.PermissionMode, bool) { return session.ModePlan, true case session.ModeAccept: return session.ModeAccept, true + case session.ModeAuto: + return session.ModeAuto, true + case session.ModeYolo: + return session.ModeYolo, true default: return "", false } diff --git a/internal/adapter/acp/mode_admission_test.go b/internal/adapter/acp/mode_admission_test.go new file mode 100644 index 0000000000..2b837d7972 --- /dev/null +++ b/internal/adapter/acp/mode_admission_test.go @@ -0,0 +1,63 @@ +package acp_test + +import ( + "encoding/json" + "slices" + "testing" + + "github.com/stacklok/mecatl/engine/adapter/mockllm" + "github.com/stacklok/mecatl/engine/session" + "github.com/stacklok/mecatl/internal/adapter/server" +) + +// TestACPModesFollowServerAdmission pins that ACP starts a session in the +// server's default mode and offers exactly the modes the server admits: the +// ceiling bounds the list, and auto appears only with an enforcing checker. +func TestACPModesFollowServerAdmission(t *testing.T) { + for _, tc := range []struct { + name string + def, ceil session.PermissionMode + checker bool + wantModes []string + wantCurrent string + }{ + {"strict ceiling", session.ModeDefault, session.ModeAccept, false, []string{"plan", "default", "acceptEdits"}, "default"}, + {"yolo without checker", session.ModeYolo, session.ModeYolo, false, []string{"plan", "default", "acceptEdits", "yolo"}, "yolo"}, + {"auto with checker", session.ModeAuto, session.ModeAuto, true, []string{"plan", "default", "acceptEdits", "auto"}, "auto"}, + } { + t.Run(tc.name, func(t *testing.T) { + svc := newServiceCfg(t, mockllm.New(), allowRules(), func(c *server.Config) { + c.DefaultMode, c.MaxAllowedMode, c.EnforcingCheckerConfigured = tc.def, tc.ceil, tc.checker + }) + e, cleanup := startAgent(t, svc) + defer cleanup() + res := e.call("session/new", map[string]any{"cwd": testCWD(t), "mcpServers": []any{}}) + var ns struct { + SessionID string `json:"sessionId"` + Modes struct { + CurrentModeID string `json:"currentModeId"` + AvailableModes []struct { + ID string `json:"id"` + } `json:"availableModes"` + } `json:"modes"` + } + if err := json.Unmarshal(res, &ns); err != nil { + t.Fatalf("session/new: %s: %v", res, err) + } + var got []string + for _, m := range ns.Modes.AvailableModes { + got = append(got, m.ID) + } + if !slices.Equal(got, tc.wantModes) || ns.Modes.CurrentModeID != tc.wantCurrent { + t.Fatalf("modes = %v current %q, want %v current %q", got, ns.Modes.CurrentModeID, tc.wantModes, tc.wantCurrent) + } + last := tc.wantModes[len(tc.wantModes)-1] + e.call("session/set_mode", map[string]any{"sessionId": ns.SessionID, "modeId": "plan"}) + e.call("session/set_mode", map[string]any{"sessionId": ns.SessionID, "modeId": last}) + sess, err := svc.GetSession(t.Context(), session.SessionID(ns.SessionID)) + if err != nil || string(sess.Mode) != last { + t.Fatalf("set_mode %s: mode %v err %v", last, sess, err) + } + }) + } +} diff --git a/internal/adapter/microvm/production_e2e_test.go b/internal/adapter/microvm/production_e2e_test.go index 0e3986c8e3..bf66347979 100644 --- a/internal/adapter/microvm/production_e2e_test.go +++ b/internal/adapter/microvm/production_e2e_test.go @@ -189,7 +189,7 @@ func TestMicroVMDefaultPlacementDailyHarnessJourney(t *testing.T) { ) built, err := app.Build(ctx, app.Config{ Workspace: source, StoreDir: filepath.Join(root, "store"), MockProvider: provider, - Shell: "/bin/sh", AllowAllTools: true, NoSoul: true, LearningMode: learning.Off, + Shell: "/bin/sh", AllowAllTools: true, GuardrailsDisabled: true, NoSoul: true, LearningMode: learning.Off, MemoryDir: filepath.Join(root, "memory"), UserModelDir: filepath.Join(root, "user-model"), PlacementProvider: placement, PlacementScope: scope, EnvironmentForkers: map[session.EnvironmentKind]tool.EnvironmentForker{"microvm": placement}, @@ -203,8 +203,9 @@ func TestMicroVMDefaultPlacementDailyHarnessJourney(t *testing.T) { t.Cleanup(built.Close) // This is deliberately the ordinary no-selector API. The deployment, not the - // client, chooses microvm-local as the default placement. - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + // client, chooses microvm-local as the default placement, and the unspecified + // mode resolves to the deployment's allow-all default. + sess, err := built.Service.CreateSession(ctx, "", session.Limits{}) if err != nil { t.Fatalf("normal CreateSession: %v", err) } diff --git a/internal/adapter/permconfig/permconfig.go b/internal/adapter/permconfig/permconfig.go index 16bf8fb63a..76d9ac0004 100644 --- a/internal/adapter/permconfig/permconfig.go +++ b/internal/adapter/permconfig/permconfig.go @@ -182,6 +182,7 @@ func (c *Config) UnmarshalYAML(node ast.Node) error { "permissions": &c.Permissions, "guardrails": newPermconfigNodePointer(&c.Guardrails), "posture": &c.Posture, + "permissionMode": &c.PermissionMode, "models": newPermconfigNodePointer(&c.Models), "reasoning-effort": &c.ReasoningEffort, "plan-mode-auto-approve": &c.PlanModeAutoApprove, diff --git a/internal/adapter/permconfig/resolve.go b/internal/adapter/permconfig/resolve.go index 5d7e56e320..d266c33f02 100644 --- a/internal/adapter/permconfig/resolve.go +++ b/internal/adapter/permconfig/resolve.go @@ -139,6 +139,11 @@ type Resolver struct { // files) out-ranks user-global (first-non-empty keeps CLI). operatorPosture string + // operatorPermissionMode is the OPERATOR-TIER permissionMode: token, + // captured exactly like operatorPosture: user-global + CLI tiers only, CLI + // first-non-empty wins, a project-tier occurrence is ignored with a WARN. + operatorPermissionMode string + // operatorReasoningEffort is the OPERATOR-TIER reasoning-effort: scalar, // read ONCE at construction from the user-global + CLI tiers ONLY. A // project-tier file's reasoning-effort: key is deliberately IGNORED (operator- @@ -379,6 +384,16 @@ func (r *Resolver) OperatorPosture() string { return r.operatorPosture } +// OperatorPermissionMode returns the operator-tier permissionMode: token +// (user-global + CLI only), or "" when none was configured. Like +// OperatorPosture it never returns a project-tier value. nil-safe. +func (r *Resolver) OperatorPermissionMode() string { + if r == nil { + return "" + } + return r.operatorPermissionMode +} + // OperatorReasoningEffort returns the operator-tier reasoning-effort: scalar // (user-global + CLI only), or "" when none was configured. It is the // SOLE accessor the composition layer uses to read reasoning-effort from config — @@ -727,16 +742,7 @@ func (r *Resolver) loadProjectRules(ws tool.WorkspaceReader) []governance.Rule { "guardrails: IGNORING a project-tier guardrails: block (operator-tier only — a project repo cannot configure/disable a security checker; set guardrails in your user-global settings.yaml or via --guardrails-model)", "file", src.path, "root", ws.Root()) } - // Posture is OPERATOR-TIER ONLY (the fail-closed core of the posture ladder): a - // project file's posture: scalar is IGNORED with a loud WARN. Honouring it would - // let a malicious repo RAISE the automation posture (e.g. posture: yolo to - // auto-run substitutions in subagents) — a security DOWNGRADE the tighten-only - // project gate forbids (it reverses here, exactly like guardrails). - if strings.TrimSpace(cfg.Posture) != "" { - r.diag.Log(context.Background(), port.LevelWarn, - "posture: IGNORING a project-tier posture: scalar (operator-tier only — a project repo cannot raise the automation posture; set posture in your user-global settings.yaml or via --posture)", - "file", src.path, "root", ws.Root()) - } + r.warnIgnoredProjectPermissionScalars(&cfg, src.path, ws.Root()) // ReasoningEffort is OPERATOR-TIER ONLY, for consistency with // posture: a project file's reasoning-effort: scalar is IGNORED // with a loud WARN. It is a cost/quality preference, not a security control, @@ -931,6 +937,7 @@ func (r *Resolver) loadUserRules(report *Report) []governance.Rule { r.captureGuardrails(cfg.Guardrails) // Operator-tier posture: same first-non-empty-keeps-CLI discipline as guardrails. r.capturePosture(cfg.Posture) + r.capturePermissionMode(cfg.PermissionMode) // Operator-tier reasoning-effort: same discipline as posture. r.captureReasoningEffort(cfg.ReasoningEffort) // Operator-tier plan-mode-auto-approve (issue #206 Wave 6a): same discipline as posture. @@ -978,6 +985,7 @@ func (r *Resolver) loadUserRules(report *Report) []governance.Rule { r.captureGuardrails(cfg.Guardrails) // User-global posture: captured only if no higher CLI file already did. r.capturePosture(cfg.Posture) + r.capturePermissionMode(cfg.PermissionMode) // User-global reasoning-effort: same discipline as posture. r.captureReasoningEffort(cfg.ReasoningEffort) // User-global plan-mode-auto-approve (issue #206 Wave 6a): same discipline as posture. @@ -1064,6 +1072,30 @@ func (r *Resolver) captureGuardrails(g *GuardrailsSection) { r.operatorGuardrails = g } +// warnIgnoredProjectPermissionScalars WARNs about the operator-tier-only +// permission scalars (posture:, permissionMode:) found in a project-tier file. +// Honouring either would let a repo raise its own automation posture, so both +// are ignored; split out of loadProjectRules to keep its complexity bounded. +func (r *Resolver) warnIgnoredProjectPermissionScalars(cfg *Config, path, root string) { + // Posture is OPERATOR-TIER ONLY (the fail-closed core of the posture ladder): a + // project file's posture: scalar is IGNORED with a loud WARN. Honouring it would + // let a malicious repo RAISE the automation posture (e.g. posture: yolo to + // auto-run substitutions in subagents) — a security DOWNGRADE the tighten-only + // project gate forbids (it reverses here, exactly like guardrails). + if strings.TrimSpace(cfg.Posture) != "" { + r.diag.Log(context.Background(), port.LevelWarn, + "posture: IGNORING a project-tier posture: scalar (operator-tier only — a project repo cannot raise the automation posture; set permissionMode in your user-global settings.yaml or via --permission-mode)", + "file", path, "root", root) + } + // PermissionMode is OPERATOR-TIER ONLY for the same reason as posture: a + // repo naming its own permission mode could raise the automation posture. + if strings.TrimSpace(cfg.PermissionMode) != "" { + r.diag.Log(context.Background(), port.LevelWarn, + "permissionMode: IGNORING a project-tier permissionMode: scalar (operator-tier only — a project repo cannot choose its own permission mode; set permissionMode in your user-global settings.yaml or via --permission-mode)", + "file", path, "root", root) + } +} + // capturePosture records the FIRST operator-tier posture: scalar seen during // construction (CLI files are parsed before user-global, so CLI wins on // first-non-empty). It is called only from loadUserRules — the operator (user- @@ -1080,6 +1112,15 @@ func (r *Resolver) capturePosture(p string) { r.operatorPosture = strings.TrimSpace(p) } +// capturePermissionMode records the FIRST operator-tier permissionMode: token, +// with the same CLI-first, operator-tier-only discipline as capturePosture. +func (r *Resolver) capturePermissionMode(p string) { + if r.operatorPermissionMode != "" || strings.TrimSpace(p) == "" { + return + } + r.operatorPermissionMode = strings.TrimSpace(p) +} + // captureReasoningEffort records the FIRST operator-tier reasoning-effort: scalar // seen during construction (CLI files are parsed before user-global, so CLI wins // on first-non-empty). It is called only from loadUserRules — the operator diff --git a/internal/adapter/permconfig/schema.go b/internal/adapter/permconfig/schema.go index 809ff7b5e6..b468406699 100644 --- a/internal/adapter/permconfig/schema.go +++ b/internal/adapter/permconfig/schema.go @@ -165,6 +165,14 @@ type Config struct { // keeps the CLI/default). The composition layer parses the string; permconfig only // reads the scalar. Posture string `yaml:"posture"` + // PermissionMode is the OPERATOR-TIER named permission-mode token + // (plan/default/accept-edits/auto/yolo). It sets the mode new sessions start in + // and the server ceiling (the most permissive mode a session may switch to) + // together, and supersedes the deprecated posture: key. It never grants + // workspace trust. Like Posture it is honoured ONLY from the + // user-global + CLI tiers; a project-tier occurrence is IGNORED with a WARN. + // Empty = absent. The composition layer parses the token. + PermissionMode string `yaml:"permissionMode"` // Models holds operator-tier model bindings. Project-tier models: blocks are // ignored with a warning before nested schema decoding. The TOP `models:` mapping // is parsed STRICTLY (an unknown key like `slotz:` errors), the inner slots/aliases diff --git a/internal/adapter/server/classification.go b/internal/adapter/server/classification.go index 313cbac503..cf265b9db7 100644 --- a/internal/adapter/server/classification.go +++ b/internal/adapter/server/classification.go @@ -359,6 +359,7 @@ var serviceAccessTable = map[string]ClassificationEntry{ // --- shared infrastructure: process-wide catalog/config, same for every caller by design --- "CompatibilityInfo": {KindCallerOwned, "projects broker inspection only for the verified caller carried in ctx; the remaining deployment capabilities are shared and no session record is read"}, + "AdmittedModes": {KindSharedInfrastructure, "reports the deployment-wide permission-mode ceiling and checker requirement, identical for every caller; it reads no session record"}, "ClientMCPFromWire": {KindSharedInfrastructure, "classifies a request's MCP entries and applies the deployment-wide client-MCP policy, which is identical for every caller; it reads and writes no session, schedule, or memory record and reaches no network"}, "ListMcpResources": {KindSharedInfrastructure, "MCP servers are process-wide composition config, not a caller-owned record; every caller may list a wired server's resources"}, "ReadMcpResource": {KindSharedInfrastructure, "reads a resource off a process-wide MCP server registration, not a caller-owned record"}, diff --git a/internal/adapter/server/compatibility_info_test.go b/internal/adapter/server/compatibility_info_test.go index baef5a9cd1..66fb56742d 100644 --- a/internal/adapter/server/compatibility_info_test.go +++ b/internal/adapter/server/compatibility_info_test.go @@ -155,6 +155,65 @@ func TestSDKServerEnablers_Scenario1_CompatibilityInfoTransportParity(t *testing } } +// TestCompatibilityInfoSubagentAskReviewerTransportParity pins that the +// optional subagent_ask_reviewer capability stays unset when the server does +// not report it and carries the exact state when it does, over gRPC and HTTP. +func TestCompatibilityInfoSubagentAskReviewerTransportParity(t *testing.T) { + for _, reviewer := range []*string{nil, new("on"), new("off"), new("inert")} { + engine := agent.NewEngine(agent.Deps{ + LLM: mockllm.New(mockllm.TextTurn("ok")), + Catalog: tool.NewCatalog(), + Policy: permpolicy.NewPolicy(nil, nil), + Model: "test-model", + }) + svc, err := newPlacementTestService(server.Config{ + Engine: engine, + Store: memstore.New(), + Now: func() time.Time { return time.Unix(0, 0) }, + SubagentAskReviewer: reviewer, + }) + if err != nil { + t.Fatalf("new service: %v", err) + } + client, cleanup := dialGRPC(t, svc) + srv := httptest.NewServer(server.NewHTTPHandler(svc)) + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + viaGRPC, err := client.GetCompatibilityInfo(ctx, &mecatlv1.GetCompatibilityInfoRequest{}) + if err != nil { + t.Fatalf("GetCompatibilityInfo (grpc): %v", err) + } + req, err := http.NewRequestWithContext(ctx, http.MethodGet, srv.URL+"/v1/compatibility", nil) + if err != nil { + t.Fatalf("new request: %v", err) + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatalf("GET /v1/compatibility: %v", err) + } + raw, err := io.ReadAll(resp.Body) + _ = resp.Body.Close() + cancel() + srv.Close() + cleanup() + if err != nil { + t.Fatalf("read body: %v", err) + } + var viaHTTP mecatlv1.GetCompatibilityInfoResponse + if err := json.Unmarshal(raw, &viaHTTP); err != nil { + t.Fatalf("decode HTTP body: %v: %s", err, raw) + } + for transport, caps := range map[string]*mecatlv1.ServerCapabilities{"grpc": viaGRPC.GetCapabilities(), "http": viaHTTP.GetCapabilities()} { + switch { + case reviewer == nil && caps.SubagentAskReviewer != nil: + t.Errorf("%s: subagent_ask_reviewer = %q, want unset", transport, caps.GetSubagentAskReviewer()) + case reviewer != nil && caps.GetSubagentAskReviewer() != *reviewer: + t.Errorf("%s: subagent_ask_reviewer = %v, want %q", transport, caps.SubagentAskReviewer, *reviewer) + } + } + } +} + // TestFeatureRegistryIsSingleSource is AC1.4. // // The feature set is non-empty, sorted, duplicate-free, and identical on both diff --git a/internal/adapter/server/default_mode_test.go b/internal/adapter/server/default_mode_test.go new file mode 100644 index 0000000000..30885797c5 --- /dev/null +++ b/internal/adapter/server/default_mode_test.go @@ -0,0 +1,66 @@ +package server_test + +import ( + "context" + "testing" + "time" + + mecatlv1 "github.com/stacklok/mecatl/contracts/gen/go/mecatl/v1" + "github.com/stacklok/mecatl/engine/adapter/memstore" + "github.com/stacklok/mecatl/engine/adapter/mockllm" + "github.com/stacklok/mecatl/engine/adapter/permpolicy" + "github.com/stacklok/mecatl/engine/agent" + "github.com/stacklok/mecatl/engine/session" + "github.com/stacklok/mecatl/engine/tool" + "github.com/stacklok/mecatl/internal/adapter/server" +) + +// TestGRPCCreateSessionUnspecifiedModeUsesServerDefault pins that a gRPC +// CreateSession leaving mode UNSPECIFIED starts in the server's configured +// DefaultMode (the session half of the operator's permission mode), +// while an explicit mode still wins. +func TestGRPCCreateSessionUnspecifiedModeUsesServerDefault(t *testing.T) { + llm := mockllm.New() + svc, err := newPlacementTeamTestService(server.Config{ + Engine: agent.NewEngine(agent.Deps{ + LLM: llm, + Catalog: tool.NewCatalog(), + Policy: permpolicy.NewPolicy(allowRules(), nil), + Model: "test-model", + }), + Store: memstore.New(), + PlacementProvider: testPlacementProvider{}, + PlacementScope: "test", + Now: func() time.Time { return time.Unix(0, 0) }, + DefaultCapabilities: llm.Capabilities(), + DefaultMode: session.ModePlan, + }) + if err != nil { + t.Fatalf("new service: %v", err) + } + client, cleanup := dialGRPC(t, svc) + defer cleanup() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + for _, tc := range []struct { + req mecatlv1.PermissionMode + want session.PermissionMode + }{ + {mecatlv1.PermissionMode_PERMISSION_MODE_UNSPECIFIED, session.ModePlan}, + {mecatlv1.PermissionMode_PERMISSION_MODE_DEFAULT, session.ModeDefault}, + {mecatlv1.PermissionMode_PERMISSION_MODE_ACCEPT_EDITS, session.ModeAccept}, + } { + cs, err := client.CreateSession(ctx, &mecatlv1.CreateSessionRequest{Mode: tc.req}) + if err != nil { + t.Fatalf("CreateSession(%s): %v", tc.req, err) + } + sess, err := svc.GetSession(ctx, session.SessionID(cs.GetSessionId())) + if err != nil { + t.Fatalf("GetSession: %v", err) + } + if sess.Mode != tc.want { + t.Errorf("CreateSession(%s) started in %q, want %q", tc.req, sess.Mode, tc.want) + } + } +} diff --git a/internal/adapter/server/exact_plan_ask.go b/internal/adapter/server/exact_plan_ask.go index c2974db7fe..9dedb34b2f 100644 --- a/internal/adapter/server/exact_plan_ask.go +++ b/internal/adapter/server/exact_plan_ask.go @@ -173,6 +173,9 @@ func (s *Service) resolvePersistedPlanAsk(ctx context.Context, id session.Sessio if err := s.validatePersistedPlanAsk(sess, expectedRunID, askID); err != nil { return RunAskAcknowledgement{}, err } + if err := s.admitStoredMode(sess); err != nil { + return RunAskAcknowledgement{}, err + } st.persistMu.Lock() s.mu.Lock() if s.runs[id] != st || st.cancelling || st.cancelSignaled { diff --git a/internal/adapter/server/features.go b/internal/adapter/server/features.go index 09780f8735..3a48630d66 100644 --- a/internal/adapter/server/features.go +++ b/internal/adapter/server/features.go @@ -82,6 +82,22 @@ const ( // FeatureSessionActivityInventory reports that ListSessions pages carry the // atomically persisted activity projection. FeatureSessionActivityInventory = "session_activity_inventory" + + // FeatureSessionPermissionModes reports the five session-scoped permission + // modes (plan, default, accept-edits, auto, yolo): AUTO and YOLO are valid on + // CreateSession, SetMode, and session reads; each mode's authority applies + // per session within the server ceiling; and an unspecified CreateSession + // mode resolves to the server's configured default. The legacy posture + // capability cannot answer this: an older server advertises auto/yolo + // posture without supporting these session modes. + FeatureSessionPermissionModes = "session_permission_modes" + + // FeatureRefusedModeRecovery reports that SetMode recovers a stored session + // parked awaiting an approval in a mode this server no longer admits: a + // SetMode to an admitted, strictly lower mode cancels the pending turn + // without running it and applies the mode, instead of refusing mid-turn. + // Every other SetMode on an awaiting session is still refused. + FeatureRefusedModeRecovery = "refused_mode_recovery" ) // FeatureScope is what the DEPLOYMENT permits, as distinct from what the build @@ -119,8 +135,10 @@ var allFeatures = []string{ FeatureHTTPSteer, FeatureMCPServersOnCreate, FeaturePromptFreeControls, + FeatureRefusedModeRecovery, FeatureServerInfo, FeatureSessionActivityInventory, + FeatureSessionPermissionModes, FeatureSkillFiles, FeatureWatchSessionEvents, } diff --git a/internal/adapter/server/grpc.go b/internal/adapter/server/grpc.go index b60b3a918b..2e8e2ba82f 100644 --- a/internal/adapter/server/grpc.go +++ b/internal/adapter/server/grpc.go @@ -113,7 +113,7 @@ func (h *HarnessServer) CreateSession(ctx context.Context, req *mecatlv1.CreateS if !grant.IsEmpty() { opts = append(opts, WithClientMCP(grant)) } - sess, err := h.svc.CreateSessionWithProfile(ctx, modeFromProto(req.GetMode()), limitsFromProto(req.GetLimits()), sel, profile, opts...) + sess, err := h.svc.CreateSessionWithProfile(ctx, createModeFromProto(req.GetMode()), limitsFromProto(req.GetLimits()), sel, profile, opts...) if err != nil { return nil, toStatus(err) } diff --git a/internal/adapter/server/grpc_schedule.go b/internal/adapter/server/grpc_schedule.go index 6a8d73cb28..4154925a75 100644 --- a/internal/adapter/server/grpc_schedule.go +++ b/internal/adapter/server/grpc_schedule.go @@ -187,7 +187,11 @@ func (h *ScheduleServer) ListFires(ctx context.Context, req *mecatlv1.ListFiresR // protoToScheduleSpec maps the proto ScheduleSpec to the port.ScheduleSpec the // Service methods take. It reuses the existing contentFromProto / limitsFromProto -// / modeFromProto mappers (the single content-validation path). TriggerSpec maps +// / createModeFromProto mappers (the single content-validation path). An +// UNSPECIFIED mode stays empty, as on CreateSession: a mutating schedule then +// fires in the server's configured default session mode, admitted at each fire, +// and a read-leaning one is still refused by the create seam unless it names +// plan. TriggerSpec maps // the cron string + one_shot Timestamp → time.Time. An absent trigger is a value // error (the create-seam's Trigger.Validate rejects it). Timestamps without a // created_at are left zero (the create-seam stamps CreatedAt itself, so a caller @@ -207,7 +211,7 @@ func protoToScheduleSpec(in *mecatlv1.ScheduleSpec) (port.ScheduleSpec, error) { Name: in.GetName(), Prompt: in.GetPrompt(), Profile: in.GetProfile(), - Mode: modeFromProto(in.GetMode()), + Mode: createModeFromProto(in.GetMode()), Limits: limitsFromProto(in.GetLimits()), Mutating: in.GetMutating(), MaxFires: int(in.GetMaxFires()), @@ -269,7 +273,7 @@ func scheduleSpecToProto(in port.ScheduleSpec) *mecatlv1.ScheduleSpec { Prompt: in.Prompt, Parts: contentToProto(in.Parts), Profile: in.Profile, - Mode: modeToProto(in.Mode), + Mode: scheduleModeToProto(in.Mode), Limits: limitsToProto(in.Limits), Mutating: in.Mutating, MaxFires: ClampInt32(in.MaxFires), diff --git a/internal/adapter/server/http.go b/internal/adapter/server/http.go index 83a862db4c..f50b81dfe5 100644 --- a/internal/adapter/server/http.go +++ b/internal/adapter/server/http.go @@ -2763,6 +2763,10 @@ func modeFromString(s string) session.PermissionMode { return session.ModeAccept case "default": return session.ModeDefault + case "auto": + return session.ModeAuto + case "yolo": + return session.ModeYolo default: return "" } diff --git a/internal/adapter/server/mapper.go b/internal/adapter/server/mapper.go index 402e3597df..8bec0bdb59 100644 --- a/internal/adapter/server/mapper.go +++ b/internal/adapter/server/mapper.go @@ -1126,6 +1126,10 @@ func modeToProto(m session.PermissionMode) mecatlv1.PermissionMode { return mecatlv1.PermissionMode_PERMISSION_MODE_PLAN case session.ModeAccept: return mecatlv1.PermissionMode_PERMISSION_MODE_ACCEPT_EDITS + case session.ModeAuto: + return mecatlv1.PermissionMode_PERMISSION_MODE_AUTO + case session.ModeYolo: + return mecatlv1.PermissionMode_PERMISSION_MODE_YOLO case session.ModeDefault: return mecatlv1.PermissionMode_PERMISSION_MODE_DEFAULT default: @@ -1398,6 +1402,10 @@ func modeFromProto(m mecatlv1.PermissionMode) session.PermissionMode { return session.ModePlan case mecatlv1.PermissionMode_PERMISSION_MODE_ACCEPT_EDITS: return session.ModeAccept + case mecatlv1.PermissionMode_PERMISSION_MODE_AUTO: + return session.ModeAuto + case mecatlv1.PermissionMode_PERMISSION_MODE_YOLO: + return session.ModeYolo case mecatlv1.PermissionMode_PERMISSION_MODE_DEFAULT, mecatlv1.PermissionMode_PERMISSION_MODE_UNSPECIFIED: return session.ModeDefault default: @@ -1405,6 +1413,29 @@ func modeFromProto(m mecatlv1.PermissionMode) session.PermissionMode { } } +// createModeFromProto maps a CreateSession or ScheduleSpec mode. Unlike +// modeFromProto, an UNSPECIFIED mode stays empty so Service.createSession +// applies the server's configured DefaultMode (the session half of the +// operator's permission mode), matching the HTTP create path. A schedule keeps +// the empty mode and resolves it the same way at each fire. +func createModeFromProto(m mecatlv1.PermissionMode) session.PermissionMode { + if m == mecatlv1.PermissionMode_PERMISSION_MODE_UNSPECIFIED { + return "" + } + return modeFromProto(m) +} + +// scheduleModeToProto reports a stored schedule mode. An empty mode means the +// fire uses the server's default session mode, so it reads back as UNSPECIFIED +// (round-tripping through createModeFromProto) rather than as an explicit +// DEFAULT that the fire may not use. +func scheduleModeToProto(m session.PermissionMode) mecatlv1.PermissionMode { + if m == "" { + return mecatlv1.PermissionMode_PERMISSION_MODE_UNSPECIFIED + } + return modeToProto(m) +} + func guardrailApprovalKindFromProto(kind mecatlv1.GuardrailApprovalKind) session.GuardrailApprovalKind { switch kind { case mecatlv1.GuardrailApprovalKind_GUARDRAIL_APPROVAL_KIND_ACTION: diff --git a/internal/adapter/server/mode_admission_test.go b/internal/adapter/server/mode_admission_test.go new file mode 100644 index 0000000000..90f139cdbc --- /dev/null +++ b/internal/adapter/server/mode_admission_test.go @@ -0,0 +1,629 @@ +package server_test + +import ( + "context" + "encoding/json" + "errors" + "net/http" + "net/http/httptest" + "strings" + "sync" + "sync/atomic" + "testing" + "time" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + mecatlv1 "github.com/stacklok/mecatl/contracts/gen/go/mecatl/v1" + "github.com/stacklok/mecatl/engine/adapter/memlease" + "github.com/stacklok/mecatl/engine/adapter/memstore" + "github.com/stacklok/mecatl/engine/adapter/mockllm" + "github.com/stacklok/mecatl/engine/adapter/permpolicy" + "github.com/stacklok/mecatl/engine/adapter/wallclock" + "github.com/stacklok/mecatl/engine/agent" + "github.com/stacklok/mecatl/engine/port" + "github.com/stacklok/mecatl/engine/session" + "github.com/stacklok/mecatl/engine/tool" + "github.com/stacklok/mecatl/internal/adapter/server" +) + +// modeAdmissionService builds a Service with the given permission-mode +// ceiling, default mode, and enforcing-checker state. +func modeAdmissionService(t *testing.T, ceiling, defaultMode session.PermissionMode, checker bool) *server.Service { + t.Helper() + return modeAdmissionServiceOn(t, memstore.New(), mockllm.New(), ceiling, defaultMode, checker) +} + +// modeAdmissionServiceOn is modeAdmissionService over an explicit store and +// provider, so a test can model a deployment restart over persisted sessions. +func modeAdmissionServiceOn(t *testing.T, store port.SessionStore, llm *mockllm.Provider, ceiling, defaultMode session.PermissionMode, checker bool) *server.Service { + t.Helper() + svc, err := newPlacementTestService(server.Config{ + Engine: agent.NewEngine(agent.Deps{ + LLM: llm, + Catalog: tool.NewCatalog(), + Policy: permpolicy.NewPolicy(allowRules(), nil), + Model: "test-model", + }), + Store: store, + PlacementProvider: testPlacementProvider{}, + PlacementScope: "test", + Now: func() time.Time { return time.Unix(0, 0) }, + DefaultCapabilities: llm.Capabilities(), + DefaultMode: defaultMode, + MaxAllowedMode: ceiling, + EnforcingCheckerConfigured: checker, + }) + if err != nil { + t.Fatalf("new service: %v", err) + } + return svc +} + +// TestGRPCAutoYoloModesRoundTrip pins that AUTO and YOLO survive the gRPC +// boundary on CreateSession, SetMode, and the session read-back instead of +// collapsing to DEFAULT. +func TestGRPCAutoYoloModesRoundTrip(t *testing.T) { + svc := modeAdmissionService(t, session.ModeYolo, session.ModeDefault, true) + client, cleanup := dialGRPC(t, svc) + defer cleanup() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + for _, tc := range []struct { + proto mecatlv1.PermissionMode + want session.PermissionMode + }{ + {mecatlv1.PermissionMode_PERMISSION_MODE_AUTO, session.ModeAuto}, + {mecatlv1.PermissionMode_PERMISSION_MODE_YOLO, session.ModeYolo}, + } { + cs, err := client.CreateSession(ctx, &mecatlv1.CreateSessionRequest{Mode: tc.proto}) + if err != nil { + t.Fatalf("CreateSession(%s): %v", tc.proto, err) + } + sess, err := svc.GetSession(ctx, session.SessionID(cs.GetSessionId())) + if err != nil { + t.Fatalf("GetSession: %v", err) + } + if sess.Mode != tc.want { + t.Errorf("CreateSession(%s) started in %q, want %q", tc.proto, sess.Mode, tc.want) + } + got, err := client.GetSession(ctx, &mecatlv1.GetSessionRequest{SessionId: cs.GetSessionId()}) + if err != nil { + t.Fatalf("GetSession RPC: %v", err) + } + if got.GetSession().GetMode() != tc.proto { + t.Errorf("GetSession mode = %s, want %s", got.GetSession().GetMode(), tc.proto) + } + + plain, err := client.CreateSession(ctx, &mecatlv1.CreateSessionRequest{Mode: mecatlv1.PermissionMode_PERMISSION_MODE_DEFAULT}) + if err != nil { + t.Fatalf("CreateSession(DEFAULT): %v", err) + } + set, err := client.SetMode(ctx, &mecatlv1.SetModeRequest{SessionId: plain.GetSessionId(), Mode: tc.proto}) + if err != nil { + t.Fatalf("SetMode(%s): %v", tc.proto, err) + } + if set.GetSession().GetMode() != tc.proto { + t.Errorf("SetMode(%s) reply mode = %s", tc.proto, set.GetSession().GetMode()) + } + } +} + +// TestGRPCModeAdmissionRefusals pins that CreateSession and SetMode share the +// ceiling and enforcing-checker admission, refusing with InvalidArgument. +func TestGRPCModeAdmissionRefusals(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + for _, tc := range []struct { + name string + ceiling session.PermissionMode + checker bool + mode mecatlv1.PermissionMode + reason string + }{ + {"yolo above strict ceiling", session.ModeDefault, true, mecatlv1.PermissionMode_PERMISSION_MODE_YOLO, "exceeds the server ceiling"}, + {"auto above strict ceiling", session.ModeAccept, true, mecatlv1.PermissionMode_PERMISSION_MODE_AUTO, "exceeds the server ceiling"}, + {"auto without checker", session.ModeYolo, false, mecatlv1.PermissionMode_PERMISSION_MODE_AUTO, "enforcing guardrails checker"}, + } { + t.Run(tc.name, func(t *testing.T) { + svc := modeAdmissionService(t, tc.ceiling, session.ModeDefault, tc.checker) + client, cleanup := dialGRPC(t, svc) + defer cleanup() + + _, err := client.CreateSession(ctx, &mecatlv1.CreateSessionRequest{Mode: tc.mode}) + if status.Code(err) != codes.InvalidArgument || !strings.Contains(err.Error(), tc.reason) { + t.Fatalf("CreateSession err = %v, want InvalidArgument naming %q", err, tc.reason) + } + + cs, err := client.CreateSession(ctx, &mecatlv1.CreateSessionRequest{Mode: mecatlv1.PermissionMode_PERMISSION_MODE_DEFAULT}) + if err != nil { + t.Fatalf("CreateSession(DEFAULT): %v", err) + } + _, err = client.SetMode(ctx, &mecatlv1.SetModeRequest{SessionId: cs.GetSessionId(), Mode: tc.mode}) + if status.Code(err) != codes.InvalidArgument || !strings.Contains(err.Error(), tc.reason) { + t.Fatalf("SetMode err = %v, want InvalidArgument naming %q", err, tc.reason) + } + sess, err := svc.GetSession(ctx, session.SessionID(cs.GetSessionId())) + if err != nil { + t.Fatalf("GetSession: %v", err) + } + if sess.Mode != session.ModeDefault { + t.Fatalf("refused SetMode changed mode to %q", sess.Mode) + } + }) + } +} + +// TestAutoDefaultWithoutCheckerRefusedAtConstruction pins that auto has no +// default-mode exemption: a service whose default is auto refuses to start +// without an enforcing checker, and starts with one. +func TestAutoDefaultWithoutCheckerRefusedAtConstruction(t *testing.T) { + _, err := newPlacementTestService(server.Config{ + Engine: agent.NewEngine(agent.Deps{LLM: mockllm.New(), Catalog: tool.NewCatalog(), Policy: permpolicy.NewPolicy(nil, nil), Model: "test-model"}), + Store: memstore.New(), + PlacementProvider: testPlacementProvider{}, + PlacementScope: "test", + DefaultMode: session.ModeAuto, + MaxAllowedMode: session.ModeAuto, + }) + if !errors.Is(err, server.ErrConfig) || !strings.Contains(err.Error(), "enforcing guardrails checker") { + t.Fatalf("auto default without checker: err = %v, want ErrConfig naming the checker", err) + } + svc := modeAdmissionService(t, session.ModeAuto, session.ModeAuto, true) + sess, err := svc.CreateSession(context.Background(), "", session.Limits{}) + if err != nil || sess.Mode != session.ModeAuto { + t.Fatalf("auto default with checker: mode %v, err %v", sess, err) + } +} + +// TestInadmissibleDefaultModeRefusedAtConstruction pins that construction +// runs the complete session admission on the default mode and validates the +// ceiling, so a service that could never create its default session fails to +// start instead of failing every default CreateSession. +func TestInadmissibleDefaultModeRefusedAtConstruction(t *testing.T) { + for _, tc := range []struct { + name string + def, ceiling session.PermissionMode + checker bool + wantErrSubstring string + }{ + {"unknown default", "bogus", session.ModeYolo, true, "unknown permission mode"}, + {"default above ceiling", session.ModeYolo, session.ModeAccept, true, "exceeds the server ceiling"}, + {"auto default above ceiling", session.ModeAuto, session.ModeDefault, true, "exceeds the server ceiling"}, + {"unknown ceiling", session.ModeDefault, "bogus", true, "unknown permission-mode ceiling"}, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := newPlacementTestService(server.Config{ + Engine: agent.NewEngine(agent.Deps{LLM: mockllm.New(), Catalog: tool.NewCatalog(), Policy: permpolicy.NewPolicy(nil, nil), Model: "test-model"}), + Store: memstore.New(), + PlacementProvider: testPlacementProvider{}, + PlacementScope: "test", + DefaultMode: tc.def, + MaxAllowedMode: tc.ceiling, + EnforcingCheckerConfigured: tc.checker, + }) + if !errors.Is(err, server.ErrConfig) || !strings.Contains(err.Error(), tc.wantErrSubstring) { + t.Fatalf("err = %v, want ErrConfig containing %q", err, tc.wantErrSubstring) + } + }) + } + // Positive control: a default at the ceiling, and any default with no ceiling. + modeAdmissionService(t, session.ModeAccept, session.ModeAccept, false) + modeAdmissionService(t, "", session.ModeYolo, false) +} + +// TestAutoRequiresCheckerEvenWhenNotDefault pins that, without an enforcing +// checker, explicit auto creation and switching (back) to auto are refused on +// every ceiling that would otherwise admit auto, while yolo stays admissible. +func TestAutoRequiresCheckerEvenWhenNotDefault(t *testing.T) { + ctx := context.Background() + for _, def := range []session.PermissionMode{session.ModeDefault, session.ModeYolo} { + svc := modeAdmissionService(t, session.ModeYolo, def, false) + if _, err := svc.CreateSession(ctx, session.ModeAuto, session.Limits{}); !errors.Is(err, server.ErrInvalidArgument) { + t.Fatalf("default %s: CreateSession(auto) err = %v, want ErrInvalidArgument", def, err) + } + sess, err := svc.CreateSession(ctx, "", session.Limits{}) + if err != nil { + t.Fatalf("default %s: CreateSession: %v", def, err) + } + if _, err := svc.SetMode(ctx, sess.ID, session.ModeAuto); !errors.Is(err, server.ErrInvalidArgument) { + t.Fatalf("default %s: SetMode(auto) err = %v, want ErrInvalidArgument", def, err) + } + if _, err := svc.SetMode(ctx, sess.ID, session.ModeYolo); err != nil { + t.Fatalf("default %s: SetMode(yolo) positive control: %v", def, err) + } + } +} + +// TestStoredModeReadmittedAfterTighterRestart persists a permissive session, +// reopens the store under a tighter deployment, and proves prompt, fork, and +// clear refuse BEFORE any provider work, without touching the session; a +// SetMode to an admitted mode then lets the same session continue. +func TestStoredModeReadmittedAfterTighterRestart(t *testing.T) { + ctx := context.Background() + for _, tc := range []struct { + name string + stored session.PermissionMode + ceiling session.PermissionMode + checker bool + }{ + {"yolo above default ceiling", session.ModeYolo, session.ModeAccept, true}, + {"auto above default ceiling", session.ModeAuto, session.ModeAccept, true}, + {"auto after checker removed", session.ModeAuto, session.ModeYolo, false}, + } { + t.Run(tc.name, func(t *testing.T) { + store := memstore.New() + loose := modeAdmissionServiceOn(t, store, mockllm.New(), session.ModeYolo, session.ModeDefault, true) + sess, err := loose.CreateSession(ctx, tc.stored, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession(%s) on loose deployment: %v", tc.stored, err) + } + + llm := mockllm.New(mockllm.TextTurn("ran")) + tight := modeAdmissionServiceOn(t, store, llm, tc.ceiling, session.ModeDefault, tc.checker) + if run, err := tight.StartRun(ctx, sess.ID, "go"); !errors.Is(err, server.ErrFailedPrecondition) || run != nil { + t.Fatalf("StartRun = run %v, err %v; want ErrFailedPrecondition", run, err) + } + if _, err := tight.ForkSessionSuccessor(ctx, server.ForkSuccessorRequest{Source: sess.ID}); !errors.Is(err, server.ErrFailedPrecondition) { + t.Fatalf("ForkSessionSuccessor err = %v, want ErrFailedPrecondition", err) + } + if _, err := tight.ClearSessionSuccessor(ctx, sess.ID, server.SuccessorPlacement{}); !errors.Is(err, server.ErrFailedPrecondition) { + t.Fatalf("ClearSessionSuccessor err = %v, want ErrFailedPrecondition", err) + } + if llm.Calls() != 0 { + t.Fatalf("provider called %d time(s) before refusal, want 0", llm.Calls()) + } + kept, err := store.Load(ctx, sess.ID) + if err != nil { + t.Fatalf("Load: %v", err) + } + if kept.Mode != tc.stored || kept.State != sess.State { + t.Fatalf("refusal mutated the session: mode %q state %q", kept.Mode, kept.State) + } + + if _, err := tight.SetMode(ctx, sess.ID, session.ModeDefault); err != nil { + t.Fatalf("SetMode(default): %v", err) + } + run, err := tight.StartRun(ctx, sess.ID, "go") + if err != nil { + t.Fatalf("StartRun after SetMode(default): %v", err) + } + for range run.Events() { + } + tight.FinishRun(sess.ID, run) + if llm.Calls() != 1 { + t.Fatalf("provider calls after re-admission = %d, want 1", llm.Calls()) + } + }) + } +} + +// gatedLease parks the named owner's first Acquire until release closes, so +// a test can let a peer mutate the session while that owner waits for the +// lease after its pre-lease admission. +type gatedLease struct { + port.SessionLease + owner string + entered chan struct{} + release chan struct{} + once sync.Once +} + +func (l *gatedLease) Acquire(ctx context.Context, id session.SessionID, owner string) (port.Lease, error) { + if owner == l.owner { + first := false + l.once.Do(func() { first = true }) + if first { + close(l.entered) + <-l.release + } + } + return l.SessionLease.Acquire(ctx, id, owner) +} + +// leasedModeService is modeAdmissionServiceOn sharing a lease backend, so two +// services model replicas on different configurations during a rollout. +func leasedModeService(t *testing.T, store port.SessionStore, lease port.SessionLease, owner string, llm *mockllm.Provider, ceiling session.PermissionMode) *server.Service { + t.Helper() + svc, err := newPlacementTestService(server.Config{ + Engine: agent.NewEngine(agent.Deps{ + LLM: llm, + Catalog: tool.NewCatalog(), + Policy: permpolicy.NewPolicy(allowRules(), nil), + Model: "test-model", + }), + Store: store, + SessionLease: lease, + LeaseOwner: owner, + LeaseTTL: time.Minute, + LeaseRenewInterval: time.Hour, + DefaultCapabilities: llm.Capabilities(), + MaxAllowedMode: ceiling, + EnforcingCheckerConfigured: true, + }) + if err != nil { + t.Fatalf("new service %s: %v", owner, err) + } + t.Cleanup(svc.Close) + return svc +} + +// TestStoredModeReadmittedUnderAcquiredLease races two replicas sharing one +// store and lease backend: the stricter replica admits the pre-lease Default +// snapshot, a looser peer takes the lease and saves Yolo while it waits, and +// the stricter replica must refuse the authoritative Yolo snapshot once it +// holds the lease, before any provider work and without mutating it. +func TestStoredModeReadmittedUnderAcquiredLease(t *testing.T) { + ctx := context.Background() + store := memstore.New() + lease := &gatedLease{ + SessionLease: memlease.New(wallclock.Clock{}, time.Minute), + owner: "strict", entered: make(chan struct{}), release: make(chan struct{}), + } + looseLLM := mockllm.New() + loose := leasedModeService(t, store, lease, "loose", looseLLM, session.ModeYolo) + strictLLM := mockllm.New(mockllm.TextTurn("ran")) + strict := leasedModeService(t, store, lease, "strict", strictLLM, session.ModeAccept) + + sess, err := loose.CreateSession(ctx, session.ModeDefault, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + result := make(chan error, 1) + go func() { + run, err := strict.StartRun(ctx, sess.ID, "go") + if run != nil { + for range run.Events() { + } + strict.FinishRun(sess.ID, run) + } + result <- err + }() + select { + case <-lease.entered: + case <-time.After(5 * time.Second): + t.Fatal("strict replica never reached lease acquisition") + } + if _, err := loose.SetMode(ctx, sess.ID, session.ModeYolo); err != nil { + t.Fatalf("peer SetMode(yolo): %v", err) + } + close(lease.release) + select { + case err := <-result: + if !errors.Is(err, server.ErrFailedPrecondition) { + t.Fatalf("StartRun err = %v, want ErrFailedPrecondition", err) + } + case <-time.After(5 * time.Second): + t.Fatal("strict StartRun did not return") + } + if strictLLM.Calls() != 0 || looseLLM.Calls() != 0 { + t.Fatalf("provider calls strict=%d loose=%d before refusal, want 0", strictLLM.Calls(), looseLLM.Calls()) + } + kept, err := store.Load(ctx, sess.ID) + if err != nil { + t.Fatalf("Load: %v", err) + } + if kept.Mode != session.ModeYolo || kept.State != session.StateIdle { + t.Fatalf("refusal mutated the session: mode %q state %q", kept.Mode, kept.State) + } +} + +// gatedLeaseFor wraps a fresh shared memlease so owner's first Acquire parks +// until the test releases it. +func gatedLeaseFor(owner string) *gatedLease { + return &gatedLease{ + SessionLease: memlease.New(wallclock.Clock{}, time.Minute), + owner: owner, entered: make(chan struct{}), release: make(chan struct{}), + } +} + +// awaitGate waits until the gated owner is parked at lease acquisition. +func awaitGate(t *testing.T, lease *gatedLease) { + t.Helper() + select { + case <-lease.entered: + case <-time.After(5 * time.Second): + t.Fatal("the gated replica never reached lease acquisition") + } +} + +// TestRunEntryAdoptsAdmittedDowngradeUnderLease is the admitted-downgrade +// variant of TestStoredModeReadmittedUnderAcquiredLease: both replicas admit +// yolo, so mode admission alone cannot catch the race. Replica A reads a +// completed yolo session and waits for the lease; replica B downgrades it to +// default and releases; A must then run on the adopted default snapshot and +// must not reopen and save its stale yolo copy over B's change. +func TestRunEntryAdoptsAdmittedDowngradeUnderLease(t *testing.T) { + ctx := context.Background() + store := memstore.New() + seed := modeAdmissionServiceOn(t, store, mockllm.New(mockllm.TextTurn("first")), session.ModeYolo, session.ModeDefault, true) + sess, err := seed.CreateSession(ctx, session.ModeYolo, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + first, err := seed.StartRun(ctx, sess.ID, "first") + if err != nil { + t.Fatalf("seed StartRun: %v", err) + } + for range first.Events() { + } + seed.Persist(ctx, sess.ID) + seed.FinishRun(sess.ID, first) + if done, err := store.Load(ctx, sess.ID); err != nil || done.State != session.StateCompleted || done.Mode != session.ModeYolo { + t.Fatalf("seeded session = %+v, %v; want a completed yolo session", done, err) + } + + lease := gatedLeaseFor("replica-a") + llmA := mockllm.New(mockllm.TextTurn("second")) + a := leasedModeService(t, store, lease, "replica-a", llmA, session.ModeYolo) + b := leasedModeService(t, store, lease, "replica-b", mockllm.New(), session.ModeYolo) + + result := make(chan error, 1) + go func() { + run, err := a.StartRun(ctx, sess.ID, "second") + if run != nil { + for range run.Events() { + } + a.Persist(ctx, sess.ID) + a.FinishRun(sess.ID, run) + } + result <- err + }() + awaitGate(t, lease) + if _, err := b.SetMode(ctx, sess.ID, session.ModeDefault); err != nil { + t.Fatalf("peer SetMode(default): %v", err) + } + close(lease.release) + select { + case err := <-result: + if err != nil { + t.Fatalf("StartRun on the downgraded session: %v", err) + } + case <-time.After(5 * time.Second): + t.Fatal("replica A StartRun did not return") + } + if llmA.Calls() != 1 { + t.Fatalf("replica A provider calls = %d, want 1", llmA.Calls()) + } + after, err := store.Load(ctx, sess.ID) + if err != nil { + t.Fatalf("Load: %v", err) + } + if after.Mode != session.ModeDefault || after.State != session.StateCompleted { + t.Fatalf("after A's run: mode %q state %q; want B's default downgrade kept and the run completed", after.Mode, after.State) + } +} + +// TestApprovalResumeAdoptsPeerCancelledTurnUnderLease: replica A preflights an +// approval for a pending ask and waits for the lease; replica B, which refuses +// the stored yolo mode, cancels that pending turn through the refused-mode +// SetMode downgrade and releases. A must refuse the resume on the adopted +// snapshot with no provider or tool work, leaving B's cancellation in place. +func TestApprovalResumeAdoptsPeerCancelledTurnUnderLease(t *testing.T) { + ctx := context.Background() + store := memstore.New() + id, askID := parkAwaitingInMode(t, store, session.ModeYolo) + + lease := gatedLeaseFor("replica-a") + var ranA, ranB atomic.Int64 + llmA := mockllm.New(mockllm.TextTurn("resumed")) + a := recoveryServiceAs(t, store, llmA, &ranA, session.ModeYolo, true, lease, "replica-a") + b := recoveryServiceAs(t, store, mockllm.New(), &ranB, session.ModeAccept, true, lease, "replica-b") + + result := make(chan error, 1) + go func() { + run, err := a.ApproveRun(ctx, id, askID, session.VerdictAllowOnce, "") + if run != nil { + for range run.Events() { + } + a.FinishRun(id, run) + } + result <- err + }() + awaitGate(t, lease) + if _, err := b.SetMode(ctx, id, session.ModeDefault); err != nil { + t.Fatalf("peer refused-mode recovery SetMode(default): %v", err) + } + close(lease.release) + select { + case err := <-result: + if !errors.Is(err, server.ErrNoActiveRun) { + t.Fatalf("ApproveRun err = %v, want ErrNoActiveRun for the cancelled turn", err) + } + case <-time.After(5 * time.Second): + t.Fatal("replica A ApproveRun did not return") + } + if llmA.Calls() != 0 || ranA.Load() != 0 || ranB.Load() != 0 { + t.Fatalf("work ran after the peer cancelled: provider calls %d, tool executions A=%d B=%d", llmA.Calls(), ranA.Load(), ranB.Load()) + } + kept, err := store.Load(ctx, id) + if err != nil { + t.Fatalf("Load: %v", err) + } + if _, pending := kept.PendingAsk(); pending || kept.State != session.StateCancelled || kept.Mode != session.ModeDefault { + t.Fatalf("stored after refused resume: mode %q state %q pending %t; want cancelled/default", kept.Mode, kept.State, pending) + } +} + +// TestHTTPAutoYoloModes pins the HTTP mode mapping and the shared admission. +func TestHTTPAutoYoloModes(t *testing.T) { + decode := func(resp *http.Response) (int, string) { + defer resp.Body.Close() + var out struct { + SessionID string `json:"session_id"` + Mode string `json:"mode"` + } + _ = json.NewDecoder(resp.Body).Decode(&out) + if out.SessionID == "" { + return resp.StatusCode, "" + } + return resp.StatusCode, out.SessionID + " " + out.Mode + } + post := func(t *testing.T, url, body string) (int, string) { + t.Helper() + resp, err := http.Post(url, "application/json", strings.NewReader(body)) + if err != nil { + t.Fatalf("POST %s: %v", url, err) + } + return decode(resp) + } + + srv := httptest.NewServer(server.NewHTTPHandler(modeAdmissionService(t, session.ModeYolo, session.ModeDefault, true))) + defer srv.Close() + for _, mode := range []string{"auto", "yolo"} { + code, got := post(t, srv.URL+"/v1/sessions", `{"mode":"`+mode+`"}`) + if code != http.StatusCreated || got == "" { + t.Fatalf("create %s = %d %q, want 201", mode, code, got) + } + id := strings.Fields(got)[0] + resp, err := http.Get(srv.URL + "/v1/sessions/" + id) + if err != nil { + t.Fatalf("GET session: %v", err) + } + if code, got = decode(resp); code != http.StatusOK || !strings.HasSuffix(got, " "+mode) { + t.Fatalf("created %s session reads back %d %q", mode, code, got) + } + code, got = post(t, srv.URL+"/v1/sessions/"+id+"/mode", `{"mode":"default"}`) + if code != http.StatusOK || !strings.HasSuffix(got, " default") { + t.Fatalf("set default = %d %q", code, got) + } + code, got = post(t, srv.URL+"/v1/sessions/"+id+"/mode", `{"mode":"`+mode+`"}`) + if code != http.StatusOK || !strings.HasSuffix(got, " "+mode) { + t.Fatalf("set %s = %d %q", mode, code, got) + } + } + + strict := httptest.NewServer(server.NewHTTPHandler(modeAdmissionService(t, session.ModeDefault, session.ModeDefault, true))) + defer strict.Close() + if code, _ := post(t, strict.URL+"/v1/sessions", `{"mode":"yolo"}`); code != http.StatusBadRequest { + t.Fatalf("create yolo above ceiling = %d, want 400", code) + } +} + +// TestApprovePlanRefusesAutoYoloTarget pins that plan approval cannot flip a +// session into auto or yolo: it refuses instead of silently denying the plan. +func TestApprovePlanRefusesAutoYoloTarget(t *testing.T) { + for _, target := range []session.PermissionMode{session.ModeAuto, session.ModeYolo} { + llm := mockllm.New(mockllm.ToolCallTurn(call("c1", "PresentPlan", `{"note":"step 1"}`))) + svc := planApprovalService(t, llm, allowRules()) + sess, err := svc.CreateSession(context.Background(), session.ModePlan, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + _, finishParked := parkPlanAsk(t, svc, sess.ID) + _, err = svc.ApprovePlan(context.Background(), sess.ID, target, "") + if !errors.Is(err, server.ErrInvalidArgument) { + t.Fatalf("ApprovePlan(%s) err = %v, want ErrInvalidArgument", target, err) + } + got, err := svc.GetSession(context.Background(), sess.ID) + if err != nil { + t.Fatalf("GetSession: %v", err) + } + if got.State != session.StateAwaiting { + t.Fatalf("refused ApprovePlan(%s) consumed the plan ask: state %q", target, got.State) + } + finishParked() + } +} diff --git a/internal/adapter/server/mode_recovery_test.go b/internal/adapter/server/mode_recovery_test.go new file mode 100644 index 0000000000..0b21a299a5 --- /dev/null +++ b/internal/adapter/server/mode_recovery_test.go @@ -0,0 +1,188 @@ +package server_test + +import ( + "context" + "encoding/json" + "errors" + "strings" + "sync/atomic" + "testing" + "time" + + "github.com/stacklok/mecatl/engine/adapter/memlease" + "github.com/stacklok/mecatl/engine/adapter/memstore" + "github.com/stacklok/mecatl/engine/adapter/mockllm" + "github.com/stacklok/mecatl/engine/adapter/permpolicy" + "github.com/stacklok/mecatl/engine/adapter/permstore" + "github.com/stacklok/mecatl/engine/adapter/wallclock" + "github.com/stacklok/mecatl/engine/agent" + "github.com/stacklok/mecatl/engine/port" + "github.com/stacklok/mecatl/engine/session" + "github.com/stacklok/mecatl/engine/tool" + "github.com/stacklok/mecatl/internal/adapter/server" +) + +// parkAwaitingInMode drives a default session to a durable pending Write ask +// on a loose deployment, then rewrites the stored mode to model a snapshot +// parked under a deployment that admitted stored. +func parkAwaitingInMode(t *testing.T, store port.SessionStore, stored session.PermissionMode) (session.SessionID, string) { + t.Helper() + ctx := context.Background() + var ran atomic.Int64 + loose := newAskingService(t, store, permstore.New(), &ran, + mockllm.New(mockllm.ToolCallTurn(session.NewToolCall("w1", "Write", json.RawMessage(`{"path":"a.go"}`)))), false) + sess, err := loose.CreateSession(ctx, session.ModeDefault, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + askID := driveServiceToAwaiting(t, loose, sess.ID) + parked, err := store.Load(ctx, sess.ID) + if err != nil { + t.Fatal(err) + } + parked.Mode = stored + if err := store.Save(ctx, parked); err != nil { + t.Fatal(err) + } + return sess.ID, askID +} + +// recoveryService is a restarted deployment over store with the given ceiling +// and checker; lease, when non-nil, is shared with a peer. +func recoveryService(t *testing.T, store port.SessionStore, llm *mockllm.Provider, ran *atomic.Int64, ceiling session.PermissionMode, checker bool, lease port.SessionLease) *server.Service { + t.Helper() + return recoveryServiceAs(t, store, llm, ran, ceiling, checker, lease, "recovering") +} + +// recoveryServiceAs is recoveryService with an explicit lease owner, so two +// replicas can share one lease backend. +func recoveryServiceAs(t *testing.T, store port.SessionStore, llm *mockllm.Provider, ran *atomic.Int64, ceiling session.PermissionMode, checker bool, lease port.SessionLease, owner string) *server.Service { + t.Helper() + cat := tool.NewCatalog() + cat.MustRegister(&writeAskTool{ran: ran}) + cfg := server.Config{ + Engine: agent.NewEngine(agent.Deps{LLM: llm, Catalog: cat, Policy: permpolicy.NewPolicy(nil, permstore.New()), Model: "test-model", Store: store}), + Store: store, + Now: func() time.Time { return time.Unix(0, 0) }, + MaxAllowedMode: ceiling, + EnforcingCheckerConfigured: checker, + } + if lease != nil { + cfg.SessionLease, cfg.LeaseOwner, cfg.LeaseTTL, cfg.LeaseRenewInterval = lease, owner, time.Minute, time.Hour + } + svc, err := newPlacementTestService(cfg) + if err != nil { + t.Fatalf("new service: %v", err) + } + t.Cleanup(svc.Close) + return svc +} + +// TestSetModeDowngradeRecoversRefusedAwaitingSession pins option B's recovery: +// a stored session awaiting an approval in a mode the restarted server refuses +// is unrecoverable by approval, and a SetMode to an admitted lower mode cancels +// the pending turn without any provider or tool work and applies the mode in +// one save; the next StartRun then runs in the new mode. +func TestSetModeDowngradeRecoversRefusedAwaitingSession(t *testing.T) { + ctx := context.Background() + store := memstore.New() + id, askID := parkAwaitingInMode(t, store, session.ModeYolo) + + var ran atomic.Int64 + llm := mockllm.New(mockllm.TextTurn("continued")) + svc := recoveryService(t, store, llm, &ran, session.ModeAccept, true, nil) + + _, err := svc.ApproveRun(ctx, id, askID, session.VerdictAllowOnce, "") + if !errors.Is(err, server.ErrFailedPrecondition) || !strings.Contains(err.Error(), "SetMode to an admitted lower mode") { + t.Fatalf("ApproveRun err = %v, want FailedPrecondition naming the SetMode downgrade", err) + } + + got, err := svc.SetMode(ctx, id, session.ModeDefault) + if err != nil { + t.Fatalf("SetMode(default) on refused awaiting session: %v", err) + } + if got.Mode != session.ModeDefault || got.State != session.StateCancelled { + t.Fatalf("SetMode returned mode %q state %q, want default/cancelled", got.Mode, got.State) + } + if llm.Calls() != 0 || ran.Load() != 0 { + t.Fatalf("recovery ran work: provider calls %d, tool executions %d", llm.Calls(), ran.Load()) + } + kept, err := store.Load(ctx, id) + if err != nil { + t.Fatal(err) + } + if _, pending := kept.PendingAsk(); pending || kept.Mode != session.ModeDefault || kept.State != session.StateCancelled { + t.Fatalf("stored after recovery: mode %q state %q pending %t", kept.Mode, kept.State, pending) + } + if _, err := svc.ApproveRun(ctx, id, askID, session.VerdictAllowOnce, ""); err == nil { + t.Fatal("the cancelled ask still resumed") + } + + run, err := svc.StartRun(ctx, id, "continue") + if err != nil { + t.Fatalf("StartRun after recovery: %v", err) + } + drainRun(t, run) + svc.FinishRun(id, run) + if llm.Calls() != 1 || ran.Load() != 0 { + t.Fatalf("next run: provider calls %d, tool executions %d; want 1, 0", llm.Calls(), ran.Load()) + } + after, err := store.Load(ctx, id) + if err != nil { + t.Fatal(err) + } + if after.Mode != session.ModeDefault || after.State != session.StateCompleted { + t.Fatalf("after next run: mode %q state %q, want default/completed", after.Mode, after.State) + } +} + +// TestSetModeOnAwaitingSessionStillRefusedOutsideRecovery pins that only the +// narrow recovery widens SetMode: every other awaiting SetMode is refused with +// the pending ask intact and no work run. +func TestSetModeOnAwaitingSessionStillRefusedOutsideRecovery(t *testing.T) { + ctx := context.Background() + for _, tc := range []struct { + name string + stored session.PermissionMode + ceiling session.PermissionMode + checker bool + target session.PermissionMode + peer bool + want error + }{ + {"stored mode still admitted", session.ModeAccept, session.ModeAccept, true, session.ModeDefault, false, server.ErrInvalidArgument}, + {"target above ceiling", session.ModeYolo, session.ModeAccept, true, session.ModeAuto, false, server.ErrInvalidArgument}, + {"target not lower", session.ModeAuto, session.ModeYolo, false, session.ModeYolo, false, server.ErrInvalidArgument}, + {"peer holds the lease", session.ModeYolo, session.ModeAccept, true, session.ModeDefault, true, server.ErrSessionLeasedElsewhere}, + } { + t.Run(tc.name, func(t *testing.T) { + store := memstore.New() + id, askID := parkAwaitingInMode(t, store, tc.stored) + var lease port.SessionLease + if tc.peer { + shared := memlease.New(wallclock.Clock{}, time.Minute) + if _, err := shared.Acquire(ctx, id, "peer"); err != nil { + t.Fatalf("peer Acquire: %v", err) + } + lease = shared + } + var ran atomic.Int64 + llm := mockllm.New(mockllm.TextTurn("must not run")) + svc := recoveryService(t, store, llm, &ran, tc.ceiling, tc.checker, lease) + + if _, err := svc.SetMode(ctx, id, tc.target); !errors.Is(err, tc.want) { + t.Fatalf("SetMode(%s) err = %v, want %v", tc.target, err, tc.want) + } + if llm.Calls() != 0 || ran.Load() != 0 { + t.Fatalf("refusal ran work: provider calls %d, tool executions %d", llm.Calls(), ran.Load()) + } + kept, err := store.Load(ctx, id) + if err != nil { + t.Fatal(err) + } + if pending, ok := kept.PendingAsk(); kept.State != session.StateAwaiting || !ok || pending.AskID != askID || kept.Mode != tc.stored { + t.Fatalf("refusal changed the session: state %q mode %q pending %t", kept.State, kept.Mode, ok) + } + }) + } +} diff --git a/internal/adapter/server/placement_successor.go b/internal/adapter/server/placement_successor.go index 0db6379786..394fdded67 100644 --- a/internal/adapter/server/placement_successor.go +++ b/internal/adapter/server/placement_successor.go @@ -160,6 +160,11 @@ func (s *Service) createPlacedSuccessorLocked(ctx context.Context, req ForkSucce if err != nil || absent { return "", err } + // The successor inherits source.Mode, so it must be admissible under the + // current deployment before anything (including clearing the source) happens. + if err := s.admitStoredMode(source); err != nil { + return "", err + } if copyHistory { if err := s.attachLiveInstructions(source, false); err != nil { return "", err diff --git a/internal/adapter/server/resume_awaiting_test.go b/internal/adapter/server/resume_awaiting_test.go index e165082ef6..d53c55432e 100644 --- a/internal/adapter/server/resume_awaiting_test.go +++ b/internal/adapter/server/resume_awaiting_test.go @@ -215,6 +215,59 @@ func TestApproveAfterRestartResumesAwaiting(t *testing.T) { } } +// TestApproveAfterTighterRestartRefusesStoredMode pins that an awaiting session +// persisted in a mode the restarted deployment no longer admits cannot resume +// through Approve: the refusal precedes provider and tool work and leaves the +// pending ask in place. +func TestApproveAfterTighterRestartRefusesStoredMode(t *testing.T) { + ctx := context.Background() + store := memstore.New() + var ran1 atomic.Int64 + svc1 := newAskingService(t, store, permstore.New(), &ran1, + mockllm.New(mockllm.ToolCallTurn(session.NewToolCall("w1", "Write", json.RawMessage(`{"path":"a.go"}`)))), false) + sess, err := svc1.CreateSession(ctx, session.ModeDefault, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + askID := driveServiceToAwaiting(t, svc1, sess.ID) + // Model a snapshot written under a looser deployment that admitted yolo. + parked, err := store.Load(ctx, sess.ID) + if err != nil { + t.Fatal(err) + } + parked.Mode = session.ModeYolo + if err := store.Save(ctx, parked); err != nil { + t.Fatal(err) + } + + var ran2 atomic.Int64 + cat := tool.NewCatalog() + cat.MustRegister(&writeAskTool{ran: &ran2}) + llm := mockllm.New(mockllm.TextTurn("done after approval")) + svc2, err := newPlacementTestService(server.Config{ + Engine: agent.NewEngine(agent.Deps{LLM: llm, Catalog: cat, Policy: permpolicy.NewPolicy(nil, permstore.New()), Model: "test-model", Store: store}), + Store: store, + Now: func() time.Time { return time.Unix(0, 0) }, + MaxAllowedMode: session.ModeAccept, + }) + if err != nil { + t.Fatalf("new service: %v", err) + } + if run, err := svc2.ApproveRun(ctx, sess.ID, askID, session.VerdictAllowOnce, ""); !errors.Is(err, server.ErrFailedPrecondition) || run != nil { + t.Fatalf("ApproveRun = run %v, err %v; want ErrFailedPrecondition", run, err) + } + if ran2.Load() != 0 || llm.Calls() != 0 { + t.Fatalf("work started before refusal: tool executions %d, provider calls %d", ran2.Load(), llm.Calls()) + } + kept, err := store.Load(ctx, sess.ID) + if err != nil { + t.Fatal(err) + } + if pending, ok := kept.PendingAsk(); kept.State != session.StateAwaiting || !ok || pending.AskID != askID { + t.Fatalf("refusal consumed the pending ask: state %s present %t", kept.State, ok) + } +} + // TestApproveNonAwaitingYieldsNoActiveRun is the state-gate mutation oracle: a // COMPLETED (terminal, non-awaiting) session resumed via Approve yields // ErrNoActiveRun and is NOT re-entered. Dropping the State!=StateAwaiting gate in diff --git a/internal/adapter/server/sdk_typescript_release_test.go b/internal/adapter/server/sdk_typescript_release_test.go index 669d093aad..3aaf12cc33 100644 --- a/internal/adapter/server/sdk_typescript_release_test.go +++ b/internal/adapter/server/sdk_typescript_release_test.go @@ -202,6 +202,7 @@ func TestSDKTypescriptRelease_Scenario1_PublicServiceProjectionParity(t *testing // call ResolveScopedRunAsk for guardrail-scoped asks. "ResolveApprovalRun", "ResolveScopedRunAsk", + "AdmittedModes", "BindPlacement", "CanProcessSchedule", "Cancel", diff --git a/internal/adapter/server/service.go b/internal/adapter/server/service.go index 690a152b5e..c4fed5b767 100644 --- a/internal/adapter/server/service.go +++ b/internal/adapter/server/service.go @@ -431,6 +431,16 @@ type Config struct { // DefaultMode is applied when a CreateSession request leaves mode // unspecified. Defaults to session.ModeDefault when empty. DefaultMode session.PermissionMode + // MaxAllowedMode is the server ceiling: the most-permissive mode sessions may + // enter. SetMode rejects a requested mode above this ceiling with ErrInvalidArgument. + // Empty means no ceiling (all modes reachable). Populated by the composition layer + // from Config.MaxAllowedMode via the permission-mode token. + MaxAllowedMode session.PermissionMode + // EnforcingCheckerConfigured reports whether a guardrails checker is configured + // in enforcing (not advisory) mode. Used by SetMode to gate entry into ModeAuto, + // which requires an enforcing checker. Set by the composition root from the + // resolved checker state at startup. + EnforcingCheckerConfigured bool // DefaultLimits are the stop limits applied to a session created without // explicit limits. Because a zero Limits value DISABLES every stop condition // by design in package session, the composition root injects non-zero @@ -514,6 +524,14 @@ type Config struct { // Empty (the zero value / an unconfigured child service) yields no badge. String // passthrough — no enum on the wire (the EvNoProgress/StopBudget discipline). Posture string + // ProjectTrusted is the effective project-trust decision for the launch + // workspace, projected into the ServerCapabilities echo as CHROME ONLY. nil + // (an unconfigured child service) leaves the echo unset. + ProjectTrusted *bool + // SubagentAskReviewer is the headless subagent ask reviewer state ("on", + // "off", "inert"), projected into the ServerCapabilities echo. nil (an + // unconfigured child service) leaves the echo unset. + SubagentAskReviewer *string // DeploymentID is an optional, opaque, operator-set label for this deployment, // surfaced on GetServerInfo. It is empty by default and is NEVER derived from // hostname, pod name, or environment: infrastructure topology is not something @@ -1553,8 +1571,8 @@ func NewService(cfg Config) (*Service, error) { return nil, err } cfg.ServerImplementation = normalizeServerImplementation(cfg.ServerImplementation) - if cfg.DefaultMode == "" { - cfg.DefaultMode = session.ModeDefault + if err := normalizeDefaultMode(&cfg); err != nil { + return nil, err } if cfg.Now == nil { cfg.Now = time.Now @@ -2507,6 +2525,9 @@ func (s *Service) createSession(ctx context.Context, mode session.PermissionMode if mode == "" { mode = s.cfg.DefaultMode } + if err := s.admitMode(mode); err != nil { + return nil, err + } // Fill any UNSET (zero) limit field from the injected defaults, per-field. An // all-zero Limits inherits every default (so a default session cannot run // unbounded); a Limits that pins only some caps keeps those and inherits the @@ -2960,21 +2981,23 @@ func (s *Service) capabilities() *mecatlv1.ServerCapabilities { // matches the client's documented semantics ("true when ≥1 provider is // available"); opening the picker fires ListModels, which runs the refresher // and populates the list, and the empty-list state is handled gracefully. - ModelSelection: len(s.currentModels()) > 0 || s.modelsRefresher.Load() != nil, - Memory: has(memory.RememberToolName), - Skills: has(skills.ToolName), - Shell: has(tool.ShellToolName), - Image: pcaps.Image, - Audio: pcaps.Audio, - Posture: s.cfg.Posture, - Worktrees: s.placementDiscoveryAvailable(), - Reflection: s.cfg.ReflectSession != nil, - LearningProposals: s.cfg.Proposals != nil, - LearnedSkills: s.cfg.LearnedSkills != nil, - Scheduling: s.scheduleStore() != nil, - StorageHealth: s.cfg.StorageManagementAuthorized != nil && (implementsStorageHealth(s.cfg.Store) || s.scheduleStore() != nil), - StorageCleanup: s.cfg.StorageManagementAuthorized != nil && s.maintenanceMutationAvailable() && supportsCleanupDelete(s.cfg.Store), - ManualDream: toProtoDreamCapabilities(s.ManualDreamCapabilities()), + ModelSelection: len(s.currentModels()) > 0 || s.modelsRefresher.Load() != nil, + Memory: has(memory.RememberToolName), + Skills: has(skills.ToolName), + Shell: has(tool.ShellToolName), + Image: pcaps.Image, + Audio: pcaps.Audio, + Posture: s.cfg.Posture, + ProjectTrusted: s.cfg.ProjectTrusted, + SubagentAskReviewer: s.cfg.SubagentAskReviewer, + Worktrees: s.placementDiscoveryAvailable(), + Reflection: s.cfg.ReflectSession != nil, + LearningProposals: s.cfg.Proposals != nil, + LearnedSkills: s.cfg.LearnedSkills != nil, + Scheduling: s.scheduleStore() != nil, + StorageHealth: s.cfg.StorageManagementAuthorized != nil && (implementsStorageHealth(s.cfg.Store) || s.scheduleStore() != nil), + StorageCleanup: s.cfg.StorageManagementAuthorized != nil && s.maintenanceMutationAvailable() && supportsCleanupDelete(s.cfg.Store), + ManualDream: toProtoDreamCapabilities(s.ManualDreamCapabilities()), // Steer reads the SAME wired engine knob the runs consult (Deps.EnableSteer // via Engine.SteerEnabled) — the advertisement can never claim a steer // path the engine did not arm, and it is computed HERE, once, never @@ -4438,7 +4461,9 @@ func (s *Service) managementTargetAwaitingDrain(ctx context.Context, id session. // mode, and propagates session.ErrIllegalTransition (wrapped as ErrInvalidArgument) // when the session is mid-turn (running/awaiting) — the aggregate refuses a mode // change while a turn is in flight, so the caller must defer it to the next -// prompt. A change to the mode the session already has is a no-op success. +// prompt. The one exception is cancelRefusedAwaiting's recovery for a stored +// awaiting session whose mode this server refuses. A change to the mode the +// session already has is a no-op success. func (s *Service) SetMode(ctx context.Context, id session.SessionID, mode session.PermissionMode) (*session.Session, error) { if s.draining.Load() { return nil, fmt.Errorf("%w: %q", ErrUnavailable, id) @@ -4446,6 +4471,9 @@ func (s *Service) SetMode(ctx context.Context, id session.SessionID, mode sessio if mode == "" { return nil, fmt.Errorf("%w: mode is required", ErrInvalidArgument) } + if err := s.admitMode(mode); err != nil { + return nil, err + } // Ownership is established before caller-selected coordination, then // revalidated under runEntryMu before and after lease acquisition. if _, err := s.GetSession(ctx, id); err != nil { @@ -4481,6 +4509,9 @@ func (s *Service) SetMode(ctx context.Context, id session.SessionID, mode sessio return nil, loadErr } sess = loaded + if err := s.cancelRefusedAwaiting(sess, mode); err != nil { + return nil, err + } } if err := sess.SetMode(mode); err != nil { @@ -4493,6 +4524,157 @@ func (s *Service) SetMode(ctx context.Context, id session.SessionID, mode sessio return sess, nil } +// admitMode is the single admission check for a caller-requested session mode, +// shared by CreateSession, SetMode, and schedule fires (which create through +// CreateSession). A mode above the server ceiling is refused, and auto requires +// an enforcing guardrails checker with no exemption: unchecked autonomy is yolo. +// admitStoredMode applies the same check to a persisted session at every run +// entry, so a mode admitted under an earlier, looser deployment cannot execute. +func (s *Service) admitMode(mode session.PermissionMode) error { + return admitModeFor(&s.cfg, mode) +} + +func admitModeFor(cfg *Config, mode session.PermissionMode) error { + if modeRank(mode) == 0 { + return fmt.Errorf("%w: unknown permission mode %q", ErrInvalidArgument, mode) + } + if cfg.MaxAllowedMode != "" && !modeWithinCeiling(mode, cfg.MaxAllowedMode) { + return fmt.Errorf("%w: mode %q exceeds the server ceiling %q; the server must be relaunched with a higher --permission-mode to allow it", ErrInvalidArgument, mode, cfg.MaxAllowedMode) + } + if mode == session.ModeAuto && !cfg.EnforcingCheckerConfigured { + return fmt.Errorf("%w: mode %q requires an enforcing guardrails checker; configure one with --guardrails-model or bind the guardrail model slot, or use yolo for gate-free autonomous operation", ErrInvalidArgument, mode) + } + return nil +} + +// normalizeDefaultMode defaults an empty DefaultMode to default and runs the +// complete session admission against it, so a service whose default sessions +// could never be created (unknown mode, above the ceiling, or auto without an +// enforcing checker) fails construction instead of every default CreateSession. +func normalizeDefaultMode(cfg *Config) error { + if cfg.MaxAllowedMode != "" && modeRank(cfg.MaxAllowedMode) == 0 { + return fmt.Errorf("%w: unknown permission-mode ceiling %q", ErrConfig, cfg.MaxAllowedMode) + } + if cfg.DefaultMode == "" { + cfg.DefaultMode = session.ModeDefault + } + if err := admitModeFor(cfg, cfg.DefaultMode); err != nil { + return fmt.Errorf("%w: default mode: %v", ErrConfig, err) + } + return nil +} + +// AdmittedModes lists, in plan → yolo order, the permission modes this server +// admits for a session right now (the ceiling and the auto checker +// requirement), so a client can offer exactly the modes SetMode will accept. +func (s *Service) AdmittedModes() []session.PermissionMode { + var out []session.PermissionMode + for _, mode := range []session.PermissionMode{session.ModePlan, session.ModeDefault, session.ModeAccept, session.ModeAuto, session.ModeYolo} { + if s.admitMode(mode) == nil { + out = append(out, mode) + } + } + return out +} + +// admitStoredMode re-admits a persisted session's mode against the CURRENT +// ceiling and checker before execution, approval resume, or successor +// publication. A refusal leaves the session and its history untouched; the +// owner can SetMode to an admitted mode and continue. For a session awaiting +// an approval that SetMode must be a downgrade, which cancels the pending turn +// (cancelRefusedAwaiting). +func (s *Service) admitStoredMode(sess *session.Session) error { + if err := s.admitMode(sess.Mode); err != nil { + if sess.State == session.StateAwaiting { + return fmt.Errorf("%w: session %q is in permission mode %q, which this server does not admit (%v), and its pending approval cannot resume in that mode; SetMode to an admitted lower mode cancels the pending turn without running it and lets the session continue", ErrFailedPrecondition, sess.ID, sess.Mode, err) + } + return fmt.Errorf("%w: session %q is in permission mode %q, which this server does not admit (%v); switch it to an admitted mode with SetMode to continue", ErrFailedPrecondition, sess.ID, sess.Mode, err) + } + return nil +} + +// cancelRefusedAwaiting is the recovery for a stored session parked awaiting +// an approval in a mode this server refuses: admitStoredMode blocks every +// resume, and the aggregate refuses SetMode mid-turn. When the requested mode +// is admitted (the caller checked) and strictly lower than the stored mode, it +// cancels the pending turn without executing anything, so the caller's mode +// change and single save are legal; the next run repairs the history through +// the ordinary interrupt path. Every other awaiting session is left for the +// aggregate's mid-turn refusal. The caller holds runEntryMu and the mutation +// lease, found no live run, and passes the snapshot loaded under that lease. +func (s *Service) cancelRefusedAwaiting(sess *session.Session, mode session.PermissionMode) error { + if sess.State != session.StateAwaiting || s.admitMode(sess.Mode) == nil || modeRank(mode) >= modeRank(sess.Mode) { + return nil + } + if err := sess.Cancel(); err != nil { + return fmt.Errorf("%w: %v", ErrInvalidArgument, err) + } + return nil +} + +// adoptLeasedSession replaces a run entry's pre-lease snapshot with the +// AUTHORITATIVE one once this process holds id's session lease, re-admits its +// mode, and adopts it as the run's aggregate (st.sess). The pre-lease snapshot +// was only a preflight: while this request waited for the lease, a peer may +// have saved the session, e.g. downgraded its mode or cancelled its pending +// turn, and continuing with the stale copy would execute or persist over that +// change. The caller must repeat its entry-specific state, run, and ask checks +// against the returned aggregate before any recovery effect, persistence, or +// execution. Every later replacement of the aggregate re-admits it, and the +// launch fences check the executed aggregate once more. +// +// Without a shared lease it returns sess unchanged: there is no peer writer, +// and every in-process writer of a session (run entry, SetMode, management +// operations) serializes on runEntryMu, which the caller held across the +// pre-lease load. +func (s *Service) adoptLeasedSession(ctx context.Context, id session.SessionID, st *runState, sess *session.Session) (*session.Session, error) { + if s.cfg.SessionLease == nil { + return sess, nil + } + loaded, err := s.GetSession(ctx, id) + if err != nil { + return nil, err + } + if err := s.admitStoredMode(loaded); err != nil { + return nil, err + } + st.persistMu.Lock() + defer st.persistMu.Unlock() + s.mu.Lock() + defer s.mu.Unlock() + if s.runs[id] != st || st.cancelling || st.cancelSignaled { + return nil, fmt.Errorf("%w: %q", ErrUnavailable, id) + } + st.sess = loaded + st.titleRevision = loaded.TitleRevision + return loaded, nil +} + +// modeWithinCeiling reports whether mode is at or below ceiling in the +// permission-mode ordering: plan < default < accept-edits < auto < yolo. +func modeWithinCeiling(mode, ceiling session.PermissionMode) bool { + return modeRank(mode) <= modeRank(ceiling) +} + +// modeRank assigns an integer rank to session.PermissionMode for ceiling +// comparison. Unknown modes get rank 0 (treated as most restrictive). +func modeRank(mode session.PermissionMode) int { + switch mode { + case session.ModePlan: + return 1 + case session.ModeDefault: + return 2 + case session.ModeAccept: + return 3 + case session.ModeAuto: + return 4 + case session.ModeYolo: + return 5 + default: + return 0 + } +} + // LoadSession resumes a previously-persisted session so a subsequent StartRun // continues it. It loads the latest snapshot from the store and, if the session // is in a terminal state, REOPENS/INTERRUPTS/RECOVERS it to StateIdle @@ -5050,6 +5232,8 @@ func (s *Service) StartScheduledRunContent(ctx context.Context, id session.Sessi // FinishRun, exactly as for StartRunContent. Eligibility is derived exclusively from the // persisted typed terminal metadata and is consumed only after every fallible setup step // has succeeded and the recovered idle snapshot has been saved. +// +//nolint:gocyclo // Retry entry keeps preflight, lease, adopted-snapshot gates, and launch in one ordered transaction. func (s *Service) RetryFailedRun(ctx context.Context, id session.SessionID) (*agent.Run, error) { generation := s.captureRunEntryGeneration(id) if s.draining.Load() { @@ -5079,9 +5263,8 @@ func (s *Service) RetryFailedRun(ctx context.Context, id session.SessionID) (*ag if err := s.deregisterTerminalRunForEntry(id, sess, ErrFailedStepRetryIneligible); err != nil { return nil, err } - eligibleFailure, err := failedStepRetryEligibility(sess) - if err != nil { - return nil, fmt.Errorf("%w: session %q: %v", ErrFailedStepRetryIneligible, id, err) + if _, err := s.retryEntryEligibility(id, sess); err != nil { + return nil, err } st, admissionParent, err := s.beginRunAdmission(ctx, id, sess, false) @@ -5100,6 +5283,15 @@ func (s *Service) RetryFailedRun(ctx context.Context, id session.SessionID) (*ag } }() ctx = admissionCtx + if sess, err = s.adoptLeasedSession(ctx, id, st, sess); err != nil { + return nil, err + } + // Repeat the retry gates against the adopted aggregate: a peer may have + // retried, recovered, or otherwise moved the session on. + eligibleFailure, err := s.retryEntryEligibility(id, sess) + if err != nil { + return nil, err + } engine, env, governanceRoot, err := s.engineAndEnvironmentFor(ctx, sess) if err != nil { return nil, err @@ -5133,6 +5325,23 @@ func (s *Service) RetryFailedRun(ctx context.Context, id session.SessionID) (*ag return run, nil } +// retryEntryEligibility is RetryFailedRun's entry gate, run on the preflight +// snapshot and again on the aggregate adopted under the lease. It reports +// whether the session is still in its failed terminal (versus a prepared retry). +func (s *Service) retryEntryEligibility(id session.SessionID, sess *session.Session) (bool, error) { + if err := s.validatePersistedWorkspace(sess); err != nil { + return false, err + } + if err := admitRunPurpose(sess, runPurposeChat); err != nil { + return false, fmt.Errorf("%w: session %q is not eligible for chat retry", ErrFailedStepRetryIneligible, id) + } + eligibleFailure, err := failedStepRetryEligibility(sess) + if err != nil { + return false, fmt.Errorf("%w: session %q: %v", ErrFailedStepRetryIneligible, id, err) + } + return eligibleFailure, nil +} + func failedStepRetryEligibility(sess *session.Session) (bool, error) { metadata := sess.FailureMetadata() pendingMetadata, pending := sess.FailedStepRetryPending() @@ -5325,6 +5534,20 @@ func (s *Service) startRunContentLocked(ctx context.Context, id session.SessionI } }() ctx = admissionCtx + if sess, err = s.adoptLeasedSession(ctx, id, st, sess); err != nil { + return nil, err + } + // Repeat the entry gates against the adopted aggregate before recovery can + // mutate or persist it. + if err := s.validatePersistedWorkspace(sess); err != nil { + return nil, err + } + if err := admitRunPurpose(sess, purpose); err != nil { + return nil, err + } + if _, pending := sess.FailedStepRetryPending(); pending { + return nil, fmt.Errorf("%w: session %q has a pending failed-step retry", ErrFailedPrecondition, id) + } // Apply the unchanged reopen/interrupt/recover funnel only after the trusted // purpose and ownership gates and lease acquisition. Rejected kinds are never // mutated as a side effect of probing. @@ -5355,6 +5578,13 @@ func (s *Service) startRunContentLocked(ctx context.Context, id session.SessionI if err != nil { return nil, err } + // The reload replaced the admitted aggregate, so admit it again. An + // interrupted authorization is refused just below regardless of mode. + if !interruptedAuthorization { + if err := s.admitStoredMode(sess); err != nil { + return nil, err + } + } // interruptRestoredAuthorizationLocked may reload/replace the aggregate // (e.g. to interrupt a restored authorizing session); st.sess was captured // by beginRunAdmission above from the PRE-repair load, so it must be kept @@ -5671,6 +5901,9 @@ func (s *Service) resolvePersistedRunAsk(ctx context.Context, id session.Session if err := s.validatePersistedRunAsk(sess, expectedRunID, resolution, scoped); err != nil { return RunAskAcknowledgement{}, err } + if err := s.admitStoredMode(sess); err != nil { + return RunAskAcknowledgement{}, err + } st.persistMu.Lock() s.mu.Lock() if s.runs[id] != st || st.cancelling || st.cancelSignaled { @@ -5807,6 +6040,11 @@ func (s *Service) promoteDetachedRunAdmission(request context.Context, id sessio if err := s.validateHeldLeaseLocked(id); err != nil { return nil, err } + // The launch fence also covers the admitted mode: the aggregate about to + // execute is st.sess, whatever reload last replaced it. + if err := s.admitStoredMode(st.sess); err != nil { + return nil, err + } run := launch() st.run = run st.runContextStop = stop @@ -7043,6 +7281,30 @@ func (s *Service) ResolveApprovalRun(ctx context.Context, id session.SessionID, return s.resumeFromAwaiting(ctx, id, resolution, expectedRunID, generation) } +// validateAwaitingResume is resumeFromAwaiting's exact-run/ask proof, run on +// the preflight snapshot and again on the aggregate adopted under the lease. +func (s *Service) validateAwaitingResume(sess *session.Session, resolution agent.ApprovalResolution, expectedRunID string) error { + if err := s.validatePersistedWorkspace(sess); err != nil { + return err + } + // On this path the persisted session IS the run — it parked awaiting the ask + // and no live *agent.Run exists — so the stored id is the authoritative answer + // to "which run would this verdict resolve?". + if err := checkExpectedRun(expectedRunID, sess.RunID()); err != nil { + return err + } + if sess.State != session.StateAwaiting { + // Awaiting is the only resumable non-terminal state. Everything else stays + // stranded-for-Approve (last-write-wins / nothing to resume). + return ErrNoActiveRun + } + pending, ok := sess.PendingAsk() + if !ok || pending.AskID != resolution.AskID { + return fmt.Errorf("%w: ask is unknown, stale, or already resolved", agent.ErrApprovalNotPending) + } + return agent.ValidateApprovalResolution(pending, resolution) +} + // resumeFromAwaiting is the service half of the fourth (awaiting-only) run-entry // seam: it handles an Approve/Deny against a session whose process died while parked // awaiting approval. It loads the persisted session and: @@ -7111,25 +7373,7 @@ func (s *Service) resumeFromAwaiting(ctx context.Context, id session.SessionID, if err != nil { return nil, err } - if err := s.validatePersistedWorkspace(sess); err != nil { - return nil, err - } - // On this path the persisted session IS the run — it parked awaiting the ask - // and no live *agent.Run exists — so the stored id is the authoritative answer - // to "which run would this verdict resolve?". - if err := checkExpectedRun(expectedRunID, sess.RunID()); err != nil { - return nil, err - } - if sess.State != session.StateAwaiting { - // Awaiting is the only resumable non-terminal state. Everything else stays - // stranded-for-Approve (last-write-wins / nothing to resume). - return nil, ErrNoActiveRun - } - pending, ok := sess.PendingAsk() - if !ok || pending.AskID != resolution.AskID { - return nil, fmt.Errorf("%w: ask is unknown, stale, or already resolved", agent.ErrApprovalNotPending) - } - if err := agent.ValidateApprovalResolution(pending, resolution); err != nil { + if err := s.validateAwaitingResume(sess, resolution, expectedRunID); err != nil { return nil, err } st, admissionParent, err := s.beginRunAdmission(ctx, id, sess, true) @@ -7151,6 +7395,14 @@ func (s *Service) resumeFromAwaiting(ctx context.Context, id session.SessionID, } }() ctx = admissionCtx + // The pre-lease snapshot was a preflight: a peer may have resumed, cancelled, + // or recovered the pending turn while this request waited for the lease. + if sess, err = s.adoptLeasedSession(ctx, id, st, sess); err != nil { + return nil, err + } + if err := s.validateAwaitingResume(sess, resolution, expectedRunID); err != nil { + return nil, err + } if err := s.attachLiveInstructions(sess, true); err != nil { return nil, err } @@ -7255,7 +7507,11 @@ func (s *Service) approvePlan(ctx context.Context, id session.SessionID, targetM if !ok || ask.Origin != session.ApprovalOriginPlan { return nil, fmt.Errorf("%w: session %q is not awaiting a plan-approval ask", ErrNotAwaitingPlan, id) } - // (3) targetMode → verdict. + // (3) targetMode → verdict. Plan approval can flip only to default or + // accept-edits; refuse auto/yolo rather than silently treating them as deny. + if targetMode == session.ModeAuto || targetMode == session.ModeYolo { + return nil, fmt.Errorf("%w: plan approval cannot switch to mode %q; approve into default or accept-edits, then change the mode with SetMode", ErrInvalidArgument, targetMode) + } verdict, allowContinuation := planVerdictForMode(targetMode) // The merged event channel. Buffered to match a Run's own buffer (64) so a @@ -8851,6 +9107,11 @@ func (s *Service) cleanupRunAdmission(id session.SessionID, st *runState, promot // path so concurrent approvals wait for its resumeMu transaction to promote. // The caller holds runEntryMu for id. func (s *Service) beginRunAdmission(parent context.Context, id session.SessionID, sess *session.Session, resumeAdmission bool) (*runState, context.Context, error) { + // Every run entry (prompt, approval resume, plan continuation, retry) passes + // here before any lease, engine, provider, or tool work. + if err := s.admitStoredMode(sess); err != nil { + return nil, nil, err + } operationRelease := func() {} if s.cfg.OperationPin != nil { var err error @@ -8954,6 +9215,11 @@ func (s *Service) promoteRunAdmission(id session.SessionID, st *runState, stop c if err := s.validateHeldLeaseLocked(id); err != nil { return nil, err } + // The launch fence also covers the admitted mode: the aggregate about to + // execute is st.sess, whatever reload last replaced it. + if err := s.admitStoredMode(st.sess); err != nil { + return nil, err + } run := launch() st.run = run st.runContextStop = stop diff --git a/internal/app/agentdefs.go b/internal/app/agentdefs.go index f1bd998a5e..00deb7e89f 100644 --- a/internal/app/agentdefs.go +++ b/internal/app/agentdefs.go @@ -334,17 +334,21 @@ func lookupModelAlias(cfg Config, sel string) (id string, known bool) { // resolvePermissionMode maps a def's frontmatter permissionMode string to a // domain session.PermissionMode, in the composition layer (the domain never sees -// the raw string). "default"/empty/unknown => "" (the caller's default — for a team -// member that means the team-wide WithTeamMode). "plan" and "acceptEdits" map to -// their domain constants. An unrecognised value warns and falls back to the default. +// the raw string). Empty => "" (the caller's default — for a team member that +// means the team-wide WithTeamMode, which inherits an auto or yolo parent). +// "default", "plan", and "acceptEdits" map to their domain constants, so an +// explicit default stays default under an auto or yolo parent. An unrecognised +// value warns and falls back to session.ModeDefault rather than inheriting. // // Note (critique B2): plan mode hard-denies mutations (existing invariant), so a // plan member is effectively read-only even if Mutating. acceptEdits is meaningful // only for a Mutating member (a read-only member has no mutating tools to accept). func resolvePermissionMode(d port.Diagnostics, def agents.AgentDef) session.PermissionMode { switch strings.TrimSpace(def.PermissionMode) { - case "", "default": + case "": return "" // caller's default + case string(session.ModeDefault): + return session.ModeDefault case string(session.ModePlan): return session.ModePlan case string(session.ModeAccept): @@ -352,7 +356,7 @@ func resolvePermissionMode(d port.Diagnostics, def agents.AgentDef) session.Perm default: d.Log(context.Background(), port.LevelWarn, "agent def references an unknown permissionMode; using the default", "agent", def.Name, "mode", def.PermissionMode, "origin", string(def.Origin)) - return "" + return session.ModeDefault } } @@ -1279,7 +1283,7 @@ func resolveAgentRegistry(ctx context.Context, cfg Config) *agents.Registry { // (projectIngestionAdmitted). The user-tier + explicit defs stay active // regardless ("ask the human" mode, not "do nothing"). if cfg.AgentsConventional && cfg.Workspace != "" && !projectIngestionAdmitted(cfg) { - cfg.diag().Log(ctx, port.LevelWarn, "agent definitions: project-tier defs WITHHELD (untrusted workspace or project ingestion not granted); user-tier and explicit defs stay active. Pass --trust-project (on a headless root) or run --posture auto on an interactive root to admit its project agent defs", + cfg.diag().Log(ctx, port.LevelWarn, "agent definitions: project-tier defs WITHHELD (untrusted workspace or project ingestion not granted); user-tier and explicit defs stay active. Pass --trust-project (on a headless root) or run --permission-mode auto on an interactive root to admit its project agent defs", "workspace", cfg.Workspace, "dirs", ".mecatl/agents,.claude/agents") } sources := agents.ResolveSources(agents.ResolveOptions{ diff --git a/internal/app/agentdefs_test.go b/internal/app/agentdefs_test.go index 9ffc23f987..316fd19aa7 100644 --- a/internal/app/agentdefs_test.go +++ b/internal/app/agentdefs_test.go @@ -426,3 +426,24 @@ func kvArgsContain(args []any, key, val string) bool { } return false } + +// TestResolvePermissionModeKeepsExplicitDefault pins that only an omitted +// permissionMode inherits the team default (which follows an auto or yolo +// parent); an explicit or unrecognised value pins the member's mode. +func TestResolvePermissionModeKeepsExplicitDefault(t *testing.T) { + for _, tc := range []struct { + in string + want session.PermissionMode + }{ + {"", ""}, + {"default", session.ModeDefault}, + {" default ", session.ModeDefault}, + {"plan", session.ModePlan}, + {"acceptEdits", session.ModeAccept}, + {"bogus", session.ModeDefault}, + } { + if got := resolvePermissionMode(port.NopDiagnostics{}, agents.AgentDef{Name: "m", PermissionMode: tc.in}); got != tc.want { + t.Errorf("resolvePermissionMode(%q) = %q, want %q", tc.in, got, tc.want) + } + } +} diff --git a/internal/app/allowall_test.go b/internal/app/allowall_test.go index 56b7babd5f..3d2b0d6520 100644 --- a/internal/app/allowall_test.go +++ b/internal/app/allowall_test.go @@ -10,185 +10,167 @@ import ( "github.com/stacklok/mecatl/engine/session" ) -// TestMainRulesInjectsAllowAllWhenSet proves the AllowAllTools posture injects a -// single ScopeCLI allow-all rule into the main engine's static ruleset, and is a -// pure no-op (identical to defaultRules) when unset. See -// docs/architecture/governance.md. -func TestMainRulesInjectsAllowAllWhenSet(t *testing.T) { - withAllowAll := mainRules(Config{AllowAllTools: true}) - var found bool - for _, r := range withAllowAll { - if r.Scope == governance.ScopeCLI && r.Tool == "" && r.Pattern == "" && r.Effect == governance.Allow { - found = true - if r.Audience != governance.AudienceMain { - t.Fatalf("the main allow-all rule must carry AudienceMain; got %v", r.Audience) - } - break +// TestAutoModeInjectsAllowAll proves that Policy.Evaluate with ModeAuto returns +// Allow for mutating tools (Shell/Edit) that ModeDefault floors at Ask. This +// verifies that allow-all is injected at evaluation time by the mode, not baked +// into the base ruleset at startup. +func TestAutoModeInjectsAllowAll(t *testing.T) { + const sid = session.SessionID("s1") + bashArgs, _ := json.Marshal(map[string]string{"command": "touch x"}) + bashCall := session.NewToolCall("c1", "Shell", bashArgs) + editArgs, _ := json.Marshal(map[string]string{"file_path": "/x"}) + editCall := session.NewToolCall("c2", "Edit", editArgs) + + p := permpolicy.NewPolicy(mainRules(Config{}), nil, mainEvaluatorOptions(Config{})...) + + // ModeAuto injects allowAllCLIRules → Ask floor is loosened → Allow. + for _, call := range []session.ToolCall{bashCall, editCall} { + if got := p.Evaluate(context.Background(), sid, session.ModeAuto, call, nil); got.Decision.Effect != governance.Allow { + t.Fatalf("ModeAuto %s: expected Allow, got %v (%s)", call.Name, got.Decision.Effect, got.Decision.Reason) } } - if !found { - t.Fatalf("AllowAllTools=true should inject a ScopeCLI empty-Tool/empty-Pattern Allow rule; rules=%+v", withAllowAll) - } - if len(withAllowAll) != len(defaultRules())+1 { - t.Fatalf("AllowAllTools=true should add exactly one rule: got %d, want %d", len(withAllowAll), len(defaultRules())+1) - } - without := mainRules(Config{AllowAllTools: false}) - if len(without) != len(defaultRules()) { - t.Fatalf("AllowAllTools=false should equal defaultRules(): got %d, want %d", len(without), len(defaultRules())) - } - for _, r := range without { - if r.Scope == governance.ScopeCLI && r.Tool == "" && r.Pattern == "" && r.Effect == governance.Allow { - t.Fatalf("AllowAllTools=false must NOT inject the allow-all rule; rules=%+v", without) + // ModeDefault: no extra rules injected → built-in mutate-Ask floor stands. + for _, call := range []session.ToolCall{bashCall, editCall} { + if got := p.Evaluate(context.Background(), sid, session.ModeDefault, call, nil); got.Decision.Effect != governance.Ask { + t.Fatalf("ModeDefault %s: expected Ask, got %v (%s)", call.Name, got.Decision.Effect, got.Decision.Reason) } } } -// TestMainRulesPolicyAutoAllowsAskFloor closes the loop on the structural check -// above: it drives mainRules through the REAL permpolicy evaluator and asserts the -// injected rule actually flips the built-in mutate-ask floor (Shell/Edit) from Ask -// to Allow when AllowAllTools is set, and leaves it at Ask when unset. This would -// catch a wrong scope (one that cannot loosen the ScopeBuiltinDefault floor) that -// the shape-only assertions miss. Benign commands only; nothing executes (the test -// stops at Evaluate). -func TestMainRulesPolicyAutoAllowsAskFloor(t *testing.T) { +// TestPolicyAutoAllowsAskFloor is the evaluator-level proof that ModeAuto +// actually flips the mutate-ask floor (Shell/Edit) from Ask to Allow. Benign +// commands only; nothing executes (the test stops at Evaluate). +func TestPolicyAutoAllowsAskFloor(t *testing.T) { const sid = session.SessionID("s1") bashArgs, _ := json.Marshal(map[string]string{"command": "touch x"}) bashCall := session.NewToolCall("c1", "Shell", bashArgs) editArgs, _ := json.Marshal(map[string]string{"file_path": "/x"}) editCall := session.NewToolCall("c2", "Edit", editArgs) - on := permpolicy.NewPolicy(mainRules(Config{AllowAllTools: true}), nil) - if got := on.Evaluate(context.Background(), sid, session.ModeDefault, bashCall, nil); got.Decision.Effect != governance.Allow { - t.Fatalf("AllowAllTools=true Shell: expected Allow, got %v", got.Decision.Effect) + p := permpolicy.NewPolicy(mainRules(Config{}), nil, mainEvaluatorOptions(Config{})...) + + if got := p.Evaluate(context.Background(), sid, session.ModeAuto, bashCall, nil); got.Decision.Effect != governance.Allow { + t.Fatalf("ModeAuto Shell: expected Allow, got %v (%s)", got.Decision.Effect, got.Decision.Reason) } - if got := on.Evaluate(context.Background(), sid, session.ModeDefault, editCall, nil); got.Decision.Effect != governance.Allow { - t.Fatalf("AllowAllTools=true Edit: expected Allow, got %v", got.Decision.Effect) + if got := p.Evaluate(context.Background(), sid, session.ModeAuto, editCall, nil); got.Decision.Effect != governance.Allow { + t.Fatalf("ModeAuto Edit: expected Allow, got %v (%s)", got.Decision.Effect, got.Decision.Reason) } - - off := permpolicy.NewPolicy(mainRules(Config{AllowAllTools: false}), nil) - if got := off.Evaluate(context.Background(), sid, session.ModeDefault, bashCall, nil); got.Decision.Effect != governance.Ask { - t.Fatalf("AllowAllTools=false Shell: expected Ask, got %v", got.Decision.Effect) + if got := p.Evaluate(context.Background(), sid, session.ModeDefault, bashCall, nil); got.Decision.Effect != governance.Ask { + t.Fatalf("ModeDefault Shell: expected Ask, got %v (%s)", got.Decision.Effect, got.Decision.Reason) } - if got := off.Evaluate(context.Background(), sid, session.ModeDefault, editCall, nil); got.Decision.Effect != governance.Ask { - t.Fatalf("AllowAllTools=false Edit: expected Ask, got %v", got.Decision.Effect) + if got := p.Evaluate(context.Background(), sid, session.ModeDefault, editCall, nil); got.Decision.Effect != governance.Ask { + t.Fatalf("ModeDefault Edit: expected Ask, got %v (%s)", got.Decision.Effect, got.Decision.Reason) } } -// TestYoloLoosensSubstitutionFloorViaComposition (T3) proves the COMPOSITION wiring that -// connects Config.AllowAllTools → governance.WithLooseSubstitution is real, not silently -// disconnected. It builds the main policy the EXACT way buildEngine does (mainRules + -// mainEvaluatorOptions), then drives a substitution command through the real evaluator: -// with --yolo a non-read-only substitution resolves Allow; without it floors at Ask. A -// silent break in mainEvaluatorOptions would flip the yolo case back to Ask and fail -// here. Uses the innocuous stand-in `zap` (a non-read-only inner); nothing executes. -func TestYoloLoosensSubstitutionFloorViaComposition(t *testing.T) { +// TestYoloModeLoosensSubstitutionFloor proves that Policy.Evaluate with ModeYolo +// allows $(...) substitutions that ModeDefault and ModeAuto floor at Ask. The +// loose-substitution evaluator is selected by ModeYolo at call time. +// Uses the innocuous stand-in `zap`; nothing executes. +func TestYoloModeLoosensSubstitutionFloor(t *testing.T) { const sid = session.SessionID("s1") args, _ := json.Marshal(map[string]string{"command": "cat $(zap)"}) call := session.NewToolCall("c1", "Shell", args) - // Built the SAME way buildEngine builds the main policy. - yoloCfg := Config{AllowAllTools: true} - yolo := permpolicy.NewPolicy(mainRules(yoloCfg), nil, mainEvaluatorOptions(yoloCfg)...) - if got := yolo.Evaluate(context.Background(), sid, session.ModeDefault, call, nil); got.Decision.Effect != governance.Allow { - t.Fatalf("--yolo substitution: expected Allow (the loose-substitution option must be wired), got %v (%s)", got.Decision.Effect, got.Decision.Reason) - } + // Build the policy the same way buildEngine builds the main policy. + p := permpolicy.NewPolicy(mainRules(Config{}), nil, mainEvaluatorOptions(Config{})...) - plainCfg := Config{AllowAllTools: false} - plain := permpolicy.NewPolicy(mainRules(plainCfg), nil, mainEvaluatorOptions(plainCfg)...) - if got := plain.Evaluate(context.Background(), sid, session.ModeDefault, call, nil); got.Decision.Effect != governance.Ask { - t.Fatalf("no-yolo substitution: expected Ask (floor stands), got %v (%s)", got.Decision.Effect, got.Decision.Reason) + // ModeYolo: loose-substitution evaluator + allowAllCLIRules → Allow. + if got := p.Evaluate(context.Background(), sid, session.ModeYolo, call, nil); got.Decision.Effect != governance.Allow { + t.Fatalf("ModeYolo substitution: expected Allow (evalLoose must be wired), got %v (%s)", got.Decision.Effect, got.Decision.Reason) } -} -// TestChildRulesInjectsAllowAllWhenSet mirrors TestMainRulesInjectsAllowAllWhenSet -// for the CHILD ruleset: under --yolo childRules prepends exactly one -// ScopeCLI/empty-Tool/empty-Pattern/Allow/AudienceSubagent rule to the allow-all -// floor; with yolo off it equals AllowAllFloorRules() with no injected rule. This -// is the structural half of the team-member/subagent auto-approve fix. -func TestChildRulesInjectsAllowAllWhenSet(t *testing.T) { - floor := permpolicy.AllowAllFloorRules() - - withAllowAll := childRules(Config{AllowAllTools: true}) - var found int - for _, r := range withAllowAll { - if r.Scope == governance.ScopeCLI && r.Tool == "" && r.Pattern == "" && r.Effect == governance.Allow { - found++ - if r.Audience != governance.AudienceSubagent { - t.Fatalf("the child allow-all rule must carry AudienceSubagent; got %v", r.Audience) - } - } + // ModeAuto: standard evaluator + allowAllCLIRules → substitution still floors at Ask. + if got := p.Evaluate(context.Background(), sid, session.ModeAuto, call, nil); got.Decision.Effect != governance.Ask { + t.Fatalf("ModeAuto substitution: expected Ask (substitution floor stands), got %v (%s)", got.Decision.Effect, got.Decision.Reason) } - if found != 1 { - t.Fatalf("AllowAllTools=true should inject exactly one ScopeCLI AudienceSubagent Allow rule; found %d in %+v", found, withAllowAll) + + // ModeDefault: no extra rules → Ask. + if got := p.Evaluate(context.Background(), sid, session.ModeDefault, call, nil); got.Decision.Effect != governance.Ask { + t.Fatalf("ModeDefault substitution: expected Ask (floor stands), got %v (%s)", got.Decision.Effect, got.Decision.Reason) } - if len(withAllowAll) != len(floor)+1 { - t.Fatalf("AllowAllTools=true should add exactly one rule: got %d, want %d", len(withAllowAll), len(floor)+1) +} + +// TestChildPolicyAutoAllowsAllMutates verifies that child policy with ModeDefault +// allows all mutates (the AllowAllFloorRules floor covers it), ModeAuto also +// allows mutates, and ModePlan still denies mutates. +func TestChildPolicyAutoAllowsAllMutates(t *testing.T) { + const sid = session.SessionID("s1") + args, _ := json.Marshal(map[string]string{"command": "python3 script.py"}) + call := session.NewToolCall("c1", "Shell", args) + + child := childPermPolicy(Config{}) + + // ModeDefault: child floor is blanket allow-all → Allow. + if got := child.Evaluate(context.Background(), sid, session.ModeDefault, call, nil); got.Decision.Effect != governance.Allow { + t.Fatalf("child ModeDefault plain mutate: expected Allow (floor), got %v (%s)", got.Decision.Effect, got.Decision.Reason) } - without := childRules(Config{AllowAllTools: false}) - if len(without) != len(floor) { - t.Fatalf("AllowAllTools=false should equal AllowAllFloorRules(): got %d, want %d", len(without), len(floor)) + // ModeAuto: floor + allowAllCLIRules → Allow (floor already allows, mode is redundant but harmless). + if got := child.Evaluate(context.Background(), sid, session.ModeAuto, call, nil); got.Decision.Effect != governance.Allow { + t.Fatalf("child ModeAuto plain mutate: expected Allow, got %v (%s)", got.Decision.Effect, got.Decision.Reason) } - for _, r := range without { - if r.Scope == governance.ScopeCLI && r.Tool == "" && r.Pattern == "" && r.Effect == governance.Allow { - t.Fatalf("AllowAllTools=false must NOT inject the ScopeCLI allow-all rule; rules=%+v", without) - } + + // ModePlan: plan-mode hard-deny runs FIRST → Deny. + if got := child.Evaluate(context.Background(), sid, session.ModePlan, call, nil); got.Decision.Effect != governance.Deny { + t.Fatalf("child ModePlan plain mutate: expected Deny (plan-mode gate), got %v (%s)", got.Decision.Effect, got.Decision.Reason) } } // TestChildPolicyAutoApprovesNonSubstitution proves the child policy -// auto-approves a plain (non-read-only, non-substitution) mutate Shell command — -// and that this holds REGARDLESS of --yolo, because the child floor is a blanket -// allow-all (children have no mutate-ask floor to loosen, unlike the main -// engine's defaultRules()). This pins the empirical reality the -// team-member/subagent "bug" was misdiagnosed against: a child's plain mutate -// never prompted; only its substitution/heredoc commands floor at Ask (kept by -// TestYoloChildSubstitutionStillFailsSafe). Innocuous non-read-only stand-in; +// auto-approves a plain (non-read-only, non-substitution) mutate Shell command. +// This holds in ModeDefault because the child floor is a blanket allow-all; +// children have no mutate-ask floor to loosen. Innocuous non-read-only stand-in; // nothing executes. func TestChildPolicyAutoApprovesNonSubstitution(t *testing.T) { const sid = session.SessionID("s1") args, _ := json.Marshal(map[string]string{"command": "python3 script.py"}) call := session.NewToolCall("c1", "Shell", args) - for _, yolo := range []bool{true, false} { - p := childPermPolicy(Config{AllowAllTools: yolo}) - if got := p.Evaluate(context.Background(), sid, session.ModeDefault, call, nil); got.Decision.Effect != governance.Allow { - t.Fatalf("child plain mutate (yolo=%v): expected Allow (blanket floor), got %v (%s)", yolo, got.Decision.Effect, got.Decision.Reason) - } + p := childPermPolicy(Config{}) + if got := p.Evaluate(context.Background(), sid, session.ModeDefault, call, nil); got.Decision.Effect != governance.Allow { + t.Fatalf("child plain mutate ModeDefault: expected Allow (blanket floor), got %v (%s)", got.Decision.Effect, got.Decision.Reason) } } -// TestYoloChildSubstitutionStillFailsSafe is the SAFETY kill test: under --yolo a -// child's substitution command with a NON-read-only inner ($(zap)) must STILL -// floor at Ask, with neither FlooredConfiguredAllow nor ConfiguredAsk set (the -// loosening is main-only; the child resolves through the child-ask model). The -// paired positive: a substitution with a READ-ONLY inner clears the -// flooredAllowSafe inner contract → FlooredConfiguredAllow, still surfaced as Ask. +// TestYoloChildSubstitutionStillFailsSafe is the SAFETY kill test: under +// ModeDefault a child's substitution command with a non-read-only inner ($(zap)) +// floors at Ask. Under ModeYolo, the evalLoose evaluator allows it. func TestYoloChildSubstitutionStillFailsSafe(t *testing.T) { const sid = session.SessionID("s1") - child := childPermPolicy(Config{AllowAllTools: true}) + child := childPermPolicy(Config{}) badArgs, _ := json.Marshal(map[string]string{"command": "cat $(zap)"}) // non-read-only inner badCall := session.NewToolCall("c1", "Shell", badArgs) - if got := child.Evaluate(context.Background(), sid, session.ModeDefault, badCall, nil); got.Decision.Effect != governance.Ask || got.Decision.AskProvenance != governance.AskProvenanceUnknown { - t.Fatalf("--yolo child substitution (bad inner) must Ask with no Floored/Configured bits; got %+v", got) + + // ModeDefault: substitution floors at Ask. The child floor is AllowAllFloorRules at + // ScopeBuiltinDefault, so flooredAllowSafe fails for the non-read-only inner (zap), + // flooredBuiltin++ → AskProvenanceBuiltinSubstitutionFloor. + if got := child.Evaluate(context.Background(), sid, session.ModeDefault, badCall, nil); got.Decision.Effect != governance.Ask || got.Decision.AskProvenance != governance.AskProvenanceBuiltinSubstitutionFloor { + t.Fatalf("child ModeDefault substitution (bad inner) must Ask with BuiltinSubstitutionFloor; got %+v", got) + } + + // ModeYolo: evalLoose evaluator + allowAllCLIRules → Allow. + if got := child.Evaluate(context.Background(), sid, session.ModeYolo, badCall, nil); got.Decision.Effect != governance.Allow { + t.Fatalf("child ModeYolo substitution: expected Allow (loose evaluator), got %v (%s)", got.Decision.Effect, got.Decision.Reason) } goodArgs, _ := json.Marshal(map[string]string{"command": "go test $(git rev-parse HEAD)"}) // read-only inner goodCall := session.NewToolCall("c2", "Shell", goodArgs) - if got := child.Evaluate(context.Background(), sid, session.ModeDefault, goodCall, nil); got.Decision.Effect != governance.Ask || got.Decision.AskProvenance != governance.AskProvenanceConfiguredAllowFloor { - t.Fatalf("--yolo child substitution (read-only inner) must Ask with FlooredConfiguredAllow; got %+v", got) + // ModeDefault: read-only inner, but the only matching Allow is the floor (ScopeBuiltinDefault) + // → flooredBuiltin → AskProvenanceBuiltinSubstitutionFloor. + if got := child.Evaluate(context.Background(), sid, session.ModeDefault, goodCall, nil); got.Decision.Effect != governance.Ask || got.Decision.AskProvenance != governance.AskProvenanceBuiltinSubstitutionFloor { + t.Fatalf("child ModeDefault substitution (read-only inner) must Ask with BuiltinSubstitutionFloor; got %+v", got) } } // TestYoloLoosensChildSubstitutionStrictDoesNot is the governance/permpolicy DECISION -// proof for the posture ladder's headline behaviour change: a CHILD policy built with -// LooseChildSubstitution=true (yolo) ALLOWS a substitution/heredoc Shell command that -// the same policy with it false (strict/trusted/auto) ASKS — AND the locked-at-every- -// tier invariants still hold under yolo: a ScopeManaged Deny still wins, and a -// configured (above-floor) Ask still asks. Innocuous non-read-only stand-ins only; -// nothing executes (the test stops at Evaluate). +// proof for the mode-driven substitution behavior: a child policy evaluated in +// ModeYolo ALLOWS a substitution/heredoc Shell command that ModeDefault and ModeAuto +// ASKS — AND the locked-at-every-tier invariants still hold: a ScopeManaged Deny +// still wins, and a configured (above-floor) Ask still asks. Uses the innocuous +// non-read-only inner `$(zap)`; nothing executes. func TestYoloLoosensChildSubstitutionStrictDoesNot(t *testing.T) { const sid = session.SessionID("s1") subArgs, _ := json.Marshal(map[string]string{"command": "cat $(zap)"}) // non-read-only inner @@ -196,153 +178,113 @@ func TestYoloLoosensChildSubstitutionStrictDoesNot(t *testing.T) { heredocArgs, _ := json.Marshal(map[string]string{"command": "python3 - <<'PY'\nprint('x')\nPY"}) heredocCall := session.NewToolCall("c2", "Shell", heredocArgs) - yolo := childPermPolicy(Config{LooseChildSubstitution: true}) + child := childPermPolicy(Config{}) + + // ModeYolo: evalLoose + allowAllCLIRules → Allow for substitution and heredoc. for _, c := range []session.ToolCall{subCall, heredocCall} { - if got := yolo.Evaluate(context.Background(), sid, session.ModeDefault, c, nil); got.Decision.Effect != governance.Allow { - t.Fatalf("yolo child substitution must Allow; got %v (%s)", got.Decision.Effect, got.Decision.Reason) + if got := child.Evaluate(context.Background(), sid, session.ModeYolo, c, nil); got.Decision.Effect != governance.Allow { + t.Fatalf("ModeYolo child substitution must Allow; got %v (%s)", got.Decision.Effect, got.Decision.Reason) } } - notYolo := childPermPolicy(Config{LooseChildSubstitution: false}) + // ModeAuto: standard evaluator + allowAllCLIRules → substitution still floors at Ask. for _, c := range []session.ToolCall{subCall, heredocCall} { - if got := notYolo.Evaluate(context.Background(), sid, session.ModeDefault, c, nil); got.Decision.Effect != governance.Allow && got.Decision.Effect != governance.Ask { - t.Fatalf("non-yolo child substitution must not auto-allow a non-read-only inner; got %v (%s)", got.Decision.Effect, got.Decision.Reason) + if got := child.Evaluate(context.Background(), sid, session.ModeAuto, c, nil); got.Decision.Effect != governance.Allow && got.Decision.Effect != governance.Ask { + t.Fatalf("ModeAuto child substitution must not auto-allow a non-read-only inner; got %v (%s)", got.Decision.Effect, got.Decision.Reason) } } - // Be exact: the bad-inner substitution ASKS under non-yolo (the floored child path). - if got := notYolo.Evaluate(context.Background(), sid, session.ModeDefault, subCall, nil); got.Decision.Effect != governance.Ask { - t.Fatalf("non-yolo child substitution (bad inner) must Ask; got %v (%s)", got.Decision.Effect, got.Decision.Reason) + // Be exact: bad-inner substitution ASKS under ModeAuto (the floored child path). + if got := child.Evaluate(context.Background(), sid, session.ModeAuto, subCall, nil); got.Decision.Effect != governance.Ask { + t.Fatalf("ModeAuto child substitution (bad inner) must Ask; got %v (%s)", got.Decision.Effect, got.Decision.Reason) + } + + // ModeDefault: no extra rules → same Ask behavior as ModeAuto for substitutions. + if got := child.Evaluate(context.Background(), sid, session.ModeDefault, subCall, nil); got.Decision.Effect != governance.Ask { + t.Fatalf("ModeDefault child substitution (bad inner) must Ask; got %v (%s)", got.Decision.Effect, got.Decision.Reason) } - // Locked-at-every-tier: a ScopeManaged Deny still wins under yolo (deny-dominance). + // Locked-at-every-tier: a ScopeManaged Deny still wins under ModeYolo (deny-dominance). denyRules := append([]governance.Rule{ {Scope: governance.ScopeManaged, Tool: "Shell", Effect: governance.Deny, Audience: governance.AudienceSubagent}, - }, childRules(Config{LooseChildSubstitution: true})...) - denyPolicy := permpolicy.NewPolicy(denyRules, nil, childEvaluatorOptions(Config{LooseChildSubstitution: true})...) - if got := denyPolicy.Evaluate(context.Background(), sid, session.ModeDefault, subCall, nil); got.Decision.Effect != governance.Deny { - t.Fatalf("yolo must NOT override a ScopeManaged Deny; got %v (%s)", got.Decision.Effect, got.Decision.Reason) + }, childRules(Config{})...) + denyPolicy := permpolicy.NewPolicy(denyRules, nil, childEvaluatorOptions(Config{})...) + if got := denyPolicy.Evaluate(context.Background(), sid, session.ModeYolo, subCall, nil); got.Decision.Effect != governance.Deny { + t.Fatalf("ModeYolo must NOT override a ScopeManaged Deny; got %v (%s)", got.Decision.Effect, got.Decision.Reason) } - // Locked-at-every-tier: a CONFIGURED (above-floor) Ask still asks under yolo. + // Locked-at-every-tier: a CONFIGURED (above-floor) Ask still asks under ModeYolo. askRules := append([]governance.Rule{ {Scope: governance.ScopeUser, Tool: "Shell", Effect: governance.Ask, Audience: governance.AudienceSubagent}, - }, childRules(Config{LooseChildSubstitution: true})...) - askPolicy := permpolicy.NewPolicy(askRules, nil, childEvaluatorOptions(Config{LooseChildSubstitution: true})...) + }, childRules(Config{})...) + askPolicy := permpolicy.NewPolicy(askRules, nil, childEvaluatorOptions(Config{})...) plainArgs, _ := json.Marshal(map[string]string{"command": "zap -rf build"}) plainCall := session.NewToolCall("c3", "Shell", plainArgs) - if got := askPolicy.Evaluate(context.Background(), sid, session.ModeDefault, plainCall, nil); got.Decision.Effect != governance.Ask { - t.Fatalf("yolo must NOT suppress a configured Ask; got %v (%s)", got.Decision.Effect, got.Decision.Reason) + if got := askPolicy.Evaluate(context.Background(), sid, session.ModeYolo, plainCall, nil); got.Decision.Effect != governance.Ask { + t.Fatalf("ModeYolo must NOT suppress a configured Ask; got %v (%s)", got.Decision.Effect, got.Decision.Reason) } - // Locked-at-every-tier: PLAN MODE hard-denies a mutate even under yolo (plan-mode - // hard-deny runs FIRST, before any loosening). A plain mutate that yolo would - // otherwise allow must Deny under ModePlan. - if got := yolo.Evaluate(context.Background(), sid, session.ModePlan, plainCall, nil); got.Decision.Effect != governance.Deny { - t.Fatalf("plan mode must hard-deny a mutate even under yolo; got %v (%s)", got.Decision.Effect, got.Decision.Reason) + // Locked-at-every-tier: PLAN MODE hard-denies a mutate even under ModeYolo. + if got := child.Evaluate(context.Background(), sid, session.ModePlan, plainCall, nil); got.Decision.Effect != governance.Deny { + t.Fatalf("plan mode must hard-deny a mutate even under ModeYolo; got %v (%s)", got.Decision.Effect, got.Decision.Reason) } } -// TestAllowAllToolsBindsMainAndChildren is the KILL-SWITCH for the regression -// class the user explicitly wants blocked: a future knob that touches only -// mainRules (and not the shared yoloAllowAllRule) would let the main and child -// allow-all rulesets diverge again. It pins that under --yolo the SAME ScopeCLI -// empty-Tool/empty-Pattern Allow rule is present in BOTH rulesets (mainRules -// carries AudienceMain, childRules AudienceSubagent) and ABSENT from both with -// yolo off. It does NOT assert a child Ask→Allow flip for a plain mutate: the -// child floor is a blanket allow-all, so a plain child mutate is Allow either -// way (TestChildPolicyAutoApprovesNonSubstitution) — only the MAIN engine has a -// mutate-ask floor for the rule to flip, asserted here via the real evaluator. -// The substitution floor stays for children regardless (pinned by -// TestSubstitutionLooseningStaysMainOnly). Mirrors the #42 catalog-drift -// kill-switch discipline in catalog_drift_test.go. -func TestAllowAllToolsBindsMainAndChildren(t *testing.T) { - hasAllowAll := func(rules []governance.Rule, want governance.Audience) bool { - for _, r := range rules { - if r.Scope == governance.ScopeCLI && r.Tool == "" && r.Pattern == "" && r.Effect == governance.Allow && r.Audience == want { - return true - } - } - return false - } - - rulesets := []struct { - name string - rules func(Config) []governance.Rule - audience governance.Audience - }{ - {"main", mainRules, governance.AudienceMain}, - {"child", childRules, governance.AudienceSubagent}, - } - for _, rs := range rulesets { - if !hasAllowAll(rs.rules(Config{AllowAllTools: true}), rs.audience) { - t.Fatalf("%s ruleset under --yolo must contain the ScopeCLI %v allow-all rule", rs.name, rs.audience) - } - if hasAllowAll(rs.rules(Config{AllowAllTools: false}), rs.audience) { - t.Fatalf("%s ruleset without --yolo must NOT contain the ScopeCLI allow-all rule", rs.name) - } - } - - // The MAIN engine has a mutate-ask floor for the rule to flip; drive it - // through the real evaluator to prove the rule is wired, not inert. +// TestAutoAndYoloModesInjectAllowAllInEvaluate is the KILL-SWITCH: proves that +// Policy.Evaluate with ModeAuto/ModeYolo returns Allow for mutating tools on the +// MAIN policy (which has defaultRules() mutate-ask floor). ModeDefault must Ask. +func TestAutoAndYoloModesInjectAllowAllInEvaluate(t *testing.T) { const sid = session.SessionID("s1") args, _ := json.Marshal(map[string]string{"command": "zap -rf build"}) // unknown-verb mutate stand-in call := session.NewToolCall("c1", "Shell", args) - on := permpolicy.NewPolicy(mainRules(Config{AllowAllTools: true}), nil, mainEvaluatorOptions(Config{AllowAllTools: true})...) - if got := on.Evaluate(context.Background(), sid, session.ModeDefault, call, nil); got.Decision.Effect != governance.Allow { - t.Fatalf("main posture under --yolo must Allow a plain mutate; got %v (%s)", got.Decision.Effect, got.Decision.Reason) + + p := permpolicy.NewPolicy(mainRules(Config{}), nil, mainEvaluatorOptions(Config{})...) + + if got := p.Evaluate(context.Background(), sid, session.ModeAuto, call, nil); got.Decision.Effect != governance.Allow { + t.Fatalf("main ModeAuto must Allow a plain mutate; got %v (%s)", got.Decision.Effect, got.Decision.Reason) + } + if got := p.Evaluate(context.Background(), sid, session.ModeYolo, call, nil); got.Decision.Effect != governance.Allow { + t.Fatalf("main ModeYolo must Allow a plain mutate; got %v (%s)", got.Decision.Effect, got.Decision.Reason) } - off := permpolicy.NewPolicy(mainRules(Config{AllowAllTools: false}), nil, mainEvaluatorOptions(Config{AllowAllTools: false})...) - if got := off.Evaluate(context.Background(), sid, session.ModeDefault, call, nil); got.Decision.Effect != governance.Ask { - t.Fatalf("main posture without --yolo must Ask a plain mutate; got %v (%s)", got.Decision.Effect, got.Decision.Reason) + if got := p.Evaluate(context.Background(), sid, session.ModeDefault, call, nil); got.Decision.Effect != governance.Ask { + t.Fatalf("main ModeDefault must Ask a plain mutate; got %v (%s)", got.Decision.Effect, got.Decision.Reason) } } -// TestChildSubstitutionLooseningIsTierDependent codifies the posture-ladder rule that the -// CHILD substitution-floor loosening is YOLO-ONLY, while the MAIN loosening fires at -// auto+yolo. It drives the EVALUATOR-OPTION seam directly (ruleset held constant at -// the floor allow-all) so only the option set under test varies: +// TestSubstitutionLooseningIsModeDriven codifies the mode-ladder rule for +// substitution loosening: +// - Main policy in ModeAuto: substitution floors at Ask (standard evaluator + allowAll rules) +// - Main policy in ModeYolo: substitution resolves Allow (evalLoose evaluator) +// - Child policy in ModeAuto: substitution floors at Ask +// - Child policy in ModeYolo: substitution resolves Allow // -// - mainEvaluatorOptions includes WithLooseSubstitution for auto+yolo, omits it for -// strict/trusted (a substitution then ALLOWS vs ASKS). -// - childEvaluatorOptions includes WithLooseSubstitution ONLY for yolo -// (LooseChildSubstitution); it OMITS it for strict/trusted/auto (the child -// substitution floor stands — the prompt-injection defense is ON). -// -// A regression that loosened children at auto (or stopped loosening them at yolo) -// flips one of these and fails here. Uses the innocuous non-read-only inner `$(zap)`. -func TestChildSubstitutionLooseningIsTierDependent(t *testing.T) { +// A regression that loosened at ModeAuto (or stopped loosening at ModeYolo) flips +// one of these and fails here. Uses the innocuous non-read-only inner `$(zap)`. +func TestSubstitutionLooseningIsModeDriven(t *testing.T) { const sid = session.SessionID("s1") args, _ := json.Marshal(map[string]string{"command": "cat $(zap)"}) call := session.NewToolCall("c1", "Shell", args) - eval := func(opts ...governance.EvaluatorOption) governance.Effect { - p := permpolicy.NewPolicy(permpolicy.AllowAllFloorRules(), nil, opts...) - return p.Evaluate(context.Background(), sid, session.ModeDefault, call, nil).Decision.Effect - } + mainPolicy := permpolicy.NewPolicy(mainRules(Config{}), nil, mainEvaluatorOptions(Config{})...) + childPolicy := childPermPolicy(Config{}) - // applyPosture maps each tier to (AllowAllTools, LooseChildSubstitution); we drive - // the option builders with the derived cfg so the test exercises the SAME mapping - // composition uses. type want struct { main governance.Effect child governance.Effect } cases := []struct { - posture Posture - want want + mode session.PermissionMode + want want }{ - {PostureStrict, want{governance.Ask, governance.Ask}}, - {PostureTrusted, want{governance.Ask, governance.Ask}}, - {PostureAuto, want{governance.Allow, governance.Ask}}, // child defense ON - {PostureYolo, want{governance.Allow, governance.Allow}}, // child defense OFF + {session.ModeDefault, want{governance.Ask, governance.Ask}}, + {session.ModeAuto, want{governance.Ask, governance.Ask}}, // auto: standard evaluator, substitution floor stands + {session.ModeYolo, want{governance.Allow, governance.Allow}}, // yolo: evalLoose, substitution allowed } for _, tc := range cases { - cfg := applyPosture(Config{Posture: tc.posture}) - if got := eval(mainEvaluatorOptions(cfg)...); got != tc.want.main { - t.Fatalf("%s main substitution: got %v, want %v", tc.posture, got, tc.want.main) + if got := mainPolicy.Evaluate(context.Background(), sid, tc.mode, call, nil).Decision.Effect; got != tc.want.main { + t.Errorf("main %s substitution: got %v, want %v", tc.mode, got, tc.want.main) } - if got := eval(childEvaluatorOptions(cfg)...); got != tc.want.child { - t.Fatalf("%s child substitution: got %v, want %v", tc.posture, got, tc.want.child) + if got := childPolicy.Evaluate(context.Background(), sid, tc.mode, call, nil).Decision.Effect; got != tc.want.child { + t.Errorf("child %s substitution: got %v, want %v", tc.mode, got, tc.want.child) } } } diff --git a/internal/app/authority_vertical_test.go b/internal/app/authority_vertical_test.go index 6ece817145..cc96a1c531 100644 --- a/internal/app/authority_vertical_test.go +++ b/internal/app/authority_vertical_test.go @@ -46,12 +46,13 @@ func TestAuthorityEvaluator_VerticalSlice(t *testing.T) { mockllm.TextTurn("parent complete"), ) cfg := Config{ - Workspace: workspace, - StoreDir: storeDir, - AgentsDirs: []string{agentsDir}, - MockProvider: provider, - NoSoul: true, - AllowAllTools: true, + Workspace: workspace, + StoreDir: storeDir, + AgentsDirs: []string{agentsDir}, + MockProvider: provider, + NoSoul: true, + AllowAllTools: true, + GuardrailsDisabled: true, } built, err := buildIsolated(t, ctx, cfg) if err != nil { @@ -59,7 +60,7 @@ func TestAuthorityEvaluator_VerticalSlice(t *testing.T) { } defer built.Close() - parent, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + parent, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { built.Close() t.Fatalf("CreateSession: %v", err) @@ -197,6 +198,7 @@ forbid(principal, action, resource) when { resource.path like "` + filepath.ToSl MockProvider: provider, NoSoul: true, AllowAllTools: true, + GuardrailsDisabled: true, AuthorityEvaluator: "cedar", CedarAuthorityPolicy: policyPath, }) @@ -205,7 +207,7 @@ forbid(principal, action, resource) when { resource.path like "` + filepath.ToSl } defer built.Close() - parent, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + parent, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -263,6 +265,7 @@ forbid(principal, action, resource) when { resource.path like "` + filepath.ToSl MockProvider: mockllm.New(mockllm.ToolCallTurn(session.NewToolCall("read", "Read", []byte(`{"path":"review/blocked.go"}`))), mockllm.TextTurn("complete")), NoSoul: true, AllowAllTools: true, + GuardrailsDisabled: true, AuthorityEvaluator: "cedar", CedarAuthorityPolicy: policyPath, }) @@ -271,7 +274,7 @@ forbid(principal, action, resource) when { resource.path like "` + filepath.ToSl } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -305,11 +308,12 @@ func TestAuthorityEvaluator_OwnerlessCompositionUsesLocalEvaluator(t *testing.T) t.Fatalf("write workspace file: %v", err) } built, err := buildIsolated(t, context.Background(), Config{ - Workspace: workspace, - StoreDir: filepath.Join(t.TempDir(), "sessions"), - MockProvider: mockllm.New(mockllm.ToolCallTurn(session.NewToolCall("read", "Read", []byte(`{"path":"README.md"}`))), mockllm.TextTurn("done")), - NoSoul: true, - AllowAllTools: true, + Workspace: workspace, + StoreDir: filepath.Join(t.TempDir(), "sessions"), + MockProvider: mockllm.New(mockllm.ToolCallTurn(session.NewToolCall("read", "Read", []byte(`{"path":"README.md"}`))), mockllm.TextTurn("done")), + NoSoul: true, + AllowAllTools: true, + GuardrailsDisabled: true, }) if err != nil { t.Fatalf("Build: %v", err) @@ -360,6 +364,7 @@ func TestAuthorityEvaluator_OwnerlessCedarSessionFailsClosed(t *testing.T) { MockProvider: mockllm.New(mockllm.ToolCallTurn(session.NewToolCall("read", "Read", []byte(`{"path":"README.md"}`))), mockllm.TextTurn("done")), NoSoul: true, AllowAllTools: true, + GuardrailsDisabled: true, AuthorityEvaluator: "cedar", CedarAuthorityPolicy: policyPath, }) @@ -411,18 +416,19 @@ func assertAuthorityVerticalMetaTarget(ctx context.Context, t *testing.T, worksp storeDir := t.TempDir() cfg := Config{ - Workspace: workspace, - StoreDir: storeDir, - MCPServers: []mcp.ServerConfig{{Name: "slack", URL: mcpURL}}, - MockProvider: mockllm.New(mockllm.TextTurn("unused")), - NoSoul: true, - AllowAllTools: true, + Workspace: workspace, + StoreDir: storeDir, + MCPServers: []mcp.ServerConfig{{Name: "slack", URL: mcpURL}}, + MockProvider: mockllm.New(mockllm.TextTurn("unused")), + NoSoul: true, + AllowAllTools: true, + GuardrailsDisabled: true, } built, err := buildIsolated(t, ctx, cfg) if err != nil { t.Fatalf("Build meta setup: %v", err) } - parent, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + parent, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { built.Close() t.Fatalf("CreateSession meta setup: %v", err) diff --git a/internal/app/build.go b/internal/app/build.go index 117f2948a4..0cee2a2eff 100644 --- a/internal/app/build.go +++ b/internal/app/build.go @@ -880,7 +880,11 @@ type Config struct { guardrailConfigured bool guardrailDetails *server.ReviewDetailRegistry guardrailHealth *guardrailRouteHealth - planApprovals *planApprovalReceipts + // guardrailSessionMode is the session mode an engine's guardrail rules are + // compiled for (set per session by the session-engine factory). Empty means + // the shared engine, whose rules follow the process posture. + guardrailSessionMode session.PermissionMode + planApprovals *planApprovalReceipts // Slash commands: directory of .md templates; EnableCommands turns on the // default directories when CommandsDir is empty. @@ -1041,16 +1045,11 @@ type Config struct { // mecatui defaults false. Headless bool - // AllowAllTools, when set, injects a single ScopeCLI allow-all rule into BOTH - // the MAIN engine's static ruleset (mainRules, AudienceMain) AND the - // child/member ruleset (childRules, AudienceSubagent) via the shared - // yoloAllowAllRule. The RULE binds main AND children — it loosens the built-in - // mutate-ask floor for both. The built-in substitution Ask floor is - // additionally loosened ONLY for the main engine (mainEvaluatorOptions' - // WithLooseSubstitution; childEvaluatorOptions deliberately omits it), so a - // child's $()/backtick command still resolves through the child-ask model. A - // Deny in any scope and any CONFIGURED Ask still win (see - // docs/architecture/governance.md). + // AllowAllTools, when set by the cmd layer (--yolo flag), is the input that + // resolvePosture uses as the `yolo` alias to MAX-fold the posture to PostureYolo. + // It is NOT used for rule injection (that is now mode-driven: Policy.Evaluate + // injects allowAllCLIRules for ModeAuto/ModeYolo at call time). Setting it + // directly in Config maps to ModeYolo behavior via applyPosture's fallback. AllowAllTools bool // Posture is the graduated operator trust/automation tier (strict < trusted < @@ -1068,19 +1067,44 @@ type Config struct { // choose is a leak with no consenting author. Set via mecated --deployment-id, // which bounds and validates it before it reaches here. DeploymentID string - // LooseChildSubstitution loosens the built-in substitution Ask floor for - // CHILD/subagent/branch engines (childEvaluatorOptions adds WithLooseSubstitution - // when set), turning OFF the child prompt-injection defense so a $()/backtick/ - // heredoc command auto-runs in a subagent. Derived by applyPosture: TRUE only - // under PostureYolo. strict/trusted/auto leave it false (the child substitution - // floor stands; a child's substitution still resolves through the child-ask - // model). NEVER set directly — it is a posture-derived knob. + // LooseChildSubstitution is now mode-driven: a child session in ModeYolo uses + // Policy's evalLoose evaluator (built with WithLooseSubstitution=true) rather + // than a startup-time evaluator option. This field is no longer set by + // applyPosture and is no longer consulted by childEvaluatorOptions. + // It is kept for backward compat with existing test fixtures; production code + // should use ModeYolo instead. LooseChildSubstitution bool // PostureFlagSet records whether the operator passed an explicit --posture flag. // When true, foldOperatorPosture leaves the operator-YAML posture: value alone // (CLI out-ranks YAML) and resolvePosture WARNs if an alias raised above the // explicit value. Set by the cmd mains alongside Posture. PostureFlagSet bool + // PermissionMode is the raw --permission-mode token, meaningful only + // when PermissionModeFlagSet. foldPermissionMode resolves it (or the + // operator-tier permissionMode: key) into Posture + DefaultSessionMode; an + // unknown token is a Build error naming the valid set. + PermissionMode string + // PermissionModeFlagSet records an explicit --permission-mode. It out-ranks + // the operator-tier permissionMode: and the deprecated posture: keys. + PermissionModeFlagSet bool + // DefaultSessionMode is the session half of a permission-mode token: the mode + // a CreateSession request that leaves mode unspecified starts in + // (server.Config.DefaultMode). Empty means session.ModeDefault. It is only a + // starting point; a session may later change its own mode. + DefaultSessionMode session.PermissionMode + // MaxAllowedMode is the server ceiling: the most-permissive mode a session on + // this server is allowed to enter. SetMode rejects a mode above this ceiling. + // It is set by foldPermissionMode from the permission-mode token. Empty means + // no ceiling (all modes reachable). See internal/adapter/server/service.go SetMode. + MaxAllowedMode session.PermissionMode + // permissionModeName is the token the operator chose, for messages. Set by + // foldPermissionMode; empty when only the deprecated surface was used. + permissionModeName string + // askReviewerDefaultOn is set by resolveAskReviewerDefault when the headless + // allow-all default engages the subagent ask reviewer. + askReviewerDefaultOn bool + // askReviewerOptOut records an explicit --subagent-ask-reviewer off. + askReviewerOptOut bool // ReasoningEffort is the OPERATOR-TIER reasoning-effort default: the // neutral vocabulary "" / "auto" (unset — provider default) / "low" / "medium" / // "high" / "xhigh" / "max". It is folded from the operator-YAML reasoning-effort: @@ -1674,15 +1698,27 @@ func Build(ctx context.Context, cfg Config) (*Built, error) { // scalar (read at construction from the user-global/CLI tiers) is taken here. The // REAL cfg.permResolver is built below with the final (posture-raised) // TrustProject. Aliases (--yolo ⇒ yolo, --trust-project ⇒ ≥trusted) raise the tier - // MAX-fold; applyPosture then derives AllowAllTools / LooseChildSubstitution / - // the TrustProject floor. + // MAX-fold; applyPosture then derives the TrustProject floor. + // AllowAllTools and LooseChildSubstitution are no longer derived by applyPosture; + // allow-all and loose-substitution behavior is now mode-driven (Policy.Evaluate + // injects allowAllCLIRules for ModeAuto/ModeYolo at evaluation time). cfg.permResolver = buildPermResolver(cfg) - cfg = foldOperatorPosture(cfg) + var modeErr error + if cfg, modeErr = foldPermissionMode(cfg); modeErr != nil { + return nil, modeErr + } cfg = foldOperatorReasoningEffort(cfg) cfg = foldOperatorPlanModeAutoApprove(cfg) cfg = foldOperatorSteer(cfg) cfg.Posture = resolvePosture(cfg, postureNoCeiling) cfg = applyPosture(cfg) + // Deprecated surface (--posture, --yolo, --trust-project, the posture: key): + // derive the session mode and ceiling from the posture AFTER the full alias + // fold, so a combination such as --posture strict --yolo lands on the same + // pair as the raised tier alone. A permission-mode token already set both. + if cfg.permissionModeName == "" { + cfg.DefaultSessionMode, cfg.MaxAllowedMode = postureToSessionModes(cfg.Posture) + } // AUTHORITATIVE root/no-sandbox refusal: applied HERE, after the full posture fold, // so an allow-all tier set ONLY via the operator-global settings.yaml `posture:` // key (which the cmd-layer CLI-only pre-check never sees) cannot escape the refusal @@ -1701,6 +1737,12 @@ func Build(ctx context.Context, cfg Config) (*Built, error) { cfg.TrustProject = trust.Trusted narratePosture(cfg.diag(), cfg.Posture, cfg.TrustProject) narrateTrust(cfg.diag(), trust, cfg.Workspace) + // Headless admission gate: after the trust fold, so every + // legitimate trust source satisfies it. + if err := headlessTrustRefusal(cfg); err != nil { + return nil, err + } + narrateWithheldTrust(cfg) // File-based permission config (issues #13/#32): construct the resolver // EXACTLY ONCE here, right after the trust fold (it consumes the effective @@ -2039,6 +2081,7 @@ func Build(ctx context.Context, cfg Config) (*Built, error) { cfg.SubagentModel = subagentModel // SubagentAskReviewerModel (issue #31): the same fail-fast normalization // posture as SubagentModel — validate the alias once here; empty = reviewer off. + cfg.askReviewerOptOut = isAskReviewerOptOut(cfg.SubagentAskReviewerModel) reviewerModel, err := normalizeAskReviewerModel(cfg) if err != nil { return nil, err @@ -2061,6 +2104,17 @@ func Build(ctx context.Context, cfg Config) (*Built, error) { cfg.guardrailModel = guardrailModel cfg.guardrailSource = guardrailSrc cfg.guardrailConfigured = guardrailConfigured + // Allow-all admission gate: the checker truth is the SAME the + // posture line and the live checker use (the binding, else the resolver). + checkerConfigured := guardrailConfigured + if !checkerConfigured { + _, _, checkerConfigured = resolveGuardrailsCheckerModel(cfg) + } + if err := checkerRefusal(cfg, checkerConfigured); err != nil { + return nil, err + } + cfg = resolveAskReviewerDefault(cfg) + narratePermissionMode(cfg, checkerConfigured) // Emit the build-once composition facts (token counter / compaction strategy / // slash commands) EXACTLY ONCE here, through the injected Diagnostics — keyed to // the resolved MAIN model. The per-derivation builders do not log these (they @@ -2406,8 +2460,14 @@ func Build(ctx context.Context, cfg Config) (*Built, error) { mcpStatus = assets.mcpReconciler.statusSnapshot } svcCfg := server.Config{ - BuildID: buildinfo.BuildID, - ServerImplementation: cfg.ServerImplementation, + // The session half of the permission-mode token: the mode a + // CreateSession that leaves mode unspecified starts in. Empty keeps the + // server's own ModeDefault. + DefaultMode: cfg.DefaultSessionMode, + MaxAllowedMode: cfg.MaxAllowedMode, + EnforcingCheckerConfigured: autoCheckerEnforcing(cfg, checkerConfigured), + BuildID: buildinfo.BuildID, + ServerImplementation: cfg.ServerImplementation, // GetServerInfo looks up only a caller-selected, already-known provider // in this immutable composition registry. It intentionally does not // inspect session selectors, discover providers, or re-read configuration. @@ -2535,6 +2595,12 @@ func Build(ctx context.Context, cfg Config) (*Built, error) { // ServerCapabilities echo as CHROME (a client renders a "⚠ auto"/"⚠ yolo" badge). // NOT session state — see server.Config.Posture. Posture: cfg.Posture.String(), + // ProjectTrusted: the effective trust fold for the launch workspace (flag, + // declared, remembered, or the interactive posture floor), CHROME ONLY. + ProjectTrusted: &cfg.TrustProject, + // SubagentAskReviewer: the reviewer state from the same reviewerStatus the + // startup line narrates, so the echo and the diagnostics cannot drift. + SubagentAskReviewer: subagentAskReviewerCapability(cfg), // DeploymentID: the operator-set, opaque deployment label surfaced on // GetCompatibilityInfo. It is passed through VERBATIM and is never // derived here from hostname, pod name, or environment — an inferred label @@ -3370,6 +3436,7 @@ func sessionEngineFactoryWithTools( ) server.SessionEngineWithToolsFactory { build := func(ctx context.Context, sel server.ProviderSelector, specs []mcp.ServerConfig, profile server.SessionProfile, workspace string, mode session.PermissionMode, sessionTools []tool.Tool, sessionToolKeys []string) (server.SessionEngineResult, error) { cfg := cfg // Permission resolver overrides belong to this invocation, not the shared closure. + cfg.guardrailSessionMode = mode runtimeAssets := mcpCatalogAssets(ctx, assets) runtimeRevision := mcpRuntimeRevision(ctx) // Pin the CHILD permission resolver to THIS session's base root (issue @@ -4223,7 +4290,7 @@ func escapePolicyForConfig(cfg Config, reg *providerRegistry, provider port.LLMP // host paths and could relax the inner decision on that false basis. return inner } - return newEscapePolicy(inner, cfg.Posture, + return newEscapePolicy(inner, withEscapeGuardrailRoute(buildGuardrailsEscapeChecker(cfg, reg, provider))) } @@ -4691,7 +4758,7 @@ func resolveRulesSeam(ctx context.Context, cfg Config) prompt.RulesSource { // same gate as agents/skills): untrusted, or the ingestion grant withheld. The // user-tier lanes stay active regardless. if cfg.Workspace != "" && !projectIngestionAdmitted(cfg) { - cfg.diag().Log(ctx, port.LevelWarn, "rules: project-tier rules WITHHELD (untrusted workspace or project ingestion not granted); user-tier rules stay active. Pass --trust-project (on a headless root) or run --posture auto on an interactive root to admit its project rules", + cfg.diag().Log(ctx, port.LevelWarn, "rules: project-tier rules WITHHELD (untrusted workspace or project ingestion not granted); user-tier rules stay active. Pass --trust-project (on a headless root) or run --permission-mode auto on an interactive root to admit its project rules", "workspace", cfg.Workspace, "dirs", ".mecatl/rules,.claude/rules") } sources := rules.ResolveSources(rules.ResolveOptions{ @@ -5073,7 +5140,7 @@ func slashCommandDecision(cfg Config) diagFact { if cfg.Workspace != "" && !projectIngestionAdmitted(cfg) { return diagFact{ level: port.LevelWarn, - msg: "slash commands: project-tier command dirs WITHHELD (untrusted workspace or project ingestion not granted); raw text passes through. Pass --trust-project (on a headless root) or run --posture auto on an interactive root, or pass --commands-dir to enable project slash commands", + msg: "slash commands: project-tier command dirs WITHHELD (untrusted workspace or project ingestion not granted); raw text passes through. Pass --trust-project (on a headless root) or run --permission-mode auto on an interactive root, or pass --commands-dir to enable project slash commands", args: []any{"dirs", ".mecatl/commands,.claude/commands"}, } } @@ -5233,8 +5300,8 @@ func normalizeSubagentModel(cfg Config) (string, error) { // the build-once ACTIVE fact. func normalizeAskReviewerModel(cfg Config) (string, error) { sel := strings.TrimSpace(cfg.SubagentAskReviewerModel) - if sel == "" { - return sel, nil + if sel == "" || isAskReviewerOptOut(sel) { + return "", nil } // FAIL-FAST validation runs FIRST, regardless of reachability: the alias // lookup is free and catches a typo'd --subagent-ask-reviewer at config time @@ -7455,10 +7522,12 @@ func buildAskAdjudicator(cfg Config, provReg *providerRegistry, provider port.LL // reviewer is not configured. func askAdjudicatorDeps(cfg Config, provReg *providerRegistry, provider port.LLMProvider, parentProviderID, parentModel string) (agent.Deps, bool) { sel := strings.TrimSpace(cfg.SubagentAskReviewerModel) - if sel == "" { + if sel == "" && !cfg.askReviewerDefaultOn { // The --subagent-ask-reviewer flag STAYS the enable gate: a slot alone does // NOT turn the reviewer on (a slot only chooses the model for a reviewer the - // operator already enabled). Empty flag ⇒ reviewer off, byte-identical. + // operator already enabled). Empty flag ⇒ reviewer off, byte-identical — + // except the headless allow-all default (askReviewerDefaultOn), + // which resolves through the slot, then the parent model, below. return agent.Deps{}, false } // ASK-REVIEWER SLOT: a configured `ask-reviewer` slot @@ -7468,11 +7537,13 @@ func askAdjudicatorDeps(cfg Config, provReg *providerRegistry, provider port.LLM reviewerProvider, reviewerProviderID := provider, parentProviderID if slotProvider, slotProviderID, slotModel, ok := resolveAuxiliarySlotTarget(cfg, provReg, slotAskReviewer, provider, parentProviderID, parentModel); ok { reviewerProvider, reviewerProviderID, model = slotProvider, slotProviderID, slotModel - } else if target, err := resolveModelTarget(cfg, parentProviderID, "", sel); err == nil { - model = target.Model - if target.ProviderID != parentProviderID && provReg != nil { - if entry, found := provReg.Lookup(target.ProviderID); found { - reviewerProvider, reviewerProviderID = entry.provider, target.ProviderID + } else if sel != "" { + if target, err := resolveModelTarget(cfg, parentProviderID, "", sel); err == nil { + model = target.Model + if target.ProviderID != parentProviderID && provReg != nil { + if entry, found := provReg.Lookup(target.ProviderID); found { + reviewerProvider, reviewerProviderID = entry.provider, target.ProviderID + } } } } @@ -9088,21 +9159,26 @@ const ( // no path to trust, which is false. // The "Three SEPARATE safety axes" substring is a stable test key. selfKnowledgePostureAxes = "Three SEPARATE safety axes, never conflated. (1) SESSION PERMISSION MODE: " + - "exactly three, one per session, in your as `permission-mode`. `default` resolves each call " + + "exactly five, one per session, in your as `permission-mode`. `default` resolves each call " + "deny→ask→allow; `plan` denies mutations and expects a plan presented for approval; `acceptEdits` " + "(accept-edits in mecatui) auto-allows Edit and Write only, leaving Shell and every other mutating " + - "tool on the normal rules. mecatui shows the mode in its header, takes `--mode` at launch, and " + - "cycles default → plan → accept-edits on shift+tab. (2) OPERATOR POSTURE: deployment-wide via " + - "--posture, rising strict < trusted < auto < yolo. auto and yolo widen what a call is authorized " + - "to do, not only how much runs without asking: both set an allow-all rule for every tool call. " + - "Posture raises project trust on INTERACTIVE roots only: a HEADLESS deployment does NOT trust the " + + "tool on the normal rules; `auto` and `yolo` widen what a call is authorized to do, not only how " + + "much runs without asking: both set an allow-all rule for every tool call. `auto` requires an " + + "enforcing guardrails checker; `yolo` runs without a preventive checker. The operator's " + + "--permission-mode token (plan, default, accept-edits, auto, yolo) sets the mode new sessions start " + + "in and the most permissive mode a session may switch to; switching applies the chosen mode's full " + + "behaviour, so leaving yolo for default restores ordinary approvals. mecatui shows the mode in its " + + "header, takes --permission-mode at launch, and cycles the modes on shift+tab. (2) WORKSPACE TRUST: " + + "a separate yes/no decision about whether the workspace's project instructions and configuration " + + "are adopted; an untrusted workspace can still be read and searched as data. An explicit " + + "--trust-project flag, a declared trusted workspace, or remembered trust grant it on any root. A " + + "--permission-mode token never grants trust; only the deprecated --posture ladder (strict < trusted " + + "< auto < yolo) still raises it, on INTERACTIVE roots only: a HEADLESS deployment does NOT trust the " + "checkout by posture alone. Project ingestion and the read-only child shell follow from project " + - "trust however it was granted — an explicit --trust-project flag, a declared trusted workspace, or " + - "remembered trust grant it on any root, while posture alone grants it only on an interactive one. " + - "(3) GUARDRAILS: an optional model-backed checker over selected " + - "tool arguments and results, independent of permissions and inert until an operator configures a " + - "checker model. Under every mode and posture a matching deny wins and a configured ask is never " + - "suppressed." + "trust. (3) GUARDRAILS: an optional model-backed checker over selected tool arguments and results, " + + "independent of permissions and inert until an operator configures a checker model; it enforces in " + + "every mode except yolo, where it is advisory. Under every mode a matching deny wins and a configured " + + "ask is never suppressed." // selfKnowledgePostureMap is the doc-tree shape, so a lookup lands on the right // page instead of the site root. Every path here is pinned by the drift gate. @@ -9719,57 +9795,20 @@ func defaultRules() []governance.Rule { } } -// yoloAllowAllRule returns the single ScopeCLI allow-all rule the --yolo posture -// (cfg.AllowAllTools) injects, pinned to the given audience. It is the ONE -// definition shared by mainRules (AudienceMain) and childRules (AudienceSubagent) -// so the main and child rulesets cannot drift: under --yolo the SAME blanket -// allow-all rule binds BOTH the main engine and its children, loosening the -// built-in mutate-ask floor for both. A Deny in any scope and any CONFIGURED -// Ask still win (deny-dominance + the configured-ask floor are unaffected). The -// substitution-floor LOOSENING (WithLooseSubstitution) is separate from this rule: it -// rides mainEvaluatorOptions always, and childEvaluatorOptions only under posture yolo -// (the tier-dependent child loosening) — see internal/app/posture.go and -// docs/architecture/governance.md. -func yoloAllowAllRule(audience governance.Audience) governance.Rule { - return governance.Rule{Scope: governance.ScopeCLI, Effect: governance.Allow, Audience: audience} -} - -// mainRules returns the main engine's static ruleset: the built-in floor, plus -// — when cfg.AllowAllTools — a single ScopeCLI allow-all rule (AudienceMain) -// that loosens that floor (a Deny in any scope and any configured Ask still -// win). The SAME rule (with AudienceSubagent) is injected into childRules under -// allow-all (posture auto/yolo), so the allow-all RULE binds both main and children; -// the substitution-floor LOOSENING is separate and tier-dependent (mainEvaluatorOptions -// always; childEvaluatorOptions only under posture yolo). -func mainRules(cfg Config) []governance.Rule { - base := defaultRules() - if cfg.AllowAllTools { - base = append([]governance.Rule{yoloAllowAllRule(governance.AudienceMain)}, base...) - } - return base +// mainRules returns the main engine's static ruleset. The built-in mutate-ask +// floor (defaultRules) applies to all modes; auto/yolo allow-all is injected +// at evaluation time by Policy.Evaluate based on the session's current mode, +// not baked in here at startup. This lets mode switches actually change behavior. +func mainRules(_ Config) []governance.Rule { + return defaultRules() } // mainEvaluatorOptions returns the governance.Evaluator construction options for the -// MAIN engine's policy. It ALWAYS pins the audience to AudienceMain (issue #32): -// without it, subagent-tagged resolver extras (the permconfig `subagent:` block) -// would bind the main engine too — the audience pin is what keeps a child-scoped -// rule from leaking into the interactive engine. When cfg.AllowAllTools (posture -// auto OR yolo) it additionally loosens the built-in substitution Ask floor -// (WithLooseSubstitution) — consistent with the mutate-ask floor the ScopeCLI -// allow-all rule already loosens, so a substitution command does not prompt on the -// MAIN engine. A configured Deny/Ask in any scope still wins (deny-dominance and the -// configured-ask floor are unaffected). The CHILD substitution loosening is -// TIER-DEPENDENT, not main-only: childEvaluatorOptions(cfg) ALSO adds -// WithLooseSubstitution under posture yolo (cfg.LooseChildSubstitution) — so at auto -// the main loosens but children still resolve their substitution through the child-ask -// model, while at yolo children loosen too (child prompt-injection defense OFF). See -// childEvaluatorOptions and internal/app/posture.go. -func mainEvaluatorOptions(cfg Config) []governance.EvaluatorOption { - opts := []governance.EvaluatorOption{governance.WithAudience(governance.AudienceMain)} - if cfg.AllowAllTools { - opts = append(opts, governance.WithLooseSubstitution(true)) - } - return opts +// main engine's policy. It pins the audience to AudienceMain (issue #32). +// Loose-substitution is now mode-driven (ModeYolo via Policy's evalLoose evaluator), +// not baked in at startup. +func mainEvaluatorOptions(_ Config) []governance.EvaluatorOption { + return []governance.EvaluatorOption{governance.WithAudience(governance.AudienceMain)} } // buildPermResolver constructs the ONE file-based permission-config resolver @@ -9846,59 +9885,21 @@ func (p pinnedResolver) Resolve(ctx context.Context, _ tool.WorkspaceReader) []g } // childRules returns the child/member engine's static ruleset: the canonical -// allow-all-at-the-floor (permpolicy.AllowAllFloorRules — the ONE definition, -// shared with every "default child posture" test fixture so they cannot -// drift). Scoping the allow-all to the BUILT-IN floor rather than the zero -// Scope (ScopeManaged) is behaviour-neutral with no config (allow-all still matches everything → Allow, and the -// floor-exception in resolveSimple only keys on the ASK side's scope) — pinned -// by TestChildRulesFloorScopeNeutral — but it is load-bearing for the issue-#32 -// decision bits: the floor allow-all must NOT register as a CONFIGURED Allow -// (scope above the floor), or every substitution-floored child ask would -// qualify for FlooredConfiguredAllow. Only a real config rule (the permconfig -// `subagent:` block) may carry an above-floor scope. -// -// Under --yolo (cfg.AllowAllTools) a ScopeCLI/AudienceSubagent allow-all rule -// (the yoloAllowAllRule sibling of mainRules') is PREPENDED to the floor. This -// is a SYMMETRY / anti-drift change, NOT a behavioural fix: the child floor is -// ALREADY a blanket allow-all, so a plain (non-substitution) mutate resolves -// Allow for a child with or without --yolo — children have no mutate-ask floor -// to loosen (unlike the main engine's defaultRules() mutate-ask). The shared -// yoloAllowAllRule keeps the main and child rulesets from diverging and gives -// the TestAllowAllToolsBindsMainAndChildren kill-switch a real structure to pin; -// it becomes load-bearing only if the child floor ever tightens to carry a real -// mutate-Ask. It cannot suppress a configured `subagent:` Ask, and it does NOT -// touch the substitution floor: WithLooseSubstitution is deliberately STILL -// absent from childEvaluatorOptions, so a child's substitution ($()/backtick/ -// heredoc) command still resolves through the child-ask model (flooredAllowSafe: -// the inner must independently classify read-only) — the substitution-floor -// loosening stays main-only. -func childRules(cfg Config) []governance.Rule { - base := permpolicy.AllowAllFloorRules() - if cfg.AllowAllTools { - base = append([]governance.Rule{yoloAllowAllRule(governance.AudienceSubagent)}, base...) - } - return base +// allow-all floor (permpolicy.AllowAllFloorRules). Children inherit the +// session's mode and mode-driven policy (allowAllCLIRules for auto/yolo) is +// injected at evaluation time by Policy.Evaluate. +func childRules(_ Config) []governance.Rule { + return permpolicy.AllowAllFloorRules() } // childEvaluatorOptions returns the governance.Evaluator options for a // child/member policy: the AudienceSubagent pin (issue #32), so top-level // (AudienceMain) config allow/ask never bind a child while `subagent:`-block -// rules and AudienceAll denies do. It is TIER-AWARE (posture ladder): under -// PostureYolo (cfg.LooseChildSubstitution) it ALSO loosens the built-in -// substitution Ask floor for children (WithLooseSubstitution) — the deliberate -// child prompt-injection-defense-OFF behaviour yolo carries, so a child's -// $()/backtick/heredoc command auto-runs. At strict/trusted/auto it deliberately -// OMITS WithLooseSubstitution: even under --yolo's allow-all RULE (auto/yolo bind -// children via childRules' AudienceSubagent variant), a child's substitution -// floor still resolves through the child-ask model (floored-configured-allow / -// isolation / surface / deny) UNLESS the operator opted into yolo. A configured -// Deny/Ask in any scope still wins regardless. See internal/app/posture.go. -func childEvaluatorOptions(cfg Config) []governance.EvaluatorOption { - opts := []governance.EvaluatorOption{governance.WithAudience(governance.AudienceSubagent)} - if cfg.LooseChildSubstitution { - opts = append(opts, governance.WithLooseSubstitution(true)) - } - return opts +// rules and AudienceAll denies do. Loose-substitution is now mode-driven: a +// child session in ModeYolo uses Policy's evalLoose evaluator, not a +// startup-time evaluator option. +func childEvaluatorOptions(_ Config) []governance.EvaluatorOption { + return []governance.EvaluatorOption{governance.WithAudience(governance.AudienceSubagent)} } // childPermPolicy builds the shared child/member permission policy (issue #32): diff --git a/internal/app/child_mode_guardrail_test.go b/internal/app/child_mode_guardrail_test.go new file mode 100644 index 0000000000..c19e58e21b --- /dev/null +++ b/internal/app/child_mode_guardrail_test.go @@ -0,0 +1,110 @@ +package app + +import ( + "encoding/json" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/stacklok/mecatl/engine/adapter/mockllm" + "github.com/stacklok/mecatl/engine/session" +) + +// TestYoloParentNarrowerTeamMemberKeepsEnforcingChecker drives the real +// composition: a yolo session forms a Team whose member definition pins +// permissionMode: default, and the checker rejects the member's Read. The +// shared reviewer must enforce for the narrower member (action blocked, unsafe +// inbound content withheld) while an inheriting member stays advisory, like +// its yolo parent. +func TestYoloParentNarrowerTeamMemberKeepsEnforcingChecker(t *testing.T) { + const canary = "MEMBER-READ-CANARY" + const prohibited = `{"assessment":"prohibited","concerns":[{"ref":"C1","category":"data_exfiltration","rationale":"member read is unsafe","source_ref":"call"}],"evidence":[],"missing_evidence":[]}` + // wantReason pins how the narrower member was stopped. For the action + // review it names a PARSED prohibited verdict; a malformed verdict fails + // closed with "could not approve this action" instead. + for _, tc := range []struct { + name, phase, agent string + wantLeak bool + wantReason string + }{ + {"action-narrow", "pre", "narrow", false, "contextual guardrail requires human approval for this action"}, + {"action-inherit", "pre", "", true, ""}, + {"inbound-narrow", "post", "narrow", false, "tool result withheld by contextual guardrail"}, + {"inbound-inherit", "post", "", true, ""}, + } { + t.Run(tc.name, func(t *testing.T) { + workspace := t.TempDir() + if err := os.WriteFile(filepath.Join(workspace, "readme.txt"), []byte(canary+"\n"), 0o644); err != nil { + t.Fatal(err) + } + agentsDir := t.TempDir() + def := "---\nname: narrow\ndescription: narrow reader\npermissionMode: default\n---\nRead files when asked." + if err := os.WriteFile(filepath.Join(agentsDir, "narrow.md"), []byte(def), 0o644); err != nil { + t.Fatal(err) + } + member := `{"name":"lead","role":"read readme.txt"}` + if tc.agent != "" { + member = `{"name":"lead","role":"read readme.txt","agent":"` + tc.agent + `"}` + } + // One provider serves parent, member, and checker in call order. + provider := mockllm.New( + mockllm.ToolCallTurn(session.NewToolCall("team", "Team", json.RawMessage(`{"goal":"inspect","members":[`+member+`]}`))), + mockllm.ToolCallTurn(session.NewToolCall("member-read", "Read", json.RawMessage(`{"path":"readme.txt"}`))), + mockllm.TextTurn(prohibited), + mockllm.TextTurn("member done"), + mockllm.TextTurn("team report"), + mockllm.TextTurn("parent done"), + ) + built, err := buildIsolated(t, t.Context(), Config{ + Workspace: workspace, NoSoul: true, UseMock: true, MockProvider: provider, + Model: "main-model", GuardrailsModel: "guardrail-model", EnableTeams: true, + AgentsDirs: []string{agentsDir}, PermissionMode: "yolo", PermissionModeFlagSet: true, + GuardrailsRules: []GuardrailRule{{Match: "Read", Phases: []string{tc.phase}, Mode: "block"}}, + }) + if err != nil { + t.Fatal(err) + } + defer built.Close() + parent, err := built.Service.CreateSession(t.Context(), session.ModeYolo, session.Limits{}) + if err != nil { + t.Fatal(err) + } + run, err := built.Service.StartRun(t.Context(), parent.ID, "inspect") + if err != nil { + t.Fatal(err) + } + var events []session.Event + for ev := range run.Events() { + events = append(events, ev) + } + built.Service.FinishRun(parent.ID, run) + memberSess, err := built.Service.GetSession(t.Context(), teamMemberSessionID(t, events)) + if err != nil { + t.Fatal(err) + } + wantMode := session.ModeYolo + if tc.agent != "" { + wantMode = session.ModeDefault + } + if memberSess.Mode != wantMode { + t.Fatalf("member mode = %q, want %q", memberSess.Mode, wantMode) + } + var read *session.ToolResult + for _, m := range memberSess.Conversation.Messages { + if m.ToolResult != nil && m.ToolResult.CallID == "member-read" { + read = m.ToolResult + } + } + if read == nil { + t.Fatalf("member Read produced no recorded result: %+v", memberSess.Conversation.Messages) + } + if leaked := strings.Contains(read.Content, canary); leaked != tc.wantLeak { + t.Fatalf("member Read result leaked=%t, want %t (enforcement must follow the member's own mode): %q", leaked, tc.wantLeak, read.Content) + } + if tc.wantReason != "" && !strings.Contains(read.Content, tc.wantReason) { + t.Fatalf("member Read result = %q, want the reason %q", read.Content, tc.wantReason) + } + }) + } +} diff --git a/internal/app/contextual_build_evidence_test.go b/internal/app/contextual_build_evidence_test.go index cadb5797a6..bcd1b323e3 100644 --- a/internal/app/contextual_build_evidence_test.go +++ b/internal/app/contextual_build_evidence_test.go @@ -443,7 +443,10 @@ func TestBuildWiresFiniteScriptEvidenceToContextualReviewer(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{MaxTurns: 2}) + // ModeYolo: Shell is auto-allowed (allow-all rules injected by Policy.Evaluate), + // so the guardrails block rule fires and routes the call to the contextual reviewer. + // ModeDefault would floor Shell at Ask, hanging the test without a human approver. + sess, err := built.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{MaxTurns: 2}) if err != nil { t.Fatalf("CreateSession: %v", err) } diff --git a/internal/app/contextual_guardrail_usage_test.go b/internal/app/contextual_guardrail_usage_test.go index 30c257ee51..afe5fa5ceb 100644 --- a/internal/app/contextual_guardrail_usage_test.go +++ b/internal/app/contextual_guardrail_usage_test.go @@ -247,7 +247,7 @@ func TestContextualGuardrailUsage_Scenario2_PathEscapeUsageProductionFlow(t *tes catalog.MustRegister(productionPathUsageTool{}) call := session.NewToolCall("escape", "Read", []byte(`{"path":"`+fixture.target+`"}`)) engine := agent.NewEngine(agent.Deps{LLM: mockllm.New(mockllm.ToolCallTurn(call), mockllm.TextTurn("done")), Catalog: catalog, Policy: policy}) - sess := session.New("path-usage", session.ModeDefault, env.Ref(), session.Limits{}, time.Now()) + sess := session.New("path-usage", session.ModeAuto, env.Ref(), session.Limits{}, time.Now()) for range engine.Run(t.Context(), sess, env, agent.RunRequest{Text: "read outside"}).Events() { } bucket := sess.TokenUsageSnapshot()[session.UsageKindGuardrail] @@ -281,7 +281,7 @@ func TestContextualGuardrailUsage_Scenario2_PathEscapeRetryAndRepeatedChecksPers t.Fatal(err) } defer built.Close() - sess, err := built.Service.CreateSession(t.Context(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(t.Context(), session.ModeAuto, session.Limits{}) if err != nil { t.Fatal(err) } @@ -331,7 +331,7 @@ func TestContextualGuardrailUsage_Scenario2_PathEscapeTerminalFailurePersistsPar t.Fatal(err) } defer built.Close() - sess, err := built.Service.CreateSession(t.Context(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(t.Context(), session.ModeAuto, session.Limits{}) if err != nil { t.Fatal(err) } @@ -391,7 +391,7 @@ func TestContextualGuardrailUsage_Scenario2_PathEscapeReevaluationAfterAwaiting( if err != nil { t.Fatal(err) } - sess, err := built1.Service.CreateSession(t.Context(), session.ModeDefault, session.Limits{}) + sess, err := built1.Service.CreateSession(t.Context(), session.ModeAuto, session.Limits{}) if err != nil { built1.Close() t.Fatal(err) @@ -483,7 +483,7 @@ func TestContextualGuardrailUsage_Scenario2_ComposedChildNeverArmsPathEscape(t * t.Fatal(err) } defer built.Close() - sess, err := built.Service.CreateSession(t.Context(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(t.Context(), session.ModeAuto, session.Limits{}) if err != nil { t.Fatal(err) } @@ -589,10 +589,10 @@ func TestEscapePolicyPreservesInnerAndCheckerUsage(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { inner := usagePermissionPolicy{port.PermissionResult{Decision: tc.inner, Usage: pathCheckerUsage(2)}} - policy := newEscapePolicy(inner, PostureAuto, withEscapeGuardrailRoute(pathUsageChecker{ + policy := newEscapePolicy(inner, withEscapeGuardrailRoute(pathUsageChecker{ result: modelhook.CheckResult{Verdict: tc.verdict, Usage: pathCheckerUsage(3)}, err: tc.err, })) - got := policy.Evaluate(t.Context(), "s1", session.ModeDefault, call, ws) + got := policy.Evaluate(t.Context(), "s1", session.ModeAuto, call, ws) if got.Decision.Effect != tc.want || got.Usage.Buckets[session.UsageKindGuardrail].Total.InputTokens != tc.usage { t.Fatalf("result = %+v; want effect %s and usage %d", got, tc.want, tc.usage) } diff --git a/internal/app/contextual_guardrails_task5_prompt_test.go b/internal/app/contextual_guardrails_task5_prompt_test.go index 433109ba68..b420bafd94 100644 --- a/internal/app/contextual_guardrails_task5_prompt_test.go +++ b/internal/app/contextual_guardrails_task5_prompt_test.go @@ -36,7 +36,8 @@ func TestBuildGuardrailsTaskWindowReachesReviewerEnvelope(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{MaxTurns: 2}) + // ModeAuto: Shell is auto-allowed (allow-all rules); the guardrails block fires. + sess, err := built.Service.CreateSession(context.Background(), session.ModeAuto, session.Limits{MaxTurns: 2}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -71,7 +72,8 @@ func TestContextualGuardrails_FactoryPrompts(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{MaxTurns: 2}) + // ModeAuto: Shell is auto-allowed (allow-all rules); the guardrails block fires. + sess, err := built.Service.CreateSession(context.Background(), session.ModeAuto, session.Limits{MaxTurns: 2}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -133,7 +135,8 @@ func TestContextualMainResultReleaseDetailUsesOwnerAuthorization(t *testing.T) { defer built.Close() owner := &session.Principal{Issuer: "test", Subject: "alice", GrantType: session.GrantTypeUser} ownerCtx := session.WithPrincipal(context.Background(), owner) - sess, err := built.Service.CreateSession(ownerCtx, session.ModeDefault, session.Limits{MaxTurns: 2}) + // ModeAuto: Shell is auto-allowed (allow-all rules); the guardrails post-block fires. + sess, err := built.Service.CreateSession(ownerCtx, session.ModeAuto, session.Limits{MaxTurns: 2}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -189,7 +192,8 @@ func TestContextualWorkerReviewDetailUsesLiveRootOwnership(t *testing.T) { defer built.Close() owner := &session.Principal{Issuer: "test", Subject: "alice", GrantType: session.GrantTypeUser} ownerCtx := session.WithPrincipal(context.Background(), owner) - sess, err := built.Service.CreateSession(ownerCtx, session.ModeDefault, session.Limits{MaxTurns: 3}) + // ModeAuto: Shell is auto-allowed (allow-all rules); the guardrails post-block fires. + sess, err := built.Service.CreateSession(ownerCtx, session.ModeAuto, session.Limits{MaxTurns: 3}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -224,7 +228,8 @@ func TestContextualGuardrails_CoverageTruth(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + // ModeAuto: matches the PostureAuto guardrail config's intended allow-all behavior. + sess, err := built.Service.CreateSession(context.Background(), session.ModeAuto, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } diff --git a/internal/app/contextual_new_write_test.go b/internal/app/contextual_new_write_test.go index 53215a49cd..6ebe20f930 100644 --- a/internal/app/contextual_new_write_test.go +++ b/internal/app/contextual_new_write_test.go @@ -25,7 +25,8 @@ func TestBuildContextualReviewerAllowsNewFileWriteWithoutMissingEvidenceAsk(t *t t.Fatal(err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{MaxTurns: 2}) + // ModeYolo: Write is auto-allowed (allow-all rules); the guardrails block fires. + sess, err := built.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{MaxTurns: 2}) if err != nil { t.Fatal(err) } diff --git a/internal/app/contextual_repair2b_test.go b/internal/app/contextual_repair2b_test.go index 0a55b427c6..57c7fa3d4c 100644 --- a/internal/app/contextual_repair2b_test.go +++ b/internal/app/contextual_repair2b_test.go @@ -124,7 +124,8 @@ func TestBuildInboundImageCannotBeBlindlyReleasedByTextOnlyChecker(t *testing.T) t.Fatal(err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{MaxTurns: 2}) + // ModeAuto: MCP tools are auto-allowed (allow-all rules); the guardrails post-block fires. + sess, err := built.Service.CreateSession(context.Background(), session.ModeAuto, session.Limits{MaxTurns: 2}) if err != nil { t.Fatal(err) } diff --git a/internal/app/escapeclassifier_test.go b/internal/app/escapeclassifier_test.go index 564839f848..18de8cc3fa 100644 --- a/internal/app/escapeclassifier_test.go +++ b/internal/app/escapeclassifier_test.go @@ -248,11 +248,11 @@ func TestPathEscapePosture_Scenario1_PseudoFsClassification(t *testing.T) { t.Errorf("%s(%q) classifies %v, want escapePseudoFS", toolName, path, got) } call := session.NewToolCall("pseudo", toolName, args) - for _, posture := range []Posture{PostureStrict, PostureTrusted, PostureAuto, PostureYolo} { - policy := newEscapePolicy(permpolicy.NewPolicy(defaultRules(), nil), posture) - decision := policy.Evaluate(t.Context(), "s1", session.ModeDefault, call, ws) + policy := newEscapePolicy(permpolicy.NewPolicy(defaultRules(), nil)) + for _, mode := range []session.PermissionMode{session.ModePlan, session.ModeDefault, session.ModeAccept, session.ModeAuto, session.ModeYolo} { + decision := policy.Evaluate(t.Context(), "s1", mode, call, ws) if decision.Decision.Effect != governance.Deny || !strings.Contains(decision.Decision.Reason, "pseudo-filesystem") { - t.Errorf("%s %s(%q): %+v, want pseudo-fs policy deny", posture, toolName, path, decision) + t.Errorf("%s %s(%q): %+v, want pseudo-fs policy deny", mode, toolName, path, decision) } } if toolName != "ListDir" { @@ -261,7 +261,7 @@ func TestPathEscapePosture_Scenario1_PseudoFsClassification(t *testing.T) { // A configured ask returns before the policy's pseudo-fs check. // Even when approved, the serving workspace must refuse it itself. inner := permpolicy.NewPolicy([]governance.Rule{{Scope: governance.ScopeUser, Tool: "ListDir", Effect: governance.Ask}}, nil) - decision := newEscapePolicy(inner, PostureYolo).Evaluate(t.Context(), "s1", session.ModeDefault, call, ws) + decision := newEscapePolicy(inner).Evaluate(t.Context(), "s1", session.ModeYolo, call, ws) if decision.Decision.Effect != governance.Ask || decision.Decision.AskProvenance != governance.AskProvenanceConfigured { t.Fatalf("configured ask for %q = %+v", path, decision) } diff --git a/internal/app/escapepolicy.go b/internal/app/escapepolicy.go index 18028576f6..9c7f2e93c4 100644 --- a/internal/app/escapepolicy.go +++ b/internal/app/escapepolicy.go @@ -19,11 +19,11 @@ import ( "github.com/stacklok/mecatl/internal/adapter/osfs" ) -// escapepolicy.go is the path-escape-posture decision half: a root-aware +// escapepolicy.go is the path-escape decision half: a root-aware // wrapping port.PermissionPolicy that relaxes an out-of-root Read/ListDir -// escape at the yolo/auto operator postures, an out-of-root WRITE escape at -// yolo (Allow) / auto (Ask), and resolves a strict/trusted out-of-root read OR -// write escape to ASK (instead of a +// escape in yolo/auto SESSION modes, an out-of-root WRITE escape in yolo +// (Allow) / auto (Ask), and resolves an out-of-root read OR write escape in +// every other mode to ASK (instead of a // hard ErrPathEscape dead-end that only pushes the model to an opaque Shell // `cat /path`). It is COMPOSITION, not domain — the escape decision is a // posture/policy concern, and the osfs containment vetting is never stripped @@ -36,8 +36,8 @@ import ( // Ask) into an escape Allow/Ask. The plan-mode hard-deny inside permpolicy's // EvaluateWith therefore runs BEFORE any escape decision (AC4.4). -// escapePolicy wraps an inner port.PermissionPolicy with the posture-derived -// out-of-root read-escape decision, keyed to the session workspace root on +// escapePolicy wraps an inner port.PermissionPolicy with the mode-derived +// out-of-root escape decision, keyed to the session workspace root on // EVERY Evaluate (via the ws the loop hands it). It is a permpolicy sibling: // it implements the SAME port.PermissionPolicy seam, delegating the ordinary // deny/ask/allow fold to the inner policy and layering ONLY the escape @@ -50,12 +50,11 @@ import ( // first call for that root. Learn is forwarded so an "allow always" verdict // on a non-escape call still records against the inner store. type escapePolicy struct { - inner port.PermissionPolicy - posture Posture + inner port.PermissionPolicy - // route is the guardrail-routed escape checker: non-nil ONLY at - // posture auto WITH the operator-tier escape knob configured. nil is the - // byte-identical no-route posture (the ordinary posture table). + // route is the guardrail-routed escape checker: non-nil only WITH the + // operator-tier escape knob configured, and consulted only for calls in an + // auto-mode session. nil is the ordinary mode table. route *escapeGuardrailRoute mu sync.Mutex @@ -66,28 +65,26 @@ type escapePolicy struct { // optional wiring (today: the guardrail-routed escape check). type escapePolicyOption func(*escapePolicy) -// withEscapeGuardrailRoute arms the guardrail-routed escape checker. -// It is a NO-OP unless the posture is auto AND the checker is non-nil — the -// route is the auto-only knob (yolo demotes guardrails to advisory -// and never spends a checker call; strict/trusted keep their own -// Scenario-4 escape Ask). +// withEscapeGuardrailRoute arms the guardrail-routed escape checker. It is a +// no-op for a nil checker. Evaluate consults it only in auto-mode sessions: +// yolo demotes guardrails to advisory and never spends a checker call, and the +// ordinary modes keep their own Scenario-4 escape Ask. func withEscapeGuardrailRoute(checker modelhook.VerdictChecker) escapePolicyOption { return func(p *escapePolicy) { - if p.posture != PostureAuto || checker == nil { + if checker == nil { return } p.route = &escapeGuardrailRoute{checker: checker} } } -// newEscapePolicy builds the shared-engine wrapper. It is active at EVERY -// posture (the caller installs it uniformly): at strict/trusted it converts a -// non-denied escape into the Scenario-4 escape Ask. -func newEscapePolicy(inner port.PermissionPolicy, posture Posture, opts ...escapePolicyOption) port.PermissionPolicy { +// newEscapePolicy builds the shared-engine wrapper. It is active in EVERY +// session mode and decides from the mode Evaluate receives, never from the +// process posture, so a session switched out of yolo regains the escape Ask. +func newEscapePolicy(inner port.PermissionPolicy, opts ...escapePolicyOption) port.PermissionPolicy { p := &escapePolicy{ - inner: inner, - posture: posture, - clfs: make(map[string]*escapeClassifier), + inner: inner, + clfs: make(map[string]*escapeClassifier), } for _, o := range opts { o(p) @@ -126,19 +123,18 @@ func (p *escapePolicy) classifierFor(ws tool.WorkspaceReader) *escapeClassifier // 1. inner Deny → returned verbatim (deny-dominance: a configured deny, the // plan-mode hard-deny, or any inner deny is never relaxed); // 2. inner Ask with ConfiguredAsk → returned verbatim (the configured-Ask -// floor: a CONFIGURED ask on the tool is never suppressed by the posture +// floor: a CONFIGURED ask on the tool is never suppressed by the mode // relax — it demands a human, exactly as the bash substitution floor's // configured-Ask-never-suppressed invariant requires); -// 3. pseudo-fs (/proc, /sys, /dev) → Deny at EVERY posture (the never-relaxed +// 3. pseudo-fs (/proc, /sys, /dev) → Deny in EVERY mode and posture (the never-relaxed // category — an in-process Read of /proc/self/environ would return the // SERVER's raw, unscrubbed environment, a channel the envscrub-scrubbed // Shell parity path does not provide); -// 4. an out-of-root Read/ListDir escape at auto/yolo → Allow (Shell parity); at -// strict/trusted → Ask (Scenario 4 — the legible FS-tool ask instead of -// the ErrPathEscape dead-end); -// 5. an out-of-root WRITE escape → Allow at yolo, Ask at auto AND at -// strict/trusted (Scenarios 3+4 — never a silent un-asked mutation below -// yolo); +// 4. an out-of-root Read/ListDir escape in an auto/yolo session → Allow (Shell +// parity); in any other mode → Ask (Scenario 4 — the legible FS-tool ask +// instead of the ErrPathEscape dead-end); +// 5. an out-of-root WRITE escape → Allow in yolo, Ask in auto AND every other +// mode (Scenarios 3+4 — never a silent un-asked mutation below yolo); // 6. everything else → the inner decision verbatim. func (p *escapePolicy) Evaluate(ctx context.Context, sessionID session.SessionID, mode session.PermissionMode, c session.ToolCall, ws tool.WorkspaceReader) port.PermissionResult { decision := p.inner.Evaluate(ctx, sessionID, mode, c, ws) @@ -155,7 +151,7 @@ func (p *escapePolicy) Evaluate(ctx context.Context, sessionID session.SessionID kind := clf.classify(c.Name, c.Args) switch kind { case escapePseudoFS: - // Never-relaxed at every posture: an FS read of a pseudo-fs path would + // Never-relaxed in every mode: an FS read of a pseudo-fs path would // exfiltrate the SERVER's raw environment, a channel Shell does not // provide (the env-scrub gotcha). Hard-deny even at yolo. decision.Decision = governance.PermissionDecision{ @@ -165,10 +161,10 @@ func (p *escapePolicy) Evaluate(ctx context.Context, sessionID session.SessionID return decision case escapeEscape: // Auto + the operator-tier escape knob only: route the - // escape through the guardrail checker BEFORE the posture row. An + // escape through the guardrail checker BEFORE the mode row. An // unsafe verdict vetoes (Deny); a checker ERROR fails CLOSED to the // write-escape Ask; a safe verdict falls through to the ordinary row. - if p.route != nil { + if p.route != nil && mode == session.ModeAuto { result, err := p.route.review(ctx, c) decision.Usage = decision.Usage.Merge(result.Usage) if err != nil { @@ -179,12 +175,12 @@ func (p *escapePolicy) Evaluate(ctx context.Context, sessionID session.SessionID decision.Decision = p.route.denyDecision(result.Verdict) return decision } - // safe: fall through to the posture row below. + // safe: fall through to the mode row below. } path := escapePath(c.Args) switch c.Name { case "Read", listDirToolName: - if p.posture >= PostureAuto { + if mode == session.ModeAuto || mode == session.ModeYolo { // Shell parity: at auto/yolo Shell already reads the same bytes, so // the FS read boundary was cosmetic. The relaxed workspace serves // the read; the wrapper only has to not stand in its way. @@ -216,14 +212,14 @@ func (p *escapePolicy) Evaluate(ctx context.Context, sessionID session.SessionID // configured Ask both returned above (deny-dominance + the // configured-Ask floor), so the relax only ever replaces an inner // ALLOW or an unconfigured floor Ask — never a configured one. - if p.posture >= PostureYolo { + if mode == session.ModeYolo { decision.Decision = governance.PermissionDecision{Effect: governance.Allow} return decision } - if p.posture >= PostureAuto { + if mode == session.ModeAuto { decision.Decision = governance.PermissionDecision{ Effect: governance.Ask, - Reason: "out-of-workspace write: the path escapes the session workspace root (posture auto allows reads but never writes silently)", + Reason: "out-of-workspace write: the path escapes the session workspace root (auto mode allows reads but never writes silently)", } return decision } @@ -240,17 +236,17 @@ func (p *escapePolicy) Evaluate(ctx context.Context, sessionID session.SessionID // --- the guardrail-routed escape checker (auto + knob only) --- // escapeGuardrailRoute is the composition-level escape PRE-CHECK: an -// out-of-root escape at posture auto, when the operator-tier escape knob is -// configured, is judged by the LLM guardrail checker BEFORE the posture row +// out-of-root escape in an auto-mode session, when the operator-tier escape knob is +// configured, is judged by the LLM guardrail checker BEFORE the mode row // decides. It reuses the SAME engine-backed modelhook.VerdictChecker // (agent.RunGuardrailCheck + ParseVerdict over a tool-less one-turn checker // engine) the modelhook hook-path runner uses — the dual-LLM quarantine, the // whole-output-single-object verdict parse, and the bounded checker engine // are all inherited, not re-implemented. The route is: // -// - auto-only (withEscapeGuardrailRoute refuses to arm it at any other -// posture — yolo demotes guardrails to advisory and never -// spends a checker call; strict/trusted keep their own Scenario-4 Ask); +// - auto-only (Evaluate consults it only for an auto-mode session — yolo +// demotes guardrails to advisory and never spends a checker call; the +// ordinary modes keep their own Scenario-4 Ask); // - main-engine-only (it rides ONLY the main escape policy; a child engine // never relaxes escapes at all — Scenario 5 — so there is no child route); // - deny-dominant (the inner policy fold runs FIRST — a configured Deny or @@ -260,7 +256,7 @@ func (p *escapePolicy) Evaluate(ctx context.Context, sessionID session.SessionID // headless — never to a silent allow and never to a plain pass-through of // the read-allow row). // -// Verdict mapping: safe → fall through to the ordinary auto posture row +// Verdict mapping: safe → fall through to the ordinary auto mode row // (read Allow / write Ask); unsafe → DENY the escape (a checker block is a // veto, mirroring the hook-path PreToolUse block). The content under review // is the call's RAW args JSON, fenced with governance.UntrustedFence and @@ -328,10 +324,10 @@ func escapePath(args json.RawMessage) string { // Learn forwards rule-learning to the inner policy (an "allow always" verdict // on an ordinary call still records against the inner learned-rule store) — // EXCEPT for an out-of-root escape call, which it NEVER forwards: the escape -// relax is posture-derived, not learned, and v1 escape asks are allow-once +// relax is mode-derived, not learned, and v1 escape asks are allow-once // only (a learned out-of-root Write/Edit rule would silently pre-approve // every later write to that path in the session, bypassing the escape Ask the -// posture table promises). Without a session workspace here the call is +// mode table promises). Without a session workspace here the call is // classified against ANY root the policy has already classified for — a call // that escapes under every known root is suppressed; anything else is left to // the inner policy's own LearnableRule discipline. diff --git a/internal/app/guardrails.go b/internal/app/guardrails.go index 0602d3ff63..57143114b8 100644 --- a/internal/app/guardrails.go +++ b/internal/app/guardrails.go @@ -326,7 +326,7 @@ func buildGuardrailsActionReviewer(cfg Config, provReg *providerRegistry, provid if base == nil { return nil } - specs, _ := effectiveGuardrailSpecs(cfg) + specs, _ := configuredGuardrailSpecs(cfg) rules, ok := compileGuardrailRules(cfg, specs) if !ok { return nil @@ -359,6 +359,7 @@ func guardrailCoverageFor(cfg Config, sess *session.Session) server.GuardrailCov if !coverage.Enabled || sess == nil { return coverage } + cfg.guardrailSessionMode = sess.Mode specs, _ := effectiveGuardrailSpecs(cfg) rules, ok := compileGuardrailRules(cfg, specs) if !ok { @@ -442,17 +443,31 @@ var defaultGuardrailSpecs = []modelhook.RuleSpec{ // ENFORCING: under auto the approve-once ask IS the auto-mode behaviour (the checker // blocks, an interactive human allows once — CC auto-mode parity), so demoting auto // would remove that very behaviour. The interactive approve-once path is gated on -// Deps.Interactive, NOT on posture. This is composition-only (no engine change) and -// posture is operator-tier, consistent with guardrails being operator-tier (no -// project-tier downgrade). +// Deps.Interactive, NOT on posture. Posture is operator-tier, consistent with +// guardrails being operator-tier (no project-tier downgrade). These demoted specs +// feed coverage and narration; the engine reviewer compiles configuredGuardrailSpecs +// and the engine applies the same yolo demotion per executing session. func effectiveGuardrailSpecs(cfg Config) (specs []modelhook.RuleSpec, usedDefaults bool) { + specs, usedDefaults = configuredGuardrailSpecs(cfg) + for i := range specs { + specs[i].Mode = demoteForPosture(cfg, specs[i].Mode) + } + return specs, usedDefaults +} + +// configuredGuardrailSpecs returns the operator's rule specs (or the default set) +// WITHOUT the yolo demotion. The engine reviewer compiles these: a reviewRoot is +// shared with delegated children, so the engine selects enforcement from each +// executing session's mode (a yolo session is advisory, a narrower child of it +// still enforces) instead of freezing the parent's demotion into the rules. +func configuredGuardrailSpecs(cfg Config) (specs []modelhook.RuleSpec, usedDefaults bool) { if len(cfg.GuardrailsRules) > 0 { out := make([]modelhook.RuleSpec, 0, len(cfg.GuardrailsRules)) for i, gr := range cfg.GuardrailsRules { out = append(out, modelhook.RuleSpec{ Match: gr.Match, Phases: gr.Phases, - Mode: demoteForPosture(cfg, gr.Mode), + Mode: gr.Mode, Prompt: gr.Prompt, FailClosed: gr.FailClosed, FailClosedSet: gr.FailClosedSet, @@ -471,24 +486,36 @@ func effectiveGuardrailSpecs(cfg Config) (specs []modelhook.RuleSpec, usedDefaul if dm := strings.TrimSpace(cfg.GuardrailsDefaultMode); dm != "" { s.Mode = dm } - s.Mode = demoteForPosture(cfg, s.Mode) out[i] = s } return out, true } -// demoteForPosture demotes the enforcing guardrail block mode to advisory -// under posture YOLO ONLY (CC bypassPermissions parity). +// demoteForPosture demotes the enforcing guardrail block mode to advisory for +// the gate-free yolo tier ONLY (CC bypassPermissions parity). A per-session +// engine (cfg.guardrailSessionMode set) is demoted only when its session is in +// yolo mode, so an auto session on a yolo server keeps an enforcing checker; +// the shared engine follows the process posture, and sessions whose mode +// disagrees with it are promoted to a per-session engine (modeNeedsEngine). // strict/trusted/auto keep the configured mode — under auto the approve-once ask IS // the enforcement behaviour. It is the SINGLE posture→mode coupling point so the // default-set and operator-rule branches cannot drift. func demoteForPosture(cfg Config, mode string) string { - if cfg.Posture < PostureYolo { + if !guardrailsDemoted(cfg) { return mode } return string(modelhook.ModeAdvisory) } +// guardrailsDemoted reports whether the engine cfg describes runs its +// guardrail rules advisory-only. +func guardrailsDemoted(cfg Config) bool { + if cfg.guardrailSessionMode != "" { + return cfg.guardrailSessionMode == session.ModeYolo + } + return cfg.Posture >= PostureYolo +} + // guardrailsConfigured reports whether guardrails are switched on: a checker model // is configured — via --guardrails-model OR a bound `guardrail` model slot (configure = // enable, the same model as the subagent router) — AND the master kill-switch @@ -685,7 +712,7 @@ func buildGuardrailsChecker(cfg Config, provReg *providerRegistry, provider port } // buildGuardrailsEscapeChecker builds the guardrail-routed permission escape check. -// It is armed only on the main session at posture auto with the escape knob +// It rides only the main session, applies only to auto-mode sessions, and needs the escape knob // and guardrails configured. Generic HookRunner requests carry no usage reporter. func buildGuardrailsEscapeChecker(cfg Config, provReg *providerRegistry, provider port.LLMProvider) modelhook.VerdictChecker { if !cfg.GuardrailsEscape || !guardrailsConfigured(cfg) { diff --git a/internal/app/guardrails_approve_once_e2e_test.go b/internal/app/guardrails_approve_once_e2e_test.go index 43169f3471..84f3c8de72 100644 --- a/internal/app/guardrails_approve_once_e2e_test.go +++ b/internal/app/guardrails_approve_once_e2e_test.go @@ -4,6 +4,7 @@ import ( "bufio" "context" "encoding/json" + "iter" "net/http" "net/http/httptest" "os" @@ -150,7 +151,8 @@ func TestGuardrailApproveOnceE2EInteractiveAllow(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + // ModeAuto: Shell is auto-allowed (allow-all rules); the guardrails block fires. + sess, err := built.Service.CreateSession(ctx, session.ModeAuto, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -207,7 +209,8 @@ func TestGuardrailApproveOnceE2EInteractiveDeny(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + // ModeAuto: Shell is auto-allowed (allow-all rules); the guardrails block fires. + sess, err := built.Service.CreateSession(ctx, session.ModeAuto, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -259,7 +262,8 @@ func TestGuardrailApproveOnceE2EHeadlessTerminalBlock(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + // ModeAuto: Shell is auto-allowed (allow-all rules); the guardrails block fires. + sess, err := built.Service.CreateSession(ctx, session.ModeAuto, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -287,6 +291,186 @@ func TestGuardrailApproveOnceE2EHeadlessTerminalBlock(t *testing.T) { } } +// Posture YOLO, session switched to AUTO: the yolo demotion is per session mode, +// so the auto session is admitted (an enforcing checker exists for it) and its +// guardrail blocks — headless, a terminal block, and the tool does not run. +func TestGuardrailE2EYoloServerAutoSessionEnforces(t *testing.T) { + ctx := context.Background() + cfg := guardrailE2ECfg(t, false, PostureYolo, "touch "+guardrailMarker) + built, err := buildIsolated(t, ctx, cfg) + if err != nil { + t.Fatalf("Build: %v", err) + } + defer built.Close() + sess, err := built.Service.CreateSession(ctx, session.ModeYolo, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + if _, err := built.Service.SetMode(ctx, sess.ID, session.ModeAuto); err != nil { + t.Fatalf("SetMode(auto) on a yolo server with a checker: %v", err) + } + srv := httptest.NewServer(server.NewHTTPHandler(built.Service)) + defer srv.Close() + + evs := driveGuardrailPrompt(t, srv.URL, string(sess.ID), "merge it", nil) + var blocked bool + for _, ev := range evs { + if ev.Type == "tool.result" && ev.ToolResult.IsError && strings.Contains(ev.ToolResult.Content, "guardrail") { + blocked = true + } + } + if !blocked { + t.Fatal("an auto session on a yolo server must keep an enforcing checker — no guardrail block surfaced") + } + if markerExists(t, cfg.Workspace) { + t.Fatal("the enforcing auto-session block must NOT execute the Shell command — but the marker file exists") + } +} + +// modeRoutedProvider sends guardrail-reviewer requests (the GuardrailsModel) to +// reviewer and every other request to agent, so one session's agent script +// stays in order whether or not a mode consults the checker. +type modeRoutedProvider struct { + agent, reviewer *mockllm.Provider + reviewerModel string +} + +func (p modeRoutedProvider) Stream(ctx context.Context, req port.LLMRequest) (iter.Seq2[port.Chunk, error], error) { + if req.Model == p.reviewerModel { + return p.reviewer.Stream(ctx, req) + } + return p.agent.Stream(ctx, req) +} + +func (p modeRoutedProvider) Capabilities() port.ProviderCapabilities { return p.agent.Capabilities() } + +// modeTurnOutcome is what one executed turn surfaced. +type modeTurnOutcome struct { + policyAsks, guardrailAsks []string + results []session.ToolResult +} + +// runDenyingTurn runs one prompt to completion on the real service, denying +// every ask, and returns the asks (split by origin) and tool results. +func runDenyingTurn(t *testing.T, built *Built, id session.SessionID) modeTurnOutcome { + t.Helper() + run, err := built.Service.StartRun(context.Background(), id, "merge it") + if err != nil { + t.Fatalf("StartRun: %v", err) + } + var out modeTurnOutcome + for ev := range run.Events() { + if ev.Type == session.EvPermissionAsk && ev.Ask != nil { + if ev.Ask.Guardrail != nil { + out.guardrailAsks = append(out.guardrailAsks, ev.Ask.Tool) + } else { + out.policyAsks = append(out.policyAsks, ev.Ask.Tool) + } + run.Approve(ev.Ask.AskID, session.VerdictDeny) + } + if ev.Type == session.EvToolResult && ev.ToolResult != nil { + out.results = append(out.results, *ev.ToolResult) + } + } + built.Service.FinishRun(id, run) + return out +} + +// TestGuardrailE2EExecutedYoloAutoDefaultTransitions runs ONE session through +// three completed turns on a yolo server with a rejecting checker, switching +// mode between runs so the engine transitions live: yolo allows the mutation +// with the checker advisory; auto lets the same checker block it while a +// configured Deny still denies; default asks for it instead of auto-allowing. +func TestGuardrailE2EExecutedYoloAutoDefaultTransitions(t *testing.T) { + ctx := context.Background() + cfg := guardrailE2ECfg(t, true, PostureYolo, "") + settings := filepath.Join(t.TempDir(), "settings.yaml") + if err := os.WriteFile(settings, []byte("permissions:\n deny:\n - Write\n"), 0o600); err != nil { + t.Fatalf("write permission config: %v", err) + } + cfg.PermissionConfigs = []string{settings} + shell := func(id session.ToolCallID, marker string) mockllm.Turn { + return mockllm.ToolCallTurn(session.NewToolCall(id, "Shell", json.RawMessage(`{"command":"touch `+marker+`"}`))) + } + denied := filepath.Join(cfg.Workspace, "denied.txt") + agent := mockllm.New( + shell("y1", "yolo_marker"), mockllm.TextTurn("done"), + shell("a1", "auto_marker"), + mockllm.ToolCallTurn(session.NewToolCall("a2", "Write", json.RawMessage(`{"path":"`+denied+`","content":"x"}`))), + mockllm.TextTurn("done"), + shell("d1", "default_marker"), mockllm.TextTurn("done"), + ) + verdicts := make([]mockllm.Turn, 8) + for i := range verdicts { + verdicts[i] = guardrailE2EScript("")[1] + } + reviewer := mockllm.New(verdicts...) + cfg.providerConstructor = func(Config, string, string, string) port.LLMProvider { + return modeRoutedProvider{agent: agent, reviewer: reviewer, reviewerModel: cfg.GuardrailsModel} + } + built, err := buildIsolated(t, ctx, cfg) + if err != nil { + t.Fatalf("Build: %v", err) + } + defer built.Close() + sess, err := built.Service.CreateSession(ctx, session.ModeYolo, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + exists := func(name string) bool { + _, err := os.Stat(filepath.Join(cfg.Workspace, name)) + return err == nil + } + + // yolo: the mutation is auto-allowed and the rejecting checker is advisory. + yolo := runDenyingTurn(t, built, sess.ID) + if len(yolo.policyAsks)+len(yolo.guardrailAsks) != 0 || len(yolo.results) != 1 || yolo.results[0].IsError || !exists("yolo_marker") { + t.Fatalf("yolo turn: asks %v/%v results %+v marker %t; want the Shell call to run unasked", yolo.policyAsks, yolo.guardrailAsks, yolo.results, exists("yolo_marker")) + } + + if reviewer.Calls() == 0 { + t.Fatal("yolo turn: the checker was never consulted, so its advisory verdict proves nothing") + } + if _, err := built.Service.SetMode(ctx, sess.ID, session.ModeAuto); err != nil { + t.Fatalf("SetMode(auto) after a completed yolo run: %v", err) + } + // auto: no policy ask, but the same checker now blocks the mutation, and the + // configured Deny still denies Write without an ask. + auto := runDenyingTurn(t, built, sess.ID) + if len(auto.policyAsks) != 0 || len(auto.guardrailAsks) != 1 || auto.guardrailAsks[0] != "Shell" { + t.Fatalf("auto turn asks: policy %v guardrail %v; want exactly one guardrail ask for Shell", auto.policyAsks, auto.guardrailAsks) + } + if exists("auto_marker") { + t.Fatal("auto turn: the checker-blocked Shell command ran") + } + if len(auto.results) != 2 || !auto.results[0].IsError || !auto.results[1].IsError { + t.Fatalf("auto turn results = %+v; want the blocked Shell and denied Write as errors", auto.results) + } + if _, err := os.Stat(denied); !os.IsNotExist(err) { + t.Fatalf("auto turn: configured Deny let Write land (stat err %v)", err) + } + + if _, err := built.Service.SetMode(ctx, sess.ID, session.ModeDefault); err != nil { + t.Fatalf("SetMode(default) after a completed auto run: %v", err) + } + // default: the mutation is not auto-allowed; it surfaces as an ordinary + // permission ask, and the denied command never runs. + def := runDenyingTurn(t, built, sess.ID) + if len(def.policyAsks) == 0 || def.policyAsks[0] != "Shell" { + t.Fatalf("default turn asks: policy %v guardrail %v; want an ordinary Shell permission ask", def.policyAsks, def.guardrailAsks) + } + if exists("default_marker") { + t.Fatal("default turn: the denied Shell command ran") + } + final, err := built.Service.GetSession(ctx, sess.ID) + if err != nil { + t.Fatalf("GetSession: %v", err) + } + if final.Mode != session.ModeDefault || final.State != session.StateCompleted { + t.Fatalf("final session mode %q state %q, want default/completed", final.Mode, final.State) + } +} + // Posture YOLO: guardrails demote to advisory — the tool RUNS, no ask, no block. func TestGuardrailApproveOnceE2EYoloAdvisory(t *testing.T) { ctx := context.Background() @@ -296,7 +480,8 @@ func TestGuardrailApproveOnceE2EYoloAdvisory(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + // ModeYolo: Shell is auto-allowed (allow-all rules + loose evaluator); guardrails are advisory. + sess, err := built.Service.CreateSession(ctx, session.ModeYolo, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } diff --git a/internal/app/harness_context_authority_test.go b/internal/app/harness_context_authority_test.go index 55de94e0f9..42bb664254 100644 --- a/internal/app/harness_context_authority_test.go +++ b/internal/app/harness_context_authority_test.go @@ -30,6 +30,7 @@ func TestHarnessContext_ChildAttenuationPreserved(t *testing.T) { kinds.AgentDefs = permconfig.HarnessContextKind{Sources: []string{"specialists"}, Mode: "combine"} cfg := harnessPolicyConfig(t, permconfig.HarnessContextSection{EnabledSources: []string{"context", "specialists"}, Kinds: kinds}) cfg.AllowAllTools = true + cfg.GuardrailsDisabled = true if profile == server.ProfileDefault { cfg.Shell = "/bin/sh" } @@ -56,7 +57,8 @@ func TestHarnessContext_ChildAttenuationPreserved(t *testing.T) { t.Fatal(err) } defer b.Close() - sess, err := b.Service.CreateSessionWithProfile(t.Context(), session.ModeDefault, session.Limits{}, server.ProviderSelector{}, profile) + // Pass "" to use the server's DefaultMode (ModeYolo from AllowAllTools=true config). + sess, err := b.Service.CreateSessionWithProfile(t.Context(), "", session.Limits{}, server.ProviderSelector{}, profile) if err != nil { t.Fatal(err) } @@ -131,7 +133,8 @@ func testHarnessChildFactoryLifetime(t *testing.T) { t.Fatal(err) } defer built.Close() - parent, err := built.Service.CreateSessionWithProfile(t.Context(), session.ModeDefault, session.Limits{}, server.ProviderSelector{}, server.ProfileDefault, server.WithPlacementBinding(placement)) + // Pass "" to use the server's DefaultMode (ModeYolo from AllowAllTools=true config). + parent, err := built.Service.CreateSessionWithProfile(t.Context(), "", session.Limits{}, server.ProviderSelector{}, server.ProfileDefault, server.WithPlacementBinding(placement)) if err != nil { t.Fatal(err) } @@ -233,6 +236,7 @@ func TestHarnessContext_ContextOverridesCannotGrantAuthority(t *testing.T) { cfg := harnessPolicyConfig(t, permconfig.HarnessContextSection{EnabledSources: []string{"repository", "operator"}, Kinds: kinds}) cfg.TrustProject = trusted cfg.AllowAllTools = true + cfg.GuardrailsDisabled = true data, err := os.ReadFile(cfg.PermissionConfigs[0]) if err != nil { t.Fatal(err) diff --git a/internal/app/harness_context_execution_source_test.go b/internal/app/harness_context_execution_source_test.go index eca58aefab..8e2f655858 100644 --- a/internal/app/harness_context_execution_source_test.go +++ b/internal/app/harness_context_execution_source_test.go @@ -169,7 +169,7 @@ func TestRepositoryForkCarriesSnapshotsAcrossNewWorkspaceBindings(t *testing.T) var requests []port.LLMRequest cfg := harnessExecutionConfig(t, provider, &requests) cfg.Workspace = root - cfg.AllowAllTools = true + cfg.AllowAllTools, cfg.GuardrailsDisabled = true, true cfg.HarnessInstructionSources = nil cfg.HarnessCommandSources = nil registerRepositorySources(&cfg) @@ -228,7 +228,7 @@ func TestHarnessPublishedCloseRequiresExplicitLoad(t *testing.T) { t.Fatal(err) } defer built.Close() - sess, err := built.Service.CreateSessionWithProfile(ctx, session.ModeDefault, session.Limits{}, server.ProviderSelector{}, server.ProfileDefault, server.WithSessionID("closed-published"), server.WithPlacementBinding(binding)) + sess, err := built.Service.CreateSessionWithProfile(ctx, "", session.Limits{}, server.ProviderSelector{}, server.ProfileDefault, server.WithSessionID("closed-published"), server.WithPlacementBinding(binding)) if err != nil { t.Fatal(err) } @@ -275,7 +275,7 @@ func TestHarnessContext_SameOwnerSessionsStayDistinct(t *testing.T) { ctx := session.WithPrincipal(t.Context(), owner) for i, binding := range []server.PlacementBinding{first, second} { id := session.SessionID(fmt.Sprintf("exact-%d", i)) - sess, err := built.Service.CreateSessionWithProfile(ctx, session.ModeDefault, session.Limits{}, server.ProviderSelector{}, server.ProfileDefault, server.WithSessionID(id), server.WithPlacementBinding(binding)) + sess, err := built.Service.CreateSessionWithProfile(ctx, "", session.Limits{}, server.ProviderSelector{}, server.ProfileDefault, server.WithSessionID(id), server.WithPlacementBinding(binding)) if err != nil { t.Fatal(err) } @@ -337,6 +337,7 @@ func testExecutionSourceScheduledRestart(t *testing.T) { var requests []port.LLMRequest cfg := harnessExecutionConfig(t, provider, &requests) cfg.StoreDir, cfg.AllowAllTools, cfg.OwnershipEnforced = storeDir, true, true + cfg.GuardrailsDisabled = true original := cfg.HarnessInstructionSources[0].Bind var unpublished []session.SessionID var resumed atomic.Int32 @@ -361,7 +362,7 @@ func testExecutionSourceScheduledRestart(t *testing.T) { t.Fatal(err) } defer first.Close() - if _, err := first.Service.CreateSessionWithProfile(ctx, session.ModeDefault, session.Limits{}, server.ProviderSelector{}, server.ProfileDefault, server.WithSessionID(rootID), server.WithPlacementBinding(placement)); err != nil { + if _, err := first.Service.CreateSessionWithProfile(ctx, "", session.Limits{}, server.ProviderSelector{}, server.ProfileDefault, server.WithSessionID(rootID), server.WithPlacementBinding(placement)); err != nil { t.Fatal(err) } for _, ev := range harnessRun(t, first, ctx, rootID, "schedule") { @@ -524,7 +525,7 @@ func testExecutionSourceFailureBoundaries(t *testing.T) { owner := &session.Principal{Issuer: "issuer", Subject: "attempt-owner", GrantType: session.GrantTypeUser} otherCtx := session.WithPrincipal(t.Context(), &session.Principal{Issuer: "issuer", Subject: "other-owner", GrantType: session.GrantTypeUser}) create := func(ctx context.Context, id session.SessionID, binding server.PlacementBinding) error { - _, err := svc.CreateSessionWithProfile(ctx, session.ModeDefault, session.Limits{}, server.ProviderSelector{}, server.ProfileDefault, server.WithSessionID(id), server.WithPlacementBinding(binding)) + _, err := svc.CreateSessionWithProfile(ctx, "", session.Limits{}, server.ProviderSelector{}, server.ProfileDefault, server.WithSessionID(id), server.WithPlacementBinding(binding)) return err } if err := create(otherCtx, "other", otherPlacement); err != nil { diff --git a/internal/app/harness_context_execution_test.go b/internal/app/harness_context_execution_test.go index f53d2e912d..47a0c11ff2 100644 --- a/internal/app/harness_context_execution_test.go +++ b/internal/app/harness_context_execution_test.go @@ -36,7 +36,10 @@ func harnessRun(t *testing.T, b *Built, ctx context.Context, id session.SessionI //nolint:revive // Test helpers consistently take testing.T first. func harnessCreate(t *testing.T, b *Built, ctx context.Context) session.SessionID { t.Helper() - s, err := b.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + // Pass "" (empty) so the session starts in the server's configured DefaultMode: + // for allow-all configs (PostureYolo/PostureAuto) this is ModeYolo/ModeAuto, which + // injects the allow-all rules; for strict configs it is ModeDefault. + s, err := b.Service.CreateSession(ctx, "", session.Limits{}) if err != nil { t.Fatal(err) } @@ -132,6 +135,7 @@ func TestHarnessContext_ExplicitExecutionFileSource(t *testing.T) { cfg := hcConfiguredFiles(t, ws) cfg.PlacementProvider = harnessVirtualPlacement(ws) cfg.AllowAllTools = true + cfg.GuardrailsDisabled = true var requests []port.LLMRequest cfg.MockProvider = mockllm.NewWith([]mockllm.Option{mockllm.WithRequestObserver(func(r port.LLMRequest) { requests = append(requests, r) })}, mockllm.ToolCallTurn(session.ToolCall{ID: "read", Name: "Read", Args: json.RawMessage(`{"path":"data.txt"}`)}), mockllm.TextTurn("done")) b, err := buildIsolated(t, t.Context(), cfg) @@ -197,6 +201,7 @@ func TestHarnessContext_SourceReadsDoNotAuthorizeEdits(t *testing.T) { cfg := hcConfiguredFiles(t, ws) cfg.PlacementProvider = harnessVirtualPlacement(ws) cfg.AllowAllTools = true + cfg.GuardrailsDisabled = true cfg.MockProvider = mockllm.New( mockllm.ToolCallTurn(session.ToolCall{ID: "source-only", Name: "Edit", Args: json.RawMessage(`{"path":"AGENTS.md","old_string":"original","new_string":"changed"}`)}), mockllm.ToolCallTurn(session.ToolCall{ID: "read", Name: "Read", Args: json.RawMessage(`{"path":"AGENTS.md"}`)}), diff --git a/internal/app/harness_context_freshness_test.go b/internal/app/harness_context_freshness_test.go index 674ddd32e0..f9bcd7e61b 100644 --- a/internal/app/harness_context_freshness_test.go +++ b/internal/app/harness_context_freshness_test.go @@ -44,6 +44,7 @@ func TestHarnessContext_ProjectInstructionsSessionRetention(t *testing.T) { cfg := hcConfiguredFiles(t, source) cfg.NoSoul = true cfg.AllowAllTools = true + cfg.GuardrailsDisabled = true if err := os.WriteFile(filepath.Join(cfg.Workspace, "data.txt"), []byte("execution"), 0o600); err != nil { t.Fatal(err) } diff --git a/internal/app/harness_context_generation_test.go b/internal/app/harness_context_generation_test.go index 1fc36492ba..506284e4be 100644 --- a/internal/app/harness_context_generation_test.go +++ b/internal/app/harness_context_generation_test.go @@ -502,7 +502,7 @@ func TestChildHierarchyProviderFactoryScopesPreserveForkSnapshotsAndIsolation(t t.Fatal(err) } cfg := hcConfiguredFiles(t, source) - cfg.Workspace, cfg.Shell, cfg.AllowAllTools, cfg.NoSoul = website, "/bin/sh", true, true + cfg.Workspace, cfg.Shell, cfg.AllowAllTools, cfg.NoSoul, cfg.GuardrailsDisabled = website, "/bin/sh", true, true, true var requests []port.LLMRequest args := `{"prompt":"read nested file","model":"mock"}` if fork { @@ -573,7 +573,7 @@ func TestChildNoFSForkRetainsRootWithoutDiscoveringNested(t *testing.T) { } } cfg := hcConfiguredFiles(t, source) - cfg.AllowAllTools, cfg.NoSoul = true, true + cfg.AllowAllTools, cfg.NoSoul, cfg.GuardrailsDisabled = true, true, true var requests []port.LLMRequest cfg.MockProvider = mockllm.NewWith([]mockllm.Option{mockllm.WithRequestObserver(func(r port.LLMRequest) { requests = append(requests, r) @@ -853,7 +853,7 @@ func TestHarnessGenerationServiceReloadOwnerAuthorization(t *testing.T) { defer built.Close() alice := session.WithPrincipal(t.Context(), &session.Principal{Issuer: "issuer", Subject: "alice", GrantType: session.GrantTypeUser}) bob := session.WithPrincipal(t.Context(), &session.Principal{Issuer: "issuer", Subject: "bob", GrantType: session.GrantTypeUser}) - s, err := built.Service.CreateSession(alice, session.ModeDefault, session.Limits{}) + s, err := built.Service.CreateSession(alice, "", session.Limits{}) if err != nil { t.Fatal(err) } diff --git a/internal/app/harness_context_hybrid_test.go b/internal/app/harness_context_hybrid_test.go index 1b296c096d..324c2213b8 100644 --- a/internal/app/harness_context_hybrid_test.go +++ b/internal/app/harness_context_hybrid_test.go @@ -59,6 +59,7 @@ func harnessHybridConfig(t *testing.T, includeRepository bool, instructionMode s } cfg := harnessPolicyConfig(t, permconfig.HarnessContextSection{EnabledSources: enabled, Kinds: kinds}) cfg.AllowAllTools = true + cfg.GuardrailsDisabled = true deployment := t.TempDir() if err := os.WriteFile(filepath.Join(deployment, "policy.md"), []byte("DEPLOYMENT-INSTRUCTIONS"), 0o600); err != nil { t.Fatal(err) @@ -146,7 +147,8 @@ func TestHarnessContext_HelpdeskDeploymentAndServiceSources(t *testing.T) { t.Fatal(err) } defer b.Close() - sess, err := b.Service.CreateSessionWithProfile(t.Context(), session.ModeDefault, session.Limits{}, server.ProviderSelector{}, server.ProfileNoFS) + // Pass "" to use the server's DefaultMode (ModeYolo from AllowAllTools=true config). + sess, err := b.Service.CreateSessionWithProfile(t.Context(), "", session.Limits{}, server.ProviderSelector{}, server.ProfileNoFS) if err != nil { t.Fatal(err) } diff --git a/internal/app/harness_context_principal_test.go b/internal/app/harness_context_principal_test.go index b2d62d868a..b4c2cb57b9 100644 --- a/internal/app/harness_context_principal_test.go +++ b/internal/app/harness_context_principal_test.go @@ -60,7 +60,7 @@ func TestHarnessContext_PrincipalScopedBindingIsolation(t *testing.T) { binds[index].Add(1) } var requests []port.LLMRequest - cfg := Config{Workspace: t.TempDir(), UserModelDir: t.TempDir(), SoulPath: filepath.Join(t.TempDir(), "soul.md"), PermissionConfigs: []string{file}, UseMock: true, OwnershipEnforced: true, AllowAllTools: true, + cfg := Config{Workspace: t.TempDir(), UserModelDir: t.TempDir(), SoulPath: filepath.Join(t.TempDir(), "soul.md"), PermissionConfigs: []string{file}, UseMock: true, OwnershipEnforced: true, AllowAllTools: true, GuardrailsDisabled: true, MockProvider: mockllm.NewWith([]mockllm.Option{mockllm.WithRequestObserver(func(r port.LLMRequest) { requests = append(requests, r) })}, mockllm.ToolCallTurn(session.NewToolCall("agent-alice", "Subagent", []byte(`{"agent":"owner-agent-alice","prompt":"report owner"}`))), mockllm.TextTurn("alice specialist done"), @@ -107,7 +107,8 @@ func TestHarnessContext_PrincipalScopedBindingIsolation(t *testing.T) { } } ctx := session.WithPrincipal(t.Context(), &session.Principal{Issuer: "issuer", Subject: "alice", GrantType: session.GrantTypeUser}) - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + // Pass "" to use the server's DefaultMode (ModeYolo from AllowAllTools=true config). + sess, err := built.Service.CreateSession(ctx, "", session.Limits{}) if err != nil { t.Fatal(err) } diff --git a/internal/app/harness_context_project_policy_test.go b/internal/app/harness_context_project_policy_test.go index 3dd47ce6be..1338d6c663 100644 --- a/internal/app/harness_context_project_policy_test.go +++ b/internal/app/harness_context_project_policy_test.go @@ -19,6 +19,7 @@ func TestHarnessProjectPolicyIgnoredValueFreeWithoutDroppingDeny(t *testing.T) { cfg := harnessPolicyConfig(t, permconfig.HarnessContextSection{Kinds: harnessEmptyKinds()}) cfg.PermissionsConventional = true cfg.AllowAllTools = true + cfg.GuardrailsDisabled = true cfg.TrustProject = true if err := os.MkdirAll(filepath.Join(cfg.Workspace, ".mecatl"), 0o700); err != nil { t.Fatal(err) diff --git a/internal/app/harness_context_regression_test.go b/internal/app/harness_context_regression_test.go index 355e6388ea..d0e6c32b6e 100644 --- a/internal/app/harness_context_regression_test.go +++ b/internal/app/harness_context_regression_test.go @@ -43,12 +43,13 @@ func TestDefaultHarnessInstructionsFlowToChildren(t *testing.T) { {name: "untrusted", profile: server.ProfileDefault, untrusted: true, denyWrite: true}, } { t.Run(tc.name, func(t *testing.T) { - cfg := Config{Workspace: t.TempDir(), UseMock: true, Headless: true, TrustProject: true, AllowAllTools: true, Shell: "/bin/sh", UserModelDir: t.TempDir(), NoSoul: true} + cfg := Config{Workspace: t.TempDir(), UseMock: true, Headless: true, TrustProject: true, AllowAllTools: true, GuardrailsDisabled: true, Shell: "/bin/sh", UserModelDir: t.TempDir(), NoSoul: true} if tc.explicit { kinds := harnessEmptyKinds() kinds.Instructions = permconfig.HarnessContextKind{Sources: []string{"local"}, Mode: "combine"} cfg = harnessPolicyConfig(t, permconfig.HarnessContextSection{EnabledSources: []string{"local"}, Kinds: kinds}) cfg.AllowAllTools = true + cfg.GuardrailsDisabled = true cfg.Shell = "/bin/sh" } cfg.TrustProject = !tc.untrusted @@ -101,7 +102,8 @@ func TestDefaultHarnessInstructionsFlowToChildren(t *testing.T) { t.Fatal(err) } defer built.Close() - sess, err := built.Service.CreateSessionWithProfile(t.Context(), session.ModeDefault, session.Limits{}, server.ProviderSelector{}, tc.profile) + // Pass "" to use the server's DefaultMode (ModeYolo from AllowAllTools=true config). + sess, err := built.Service.CreateSessionWithProfile(t.Context(), "", session.Limits{}, server.ProviderSelector{}, tc.profile) if err != nil { t.Fatal(err) } diff --git a/internal/app/harness_context_replace_lifetime_test.go b/internal/app/harness_context_replace_lifetime_test.go index 7086974fa1..9df4d80927 100644 --- a/internal/app/harness_context_replace_lifetime_test.go +++ b/internal/app/harness_context_replace_lifetime_test.go @@ -46,7 +46,7 @@ func TestBuildReplacePolicyGlobalStableAcrossReadDiscovery(t *testing.T) { kinds := harnessEmptyKinds() kinds.Instructions = permconfig.HarnessContextKind{Sources: []string{"project", "global"}, Mode: "replace"} cfg := harnessPolicyConfig(t, permconfig.HarnessContextSection{EnabledSources: []string{"project", "global"}, Kinds: kinds}) - cfg.Workspace, cfg.AllowAllTools = root, true + cfg.Workspace, cfg.AllowAllTools, cfg.GuardrailsDisabled = root, true, true global := &changingPolicyGlobal{} cfg.HarnessInstructionSources = []HarnessSourceRegistration[prompt.InstructionAssembler]{ {ID: "project", Provenance: HarnessProvenancePolicy{Fixed: harnessProjectTier}, Bind: func(context.Context, HarnessSourceScope) (prompt.InstructionAssembler, func() error, error) { diff --git a/internal/app/harness_context_restart_test.go b/internal/app/harness_context_restart_test.go index b81e4d8e43..7d9fd3d144 100644 --- a/internal/app/harness_context_restart_test.go +++ b/internal/app/harness_context_restart_test.go @@ -32,6 +32,7 @@ func TestHarnessContext_RestartRebindsCurrentPolicy(t *testing.T) { cfg.MemoryDir = memoryDir cfg.OwnershipEnforced = true cfg.AllowAllTools = true + cfg.GuardrailsDisabled = true cfg.MockProvider = provider cfg.HarnessInstructionSources = []HarnessSourceRegistration[prompt.InstructionAssembler]{{ID: HarnessSourceID(id), Scope: HarnessSourceScopePrincipal, Provenance: HarnessProvenancePolicy{Fixed: "driver"}, Bind: func(_ context.Context, scope HarnessSourceScope) (prompt.InstructionAssembler, func() error, error) { if !scope.Principal.SameIdentity(owner) { diff --git a/internal/app/hierarchy_fork_repair_test.go b/internal/app/hierarchy_fork_repair_test.go index 9e13ef4078..8500e21e6d 100644 --- a/internal/app/hierarchy_fork_repair_test.go +++ b/internal/app/hierarchy_fork_repair_test.go @@ -24,7 +24,7 @@ func TestBuildSamePlacementForkKeepsExaminedInstructions(t *testing.T) { } cfg := hcConfiguredFiles(t, ws) cfg.Workspace = ws.Root() - cfg.AllowAllTools = true + cfg.AllowAllTools, cfg.GuardrailsDisabled = true, true var requests []port.LLMRequest cfg.MockProvider = mockllm.NewWith([]mockllm.Option{mockllm.WithRequestObserver(func(r port.LLMRequest) { requests = append(requests, r) })}, mockllm.ToolCallTurn(session.ToolCall{ID: "read", Name: "Read", Args: json.RawMessage(`{"path":"nested/draft.txt"}`)}), mockllm.TextTurn("parent"), mockllm.TextTurn("fork"), mockllm.TextTurn("fresh")) @@ -142,7 +142,7 @@ func TestBuildForkDiscardsRemovedBinding(t *testing.T) { } cfg := hcConfiguredFiles(t, old) cfg.Workspace = execution.Root() - cfg.AllowAllTools = true + cfg.AllowAllTools, cfg.GuardrailsDisabled = true, true cfg.HarnessInstructionSources[0].Scope = HarnessSourceScopePrincipal binds := 0 cfg.HarnessInstructionSources[0].Bind = func(context.Context, HarnessSourceScope) (prompt.InstructionAssembler, func() error, error) { diff --git a/internal/app/hierarchy_test.go b/internal/app/hierarchy_test.go index 931cfd18ac..3ac7256a1d 100644 --- a/internal/app/hierarchy_test.go +++ b/internal/app/hierarchy_test.go @@ -146,7 +146,7 @@ func TestHierarchyFactorySelectedSeparateSource(t *testing.T) { if err := os.WriteFile(filepath.Join(cfg.Workspace, "nested/draft.txt"), []byte("draft"), 0600); err != nil { t.Fatal(err) } - cfg.AllowAllTools = true + cfg.AllowAllTools, cfg.GuardrailsDisabled = true, true var requests []port.LLMRequest cfg.MockProvider = mockllm.NewWith([]mockllm.Option{mockllm.WithRequestObserver(func(r port.LLMRequest) { requests = append(requests, r) })}, mockllm.ToolCallTurn(session.ToolCall{ID: "read", Name: "Read", Args: json.RawMessage(`{"path":"nested/draft.txt"}`)}), mockllm.TextTurn("done")) @@ -193,7 +193,7 @@ func TestHierarchyFactoryAbsentRootLoadsNestedThroughHostWrapper(t *testing.T) { if err := os.WriteFile(filepath.Join(cfg.Workspace, "nested/draft.txt"), []byte("draft"), 0600); err != nil { t.Fatal(err) } - cfg.AllowAllTools = true + cfg.AllowAllTools, cfg.GuardrailsDisabled = true, true var requests []port.LLMRequest cfg.MockProvider = mockllm.NewWith([]mockllm.Option{mockllm.WithRequestObserver(func(r port.LLMRequest) { requests = append(requests, r) })}, mockllm.ToolCallTurn(session.ToolCall{ID: "read", Name: "Read", Args: json.RawMessage(`{"path":"nested/draft.txt"}`)}), mockllm.TextTurn("done")) @@ -245,7 +245,7 @@ func TestHierarchyFactoryMixedHostSourceCachesEmptyGlobal(t *testing.T) { if err := os.WriteFile(filepath.Join(cfg.Workspace, "nested/draft.txt"), []byte("draft"), 0600); err != nil { t.Fatal(err) } - cfg.AllowAllTools = true + cfg.AllowAllTools, cfg.GuardrailsDisabled = true, true var requests []port.LLMRequest cfg.MockProvider = mockllm.NewWith([]mockllm.Option{mockllm.WithRequestObserver(func(r port.LLMRequest) { requests = append(requests, r) @@ -279,7 +279,7 @@ func TestDefaultSourceChildAndConversationForkInstructionSnapshots(t *testing.T) if err := os.WriteFile(filepath.Join(ws.Root(), "nested/draft.txt"), []byte("draft"), 0600); err != nil { t.Fatal(err) } - cfg := Config{Workspace: ws.Root(), UseMock: true, TrustProject: true, Headless: true, NoSoul: true, NoShell: true, AllowAllTools: true, UserModelDir: t.TempDir()} + cfg := Config{Workspace: ws.Root(), UseMock: true, TrustProject: true, Headless: true, NoSoul: true, NoShell: true, AllowAllTools: true, GuardrailsDisabled: true, UserModelDir: t.TempDir()} var requests []port.LLMRequest cfg.MockProvider = mockllm.NewWith([]mockllm.Option{mockllm.WithRequestObserver(func(r port.LLMRequest) { requests = append(requests, r) @@ -317,14 +317,14 @@ func TestHierarchyNoFSChildKeepsOnlyStartingGuidance(t *testing.T) { for _, configured := range []bool{false, true} { t.Run(fmt.Sprintf("configured=%v", configured), func(t *testing.T) { ws := osfsWorkspaceForHierarchy(t) - cfg := Config{Workspace: ws.Root(), UseMock: true, TrustProject: true, Headless: true, NoSoul: true, NoShell: true, AllowAllTools: true, UserModelDir: t.TempDir()} + cfg := Config{Workspace: ws.Root(), UseMock: true, TrustProject: true, Headless: true, NoSoul: true, NoShell: true, AllowAllTools: true, GuardrailsDisabled: true, UserModelDir: t.TempDir()} prefix := "." if configured { // The execution root is below the source root. Both ancestors are // starting guidance, although only one has source-relative path '.'. prefix = "website" cfg = hcConfiguredFiles(t, ws) - cfg.NoSoul, cfg.NoShell, cfg.AllowAllTools = true, true, true + cfg.NoSoul, cfg.NoShell, cfg.AllowAllTools, cfg.GuardrailsDisabled = true, true, true, true cfg.HarnessInstructionSources[0].Bind = func(context.Context, HarnessSourceScope) (prompt.InstructionAssembler, func() error, error) { return prompt.RootAssembler{Source: ws, SourceID: "source", SourcePrefix: prefix}, nil, nil } @@ -407,7 +407,7 @@ func TestHierarchyDefaultSourceChildPlacementMapping(t *testing.T) { if err := execution.Write(t.Context(), "nested/draft.txt", []byte("execution draft")); err != nil { t.Fatal(err) } - cfg := Config{Workspace: selected.Root(), UseMock: true, TrustProject: true, Headless: true, NoSoul: true, Shell: "/bin/sh", AllowAllTools: true, UserModelDir: t.TempDir()} + cfg := Config{Workspace: selected.Root(), UseMock: true, TrustProject: true, Headless: true, NoSoul: true, Shell: "/bin/sh", AllowAllTools: true, GuardrailsDisabled: true, UserModelDir: t.TempDir()} var requests []port.LLMRequest cfg.MockProvider = mockllm.NewWith([]mockllm.Option{mockllm.WithRequestObserver(func(r port.LLMRequest) { requests = append(requests, r) })}, mockllm.ToolCallTurn(session.ToolCall{ID: "parent-read", Name: "Read", Args: json.RawMessage(`{"path":"nested/draft.txt"}`)}), @@ -552,7 +552,7 @@ func TestHierarchyConfiguredNilRefreshRetainsGuidance(t *testing.T) { kinds := harnessEmptyKinds() kinds.Instructions = permconfig.HarnessContextKind{Sources: []string{"custom", "lower"}, Mode: mode} cfg := harnessPolicyConfig(t, permconfig.HarnessContextSection{EnabledSources: []string{"custom", "lower"}, Kinds: kinds}) - cfg.AllowAllTools, cfg.NoSoul, cfg.NoShell = true, true, true + cfg.AllowAllTools, cfg.NoSoul, cfg.NoShell, cfg.GuardrailsDisabled = true, true, true, true source := &failingRefreshInstructions{} lower := &policyCountWorkspace{Workspace: memfs.NewWorkspace("/lower")} harnessSeed(t, lower.Workspace, "AGENTS.md", "LOWER-GUIDANCE") @@ -662,7 +662,7 @@ func TestHierarchyConfiguredFactoryPartialRefresh(t *testing.T) { kinds := harnessEmptyKinds() kinds.Instructions = permconfig.HarnessContextKind{Sources: ids, Mode: mode} cfg := harnessPolicyConfig(t, permconfig.HarnessContextSection{EnabledSources: ids, Kinds: kinds}) - cfg.AllowAllTools, cfg.NoSoul, cfg.NoShell = true, true, true + cfg.AllowAllTools, cfg.NoSoul, cfg.NoShell, cfg.GuardrailsDisabled = true, true, true, true cfg.HarnessInstructionSources = []HarnessSourceRegistration[prompt.InstructionAssembler]{ {ID: "fault", Provenance: HarnessProvenancePolicy{Fixed: "project"}, Bind: func(context.Context, HarnessSourceScope) (prompt.InstructionAssembler, func() error, error) { return prompt.NewMultiAssembler(prompt.RootAssembler{Source: fault, SourceID: "fault", SourcePrefix: "."}, hcAssembler("FAULT-GLOBAL")), nil, nil diff --git a/internal/app/jev_router_acceptance_test.go b/internal/app/jev_router_acceptance_test.go index 26a8afc21d..4270333201 100644 --- a/internal/app/jev_router_acceptance_test.go +++ b/internal/app/jev_router_acceptance_test.go @@ -255,19 +255,20 @@ func TestJEVRouter_Scenario3_CompositionParity(t *testing.T) { srv, calls := jevTestServer(t, "small", 1, 4, 2) provider := &jevBuildProvider{} built, err := buildIsolated(t, t.Context(), Config{ - Workspace: t.TempDir(), - UserModelDir: t.TempDir(), - UseMock: true, - MockProvider: provider, - NoSoul: true, - NoUserModel: true, - NoShell: true, - AllowAllTools: true, - Model: "parent-model", - RouterBackend: "jev", - RouterJevBaseURL: srv.URL, - TypesafeAPIKey: "secret", - ModelAliases: map[string]string{"tiny": "resolved-model"}, + Workspace: t.TempDir(), + UserModelDir: t.TempDir(), + UseMock: true, + MockProvider: provider, + NoSoul: true, + NoUserModel: true, + NoShell: true, + AllowAllTools: true, + GuardrailsDisabled: true, + Model: "parent-model", + RouterBackend: "jev", + RouterJevBaseURL: srv.URL, + TypesafeAPIKey: "secret", + ModelAliases: map[string]string{"tiny": "resolved-model"}, RouterCategories: []permconfig.RouterCategory{ {Name: "small", Description: "small task", Model: "tiny"}, {Name: "large", Description: "large task", Model: "other-model"}, @@ -330,7 +331,7 @@ func TestJEVRouter_Scenario4_BuildConfiguredRequestLimit(t *testing.T) { provider := &jevBuildProvider{} built, err := buildIsolated(t, t.Context(), Config{ Workspace: t.TempDir(), UserModelDir: t.TempDir(), UseMock: true, MockProvider: provider, - NoSoul: true, NoUserModel: true, NoShell: true, AllowAllTools: true, Model: "inherited-model", + NoSoul: true, NoUserModel: true, NoShell: true, AllowAllTools: true, GuardrailsDisabled: true, Model: "inherited-model", RouterBackend: "jev", RouterJevBaseURL: srv.URL, RouterJevMaximumInputBytes: 1, TypesafeAPIKey: "secret", RouterCategories: []permconfig.RouterCategory{ {Name: "small", Description: "small task", Model: "routed-model"}, @@ -366,7 +367,7 @@ func TestJEVRouter_Scenario4_BuildSharedCapacity(t *testing.T) { provider := &jevBuildProvider{parentDelegated: make(chan struct{}, 9)} built, err := buildIsolated(t, t.Context(), Config{ Workspace: t.TempDir(), UserModelDir: t.TempDir(), UseMock: true, MockProvider: provider, - NoSoul: true, NoUserModel: true, NoShell: true, AllowAllTools: true, Model: "parent-model", + NoSoul: true, NoUserModel: true, NoShell: true, AllowAllTools: true, GuardrailsDisabled: true, Model: "parent-model", RouterBackend: "jev", RouterJevBaseURL: srv.URL, TypesafeAPIKey: "secret", RouterCategories: []permconfig.RouterCategory{{Name: "small", Description: "small task", Model: "routed-model"}}, }) @@ -431,7 +432,7 @@ func TestJevLowConfidenceCandidateSurvivesRealDelegation(t *testing.T) { provider := &jevBuildProvider{} built, err := buildIsolated(t, t.Context(), Config{ Workspace: t.TempDir(), UserModelDir: t.TempDir(), UseMock: true, MockProvider: provider, - NoSoul: true, NoUserModel: true, NoShell: true, AllowAllTools: true, Model: "inherited-model", + NoSoul: true, NoUserModel: true, NoShell: true, AllowAllTools: true, GuardrailsDisabled: true, Model: "inherited-model", RouterBackend: "jev", RouterJevModel: "jev-1.13.0", RouterJevBaseURL: srv.URL, RouterJevMinimumConfidence: 0.50, TypesafeAPIKey: "secret", ModelAliases: map[string]string{"candidate-alias": "locally-resolved-candidate"}, @@ -529,7 +530,7 @@ func TestJEVRouter_Scenario2_ExistingRoutingSemantics(t *testing.T) { provider := &jevBuildProvider{subagentArgs: tc.args} cfg := Config{ Workspace: t.TempDir(), UserModelDir: t.TempDir(), UseMock: true, MockProvider: provider, - NoSoul: true, NoShell: true, AllowAllTools: true, Model: "parent-model", + NoSoul: true, NoShell: true, AllowAllTools: true, GuardrailsDisabled: true, Model: "parent-model", } applyJevRouting(&cfg, srv) if tc.agentSetup { diff --git a/internal/app/learned_skills_composition_test.go b/internal/app/learned_skills_composition_test.go index c7bde861b0..15b971f961 100644 --- a/internal/app/learned_skills_composition_test.go +++ b/internal/app/learned_skills_composition_test.go @@ -25,7 +25,7 @@ func TestBuildOwnerlessSessionHydratesLearnedSkillIntoTool(t *testing.T) { built, err := Build(context.Background(), Config{ Workspace: workspace, Model: "mock", StoreDir: t.TempDir(), UserModelDir: userModelDir, - NoSoul: true, AllowAllTools: true, MockProvider: mockllm.New( + NoSoul: true, AllowAllTools: true, GuardrailsDisabled: true, MockProvider: mockllm.New( mockllm.ToolCallTurn(session.NewToolCall("skill-ownerless", "Skill", []byte(`{"name":"ownerless-recovery"}`))), mockllm.TextTurn("done"), ), @@ -35,7 +35,8 @@ func TestBuildOwnerlessSessionHydratesLearnedSkillIntoTool(t *testing.T) { } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + // Pass "" to use the server's DefaultMode (ModeYolo from AllowAllTools=true config). + sess, err := built.Service.CreateSession(context.Background(), "", session.Limits{}) if err != nil { t.Fatal(err) } @@ -63,14 +64,15 @@ func TestBuildRestartHydratesCallerBoundLearnedSkillIntoListAndTool(t *testing.T config := func(provider *mockllm.Provider) Config { return Config{ Workspace: workspace, Model: "mock", StoreDir: storeDir, UserModelDir: userModelDir, - NoSoul: true, OwnershipEnforced: true, AllowAllTools: true, MockProvider: provider, + NoSoul: true, OwnershipEnforced: true, AllowAllTools: true, GuardrailsDisabled: true, MockProvider: provider, } } built1, err := Build(context.Background(), config(mockllm.New(mockllm.TextTurn("created")))) if err != nil { t.Fatal(err) } - sess, err := built1.Service.CreateSession(aliceCtx, session.ModeDefault, session.Limits{}) + // Pass "" to use the server's DefaultMode (ModeYolo from AllowAllTools=true config). + sess, err := built1.Service.CreateSession(aliceCtx, "", session.Limits{}) if err != nil { built1.Close() t.Fatal(err) @@ -131,17 +133,18 @@ func TestReplicaHydrationConvergesAcrossReplacementAndRollback(t *testing.T) { ) replicaB, err := Build(context.Background(), Config{ Workspace: workspace, Model: "mock", StoreDir: storeDir, UserModelDir: userModelDir, - NoSoul: true, Headless: true, OwnershipEnforced: true, AllowAllTools: true, MockProvider: provider, + NoSoul: true, Headless: true, OwnershipEnforced: true, AllowAllTools: true, GuardrailsDisabled: true, MockProvider: provider, }) if err != nil { t.Fatal(err) } defer replicaB.Close() - aliceSession, err := replicaB.Service.CreateSession(aliceCtx, session.ModeDefault, session.Limits{}) + // Pass "" to use the server's DefaultMode (ModeYolo from AllowAllTools=true config). + aliceSession, err := replicaB.Service.CreateSession(aliceCtx, "", session.Limits{}) if err != nil { t.Fatal(err) } - bobSession, err := replicaB.Service.CreateSession(bobCtx, session.ModeDefault, session.Limits{}) + bobSession, err := replicaB.Service.CreateSession(bobCtx, "", session.Limits{}) if err != nil { t.Fatal(err) } diff --git a/internal/app/listdir_external_posture_test.go b/internal/app/listdir_external_posture_test.go index 2d1c665658..b779e06840 100644 --- a/internal/app/listdir_external_posture_test.go +++ b/internal/app/listdir_external_posture_test.go @@ -77,7 +77,7 @@ func TestListDirExternalAbsolutePathFollowsMainPosture(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), postureSessionMode(tc.posture), session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -119,7 +119,7 @@ func TestListDirExternalConfiguredRulesSurviveYolo(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { inner := permpolicy.NewPolicy([]governance.Rule{{Scope: governance.ScopeUser, Tool: "ListDir", Effect: tc.effect}}, nil) - decision := newEscapePolicy(inner, PostureYolo).Evaluate(context.Background(), "s1", session.ModeDefault, call, ws) + decision := newEscapePolicy(inner).Evaluate(context.Background(), "s1", session.ModeYolo, call, ws) if decision.Decision.Effect != tc.effect { t.Fatalf("configured %s became %s at yolo", tc.effect, decision.Decision.Effect) } @@ -187,7 +187,7 @@ func TestListDirPseudoFSAliasApprovedAskStillRefused(t *testing.T) { t.Fatal(err) } defer built.Close() - sess, err := built.Service.CreateSession(t.Context(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(t.Context(), session.ModeYolo, session.Limits{}) if err != nil { t.Fatal(err) } @@ -216,7 +216,7 @@ func TestListDirExternalAuthorityDenialStillWins(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(ctx, session.ModeYolo, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } diff --git a/internal/app/mcp_broker_authorization_vertical_test.go b/internal/app/mcp_broker_authorization_vertical_test.go index 38c5eba715..2fd970723e 100644 --- a/internal/app/mcp_broker_authorization_vertical_test.go +++ b/internal/app/mcp_broker_authorization_vertical_test.go @@ -83,14 +83,15 @@ func TestSessionMCPAuthorization_GrantRegressionParksAndResumes(t *testing.T) { }) fixture.mcpClient = fixture.clientWithRoots(roots) built, err := app.Build(ctx, app.Config{ - Workspace: t.TempDir(), - UserModelDir: t.TempDir(), - StoreDir: t.TempDir(), - MockProvider: provider, - NoSoul: true, - AllowAllTools: true, - OwnershipEnforced: true, - MCPAuthority: declaration, + Workspace: t.TempDir(), + UserModelDir: t.TempDir(), + StoreDir: t.TempDir(), + MockProvider: provider, + NoSoul: true, + AllowAllTools: true, + GuardrailsDisabled: true, + OwnershipEnforced: true, + MCPAuthority: declaration, MCPBrokerDiscovered: []mcpbroker.ToolDefinition{{ Backend: "github", Name: toolName, Description: "read protected data", Schema: json.RawMessage(`{"type":"object"}`), ReadOnly: true, }}, @@ -118,7 +119,7 @@ func TestSessionMCPAuthorization_GrantRegressionParksAndResumes(t *testing.T) { owner := &session.Principal{Issuer: "https://identity.example", Subject: "alice", GrantType: session.GrantTypeUser} ownerCtx := session.WithPrincipal(ctx, owner) intruderCtx := session.WithPrincipal(ctx, &session.Principal{Issuer: owner.Issuer, Subject: "mallory", GrantType: session.GrantTypeUser}) - sess, err := built.Service.CreateSession(ownerCtx, session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(ownerCtx, "", session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } diff --git a/internal/app/microvm_harness_context_test.go b/internal/app/microvm_harness_context_test.go index 9e3b5c3cd4..006b56a1c5 100644 --- a/internal/app/microvm_harness_context_test.go +++ b/internal/app/microvm_harness_context_test.go @@ -430,7 +430,7 @@ func TestMicroVMSuccessorRetainsPlacementAndContext(t *testing.T) { if !selected { cfg.PermissionConfigs = nil } - cfg.Shell, cfg.AllowAllTools = "/bin/sh", true + cfg.Shell, cfg.AllowAllTools, cfg.GuardrailsDisabled = "/bin/sh", true, true var requests []port.LLMRequest cfg.MockProvider = mockllm.NewWith([]mockllm.Option{mockllm.WithRequestObserver(func(r port.LLMRequest) { requests = append(requests, r) })}, mockllm.ToolCallTurn(session.NewToolCall("still-attached", "Shell", json.RawMessage(`{"command":"test -f AGENTS.md"}`))), mockllm.TextTurn("successor")) @@ -439,7 +439,8 @@ func TestMicroVMSuccessorRetainsPlacementAndContext(t *testing.T) { t.Fatal(err) } defer built.Close() - parent, err := built.Service.CreateSession(t.Context(), session.ModeDefault, session.Limits{}) + // Pass "" to use the server's DefaultMode (ModeYolo from AllowAllTools=true config). + parent, err := built.Service.CreateSession(t.Context(), "", session.Limits{}) if err != nil { t.Fatal(err) } @@ -569,7 +570,7 @@ func TestMicroVMRepositorySelectionDoesNotGrantProjectAdmission(t *testing.T) { cfg := microVMHarnessConfig(t, daemon, "repository") cfg.TrustProject = false cfg.Posture, cfg.PostureFlagSet = PostureAuto, true - built, err := buildIsolated(t, t.Context(), cfg) + built, err := buildIsolated(t, t.Context(), withInertEnforcingChecker(cfg)) if err != nil { t.Fatal(err) } @@ -601,7 +602,7 @@ func TestMicroVMRequiredRepositorySourceFailsForNoFS(t *testing.T) { func TestMicroVMSelectedContextFreshnessAndReadEvidence(t *testing.T) { daemon := startPlacementTestDaemon(t) cfg := microVMHarnessConfig(t, daemon, "repository") - cfg.AllowAllTools = true + cfg.AllowAllTools, cfg.GuardrailsDisabled = true, true diag := newCapturingDiagnostics() cfg.Diagnostics = diag var requests []port.LLMRequest @@ -612,7 +613,8 @@ func TestMicroVMSelectedContextFreshnessAndReadEvidence(t *testing.T) { t.Fatal(err) } defer built.Close() - sess, err := built.Service.CreateSession(t.Context(), session.ModeDefault, session.Limits{}) + // Pass "" to use the server's DefaultMode (ModeYolo from AllowAllTools=true config). + sess, err := built.Service.CreateSession(t.Context(), "", session.Limits{}) if err != nil { t.Fatal(err) } diff --git a/internal/app/microvm_lifecycle_e2e_test.go b/internal/app/microvm_lifecycle_e2e_test.go index 4c4feca14f..8683313bd5 100644 --- a/internal/app/microvm_lifecycle_e2e_test.go +++ b/internal/app/microvm_lifecycle_e2e_test.go @@ -495,7 +495,7 @@ func TestMicroVMDefaultPlacementRetainsOneExactAttachmentUntilCloseSession(t *te } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(ctx, "", session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -566,11 +566,11 @@ harness_context: } defer built.Close() - first, err := built.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + first, err := built.Service.CreateSession(ctx, "", session.Limits{}) if err != nil { t.Fatal(err) } - second, err := built.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + second, err := built.Service.CreateSession(ctx, "", session.Limits{}) if err != nil { t.Fatal(err) } @@ -638,7 +638,7 @@ func TestMicroVMOperatorJourneyIsLazyIsolatedAndRestartExact(t *testing.T) { config := func(provider *mockllm.Provider) Config { configured, err := ConfigureExecution(Config{ Workspace: hostSource, StoreDir: storeDir, UseMock: true, MockProvider: provider, - Shell: "/bin/sh", AllowAllTools: true, TrustProject: true, PermissionConfigs: []string{settings}, + Shell: "/bin/sh", AllowAllTools: true, GuardrailsDisabled: true, TrustProject: true, PermissionConfigs: []string{settings}, MicroVMReadyRequest: func(microvmmanager.GuestEgressSelection) (microvmmanager.ReadyRequest, error) { return microvmmanager.ReadyRequest{}, nil }, @@ -675,7 +675,7 @@ func TestMicroVMOperatorJourneyIsLazyIsolatedAndRestartExact(t *testing.T) { t.Fatalf("composition allocated a microVM: readiness=%d create=%d", manager.calls, daemon.operationCount("create")) } - noFS, err := first.Service.CreateSessionWithProfile(ctx, session.ModeDefault, session.Limits{}, server.ProviderSelector{}, server.ProfileNoFS) + noFS, err := first.Service.CreateSessionWithProfile(ctx, "", session.Limits{}, server.ProviderSelector{}, server.ProfileNoFS) if err != nil { first.Close() t.Fatalf("create no-fs session: %v", err) @@ -685,12 +685,12 @@ func TestMicroVMOperatorJourneyIsLazyIsolatedAndRestartExact(t *testing.T) { t.Fatalf("no-fs touched microVM state: ref=%+v readiness=%d create=%d", noFS.EnvironmentRef, manager.calls, daemon.operationCount("create")) } - one, err := first.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + one, err := first.Service.CreateSession(ctx, "", session.Limits{}) if err != nil { first.Close() t.Fatalf("create first default session: %v", err) } - two, err := first.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + two, err := first.Service.CreateSession(ctx, "", session.Limits{}) if err != nil { first.Close() t.Fatalf("create second default session: %v", err) @@ -815,7 +815,7 @@ func TestMicroVMScheduledPlacementSurvivesFiresRestartAndDeletion(t *testing.T) } built, err := Build(ctx, Config{ Workspace: workspace, StoreDir: storeDir, UseMock: true, MockProvider: llm, - Shell: "/bin/sh", AllowAllTools: true, TrustProject: true, + Shell: "/bin/sh", AllowAllTools: true, GuardrailsDisabled: true, TrustProject: true, PlacementProvider: provider, PlacementScope: scope, EnvironmentForkers: map[session.EnvironmentKind]tool.EnvironmentForker{session.EnvironmentKind("microvm"): provider}, EnvironmentMergers: map[session.EnvironmentKind]tool.EnvironmentMerger{session.EnvironmentKind("microvm"): provider}, @@ -831,7 +831,7 @@ func TestMicroVMScheduledPlacementSurvivesFiresRestartAndDeletion(t *testing.T) mockllm.ToolCallTurn(session.NewToolCall("write-first", "Shell", json.RawMessage(`{"command":"printf first > shared.txt"}`))), mockllm.TextTurn("first fire complete"), )) - origin, err := first.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + origin, err := first.Service.CreateSession(ctx, "", session.Limits{}) if err != nil { first.Close() t.Fatal(err) diff --git a/internal/app/multiprovider_e2e_test.go b/internal/app/multiprovider_e2e_test.go index 9e3544fe06..d695a77ce0 100644 --- a/internal/app/multiprovider_e2e_test.go +++ b/internal/app/multiprovider_e2e_test.go @@ -120,14 +120,14 @@ func TestMultiProviderE2E(t *testing.T) { } // An unknown provider still ⇒ InvalidArgument (never a silent fallback). - _, err = svc.CreateSessionWithProvider(ctx, session.ModeDefault, defaultLimits(), + _, err = svc.CreateSessionWithProvider(ctx, "", defaultLimits(), server.ProviderSelector{ProviderID: "does-not-exist"}) if !errors.Is(err, server.ErrInvalidArgument) { t.Fatalf("unknown-provider create error = %v, want ErrInvalidArgument", err) } // (5) zero selector ⇒ the shared default-provider engine (openai is the default). - zeroSess, err := svc.CreateSession(ctx, session.ModeDefault, defaultLimits()) + zeroSess, err := svc.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession(zero): %v", err) } @@ -183,7 +183,7 @@ func TestMultiProviderCapabilityEcho(t *testing.T) { // (REPLY-FROM-openrouter) AND that session's caps reflect openrouter's intersected // caps (Image:true), in ONE test. imgModel, _ := firstImageModel(t, providerOpenRouter) - orSess, err := svc.CreateSessionWithProvider(ctx, session.ModeDefault, defaultLimits(), + orSess, err := svc.CreateSessionWithProvider(ctx, "", defaultLimits(), server.ProviderSelector{ProviderID: providerOpenRouter, ModelID: imgModel}) if err != nil { t.Fatalf("create openrouter session: %v", err) @@ -203,7 +203,7 @@ func TestMultiProviderCapabilityEcho(t *testing.T) { // intersection is false, it DIFFERS from the openrouter echo (per-session), AND the // turn routes to the openai-bound provider — proving routing and caps agree on the // SAME provider per session. - oaSess, err := svc.CreateSessionWithProvider(ctx, session.ModeDefault, defaultLimits(), + oaSess, err := svc.CreateSessionWithProvider(ctx, "", defaultLimits(), server.ProviderSelector{ProviderID: providerOpenAI, ModelID: imgModel}) if err != nil { t.Fatalf("create openai session: %v", err) @@ -221,7 +221,7 @@ func TestMultiProviderCapabilityEcho(t *testing.T) { // (5) zero-selector session ⇒ echo == DefaultCapabilities (the default provider's // intersected caps). The default is openai (Image:false) on the empty model. - zeroSess, err := svc.CreateSession(ctx, session.ModeDefault, defaultLimits()) + zeroSess, err := svc.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("create zero-selector session: %v", err) } @@ -272,7 +272,7 @@ func TestMultiProviderResolvedModelEcho(t *testing.T) { // (a) zero-selector default session ⇒ DefaultResolvedModel: the default provider // (openai) + the resolved cfg.Model ("gpt-5"), echoed verbatim — NOT the empty // request model_id. The context window is the catalog's gpt-5 limit (>0). - zeroSess, err := svc.CreateSession(ctx, session.ModeDefault, defaultLimits()) + zeroSess, err := svc.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession(zero): %v", err) } @@ -288,7 +288,7 @@ func TestMultiProviderResolvedModelEcho(t *testing.T) { // model), not the default and not a naive read-back. Use a real catalogued model // so the window resolves from the catalog. orModel, _ := firstImageModel(t, providerOpenRouter) - orSess, err := svc.CreateSessionWithProvider(ctx, session.ModeDefault, defaultLimits(), + orSess, err := svc.CreateSessionWithProvider(ctx, "", defaultLimits(), server.ProviderSelector{ProviderID: providerOpenRouter, ModelID: orModel}) if err != nil { t.Fatalf("CreateSessionWithProvider(openrouter): %v", err) @@ -339,7 +339,7 @@ func TestServerConfiguredDefaultModelEcho(t *testing.T) { defer built.Close() svc := built.Service - sess, err := svc.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := svc.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession(zero-selector): %v", err) } @@ -365,7 +365,7 @@ func TestServerConfiguredDefaultModelEcho(t *testing.T) { // default: the echo and the routing both follow the selector (the configured // tier sits BELOW client-side choices in the precedence chain). const selModel = "gpt-5" - selSess, err := svc.CreateSessionWithProvider(ctx, session.ModeDefault, defaultLimits(), + selSess, err := svc.CreateSessionWithProvider(ctx, "", defaultLimits(), server.ProviderSelector{ProviderID: providerOpenAI, ModelID: selModel}) if err != nil { t.Fatalf("CreateSessionWithProvider(openai selector): %v", err) @@ -415,7 +415,7 @@ func TestResolvedModelMCPOnlySessionMatchesDefaultWindow(t *testing.T) { svc := built.Service // The default-path window (no per-session engine) — the single-source value. - dfltSess, err := svc.CreateSession(ctx, session.ModeDefault, defaultLimits()) + dfltSess, err := svc.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession(default): %v", err) } @@ -541,10 +541,11 @@ func TestSubAgentPinsAnthropic(t *testing.T) { } built, err := buildIsolated(t, ctx, Config{ - Workspace: workspace, - NoSoul: true, - AgentsDirs: []string{agentsDir}, - AllowAllTools: true, // --yolo: auto-approve the routed Subagent + Workspace: workspace, + NoSoul: true, + AgentsDirs: []string{agentsDir}, + AllowAllTools: true, // --yolo: auto-approve the routed Subagent + GuardrailsDisabled: true, envDetector: fakeEnv(map[string]string{ "OPENAI_API_KEY": "sk-x", "ANTHROPIC_API_KEY": "sk-x", @@ -565,7 +566,7 @@ func TestSubAgentPinsAnthropic(t *testing.T) { defer built.Close() svc := built.Service - sess, err := svc.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := svc.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } diff --git a/internal/app/openai_codex_composition_test.go b/internal/app/openai_codex_composition_test.go index c824118bfb..2e196073a5 100644 --- a/internal/app/openai_codex_composition_test.go +++ b/internal/app/openai_codex_composition_test.go @@ -139,7 +139,7 @@ func TestOpenAICodexCompositionScenario(t *testing.T) { t.Fatalf("ListModels provider inventory = Codex:%t API-OpenAI:%t, want both independently selectable", sawCodex, sawAPI) } - codexSession, err := built.Service.CreateSessionWithProvider(ctx, session.ModeDefault, defaultLimits(), + codexSession, err := built.Service.CreateSessionWithProvider(ctx, "", defaultLimits(), server.ProviderSelector{ProviderID: providerOpenAICodex, ModelID: "gpt-5"}) if err != nil { t.Fatalf("create Codex session: %v", err) @@ -159,7 +159,7 @@ func TestOpenAICodexCompositionScenario(t *testing.T) { t.Fatalf("continuation request omitted the real Read result: %s", bodies[1]) } - apiSession, err := built.Service.CreateSessionWithProvider(ctx, session.ModeDefault, defaultLimits(), + apiSession, err := built.Service.CreateSessionWithProvider(ctx, "", defaultLimits(), server.ProviderSelector{ProviderID: providerOpenAI, ModelID: "gpt-5"}) if err != nil { t.Fatalf("create API OpenAI session: %v", err) @@ -337,7 +337,7 @@ func TestOpenAICodexExplicitSelectorRehydrates(t *testing.T) { if err != nil { t.Fatalf("Build #1: %v", err) } - sess, err := built1.Service.CreateSessionWithProvider(ctx, session.ModeDefault, defaultLimits(), + sess, err := built1.Service.CreateSessionWithProvider(ctx, "", defaultLimits(), server.ProviderSelector{ProviderID: providerOpenAICodex, ModelID: "gpt-5", ReasoningEffort: "high"}) if err != nil { built1.Close() @@ -382,7 +382,7 @@ func TestZeroSelectorStillFollowsDeploymentDefault(t *testing.T) { if err != nil { t.Fatalf("Build #1: %v", err) } - sess, err := built1.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := built1.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { built1.Close() t.Fatalf("CreateSession: %v", err) @@ -463,7 +463,7 @@ func TestOpenAICodexSecretSentinels(t *testing.T) { } addArtifact("provider status", statusJSON) - sess, err := built.Service.CreateSessionWithProvider(ctx, session.ModeDefault, defaultLimits(), + sess, err := built.Service.CreateSessionWithProvider(ctx, "", defaultLimits(), server.ProviderSelector{ProviderID: providerOpenAICodex, ModelID: "gpt-5"}) if err != nil { built.Close() diff --git a/internal/app/pathescape_guardrail_test.go b/internal/app/pathescape_guardrail_test.go index 16677a43d2..7cbabe77c1 100644 --- a/internal/app/pathescape_guardrail_test.go +++ b/internal/app/pathescape_guardrail_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "strings" + "sync/atomic" "testing" "github.com/stacklok/mecatl/engine/adapter/mockllm" @@ -11,9 +12,19 @@ import ( "github.com/stacklok/mecatl/engine/governance" "github.com/stacklok/mecatl/engine/port" "github.com/stacklok/mecatl/engine/session" + "github.com/stacklok/mecatl/internal/adapter/modelhook" "github.com/stacklok/mecatl/internal/adapter/osfs" ) +// countingVerdictChecker counts Check calls and answers unsafe, so a consulted +// checker would also flip the escape decision to Deny. +type countingVerdictChecker struct{ calls atomic.Int32 } + +func (c *countingVerdictChecker) Check(context.Context, modelhook.CheckRequest) (modelhook.CheckResult, error) { + c.calls.Add(1) + return modelhook.CheckResult{Verdict: modelhook.Verdict{Safe: new(false), Reason: "must not be consulted"}}, nil +} + // pathescape_guardrail_test.go pins guardrail-routed escape checking: at // posture auto WITH the operator-tier // escape knob (guardrails.escape) configured, an out-of-root escape is routed @@ -35,18 +46,24 @@ func buildRoutedEscapePolicy(t *testing.T, cfg Config, checkerTurns ...mockllm.T if checker == nil { t.Fatal("buildGuardrailsChecker returned nil — the test scripts the checker via the same mockllm path the composition uses") } - return newEscapePolicy(permpolicy.NewPolicy(defaultRules(), nil), PostureAuto, withEscapeGuardrailRoute(checker)) + return newEscapePolicy(permpolicy.NewPolicy(defaultRules(), nil), withEscapeGuardrailRoute(checker)) } -// evalEscapeAtAuto evaluates one call through the routed policy against a -// real osfs workspace rooted at the fixture workspace. +// evalEscapeAtAuto evaluates one call in an auto-mode session through the +// routed policy against a real osfs workspace rooted at the fixture workspace. func evalEscapeAtAuto(t *testing.T, p port.PermissionPolicy, workspace string, c session.ToolCall) governance.PermissionDecision { + t.Helper() + return evalEscapeInMode(t, p, workspace, session.ModeAuto, c) +} + +// evalEscapeInMode evaluates one call in a session of the given mode. +func evalEscapeInMode(t *testing.T, p port.PermissionPolicy, workspace string, mode session.PermissionMode, c session.ToolCall) governance.PermissionDecision { t.Helper() ws, err := osfs.NewWorkspace(workspace) if err != nil { t.Fatalf("NewWorkspace: %v", err) } - return p.Evaluate(context.Background(), session.SessionID("s1"), session.ModeDefault, c, ws).Decision + return p.Evaluate(context.Background(), session.SessionID("s1"), mode, c, ws).Decision } // TestPathEscapePosture_GuardrailRoutedEscape pins AC-W2-G2: auto + the @@ -106,31 +123,26 @@ func TestPathEscapePosture_GuardrailRoutedEscape(t *testing.T) { } }) - t.Run("yolo and strict never route", func(t *testing.T) { + t.Run("non-auto session modes never consult the armed route", func(t *testing.T) { t.Parallel() - // The route is the AUTO-only knob: yolo demotes guardrails to advisory - // and never spends a checker call on a decision the posture - // already made; strict/trusted keep their own Scenario-4 escape Ask. - for _, posture := range []Posture{PostureYolo, PostureStrict, PostureTrusted} { - posture := posture - t.Run(posture.String(), func(t *testing.T) { + // The route is armed but consulted only for auto-mode sessions: a yolo + // session demotes guardrails to advisory and never spends a checker + // call; the ordinary modes keep their own Scenario-4 escape Ask. + for _, mode := range []session.PermissionMode{session.ModePlan, session.ModeDefault, session.ModeAccept, session.ModeYolo} { + t.Run(string(mode), func(t *testing.T) { t.Parallel() - // Script a verdict that would DENY if consulted; assert the - // posture row wins and the checker was never consulted (a - // consumed verdict would flip the outcome). - checker := buildGuardrailsChecker(Config{UseMock: true, GuardrailsModel: "checker-model"}, nil, - mockllm.New(mockllm.TextTurn(`{"assessment":"prohibited","concerns":[{"ref":"C1","category":"authority_crossing","rationale":"must not be consulted","source_ref":"call"}],"evidence":[],"missing_evidence":[]}`)), "mock", "m") - p := newEscapePolicy(permpolicy.NewPolicy(defaultRules(), nil), posture, withEscapeGuardrailRoute(checker)) - d := evalEscapeAtAuto(t, p, f.workspace, call) - switch posture { - case PostureYolo: - if d.Effect != governance.Allow { - t.Fatalf("yolo escape = %v, want Allow (the checker must never be consulted at yolo)", d.Effect) - } - default: - if d.Effect != governance.Ask { - t.Fatalf("%s escape = %v, want the Scenario-4 Ask (the route is auto-only)", posture, d.Effect) - } + checker := &countingVerdictChecker{} + p := newEscapePolicy(permpolicy.NewPolicy(defaultRules(), nil), withEscapeGuardrailRoute(checker)) + d := evalEscapeInMode(t, p, f.workspace, mode, call) + want := governance.Ask + if mode == session.ModeYolo { + want = governance.Allow + } + if d.Effect != want { + t.Fatalf("%s-mode escape = %v (%q), want %v", mode, d.Effect, d.Reason, want) + } + if n := checker.calls.Load(); n != 0 { + t.Fatalf("%s-mode escape consulted the checker %d times, want 0 (the route is auto-only)", mode, n) } }) } @@ -140,7 +152,7 @@ func TestPathEscapePosture_GuardrailRoutedEscape(t *testing.T) { t.Parallel() // Without the escape knob the auto read row is a plain Allow (Shell // parity) with NO checker call — the route is strictly opt-in. - p := newEscapePolicy(permpolicy.NewPolicy(defaultRules(), nil), PostureAuto) + p := newEscapePolicy(permpolicy.NewPolicy(defaultRules(), nil)) d := evalEscapeAtAuto(t, p, f.workspace, call) if d.Effect != governance.Allow { t.Fatalf("auto escape without the knob = %v, want Allow (no route, no checker spend)", d.Effect) @@ -153,7 +165,7 @@ func TestPathEscapePosture_GuardrailRoutedEscape(t *testing.T) { {Scope: governance.ScopeUser, Tool: "Read", Effect: governance.Deny}, }, nil) checker := buildGuardrailsChecker(cfg, nil, mockllm.New(mockllm.TextTurn(`{"assessment":"acceptable","concerns":[],"evidence":[],"missing_evidence":[]}`)), "mock", "m") - p := newEscapePolicy(inner, PostureAuto, withEscapeGuardrailRoute(checker)) + p := newEscapePolicy(inner, withEscapeGuardrailRoute(checker)) d := evalEscapeAtAuto(t, p, f.workspace, call) if d.Effect != governance.Deny { t.Fatalf("configured deny + routed escape = %v, want Deny — the route runs only after the inner fold", d.Effect) @@ -177,13 +189,14 @@ func TestPathEscapePosture_GuardrailRoutedEscape(t *testing.T) { } bcfg := escapeCfg(t, f, PostureAuto, turns...) bcfg.GuardrailsModel = "checker-model" + bcfg.GuardrailsDisabled = false // escapeCfg declares off; this test configures a checker instead bcfg.GuardrailsEscape = true built, err := buildIsolated(t, context.Background(), bcfg) if err != nil { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), session.ModeAuto, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -202,6 +215,7 @@ func TestPathEscapePosture_GuardrailRoutedEscape(t *testing.T) { f := setupEscapeFS(t) bcfg := escapeCfg(t, f, PostureAuto, readEscapeTurns(f.target)...) bcfg.GuardrailsModel = "checker-model" + bcfg.GuardrailsDisabled = false // escapeCfg declares off; this test configures a checker instead // Isolate the escape-route assertion from the contextual guardrails' default inbound Read // coverage: an explicit unrelated rule keeps guardrails enabled without // reviewing this Read result through the ordinary tool boundary. @@ -211,7 +225,7 @@ func TestPathEscapePosture_GuardrailRoutedEscape(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), session.ModeAuto, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } diff --git a/internal/app/pathescape_scenario2_test.go b/internal/app/pathescape_scenario2_test.go index f343addfc3..47599d49ff 100644 --- a/internal/app/pathescape_scenario2_test.go +++ b/internal/app/pathescape_scenario2_test.go @@ -58,13 +58,15 @@ func setupEscapeFS(t *testing.T) escapeFixture { } // escapeCfg returns the offline Build config for a relaxed-read e2e: mockllm -// provider, real osfs workspace, no network. Posture is set by the caller. +// provider, real osfs workspace, no network. Posture is set by the caller; an +// auto deployment gets an inert enforcing checker (auto requires one). func escapeCfg(t *testing.T, f escapeFixture, posture Posture, turns ...mockllm.Turn) Config { t.Helper() - return Config{ + cfg := Config{ Workspace: f.workspace, NoSoul: true, Posture: posture, + GuardrailsDisabled: true, PostureFlagSet: true, envDetector: fakeEnv(map[string]string{"OPENAI_API_KEY": "sk-openai"}), liveModelHTTPClient: offlineHTTPClient(), @@ -72,6 +74,10 @@ func escapeCfg(t *testing.T, f escapeFixture, posture Posture, turns ...mockllm. return mockllm.New(turns...) }, } + if posture == PostureAuto { + cfg = withInertEnforcingChecker(cfg) + } + return cfg } // auditRecorder is a test ToolCallRecorder capturing each call verbatim. @@ -132,7 +138,7 @@ func TestPathEscapePosture_Scenario2_YoloReadEscapeAllowed(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -162,7 +168,7 @@ func TestPathEscapePosture_Scenario2_AutoReadEscapeAllowed(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), session.ModeAuto, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -193,7 +199,7 @@ func TestPathEscapePosture_Scenario2_ReadEscapeAuditParity(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -300,7 +306,7 @@ func TestPathEscapePosture_Scenario2_ProcEnvironNotExposed(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -354,7 +360,7 @@ func TestPathEscapePosture_Scenario2_NestedSymlinkEscapeRejected(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -389,7 +395,7 @@ func TestPathEscapePosture_Scenario2_RestartRehydratesRelaxedWorkspace(t *testin if err != nil { t.Fatalf("Build #1: %v", err) } - sess, err := built1.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built1.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{}) if err != nil { built1.Close() t.Fatalf("CreateSession: %v", err) diff --git a/internal/app/pathescape_scenario3_test.go b/internal/app/pathescape_scenario3_test.go index cccdf00ff1..f0f27942cc 100644 --- a/internal/app/pathescape_scenario3_test.go +++ b/internal/app/pathescape_scenario3_test.go @@ -62,13 +62,14 @@ func setupWriteFS(t *testing.T) writeFixture { } // writeEscapeCfg mirrors escapeCfg but scripts Write turns (posture set by -// the caller). +// the caller; an auto deployment gets an inert enforcing checker). func writeEscapeCfg(t *testing.T, f writeFixture, posture Posture, turns ...mockllm.Turn) Config { t.Helper() - return Config{ + cfg := Config{ Workspace: f.workspace, NoSoul: true, Posture: posture, + GuardrailsDisabled: true, PostureFlagSet: true, envDetector: fakeEnv(map[string]string{"OPENAI_API_KEY": "sk-openai"}), liveModelHTTPClient: offlineHTTPClient(), @@ -76,6 +77,10 @@ func writeEscapeCfg(t *testing.T, f writeFixture, posture Posture, turns ...mock return mockllm.New(turns...) }, } + if posture == PostureAuto { + cfg = withInertEnforcingChecker(cfg) + } + return cfg } // scenario3Call marshals one tool call with the given arg map. @@ -84,10 +89,12 @@ func scenario3Call(id, name string, argMap map[string]string) session.ToolCall { return session.NewToolCall(session.ToolCallID(id), name, json.RawMessage(args)) } -// TestPathEscapePosture_Scenario3_YoloWriteEscapeAllowed pins AC3.1: at -// posture yolo, Write to an out-of-root absolute path creates the file (and a -// second Write replaces it) — the relax flows through the ordinary FS tool, -// never a Shell workaround, and never surfaces an ask at yolo. +// TestPathEscapePosture_Scenario3_YoloWriteEscapeAllowed pins AC3.1: in a +// yolo-mode session, Write to an out-of-root absolute path creates the file +// (and a second Write replaces it) — the relax flows through the ordinary FS +// tool, never a Shell workaround, and never surfaces an ask in yolo. The relax +// keys off the SESSION mode, not the server posture: a default-mode session on +// the same yolo server asks (TestPathEscapePosture_Scenario3_YoloToDefaultTransitionAsks). func TestPathEscapePosture_Scenario3_YoloWriteEscapeAllowed(t *testing.T) { t.Parallel() f := setupWriteFS(t) @@ -102,7 +109,7 @@ func TestPathEscapePosture_Scenario3_YoloWriteEscapeAllowed(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -141,6 +148,156 @@ func TestPathEscapePosture_Scenario3_YoloWriteEscapeAllowed(t *testing.T) { } } +// writeTurnOutcome is what one prompt's run surfaced for the write-escape +// transition tests. +type writeTurnOutcome struct { + asks []session.PendingAsk + results []session.ToolResult +} + +// runWriteTurn drives one prompt, answering every permission ask with verdict. +func runWriteTurn(t *testing.T, built *Built, id session.SessionID, verdict session.ApprovalVerdict) writeTurnOutcome { + t.Helper() + run, err := built.Service.StartRun(context.Background(), id, "write outside the workspace") + if err != nil { + t.Fatalf("StartRun: %v", err) + } + var out writeTurnOutcome + for ev := range run.Events() { + if ev.Type == session.EvPermissionAsk && ev.Ask != nil { + out.asks = append(out.asks, *ev.Ask) + run.Approve(ev.Ask.AskID, verdict) + } + if ev.Type == session.EvToolResult && ev.ToolResult != nil { + out.results = append(out.results, *ev.ToolResult) + } + } + built.Service.FinishRun(id, run) + return out +} + +// TestPathEscapePosture_Scenario3_YoloToDefaultTransitionAsks pins that the +// write-escape relax follows the SESSION mode, not the server posture: on a +// yolo server, a yolo-mode session writes an external file without an ask; +// after SetMode(default) on the SAME session, the next Write to that external +// target raises the escape ask and, denied, leaves the file unchanged. A +// session that starts in default on the yolo server asks the same way, and a +// configured Deny still wins in yolo. +func TestPathEscapePosture_Scenario3_YoloToDefaultTransitionAsks(t *testing.T) { + t.Parallel() + + t.Run("yolo then default on the same session", func(t *testing.T) { + t.Parallel() + f := setupWriteFS(t) + target := filepath.Join(t.TempDir(), "external.txt") + const yoloContent = "written-in-yolo-4d1e" + create := scenario3Call("w1", "Write", map[string]string{"path": target, "content": yoloContent}) + overwrite := scenario3Call("w2", "Write", map[string]string{"path": target, "content": "must-never-land-after-default"}) + built, err := buildIsolated(t, context.Background(), writeEscapeCfg(t, f, PostureYolo, + mockllm.ToolCallTurn(create), + mockllm.TextTurn("done"), + mockllm.ToolCallTurn(overwrite), + mockllm.TextTurn("done"), + )) + if err != nil { + t.Fatalf("Build: %v", err) + } + defer built.Close() + sess, err := built.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + + first := runWriteTurn(t, built, sess.ID, session.VerdictDeny) + if len(first.asks) != 0 { + t.Fatalf("yolo-mode Write escape asked: %+v — yolo must allow it", first.asks) + } + if len(first.results) != 1 || first.results[0].IsError { + t.Fatalf("yolo-mode Write escape results = %+v, want one success", first.results) + } + if data, err := os.ReadFile(target); err != nil || string(data) != yoloContent { + t.Fatalf("after yolo turn: content = %q, err = %v; want %q", data, err, yoloContent) + } + + if _, err := built.Service.SetMode(context.Background(), sess.ID, session.ModeDefault); err != nil { + t.Fatalf("SetMode(default): %v", err) + } + second := runWriteTurn(t, built, sess.ID, session.VerdictDeny) + if len(second.asks) != 1 || second.asks[0].Tool != "Write" || !strings.Contains(second.asks[0].Reason, target) { + t.Fatalf("default-mode Write escape asks = %+v, want one Write escape ask naming %q", second.asks, target) + } + if len(second.results) != 1 || !second.results[0].IsError { + t.Fatalf("denied default-mode Write escape results = %+v, want one error result", second.results) + } + if data, err := os.ReadFile(target); err != nil || string(data) != yoloContent { + t.Fatalf("after denied default turn: content = %q, err = %v; want unchanged %q", data, err, yoloContent) + } + }) + + t.Run("default session on a yolo server asks", func(t *testing.T) { + t.Parallel() + f := setupWriteFS(t) + const original = "external-original-71c0" + if err := os.WriteFile(f.target, []byte(original), 0o644); err != nil { + t.Fatalf("WriteFile(target): %v", err) + } + overwrite := scenario3Call("w1", "Write", map[string]string{"path": f.target, "content": "must-never-land"}) + built, err := buildIsolated(t, context.Background(), writeEscapeCfg(t, f, PostureYolo, + mockllm.ToolCallTurn(overwrite), + mockllm.TextTurn("done"), + )) + if err != nil { + t.Fatalf("Build: %v", err) + } + defer built.Close() + sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + out := runWriteTurn(t, built, sess.ID, session.VerdictDeny) + if len(out.asks) != 1 || out.asks[0].Tool != "Write" { + t.Fatalf("asks = %+v, want one Write escape ask — the yolo server posture must not relax a default-mode session", out.asks) + } + if len(out.results) != 1 || !out.results[0].IsError { + t.Fatalf("results = %+v, want one denied error result", out.results) + } + if data, err := os.ReadFile(f.target); err != nil || string(data) != original { + t.Fatalf("content = %q, err = %v; want unchanged %q", data, err, original) + } + }) + + t.Run("configured deny still denies in yolo", func(t *testing.T) { + t.Parallel() + f := setupWriteFS(t) + settings := filepath.Join(t.TempDir(), "settings.yaml") + if err := os.WriteFile(settings, []byte("permissions:\n deny:\n - Write\n"), 0o600); err != nil { + t.Fatalf("write permission config: %v", err) + } + create := scenario3Call("w1", "Write", map[string]string{"path": f.target, "content": "must-never-land"}) + cfg := writeEscapeCfg(t, f, PostureYolo, mockllm.ToolCallTurn(create), mockllm.TextTurn("done")) + cfg.PermissionConfigs = []string{settings} + built, err := buildIsolated(t, context.Background(), cfg) + if err != nil { + t.Fatalf("Build: %v", err) + } + defer built.Close() + sess, err := built.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + out := runWriteTurn(t, built, sess.ID, session.VerdictAllowOnce) + if len(out.asks) != 0 { + t.Fatalf("configured deny surfaced asks %+v, want a plain deny", out.asks) + } + if len(out.results) != 1 || !out.results[0].IsError { + t.Fatalf("results = %+v, want one denied error result", out.results) + } + if _, err := os.Stat(f.target); !errors.Is(err, os.ErrNotExist) { + t.Fatalf("Stat(%q) = %v, want nothing written under a configured deny", f.target, err) + } + }) +} + // TestPathEscapePosture_Scenario3_AutoWriteEscapeAsks pins AC3.2: at posture // auto (no guardrail knob), a Write escape surfaces an EvPermissionAsk and // executes ONLY on an allow verdict — a deny verdict leaves nothing written @@ -160,7 +317,7 @@ func TestPathEscapePosture_Scenario3_AutoWriteEscapeAsks(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), session.ModeAuto, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -202,7 +359,7 @@ func TestPathEscapePosture_Scenario3_AutoWriteEscapeAsks(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), session.ModeAuto, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -347,7 +504,7 @@ func TestPathEscapePosture_Scenario3_EditLedgerOutOfRoot(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -530,7 +687,7 @@ func TestPathEscapePosture_Scenario3_WriteEscapeMutateSerial(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -609,7 +766,7 @@ func TestPathEscapePosture_Scenario3_WriteEscapeServedThroughOsRoot(t *testing.T t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -648,8 +805,8 @@ func TestPathEscapePosture_Scenario3_ConfiguredDenyWinsOverEscapeAllow(t *testin inner := permpolicy.NewPolicy([]governance.Rule{ {Scope: governance.ScopeUser, Tool: "Write", Effect: governance.Deny}, }, nil) - p := newEscapePolicy(inner, PostureYolo) - d := p.Evaluate(context.Background(), session.SessionID("s1"), session.ModeDefault, call, ws) + p := newEscapePolicy(inner) + d := p.Evaluate(context.Background(), session.SessionID("s1"), session.ModeYolo, call, ws) if d.Decision.Effect != governance.Deny { t.Fatalf("effect = %v, want Deny — a configured Deny must win over the posture-relaxed escape Allow", d.Decision.Effect) } @@ -660,8 +817,8 @@ func TestPathEscapePosture_Scenario3_ConfiguredDenyWinsOverEscapeAllow(t *testin inner := permpolicy.NewPolicy([]governance.Rule{ {Scope: governance.ScopeUser, Tool: "Write", Effect: governance.Ask}, }, nil) - p := newEscapePolicy(inner, PostureYolo) - d := p.Evaluate(context.Background(), session.SessionID("s1"), session.ModeDefault, call, ws) + p := newEscapePolicy(inner) + d := p.Evaluate(context.Background(), session.SessionID("s1"), session.ModeYolo, call, ws) if d.Decision.Effect != governance.Ask { t.Fatalf("effect = %v, want Ask — the relax must NEVER suppress a configured Ask", d.Decision.Effect) } @@ -674,11 +831,11 @@ func TestPathEscapePosture_Scenario3_ConfiguredDenyWinsOverEscapeAllow(t *testin t.Parallel() store := permstore.New() inner := permpolicy.NewPolicy(defaultRules(), store) - p := newEscapePolicy(inner, PostureAuto) + p := newEscapePolicy(inner) // Warm the per-root classifier (the Learn guard classifies against the // roots the policy has already seen — in the loop, Learn only ever // follows an Evaluate of the same call). - d := p.Evaluate(context.Background(), session.SessionID("s1"), session.ModeDefault, call, ws) + d := p.Evaluate(context.Background(), session.SessionID("s1"), session.ModeAuto, call, ws) if d.Decision.Effect != governance.Ask || d.Decision.AskProvenance == governance.AskProvenanceConfigured { t.Fatalf("escape at auto = %+v, want an unconfigured escape Ask", d) } diff --git a/internal/app/pathescape_scenario4_test.go b/internal/app/pathescape_scenario4_test.go index 4fc9deeb5b..a01004ad0d 100644 --- a/internal/app/pathescape_scenario4_test.go +++ b/internal/app/pathescape_scenario4_test.go @@ -303,35 +303,31 @@ func TestPathEscapePosture_Scenario4_PlanModeWriteEscapeDenied(t *testing.T) { writeCall := scenario3Call("w1", "Write", map[string]string{"path": f.target, "content": "x"}) readCall := scenario3Call("r1", "Read", map[string]string{"path": f.target}) - for _, posture := range []Posture{PostureStrict, PostureTrusted} { - posture := posture - t.Run(posture.String(), func(t *testing.T) { - t.Parallel() - p := newEscapePolicy(permpolicy.NewPolicy(defaultRules(), nil), posture) + p := newEscapePolicy(permpolicy.NewPolicy(defaultRules(), nil)) - d := p.Evaluate(context.Background(), session.SessionID("s1"), session.ModePlan, writeCall, ws) - if d.Decision.Effect != governance.Deny { - t.Fatalf("plan-mode write escape at %s = %v, want Deny — the plan-mode hard-deny must precede any escape Ask", posture, d.Decision.Effect) - } + d := p.Evaluate(context.Background(), session.SessionID("s1"), session.ModePlan, writeCall, ws) + if d.Decision.Effect != governance.Deny { + t.Fatalf("plan-mode write escape = %v, want Deny — the plan-mode hard-deny must precede any escape Ask", d.Decision.Effect) + } - // Positive control 1: the SAME wrapper, default mode → the escape - // Ask (the deny above is plan-mode precedence, not a blanket - // strict/trusted deny). - d = p.Evaluate(context.Background(), session.SessionID("s1"), session.ModeDefault, writeCall, ws) - if d.Decision.Effect != governance.Ask { - t.Fatalf("default-mode write escape at %s = %v, want the escape Ask", posture, d.Decision.Effect) - } - if d.Decision.AskProvenance != governance.AskProvenanceUnknown { - t.Fatalf("escape Ask at %s carries auto-approval provenance %v — it must surface to a human", posture, d.Decision.AskProvenance) - } + // Positive control 1: the SAME wrapper in default and acceptEdits sessions + // → the escape Ask (the deny above is plan-mode precedence, not a blanket + // deny). + for _, mode := range []session.PermissionMode{session.ModeDefault, session.ModeAccept} { + d = p.Evaluate(context.Background(), session.SessionID("s1"), mode, writeCall, ws) + if d.Decision.Effect != governance.Ask { + t.Fatalf("%s-mode write escape = %v, want the escape Ask", mode, d.Decision.Effect) + } + if d.Decision.AskProvenance != governance.AskProvenanceUnknown { + t.Fatalf("%s-mode escape Ask carries auto-approval provenance %v — it must surface to a human", mode, d.Decision.AskProvenance) + } + } - // Positive control 2: a plan-mode READ escape follows the read row - // (allow — plan mode hard-denies mutations only). - d = p.Evaluate(context.Background(), session.SessionID("s1"), session.ModePlan, readCall, ws) - if d.Decision.Effect == governance.Deny { - t.Fatalf("plan-mode read escape at %s = Deny (%q) — plan mode denies mutations only; a read escape must not be hard-denied", posture, d.Decision.Reason) - } - }) + // Positive control 2: a plan-mode READ escape follows the read row + // (allow — plan mode hard-denies mutations only). + d = p.Evaluate(context.Background(), session.SessionID("s1"), session.ModePlan, readCall, ws) + if d.Decision.Effect == governance.Deny { + t.Fatalf("plan-mode read escape = Deny (%q) — plan mode denies mutations only; a read escape must not be hard-denied", d.Decision.Reason) } } @@ -354,7 +350,7 @@ func TestPathEscapePosture_Scenario4_ConfiguredRulesStillWin(t *testing.T) { inner := permpolicy.NewPolicy([]governance.Rule{ {Scope: governance.ScopeUser, Tool: "Read", Effect: governance.Deny}, }, nil) - p := newEscapePolicy(inner, PostureStrict) + p := newEscapePolicy(inner) d := p.Evaluate(context.Background(), session.SessionID("s1"), session.ModeDefault, call, ws) if d.Decision.Effect != governance.Deny { t.Fatalf("effect = %v, want Deny — a configured Deny must win over the escape Ask", d.Decision.Effect) @@ -366,7 +362,7 @@ func TestPathEscapePosture_Scenario4_ConfiguredRulesStillWin(t *testing.T) { inner := permpolicy.NewPolicy([]governance.Rule{ {Scope: governance.ScopeUser, Tool: "Read", Effect: governance.Ask}, }, nil) - p := newEscapePolicy(inner, PostureTrusted) + p := newEscapePolicy(inner) d := p.Evaluate(context.Background(), session.SessionID("s1"), session.ModeDefault, call, ws) if d.Decision.Effect != governance.Ask || d.Decision.AskProvenance != governance.AskProvenanceConfigured { t.Fatalf("effect = %+v, want the CONFIGURED Ask — the escape Ask must never replace a configured Ask", d) diff --git a/internal/app/pathescape_scenario5_test.go b/internal/app/pathescape_scenario5_test.go index 241b929e1f..570873a49f 100644 --- a/internal/app/pathescape_scenario5_test.go +++ b/internal/app/pathescape_scenario5_test.go @@ -88,7 +88,7 @@ func TestPathEscapePosture_Scenario5_ChildReadEscapeDenied(t *testing.T) { t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), postureSessionMode(posture), session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } diff --git a/internal/app/pathescape_scenario5b_test.go b/internal/app/pathescape_scenario5b_test.go index dd0f7c0002..7a346cf46c 100644 --- a/internal/app/pathescape_scenario5b_test.go +++ b/internal/app/pathescape_scenario5b_test.go @@ -72,7 +72,7 @@ func TestPathEscapePosture_Scenario5_SharedWorkspaceChildNotRelaxed(t *testing.T t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), postureSessionMode(posture), session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -169,7 +169,7 @@ func TestPathEscapePosture_Scenario5_SharedWorkspaceChildWriteDenied(t *testing. t.Fatalf("Build: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } diff --git a/internal/app/permission_mode.go b/internal/app/permission_mode.go new file mode 100644 index 0000000000..49f848057b --- /dev/null +++ b/internal/app/permission_mode.go @@ -0,0 +1,425 @@ +package app + +import ( + "context" + "fmt" + "strings" + + "github.com/stacklok/mecatl/engine/port" + "github.com/stacklok/mecatl/engine/session" + "github.com/stacklok/mecatl/internal/adapter/permconfig" +) + +// permission_mode.go is the COMPOSITION-LAYER named permission-mode vocabulary. +// A token is an operator shorthand that writes EXISTING values: the +// process-wide Posture, the default session PermissionMode +// (server.Config.DefaultMode), and the server ceiling (MaxAllowedMode). The +// table below is the whole mechanism. A token never grants project trust: that +// is a separate decision (--trust-project, trustedWorkspaces, remembered trust). + +// PermissionModeToken is one named combination of server ceiling and default +// session mode. +type PermissionModeToken struct { + // Name is the CLI/YAML spelling. + Name string + // Posture is the process-wide posture tier, used for TrustProject floor and + // for checker/reviewer gating. No longer drives allow-all rule injection + // (that is now mode-driven in Policy.Evaluate). + Posture Posture + // SessionMode is the default mode new sessions start in. + SessionMode session.PermissionMode + // MaxMode is the server ceiling: the most-permissive mode sessions on this + // server are allowed to enter. SetMode rejects a mode above this ceiling. + MaxMode session.PermissionMode +} + +// permissionModeTable is the token contract. 5 modes (plan, default, +// accept-edits, auto, yolo). Redundant products (an accept-edits variant of +// auto or yolo) are deliberately absent: allow-all already permits edits. +// trusted and trusted-accept-edits are dropped as modes; project trust is +// raised via --trust-project combined with any mode. +// +// The strict tiers share one ceiling, accept-edits: plan, default, and +// accept-edits are all reachable on a strict server (the TUI cycles through +// them live), so the ceiling only bounds the allow-all modes. +var permissionModeTable = []PermissionModeToken{ + {Name: "plan", Posture: PostureStrict, SessionMode: session.ModePlan, MaxMode: session.ModeAccept}, + {Name: "default", Posture: PostureStrict, SessionMode: session.ModeDefault, MaxMode: session.ModeAccept}, + {Name: "accept-edits", Posture: PostureStrict, SessionMode: session.ModeAccept, MaxMode: session.ModeAccept}, + // auto and yolo: sessions start in the named mode. The allow-all and + // loose-substitution behaviors are injected at evaluation time by + // Policy.Evaluate based on the session's current mode, NOT from Posture. + {Name: PostureAuto.String(), Posture: PostureAuto, SessionMode: session.ModeAuto, MaxMode: session.ModeAuto}, + {Name: PostureYolo.String(), Posture: PostureYolo, SessionMode: session.ModeYolo, MaxMode: session.ModeYolo}, +} + +// PermissionModeNames lists the valid tokens in table order, for help text and +// error messages. +func PermissionModeNames() []string { + names := make([]string, len(permissionModeTable)) + for i, tok := range permissionModeTable { + names[i] = tok.Name + } + return names +} + +// ParsePermissionMode resolves a token. An unknown or empty value is an error +// naming the valid set; unlike the deprecated --posture it never fails closed +// silently, because an operator who typed a mode name must learn it did nothing. +func ParsePermissionMode(s string) (PermissionModeToken, error) { + want := strings.ToLower(strings.TrimSpace(s)) + for _, tok := range permissionModeTable { + if tok.Name == want { + return tok, nil + } + } + return PermissionModeToken{}, fmt.Errorf("unknown permission mode %q: valid values are %s", s, strings.Join(PermissionModeNames(), ", ")) +} + +// PermissionModeForPair returns the token naming (posture, mode), if one exists. +// The deprecated alias surface can produce pairs no token names (for example +// auto with a plan session); those stay expressible and report ok=false. +func PermissionModeForPair(p Posture, mode session.PermissionMode) (PermissionModeToken, bool) { + if mode == "" { + mode = session.ModeDefault + } + for _, tok := range permissionModeTable { + if tok.Posture == p && tok.SessionMode == mode { + return tok, true + } + } + return PermissionModeToken{}, false +} + +// foldPermissionMode applies the permission-mode token BEFORE the posture fold. +// Precedence: an explicit --permission-mode flag, then the operator-tier +// permissionMode: key, then the deprecated surface (--posture/--yolo and the +// operator-tier posture: key, handled by foldOperatorPosture). A token writes +// cfg.Posture, cfg.DefaultSessionMode, and cfg.MaxAllowedMode and marks the +// posture explicit, so the deprecated posture: key cannot override it. +// The permissionMode: key combined with a deprecated --posture or --yolo flag +// is refused (settingsTokenAliasConflict), mirroring the cmd roots' refusal of +// --permission-mode with those flags. --trust-project is not deprecated and +// still folds MAX-tier on top in resolvePosture. +func foldPermissionMode(cfg Config) (Config, error) { + raw := "" + source := "" + if cfg.PermissionModeFlagSet { + raw, source = cfg.PermissionMode, "--permission-mode" + } else if res, ok := cfg.permResolver.(*permconfig.Resolver); ok && res != nil { + if v := strings.TrimSpace(res.OperatorPermissionMode()); v != "" { + if err := settingsTokenAliasConflict(cfg, v); err != nil { + return cfg, err + } + raw, source = v, "settings permissionMode" + } + } + // Without a token, the deprecated --posture is already on cfg.Posture (set + // by the cmd layer) and foldOperatorPosture no-ops for it. The session mode + // and ceiling are NOT derived here: --yolo and --trust-project may still + // raise the posture, so Build derives them after the full alias fold. + if source == "" { + return foldOperatorPosture(cfg), nil + } + tok, err := ParsePermissionMode(raw) + if err != nil { + return cfg, fmt.Errorf("%s: %w", source, err) + } + cfg.Posture = tok.Posture + cfg.PostureFlagSet = true + cfg.DefaultSessionMode = tok.SessionMode + cfg.MaxAllowedMode = tok.MaxMode + cfg.permissionModeName = tok.Name + return cfg, nil +} + +// settingsTokenAliasConflict refuses the operator-tier permissionMode: key +// combined with a deprecated --posture or --yolo flag. Either side alone names +// the posture; together the operator would have to guess which one won, and the +// --yolo MAX fold would raise the posture without the token's session mode and +// ceiling following it. The message names both ways to migrate. +func settingsTokenAliasConflict(cfg Config, token string) error { + var flags, replacements []string + if cfg.PostureFlagSet { + flags = append(flags, "--posture "+cfg.Posture.String()) + replacements = append(replacements, PostureReplacementToken(cfg.Posture)) + } + if cfg.AllowAllTools { + flags = append(flags, "--yolo") + replacements = append(replacements, PostureReplacementToken(PostureYolo)) + } + if len(flags) == 0 { + return nil + } + noun := "flag" + if len(flags) > 1 { + noun = "flags" + } + named := strings.Join(flags, " and ") + return fmt.Errorf("settings.yaml permissionMode: %s cannot be combined with the deprecated %s %s; pass one: drop %s and keep permissionMode: %s, or replace the deprecated %s with --permission-mode %s, which overrides the settings key", + token, named, noun, named, token, noun, replacements[len(replacements)-1]) +} + +// PostureReplacementToken names the --permission-mode token that replaces a +// deprecated posture value, so every root's deprecation warning names the same +// replacement. trusted maps to default: its trust half is now --trust-project. +func PostureReplacementToken(p Posture) string { + mode, _ := postureToSessionModes(p) + for _, tok := range permissionModeTable { + if tok.SessionMode == mode { + return tok.Name + } + } + return string(session.ModeDefault) +} + +// postureToSessionModes maps a Posture to (DefaultSessionMode, MaxAllowedMode) +// for the deprecated --posture flag path, maintaining backward compat. +func postureToSessionModes(p Posture) (session.PermissionMode, session.PermissionMode) { + switch p { + case PostureAuto: + return session.ModeAuto, session.ModeAuto + case PostureYolo: + return session.ModeYolo, session.ModeYolo + default: // PostureStrict, PostureTrusted → default mode, strict ceiling + return session.ModeDefault, session.ModeAccept + } +} + +// checkerRefusal refuses a deployment whose DEFAULT session mode is auto +// without an enforcing checker (missing, --guardrails off, or advisory-only): +// auto's defining guarantee has no default-mode exemption, and unchecked +// autonomy must be chosen explicitly as yolo. A yolo deployment without a +// checker only WARNs that its sessions cannot enter auto. +func checkerRefusal(cfg Config, checkerConfigured bool) error { + if cfg.DefaultSessionMode == session.ModeAuto && !autoCheckerEnforcing(cfg, checkerConfigured) { + state, reason := checkerState(cfg, checkerConfigured) + if state == checkerEnforcing { + state, reason = checkerAdvisory, "configured advisory" + } + return fmt.Errorf("permission mode %q requires an enforcing guardrails checker, but the checker is %s (%s). Fix it by setting a checker with --guardrails-model or binding the guardrail model slot (in enforcing mode), or choose yolo for autonomous operation without a preventive checker", + cfg.resolvedPermissionModeName(), state, reason) + } + if cfg.MaxAllowedMode == session.ModeYolo && !autoCheckerEnforcing(cfg, checkerConfigured) { + cfg.diag().Log(context.Background(), port.LevelWarn, + fmt.Sprintf("permission mode %q: no enforcing guardrails checker, so sessions cannot enter auto; configure one with --guardrails-model to allow it", cfg.resolvedPermissionModeName())) + } + return nil +} + +// yoloCheckerAdvisoryNote states what a configured checker does NOT do at the +// gate-free tier, where demoteForPosture makes it advisory. Shared by the +// refusal and the startup line so they cannot disagree. +const yoloCheckerAdvisoryNote = "In yolo-mode sessions a configured checker is observability-only: it has no pre-tool veto, no approve-once human ask, and no fail-closed on checker failure, so a checker outage looks the same as a clean result. Sessions switched to another mode, including auto, keep it enforcing" + +// headlessTrustRefusal is the headless admission gate. +// It runs AFTER resolveTrust, so any legitimate trust source satisfies it, and +// applies only to the trust-raising posture: trust is not auto's or yolo's +// defining increment, so those WARN instead (narrateWithheldTrust). +func headlessTrustRefusal(cfg Config) error { + if !cfg.Headless || cfg.Posture != PostureTrusted || cfg.TrustProject { + return nil + } + return fmt.Errorf("the trust-raising posture requires a trust source on a headless root, but none is present. Fix it one of three ways: pass --trust-project; add this workspace to trustedWorkspaces in your user-global settings.yaml; or choose a mode that does not raise project trust, such as default or auto") +} + +// narrateWithheldTrust WARNs when a headless allow-all root starts without +// project trust: it is a legitimate production state, but the +// operator should see that project instructions and rules are not loaded. +func narrateWithheldTrust(cfg Config) { + if !cfg.Headless || cfg.Posture < PostureAuto || cfg.TrustProject { + return + } + cfg.diag().Log(context.Background(), port.LevelWarn, + "permission mode: project trust WITHHELD on this headless root; project instructions and rules are not loaded. Pass --trust-project or add the workspace to trustedWorkspaces to load them", + "permission_mode", cfg.resolvedPermissionModeName()) +} + +// resolvedPermissionModeName names the effective pair for messages: the token +// the operator chose, else the token equivalent to the resolved pair, else a +// description of a pair no token names (reachable only via deprecated aliases). +func (cfg Config) resolvedPermissionModeName() string { + if cfg.permissionModeName != "" { + return cfg.permissionModeName + } + if tok, ok := PermissionModeForPair(cfg.Posture, cfg.DefaultSessionMode); ok { + return tok.Name + } + mode := cfg.DefaultSessionMode + if mode == "" { + mode = session.ModeDefault + } + return fmt.Sprintf("posture %s with session mode %s", cfg.Posture, mode) +} + +// The three checker states the startup line reports. +const ( + checkerEnforcing = "enforcing" + checkerAdvisory = "advisory" + checkerDisabled = "disabled" +) + +// checkerState reports the guardrails checker as exactly one of three named +// states, plus the reason when it is disabled. +func checkerState(cfg Config, configured bool) (state, reason string) { + switch { + case cfg.GuardrailsDisabled: + return checkerDisabled, "kill-switch (--guardrails off)" + case !configured: + return checkerDisabled, "no checker configured" + } + compiled, enforcing := compiledGuardrailEnforcement(cfg) + switch { + case !compiled: + return checkerDisabled, noGuardrailRuleCompiled + case cfg.MaxAllowedMode == session.ModeYolo: + return checkerAdvisory, "demoted in yolo-mode sessions; other modes keep it enforcing" + case !enforcing: + return checkerAdvisory, "configured advisory" + } + return checkerEnforcing, "" +} + +// noGuardrailRuleCompiled is the disabled reason when every configured rule +// fails compilation, so no reviewer is installed at runtime. +const noGuardrailRuleCompiled = "no guardrail rule compiled; every configured rule has an invalid match, mode, or phases" + +// compiledGuardrailEnforcement compiles the effective rules exactly as the +// installed reviewer does (buildGuardrailsActionReviewer) and reports whether +// any rule survived and whether a surviving rule enforces. Raw specs are not +// enough: settings decoding accepts matchers and phases CompileRule rejects, +// no reviewer is installed when nothing compiles, and CompileRule normalizes +// the mode spelling. The per-rule WARNs stay with the engine build. +func compiledGuardrailEnforcement(cfg Config) (compiled, enforcing bool) { + cfg.Diagnostics = port.NopDiagnostics{} + specs, _ := effectiveGuardrailSpecs(cfg) + rules, ok := compileGuardrailRules(cfg, specs) + if !ok { + return false, false + } + for _, rule := range rules { + if !rule.Advisory() { + return true, true + } + } + return true, false +} + +// autoCheckerEnforcing reports whether an auto session runs with an enforcing +// checker: a configured checker plus at least one compiled enforcing rule, the +// same condition under which the runtime installs an enforcing reviewer. +// Unlike checkerState it ignores the yolo demotion, which applies per session +// mode (guardrailsDemoted): an auto session on a yolo server still compiles its +// rules enforcing. +func autoCheckerEnforcing(cfg Config, configured bool) bool { + if cfg.GuardrailsDisabled || !configured { + return false + } + cfg.guardrailSessionMode = session.ModeAuto + _, enforcing := compiledGuardrailEnforcement(cfg) + return enforcing +} + +// Subagent ask reviewer states: the stable vocabulary of the +// ServerCapabilities.subagent_ask_reviewer echo and the startup line. +const ( + reviewerOn = "on" + reviewerOff = "off" + reviewerInert = "inert" +) + +// reviewerStatus reports the subagent ask reviewer as one stable state plus a +// human-readable reason (empty when there is none). It is the single source +// for both the startup line and the capabilities echo. +func reviewerStatus(cfg Config) (state, reason string) { + switch { + case cfg.askReviewerDefaultOn: + return reviewerOn, "default for headless allow-all" + case cfg.SubagentAskReviewerModel != "" && cfg.Interactive: + return reviewerInert, "interactive deployment" + case cfg.SubagentAskReviewerModel != "": + return reviewerOn, "configured" + case cfg.askReviewerOptOut: + return reviewerOff, "--subagent-ask-reviewer off" + default: + return reviewerOff, "" + } +} + +// subagentAskReviewerCapability is the reviewer state for the capabilities +// echo; the reason stays in diagnostics only. +func subagentAskReviewerCapability(cfg Config) *string { + state, _ := reviewerStatus(cfg) + return &state +} + +// reviewerState reports the subagent ask reviewer for the startup line. +func reviewerState(cfg Config) string { + state, reason := reviewerStatus(cfg) + if reason == "" { + return state + } + return state + " (" + reason + ")" +} + +// narratePermissionMode is the ONE build-once line reporting the resolved token, +// both halves it set with their lifetimes, and the checker and reviewer states +// as separately scannable items. The reviewer and the checker +// are different mechanisms and are named separately; neither is described in +// the other's terms. +func narratePermissionMode(cfg Config, checkerConfigured bool) { + mode := cfg.DefaultSessionMode + if mode == "" { + mode = session.ModeDefault + } + checker, checkerReason := checkerState(cfg, checkerConfigured) + attrs := []any{ + "permission_mode", cfg.resolvedPermissionModeName(), + "posture", cfg.Posture.String(), + "posture_scope", "process-wide, fixed at startup; sets TrustProject floor and checker/reviewer gating", + "session_mode", string(mode), + "session_mode_scope", "default for new sessions; a session may change it up to the server ceiling", + "max_allowed_mode", string(cfg.MaxAllowedMode), + "guardrails_checker", checker, + "subagent_ask_reviewer", reviewerState(cfg), + } + if checkerReason != "" { + attrs = append(attrs, "guardrails_checker_reason", checkerReason) + } + if checker == checkerAdvisory && cfg.Posture >= PostureYolo { + attrs = append(attrs, "guardrails_checker_note", yoloCheckerAdvisoryNote) + } + cfg.diag().Log(context.Background(), port.LevelInfo, "permission mode", attrs...) + if cfg.Posture == PostureAuto { + cfg.diag().Log(context.Background(), port.LevelInfo, + "permission mode auto: subagents KEEP the command-substitution guard the main agent loses at auto mode, so a subagent's $(...), backtick, or subshell command that cannot be proved read-only is not auto-run. Headless, the subagent ask reviewer adjudicates it instead of denying it (--subagent-ask-reviewer)") + } +} + +// isAskReviewerOptOut reports the explicit --subagent-ask-reviewer off value, +// mirroring the --guardrails off kill-switch. +func isAskReviewerOptOut(s string) bool { + return strings.EqualFold(strings.TrimSpace(s), "off") +} + +// resolveAskReviewerDefault decides whether the headless subagent ask reviewer +// is on by default: headless (no human approver), an +// allow-all mode, no explicit reviewer model, and no explicit opt-out. It +// resolves its model through the existing ask-reviewer slot with the +// parent-model fallback; when neither resolves, behaviour stays today's +// denial and a WARN names the fix. +func resolveAskReviewerDefault(cfg Config) Config { + isAllowAll := cfg.MaxAllowedMode == session.ModeAuto || cfg.MaxAllowedMode == session.ModeYolo + if cfg.Interactive || !isAllowAll || cfg.SubagentAskReviewerModel != "" || cfg.askReviewerOptOut { + return cfg + } + if _, ok := resolveSlotModel(cfg, slotAskReviewer, cfg.Model); !ok && strings.TrimSpace(cfg.Model) == "" { + cfg.diag().Log(context.Background(), port.LevelWarn, + "subagent ask reviewer: the headless allow-all default could not resolve a reviewer model (no ask-reviewer slot and no session model), so a subagent's unresolved permission request is denied as before. Set --subagent-ask-reviewer or bind the ask-reviewer model slot") + return cfg + } + cfg.askReviewerDefaultOn = true + cfg.diag().Log(context.Background(), port.LevelInfo, + "subagent ask reviewer ON by default (headless allow-all): a model may approve a subagent's permission request that nobody else can answer, and each adjudication spends tokens. Configured Deny and Ask rules still win. Opt out with --subagent-ask-reviewer off") + return cfg +} diff --git a/internal/app/permission_mode_test.go b/internal/app/permission_mode_test.go new file mode 100644 index 0000000000..6264b844b3 --- /dev/null +++ b/internal/app/permission_mode_test.go @@ -0,0 +1,783 @@ +package app + +import ( + "context" + "os" + "path/filepath" + "slices" + "strings" + "testing" + + mecatlv1 "github.com/stacklok/mecatl/contracts/gen/go/mecatl/v1" + "github.com/stacklok/mecatl/engine/adapter/memfs" + "github.com/stacklok/mecatl/engine/port" + "github.com/stacklok/mecatl/engine/session" + "github.com/stacklok/mecatl/internal/adapter/permconfig" +) + +// permissionModeCfg is the offline Build fixture for the permission-mode tests: an +// explicit --permission-mode token over the mock provider. +func permissionModeCfg(t *testing.T, token string, diag port.Diagnostics) Config { + t.Helper() + return Config{ + Workspace: t.TempDir(), + Model: "mock", + UseMock: true, + NoSoul: true, + PermissionMode: token, + PermissionModeFlagSet: true, + Diagnostics: diag, + } +} + +// builtDefaultSessionMode reports the mode a CreateSession that leaves mode +// unspecified starts in: the session half of the token. +func builtDefaultSessionMode(t *testing.T, built *Built) session.PermissionMode { + t.Helper() + sess, err := built.Service.CreateSession(context.Background(), "", defaultLimits()) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + return sess.Mode +} + +func TestTokenTableResolvesExactPairs(t *testing.T) { + want := map[string]struct { + posture Posture + mode session.PermissionMode + }{ + "plan": {PostureStrict, session.ModePlan}, + "default": {PostureStrict, session.ModeDefault}, + "accept-edits": {PostureStrict, session.ModeAccept}, + "auto": {PostureAuto, session.ModeAuto}, + "yolo": {PostureYolo, session.ModeYolo}, + } + if got := PermissionModeNames(); len(got) != 5 { + t.Fatalf("token set = %v, want exactly the 5 tokens", got) + } + for name, pair := range want { + tok, err := ParsePermissionMode(name) + if err != nil { + t.Fatalf("ParsePermissionMode(%q): %v", name, err) + } + if tok.Posture != pair.posture || tok.SessionMode != pair.mode { + t.Errorf("%s resolves to (%s, %s), want (%s, %s)", name, tok.Posture, tok.SessionMode, pair.posture, pair.mode) + } + + // Through the real Build: the posture echo and the session default. + cfg := permissionModeCfg(t, name, port.NopDiagnostics{}) + switch pair.posture { + case PostureAuto: + cfg.GuardrailsModel = "mock-checker" + case PostureYolo: + cfg.GuardrailsDisabled = true + } + built, err := buildIsolated(t, context.Background(), cfg) + if err != nil { + t.Fatalf("Build %s: %v", name, err) + } + if got := postureEchoFromBuild(t, built); got != pair.posture.String() { + t.Errorf("%s: echoed posture %q, want %q", name, got, pair.posture) + } + if got := builtDefaultSessionMode(t, built); got != pair.mode { + t.Errorf("%s: unspecified-mode session started in %q, want %q", name, got, pair.mode) + } + // The ceiling: every strict token admits all three strict modes, and no + // token admits a mode above its own allow-all tier. + for _, mode := range []session.PermissionMode{session.ModePlan, session.ModeDefault, session.ModeAccept, session.ModeYolo} { + _, err := built.Service.CreateSession(context.Background(), mode, defaultLimits()) + if wantOK := mode != session.ModeYolo || pair.posture == PostureYolo; (err == nil) != wantOK { + t.Errorf("%s: CreateSession(%s) err = %v, want admitted=%v", name, mode, err, wantOK) + } + } + built.Close() + } + + _, err := ParsePermissionMode("autonomous") + if err == nil { + t.Fatal("an unknown token must be an error") + } + for _, name := range PermissionModeNames() { + if !strings.Contains(err.Error(), name) { + t.Errorf("unknown-token error must name the valid set; missing %q in %v", name, err) + } + } + if _, err := buildIsolated(t, context.Background(), permissionModeCfg(t, "autonomous", port.NopDiagnostics{})); err == nil || !strings.Contains(err.Error(), "accept-edits") { + t.Fatalf("Build with an unknown token must fail naming the valid set; got %v", err) + } +} + +func TestTrustedKeepsCurrentMeaning(t *testing.T) { + // The deprecated --posture trusted still resolves to PostureTrusted. + legacy := Config{Workspace: t.TempDir(), Model: "mock", UseMock: true, NoSoul: true, Posture: PostureTrusted, PostureFlagSet: true} + built, err := buildIsolated(t, context.Background(), legacy) + if err != nil { + t.Fatalf("--posture trusted: Build: %v", err) + } + defer built.Close() + if got := postureEchoFromBuild(t, built); got != "trusted" { + t.Errorf("--posture trusted: posture %q, want trusted", got) + } + if got := builtDefaultSessionMode(t, built); got != session.ModeDefault { + t.Errorf("--posture trusted: session mode %q, want default", got) + } +} + +func TestAutoTokenRequiresEnforcingChecker(t *testing.T) { + // auto's guarantee is an enforcing checker, with no default-mode exemption: + // every non-enforcing checker state refuses at startup. + for name, mutate := range map[string]func(*Config){ + "missing": func(*Config) {}, + "disabled": func(c *Config) { c.GuardrailsDisabled = true; c.GuardrailsModel = "mock-checker" }, + "advisory-only": func(c *Config) { c.GuardrailsModel = "mock-checker"; c.GuardrailsDefaultMode = "advisory" }, + } { + cfg := permissionModeCfg(t, "auto", port.NopDiagnostics{}) + mutate(&cfg) + if built, err := buildIsolated(t, context.Background(), cfg); err == nil { + built.Close() + t.Fatalf("auto with a %s checker must refuse to start", name) + } else if !strings.Contains(err.Error(), "requires an enforcing guardrails checker") { + t.Fatalf("auto with a %s checker: refusal must name the requirement; got %v", name, err) + } + } + checked := permissionModeCfg(t, "auto", port.NopDiagnostics{}) + checked.GuardrailsModel = "mock-checker" + built, err := buildIsolated(t, context.Background(), checked) + if err != nil { + t.Fatalf("auto with an enforcing checker must start: %v", err) + } + built.Close() +} + +func TestAutoAdmissionUsesCompiledGuardrailRules(t *testing.T) { + // Admission follows the rules the installed reviewer compiles, not the raw + // specs: settings decoding accepts matchers and phases CompileRule rejects, + // and it validates a padded mode trimmed while CompileRule normalizes it. + refused := map[string]string{ + "invalid phase": "guardrails:\n rules:\n - match: Shell\n phases: [during]\n mode: block\n", + "blank matcher": "guardrails:\n rules:\n - match: \" \"\n mode: block\n", + "padded advisory mode": "guardrails:\n rules:\n - match: Shell\n mode: \" advisory \"\n", + "only the advisory rule compiles": "guardrails:\n rules:\n - match: Shell\n phases: [during]\n mode: block\n" + + " - match: WebFetch\n mode: advisory\n", + } + for name, body := range refused { + cfg := permissionModeCfg(t, "auto", port.NopDiagnostics{}) + cfg.GuardrailsModel = "mock-checker" + cfg.PermissionConfigs = []string{writeOperatorSettingsFile(t, body)} + if built, err := buildIsolated(t, context.Background(), cfg); err == nil { + built.Close() + t.Errorf("%s: auto must refuse when no enforcing rule survives compilation", name) + } else if !strings.Contains(err.Error(), "requires an enforcing guardrails checker") { + t.Errorf("%s: refusal must name the requirement; got %v", name, err) + } + } + + // A yolo server with no compiled enforcing rule must not advertise auto. + yolo := permissionModeCfg(t, "yolo", port.NopDiagnostics{}) + yolo.GuardrailsModel = "mock-checker" + yolo.PermissionConfigs = []string{writeOperatorSettingsFile(t, refused["invalid phase"])} + built, err := buildIsolated(t, context.Background(), yolo) + if err != nil { + t.Fatalf("yolo with uncompilable rules must still start: %v", err) + } + if _, err := built.Service.CreateSession(context.Background(), session.ModeAuto, defaultLimits()); err == nil { + t.Error("yolo with no compiled enforcing rule must refuse an auto session") + } + built.Close() + + // A valid enforcing rule surviving next to an invalid one admits auto. + ok := permissionModeCfg(t, "auto", port.NopDiagnostics{}) + ok.GuardrailsModel = "mock-checker" + ok.PermissionConfigs = []string{writeOperatorSettingsFile(t, refused["invalid phase"]+ + " - match: NoSuchTool_inert_checker_rule\n mode: block\n")} + built, err = buildIsolated(t, context.Background(), ok) + if err != nil { + t.Fatalf("auto with a compiled enforcing rule must start: %v", err) + } + built.Close() +} + +func TestYoloTokenStartsWithoutChecker(t *testing.T) { + // Unchecked autonomy is yolo: it starts with no checker, WARNing only that + // its sessions cannot enter auto, and refuses an auto session at runtime. + rec := slogdiagBuffer(t) + built, err := buildIsolated(t, context.Background(), permissionModeCfg(t, "yolo", rec.diag)) + if err != nil { + t.Fatalf("yolo with no checker must start: %v", err) + } + defer built.Close() + if !strings.Contains(rec.String(), "sessions cannot enter auto") { + t.Errorf("yolo without a checker must WARN that auto is unavailable; log:\n%s", rec.String()) + } + if _, err := built.Service.CreateSession(context.Background(), session.ModeAuto, defaultLimits()); err == nil { + t.Error("yolo without a checker must refuse an auto session") + } +} + +func TestCheckerRefusalNamesEveryFix(t *testing.T) { + _, err := buildIsolated(t, context.Background(), permissionModeCfg(t, "auto", port.NopDiagnostics{})) + if err == nil { + t.Fatal("auto with no checker must refuse to start") + } + for _, want := range []string{"--guardrails-model", "guardrail model slot", "choose yolo"} { + if !strings.Contains(err.Error(), want) { + t.Errorf("refusal must name %q; got: %v", want, err) + } + } +} + +func TestGateFreeTokenStatesCheckerIsAdvisoryOnly(t *testing.T) { + properties := []string{"pre-tool veto", "approve-once human ask", "fail-closed"} + + rec := slogdiagBuffer(t) + cfg := permissionModeCfg(t, "yolo", rec.diag) + cfg.GuardrailsModel = "mock-checker" + built, err := buildIsolated(t, context.Background(), cfg) + if err != nil { + t.Fatalf("Build: %v", err) + } + defer built.Close() + line := rec.lineContaining(`msg="permission mode"`) + if !strings.Contains(line, "guardrails_checker=advisory") { + t.Fatalf("yolo startup line must report the checker as advisory; line: %q", line) + } + for _, p := range properties { + if !strings.Contains(line, p) { + t.Errorf("yolo startup line must name %q; line: %q", p, line) + } + } +} + +func TestNonAllowAllTokensRequireNoChecker(t *testing.T) { + for _, token := range []string{"plan", "default", "accept-edits"} { + built, err := buildIsolated(t, context.Background(), permissionModeCfg(t, token, port.NopDiagnostics{})) + if err != nil { + t.Fatalf("%s must start with no checker: %v", token, err) + } + built.Close() + } +} + +func TestStartupLineNamesCheckerState(t *testing.T) { + cases := []struct { + name string + cfg Config + want string + }{ + {"enforcing", Config{Posture: PostureAuto, GuardrailsModel: "m"}, "enforcing"}, + {"advisory at yolo", Config{Posture: PostureYolo, MaxAllowedMode: session.ModeYolo, GuardrailsModel: "m"}, "advisory"}, + {"kill-switch", Config{Posture: PostureAuto, GuardrailsDisabled: true}, "disabled"}, + {"none at strict", Config{}, "disabled"}, + } + for _, tc := range cases { + configured := tc.cfg.GuardrailsModel != "" + if got, _ := checkerState(tc.cfg, configured); got != tc.want { + t.Errorf("%s: checker state %q, want %q", tc.name, got, tc.want) + } + } + + rec := slogdiagBuffer(t) + cfg := permissionModeCfg(t, "auto", rec.diag) + cfg.GuardrailsModel = "mock-checker" + built, err := buildIsolated(t, context.Background(), cfg) + if err != nil { + t.Fatalf("Build: %v", err) + } + defer built.Close() + if line := rec.lineContaining(`msg="permission mode"`); !strings.Contains(line, "guardrails_checker=enforcing") { + t.Fatalf("an auto deployment with a checker must report it enforcing; line: %q", line) + } +} + +// headlessCfg is a headless root with no trust source. +func headlessCfg(t *testing.T, token string, diag port.Diagnostics) Config { + t.Helper() + withTrustEnv(t, trustSettingsEnv(t.TempDir(), nil)) + cfg := permissionModeCfg(t, token, diag) + cfg.Headless = true + return cfg +} + +func TestHeadlessTrustTokenRefused(t *testing.T) { + // The trust-raising posture (PostureTrusted) on a headless root with no trust + // source is refused. Reachable via the deprecated --posture trusted flag. + withTrustEnv(t, trustSettingsEnv(t.TempDir(), nil)) + cfg := Config{ + Workspace: t.TempDir(), + Model: "mock", + UseMock: true, + NoSoul: true, + Headless: true, + Posture: PostureTrusted, + PostureFlagSet: true, + Diagnostics: port.NopDiagnostics{}, + } + _, err := buildIsolated(t, context.Background(), cfg) + if err == nil { + t.Fatal("--posture trusted on a headless root with no trust source must refuse") + } + for _, want := range []string{"--trust-project", "trustedWorkspaces", "default or auto"} { + if !strings.Contains(err.Error(), want) { + t.Errorf("refusal must name %q; got: %v", want, err) + } + } +} + +func TestHeadlessTrustTokenAcceptsEveryTrustSource(t *testing.T) { + baseHeadlessTrusted := func(t *testing.T) Config { + t.Helper() + withTrustEnv(t, trustSettingsEnv(t.TempDir(), nil)) + return Config{ + Workspace: t.TempDir(), + Model: "mock", + UseMock: true, + NoSoul: true, + Headless: true, + Posture: PostureTrusted, + PostureFlagSet: true, + Diagnostics: port.NopDiagnostics{}, + } + } + t.Run("explicit --trust-project", func(t *testing.T) { + cfg := baseHeadlessTrusted(t) + cfg.TrustProject = true + built, err := buildIsolated(t, context.Background(), cfg) + if err != nil { + t.Fatalf("explicit trust must satisfy the gate: %v", err) + } + built.Close() + }) + t.Run("declared trustedWorkspaces", func(t *testing.T) { + ws := t.TempDir() + withTrustEnv(t, trustSettingsEnv(t.TempDir(), []byte("trustedWorkspaces:\n - "+ws+"\n"))) + cfg := Config{ + Workspace: ws, + Model: "mock", + UseMock: true, + NoSoul: true, + Headless: true, + Posture: PostureTrusted, + PostureFlagSet: true, + Diagnostics: port.NopDiagnostics{}, + } + built, err := buildIsolated(t, context.Background(), cfg) + if err != nil { + t.Fatalf("declared trust must satisfy the gate: %v", err) + } + built.Close() + }) +} + +func TestHeadlessAllowAllStartsWithoutTrust(t *testing.T) { + for _, token := range []string{"auto", "yolo"} { + rec := slogdiagBuffer(t) + cfg := headlessCfg(t, token, rec.diag) + if token == "auto" { + cfg.GuardrailsModel = "mock-checker" + } else { + cfg.GuardrailsDisabled = true + } + built, err := buildIsolated(t, context.Background(), cfg) + if err != nil { + t.Fatalf("%s on a headless root must start: %v", token, err) + } + built.Close() + if line := rec.lineContaining(`msg="operator posture"`); !strings.Contains(line, "project_ingestion=false") { + t.Errorf("%s: headless without trust must admit no project ingestion; line: %q", token, line) + } + if !strings.Contains(rec.String(), "project trust WITHHELD") { + t.Errorf("%s: headless allow-all without trust must WARN naming the withheld trust; log:\n%s", token, rec.String()) + } + } +} + +func TestInteractiveTrustTokenGrantsTrust(t *testing.T) { + withTrustEnv(t, trustSettingsEnv(t.TempDir(), nil)) + rec := slogdiagBuffer(t) + built, err := buildIsolated(t, context.Background(), Config{ + Workspace: t.TempDir(), + Model: "mock", + UseMock: true, + NoSoul: true, + Posture: PostureTrusted, + PostureFlagSet: true, + Diagnostics: rec.diag, + }) + if err != nil { + t.Fatalf("interactive trusted must start: %v", err) + } + defer built.Close() + if line := rec.lineContaining(`msg="operator posture"`); !strings.Contains(line, "trust_project=true") { + t.Errorf("interactive trusted must grant project trust; line: %q", line) + } + if strings.Contains(rec.String(), "WITHHELD") { + t.Errorf("interactive trusted must not WARN about withheld trust; log:\n%s", rec.String()) + } +} + +// TestPermissionModeTokenDerivesNoTrust pins that the mode surface is not a +// trust decision: an interactive root under any --permission-mode token stays +// untrusted unless an explicit trust source is present, while the deprecated +// --posture surface keeps its interactive trust floor for compatibility. +func TestPermissionModeTokenDerivesNoTrust(t *testing.T) { + trustOf := func(t *testing.T, cfg Config, rec *slogdiagRecorder) bool { + t.Helper() + built, err := buildIsolated(t, context.Background(), cfg) + if err != nil { + t.Fatalf("Build: %v", err) + } + built.Close() + return strings.Contains(rec.lineContaining(`msg="operator posture"`), "trust_project=true") + } + for _, token := range PermissionModeNames() { + t.Run(token, func(t *testing.T) { + withTrustEnv(t, trustSettingsEnv(t.TempDir(), nil)) + rec := slogdiagBuffer(t) + cfg := permissionModeCfg(t, token, rec.diag) + switch token { + case "auto": + cfg.GuardrailsModel = "mock-checker" + case "yolo": + cfg.GuardrailsDisabled = true + } + if trustOf(t, cfg, rec) { + t.Fatalf("interactive --permission-mode %s granted project trust with no trust source", token) + } + rec = slogdiagBuffer(t) + cfg.Diagnostics = rec.diag + cfg.TrustProject = true + if !trustOf(t, cfg, rec) { + t.Fatalf("--permission-mode %s --trust-project must be trusted", token) + } + }) + } + withTrustEnv(t, trustSettingsEnv(t.TempDir(), nil)) + rec := slogdiagBuffer(t) + legacy := Config{Workspace: t.TempDir(), Model: "mock", UseMock: true, NoSoul: true, Posture: PostureYolo, PostureFlagSet: true, GuardrailsDisabled: true, Diagnostics: rec.diag} + if !trustOf(t, legacy, rec) { + t.Fatal("deprecated interactive --posture yolo must keep its trust floor") + } +} + +func TestAllowAllNarratesSubagentAsymmetry(t *testing.T) { + rec := slogdiagBuffer(t) + cfg := permissionModeCfg(t, "auto", rec.diag) + cfg.GuardrailsModel = "mock-checker" + built, err := buildIsolated(t, context.Background(), cfg) + if err != nil { + t.Fatalf("Build: %v", err) + } + built.Close() + line := rec.lineContaining("KEEP the command-substitution guard") + if line == "" || !strings.Contains(line, "--subagent-ask-reviewer") { + t.Fatalf("auto must narrate the subagent asymmetry and name the reviewer; log:\n%s", rec.String()) + } +} + +// headlessAllowAll is a headless auto deployment with an enforcing checker. +func headlessAllowAll(t *testing.T, diag port.Diagnostics) Config { + t.Helper() + cfg := headlessCfg(t, "auto", diag) + cfg.GuardrailsModel = "mock-checker" + return cfg +} + +func TestHeadlessAllowAllDefaultsReviewerOn(t *testing.T) { + rec := slogdiagBuffer(t) + built, err := buildIsolated(t, context.Background(), headlessAllowAll(t, rec.diag)) + if err != nil { + t.Fatalf("Build: %v", err) + } + built.Close() + if !strings.Contains(rec.String(), "subagent ask reviewer ON by default") { + t.Fatalf("headless allow-all must engage the reviewer by default; log:\n%s", rec.String()) + } + + // The model resolves through the existing slot, then the parent model. + cfg := Config{Posture: PostureAuto, MaxAllowedMode: session.ModeAuto, Model: "parent-model", Diagnostics: port.NopDiagnostics{}} + cfg = resolveAskReviewerDefault(cfg) + if !cfg.askReviewerDefaultOn { + t.Fatal("resolveAskReviewerDefault must engage the reviewer") + } + deps, ok := askAdjudicatorDeps(cfg, nil, nil, "", "parent-model") + if !ok || deps.Model != "parent-model" { + t.Fatalf("default reviewer must fall back to the parent model; ok=%t model=%q", ok, deps.Model) + } + cfg.ModelSlots = map[string]string{slotAskReviewer: "reviewer-model"} + deps, ok = askAdjudicatorDeps(cfg, nil, nil, "", "parent-model") + if !ok || deps.Model != "reviewer-model" { + t.Fatalf("default reviewer must resolve through the ask-reviewer slot; ok=%t model=%q", ok, deps.Model) + } +} + +func TestReviewerDefaultIsOptOutAndNarrated(t *testing.T) { + rec := slogdiagBuffer(t) + built, err := buildIsolated(t, context.Background(), headlessAllowAll(t, rec.diag)) + if err != nil { + t.Fatalf("Build: %v", err) + } + built.Close() + on := rec.lineContaining("subagent ask reviewer ON by default") + for _, want := range []string{"a model may approve", "spends tokens", "--subagent-ask-reviewer off"} { + if !strings.Contains(on, want) { + t.Errorf("default-on narration must say %q; line: %q", want, on) + } + } + + rec = slogdiagBuffer(t) + cfg := headlessAllowAll(t, rec.diag) + cfg.SubagentAskReviewerModel = "off" + built, err = buildIsolated(t, context.Background(), cfg) + if err != nil { + t.Fatalf("Build with --subagent-ask-reviewer off: %v", err) + } + built.Close() + if strings.Contains(rec.String(), "ON by default") { + t.Fatalf("the opt-out must restore today's behaviour; log:\n%s", rec.String()) + } + if line := rec.lineContaining(`msg="permission mode"`); !strings.Contains(line, "off (--subagent-ask-reviewer off)") { + t.Fatalf("the startup line must state the opt-out is in effect; line: %q", line) + } +} + +func TestReviewerDefaultDegradesToTodayBehaviour(t *testing.T) { + rec := slogdiagBuffer(t) + cfg := resolveAskReviewerDefault(Config{Posture: PostureAuto, MaxAllowedMode: session.ModeAuto, Diagnostics: rec.diag}) + if cfg.askReviewerDefaultOn { + t.Fatal("with no resolvable reviewer model the default must stay off") + } + if _, ok := askAdjudicatorDeps(cfg, nil, nil, "", ""); ok { + t.Fatal("no reviewer must be built, so behaviour is today's denial") + } + if !strings.Contains(rec.String(), "--subagent-ask-reviewer ") { + t.Fatalf("a WARN must name the fix; log:\n%s", rec.String()) + } +} + +func TestReviewerDefaultIsConfinedToHeadlessAllowAll(t *testing.T) { + for name, cfg := range map[string]Config{ + "interactive auto": {Posture: PostureAuto, MaxAllowedMode: session.ModeAuto, Interactive: true, Model: "m"}, + "headless strict": {Posture: PostureStrict, Model: "m"}, + "headless trusted": {Posture: PostureTrusted, Model: "m"}, + } { + cfg.Diagnostics = port.NopDiagnostics{} + if resolveAskReviewerDefault(cfg).askReviewerDefaultOn { + t.Errorf("%s must not engage the reviewer default", name) + } + } +} + +func TestReviewerAndCheckerNarratedSeparately(t *testing.T) { + rec := slogdiagBuffer(t) + cfg := headlessCfg(t, "auto", rec.diag) + cfg.GuardrailsModel = "mock-checker" + built, err := buildIsolated(t, context.Background(), cfg) + if err != nil { + t.Fatalf("Build: %v", err) + } + built.Close() + line := rec.lineContaining(`msg="permission mode"`) + if !strings.Contains(line, "guardrails_checker=") || !strings.Contains(line, "subagent_ask_reviewer=") { + t.Fatalf("the startup line must carry the checker and the reviewer as separate items; line: %q", line) + } + on := strings.ToLower(rec.lineContaining("subagent ask reviewer ON by default")) + if strings.Contains(on, "guardrail") || strings.Contains(on, "checker") { + t.Errorf("the reviewer narration must not describe itself in the checker's terms; line: %q", on) + } + for _, s := range []string{"enforcing", "advisory", "disabled"} { + if strings.Contains(s, "review") { + t.Errorf("checker state %q must not describe itself in the reviewer's terms", s) + } + } +} + +func TestStartupLineReportsTokenBothHalvesCheckerAndReviewer(t *testing.T) { + rec := slogdiagBuffer(t) + cfg := permissionModeCfg(t, "auto", rec.diag) + cfg.GuardrailsModel = "mock-checker" + built, err := buildIsolated(t, context.Background(), cfg) + if err != nil { + t.Fatalf("Build: %v", err) + } + built.Close() + line := rec.lineContaining(`msg="permission mode"`) + for _, want := range []string{ + "permission_mode=auto", + "posture=auto", + "session_mode=auto", + "process-wide", + "a session may change it", + "guardrails_checker=enforcing", + "subagent_ask_reviewer=", + } { + if !strings.Contains(line, want) { + t.Errorf("startup line must carry %q; line: %q", want, line) + } + } + if n := strings.Count(rec.String(), `msg="permission mode"`); n != 1 { + t.Errorf("the permission-mode line must be emitted exactly once per Build, got %d", n) + } +} + +func TestPermissionModeIsOperatorTierOnly(t *testing.T) { + operatorFile := func(t *testing.T, token string) string { + t.Helper() + path := filepath.Join(t.TempDir(), "operator.yaml") + if err := os.WriteFile(path, []byte("permissionMode: "+token+"\n"), 0o600); err != nil { + t.Fatal(err) + } + return path + } + base := func(t *testing.T) Config { + return Config{Workspace: t.TempDir(), Model: "mock", UseMock: true, NoSoul: true, Diagnostics: port.NopDiagnostics{}} + } + + t.Run("CLI tier honoured", func(t *testing.T) { + cfg := base(t) + cfg.PermissionConfigs = []string{operatorFile(t, "accept-edits")} + built, err := buildIsolated(t, context.Background(), cfg) + if err != nil { + t.Fatalf("Build: %v", err) + } + defer built.Close() + if got := postureEchoFromBuild(t, built); got != "strict" { + t.Errorf("posture %q, want strict", got) + } + if got := builtDefaultSessionMode(t, built); got != session.ModeAccept { + t.Errorf("session mode %q, want acceptEdits", got) + } + }) + t.Run("user-global tier honoured", func(t *testing.T) { + env := trustSettingsEnv(t.TempDir(), []byte("permissionMode: plan\n")) + withTrustEnv(t, env) + cfg := base(t) + cfg.PermissionsConventional = true + cfg.permConfigEnv = &env + built, err := buildIsolated(t, context.Background(), cfg) + if err != nil { + t.Fatalf("Build: %v", err) + } + defer built.Close() + if got := builtDefaultSessionMode(t, built); got != session.ModePlan { + t.Errorf("session mode %q, want plan", got) + } + }) + t.Run("explicit flag outranks the key", func(t *testing.T) { + cfg := permissionModeCfg(t, "plan", port.NopDiagnostics{}) + cfg.PermissionConfigs = []string{operatorFile(t, "default")} + built, err := buildIsolated(t, context.Background(), cfg) + if err != nil { + t.Fatalf("Build: %v", err) + } + defer built.Close() + if got := postureEchoFromBuild(t, built); got != "strict" { + t.Errorf("posture %q, want strict from the flag", got) + } + if got := builtDefaultSessionMode(t, built); got != session.ModePlan { + t.Errorf("session mode %q, want plan from the flag", got) + } + }) + t.Run("project tier ignored with a WARN", func(t *testing.T) { + // Composed: a trusted workspace's project permissionMode: never becomes the + // mode; only --trust-project's own trusted posture applies. + cfg := base(t) + cfg.TrustProject = true + mkdirProjectSettings(t, cfg.Workspace, "permissionMode: yolo\n") + built, err := buildIsolated(t, context.Background(), cfg) + if err != nil { + t.Fatalf("Build: %v", err) + } + defer built.Close() + if got := postureEchoFromBuild(t, built); got != "trusted" { + t.Errorf("a project-tier permissionMode must be ignored; posture %q", got) + } + // The WARN fires where the project file is read: the resolver's lazy + // per-root Resolve. + isolateUserConfig(t) + rec := slogdiagBuffer(t) + ws := memfs.NewWorkspace("/repo") + if err := ws.Write(context.Background(), ".mecatl/settings.yaml", []byte("permissionMode: yolo\n")); err != nil { + t.Fatal(err) + } + res := permconfig.New(permconfig.Options{Conventional: true, TrustProject: true, Diagnostics: rec.diag}) + _ = res.Resolve(context.Background(), ws) + if got := res.OperatorPermissionMode(); got != "" { + t.Errorf("a project-tier permissionMode must not become the operator value; got %q", got) + } + if !strings.Contains(rec.String(), "IGNORING a project-tier permissionMode") { + t.Errorf("a project-tier permissionMode must WARN; log:\n%s", rec.String()) + } + }) +} + +// TestInvariant_NoWireOrDomainSurfaceChanged pins that: +// - the five session mode wire strings are stable; +// - the proto enum has exactly the six approved values (UNSPECIFIED, DEFAULT, +// PLAN, ACCEPT_EDITS, AUTO, YOLO — AUTO and YOLO added with the unified +// permission-mode architecture, intentional additive wire change); +// - the token table only writes the five known session modes. +// +// `task api:check` covers the engine API surface itself. +func TestInvariant_NoWireOrDomainSurfaceChanged(t *testing.T) { + modes := []session.PermissionMode{ + session.ModeDefault, session.ModePlan, session.ModeAccept, + session.ModeAuto, session.ModeYolo, + } + want := []session.PermissionMode{"default", "plan", "acceptEdits", "auto", "yolo"} + if !slices.Equal(modes, want) { + t.Fatalf("session mode wire strings changed: %v", modes) + } + names := make([]string, 0, len(mecatlv1.PermissionMode_value)) + for name := range mecatlv1.PermissionMode_value { + names = append(names, name) + } + slices.Sort(names) + // AUTO and YOLO are intentional additive additions (no break: existing clients + // receive unknown enum values and must ignore them per the proto compatibility + // contract; they do not need to send or recognise them to stay correct). + wantProto := []string{ + "PERMISSION_MODE_ACCEPT_EDITS", + "PERMISSION_MODE_AUTO", + "PERMISSION_MODE_DEFAULT", + "PERMISSION_MODE_PLAN", + "PERMISSION_MODE_UNSPECIFIED", + "PERMISSION_MODE_YOLO", + } + if !slices.Equal(names, wantProto) { + t.Fatalf("proto PermissionMode values = %v, want %v", names, wantProto) + } + knownModes := []session.PermissionMode{ + session.ModeDefault, session.ModePlan, session.ModeAccept, + session.ModeAuto, session.ModeYolo, + } + for _, tok := range permissionModeTable { + if !slices.Contains(knownModes, tok.SessionMode) { + t.Errorf("token %s writes an unknown session mode %q", tok.Name, tok.SessionMode) + } + } +} + +// TestAuthoritativePostureFoldsPermissionModeKey pins that the cmd +// fast paths (root refusal, pre-launch WARN) see an operator-YAML-only +// permissionMode: allow-all token, not only an explicit flag. +func TestAuthoritativePostureFoldsPermissionModeKey(t *testing.T) { + path := filepath.Join(t.TempDir(), "operator.yaml") + if err := os.WriteFile(path, []byte("permissionMode: yolo\n"), 0o600); err != nil { + t.Fatal(err) + } + cfg := Config{Workspace: t.TempDir(), PermissionConfigs: []string{path}, Diagnostics: port.NopDiagnostics{}} + if got := ResolveAuthoritativePosture(cfg); got != PostureYolo { + t.Fatalf("authoritative posture = %s, want yolo from the permissionMode: key", got) + } + if PostureRefusalReason(ResolveAuthoritativePosture(cfg), true) == nil { + t.Fatal("a YAML-only permissionMode: yolo must hit the root/no-sandbox refusal fast path") + } +} + +func TestPostureReplacementToken(t *testing.T) { + for p, want := range map[Posture]string{PostureStrict: "default", PostureTrusted: "default", PostureAuto: "auto", PostureYolo: "yolo"} { + if got := PostureReplacementToken(p); got != want { + t.Errorf("PostureReplacementToken(%s) = %q, want %q", p, got, want) + } + } +} diff --git a/internal/app/posture.go b/internal/app/posture.go index e82c27fdcf..1ea31bbe46 100644 --- a/internal/app/posture.go +++ b/internal/app/posture.go @@ -13,18 +13,24 @@ import ( // trust/automation ladder that folds the --yolo / --trust-project // switches into ONE ordered tier (strict < trusted < auto < yolo). Like trust.go // it is a pure composition concern — engine/governance stays session-free and -// posture-unaware; posture only COMPOSES the derived knobs (the allow-all rule, -// the evaluator's loose-substitution option, and the root-aware project-trust -// floor) and every decision still goes THROUGH governance.Evaluate, never a bypass. +// posture-unaware; posture only COMPOSES the derived knobs (the root-aware +// project-trust floor and checker/reviewer gating) and every decision still goes +// THROUGH governance.Evaluate, never a bypass. // // The ladder (owner-approved), and the knobs each tier derives in applyPosture: // -// posture | AllowAllTools | main loose-subst | child loose-subst | TrustProject floor -// --------|---------------|------------------|----------------------|------------------- -// strict | false | false | false | (trust sources only) -// trusted | false | false | false | interactive-only -// auto | true | true | false (child def ON) | interactive-only -// yolo | true | true | TRUE (child def OFF)| interactive-only +// posture | TrustProject floor | DefaultSessionMode (via postureToSessionModes) +// --------|---------------------------|--------------------------------------------- +// strict | (trust sources only) | default +// trusted | interactive-only | default +// auto | interactive-only | auto (allow-all via ModeAuto at eval time) +// yolo | interactive-only | yolo (allow-all + loose-subst at eval time) +// +// Allow-all and loose-substitution are now mode-driven: Policy.Evaluate injects +// allowAllCLIRules() for ModeAuto/ModeYolo and selects the loose evaluator for +// ModeYolo, at call time. Switching a session from yolo→default actually removes +// the allow-all floor. AllowAllTools and LooseChildSubstitution in Config are kept +// for backward compat with test fixtures but no longer drive rule injection. // // Issue #359. TrustProject is the ONE effective positive workspace-trust // decision after resolveTrust. It gates project-tier ingestion through the named @@ -37,13 +43,6 @@ import ( // repo gets neither steering nor a read-only child shell, while every legitimate // trust source admits both. // -// "child loose-subst" is the NEW Config.LooseChildSubstitution: under yolo a -// child/subagent/branch engine ALSO loosens the built-in substitution Ask floor -// (so a $()/backtick/heredoc command auto-runs), turning OFF the child -// prompt-injection defense — the deliberate behaviour change yolo now carries. -// strict/trusted/auto keep that defense ON (childEvaluatorOptions omits -// WithLooseSubstitution), so only yolo lets a child auto-run a substitution. -// // PostureStrict is iota-zero by design: a zero/unrecognised value fails CLOSED to // the safest tier, the structural backstop that mirrors the rest of the harness's // fail-closed posture (an unknown --posture WARNs and lands here, never refusing @@ -206,19 +205,28 @@ func resolvePosture(cfg Config, ceiling Posture) Posture { return resolved } -// applyPosture maps the resolved tier to the derived composition knobs per the -// ladder table: AllowAllTools (the allow-all rule, main + children), -// LooseChildSubstitution (child substitution loosening, yolo only), and the -// project-trust floor. The floor is raised for trusted/auto/yolo on INTERACTIVE -// roots only; a HEADLESS root's ladder never raises TrustProject. Explicit trust -// survives because this function only raises. It MUST run BEFORE resolveTrust so -// the trust fold and permission resolver see the posture-derived interactive -// floor. +// applyPosture maps the resolved tier to the project-trust floor. The floor is +// raised for trusted/auto/yolo on INTERACTIVE roots only; a HEADLESS root's +// ladder never raises TrustProject. Explicit trust survives because this +// function only raises. It MUST run BEFORE resolveTrust so the trust fold and +// permission resolver see the posture-derived interactive floor. +// +// Allow-all and loose-substitution are no longer derived here: they are +// mode-driven (Policy.Evaluate injects allowAllCLIRules for ModeAuto/ModeYolo +// at call time). DefaultSessionMode and MaxAllowedMode are set by foldPermissionMode +// (for a permission-mode token) or, after the full alias fold, by Build() +// (for the deprecated --posture/--yolo/--trust-project surface). +// +// A posture written by a --permission-mode token or the permissionMode: key +// derives NO trust: workspace trust is a separate decision taken only from +// explicit or remembered trust sources (--trust-project, trustedWorkspaces, +// a remembered grant). Only the deprecated posture surface keeps the floor. func applyPosture(cfg Config) Config { + if cfg.permissionModeName != "" { + return cfg + } switch cfg.Posture { case PostureYolo: - cfg.AllowAllTools = true - cfg.LooseChildSubstitution = true // TrustProject floor: INTERACTIVE-only. On a HEADLESS root the ladder does // NOT raise TrustProject — the fail-safe default (an untrusted clone gets no // ingestion and no subagent shell without an explicit --trust-project). @@ -226,8 +234,6 @@ func applyPosture(cfg Config) Config { cfg.TrustProject = true } case PostureAuto: - cfg.AllowAllTools = true - cfg.LooseChildSubstitution = false if !cfg.Headless { cfg.TrustProject = true } @@ -260,6 +266,9 @@ func foldOperatorPosture(cfg Config) Config { return cfg } cfg.Posture = parsePosture(yamlPosture) + cfg.diag().Log(context.Background(), port.LevelWarn, + "posture: the settings.yaml posture: key is DEPRECATED; use permissionMode: instead", + "posture", yamlPosture) return cfg } @@ -272,7 +281,14 @@ func foldOperatorPosture(cfg Config) Config { // decision drive the real Build and read its structured diagnostic. func ResolveAuthoritativePosture(cfg Config) Posture { cfg.permResolver = buildPermResolver(cfg) - cfg = foldOperatorPosture(cfg) + // Fold the permission-mode token first, so an operator-YAML-only + // permissionMode: auto/yolo reaches the cmd fast paths. An invalid token is + // Build's to report; the pre-check falls back to the deprecated surface. + if folded, err := foldPermissionMode(cfg); err == nil { + cfg = folded + } else { + cfg = foldOperatorPosture(cfg) + } return resolvePosture(cfg, postureNoCeiling) } @@ -281,12 +297,11 @@ func ResolveAuthoritativePosture(cfg Config) Posture { // resolveTrust, so it cannot contradict the following workspace-trust narration. // It is the sole observation/debug surface for the resolved posture + per-defence // state; there is no separate print-and-exit CLI. +// Allow-all and loose-substitution are now mode-driven (not posture-derived), +// so they are not included here. func narratePosture(diag port.Diagnostics, p Posture, trustProject bool) { diag.Log(context.Background(), port.LevelInfo, "operator posture", "posture", p.String(), - "allow_all", p >= PostureAuto, - "main_loose_substitution", p >= PostureAuto, - "child_loose_substitution", p == PostureYolo, "project_ingestion", trustProject, "trust_project", trustProject) } diff --git a/internal/app/posture_build_test.go b/internal/app/posture_build_test.go index 3b151d6119..65e4514071 100644 --- a/internal/app/posture_build_test.go +++ b/internal/app/posture_build_test.go @@ -39,21 +39,21 @@ func postureEchoFromBuild(t *testing.T, built *Built) string { // silently disconnect, and the input to the #1 security fix. // // (a) operator-tier `posture: auto` → echoed posture "auto" AND the narrated -// composition fact reports allow_all=true (applyPosture derived the knob in Build). +// composition fact reports trust_project= (the posture ladder set it in Build). // (b) a PROJECT-tier `posture: yolo` fixture is IGNORED → echoed posture stays "strict". // (c) user-global `posture: yolo` + a PRIVILEGED Config → Build REFUSES (the // operator-YAML tier cannot escape the root/no-sandbox refusal). func TestBuildOperatorYAMLPostureSeam(t *testing.T) { t.Run("operator auto flows to composed posture", func(t *testing.T) { diag := slogdiagBuffer(t) - built, err := buildIsolated(t, context.Background(), Config{ + built, err := buildIsolated(t, context.Background(), withInertEnforcingChecker(Config{ Workspace: t.TempDir(), Model: "mock", UseMock: true, NoSoul: true, PermissionConfigs: []string{writeOperatorPostureFile(t, "auto")}, Diagnostics: diag.diag, - }) + })) if err != nil { t.Fatalf("Build: %v", err) } @@ -67,15 +67,15 @@ func TestBuildOperatorYAMLPostureSeam(t *testing.T) { if fact == "" { t.Fatalf("expected the narratePosture composition fact; log:\n%s", diag.String()) } - if !strings.Contains(fact, "allow_all=true") { - t.Fatalf("auto must derive allow_all=true in Build; fact: %q", fact) + if !strings.Contains(fact, "trust_project=") { + t.Fatalf("auto posture must narrate trust_project in Build; fact: %q", fact) } }) t.Run("injected user-global posture is discovered", func(t *testing.T) { env := trustSettingsEnv(t.TempDir(), []byte("posture: auto\n")) withTrustEnv(t, env) - built, err := buildIsolated(t, context.Background(), Config{ + built, err := buildIsolated(t, context.Background(), withInertEnforcingChecker(Config{ Workspace: t.TempDir(), Model: "mock", UseMock: true, @@ -83,7 +83,7 @@ func TestBuildOperatorYAMLPostureSeam(t *testing.T) { PermissionsConventional: true, permConfigEnv: &env, Diagnostics: port.NopDiagnostics{}, - }) + })) if err != nil { t.Fatalf("Build: %v", err) } @@ -97,7 +97,7 @@ func TestBuildOperatorYAMLPostureSeam(t *testing.T) { // narration and the shell consumer must both report the fail-safe result. t.Run("operator auto headless withholds ingestion (fail-safe)", func(t *testing.T) { diag := slogdiagBuffer(t) - built, err := buildIsolated(t, context.Background(), Config{ + built, err := buildIsolated(t, context.Background(), withInertEnforcingChecker(Config{ Workspace: t.TempDir(), Model: "mock", UseMock: true, @@ -106,7 +106,7 @@ func TestBuildOperatorYAMLPostureSeam(t *testing.T) { Shell: "/bin/sh", // a configured shell so the UNTRUSTED cause (not the empty-shell cause) is operative for the shell-DISABLED fact PermissionConfigs: []string{writeOperatorPostureFile(t, "auto")}, Diagnostics: diag.diag, - }) + })) if err != nil { t.Fatalf("Build: %v", err) } @@ -140,7 +140,7 @@ func TestBuildOperatorYAMLPostureSeam(t *testing.T) { // both project steering and the read-only child shell. t.Run("operator auto interactive grants ingestion (dev default)", func(t *testing.T) { diag := slogdiagBuffer(t) - built, err := buildIsolated(t, context.Background(), Config{ + built, err := buildIsolated(t, context.Background(), withInertEnforcingChecker(Config{ Workspace: t.TempDir(), Model: "mock", UseMock: true, @@ -149,7 +149,7 @@ func TestBuildOperatorYAMLPostureSeam(t *testing.T) { Shell: "/bin/sh", // a configured shell so the untrusted-shell-DISABLED fact would fire if the gate tripped (it must NOT here) PermissionConfigs: []string{writeOperatorPostureFile(t, "auto")}, Diagnostics: diag.diag, - }) + })) if err != nil { t.Fatalf("Build: %v", err) } @@ -206,13 +206,14 @@ func TestBuildOperatorYAMLPostureSeam(t *testing.T) { t.Run("operator yolo on a privileged Config is refused", func(t *testing.T) { built, err := buildIsolated(t, context.Background(), Config{ - Workspace: t.TempDir(), - Model: "mock", - UseMock: true, - NoSoul: true, - PermissionConfigs: []string{writeOperatorPostureFile(t, "yolo")}, - Privileged: true, // root && !sandbox, as the cmd would compute - Diagnostics: port.NopDiagnostics{}, + Workspace: t.TempDir(), + Model: "mock", + UseMock: true, + NoSoul: true, + PermissionConfigs: []string{writeOperatorPostureFile(t, "yolo")}, + GuardrailsDisabled: true, + Privileged: true, // root && !sandbox, as the cmd would compute + Diagnostics: port.NopDiagnostics{}, }) if err == nil { built.Close() @@ -285,3 +286,83 @@ func (r *slogdiagRecorder) lineContaining(substr string) string { } return "" } + +// TestBuildEchoesProjectTrust pins that the capabilities echo carries the +// effective project-trust decision as its own axis: an explicit trust grant +// reports trusted at strict posture, a headless allow-all root without a trust +// source reports untrusted, and the echo is always set by a composed server. +func TestBuildEchoesProjectTrust(t *testing.T) { + cases := []struct { + name string + token string + trust bool + want bool + }{ + {"default untrusted", "default", false, false}, + {"default with --trust-project", "default", true, true}, + {"headless yolo without a trust source", "yolo", false, false}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + if tc.token == "yolo" { + t.Setenv("MECATL_SANDBOX", "1") + } + cfg := permissionModeCfg(t, tc.token, port.NopDiagnostics{}) + cfg.TrustProject = tc.trust + cfg.Headless = true + cfg.GuardrailsDisabled = tc.token == "yolo" + built, err := buildIsolated(t, context.Background(), cfg) + if err != nil { + t.Fatalf("Build: %v", err) + } + defer built.Close() + caps := built.Service.CompatibilityInfo(context.Background()).GetCapabilities() + if caps.ProjectTrusted == nil { + t.Fatal("project_trusted must be set by a composed server") + } + if got := caps.GetProjectTrusted(); got != tc.want { + t.Fatalf("project_trusted = %v, want %v", got, tc.want) + } + }) + } +} + +// TestBuildEchoesSubagentAskReviewer pins that the capabilities echo carries +// the reviewer state from the same reviewerStatus the startup line narrates. +func TestBuildEchoesSubagentAskReviewer(t *testing.T) { + cases := []struct { + name string + token string + interactive bool + reviewer string + want string + }{ + {"headless default is off", "default", false, "", reviewerOff}, + {"headless yolo defaults on", "yolo", false, "", reviewerOn}, + {"interactive configured is inert", "default", true, "mock", reviewerInert}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + if tc.token == "yolo" { + t.Setenv("MECATL_SANDBOX", "1") + } + cfg := permissionModeCfg(t, tc.token, port.NopDiagnostics{}) + cfg.Headless = !tc.interactive + cfg.Interactive = tc.interactive + cfg.SubagentAskReviewerModel = tc.reviewer + cfg.GuardrailsDisabled = tc.token == "yolo" + built, err := buildIsolated(t, context.Background(), cfg) + if err != nil { + t.Fatalf("Build: %v", err) + } + defer built.Close() + caps := built.Service.CompatibilityInfo(context.Background()).GetCapabilities() + if caps.SubagentAskReviewer == nil { + t.Fatal("subagent_ask_reviewer must be set by a composed server") + } + if got := caps.GetSubagentAskReviewer(); got != tc.want { + t.Fatalf("subagent_ask_reviewer = %q, want %q", got, tc.want) + } + }) + } +} diff --git a/internal/app/posture_test.go b/internal/app/posture_test.go index 591474e15a..e5e9c8d4d9 100644 --- a/internal/app/posture_test.go +++ b/internal/app/posture_test.go @@ -80,64 +80,58 @@ func TestPostureConflictWarnFires(t *testing.T) { // TestPostureResolutionTable is the structural anti-drift guard for the // root-aware ladder. TrustProject is both the ingestion decision (through the -// named seam) and the read-only child-shell gate. +// named seam) and the read-only child-shell gate. Allow-all and loose-child +// substitution are now mode-driven (Policy.Evaluate, not applyPosture), so only +// the TrustProject ladder is tested here. func TestPostureResolutionTable(t *testing.T) { for _, root := range []struct { name string headless bool cases []struct { - name string - posture Posture - trustFlag bool - allowAll bool - looseChild bool - trusted bool + name string + posture Posture + trustFlag bool + trusted bool } }{ {"headless", true, []struct { - name string - posture Posture - trustFlag, allowAll, looseChild, trusted bool + name string + posture Posture + trustFlag, trusted bool }{ - {"strict+no-trust", PostureStrict, false, false, false, false}, - {"strict+trust", PostureStrict, true, false, false, true}, - {"trusted+no-trust", PostureTrusted, false, false, false, false}, - {"trusted+trust", PostureTrusted, true, false, false, true}, - {"auto+no-trust", PostureAuto, false, true, false, false}, - {"auto+trust", PostureAuto, true, true, false, true}, - {"yolo+no-trust", PostureYolo, false, true, true, false}, - {"yolo+trust", PostureYolo, true, true, true, true}, + {"strict+no-trust", PostureStrict, false, false}, + {"strict+trust", PostureStrict, true, true}, + {"trusted+no-trust", PostureTrusted, false, false}, + {"trusted+trust", PostureTrusted, true, true}, + {"auto+no-trust", PostureAuto, false, false}, + {"auto+trust", PostureAuto, true, true}, + {"yolo+no-trust", PostureYolo, false, false}, + {"yolo+trust", PostureYolo, true, true}, }}, {"interactive", false, []struct { - name string - posture Posture - trustFlag, allowAll, looseChild, trusted bool + name string + posture Posture + trustFlag, trusted bool }{ - {"strict+no-trust", PostureStrict, false, false, false, false}, - {"strict+trust", PostureStrict, true, false, false, true}, - {"trusted+no-trust", PostureTrusted, false, false, false, true}, - {"trusted+trust", PostureTrusted, true, false, false, true}, - {"auto+no-trust", PostureAuto, false, true, false, true}, - {"auto+trust", PostureAuto, true, true, false, true}, - {"yolo+no-trust", PostureYolo, false, true, true, true}, - {"yolo+trust", PostureYolo, true, true, true, true}, + {"strict+no-trust", PostureStrict, false, false}, + {"strict+trust", PostureStrict, true, true}, + {"trusted+no-trust", PostureTrusted, false, true}, + {"trusted+trust", PostureTrusted, true, true}, + {"auto+no-trust", PostureAuto, false, true}, + {"auto+trust", PostureAuto, true, true}, + {"yolo+no-trust", PostureYolo, false, true}, + {"yolo+trust", PostureYolo, true, true}, }}, } { t.Run(root.name, func(t *testing.T) { for _, tc := range root.cases { got := applyPosture(Config{Headless: root.headless, Posture: tc.posture, TrustProject: tc.trustFlag}) - if got.AllowAllTools != tc.allowAll || got.LooseChildSubstitution != tc.looseChild || got.TrustProject != tc.trusted { - t.Errorf("%s: got allowAll=%v looseChild=%v trust=%v; want %v %v %v", tc.name, - got.AllowAllTools, got.LooseChildSubstitution, got.TrustProject, - tc.allowAll, tc.looseChild, tc.trusted) + if got.TrustProject != tc.trusted { + t.Errorf("%s: got trust=%v; want %v", tc.name, got.TrustProject, tc.trusted) } if projectIngestionAdmitted(got) != tc.trusted { t.Errorf("%s: ingestion=%v, want %v", tc.name, projectIngestionAdmitted(got), tc.trusted) } - mainLoose := len(mainEvaluatorOptions(got)) > 1 - if mainLoose != tc.allowAll { - t.Errorf("%s: main loose-substitution = %v, want %v", tc.name, mainLoose, tc.allowAll) - } } }) } diff --git a/internal/app/project_ingestion_build_test.go b/internal/app/project_ingestion_build_test.go index f9ed11c0d3..b38283365a 100644 --- a/internal/app/project_ingestion_build_test.go +++ b/internal/app/project_ingestion_build_test.go @@ -33,7 +33,7 @@ func TestHeadlessAutoWithoutTrustProjectSuppressesProjectAgentDefs(t *testing.T) ws := seedProjectAgentDef(t, "scout") diag := slogdiagBuffer(t) - built, err := buildIsolated(t, context.Background(), Config{ + built, err := buildIsolated(t, context.Background(), withInertEnforcingChecker(Config{ Workspace: ws, Model: "mock", UseMock: true, @@ -43,7 +43,7 @@ func TestHeadlessAutoWithoutTrustProjectSuppressesProjectAgentDefs(t *testing.T) Headless: true, // a headless root does NOT grant ingestion via the ladder // TrustProject stays FALSE — the operator did not pass --trust-project. Diagnostics: diag.diag, - }) + })) if err != nil { t.Fatalf("Build: %v", err) } @@ -62,7 +62,7 @@ func TestHeadlessAutoWithoutTrustProjectSuppressesProjectAgentDefs(t *testing.T) // def — this proves the pinned assertion is non-vacuous. ws := seedProjectAgentDef(t, "scout") - built, err := buildIsolated(t, context.Background(), Config{ + built, err := buildIsolated(t, context.Background(), withInertEnforcingChecker(Config{ Workspace: ws, Model: "mock", UseMock: true, @@ -72,7 +72,7 @@ func TestHeadlessAutoWithoutTrustProjectSuppressesProjectAgentDefs(t *testing.T) Headless: true, TrustProject: true, // the explicit opt-in admits ingestion on the headless root Diagnostics: nil, // port.NopDiagnostics via nil - }) + })) if err != nil { t.Fatalf("Build: %v", err) } diff --git a/internal/app/redis_workspace_e2e_test.go b/internal/app/redis_workspace_e2e_test.go index 2cab10912a..fb0961ba81 100644 --- a/internal/app/redis_workspace_e2e_test.go +++ b/internal/app/redis_workspace_e2e_test.go @@ -234,6 +234,7 @@ func TestRedisWorkspaceExternalLookingListDirNeverFallsBackToHost(t *testing.T) MockProvider: provider, NoSoul: true, Posture: PostureYolo, + GuardrailsDisabled: true, PostureFlagSet: true, }) if err != nil { diff --git a/internal/app/relative_authority_test.go b/internal/app/relative_authority_test.go index 0513394947..0582b44f59 100644 --- a/internal/app/relative_authority_test.go +++ b/internal/app/relative_authority_test.go @@ -36,7 +36,7 @@ forbid(principal, action, resource) when { resource.path like "` + filepath.ToSl t.Fatal(err) } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(ctx, session.ModeYolo, session.Limits{}) if err != nil { t.Fatal(err) } diff --git a/internal/app/relative_escape_test.go b/internal/app/relative_escape_test.go index bec04e7b89..cab4a96d30 100644 --- a/internal/app/relative_escape_test.go +++ b/internal/app/relative_escape_test.go @@ -41,7 +41,7 @@ func TestYoloRelativeEscapeTools(t *testing.T) { t.Fatal(err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{}) if err != nil { t.Fatal(err) } @@ -142,7 +142,7 @@ func TestYoloRelativeEscapeConfiguredDeny(t *testing.T) { t.Fatal(err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{}) if err != nil { t.Fatal(err) } @@ -165,7 +165,7 @@ func TestYoloRelativeEscapeConfiguredAsk(t *testing.T) { t.Fatal(err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{}) if err != nil { t.Fatal(err) } @@ -226,7 +226,7 @@ func TestYoloRelativeEscapeServiceRefusals(t *testing.T) { t.Fatal(err) } defer built.Close() - sess, err := built.Service.CreateSession(context.Background(), session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(context.Background(), session.ModeYolo, session.Limits{}) if err != nil { t.Fatal(err) } diff --git a/internal/app/remote_execution_test.go b/internal/app/remote_execution_test.go index 1de880874e..6329c4b30b 100644 --- a/internal/app/remote_execution_test.go +++ b/internal/app/remote_execution_test.go @@ -59,7 +59,7 @@ func TestRemoteScheduleCatalogAndPromptThroughFactory(t *testing.T) { } defer built.Close() ctx := session.WithPrincipal(t.Context(), &session.Principal{Issuer: "issuer", Subject: "alice", GrantType: session.GrantTypeUser}) - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(ctx, "", session.Limits{}) if err != nil { t.Fatal(err) } @@ -236,7 +236,7 @@ func TestRemoteExecutionPreservesOperatorPermissionsUnderAuto(t *testing.T) { session.NewToolCall("allowed", "Shell", json.RawMessage(`{"command":"echo allowed"}`)), session.NewToolCall("project-ask", "Shell", json.RawMessage(`{"command":"echo project-ask"}`)), ), mockllm.TextTurn("done")) - cfg := Config{MockProvider: llm, Workspace: project, Posture: PostureAuto, PermissionsConventional: true, NoSoul: true, RemoteExecution: true, PlacementScope: "remote", PlacementProvider: remoteFactoryPlacement{env: env}} + cfg := withInertEnforcingChecker(Config{MockProvider: llm, Workspace: project, Posture: PostureAuto, PermissionsConventional: true, NoSoul: true, RemoteExecution: true, PlacementScope: "remote", PlacementProvider: remoteFactoryPlacement{env: env}}) if source == "explicit" { cfg.PermissionConfigs = []string{operatorPath} } @@ -246,7 +246,7 @@ func TestRemoteExecutionPreservesOperatorPermissionsUnderAuto(t *testing.T) { } defer built.Close() ctx := session.WithPrincipal(t.Context(), &session.Principal{Issuer: "issuer", Subject: "alice", GrantType: session.GrantTypeUser}) - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(ctx, "", session.Limits{}) if err != nil { t.Fatal(err) } @@ -357,14 +357,16 @@ func TestRemoteExecutionRealFactoryCarriesPostureAndAttenuatedCatalog(t *testing if err := os.WriteFile(filepath.Join(commandDir, "project-only.md"), []byte("LOCAL_COMMAND_MARKER_DO_NOT_LOAD"), 0o600); err != nil { t.Fatal(err) } - built, err := buildIsolated(t, context.Background(), Config{MockProvider: provider, PlacementProvider: remoteFactoryPlacement{env: env}, PlacementScope: "remote", RemoteExecution: true, Workspace: localProject, TrustProject: true, AllowAllTools: true, EnableCommands: true, SkillsConventional: true, NoSoul: true, SchedulerEnabled: false}) + built, err := buildIsolated(t, context.Background(), Config{MockProvider: provider, PlacementProvider: remoteFactoryPlacement{env: env}, PlacementScope: "remote", RemoteExecution: true, Workspace: localProject, TrustProject: true, AllowAllTools: true, GuardrailsDisabled: true, EnableCommands: true, SkillsConventional: true, NoSoul: true, SchedulerEnabled: false}) if err != nil { t.Fatal(err) } defer built.Close() principal := &session.Principal{Issuer: "https://issuer.example", Subject: "alice", GrantType: session.GrantTypeUser} ctx := session.WithPrincipal(context.Background(), principal) - sess, err := built.Service.CreateSession(ctx, session.ModeAccept, session.Limits{}) + // Pass "" to use the server's DefaultMode (ModeYolo from AllowAllTools=true config), + // so all tool calls are auto-allowed and unsupported remote tools return model-visible errors. + sess, err := built.Service.CreateSession(ctx, "", session.Limits{}) if err != nil { t.Fatal(err) } diff --git a/internal/app/router_test.go b/internal/app/router_test.go index 6318ab715b..bdc159ead6 100644 --- a/internal/app/router_test.go +++ b/internal/app/router_test.go @@ -460,6 +460,7 @@ func TestRouterRoutesChildToClassifiedModelE2E(t *testing.T) { }, RouterDefaultCategory: "small", AllowAllTools: true, + GuardrailsDisabled: true, envDetector: fakeEnv(map[string]string{"OPENAI_API_KEY": "sk-x"}), liveModelHTTPClient: offlineHTTPClient(), providerConstructor: func(_ Config, _, _, _ string) port.LLMProvider { @@ -471,7 +472,7 @@ func TestRouterRoutesChildToClassifiedModelE2E(t *testing.T) { } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -522,6 +523,7 @@ func TestRouterOffIsByteIdenticalE2E(t *testing.T) { Model: "gpt-5", // OFF: no taxonomy ⇒ byte-identical to no router (no classifier call). AllowAllTools: true, + GuardrailsDisabled: true, envDetector: fakeEnv(map[string]string{"OPENAI_API_KEY": "sk-x"}), liveModelHTTPClient: offlineHTTPClient(), providerConstructor: func(_ Config, _, _, _ string) port.LLMProvider { @@ -543,7 +545,7 @@ func TestRouterOffIsByteIdenticalE2E(t *testing.T) { } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -612,6 +614,7 @@ func TestRouterCategorySelectorEmptyReasonReducesOnWireE2E(t *testing.T) { }, RouterDefaultCategory: "small", AllowAllTools: true, + GuardrailsDisabled: true, envDetector: fakeEnv(map[string]string{"OPENAI_API_KEY": "sk-x"}), liveModelHTTPClient: offlineHTTPClient(), providerConstructor: func(_ Config, _, _, _ string) port.LLMProvider { @@ -628,7 +631,7 @@ func TestRouterCategorySelectorEmptyReasonReducesOnWireE2E(t *testing.T) { } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } diff --git a/internal/app/scheduler_fire.go b/internal/app/scheduler_fire.go index d5e7bb7394..2b26b2dd92 100644 --- a/internal/app/scheduler_fire.go +++ b/internal/app/scheduler_fire.go @@ -70,13 +70,18 @@ func makeFireFuncWithCancel(svc *server.Service, store port.ScheduleStore, defau // Read-leaning default (decision #3): a schedule that does NOT opt into // mutating (Mutating=false) runs in plan mode (read-only toolset) — the // conservative posture for unattended runs. A schedule that opts into - // mutating (Mutating=true) honors its explicit Mode (or default if unset). + // mutating (Mutating=true) honors its explicit Mode (or the server's + // DefaultMode when unset, which reflects the operator's permission + // configuration — for allow-all deployments this is ModeAuto or ModeYolo). + // The in-chat Schedule tool always records its creating session's mode; + // "" comes from a gRPC/REST create that left mode UNSPECIFIED or from an + // embedder's spec. Passing "" to CreateSessionWithProfile falls back to + // s.cfg.DefaultMode. Either way + // CreateSessionWithProfile admits the mode against the current ceiling, + // so a mode recorded under a looser deployment fails the fire. // This is the fire-time enforcement; the create seam (the schedule // manager) additionally rejects Mutating=false with a non-plan Mode at save time. - mode := sched.Spec.Mode - if mode == "" { - mode = session.ModeDefault - } + mode := sched.Spec.Mode // "" means "use server's DefaultMode" if !sched.Spec.Mutating { mode = session.ModePlan } diff --git a/internal/app/scheduler_fire_mode_test.go b/internal/app/scheduler_fire_mode_test.go new file mode 100644 index 0000000000..69f4828b30 --- /dev/null +++ b/internal/app/scheduler_fire_mode_test.go @@ -0,0 +1,255 @@ +package app + +import ( + "context" + "encoding/json" + "net" + "os" + "path/filepath" + "testing" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/status" + "google.golang.org/grpc/test/bufconn" + + "github.com/stacklok/mecatl/adapters/jsonlstore" + mecatlv1 "github.com/stacklok/mecatl/contracts/gen/go/mecatl/v1" + "github.com/stacklok/mecatl/engine/adapter/mockllm" + "github.com/stacklok/mecatl/engine/agent" + "github.com/stacklok/mecatl/engine/session" + "github.com/stacklok/mecatl/internal/adapter/server" +) + +// scheduleModeFireTimeout bounds each fire so a fire that blocks on an +// unanswerable ask fails the test instead of hanging it. +const scheduleModeFireTimeout = 20 * time.Second + +// TestScheduledFireRunsInCreatingSessionMode is the regression for a mutating +// in-chat schedule that always recorded default mode: on a yolo server the +// fire then ran in default, its Write asked with no approver, and the fire +// hung until its deadline. A schedule created from a yolo session must fire in +// yolo and run its mutation without an ask. Once the same store is served by a +// deployment whose ceiling no longer admits yolo, the recorded mode is refused +// at fire-session admission: the fire fails fast and nothing runs. +func TestScheduledFireRunsInCreatingSessionMode(t *testing.T) { + ctx := context.Background() + workspace := t.TempDir() + storeDir := t.TempDir() + written := filepath.Join(workspace, "fired.txt") + writeCall := func(id session.ToolCallID, path string) mockllm.Turn { + args, err := json.Marshal(map[string]string{"path": path, "content": "fired\n"}) + if err != nil { + t.Fatal(err) + } + return mockllm.ToolCallTurn(session.NewToolCall(id, "Write", args)) + } + provider := mockllm.New( + mockllm.ToolCallTurn(session.NewToolCall("create", agent.ScheduleToolName, + json.RawMessage(`{"verb":"create","name":"writer","prompt":"write the file","cron":"@every 1h","mutating":true}`))), + mockllm.TextTurn("scheduled"), + writeCall("fire-write", written), + mockllm.TextTurn("fire done"), + ) + built, err := buildIsolated(t, ctx, Config{ + Workspace: workspace, StoreDir: storeDir, NoSoul: true, UseMock: true, MockProvider: provider, + Model: "main-model", PermissionMode: "yolo", PermissionModeFlagSet: true, + }) + if err != nil { + t.Fatalf("Build(yolo): %v", err) + } + creator, err := built.Service.CreateSession(ctx, session.ModeYolo, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession(yolo): %v", err) + } + run, err := built.Service.StartRun(ctx, creator.ID, "schedule a writer") + if err != nil { + t.Fatalf("StartRun: %v", err) + } + for ev := range run.Events() { + if ev.Type == session.EvToolResult && ev.ToolResult != nil && ev.ToolResult.IsError { + t.Fatalf("in-chat create failed: %q", ev.ToolResult.Content) + } + } + built.Service.FinishRun(creator.ID, run) + + jstore, err := jsonlstore.New(storeDir) + if err != nil { + t.Fatalf("jsonlstore.New: %v", err) + } + sched, err := jstore.ScheduleStore().Load(ctx, "writer") + if err != nil { + t.Fatalf("Load(writer): %v", err) + } + if sched.Spec.Mode != session.ModeYolo || !sched.Spec.Mutating { + t.Fatalf("recorded (mode %q, mutating %t), want (yolo, true): the creating session's mode", sched.Spec.Mode, sched.Spec.Mutating) + } + + fire, err := makeFireFunc(built.Service, jstore.ScheduleStore(), scheduleModeFireTimeout, nil)(ctx, sched, time.Now()) + if err != nil || fire.Stop != session.StopEndTurn { + t.Fatalf("yolo fire = (stop %q, err %v, %q), want end_turn", fire.Stop, err, fire.Err) + } + fired, err := built.Service.GetSession(ctx, fire.SessionID) + if err != nil { + t.Fatalf("GetSession(fire): %v", err) + } + if fired.Mode != session.ModeYolo { + t.Fatalf("fire session mode = %q, want yolo", fired.Mode) + } + if got, err := os.ReadFile(written); err != nil || string(got) != "fired\n" { + t.Fatalf("fire Write did not run unasked: content %q, err %v", got, err) + } + built.Close() + + // The same store under a deployment whose ceiling is accept-edits: the + // yolo-recorded schedule must be refused, not escalated or downgraded. + refusedPath := filepath.Join(workspace, "refused.txt") + tightProvider := mockllm.New(writeCall("refused-write", refusedPath), mockllm.TextTurn("should not run")) + tight, err := buildIsolated(t, ctx, Config{ + Workspace: workspace, StoreDir: storeDir, NoSoul: true, UseMock: true, MockProvider: tightProvider, + Model: "main-model", PermissionMode: "default", PermissionModeFlagSet: true, + }) + if err != nil { + t.Fatalf("Build(default): %v", err) + } + defer tight.Close() + tightStore, err := jsonlstore.New(storeDir) + if err != nil { + t.Fatalf("jsonlstore.New: %v", err) + } + sched, err = tightStore.ScheduleStore().Load(ctx, "writer") + if err != nil { + t.Fatalf("Load(writer) after relaunch: %v", err) + } + start := time.Now() + refused, err := makeFireFunc(tight.Service, tightStore.ScheduleStore(), scheduleModeFireTimeout, nil)(ctx, sched, time.Now()) + if err == nil || refused.Stop != session.StopError { + t.Fatalf("over-ceiling fire = (stop %q, err %v), want a refused fire", refused.Stop, err) + } + if elapsed := time.Since(start); elapsed >= scheduleModeFireTimeout { + t.Fatalf("over-ceiling fire took %s, want refused at admission, not at the deadline", elapsed) + } + if tightProvider.Calls() != 0 { + t.Fatalf("over-ceiling fire reached the model %d times, want 0", tightProvider.Calls()) + } + if _, err := os.Stat(refusedPath); !os.IsNotExist(err) { + t.Fatalf("over-ceiling fire wrote %s (stat err %v), want nothing run", refusedPath, err) + } +} + +// dialScheduleService serves the real ScheduleService handler for built over an +// in-memory listener, so a test drives the wire mapping a remote client uses. +func dialScheduleService(t *testing.T, built *Built) mecatlv1.ScheduleServiceClient { + t.Helper() + lis := bufconn.Listen(1 << 20) + gs := grpc.NewServer() + mecatlv1.RegisterScheduleServiceServer(gs, server.NewScheduleServer(built.Service)) + go func() { _ = gs.Serve(lis) }() + conn, err := grpc.NewClient("passthrough:///bufnet", + grpc.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) { return lis.DialContext(ctx) }), + grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + t.Fatalf("dial: %v", err) + } + t.Cleanup(func() { + _ = conn.Close() + gs.Stop() + _ = lis.Close() + }) + return mecatlv1.NewScheduleServiceClient(conn) +} + +// TestGRPCScheduleUnspecifiedModeUsesServerDefault pins the wire half of the +// same hang: a gRPC CreateSchedule that left mode UNSPECIFIED was stored as +// default, so on a yolo server its fire asked with no approver. UNSPECIFIED +// now stays unset, reads back as UNSPECIFIED, and the fire runs in the +// server's default session mode. An explicit DEFAULT is still stored and read +// back as default, and a read-leaning schedule still has to name PLAN. +func TestGRPCScheduleUnspecifiedModeUsesServerDefault(t *testing.T) { + ctx := context.Background() + workspace := t.TempDir() + storeDir := t.TempDir() + written := filepath.Join(workspace, "wire-fired.txt") + args, err := json.Marshal(map[string]string{"path": written, "content": "fired\n"}) + if err != nil { + t.Fatal(err) + } + provider := mockllm.New( + mockllm.ToolCallTurn(session.NewToolCall("fire-write", "Write", args)), + mockllm.TextTurn("fire done"), + ) + built, err := buildIsolated(t, ctx, Config{ + Workspace: workspace, StoreDir: storeDir, NoSoul: true, UseMock: true, MockProvider: provider, + Model: "main-model", PermissionMode: "yolo", PermissionModeFlagSet: true, + }) + if err != nil { + t.Fatalf("Build(yolo): %v", err) + } + defer built.Close() + client := dialScheduleService(t, built) + spec := func(name string, mutating bool, mode mecatlv1.PermissionMode) *mecatlv1.ScheduleSpec { + return &mecatlv1.ScheduleSpec{ + Name: name, Prompt: "write the file", Mutating: mutating, Mode: mode, + Trigger: &mecatlv1.TriggerSpec{Cron: "@every 1h"}, + } + } + + unset, err := client.CreateSchedule(ctx, &mecatlv1.CreateScheduleRequest{Spec: spec("unset", true, mecatlv1.PermissionMode_PERMISSION_MODE_UNSPECIFIED)}) + if err != nil { + t.Fatalf("CreateSchedule(unset): %v", err) + } + if got := unset.GetSchedule().GetSpec().GetMode(); got != mecatlv1.PermissionMode_PERMISSION_MODE_UNSPECIFIED { + t.Fatalf("unset create read back mode %s, want UNSPECIFIED", got) + } + explicit, err := client.CreateSchedule(ctx, &mecatlv1.CreateScheduleRequest{Spec: spec("explicit", true, mecatlv1.PermissionMode_PERMISSION_MODE_DEFAULT)}) + if err != nil { + t.Fatalf("CreateSchedule(explicit): %v", err) + } + if got := explicit.GetSchedule().GetSpec().GetMode(); got != mecatlv1.PermissionMode_PERMISSION_MODE_DEFAULT { + t.Fatalf("explicit create read back mode %s, want DEFAULT", got) + } + _, err = client.CreateSchedule(ctx, &mecatlv1.CreateScheduleRequest{Spec: spec("ro-unset", false, mecatlv1.PermissionMode_PERMISSION_MODE_UNSPECIFIED)}) + if status.Code(err) != codes.InvalidArgument { + t.Fatalf("read-leaning UNSPECIFIED create = %v, want InvalidArgument (a read-leaning schedule must name plan)", err) + } + if _, err := client.CreateSchedule(ctx, &mecatlv1.CreateScheduleRequest{Spec: spec("ro-plan", false, mecatlv1.PermissionMode_PERMISSION_MODE_PLAN)}); err != nil { + t.Fatalf("read-leaning PLAN create: %v", err) + } + + jstore, err := jsonlstore.New(storeDir) + if err != nil { + t.Fatalf("jsonlstore.New: %v", err) + } + stored, err := jstore.ScheduleStore().Load(ctx, "explicit") + if err != nil || stored.Spec.Mode != session.ModeDefault { + t.Fatalf("explicit stored mode = %q (err %v), want default", stored.Spec.Mode, err) + } + sched, err := jstore.ScheduleStore().Load(ctx, "unset") + if err != nil { + t.Fatalf("Load(unset): %v", err) + } + if sched.Spec.Mode != "" { + t.Fatalf("unset stored mode = %q, want empty (the server default at fire)", sched.Spec.Mode) + } + if got, err := client.GetSchedule(ctx, &mecatlv1.GetScheduleRequest{Name: "unset"}); err != nil || + got.GetSchedule().GetSpec().GetMode() != mecatlv1.PermissionMode_PERMISSION_MODE_UNSPECIFIED { + t.Fatalf("GetSchedule(unset) = (%v, %v), want mode UNSPECIFIED", got.GetSchedule().GetSpec().GetMode(), err) + } + + fire, err := makeFireFunc(built.Service, jstore.ScheduleStore(), scheduleModeFireTimeout, nil)(ctx, sched, time.Now()) + if err != nil || fire.Stop != session.StopEndTurn { + t.Fatalf("unset-mode fire = (stop %q, err %v, %q), want end_turn", fire.Stop, err, fire.Err) + } + fired, err := built.Service.GetSession(ctx, fire.SessionID) + if err != nil { + t.Fatalf("GetSession(fire): %v", err) + } + if fired.Mode != session.ModeYolo { + t.Fatalf("fire session mode = %q, want the server default yolo", fired.Mode) + } + if got, err := os.ReadFile(written); err != nil || string(got) != "fired\n" { + t.Fatalf("fire Write did not run unasked: content %q, err %v", got, err) + } +} diff --git a/internal/app/self_knowledge_posture_test.go b/internal/app/self_knowledge_posture_test.go index 1de8a41b4f..8447b2dd7e 100644 --- a/internal/app/self_knowledge_posture_test.go +++ b/internal/app/self_knowledge_posture_test.go @@ -541,6 +541,21 @@ func TestSelfKnowledgeBehaviouralClaimsMatchImplementation(t *testing.T) { } }) + t.Run("a permission-mode token never grants trust", func(t *testing.T) { + for _, name := range PermissionModeNames() { + tok, err := ParsePermissionMode(name) + if err != nil { + t.Fatal(err) + } + if applyPosture(Config{Posture: tok.Posture, permissionModeName: tok.Name}).TrustProject { + t.Errorf("--permission-mode %s raises TrustProject on an interactive root; the self-knowledge note tells the model a token never grants trust", name) + } + } + if !strings.Contains(selfKnowledgePostureAxes, "--permission-mode token never grants trust") { + t.Error("the axes clause must state that a --permission-mode token never grants workspace trust") + } + }) + t.Run("headless project trust is not limited to --trust-project", func(t *testing.T) { // resolveTrust is the implementation of the claim that a declared or // remembered trust source, not only the --trust-project flag, can grant diff --git a/internal/app/server_provider_recovery_composition_test.go b/internal/app/server_provider_recovery_composition_test.go index 73280e5b38..fc6666c555 100644 --- a/internal/app/server_provider_recovery_composition_test.go +++ b/internal/app/server_provider_recovery_composition_test.go @@ -25,7 +25,7 @@ func recoveryAppConfig(t *testing.T, endpoint string) Config { t.Helper() return isolateConfig(t, Config{ Workspace: t.TempDir(), StoreDir: t.TempDir(), NoSoul: true, NoUserModel: true, - Model: "gpt-5", UseOpenAI: true, OpenAIKey: "test", AllowAllTools: true, + Model: "gpt-5", UseOpenAI: true, OpenAIKey: "test", AllowAllTools: true, GuardrailsDisabled: true, ContextWindowOverride: defaultContextWindowTokens, ProviderOverrides: permconfig.ProviderOverrides{providerOpenAI: {BaseURL: endpoint + "/v1"}}, envDetector: fakeEnv(nil), liveModelHTTPClient: offlineHTTPClient(), @@ -160,7 +160,7 @@ func TestServerProviderRecovery_Scenario2_EffectiveDelayNeverRetriesEarly_Compos if err != nil { t.Fatal(err) } - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatal(err) } @@ -349,7 +349,7 @@ func TestServerProviderRecovery_Scenario1_RootAndDirectWriteChildRecoverBeforeTe t.Fatal(err) } defer built.Close() - parent, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + parent, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatal(err) } diff --git a/internal/app/server_provider_recovery_deadlines_test.go b/internal/app/server_provider_recovery_deadlines_test.go index b62ccbacd7..61586191fc 100644 --- a/internal/app/server_provider_recovery_deadlines_test.go +++ b/internal/app/server_provider_recovery_deadlines_test.go @@ -131,6 +131,7 @@ func TestServerProviderRecovery_Scenario4_ShorterAuxiliaryAndScheduleDeadlines(t defer func() { cancel(); srv.Close() }() cfg := recoveryAppConfig(t, srv.URL) cfg.LLMBreakerCooldown = 10 * time.Millisecond + cfg.GuardrailsDisabled = false // recoveryAppConfig declares off; this test configures a checker instead cfg.GuardrailsModel = cfg.Model cfg.GuardrailsOnCheckerDown = policy cfg.GuardrailsRules = []GuardrailRule{{Match: "Write", Phases: []string{"pre"}, Mode: "block"}} @@ -230,6 +231,7 @@ func TestServerProviderRecovery_Scenario4_CheckerDownPolicyAtToolDispatch(t *tes })) defer srv.Close() cfg := recoveryAppConfig(t, srv.URL) + cfg.GuardrailsDisabled = false // recoveryAppConfig declares off; this test configures a checker instead cfg.GuardrailsModel = cfg.Model cfg.GuardrailsOnCheckerDown = policy cfg.GuardrailsRules = []GuardrailRule{{Match: "Grep", Phases: []string{"pre"}, Mode: "block"}} diff --git a/internal/app/session_debug_acceptance_test.go b/internal/app/session_debug_acceptance_test.go index ac994fbc6f..990ebec604 100644 --- a/internal/app/session_debug_acceptance_test.go +++ b/internal/app/session_debug_acceptance_test.go @@ -243,7 +243,7 @@ func TestSessionDebuggerCrossBoundaryAcceptance(t *testing.T) { ), mockllm.TextTurn("diagnosis and issue draft ready"), ) - built2, err := buildIsolated(t, ctx, Config{Workspace: workspace, StoreDir: storeDir, NoSoul: true, MockProvider: debugProvider, MCPServers: []mcp.ServerConfig{{Name: "github", URL: mcpURL}}, Posture: PostureYolo, PostureFlagSet: true, Interactive: true}) + built2, err := buildIsolated(t, ctx, Config{Workspace: workspace, StoreDir: storeDir, NoSoul: true, MockProvider: debugProvider, MCPServers: []mcp.ServerConfig{{Name: "github", URL: mcpURL}}, Posture: PostureYolo, PostureFlagSet: true, GuardrailsDisabled: true, Interactive: true}) if err != nil { t.Fatalf("restart Build: %v", err) } diff --git a/internal/app/skill_files_composition_test.go b/internal/app/skill_files_composition_test.go index c9911ca829..6689035473 100644 --- a/internal/app/skill_files_composition_test.go +++ b/internal/app/skill_files_composition_test.go @@ -31,7 +31,7 @@ func TestBuildSkillFilesResolveThroughTheCallersOwnView(t *testing.T) { built, err := Build(context.Background(), Config{ Workspace: t.TempDir(), Model: "mock", StoreDir: t.TempDir(), UserModelDir: userModelDir, - NoSoul: true, OwnershipEnforced: true, AllowAllTools: true, MockProvider: mockllm.New(), + NoSoul: true, OwnershipEnforced: true, AllowAllTools: true, GuardrailsDisabled: true, MockProvider: mockllm.New(), }) if err != nil { t.Fatal(err) @@ -85,7 +85,7 @@ func TestBuildSkillFilesWithoutALearnedSkillStore(t *testing.T) { t.Setenv("XDG_DATA_HOME", "") built, err := Build(context.Background(), Config{ Workspace: t.TempDir(), Model: "mock", StoreDir: t.TempDir(), UserModelDir: userModelDir, - NoSoul: true, AllowAllTools: true, MockProvider: mockllm.New(), SkillsDirs: []string{skills}, + NoSoul: true, AllowAllTools: true, GuardrailsDisabled: true, MockProvider: mockllm.New(), SkillsDirs: []string{skills}, }) if err != nil { t.Fatal(err) diff --git a/internal/app/slots.go b/internal/app/slots.go index 9fa96b2f9c..c9284a0d73 100644 --- a/internal/app/slots.go +++ b/internal/app/slots.go @@ -841,19 +841,31 @@ func boundedRouterFact(value string) string { // keep the session model). The predicate intentionally compares against cfg.Model (the // shared-engine model), because promotion only matters for a session that would // otherwise ride the shared engine. +// +// It is also true for a mode whose guardrail enforcement differs from the shared +// engine's: the shared engine demotes the checker to advisory only at yolo +// posture, so on a yolo server a non-yolo session (notably auto, which requires +// an enforcing checker) is promoted to a per-session engine compiled for its own +// mode (guardrailsDemoted). func modeNeedsEngine(cfg Config) func(mode session.PermissionMode) bool { providerID := strings.TrimSpace(cfg.DefaultProvider) if cfg.modelProviderRegistry != nil { providerID = cfg.modelProviderRegistry.Default() } planModel, crossProviderFallback := resolvePlanSessionModel(cfg, providerID, cfg.Model, session.ModePlan) - if !crossProviderFallback && planModel == cfg.Model { - // No active plan slot (or it resolves to the shared-engine model): a mode flip - // never changes the model, so never promote — nil keeps the default-FS path - // byte-identical. + planPromotes := crossProviderFallback || planModel != cfg.Model + guardrailsPromote := guardrailsConfigured(cfg) + if !planPromotes && !guardrailsPromote { + // No active plan slot (or it resolves to the shared-engine model) and no + // checker: a mode flip never changes the engine, so never promote — nil + // keeps the default-FS path byte-identical. return nil } + sharedDemoted := guardrailsDemoted(cfg) return func(mode session.PermissionMode) bool { - return mode == session.ModePlan + if planPromotes && mode == session.ModePlan { + return true + } + return guardrailsPromote && (mode == session.ModeYolo) != sharedDemoted } } diff --git a/internal/app/soulselect.go b/internal/app/soulselect.go index 07b26a1487..0d7d7c5a43 100644 --- a/internal/app/soulselect.go +++ b/internal/app/soulselect.go @@ -303,7 +303,7 @@ func selectSoulSource(cfg Config, io baselineIO, gate soulGate) (prompt.SoulSour // trusted AND the ingestion grant (projectIngestionAdmitted). A not-admitted // project soul is dropped silently-but-LOGGED (Warn), never an error. if !projectIngestionAdmitted(cfg) { - cfg.diag().Log(context.Background(), port.LevelWarn, "soul: a project-sourced soul was discovered but is NOT INGESTED (untrusted workspace or project ingestion not granted); dropping it (no fragment). Pass --trust-project (on a headless root) or run --posture auto on an interactive root to honour a soul discovered in this repo (only for a repo you trust)", + cfg.diag().Log(context.Background(), port.LevelWarn, "soul: a project-sourced soul was discovered but is NOT INGESTED (untrusted workspace or project ingestion not granted); dropping it (no fragment). Pass --trust-project (on a headless root) or run --permission-mode auto on an interactive root to honour a soul discovered in this repo (only for a repo you trust)", "path", projectPath) return nil, soulMeta{Provenance: soulProject, Trusted: false, SHA256: projRes.SHA256, Size: projRes.Size} } diff --git a/internal/app/steer_composition_test.go b/internal/app/steer_composition_test.go index 56f6860a2a..b4c6048d7b 100644 --- a/internal/app/steer_composition_test.go +++ b/internal/app/steer_composition_test.go @@ -97,12 +97,13 @@ func TestSteer_EnabledByDefaultEndToEnd(t *testing.T) { // dispatch auto-approves (Interactive is false; an ask would park the run // headless instead of holding it mid-dispatch the way this test needs). built, err := buildIsolated(t, ctx, Config{ - Workspace: t.TempDir(), - Model: "mock", - NoSoul: true, - MockProvider: llm, - AllowAllTools: true, - extraCoreTools: []tool.Tool{block}, + Workspace: t.TempDir(), + Model: "mock", + NoSoul: true, + MockProvider: llm, + AllowAllTools: true, + GuardrailsDisabled: true, + extraCoreTools: []tool.Tool{block}, extraCoreToolClassifications: map[string]server.ClassificationEntry{ "SteerBlock": {Kind: server.KindDerived, Rationale: "test tool is contained within the current authorized run"}, }, @@ -119,7 +120,7 @@ func TestSteer_EnabledByDefaultEndToEnd(t *testing.T) { t.Fatal("steer capability = false, want true (DEFAULT ON)") } - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(ctx, "", session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -209,7 +210,7 @@ func TestSteer_DisabledCompositionInert(t *testing.T) { t.Fatal("steer capability = true, want false (DisableSteer)") } - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, session.Limits{}) + sess, err := built.Service.CreateSession(ctx, "", session.Limits{}) if err != nil { t.Fatalf("CreateSession: %v", err) } diff --git a/internal/app/subagent_agent_router_test.go b/internal/app/subagent_agent_router_test.go index 2088d84c4f..3e65e39b57 100644 --- a/internal/app/subagent_agent_router_test.go +++ b/internal/app/subagent_agent_router_test.go @@ -68,6 +68,7 @@ func TestRoutableDefRoutesToClassifiedModelE2E(t *testing.T) { RouterCategories: routerTaxonomyCategories(), RouterDefaultCategory: "small", AllowAllTools: true, + GuardrailsDisabled: true, envDetector: fakeEnv(map[string]string{"OPENAI_API_KEY": "sk-x"}), liveModelHTTPClient: offlineHTTPClient(), providerConstructor: func(_ Config, _, _, _ string) port.LLMProvider { @@ -88,7 +89,7 @@ func TestRoutableDefRoutesToClassifiedModelE2E(t *testing.T) { } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -137,6 +138,7 @@ func TestPinnedInheritDefDoesNotRouteE2E(t *testing.T) { RouterCategories: routerTaxonomyCategories(), // router ON, but the pinned def must not route RouterDefaultCategory: "small", AllowAllTools: true, + GuardrailsDisabled: true, envDetector: fakeEnv(map[string]string{"OPENAI_API_KEY": "sk-x"}), liveModelHTTPClient: offlineHTTPClient(), providerConstructor: func(_ Config, _, _, _ string) port.LLMProvider { @@ -158,7 +160,7 @@ func TestPinnedInheritDefDoesNotRouteE2E(t *testing.T) { } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -209,6 +211,7 @@ func TestRoutableDefHallucinatedCategoryFailSoftE2E(t *testing.T) { RouterCategories: routerTaxonomyCategories(), RouterDefaultCategory: "small", AllowAllTools: true, + GuardrailsDisabled: true, Diagnostics: diag, envDetector: fakeEnv(map[string]string{"OPENAI_API_KEY": "sk-x"}), liveModelHTTPClient: offlineHTTPClient(), @@ -230,7 +233,7 @@ func TestRoutableDefHallucinatedCategoryFailSoftE2E(t *testing.T) { } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } diff --git a/internal/app/subagent_model_default_test.go b/internal/app/subagent_model_default_test.go index 953f1e893f..77926989a0 100644 --- a/internal/app/subagent_model_default_test.go +++ b/internal/app/subagent_model_default_test.go @@ -585,12 +585,13 @@ func TestSubagentModelRoutesChildToCheapModel(t *testing.T) { models []string ) built, err := buildIsolated(t, ctx, Config{ - Workspace: workspace, - NoSoul: true, - Model: "gpt-5", - SubagentModel: cheapModel, - AllowAllTools: true, // auto-approve so the delegation turn runs unattended - envDetector: fakeEnv(map[string]string{"OPENAI_API_KEY": "sk-x"}), + Workspace: workspace, + NoSoul: true, + Model: "gpt-5", + SubagentModel: cheapModel, + AllowAllTools: true, // auto-approve so the delegation turn runs unattended + GuardrailsDisabled: true, + envDetector: fakeEnv(map[string]string{"OPENAI_API_KEY": "sk-x"}), // Strictly offline: refuse any (keyed) live model fetch ⇒ embedded floor. liveModelHTTPClient: offlineHTTPClient(), providerConstructor: func(_ Config, _, _, _ string) port.LLMProvider { @@ -614,7 +615,7 @@ func TestSubagentModelRoutesChildToCheapModel(t *testing.T) { defer built.Close() svc := built.Service - sess, err := svc.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := svc.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } diff --git a/internal/app/subagent_perm_config_test.go b/internal/app/subagent_perm_config_test.go index 68aa3d724a..bda3722aad 100644 --- a/internal/app/subagent_perm_config_test.go +++ b/internal/app/subagent_perm_config_test.go @@ -214,7 +214,14 @@ permissions: // TestChildPolicyAutoApprovesNonSubstitution; asserted here only to document the // contrast with the still-floored substitution case at auto. func TestAutoTierChildLoosensMutateFloorNotSubstitution(t *testing.T) { - // AUTO tier: allow-all, but the child substitution defense stays ON. + // In the unified permission-mode architecture, allow-all and substitution-loosening + // are mode-driven (Policy.Evaluate), not baked in at policy construction. + // - ModeAuto: allowAllCLIRules injected → mutates Allowed; standard evaluator → + // substitution still Asks (the defense stays ON at auto). + // - ModeYolo: allowAllCLIRules + evalLoose injected → both mutates and substitutions + // Allowed on the MAIN policy. + // - Children in ModeAuto: blanket Allow floor for mutates (no ask-floor to loosen), + // Ask for substitutions (defense stays ON — the load-bearing safety assertion). cfg := Config{Workspace: "", Model: "mock", AllowAllTools: true, LooseChildSubstitution: false} mutateArgs, _ := json.Marshal(map[string]string{"command": "zap -rf build"}) // unknown-verb mutate stand-in mutateCall := session.NewToolCall("c1", "Shell", mutateArgs) @@ -224,19 +231,27 @@ func TestAutoTierChildLoosensMutateFloorNotSubstitution(t *testing.T) { // Mirror buildEngine's main-policy construction (rules + evaluator options + // the build-once resolver — nil here, no config sources). mainPolicy := permpolicy.NewPolicyWithResolver(mainRules(cfg), nil, cfg.permResolver, mainEvaluatorOptions(cfg)...) - if got := mainPolicy.Evaluate(context.Background(), "s1", session.ModeDefault, subCall, nil); got.Decision.Effect != governance.Allow { - t.Fatalf("auto-tier main policy should loosen the substitution floor; got %+v", got) + // ModeAuto: allow-all rules + standard evaluator → plain mutate Allows, substitution Asks. + if got := mainPolicy.Evaluate(context.Background(), "s1", session.ModeAuto, mutateCall, nil); got.Decision.Effect != governance.Allow { + t.Fatalf("auto-tier main policy should Allow a plain mutate; got %+v", got) + } + if got := mainPolicy.Evaluate(context.Background(), "s1", session.ModeAuto, subCall, nil); got.Decision.Effect != governance.Ask { + t.Fatalf("auto-tier main policy must NOT loosen the substitution floor (ModeAuto uses standard evaluator); got %+v", got) + } + // ModeYolo: allow-all rules + evalLoose evaluator → both mutates and substitutions Allowed. + if got := mainPolicy.Evaluate(context.Background(), "s1", session.ModeYolo, subCall, nil); got.Decision.Effect != governance.Allow { + t.Fatalf("yolo-tier main policy should loosen the substitution floor (evalLoose); got %+v", got) } childPolicy := childPermPolicy(cfg) // Plain mutate: the blanket child floor allows it (no mutate-ask floor exists // for a child to loosen) → Allow regardless of tier. - if got := childPolicy.Evaluate(context.Background(), "s1", session.ModeDefault, mutateCall, nil); got.Decision.Effect != governance.Allow { + if got := childPolicy.Evaluate(context.Background(), "s1", session.ModeAuto, mutateCall, nil); got.Decision.Effect != governance.Allow { t.Fatalf("child plain mutate should be Allow (blanket floor); got %+v", got) } // Substitution with a non-read-only inner: at AUTO the child loosening is OFF, so the // child still floors at Ask — the load-bearing safety assertion. - if got := childPolicy.Evaluate(context.Background(), "s1", session.ModeDefault, subCall, nil); got.Decision.Effect != governance.Ask { + if got := childPolicy.Evaluate(context.Background(), "s1", session.ModeAuto, subCall, nil); got.Decision.Effect != governance.Ask { t.Fatalf("auto tier must NOT loosen a CHILD's substitution floor; got %+v", got) } } diff --git a/internal/app/subagent_selector_build_test.go b/internal/app/subagent_selector_build_test.go index 236ecf3fb4..fd15185d05 100644 --- a/internal/app/subagent_selector_build_test.go +++ b/internal/app/subagent_selector_build_test.go @@ -28,9 +28,10 @@ func TestBuildWritableSubagentCrossProviderSelection(t *testing.T) { childReq []port.LLMRequest ) built, err := buildIsolated(t, ctx, Config{ - Workspace: workspace, - NoSoul: true, - AllowAllTools: true, + Workspace: workspace, + NoSoul: true, + AllowAllTools: true, + GuardrailsDisabled: true, envDetector: fakeEnv(map[string]string{ "OPENAI_API_KEY": "sk-test", "OPENROUTER_API_KEY": "sk-test", @@ -58,7 +59,7 @@ func TestBuildWritableSubagentCrossProviderSelection(t *testing.T) { } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -130,7 +131,7 @@ func TestBuildNoFSSubagentCrossProviderSelection(t *testing.T) { } defer built.Close() - sess, err := built.Service.CreateSessionWithProfile(ctx, session.ModeDefault, defaultLimits(), server.ProviderSelector{}, server.ProfileNoFS) + sess, err := built.Service.CreateSessionWithProfile(ctx, "", defaultLimits(), server.ProviderSelector{}, server.ProfileNoFS) if err != nil { t.Fatalf("CreateSessionWithProfile(no-fs): %v", err) } diff --git a/internal/app/subagent_writable_agent_router_test.go b/internal/app/subagent_writable_agent_router_test.go index 0c95953d09..fd6d2a677e 100644 --- a/internal/app/subagent_writable_agent_router_test.go +++ b/internal/app/subagent_writable_agent_router_test.go @@ -52,6 +52,7 @@ func TestWritableRoutableDefFullBuildE2E(t *testing.T) { RouterCategories: routerTaxonomyCategories(), RouterDefaultCategory: "small", AllowAllTools: true, + GuardrailsDisabled: true, envDetector: fakeEnv(map[string]string{"OPENAI_API_KEY": "sk-x"}), liveModelHTTPClient: offlineHTTPClient(), providerConstructor: func(_ Config, _, _, _ string) port.LLMProvider { @@ -79,7 +80,7 @@ func TestWritableRoutableDefFullBuildE2E(t *testing.T) { } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } diff --git a/internal/app/subprovider_test.go b/internal/app/subprovider_test.go index 5ada58dedd..92d5f2b67c 100644 --- a/internal/app/subprovider_test.go +++ b/internal/app/subprovider_test.go @@ -305,7 +305,7 @@ func TestDefaultConfigPolicyEvaluatesWithoutPanic(t *testing.T) { defer built.Close() svc := built.Service - sess, err := svc.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := svc.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -363,10 +363,11 @@ func TestSubproviderHalfAFullBuildE2E(t *testing.T) { writeFile(t, agentsDir, "orspec.md", "---\nname: orspec\ndescription: runs on openrouter\nprovider: openrouter\ntools: [Read]\n---\nYou run on openrouter.\n") built, err := buildIsolated(t, ctx, Config{ - Workspace: workspace, - NoSoul: true, - AgentsDirs: []string{agentsDir}, - AllowAllTools: true, // --yolo: auto-approve the routed Subagent (no interactive gate) + Workspace: workspace, + NoSoul: true, + AgentsDirs: []string{agentsDir}, + AllowAllTools: true, // --yolo: auto-approve the routed Subagent (no interactive gate) + GuardrailsDisabled: true, envDetector: fakeEnv(map[string]string{ "OPENAI_API_KEY": "sk-x", "OPENROUTER_API_KEY": "sk-x", @@ -387,7 +388,7 @@ func TestSubproviderHalfAFullBuildE2E(t *testing.T) { defer built.Close() svc := built.Service - sess, err := svc.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := svc.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } diff --git a/internal/app/team_parallel_router_test.go b/internal/app/team_parallel_router_test.go index cb342e1f97..a958b37c1d 100644 --- a/internal/app/team_parallel_router_test.go +++ b/internal/app/team_parallel_router_test.go @@ -358,6 +358,7 @@ func testTeamBareModelSelectorUsesContextualProvider(t *testing.T, noFS bool, se ModelAliasTargets: ModelAliases{childDefault: {ProviderID: childProvider, Model: "child-default-model"}, "contextual": {Model: selectedModel}}, EnableTeams: true, AllowAllTools: true, + GuardrailsDisabled: true, ContextWindowOverride: 128000, envDetector: fakeEnv(map[string]string{ "OPENAI_API_KEY": "test-key", @@ -555,6 +556,7 @@ func routerE2ECfg(workspace string, parentCtor func() port.LLMProvider, enable f }, RouterDefaultCategory: "small", AllowAllTools: true, + GuardrailsDisabled: true, envDetector: fakeEnv(map[string]string{"OPENAI_API_KEY": "sk-x"}), liveModelHTTPClient: offlineHTTPClient(), providerConstructor: func(_ Config, _, _, _ string) port.LLMProvider { return parentCtor() }, @@ -581,7 +583,7 @@ func TestTeamRoutesMembersToCategoryModelsE2E(t *testing.T) { } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -630,7 +632,7 @@ func TestTeamRouterOffByteIdenticalE2E(t *testing.T) { } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -665,7 +667,7 @@ func TestParallelRoutesBranchesToCategoryModelsE2E(t *testing.T) { } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -709,7 +711,7 @@ func TestBuiltCloseReapsPreservedParallelWinner(t *testing.T) { } t.Cleanup(built.Close) - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -775,7 +777,7 @@ func TestParallelRouterOffByteIdenticalE2E(t *testing.T) { } defer built.Close() - sess, err := built.Service.CreateSession(ctx, session.ModeDefault, defaultLimits()) + sess, err := built.Service.CreateSession(ctx, "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } diff --git a/internal/app/test_helpers_test.go b/internal/app/test_helpers_test.go index 66e0b55167..4a6525dab4 100644 --- a/internal/app/test_helpers_test.go +++ b/internal/app/test_helpers_test.go @@ -7,6 +7,7 @@ import ( "github.com/stacklok/mecatl/engine/adapter/mockllm" "github.com/stacklok/mecatl/engine/learning" + "github.com/stacklok/mecatl/engine/session" "github.com/stacklok/mecatl/internal/adapter/skillstore" ) @@ -24,6 +25,23 @@ func buildIsolated(t testing.TB, ctx context.Context, cfg Config) (*Built, error return Build(ctx, isolateConfig(t, cfg)) } +// withInertEnforcingChecker declares an enforcing guardrails checker whose +// only rule matches no tool, so an auto deployment is admissible without the +// checker reviewing (or consuming mock turns for) the test's own calls. +func withInertEnforcingChecker(cfg Config) Config { + cfg.GuardrailsDisabled = false + cfg.GuardrailsModel = "mock-checker" + cfg.GuardrailsRules = []GuardrailRule{{Match: "NoSuchTool_inert_checker_rule", Mode: "block"}} + return cfg +} + +// postureSessionMode is the default session mode a deployment at posture p +// creates, so a posture-parameterized test can run its session in that mode. +func postureSessionMode(p Posture) session.PermissionMode { + mode, _ := postureToSessionModes(p) + return mode +} + func TestBuildIsolatedIgnoresAmbientUserModel(t *testing.T) { xdg := t.TempDir() t.Setenv("XDG_CONFIG_HOME", xdg) diff --git a/internal/app/universal_alias_targets_test.go b/internal/app/universal_alias_targets_test.go index ee6cb2c00a..1bc5c71064 100644 --- a/internal/app/universal_alias_targets_test.go +++ b/internal/app/universal_alias_targets_test.go @@ -167,6 +167,7 @@ func TestADR_0369_Scenario1_UniversalAliasTargets(t *testing.T) { ModelAliasTargets: cfg.ModelAliasTargets, EnableParallel: true, AllowAllTools: true, + GuardrailsDisabled: true, ContextWindowOverride: 128000, envDetector: fakeEnv(map[string]string{ "OPENAI_API_KEY": "test-key", @@ -190,7 +191,8 @@ func TestADR_0369_Scenario1_UniversalAliasTargets(t *testing.T) { t.Fatalf("Build pair child default: %v", err) } defer built.Close() - sess, err := built.Service.CreateSession(t.Context(), session.ModeDefault, defaultLimits()) + // Pass "" to use the server's DefaultMode (ModeYolo from AllowAllTools=true config). + sess, err := built.Service.CreateSession(t.Context(), "", defaultLimits()) if err != nil { t.Fatalf("CreateSession: %v", err) } @@ -308,6 +310,7 @@ func TestADR_0369_Scenario1_SettingsScalarRouterUsesDefaultProvider(t *testing.T NoSoul: true, PermissionConfigs: []string{settings}, AllowAllTools: true, + GuardrailsDisabled: true, ContextWindowOverride: 128000, envDetector: fakeEnv(map[string]string{"OPENAI_API_KEY": "test-key", "OPENROUTER_API_KEY": "test-key"}), liveModelHTTPClient: offlineHTTPClient(), @@ -372,6 +375,7 @@ func TestADR_0369_Scenario1_NamedAutomaticRouterPairAliasTarget(t *testing.T) { RouterCategories: []permconfig.RouterCategory{{Name: "deep", Description: "deep specialist work", Model: "pair-route"}}, RouterDefaultCategory: "deep", AllowAllTools: true, + GuardrailsDisabled: true, envDetector: fakeEnv(map[string]string{"OPENAI_API_KEY": "test-key", "OPENROUTER_API_KEY": "test-key"}), liveModelHTTPClient: offlineHTTPClient(), providerConstructor: func(_ Config, id, _, _ string) port.LLMProvider { diff --git a/internal/configgen/build.go b/internal/configgen/build.go index a25a73d8e9..e604513c92 100644 --- a/internal/configgen/build.go +++ b/internal/configgen/build.go @@ -29,6 +29,7 @@ func BuildModel(docs Docs) *Model { return &Model{Subtrees: []*Subtree{ permissionsSubtree(docs), guardrailsSubtree(docs), + permissionModeSubtree(docs), postureSubtree(docs), reasoningEffortSubtree(docs), planModeAutoApproveSubtree(docs), @@ -398,11 +399,35 @@ func learningSubtree(docs Docs) *Subtree { } } +func permissionModeSubtree(docs Docs) *Subtree { + return &Subtree{ + Key: "permissionMode", + Tier: TierOperator, + Doc: "OPERATOR-TIER named permission mode: plan, default, accept-edits, auto, or yolo. " + + "Each token sets two things: the mode new sessions start in, and the server ceiling, " + + "fixed at startup, which is the most permissive mode a session may switch to " + + "(accept-edits for plan, default, and accept-edits). auto refuses to start without an " + + "enforcing guardrails checker; yolo needs none. A mode never grants workspace trust. " + + "An explicit --permission-mode out-ranks this key, and it out-ranks the deprecated posture: " + + "key. A project-tier permissionMode: is IGNORED with a WARN. Empty = keep the CLI/default.", + CommentedOut: true, + Scalar: true, + Fields: []*Field{{ + Key: "permissionMode", + Type: "string", + Default: "(empty)", + Doc: docFor(docs, "Config.PermissionMode", "the named permission-mode token"), + ExampleValue: "accept-edits", + }}, + } +} + func postureSubtree(docs Docs) *Subtree { return &Subtree{ Key: "posture", Tier: TierOperator, - Doc: "OPERATOR-TIER posture-ladder scalar: strict < trusted < auto < yolo (the graduated " + + Doc: "DEPRECATED: use permissionMode: instead; still honoured with a WARN for one release. " + + "OPERATOR-TIER posture-ladder scalar: strict < trusted < auto < yolo (the graduated " + "trust/automation tier). A project-tier posture: is IGNORED with a WARN (a project " + "cannot raise the automation posture). Empty = keep the CLI/default.", CommentedOut: true, diff --git a/internal/configgen/configgen_test.go b/internal/configgen/configgen_test.go index dad6914e64..db3d005edb 100644 --- a/internal/configgen/configgen_test.go +++ b/internal/configgen/configgen_test.go @@ -110,6 +110,8 @@ func authoritativeKeys() []string { collect("mcp.servers.auth.oauth.tools", permconfig.MCPStaticToolProfile{}) // posture is a bare scalar Config field, not a *Section. keys = append(keys, "posture") + // permissionMode is likewise a bare scalar Config field. + keys = append(keys, "permissionMode") // output-economy is absent: the setting was REMOVED (a // clean break) and MUST NOT appear in generated artifacts (pinned by // TestDeprecatedOutputEconomyIsAbsentFromGeneratedArtifacts). @@ -366,6 +368,7 @@ func TestSubtreeTiersAreAsPinned(t *testing.T) { "permissions": configgen.TierProject, // allow/ask/deny + subagent: project-settable (allows trust-gated) "guardrails": configgen.TierOperator, // operator-only: a project cannot weaken a security checker "posture": configgen.TierOperator, // operator-only: a project cannot raise the automation posture + "permissionMode": configgen.TierOperator, // operator-only: a project cannot choose its own permission mode "reasoning-effort": configgen.TierOperator, // operator-only: a project cannot raise the model's reasoning spend "plan-mode-auto-approve": configgen.TierOperator, // operator-only: a project cannot grant an autonomous approval capability (issue #206) "providers": configgen.TierOperator, // operator-only: a project cannot choose LLM endpoints or auth posture diff --git a/internal/configgen/settings.skeleton.yaml b/internal/configgen/settings.skeleton.yaml index edcae4b61b..eba9a20786 100644 --- a/internal/configgen/settings.skeleton.yaml +++ b/internal/configgen/settings.skeleton.yaml @@ -96,7 +96,18 @@ # prompt: "" # failClosed: false +#| === permissionMode === (tier: operator) +#| OPERATOR-TIER named permission mode: plan, default, accept-edits, auto, or yolo. Each +#| token sets two things: the mode new sessions start in, and the server ceiling, fixed +#| at startup, which is the most permissive mode a session may switch to (accept-edits +#| for plan, default, and accept-edits). auto refuses to start without an enforcing +#| guardrails checker; yolo needs none. A mode never grants workspace trust. An explicit +#| --permission-mode out-ranks this key, and it out-ranks the deprecated posture: key. A +#| project-tier permissionMode: is IGNORED with a WARN. Empty = keep the CLI/default. +# permissionMode: accept-edits + #| === posture === (tier: operator) +#| DEPRECATED: use permissionMode: instead; still honoured with a WARN for one release. #| OPERATOR-TIER posture-ladder scalar: strict < trusted < auto < yolo (the graduated #| trust/automation tier). A project-tier posture: is IGNORED with a WARN (a project #| cannot raise the automation posture). Empty = keep the CLI/default. diff --git a/sdk/typescript/etc/mecatl-sdk-deno.api.md b/sdk/typescript/etc/mecatl-sdk-deno.api.md index dd2e361c7f..c0729966c1 100644 --- a/sdk/typescript/etc/mecatl-sdk-deno.api.md +++ b/sdk/typescript/etc/mecatl-sdk-deno.api.md @@ -1317,6 +1317,7 @@ export interface ServerCapabilities { readonly modelSelection: boolean; // (undocumented) readonly posture: string; + readonly projectTrusted?: boolean; // (undocumented) readonly reflection: boolean; // (undocumented) @@ -1337,6 +1338,7 @@ export interface ServerCapabilities { readonly storageCleanup: boolean; // (undocumented) readonly storageHealth: boolean; + readonly subagentAskReviewer?: string; // (undocumented) readonly teams: boolean; // (undocumented) @@ -1374,8 +1376,10 @@ export const ServerFeature: { readonly HttpSteer: "http_steer"; readonly McpServersOnCreate: "mcp_servers_on_create"; readonly PromptFreeControls: "prompt_free_controls"; + readonly RefusedModeRecovery: "refused_mode_recovery"; readonly ServerInfo: "server_info"; readonly SessionActivityInventory: "session_activity_inventory"; + readonly SessionPermissionModes: "session_permission_modes"; readonly SkillFiles: "skill_files"; readonly WatchSessionEvents: "watch_session_events"; }; @@ -1501,6 +1505,8 @@ export const SessionMode: { readonly Default: 1; readonly Plan: 2; readonly AcceptEdits: 3; + readonly Auto: 4; + readonly Yolo: 5; }; // @public diff --git a/sdk/typescript/etc/mecatl-sdk-node.api.md b/sdk/typescript/etc/mecatl-sdk-node.api.md index e886bf025a..8ba6ebe970 100644 --- a/sdk/typescript/etc/mecatl-sdk-node.api.md +++ b/sdk/typescript/etc/mecatl-sdk-node.api.md @@ -1346,6 +1346,7 @@ export interface ServerCapabilities { readonly modelSelection: boolean; // (undocumented) readonly posture: string; + readonly projectTrusted?: boolean; // (undocumented) readonly reflection: boolean; // (undocumented) @@ -1366,6 +1367,7 @@ export interface ServerCapabilities { readonly storageCleanup: boolean; // (undocumented) readonly storageHealth: boolean; + readonly subagentAskReviewer?: string; // (undocumented) readonly teams: boolean; // (undocumented) @@ -1403,8 +1405,10 @@ export const ServerFeature: { readonly HttpSteer: "http_steer"; readonly McpServersOnCreate: "mcp_servers_on_create"; readonly PromptFreeControls: "prompt_free_controls"; + readonly RefusedModeRecovery: "refused_mode_recovery"; readonly ServerInfo: "server_info"; readonly SessionActivityInventory: "session_activity_inventory"; + readonly SessionPermissionModes: "session_permission_modes"; readonly SkillFiles: "skill_files"; readonly WatchSessionEvents: "watch_session_events"; }; @@ -1530,6 +1534,8 @@ export const SessionMode: { readonly Default: 1; readonly Plan: 2; readonly AcceptEdits: 3; + readonly Auto: 4; + readonly Yolo: 5; }; // @public diff --git a/sdk/typescript/etc/mecatl-sdk.api.md b/sdk/typescript/etc/mecatl-sdk.api.md index b49172b668..4a22dc6757 100644 --- a/sdk/typescript/etc/mecatl-sdk.api.md +++ b/sdk/typescript/etc/mecatl-sdk.api.md @@ -1269,6 +1269,7 @@ export interface ServerCapabilities { readonly modelSelection: boolean; // (undocumented) readonly posture: string; + readonly projectTrusted?: boolean; // (undocumented) readonly reflection: boolean; // (undocumented) @@ -1289,6 +1290,7 @@ export interface ServerCapabilities { readonly storageCleanup: boolean; // (undocumented) readonly storageHealth: boolean; + readonly subagentAskReviewer?: string; // (undocumented) readonly teams: boolean; // (undocumented) @@ -1326,8 +1328,10 @@ export const ServerFeature: { readonly HttpSteer: "http_steer"; readonly McpServersOnCreate: "mcp_servers_on_create"; readonly PromptFreeControls: "prompt_free_controls"; + readonly RefusedModeRecovery: "refused_mode_recovery"; readonly ServerInfo: "server_info"; readonly SessionActivityInventory: "session_activity_inventory"; + readonly SessionPermissionModes: "session_permission_modes"; readonly SkillFiles: "skill_files"; readonly WatchSessionEvents: "watch_session_events"; }; @@ -1453,6 +1457,8 @@ export const SessionMode: { readonly Default: 1; readonly Plan: 2; readonly AcceptEdits: 3; + readonly Auto: 4; + readonly Yolo: 5; }; // @public diff --git a/sdk/typescript/src/gen/mecatl/v1/harness_pb.ts b/sdk/typescript/src/gen/mecatl/v1/harness_pb.ts index dc700c25a5..786b0402f7 100644 --- a/sdk/typescript/src/gen/mecatl/v1/harness_pb.ts +++ b/sdk/typescript/src/gen/mecatl/v1/harness_pb.ts @@ -36,7 +36,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file mecatl/v1/harness.proto. */ export const file_mecatl_v1_harness: GenFile = /*@__PURE__*/ - fileDesc("ChdtZWNhdGwvdjEvaGFybmVzcy5wcm90bxIJbWVjYXRsLnYxIlUKBkxpbWl0cxIRCgltYXhfdHVybnMYASABKAUSFgoObWF4X3Rvb2xfY2FsbHMYAiABKAUSIAoYbWF4X2NvbnNlY3V0aXZlX2ZhaWx1cmVzGAMgASgFIvkCChRDcmVhdGVTZXNzaW9uUmVxdWVzdBInCgRtb2RlGAIgASgOMhkubWVjYXRsLnYxLlBlcm1pc3Npb25Nb2RlEiEKBmxpbWl0cxgDIAEoCzIRLm1lY2F0bC52MS5MaW1pdHMSEwoLcHJvdmlkZXJfaWQYBCABKAkSEAoIbW9kZWxfaWQYBSABKAkSMAoJZXhlY3V0aW9uGAwgASgLMh0ubWVjYXRsLnYxLkV4ZWN1dGlvblNlbGVjdGlvbhIYChByZWFzb25pbmdfZWZmb3J0GAcgASgJEh8KF2RlYnVnX3RhcmdldF9zZXNzaW9uX2lkGAkgASgJEhkKEWRlYnVnX21jcF9zZXJ2ZXJzGAogAygJEi0KC21jcF9zZXJ2ZXJzGAsgAygLMhgubWVjYXRsLnYxLk1jcFNlcnZlclNwZWNKBAgBEAJKBAgIEAlKBAgGEAdSCXdvcmtzcGFjZVIRc291cmNlX3Nlc3Npb25faWRSB3Byb2ZpbGUibAoSRXhlY3V0aW9uU2VsZWN0aW9uEiYKBG5vbmUYASABKAsyGC5tZWNhdGwudjEuRXhlY3V0aW9uTm9uZRIuCgh0ZW1wbGF0ZRgCIAEoCzIcLm1lY2F0bC52MS5FeGVjdXRpb25UZW1wbGF0ZSIPCg1FeGVjdXRpb25Ob25lIjEKEUV4ZWN1dGlvblRlbXBsYXRlEgoKAmlkGAEgASgJEhAKCHJldmlzaW9uGAIgASgJIh8KHUxpc3RFeGVjdXRpb25UZW1wbGF0ZXNSZXF1ZXN0Im0KHkxpc3RFeGVjdXRpb25UZW1wbGF0ZXNSZXNwb25zZRIvCgVpdGVtcxgBIAMoCzIgLm1lY2F0bC52MS5FeGVjdXRpb25UZW1wbGF0ZUluZm8SGgoSaW52ZW50b3J5X3JldmlzaW9uGAIgASgJIr0CChVFeGVjdXRpb25UZW1wbGF0ZUluZm8SLgoIdGVtcGxhdGUYASABKAsyHC5tZWNhdGwudjEuRXhlY3V0aW9uVGVtcGxhdGUSDAoEbmFtZRgCIAEoCRITCgtkZXNjcmlwdGlvbhgDIAEoCRIVCg1kaXNwbGF5X3Rva2VuGAQgASgJEkQKCmV4dGVuc2lvbnMYBSADKAsyMC5tZWNhdGwudjEuRXhlY3V0aW9uVGVtcGxhdGVJbmZvLkV4dGVuc2lvbnNFbnRyeRIgChhkZWNsYXJlZF9leGVjdXRpb25fZmlsZXMYBiABKAgSHwoXZGVjbGFyZWRfYnVpbHRfaW5fc2hlbGwYByABKAgaMQoPRXh0ZW5zaW9uc0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEisQEKDU1jcFNlcnZlclNwZWMSDAoEbmFtZRgBIAEoCRILCgN1cmwYAiABKAkSDAoEdHlwZRgDIAEoCRIPCgdjb21tYW5kGAQgASgJEjYKB2hlYWRlcnMYBSADKAsyJS5tZWNhdGwudjEuTWNwU2VydmVyU3BlYy5IZWFkZXJzRW50cnkaLgoMSGVhZGVyc0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEiKwoUR2V0U2VydmVySW5mb1JlcXVlc3QSEwoLcHJvdmlkZXJfaWQYASABKAkibwoVR2V0U2VydmVySW5mb1Jlc3BvbnNlEhAKCGJ1aWxkX2lkGAEgASgJEh0KFXNlcnZlcl9pbXBsZW1lbnRhdGlvbhgCIAEoCRIlCh1sbG1fcHJvdmlkZXJfZGlzcGxheV9lbmRwb2ludBgDIAEoCSIdChtHZXRDb21wYXRpYmlsaXR5SW5mb1JlcXVlc3QijAEKHEdldENvbXBhdGliaWxpdHlJbmZvUmVzcG9uc2USEQoJYXBpX21ham9yGAEgASgFEjMKDGNhcGFiaWxpdGllcxgCIAEoCzIdLm1lY2F0bC52MS5TZXJ2ZXJDYXBhYmlsaXRpZXMSEAoIZmVhdHVyZXMYAyADKAkSEgoKZGVwbG95bWVudBgEIAEoCSK9BQoSU2VydmVyQ2FwYWJpbGl0aWVzEgsKA21jcBgBIAEoCBIWCg5zbGFzaF9jb21tYW5kcxgCIAEoCBIOCgZtZW1vcnkYAyABKAgSDgoGc2tpbGxzGAQgASgIEg0KBXRlYW1zGAUgASgIEg0KBXNoZWxsGAYgASgIEg0KBWltYWdlGAcgASgIEg0KBWF1ZGlvGAggASgIEg4KBmFnZW50cxgJIAEoCBIMCgRzb3VsGAogASgIEhIKCnVzZXJfbW9kZWwYCyABKAgSFwoPbW9kZWxfc2VsZWN0aW9uGAwgASgIEg8KB3Bvc3R1cmUYDSABKAkSEQoJd29ya3RyZWVzGA4gASgIEhIKCnNjaGVkdWxpbmcYDyABKAgSEgoKcmVmbGVjdGlvbhgQIAEoCBIaChJsZWFybmluZ19wcm9wb3NhbHMYESABKAgSFgoObGVhcm5lZF9za2lsbHMYEiABKAgSFwoPc3RvcmFnZV9jbGVhbnVwGBYgASgIEjgKDG1hbnVhbF9kcmVhbRgTIAEoCzIiLm1lY2F0bC52MS5NYW51YWxEcmVhbUNhcGFiaWxpdGllcxIWCg5zdG9yYWdlX2hlYWx0aBgXIAEoCBINCgVzdGVlchgYIAEoCBIZChFtYW51YWxfY29tcGFjdGlvbhgZIAEoCBIVCg1zZXNzaW9uX2RlYnVnGBogASgIEhEKCWRlYnVnX21jcBgbIAEoCBIcChR3b3Jrc3BhY2VfZW5yb2xsbWVudBgcIAEoCBIcChRtY3BfY29ubmVjdG9yX3N0YXR1cxgdIAEoCBITCgttY3BfcmVmcmVzaBgeIAEoCBIbChNleGVjdXRpb25fdGVtcGxhdGVzGB8gASgISgQIFBAVSgQIFRAWUg9sZWdhY3lfYWRvcHRpb25SEXN0b3JhZ2VfbWlncmF0aW9uIjUKH0xpc3RTZXNzaW9uTWNwQ29ubmVjdG9yc1JlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCSKyAQogTGlzdFNlc3Npb25NY3BDb25uZWN0b3JzUmVzcG9uc2USFAoMYXZhaWxhYmlsaXR5GAEgASgJEhgKEGVucm9sbG1lbnRfc3RhdGUYAiABKAkSMQoKY29ubmVjdG9ycxgDIAMoCzIdLm1lY2F0bC52MS5NY3BDb25uZWN0b3JTdGF0dXMSGAoQdG90YWxfY29ubmVjdG9ycxgEIAEoDRIRCgl0cnVuY2F0ZWQYBSABKAgiTwoSTWNwQ29ubmVjdG9yU3RhdHVzEgwKBG5hbWUYASABKAkSFwoPY2F0YWxvZ3VlX3N0YXRlGAIgASgJEhIKCnRvb2xfY291bnQYAyABKA0i4AEKFUNyZWF0ZVNlc3Npb25SZXNwb25zZRISCgpzZXNzaW9uX2lkGAEgASgJEjwKFHNlc3Npb25fY2FwYWJpbGl0aWVzGAMgASgLMh4ubWVjYXRsLnYxLlNlc3Npb25DYXBhYmlsaXRpZXMSMAoOcmVzb2x2ZWRfbW9kZWwYBCABKAsyGC5tZWNhdGwudjEuUmVzb2x2ZWRNb2RlbBIvCglwbGFjZW1lbnQYBSABKAsyHC5tZWNhdGwudjEuUGxhY2VtZW50TWV0YWRhdGFKBAgCEANSDGNhcGFiaWxpdGllcyJSChFQbGFjZW1lbnRNZXRhZGF0YRIMCgRraW5kGAEgASgJEg0KBWxhYmVsGAIgASgJEg4KBmJyYW5jaBgDIAEoCRIQCghyZXZpc2lvbhgEIAEoCSJoCg1SZXNvbHZlZE1vZGVsEhMKC3Byb3ZpZGVyX2lkGAEgASgJEhAKCG1vZGVsX2lkGAIgASgJEhYKDmNvbnRleHRfd2luZG93GAMgASgDEhgKEHJlYXNvbmluZ19lZmZvcnQYBCABKAkiZAoTU2Vzc2lvbkNhcGFiaWxpdGllcxINCgVpbWFnZRgBIAEoCBINCgVhdWRpbxgCIAEoCBIXCg9leGVjdXRpb25fZmlsZXMYAyABKAgSFgoOYnVpbHRfaW5fc2hlbGwYBCABKAgiMAoRR2V0U2Vzc2lvblJlcXVlc3QSGwoKc2Vzc2lvbl9pZBgBIAEoCUIHukgEcgIQASI5ChJHZXRTZXNzaW9uUmVzcG9uc2USIwoHc2Vzc2lvbhgBIAEoCzISLm1lY2F0bC52MS5TZXNzaW9uIjoKG0dldFNlc3Npb25UcmFuc2NyaXB0UmVxdWVzdBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABIlIKFEFjdGl2aXR5UmVwbGF5U3RhdHVzEhEKCWF2YWlsYWJsZRgBIAEoCBIQCghjb21wbGV0ZRgCIAEoCBIVCg1hdXRob3JpdGF0aXZlGAMgASgIIu0BChxHZXRTZXNzaW9uVHJhbnNjcmlwdFJlc3BvbnNlEhIKCnNlc3Npb25faWQYASABKAkSMAoIbWVzc2FnZXMYAiADKAsyHi5tZWNhdGwudjEuQ29udmVyc2F0aW9uTWVzc2FnZRIQCghjb21wbGV0ZRgDIAEoCBIxCghhY3Rpdml0eRgEIAEoCzIfLm1lY2F0bC52MS5BY3Rpdml0eVJlcGxheVN0YXR1cxIMCgRraW5kGAUgASgJEjQKDHJlbGF0aW9uc2hpcBgGIAEoCzIeLm1lY2F0bC52MS5TZXNzaW9uUmVsYXRpb25zaGlwIjkKGlN0cmVhbVNlc3Npb25FdmVudHNSZXF1ZXN0EhsKCnNlc3Npb25faWQYASABKAlCB7pIBHICEAEiNwoYU3RyZWFtU2Vzc2lvbkxpdmVSZXF1ZXN0EhsKCnNlc3Npb25faWQYASABKAlCB7pIBHICEAEiWAoZV2F0Y2hTZXNzaW9uRXZlbnRzUmVxdWVzdBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABEg4KBmN1cnNvchgCIAEoCRIOCgZydW5faWQYAyABKAkiXAoaV2F0Y2hTZXNzaW9uRXZlbnRzUmVzcG9uc2USHwoFZXZlbnQYASABKAsyEC5tZWNhdGwudjEuRXZlbnQSDgoGY3Vyc29yGAIgASgJEg0KBXBoYXNlGAMgASgJIjgKE0xpc3RTZXNzaW9uc1JlcXVlc3QSEQoJcGFnZV9zaXplGAEgASgFEg4KBmN1cnNvchgCIAEoCSJOCglQcmluY2lwYWwSDgoGaXNzdWVyGAEgASgJEg8KB3N1YmplY3QYAiABKAkSEgoKZ3JhbnRfdHlwZRgDIAEoCRIMCgRuYW1lGAQgASgJIv4ECg5TZXNzaW9uU3VtbWFyeRISCgpzZXNzaW9uX2lkGAEgASgJEhgKEG1vZGlmaWVkX2F0X3VuaXgYAiABKAMSDQoFc3RhdGUYAyABKAkSDQoFdHVybnMYBCABKAUSEAoIbW9kZWxfaWQYBSABKAkSFwoPY3JlYXRlZF9hdF91bml4GAYgASgDEiMKBW93bmVyGAggASgLMhQubWVjYXRsLnYxLlByaW5jaXBhbBIMCgRraW5kGAkgASgJEjQKDHJlbGF0aW9uc2hpcBgKIAEoCzIeLm1lY2F0bC52MS5TZXNzaW9uUmVsYXRpb25zaGlwEj0KDGNhcGFiaWxpdGllcxgLIAEoCzInLm1lY2F0bC52MS5TZXNzaW9uSW52ZW50b3J5Q2FwYWJpbGl0aWVzEhMKC3JlYXNvbl9jb2RlGAwgASgJEi8KCXBsYWNlbWVudBgPIAEoCzIcLm1lY2F0bC52MS5QbGFjZW1lbnRNZXRhZGF0YRIvCg50aXRsZV9tZXRhZGF0YRgQIAEoCzIXLm1lY2F0bC52MS5TZXNzaW9uVGl0bGUSPgoLdG9rZW5fdXNhZ2UYESADKAsyKS5tZWNhdGwudjEuU2Vzc2lvblN1bW1hcnkuVG9rZW5Vc2FnZUVudHJ5EhYKDmFjdGl2aXR5X3N0YXRlGBIgASgJGkgKD1Rva2VuVXNhZ2VFbnRyeRILCgNrZXkYASABKAkSJAoFdmFsdWUYAiABKAsyFS5tZWNhdGwudjEuVG9rZW5Vc2FnZToCOAFKBAgHEAhKBAgNEA5KBAgOEA9SBXRpdGxlUgl3b3Jrc3BhY2VSEHRpdGxlX3Byb3ZlbmFuY2Ui5gEKE1Nlc3Npb25SZWxhdGlvbnNoaXASGQoRcGFyZW50X3Nlc3Npb25faWQYASABKAkSDwoHY2FsbF9pZBgCIAEoCRIZCgxicmFuY2hfaW5kZXgYAyABKAVIAIgBARIVCg1zY2hlZHVsZV9uYW1lGAQgASgJEhkKEW9yaWdpbl9zZXNzaW9uX2lkGAUgASgJEg8KB3RlYW1faWQYBiABKAkSEwoLbWVtYmVyX25hbWUYByABKAkSHwoXZGVidWdfdGFyZ2V0X3Nlc3Npb25faWQYCCABKAlCDwoNX2JyYW5jaF9pbmRleCKSAgocU2Vzc2lvbkludmVudG9yeUNhcGFiaWxpdGllcxITCgtwdWJsaWNfY2hhdBgBIAEoCBIPCgdpbnNwZWN0GAIgASgIEiAKGGF1dGhvcml0YXRpdmVfdHJhbnNjcmlwdBgDIAEoCBIXCg9hY3Rpdml0eV9yZXBsYXkYBCABKAgSDwoHY29weV9pZBgFIAEoCBIXCg92aWV3X3RyYW5zY3JpcHQYBiABKAgSDAoEZm9yaxgHIAEoCBIOCgZyZW5hbWUYCCABKAgSDgoGZGVsZXRlGAkgASgIEjkKB3JlYXNvbnMYCiABKAsyKC5tZWNhdGwudjEuU2Vzc2lvbkludmVudG9yeUFjdGlvblJlYXNvbnMinQEKHVNlc3Npb25JbnZlbnRvcnlBY3Rpb25SZWFzb25zEhMKC3B1YmxpY19jaGF0GAEgASgJEg8KB2luc3BlY3QYAiABKAkSDwoHY29weV9pZBgDIAEoCRIXCg92aWV3X3RyYW5zY3JpcHQYBCABKAkSDAoEZm9yaxgFIAEoCRIOCgZyZW5hbWUYBiABKAkSDgoGZGVsZXRlGAcgASgJIm0KFExpc3RTZXNzaW9uc1Jlc3BvbnNlEisKCHNlc3Npb25zGAEgAygLMhkubWVjYXRsLnYxLlNlc3Npb25TdW1tYXJ5EhMKC25leHRfY3Vyc29yGAIgASgJEhMKC3RvdGFsX2NvdW50GAMgASgFIhkKF0dldFN0b3JhZ2VIZWFsdGhSZXF1ZXN0It4BCg9SZXRlbnRpb25Qb2xpY3kSHAoUbWFpbl9tYXhfYWdlX3NlY29uZHMYASABKAMSFgoObWFpbl9tYXhfY291bnQYAiABKAUSHQoVY2hpbGRfbWF4X2FnZV9zZWNvbmRzGAMgASgDEhcKD2NoaWxkX21heF9jb3VudBgEIAEoBRIhChlzY2hlZHVsZWRfbWF4X2FnZV9zZWNvbmRzGAUgASgDEhsKE3NjaGVkdWxlZF9tYXhfY291bnQYBiABKAUSHQoVc3dlZXBfY2FkZW5jZV9zZWNvbmRzGAcgASgDIskHChhHZXRTdG9yYWdlSGVhbHRoUmVzcG9uc2USEQoJYXZhaWxhYmxlGAEgASgIEhoKEnVuYXZhaWxhYmxlX3JlYXNvbhgCIAEoCRIVCg1jdXJyZW50X2J5dGVzGAMgASgDEh8KF2N1cnJlbnRfYnl0ZXNfYXZhaWxhYmxlGAQgASgIEhkKEXJlY2xhaW1hYmxlX2J5dGVzGAUgASgDEiMKG3JlY2xhaW1hYmxlX2J5dGVzX2F2YWlsYWJsZRgGIAEoCBIVCg1zZXNzaW9uX2NvdW50GAcgASgDEhIKCmZpbGVfY291bnQYCCABKAMSEAoIdjFfY291bnQYCSABKAMSEAoIdjJfY291bnQYCiABKAMSEgoKbWFpbl9jb3VudBgLIAEoAxITCgtjaGlsZF9jb3VudBgMIAEoAxIXCg9zY2hlZHVsZWRfY291bnQYDSABKAMSFQoNdW5rbm93bl9jb3VudBgOIAEoAxIVCg1jb3JydXB0X2NvdW50GA8gASgDEioKBnBvbGljeRgQIAEoCzIaLm1lY2F0bC52MS5SZXRlbnRpb25Qb2xpY3kSFwoPbGFzdF9zd2VlcF91bml4GBEgASgDEhwKFGxhc3Rfc3dlZXBfYXZhaWxhYmxlGBIgASgIEhcKD25leHRfc3dlZXBfdW5peBgTIAEoAxIcChRuZXh0X3N3ZWVwX2F2YWlsYWJsZRgUIAEoCBISCgphY3RpdmVfam9iGBUgASgJEhQKDGxhc3RfZmFpbHVyZRgWIAEoCRIkChxvd25lcmxlc3Nfc2Vzc2lvbnNfYXZhaWxhYmxlGBcgASgIEi0KJW93bmVybGVzc19zZXNzaW9uc191bmF2YWlsYWJsZV9yZWFzb24YGCABKAkSHwoXb3duZXJsZXNzX3Nlc3Npb25fY291bnQYGSABKAMSHQoVb3duZXJsZXNzX3Nlc3Npb25faWRzGBogAygJEicKH293bmVybGVzc19zZXNzaW9uX2lkc190cnVuY2F0ZWQYGyABKAgSJQodb3duZXJsZXNzX3NjaGVkdWxlc19hdmFpbGFibGUYHCABKAgSLgomb3duZXJsZXNzX3NjaGVkdWxlc191bmF2YWlsYWJsZV9yZWFzb24YHSABKAkSIAoYb3duZXJsZXNzX3NjaGVkdWxlX2NvdW50GB4gASgDEiAKGG93bmVybGVzc19zY2hlZHVsZV9uYW1lcxgfIAMoCRIqCiJvd25lcmxlc3Nfc2NoZWR1bGVfbmFtZXNfdHJ1bmNhdGVkGCAgASgIItkCCg1DbGVhbnVwQ291bnRzEg0KBXRvdGFsGAEgASgFEjUKB2J5X2tpbmQYAiADKAsyJC5tZWNhdGwudjEuQ2xlYW51cENvdW50cy5CeUtpbmRFbnRyeRI3CghieV9zdGF0ZRgDIAMoCzIlLm1lY2F0bC52MS5DbGVhbnVwQ291bnRzLkJ5U3RhdGVFbnRyeRI5CglieV9yZWFzb24YBCADKAsyJi5tZWNhdGwudjEuQ2xlYW51cENvdW50cy5CeVJlYXNvbkVudHJ5Gi0KC0J5S2luZEVudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoBToCOAEaLgoMQnlTdGF0ZUVudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoBToCOAEaLwoNQnlSZWFzb25FbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAU6AjgBIoYBChBDbGVhbnVwQ2FuZGlkYXRlEhIKCnNlc3Npb25faWQYASABKAkSDAoEa2luZBgCIAEoCRINCgVzdGF0ZRgDIAEoCRIOCgZyZWFzb24YBCABKAkSGAoQbW9kaWZpZWRfYXRfdW5peBgFIAEoAxIXCg9lc3RpbWF0ZWRfYnl0ZXMYBiABKAMiKgoZUGxhblNlc3Npb25DbGVhbnVwUmVxdWVzdBINCgVraW5kcxgBIAMoCSLTAgoaUGxhblNlc3Npb25DbGVhbnVwUmVzcG9uc2USGgoSY29uZmlybWF0aW9uX3Rva2VuGAEgASgJEhEKCWF2YWlsYWJsZRgCIAEoCBIaChJ1bmF2YWlsYWJsZV9yZWFzb24YAyABKAkSEgoKZ2VuZXJhdGlvbhgEIAEoCRIWCg5wb2xpY3lfdmVyc2lvbhgFIAEoCRItCghlbGlnaWJsZRgGIAMoCzIbLm1lY2F0bC52MS5DbGVhbnVwQ2FuZGlkYXRlEisKCXByb3RlY3RlZBgHIAEoCzIYLm1lY2F0bC52MS5DbGVhbnVwQ291bnRzEhcKD2VzdGltYXRlZF9ieXRlcxgIIAEoAxIWCg5wbGFubmVkX2pvYl9pZBgJIAEoCRIxCg9lbGlnaWJsZV9jb3VudHMYCiABKAsyGC5tZWNhdGwudjEuQ2xlYW51cENvdW50cyJBChpBcHBseVNlc3Npb25DbGVhbnVwUmVxdWVzdBIjChJjb25maXJtYXRpb25fdG9rZW4YASABKAlCB7pIBHICEAEiNgobQ2FuY2VsU2Vzc2lvbkNsZWFudXBSZXF1ZXN0EhcKBmpvYl9pZBgBIAEoCUIHukgEcgIQASI2ChtHZXRTZXNzaW9uQ2xlYW51cEpvYlJlcXVlc3QSFwoGam9iX2lkGAEgASgJQge6SARyAhABIk0KEENsZWFudXBJdGVtRXJyb3ISEwoLaXRlbV9oYW5kbGUYASABKAkSEwoLcmVhc29uX2NvZGUYAiABKAkSDwoHbWVzc2FnZRgDIAEoCSKsAQoKQ2xlYW51cEpvYhIOCgZqb2JfaWQYASABKAkSDQoFc3RhdGUYAiABKAkSEQoJcHJvY2Vzc2VkGAMgASgFEg8KB2RlbGV0ZWQYBCABKAUSDwoHc2tpcHBlZBgFIAEoBRINCgVzdGFsZRgGIAEoBRIOCgZmYWlsZWQYByABKAUSKwoGZXJyb3JzGAggAygLMhsubWVjYXRsLnYxLkNsZWFudXBJdGVtRXJyb3IiVgoOU2V0TW9kZVJlcXVlc3QSGwoKc2Vzc2lvbl9pZBgBIAEoCUIHukgEcgIQARInCgRtb2RlGAIgASgOMhkubWVjYXRsLnYxLlBlcm1pc3Npb25Nb2RlIjYKD1NldE1vZGVSZXNwb25zZRIjCgdzZXNzaW9uGAEgASgLMhIubWVjYXRsLnYxLlNlc3Npb24iMgoTQ2xvc2VTZXNzaW9uUmVxdWVzdBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABIhYKFENsb3NlU2Vzc2lvblJlc3BvbnNlIksKFFJlbmFtZVNlc3Npb25SZXF1ZXN0EhsKCnNlc3Npb25faWQYASABKAlCB7pIBHICEAESFgoFdGl0bGUYAiABKAlCB7pIBHICEAEiPAoVUmVuYW1lU2Vzc2lvblJlc3BvbnNlEiMKB3Nlc3Npb24YASABKAsyEi5tZWNhdGwudjEuU2Vzc2lvbiIzChREZWxldGVTZXNzaW9uUmVxdWVzdBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABIhcKFURlbGV0ZVNlc3Npb25SZXNwb25zZSJvChNDbGVhclNlc3Npb25SZXF1ZXN0EiIKEXNvdXJjZV9zZXNzaW9uX2lkGAEgASgJQge6SARyAhABEh4KEXdvcmt0cmVlX3NlbGVjdG9yGAIgASgJSACIAQFCFAoSX3dvcmt0cmVlX3NlbGVjdG9yIlsKFENsZWFyU2Vzc2lvblJlc3BvbnNlEhIKCnNlc3Npb25faWQYASABKAkSLwoJcGxhY2VtZW50GAIgASgLMhwubWVjYXRsLnYxLlBsYWNlbWVudE1ldGFkYXRhIr4BChJGb3JrU2Vzc2lvblJlcXVlc3QSIgoRc291cmNlX3Nlc3Npb25faWQYASABKAlCB7pIBHICEAESDQoFdGl0bGUYAiABKAkSGAoQcmVhc29uaW5nX2VmZm9ydBgDIAEoCRIeChF3b3JrdHJlZV9zZWxlY3RvchgEIAEoCUgAiAEBEhMKC3Byb3ZpZGVyX2lkGAUgASgJEhAKCG1vZGVsX2lkGAYgASgJQhQKEl93b3JrdHJlZV9zZWxlY3RvciJaChNGb3JrU2Vzc2lvblJlc3BvbnNlEhIKCnNlc3Npb25faWQYASABKAkSLwoJcGxhY2VtZW50GAIgASgLMhwubWVjYXRsLnYxLlBsYWNlbWVudE1ldGFkYXRhIjsKEENvbnRleHRPY2N1cGFuY3kSFAoMaW5wdXRfdG9rZW5zGAEgASgDEhEKCWVzdGltYXRlZBgCIAEoCCKsBgoHU2Vzc2lvbhISCgpzZXNzaW9uX2lkGAEgASgJEg0KBXN0YXRlGAIgASgJEicKBG1vZGUYAyABKA4yGS5tZWNhdGwudjEuUGVybWlzc2lvbk1vZGUSIQoGbGltaXRzGAUgASgLMhEubWVjYXRsLnYxLkxpbWl0cxINCgV0dXJucxgGIAEoBRISCgp0b29sX2NhbGxzGAcgASgFEhcKD2NyZWF0ZWRfYXRfdW5peBgIIAEoAxIwCg5yZXNvbHZlZF9tb2RlbBgJIAEoCzIYLm1lY2F0bC52MS5SZXNvbHZlZE1vZGVsEgwKBGtpbmQYDiABKAkSNAoMcmVsYXRpb25zaGlwGA8gASgLMh4ubWVjYXRsLnYxLlNlc3Npb25SZWxhdGlvbnNoaXASGQoRZGVidWdfbWNwX3NlcnZlcnMYECADKAkSFwoPZGVidWdfbWNwX3Rvb2xzGBEgAygJEi8KCXBsYWNlbWVudBgSIAEoCzIcLm1lY2F0bC52MS5QbGFjZW1lbnRNZXRhZGF0YRIvCg50aXRsZV9tZXRhZGF0YRgTIAEoCzIXLm1lY2F0bC52MS5TZXNzaW9uVGl0bGUSNwoLdG9rZW5fdXNhZ2UYFCADKAsyIi5tZWNhdGwudjEuU2Vzc2lvbi5Ub2tlblVzYWdlRW50cnkSPAoUc2Vzc2lvbl9jYXBhYmlsaXRpZXMYFSABKAsyHi5tZWNhdGwudjEuU2Vzc2lvbkNhcGFiaWxpdGllcxI9ChhsYXRlc3RfY29udGV4dF9vY2N1cGFuY3kYFiABKAsyGy5tZWNhdGwudjEuQ29udGV4dE9jY3VwYW5jeRpICg9Ub2tlblVzYWdlRW50cnkSCwoDa2V5GAEgASgJEiQKBXZhbHVlGAIgASgLMhUubWVjYXRsLnYxLlRva2VuVXNhZ2U6AjgBSgQIBBAFSgQIChALSgQICxAMSgQIDBANSgQIDRAOUgl3b3Jrc3BhY2VSBXRpdGxlUgxjYXBhYmlsaXRpZXNSEHRpdGxlX3Byb3ZlbmFuY2VSGmFkb3B0aW9uX3NvdXJjZV9zZXNzaW9uX2lkIpUBCgxTZXNzaW9uVGl0bGUSDQoFdGl0bGUYASABKAkSEgoKcHJvdmVuYW5jZRgCIAEoCRIYChBnZW5lcmF0aW9uX3N0YXRlGAMgASgJEjYKDmxhdGVzdF9hdHRlbXB0GAQgASgLMh4ubWVjYXRsLnYxLlRpdGxlQXR0ZW1wdFN1bW1hcnkSEAoIcmV2aXNpb24YBSABKAQiOAoTVGl0bGVBdHRlbXB0U3VtbWFyeRIKCgJpZBgBIAEoCRIPCgdvdXRjb21lGAIgASgJSgQIAxAEIqEBCgpUb2tlblVzYWdlEh8KBXRvdGFsGAEgASgLMhAubWVjYXRsLnYxLlVzYWdlEjEKBm1vZGVscxgCIAMoCzIhLm1lY2F0bC52MS5Ub2tlblVzYWdlLk1vZGVsc0VudHJ5Gj8KC01vZGVsc0VudHJ5EgsKA2tleRgBIAEoCRIfCgV2YWx1ZRgCIAEoCzIQLm1lY2F0bC52MS5Vc2FnZToCOAEixAIKD0NvbnZlcnNlUmVxdWVzdBIjCgZwcm9tcHQYASABKAsyES5tZWNhdGwudjEuUHJvbXB0SAASJgoFcmV0cnkYAiABKAsyFS5tZWNhdGwudjEuUmV0cnlTdGFydEgAEjQKD3Jlc3VtZV9hcHByb3ZhbBgKIAEoCzIZLm1lY2F0bC52MS5SZXN1bWVBcHByb3ZhbEgAEiMKBmNhbmNlbBgLIAEoCzIRLm1lY2F0bC52MS5DYW5jZWxIABIuCgxjYW5jZWxfY2hpbGQYDCABKAsyFi5tZWNhdGwudjEuQ2FuY2VsQ2hpbGRIABIhCgVzdGVlchgNIAEoCzIQLm1lY2F0bC52MS5TdGVlckgAEi4KDHN0ZWVyX2NhbmNlbBgOIAEoCzIWLm1lY2F0bC52MS5TdGVlckNhbmNlbEgAQgYKBGtpbmQiMwoQQ29udmVyc2VSZXNwb25zZRIfCgVldmVudBgBIAEoCzIQLm1lY2F0bC52MS5FdmVudCKcAQoHQ29udGVudBIlCgRraW5kGAEgASgOMhcubWVjYXRsLnYxLkNvbnRlbnQuS2luZBIRCgltaW1lX3R5cGUYAiABKAkSDAoEZGF0YRgDIAEoDBILCgN1cmwYBCABKAkiPAoES2luZBIUChBLSU5EX1VOU1BFQ0lGSUVEEAASDgoKS0lORF9JTUFHRRABEg4KCktJTkRfQVVESU8QAiKQAwoMQ29udGVudEJsb2NrEioKBGtpbmQYASABKA4yHC5tZWNhdGwudjEuQ29udGVudEJsb2NrLktpbmQSEQoJbWltZV90eXBlGAIgASgJEgwKBGRhdGEYAyABKAwSCwoDdXJsGAQgASgJEgwKBHRleHQYBSABKAkSDAoEbmFtZRgGIAEoCRINCgV0aXRsZRgHIAEoCRITCgtkZXNjcmlwdGlvbhgIIAEoCRIMCgRzaXplGAkgASgDEhAKCGF1ZGllbmNlGAogAygJEhAKCHByaW9yaXR5GAsgASgBEhUKDWxhc3RfbW9kaWZpZWQYDCABKAkinAEKBEtpbmQSFAoQS0lORF9VTlNQRUNJRklFRBAAEg0KCUtJTkRfVEVYVBABEg4KCktJTkRfSU1BR0UQAhIOCgpLSU5EX0FVRElPEAMSFgoSS0lORF9SRVNPVVJDRV9MSU5LEAQSGgoWS0lORF9FTUJFRERFRF9SRVNPVVJDRRAFEhsKF0tJTkRfU1RSVUNUVVJFRF9DT05URU5UEAYiKQoKUmV0cnlTdGFydBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABIn4KBlByb21wdBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABEgwKBHRleHQYAiABKAkSIQoFcGFydHMYAyADKAsyEi5tZWNhdGwudjEuQ29udGVudBImCh5zZXJ2ZXJfb3duZWRfcGxhbl9jb250aW51YXRpb24YBCABKAgi1wEKDlJlc3VtZUFwcHJvdmFsEhcKBmFza19pZBgBIAEoCUIHukgEcgIQARI5Cgd2ZXJkaWN0GAMgASgOMhoubWVjYXRsLnYxLkFwcHJvdmFsVmVyZGljdEIMukgJggEGGAEYAhgDEhcKD2V4cGVjdGVkX3J1bl9pZBgEIAEoCRIRCglyZXZpZXdfaWQYBSABKAkSOAoOZ3VhcmRyYWlsX2tpbmQYBiABKA4yIC5tZWNhdGwudjEuR3VhcmRyYWlsQXBwcm92YWxLaW5kSgQIAhADUgVhbGxvdyIhCgZDYW5jZWwSFwoPZXhwZWN0ZWRfcnVuX2lkGAEgASgJIigKC0NhbmNlbENoaWxkEhkKCGNoaWxkX2lkGAEgASgJQge6SARyAhABImUKBVN0ZWVyEgwKBHRleHQYASABKAkSEgoKbWVzc2FnZV9pZBgCIAEoCRIhCgVwYXJ0cxgDIAMoCzISLm1lY2F0bC52MS5Db250ZW50EhcKD2V4cGVjdGVkX3J1bl9pZBgEIAEoCSI6CgtTdGVlckNhbmNlbBISCgptZXNzYWdlX2lkGAEgASgJEhcKD2V4cGVjdGVkX3J1bl9pZBgCIAEoCSL2AQoUUmVzb2x2ZVJ1bkFza1JlcXVlc3QSGwoKc2Vzc2lvbl9pZBgBIAEoCUIHukgEcgIQARIgCg9leHBlY3RlZF9ydW5faWQYAiABKAlCB7pIBHICEAESFwoGYXNrX2lkGAMgASgJQge6SARyAhABEjkKB3ZlcmRpY3QYBCABKA4yGi5tZWNhdGwudjEuQXBwcm92YWxWZXJkaWN0Qgy6SAmCAQYYARgCGAMSEQoJcmV2aWV3X2lkGAUgASgJEjgKDmd1YXJkcmFpbF9raW5kGAYgASgOMiAubWVjYXRsLnYxLkd1YXJkcmFpbEFwcHJvdmFsS2luZCI3ChVSZXNvbHZlUnVuQXNrUmVzcG9uc2USDgoGcnVuX2lkGAEgASgJEg4KBmFza19pZBgCIAEoCSKqAQoVUmVzb2x2ZVBsYW5Bc2tSZXF1ZXN0EhsKCnNlc3Npb25faWQYASABKAlCB7pIBHICEAESIAoPZXhwZWN0ZWRfcnVuX2lkGAIgASgJQge6SARyAhABEhcKBmFza19pZBgDIAEoCUIHukgEcgIQARI5Cgd2ZXJkaWN0GAQgASgOMhoubWVjYXRsLnYxLkFwcHJvdmFsVmVyZGljdEIMukgJggEGGAEYAhgDIjgKFlJlc29sdmVQbGFuQXNrUmVzcG9uc2USDgoGcnVuX2lkGAEgASgJEg4KBmFza19pZBgCIAEoCSJRChBDYW5jZWxSdW5SZXF1ZXN0EhsKCnNlc3Npb25faWQYASABKAlCB7pIBHICEAESIAoPZXhwZWN0ZWRfcnVuX2lkGAIgASgJQge6SARyAhABIiMKEUNhbmNlbFJ1blJlc3BvbnNlEg4KBnJ1bl9pZBgBIAEoCSKQAgoPU3RlZXJSdW5SZXF1ZXN0EhsKCnNlc3Npb25faWQYASABKAlCB7pIBHICEAESIAoPZXhwZWN0ZWRfcnVuX2lkGAIgASgJQge6SARyAhABEgwKBHRleHQYAyABKAkSKwoFcGFydHMYBCADKAsyEi5tZWNhdGwudjEuQ29udGVudEIIukgFkgECEBASGwoKbWVzc2FnZV9pZBgFIAEoCUIHukgEcgIYQDpmukhjGmEKEXN0ZWVyX3J1bi5jb250ZW50EiN0ZXh0IGFuZCBwYXJ0cyBjYW5ub3QgYm90aCBiZSBlbXB0eRondGhpcy50ZXh0ICE9ICcnIHx8IHNpemUodGhpcy5wYXJ0cykgPiAwImAKEFN0ZWVyUnVuUmVzcG9uc2USKAoHb3V0Y29tZRgBIAEoDjIXLm1lY2F0bC52MS5TdGVlck91dGNvbWUSDgoGcnVuX2lkGAIgASgJEhIKCm1lc3NhZ2VfaWQYAyABKAkicwoVQ2FuY2VsUnVuU3RlZXJSZXF1ZXN0EhsKCnNlc3Npb25faWQYASABKAlCB7pIBHICEAESIAoPZXhwZWN0ZWRfcnVuX2lkGAIgASgJQge6SARyAhABEhsKCm1lc3NhZ2VfaWQYAyABKAlCB7pIBHICGEAiZgoWQ2FuY2VsUnVuU3RlZXJSZXNwb25zZRIoCgdvdXRjb21lGAEgASgOMhcubWVjYXRsLnYxLlN0ZWVyT3V0Y29tZRIOCgZydW5faWQYAiABKAkSEgoKbWVzc2FnZV9pZBgDIAEoCSJoCiZHZXRNY3BBdXRob3JpemF0aW9uUHJlc2VudGF0aW9uUmVxdWVzdBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABEiEKEGF1dGhvcml6YXRpb25faWQYAiABKAlCB7pIBHICEAEiNgonR2V0TWNwQXV0aG9yaXphdGlvblByZXNlbnRhdGlvblJlc3BvbnNlEgsKA3VybBgBIAEoCSK0AQoeUmVjaGVja01jcEF1dGhvcml6YXRpb25SZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSGAoQYXV0aG9yaXphdGlvbl9pZBgCIAEoCRI0Cg9yZXN1bWVfYXBwcm92YWwYCiABKAsyGS5tZWNhdGwudjEuUmVzdW1lQXBwcm92YWxIABIjCgZjYW5jZWwYCyABKAsyES5tZWNhdGwudjEuQ2FuY2VsSABCCQoHY29udHJvbCJCCh9SZWNoZWNrTWNwQXV0aG9yaXphdGlvblJlc3BvbnNlEh8KBWV2ZW50GAEgASgLMhAubWVjYXRsLnYxLkV2ZW50IrMBCh1DYW5jZWxNY3BBdXRob3JpemF0aW9uUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEhgKEGF1dGhvcml6YXRpb25faWQYAiABKAkSNAoPcmVzdW1lX2FwcHJvdmFsGAogASgLMhkubWVjYXRsLnYxLlJlc3VtZUFwcHJvdmFsSAASIwoGY2FuY2VsGAsgASgLMhEubWVjYXRsLnYxLkNhbmNlbEgAQgkKB2NvbnRyb2wiQQoeQ2FuY2VsTWNwQXV0aG9yaXphdGlvblJlc3BvbnNlEh8KBWV2ZW50GAEgASgLMhAubWVjYXRsLnYxLkV2ZW50IpABCg1BdXRob3JpemF0aW9uEhgKEGF1dGhvcml6YXRpb25faWQYASABKAkSDwoHY2FsbF9pZBgCIAEoCRIuCgpleHBpcmVzX2F0GAMgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIOCgZzdGF0dXMYBCABKAkSFAoMZGlzcGxheV9uYW1lGAUgASgJIj4KF1BsYW5Db250aW51YXRpb25GYWlsdXJlEhMKC3BsYW5fcnVuX2lkGAEgASgJEg4KBmFza19pZBgCIAEoCSK3BwoFRXZlbnQSDAoEdHlwZRgBIAEoCRILCgNzZXEYAiABKAMSDAoEdHVybhgDIAEoBRIMCgR0ZXh0GAQgASgJEiYKCXRvb2xfY2FsbBgFIAEoCzITLm1lY2F0bC52MS5Ub29sQ2FsbBIqCgt0b29sX3Jlc3VsdBgGIAEoCzIVLm1lY2F0bC52MS5Ub29sUmVzdWx0EiUKA2FzaxgHIAEoCzIYLm1lY2F0bC52MS5QZXJtaXNzaW9uQXNrEiEKBnJlc3VsdBgIIAEoCzIRLm1lY2F0bC52MS5SZXN1bHQSJAoIdHVybl9lbmQYCiABKAsyEi5tZWNhdGwudjEuVHVybkVuZBIdCgRob29rGAsgASgLMg8ubWVjYXRsLnYxLkhvb2sSJQoIc3ViYWdlbnQYDCABKAsyEy5tZWNhdGwudjEuU3ViYWdlbnQSHQoEdGVhbRgNIAEoCzIPLm1lY2F0bC52MS5UZWFtEiUKCHBhcmFsbGVsGA4gASgLMhMubWVjYXRsLnYxLlBhcmFsbGVsEiwKCHNjaGVkdWxlGA8gASgLMhoubWVjYXRsLnYxLlNjaGVkdWxlUGF5bG9hZBIlCghhcHByb3ZhbBgQIAEoCzITLm1lY2F0bC52MS5BcHByb3ZhbBIqCgt1c2VyX3Byb21wdBgRIAEoCzIVLm1lY2F0bC52MS5Vc2VyUHJvbXB0EjgKEmNvbXBhY3Rpb25fYXJjaGl2ZRgSIAEoCzIcLm1lY2F0bC52MS5Db21wYWN0aW9uQXJjaGl2ZRIjCgVzdGVlchgTIAEoCzIULm1lY2F0bC52MS5TdGVlckVjaG8SKgoNc3RlZXJfb3V0Y29tZRgUIAEoCzITLm1lY2F0bC52MS5TdGVlckFjaxIqCgttb2RlbF9yZXRyeRgVIAEoCzIVLm1lY2F0bC52MS5Nb2RlbFJldHJ5Eg4KBnJ1bl9pZBgWIAEoCRImCgV0aXRsZRgXIAEoCzIXLm1lY2F0bC52MS5TZXNzaW9uVGl0bGUSLwoNYXV0aG9yaXphdGlvbhgYIAEoCzIYLm1lY2F0bC52MS5BdXRob3JpemF0aW9uEjIKD2NvbnRyb2xfcmVmdXNlZBgZIAEoCzIZLm1lY2F0bC52MS5Db250cm9sUmVmdXNlZBJFChlwbGFuX2NvbnRpbnVhdGlvbl9mYWlsdXJlGBogASgLMiIubWVjYXRsLnYxLlBsYW5Db250aW51YXRpb25GYWlsdXJlSgQICRAKUgV1c2FnZSIyCg5Db250cm9sUmVmdXNlZBIOCgZhc2tfaWQYASABKAkSEAoIY2F0ZWdvcnkYAiABKAkieAoKTW9kZWxSZXRyeRI2ChFyZXRyeV9kaXNwb3NpdGlvbhgBIAEoDjIbLm1lY2F0bC52MS5SZXRyeURpc3Bvc2l0aW9uEjIKD3N0cmVhbV9wcm9ncmVzcxgCIAEoDjIZLm1lY2F0bC52MS5TdHJlYW1Qcm9ncmVzcyJQCglTdGVlckVjaG8SDAoEdGV4dBgBIAEoCRISCgptZXNzYWdlX2lkGAIgASgJEiEKBXBhcnRzGAMgAygLMhIubWVjYXRsLnYxLkNvbnRlbnQiaAoIU3RlZXJBY2sSKAoHb3V0Y29tZRgBIAEoDjIXLm1lY2F0bC52MS5TdGVlck91dGNvbWUSDAoEdGV4dBgCIAEoCRIQCghwcm9tb3RlZBgDIAEoCBISCgptZXNzYWdlX2lkGAQgASgJIi0KDEd1YXJkcmFpbFJlZhILCgNyZWYYASABKAkSEAoIY2F0ZWdvcnkYAiABKAkirwMKD0d1YXJkcmFpbFJldmlldxIRCglyZXZpZXdfaWQYASABKAkSJAoDam9iGAIgASgOMhcubWVjYXRsLnYxLkd1YXJkcmFpbEpvYhIyCgphc3Nlc3NtZW50GAMgASgOMh4ubWVjYXRsLnYxLkd1YXJkcmFpbEFzc2Vzc21lbnQSMgoKaW5zcGVjdGlvbhgEIAEoDjIeLm1lY2F0bC52MS5HdWFyZHJhaWxJbnNwZWN0aW9uEjQKC2Rpc3Bvc2l0aW9uGAUgASgOMh8ubWVjYXRsLnYxLkd1YXJkcmFpbERpc3Bvc2l0aW9uEhMKC3JlYXNvbl9jb2RlGAYgASgJEg8KB3J1bGVfaWQYByABKAkSEwoLcnVsZV9vcmlnaW4YCCABKAkSGwoTY2hlY2tlcl9wcm92aWRlcl9pZBgJIAEoCRIYChBjaGVja2VyX21vZGVsX2lkGAogASgJEikKCGNvbmNlcm5zGAsgAygLMhcubWVjYXRsLnYxLkd1YXJkcmFpbFJlZhIoCgdzb3VyY2VzGAwgAygLMhcubWVjYXRsLnYxLkd1YXJkcmFpbFJlZiKnAQoWR3VhcmRyYWlsQXBwcm92YWxTY29wZRIRCglyZXZpZXdfaWQYASABKAkSLgoEa2luZBgCIAEoDjIgLm1lY2F0bC52MS5HdWFyZHJhaWxBcHByb3ZhbEtpbmQSFAoMZ3JhbnRfZGlnZXN0GAMgASgJEhQKDHNlc3Npb25fb25seRgEIAEoCBIYChByZXBlYXRfYXZhaWxhYmxlGAUgASgISgQIBhAHIjIKHExpc3RHdWFyZHJhaWxDb3ZlcmFnZVJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCSLTAQoWR3VhcmRyYWlsQ292ZXJhZ2VFbnRyeRIMCgR0b29sGAEgASgJEg0KBXBoYXNlGAIgASgJEiQKA2pvYhgDIAEoDjIXLm1lY2F0bC52MS5HdWFyZHJhaWxKb2ISDAoEbW9kZRgEIAEoCRIPCgdydWxlX2lkGAUgASgJEhMKC3J1bGVfb3JpZ2luGAYgASgJEjIKCmluc3BlY3Rpb24YByABKA4yHi5tZWNhdGwudjEuR3VhcmRyYWlsSW5zcGVjdGlvbhIOCgZyZWFzb24YCCABKAkimwEKHUxpc3RHdWFyZHJhaWxDb3ZlcmFnZVJlc3BvbnNlEg8KB2VuYWJsZWQYASABKAgSGwoTY2hlY2tlcl9wcm92aWRlcl9pZBgCIAEoCRIYChBjaGVja2VyX21vZGVsX2lkGAMgASgJEjIKB2VudHJpZXMYBCADKAsyIS5tZWNhdGwudjEuR3VhcmRyYWlsQ292ZXJhZ2VFbnRyeSJICh9HZXRHdWFyZHJhaWxSZXZpZXdEZXRhaWxSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkSEQoJcmV2aWV3X2lkGAIgASgJInMKIEdldEd1YXJkcmFpbFJldmlld0RldGFpbFJlc3BvbnNlEhEKCXJldmlld19pZBgBIAEoCRIPCgdjb25jZXJuGAIgASgJEhYKDnNvdXJjZV9kaXNwbGF5GAMgASgJEhMKC25leHRfYWN0aW9uGAQgASgJIpABCghBcHByb3ZhbBIOCgZhc2tfaWQYASABKAkSDAoEdG9vbBgDIAEoCRIPCgdjYWxsX2lkGAQgASgJEisKB3ZlcmRpY3QYBiABKA4yGi5tZWNhdGwudjEuQXBwcm92YWxWZXJkaWN0Eg4KBm9yaWdpbhgHIAEoCUoECAIQA0oECAUQBlIMYWxsb3dfYWx3YXlzIlAKClVzZXJQcm9tcHQSDAoEdGV4dBgBIAEoCRIhCgVwYXJ0cxgCIAMoCzISLm1lY2F0bC52MS5Db250ZW50EhEKCXN5bnRoZXRpYxgDIAEoCCJFChFDb21wYWN0aW9uQXJjaGl2ZRIwCghyZXBsYWNlZBgBIAMoCzIeLm1lY2F0bC52MS5Db252ZXJzYXRpb25NZXNzYWdlIu8BChNDb252ZXJzYXRpb25NZXNzYWdlEgwKBHJvbGUYASABKAkSDAoEdGV4dBgCIAEoCRInCgp0b29sX2NhbGxzGAMgAygLMhMubWVjYXRsLnYxLlRvb2xDYWxsEioKC3Rvb2xfcmVzdWx0GAQgASgLMhUubWVjYXRsLnYxLlRvb2xSZXN1bHQSEQoJcmVhc29uaW5nGAUgASgJEhYKDnByb3ZpZGVyX3BoYXNlGAYgASgJEiEKBXBhcnRzGAcgAygLMhIubWVjYXRsLnYxLkNvbnRlbnQSGQoRcmVhc29uaW5nX2l0ZW1faWQYCCABKAkidgoPU2NoZWR1bGVQYXlsb2FkEhUKDXNjaGVkdWxlX25hbWUYASABKAkSDwoHZmlyZV9pZBgCIAEoCRISCgpzZXNzaW9uX2lkGAMgASgJEgwKBGtpbmQYBCABKAkSDAoEc3RvcBgFIAEoCRILCgNlcnIYBiABKAkijgEKBEhvb2sSDQoFcGhhc2UYASABKAkSDAoEdG9vbBgCIAEoCRIpCghkZWNpc2lvbhgDIAEoDjIXLm1lY2F0bC52MS5Ib29rRGVjaXNpb24SDwoHY2FsbF9pZBgEIAEoCRItCglndWFyZHJhaWwYBSABKAsyGi5tZWNhdGwudjEuR3VhcmRyYWlsUmV2aWV3IqgCCg9Sb3V0aW5nRGVjaXNpb24SDwoHYmFja2VuZBgBIAEoCRIYChBjbGFzc2lmaWVyX21vZGVsGAIgASgJEhoKEmNhbmRpZGF0ZV9jYXRlZ29yeRgDIAEoCRIXCg9jYW5kaWRhdGVfbW9kZWwYBCABKAkSFwoKY29uZmlkZW5jZRgFIAEoAUgAiAEBEh8KEm1pbmltdW1fY29uZmlkZW5jZRgGIAEoAUgBiAEBEg8KB291dGNvbWUYByABKAkSGgoSY29uc2VjdXRpdmVfbWlzc2VzGAggASgFEhIKCm1pc3NfbGltaXQYCSABKAUSFAoMYnJlYWtlcl9vcGVuGAogASgIQg0KC19jb25maWRlbmNlQhUKE19taW5pbXVtX2NvbmZpZGVuY2UiigQKCFN1YmFnZW50EhYKDnBhcmVudF9jYWxsX2lkGAEgASgJEhAKCGNoaWxkX2lkGAIgASgJEgwKBGdvYWwYAyABKAkSEQoJdG9vbF9uYW1lGAQgASgJEhAKCGlzX2Vycm9yGAUgASgIEhIKCnRvb2xfY291bnQYBiABKAUSHwoFdXNhZ2UYByABKAsyEC5tZWNhdGwudjEuVXNhZ2USDAoEc3RvcBgIIAEoCRITCgtkdXJhdGlvbl9tcxgJIAEoAxISCgpiYWNrZ3JvdW5kGAogASgIEhcKD3JvdXRlZF9jYXRlZ29yeRgLIAEoCRIUCgxyb3V0ZWRfbW9kZWwYDCABKAkSDQoFbW9kZWwYDSABKAkSEgoKaW5uZXJfa2luZBgOIAEoCRIMCgR0ZXh0GA8gASgJEg4KBmRldGFpbBgQIAEoCRINCgVjYXVzZRgRIAEoCRIWCg5yb3V0aW5nX3JlYXNvbhgSIAEoCRI5ChByb3V0aW5nX2RlY2lzaW9uGBMgASgLMhoubWVjYXRsLnYxLlJvdXRpbmdEZWNpc2lvbkgAiAEBEhoKEmNoaWxkX3Rvb2xfY2FsbF9pZBgUIAEoCRIQCghwcm92aWRlchgVIAEoCRIgChhleHBsaWNpdF9yb3V0ZXJfY2F0ZWdvcnkYFiABKAlCEwoRX3JvdXRpbmdfZGVjaXNpb24ipgIKDlRlYW1NZW1iZXJTcGVjEgwKBG5hbWUYASABKAkSDAoEcm9sZRgCIAEoCRIQCghtdXRhdGluZxgDIAEoCBIMCgRsZWFkGAQgASgIEhcKD3JvdXRlZF9jYXRlZ29yeRgFIAEoCRIUCgxyb3V0ZWRfbW9kZWwYBiABKAkSDQoFbW9kZWwYByABKAkSFgoOcm91dGluZ19yZWFzb24YCCABKAkSOQoQcm91dGluZ19kZWNpc2lvbhgJIAEoCzIaLm1lY2F0bC52MS5Sb3V0aW5nRGVjaXNpb25IAIgBARIQCghwcm92aWRlchgKIAEoCRIgChhleHBsaWNpdF9yb3V0ZXJfY2F0ZWdvcnkYCyABKAlCEwoRX3JvdXRpbmdfZGVjaXNpb24i+gMKBFRlYW0SFgoOcGFyZW50X2NhbGxfaWQYASABKAkSDwoHdGVhbV9pZBgCIAEoCRIpCgZyb3N0ZXIYAyADKAsyGS5tZWNhdGwudjEuVGVhbU1lbWJlclNwZWMSDgoGbWVtYmVyGAQgASgJEhIKCmlubmVyX2tpbmQYBSABKAkSDAoEdGV4dBgGIAEoCRIRCgl0b29sX25hbWUYByABKAkSDgoGZGV0YWlsGAggASgJEhAKCGlzX2Vycm9yGAkgASgIEg4KBnJvdW5kcxgKIAEoBRIMCgRzdG9wGAsgASgJEh8KBXVzYWdlGAwgASgLMhAubWVjYXRsLnYxLlVzYWdlEhQKDGNvbnRleHRfdXNlZBgNIAEoAxIWCg5jb250ZXh0X3dpbmRvdxgOIAEoAxIiCgV0YXNrcxgPIAMoCzITLm1lY2F0bC52MS5UZWFtVGFzaxIoCghmaW5kaW5ncxgQIAMoCzIWLm1lY2F0bC52MS5UZWFtRmluZGluZxI2CgxkaXNwb3NpdGlvbnMYESADKAsyIC5tZWNhdGwudjEuVGVhbU1lbWJlckRpc3Bvc2l0aW9uEhkKEW1lbWJlcl9zZXNzaW9uX2lkGBIgASgJEg0KBWNhdXNlGBMgASgJEhoKEmNoaWxkX3Rvb2xfY2FsbF9pZBgUIAEoCSKOBQoIUGFyYWxsZWwSFgoOcGFyZW50X2NhbGxfaWQYASABKAkSDAoEa2luZBgCIAEoCRIMCgRqb2luGAMgASgJEhQKDGJyYW5jaF9jb3VudBgEIAEoBRIUCgxicmFuY2hfaW5kZXgYBSABKAUSFAoMYnJhbmNoX2xhYmVsGAYgASgJEgwKBGdvYWwYByABKAkSEQoJdG9vbF9uYW1lGAggASgJEhAKCGlzX2Vycm9yGAkgASgIEhIKCnRvb2xfY291bnQYCiABKAUSDgoGZmFpbGVkGAsgASgIEgwKBHN0b3AYDSABKAkSHwoFdXNhZ2UYDiABKAsyEC5tZWNhdGwudjEuVXNhZ2USEwoLZHVyYXRpb25fbXMYDyABKAMSDgoGd2lubmVyGBAgASgFEhAKCGNoaWxkX2lkGBIgASgJEhcKD3JvdXRlZF9jYXRlZ29yeRgTIAEoCRIUCgxyb3V0ZWRfbW9kZWwYFCABKAkSDQoFbW9kZWwYFSABKAkSEgoKaW5uZXJfa2luZBgWIAEoCRIMCgR0ZXh0GBcgASgJEg4KBmRldGFpbBgYIAEoCRIWCg5yb3V0aW5nX3JlYXNvbhgZIAEoCRI5ChByb3V0aW5nX2RlY2lzaW9uGBogASgLMhoubWVjYXRsLnYxLlJvdXRpbmdEZWNpc2lvbkgAiAEBEhoKEmNoaWxkX3Rvb2xfY2FsbF9pZBgbIAEoCRIQCghwcm92aWRlchgcIAEoCRIgChhleHBsaWNpdF9yb3V0ZXJfY2F0ZWdvcnkYHSABKAlCEwoRX3JvdXRpbmdfZGVjaXNpb25KBAgMEA1KBAgREBJSCXdvcmtzcGFjZVIQd2lubmVyX3dvcmtzcGFjZSIyCghUb29sQ2FsbBIKCgJpZBgBIAEoCRIMCgRuYW1lGAIgASgJEgwKBGFyZ3MYAyABKAkihQEKClRvb2xSZXN1bHQSDwoHY2FsbF9pZBgBIAEoCRIPCgdjb250ZW50GAIgASgJEhAKCGlzX2Vycm9yGAMgASgIEicKBmJsb2NrcxgEIAMoCzIXLm1lY2F0bC52MS5Db250ZW50QmxvY2sSGgoSc3RydWN0dXJlZF9jb250ZW50GAUgASgJIqMBCg1QZXJtaXNzaW9uQXNrEg4KBmFza19pZBgBIAEoCRIMCgR0b29sGAIgASgJEgwKBGFyZ3MYAyABKAkSDgoGcmVhc29uGAQgASgJEjQKCWd1YXJkcmFpbBgFIAEoCzIhLm1lY2F0bC52MS5HdWFyZHJhaWxBcHByb3ZhbFNjb3BlEhQKB2NhbGxfaWQYBiABKAlIAIgBAUIKCghfY2FsbF9pZCKFAgoGUmVzdWx0EgwKBHN0b3AYASABKAkSDAoEdGV4dBgCIAEoCRIfCgV1c2FnZRgDIAEoCzIQLm1lY2F0bC52MS5Vc2FnZRINCgVlcnJvchgEIAEoCRI7ChFyZXRyeV9kaXNwb3NpdGlvbhgGIAEoDjIbLm1lY2F0bC52MS5SZXRyeURpc3Bvc2l0aW9uSACIAQESNwoPc3RyZWFtX3Byb2dyZXNzGAcgASgOMhkubWVjYXRsLnYxLlN0cmVhbVByb2dyZXNzSAGIAQFCFAoSX3JldHJ5X2Rpc3Bvc2l0aW9uQhIKEF9zdHJlYW1fcHJvZ3Jlc3NKBAgFEAZSCXBlcm1hbmVudCJSCgdUdXJuRW5kEh8KBXVzYWdlGAEgASgLMhAubWVjYXRsLnYxLlVzYWdlEhMKC2R1cmF0aW9uX21zGAIgASgDEhEKCWVzdGltYXRlZBgDIAEoCCKFAQoFVXNhZ2USFAoMaW5wdXRfdG9rZW5zGAEgASgDEhUKDW91dHB1dF90b2tlbnMYAiABKAMSGQoRY2FjaGVfcmVhZF90b2tlbnMYAyABKAMSGgoSY2FjaGVfd3JpdGVfdG9rZW5zGAQgASgDEhgKEHJlYXNvbmluZ190b2tlbnMYBSABKAMikAEKC01jcFJlc291cmNlEg4KBnNlcnZlchgBIAEoCRILCgN1cmkYAiABKAkSDAoEbmFtZRgDIAEoCRINCgV0aXRsZRgEIAEoCRITCgtkZXNjcmlwdGlvbhgFIAEoCRIRCgltaW1lX3R5cGUYBiABKAkSDAoEc2l6ZRgHIAEoAxIRCglyZWFkX29ubHkYCCABKAgiKQoXTGlzdE1jcFJlc291cmNlc1JlcXVlc3QSDgoGc2VydmVyGAEgASgJIkUKGExpc3RNY3BSZXNvdXJjZXNSZXNwb25zZRIpCglyZXNvdXJjZXMYASADKAsyFi5tZWNhdGwudjEuTWNwUmVzb3VyY2UiUQoTTWNwUmVzb3VyY2VDb250ZW50cxILCgN1cmkYASABKAkSEQoJbWltZV90eXBlGAIgASgJEgwKBHRleHQYAyABKAkSDAoEYmxvYhgEIAEoDCJHChZSZWFkTWNwUmVzb3VyY2VSZXF1ZXN0EhcKBnNlcnZlchgBIAEoCUIHukgEcgIQARIUCgN1cmkYAiABKAlCB7pIBHICEAEiSwoXUmVhZE1jcFJlc291cmNlUmVzcG9uc2USMAoIY29udGVudHMYASADKAsyHi5tZWNhdGwudjEuTWNwUmVzb3VyY2VDb250ZW50cyJXChFNY3BQcm9tcHRBcmd1bWVudBIMCgRuYW1lGAEgASgJEg0KBXRpdGxlGAIgASgJEhMKC2Rlc2NyaXB0aW9uGAMgASgJEhAKCHJlcXVpcmVkGAQgASgIIn4KCU1jcFByb21wdBIOCgZzZXJ2ZXIYASABKAkSDAoEbmFtZRgCIAEoCRINCgV0aXRsZRgDIAEoCRITCgtkZXNjcmlwdGlvbhgEIAEoCRIvCglhcmd1bWVudHMYBSADKAsyHC5tZWNhdGwudjEuTWNwUHJvbXB0QXJndW1lbnQiJwoVTGlzdE1jcFByb21wdHNSZXF1ZXN0Eg4KBnNlcnZlchgBIAEoCSI/ChZMaXN0TWNwUHJvbXB0c1Jlc3BvbnNlEiUKB3Byb21wdHMYASADKAsyFC5tZWNhdGwudjEuTWNwUHJvbXB0IrkBChNHZXRNY3BQcm9tcHRSZXF1ZXN0EhcKBnNlcnZlchgBIAEoCUIHukgEcgIQARIVCgRuYW1lGAIgASgJQge6SARyAhABEkAKCWFyZ3VtZW50cxgDIAMoCzItLm1lY2F0bC52MS5HZXRNY3BQcm9tcHRSZXF1ZXN0LkFyZ3VtZW50c0VudHJ5GjAKDkFyZ3VtZW50c0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEiLgoQTWNwUHJvbXB0TWVzc2FnZRIMCgRyb2xlGAEgASgJEgwKBHRleHQYAiABKAkiWgoUR2V0TWNwUHJvbXB0UmVzcG9uc2USEwoLZGVzY3JpcHRpb24YASABKAkSLQoIbWVzc2FnZXMYAiADKAsyGy5tZWNhdGwudjEuTWNwUHJvbXB0TWVzc2FnZSJMCg1NY3BTZXJ2ZXJJbmZvEgwKBG5hbWUYASABKAkSCwoDdXJsGAIgASgJEhEKCXRyYW5zcG9ydBgDIAEoCRINCgVncm91cBgEIAEoCSKHAQoJTWNwU291cmNlEgwKBG5hbWUYASABKAkSDAoEa2luZBgCIAEoCRIPCgdlbmFibGVkGAMgASgIEg0KBWdyb3VwGAQgASgJEikKB3NlcnZlcnMYBSADKAsyGC5tZWNhdGwudjEuTWNwU2VydmVySW5mbxITCgtkaWFnbm9zdGljcxgGIAMoCSIXChVMaXN0TWNwU291cmNlc1JlcXVlc3QidQoWTGlzdE1jcFNvdXJjZXNSZXNwb25zZRIlCgdzb3VyY2VzGAEgAygLMhQubWVjYXRsLnYxLk1jcFNvdXJjZRIQCghyZXZpc2lvbhgCIAEoBBINCgVzdGFsZRgDIAEoCBITCgtyZWNvbmNpbGluZxgEIAEoCCI3ChhSZWZyZXNoTWNwU291cmNlc1JlcXVlc3QSGwoKc2Vzc2lvbl9pZBgBIAEoCUIHukgEcgIQASI+ChlSZWZyZXNoTWNwU291cmNlc1Jlc3BvbnNlEhAKCHJldmlzaW9uGAEgASgEEg8KB2NoYW5nZWQYAiABKAgiGwoZTGlzdFRvb2xIaXZlR3JvdXBzUmVxdWVzdCIsChpMaXN0VG9vbEhpdmVHcm91cHNSZXNwb25zZRIOCgZncm91cHMYASADKAkidAoJQWdlbnRJbmZvEgwKBG5hbWUYASABKAkSEwoLZGVzY3JpcHRpb24YAiABKAkSDQoFbW9kZWwYAyABKAkSDQoFdG9vbHMYBCADKAkSFwoPcGVybWlzc2lvbl9tb2RlGAUgASgJEg0KBWNvbG9yGAYgASgJIhMKEUxpc3RBZ2VudHNSZXF1ZXN0IjoKEkxpc3RBZ2VudHNSZXNwb25zZRIkCgZhZ2VudHMYASADKAsyFC5tZWNhdGwudjEuQWdlbnRJbmZvIiwKB0NvbW1hbmQSDAoEbmFtZRgBIAEoCRITCgtkZXNjcmlwdGlvbhgCIAEoCSIyChNMaXN0Q29tbWFuZHNSZXF1ZXN0EhsKCnNlc3Npb25faWQYASABKAlCB7pIBHICEAEiPAoUTGlzdENvbW1hbmRzUmVzcG9uc2USJAoIY29tbWFuZHMYASADKAsyEi5tZWNhdGwudjEuQ29tbWFuZCJpCghXb3JrdHJlZRIQCghzZWxlY3RvchgBIAEoCRIMCgRraW5kGAIgASgJEg0KBWxhYmVsGAMgASgJEg4KBmJyYW5jaBgEIAEoCRIQCghyZXZpc2lvbhgFIAEoCRIMCgRiYXJlGAYgASgIIjMKFExpc3RXb3JrdHJlZXNSZXF1ZXN0EhsKCnNlc3Npb25faWQYASABKAlCB7pIBHICEAEiPwoVTGlzdFdvcmt0cmVlc1Jlc3BvbnNlEiYKCXdvcmt0cmVlcxgBIAMoCzITLm1lY2F0bC52MS5Xb3JrdHJlZSJwCglTa2lsbEluZm8SDAoEbmFtZRgBIAEoCRITCgtkZXNjcmlwdGlvbhgCIAEoCRITCgthZ2VudF9vd25lZBgDIAEoCBITCgtvd25lcl9hZ2VudBgEIAEoCRIWCg5hY3RpdmVfdmVyc2lvbhgFIAEoCSITChFMaXN0U2tpbGxzUmVxdWVzdCI6ChJMaXN0U2tpbGxzUmVzcG9uc2USJAoGc2tpbGxzGAEgAygLMhQubWVjYXRsLnYxLlNraWxsSW5mbyJBCg1Ta2lsbEZpbGVJbmZvEgwKBG5hbWUYASABKAkSDAoEc2l6ZRgCIAEoAxIUCgxpbnN0cnVjdGlvbnMYAyABKAgiLgoVTGlzdFNraWxsRmlsZXNSZXF1ZXN0EhUKBG5hbWUYASABKAlCB7pIBHICEAEiQQoWTGlzdFNraWxsRmlsZXNSZXNwb25zZRInCgVmaWxlcxgBIAMoCzIYLm1lY2F0bC52MS5Ta2lsbEZpbGVJbmZvIkQKFFJlYWRTa2lsbEZpbGVSZXF1ZXN0EhUKBG5hbWUYASABKAlCB7pIBHICEAESFQoEZmlsZRgCIAEoCUIHukgEcgIQASIoChVSZWFkU2tpbGxGaWxlUmVzcG9uc2USDwoHY29udGVudBgBIAEoCSKhAQoIU291bEluZm8SDwoHY29udGVudBgBIAEoCRISCgpzaXplX2J5dGVzGAIgASgDEg4KBnNoYTI1NhgDIAEoCRIPCgdwcmVzZW50GAQgASgIEi0KCnByb3ZlbmFuY2UYBSABKA4yGS5tZWNhdGwudjEuU291bFByb3ZlbmFuY2USDwoHdHJ1c3RlZBgGIAEoCBIPCgdkcmlmdGVkGAcgASgIIhAKDkdldFNvdWxSZXF1ZXN0IjQKD0dldFNvdWxSZXNwb25zZRIhCgRzb3VsGAEgASgLMhMubWVjYXRsLnYxLlNvdWxJbmZvIjIKDlVzZXJNb2RlbEVudHJ5EgsKA2tleRgBIAEoCRITCgtkZXNjcmlwdGlvbhgCIAEoCSIiChNHZXRVc2VyTW9kZWxSZXF1ZXN0EgsKA2tleRgBIAEoCSKWAgoRVXNlck1vZGVsUmV2aXNpb24SCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJEhMKC2Rlc2NyaXB0aW9uGAMgASgJEg8KB3ZlcnNpb24YBCABKAkSDgoGc3RhdHVzGAUgASgJEg4KBndyaXRlchgGIAEoCRIOCgZvcmlnaW4YByABKAkSGQoRc291cmNlX3Nlc3Npb25faWQYCCABKAkSGgoSc291cmNlX3Byb3Bvc2FsX2lkGAkgASgJEi4KCnVwZGF0ZWRfYXQYCyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wSgQIChALUg9zb3VyY2VfZXZlbnRfaWRSEXNvdXJjZV9tZXNzYWdlX2lkIooBCg9Vc2VyTW9kZWxEZXRhaWwSLQoHY3VycmVudBgBIAEoCzIcLm1lY2F0bC52MS5Vc2VyTW9kZWxSZXZpc2lvbhItCgdoaXN0b3J5GAIgAygLMhwubWVjYXRsLnYxLlVzZXJNb2RlbFJldmlzaW9uEhkKEWhpc3RvcnlfYXZhaWxhYmxlGAMgASgIIpIBChRHZXRVc2VyTW9kZWxSZXNwb25zZRIqCgdlbnRyaWVzGAEgAygLMhkubWVjYXRsLnYxLlVzZXJNb2RlbEVudHJ5EhIKCnNpemVfYnl0ZXMYAiABKAMSDgoGc2hhMjU2GAMgASgJEioKBmRldGFpbBgEIAEoCzIaLm1lY2F0bC52MS5Vc2VyTW9kZWxEZXRhaWwikgEKCU1vZGVsSW5mbxIKCgJpZBgBIAEoCRITCgtwcm92aWRlcl9pZBgCIAEoCRIUCgxkaXNwbGF5X25hbWUYAyABKAkSDQoFaW1hZ2UYBCABKAgSEQoJcmVhc29uaW5nGAUgASgIEhUKDWNvbnRleHRfbGltaXQYBiABKAMSFQoNcHJvbXB0X2NhY2hlZBgHIAEoCCITChFMaXN0TW9kZWxzUmVxdWVzdCKbAQoOUHJvdmlkZXJTdGF0dXMSEwoLcHJvdmlkZXJfaWQYASABKAkSDQoFc3RhdGUYAiABKAkSDAoEaGludBgDIAEoCRIjChtkZWZhdWx0X21vZGVsX2F1dG9fc2VsZWN0ZWQYBCABKAgSEwoLbW9kZWxfY291bnQYBSABKAUSHQoVYXZhaWxhYmxlX25vdF9kZWZhdWx0GAYgASgIIm4KEkxpc3RNb2RlbHNSZXNwb25zZRIkCgZtb2RlbHMYASADKAsyFC5tZWNhdGwudjEuTW9kZWxJbmZvEjIKD3Byb3ZpZGVyX3N0YXR1cxgCIAMoCzIZLm1lY2F0bC52MS5Qcm92aWRlclN0YXR1cyJBChVSZWZsZWN0U2Vzc2lvblJlcXVlc3QSGwoKc2Vzc2lvbl9pZBgBIAEoCUIHukgEcgIQAUoECAIQA1IFYXN5bmMiuQEKEVJlZmxlY3Rpb25SZWNlaXB0EhUKDXJlZmxlY3Rpb25faWQYASABKAkSEwoLZGlzcG9zaXRpb24YAiABKAkSDgoGcXVldWVkGAMgASgFEhEKCWFic3RhaW5lZBgEIAEoCBIOCgZzdGFnZWQYBSABKAUSEAoIcHJvbW90ZWQYBiABKAUSEgoKY29uZmxpY3RlZBgHIAEoBRIOCgZyZWFzb24YCCABKAkSDwoHbWVzc2FnZRgJIAEoCSJHChZSZWZsZWN0U2Vzc2lvblJlc3BvbnNlEi0KB3JlY2VpcHQYASABKAsyHC5tZWNhdGwudjEuUmVmbGVjdGlvblJlY2VpcHQi8QIKD0xlYXJuaW5nQXR0ZW1wdBIKCgJpZBgBIAEoCRIPCgd2ZXJzaW9uGAIgASgJEg0KBXN0YXRlGAMgASgJEg8KB291dGNvbWUYBCABKAkSFAoMZmFpbHVyZV9jb2RlGAUgASgJEhoKEmF0dGVtcHRfZ2VuZXJhdGlvbhgGIAEoBBIYChBjbGFpbV9nZW5lcmF0aW9uGAcgASgEEjQKEGNsYWltX2V4cGlyZXNfYXQYCCABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEhgKEGNoZWNrcG9pbnRfc3RhZ2UYCSABKAkSEwoLcHJvcG9zYWxfaWQYCiABKAkSEAoIc2tpbGxfaWQYCyABKAkSLgoKY3JlYXRlZF9hdBgMIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASLgoKdXBkYXRlZF9hdBgNIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXAiMAoZR2V0TGVhcm5pbmdBdHRlbXB0UmVxdWVzdBITCgJpZBgBIAEoCUIHukgEcgIQASJJChpHZXRMZWFybmluZ0F0dGVtcHRSZXNwb25zZRIrCgdhdHRlbXB0GAEgASgLMhoubWVjYXRsLnYxLkxlYXJuaW5nQXR0ZW1wdCJLChtMaXN0TGVhcm5pbmdBdHRlbXB0c1JlcXVlc3QSDQoFc3RhdGUYASABKAkSDgoGY3Vyc29yGAIgASgJEg0KBWxpbWl0GAMgASgFImEKHExpc3RMZWFybmluZ0F0dGVtcHRzUmVzcG9uc2USLAoIYXR0ZW1wdHMYASADKAsyGi5tZWNhdGwudjEuTGVhcm5pbmdBdHRlbXB0EhMKC25leHRfY3Vyc29yGAIgASgJIlYKHE11dGF0ZUxlYXJuaW5nQXR0ZW1wdFJlcXVlc3QSEwoCaWQYASABKAlCB7pIBHICEAESIQoQZXhwZWN0ZWRfdmVyc2lvbhgCIAEoCUIHukgEcgIQASJMCh1NdXRhdGVMZWFybmluZ0F0dGVtcHRSZXNwb25zZRIrCgdhdHRlbXB0GAEgASgLMhoubWVjYXRsLnYxLkxlYXJuaW5nQXR0ZW1wdCJeChxMaXN0TGVhcm5pbmdQcm9wb3NhbHNSZXF1ZXN0Eg4KBnN0YXR1cxgBIAEoCRIOCgZjdXJzb3IYAiABKAkSDQoFbGltaXQYAyABKAUSDwoHcHJvamVjdBgEIAEoCSK+AQoTTGVhcm5pbmdFdmlkZW5jZVJlZhISCgpzZXNzaW9uX2lkGAEgASgJEg8KB2xvY2F0b3IYAiABKAkSDwoHb3JkaW5hbBgDIAEoBRIRCglldmVudF9zZXEYBCABKAMSFAoMdG9vbF9jYWxsX2lkGAUgASgJEg4KBmRpZ2VzdBgGIAEoCRIRCglhdmFpbGFibGUYByABKAgSFAoMYXZhaWxhYmlsaXR5GAggASgJEg8KB3ByZXZpZXcYCSABKAkiZwoQTGVhcm5pbmdEZWNpc2lvbhIMCgRraW5kGAEgASgJEg0KBWFjdG9yGAIgASgJEg4KBnJlYXNvbhgDIAEoCRImCgJhdBgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXAieQoYTGVhcm5pbmdQcm9tb3Rpb25SZWNlaXB0EhIKCm1lbW9yeV9rZXkYASABKAkSFwoPcHJldmlvdXNfZXhpc3RzGAIgASgIEhgKEHByZXZpb3VzX3ZlcnNpb24YAyABKAkSFgoOcmVzdWx0X3ZlcnNpb24YBCABKAkinAQKEExlYXJuaW5nUHJvcG9zYWwSCgoCaWQYASABKAkSDwoHdmVyc2lvbhgCIAEoCRIOCgZzdGF0dXMYAyABKAkSDAoEa2luZBgEIAEoCRILCgNrZXkYBSABKAkSDQoFdmFsdWUYBiABKAkSEwoLZGVzY3JpcHRpb24YByABKAkSDQoFdGl0bGUYCCABKAkSDAoEYm9keRgJIAEoCRIwCghldmlkZW5jZRgKIAMoCzIeLm1lY2F0bC52MS5MZWFybmluZ0V2aWRlbmNlUmVmEhAKCHRyaWdnZXJzGAsgAygJEi4KCWRlY2lzaW9ucxgMIAMoCzIbLm1lY2F0bC52MS5MZWFybmluZ0RlY2lzaW9uEjYKCXByb21vdGlvbhgNIAEoCzIjLm1lY2F0bC52MS5MZWFybmluZ1Byb21vdGlvblJlY2VpcHQSLgoKY3JlYXRlZF9hdBgOIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASLgoKdXBkYXRlZF9hdBgPIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASFgoOcHJvamVjdF9zY29wZWQYECABKAgSGwoTcHJvbW90aW9uX2F2YWlsYWJsZRgRIAEoCBIkChxwcm9tb3Rpb25fdW5hdmFpbGFibGVfcmVhc29uGBIgASgJEhgKEGxlYXJuZWRfc2tpbGxfaWQYEyABKAkiZAodTGlzdExlYXJuaW5nUHJvcG9zYWxzUmVzcG9uc2USLgoJcHJvcG9zYWxzGAEgAygLMhsubWVjYXRsLnYxLkxlYXJuaW5nUHJvcG9zYWwSEwoLbmV4dF9jdXJzb3IYAiABKAkiQgoaR2V0TGVhcm5pbmdQcm9wb3NhbFJlcXVlc3QSEwoCaWQYASABKAlCB7pIBHICEAESDwoHcHJvamVjdBgCIAEoCSJMChtHZXRMZWFybmluZ1Byb3Bvc2FsUmVzcG9uc2USLQoIcHJvcG9zYWwYASABKAsyGy5tZWNhdGwudjEuTGVhcm5pbmdQcm9wb3NhbCKTAQodRGVjaWRlTGVhcm5pbmdQcm9wb3NhbFJlcXVlc3QSEwoCaWQYASABKAlCB7pIBHICEAESIQoQZXhwZWN0ZWRfdmVyc2lvbhgCIAEoCUIHukgEcgIQARIZCghkZWNpc2lvbhgDIAEoCUIHukgEcgIQARIOCgZyZWFzb24YBCABKAkSDwoHcHJvamVjdBgFIAEoCSJPCh5EZWNpZGVMZWFybmluZ1Byb3Bvc2FsUmVzcG9uc2USLQoIcHJvcG9zYWwYASABKAsyGy5tZWNhdGwudjEuTGVhcm5pbmdQcm9wb3NhbCJnChxVbmRvTGVhcm5pbmdQcm9tb3Rpb25SZXF1ZXN0EhMKAmlkGAEgASgJQge6SARyAhABEiEKEGV4cGVjdGVkX3ZlcnNpb24YAiABKAlCB7pIBHICEAESDwoHcHJvamVjdBgDIAEoCSJOCh1VbmRvTGVhcm5pbmdQcm9tb3Rpb25SZXNwb25zZRItCghwcm9wb3NhbBgBIAEoCzIbLm1lY2F0bC52MS5MZWFybmluZ1Byb3Bvc2FsIuwDChNMZWFybmVkU2tpbGxWZXJzaW9uEgoKAmlkGAEgASgJEgwKBG5hbWUYAiABKAkSDwoHdmVyc2lvbhgDIAEoCRIQCghyZXZpc2lvbhgEIAEoCRINCgVzdGF0ZRgFIAEoCRITCgtvd25lcl9hZ2VudBgGIAEoCRITCgtkZXNjcmlwdGlvbhgHIAEoCRIMCgRib2R5GAggASgJEhIKCnN1cGVyc2VkZXMYCSABKAkSFgoOZXZpZGVuY2VfY291bnQYCiABKAUSMAoIZXZpZGVuY2UYCyADKAsyHi5tZWNhdGwudjEuTGVhcm5pbmdFdmlkZW5jZVJlZhIvCgtldmFsdWF0aW9ucxgMIAMoCzIaLm1lY2F0bC52MS5Ta2lsbEV2YWx1YXRpb24SLwoIcmVjZWlwdHMYDSADKAsyHS5tZWNhdGwudjEuU2tpbGxDaGFuZ2VSZWNlaXB0Ei4KCmNyZWF0ZWRfYXQYDiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi4KCnVwZGF0ZWRfYXQYDyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEhkKEWluc3BlY3RfYXZhaWxhYmxlGBAgASgIEhYKDnVuZG9fYXZhaWxhYmxlGBEgASgIIpQBCg9Ta2lsbEV2YWx1YXRpb24SDwoHdmVyZGljdBgBIAEoCRITCgtmaXh0dXJlX2lkcxgCIAMoCRIQCghiYXNlbGluZRgDIAEoCRIRCgl0cmVhdG1lbnQYBCABKAkSDgoGcmVhc29uGAUgASgJEiYKAmF0GAYgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcCKCAwoSU2tpbGxDaGFuZ2VSZWNlaXB0EgoKAmlkGAEgASgJEhAKCHNraWxsX2lkGAIgASgJEgwKBG5hbWUYAyABKAkSDwoHdmVyc2lvbhgEIAEoCRIRCglvcGVyYXRpb24YBSABKAkSEgoKZnJvbV9zdGF0ZRgGIAEoCRIQCgh0b19zdGF0ZRgHIAEoCRIWCg5ldmlkZW5jZV9jb3VudBgIIAEoBRIaChJzb3VyY2Vfc2Vzc2lvbl9pZHMYCSADKAkSHAoUc291cmNlX3Rvb2xfY2FsbF9pZHMYCiADKAkSDwoHdmVyZGljdBgLIAEoCRITCgtmaXh0dXJlX2lkcxgMIAMoCRIQCghiYXNlbGluZRgNIAEoCRIRCgl0cmVhdG1lbnQYDiABKAkSJgoCYXQYDyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEhkKEWluc3BlY3RfYXZhaWxhYmxlGBAgASgIEhYKDnVuZG9fYXZhaWxhYmxlGBEgASgIIm4KGExpc3RMZWFybmVkU2tpbGxzUmVxdWVzdBIPCgdwcm9qZWN0GAEgASgJEg4KBmN1cnNvchgCIAEoCRINCgVsaW1pdBgDIAEoBRINCgVzdGF0ZRgEIAEoCRITCgtvd25lcl9hZ2VudBgFIAEoCSKFAQoZTGlzdExlYXJuZWRTa2lsbHNSZXNwb25zZRIuCgZza2lsbHMYASADKAsyHi5tZWNhdGwudjEuTGVhcm5lZFNraWxsVmVyc2lvbhITCgtuZXh0X2N1cnNvchgCIAEoCRISCgpnZW5lcmF0aW9uGAMgASgEEg8KB3Byb2plY3QYBCABKAkibQoWR2V0TGVhcm5lZFNraWxsUmVxdWVzdBIPCgdwcm9qZWN0GAEgASgJEhwKC293bmVyX2FnZW50GAIgASgJQge6SARyAhABEhMKAmlkGAMgASgJQge6SARyAhABEg8KB3ZlcnNpb24YBCABKAkibQoXR2V0TGVhcm5lZFNraWxsUmVzcG9uc2USLQoFc2tpbGwYASABKAsyHi5tZWNhdGwudjEuTGVhcm5lZFNraWxsVmVyc2lvbhISCgpnZW5lcmF0aW9uGAIgASgEEg8KB3Byb2plY3QYAyABKAkioQEKH0RpZmZMZWFybmVkU2tpbGxWZXJzaW9uc1JlcXVlc3QSDwoHcHJvamVjdBgBIAEoCRIcCgtvd25lcl9hZ2VudBgCIAEoCUIHukgEcgIQARITCgJpZBgDIAEoCUIHukgEcgIQARIdCgxmcm9tX3ZlcnNpb24YBCABKAlCB7pIBHICEAESGwoKdG9fdmVyc2lvbhgFIAEoCUIHukgEcgIQASKRAQogRGlmZkxlYXJuZWRTa2lsbFZlcnNpb25zUmVzcG9uc2USDAoEZGlmZhgBIAEoCRISCgpnZW5lcmF0aW9uGAIgASgEEg8KB3Byb2plY3QYAyABKAkSEAoIc2tpbGxfaWQYBCABKAkSFAoMZnJvbV92ZXJzaW9uGAUgASgJEhIKCnRvX3ZlcnNpb24YBiABKAkinQEKGU11dGF0ZUxlYXJuZWRTa2lsbFJlcXVlc3QSDwoHcHJvamVjdBgBIAEoCRIcCgtvd25lcl9hZ2VudBgCIAEoCUIHukgEcgIQARITCgJpZBgDIAEoCUIHukgEcgIQARIYCgd2ZXJzaW9uGAQgASgJQge6SARyAhABEiIKEWV4cGVjdGVkX3JldmlzaW9uGAUgASgJQge6SARyAhABIqcBChpNdXRhdGVMZWFybmVkU2tpbGxSZXNwb25zZRItCgVza2lsbBgBIAEoCzIeLm1lY2F0bC52MS5MZWFybmVkU2tpbGxWZXJzaW9uEhIKCmdlbmVyYXRpb24YAiABKAQSDwoHcHJvamVjdBgDIAEoCRIaChJwdWJsaWNhdGlvbl9zdGF0dXMYBCABKAkSGQoRcHVibGljYXRpb25fZXJyb3IYBSABKAkipgEKG1JvbGxiYWNrTGVhcm5lZFNraWxsUmVxdWVzdBIPCgdwcm9qZWN0GAEgASgJEhwKC293bmVyX2FnZW50GAIgASgJQge6SARyAhABEhMKAmlkGAMgASgJQge6SARyAhABEh8KDnRhcmdldF92ZXJzaW9uGAQgASgJQge6SARyAhABEiIKEWV4cGVjdGVkX3JldmlzaW9uGAUgASgJQge6SARyAhABIkkKF0xpc3RTa2lsbENoYW5nZXNSZXF1ZXN0Eg8KB3Byb2plY3QYASABKAkSDgoGY3Vyc29yGAIgASgJEg0KBWxpbWl0GAMgASgFIoQBChhMaXN0U2tpbGxDaGFuZ2VzUmVzcG9uc2USLgoHY2hhbmdlcxgBIAMoCzIdLm1lY2F0bC52MS5Ta2lsbENoYW5nZVJlY2VpcHQSEwoLbmV4dF9jdXJzb3IYAiABKAkSEgoKZ2VuZXJhdGlvbhgDIAEoBBIPCgdwcm9qZWN0GAQgASgJIo8BChFDcmVhdGVUZWFtUmVxdWVzdBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABEgwKBG5hbWUYAiABKAkSKAoHbWVtYmVycxgDIAMoCzIXLm1lY2F0bC52MS5UZWFtbWF0ZVNwZWMSDAoEZ29hbBgEIAEoCRIXCg9tYXhfdGVhbV90b2tlbnMYBSABKAUiTQoSQ3JlYXRlVGVhbVJlc3BvbnNlEg8KB3RlYW1faWQYASABKAkSJgoHbWVtYmVycxgCIAMoCzIVLm1lY2F0bC52MS5UZWFtTWVtYmVyInEKDFRlYW1tYXRlU3BlYxIVCgRuYW1lGAEgASgJQge6SARyAhABEhIKCmFnZW50X3R5cGUYAiABKAkSDAoEbGVhZBgDIAEoCBIQCghtdXRhdGluZxgEIAEoCBIWCg5pbml0aWFsX3Byb21wdBgFIAEoCSKTAQoUU3Bhd25UZWFtbWF0ZVJlcXVlc3QSGAoHdGVhbV9pZBgBIAEoCUIHukgEcgIQARIVCgRuYW1lGAIgASgJQge6SARyAhABEhIKCmFnZW50X3R5cGUYAyABKAkSDAoEbGVhZBgEIAEoCBIQCghtdXRhdGluZxgFIAEoCBIWCg5pbml0aWFsX3Byb21wdBgGIAEoCSI+ChVTcGF3blRlYW1tYXRlUmVzcG9uc2USJQoGbWVtYmVyGAEgASgLMhUubWVjYXRsLnYxLlRlYW1NZW1iZXIicAoaU2VuZFRlYW1tYXRlTWVzc2FnZVJlcXVlc3QSGAoHdGVhbV9pZBgBIAEoCUIHukgEcgIQARITCgJ0bxgCIAEoCUIHukgEcgIQARIMCgRmcm9tGAMgASgJEhUKBGJvZHkYBCABKAlCB7pIBHICEAEiHQobU2VuZFRlYW1tYXRlTWVzc2FnZVJlc3BvbnNlIkoKFUNhbmNlbFRlYW1tYXRlUmVxdWVzdBIYCgd0ZWFtX2lkGAEgASgJQge6SARyAhABEhcKBm1lbWJlchgCIAEoCUIHukgEcgIQASIYChZDYW5jZWxUZWFtbWF0ZVJlc3BvbnNlIioKDlJ1blRlYW1SZXF1ZXN0EhgKB3RlYW1faWQYASABKAlCB7pIBHICEAEiZQoJVGVhbUV2ZW50Eg4KBm1lbWJlchgBIAEoCRIfCgVldmVudBgCIAEoCzIQLm1lY2F0bC52MS5FdmVudBInCgdvdXRjb21lGAMgASgLMhYubWVjYXRsLnYxLlRlYW1PdXRjb21lItsBCgtUZWFtT3V0Y29tZRIOCgZyb3VuZHMYASABKAUSEQoJcXVpZXNjZW50GAIgASgIEhgKEGJ1ZGdldF9leGhhdXN0ZWQYAyABKAgSDAoEc3RvcBgEIAEoCRIfCgV1c2FnZRgFIAEoCzIQLm1lY2F0bC52MS5Vc2FnZRI2CgxkaXNwb3NpdGlvbnMYBiADKAsyIC5tZWNhdGwudjEuVGVhbU1lbWJlckRpc3Bvc2l0aW9uEigKCGZpbmRpbmdzGAcgAygLMhYubWVjYXRsLnYxLlRlYW1GaW5kaW5nIisKD0xpc3RUZWFtUmVxdWVzdBIYCgd0ZWFtX2lkGAEgASgJQge6SARyAhABInEKEExpc3RUZWFtUmVzcG9uc2USJgoHbWVtYmVycxgBIAMoCzIVLm1lY2F0bC52MS5UZWFtTWVtYmVyEiIKBXRhc2tzGAIgAygLMhMubWVjYXRsLnYxLlRlYW1UYXNrEhEKCXF1aWVzY2VudBgDIAEoCCJRCgpUZWFtTWVtYmVyEgwKBG5hbWUYASABKAkSEgoKYWdlbnRfdHlwZRgCIAEoCRINCgVzdGF0ZRgDIAEoCRISCgpzZXNzaW9uX2lkGAQgASgJIloKCFRlYW1UYXNrEgoKAmlkGAEgASgJEhMKC2Rlc2NyaXB0aW9uGAIgASgJEg0KBXN0YXRlGAMgASgJEhAKCGFzc2lnbmVlGAQgASgJEgwKBGRlcHMYBSADKAkiKwoLVGVhbUZpbmRpbmcSDgoGbWVtYmVyGAEgASgJEgwKBGJvZHkYAiABKAkifQoVVGVhbU1lbWJlckRpc3Bvc2l0aW9uEgwKBG5hbWUYASABKAkSDwoHc3RvcHBlZBgCIAEoCBIvCgZyZWFzb24YAyABKA4yHy5tZWNhdGwudjEuVGVhbU1lbWJlclN0b3BSZWFzb24SFAoMZXJyb3Jfcm91bmRzGAQgASgFIi4KEkNsZWFudXBUZWFtUmVxdWVzdBIYCgd0ZWFtX2lkGAEgASgJQge6SARyAhABIhUKE0NsZWFudXBUZWFtUmVzcG9uc2UibwoSQXBwcm92ZVBsYW5SZXF1ZXN0EhsKCnNlc3Npb25faWQYASABKAlCB7pIBHICEAESLgoLdGFyZ2V0X21vZGUYAiABKA4yGS5tZWNhdGwudjEuUGVybWlzc2lvbk1vZGUSDAoEbm90ZRgDIAEoCSKJAQoXTWFudWFsRHJlYW1DYXBhYmlsaXRpZXMSOAoOcHJvamVjdF9tZW1vcnkYASABKAsyIC5tZWNhdGwudjEuRHJlYW1UYXJnZXRDYXBhYmlsaXR5EjQKCnVzZXJfbW9kZWwYAiABKAsyIC5tZWNhdGwudjEuRHJlYW1UYXJnZXRDYXBhYmlsaXR5IlUKFURyZWFtVGFyZ2V0Q2FwYWJpbGl0eRIQCghnZW5lcmF0ZRgBIAEoCBIOCgZkZWNpZGUYAiABKAgSGgoSdW5hdmFpbGFibGVfcmVhc29uGAMgASgJIioKGEdlbmVyYXRlRHJlYW1QbGFuUmVxdWVzdBIOCgZ0YXJnZXQYASABKAkiRQoZR2VuZXJhdGVEcmVhbVBsYW5SZXNwb25zZRIoCgRwbGFuGAEgASgLMhoubWVjYXRsLnYxLkRyZWFtUmV2aWV3UGxhbiI7ChZEZWNpZGVEcmVhbVBsYW5SZXF1ZXN0Eg8KB3BsYW5faWQYASABKAkSEAoIZGVjaXNpb24YAiABKAkiQwoXRGVjaWRlRHJlYW1QbGFuUmVzcG9uc2USKAoHcmVjZWlwdBgBIAEoCzIXLm1lY2F0bC52MS5EcmVhbVJlY2VpcHQiywEKD0RyZWFtUmV2aWV3UGxhbhIKCgJpZBgBIAEoCRIOCgZ0YXJnZXQYAiABKAkSLgoKZXhwaXJlc19hdBgDIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASHwoXcGxhbm5lZF9vcGVyYXRpb25fY291bnQYBCABKAUSHAoUcGxhbm5lZF9zb3VyY2VfY291bnQYBSABKAUSLQoKb3BlcmF0aW9ucxgGIAMoCzIZLm1lY2F0bC52MS5EcmVhbU9wZXJhdGlvbiLfAQoORHJlYW1PcGVyYXRpb24SDAoEa2luZBgBIAEoCRItCghzdXJ2aXZvchgCIAEoCzIbLm1lY2F0bC52MS5EcmVhbVBhcnRpY2lwYW50EiwKB3NvdXJjZXMYAyADKAsyGy5tZWNhdGwudjEuRHJlYW1QYXJ0aWNpcGFudBIwCgtyZXBsYWNlbWVudBgEIAEoCzIbLm1lY2F0bC52MS5EcmVhbVJlcGxhY2VtZW50Eg4KBnJlYXNvbhgFIAEoCRIgChhleGFjdF9kdXBsaWNhdGVfZWxpZ2libGUYBiABKAgiQwoQRHJlYW1QYXJ0aWNpcGFudBILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAkSEwoLZGVzY3JpcHRpb24YAyABKAkiNgoQRHJlYW1SZXBsYWNlbWVudBINCgV2YWx1ZRgBIAEoCRITCgtkZXNjcmlwdGlvbhgCIAEoCSLXAQoMRHJlYW1SZWNlaXB0EgoKAmlkGAEgASgJEg4KBnRhcmdldBgCIAEoCRITCgtkaXNwb3NpdGlvbhgDIAEoCRIcChRwbGFubmVkX3NvdXJjZV9jb3VudBgEIAEoBRIcChRhcHBsaWVkX3NvdXJjZV9jb3VudBgFIAEoBRIfChdjb25mbGljdGVkX3NvdXJjZV9jb3VudBgGIAEoBRIcChRza2lwcGVkX3NvdXJjZV9jb3VudBgHIAEoBRIbChNmYWlsZWRfc291cmNlX2NvdW50GAggASgFIjQKFUNvbXBhY3RTZXNzaW9uUmVxdWVzdBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABIisKFkNvbXBhY3RTZXNzaW9uUmVzcG9uc2USEQoJY29tcGFjdGVkGAEgASgIIjcKIVdvcmtzcGFjZUVucm9sbG1lbnRDb25uZWN0UmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJIk4KIVdvcmtzcGFjZUVucm9sbG1lbnRDb250cm9sUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEhUKDWVucm9sbG1lbnRfaWQYAiABKAkicQoTV29ya3NwYWNlRW5yb2xsbWVudBIVCg1lbnJvbGxtZW50X2lkGAEgASgJEg4KBnN0YXR1cxgCIAEoCRIZChFyZXF1aXJlZF9zZXJ2aWNlcxgDIAEoDRIYChBwcmVzZW50YXRpb25fdXJsGAQgASgJKooBCg5QZXJtaXNzaW9uTW9kZRIfChtQRVJNSVNTSU9OX01PREVfVU5TUEVDSUZJRUQQABIbChdQRVJNSVNTSU9OX01PREVfREVGQVVMVBABEhgKFFBFUk1JU1NJT05fTU9ERV9QTEFOEAISIAocUEVSTUlTU0lPTl9NT0RFX0FDQ0VQVF9FRElUUxADKpIBCg9BcHByb3ZhbFZlcmRpY3QSIAocQVBQUk9WQUxfVkVSRElDVF9VTlNQRUNJRklFRBAAEhkKFUFQUFJPVkFMX1ZFUkRJQ1RfREVOWRABEh8KG0FQUFJPVkFMX1ZFUkRJQ1RfQUxMT1dfT05DRRACEiEKHUFQUFJPVkFMX1ZFUkRJQ1RfQUxMT1dfQUxXQVlTEAMqvgEKDFN0ZWVyT3V0Y29tZRIdChlTVEVFUl9PVVRDT01FX1VOU1BFQ0lGSUVEEAASGgoWU1RFRVJfT1VUQ09NRV9BQ0NFUFRFRBABEhoKFlNURUVSX09VVENPTUVfQVBQRU5ERUQQAhIbChdTVEVFUl9PVVRDT01FX1JFVFJBQ1RFRBADEh4KGlNURUVSX09VVENPTUVfTk9ORV9QRU5ESU5HEAQSGgoWU1RFRVJfT1VUQ09NRV9UT09fTEFURRAFKoABCgxHdWFyZHJhaWxKb2ISHQoZR1VBUkRSQUlMX0pPQl9VTlNQRUNJRklFRBAAEhgKFEdVQVJEUkFJTF9KT0JfQUNUSU9OEAESGQoVR1VBUkRSQUlMX0pPQl9JTkJPVU5EEAISHAoYR1VBUkRSQUlMX0pPQl9QRVJNSVNTSU9OEAMqqgEKE0d1YXJkcmFpbEFzc2Vzc21lbnQSJAogR1VBUkRSQUlMX0FTU0VTU01FTlRfVU5TUEVDSUZJRUQQABIjCh9HVUFSRFJBSUxfQVNTRVNTTUVOVF9BQ0NFUFRBQkxFEAESIwofR1VBUkRSQUlMX0FTU0VTU01FTlRfUFJPSElCSVRFRBACEiMKH0dVQVJEUkFJTF9BU1NFU1NNRU5UX1VOUkVTT0xWRUQQAyqMAQoTR3VhcmRyYWlsSW5zcGVjdGlvbhIkCiBHVUFSRFJBSUxfSU5TUEVDVElPTl9VTlNQRUNJRklFRBAAEiEKHUdVQVJEUkFJTF9JTlNQRUNUSU9OX0NPTVBMRVRFEAESLAooR1VBUkRSQUlMX0lOU1BFQ1RJT05fT1BFUkFUSU9OQUxfRkFJTFVSRRACKtACChRHdWFyZHJhaWxEaXNwb3NpdGlvbhIlCiFHVUFSRFJBSUxfRElTUE9TSVRJT05fVU5TUEVDSUZJRUQQABIhCh1HVUFSRFJBSUxfRElTUE9TSVRJT05fRVhFQ1VURRABEiQKIEdVQVJEUkFJTF9ESVNQT1NJVElPTl9BU0tfQUNUSU9OEAISKQolR1VBUkRSQUlMX0RJU1BPU0lUSU9OX1dJVEhIT0xEX1JFU1VMVBADEigKJEdVQVJEUkFJTF9ESVNQT1NJVElPTl9SRUxFQVNFX1JFU1VMVBAEEh4KGkdVQVJEUkFJTF9ESVNQT1NJVElPTl9ERU5ZEAUSJwojR1VBUkRSQUlMX0RJU1BPU0lUSU9OX1BBU1NfQURWSVNPUlkQBhIqCiZHVUFSRFJBSUxfRElTUE9TSVRJT05fQ09OVElOVUVfV0FSTklORxAHKpABChVHdWFyZHJhaWxBcHByb3ZhbEtpbmQSJwojR1VBUkRSQUlMX0FQUFJPVkFMX0tJTkRfVU5TUEVDSUZJRUQQABIiCh5HVUFSRFJBSUxfQVBQUk9WQUxfS0lORF9BQ1RJT04QARIqCiZHVUFSRFJBSUxfQVBQUk9WQUxfS0lORF9SRVNVTFRfUkVMRUFTRRACKpgBCgxIb29rRGVjaXNpb24SHQoZSE9PS19ERUNJU0lPTl9VTlNQRUNJRklFRBAAEhYKEkhPT0tfREVDSVNJT05fSU5GTxABEhkKFUhPT0tfREVDSVNJT05fQkxPQ0tFRBACEhoKFkhPT0tfREVDSVNJT05fTU9ESUZJRUQQAxIaChZIT09LX0RFQ0lTSU9OX0FEVklTT1JZEAQqlgEKEFJldHJ5RGlzcG9zaXRpb24SIQodUkVUUllfRElTUE9TSVRJT05fVU5TUEVDSUZJRUQQABIdChlSRVRSWV9ESVNQT1NJVElPTl9VTktOT1dOEAESHwobUkVUUllfRElTUE9TSVRJT05fUkVUUllBQkxFEAISHwobUkVUUllfRElTUE9TSVRJT05fUEVSTUFORU5UEAMqqAEKDlN0cmVhbVByb2dyZXNzEh8KG1NUUkVBTV9QUk9HUkVTU19VTlNQRUNJRklFRBAAEhsKF1NUUkVBTV9QUk9HUkVTU19VTktOT1dOEAESHQoZU1RSRUFNX1BST0dSRVNTX1BSRUNPTU1JVBACEhsKF1NUUkVBTV9QUk9HUkVTU19WSVNJQkxFEAMSHAoYU1RSRUFNX1BST0dSRVNTX0NPTVBMRVRFEAQqhAEKDlNvdWxQcm92ZW5hbmNlEh8KG1NPVUxfUFJPVkVOQU5DRV9VTlNQRUNJRklFRBAAEhgKFFNPVUxfUFJPVkVOQU5DRV9VU0VSEAESGwoXU09VTF9QUk9WRU5BTkNFX1BST0pFQ1QQAhIaChZTT1VMX1BST1ZFTkFOQ0VfRFJJVkVSEAMqrQEKFFRlYW1NZW1iZXJTdG9wUmVhc29uEicKI1RFQU1fTUVNQkVSX1NUT1BfUkVBU09OX1VOU1BFQ0lGSUVEEAASIQodVEVBTV9NRU1CRVJfU1RPUF9SRUFTT05fRVJST1IQARIlCiFURUFNX01FTUJFUl9TVE9QX1JFQVNPTl9DQU5DRUxMRUQQAhIiCh5URUFNX01FTUJFUl9TVE9QX1JFQVNPTl9CVURHRVQQAzKcOQoOSGFybmVzc1NlcnZpY2USZwoUR2V0Q29tcGF0aWJpbGl0eUluZm8SJi5tZWNhdGwudjEuR2V0Q29tcGF0aWJpbGl0eUluZm9SZXF1ZXN0GicubWVjYXRsLnYxLkdldENvbXBhdGliaWxpdHlJbmZvUmVzcG9uc2USbQoWTGlzdEV4ZWN1dGlvblRlbXBsYXRlcxIoLm1lY2F0bC52MS5MaXN0RXhlY3V0aW9uVGVtcGxhdGVzUmVxdWVzdBopLm1lY2F0bC52MS5MaXN0RXhlY3V0aW9uVGVtcGxhdGVzUmVzcG9uc2USUgoNQ3JlYXRlU2Vzc2lvbhIfLm1lY2F0bC52MS5DcmVhdGVTZXNzaW9uUmVxdWVzdBogLm1lY2F0bC52MS5DcmVhdGVTZXNzaW9uUmVzcG9uc2USUgoNR2V0U2VydmVySW5mbxIfLm1lY2F0bC52MS5HZXRTZXJ2ZXJJbmZvUmVxdWVzdBogLm1lY2F0bC52MS5HZXRTZXJ2ZXJJbmZvUmVzcG9uc2USSQoKR2V0U2Vzc2lvbhIcLm1lY2F0bC52MS5HZXRTZXNzaW9uUmVxdWVzdBodLm1lY2F0bC52MS5HZXRTZXNzaW9uUmVzcG9uc2USagoVTGlzdEd1YXJkcmFpbENvdmVyYWdlEicubWVjYXRsLnYxLkxpc3RHdWFyZHJhaWxDb3ZlcmFnZVJlcXVlc3QaKC5tZWNhdGwudjEuTGlzdEd1YXJkcmFpbENvdmVyYWdlUmVzcG9uc2UScwoYR2V0R3VhcmRyYWlsUmV2aWV3RGV0YWlsEioubWVjYXRsLnYxLkdldEd1YXJkcmFpbFJldmlld0RldGFpbFJlcXVlc3QaKy5tZWNhdGwudjEuR2V0R3VhcmRyYWlsUmV2aWV3RGV0YWlsUmVzcG9uc2USZwoUR2V0U2Vzc2lvblRyYW5zY3JpcHQSJi5tZWNhdGwudjEuR2V0U2Vzc2lvblRyYW5zY3JpcHRSZXF1ZXN0GicubWVjYXRsLnYxLkdldFNlc3Npb25UcmFuc2NyaXB0UmVzcG9uc2USQAoHU2V0TW9kZRIZLm1lY2F0bC52MS5TZXRNb2RlUmVxdWVzdBoaLm1lY2F0bC52MS5TZXRNb2RlUmVzcG9uc2USTwoMQ2xvc2VTZXNzaW9uEh4ubWVjYXRsLnYxLkNsb3NlU2Vzc2lvblJlcXVlc3QaHy5tZWNhdGwudjEuQ2xvc2VTZXNzaW9uUmVzcG9uc2USUgoNUmVuYW1lU2Vzc2lvbhIfLm1lY2F0bC52MS5SZW5hbWVTZXNzaW9uUmVxdWVzdBogLm1lY2F0bC52MS5SZW5hbWVTZXNzaW9uUmVzcG9uc2USUgoNRGVsZXRlU2Vzc2lvbhIfLm1lY2F0bC52MS5EZWxldGVTZXNzaW9uUmVxdWVzdBogLm1lY2F0bC52MS5EZWxldGVTZXNzaW9uUmVzcG9uc2USVQoOQ29tcGFjdFNlc3Npb24SIC5tZWNhdGwudjEuQ29tcGFjdFNlc3Npb25SZXF1ZXN0GiEubWVjYXRsLnYxLkNvbXBhY3RTZXNzaW9uUmVzcG9uc2USTwoMQ2xlYXJTZXNzaW9uEh4ubWVjYXRsLnYxLkNsZWFyU2Vzc2lvblJlcXVlc3QaHy5tZWNhdGwudjEuQ2xlYXJTZXNzaW9uUmVzcG9uc2USTAoLRm9ya1Nlc3Npb24SHS5tZWNhdGwudjEuRm9ya1Nlc3Npb25SZXF1ZXN0Gh4ubWVjYXRsLnYxLkZvcmtTZXNzaW9uUmVzcG9uc2USRwoIQ29udmVyc2USGi5tZWNhdGwudjEuQ29udmVyc2VSZXF1ZXN0GhsubWVjYXRsLnYxLkNvbnZlcnNlUmVzcG9uc2UoATABElIKDVJlc29sdmVSdW5Bc2sSHy5tZWNhdGwudjEuUmVzb2x2ZVJ1bkFza1JlcXVlc3QaIC5tZWNhdGwudjEuUmVzb2x2ZVJ1bkFza1Jlc3BvbnNlElUKDlJlc29sdmVQbGFuQXNrEiAubWVjYXRsLnYxLlJlc29sdmVQbGFuQXNrUmVxdWVzdBohLm1lY2F0bC52MS5SZXNvbHZlUGxhbkFza1Jlc3BvbnNlEkYKCUNhbmNlbFJ1bhIbLm1lY2F0bC52MS5DYW5jZWxSdW5SZXF1ZXN0GhwubWVjYXRsLnYxLkNhbmNlbFJ1blJlc3BvbnNlEkMKCFN0ZWVyUnVuEhoubWVjYXRsLnYxLlN0ZWVyUnVuUmVxdWVzdBobLm1lY2F0bC52MS5TdGVlclJ1blJlc3BvbnNlElUKDkNhbmNlbFJ1blN0ZWVyEiAubWVjYXRsLnYxLkNhbmNlbFJ1blN0ZWVyUmVxdWVzdBohLm1lY2F0bC52MS5DYW5jZWxSdW5TdGVlclJlc3BvbnNlElsKEExpc3RNY3BSZXNvdXJjZXMSIi5tZWNhdGwudjEuTGlzdE1jcFJlc291cmNlc1JlcXVlc3QaIy5tZWNhdGwudjEuTGlzdE1jcFJlc291cmNlc1Jlc3BvbnNlElgKD1JlYWRNY3BSZXNvdXJjZRIhLm1lY2F0bC52MS5SZWFkTWNwUmVzb3VyY2VSZXF1ZXN0GiIubWVjYXRsLnYxLlJlYWRNY3BSZXNvdXJjZVJlc3BvbnNlElUKDkxpc3RNY3BQcm9tcHRzEiAubWVjYXRsLnYxLkxpc3RNY3BQcm9tcHRzUmVxdWVzdBohLm1lY2F0bC52MS5MaXN0TWNwUHJvbXB0c1Jlc3BvbnNlEk8KDEdldE1jcFByb21wdBIeLm1lY2F0bC52MS5HZXRNY3BQcm9tcHRSZXF1ZXN0Gh8ubWVjYXRsLnYxLkdldE1jcFByb21wdFJlc3BvbnNlElUKDkxpc3RNY3BTb3VyY2VzEiAubWVjYXRsLnYxLkxpc3RNY3BTb3VyY2VzUmVxdWVzdBohLm1lY2F0bC52MS5MaXN0TWNwU291cmNlc1Jlc3BvbnNlEl4KEVJlZnJlc2hNY3BTb3VyY2VzEiMubWVjYXRsLnYxLlJlZnJlc2hNY3BTb3VyY2VzUmVxdWVzdBokLm1lY2F0bC52MS5SZWZyZXNoTWNwU291cmNlc1Jlc3BvbnNlEnMKGExpc3RTZXNzaW9uTWNwQ29ubmVjdG9ycxIqLm1lY2F0bC52MS5MaXN0U2Vzc2lvbk1jcENvbm5lY3RvcnNSZXF1ZXN0GisubWVjYXRsLnYxLkxpc3RTZXNzaW9uTWNwQ29ubmVjdG9yc1Jlc3BvbnNlEmEKEkxpc3RUb29sSGl2ZUdyb3VwcxIkLm1lY2F0bC52MS5MaXN0VG9vbEhpdmVHcm91cHNSZXF1ZXN0GiUubWVjYXRsLnYxLkxpc3RUb29sSGl2ZUdyb3Vwc1Jlc3BvbnNlEkkKCkxpc3RBZ2VudHMSHC5tZWNhdGwudjEuTGlzdEFnZW50c1JlcXVlc3QaHS5tZWNhdGwudjEuTGlzdEFnZW50c1Jlc3BvbnNlEk8KDExpc3RDb21tYW5kcxIeLm1lY2F0bC52MS5MaXN0Q29tbWFuZHNSZXF1ZXN0Gh8ubWVjYXRsLnYxLkxpc3RDb21tYW5kc1Jlc3BvbnNlElIKDUxpc3RXb3JrdHJlZXMSHy5tZWNhdGwudjEuTGlzdFdvcmt0cmVlc1JlcXVlc3QaIC5tZWNhdGwudjEuTGlzdFdvcmt0cmVlc1Jlc3BvbnNlElAKE1N0cmVhbVNlc3Npb25FdmVudHMSJS5tZWNhdGwudjEuU3RyZWFtU2Vzc2lvbkV2ZW50c1JlcXVlc3QaEC5tZWNhdGwudjEuRXZlbnQwARJMChFTdHJlYW1TZXNzaW9uTGl2ZRIjLm1lY2F0bC52MS5TdHJlYW1TZXNzaW9uTGl2ZVJlcXVlc3QaEC5tZWNhdGwudjEuRXZlbnQwARJjChJXYXRjaFNlc3Npb25FdmVudHMSJC5tZWNhdGwudjEuV2F0Y2hTZXNzaW9uRXZlbnRzUmVxdWVzdBolLm1lY2F0bC52MS5XYXRjaFNlc3Npb25FdmVudHNSZXNwb25zZTABEogBCh9HZXRNY3BBdXRob3JpemF0aW9uUHJlc2VudGF0aW9uEjEubWVjYXRsLnYxLkdldE1jcEF1dGhvcml6YXRpb25QcmVzZW50YXRpb25SZXF1ZXN0GjIubWVjYXRsLnYxLkdldE1jcEF1dGhvcml6YXRpb25QcmVzZW50YXRpb25SZXNwb25zZRJ0ChdSZWNoZWNrTWNwQXV0aG9yaXphdGlvbhIpLm1lY2F0bC52MS5SZWNoZWNrTWNwQXV0aG9yaXphdGlvblJlcXVlc3QaKi5tZWNhdGwudjEuUmVjaGVja01jcEF1dGhvcml6YXRpb25SZXNwb25zZSgBMAEScQoWQ2FuY2VsTWNwQXV0aG9yaXphdGlvbhIoLm1lY2F0bC52MS5DYW5jZWxNY3BBdXRob3JpemF0aW9uUmVxdWVzdBopLm1lY2F0bC52MS5DYW5jZWxNY3BBdXRob3JpemF0aW9uUmVzcG9uc2UoATABEk8KDExpc3RTZXNzaW9ucxIeLm1lY2F0bC52MS5MaXN0U2Vzc2lvbnNSZXF1ZXN0Gh8ubWVjYXRsLnYxLkxpc3RTZXNzaW9uc1Jlc3BvbnNlElsKEEdldFN0b3JhZ2VIZWFsdGgSIi5tZWNhdGwudjEuR2V0U3RvcmFnZUhlYWx0aFJlcXVlc3QaIy5tZWNhdGwudjEuR2V0U3RvcmFnZUhlYWx0aFJlc3BvbnNlEmEKElBsYW5TZXNzaW9uQ2xlYW51cBIkLm1lY2F0bC52MS5QbGFuU2Vzc2lvbkNsZWFudXBSZXF1ZXN0GiUubWVjYXRsLnYxLlBsYW5TZXNzaW9uQ2xlYW51cFJlc3BvbnNlElMKE0FwcGx5U2Vzc2lvbkNsZWFudXASJS5tZWNhdGwudjEuQXBwbHlTZXNzaW9uQ2xlYW51cFJlcXVlc3QaFS5tZWNhdGwudjEuQ2xlYW51cEpvYhJVChRDYW5jZWxTZXNzaW9uQ2xlYW51cBImLm1lY2F0bC52MS5DYW5jZWxTZXNzaW9uQ2xlYW51cFJlcXVlc3QaFS5tZWNhdGwudjEuQ2xlYW51cEpvYhJVChRHZXRTZXNzaW9uQ2xlYW51cEpvYhImLm1lY2F0bC52MS5HZXRTZXNzaW9uQ2xlYW51cEpvYlJlcXVlc3QaFS5tZWNhdGwudjEuQ2xlYW51cEpvYhJJCgpMaXN0U2tpbGxzEhwubWVjYXRsLnYxLkxpc3RTa2lsbHNSZXF1ZXN0Gh0ubWVjYXRsLnYxLkxpc3RTa2lsbHNSZXNwb25zZRJVCg5MaXN0U2tpbGxGaWxlcxIgLm1lY2F0bC52MS5MaXN0U2tpbGxGaWxlc1JlcXVlc3QaIS5tZWNhdGwudjEuTGlzdFNraWxsRmlsZXNSZXNwb25zZRJSCg1SZWFkU2tpbGxGaWxlEh8ubWVjYXRsLnYxLlJlYWRTa2lsbEZpbGVSZXF1ZXN0GiAubWVjYXRsLnYxLlJlYWRTa2lsbEZpbGVSZXNwb25zZRJACgdHZXRTb3VsEhkubWVjYXRsLnYxLkdldFNvdWxSZXF1ZXN0GhoubWVjYXRsLnYxLkdldFNvdWxSZXNwb25zZRJPCgxHZXRVc2VyTW9kZWwSHi5tZWNhdGwudjEuR2V0VXNlck1vZGVsUmVxdWVzdBofLm1lY2F0bC52MS5HZXRVc2VyTW9kZWxSZXNwb25zZRJVCg5SZWZsZWN0U2Vzc2lvbhIgLm1lY2F0bC52MS5SZWZsZWN0U2Vzc2lvblJlcXVlc3QaIS5tZWNhdGwudjEuUmVmbGVjdFNlc3Npb25SZXNwb25zZRJhChJHZXRMZWFybmluZ0F0dGVtcHQSJC5tZWNhdGwudjEuR2V0TGVhcm5pbmdBdHRlbXB0UmVxdWVzdBolLm1lY2F0bC52MS5HZXRMZWFybmluZ0F0dGVtcHRSZXNwb25zZRJnChRMaXN0TGVhcm5pbmdBdHRlbXB0cxImLm1lY2F0bC52MS5MaXN0TGVhcm5pbmdBdHRlbXB0c1JlcXVlc3QaJy5tZWNhdGwudjEuTGlzdExlYXJuaW5nQXR0ZW1wdHNSZXNwb25zZRJpChRSZXRyeUxlYXJuaW5nQXR0ZW1wdBInLm1lY2F0bC52MS5NdXRhdGVMZWFybmluZ0F0dGVtcHRSZXF1ZXN0GigubWVjYXRsLnYxLk11dGF0ZUxlYXJuaW5nQXR0ZW1wdFJlc3BvbnNlEmsKFkFiYW5kb25MZWFybmluZ0F0dGVtcHQSJy5tZWNhdGwudjEuTXV0YXRlTGVhcm5pbmdBdHRlbXB0UmVxdWVzdBooLm1lY2F0bC52MS5NdXRhdGVMZWFybmluZ0F0dGVtcHRSZXNwb25zZRJeChFHZW5lcmF0ZURyZWFtUGxhbhIjLm1lY2F0bC52MS5HZW5lcmF0ZURyZWFtUGxhblJlcXVlc3QaJC5tZWNhdGwudjEuR2VuZXJhdGVEcmVhbVBsYW5SZXNwb25zZRJYCg9EZWNpZGVEcmVhbVBsYW4SIS5tZWNhdGwudjEuRGVjaWRlRHJlYW1QbGFuUmVxdWVzdBoiLm1lY2F0bC52MS5EZWNpZGVEcmVhbVBsYW5SZXNwb25zZRJqChVMaXN0TGVhcm5pbmdQcm9wb3NhbHMSJy5tZWNhdGwudjEuTGlzdExlYXJuaW5nUHJvcG9zYWxzUmVxdWVzdBooLm1lY2F0bC52MS5MaXN0TGVhcm5pbmdQcm9wb3NhbHNSZXNwb25zZRJkChNHZXRMZWFybmluZ1Byb3Bvc2FsEiUubWVjYXRsLnYxLkdldExlYXJuaW5nUHJvcG9zYWxSZXF1ZXN0GiYubWVjYXRsLnYxLkdldExlYXJuaW5nUHJvcG9zYWxSZXNwb25zZRJtChZEZWNpZGVMZWFybmluZ1Byb3Bvc2FsEigubWVjYXRsLnYxLkRlY2lkZUxlYXJuaW5nUHJvcG9zYWxSZXF1ZXN0GikubWVjYXRsLnYxLkRlY2lkZUxlYXJuaW5nUHJvcG9zYWxSZXNwb25zZRJqChVVbmRvTGVhcm5pbmdQcm9tb3Rpb24SJy5tZWNhdGwudjEuVW5kb0xlYXJuaW5nUHJvbW90aW9uUmVxdWVzdBooLm1lY2F0bC52MS5VbmRvTGVhcm5pbmdQcm9tb3Rpb25SZXNwb25zZRJeChFMaXN0TGVhcm5lZFNraWxscxIjLm1lY2F0bC52MS5MaXN0TGVhcm5lZFNraWxsc1JlcXVlc3QaJC5tZWNhdGwudjEuTGlzdExlYXJuZWRTa2lsbHNSZXNwb25zZRJYCg9HZXRMZWFybmVkU2tpbGwSIS5tZWNhdGwudjEuR2V0TGVhcm5lZFNraWxsUmVxdWVzdBoiLm1lY2F0bC52MS5HZXRMZWFybmVkU2tpbGxSZXNwb25zZRJzChhEaWZmTGVhcm5lZFNraWxsVmVyc2lvbnMSKi5tZWNhdGwudjEuRGlmZkxlYXJuZWRTa2lsbFZlcnNpb25zUmVxdWVzdBorLm1lY2F0bC52MS5EaWZmTGVhcm5lZFNraWxsVmVyc2lvbnNSZXNwb25zZRJjChRBY3RpdmF0ZUxlYXJuZWRTa2lsbBIkLm1lY2F0bC52MS5NdXRhdGVMZWFybmVkU2tpbGxSZXF1ZXN0GiUubWVjYXRsLnYxLk11dGF0ZUxlYXJuZWRTa2lsbFJlc3BvbnNlEmEKElJlamVjdExlYXJuZWRTa2lsbBIkLm1lY2F0bC52MS5NdXRhdGVMZWFybmVkU2tpbGxSZXF1ZXN0GiUubWVjYXRsLnYxLk11dGF0ZUxlYXJuZWRTa2lsbFJlc3BvbnNlEmIKE0FyY2hpdmVMZWFybmVkU2tpbGwSJC5tZWNhdGwudjEuTXV0YXRlTGVhcm5lZFNraWxsUmVxdWVzdBolLm1lY2F0bC52MS5NdXRhdGVMZWFybmVkU2tpbGxSZXNwb25zZRJlChRSb2xsYmFja0xlYXJuZWRTa2lsbBImLm1lY2F0bC52MS5Sb2xsYmFja0xlYXJuZWRTa2lsbFJlcXVlc3QaJS5tZWNhdGwudjEuTXV0YXRlTGVhcm5lZFNraWxsUmVzcG9uc2USWwoQTGlzdFNraWxsQ2hhbmdlcxIiLm1lY2F0bC52MS5MaXN0U2tpbGxDaGFuZ2VzUmVxdWVzdBojLm1lY2F0bC52MS5MaXN0U2tpbGxDaGFuZ2VzUmVzcG9uc2USSQoKTGlzdE1vZGVscxIcLm1lY2F0bC52MS5MaXN0TW9kZWxzUmVxdWVzdBodLm1lY2F0bC52MS5MaXN0TW9kZWxzUmVzcG9uc2USSQoKQ3JlYXRlVGVhbRIcLm1lY2F0bC52MS5DcmVhdGVUZWFtUmVxdWVzdBodLm1lY2F0bC52MS5DcmVhdGVUZWFtUmVzcG9uc2USUgoNU3Bhd25UZWFtbWF0ZRIfLm1lY2F0bC52MS5TcGF3blRlYW1tYXRlUmVxdWVzdBogLm1lY2F0bC52MS5TcGF3blRlYW1tYXRlUmVzcG9uc2USZAoTU2VuZFRlYW1tYXRlTWVzc2FnZRIlLm1lY2F0bC52MS5TZW5kVGVhbW1hdGVNZXNzYWdlUmVxdWVzdBomLm1lY2F0bC52MS5TZW5kVGVhbW1hdGVNZXNzYWdlUmVzcG9uc2USVQoOQ2FuY2VsVGVhbW1hdGUSIC5tZWNhdGwudjEuQ2FuY2VsVGVhbW1hdGVSZXF1ZXN0GiEubWVjYXRsLnYxLkNhbmNlbFRlYW1tYXRlUmVzcG9uc2USPAoHUnVuVGVhbRIZLm1lY2F0bC52MS5SdW5UZWFtUmVxdWVzdBoULm1lY2F0bC52MS5UZWFtRXZlbnQwARJDCghMaXN0VGVhbRIaLm1lY2F0bC52MS5MaXN0VGVhbVJlcXVlc3QaGy5tZWNhdGwudjEuTGlzdFRlYW1SZXNwb25zZRJMCgtDbGVhbnVwVGVhbRIdLm1lY2F0bC52MS5DbGVhbnVwVGVhbVJlcXVlc3QaHi5tZWNhdGwudjEuQ2xlYW51cFRlYW1SZXNwb25zZRJACgtBcHByb3ZlUGxhbhIdLm1lY2F0bC52MS5BcHByb3ZlUGxhblJlcXVlc3QaEC5tZWNhdGwudjEuRXZlbnQwARJoChhDb25uZWN0V29ya3NwYWNlU2VydmljZXMSLC5tZWNhdGwudjEuV29ya3NwYWNlRW5yb2xsbWVudENvbm5lY3RSZXF1ZXN0Gh4ubWVjYXRsLnYxLldvcmtzcGFjZUVucm9sbG1lbnQSaAoYUmV0cnlXb3Jrc3BhY2VFbnJvbGxtZW50EiwubWVjYXRsLnYxLldvcmtzcGFjZUVucm9sbG1lbnRDb250cm9sUmVxdWVzdBoeLm1lY2F0bC52MS5Xb3Jrc3BhY2VFbnJvbGxtZW50EmkKGUNhbmNlbFdvcmtzcGFjZUVucm9sbG1lbnQSLC5tZWNhdGwudjEuV29ya3NwYWNlRW5yb2xsbWVudENvbnRyb2xSZXF1ZXN0Gh4ubWVjYXRsLnYxLldvcmtzcGFjZUVucm9sbG1lbnRCogEKDWNvbS5tZWNhdGwudjFCDEhhcm5lc3NQcm90b1ABWj5naXRodWIuY29tL3N0YWNrbG9rL21lY2F0bC9jb250cmFjdHMvZ2VuL2dvL21lY2F0bC92MTttZWNhdGx2MaICA01YWKoCCU1lY2F0bC5WMcoCCU1lY2F0bFxWMeICFU1lY2F0bFxWMVxHUEJNZXRhZGF0YeoCCk1lY2F0bDo6VjFiBnByb3RvMw", [file_buf_validate_validate, file_google_protobuf_timestamp]); + fileDesc("ChdtZWNhdGwvdjEvaGFybmVzcy5wcm90bxIJbWVjYXRsLnYxIlUKBkxpbWl0cxIRCgltYXhfdHVybnMYASABKAUSFgoObWF4X3Rvb2xfY2FsbHMYAiABKAUSIAoYbWF4X2NvbnNlY3V0aXZlX2ZhaWx1cmVzGAMgASgFIvkCChRDcmVhdGVTZXNzaW9uUmVxdWVzdBInCgRtb2RlGAIgASgOMhkubWVjYXRsLnYxLlBlcm1pc3Npb25Nb2RlEiEKBmxpbWl0cxgDIAEoCzIRLm1lY2F0bC52MS5MaW1pdHMSEwoLcHJvdmlkZXJfaWQYBCABKAkSEAoIbW9kZWxfaWQYBSABKAkSMAoJZXhlY3V0aW9uGAwgASgLMh0ubWVjYXRsLnYxLkV4ZWN1dGlvblNlbGVjdGlvbhIYChByZWFzb25pbmdfZWZmb3J0GAcgASgJEh8KF2RlYnVnX3RhcmdldF9zZXNzaW9uX2lkGAkgASgJEhkKEWRlYnVnX21jcF9zZXJ2ZXJzGAogAygJEi0KC21jcF9zZXJ2ZXJzGAsgAygLMhgubWVjYXRsLnYxLk1jcFNlcnZlclNwZWNKBAgBEAJKBAgIEAlKBAgGEAdSCXdvcmtzcGFjZVIRc291cmNlX3Nlc3Npb25faWRSB3Byb2ZpbGUibAoSRXhlY3V0aW9uU2VsZWN0aW9uEiYKBG5vbmUYASABKAsyGC5tZWNhdGwudjEuRXhlY3V0aW9uTm9uZRIuCgh0ZW1wbGF0ZRgCIAEoCzIcLm1lY2F0bC52MS5FeGVjdXRpb25UZW1wbGF0ZSIPCg1FeGVjdXRpb25Ob25lIjEKEUV4ZWN1dGlvblRlbXBsYXRlEgoKAmlkGAEgASgJEhAKCHJldmlzaW9uGAIgASgJIh8KHUxpc3RFeGVjdXRpb25UZW1wbGF0ZXNSZXF1ZXN0Im0KHkxpc3RFeGVjdXRpb25UZW1wbGF0ZXNSZXNwb25zZRIvCgVpdGVtcxgBIAMoCzIgLm1lY2F0bC52MS5FeGVjdXRpb25UZW1wbGF0ZUluZm8SGgoSaW52ZW50b3J5X3JldmlzaW9uGAIgASgJIr0CChVFeGVjdXRpb25UZW1wbGF0ZUluZm8SLgoIdGVtcGxhdGUYASABKAsyHC5tZWNhdGwudjEuRXhlY3V0aW9uVGVtcGxhdGUSDAoEbmFtZRgCIAEoCRITCgtkZXNjcmlwdGlvbhgDIAEoCRIVCg1kaXNwbGF5X3Rva2VuGAQgASgJEkQKCmV4dGVuc2lvbnMYBSADKAsyMC5tZWNhdGwudjEuRXhlY3V0aW9uVGVtcGxhdGVJbmZvLkV4dGVuc2lvbnNFbnRyeRIgChhkZWNsYXJlZF9leGVjdXRpb25fZmlsZXMYBiABKAgSHwoXZGVjbGFyZWRfYnVpbHRfaW5fc2hlbGwYByABKAgaMQoPRXh0ZW5zaW9uc0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEisQEKDU1jcFNlcnZlclNwZWMSDAoEbmFtZRgBIAEoCRILCgN1cmwYAiABKAkSDAoEdHlwZRgDIAEoCRIPCgdjb21tYW5kGAQgASgJEjYKB2hlYWRlcnMYBSADKAsyJS5tZWNhdGwudjEuTWNwU2VydmVyU3BlYy5IZWFkZXJzRW50cnkaLgoMSGVhZGVyc0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEiKwoUR2V0U2VydmVySW5mb1JlcXVlc3QSEwoLcHJvdmlkZXJfaWQYASABKAkibwoVR2V0U2VydmVySW5mb1Jlc3BvbnNlEhAKCGJ1aWxkX2lkGAEgASgJEh0KFXNlcnZlcl9pbXBsZW1lbnRhdGlvbhgCIAEoCRIlCh1sbG1fcHJvdmlkZXJfZGlzcGxheV9lbmRwb2ludBgDIAEoCSIdChtHZXRDb21wYXRpYmlsaXR5SW5mb1JlcXVlc3QijAEKHEdldENvbXBhdGliaWxpdHlJbmZvUmVzcG9uc2USEQoJYXBpX21ham9yGAEgASgFEjMKDGNhcGFiaWxpdGllcxgCIAEoCzIdLm1lY2F0bC52MS5TZXJ2ZXJDYXBhYmlsaXRpZXMSEAoIZmVhdHVyZXMYAyADKAkSEgoKZGVwbG95bWVudBgEIAEoCSKtBgoSU2VydmVyQ2FwYWJpbGl0aWVzEgsKA21jcBgBIAEoCBIWCg5zbGFzaF9jb21tYW5kcxgCIAEoCBIOCgZtZW1vcnkYAyABKAgSDgoGc2tpbGxzGAQgASgIEg0KBXRlYW1zGAUgASgIEg0KBXNoZWxsGAYgASgIEg0KBWltYWdlGAcgASgIEg0KBWF1ZGlvGAggASgIEg4KBmFnZW50cxgJIAEoCBIMCgRzb3VsGAogASgIEhIKCnVzZXJfbW9kZWwYCyABKAgSFwoPbW9kZWxfc2VsZWN0aW9uGAwgASgIEg8KB3Bvc3R1cmUYDSABKAkSEQoJd29ya3RyZWVzGA4gASgIEhIKCnNjaGVkdWxpbmcYDyABKAgSEgoKcmVmbGVjdGlvbhgQIAEoCBIaChJsZWFybmluZ19wcm9wb3NhbHMYESABKAgSFgoObGVhcm5lZF9za2lsbHMYEiABKAgSFwoPc3RvcmFnZV9jbGVhbnVwGBYgASgIEjgKDG1hbnVhbF9kcmVhbRgTIAEoCzIiLm1lY2F0bC52MS5NYW51YWxEcmVhbUNhcGFiaWxpdGllcxIWCg5zdG9yYWdlX2hlYWx0aBgXIAEoCBINCgVzdGVlchgYIAEoCBIZChFtYW51YWxfY29tcGFjdGlvbhgZIAEoCBIVCg1zZXNzaW9uX2RlYnVnGBogASgIEhEKCWRlYnVnX21jcBgbIAEoCBIcChR3b3Jrc3BhY2VfZW5yb2xsbWVudBgcIAEoCBIcChRtY3BfY29ubmVjdG9yX3N0YXR1cxgdIAEoCBITCgttY3BfcmVmcmVzaBgeIAEoCBIbChNleGVjdXRpb25fdGVtcGxhdGVzGB8gASgIEhwKD3Byb2plY3RfdHJ1c3RlZBggIAEoCEgAiAEBEiIKFXN1YmFnZW50X2Fza19yZXZpZXdlchghIAEoCUgBiAEBQhIKEF9wcm9qZWN0X3RydXN0ZWRCGAoWX3N1YmFnZW50X2Fza19yZXZpZXdlckoECBQQFUoECBUQFlIPbGVnYWN5X2Fkb3B0aW9uUhFzdG9yYWdlX21pZ3JhdGlvbiI1Ch9MaXN0U2Vzc2lvbk1jcENvbm5lY3RvcnNSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAkisgEKIExpc3RTZXNzaW9uTWNwQ29ubmVjdG9yc1Jlc3BvbnNlEhQKDGF2YWlsYWJpbGl0eRgBIAEoCRIYChBlbnJvbGxtZW50X3N0YXRlGAIgASgJEjEKCmNvbm5lY3RvcnMYAyADKAsyHS5tZWNhdGwudjEuTWNwQ29ubmVjdG9yU3RhdHVzEhgKEHRvdGFsX2Nvbm5lY3RvcnMYBCABKA0SEQoJdHJ1bmNhdGVkGAUgASgIIk8KEk1jcENvbm5lY3RvclN0YXR1cxIMCgRuYW1lGAEgASgJEhcKD2NhdGFsb2d1ZV9zdGF0ZRgCIAEoCRISCgp0b29sX2NvdW50GAMgASgNIuABChVDcmVhdGVTZXNzaW9uUmVzcG9uc2USEgoKc2Vzc2lvbl9pZBgBIAEoCRI8ChRzZXNzaW9uX2NhcGFiaWxpdGllcxgDIAEoCzIeLm1lY2F0bC52MS5TZXNzaW9uQ2FwYWJpbGl0aWVzEjAKDnJlc29sdmVkX21vZGVsGAQgASgLMhgubWVjYXRsLnYxLlJlc29sdmVkTW9kZWwSLwoJcGxhY2VtZW50GAUgASgLMhwubWVjYXRsLnYxLlBsYWNlbWVudE1ldGFkYXRhSgQIAhADUgxjYXBhYmlsaXRpZXMiUgoRUGxhY2VtZW50TWV0YWRhdGESDAoEa2luZBgBIAEoCRINCgVsYWJlbBgCIAEoCRIOCgZicmFuY2gYAyABKAkSEAoIcmV2aXNpb24YBCABKAkiaAoNUmVzb2x2ZWRNb2RlbBITCgtwcm92aWRlcl9pZBgBIAEoCRIQCghtb2RlbF9pZBgCIAEoCRIWCg5jb250ZXh0X3dpbmRvdxgDIAEoAxIYChByZWFzb25pbmdfZWZmb3J0GAQgASgJImQKE1Nlc3Npb25DYXBhYmlsaXRpZXMSDQoFaW1hZ2UYASABKAgSDQoFYXVkaW8YAiABKAgSFwoPZXhlY3V0aW9uX2ZpbGVzGAMgASgIEhYKDmJ1aWx0X2luX3NoZWxsGAQgASgIIjAKEUdldFNlc3Npb25SZXF1ZXN0EhsKCnNlc3Npb25faWQYASABKAlCB7pIBHICEAEiOQoSR2V0U2Vzc2lvblJlc3BvbnNlEiMKB3Nlc3Npb24YASABKAsyEi5tZWNhdGwudjEuU2Vzc2lvbiI6ChtHZXRTZXNzaW9uVHJhbnNjcmlwdFJlcXVlc3QSGwoKc2Vzc2lvbl9pZBgBIAEoCUIHukgEcgIQASJSChRBY3Rpdml0eVJlcGxheVN0YXR1cxIRCglhdmFpbGFibGUYASABKAgSEAoIY29tcGxldGUYAiABKAgSFQoNYXV0aG9yaXRhdGl2ZRgDIAEoCCLtAQocR2V0U2Vzc2lvblRyYW5zY3JpcHRSZXNwb25zZRISCgpzZXNzaW9uX2lkGAEgASgJEjAKCG1lc3NhZ2VzGAIgAygLMh4ubWVjYXRsLnYxLkNvbnZlcnNhdGlvbk1lc3NhZ2USEAoIY29tcGxldGUYAyABKAgSMQoIYWN0aXZpdHkYBCABKAsyHy5tZWNhdGwudjEuQWN0aXZpdHlSZXBsYXlTdGF0dXMSDAoEa2luZBgFIAEoCRI0CgxyZWxhdGlvbnNoaXAYBiABKAsyHi5tZWNhdGwudjEuU2Vzc2lvblJlbGF0aW9uc2hpcCI5ChpTdHJlYW1TZXNzaW9uRXZlbnRzUmVxdWVzdBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABIjcKGFN0cmVhbVNlc3Npb25MaXZlUmVxdWVzdBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABIlgKGVdhdGNoU2Vzc2lvbkV2ZW50c1JlcXVlc3QSGwoKc2Vzc2lvbl9pZBgBIAEoCUIHukgEcgIQARIOCgZjdXJzb3IYAiABKAkSDgoGcnVuX2lkGAMgASgJIlwKGldhdGNoU2Vzc2lvbkV2ZW50c1Jlc3BvbnNlEh8KBWV2ZW50GAEgASgLMhAubWVjYXRsLnYxLkV2ZW50Eg4KBmN1cnNvchgCIAEoCRINCgVwaGFzZRgDIAEoCSI4ChNMaXN0U2Vzc2lvbnNSZXF1ZXN0EhEKCXBhZ2Vfc2l6ZRgBIAEoBRIOCgZjdXJzb3IYAiABKAkiTgoJUHJpbmNpcGFsEg4KBmlzc3VlchgBIAEoCRIPCgdzdWJqZWN0GAIgASgJEhIKCmdyYW50X3R5cGUYAyABKAkSDAoEbmFtZRgEIAEoCSL+BAoOU2Vzc2lvblN1bW1hcnkSEgoKc2Vzc2lvbl9pZBgBIAEoCRIYChBtb2RpZmllZF9hdF91bml4GAIgASgDEg0KBXN0YXRlGAMgASgJEg0KBXR1cm5zGAQgASgFEhAKCG1vZGVsX2lkGAUgASgJEhcKD2NyZWF0ZWRfYXRfdW5peBgGIAEoAxIjCgVvd25lchgIIAEoCzIULm1lY2F0bC52MS5QcmluY2lwYWwSDAoEa2luZBgJIAEoCRI0CgxyZWxhdGlvbnNoaXAYCiABKAsyHi5tZWNhdGwudjEuU2Vzc2lvblJlbGF0aW9uc2hpcBI9CgxjYXBhYmlsaXRpZXMYCyABKAsyJy5tZWNhdGwudjEuU2Vzc2lvbkludmVudG9yeUNhcGFiaWxpdGllcxITCgtyZWFzb25fY29kZRgMIAEoCRIvCglwbGFjZW1lbnQYDyABKAsyHC5tZWNhdGwudjEuUGxhY2VtZW50TWV0YWRhdGESLwoOdGl0bGVfbWV0YWRhdGEYECABKAsyFy5tZWNhdGwudjEuU2Vzc2lvblRpdGxlEj4KC3Rva2VuX3VzYWdlGBEgAygLMikubWVjYXRsLnYxLlNlc3Npb25TdW1tYXJ5LlRva2VuVXNhZ2VFbnRyeRIWCg5hY3Rpdml0eV9zdGF0ZRgSIAEoCRpICg9Ub2tlblVzYWdlRW50cnkSCwoDa2V5GAEgASgJEiQKBXZhbHVlGAIgASgLMhUubWVjYXRsLnYxLlRva2VuVXNhZ2U6AjgBSgQIBxAISgQIDRAOSgQIDhAPUgV0aXRsZVIJd29ya3NwYWNlUhB0aXRsZV9wcm92ZW5hbmNlIuYBChNTZXNzaW9uUmVsYXRpb25zaGlwEhkKEXBhcmVudF9zZXNzaW9uX2lkGAEgASgJEg8KB2NhbGxfaWQYAiABKAkSGQoMYnJhbmNoX2luZGV4GAMgASgFSACIAQESFQoNc2NoZWR1bGVfbmFtZRgEIAEoCRIZChFvcmlnaW5fc2Vzc2lvbl9pZBgFIAEoCRIPCgd0ZWFtX2lkGAYgASgJEhMKC21lbWJlcl9uYW1lGAcgASgJEh8KF2RlYnVnX3RhcmdldF9zZXNzaW9uX2lkGAggASgJQg8KDV9icmFuY2hfaW5kZXgikgIKHFNlc3Npb25JbnZlbnRvcnlDYXBhYmlsaXRpZXMSEwoLcHVibGljX2NoYXQYASABKAgSDwoHaW5zcGVjdBgCIAEoCBIgChhhdXRob3JpdGF0aXZlX3RyYW5zY3JpcHQYAyABKAgSFwoPYWN0aXZpdHlfcmVwbGF5GAQgASgIEg8KB2NvcHlfaWQYBSABKAgSFwoPdmlld190cmFuc2NyaXB0GAYgASgIEgwKBGZvcmsYByABKAgSDgoGcmVuYW1lGAggASgIEg4KBmRlbGV0ZRgJIAEoCBI5CgdyZWFzb25zGAogASgLMigubWVjYXRsLnYxLlNlc3Npb25JbnZlbnRvcnlBY3Rpb25SZWFzb25zIp0BCh1TZXNzaW9uSW52ZW50b3J5QWN0aW9uUmVhc29ucxITCgtwdWJsaWNfY2hhdBgBIAEoCRIPCgdpbnNwZWN0GAIgASgJEg8KB2NvcHlfaWQYAyABKAkSFwoPdmlld190cmFuc2NyaXB0GAQgASgJEgwKBGZvcmsYBSABKAkSDgoGcmVuYW1lGAYgASgJEg4KBmRlbGV0ZRgHIAEoCSJtChRMaXN0U2Vzc2lvbnNSZXNwb25zZRIrCghzZXNzaW9ucxgBIAMoCzIZLm1lY2F0bC52MS5TZXNzaW9uU3VtbWFyeRITCgtuZXh0X2N1cnNvchgCIAEoCRITCgt0b3RhbF9jb3VudBgDIAEoBSIZChdHZXRTdG9yYWdlSGVhbHRoUmVxdWVzdCLeAQoPUmV0ZW50aW9uUG9saWN5EhwKFG1haW5fbWF4X2FnZV9zZWNvbmRzGAEgASgDEhYKDm1haW5fbWF4X2NvdW50GAIgASgFEh0KFWNoaWxkX21heF9hZ2Vfc2Vjb25kcxgDIAEoAxIXCg9jaGlsZF9tYXhfY291bnQYBCABKAUSIQoZc2NoZWR1bGVkX21heF9hZ2Vfc2Vjb25kcxgFIAEoAxIbChNzY2hlZHVsZWRfbWF4X2NvdW50GAYgASgFEh0KFXN3ZWVwX2NhZGVuY2Vfc2Vjb25kcxgHIAEoAyLJBwoYR2V0U3RvcmFnZUhlYWx0aFJlc3BvbnNlEhEKCWF2YWlsYWJsZRgBIAEoCBIaChJ1bmF2YWlsYWJsZV9yZWFzb24YAiABKAkSFQoNY3VycmVudF9ieXRlcxgDIAEoAxIfChdjdXJyZW50X2J5dGVzX2F2YWlsYWJsZRgEIAEoCBIZChFyZWNsYWltYWJsZV9ieXRlcxgFIAEoAxIjChtyZWNsYWltYWJsZV9ieXRlc19hdmFpbGFibGUYBiABKAgSFQoNc2Vzc2lvbl9jb3VudBgHIAEoAxISCgpmaWxlX2NvdW50GAggASgDEhAKCHYxX2NvdW50GAkgASgDEhAKCHYyX2NvdW50GAogASgDEhIKCm1haW5fY291bnQYCyABKAMSEwoLY2hpbGRfY291bnQYDCABKAMSFwoPc2NoZWR1bGVkX2NvdW50GA0gASgDEhUKDXVua25vd25fY291bnQYDiABKAMSFQoNY29ycnVwdF9jb3VudBgPIAEoAxIqCgZwb2xpY3kYECABKAsyGi5tZWNhdGwudjEuUmV0ZW50aW9uUG9saWN5EhcKD2xhc3Rfc3dlZXBfdW5peBgRIAEoAxIcChRsYXN0X3N3ZWVwX2F2YWlsYWJsZRgSIAEoCBIXCg9uZXh0X3N3ZWVwX3VuaXgYEyABKAMSHAoUbmV4dF9zd2VlcF9hdmFpbGFibGUYFCABKAgSEgoKYWN0aXZlX2pvYhgVIAEoCRIUCgxsYXN0X2ZhaWx1cmUYFiABKAkSJAocb3duZXJsZXNzX3Nlc3Npb25zX2F2YWlsYWJsZRgXIAEoCBItCiVvd25lcmxlc3Nfc2Vzc2lvbnNfdW5hdmFpbGFibGVfcmVhc29uGBggASgJEh8KF293bmVybGVzc19zZXNzaW9uX2NvdW50GBkgASgDEh0KFW93bmVybGVzc19zZXNzaW9uX2lkcxgaIAMoCRInCh9vd25lcmxlc3Nfc2Vzc2lvbl9pZHNfdHJ1bmNhdGVkGBsgASgIEiUKHW93bmVybGVzc19zY2hlZHVsZXNfYXZhaWxhYmxlGBwgASgIEi4KJm93bmVybGVzc19zY2hlZHVsZXNfdW5hdmFpbGFibGVfcmVhc29uGB0gASgJEiAKGG93bmVybGVzc19zY2hlZHVsZV9jb3VudBgeIAEoAxIgChhvd25lcmxlc3Nfc2NoZWR1bGVfbmFtZXMYHyADKAkSKgoib3duZXJsZXNzX3NjaGVkdWxlX25hbWVzX3RydW5jYXRlZBggIAEoCCLZAgoNQ2xlYW51cENvdW50cxINCgV0b3RhbBgBIAEoBRI1CgdieV9raW5kGAIgAygLMiQubWVjYXRsLnYxLkNsZWFudXBDb3VudHMuQnlLaW5kRW50cnkSNwoIYnlfc3RhdGUYAyADKAsyJS5tZWNhdGwudjEuQ2xlYW51cENvdW50cy5CeVN0YXRlRW50cnkSOQoJYnlfcmVhc29uGAQgAygLMiYubWVjYXRsLnYxLkNsZWFudXBDb3VudHMuQnlSZWFzb25FbnRyeRotCgtCeUtpbmRFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAU6AjgBGi4KDEJ5U3RhdGVFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAU6AjgBGi8KDUJ5UmVhc29uRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgFOgI4ASKGAQoQQ2xlYW51cENhbmRpZGF0ZRISCgpzZXNzaW9uX2lkGAEgASgJEgwKBGtpbmQYAiABKAkSDQoFc3RhdGUYAyABKAkSDgoGcmVhc29uGAQgASgJEhgKEG1vZGlmaWVkX2F0X3VuaXgYBSABKAMSFwoPZXN0aW1hdGVkX2J5dGVzGAYgASgDIioKGVBsYW5TZXNzaW9uQ2xlYW51cFJlcXVlc3QSDQoFa2luZHMYASADKAki0wIKGlBsYW5TZXNzaW9uQ2xlYW51cFJlc3BvbnNlEhoKEmNvbmZpcm1hdGlvbl90b2tlbhgBIAEoCRIRCglhdmFpbGFibGUYAiABKAgSGgoSdW5hdmFpbGFibGVfcmVhc29uGAMgASgJEhIKCmdlbmVyYXRpb24YBCABKAkSFgoOcG9saWN5X3ZlcnNpb24YBSABKAkSLQoIZWxpZ2libGUYBiADKAsyGy5tZWNhdGwudjEuQ2xlYW51cENhbmRpZGF0ZRIrCglwcm90ZWN0ZWQYByABKAsyGC5tZWNhdGwudjEuQ2xlYW51cENvdW50cxIXCg9lc3RpbWF0ZWRfYnl0ZXMYCCABKAMSFgoOcGxhbm5lZF9qb2JfaWQYCSABKAkSMQoPZWxpZ2libGVfY291bnRzGAogASgLMhgubWVjYXRsLnYxLkNsZWFudXBDb3VudHMiQQoaQXBwbHlTZXNzaW9uQ2xlYW51cFJlcXVlc3QSIwoSY29uZmlybWF0aW9uX3Rva2VuGAEgASgJQge6SARyAhABIjYKG0NhbmNlbFNlc3Npb25DbGVhbnVwUmVxdWVzdBIXCgZqb2JfaWQYASABKAlCB7pIBHICEAEiNgobR2V0U2Vzc2lvbkNsZWFudXBKb2JSZXF1ZXN0EhcKBmpvYl9pZBgBIAEoCUIHukgEcgIQASJNChBDbGVhbnVwSXRlbUVycm9yEhMKC2l0ZW1faGFuZGxlGAEgASgJEhMKC3JlYXNvbl9jb2RlGAIgASgJEg8KB21lc3NhZ2UYAyABKAkirAEKCkNsZWFudXBKb2ISDgoGam9iX2lkGAEgASgJEg0KBXN0YXRlGAIgASgJEhEKCXByb2Nlc3NlZBgDIAEoBRIPCgdkZWxldGVkGAQgASgFEg8KB3NraXBwZWQYBSABKAUSDQoFc3RhbGUYBiABKAUSDgoGZmFpbGVkGAcgASgFEisKBmVycm9ycxgIIAMoCzIbLm1lY2F0bC52MS5DbGVhbnVwSXRlbUVycm9yIlYKDlNldE1vZGVSZXF1ZXN0EhsKCnNlc3Npb25faWQYASABKAlCB7pIBHICEAESJwoEbW9kZRgCIAEoDjIZLm1lY2F0bC52MS5QZXJtaXNzaW9uTW9kZSI2Cg9TZXRNb2RlUmVzcG9uc2USIwoHc2Vzc2lvbhgBIAEoCzISLm1lY2F0bC52MS5TZXNzaW9uIjIKE0Nsb3NlU2Vzc2lvblJlcXVlc3QSGwoKc2Vzc2lvbl9pZBgBIAEoCUIHukgEcgIQASIWChRDbG9zZVNlc3Npb25SZXNwb25zZSJLChRSZW5hbWVTZXNzaW9uUmVxdWVzdBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABEhYKBXRpdGxlGAIgASgJQge6SARyAhABIjwKFVJlbmFtZVNlc3Npb25SZXNwb25zZRIjCgdzZXNzaW9uGAEgASgLMhIubWVjYXRsLnYxLlNlc3Npb24iMwoURGVsZXRlU2Vzc2lvblJlcXVlc3QSGwoKc2Vzc2lvbl9pZBgBIAEoCUIHukgEcgIQASIXChVEZWxldGVTZXNzaW9uUmVzcG9uc2UibwoTQ2xlYXJTZXNzaW9uUmVxdWVzdBIiChFzb3VyY2Vfc2Vzc2lvbl9pZBgBIAEoCUIHukgEcgIQARIeChF3b3JrdHJlZV9zZWxlY3RvchgCIAEoCUgAiAEBQhQKEl93b3JrdHJlZV9zZWxlY3RvciJbChRDbGVhclNlc3Npb25SZXNwb25zZRISCgpzZXNzaW9uX2lkGAEgASgJEi8KCXBsYWNlbWVudBgCIAEoCzIcLm1lY2F0bC52MS5QbGFjZW1lbnRNZXRhZGF0YSK+AQoSRm9ya1Nlc3Npb25SZXF1ZXN0EiIKEXNvdXJjZV9zZXNzaW9uX2lkGAEgASgJQge6SARyAhABEg0KBXRpdGxlGAIgASgJEhgKEHJlYXNvbmluZ19lZmZvcnQYAyABKAkSHgoRd29ya3RyZWVfc2VsZWN0b3IYBCABKAlIAIgBARITCgtwcm92aWRlcl9pZBgFIAEoCRIQCghtb2RlbF9pZBgGIAEoCUIUChJfd29ya3RyZWVfc2VsZWN0b3IiWgoTRm9ya1Nlc3Npb25SZXNwb25zZRISCgpzZXNzaW9uX2lkGAEgASgJEi8KCXBsYWNlbWVudBgCIAEoCzIcLm1lY2F0bC52MS5QbGFjZW1lbnRNZXRhZGF0YSI7ChBDb250ZXh0T2NjdXBhbmN5EhQKDGlucHV0X3Rva2VucxgBIAEoAxIRCgllc3RpbWF0ZWQYAiABKAgirAYKB1Nlc3Npb24SEgoKc2Vzc2lvbl9pZBgBIAEoCRINCgVzdGF0ZRgCIAEoCRInCgRtb2RlGAMgASgOMhkubWVjYXRsLnYxLlBlcm1pc3Npb25Nb2RlEiEKBmxpbWl0cxgFIAEoCzIRLm1lY2F0bC52MS5MaW1pdHMSDQoFdHVybnMYBiABKAUSEgoKdG9vbF9jYWxscxgHIAEoBRIXCg9jcmVhdGVkX2F0X3VuaXgYCCABKAMSMAoOcmVzb2x2ZWRfbW9kZWwYCSABKAsyGC5tZWNhdGwudjEuUmVzb2x2ZWRNb2RlbBIMCgRraW5kGA4gASgJEjQKDHJlbGF0aW9uc2hpcBgPIAEoCzIeLm1lY2F0bC52MS5TZXNzaW9uUmVsYXRpb25zaGlwEhkKEWRlYnVnX21jcF9zZXJ2ZXJzGBAgAygJEhcKD2RlYnVnX21jcF90b29scxgRIAMoCRIvCglwbGFjZW1lbnQYEiABKAsyHC5tZWNhdGwudjEuUGxhY2VtZW50TWV0YWRhdGESLwoOdGl0bGVfbWV0YWRhdGEYEyABKAsyFy5tZWNhdGwudjEuU2Vzc2lvblRpdGxlEjcKC3Rva2VuX3VzYWdlGBQgAygLMiIubWVjYXRsLnYxLlNlc3Npb24uVG9rZW5Vc2FnZUVudHJ5EjwKFHNlc3Npb25fY2FwYWJpbGl0aWVzGBUgASgLMh4ubWVjYXRsLnYxLlNlc3Npb25DYXBhYmlsaXRpZXMSPQoYbGF0ZXN0X2NvbnRleHRfb2NjdXBhbmN5GBYgASgLMhsubWVjYXRsLnYxLkNvbnRleHRPY2N1cGFuY3kaSAoPVG9rZW5Vc2FnZUVudHJ5EgsKA2tleRgBIAEoCRIkCgV2YWx1ZRgCIAEoCzIVLm1lY2F0bC52MS5Ub2tlblVzYWdlOgI4AUoECAQQBUoECAoQC0oECAsQDEoECAwQDUoECA0QDlIJd29ya3NwYWNlUgV0aXRsZVIMY2FwYWJpbGl0aWVzUhB0aXRsZV9wcm92ZW5hbmNlUhphZG9wdGlvbl9zb3VyY2Vfc2Vzc2lvbl9pZCKVAQoMU2Vzc2lvblRpdGxlEg0KBXRpdGxlGAEgASgJEhIKCnByb3ZlbmFuY2UYAiABKAkSGAoQZ2VuZXJhdGlvbl9zdGF0ZRgDIAEoCRI2Cg5sYXRlc3RfYXR0ZW1wdBgEIAEoCzIeLm1lY2F0bC52MS5UaXRsZUF0dGVtcHRTdW1tYXJ5EhAKCHJldmlzaW9uGAUgASgEIjgKE1RpdGxlQXR0ZW1wdFN1bW1hcnkSCgoCaWQYASABKAkSDwoHb3V0Y29tZRgCIAEoCUoECAMQBCKhAQoKVG9rZW5Vc2FnZRIfCgV0b3RhbBgBIAEoCzIQLm1lY2F0bC52MS5Vc2FnZRIxCgZtb2RlbHMYAiADKAsyIS5tZWNhdGwudjEuVG9rZW5Vc2FnZS5Nb2RlbHNFbnRyeRo/CgtNb2RlbHNFbnRyeRILCgNrZXkYASABKAkSHwoFdmFsdWUYAiABKAsyEC5tZWNhdGwudjEuVXNhZ2U6AjgBIsQCCg9Db252ZXJzZVJlcXVlc3QSIwoGcHJvbXB0GAEgASgLMhEubWVjYXRsLnYxLlByb21wdEgAEiYKBXJldHJ5GAIgASgLMhUubWVjYXRsLnYxLlJldHJ5U3RhcnRIABI0Cg9yZXN1bWVfYXBwcm92YWwYCiABKAsyGS5tZWNhdGwudjEuUmVzdW1lQXBwcm92YWxIABIjCgZjYW5jZWwYCyABKAsyES5tZWNhdGwudjEuQ2FuY2VsSAASLgoMY2FuY2VsX2NoaWxkGAwgASgLMhYubWVjYXRsLnYxLkNhbmNlbENoaWxkSAASIQoFc3RlZXIYDSABKAsyEC5tZWNhdGwudjEuU3RlZXJIABIuCgxzdGVlcl9jYW5jZWwYDiABKAsyFi5tZWNhdGwudjEuU3RlZXJDYW5jZWxIAEIGCgRraW5kIjMKEENvbnZlcnNlUmVzcG9uc2USHwoFZXZlbnQYASABKAsyEC5tZWNhdGwudjEuRXZlbnQinAEKB0NvbnRlbnQSJQoEa2luZBgBIAEoDjIXLm1lY2F0bC52MS5Db250ZW50LktpbmQSEQoJbWltZV90eXBlGAIgASgJEgwKBGRhdGEYAyABKAwSCwoDdXJsGAQgASgJIjwKBEtpbmQSFAoQS0lORF9VTlNQRUNJRklFRBAAEg4KCktJTkRfSU1BR0UQARIOCgpLSU5EX0FVRElPEAIikAMKDENvbnRlbnRCbG9jaxIqCgRraW5kGAEgASgOMhwubWVjYXRsLnYxLkNvbnRlbnRCbG9jay5LaW5kEhEKCW1pbWVfdHlwZRgCIAEoCRIMCgRkYXRhGAMgASgMEgsKA3VybBgEIAEoCRIMCgR0ZXh0GAUgASgJEgwKBG5hbWUYBiABKAkSDQoFdGl0bGUYByABKAkSEwoLZGVzY3JpcHRpb24YCCABKAkSDAoEc2l6ZRgJIAEoAxIQCghhdWRpZW5jZRgKIAMoCRIQCghwcmlvcml0eRgLIAEoARIVCg1sYXN0X21vZGlmaWVkGAwgASgJIpwBCgRLaW5kEhQKEEtJTkRfVU5TUEVDSUZJRUQQABINCglLSU5EX1RFWFQQARIOCgpLSU5EX0lNQUdFEAISDgoKS0lORF9BVURJTxADEhYKEktJTkRfUkVTT1VSQ0VfTElOSxAEEhoKFktJTkRfRU1CRURERURfUkVTT1VSQ0UQBRIbChdLSU5EX1NUUlVDVFVSRURfQ09OVEVOVBAGIikKClJldHJ5U3RhcnQSGwoKc2Vzc2lvbl9pZBgBIAEoCUIHukgEcgIQASJ+CgZQcm9tcHQSGwoKc2Vzc2lvbl9pZBgBIAEoCUIHukgEcgIQARIMCgR0ZXh0GAIgASgJEiEKBXBhcnRzGAMgAygLMhIubWVjYXRsLnYxLkNvbnRlbnQSJgoec2VydmVyX293bmVkX3BsYW5fY29udGludWF0aW9uGAQgASgIItcBCg5SZXN1bWVBcHByb3ZhbBIXCgZhc2tfaWQYASABKAlCB7pIBHICEAESOQoHdmVyZGljdBgDIAEoDjIaLm1lY2F0bC52MS5BcHByb3ZhbFZlcmRpY3RCDLpICYIBBhgBGAIYAxIXCg9leHBlY3RlZF9ydW5faWQYBCABKAkSEQoJcmV2aWV3X2lkGAUgASgJEjgKDmd1YXJkcmFpbF9raW5kGAYgASgOMiAubWVjYXRsLnYxLkd1YXJkcmFpbEFwcHJvdmFsS2luZEoECAIQA1IFYWxsb3ciIQoGQ2FuY2VsEhcKD2V4cGVjdGVkX3J1bl9pZBgBIAEoCSIoCgtDYW5jZWxDaGlsZBIZCghjaGlsZF9pZBgBIAEoCUIHukgEcgIQASJlCgVTdGVlchIMCgR0ZXh0GAEgASgJEhIKCm1lc3NhZ2VfaWQYAiABKAkSIQoFcGFydHMYAyADKAsyEi5tZWNhdGwudjEuQ29udGVudBIXCg9leHBlY3RlZF9ydW5faWQYBCABKAkiOgoLU3RlZXJDYW5jZWwSEgoKbWVzc2FnZV9pZBgBIAEoCRIXCg9leHBlY3RlZF9ydW5faWQYAiABKAki9gEKFFJlc29sdmVSdW5Bc2tSZXF1ZXN0EhsKCnNlc3Npb25faWQYASABKAlCB7pIBHICEAESIAoPZXhwZWN0ZWRfcnVuX2lkGAIgASgJQge6SARyAhABEhcKBmFza19pZBgDIAEoCUIHukgEcgIQARI5Cgd2ZXJkaWN0GAQgASgOMhoubWVjYXRsLnYxLkFwcHJvdmFsVmVyZGljdEIMukgJggEGGAEYAhgDEhEKCXJldmlld19pZBgFIAEoCRI4Cg5ndWFyZHJhaWxfa2luZBgGIAEoDjIgLm1lY2F0bC52MS5HdWFyZHJhaWxBcHByb3ZhbEtpbmQiNwoVUmVzb2x2ZVJ1bkFza1Jlc3BvbnNlEg4KBnJ1bl9pZBgBIAEoCRIOCgZhc2tfaWQYAiABKAkiqgEKFVJlc29sdmVQbGFuQXNrUmVxdWVzdBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABEiAKD2V4cGVjdGVkX3J1bl9pZBgCIAEoCUIHukgEcgIQARIXCgZhc2tfaWQYAyABKAlCB7pIBHICEAESOQoHdmVyZGljdBgEIAEoDjIaLm1lY2F0bC52MS5BcHByb3ZhbFZlcmRpY3RCDLpICYIBBhgBGAIYAyI4ChZSZXNvbHZlUGxhbkFza1Jlc3BvbnNlEg4KBnJ1bl9pZBgBIAEoCRIOCgZhc2tfaWQYAiABKAkiUQoQQ2FuY2VsUnVuUmVxdWVzdBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABEiAKD2V4cGVjdGVkX3J1bl9pZBgCIAEoCUIHukgEcgIQASIjChFDYW5jZWxSdW5SZXNwb25zZRIOCgZydW5faWQYASABKAkikAIKD1N0ZWVyUnVuUmVxdWVzdBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABEiAKD2V4cGVjdGVkX3J1bl9pZBgCIAEoCUIHukgEcgIQARIMCgR0ZXh0GAMgASgJEisKBXBhcnRzGAQgAygLMhIubWVjYXRsLnYxLkNvbnRlbnRCCLpIBZIBAhAQEhsKCm1lc3NhZ2VfaWQYBSABKAlCB7pIBHICGEA6ZrpIYxphChFzdGVlcl9ydW4uY29udGVudBIjdGV4dCBhbmQgcGFydHMgY2Fubm90IGJvdGggYmUgZW1wdHkaJ3RoaXMudGV4dCAhPSAnJyB8fCBzaXplKHRoaXMucGFydHMpID4gMCJgChBTdGVlclJ1blJlc3BvbnNlEigKB291dGNvbWUYASABKA4yFy5tZWNhdGwudjEuU3RlZXJPdXRjb21lEg4KBnJ1bl9pZBgCIAEoCRISCgptZXNzYWdlX2lkGAMgASgJInMKFUNhbmNlbFJ1blN0ZWVyUmVxdWVzdBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABEiAKD2V4cGVjdGVkX3J1bl9pZBgCIAEoCUIHukgEcgIQARIbCgptZXNzYWdlX2lkGAMgASgJQge6SARyAhhAImYKFkNhbmNlbFJ1blN0ZWVyUmVzcG9uc2USKAoHb3V0Y29tZRgBIAEoDjIXLm1lY2F0bC52MS5TdGVlck91dGNvbWUSDgoGcnVuX2lkGAIgASgJEhIKCm1lc3NhZ2VfaWQYAyABKAkiaAomR2V0TWNwQXV0aG9yaXphdGlvblByZXNlbnRhdGlvblJlcXVlc3QSGwoKc2Vzc2lvbl9pZBgBIAEoCUIHukgEcgIQARIhChBhdXRob3JpemF0aW9uX2lkGAIgASgJQge6SARyAhABIjYKJ0dldE1jcEF1dGhvcml6YXRpb25QcmVzZW50YXRpb25SZXNwb25zZRILCgN1cmwYASABKAkitAEKHlJlY2hlY2tNY3BBdXRob3JpemF0aW9uUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEhgKEGF1dGhvcml6YXRpb25faWQYAiABKAkSNAoPcmVzdW1lX2FwcHJvdmFsGAogASgLMhkubWVjYXRsLnYxLlJlc3VtZUFwcHJvdmFsSAASIwoGY2FuY2VsGAsgASgLMhEubWVjYXRsLnYxLkNhbmNlbEgAQgkKB2NvbnRyb2wiQgofUmVjaGVja01jcEF1dGhvcml6YXRpb25SZXNwb25zZRIfCgVldmVudBgBIAEoCzIQLm1lY2F0bC52MS5FdmVudCKzAQodQ2FuY2VsTWNwQXV0aG9yaXphdGlvblJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIYChBhdXRob3JpemF0aW9uX2lkGAIgASgJEjQKD3Jlc3VtZV9hcHByb3ZhbBgKIAEoCzIZLm1lY2F0bC52MS5SZXN1bWVBcHByb3ZhbEgAEiMKBmNhbmNlbBgLIAEoCzIRLm1lY2F0bC52MS5DYW5jZWxIAEIJCgdjb250cm9sIkEKHkNhbmNlbE1jcEF1dGhvcml6YXRpb25SZXNwb25zZRIfCgVldmVudBgBIAEoCzIQLm1lY2F0bC52MS5FdmVudCKQAQoNQXV0aG9yaXphdGlvbhIYChBhdXRob3JpemF0aW9uX2lkGAEgASgJEg8KB2NhbGxfaWQYAiABKAkSLgoKZXhwaXJlc19hdBgDIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASDgoGc3RhdHVzGAQgASgJEhQKDGRpc3BsYXlfbmFtZRgFIAEoCSI+ChdQbGFuQ29udGludWF0aW9uRmFpbHVyZRITCgtwbGFuX3J1bl9pZBgBIAEoCRIOCgZhc2tfaWQYAiABKAkitwcKBUV2ZW50EgwKBHR5cGUYASABKAkSCwoDc2VxGAIgASgDEgwKBHR1cm4YAyABKAUSDAoEdGV4dBgEIAEoCRImCgl0b29sX2NhbGwYBSABKAsyEy5tZWNhdGwudjEuVG9vbENhbGwSKgoLdG9vbF9yZXN1bHQYBiABKAsyFS5tZWNhdGwudjEuVG9vbFJlc3VsdBIlCgNhc2sYByABKAsyGC5tZWNhdGwudjEuUGVybWlzc2lvbkFzaxIhCgZyZXN1bHQYCCABKAsyES5tZWNhdGwudjEuUmVzdWx0EiQKCHR1cm5fZW5kGAogASgLMhIubWVjYXRsLnYxLlR1cm5FbmQSHQoEaG9vaxgLIAEoCzIPLm1lY2F0bC52MS5Ib29rEiUKCHN1YmFnZW50GAwgASgLMhMubWVjYXRsLnYxLlN1YmFnZW50Eh0KBHRlYW0YDSABKAsyDy5tZWNhdGwudjEuVGVhbRIlCghwYXJhbGxlbBgOIAEoCzITLm1lY2F0bC52MS5QYXJhbGxlbBIsCghzY2hlZHVsZRgPIAEoCzIaLm1lY2F0bC52MS5TY2hlZHVsZVBheWxvYWQSJQoIYXBwcm92YWwYECABKAsyEy5tZWNhdGwudjEuQXBwcm92YWwSKgoLdXNlcl9wcm9tcHQYESABKAsyFS5tZWNhdGwudjEuVXNlclByb21wdBI4ChJjb21wYWN0aW9uX2FyY2hpdmUYEiABKAsyHC5tZWNhdGwudjEuQ29tcGFjdGlvbkFyY2hpdmUSIwoFc3RlZXIYEyABKAsyFC5tZWNhdGwudjEuU3RlZXJFY2hvEioKDXN0ZWVyX291dGNvbWUYFCABKAsyEy5tZWNhdGwudjEuU3RlZXJBY2sSKgoLbW9kZWxfcmV0cnkYFSABKAsyFS5tZWNhdGwudjEuTW9kZWxSZXRyeRIOCgZydW5faWQYFiABKAkSJgoFdGl0bGUYFyABKAsyFy5tZWNhdGwudjEuU2Vzc2lvblRpdGxlEi8KDWF1dGhvcml6YXRpb24YGCABKAsyGC5tZWNhdGwudjEuQXV0aG9yaXphdGlvbhIyCg9jb250cm9sX3JlZnVzZWQYGSABKAsyGS5tZWNhdGwudjEuQ29udHJvbFJlZnVzZWQSRQoZcGxhbl9jb250aW51YXRpb25fZmFpbHVyZRgaIAEoCzIiLm1lY2F0bC52MS5QbGFuQ29udGludWF0aW9uRmFpbHVyZUoECAkQClIFdXNhZ2UiMgoOQ29udHJvbFJlZnVzZWQSDgoGYXNrX2lkGAEgASgJEhAKCGNhdGVnb3J5GAIgASgJIngKCk1vZGVsUmV0cnkSNgoRcmV0cnlfZGlzcG9zaXRpb24YASABKA4yGy5tZWNhdGwudjEuUmV0cnlEaXNwb3NpdGlvbhIyCg9zdHJlYW1fcHJvZ3Jlc3MYAiABKA4yGS5tZWNhdGwudjEuU3RyZWFtUHJvZ3Jlc3MiUAoJU3RlZXJFY2hvEgwKBHRleHQYASABKAkSEgoKbWVzc2FnZV9pZBgCIAEoCRIhCgVwYXJ0cxgDIAMoCzISLm1lY2F0bC52MS5Db250ZW50ImgKCFN0ZWVyQWNrEigKB291dGNvbWUYASABKA4yFy5tZWNhdGwudjEuU3RlZXJPdXRjb21lEgwKBHRleHQYAiABKAkSEAoIcHJvbW90ZWQYAyABKAgSEgoKbWVzc2FnZV9pZBgEIAEoCSItCgxHdWFyZHJhaWxSZWYSCwoDcmVmGAEgASgJEhAKCGNhdGVnb3J5GAIgASgJIq8DCg9HdWFyZHJhaWxSZXZpZXcSEQoJcmV2aWV3X2lkGAEgASgJEiQKA2pvYhgCIAEoDjIXLm1lY2F0bC52MS5HdWFyZHJhaWxKb2ISMgoKYXNzZXNzbWVudBgDIAEoDjIeLm1lY2F0bC52MS5HdWFyZHJhaWxBc3Nlc3NtZW50EjIKCmluc3BlY3Rpb24YBCABKA4yHi5tZWNhdGwudjEuR3VhcmRyYWlsSW5zcGVjdGlvbhI0CgtkaXNwb3NpdGlvbhgFIAEoDjIfLm1lY2F0bC52MS5HdWFyZHJhaWxEaXNwb3NpdGlvbhITCgtyZWFzb25fY29kZRgGIAEoCRIPCgdydWxlX2lkGAcgASgJEhMKC3J1bGVfb3JpZ2luGAggASgJEhsKE2NoZWNrZXJfcHJvdmlkZXJfaWQYCSABKAkSGAoQY2hlY2tlcl9tb2RlbF9pZBgKIAEoCRIpCghjb25jZXJucxgLIAMoCzIXLm1lY2F0bC52MS5HdWFyZHJhaWxSZWYSKAoHc291cmNlcxgMIAMoCzIXLm1lY2F0bC52MS5HdWFyZHJhaWxSZWYipwEKFkd1YXJkcmFpbEFwcHJvdmFsU2NvcGUSEQoJcmV2aWV3X2lkGAEgASgJEi4KBGtpbmQYAiABKA4yIC5tZWNhdGwudjEuR3VhcmRyYWlsQXBwcm92YWxLaW5kEhQKDGdyYW50X2RpZ2VzdBgDIAEoCRIUCgxzZXNzaW9uX29ubHkYBCABKAgSGAoQcmVwZWF0X2F2YWlsYWJsZRgFIAEoCEoECAYQByIyChxMaXN0R3VhcmRyYWlsQ292ZXJhZ2VSZXF1ZXN0EhIKCnNlc3Npb25faWQYASABKAki0wEKFkd1YXJkcmFpbENvdmVyYWdlRW50cnkSDAoEdG9vbBgBIAEoCRINCgVwaGFzZRgCIAEoCRIkCgNqb2IYAyABKA4yFy5tZWNhdGwudjEuR3VhcmRyYWlsSm9iEgwKBG1vZGUYBCABKAkSDwoHcnVsZV9pZBgFIAEoCRITCgtydWxlX29yaWdpbhgGIAEoCRIyCgppbnNwZWN0aW9uGAcgASgOMh4ubWVjYXRsLnYxLkd1YXJkcmFpbEluc3BlY3Rpb24SDgoGcmVhc29uGAggASgJIpsBCh1MaXN0R3VhcmRyYWlsQ292ZXJhZ2VSZXNwb25zZRIPCgdlbmFibGVkGAEgASgIEhsKE2NoZWNrZXJfcHJvdmlkZXJfaWQYAiABKAkSGAoQY2hlY2tlcl9tb2RlbF9pZBgDIAEoCRIyCgdlbnRyaWVzGAQgAygLMiEubWVjYXRsLnYxLkd1YXJkcmFpbENvdmVyYWdlRW50cnkiSAofR2V0R3VhcmRyYWlsUmV2aWV3RGV0YWlsUmVxdWVzdBISCgpzZXNzaW9uX2lkGAEgASgJEhEKCXJldmlld19pZBgCIAEoCSJzCiBHZXRHdWFyZHJhaWxSZXZpZXdEZXRhaWxSZXNwb25zZRIRCglyZXZpZXdfaWQYASABKAkSDwoHY29uY2VybhgCIAEoCRIWCg5zb3VyY2VfZGlzcGxheRgDIAEoCRITCgtuZXh0X2FjdGlvbhgEIAEoCSKQAQoIQXBwcm92YWwSDgoGYXNrX2lkGAEgASgJEgwKBHRvb2wYAyABKAkSDwoHY2FsbF9pZBgEIAEoCRIrCgd2ZXJkaWN0GAYgASgOMhoubWVjYXRsLnYxLkFwcHJvdmFsVmVyZGljdBIOCgZvcmlnaW4YByABKAlKBAgCEANKBAgFEAZSDGFsbG93X2Fsd2F5cyJQCgpVc2VyUHJvbXB0EgwKBHRleHQYASABKAkSIQoFcGFydHMYAiADKAsyEi5tZWNhdGwudjEuQ29udGVudBIRCglzeW50aGV0aWMYAyABKAgiRQoRQ29tcGFjdGlvbkFyY2hpdmUSMAoIcmVwbGFjZWQYASADKAsyHi5tZWNhdGwudjEuQ29udmVyc2F0aW9uTWVzc2FnZSLvAQoTQ29udmVyc2F0aW9uTWVzc2FnZRIMCgRyb2xlGAEgASgJEgwKBHRleHQYAiABKAkSJwoKdG9vbF9jYWxscxgDIAMoCzITLm1lY2F0bC52MS5Ub29sQ2FsbBIqCgt0b29sX3Jlc3VsdBgEIAEoCzIVLm1lY2F0bC52MS5Ub29sUmVzdWx0EhEKCXJlYXNvbmluZxgFIAEoCRIWCg5wcm92aWRlcl9waGFzZRgGIAEoCRIhCgVwYXJ0cxgHIAMoCzISLm1lY2F0bC52MS5Db250ZW50EhkKEXJlYXNvbmluZ19pdGVtX2lkGAggASgJInYKD1NjaGVkdWxlUGF5bG9hZBIVCg1zY2hlZHVsZV9uYW1lGAEgASgJEg8KB2ZpcmVfaWQYAiABKAkSEgoKc2Vzc2lvbl9pZBgDIAEoCRIMCgRraW5kGAQgASgJEgwKBHN0b3AYBSABKAkSCwoDZXJyGAYgASgJIo4BCgRIb29rEg0KBXBoYXNlGAEgASgJEgwKBHRvb2wYAiABKAkSKQoIZGVjaXNpb24YAyABKA4yFy5tZWNhdGwudjEuSG9va0RlY2lzaW9uEg8KB2NhbGxfaWQYBCABKAkSLQoJZ3VhcmRyYWlsGAUgASgLMhoubWVjYXRsLnYxLkd1YXJkcmFpbFJldmlldyKoAgoPUm91dGluZ0RlY2lzaW9uEg8KB2JhY2tlbmQYASABKAkSGAoQY2xhc3NpZmllcl9tb2RlbBgCIAEoCRIaChJjYW5kaWRhdGVfY2F0ZWdvcnkYAyABKAkSFwoPY2FuZGlkYXRlX21vZGVsGAQgASgJEhcKCmNvbmZpZGVuY2UYBSABKAFIAIgBARIfChJtaW5pbXVtX2NvbmZpZGVuY2UYBiABKAFIAYgBARIPCgdvdXRjb21lGAcgASgJEhoKEmNvbnNlY3V0aXZlX21pc3NlcxgIIAEoBRISCgptaXNzX2xpbWl0GAkgASgFEhQKDGJyZWFrZXJfb3BlbhgKIAEoCEINCgtfY29uZmlkZW5jZUIVChNfbWluaW11bV9jb25maWRlbmNlIooECghTdWJhZ2VudBIWCg5wYXJlbnRfY2FsbF9pZBgBIAEoCRIQCghjaGlsZF9pZBgCIAEoCRIMCgRnb2FsGAMgASgJEhEKCXRvb2xfbmFtZRgEIAEoCRIQCghpc19lcnJvchgFIAEoCBISCgp0b29sX2NvdW50GAYgASgFEh8KBXVzYWdlGAcgASgLMhAubWVjYXRsLnYxLlVzYWdlEgwKBHN0b3AYCCABKAkSEwoLZHVyYXRpb25fbXMYCSABKAMSEgoKYmFja2dyb3VuZBgKIAEoCBIXCg9yb3V0ZWRfY2F0ZWdvcnkYCyABKAkSFAoMcm91dGVkX21vZGVsGAwgASgJEg0KBW1vZGVsGA0gASgJEhIKCmlubmVyX2tpbmQYDiABKAkSDAoEdGV4dBgPIAEoCRIOCgZkZXRhaWwYECABKAkSDQoFY2F1c2UYESABKAkSFgoOcm91dGluZ19yZWFzb24YEiABKAkSOQoQcm91dGluZ19kZWNpc2lvbhgTIAEoCzIaLm1lY2F0bC52MS5Sb3V0aW5nRGVjaXNpb25IAIgBARIaChJjaGlsZF90b29sX2NhbGxfaWQYFCABKAkSEAoIcHJvdmlkZXIYFSABKAkSIAoYZXhwbGljaXRfcm91dGVyX2NhdGVnb3J5GBYgASgJQhMKEV9yb3V0aW5nX2RlY2lzaW9uIqYCCg5UZWFtTWVtYmVyU3BlYxIMCgRuYW1lGAEgASgJEgwKBHJvbGUYAiABKAkSEAoIbXV0YXRpbmcYAyABKAgSDAoEbGVhZBgEIAEoCBIXCg9yb3V0ZWRfY2F0ZWdvcnkYBSABKAkSFAoMcm91dGVkX21vZGVsGAYgASgJEg0KBW1vZGVsGAcgASgJEhYKDnJvdXRpbmdfcmVhc29uGAggASgJEjkKEHJvdXRpbmdfZGVjaXNpb24YCSABKAsyGi5tZWNhdGwudjEuUm91dGluZ0RlY2lzaW9uSACIAQESEAoIcHJvdmlkZXIYCiABKAkSIAoYZXhwbGljaXRfcm91dGVyX2NhdGVnb3J5GAsgASgJQhMKEV9yb3V0aW5nX2RlY2lzaW9uIvoDCgRUZWFtEhYKDnBhcmVudF9jYWxsX2lkGAEgASgJEg8KB3RlYW1faWQYAiABKAkSKQoGcm9zdGVyGAMgAygLMhkubWVjYXRsLnYxLlRlYW1NZW1iZXJTcGVjEg4KBm1lbWJlchgEIAEoCRISCgppbm5lcl9raW5kGAUgASgJEgwKBHRleHQYBiABKAkSEQoJdG9vbF9uYW1lGAcgASgJEg4KBmRldGFpbBgIIAEoCRIQCghpc19lcnJvchgJIAEoCBIOCgZyb3VuZHMYCiABKAUSDAoEc3RvcBgLIAEoCRIfCgV1c2FnZRgMIAEoCzIQLm1lY2F0bC52MS5Vc2FnZRIUCgxjb250ZXh0X3VzZWQYDSABKAMSFgoOY29udGV4dF93aW5kb3cYDiABKAMSIgoFdGFza3MYDyADKAsyEy5tZWNhdGwudjEuVGVhbVRhc2sSKAoIZmluZGluZ3MYECADKAsyFi5tZWNhdGwudjEuVGVhbUZpbmRpbmcSNgoMZGlzcG9zaXRpb25zGBEgAygLMiAubWVjYXRsLnYxLlRlYW1NZW1iZXJEaXNwb3NpdGlvbhIZChFtZW1iZXJfc2Vzc2lvbl9pZBgSIAEoCRINCgVjYXVzZRgTIAEoCRIaChJjaGlsZF90b29sX2NhbGxfaWQYFCABKAkijgUKCFBhcmFsbGVsEhYKDnBhcmVudF9jYWxsX2lkGAEgASgJEgwKBGtpbmQYAiABKAkSDAoEam9pbhgDIAEoCRIUCgxicmFuY2hfY291bnQYBCABKAUSFAoMYnJhbmNoX2luZGV4GAUgASgFEhQKDGJyYW5jaF9sYWJlbBgGIAEoCRIMCgRnb2FsGAcgASgJEhEKCXRvb2xfbmFtZRgIIAEoCRIQCghpc19lcnJvchgJIAEoCBISCgp0b29sX2NvdW50GAogASgFEg4KBmZhaWxlZBgLIAEoCBIMCgRzdG9wGA0gASgJEh8KBXVzYWdlGA4gASgLMhAubWVjYXRsLnYxLlVzYWdlEhMKC2R1cmF0aW9uX21zGA8gASgDEg4KBndpbm5lchgQIAEoBRIQCghjaGlsZF9pZBgSIAEoCRIXCg9yb3V0ZWRfY2F0ZWdvcnkYEyABKAkSFAoMcm91dGVkX21vZGVsGBQgASgJEg0KBW1vZGVsGBUgASgJEhIKCmlubmVyX2tpbmQYFiABKAkSDAoEdGV4dBgXIAEoCRIOCgZkZXRhaWwYGCABKAkSFgoOcm91dGluZ19yZWFzb24YGSABKAkSOQoQcm91dGluZ19kZWNpc2lvbhgaIAEoCzIaLm1lY2F0bC52MS5Sb3V0aW5nRGVjaXNpb25IAIgBARIaChJjaGlsZF90b29sX2NhbGxfaWQYGyABKAkSEAoIcHJvdmlkZXIYHCABKAkSIAoYZXhwbGljaXRfcm91dGVyX2NhdGVnb3J5GB0gASgJQhMKEV9yb3V0aW5nX2RlY2lzaW9uSgQIDBANSgQIERASUgl3b3Jrc3BhY2VSEHdpbm5lcl93b3Jrc3BhY2UiMgoIVG9vbENhbGwSCgoCaWQYASABKAkSDAoEbmFtZRgCIAEoCRIMCgRhcmdzGAMgASgJIoUBCgpUb29sUmVzdWx0Eg8KB2NhbGxfaWQYASABKAkSDwoHY29udGVudBgCIAEoCRIQCghpc19lcnJvchgDIAEoCBInCgZibG9ja3MYBCADKAsyFy5tZWNhdGwudjEuQ29udGVudEJsb2NrEhoKEnN0cnVjdHVyZWRfY29udGVudBgFIAEoCSKjAQoNUGVybWlzc2lvbkFzaxIOCgZhc2tfaWQYASABKAkSDAoEdG9vbBgCIAEoCRIMCgRhcmdzGAMgASgJEg4KBnJlYXNvbhgEIAEoCRI0CglndWFyZHJhaWwYBSABKAsyIS5tZWNhdGwudjEuR3VhcmRyYWlsQXBwcm92YWxTY29wZRIUCgdjYWxsX2lkGAYgASgJSACIAQFCCgoIX2NhbGxfaWQihQIKBlJlc3VsdBIMCgRzdG9wGAEgASgJEgwKBHRleHQYAiABKAkSHwoFdXNhZ2UYAyABKAsyEC5tZWNhdGwudjEuVXNhZ2USDQoFZXJyb3IYBCABKAkSOwoRcmV0cnlfZGlzcG9zaXRpb24YBiABKA4yGy5tZWNhdGwudjEuUmV0cnlEaXNwb3NpdGlvbkgAiAEBEjcKD3N0cmVhbV9wcm9ncmVzcxgHIAEoDjIZLm1lY2F0bC52MS5TdHJlYW1Qcm9ncmVzc0gBiAEBQhQKEl9yZXRyeV9kaXNwb3NpdGlvbkISChBfc3RyZWFtX3Byb2dyZXNzSgQIBRAGUglwZXJtYW5lbnQiUgoHVHVybkVuZBIfCgV1c2FnZRgBIAEoCzIQLm1lY2F0bC52MS5Vc2FnZRITCgtkdXJhdGlvbl9tcxgCIAEoAxIRCgllc3RpbWF0ZWQYAyABKAgihQEKBVVzYWdlEhQKDGlucHV0X3Rva2VucxgBIAEoAxIVCg1vdXRwdXRfdG9rZW5zGAIgASgDEhkKEWNhY2hlX3JlYWRfdG9rZW5zGAMgASgDEhoKEmNhY2hlX3dyaXRlX3Rva2VucxgEIAEoAxIYChByZWFzb25pbmdfdG9rZW5zGAUgASgDIpABCgtNY3BSZXNvdXJjZRIOCgZzZXJ2ZXIYASABKAkSCwoDdXJpGAIgASgJEgwKBG5hbWUYAyABKAkSDQoFdGl0bGUYBCABKAkSEwoLZGVzY3JpcHRpb24YBSABKAkSEQoJbWltZV90eXBlGAYgASgJEgwKBHNpemUYByABKAMSEQoJcmVhZF9vbmx5GAggASgIIikKF0xpc3RNY3BSZXNvdXJjZXNSZXF1ZXN0Eg4KBnNlcnZlchgBIAEoCSJFChhMaXN0TWNwUmVzb3VyY2VzUmVzcG9uc2USKQoJcmVzb3VyY2VzGAEgAygLMhYubWVjYXRsLnYxLk1jcFJlc291cmNlIlEKE01jcFJlc291cmNlQ29udGVudHMSCwoDdXJpGAEgASgJEhEKCW1pbWVfdHlwZRgCIAEoCRIMCgR0ZXh0GAMgASgJEgwKBGJsb2IYBCABKAwiRwoWUmVhZE1jcFJlc291cmNlUmVxdWVzdBIXCgZzZXJ2ZXIYASABKAlCB7pIBHICEAESFAoDdXJpGAIgASgJQge6SARyAhABIksKF1JlYWRNY3BSZXNvdXJjZVJlc3BvbnNlEjAKCGNvbnRlbnRzGAEgAygLMh4ubWVjYXRsLnYxLk1jcFJlc291cmNlQ29udGVudHMiVwoRTWNwUHJvbXB0QXJndW1lbnQSDAoEbmFtZRgBIAEoCRINCgV0aXRsZRgCIAEoCRITCgtkZXNjcmlwdGlvbhgDIAEoCRIQCghyZXF1aXJlZBgEIAEoCCJ+CglNY3BQcm9tcHQSDgoGc2VydmVyGAEgASgJEgwKBG5hbWUYAiABKAkSDQoFdGl0bGUYAyABKAkSEwoLZGVzY3JpcHRpb24YBCABKAkSLwoJYXJndW1lbnRzGAUgAygLMhwubWVjYXRsLnYxLk1jcFByb21wdEFyZ3VtZW50IicKFUxpc3RNY3BQcm9tcHRzUmVxdWVzdBIOCgZzZXJ2ZXIYASABKAkiPwoWTGlzdE1jcFByb21wdHNSZXNwb25zZRIlCgdwcm9tcHRzGAEgAygLMhQubWVjYXRsLnYxLk1jcFByb21wdCK5AQoTR2V0TWNwUHJvbXB0UmVxdWVzdBIXCgZzZXJ2ZXIYASABKAlCB7pIBHICEAESFQoEbmFtZRgCIAEoCUIHukgEcgIQARJACglhcmd1bWVudHMYAyADKAsyLS5tZWNhdGwudjEuR2V0TWNwUHJvbXB0UmVxdWVzdC5Bcmd1bWVudHNFbnRyeRowCg5Bcmd1bWVudHNFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAk6AjgBIi4KEE1jcFByb21wdE1lc3NhZ2USDAoEcm9sZRgBIAEoCRIMCgR0ZXh0GAIgASgJIloKFEdldE1jcFByb21wdFJlc3BvbnNlEhMKC2Rlc2NyaXB0aW9uGAEgASgJEi0KCG1lc3NhZ2VzGAIgAygLMhsubWVjYXRsLnYxLk1jcFByb21wdE1lc3NhZ2UiTAoNTWNwU2VydmVySW5mbxIMCgRuYW1lGAEgASgJEgsKA3VybBgCIAEoCRIRCgl0cmFuc3BvcnQYAyABKAkSDQoFZ3JvdXAYBCABKAkihwEKCU1jcFNvdXJjZRIMCgRuYW1lGAEgASgJEgwKBGtpbmQYAiABKAkSDwoHZW5hYmxlZBgDIAEoCBINCgVncm91cBgEIAEoCRIpCgdzZXJ2ZXJzGAUgAygLMhgubWVjYXRsLnYxLk1jcFNlcnZlckluZm8SEwoLZGlhZ25vc3RpY3MYBiADKAkiFwoVTGlzdE1jcFNvdXJjZXNSZXF1ZXN0InUKFkxpc3RNY3BTb3VyY2VzUmVzcG9uc2USJQoHc291cmNlcxgBIAMoCzIULm1lY2F0bC52MS5NY3BTb3VyY2USEAoIcmV2aXNpb24YAiABKAQSDQoFc3RhbGUYAyABKAgSEwoLcmVjb25jaWxpbmcYBCABKAgiNwoYUmVmcmVzaE1jcFNvdXJjZXNSZXF1ZXN0EhsKCnNlc3Npb25faWQYASABKAlCB7pIBHICEAEiPgoZUmVmcmVzaE1jcFNvdXJjZXNSZXNwb25zZRIQCghyZXZpc2lvbhgBIAEoBBIPCgdjaGFuZ2VkGAIgASgIIhsKGUxpc3RUb29sSGl2ZUdyb3Vwc1JlcXVlc3QiLAoaTGlzdFRvb2xIaXZlR3JvdXBzUmVzcG9uc2USDgoGZ3JvdXBzGAEgAygJInQKCUFnZW50SW5mbxIMCgRuYW1lGAEgASgJEhMKC2Rlc2NyaXB0aW9uGAIgASgJEg0KBW1vZGVsGAMgASgJEg0KBXRvb2xzGAQgAygJEhcKD3Blcm1pc3Npb25fbW9kZRgFIAEoCRINCgVjb2xvchgGIAEoCSITChFMaXN0QWdlbnRzUmVxdWVzdCI6ChJMaXN0QWdlbnRzUmVzcG9uc2USJAoGYWdlbnRzGAEgAygLMhQubWVjYXRsLnYxLkFnZW50SW5mbyIsCgdDb21tYW5kEgwKBG5hbWUYASABKAkSEwoLZGVzY3JpcHRpb24YAiABKAkiMgoTTGlzdENvbW1hbmRzUmVxdWVzdBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABIjwKFExpc3RDb21tYW5kc1Jlc3BvbnNlEiQKCGNvbW1hbmRzGAEgAygLMhIubWVjYXRsLnYxLkNvbW1hbmQiaQoIV29ya3RyZWUSEAoIc2VsZWN0b3IYASABKAkSDAoEa2luZBgCIAEoCRINCgVsYWJlbBgDIAEoCRIOCgZicmFuY2gYBCABKAkSEAoIcmV2aXNpb24YBSABKAkSDAoEYmFyZRgGIAEoCCIzChRMaXN0V29ya3RyZWVzUmVxdWVzdBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABIj8KFUxpc3RXb3JrdHJlZXNSZXNwb25zZRImCgl3b3JrdHJlZXMYASADKAsyEy5tZWNhdGwudjEuV29ya3RyZWUicAoJU2tpbGxJbmZvEgwKBG5hbWUYASABKAkSEwoLZGVzY3JpcHRpb24YAiABKAkSEwoLYWdlbnRfb3duZWQYAyABKAgSEwoLb3duZXJfYWdlbnQYBCABKAkSFgoOYWN0aXZlX3ZlcnNpb24YBSABKAkiEwoRTGlzdFNraWxsc1JlcXVlc3QiOgoSTGlzdFNraWxsc1Jlc3BvbnNlEiQKBnNraWxscxgBIAMoCzIULm1lY2F0bC52MS5Ta2lsbEluZm8iQQoNU2tpbGxGaWxlSW5mbxIMCgRuYW1lGAEgASgJEgwKBHNpemUYAiABKAMSFAoMaW5zdHJ1Y3Rpb25zGAMgASgIIi4KFUxpc3RTa2lsbEZpbGVzUmVxdWVzdBIVCgRuYW1lGAEgASgJQge6SARyAhABIkEKFkxpc3RTa2lsbEZpbGVzUmVzcG9uc2USJwoFZmlsZXMYASADKAsyGC5tZWNhdGwudjEuU2tpbGxGaWxlSW5mbyJEChRSZWFkU2tpbGxGaWxlUmVxdWVzdBIVCgRuYW1lGAEgASgJQge6SARyAhABEhUKBGZpbGUYAiABKAlCB7pIBHICEAEiKAoVUmVhZFNraWxsRmlsZVJlc3BvbnNlEg8KB2NvbnRlbnQYASABKAkioQEKCFNvdWxJbmZvEg8KB2NvbnRlbnQYASABKAkSEgoKc2l6ZV9ieXRlcxgCIAEoAxIOCgZzaGEyNTYYAyABKAkSDwoHcHJlc2VudBgEIAEoCBItCgpwcm92ZW5hbmNlGAUgASgOMhkubWVjYXRsLnYxLlNvdWxQcm92ZW5hbmNlEg8KB3RydXN0ZWQYBiABKAgSDwoHZHJpZnRlZBgHIAEoCCIQCg5HZXRTb3VsUmVxdWVzdCI0Cg9HZXRTb3VsUmVzcG9uc2USIQoEc291bBgBIAEoCzITLm1lY2F0bC52MS5Tb3VsSW5mbyIyCg5Vc2VyTW9kZWxFbnRyeRILCgNrZXkYASABKAkSEwoLZGVzY3JpcHRpb24YAiABKAkiIgoTR2V0VXNlck1vZGVsUmVxdWVzdBILCgNrZXkYASABKAkilgIKEVVzZXJNb2RlbFJldmlzaW9uEgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCRITCgtkZXNjcmlwdGlvbhgDIAEoCRIPCgd2ZXJzaW9uGAQgASgJEg4KBnN0YXR1cxgFIAEoCRIOCgZ3cml0ZXIYBiABKAkSDgoGb3JpZ2luGAcgASgJEhkKEXNvdXJjZV9zZXNzaW9uX2lkGAggASgJEhoKEnNvdXJjZV9wcm9wb3NhbF9pZBgJIAEoCRIuCgp1cGRhdGVkX2F0GAsgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcEoECAoQC1IPc291cmNlX2V2ZW50X2lkUhFzb3VyY2VfbWVzc2FnZV9pZCKKAQoPVXNlck1vZGVsRGV0YWlsEi0KB2N1cnJlbnQYASABKAsyHC5tZWNhdGwudjEuVXNlck1vZGVsUmV2aXNpb24SLQoHaGlzdG9yeRgCIAMoCzIcLm1lY2F0bC52MS5Vc2VyTW9kZWxSZXZpc2lvbhIZChFoaXN0b3J5X2F2YWlsYWJsZRgDIAEoCCKSAQoUR2V0VXNlck1vZGVsUmVzcG9uc2USKgoHZW50cmllcxgBIAMoCzIZLm1lY2F0bC52MS5Vc2VyTW9kZWxFbnRyeRISCgpzaXplX2J5dGVzGAIgASgDEg4KBnNoYTI1NhgDIAEoCRIqCgZkZXRhaWwYBCABKAsyGi5tZWNhdGwudjEuVXNlck1vZGVsRGV0YWlsIpIBCglNb2RlbEluZm8SCgoCaWQYASABKAkSEwoLcHJvdmlkZXJfaWQYAiABKAkSFAoMZGlzcGxheV9uYW1lGAMgASgJEg0KBWltYWdlGAQgASgIEhEKCXJlYXNvbmluZxgFIAEoCBIVCg1jb250ZXh0X2xpbWl0GAYgASgDEhUKDXByb21wdF9jYWNoZWQYByABKAgiEwoRTGlzdE1vZGVsc1JlcXVlc3QimwEKDlByb3ZpZGVyU3RhdHVzEhMKC3Byb3ZpZGVyX2lkGAEgASgJEg0KBXN0YXRlGAIgASgJEgwKBGhpbnQYAyABKAkSIwobZGVmYXVsdF9tb2RlbF9hdXRvX3NlbGVjdGVkGAQgASgIEhMKC21vZGVsX2NvdW50GAUgASgFEh0KFWF2YWlsYWJsZV9ub3RfZGVmYXVsdBgGIAEoCCJuChJMaXN0TW9kZWxzUmVzcG9uc2USJAoGbW9kZWxzGAEgAygLMhQubWVjYXRsLnYxLk1vZGVsSW5mbxIyCg9wcm92aWRlcl9zdGF0dXMYAiADKAsyGS5tZWNhdGwudjEuUHJvdmlkZXJTdGF0dXMiQQoVUmVmbGVjdFNlc3Npb25SZXF1ZXN0EhsKCnNlc3Npb25faWQYASABKAlCB7pIBHICEAFKBAgCEANSBWFzeW5jIrkBChFSZWZsZWN0aW9uUmVjZWlwdBIVCg1yZWZsZWN0aW9uX2lkGAEgASgJEhMKC2Rpc3Bvc2l0aW9uGAIgASgJEg4KBnF1ZXVlZBgDIAEoBRIRCglhYnN0YWluZWQYBCABKAgSDgoGc3RhZ2VkGAUgASgFEhAKCHByb21vdGVkGAYgASgFEhIKCmNvbmZsaWN0ZWQYByABKAUSDgoGcmVhc29uGAggASgJEg8KB21lc3NhZ2UYCSABKAkiRwoWUmVmbGVjdFNlc3Npb25SZXNwb25zZRItCgdyZWNlaXB0GAEgASgLMhwubWVjYXRsLnYxLlJlZmxlY3Rpb25SZWNlaXB0IvECCg9MZWFybmluZ0F0dGVtcHQSCgoCaWQYASABKAkSDwoHdmVyc2lvbhgCIAEoCRINCgVzdGF0ZRgDIAEoCRIPCgdvdXRjb21lGAQgASgJEhQKDGZhaWx1cmVfY29kZRgFIAEoCRIaChJhdHRlbXB0X2dlbmVyYXRpb24YBiABKAQSGAoQY2xhaW1fZ2VuZXJhdGlvbhgHIAEoBBI0ChBjbGFpbV9leHBpcmVzX2F0GAggASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIYChBjaGVja3BvaW50X3N0YWdlGAkgASgJEhMKC3Byb3Bvc2FsX2lkGAogASgJEhAKCHNraWxsX2lkGAsgASgJEi4KCmNyZWF0ZWRfYXQYDCABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi4KCnVwZGF0ZWRfYXQYDSABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wIjAKGUdldExlYXJuaW5nQXR0ZW1wdFJlcXVlc3QSEwoCaWQYASABKAlCB7pIBHICEAEiSQoaR2V0TGVhcm5pbmdBdHRlbXB0UmVzcG9uc2USKwoHYXR0ZW1wdBgBIAEoCzIaLm1lY2F0bC52MS5MZWFybmluZ0F0dGVtcHQiSwobTGlzdExlYXJuaW5nQXR0ZW1wdHNSZXF1ZXN0Eg0KBXN0YXRlGAEgASgJEg4KBmN1cnNvchgCIAEoCRINCgVsaW1pdBgDIAEoBSJhChxMaXN0TGVhcm5pbmdBdHRlbXB0c1Jlc3BvbnNlEiwKCGF0dGVtcHRzGAEgAygLMhoubWVjYXRsLnYxLkxlYXJuaW5nQXR0ZW1wdBITCgtuZXh0X2N1cnNvchgCIAEoCSJWChxNdXRhdGVMZWFybmluZ0F0dGVtcHRSZXF1ZXN0EhMKAmlkGAEgASgJQge6SARyAhABEiEKEGV4cGVjdGVkX3ZlcnNpb24YAiABKAlCB7pIBHICEAEiTAodTXV0YXRlTGVhcm5pbmdBdHRlbXB0UmVzcG9uc2USKwoHYXR0ZW1wdBgBIAEoCzIaLm1lY2F0bC52MS5MZWFybmluZ0F0dGVtcHQiXgocTGlzdExlYXJuaW5nUHJvcG9zYWxzUmVxdWVzdBIOCgZzdGF0dXMYASABKAkSDgoGY3Vyc29yGAIgASgJEg0KBWxpbWl0GAMgASgFEg8KB3Byb2plY3QYBCABKAkivgEKE0xlYXJuaW5nRXZpZGVuY2VSZWYSEgoKc2Vzc2lvbl9pZBgBIAEoCRIPCgdsb2NhdG9yGAIgASgJEg8KB29yZGluYWwYAyABKAUSEQoJZXZlbnRfc2VxGAQgASgDEhQKDHRvb2xfY2FsbF9pZBgFIAEoCRIOCgZkaWdlc3QYBiABKAkSEQoJYXZhaWxhYmxlGAcgASgIEhQKDGF2YWlsYWJpbGl0eRgIIAEoCRIPCgdwcmV2aWV3GAkgASgJImcKEExlYXJuaW5nRGVjaXNpb24SDAoEa2luZBgBIAEoCRINCgVhY3RvchgCIAEoCRIOCgZyZWFzb24YAyABKAkSJgoCYXQYBCABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wInkKGExlYXJuaW5nUHJvbW90aW9uUmVjZWlwdBISCgptZW1vcnlfa2V5GAEgASgJEhcKD3ByZXZpb3VzX2V4aXN0cxgCIAEoCBIYChBwcmV2aW91c192ZXJzaW9uGAMgASgJEhYKDnJlc3VsdF92ZXJzaW9uGAQgASgJIpwEChBMZWFybmluZ1Byb3Bvc2FsEgoKAmlkGAEgASgJEg8KB3ZlcnNpb24YAiABKAkSDgoGc3RhdHVzGAMgASgJEgwKBGtpbmQYBCABKAkSCwoDa2V5GAUgASgJEg0KBXZhbHVlGAYgASgJEhMKC2Rlc2NyaXB0aW9uGAcgASgJEg0KBXRpdGxlGAggASgJEgwKBGJvZHkYCSABKAkSMAoIZXZpZGVuY2UYCiADKAsyHi5tZWNhdGwudjEuTGVhcm5pbmdFdmlkZW5jZVJlZhIQCgh0cmlnZ2VycxgLIAMoCRIuCglkZWNpc2lvbnMYDCADKAsyGy5tZWNhdGwudjEuTGVhcm5pbmdEZWNpc2lvbhI2Cglwcm9tb3Rpb24YDSABKAsyIy5tZWNhdGwudjEuTGVhcm5pbmdQcm9tb3Rpb25SZWNlaXB0Ei4KCmNyZWF0ZWRfYXQYDiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi4KCnVwZGF0ZWRfYXQYDyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEhYKDnByb2plY3Rfc2NvcGVkGBAgASgIEhsKE3Byb21vdGlvbl9hdmFpbGFibGUYESABKAgSJAoccHJvbW90aW9uX3VuYXZhaWxhYmxlX3JlYXNvbhgSIAEoCRIYChBsZWFybmVkX3NraWxsX2lkGBMgASgJImQKHUxpc3RMZWFybmluZ1Byb3Bvc2Fsc1Jlc3BvbnNlEi4KCXByb3Bvc2FscxgBIAMoCzIbLm1lY2F0bC52MS5MZWFybmluZ1Byb3Bvc2FsEhMKC25leHRfY3Vyc29yGAIgASgJIkIKGkdldExlYXJuaW5nUHJvcG9zYWxSZXF1ZXN0EhMKAmlkGAEgASgJQge6SARyAhABEg8KB3Byb2plY3QYAiABKAkiTAobR2V0TGVhcm5pbmdQcm9wb3NhbFJlc3BvbnNlEi0KCHByb3Bvc2FsGAEgASgLMhsubWVjYXRsLnYxLkxlYXJuaW5nUHJvcG9zYWwikwEKHURlY2lkZUxlYXJuaW5nUHJvcG9zYWxSZXF1ZXN0EhMKAmlkGAEgASgJQge6SARyAhABEiEKEGV4cGVjdGVkX3ZlcnNpb24YAiABKAlCB7pIBHICEAESGQoIZGVjaXNpb24YAyABKAlCB7pIBHICEAESDgoGcmVhc29uGAQgASgJEg8KB3Byb2plY3QYBSABKAkiTwoeRGVjaWRlTGVhcm5pbmdQcm9wb3NhbFJlc3BvbnNlEi0KCHByb3Bvc2FsGAEgASgLMhsubWVjYXRsLnYxLkxlYXJuaW5nUHJvcG9zYWwiZwocVW5kb0xlYXJuaW5nUHJvbW90aW9uUmVxdWVzdBITCgJpZBgBIAEoCUIHukgEcgIQARIhChBleHBlY3RlZF92ZXJzaW9uGAIgASgJQge6SARyAhABEg8KB3Byb2plY3QYAyABKAkiTgodVW5kb0xlYXJuaW5nUHJvbW90aW9uUmVzcG9uc2USLQoIcHJvcG9zYWwYASABKAsyGy5tZWNhdGwudjEuTGVhcm5pbmdQcm9wb3NhbCLsAwoTTGVhcm5lZFNraWxsVmVyc2lvbhIKCgJpZBgBIAEoCRIMCgRuYW1lGAIgASgJEg8KB3ZlcnNpb24YAyABKAkSEAoIcmV2aXNpb24YBCABKAkSDQoFc3RhdGUYBSABKAkSEwoLb3duZXJfYWdlbnQYBiABKAkSEwoLZGVzY3JpcHRpb24YByABKAkSDAoEYm9keRgIIAEoCRISCgpzdXBlcnNlZGVzGAkgASgJEhYKDmV2aWRlbmNlX2NvdW50GAogASgFEjAKCGV2aWRlbmNlGAsgAygLMh4ubWVjYXRsLnYxLkxlYXJuaW5nRXZpZGVuY2VSZWYSLwoLZXZhbHVhdGlvbnMYDCADKAsyGi5tZWNhdGwudjEuU2tpbGxFdmFsdWF0aW9uEi8KCHJlY2VpcHRzGA0gAygLMh0ubWVjYXRsLnYxLlNraWxsQ2hhbmdlUmVjZWlwdBIuCgpjcmVhdGVkX2F0GA4gASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIuCgp1cGRhdGVkX2F0GA8gASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIZChFpbnNwZWN0X2F2YWlsYWJsZRgQIAEoCBIWCg51bmRvX2F2YWlsYWJsZRgRIAEoCCKUAQoPU2tpbGxFdmFsdWF0aW9uEg8KB3ZlcmRpY3QYASABKAkSEwoLZml4dHVyZV9pZHMYAiADKAkSEAoIYmFzZWxpbmUYAyABKAkSEQoJdHJlYXRtZW50GAQgASgJEg4KBnJlYXNvbhgFIAEoCRImCgJhdBgGIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXAiggMKElNraWxsQ2hhbmdlUmVjZWlwdBIKCgJpZBgBIAEoCRIQCghza2lsbF9pZBgCIAEoCRIMCgRuYW1lGAMgASgJEg8KB3ZlcnNpb24YBCABKAkSEQoJb3BlcmF0aW9uGAUgASgJEhIKCmZyb21fc3RhdGUYBiABKAkSEAoIdG9fc3RhdGUYByABKAkSFgoOZXZpZGVuY2VfY291bnQYCCABKAUSGgoSc291cmNlX3Nlc3Npb25faWRzGAkgAygJEhwKFHNvdXJjZV90b29sX2NhbGxfaWRzGAogAygJEg8KB3ZlcmRpY3QYCyABKAkSEwoLZml4dHVyZV9pZHMYDCADKAkSEAoIYmFzZWxpbmUYDSABKAkSEQoJdHJlYXRtZW50GA4gASgJEiYKAmF0GA8gASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIZChFpbnNwZWN0X2F2YWlsYWJsZRgQIAEoCBIWCg51bmRvX2F2YWlsYWJsZRgRIAEoCCJuChhMaXN0TGVhcm5lZFNraWxsc1JlcXVlc3QSDwoHcHJvamVjdBgBIAEoCRIOCgZjdXJzb3IYAiABKAkSDQoFbGltaXQYAyABKAUSDQoFc3RhdGUYBCABKAkSEwoLb3duZXJfYWdlbnQYBSABKAkihQEKGUxpc3RMZWFybmVkU2tpbGxzUmVzcG9uc2USLgoGc2tpbGxzGAEgAygLMh4ubWVjYXRsLnYxLkxlYXJuZWRTa2lsbFZlcnNpb24SEwoLbmV4dF9jdXJzb3IYAiABKAkSEgoKZ2VuZXJhdGlvbhgDIAEoBBIPCgdwcm9qZWN0GAQgASgJIm0KFkdldExlYXJuZWRTa2lsbFJlcXVlc3QSDwoHcHJvamVjdBgBIAEoCRIcCgtvd25lcl9hZ2VudBgCIAEoCUIHukgEcgIQARITCgJpZBgDIAEoCUIHukgEcgIQARIPCgd2ZXJzaW9uGAQgASgJIm0KF0dldExlYXJuZWRTa2lsbFJlc3BvbnNlEi0KBXNraWxsGAEgASgLMh4ubWVjYXRsLnYxLkxlYXJuZWRTa2lsbFZlcnNpb24SEgoKZ2VuZXJhdGlvbhgCIAEoBBIPCgdwcm9qZWN0GAMgASgJIqEBCh9EaWZmTGVhcm5lZFNraWxsVmVyc2lvbnNSZXF1ZXN0Eg8KB3Byb2plY3QYASABKAkSHAoLb3duZXJfYWdlbnQYAiABKAlCB7pIBHICEAESEwoCaWQYAyABKAlCB7pIBHICEAESHQoMZnJvbV92ZXJzaW9uGAQgASgJQge6SARyAhABEhsKCnRvX3ZlcnNpb24YBSABKAlCB7pIBHICEAEikQEKIERpZmZMZWFybmVkU2tpbGxWZXJzaW9uc1Jlc3BvbnNlEgwKBGRpZmYYASABKAkSEgoKZ2VuZXJhdGlvbhgCIAEoBBIPCgdwcm9qZWN0GAMgASgJEhAKCHNraWxsX2lkGAQgASgJEhQKDGZyb21fdmVyc2lvbhgFIAEoCRISCgp0b192ZXJzaW9uGAYgASgJIp0BChlNdXRhdGVMZWFybmVkU2tpbGxSZXF1ZXN0Eg8KB3Byb2plY3QYASABKAkSHAoLb3duZXJfYWdlbnQYAiABKAlCB7pIBHICEAESEwoCaWQYAyABKAlCB7pIBHICEAESGAoHdmVyc2lvbhgEIAEoCUIHukgEcgIQARIiChFleHBlY3RlZF9yZXZpc2lvbhgFIAEoCUIHukgEcgIQASKnAQoaTXV0YXRlTGVhcm5lZFNraWxsUmVzcG9uc2USLQoFc2tpbGwYASABKAsyHi5tZWNhdGwudjEuTGVhcm5lZFNraWxsVmVyc2lvbhISCgpnZW5lcmF0aW9uGAIgASgEEg8KB3Byb2plY3QYAyABKAkSGgoScHVibGljYXRpb25fc3RhdHVzGAQgASgJEhkKEXB1YmxpY2F0aW9uX2Vycm9yGAUgASgJIqYBChtSb2xsYmFja0xlYXJuZWRTa2lsbFJlcXVlc3QSDwoHcHJvamVjdBgBIAEoCRIcCgtvd25lcl9hZ2VudBgCIAEoCUIHukgEcgIQARITCgJpZBgDIAEoCUIHukgEcgIQARIfCg50YXJnZXRfdmVyc2lvbhgEIAEoCUIHukgEcgIQARIiChFleHBlY3RlZF9yZXZpc2lvbhgFIAEoCUIHukgEcgIQASJJChdMaXN0U2tpbGxDaGFuZ2VzUmVxdWVzdBIPCgdwcm9qZWN0GAEgASgJEg4KBmN1cnNvchgCIAEoCRINCgVsaW1pdBgDIAEoBSKEAQoYTGlzdFNraWxsQ2hhbmdlc1Jlc3BvbnNlEi4KB2NoYW5nZXMYASADKAsyHS5tZWNhdGwudjEuU2tpbGxDaGFuZ2VSZWNlaXB0EhMKC25leHRfY3Vyc29yGAIgASgJEhIKCmdlbmVyYXRpb24YAyABKAQSDwoHcHJvamVjdBgEIAEoCSKPAQoRQ3JlYXRlVGVhbVJlcXVlc3QSGwoKc2Vzc2lvbl9pZBgBIAEoCUIHukgEcgIQARIMCgRuYW1lGAIgASgJEigKB21lbWJlcnMYAyADKAsyFy5tZWNhdGwudjEuVGVhbW1hdGVTcGVjEgwKBGdvYWwYBCABKAkSFwoPbWF4X3RlYW1fdG9rZW5zGAUgASgFIk0KEkNyZWF0ZVRlYW1SZXNwb25zZRIPCgd0ZWFtX2lkGAEgASgJEiYKB21lbWJlcnMYAiADKAsyFS5tZWNhdGwudjEuVGVhbU1lbWJlciJxCgxUZWFtbWF0ZVNwZWMSFQoEbmFtZRgBIAEoCUIHukgEcgIQARISCgphZ2VudF90eXBlGAIgASgJEgwKBGxlYWQYAyABKAgSEAoIbXV0YXRpbmcYBCABKAgSFgoOaW5pdGlhbF9wcm9tcHQYBSABKAkikwEKFFNwYXduVGVhbW1hdGVSZXF1ZXN0EhgKB3RlYW1faWQYASABKAlCB7pIBHICEAESFQoEbmFtZRgCIAEoCUIHukgEcgIQARISCgphZ2VudF90eXBlGAMgASgJEgwKBGxlYWQYBCABKAgSEAoIbXV0YXRpbmcYBSABKAgSFgoOaW5pdGlhbF9wcm9tcHQYBiABKAkiPgoVU3Bhd25UZWFtbWF0ZVJlc3BvbnNlEiUKBm1lbWJlchgBIAEoCzIVLm1lY2F0bC52MS5UZWFtTWVtYmVyInAKGlNlbmRUZWFtbWF0ZU1lc3NhZ2VSZXF1ZXN0EhgKB3RlYW1faWQYASABKAlCB7pIBHICEAESEwoCdG8YAiABKAlCB7pIBHICEAESDAoEZnJvbRgDIAEoCRIVCgRib2R5GAQgASgJQge6SARyAhABIh0KG1NlbmRUZWFtbWF0ZU1lc3NhZ2VSZXNwb25zZSJKChVDYW5jZWxUZWFtbWF0ZVJlcXVlc3QSGAoHdGVhbV9pZBgBIAEoCUIHukgEcgIQARIXCgZtZW1iZXIYAiABKAlCB7pIBHICEAEiGAoWQ2FuY2VsVGVhbW1hdGVSZXNwb25zZSIqCg5SdW5UZWFtUmVxdWVzdBIYCgd0ZWFtX2lkGAEgASgJQge6SARyAhABImUKCVRlYW1FdmVudBIOCgZtZW1iZXIYASABKAkSHwoFZXZlbnQYAiABKAsyEC5tZWNhdGwudjEuRXZlbnQSJwoHb3V0Y29tZRgDIAEoCzIWLm1lY2F0bC52MS5UZWFtT3V0Y29tZSLbAQoLVGVhbU91dGNvbWUSDgoGcm91bmRzGAEgASgFEhEKCXF1aWVzY2VudBgCIAEoCBIYChBidWRnZXRfZXhoYXVzdGVkGAMgASgIEgwKBHN0b3AYBCABKAkSHwoFdXNhZ2UYBSABKAsyEC5tZWNhdGwudjEuVXNhZ2USNgoMZGlzcG9zaXRpb25zGAYgAygLMiAubWVjYXRsLnYxLlRlYW1NZW1iZXJEaXNwb3NpdGlvbhIoCghmaW5kaW5ncxgHIAMoCzIWLm1lY2F0bC52MS5UZWFtRmluZGluZyIrCg9MaXN0VGVhbVJlcXVlc3QSGAoHdGVhbV9pZBgBIAEoCUIHukgEcgIQASJxChBMaXN0VGVhbVJlc3BvbnNlEiYKB21lbWJlcnMYASADKAsyFS5tZWNhdGwudjEuVGVhbU1lbWJlchIiCgV0YXNrcxgCIAMoCzITLm1lY2F0bC52MS5UZWFtVGFzaxIRCglxdWllc2NlbnQYAyABKAgiUQoKVGVhbU1lbWJlchIMCgRuYW1lGAEgASgJEhIKCmFnZW50X3R5cGUYAiABKAkSDQoFc3RhdGUYAyABKAkSEgoKc2Vzc2lvbl9pZBgEIAEoCSJaCghUZWFtVGFzaxIKCgJpZBgBIAEoCRITCgtkZXNjcmlwdGlvbhgCIAEoCRINCgVzdGF0ZRgDIAEoCRIQCghhc3NpZ25lZRgEIAEoCRIMCgRkZXBzGAUgAygJIisKC1RlYW1GaW5kaW5nEg4KBm1lbWJlchgBIAEoCRIMCgRib2R5GAIgASgJIn0KFVRlYW1NZW1iZXJEaXNwb3NpdGlvbhIMCgRuYW1lGAEgASgJEg8KB3N0b3BwZWQYAiABKAgSLwoGcmVhc29uGAMgASgOMh8ubWVjYXRsLnYxLlRlYW1NZW1iZXJTdG9wUmVhc29uEhQKDGVycm9yX3JvdW5kcxgEIAEoBSIuChJDbGVhbnVwVGVhbVJlcXVlc3QSGAoHdGVhbV9pZBgBIAEoCUIHukgEcgIQASIVChNDbGVhbnVwVGVhbVJlc3BvbnNlIm8KEkFwcHJvdmVQbGFuUmVxdWVzdBIbCgpzZXNzaW9uX2lkGAEgASgJQge6SARyAhABEi4KC3RhcmdldF9tb2RlGAIgASgOMhkubWVjYXRsLnYxLlBlcm1pc3Npb25Nb2RlEgwKBG5vdGUYAyABKAkiiQEKF01hbnVhbERyZWFtQ2FwYWJpbGl0aWVzEjgKDnByb2plY3RfbWVtb3J5GAEgASgLMiAubWVjYXRsLnYxLkRyZWFtVGFyZ2V0Q2FwYWJpbGl0eRI0Cgp1c2VyX21vZGVsGAIgASgLMiAubWVjYXRsLnYxLkRyZWFtVGFyZ2V0Q2FwYWJpbGl0eSJVChVEcmVhbVRhcmdldENhcGFiaWxpdHkSEAoIZ2VuZXJhdGUYASABKAgSDgoGZGVjaWRlGAIgASgIEhoKEnVuYXZhaWxhYmxlX3JlYXNvbhgDIAEoCSIqChhHZW5lcmF0ZURyZWFtUGxhblJlcXVlc3QSDgoGdGFyZ2V0GAEgASgJIkUKGUdlbmVyYXRlRHJlYW1QbGFuUmVzcG9uc2USKAoEcGxhbhgBIAEoCzIaLm1lY2F0bC52MS5EcmVhbVJldmlld1BsYW4iOwoWRGVjaWRlRHJlYW1QbGFuUmVxdWVzdBIPCgdwbGFuX2lkGAEgASgJEhAKCGRlY2lzaW9uGAIgASgJIkMKF0RlY2lkZURyZWFtUGxhblJlc3BvbnNlEigKB3JlY2VpcHQYASABKAsyFy5tZWNhdGwudjEuRHJlYW1SZWNlaXB0IssBCg9EcmVhbVJldmlld1BsYW4SCgoCaWQYASABKAkSDgoGdGFyZ2V0GAIgASgJEi4KCmV4cGlyZXNfYXQYAyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEh8KF3BsYW5uZWRfb3BlcmF0aW9uX2NvdW50GAQgASgFEhwKFHBsYW5uZWRfc291cmNlX2NvdW50GAUgASgFEi0KCm9wZXJhdGlvbnMYBiADKAsyGS5tZWNhdGwudjEuRHJlYW1PcGVyYXRpb24i3wEKDkRyZWFtT3BlcmF0aW9uEgwKBGtpbmQYASABKAkSLQoIc3Vydml2b3IYAiABKAsyGy5tZWNhdGwudjEuRHJlYW1QYXJ0aWNpcGFudBIsCgdzb3VyY2VzGAMgAygLMhsubWVjYXRsLnYxLkRyZWFtUGFydGljaXBhbnQSMAoLcmVwbGFjZW1lbnQYBCABKAsyGy5tZWNhdGwudjEuRHJlYW1SZXBsYWNlbWVudBIOCgZyZWFzb24YBSABKAkSIAoYZXhhY3RfZHVwbGljYXRlX2VsaWdpYmxlGAYgASgIIkMKEERyZWFtUGFydGljaXBhbnQSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJEhMKC2Rlc2NyaXB0aW9uGAMgASgJIjYKEERyZWFtUmVwbGFjZW1lbnQSDQoFdmFsdWUYASABKAkSEwoLZGVzY3JpcHRpb24YAiABKAki1wEKDERyZWFtUmVjZWlwdBIKCgJpZBgBIAEoCRIOCgZ0YXJnZXQYAiABKAkSEwoLZGlzcG9zaXRpb24YAyABKAkSHAoUcGxhbm5lZF9zb3VyY2VfY291bnQYBCABKAUSHAoUYXBwbGllZF9zb3VyY2VfY291bnQYBSABKAUSHwoXY29uZmxpY3RlZF9zb3VyY2VfY291bnQYBiABKAUSHAoUc2tpcHBlZF9zb3VyY2VfY291bnQYByABKAUSGwoTZmFpbGVkX3NvdXJjZV9jb3VudBgIIAEoBSI0ChVDb21wYWN0U2Vzc2lvblJlcXVlc3QSGwoKc2Vzc2lvbl9pZBgBIAEoCUIHukgEcgIQASIrChZDb21wYWN0U2Vzc2lvblJlc3BvbnNlEhEKCWNvbXBhY3RlZBgBIAEoCCI3CiFXb3Jrc3BhY2VFbnJvbGxtZW50Q29ubmVjdFJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCSJOCiFXb3Jrc3BhY2VFbnJvbGxtZW50Q29udHJvbFJlcXVlc3QSEgoKc2Vzc2lvbl9pZBgBIAEoCRIVCg1lbnJvbGxtZW50X2lkGAIgASgJInEKE1dvcmtzcGFjZUVucm9sbG1lbnQSFQoNZW5yb2xsbWVudF9pZBgBIAEoCRIOCgZzdGF0dXMYAiABKAkSGQoRcmVxdWlyZWRfc2VydmljZXMYAyABKA0SGAoQcHJlc2VudGF0aW9uX3VybBgEIAEoCSq+AQoOUGVybWlzc2lvbk1vZGUSHwobUEVSTUlTU0lPTl9NT0RFX1VOU1BFQ0lGSUVEEAASGwoXUEVSTUlTU0lPTl9NT0RFX0RFRkFVTFQQARIYChRQRVJNSVNTSU9OX01PREVfUExBThACEiAKHFBFUk1JU1NJT05fTU9ERV9BQ0NFUFRfRURJVFMQAxIYChRQRVJNSVNTSU9OX01PREVfQVVUTxAEEhgKFFBFUk1JU1NJT05fTU9ERV9ZT0xPEAUqkgEKD0FwcHJvdmFsVmVyZGljdBIgChxBUFBST1ZBTF9WRVJESUNUX1VOU1BFQ0lGSUVEEAASGQoVQVBQUk9WQUxfVkVSRElDVF9ERU5ZEAESHwobQVBQUk9WQUxfVkVSRElDVF9BTExPV19PTkNFEAISIQodQVBQUk9WQUxfVkVSRElDVF9BTExPV19BTFdBWVMQAyq+AQoMU3RlZXJPdXRjb21lEh0KGVNURUVSX09VVENPTUVfVU5TUEVDSUZJRUQQABIaChZTVEVFUl9PVVRDT01FX0FDQ0VQVEVEEAESGgoWU1RFRVJfT1VUQ09NRV9BUFBFTkRFRBACEhsKF1NURUVSX09VVENPTUVfUkVUUkFDVEVEEAMSHgoaU1RFRVJfT1VUQ09NRV9OT05FX1BFTkRJTkcQBBIaChZTVEVFUl9PVVRDT01FX1RPT19MQVRFEAUqgAEKDEd1YXJkcmFpbEpvYhIdChlHVUFSRFJBSUxfSk9CX1VOU1BFQ0lGSUVEEAASGAoUR1VBUkRSQUlMX0pPQl9BQ1RJT04QARIZChVHVUFSRFJBSUxfSk9CX0lOQk9VTkQQAhIcChhHVUFSRFJBSUxfSk9CX1BFUk1JU1NJT04QAyqqAQoTR3VhcmRyYWlsQXNzZXNzbWVudBIkCiBHVUFSRFJBSUxfQVNTRVNTTUVOVF9VTlNQRUNJRklFRBAAEiMKH0dVQVJEUkFJTF9BU1NFU1NNRU5UX0FDQ0VQVEFCTEUQARIjCh9HVUFSRFJBSUxfQVNTRVNTTUVOVF9QUk9ISUJJVEVEEAISIwofR1VBUkRSQUlMX0FTU0VTU01FTlRfVU5SRVNPTFZFRBADKowBChNHdWFyZHJhaWxJbnNwZWN0aW9uEiQKIEdVQVJEUkFJTF9JTlNQRUNUSU9OX1VOU1BFQ0lGSUVEEAASIQodR1VBUkRSQUlMX0lOU1BFQ1RJT05fQ09NUExFVEUQARIsCihHVUFSRFJBSUxfSU5TUEVDVElPTl9PUEVSQVRJT05BTF9GQUlMVVJFEAIq0AIKFEd1YXJkcmFpbERpc3Bvc2l0aW9uEiUKIUdVQVJEUkFJTF9ESVNQT1NJVElPTl9VTlNQRUNJRklFRBAAEiEKHUdVQVJEUkFJTF9ESVNQT1NJVElPTl9FWEVDVVRFEAESJAogR1VBUkRSQUlMX0RJU1BPU0lUSU9OX0FTS19BQ1RJT04QAhIpCiVHVUFSRFJBSUxfRElTUE9TSVRJT05fV0lUSEhPTERfUkVTVUxUEAMSKAokR1VBUkRSQUlMX0RJU1BPU0lUSU9OX1JFTEVBU0VfUkVTVUxUEAQSHgoaR1VBUkRSQUlMX0RJU1BPU0lUSU9OX0RFTlkQBRInCiNHVUFSRFJBSUxfRElTUE9TSVRJT05fUEFTU19BRFZJU09SWRAGEioKJkdVQVJEUkFJTF9ESVNQT1NJVElPTl9DT05USU5VRV9XQVJOSU5HEAcqkAEKFUd1YXJkcmFpbEFwcHJvdmFsS2luZBInCiNHVUFSRFJBSUxfQVBQUk9WQUxfS0lORF9VTlNQRUNJRklFRBAAEiIKHkdVQVJEUkFJTF9BUFBST1ZBTF9LSU5EX0FDVElPThABEioKJkdVQVJEUkFJTF9BUFBST1ZBTF9LSU5EX1JFU1VMVF9SRUxFQVNFEAIqmAEKDEhvb2tEZWNpc2lvbhIdChlIT09LX0RFQ0lTSU9OX1VOU1BFQ0lGSUVEEAASFgoSSE9PS19ERUNJU0lPTl9JTkZPEAESGQoVSE9PS19ERUNJU0lPTl9CTE9DS0VEEAISGgoWSE9PS19ERUNJU0lPTl9NT0RJRklFRBADEhoKFkhPT0tfREVDSVNJT05fQURWSVNPUlkQBCqWAQoQUmV0cnlEaXNwb3NpdGlvbhIhCh1SRVRSWV9ESVNQT1NJVElPTl9VTlNQRUNJRklFRBAAEh0KGVJFVFJZX0RJU1BPU0lUSU9OX1VOS05PV04QARIfChtSRVRSWV9ESVNQT1NJVElPTl9SRVRSWUFCTEUQAhIfChtSRVRSWV9ESVNQT1NJVElPTl9QRVJNQU5FTlQQAyqoAQoOU3RyZWFtUHJvZ3Jlc3MSHwobU1RSRUFNX1BST0dSRVNTX1VOU1BFQ0lGSUVEEAASGwoXU1RSRUFNX1BST0dSRVNTX1VOS05PV04QARIdChlTVFJFQU1fUFJPR1JFU1NfUFJFQ09NTUlUEAISGwoXU1RSRUFNX1BST0dSRVNTX1ZJU0lCTEUQAxIcChhTVFJFQU1fUFJPR1JFU1NfQ09NUExFVEUQBCqEAQoOU291bFByb3ZlbmFuY2USHwobU09VTF9QUk9WRU5BTkNFX1VOU1BFQ0lGSUVEEAASGAoUU09VTF9QUk9WRU5BTkNFX1VTRVIQARIbChdTT1VMX1BST1ZFTkFOQ0VfUFJPSkVDVBACEhoKFlNPVUxfUFJPVkVOQU5DRV9EUklWRVIQAyqtAQoUVGVhbU1lbWJlclN0b3BSZWFzb24SJwojVEVBTV9NRU1CRVJfU1RPUF9SRUFTT05fVU5TUEVDSUZJRUQQABIhCh1URUFNX01FTUJFUl9TVE9QX1JFQVNPTl9FUlJPUhABEiUKIVRFQU1fTUVNQkVSX1NUT1BfUkVBU09OX0NBTkNFTExFRBACEiIKHlRFQU1fTUVNQkVSX1NUT1BfUkVBU09OX0JVREdFVBADMpw5Cg5IYXJuZXNzU2VydmljZRJnChRHZXRDb21wYXRpYmlsaXR5SW5mbxImLm1lY2F0bC52MS5HZXRDb21wYXRpYmlsaXR5SW5mb1JlcXVlc3QaJy5tZWNhdGwudjEuR2V0Q29tcGF0aWJpbGl0eUluZm9SZXNwb25zZRJtChZMaXN0RXhlY3V0aW9uVGVtcGxhdGVzEigubWVjYXRsLnYxLkxpc3RFeGVjdXRpb25UZW1wbGF0ZXNSZXF1ZXN0GikubWVjYXRsLnYxLkxpc3RFeGVjdXRpb25UZW1wbGF0ZXNSZXNwb25zZRJSCg1DcmVhdGVTZXNzaW9uEh8ubWVjYXRsLnYxLkNyZWF0ZVNlc3Npb25SZXF1ZXN0GiAubWVjYXRsLnYxLkNyZWF0ZVNlc3Npb25SZXNwb25zZRJSCg1HZXRTZXJ2ZXJJbmZvEh8ubWVjYXRsLnYxLkdldFNlcnZlckluZm9SZXF1ZXN0GiAubWVjYXRsLnYxLkdldFNlcnZlckluZm9SZXNwb25zZRJJCgpHZXRTZXNzaW9uEhwubWVjYXRsLnYxLkdldFNlc3Npb25SZXF1ZXN0Gh0ubWVjYXRsLnYxLkdldFNlc3Npb25SZXNwb25zZRJqChVMaXN0R3VhcmRyYWlsQ292ZXJhZ2USJy5tZWNhdGwudjEuTGlzdEd1YXJkcmFpbENvdmVyYWdlUmVxdWVzdBooLm1lY2F0bC52MS5MaXN0R3VhcmRyYWlsQ292ZXJhZ2VSZXNwb25zZRJzChhHZXRHdWFyZHJhaWxSZXZpZXdEZXRhaWwSKi5tZWNhdGwudjEuR2V0R3VhcmRyYWlsUmV2aWV3RGV0YWlsUmVxdWVzdBorLm1lY2F0bC52MS5HZXRHdWFyZHJhaWxSZXZpZXdEZXRhaWxSZXNwb25zZRJnChRHZXRTZXNzaW9uVHJhbnNjcmlwdBImLm1lY2F0bC52MS5HZXRTZXNzaW9uVHJhbnNjcmlwdFJlcXVlc3QaJy5tZWNhdGwudjEuR2V0U2Vzc2lvblRyYW5zY3JpcHRSZXNwb25zZRJACgdTZXRNb2RlEhkubWVjYXRsLnYxLlNldE1vZGVSZXF1ZXN0GhoubWVjYXRsLnYxLlNldE1vZGVSZXNwb25zZRJPCgxDbG9zZVNlc3Npb24SHi5tZWNhdGwudjEuQ2xvc2VTZXNzaW9uUmVxdWVzdBofLm1lY2F0bC52MS5DbG9zZVNlc3Npb25SZXNwb25zZRJSCg1SZW5hbWVTZXNzaW9uEh8ubWVjYXRsLnYxLlJlbmFtZVNlc3Npb25SZXF1ZXN0GiAubWVjYXRsLnYxLlJlbmFtZVNlc3Npb25SZXNwb25zZRJSCg1EZWxldGVTZXNzaW9uEh8ubWVjYXRsLnYxLkRlbGV0ZVNlc3Npb25SZXF1ZXN0GiAubWVjYXRsLnYxLkRlbGV0ZVNlc3Npb25SZXNwb25zZRJVCg5Db21wYWN0U2Vzc2lvbhIgLm1lY2F0bC52MS5Db21wYWN0U2Vzc2lvblJlcXVlc3QaIS5tZWNhdGwudjEuQ29tcGFjdFNlc3Npb25SZXNwb25zZRJPCgxDbGVhclNlc3Npb24SHi5tZWNhdGwudjEuQ2xlYXJTZXNzaW9uUmVxdWVzdBofLm1lY2F0bC52MS5DbGVhclNlc3Npb25SZXNwb25zZRJMCgtGb3JrU2Vzc2lvbhIdLm1lY2F0bC52MS5Gb3JrU2Vzc2lvblJlcXVlc3QaHi5tZWNhdGwudjEuRm9ya1Nlc3Npb25SZXNwb25zZRJHCghDb252ZXJzZRIaLm1lY2F0bC52MS5Db252ZXJzZVJlcXVlc3QaGy5tZWNhdGwudjEuQ29udmVyc2VSZXNwb25zZSgBMAESUgoNUmVzb2x2ZVJ1bkFzaxIfLm1lY2F0bC52MS5SZXNvbHZlUnVuQXNrUmVxdWVzdBogLm1lY2F0bC52MS5SZXNvbHZlUnVuQXNrUmVzcG9uc2USVQoOUmVzb2x2ZVBsYW5Bc2sSIC5tZWNhdGwudjEuUmVzb2x2ZVBsYW5Bc2tSZXF1ZXN0GiEubWVjYXRsLnYxLlJlc29sdmVQbGFuQXNrUmVzcG9uc2USRgoJQ2FuY2VsUnVuEhsubWVjYXRsLnYxLkNhbmNlbFJ1blJlcXVlc3QaHC5tZWNhdGwudjEuQ2FuY2VsUnVuUmVzcG9uc2USQwoIU3RlZXJSdW4SGi5tZWNhdGwudjEuU3RlZXJSdW5SZXF1ZXN0GhsubWVjYXRsLnYxLlN0ZWVyUnVuUmVzcG9uc2USVQoOQ2FuY2VsUnVuU3RlZXISIC5tZWNhdGwudjEuQ2FuY2VsUnVuU3RlZXJSZXF1ZXN0GiEubWVjYXRsLnYxLkNhbmNlbFJ1blN0ZWVyUmVzcG9uc2USWwoQTGlzdE1jcFJlc291cmNlcxIiLm1lY2F0bC52MS5MaXN0TWNwUmVzb3VyY2VzUmVxdWVzdBojLm1lY2F0bC52MS5MaXN0TWNwUmVzb3VyY2VzUmVzcG9uc2USWAoPUmVhZE1jcFJlc291cmNlEiEubWVjYXRsLnYxLlJlYWRNY3BSZXNvdXJjZVJlcXVlc3QaIi5tZWNhdGwudjEuUmVhZE1jcFJlc291cmNlUmVzcG9uc2USVQoOTGlzdE1jcFByb21wdHMSIC5tZWNhdGwudjEuTGlzdE1jcFByb21wdHNSZXF1ZXN0GiEubWVjYXRsLnYxLkxpc3RNY3BQcm9tcHRzUmVzcG9uc2USTwoMR2V0TWNwUHJvbXB0Eh4ubWVjYXRsLnYxLkdldE1jcFByb21wdFJlcXVlc3QaHy5tZWNhdGwudjEuR2V0TWNwUHJvbXB0UmVzcG9uc2USVQoOTGlzdE1jcFNvdXJjZXMSIC5tZWNhdGwudjEuTGlzdE1jcFNvdXJjZXNSZXF1ZXN0GiEubWVjYXRsLnYxLkxpc3RNY3BTb3VyY2VzUmVzcG9uc2USXgoRUmVmcmVzaE1jcFNvdXJjZXMSIy5tZWNhdGwudjEuUmVmcmVzaE1jcFNvdXJjZXNSZXF1ZXN0GiQubWVjYXRsLnYxLlJlZnJlc2hNY3BTb3VyY2VzUmVzcG9uc2UScwoYTGlzdFNlc3Npb25NY3BDb25uZWN0b3JzEioubWVjYXRsLnYxLkxpc3RTZXNzaW9uTWNwQ29ubmVjdG9yc1JlcXVlc3QaKy5tZWNhdGwudjEuTGlzdFNlc3Npb25NY3BDb25uZWN0b3JzUmVzcG9uc2USYQoSTGlzdFRvb2xIaXZlR3JvdXBzEiQubWVjYXRsLnYxLkxpc3RUb29sSGl2ZUdyb3Vwc1JlcXVlc3QaJS5tZWNhdGwudjEuTGlzdFRvb2xIaXZlR3JvdXBzUmVzcG9uc2USSQoKTGlzdEFnZW50cxIcLm1lY2F0bC52MS5MaXN0QWdlbnRzUmVxdWVzdBodLm1lY2F0bC52MS5MaXN0QWdlbnRzUmVzcG9uc2USTwoMTGlzdENvbW1hbmRzEh4ubWVjYXRsLnYxLkxpc3RDb21tYW5kc1JlcXVlc3QaHy5tZWNhdGwudjEuTGlzdENvbW1hbmRzUmVzcG9uc2USUgoNTGlzdFdvcmt0cmVlcxIfLm1lY2F0bC52MS5MaXN0V29ya3RyZWVzUmVxdWVzdBogLm1lY2F0bC52MS5MaXN0V29ya3RyZWVzUmVzcG9uc2USUAoTU3RyZWFtU2Vzc2lvbkV2ZW50cxIlLm1lY2F0bC52MS5TdHJlYW1TZXNzaW9uRXZlbnRzUmVxdWVzdBoQLm1lY2F0bC52MS5FdmVudDABEkwKEVN0cmVhbVNlc3Npb25MaXZlEiMubWVjYXRsLnYxLlN0cmVhbVNlc3Npb25MaXZlUmVxdWVzdBoQLm1lY2F0bC52MS5FdmVudDABEmMKEldhdGNoU2Vzc2lvbkV2ZW50cxIkLm1lY2F0bC52MS5XYXRjaFNlc3Npb25FdmVudHNSZXF1ZXN0GiUubWVjYXRsLnYxLldhdGNoU2Vzc2lvbkV2ZW50c1Jlc3BvbnNlMAESiAEKH0dldE1jcEF1dGhvcml6YXRpb25QcmVzZW50YXRpb24SMS5tZWNhdGwudjEuR2V0TWNwQXV0aG9yaXphdGlvblByZXNlbnRhdGlvblJlcXVlc3QaMi5tZWNhdGwudjEuR2V0TWNwQXV0aG9yaXphdGlvblByZXNlbnRhdGlvblJlc3BvbnNlEnQKF1JlY2hlY2tNY3BBdXRob3JpemF0aW9uEikubWVjYXRsLnYxLlJlY2hlY2tNY3BBdXRob3JpemF0aW9uUmVxdWVzdBoqLm1lY2F0bC52MS5SZWNoZWNrTWNwQXV0aG9yaXphdGlvblJlc3BvbnNlKAEwARJxChZDYW5jZWxNY3BBdXRob3JpemF0aW9uEigubWVjYXRsLnYxLkNhbmNlbE1jcEF1dGhvcml6YXRpb25SZXF1ZXN0GikubWVjYXRsLnYxLkNhbmNlbE1jcEF1dGhvcml6YXRpb25SZXNwb25zZSgBMAESTwoMTGlzdFNlc3Npb25zEh4ubWVjYXRsLnYxLkxpc3RTZXNzaW9uc1JlcXVlc3QaHy5tZWNhdGwudjEuTGlzdFNlc3Npb25zUmVzcG9uc2USWwoQR2V0U3RvcmFnZUhlYWx0aBIiLm1lY2F0bC52MS5HZXRTdG9yYWdlSGVhbHRoUmVxdWVzdBojLm1lY2F0bC52MS5HZXRTdG9yYWdlSGVhbHRoUmVzcG9uc2USYQoSUGxhblNlc3Npb25DbGVhbnVwEiQubWVjYXRsLnYxLlBsYW5TZXNzaW9uQ2xlYW51cFJlcXVlc3QaJS5tZWNhdGwudjEuUGxhblNlc3Npb25DbGVhbnVwUmVzcG9uc2USUwoTQXBwbHlTZXNzaW9uQ2xlYW51cBIlLm1lY2F0bC52MS5BcHBseVNlc3Npb25DbGVhbnVwUmVxdWVzdBoVLm1lY2F0bC52MS5DbGVhbnVwSm9iElUKFENhbmNlbFNlc3Npb25DbGVhbnVwEiYubWVjYXRsLnYxLkNhbmNlbFNlc3Npb25DbGVhbnVwUmVxdWVzdBoVLm1lY2F0bC52MS5DbGVhbnVwSm9iElUKFEdldFNlc3Npb25DbGVhbnVwSm9iEiYubWVjYXRsLnYxLkdldFNlc3Npb25DbGVhbnVwSm9iUmVxdWVzdBoVLm1lY2F0bC52MS5DbGVhbnVwSm9iEkkKCkxpc3RTa2lsbHMSHC5tZWNhdGwudjEuTGlzdFNraWxsc1JlcXVlc3QaHS5tZWNhdGwudjEuTGlzdFNraWxsc1Jlc3BvbnNlElUKDkxpc3RTa2lsbEZpbGVzEiAubWVjYXRsLnYxLkxpc3RTa2lsbEZpbGVzUmVxdWVzdBohLm1lY2F0bC52MS5MaXN0U2tpbGxGaWxlc1Jlc3BvbnNlElIKDVJlYWRTa2lsbEZpbGUSHy5tZWNhdGwudjEuUmVhZFNraWxsRmlsZVJlcXVlc3QaIC5tZWNhdGwudjEuUmVhZFNraWxsRmlsZVJlc3BvbnNlEkAKB0dldFNvdWwSGS5tZWNhdGwudjEuR2V0U291bFJlcXVlc3QaGi5tZWNhdGwudjEuR2V0U291bFJlc3BvbnNlEk8KDEdldFVzZXJNb2RlbBIeLm1lY2F0bC52MS5HZXRVc2VyTW9kZWxSZXF1ZXN0Gh8ubWVjYXRsLnYxLkdldFVzZXJNb2RlbFJlc3BvbnNlElUKDlJlZmxlY3RTZXNzaW9uEiAubWVjYXRsLnYxLlJlZmxlY3RTZXNzaW9uUmVxdWVzdBohLm1lY2F0bC52MS5SZWZsZWN0U2Vzc2lvblJlc3BvbnNlEmEKEkdldExlYXJuaW5nQXR0ZW1wdBIkLm1lY2F0bC52MS5HZXRMZWFybmluZ0F0dGVtcHRSZXF1ZXN0GiUubWVjYXRsLnYxLkdldExlYXJuaW5nQXR0ZW1wdFJlc3BvbnNlEmcKFExpc3RMZWFybmluZ0F0dGVtcHRzEiYubWVjYXRsLnYxLkxpc3RMZWFybmluZ0F0dGVtcHRzUmVxdWVzdBonLm1lY2F0bC52MS5MaXN0TGVhcm5pbmdBdHRlbXB0c1Jlc3BvbnNlEmkKFFJldHJ5TGVhcm5pbmdBdHRlbXB0EicubWVjYXRsLnYxLk11dGF0ZUxlYXJuaW5nQXR0ZW1wdFJlcXVlc3QaKC5tZWNhdGwudjEuTXV0YXRlTGVhcm5pbmdBdHRlbXB0UmVzcG9uc2USawoWQWJhbmRvbkxlYXJuaW5nQXR0ZW1wdBInLm1lY2F0bC52MS5NdXRhdGVMZWFybmluZ0F0dGVtcHRSZXF1ZXN0GigubWVjYXRsLnYxLk11dGF0ZUxlYXJuaW5nQXR0ZW1wdFJlc3BvbnNlEl4KEUdlbmVyYXRlRHJlYW1QbGFuEiMubWVjYXRsLnYxLkdlbmVyYXRlRHJlYW1QbGFuUmVxdWVzdBokLm1lY2F0bC52MS5HZW5lcmF0ZURyZWFtUGxhblJlc3BvbnNlElgKD0RlY2lkZURyZWFtUGxhbhIhLm1lY2F0bC52MS5EZWNpZGVEcmVhbVBsYW5SZXF1ZXN0GiIubWVjYXRsLnYxLkRlY2lkZURyZWFtUGxhblJlc3BvbnNlEmoKFUxpc3RMZWFybmluZ1Byb3Bvc2FscxInLm1lY2F0bC52MS5MaXN0TGVhcm5pbmdQcm9wb3NhbHNSZXF1ZXN0GigubWVjYXRsLnYxLkxpc3RMZWFybmluZ1Byb3Bvc2Fsc1Jlc3BvbnNlEmQKE0dldExlYXJuaW5nUHJvcG9zYWwSJS5tZWNhdGwudjEuR2V0TGVhcm5pbmdQcm9wb3NhbFJlcXVlc3QaJi5tZWNhdGwudjEuR2V0TGVhcm5pbmdQcm9wb3NhbFJlc3BvbnNlEm0KFkRlY2lkZUxlYXJuaW5nUHJvcG9zYWwSKC5tZWNhdGwudjEuRGVjaWRlTGVhcm5pbmdQcm9wb3NhbFJlcXVlc3QaKS5tZWNhdGwudjEuRGVjaWRlTGVhcm5pbmdQcm9wb3NhbFJlc3BvbnNlEmoKFVVuZG9MZWFybmluZ1Byb21vdGlvbhInLm1lY2F0bC52MS5VbmRvTGVhcm5pbmdQcm9tb3Rpb25SZXF1ZXN0GigubWVjYXRsLnYxLlVuZG9MZWFybmluZ1Byb21vdGlvblJlc3BvbnNlEl4KEUxpc3RMZWFybmVkU2tpbGxzEiMubWVjYXRsLnYxLkxpc3RMZWFybmVkU2tpbGxzUmVxdWVzdBokLm1lY2F0bC52MS5MaXN0TGVhcm5lZFNraWxsc1Jlc3BvbnNlElgKD0dldExlYXJuZWRTa2lsbBIhLm1lY2F0bC52MS5HZXRMZWFybmVkU2tpbGxSZXF1ZXN0GiIubWVjYXRsLnYxLkdldExlYXJuZWRTa2lsbFJlc3BvbnNlEnMKGERpZmZMZWFybmVkU2tpbGxWZXJzaW9ucxIqLm1lY2F0bC52MS5EaWZmTGVhcm5lZFNraWxsVmVyc2lvbnNSZXF1ZXN0GisubWVjYXRsLnYxLkRpZmZMZWFybmVkU2tpbGxWZXJzaW9uc1Jlc3BvbnNlEmMKFEFjdGl2YXRlTGVhcm5lZFNraWxsEiQubWVjYXRsLnYxLk11dGF0ZUxlYXJuZWRTa2lsbFJlcXVlc3QaJS5tZWNhdGwudjEuTXV0YXRlTGVhcm5lZFNraWxsUmVzcG9uc2USYQoSUmVqZWN0TGVhcm5lZFNraWxsEiQubWVjYXRsLnYxLk11dGF0ZUxlYXJuZWRTa2lsbFJlcXVlc3QaJS5tZWNhdGwudjEuTXV0YXRlTGVhcm5lZFNraWxsUmVzcG9uc2USYgoTQXJjaGl2ZUxlYXJuZWRTa2lsbBIkLm1lY2F0bC52MS5NdXRhdGVMZWFybmVkU2tpbGxSZXF1ZXN0GiUubWVjYXRsLnYxLk11dGF0ZUxlYXJuZWRTa2lsbFJlc3BvbnNlEmUKFFJvbGxiYWNrTGVhcm5lZFNraWxsEiYubWVjYXRsLnYxLlJvbGxiYWNrTGVhcm5lZFNraWxsUmVxdWVzdBolLm1lY2F0bC52MS5NdXRhdGVMZWFybmVkU2tpbGxSZXNwb25zZRJbChBMaXN0U2tpbGxDaGFuZ2VzEiIubWVjYXRsLnYxLkxpc3RTa2lsbENoYW5nZXNSZXF1ZXN0GiMubWVjYXRsLnYxLkxpc3RTa2lsbENoYW5nZXNSZXNwb25zZRJJCgpMaXN0TW9kZWxzEhwubWVjYXRsLnYxLkxpc3RNb2RlbHNSZXF1ZXN0Gh0ubWVjYXRsLnYxLkxpc3RNb2RlbHNSZXNwb25zZRJJCgpDcmVhdGVUZWFtEhwubWVjYXRsLnYxLkNyZWF0ZVRlYW1SZXF1ZXN0Gh0ubWVjYXRsLnYxLkNyZWF0ZVRlYW1SZXNwb25zZRJSCg1TcGF3blRlYW1tYXRlEh8ubWVjYXRsLnYxLlNwYXduVGVhbW1hdGVSZXF1ZXN0GiAubWVjYXRsLnYxLlNwYXduVGVhbW1hdGVSZXNwb25zZRJkChNTZW5kVGVhbW1hdGVNZXNzYWdlEiUubWVjYXRsLnYxLlNlbmRUZWFtbWF0ZU1lc3NhZ2VSZXF1ZXN0GiYubWVjYXRsLnYxLlNlbmRUZWFtbWF0ZU1lc3NhZ2VSZXNwb25zZRJVCg5DYW5jZWxUZWFtbWF0ZRIgLm1lY2F0bC52MS5DYW5jZWxUZWFtbWF0ZVJlcXVlc3QaIS5tZWNhdGwudjEuQ2FuY2VsVGVhbW1hdGVSZXNwb25zZRI8CgdSdW5UZWFtEhkubWVjYXRsLnYxLlJ1blRlYW1SZXF1ZXN0GhQubWVjYXRsLnYxLlRlYW1FdmVudDABEkMKCExpc3RUZWFtEhoubWVjYXRsLnYxLkxpc3RUZWFtUmVxdWVzdBobLm1lY2F0bC52MS5MaXN0VGVhbVJlc3BvbnNlEkwKC0NsZWFudXBUZWFtEh0ubWVjYXRsLnYxLkNsZWFudXBUZWFtUmVxdWVzdBoeLm1lY2F0bC52MS5DbGVhbnVwVGVhbVJlc3BvbnNlEkAKC0FwcHJvdmVQbGFuEh0ubWVjYXRsLnYxLkFwcHJvdmVQbGFuUmVxdWVzdBoQLm1lY2F0bC52MS5FdmVudDABEmgKGENvbm5lY3RXb3Jrc3BhY2VTZXJ2aWNlcxIsLm1lY2F0bC52MS5Xb3Jrc3BhY2VFbnJvbGxtZW50Q29ubmVjdFJlcXVlc3QaHi5tZWNhdGwudjEuV29ya3NwYWNlRW5yb2xsbWVudBJoChhSZXRyeVdvcmtzcGFjZUVucm9sbG1lbnQSLC5tZWNhdGwudjEuV29ya3NwYWNlRW5yb2xsbWVudENvbnRyb2xSZXF1ZXN0Gh4ubWVjYXRsLnYxLldvcmtzcGFjZUVucm9sbG1lbnQSaQoZQ2FuY2VsV29ya3NwYWNlRW5yb2xsbWVudBIsLm1lY2F0bC52MS5Xb3Jrc3BhY2VFbnJvbGxtZW50Q29udHJvbFJlcXVlc3QaHi5tZWNhdGwudjEuV29ya3NwYWNlRW5yb2xsbWVudEKiAQoNY29tLm1lY2F0bC52MUIMSGFybmVzc1Byb3RvUAFaPmdpdGh1Yi5jb20vc3RhY2tsb2svbWVjYXRsL2NvbnRyYWN0cy9nZW4vZ28vbWVjYXRsL3YxO21lY2F0bHYxogIDTVhYqgIJTWVjYXRsLlYxygIJTWVjYXRsXFYx4gIVTWVjYXRsXFYxXEdQQk1ldGFkYXRh6gIKTWVjYXRsOjpWMWIGcHJvdG8z", [file_buf_validate_validate, file_google_protobuf_timestamp]); /** * Limits are the configured stop conditions for a session. A zero value in @@ -81,7 +81,9 @@ export const LimitsSchema: GenMessage = /*@__PURE__*/ */ export type CreateSessionRequest = Message<"mecatl.v1.CreateSessionRequest"> & { /** - * mode is the permission posture; unspecified defaults to DEFAULT. + * mode is the permission posture; unspecified uses the server's configured + * default session mode, which is DEFAULT unless the operator chose a + * permission mode that starts sessions elsewhere. * * @generated from field: mecatl.v1.PermissionMode mode = 2; */ @@ -667,12 +669,10 @@ export type ServerCapabilities = Message<"mecatl.v1.ServerCapabilities"> & { /** * posture is the SERVER-WIDE operator posture-ladder tier ("strict"/"trusted"/ - * "auto"/"yolo"). It is CHROME ONLY — a client renders an honest "⚠ auto"/"⚠ yolo" - * badge so an operator can see the daemon's automation posture at a glance — and is + * "auto"/"yolo"). It bounds which session modes a client may cycle to and is * NEVER session state (it does not change per session; permission MODE is the * per-session knob). String passthrough, no enum on the wire (the EvNoProgress / - * StopBudget discipline). Additive: an older server leaves it empty and the client - * shows no badge (strict/trusted also show no badge). + * StopBudget discipline). Additive: an older server leaves it empty. * * @generated from field: string posture = 13; */ @@ -816,6 +816,28 @@ export type ServerCapabilities = Message<"mecatl.v1.ServerCapabilities"> & { * @generated from field: bool execution_templates = 31; */ executionTemplates: boolean; + + /** + * project_trusted reports whether the server trusts its launch workspace's + * project content (rules, instructions, soul). It is an axis separate from + * the permission mode. CHROME ONLY. Additive: an older server leaves it + * unset and the client shows no trust indicator. + * + * @generated from field: optional bool project_trusted = 32; + */ + projectTrusted?: boolean | undefined; + + /** + * subagent_ask_reviewer reports the subagent ask reviewer, a model that may + * approve a subagent's, team member's, or parallel branch's permission + * request that no human can answer: "on" (enabled), "off" (not enabled), or + * "inert" (configured, but an interactive approver answers instead). Clients + * treat other values as unknown. CHROME ONLY. Additive: an older server + * leaves it unset and the client shows nothing. + * + * @generated from field: optional string subagent_ask_reviewer = 33; + */ + subagentAskReviewer?: string | undefined; }; /** @@ -2364,6 +2386,9 @@ export type SetModeRequest = Message<"mecatl.v1.SetModeRequest"> & { /** * mode is the desired permission posture; unspecified defaults to DEFAULT. + * AUTO requires an enforcing guardrails checker; the server rejects SetMode + * to AUTO without one. The server may also enforce a MaxAllowedMode ceiling; + * requesting a mode above it is rejected with INVALID_ARGUMENT. * * @generated from field: mecatl.v1.PermissionMode mode = 2; */ @@ -10435,7 +10460,8 @@ export const WorkspaceEnrollmentSchema: GenMessage = /*@__P /** * PermissionMode is the session-wide permission posture, mirroring * session.PermissionMode. Plan mode exposes a read-only toolset; acceptEdits - * auto-accepts edits. + * auto-accepts edits; auto runs autonomously with an enforcing guardrails + * checker; yolo runs gate-free with no enforcing checker. * * @generated from enum mecatl.v1.PermissionMode */ @@ -10467,6 +10493,23 @@ export enum PermissionMode { * @generated from enum value: PERMISSION_MODE_ACCEPT_EDITS = 3; */ ACCEPT_EDITS = 3, + + /** + * PERMISSION_MODE_AUTO runs autonomously using an enforcing guardrails checker. + * Entering this mode requires a checker; the server rejects SetMode to AUTO + * without one. + * + * @generated from enum value: PERMISSION_MODE_AUTO = 4; + */ + AUTO = 4, + + /** + * PERMISSION_MODE_YOLO runs gate-free with no enforcing checker. Advisory + * monitoring may remain. Configured deny rules still apply. + * + * @generated from enum value: PERMISSION_MODE_YOLO = 5; + */ + YOLO = 5, } /** @@ -11077,6 +11120,10 @@ export const HarnessService: GenService<{ * SetMode changes an existing session's permission posture. The session aggregate * remains authoritative: a mid-turn change is rejected with InvalidArgument, so * clients that want "next prompt" semantics must defer and retry once idle. + * One exception, advertised by the refused_mode_recovery feature: for a stored + * session awaiting an approval in a mode the server no longer admits, a change + * to an admitted, strictly lower mode cancels the pending turn without running + * it and applies the mode. * * @generated from rpc mecatl.v1.HarnessService.SetMode */ diff --git a/sdk/typescript/src/gen/mecatl/v1/schedule_pb.ts b/sdk/typescript/src/gen/mecatl/v1/schedule_pb.ts index 0e3aa38c84..65b2a8a27b 100644 --- a/sdk/typescript/src/gen/mecatl/v1/schedule_pb.ts +++ b/sdk/typescript/src/gen/mecatl/v1/schedule_pb.ts @@ -176,6 +176,10 @@ export type ScheduleSpec = Message<"mecatl.v1.ScheduleSpec"> & { /** * mode is the session permission posture. Reuses the PermissionMode enum from * harness.proto (same package — proto allows cross-file refs within a package). + * A non-mutating schedule must use PLAN. For a mutating schedule, unspecified + * means each fire uses the server's configured default session mode at fire + * time; it reads back as unspecified. Every fire checks the mode against the + * server's mode ceiling and fails if the ceiling no longer allows it. * * @generated from field: mecatl.v1.PermissionMode mode = 8; */ diff --git a/sdk/typescript/src/http.ts b/sdk/typescript/src/http.ts index 87de8772a9..1cb7377598 100644 --- a/sdk/typescript/src/http.ts +++ b/sdk/typescript/src/http.ts @@ -61,6 +61,10 @@ function permissionMode(value: JsonValue | undefined): string { return "plan"; case 3: return "accept_edits"; + case 4: + return "auto"; + case 5: + return "yolo"; default: return "default"; } @@ -127,7 +131,11 @@ function encodeInput( const json = record( toJson(method.input, message, { enumAsInteger: true, useProtoFieldName: true }), ); - if (method.name === "CreateSession" || method.name === "SetMode") { + if (method.name === "CreateSession" && (json.mode === undefined || json.mode === 0)) { + // An unspecified create mode is omitted so the server's configured default + // mode applies, matching gRPC. + delete json.mode; + } else if (method.name === "CreateSession" || method.name === "SetMode") { json.mode = permissionMode(json.mode); } if (method.name === "ApprovePlan" && json.target_mode !== undefined) { @@ -163,7 +171,12 @@ function permissionModeTextToNumber(value: JsonValue | undefined): number { return 2; case "accept_edits": case "acceptedits": + case "acceptEdits": return 3; + case "auto": + return 4; + case "yolo": + return 5; default: return 0; } diff --git a/sdk/typescript/src/server.ts b/sdk/typescript/src/server.ts index 1af1b0f97a..5c4484e523 100644 --- a/sdk/typescript/src/server.ts +++ b/sdk/typescript/src/server.ts @@ -23,8 +23,10 @@ export const ServerFeature = { HttpSteer: "http_steer", McpServersOnCreate: "mcp_servers_on_create", PromptFreeControls: "prompt_free_controls", + RefusedModeRecovery: "refused_mode_recovery", ServerInfo: "server_info", SessionActivityInventory: "session_activity_inventory", + SessionPermissionModes: "session_permission_modes", SkillFiles: "skill_files", WatchSessionEvents: "watch_session_events", } as const; diff --git a/sdk/typescript/src/session-projections.ts b/sdk/typescript/src/session-projections.ts index 5c228e4201..2bf2596acf 100644 --- a/sdk/typescript/src/session-projections.ts +++ b/sdk/typescript/src/session-projections.ts @@ -26,6 +26,10 @@ export const SessionMode = { Default: 1, Plan: 2, AcceptEdits: 3, + /** Checker-approved autonomy; requires the `session_permission_modes` server feature. */ + Auto: 4, + /** Gate-free autonomy; requires the `session_permission_modes` server feature. */ + Yolo: 5, } as const; /** One SDK permission-mode value. @public */ @@ -124,6 +128,13 @@ export interface ServerCapabilities { readonly userModel: boolean; readonly modelSelection: boolean; readonly posture: string; + /** Whether the server trusts its launch workspace; absent from older servers. */ + readonly projectTrusted?: boolean; + /** + * Subagent ask reviewer state: `"on"`, `"off"`, or `"inert"`; treat other + * values as unknown. Absent from older servers. + */ + readonly subagentAskReviewer?: string; readonly worktrees: boolean; readonly scheduling: boolean; readonly reflection: boolean; @@ -263,6 +274,7 @@ export function projectServerCapabilities(value: ProtoServerCapabilities): Serve memory: value.memory, modelSelection: value.modelSelection, posture: value.posture, + ...(value.projectTrusted === undefined ? {} : { projectTrusted: value.projectTrusted }), reflection: value.reflection, scheduling: value.scheduling, sessionDebug: value.sessionDebug, @@ -270,6 +282,9 @@ export function projectServerCapabilities(value: ProtoServerCapabilities): Serve slashCommands: value.slashCommands, soul: value.soul, steer: value.steer, + ...(value.subagentAskReviewer === undefined + ? {} + : { subagentAskReviewer: value.subagentAskReviewer }), storageCleanup: value.storageCleanup, storageHealth: value.storageHealth, teams: value.teams, @@ -283,7 +298,9 @@ function sessionMode(value: number): SessionMode | undefined { return value === SessionMode.Unspecified || value === SessionMode.Default || value === SessionMode.Plan || - value === SessionMode.AcceptEdits + value === SessionMode.AcceptEdits || + value === SessionMode.Auto || + value === SessionMode.Yolo ? value : undefined; } diff --git a/sdk/typescript/test/package.test.ts b/sdk/typescript/test/package.test.ts index 82d5544390..951e1cde4b 100644 --- a/sdk/typescript/test/package.test.ts +++ b/sdk/typescript/test/package.test.ts @@ -630,7 +630,7 @@ test("the lifecycle surface is exported documented and API reviewed", () => { "--eval", ` import { SessionMode } from "@stacklok-oss/mecatl-sdk"; -if (JSON.stringify(SessionMode) !== JSON.stringify({ Unspecified: 0, Default: 1, Plan: 2, AcceptEdits: 3 })) { +if (JSON.stringify(SessionMode) !== JSON.stringify({ Unspecified: 0, Default: 1, Plan: 2, AcceptEdits: 3, Auto: 4, Yolo: 5 })) { throw new Error("unexpected SessionMode export"); } `, diff --git a/sdk/typescript/test/server-discovery.test.ts b/sdk/typescript/test/server-discovery.test.ts index 8800e3c471..ae753b753d 100644 --- a/sdk/typescript/test/server-discovery.test.ts +++ b/sdk/typescript/test/server-discovery.test.ts @@ -264,8 +264,10 @@ describe("SDK server discovery", () => { HttpSteer: "http_steer", McpServersOnCreate: "mcp_servers_on_create", PromptFreeControls: "prompt_free_controls", + RefusedModeRecovery: "refused_mode_recovery", ServerInfo: "server_info", SessionActivityInventory: "session_activity_inventory", + SessionPermissionModes: "session_permission_modes", SkillFiles: "skill_files", WatchSessionEvents: "watch_session_events", }); @@ -880,6 +882,72 @@ describe("SDK server discovery", () => { await expect(httpMalformedInfo.server.info()).rejects.toBeInstanceOf(ProtocolError); }); + it("projectTrusted stays absent when omitted and exact when present over grpc and http", async () => { + for (const projectTrusted of [undefined, true, false]) { + const grpcTransport = new DiscoveryTransport(); + const grpcResponse = { + ...validCompatibility, + capabilities: { ...completeCapabilities, projectTrusted }, + }; + grpcTransport.compatibility.push( + async () => grpcResponse, + async () => grpcResponse, + ); + const http = connect({ + baseUrl: "http://mecatl.test", + fetch: async () => + Response.json({ + api_major: 1, + capabilities: { + ...completeCapabilities, + ...(projectTrusted === undefined ? {} : { project_trusted: projectTrusted }), + }, + features: ["server_info"], + }), + }); + for (const client of [connect({ transport: grpcTransport }), http]) { + const { capabilities } = await client.server.compatibility(); + expect(capabilities.projectTrusted).toBe(projectTrusted); + expect(Object.hasOwn(capabilities, "projectTrusted")).toBe(projectTrusted !== undefined); + } + } + }); + + it("subagentAskReviewer stays absent when omitted and exact when present over grpc and http", async () => { + for (const subagentAskReviewer of [undefined, "on", "off", "inert"]) { + const grpcTransport = new DiscoveryTransport(); + const grpcResponse = { + ...validCompatibility, + capabilities: { ...completeCapabilities, subagentAskReviewer }, + }; + grpcTransport.compatibility.push( + async () => grpcResponse, + async () => grpcResponse, + ); + const http = connect({ + baseUrl: "http://mecatl.test", + fetch: async () => + Response.json({ + api_major: 1, + capabilities: { + ...completeCapabilities, + ...(subagentAskReviewer === undefined + ? {} + : { subagent_ask_reviewer: subagentAskReviewer }), + }, + features: ["server_info"], + }), + }); + for (const client of [connect({ transport: grpcTransport }), http]) { + const { capabilities } = await client.server.compatibility(); + expect(capabilities.subagentAskReviewer).toBe(subagentAskReviewer); + expect(Object.hasOwn(capabilities, "subagentAskReviewer")).toBe( + subagentAskReviewer !== undefined, + ); + } + } + }); + it("server discovery exports and api reports stay complete", () => { const typeProof: [Server, ServerCompatibility, ServerInfo, ServerInfoOptions] | undefined = undefined; @@ -924,8 +992,10 @@ describe("SDK server discovery", () => { 'readonly HttpSteer: "http_steer";', 'readonly McpServersOnCreate: "mcp_servers_on_create";', 'readonly PromptFreeControls: "prompt_free_controls";', + 'readonly RefusedModeRecovery: "refused_mode_recovery";', 'readonly ServerInfo: "server_info";', 'readonly SessionActivityInventory: "session_activity_inventory";', + 'readonly SessionPermissionModes: "session_permission_modes";', 'readonly SkillFiles: "skill_files";', 'readonly WatchSessionEvents: "watch_session_events";', ]); diff --git a/sdk/typescript/test/session-lifecycle-transports.test.ts b/sdk/typescript/test/session-lifecycle-transports.test.ts index 4165f1be56..317d97ea14 100644 --- a/sdk/typescript/test/session-lifecycle-transports.test.ts +++ b/sdk/typescript/test/session-lifecycle-transports.test.ts @@ -6,6 +6,7 @@ import { type Client, connect, createHttpTransport, + ProtocolError, SESSION_ID_HEADER_NAME, ServerError, SessionMode, @@ -274,6 +275,147 @@ describe("session lifecycle transport parity", () => { await http.close(); }); + it("auto and yolo session modes round-trip over grpc", async () => { + const seenModes: number[] = []; + let stored: number = SessionMode.Unspecified; + let override: number | undefined; + const grpc = connect({ + transport: createRouterTransport((router) => { + router.service(HarnessService, { + createSession: (request) => { + seenModes.push(request.mode); + stored = request.mode; + return { sessionId: "session" }; + }, + getCompatibilityInfo: () => ({ + apiMajor: 1, + capabilities: {}, + features: ["server_info", "session_permission_modes"], + }), + getSession: (request) => ({ + session: { mode: override ?? stored, sessionId: request.sessionId }, + }), + setMode: (request) => { + seenModes.push(request.mode); + stored = request.mode; + return { session: { mode: stored, sessionId: request.sessionId } }; + }, + }); + }), + transportKind: "grpc", + }); + + const session = await grpc.sessions.create({ mode: SessionMode.Auto }); + expect((await session.snapshot()).mode).toBe(SessionMode.Auto); + expect((await (await grpc.sessions.get(session.id)).snapshot()).mode).toBe(SessionMode.Auto); + expect((await session.setMode(SessionMode.Yolo)).mode).toBe(SessionMode.Yolo); + expect((await session.snapshot()).mode).toBe(SessionMode.Yolo); + expect((await session.setMode(SessionMode.Auto)).mode).toBe(SessionMode.Auto); + expect(seenModes).toEqual([SessionMode.Auto, SessionMode.Yolo, SessionMode.Auto]); + + override = SessionMode.Yolo; + await expect(session.snapshot()).resolves.toMatchObject({ mode: SessionMode.Yolo }); + override = 6; + await expect(session.snapshot()).rejects.toBeInstanceOf(ProtocolError); + await grpc.close(); + }); + + it("auto and yolo session modes round-trip over http", async () => { + const seenModes: unknown[] = []; + // Mirrors the server's modeFromString and string(session.Mode) projection. + const canonical: Record = { + accept_edits: "acceptEdits", + auto: "auto", + default: "default", + plan: "plan", + yolo: "yolo", + }; + let stored = ""; + let override: string | undefined; + const http = connect({ + transport: createHttpTransport({ + baseUrl: "http://mecatl.test", + fetch: async (input, init) => { + const path = new URL(String(input)).pathname; + if (path === "/v1/compatibility") { + return Response.json({ + api_major: 1, + capabilities: {}, + features: ["server_info", "session_permission_modes"], + }); + } + const body = init?.body === undefined ? {} : JSON.parse(String(init.body)); + if (path === "/v1/sessions") { + seenModes.push(body.mode); + stored = canonical[body.mode] ?? ""; + return Response.json({ session_id: "session" }, { status: 201 }); + } + if (path === "/v1/sessions/session/mode") { + seenModes.push(body.mode); + stored = canonical[body.mode] ?? ""; + return Response.json({ mode: stored, session_id: "session" }); + } + if (path === "/v1/sessions/session") { + return Response.json({ mode: override ?? stored, session_id: "session" }); + } + throw new Error(`unexpected path ${path}`); + }, + }), + transportKind: "http", + }); + + const session = await http.sessions.create({ mode: SessionMode.Auto }); + expect((await session.snapshot()).mode).toBe(SessionMode.Auto); + expect((await (await http.sessions.get(session.id)).snapshot()).mode).toBe(SessionMode.Auto); + expect((await session.setMode(SessionMode.Yolo)).mode).toBe(SessionMode.Yolo); + expect((await session.snapshot()).mode).toBe(SessionMode.Yolo); + expect((await session.setMode(SessionMode.AcceptEdits)).mode).toBe(SessionMode.AcceptEdits); + expect((await session.setMode(SessionMode.Auto)).mode).toBe(SessionMode.Auto); + expect(seenModes).toEqual(["auto", "yolo", "accept_edits", "auto"]); + + override = "yolo"; + expect((await session.snapshot()).mode).toBe(SessionMode.Yolo); + override = "acceptEdits"; + expect((await session.snapshot()).mode).toBe(SessionMode.AcceptEdits); + override = ""; + expect((await session.snapshot()).mode).toBe(SessionMode.Unspecified); + await http.close(); + }); + + it("an unspecified http create omits mode so the server default applies", async () => { + const createBodies: Record[] = []; + const http = connect({ + transport: createHttpTransport({ + baseUrl: "http://mecatl.test", + fetch: async (input, init) => { + const path = new URL(String(input)).pathname; + if (path === "/v1/compatibility") { + return Response.json({ + api_major: 1, + capabilities: {}, + features: ["server_info", "session_permission_modes"], + }); + } + if (path === "/v1/sessions") { + createBodies.push(init?.body === undefined ? {} : JSON.parse(String(init.body))); + return Response.json({ session_id: "session" }, { status: 201 }); + } + if (path === "/v1/sessions/session") { + return Response.json({ mode: "yolo", session_id: "session" }); + } + throw new Error(`unexpected path ${path}`); + }, + }), + transportKind: "http", + }); + const session = await http.sessions.create({}); + await http.sessions.create({ mode: SessionMode.Default }); + expect("mode" in (createBodies[0] ?? {})).toBe(false); + expect(createBodies[1]?.mode).toBe("default"); + expect((await session.snapshot()).mode).toBe(SessionMode.Yolo); + await http.close(); + }); + it("both injected transports normalize every unary lifecycle failure class", async () => { const classes = ["projection", "snapshot-mutation", "acknowledgement", "acquisition"] as const; for (const failureClass of classes) { diff --git a/sdk/typescript/test/session-projections.test.ts b/sdk/typescript/test/session-projections.test.ts index f489802fbe..837f1894d3 100644 --- a/sdk/typescript/test/session-projections.test.ts +++ b/sdk/typescript/test/session-projections.test.ts @@ -312,6 +312,7 @@ describe("session projections", () => { "memory", "modelSelection", "posture", + "projectTrusted", "reflection", "scheduling", "sessionDebug", @@ -319,6 +320,7 @@ describe("session projections", () => { "slashCommands", "soul", "steer", + "subagentAskReviewer", "storageCleanup", "storageHealth", "teams", diff --git a/user-docs/building/ci/mecatequi.md b/user-docs/building/ci/mecatequi.md index 0ef0717bca..f9fcb0ecfc 100644 --- a/user-docs/building/ci/mecatequi.md +++ b/user-docs/building/ci/mecatequi.md @@ -168,6 +168,12 @@ Replace `v0.0.3` with the latest released tag. The workflow preserves the three jobs and their separate permissions. Its composite action builds `mecatequi` from the tagged Mecatl source. +The workflow's `posture` input defaults to `auto`, which refuses to start +without an enforcing guardrails checker. Set `guardrails-model` to a checker +model, or set `posture: yolo` with `guardrails-model: "off"` to run without one. +Callers that relied on earlier releases running `auto` unchecked must make one +of these choices when they move to a newer tag. + Use `.github/workflows/mecatequi-example.yml` as a customizable alternative when you need a different trigger, approval stage, or author gate. @@ -246,7 +252,7 @@ corrupt both. |`--workspace`|cwd|Session workspace root. Must be a git repository top level.| |`--default-provider`|`""`|Provider ID: `openai`, `openai-codex`, `openrouter`, `anthropic`, or `opencode`.| |`--model`|`""`|Per-session passthrough model ID. Accepts any ID the provider serves, including IDs newer than the embedded catalog. Prefer this over `--default-model` for newer models.| -|`--posture`|`""` (strict)|Permission posture. Use `auto` for autonomous CI. With `strict` and `--headless`, a main-agent permission ask cancels the run and exits 1.| +|`--permission-mode`|`default`|Permission mode. Use `auto` for autonomous CI with an enforcing guardrails checker, or `yolo` to run with no checker. With `default` and `--headless`, a main-agent permission ask cancels the run and exits 1. `--posture` is a deprecated alias.| |`--trust-project`|`false`|Trust this workspace for project content and read-only child shell access. No posture grants project trust in headless mode. Without this flag or a declared or remembered trust decision, Mecatl ignores project content such as `AGENTS.md` and does not give read-only children a shell.| |`--headless`|`true`|Controls child permission requests. The default denies them or routes them to the optional ask reviewer. See [Headless posture and permission asks](#headless-posture-and-permission-asks).| |`--timeout`|`0` (disabled)|Wall-clock bound on the whole run (for example, `40m`). A timeout-cancelled run exits 1 with `stop_reason: cancelled`.| @@ -300,23 +306,39 @@ Metrics use bounded role labels and do not include session or model IDs. `mecatequi` defaults to `--headless=true`. Permission asks behave differently for the main agent and its children. -Child asks from subagents, team members, and parallel branches are denied by -default. `--subagent-ask-reviewer` enables a tool-less, one-turn LLM reviewer -that can approve the current call only. A reviewer error leaves the call denied. -This flag applies only in headless mode. +Child asks from subagents, team members, and parallel branches go to a +tool-less, one-turn LLM reviewer under `auto` and `yolo`, which can approve the +current call only. The reviewer uses the `ask-reviewer` model slot, or the +session model. Set `--subagent-ask-reviewer ` to choose its model, or +`--subagent-ask-reviewer off` to deny child asks instead. In other modes child +asks are denied unless you set a reviewer model. A reviewer error leaves the +call denied. The reviewer applies only in headless mode. `--headless=false` surfaces child asks instead. `mecatequi` has no approval interface, so the first surfaced ask cancels the run, produces `stop_reason: cancelled`, and exits 1. -With `--posture strict` and `--headless`, the main agent has no approver. Its -first permission ask cancels the run, produces `stop_reason: cancelled`, and -exits 1 with guidance to add allow rules or choose another posture. +With `--permission-mode default` and `--headless`, the main agent has no +approver. Its first permission ask cancels the run, produces +`stop_reason: cancelled`, and exits 1 with guidance to add allow rules or choose +another permission mode. -Use `--posture auto` for autonomous CI. It allows main-agent and child tool -calls while retaining child prompt-injection defenses. Reserve `yolo` for a +Use `--permission-mode auto` for autonomous CI. It allows main-agent and child +tool calls while retaining child prompt-injection defenses. It refuses to start +without an enforcing guardrails checker, set with `--guardrails-model` or +configured in operator settings. `yolo` runs with no checker; reserve it for a disposable, isolated, single-tenant environment. +The GitHub Action passes `--permission-mode auto` unless you set its +`permission-mode` input; its `posture` input is a deprecated alias. The +`guardrails-model` input has three choices: + +- Empty, the default: the action passes no guardrails flag, and the run uses + the checker configured in operator settings, if any. +- `off`: the action passes the `--guardrails off` kill-switch, which also + disables a checker in settings. Pair it with `permission-mode: yolo`. +- Any other value: the action passes `--guardrails-model `. + For shared posture behavior, project trust, and permission-rule precedence, see [Permissions and posture](/features/security-and-execution/permissions-and-posture.md). diff --git a/user-docs/features/security-and-execution/permissions-and-posture.md b/user-docs/features/security-and-execution/permissions-and-posture.md index 30801b2a9a..6620fb29f5 100644 --- a/user-docs/features/security-and-execution/permissions-and-posture.md +++ b/user-docs/features/security-and-execution/permissions-and-posture.md @@ -11,12 +11,13 @@ description: Configure two independent safety controls: 1. **Permissions** decide whether a tool call is allowed, needs approval, or is - denied before it runs. + denied before it runs. A single launch-time setting, the _permission mode_, + selects how much Mecatl allows without asking. 2. **Guardrails** are an optional model-backed checker that inspects selected tool arguments and results. They are independent of the permission rules. Permissions always apply. Guardrails require an operator-configured checker -model. +model, and the `auto` permission mode requires an enforcing one. For a custom authorization implementation, see [PermissionPolicy](/building/go/extension-points/permission-policy.md). @@ -25,77 +26,261 @@ For a custom authorization implementation, see Permissions and posture apply to `mecated`, `mecak8s`, `mecatequi`, and the embedded server hosted by `mecatui`. A connected `mecatui` uses the posture and -permission configuration of the remote server; local embedded-server flags do -not apply to `connect` sessions. +permission configuration of the remote server. Under `mecatui connect`, the +local `--permission-mode` flag accepts only `plan`, `default`, and +`accept-edits`, which choose the mode of the sessions that client creates. + +An ACP editor connected to `mecated acp` starts each session in the server's +default mode, and its mode picker offers only the modes the server admits. Interactive clients such as `mecatui` can answer approval requests. For headless servers and one-shot jobs, configure the permissions required by the workload before it starts. -## Choose a posture +## Choose a permission mode -`--posture` selects the operator posture ladder: +Pass `--permission-mode` when you start `mecated`, `mecatui`, `mecak8s`, or +`mecatequi`. Deny rules and configured asks win in every mode, including +`yolo`. -|Posture|Behavior| -|-|-| -|`strict`|Default for interactive `mecated`; read-only calls are allowed and mutating calls use the permission rules, normally asking before they run. Project trust is not granted by the posture.| -|`trusted`|Interactive roots admit trusted project instructions and project permission allows, but mutating calls still use the normal approval rules.| -|`auto`|Enables the allow-all posture for the main agent and children while keeping deny rules and deliberately configured asks effective. Child substitution defenses remain enabled.| -|`yolo`|Extends `auto` by allowing child command substitutions, backticks, and heredoc-style substitutions that `auto` keeps behind the child safety floor. The main session also accepts external relative `../` paths for `Read`, `ListDir`, `Write`, and `Edit`. Use only for isolated, disposable, single-tenant deployments.| +|Mode|Edits|Shell and other mutations|Command substitution in subagents|Guardrails checker| +|-|-|-|-|-| +|`plan`|Denied|Denied|Not applicable, read-only|Optional| +|`default`|Ask|Ask|Ask|Optional| +|`accept-edits`|Allowed|Ask|Ask|Optional| +|`auto`|Allowed|Allowed|Adjudicated by a reviewer model when headless|Required, enforcing| +|`yolo`|Allowed|Allowed|Allowed|Optional, advisory only| -`--yolo`, `--trust-project`, and operator-global `posture:` settings can raise -the posture tier; the highest tier wins. A project file cannot raise it. +Whether Mecatl loads project instructions, rules, and allow rules is the +separate [project trust](#project-trust) decision; a permission mode never +grants it. Use `yolo` only for isolated, disposable, single-tenant deployments. -Deployment defaults differ: `mecated` is interactive and defaults to `strict`, -while unattended `mecak8s` and `mecatequi` deployments commonly select `auto`. -Check the individual command's help before assuming a default. +A main-session external path can be addressed absolutely in any mode, subject +to its escape permission decision. Only `yolo` also serves external relative +`../` paths for `Read`, `ListDir`, `Write`, and `Edit`; in every other mode, +those relative paths remain workspace-confined even after approval. Configured +denies and asks still apply. Pseudo-filesystems (`/proc`, `/sys`, `/dev`) are +never served through these tools. Children, namespace tools, `Glob`, and `Grep` +remain confined to the workspace; symlink escapes are not a substitute for `../`. -For example: +Every root defaults to `default`, except `mecak8s`, which defaults to `auto`. +The [`mecatequi` GitHub Action](/building/ci/mecatequi.md) also passes +`auto` unless you set its `permission-mode` input. ```sh -# Interactive, fail-closed default. -mecated serve --posture strict +# Interactive: honor this project's rules and auto-accept edits. +mecatui --workspace "$PWD" --permission-mode accept-edits --trust-project -# Unattended single-tenant deployment; review the trust implications first. -mecated serve --headless --posture auto +# Unattended single-tenant deployment with a guardrails checker. +mecated serve --headless --permission-mode auto --guardrails-model ``` -Allow-all postures are refused when running as root unless the deployment -explicitly declares an isolated sandbox with `MECATL_SANDBOX=1` or -`IS_SANDBOX=1`. - -A main-session external path can be addressed absolutely at any posture, subject -to its escape permission decision. Only `yolo` also serves external relative -`../` paths for `Read`, `ListDir`, `Write`, and `Edit`; below `yolo`, those -relative paths remain workspace-confined even after approval. Configured denies -and asks still apply. Pseudo-filesystems (`/proc`, `/sys`, `/dev`) are never -served through these tools. Children, namespace tools, `Glob`, and `Grep` remain -confined to the workspace; symlink escapes are not a substitute for `../`. - -## Choose a permission mode +To set a default for every launch, add the key to your user-global +`settings.yaml`: -A permission mode controls one session. It works alongside the deployment -posture and configured rules. - -|Mode|Behavior| -|-|-| -|`default`|Uses the normal permission policy: read-only work is usually allowed, while operations that can mutate state normally ask first.| -|`plan`|Exposes a read-only toolset so the model can inspect the workspace and prepare a plan without changing it.| -|`accept-edits`|Automatically allows `Edit` and `Write` when they would otherwise hit only the built-in approval floor. Shell commands and other tools still use the normal policy, and configured asks and denies still win.| - -In `mecatui`, press `shift+tab` to cycle the active session through **default → -plan → accept-edits → default**. You can remap the `ModeSwitch` action in the -client keymap or with `--keymap`; see [Keybindings](/mecatui/keybindings.md). - -To select the initial mode when launching `mecatui`, use `--mode`: - -```sh -mecatui --workspace "$PWD" --mode plan -mecatui --workspace "$PWD" --mode accept-edits +```yaml title="~/.config/mecatl/settings.yaml" +permissionMode: accept-edits ``` -The header displays the active mode. Changing modes does not bypass configured -permission rules or guardrails. +An explicit `--permission-mode` flag outranks the key. Mecatl ignores a +`permissionMode:` key in a project's `.mecatl/settings.yaml` and logs a warning, +so a repository cannot choose its own permission mode. `--trust-project` +combines with any mode: it supplies project trust, and at `plan`, `default`, or +`accept-edits` it also raises the posture to `trusted`. + +### What a mode sets + +Each mode sets two things: + +|Mode|New sessions start in|Server ceiling: the most permissive mode a session may switch to| +|-|-|-| +|`plan`|`plan`|`accept-edits`| +|`default`|`default`|`accept-edits`| +|`accept-edits`|`accept-edits`|`accept-edits`| +|`auto`|`auto`|`auto`| +|`yolo`|`yolo`|`yolo`| + +The ceiling is fixed at startup and applies to every session the server hosts. +Within it, a client can switch its own session, and the new mode takes full +effect: switching from `yolo` to `default` restores ordinary approvals. + +### Change the mode after launch + +In `mecatui`, press `shift+tab` to cycle through every mode: **default → plan → +accept-edits → auto → yolo → default**. You can remap the `ModeSwitch` action +in the client keymap or with `--keymap`; see +[Keybindings](/mecatui/keybindings.md). + +What happens when the cycle lands on a mode depends on the server ceiling: + +- **At or below the ceiling.** The mode applies to the session immediately. +- **Above the ceiling.** The mode is unavailable until the server restarts with + a higher `--permission-mode`. The session keeps its current mode, which the + header continues to show, and you can keep typing, sending, and queueing + prompts. The footer says what the mode needs. `auto` also needs an enforcing + guardrails checker, such as `guardrails.model` in your user-global + `settings.yaml`. Press `shift+tab` again to move on. + +The server also refuses `auto` when it has no enforcing guardrails checker. The +session keeps its current mode, and the footer gives the server's reason. + +A session waiting on an approval cannot change mode, except to recover from a +refused mode as described below. Answer the pending request first: it resolves under the mode it was raised in, and the new mode applies +from the next run. A mode change never stops a command that has already +started. + +If the server restarts with a lower ceiling, or without the checker `auto` +needs, a stored session in a mode it no longer admits refuses to run, resume an +approval, fork, or clear until its owner switches it to an admitted mode. Its +history is unchanged. If that session is waiting on an approval, switch it to an +admitted mode lower than its stored one: the server cancels the pending request +without running it, then applies the mode, and your next prompt continues the +conversation. Servers that support this advertise the `refused_mode_recovery` +feature identifier. + +The restart a ceiling change needs depends on how you connect: + +- With the embedded server, quit and relaunch `mecatui` with the new + `--permission-mode` value. +- Under `mecatui connect`, the server operator changes `mecated`'s + configuration and restarts it. Relaunching the client changes nothing. + +The `mecatui` header shows the session mode, which changes as you cycle; `yolo` +appears as a red `YOLO` pill. The top-right corner shows whether the server +trusts the project: `trusted` or +`untrusted`. Project trust is independent of the mode. An older server that +does not report trust shows no trust label. + +Press `?` in `mecatui` to see every mode, the two things each one sets, and the +restart that applies to your connection. + +### Check the resolved mode at startup + +Every root logs one `permission mode` line at startup. Its fields report: + +- `permission_mode`: the resolved mode. +- `session_mode` and `max_allowed_mode`: where new sessions start and the + server ceiling. +- `guardrails_checker`: exactly one of `enforcing`, `advisory`, or `disabled`, + with a `guardrails_checker_reason` when it is not enforcing. +- `subagent_ask_reviewer`: whether the + [subagent ask reviewer](#subagents-under-auto) is on, and why. + +`mecatui` writes its startup diagnostics to its log file rather than the +terminal. + +### Deprecated flags + +`--posture`, `--yolo`, the `mecatui` `--mode` flag, and the `posture:` settings +key still work and resolve to the equivalent mode. Each logs a deprecation +warning naming the `--permission-mode` replacement, and they will be removed in +a future release. Passing `--permission-mode` together with `--posture`, +`--yolo`, or `--mode` is a startup error. So is passing `--posture` or `--yolo` +while your operator `settings.yaml` sets `permissionMode:`. Drop the deprecated +flag to keep the settings key, or replace it with `--permission-mode`, which +overrides the key. `--trust-project` is not deprecated. + +## Auto needs an enforcing checker + +`auto` and `yolo` allow every tool without asking, which leaves a guardrails +checker as the only inspection of tool content. A server that starts in `auto` +refuses to start without an enforcing checker, and a session cannot be created +in or switched to `auto` without one. Configure one in either of these ways: + +- Set a checker with `--guardrails-model `. +- Bind the `guardrail` model slot, with `--model-slot guardrail=` or + `models.slots.guardrail` in `settings.yaml`. + +`--guardrails off` disables any configured checker, so `auto` refuses to start +with it. To run every tool with no checker, choose `yolo`. + +The `mecak8s` Helm chart and the `mecatequi` GitHub Action pass no guardrails +flag by default, so the checker configured in settings applies. Each can also +name a checker model or pass the kill-switch. See [Guardrails for mecak8s](/operating/mecak8s.md#choose-a-guardrails-checker) +and the [`mecatequi` action inputs](/building/ci/mecatequi.md#headless-posture-and-permission-asks). + +In a session running in `yolo` mode, a configured checker is advisory only. It +has no pre-tool veto, no approve-once human ask, and no fail-closed behavior when +the checker fails, so a checker outage looks the same as a clean result. The +startup line reports it as `advisory`. Choose `auto` when you want the checker to +block findings. On a server started with `yolo`, a session you switch to `auto` +or a lower mode keeps the checker enforcing. + +## Headless roots and project trust + +Without a trust source, every mode starts without loading project content. +`auto` and `yolo` also log a warning that project instructions and rules are +withheld; tools are still allowed without asking. The deprecated +`--posture trusted` refuses to start on a headless root with no trust source. + +## Subagents under auto + +Subagents, `Parallel` branches, and team members started from an `auto` or +`yolo` session run in that same mode. A team member whose agent definition sets +`permissionMode` keeps that mode instead. Children of a `plan`, `default`, or `accept-edits` session +run in `default` mode. + +Under `auto`, subagents keep the command-substitution guard that the main agent +loses. A subagent Shell command containing `$(...)`, backticks, or a subshell +that Mecatl cannot prove read-only is not run automatically, even though the +main agent would run the same command. `yolo` removes that guard for subagents +too. + +An interactive client receives these requests as ordinary approval prompts. A +headless `auto` or `yolo` deployment has nobody to ask, so the _subagent ask +reviewer_ is on by default there. The reviewer is a tool-free model that decides +a subagent permission request nobody else can answer. An approval applies only +to that request, and each decision spends tokens. + +The reviewer uses the `ask-reviewer` model slot, or the session model when the +slot is unbound. If neither resolves, subagent requests are denied and Mecatl +logs a warning naming the fix. To keep the reviewer off and deny these requests, +pass `--subagent-ask-reviewer off`. Configured deny and ask rules still win over +the reviewer. + +The reviewer is separate from the guardrails checker. The checker inspects tool +arguments and results; the reviewer decides pending subagent permission +requests. + +Clients can read the reviewer state from the server capabilities as +`subagent_ask_reviewer`: `on`, `off`, or `inert` when a reviewer is configured +but an interactive client answers the requests. An older server leaves it unset. +`mecatui`'s `/posture` shows the state when the server reports it. + +## Upgrade from the posture model + +Earlier releases fixed allow-all for the whole process with `--posture`. Allow-all +now belongs to each session's mode. This changes behavior that existing clients +and deployments can observe: + +- **Sessions created without a mode** start in the server's default mode, which + is `auto` or `yolo` on a server started that way. Previously, a gRPC session + created without a mode started in `default`. A client that cannot handle the + `auto` or `yolo` mode value in a session must pass an explicit mode. +- **An explicit `default` session gets ordinary approvals** even on a server + started with `auto` or `yolo`. Previously, such a session inherited the + process-wide allow-all. Request `auto` or `yolo` for autonomous sessions. +- **`auto` refuses to start without an enforcing checker**, including with + `--guardrails off`. A deployment that ran `--posture auto` without a checker + must configure one or choose `--permission-mode yolo`. The `mecak8s` chart + default and the `mecatequi` action default are `auto`, so they need the same + choice. +- **A mode never grants project trust.** `--permission-mode` no longer loads + project instructions and rules on interactive roots the way `--posture` did. + Pass `--trust-project` or add the workspace to `trustedWorkspaces`. +- **Stored sessions** keep their recorded mode. A session recorded in a mode the + upgraded server does not admit refuses to run until its owner switches it to + an admitted mode. If you roll back to a release without session-scoped + modes, that release still loads `auto` and `yolo` sessions but treats them + as `default`: they get ordinary approvals unless that server's own + `--posture` allows all tools, and gRPC reports them as `DEFAULT`. It keeps + the stored mode, so upgrading again restores it and admits it again. + +Clients detect a server with session-scoped modes through the +`session_permission_modes` feature identifier. The API major version is +unchanged. Against an older server without that feature, `mecatui` cycles only +`plan`, `default`, and `accept-edits`, and labels the header mode with the +server's `auto` or `yolo` posture, which still applies to every session there. ## Approve and constrain work @@ -248,14 +433,16 @@ Project trust controls whether Mecatl admits project-provided authority: - project skills, soul, and named agents; and - the read-only child shell and workspace inspection posture. -Trust can come from an explicit operator flag, a trusted-workspace setting, an -undrifted remembered trust decision, or the interactive posture floor. A -headless root does not gain project trust merely because it uses `trusted`, -`auto`, or `yolo`; it needs an explicit trust source. +Trust can come from `--trust-project`, a `trustedWorkspaces` entry in the +user-global `settings.yaml`, or an undrifted remembered trust decision. A +permission mode never grants trust; only the deprecated `--posture` and +`posture:` key still raise it on an interactive root. Without trust, Mecatl can +still read and search the workspace as data. See +[Headless roots and project trust](#headless-roots-and-project-trust). -On an untrusted headless checkout, `--posture auto` can allow admitted tools -without loading project steering or enabling a read-only child shell. `--trust-project` asserts that you trust the repository and its `.git` metadata. +Without trust, a headless `auto` deployment still allows admitted tools, but it +loads no project steering and gives read-only subagents no Shell. ## Guardrails @@ -331,8 +518,10 @@ replacement actions are not supported. - Permission rules and guardrails are different controls: an allow does not disable guardrails, and a guardrail advisory does not change the tool's permission result. -- `--posture`, `--trust-project`, and permission configuration on a local - `mecatui` invocation do not change a remote server used through `connect`. +- `--trust-project` and permission configuration on a local `mecatui` + invocation leave a remote server used through `connect` unchanged, and + `mecatui connect` refuses `--permission-mode auto` or `yolo`. The server + operator owns that server's ceiling. ## Next steps diff --git a/user-docs/features/sessions/scheduled-tasks.md b/user-docs/features/sessions/scheduled-tasks.md index f6b0647c42..c228c685b5 100644 --- a/user-docs/features/sessions/scheduled-tasks.md +++ b/user-docs/features/sessions/scheduled-tasks.md @@ -57,6 +57,16 @@ It defaults to read-leaning behavior. A schedule that may use `Edit`, `Write`, or `Shell` must explicitly set `mutating: true`; this is not an implicit allow-all mode. +A read-leaning schedule must set `mode` to plan. For a mutating schedule, the +fire's permission mode depends on how you created it: + +- The in-chat `Schedule` tool records the mode its creating session had. +- A gRPC or REST request uses its `mode` field. When `mode` is unspecified, as + in the `mecatui` overlay, each fire uses the server's default session mode. + +Every fire checks its mode against the server ceiling again. If the server has +since restarted with a lower ceiling, the fire fails without running. + For an independently created schedule, the server selects placement from its configured default. The create request has no workspace field. Use the HTTP listener for this REST workflow: diff --git a/user-docs/mecatui/keybindings.md b/user-docs/mecatui/keybindings.md index 6405a6a651..b13e5ca622 100644 --- a/user-docs/mecatui/keybindings.md +++ b/user-docs/mecatui/keybindings.md @@ -23,7 +23,7 @@ bindings and dims features that the connected server does not provide. |`esc`|Clear an active selection first. While work is running, cancel directly and preserve the draft, queued follow-ups, and steer. While idle with a paused queue, clear that queue but preserve the draft.| |`ctrl+t`|Open `/toolcalls` for the current session; approval requests use it for details.| |`f9`|Reveal conversation details: reasoning, turn stats, changed files, permanent error details, benign guardrail notices, and individual settled tool-call summaries. Use `ctrl+t` for tool arguments and results.| -|`shift+tab`|Cycle the current session permission mode: **default → plan → accept-edits → default**. In an MCP prompt argument form, it instead moves to the previous required field.| +|`shift+tab`|Cycle every permission mode: **default → plan → accept-edits → auto → yolo → default**. A mode above the server's ceiling keeps the current mode and your draft, and the footer says how to make it available. In an MCP prompt argument form, it instead moves to the previous required field.| |`ctrl+g`|Select all prompt text.| |`ctrl+y`|Copy the active prompt or visible conversation selection.| |`f6` / `f7` / `f8`|Open Agents / Effort / MCP Prompts.| diff --git a/user-docs/mecatui/status-line.md b/user-docs/mecatui/status-line.md index 732215b619..9154d0cb03 100644 --- a/user-docs/mecatui/status-line.md +++ b/user-docs/mecatui/status-line.md @@ -126,7 +126,7 @@ refreshes it. |JSON path|Type|Meaning| |-|-|-| -|`Version`|integer|Status input protocol version (currently `5`).| +|`Version`|integer|Status input protocol version (currently `6`).| |`Server.DisplayTarget`|string|Credential-free target shown by the client.| |`Server.ConnectionMode`|string|`embedded`, `connect`, or empty while unknown.| |`Session.Title`|string|Optional display title.| @@ -149,6 +149,7 @@ refreshes it. |`Workspace.Path`|string|Exact local root returned by the privileged local-context RPC. It is available to templates through their escaped projection and to a configured direct local status command. It is empty for remote, untrusted, no-FS, unavailable, and otherwise ineligible sessions.| |`Terminal.Rows`, `Terminal.Cols`|integers|Measured terminal dimensions.| |`Terminal.HeaderAvailCols`, `Terminal.FooterAvailCols`|integers|Columns remaining after the client reserves mandatory header and footer lanes.| +|`Terminal.Emoji`|boolean|Whether the terminal is likely to draw emoji such as ⚡️ at their expected width. The shipped header uses it to add a bolt before the `yolo` pill.| |`MainAgent.State`|string|`connecting`, `idle`, `thinking`, `running_tool`, `awaiting_approval`, `completed`, `failed`, or `cancelled`.| |`MainAgent.Activity`|string|Bounded display activity label.| |`MainAgent.Approval`|string|`none` or `awaiting`.| @@ -179,7 +180,12 @@ left-aligned by the renderer; footers are right-aligned. Text may be wrapped in these semantic tokens: `text`, `muted`, `primary`, `secondary`, `accent`, `success`, `warning`, -`error`, and `info`. +`error`, `info`, and `danger`. + +`danger` fills its text with a fixed red background that doesn't change with the +theme. It is the pill the default header uses for `yolo` mode. The fill covers +exactly the wrapped text, so include any padding spaces inside the element, for +example ` YOLO `. A link keeps its display text separate from its destination. Use the StatusML `` element instead of the HTML `` element. It must be a direct child of diff --git a/user-docs/mecatui/using-the-tui.md b/user-docs/mecatui/using-the-tui.md index 885fb67ed8..396b8df16e 100644 --- a/user-docs/mecatui/using-the-tui.md +++ b/user-docs/mecatui/using-the-tui.md @@ -218,9 +218,11 @@ for delegation behavior and the information available in `mecatui`. ## Change conversation settings -Press `shift+tab` to switch the active permission mode. See -[Choose a permission mode](/features/security-and-execution/permissions-and-posture.md#choose-a-permission-mode) -for the available modes and their behavior. +Press `shift+tab` to cycle through every permission mode. A mode up to the +server's ceiling applies immediately. A mode above it keeps the session in its +current mode and leaves your draft usable, and the footer says how to relaunch +with a higher `--permission-mode`. See +[Change the mode after launch](/features/security-and-execution/permissions-and-posture.md#change-the-mode-after-launch). |Command|Result| |-|-| @@ -229,7 +231,7 @@ for the available modes and their behavior. |`/compact`|Reduces model history while keeping the session and visible scrollback. Run it without arguments while idle. Creating a cascade summary can use model tokens.| |`/clear`|Creates an empty-history session with the same placement. It does not roll back workspace changes.| |`/session`|Shows path-free details for the active session.| -|`/posture`|Shows the server's operator posture and the independent effective checker state. Off includes setup guidance; unavailable posture status is unknown. See [Permissions and posture](/features/security-and-execution/permissions-and-posture.md).| +|`/posture`|Shows the server's operator posture, the independent effective checker state, and the subagent ask reviewer state when the server reports it. Off includes setup guidance; unavailable posture status is unknown. See [Permissions and posture](/features/security-and-execution/permissions-and-posture.md).| If `/clear` cancels an active run or approval and then fails to create the replacement, the original session remains selected and may be cancelled. Wait diff --git a/user-docs/operating/mecak8s.md b/user-docs/operating/mecak8s.md index 14e206e3a3..d916607a53 100644 --- a/user-docs/operating/mecak8s.md +++ b/user-docs/operating/mecak8s.md @@ -128,7 +128,7 @@ the values file if you need a fixed name. |Setting|Default| |-|-| |Replicas|Two| -|Posture|Headless with `auto` permissions| +|Permission mode|Headless `auto`, which needs an enforcing guardrails checker| |Session state|Redis| |Session ownership|Kubernetes Leases| |Filesystem|No filesystem access| @@ -139,6 +139,43 @@ Project-provided instructions, skills, agents, and read-only child Shell access still require explicit project trust. `mecak8s` does not include ACP, local JSONL storage, or the `mecated` operator subcommands. +### Choose a guardrails checker + +`auto` allows every tool without asking, so `mecak8s` refuses to start it +without an enforcing guardrails checker. The chart renders +`--permission-mode=`, which defaults to `auto`. Configure the +checker in one of two ways: + +- Set `guardrails.model`, which renders `--guardrails-model=`: + + ```yaml + guardrails: + model: + ``` + +- Leave `guardrails.model` empty and configure the checker in mounted + settings, with a `guardrails:` model or the `guardrail` model slot. The chart + then passes no guardrails flag, and `mecak8s` uses the settings checker. + +To run every tool with no checker, set `permissionMode: yolo`. +`guardrails.disabled: true` renders the `--guardrails=off` kill-switch, which +also disables a checker in mounted settings. Setting both `guardrails.model` and +`guardrails.disabled` fails rendering. + +The chart owns the permission mode. Rendering fails if `extraArgs` contains +`--posture`, `--permission-mode`, or `--yolo`; move that choice to +`permissionMode`. `--posture=strict` becomes `permissionMode: default`. + +Earlier chart versions always ran `auto` with `--guardrails=off`. Before you +upgrade such a release, configure a checker or set `permissionMode: yolo`, or +the new pods refuse to start. + +Because `mecak8s` is headless, the subagent ask reviewer is on by default. It +uses the `ask-reviewer` model slot or the session model to decide subagent +permission requests, and each decision spends tokens. Add +`--subagent-ask-reviewer=off` to `extraArgs` to keep it off. See +[Subagents under auto](/features/security-and-execution/permissions-and-posture.md#subagents-under-auto). + ## Production Helm chart The production chart is published at diff --git a/user-docs/operating/mecated.md b/user-docs/operating/mecated.md index 0d942f788c..54dbf7e8a4 100644 --- a/user-docs/operating/mecated.md +++ b/user-docs/operating/mecated.md @@ -69,7 +69,8 @@ To retain sessions across restarts and authenticate local clients: export MECATL_AUTH_TOKEN="" mecated serve \ --store-dir ~/.local/share/mecatl/sessions \ - --posture auto + --permission-mode auto \ + --guardrails-model ``` `--log-level` accepts `debug`, `info` (default), `warn`, or `error`. Startup @@ -78,10 +79,11 @@ logs the binary version with `msg="mecated starting"`. `--store-dir` enables local JSONL persistence. The path and its ancestors must be physical directories, not symlinks. On macOS, use `/private/...` instead of a path through the `/var` symlink. `--auth-token` requires the token on every -request and can also read `MECATL_AUTH_TOKEN`. `--posture auto` permits +request and can also read `MECATL_AUTH_TOKEN`. `--permission-mode auto` permits unattended calls within configured permission rules while retaining child -prompt-injection protections. Configured Ask and Deny rules still apply; project -trust is a separate grant. +prompt-injection protections. It refuses to start without a guardrails checker +unless you pass `--guardrails off`. Configured Ask and Deny rules still apply; +project trust is a separate grant. Before binding a non-loopback address, add TLS and caller authentication. See [Secure and expose mecated](/operating/mecated/secure-and-expose.md). diff --git a/user-docs/operating/mecated/secure-and-expose.md b/user-docs/operating/mecated/secure-and-expose.md index 0f1d2ac7c2..8462507c67 100644 --- a/user-docs/operating/mecated/secure-and-expose.md +++ b/user-docs/operating/mecated/secure-and-expose.md @@ -48,28 +48,35 @@ and caller identifiers. See [Caller identity and OIDC](/features/security-and-execution/caller-identity.md) for the full behavior. -## Posture - -|Flag|Notes| -|-|-| -|`--posture strict`|Default approval posture; configured permission rules apply| -|`--posture trusted`|Honor a project's ALLOW rules (alias: `--trust-project`)| -|`--posture auto`|Allow unattended calls within configured rules; retain child injection defense| -|`--posture yolo`|Also disable child injection defense. Isolated single-tenant only. Refused as root without `MECATL_SANDBOX=1`| - -On a **headless** root (`--headless`), posture never raises `TrustProject`. -Explicit `--trust-project`, `trustedWorkspaces:`, or undrifted remembered trust -admits repository steering and the read-only child shell. Without a trust -source, `--posture auto` does not grant either access merely because the -directory contains `.git`. See -[Permissions and posture](/features/security-and-execution/permissions-and-posture.md#project-trust) -for the trust sources and headless behavior. - -See +## Permission mode + +`--permission-mode` defaults to `default`. It accepts `plan`, `default`, +`accept-edits`, `auto`, or `yolo`, and it sets the mode new sessions start in +and the server ceiling, the most permissive mode a session can switch to. The +operator-global `settings.yaml` `permissionMode:` key sets the same value; the +flag overrides it. A mode never grants project trust; pass `--trust-project` or +add the workspace to `trustedWorkspaces:`. See +[Choose a permission mode](/features/security-and-execution/permissions-and-posture.md#choose-a-permission-mode) +for what each mode allows. + +`mecated` refuses to start with `auto` unless an enforcing guardrails checker is +configured: pass `--guardrails-model` or bind the `guardrail` model slot. +`--guardrails off` or an advisory-only checker does not satisfy `auto`; choose +`yolo` to run without a checker. If you lower the ceiling or remove the checker, +sessions stored in a mode the server no longer admits stay intact but refuse to +run until their owner switches them to an admitted mode. + +On a headless root, `auto` and `yolo` start without a trust source but load no +project steering and give read-only subagents no Shell, because `.git` is not +vouched. Headless `auto` and `yolo` also turn on the subagent ask reviewer; pass +`--subagent-ask-reviewer off` to keep it off. See +[Subagents under auto](/features/security-and-execution/permissions-and-posture.md#subagents-under-auto). +`auto` and `yolo` are refused as root without `MECATL_SANDBOX=1`. + +`--posture`, `--yolo`, and the `posture:` key are deprecated aliases that log a +warning. See [Permissions and posture](/features/security-and-execution/permissions-and-posture.md) -for the full rule engine. Posture is read from the operator-global -`settings.yaml` (`posture:` key) and overridden by the CLI flag when both are -set. +for the full rule engine. ## Guardrails diff --git a/user-docs/reference/configuration.md b/user-docs/reference/configuration.md index 2038f1a5aa..598e22fbf3 100644 --- a/user-docs/reference/configuration.md +++ b/user-docs/reference/configuration.md @@ -74,11 +74,21 @@ OPERATOR-TIER LLM content-checker (issue #27). Parsed strictly. A project-tier g | `guardrails.rules[].prompt` | `string` | `(empty)` | Prompt adds operator task-risk context beneath the fixed harness safety, provenance, evidence, and structured-output rubric; it cannot replace it. | | `guardrails.rules[].failClosed` | `bool` | `false` | FailClosed optionally overrides the fail-closed global default for this rule. | +## `permissionMode` + +Tier: **operator** + +OPERATOR-TIER named permission mode: plan, default, accept-edits, auto, or yolo. Each token sets two things: the mode new sessions start in, and the server ceiling, fixed at startup, which is the most permissive mode a session may switch to (accept-edits for plan, default, and accept-edits). auto refuses to start without an enforcing guardrails checker; yolo needs none. A mode never grants workspace trust. An explicit --permission-mode out-ranks this key, and it out-ranks the deprecated posture: key. A project-tier permissionMode: is IGNORED with a WARN. Empty = keep the CLI/default. + +| Value | Type | Default | Description | +| --- | --- | --- | --- | +| `permissionMode` | `string` | `(empty)` | PermissionMode is the OPERATOR-TIER named permission-mode token (plan/default/accept-edits/auto/yolo). It sets the mode new sessions start in and the server ceiling (the most permissive mode a session may switch to) together, and supersedes the deprecated posture: key. It never grants workspace trust. Like Posture it is honoured ONLY from the user-global + CLI tiers; a project-tier occurrence is IGNORED with a WARN. Empty = absent. The composition layer parses the token. | + ## `posture` Tier: **operator** -OPERATOR-TIER posture-ladder scalar: strict < trusted < auto < yolo (the graduated trust/automation tier). A project-tier posture: is IGNORED with a WARN (a project cannot raise the automation posture). Empty = keep the CLI/default. +DEPRECATED: use permissionMode: instead; still honoured with a WARN for one release. OPERATOR-TIER posture-ladder scalar: strict < trusted < auto < yolo (the graduated trust/automation tier). A project-tier posture: is IGNORED with a WARN (a project cannot raise the automation posture). Empty = keep the CLI/default. | Value | Type | Default | Description | | --- | --- | --- | --- | diff --git a/user-docs/reference/grpc-api.md b/user-docs/reference/grpc-api.md index 96987284bc..8943d60356 100644 --- a/user-docs/reference/grpc-api.md +++ b/user-docs/reference/grpc-api.md @@ -604,10 +604,10 @@ data into the model or dedicated session debugger. ### Client controls and posture -The embedded server accepts `--yolo` with the same allow-all semantics, -root-user refusal, and `MECATL_SANDBOX`/`IS_SANDBOX` environment checks as -`mecated`. Connect mode rejects `--yolo` because the external server owns its -posture. See +The embedded server accepts `--permission-mode` with the same modes, checker +requirement, root-user refusal, and `MECATL_SANDBOX`/`IS_SANDBOX` environment +checks as `mecated`. Connect mode accepts only `plan`, `default`, and +`accept-edits` because the external server owns its posture. See [Permissions and posture](/features/security-and-execution/permissions-and-posture.md). Client built-in commands remain available when workspace command expansion is diff --git a/user-docs/reference/grpc-schema.md b/user-docs/reference/grpc-schema.md index cc67fff6ab..877a58e0a9 100644 --- a/user-docs/reference/grpc-schema.md +++ b/user-docs/reference/grpc-schema.md @@ -39,7 +39,7 @@ the bidi Converse stream that drives one agent run. | **RPC:** `ListGuardrailCoverage`
**Request:** `ListGuardrailCoverageRequest`
**Response:** `ListGuardrailCoverageResponse` | Client streamingNo | Server streamingNo | | | **RPC:** `GetGuardrailReviewDetail`
**Request:** `GetGuardrailReviewDetailRequest`
**Response:** `GetGuardrailReviewDetailResponse` | Client streamingNo | Server streamingNo | | | **RPC:** `GetSessionTranscript`
**Request:** `GetSessionTranscriptRequest`
**Response:** `GetSessionTranscriptResponse` | Client streamingNo | Server streamingNo | DescriptionGetSessionTranscript returns the authoritative, snapshot-derived human transcript for one owned session. It is read-only and does not use EventLog. | -| **RPC:** `SetMode`
**Request:** `SetModeRequest`
**Response:** `SetModeResponse` | Client streamingNo | Server streamingNo | DescriptionSetMode changes an existing session's permission posture. The session aggregate remains authoritative: a mid-turn change is rejected with InvalidArgument, so clients that want "next prompt" semantics must defer and retry once idle. | +| **RPC:** `SetMode`
**Request:** `SetModeRequest`
**Response:** `SetModeResponse` | Client streamingNo | Server streamingNo | DescriptionSetMode changes an existing session's permission posture. The session aggregate remains authoritative: a mid-turn change is rejected with InvalidArgument, so clients that want "next prompt" semantics must defer and retry once idle. One exception, advertised by the refused_mode_recovery feature: for a stored session awaiting an approval in a mode the server no longer admits, a change to an admitted, strictly lower mode cancels the pending turn without running it and applies the mode. | | **RPC:** `CloseSession`
**Request:** `CloseSessionRequest`
**Response:** `CloseSessionResponse` | Client streamingNo | Server streamingNo | DescriptionCloseSession ends a session and releases its server-side resources (learned permission rules, bound placement, and any per-session engine). Idempotent: closing an unknown or already-closed session via the wire returns NotFound only for a never-created id; an already-released session succeeds. | | **RPC:** `RenameSession`
**Request:** `RenameSessionRequest`
**Response:** `RenameSessionResponse` | Client streamingNo | Server streamingNo | DescriptionRenameSession explicitly replaces an idle main session's title. | | **RPC:** `DeleteSession`
**Request:** `DeleteSessionRequest`
**Response:** `DeleteSessionResponse` | Client streamingNo | Server streamingNo | DescriptionDeleteSession physically removes an idle main session and its sidecars. | @@ -730,7 +730,7 @@ CreateSessionRequest opens a new session. | Field | Type | Label | Oneof | Description | |---|---|---|---|---| -| `mode` | `PermissionMode` | | | mode is the permission posture; unspecified defaults to DEFAULT. | +| `mode` | `PermissionMode` | | | mode is the permission posture; unspecified uses the server's configured default session mode, which is DEFAULT unless the operator chose a permission mode that starts sessions elsewhere. | | `limits` | `Limits` | | | limits are the optional stop conditions. | | `provider_id` | `string` | | | provider_id selects which configured provider backs this session, by its stable registry id ("openai" / "openrouter"). Empty => the server default provider (today's behaviour). An unknown/unavailable id is a loud InvalidArgument, never a silent fallback. The provider is FIXED for the session lifetime (reasoning-replay + byte-stable prefix are provider-private); "switch provider" = new session. Keys are NEVER on the wire — only this id. (multi-provider Phase 0, S3.) | | `model_id` | `string` | | | model_id is the opaque model selector within provider_id. Empty => the provider's default model. An id ListModels did not advertise is passed through to the provider VERBATIM (a power-user escape hatch for a model the embedded catalog doesn't yet know). NOT slash-joined with provider_id — two distinct fields by design. Setting model_id WITHOUT provider_id is a loud InvalidArgument (a bare model on an env-derived default provider is ambiguous). | @@ -3044,7 +3044,7 @@ old clients ignore and new clients reading an old server see as false. | `soul` | `bool` | | | soul is true when a soul source is wired (a soul was selected at startup, so GetSoul returns its snapshot). Gates the /soul read-only inspection panel. | | `user_model` | `bool` | | | user_model is true when the user-model store is wired (GetUserModel reads its live index). Gates the /memory read-only inspection panel. | | `model_selection` | `bool` | | | model_selection is true when >=1 provider is available (ListModels would return >0). Gates the client's /models picker the same way `agents` gates /agents. Additive: an older server leaves it false and the client hides the picker. (multi-provider Phase 0, S3.) | -| `posture` | `string` | | | posture is the SERVER-WIDE operator posture-ladder tier ("strict"/"trusted"/ "auto"/"yolo"). It is CHROME ONLY — a client renders an honest "⚠ auto"/"⚠ yolo" badge so an operator can see the daemon's automation posture at a glance — and is NEVER session state (it does not change per session; permission MODE is the per-session knob). String passthrough, no enum on the wire (the EvNoProgress / StopBudget discipline). Additive: an older server leaves it empty and the client shows no badge (strict/trusted also show no badge). | +| `posture` | `string` | | | posture is the SERVER-WIDE operator posture-ladder tier ("strict"/"trusted"/ "auto"/"yolo"). It bounds which session modes a client may cycle to and is NEVER session state (it does not change per session; permission MODE is the per-session knob). String passthrough, no enum on the wire (the EvNoProgress / StopBudget discipline). Additive: an older server leaves it empty. | | `worktrees` | `bool` | | | worktrees is true when a WorktreeLister is wired (ListWorktrees may return a non-empty list for a real git repo). Gates the client's /worktrees overlay — the first-class operator workflow for binding a session to an EXISTING sibling git worktree (issue #102). Additive: an older server leaves it false and the client hides the overlay. A no-FS/cloud server (no lister wired) also yields false, so the feature is honestly absent there. | | `scheduling` | `bool` | | | scheduling is true when a ScheduleStore is reachable (the schedule RPCs are functional: Create/List/Get/Pause/Resume/Delete/FireNow/GetFire/ListFires). Gates the client's /schedule overlay. Independent of the scheduler tick loop (--scheduler): the store-backed RPCs work without the tick loop. Additive: an older server leaves it false and the client hides the overlay. | | `reflection` | `bool` | | | reflection is true when explicit completed-session reflection is available. | @@ -3061,6 +3061,8 @@ old clients ignore and new clients reading an old server see as false. | `mcp_connector_status` | `bool` | | | mcp_connector_status requires a wired broker inspector, enforced ownership and a verified caller. It does not enable direct MCP resources or prompts. | | `mcp_refresh` | `bool` | | | mcp_refresh is true when direct/global MCP source reconciliation is wired. It is mutually exclusive with workspace_enrollment in a valid deployment. | | `execution_templates` | `bool` | | | execution_templates is true only when an authenticated template catalog is wired; an eligible inventory may still be empty. | +| `project_trusted` | `bool` | optional | | project_trusted reports whether the server trusts its launch workspace's project content (rules, instructions, soul). It is an axis separate from the permission mode. CHROME ONLY. Additive: an older server leaves it unset and the client shows no trust indicator. | +| `subagent_ask_reviewer` | `string` | optional | | subagent_ask_reviewer reports the subagent ask reviewer, a model that may approve a subagent's, team member's, or parallel branch's permission request that no human can answer: "on" (enabled), "off" (not enabled), or "inert" (configured, but an interactive approver answers instead). Clients treat other values as unknown. CHROME ONLY. Additive: an older server leaves it unset and the client shows nothing. | @@ -3243,7 +3245,7 @@ SetModeRequest changes a session's permission posture. | Field | Type | Label | Oneof | Description | |---|---|---|---|---| | `session_id` | `string` | | | session_id is the session to update. | -| `mode` | `PermissionMode` | | | mode is the desired permission posture; unspecified defaults to DEFAULT. | +| `mode` | `PermissionMode` | | | mode is the desired permission posture; unspecified defaults to DEFAULT. AUTO requires an enforcing guardrails checker; the server rejects SetMode to AUTO without one. The server may also enforce a MaxAllowedMode ceiling; requesting a mode above it is rejected with INVALID_ARGUMENT. | @@ -4102,7 +4104,8 @@ its prose. PermissionMode is the session-wide permission posture, mirroring session.PermissionMode. Plan mode exposes a read-only toolset; acceptEdits -auto-accepts edits. +auto-accepts edits; auto runs autonomously with an enforcing guardrails +checker; yolo runs gate-free with no enforcing checker. | Name | Number | Description | |---|---|---| @@ -4110,6 +4113,8 @@ auto-accepts edits. | `PERMISSION_MODE_DEFAULT` | `1` | PERMISSION_MODE_DEFAULT is the standard deny -> ask -> allow posture. | | `PERMISSION_MODE_PLAN` | `2` | PERMISSION_MODE_PLAN enforces a read-only toolset. | | `PERMISSION_MODE_ACCEPT_EDITS` | `3` | PERMISSION_MODE_ACCEPT_EDITS auto-accepts edits. | +| `PERMISSION_MODE_AUTO` | `4` | PERMISSION_MODE_AUTO runs autonomously using an enforcing guardrails checker. Entering this mode requires a checker; the server rejects SetMode to AUTO without one. | +| `PERMISSION_MODE_YOLO` | `5` | PERMISSION_MODE_YOLO runs gate-free with no enforcing checker. Advisory monitoring may remain. Configured deny rules still apply. | #### `mecatl.v1.RetryDisposition` @@ -4437,7 +4442,7 @@ ScheduleState; the two halves together form a Schedule. | `trigger` | `TriggerSpec` | | | trigger is the firing trigger (cron XOR one_shot). | | `selector` | `ScheduleProviderSelector` | | | selector selects the provider+model the fires run on. A zero value means the deployment default. | | `profile` | `string` | | | profile is the session tool-surface profile ("" default, "no-fs" file-less). | -| `mode` | `PermissionMode` | | | mode is the session permission posture. Reuses the PermissionMode enum from harness.proto (same package — proto allows cross-file refs within a package). | +| `mode` | `PermissionMode` | | | mode is the session permission posture. Reuses the PermissionMode enum from harness.proto (same package — proto allows cross-file refs within a package). A non-mutating schedule must use PLAN. For a mutating schedule, unspecified means each fire uses the server's configured default session mode at fire time; it reads back as unspecified. Every fire checks the mode against the server's mode ceiling and fails if the ceiling no longer allows it. | | `limits` | `Limits` | | | limits are the bounded per-fire budgets. Reuses the Limits message from harness.proto. They are PER-FIRE: each fire gets a fresh session with these limits. | | `mutating` | `bool` | | | mutating is the explicit write opt-in. The DEFAULT is false (read-leaning), the same conservative default as a subagent. | | `max_fires` | `int32` | | | max_fires bounds the TOTAL number of fires for a cron schedule (0 = forever). Cron-only: a one-shot fires once by definition and max_fires is ignored. | diff --git a/user-docs/reference/server-cli.md b/user-docs/reference/server-cli.md index 2f0b8dfd36..438154eb90 100644 --- a/user-docs/reference/server-cli.md +++ b/user-docs/reference/server-cli.md @@ -364,6 +364,17 @@ Flags: --permission-config value path to a repeatable YAML permission configuration. CLI rules have highest precedence; configured deny and ask rules always override allows. + --permission-mode string + permission mode, one of: plan, default, accept-edits, auto, yolo. Sets two + things: the mode new sessions start in, and the process-wide server ceiling, + fixed at startup, which is the most permissive mode a session may switch to + (accept-edits for plan, default, and accept-edits). Default: default. auto and + yolo allow tools without asking. auto needs an enforcing guardrails checker (-- + guardrails-model or the guardrail model slot) and refuses to start without one; + yolo runs without one. Both are refused for root unless MECATL_SANDBOX=1 or + IS_SANDBOX=1. Deny rules and configured ask rules still apply. A mode never + grants project trust; use --trust-project. Replaces the deprecated --posture + and --yolo. --permissions-conventional load project and user permission settings for each session. Enabled by default. Project allow rules require --trust-project; project deny and ask rules always @@ -372,11 +383,12 @@ Flags: approve plan-mode results automatically in headless mode. Interactive sessions and non-plan requests are unaffected. Disabled by default. --posture string - permission posture: strict prompts mutations; trusted applies project allow - rules; auto allows tools and relaxes main shell substitutions; yolo also runs - child command substitutions. Deny rules and configured ask rules still apply. - strict is the default. --trust-project and --yolo select trusted and yolo. auto - and yolo are refused for root outside MECATL_SANDBOX. + DEPRECATED: use --permission-mode. Permission posture: strict prompts + mutations; trusted applies project allow rules; auto allows tools and relaxes + main shell substitutions; yolo also runs child command substitutions. Deny + rules and configured ask rules still apply. strict is the default. --trust- + project and --yolo select trusted and yolo. auto and yolo are refused for root + outside MECATL_SANDBOX. --product-metrics send anonymous product-adoption metrics to Stacklok: version, OS and architecture, enabled features, and coarse session, run, and tool-call counts. @@ -482,7 +494,9 @@ Flags: --subagent-ask-reviewer string model identifier or --model-alias for reviewing child permission requests in headless mode. An allow applies only to that request; denied or failed reviews - deny it. Empty disables reviews. + deny it. Empty leaves reviews off, except that a headless auto or yolo + permission mode turns them on by default (ask-reviewer model slot, else the + session model); off disables reviews explicitly. --subagent-ask-reviewer-max-denies int maximum consecutive denied, failed, or timed-out child-permission reviews before later requests are automatically denied. An allowed review resets the @@ -554,10 +568,11 @@ Flags: default session workspace root (default "") --yolo - alias for --posture=yolo. Allows tools server-wide and lets child command - substitutions run automatically. Deny rules and configured ask rules still - apply. Intended only for isolated, ephemeral, single-tenant environments. - Refused for root unless MECATL_SANDBOX=1 or IS_SANDBOX=1. + DEPRECATED: use --permission-mode yolo. Alias for --posture=yolo. Allows tools + server-wide and lets child command substitutions run automatically. Deny rules + and configured ask rules still apply. Intended only for isolated, ephemeral, + single-tenant environments. Refused for root unless MECATL_SANDBOX=1 or + IS_SANDBOX=1. ``` ## mecak8s @@ -804,14 +819,26 @@ Usage: mecak8s [flags] 5s) --permission-config value Trusted permission YAML file. Repeatable + --permission-mode string + Permission mode, one of: plan, default, accept-edits, auto, yolo. Sets two + things: the mode new sessions start in, and the process-wide server ceiling, + fixed at startup, which is the most permissive mode a session may switch to + (accept-edits for plan, default, and accept-edits). Default: auto. auto and + yolo allow tools without asking and require MECATL_SANDBOX when running as + root. auto needs an enforcing guardrails checker (--guardrails-model or the + guardrail model slot) and refuses to start without one, including with -- + guardrails off; yolo runs without one. Deny rules and configured ask rules + still apply. A mode never grants project trust; use --trust-project. Replaces + the deprecated --posture --permissions-conventional Discover project and user permission settings for each session (default true) --posture string - Permission posture: strict prompts for mutations; trusted honors project allow - rules; auto allows tools by default and relaxes main shell substitutions while - child injection defenses remain enabled; yolo also runs child command - substitutions automatically. Deny rules and configured ask rules still apply. - auto and yolo require MECATL_SANDBOX when running as root (default "auto") + DEPRECATED: use --permission-mode. Permission posture: strict prompts for + mutations; trusted honors project allow rules; auto allows tools by default and + relaxes main shell substitutions while child injection defenses remain enabled; + yolo also runs child command substitutions automatically. Deny rules and + configured ask rules still apply. auto and yolo require MECATL_SANDBOX when + running as root (default "auto") --product-metrics Report anonymous product-adoption metrics to Stacklok. Reports version, OS/architecture, enabled features, and aggregate counts, never prompts, file @@ -888,7 +915,9 @@ Usage: mecak8s [flags] Path to a user persona file. Empty uses the conventional location --subagent-ask-reviewer string Model ID or alias for reviewing child permission requests in headless mode. - Empty disables the reviewer + Empty leaves the reviewer off, except that the auto and yolo permission modes + turn it on by default (ask-reviewer model slot, else the session model); off + disables it explicitly --subagent-ask-reviewer-max-denies int Consecutive non-allow outcomes that disable the reviewer for the run. Values less than or equal to zero use the default (default 3) @@ -923,7 +952,8 @@ Usage: mecak8s [flags] override is always proxy mode (default "auto") --trust-project Honor a discovered project's allow rules. Deny and ask rules are always - honored; equivalent to --posture=trusted + honored; raises the posture to at least trusted and combines with --permission- + mode --user-model-dir string Directory for the user model. Empty uses the conventional location --workspace string diff --git a/user-docs/reference/typescript-sdk-api/core.md b/user-docs/reference/typescript-sdk-api/core.md index 5c5e8b6059..bea6524e9b 100644 --- a/user-docs/reference/typescript-sdk-api/core.md +++ b/user-docs/reference/typescript-sdk-api/core.md @@ -4445,6 +4445,14 @@ readonly modelSelection: boolean; readonly posture: string; ``` +ServerCapabilities.projectTrusted + +Whether the server trusts its launch workspace; absent from older servers. + +```ts +readonly projectTrusted?: boolean; +``` + ServerCapabilities.reflection ```ts @@ -4505,6 +4513,14 @@ readonly storageCleanup: boolean; readonly storageHealth: boolean; ``` +ServerCapabilities.subagentAskReviewer + +Subagent ask reviewer state: `"on"`, `"off"`, or `"inert"`; treat other values as unknown. Absent from older servers. + +```ts +readonly subagentAskReviewer?: string; +``` + ServerCapabilities.teams ```ts @@ -7483,8 +7499,10 @@ ServerFeature: { readonly HttpSteer: "http_steer"; readonly McpServersOnCreate: "mcp_servers_on_create"; readonly PromptFreeControls: "prompt_free_controls"; + readonly RefusedModeRecovery: "refused_mode_recovery"; readonly ServerInfo: "server_info"; readonly SessionActivityInventory: "session_activity_inventory"; + readonly SessionPermissionModes: "session_permission_modes"; readonly SkillFiles: "skill_files"; readonly WatchSessionEvents: "watch_session_events"; } @@ -7521,6 +7539,8 @@ SessionMode: { readonly Default: 1; readonly Plan: 2; readonly AcceptEdits: 3; + readonly Auto: 4; + readonly Yolo: 5; } ```