diff --git a/.claude/plans/steer-while-running/tasks/01-engine-steer-inbox.md b/.claude/plans/steer-while-running/tasks/01-engine-steer-inbox.md new file mode 100644 index 0000000000..7aeedd768d --- /dev/null +++ b/.claude/plans/steer-while-running/tasks/01-engine-steer-inbox.md @@ -0,0 +1,45 @@ +--- +id: 01-engine-steer-inbox +title: Engine steer inbox + Step 2a turn-boundary drain +blocked_by: [] +status: done +branch: "plan-steer-while-running/01-engine-steer-inbox" +worktree: "" +issue: "512" +retries: 0 +last_error: "" +accumulator: feat/steer-while-running +--- + +# Task brief + +Implement the engine core of steer-while-running: a `Run`-scoped, in-memory +steer inbox on the agent loop, drained at the existing Step 2a turn-boundary +seam in `Engine.runLoop` (`engine/agent/loop.go`) — the same provider-legal +point `injectBackgroundNotice`/`drainPendingDelivery` use (history always ends +on a user prompt / tool result / nudge, never inside a tool_use pair). The +drained steer is recorded via `recordContinuation` (`RecordUserPrompt` + the +log-only `EvUserPrompt`), so it replays cleanly, survives +`session.ValidateToolPairing` and compaction, and rehydrates under ADR-0038. +This task is the injection + recording path only; the supersede/cancel +contract, the wire frame, and composition are later tasks. Offline tests via +`engine/adapter/mockllm` + `engine/adapter/memfs`. + +## Acceptance criteria + +- AC1.1: A steer enqueued while a run is mid-flight is recorded as an ordinary user message at the next turn boundary, appears in `Conversation.Messages`, and is replayed to the model on the following turn. + - verify: `TestSteer_InjectedAtTurnBoundary` +- AC1.2: The steer is recorded via `recordContinuation`, so the durable log captures it (log-only `EvUserPrompt`) and `eventsource.Fold` reconstructs the user turn across rehydration. + - verify: `TestSteer_RecordedAndRehydrated` +- AC1.3: The steer is appended *after* the settled tool results, never inside a `tool_use` pair — `session.ValidateToolPairing` holds on the post-injection history. + - verify: `TestSteer_PreservesToolPairing` +- AC1.4: The injected conversation keeps the message prefix byte-stable within the run (fragments assembled once, conversation appended), so the steer costs no prompt-cache rebuild beyond normal history growth. + - verify: `TestSteer_KeepsPromptPrefixByteStable` +- AC1.5: A run with an empty inbox drives exactly as before (the drain is a no-op) — no behavioural change when steer is unused. + - verify: `TestSteer_EmptyInboxNoOp` +- AC1.6: A steer drained at a boundary where compaction also fires is still replayed **verbatim** to the model on the following turn — the compaction recent-user back-snap keeps the just-drained user message out of the summarized head. + - verify: `TestSteer_SurvivesCompactionBoundary` +- AC1.7: A pending steer that trips the turn-limit / token-budget brake at the next boundary is recorded (durable history) and the run terminates `StopMaxTurns`/`StopBudget` normally; the recorded steer is addressed by the *next* run — it is never silently dropped and never bypasses `BeginTurn`'s bound. + - verify: `TestSteer_BrakeTerminalKeepsRecorded` +- AC1.8: A pending (un-drained) steer is **best-effort, in-memory, and lost with the run** on a process crash/session `Abandon` — the explicit honest contract; it is NOT persisted across restart. + - verify: `TestSteer_PendingSteerLostOnRestart` diff --git a/.claude/plans/steer-while-running/tasks/02-steer-supersede-contract.md b/.claude/plans/steer-while-running/tasks/02-steer-supersede-contract.md new file mode 100644 index 0000000000..4554af1729 --- /dev/null +++ b/.claude/plans/steer-while-running/tasks/02-steer-supersede-contract.md @@ -0,0 +1,39 @@ +--- +id: 02-steer-supersede-contract +title: Steer supersede / cancel single-slot contract + drain echo +blocked_by: [01-engine-steer-inbox] +status: done +branch: "plan-steer-while-running/02-steer-supersede-contract" +worktree: "" +issue: "512" +retries: 0 +last_error: "" +accumulator: feat/steer-while-running +--- + +# Task brief + +Build the single-slot supersedable contract on the steer inbox from task 01. +At most one steer is pending per run: a second `EnqueueSteer` supersedes the +pending one; `CancelSteer` retracts a pending one; the boundary drain commits +it. Outcomes are an enum (`accepted`/`superseded`/`retracted`/`none_pending`/ +`too_late`), not booleans (AGENTS.md "a third provenance ⇒ extract an enum"). +The drain emits `EvSteer` carrying the committed text (the authoritative +echo). Engine is authoritative; the client renders the echoed truth. All +transitions safe under concurrent access (run goroutine drains while a wire +handler enqueues) — run under `-race`. Offline (`mockllm`/`memfs`). + +## Acceptance criteria + +- AC2.1: At most one steer is pending per run; a second `steer` supersedes the first, and only the superseding text is ever drained. + - verify: `TestSteer_SingleSlotSupersede` +- AC2.2: `steer_cancel` on a pending steer retracts it; the run drains nothing and the next turn sees no injected message. + - verify: `TestSteer_CancelRetractsPending` +- AC2.3: A `steer` that arrives *after* the boundary drained (or after the run went terminal) reports `too_late` / not-live — never silently drained into a finished turn. + - verify: `TestSteer_TooLateNotDrained` +- AC2.4: The drain emits `EvSteer` carrying the committed text; on a supersede race the echo reflects the version actually drained. + - verify: `TestSteer_DrainEmitsCommittedEcho` +- AC2.5: Supersede/cancel/drain are safe under concurrent access (the run goroutine drains while a wire handler enqueues) — no data race, no double-drain. + - verify: `TestSteer_InboxConcurrentSafe` (run under `-race`) +- AC2.6: The recorded steer, the streamed `EvSteer` echo, and the message the model sees are the same text — the recorded == streamed == model-view invariant holds. + - verify: `TestInvariant_recorded_streamed_model_view` diff --git a/.claude/plans/steer-while-running/tasks/03-steer-terminal-race-promote.md b/.claude/plans/steer-while-running/tasks/03-steer-terminal-race-promote.md new file mode 100644 index 0000000000..5854635457 --- /dev/null +++ b/.claude/plans/steer-while-running/tasks/03-steer-terminal-race-promote.md @@ -0,0 +1,35 @@ +--- +id: 03-steer-terminal-race-promote +title: Lost terminal race → auto-promote to a fresh follow-up run +blocked_by: [01-engine-steer-inbox] +status: done +branch: "plan-steer-while-running/03-steer-terminal-race-promote" +worktree: "" +issue: "512" +retries: 0 +last_error: "" +accumulator: feat/steer-while-running +--- + +# Task brief + +Service/loop behaviour: when a steer arrives for a session whose run is +already terminal (the user's "still running" belief lagged the real state), +auto-promote the steer into a fresh follow-up run through the existing hardened +run-entry funnel — `loadAndReopen` + the lease + recover-if-terminal +(`internal/adapter/server/service.go` `StartRunContent`) — never silently +dropping the text. Promotion reuses the reopen/recover discipline; it never +starts a run on an unrepaired terminal session. A live run → enqueue to its +inbox (task 01). The `too_late`/not-live signal is what the service promotes +on. Note: task 05 owns the wire frame; this task owns the Service routing +decision helper (`IsLive`/state → enqueue vs promote) and its tests, callable +from the wire handler. + +## Acceptance criteria + +- AC3.1: A steer arriving on a live run is enqueued to that run's inbox (not promoted). + - verify: `TestSteer_LiveRunEnqueues` +- AC3.2: A steer arriving after the run went terminal is promoted to a fresh follow-up run that drives through `loadAndReopen` (recovering the terminal state) and is *not* silently dropped. + - verify: `TestSteer_TerminalRacePromotes` +- AC3.3: Promotion reuses the run-entry funnel (recover-if-completed / interrupt-if-cancelled / recover-if-failed) — it never starts a run on an unrepaired terminal session. + - verify: `TestSteer_PromotionUsesRunEntryFunnel` diff --git a/.claude/plans/steer-while-running/tasks/04-steer-awaiting-queue-only.md b/.claude/plans/steer-while-running/tasks/04-steer-awaiting-queue-only.md new file mode 100644 index 0000000000..094bd0afad --- /dev/null +++ b/.claude/plans/steer-while-running/tasks/04-steer-awaiting-queue-only.md @@ -0,0 +1,32 @@ +--- +id: 04-steer-awaiting-queue-only +title: Steer while awaiting an ask — queue-only, drain on resume +blocked_by: [01-engine-steer-inbox] +status: done +branch: "plan-steer-while-running/04-steer-awaiting-queue-only" +worktree: "" +issue: "512" +retries: 0 +last_error: "" +accumulator: feat/steer-while-running +--- + +# Task brief + +Loop behaviour: while a run is parked `awaiting` on a permission or plan ask, +the loop is suspended in `PauseForApproval` and is not iterating `runLoop`, so +a steer cannot be drained until the ask resolves. The inbox holds the pending +steer across the parked state; `driveFromAwaiting`/`resumeFromAwaiting` +re-enters `runLoop`, whose Step 2a drain picks it up at the resumed run's +first turn boundary. The ask still requires an explicit verdict — the steer is +purely additive input (the "yes-and"/"no-and" case), never a verdict and never +a bypass. See the awaiting run-entry seam (AGENTS.md) and ADR-0069. + +## Acceptance criteria + +- AC4.1: A steer submitted while a run is parked `awaiting` is held, not rejected and not delivered early. + - verify: `TestSteer_AwaitingAskIsHeld` +- AC4.2: On verdict-driven resume, the held steer is drained at the resumed run's first turn boundary and replayed to the model. + - verify: `TestSteer_AwaitingResumeDrains` +- AC4.3: The held steer does not resolve, modify, or bypass the pending ask — the ask still requires an explicit verdict. + - verify: `TestSteer_AskStillRequiresVerdict` diff --git a/.claude/plans/steer-while-running/tasks/05-wire-grpc-steer-frame.md b/.claude/plans/steer-while-running/tasks/05-wire-grpc-steer-frame.md new file mode 100644 index 0000000000..823e5ef3e1 --- /dev/null +++ b/.claude/plans/steer-while-running/tasks/05-wire-grpc-steer-frame.md @@ -0,0 +1,37 @@ +--- +id: 05-wire-grpc-steer-frame +title: Wire — gRPC Converse steer frame + ServerCapabilities + EvSteer projection +blocked_by: [02-steer-supersede-contract] +status: done +branch: "plan-steer-while-running/05-wire-grpc-steer-frame" +worktree: "" +issue: "512" +retries: 0 +last_error: "" +accumulator: feat/steer-while-running +--- + +# Task brief + +The wire surface. `contracts/proto/mecatl/v1/harness.proto`: add `Steer` + +`SteerCancel` messages, a `steer` / `steer_cancel` oneof arm on +`ConverseRequest` (alongside `prompt`/`resume_approval`/`cancel`/ +`cancel_child`), a `ServerCapabilities.steer` bit, and the `EvSteer` echo on +the event stream. `task generate` regenerates `contracts/gen` (commit it; do +NOT hand-edit). Engine app: an `Engine.Steer`/`Run.EnqueueSteer` entry point +the gRPC handler drives, and the `EvSteer` projection. Server: the `Converse` +handler routes steer frames to the live run's inbox and relays the outcome to +the client. The capability bit is computed once in `Service.capabilities()` +(the single-composition-intersection invariant) and consistent across every +sink. gRPC-only v1 — no HTTP/SSE, no ACP (both deferred; see plan Out of +scope). This task widens the engine exported surface → `task api:update` + +`engine/CHANGELOG.md` note (Added = minor) is REQUIRED. + +## Acceptance criteria + +- AC5.1: A client can send a `steer` frame mid-run on the `Converse` stream and observe the injected message + the `EvSteer` echo on the same stream. + - verify: `TestSteer_ConverseFrameRoundTrip` +- AC5.2: `ServerCapabilities` advertises `steer` true when the feature is enabled and false/absent when not; an old server (no field) reads as false. The bit is computed **once** in composition (`Service.capabilities()`) and is consistent across every sink that surfaces it — never recomputed per sink. + - verify: `TestSteer_CapabilityAdvertised`; `TestSteer_CapabilitySingleSource` +- AC5.3: `task generate` keeps `contracts/gen` in sync; the new oneof arm does not change the behaviour of existing arms. + - verify: inspection — `buf generate` output committed; existing Converse control frames unchanged. diff --git a/.claude/plans/steer-while-running/tasks/06-composition-and-tui-flip.md b/.claude/plans/steer-while-running/tasks/06-composition-and-tui-flip.md new file mode 100644 index 0000000000..ea4f97c39e --- /dev/null +++ b/.claude/plans/steer-while-running/tasks/06-composition-and-tui-flip.md @@ -0,0 +1,37 @@ +--- +id: 06-composition-and-tui-flip +title: Composition gate + capability flip + mecatui steer mode +blocked_by: [05-wire-grpc-steer-frame] +status: done +branch: "plan-steer-while-running/06-composition-and-tui-flip" +worktree: "" +issue: "512" +retries: 0 +last_error: "" +accumulator: feat/steer-while-running +--- + +# Task brief + +The composition gate + the only client-facing piece. The steer inbox is armed +by a posture/run-level knob resolved in `internal/app` (posture ladder), wired +through `app.Build` into the engine deps AND the `ServerCapabilities.steer` bit; +default ON. mecatui reads the `steer` capability: when present, `enter` mid-run +sends a `steer` frame and the queue card reflects the authoritative echoed/acked +state (pending → sent → too-late-promoted); when absent, keep the #228 local +merge-queue byte-identical. The client-side merge (join staged lines with a +blank line) happens BEFORE send so the engine sees one frame; `↑` edits the +combined message; the engine's single-slot supersede is the replace path for an +already-sent-but-un-drained steer (do NOT conflate the two). Golden files shift +for the card → `task test:golden`. + +## Acceptance criteria + +- AC6.1: With steer enabled (default), the capability is advertised and a mid-run input reaches the model without a separate follow-up run. + - verify: `TestSteer_EnabledByDefaultEndToEnd` +- AC6.2: With steer disabled, the engine inbox is inert and the TUI falls back to the client-side terminal queue unchanged. + - verify: `TestSteer_DisabledFallsBackToLocalQueue` +- AC6.3: The TUI renders the merged pending message as a single item, reflects the echoed/acked state honestly (pending vs sent vs promoted-after-race), and `↑` edits the combined message. + - verify: `TestSteer_TUIRendersAuthoritativeState` (+ `task test:golden` for the card) +- AC6.4: `go run ./cmd/mecademo` still prints a full offline session (no behavioural regression with steer unused). + - verify: demonstration — `go run ./cmd/mecademo` diff --git a/.claude/plans/steer-while-running/tasks/07-wire-routing-and-send-safety-repair.md b/.claude/plans/steer-while-running/tasks/07-wire-routing-and-send-safety-repair.md new file mode 100644 index 0000000000..4bbf7f198c --- /dev/null +++ b/.claude/plans/steer-while-running/tasks/07-wire-routing-and-send-safety-repair.md @@ -0,0 +1,91 @@ +--- +id: 07-wire-routing-and-send-safety-repair +title: Repair — gRPC steer routing single-owner + concurrent-Send hazard + dead exports +blocked_by: [05-wire-grpc-steer-frame] +status: done +branch: "plan-steer-while-running/07-wire-routing-and-send-safety-repair" +worktree: "" +issue: "512" +retries: 0 +last_error: "" +accumulator: feat/steer-while-running +repair_of: panel-review ship-blockers +--- + +# Task brief + +Panel-review repair wave (round 1). Fix the ship-blockers the panel found in +the steer wire path. The accumulator `feat/steer-while-running` has tasks 01–06 +merged. Branch `plan-steer-while-running/07-wire-routing-and-send-safety-repair` +off the CURRENT HEAD. Commit ONLY this task's files. Do NOT push. + +## Ship-blockers to fix + +1. **Concurrent `stream.Send` (gRPC stream is not goroutine-safe).** In + `internal/adapter/server/grpc.go`: the original run's `relayRun` (grpc.go:238) + Sends until the run's event channel CLOSES, but `closeSteer()` runs at the top + of `terminate` (`engine/agent/loop.go:2264`, `:2317`) BEFORE `drainChildren` and + the terminal `EvResult` drain. A steer landing in that window → `too_late` → + `handleSteerFrame` spawns `drivePromotedSteer` on a new goroutine (grpc.go:359) + whose `relayRun` (grpc.go:388) ALSO `stream.Send`s while the original relay is + still draining → two goroutines Send on one stream. FIX: serialize all Sends on + the Converse stream behind a single send mutex (or a single-writer lane) shared + by the original relay, the promoted relay, the RecoverNotice pre-send, and the + steer-ack interleave. No two goroutines may Send concurrently. Add a regression + test that a terminal-race steer during the drain window does not race the + original relay (`-race`). + +2. **Lost-steer in the terminate window.** Same window: `Service.Steer`'s promote + path calls `StartRunContent` which hits `IsLive(id)` (the original run is still + registered) → `ErrFailedPrecondition`, so the ack is `too_late`/`promoted=false` + and the steer is DROPPED — contradicting the "never silently dropped" contract + (`harness.proto` Steer comment, `SteerAck.promoted`). FIX: make the terminate- + window steer reliably promote (e.g. `drivePromotedSteer` retries/waits until the + original run deregisters, bounded) OR hold the steer until the inbox closes AND + the run is no longer live, then promote. The honest contract: a steer is either + accepted into a live run OR promoted to a follow-up run — never dropped without + a loud ack AND a follow-up run. + +3. **Single-owner routing (architecture HIGH).** `handleSteerFrame` calls + `run.EnqueueSteer` directly and only calls `Service.Steer` for the promoted + half; the `SteerCancel` arm calls `run.CancelSteer` directly with NO Service + path. The "live-enqueue vs terminal-race promote" decision has ONE owner: + `Service.Steer`. FIX: move the whole route into `Service.Steer` / a new + `Service.CancelSteer`; keep the grpc handler a dumb frame→Service mapper + (mirroring how `Approve`/`Cancel` route through the Service). The deferred + HTTP/SSE endpoint must be able to reuse this without re-deriving logic. + +4. **Dead exported engine vars (api-gate ossification).** + `ErrSteerDisabled`/`ErrSteerPending` in `engine/agent` are exported, in + `engine/api/agent.txt`, CHANGELOG'd, but nothing returns them (their own doc + comments admit it). FIX: delete both vars, regenerate `engine/api/agent.txt` via + `task api:update`, and update `engine/CHANGELOG.md`. (Note: removing an + already-added symbol before release is still Added=minor net — it never shipped + in a release.) + +5. **Optional-but-recommended (reuse MEDIUM): `steerInbox` → `chan string` cap 1.** + Replace the hand-rolled `sync.Mutex` + `pending/has/closed` bools in + `engine/agent/steer.go` with a `chan string` (cap 1) + a `chan struct{}` close + signal, using `select` for enqueue/cancel/drain. Shorter, race-free by design, + expresses the single-slot contract in the type system. Only do this if it stays + strictly internal (the exported `SteerOutcome` enum + `EnqueueSteer`/`CancelSteer` + signatures must not change) — otherwise skip and note why. + +## Acceptance criteria (map to the plan's contract; the named tests prove them) + +- AC-R1: All Converse-stream Sends are serialized; a terminal-race steer during the + drain window never races the original relay. verify: `TestSteer_NoConcurrentSendOnTerminalRace` (`-race`) +- AC-R2: A steer arriving in the terminate window is promoted to a follow-up run + (or accepted), never dropped with a bare `too_late` ack. verify: `TestSteer_TerminateWindowPromotes` +- AC-R3: The grpc handler routes steer + steer_cancel through `Service` (no direct + `run.EnqueueSteer`/`run.CancelSteer` in grpc.go). verify: `TestSteer_ServiceOwnsRouting` +- AC-R4: `ErrSteerDisabled`/`ErrSteerPending` are gone from the exported surface. + verify: `task api:check` green + `git grep -c ErrSteerDisabled engine/` reports 0 +- AC-R5: `task lint`, `task test` (both modules, `-race`), `task api:update` all green. + verify: gates + +Run `cd engine && go test ./agent/ -run TestSteer -race` and +`go test ./internal/adapter/server/ -run TestSteer -race` from root, then the full +gates. Respect the layering rule; the routing decision stays in the Service layer. +Report the branch, `git log --oneline`, tests added, and which of 1–5 you did NOT +complete (with why). diff --git a/.claude/plans/steer-while-running/tasks/08-rework-round2-engine-inbox.md b/.claude/plans/steer-while-running/tasks/08-rework-round2-engine-inbox.md new file mode 100644 index 0000000000..36b56b82d3 --- /dev/null +++ b/.claude/plans/steer-while-running/tasks/08-rework-round2-engine-inbox.md @@ -0,0 +1,59 @@ +--- +id: 08-rework-round2-engine-inbox +title: "Rework: engine mutex inbox (drop supersede) + clean-exit continue-run" +blocked_by: [] +status: done +branch: "plan-steer-while-running/08-engine-inbox-rework" +worktree: "" +issue: "512" +retries: 0 +last_error: "" +accumulator: feat/steer-while-running +round: 2 +--- + +# Task brief + +Engine rework per the Ozz-review decisions (HANDOFF.md). Branch +`plan-steer-while-running/08-engine-inbox-rework` off the CURRENT +`feat/steer-while-running` HEAD. Commit only this task's files. Do NOT push. + +Two engine changes: + +1. **Mutex inbox + DROP SUPERSEDE** (`engine/agent/steer.go`). Replace the + cap-1-channel `steerInbox` with a `sync.Mutex` + `{closed bool, pending string, + has bool}` triple. Transitions, each ONE critical section: + - enqueue: `closed → SteerTooLate`; `has → SteerSlotFull` (NEW outcome — reject, + "cancel first"); else `pending,has = text,true → SteerAccepted`. + - cancel: take+clear → `SteerRetracted` / `SteerNonePending`. + - drain: take+clear → commit. + - close: `closed = true`. + DELETE the supersede path entirely (`SteerSuperseded` is removed from the + `SteerOutcome` enum). Add `session.ToValidUTF8` repair in `enqueueSteer` BEFORE + the text enters the inbox (history==echo==model-view, two-layer UTF-8 rule). + Update `EnqueueSteer`/`CancelSteer` exported wrappers accordingly. Deterministic + liveness tests forcing drain/close between observe & mutate (the finding-#1 + deadlock shape). + +2. **Clean-exit continue-run** (`engine/agent/loop.go` `finishTurnNoTools` + + `runLoop`). Before a clean terminal (`terminateComplete` for meaningful-text / + benign stop), re-check the inbox: if a steer is PARKED, do NOT terminate — + continue the loop so the steer drains at the next Step 2a (the run extends until + the steer is consumed; the never-drop contract holds engine-internally). Mirror + the `bgPendingNudged` defer pattern but steer blocks clean exit *until drained* + (not once-only). A parked steer must never be closed-unconsumed by + `closeSteer()`. + +## Acceptance criteria (named tests, offline mockllm/memfs, `-race`) + +- R2-1: enqueue on a full slot returns `slot_full`, leaves the pending steer intact; no supersede. verify: `TestSteer_SlotFullRejects` +- R2-2: the supersede path is gone — `SteerSuperseded` is not in the enum and no second-enqueue-replaces path exists. verify: `TestSteer_NoSupersedePath` +- R2-3: enqueue/cancel/drain/close are each one critical section; a forced drain-between-observe-and-mutate cannot deadlock or double-commit. verify: `TestSteer_InboxLinearizable` (`-race`) +- R2-4: invalid-UTF-8 steer text is repaired at ingress; the recorded history, the EvSteer echo, and the model-visible message are all valid UTF-8 and identical. verify: `TestSteer_IngressUTF8Repaired` +- R2-5: a steer enqueued while a meaningful-text no-tool-call answer is streaming is NOT dropped: the clean exit defers, the steer drains at the next boundary, the model acts on it. verify: `TestSteer_CleanExitContinuesRun` +- R2-6: a parked steer is never closed-unconsumed on any terminate path. verify: `TestSteer_NeverClosedParked` + +Regenerate `engine/api/agent.txt` via `task api:update` (enum change) + +`engine/CHANGELOG.md` note. Run `cd engine && go test ./agent/ -run TestSteer -race` +then `task lint` + `task test`. Report branch, git log, tests added, any R2-x not +satisfied. diff --git a/.claude/plans/steer-while-running/tasks/09-rework-round2-wire-and-handoff.md b/.claude/plans/steer-while-running/tasks/09-rework-round2-wire-and-handoff.md new file mode 100644 index 0000000000..794fb78d4b --- /dev/null +++ b/.claude/plans/steer-while-running/tasks/09-rework-round2-wire-and-handoff.md @@ -0,0 +1,57 @@ +--- +id: 09-rework-round2-wire-and-handoff +title: "Rework: proto message_id + slot_full + sequential active-run handoff" +blocked_by: [08-rework-round2-engine-inbox] +status: done +branch: "plan-steer-while-running/09-wire-handoff-rework" +worktree: "" +issue: "512" +retries: 0 +last_error: "" +accumulator: feat/steer-while-running +round: 2 +--- + +# Task brief + +Wire + server rework per the Ozz-review decisions (HANDOFF.md). Branch +`plan-steer-while-running/09-wire-handoff-rework` off the CURRENT +`feat/steer-while-running` HEAD (post task 08). Commit only this task's files. Do +NOT push. + +Three changes: + +1. **Proto** (`contracts/proto/mecatl/v1/harness.proto`): add `string message_id` + to `Steer`, `SteerCancel`, `SteerAck`, `SteerEcho` (client-minted, + server-echoed). Replace `STEER_OUTCOME_SUPERSEDED` with `STEER_OUTCOME_SLOT_FULL` + in the `SteerOutcome` enum. NO `expected_run_id` (the wire has no run-id + concept). `task generate` regenerates `contracts/gen` — commit it. + +2. **Sequential active-run handoff** (`internal/adapter/server/grpc.go` + + `service.go`). Replace `go h.drivePromotedSteer(...)` (an unjoined second relay + that orphans the promoted run when `Converse` returns and races `runRelay.sendErr`) + with a SEQUENTIAL handoff: after the original run's relay drains, if a steer was + promoted, `Converse` relays the promoted run on the SAME stream (reusing the + single-writer `streamSender`) before returning, and `readControl`'s control + target (`run`) swaps ATOMICALLY to the promoted run at the same moment (so + ResumeApproval/Cancel/CancelChild hit the active run — fixing finding #4 too). + `runRelay.sendErr` gets a single owner (no cross-goroutine access). The promoted + run's terminal EvResult + its `steer.outcome{promoted:true}` ack must be relayed + on the stream BEFORE the RPC returns (no ack-after-close). + +3. **Service updates** (`service.go`): `Service.Steer`/`CancelSteer` carry/echo + `message_id` end-to-end; the `slot_full` outcome maps to the proto enum; the + authorize-first (GetSession) ordering is preserved. Remove any supersede + handling in the wire path. + +## Acceptance criteria (named tests, offline, `-race`) + +- R3-1: `message_id` round-trips: a steer sent with `message_id` echoes it back on the ack AND the EvSteer echo; a client can correlate. verify: `TestSteer_MessageIdRoundTrip` +- R3-2: `slot_full` maps to the proto enum on a second enqueue while one's pending. verify: `TestSteer_SlotFullOnWire` +- R3-3: a promoted steer relays its full event stream + terminal ack on the SAME stream before Converse returns (no orphan, no ack-after-close). verify: `TestSteer_PromotedRelaySequential` +- R3-4: after promote, ResumeApproval/Cancel target the PROMOTED run, not the terminal original. verify: `TestSteer_ControlTargetsPromotedRun` +- R3-5: `runRelay.sendErr` has a single owner — no data race between original + promoted relays. verify: `TestSteer_RelaySendErrSingleOwner` (`-race`) +- R3-6: `task generate` + `task api:update` committed; existing Converse arms unchanged. verify: inspection + +Run `task lint` + `task test` + `task generate` + `task api:update`. Report branch, +git log, tests added, any R3-x not satisfied. diff --git a/.claude/plans/steer-while-running/tasks/10-rework-round2-tui.md b/.claude/plans/steer-while-running/tasks/10-rework-round2-tui.md new file mode 100644 index 0000000000..662616adc9 --- /dev/null +++ b/.claude/plans/steer-while-running/tasks/10-rework-round2-tui.md @@ -0,0 +1,64 @@ +--- +id: 10-rework-round2-tui +title: "Rework: mecatui queue→one bundle, cancel-then-recompose edit, queued-until-landed render" +blocked_by: [09-rework-round2-wire-and-handoff] +status: done +branch: "plan-steer-while-running/10-tui-rework" +worktree: "" +issue: "512" +retries: 0 +last_error: "" +accumulator: feat/steer-while-running +round: 2 +--- + +# Task brief + +mecatui rework per the Ozz-review decisions (HANDOFF.md). Branch +`plan-steer-while-running/10-tui-rework` off the CURRENT `feat/steer-while-running` +HEAD (post task 09). Commit only this task's files. Do NOT push. + +The client model (resolves finding #6 + the multi-message confusion): + +1. **Queue → one bundle.** The client keeps a LIST of staged messages; on send it + collapses them into ONE steer bundle and mints a fresh `message_id`. Server + inbox stays single-slot ⇒ at most ONE bundle outstanding (no "cancel-latest vs + edit-earlier" confusion). + +2. **Optimistic `↑` edit = cancel-then-recompose.** On `↑`: issue + `steer_cancel{message_id}` for the outstanding bundle (do NOT block on the ack), + immediately pull the whole not-yet-drained set into the textarea as ONE editable + blob, let the user edit (earlier steers included — they're one un-drained set), + resend as ONE new bundle with a fresh `message_id`. A drained `message_id` + (EvSteer echo seen) is BURNED — resend after a drain is a NEW submission, never a + confused re-edit. Late ack reconciles: `retracted`=clean edit, + `none_pending`=drained→"shipped" treatment (the text went out; the edit is a + fresh draft). NO synchronous wait on the cancel ack. + +3. **Queued-until-landed rendering.** The steer shows as a PENDING card at the + bottom until the `EvSteer` echo lands, then appears IN CONTEXT at its true + position (the echo is stream-positioned: after the settled tool results, before + the next `EvTurnStart`). The client renders the ECHO — never infers position by + text-matching history. The pending card clears when the echo lands; the landed + line appears at the boundary. Fixes the edit-back duplication bug + (`update.go:2256`) — `↑` no longer leaves the old steer in place to be merged on + resend. + +4. **Fallback unchanged.** When `Capabilities.Steer` is false, the #228 local + merge-queue stays byte-identical. + +Update the card golden (`task test:golden`) and the relevant assertions (the +`Contains` asserts at `steer_test.go` that masked the duplication bug must become +exact-equality). + +## Acceptance criteria + +- R4-1: a sent steer carries a fresh `message_id`; the TUI renders the ack/echo by id, ignoring stale ones. verify: `TestSteer_TUIIgnoresStaleAck` +- R4-2: `↑` cancels the outstanding bundle and pulls the whole not-yet-drained set into one editable blob; resend collapses to one new bundle with a NEW id. verify: `TestSteer_TUIEditCancelThenRecompose` +- R4-3: resend after `↑` does NOT duplicate the old text (exact-equality assertion). verify: `TestSteer_TUIEditBackNoDuplicate` +- R4-4: a drained `message_id` is burned — editing after the echo opens a fresh draft, and the "shipped" state is shown. verify: `TestSteer_TUIBurnedIdFreshDraft` +- R4-5: the card shows pending until the echo lands, then the landed line appears in context. verify: `TestSteer_TUIQueuedUntilLanded` (+ `task test:golden`) +- R4-6: capability-absent → the #228 local queue is byte-identical. verify: `TestSteer_DisabledFallsBackToLocalQueue` (existing, must stay green) + +Run `task lint` + `task test` + `task test:golden`. Report branch, git log, tests +added, any R4-x not satisfied. diff --git a/.claude/plans/steer-while-running/tasks/11-rework-round2-docs.md b/.claude/plans/steer-while-running/tasks/11-rework-round2-docs.md new file mode 100644 index 0000000000..ca62b1102c --- /dev/null +++ b/.claude/plans/steer-while-running/tasks/11-rework-round2-docs.md @@ -0,0 +1,55 @@ +--- +id: 11-rework-round2-docs +title: "Rework: architecture.md section + ADR-0027 List 1/2 + ADR-0114/plan supersede-drop & message_id" +blocked_by: [10-rework-round2-tui] +status: done +branch: "plan-steer-while-running/11-docs-rework" +worktree: "" +issue: "512" +retries: 0 +last_error: "" +accumulator: feat/steer-while-running +round: 2 +--- + +# Task brief + +Doc-lifecycle rework per the Ozz-review decisions (HANDOFF.md). Branch +`plan-steer-while-running/11-docs-rework` off the CURRENT `feat/steer-while-running` +HEAD (post task 10). Commit only this task's files. Do NOT push. + +1. **`docs/architecture.md`** — add a living "how it works" section for + steer-while-running: the run-scoped mutex inbox (single-slot, `slot_full` — + supersede dropped), the Step 2a drain, the sequential active-run handoff, the + promote-grace, the `message_id` correlation. (AGENTS.md requires the living doc + move with behavior.) + +2. **ADR-0027 (`docs/adr/0027-cloud-native.md`)** — add the resource-inventory + rows the steer work introduces: + - **List 1** (outlives one call): the `Run`-scoped `steerInbox` (mutex+state, + lives as long as the run), the `steerAcks` lane, the `streamSender` gate, the + promote-path handoff. + - **List 2** (restart fidelity): the pending (un-drained) steer is **restart-lost + BY DESIGN** — record the explicit fidelity decision (reset-by-design: in-memory, + dies with the run; only a recorded steer survives as durable history). + +3. **ADR-0114** (`docs/adr/0114-steer-while-running.md`) — update for the decisions + made after it was written: supersede dropped in favour of `slot_full` (replace = + cancel-then-resend), `message_id` correlation, the sequential active-run handoff, + the clean-exit continue-run rule, the queued-until-landed rendering rule. (ADR + content is frozen-once-accepted; since ADR-0114 is still pre-merge on this + branch, update it in place to match the shipped design — do NOT contradict + silently.) + +4. **Acceptance plan** (`docs/acceptance/steer-while-running.md`) — update the + scope-cut bullets that say "a second enqueue supersedes" → "a second enqueue + while one's pending is rejected (`slot_full`) — cancel first", and the + correlation/echo descriptions for `message_id`. Keep the 27 AC ids stable; only + reword the behaviour text where the design changed (supersede → slot_full). + +5. **user-docs** (`user-docs/what-you-get/mecatui.md`) — adjust the steer paragraph + for the queued-until-landed rendering and the cancel-then-recompose edit (`↑` + pulls the whole not-yet-drained set back as one blob). + +Run `task docs` (llms.txt regen + matlatl strict) and `task site:build`. Report +branch, git log, files changed, any item not done. diff --git a/.claude/plans/steer-while-running/tasks/12-append-default-engine.md b/.claude/plans/steer-while-running/tasks/12-append-default-engine.md new file mode 100644 index 0000000000..1997dde827 --- /dev/null +++ b/.claude/plans/steer-while-running/tasks/12-append-default-engine.md @@ -0,0 +1,54 @@ +--- +id: 12-append-default-engine +title: "Round 3: engine append-default enqueue (accepted/appended), drop SUPERSEDED+slot_full" +blocked_by: [] +status: done +branch: "" +worktree: "" +issue: "512" +retries: 0 +last_error: "" +accumulator: feat/steer-while-running +round: 3 +--- + +# Task brief + +Engine rework (round 3) per HANDOFF.md "Round 3" section. Branch +`plan-steer-while-running/12-append-default-engine` off the CURRENT +`feat/steer-while-running` HEAD. Commit only this task's files. Do NOT push. + +Change `engine/agent/steer.go` `enqueueSteer` from reject-on-full to +append-default: + +- `closed → SteerTooLate` +- `has → APPEND: pending = pending + "\n\n" + text, report NEW outcome SteerAppended` +- `else → SteerAccepted` (park as the pending bundle) + +DELETE `SteerSlotFull` and `SteerSuperseded` from the `SteerOutcome` enum (append +covers both). Add `SteerAppended`. The single-slot invariant is preserved (one +pending bundle); the slot just grows by append. The mutex discipline is unchanged +(each transition one critical section). + +Regenerate `engine/api/agent.txt` via `task api:update` + `engine/CHANGELOG.md` +note (enum change: SUPERSEDED/SLOT_FULL removed, APPENDED added — Changed, +pre-v1). + +Downstream-compat: the wire mapping (`internal/adapter/server/grpc.go` +`steerOutcomeToProto`) and the proto enum reference `slot_full`/`superseded` — +update those references so `task test` stays green repo-wide (the full proto +rework is task 13; here keep the tree compiling: map `SteerAppended` to a sensible +proto value and drop the removed engine enum references). The TUI's +`client.SteerSlotFull` may need a temporary mapping too. + +## Acceptance criteria (offline mockllm/memfs, `-race`) + +- R5-1: enqueue on an empty slot → `accepted`, parks the text. verify: `TestSteer_AcceptedOnEmpty` +- R5-2: enqueue on an occupied slot → `appended`, the pending text is `old + "\n\n" + new` (one bundle, merged). verify: `TestSteer_AppendedMerges` +- R5-3: `SteerSlotFull` and `SteerSuperseded` are gone from the engine enum. verify: `TestSteer_NoSlotFullOrSuperseded` +- R5-4: append preserves the single-slot invariant and the mutex linearizability (concurrent append/drain/close, `-race`). verify: `TestSteer_AppendLinearizable` +- R5-5: an appended bundle drains as ONE merged user continuation at the boundary (recordContinuation + EvSteer carry the merged text). verify: `TestSteer_AppendedDrainMerged` + +Run `cd engine && go test ./agent/ -run TestSteer -race`, then `task lint` + +`task test` + `task api:update`. Report branch, git log, tests added, any R5-x +not satisfied. diff --git a/.claude/plans/steer-while-running/tasks/13-watermark-wire.md b/.claude/plans/steer-while-running/tasks/13-watermark-wire.md new file mode 100644 index 0000000000..afa94122e4 --- /dev/null +++ b/.claude/plans/steer-while-running/tasks/13-watermark-wire.md @@ -0,0 +1,51 @@ +--- +id: 13-watermark-wire +title: "Round 3: proto appended outcome + Service watermark message_id (tail-on-drain)" +blocked_by: [12-append-default-engine] +status: done +branch: "plan-steer-while-running/12-append-default-engine" +worktree: "" +issue: "512" +retries: 0 +last_error: "" +accumulator: feat/steer-while-running +round: 3 +--- + +# Task brief + +Wire/server rework (round 3) per HANDOFF.md "Round 3". Branch +`plan-steer-while-running/13-watermark-wire` off the CURRENT +`feat/steer-while-running` HEAD (post task 12). Commit only this task's files. Do +NOT push. + +1. **Proto** (`contracts/proto/mecatl/v1/harness.proto`): the `SteerOutcome` enum + drops `STEER_OUTCOME_SUPERSEDED` and `STEER_OUTCOME_SLOT_FULL`, adds + `STEER_OUTCOME_APPENDED`. `message_id` stays SINGLE on all four messages (no + repeated ids). `task generate` regenerates `contracts/gen` — commit it. + +2. **Service watermark correlation** (`internal/adapter/server/service.go`): the + `steerMsgIDs` FIFO changes from text-match to an ORDERED id-list per pending + bundle. On each accepted/appended steer, append the frame's `message_id` to the + bundle's list. On the `EvSteer` drain echo, `SteerEcho.message_id` = the LATEST + (tail) contributing send's id — a WATERMARK the client splits its ordered queue + on. The engine stays text-only; the id lives only at this wire-correlation + layer. Clear the list on fresh-run `register()` (as today). Update the + `steer echo uncorrelated` WARN to fire when a drain finds NO tracked ids (the + genuine miss), not on a text-match fail. + +3. **Server mapping** (`grpc.go` `steerOutcomeToProto` + the sendEvent EvSteer + stamp): map `SteerAppended` → `STEER_OUTCOME_APPENDED`; the echo's message_id + comes from the watermark lookup (tail id), not the text-match pop. + +## Acceptance criteria (offline, `-race`) + +- R6-1: proto enum has APPENDED, no SUPERSEDED/SLOT_FULL. verify: `TestSteer_ProtoEnumAppend` +- R6-2: two appends (m1 then m2) into one bundle → the drain echo's message_id = m2 (the tail/watermark). verify: `TestSteer_WatermarkEchoLatestId` +- R6-3: a single-send bundle drains → echo id = that send's id. verify: `TestSteer_WatermarkSingleSend` +- R6-4: after a drain, the id-list is consumed; the next bundle starts a fresh list (no stale watermark). verify: `TestSteer_WatermarkFreshAfterDrain` +- R6-5: a drain with NO tracked id (id-less steer) fires the uncorrelated WARN. verify: `TestSteer_UncorrelatedWarnOnMissingIds` +- R6-6: `task generate` + `task api:update` committed; existing arms unchanged. verify: inspection + +Run `task lint` + `task test` + `task generate` + `task api:update`. Report branch, +git log, tests added, any R6-x not satisfied. diff --git a/.claude/plans/steer-while-running/tasks/14-tui-watermark-queue.md b/.claude/plans/steer-while-running/tasks/14-tui-watermark-queue.md new file mode 100644 index 0000000000..98cdd7a77d --- /dev/null +++ b/.claude/plans/steer-while-running/tasks/14-tui-watermark-queue.md @@ -0,0 +1,61 @@ +--- +id: 14-tui-watermark-queue +title: "Round 3: TUI ordered queue + watermark split on echo (sendSteer appends, holds one wire frame)" +blocked_by: [13-watermark-wire] +status: done +branch: "plan-steer-while-running/14-tui-watermark-queue" +worktree: "" +issue: "512" +retries: 0 +last_error: "" +accumulator: feat/steer-while-running +round: 3 +--- + +# Task brief + +TUI rework (round 3) per HANDOFF.md "Round 3". Branch +`plan-steer-while-running/14-tui-watermark-queue` off the CURRENT +`feat/steer-while-running` HEAD (post task 13). Commit only this task's files. Do +NOT push. This fixes the live-tested stuck-queue bug (session 00a10fc8): typing N +messages while a bundle was outstanding only dispatched the first. + +The client model: + +1. **Ordered queue of sends.** The TUI keeps an ordered list of pending sends + `{message_id, text}` (each `enter` mints a FRESH `message_id`). A bundle is the + collapsed join of the queue's texts. + +2. **`sendSteer` while a bundle is outstanding → SEND AN APPEND, do not + hold-and-pray.** When `m.caps.Steer && m.stream != nil`, `enter` sends the new + line as a steer frame with its fresh id. The ENGINE appends it to the pending + bundle (task 12/13). At most ONE bundle is outstanding; multiple sends are + appended into it server-side. (The wire never carries two bundles — the + single-slot inbox is the bound.) + +3. **Watermark split on the echo.** The drain echo carries `message_id` = the + LATEST contributing send (the watermark). The TUI walks its ordered queue: + entries UP TO AND INCLUDING the watermark id are drained (rendered in context); + entries AFTER it stay pending. This replaces the burned-id set with a clean + watermark cursor. A bundle drains in one echo; the queue's tail (un-drained + sends) is re-offered as the next bundle. + +4. **Queued-until-landed rendering unchanged in spirit:** the card shows the + pending (un-drained) tail of the queue; the echo lands the drained prefix in + context at its true stream position. + +5. **The `slot_full` path is gone** (append is default); the TUI handles + `accepted` (new bundle) vs `appended` (merged into pending) acks to render + honestly, and `too_late` (promote / not-sent) as today. + +## Acceptance criteria + +- R7-1: typing 3 messages while a bundle is outstanding sends 3 appends; the drain echo (watermark = the 3rd id) drains all 3 (rendered in context), none stuck. verify: `TestSteer_TUIAppendWatermarkDrainsAll` +- R7-2: the echo's watermark id splits the ordered queue — prefix rendered, suffix stays pending. verify: `TestSteer_TUIWatermarkSplitsQueue` +- R7-3: after a full drain, the next send starts a fresh bundle (no stale watermark carryover). verify: `TestSteer_TUIFreshBundleAfterDrain` +- R7-4: the "m1 drained before m2 appended" case renders m1 landed + m2 as the new pending bundle (accepted ack), no id conflation. verify: `TestSteer_TUIDrainBeforeAppend` +- R7-5: capability-absent → the #228 local queue is byte-identical. verify: `TestSteer_DisabledFallsBackToLocalQueue` (existing, must stay green) +- R7-6: card shows the merged pending parts on separate lines (the symptom-1 fix holds). verify: `task test:golden` + +Run `task lint` + `task test` + `task test:golden`. Report branch, git log, tests +added, any R7-x not satisfied. diff --git a/cmd/mecated/helpmeta.go b/cmd/mecated/helpmeta.go index c83b398318..e900c7fa10 100644 --- a/cmd/mecated/helpmeta.go +++ b/cmd/mecated/helpmeta.go @@ -171,6 +171,7 @@ var flagMetaByFlag = map[string]flagMeta{ "import-claude-permissions": {group: groupPermissions, common: false, acp: acpInclude}, "permission-config": {group: groupPermissions, common: false, acp: acpInclude}, "plan-mode-auto-approve": {group: groupPermissions, common: false, acp: acpInclude}, + "no-steer": {group: groupPermissions, common: false, acp: acpInclude}, // ── Guardrails (both) ──────────────────────────────────────────────── "guardrails-model": {group: groupGuardrails, common: false, acp: acpInclude}, diff --git a/cmd/mecated/main.go b/cmd/mecated/main.go index c0fd1fad7f..880ff8c674 100644 --- a/cmd/mecated/main.go +++ b/cmd/mecated/main.go @@ -464,6 +464,13 @@ type config struct { // reasoningEffortFlagSet is true when --reasoning-effort was passed explicitly, // so composition lets CLI out-rank the settings.yaml reasoning-effort: key. reasoningEffortFlagSet bool + // noSteer disables the mid-run steer inbox (steer-while-running, issue #512) — + // the opt-OUT of a DEFAULT-ON knob (steer is armed unless this is passed or the + // operator-tier settings.yaml steer: false folds in). + noSteer bool + // noSteerFlagSet is true when --no-steer was passed explicitly, so composition + // lets CLI out-rank the settings.yaml steer: key. + noSteerFlagSet bool // planModeAutoApprove is the OPT-IN, OPERATOR-TIER-ONLY, DEFAULT-OFF flag that // auto-approves a plan-mode PresentPlan ask when the run ends without a human @@ -1156,6 +1163,10 @@ func appConfig(cfg config, sink port.EventSink, recorder port.ToolCallRecorder, Diagnostics: diag, // Plan-mode auto-approve (issue #206 Wave 6a): the OPT-IN operator flag. PlanModeAutoApprove: cfg.planModeAutoApprove, + // Steer (steer-while-running, issue #512): the opt-OUT of the default-ON + // mid-run inbox. noSteerFlagSet lets CLI out-rank the settings.yaml steer: key. + DisableSteer: cfg.noSteer, + DisableSteerFlagSet: cfg.noSteerFlagSet, } // Project the once-resolved credentials and parsed base URLs without I/O. // An OPENAI_API_KEY in the environment implies the user wants the real provider — @@ -1501,6 +1512,7 @@ func parseFlagsModeOut(mode commandMode, argv []string, out io.Writer) (*flag.Fl fs.StringVar(&cfg.websearchQueryParam, "websearch-query-param", "", "WEBSEARCH: URL query parameter the search string is placed in (default \"q\"). Tune for a generic JSON search endpoint that expects a different parameter name. See docs/usage.md \"Enabling web search\"") fs.IntVar(&cfg.forkPreservedCap, "fork-preserved-cap", agent.DefaultPreservedForkCap, "max PRESERVED winner forks (join=first/judge) kept on disk at once; the oldest beyond this is LRU-reaped. Preserved forks stay inspectable until reaped") fs.BoolVar(&cfg.enableTeams, "enable-teams", true, "register the experimental agent-teams capability (CreateTeam/SpawnTeammate/RunTeam); on by default and inert until a client drives a team. Pass --enable-teams=false to disable") + fs.BoolVar(&cfg.noSteer, "no-steer", false, "disable the mid-run steer inbox (steer-while-running, issue #512): a client `steer` frame on the Converse stream then reports too_late and ServerCapabilities.steer reads false. Steer is ON by default; this is the opt-OUT. The operator-tier settings.yaml `steer: false` scalar is the YAML twin (CLI out-ranks YAML; a project-tier steer: key is ignored)") cfg.mcpServers = cliconfig.RegisterMCPServerFlag(fs, "") fs.BoolVar(&cfg.mcpResourceTools, "mcp-resource-tools", true, "register the ListMcpResources/ReadMcpResource meta-tools when a connected MCP server exposes resources (no-op when none do). TRUST BOUNDARY: a remote resource's contents enter the model context like any other MCP output — enable only for servers you trust") @@ -1689,6 +1701,8 @@ func recordExplicitFlags(fs *flag.FlagSet, cfg *config) { cfg.retentionCLISet.ScheduledMaxCount = true case "child-gc-interval": cfg.retentionCLISet.SweepCadence = true + case "no-steer": + cfg.noSteerFlagSet = true } if f.Name == "reasoning-effort" { cfg.reasoningEffortFlagSet = true diff --git a/cmd/mecatui/client/capabilities.go b/cmd/mecatui/client/capabilities.go index f99473e452..2213a33679 100644 --- a/cmd/mecatui/client/capabilities.go +++ b/cmd/mecatui/client/capabilities.go @@ -59,6 +59,14 @@ type Capabilities struct { // A non-nil value keeps /dream discoverable even when both targets are unavailable, // so the overlay can explain the target-specific reasons. ManualDream *ManualDreamCapabilities + // Steer is true when the server's engine arms the mid-run steer inbox + // (steer-while-running, issue #512): a `steer` frame on the bidi Converse stream + // then drains at the next turn boundary and the authoritative steer / steer.outcome + // events echo back. When false (the operator disabled it, or an older server with + // no field → proto3 default false), the ui keeps the client-side terminal + // merge-queue (issue #228) byte-identical — it never sends a steer frame the + // server would only ack too_late. + Steer bool } // capabilitiesFrom maps a proto ServerCapabilities (nil-safe) to the plain @@ -91,6 +99,7 @@ func capabilitiesFrom(c *mecatlv1.ServerCapabilities) Capabilities { StorageCleanup: c.GetStorageCleanup(), LegacyAdoption: c.GetLegacyAdoption(), ManualDream: manualDreamCapabilitiesFrom(c.GetManualDream()), + Steer: c.GetSteer(), } } diff --git a/cmd/mecatui/client/msgs.go b/cmd/mecatui/client/msgs.go index 82afb1ca2e..ea388336ee 100644 --- a/cmd/mecatui/client/msgs.go +++ b/cmd/mecatui/client/msgs.go @@ -484,6 +484,62 @@ type ProviderRouteMsg struct{ Text string } // content). type RecoverNoticeMsg struct{ Text string } +// SteerOutcome is the proto-free, closed-enum result of a steer / steer_cancel +// frame (steer-while-running, issue #512) — the mirror of the proto SteerOutcome +// (and the engine's agent.SteerOutcome). The server is AUTHORITATIVE: the client +// cannot observe the exact drain moment across stream latency, so it renders the +// outcome the server reports rather than guessing which version won a race. +type SteerOutcome string + +const ( + // SteerAccepted reports the steer parked in the empty single slot; it drains + // at the next turn boundary. + SteerAccepted SteerOutcome = "accepted" + // SteerAppended reports the steer found the pending slot OCCUPIED and was + // MERGED into it (append is the default): the pending bundle's text grew by a + // blank-line separator + text, and it still drains as ONE bundle. + // Distinguished from SteerAccepted (a NEW pending bundle) so the ui can render + // "merged onto pending" honestly. Replacing the pending bundle is explicit + // steer_cancel-then-resend. + SteerAppended SteerOutcome = "appended" + // SteerRetracted reports a steer_cancel found a pending steer and retracted it. + SteerRetracted SteerOutcome = "retracted" + // SteerNonePending reports a steer_cancel found the slot EMPTY (nothing to + // retract). + SteerNonePending SteerOutcome = "none_pending" + // SteerTooLate reports the steer arrived after the run went terminal; it is + // never parked. When Promoted is set on the msg, the server auto-promoted the + // text to a fresh follow-up run (never silently dropped). + SteerTooLate SteerOutcome = "too_late" +) + +// SteerOutcomeMsg is the AUTHORITATIVE ack for a steer / steer_cancel frame the +// ui sent on the Converse stream (proto type "steer.outcome"). One per frame, +// sequenced in send order. Text echoes the steer text the outcome is about +// (empty for steer_cancel); Promoted is true only for a too_late steer the +// server promoted to a fresh follow-up run. MessageID echoes the client-minted +// id of the Steer / SteerCancel frame this ack answers — the ui correlates by +// ID and ignores stale acks (text is not a safe key). +type SteerOutcomeMsg struct { + Outcome SteerOutcome + Text string + Promoted bool + MessageID string +} + +// SteerEchoMsg is the run's steer-inbox DRAIN echo (proto type "steer"): the +// COMMITTED operator steer just recorded into the conversation as an ordinary +// user continuation. CLIENT-VISIBLE and AUTHORITATIVE — the engine is the sole +// authority on which pending bundle drained, so the ui renders THIS text, +// byte-identical to the recorded user message replayed to the model +// (recorded == streamed == model-view). MessageID echoes the client-minted id +// of the Steer frame that drained (empty for an id-less sender), so the ui +// matches the echo to the frame it sent. +type SteerEchoMsg struct { + Text string + MessageID string +} + // ResultMsg is the terminal event: stop reason, final text, error, usage. type ResultMsg struct { Stop string @@ -1049,6 +1105,13 @@ func advisoryEventToMsg(ev *mecatlv1.Event) tea.Msg { return NoProgressMsg{Text: ev.GetText()} case "provider.route": return ProviderRouteMsg{Text: ev.GetText()} + case "steer": + // The steer-inbox DRAIN echo (committed text) + the authoritative + // steer.outcome ack ride this dispatcher (not EventToMsg's main switch) to + // keep EventToMsg under the cyclomatic-complexity bound. + return SteerEchoMsg{Text: ev.GetSteer().GetText(), MessageID: ev.GetSteer().GetMessageId()} + case "steer.outcome": + return steerOutcomeMsg(ev.GetSteerOutcome()) default: return nil } @@ -1189,6 +1252,28 @@ func approvalMsg(a *mecatlv1.Approval) ApprovalMsg { } } +// steerOutcomeMsg builds a SteerOutcomeMsg from a proto SteerAck payload +// (nil-safe via the generated getters). It is the single translation point for +// the "steer.outcome" event kind; the proto enum maps to the proto-free +// SteerOutcome closed string set (UNSPECIFIED / unknown → "" so a +// forward-compat value is inert, not a crash). +func steerOutcomeMsg(a *mecatlv1.SteerAck) SteerOutcomeMsg { + var outcome SteerOutcome + switch a.GetOutcome() { + case mecatlv1.SteerOutcome_STEER_OUTCOME_ACCEPTED: + outcome = SteerAccepted + case mecatlv1.SteerOutcome_STEER_OUTCOME_APPENDED: + outcome = SteerAppended + case mecatlv1.SteerOutcome_STEER_OUTCOME_RETRACTED: + outcome = SteerRetracted + case mecatlv1.SteerOutcome_STEER_OUTCOME_NONE_PENDING: + outcome = SteerNonePending + case mecatlv1.SteerOutcome_STEER_OUTCOME_TOO_LATE: + outcome = SteerTooLate + } + return SteerOutcomeMsg{Outcome: outcome, Text: a.GetText(), Promoted: a.GetPromoted(), MessageID: a.GetMessageId()} +} + // compactionArchiveMsg builds a CompactionArchiveMsg from a proto // CompactionArchive payload (nil-safe via the generated getters). It is the // single translation point for the log-only "compaction.archive" event kind. diff --git a/cmd/mecatui/client/stream.go b/cmd/mecatui/client/stream.go index 6e8b1a27a6..323201a67f 100644 --- a/cmd/mecatui/client/stream.go +++ b/cmd/mecatui/client/stream.go @@ -210,6 +210,31 @@ func (s *Stream) SendCancelChild(childID string) error { }) } +// SendSteer sends a mid-run operator steer frame on the bidi Converse stream +// (steer-while-running, issue #512). The server routes it to the live run's +// single-slot inbox; the AUTHORITATIVE outcome (accepted / appended / +// too_late+promoted) arrives on the SAME stream as a SteerOutcomeMsg — never +// assumed client-side, since the client cannot observe the exact drain moment +// across stream latency. The ui only calls this when Capabilities.Steer is true +// (a disabled/old server falls back to the client-side merge-queue instead). +// messageID is the client-minted correlation key the server echoes verbatim on +// the ack and the drain echo; empty degrades to text-order matching. +func (s *Stream) SendSteer(text, messageID string) error { + return s.sendFrame(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Steer{Steer: &mecatlv1.Steer{Text: text, MessageId: messageID}}, + }) +} + +// SendSteerCancel retracts the run's PENDING (un-drained) steer, if any. The +// authoritative outcome (retracted / none_pending) arrives as a SteerOutcomeMsg. +// messageID echoes the id of the Steer frame it cancels (the ui only ever has +// ONE bundle outstanding, so the id is a scoping hint, not a selector). +func (s *Stream) SendSteerCancel(messageID string) error { + return s.sendFrame(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_SteerCancel{SteerCancel: &mecatlv1.SteerCancel{MessageId: messageID}}, + }) +} + // sendFrame serialises one Send under the mutex. func (s *Stream) sendFrame(req *mecatlv1.ConverseRequest) error { s.mu.Lock() diff --git a/cmd/mecatui/config.go b/cmd/mecatui/config.go index 8a7558aa8b..bb7948f5c0 100644 --- a/cmd/mecatui/config.go +++ b/cmd/mecatui/config.go @@ -131,6 +131,11 @@ type config struct { openCodeKey string mock bool noBash bool + // noSteer disables the mid-run steer inbox (steer-while-running, issue #512) on + // the embedded server — the opt-OUT of a DEFAULT-ON knob. noSteerFlagSet records + // an explicit --no-steer so CLI out-ranks the settings.yaml steer: key. + noSteer bool + noSteerFlagSet bool // resumeID and resumeLatest select an existing owned main chat for static // startup adoption. They are shared by embedded and connect modes and mutually @@ -205,6 +210,13 @@ type config struct { // entirely for an operator who wants zero on-disk diagnostics. quiet bool + // diagnosticsLog overrides the embedded server's diagnostics log path. Empty + // (the default) means the shared per-user $XDG_STATE_HOME/mecatl/mecatui.log; + // a non-empty value is the exact file to open (created mode 0600, parent dir + // 0700). Used to give a specific mecatui instance its own diagnostics file + // (e.g. ad-hoc multi-instance testing) instead of sharing the per-user log. + diagnosticsLog string + // Embedded-server memory config (used only when hosting an in-process // server). An empty memoryDir means "compute the per-project default under // $XDG_DATA_HOME/mecatui/memory"; an explicit path overrides it. noMemory @@ -381,6 +393,7 @@ func parseTransportFlags(mode transportMode, out io.Writer, args []string, brows }) fs.BoolVar(&cfg.mock, "mock", false, "embedded server only: use the canned offline mock provider instead of OpenAI (no network)") fs.BoolVar(&cfg.noBash, "no-bash", false, "embedded server only: disable the Bash tool (shell-less mode)") + fs.BoolVar(&cfg.noSteer, "no-steer", false, "embedded server only: disable the mid-run steer inbox (steer-while-running, issue #512): `enter` mid-run then falls back to the client-side terminal merge-queue and ServerCapabilities.steer reads false. Steer is ON by default; this is the opt-OUT. The operator-tier settings.yaml `steer: false` scalar is the YAML twin (CLI out-ranks YAML; a project-tier steer: key is ignored)") fs.DurationVar(&cfg.llmPerAttemptTimeout, "llm-per-attempt-timeout", 300*time.Second, "embedded server only: per-attempt timeout for ESTABLISHING an LLM stream (connect + first chunk only; never cuts an actively-streaming turn). 0 disables; large-context reasoning models can take a long time to first token") fs.DurationVar(&cfg.llmStreamIdleTimeout, "llm-stream-idle-timeout", 180*time.Second, "embedded server only: max idle gap between LLM stream chunks after the first chunk; a longer stall terminates the turn (0 disables)") fs.IntVar(&cfg.contextWindowOverride, "context-window-override", 0, "embedded server only: override the model's context window in tokens for BOTH the compaction trigger (compaction fires at 80% of it) AND the footer context-meter denominator echoed to clients. Set this to the model's ACTUAL window when a model under-reports its window or sits behind a proxy that does. 0 (default) keeps the configured/live/catalogued/128k resolution unchanged. A small value (below a few thousand tokens) forces the agent to compact on nearly every turn — degraded, only useful for stress-testing compaction.") @@ -395,6 +408,8 @@ func parseTransportFlags(mode transportMode, out io.Writer, args []string, brows "embedded server only: OPERATOR REASONING-EFFORT TIER (ADR 0055): auto (default — unset, the provider default applies) or low/medium/high/xhigh/max. OpenAI supports low/medium/high only (xhigh/max clamp to high); Anthropic maps all five. Empty = unset (honours the operator-global settings.yaml reasoning-effort: key). A per-session /effort out-ranks it. Operator-tier only; a project-tier key is ignored with a WARN. An unknown value fail-softs to unset with a WARN.") fs.BoolVar(&cfg.quiet, "quiet", false, "discard the embedded server's operational diagnostics instead of writing them to $XDG_STATE_HOME/mecatl/mecatui.log (fallback ~/.local/state/mecatl/mecatui.log). Diagnostics NEVER go to stderr (that corrupts the TUI alt-screen); --quiet drops them entirely") + fs.StringVar(&cfg.diagnosticsLog, "diagnostics-log", "", + "embedded server only: override the diagnostics log file. Empty (default) writes to the shared per-user $XDG_STATE_HOME/mecatl/mecatui.log; a non-empty value is the exact file to open (created mode 0600, parent dir 0700). Use to give one mecatui instance its own diagnostics file (e.g. multi-instance testing) instead of sharing the per-user log. Ignored under --quiet") fs.StringVar(&cfg.memoryDir, "memory-dir", "", "embedded server only: per-project memory store directory (empty = a per-project default under $XDG_DATA_HOME/mecatui/memory)") fs.BoolVar(&cfg.noMemory, "no-memory", false, "embedded server only: disable cross-session memory (Remember/Recall) entirely") fs.StringVar(&cfg.storeDir, "store-dir", "", "embedded server only: durable JSONL session/event store directory (empty = a per-workspace default under $XDG_STATE_HOME/mecatui/sessions, so sessions survive restart and can be inspected after the fact). PRIVACY: stores the RAW conversation (prompts, model output, tool args/results) in PLAINTEXT; the dir is created mode 0700 (owner-only). Tool args/results include file contents and command output the agent read, so secrets it touched (e.g. a .env it opened) are persisted too") @@ -501,30 +516,37 @@ func validateSessionsLaunch(cfg config) error { // workspace to an absolute path. It is extracted from parseTransportFlags to // keep parseTransportFlags' cyclomatic complexity under the lint gate; the // helper owns the post-parse branches. +// recordExplicitFlag records ONE explicitly-passed flag's "set" marker onto cfg, so +// composition lets CLI out-rank the operator-global settings.yaml keys (mirrors +// mecated's recordExplicitFlags). Split out of finalizeParsedConfig's fs.Visit to keep +// that function under the cyclomatic-complexity bound. +func recordExplicitFlag(f *flag.Flag, cfg *config) { + switch f.Name { + case "posture": + cfg.postureFlagSet = true + case "subagent-model-router": + // Kill-switch (ADR 0042): record that the flag was given so embeddedConfig can + // distinguish unset (router governed by the taxonomy) from =false (kill-switch) + // and =true/bare (a harmless no-op, the router stays governed by the taxonomy). + cfg.subagentModelRouterSet = true + case "no-steer": + // Record an explicit --no-steer so CLI out-ranks the settings.yaml steer: key. + cfg.noSteerFlagSet = true + case "reasoning-effort": + cfg.reasoningEffortFlagSet = true + case "default-provider": + cfg.defaultProviderFlagSet = true + case "terminal-title": + cfg.terminalTitleFlagSet = true + } + markRetentionCLIFlag(&cfg.retentionCLISet, f.Name) +} + func finalizeParsedConfig(fs *flag.FlagSet, cfg *config) error { - // Record an explicit --posture so CLI out-ranks the operator-global settings.yaml - // posture: key (mirrors mecated). - fs.Visit(func(f *flag.Flag) { - switch f.Name { - case "posture": - cfg.postureFlagSet = true - case "subagent-model-router": - // Kill-switch (ADR 0042): record that the flag was given so embeddedConfig can - // distinguish unset (router governed by the taxonomy) from =false (kill-switch) - // and =true/bare (a harmless no-op, the router stays governed by the taxonomy). - cfg.subagentModelRouterSet = true - } - markRetentionCLIFlag(&cfg.retentionCLISet, f.Name) - if f.Name == "reasoning-effort" { - cfg.reasoningEffortFlagSet = true - } - if f.Name == "default-provider" { - cfg.defaultProviderFlagSet = true - } - if f.Name == "terminal-title" { - cfg.terminalTitleFlagSet = true - } - }) + // Record explicit flags so composition lets CLI out-rank the operator-global + // 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 cfg.authToken == "" { cfg.authToken = os.Getenv("MECATL_AUTH_TOKEN") diff --git a/cmd/mecatui/diaglog.go b/cmd/mecatui/diaglog.go index b8b1335f4a..d0cf625fd0 100644 --- a/cmd/mecatui/diaglog.go +++ b/cmd/mecatui/diaglog.go @@ -61,9 +61,10 @@ func resolveDiagLogPath(env xdgconfig.ResolveEnv) string { // diagnostics. The contract (and the render-leak fix it exists for): // // - quiet ⇒ io.Discard (the operator asked for zero on-disk diagnostics); -// - else open $XDG_STATE_HOME/mecatl/mecatui.log (fallback -// ~/.local/state/mecatl/mecatui.log) for APPEND, creating the dir 0700; -// - on ANY failure (no resolvable state base, mkdir/open error) ⇒ io.Discard, +// - else open overridePath when non-empty (--diagnostics-log), else +// $XDG_STATE_HOME/mecatl/mecatui.log (fallback ~/.local/state/mecatl/mecatui.log) +// for APPEND, creating the dir 0700; +// - on ANY failure (no resolvable path, mkdir/open error) ⇒ io.Discard, // so the TUI never crashes on a diagnostics-sink problem. // // It NEVER returns os.Stderr/os.Stdout: a diagnostics line on either corrupts the @@ -71,12 +72,15 @@ func resolveDiagLogPath(env xdgconfig.ResolveEnv) string { // non-nil only when a real file was opened (so the caller closes it on shutdown); // for the discard paths it is a no-op closer. The bool reports whether a file was // actually opened (logged once by the caller, off the TUI render path). -func openDiagLogWriter(env xdgconfig.ResolveEnv, quiet bool) (w io.Writer, closer io.Closer, toFile bool) { +func openDiagLogWriter(env xdgconfig.ResolveEnv, quiet bool, overridePath string) (w io.Writer, closer io.Closer, toFile bool) { noop := io.NopCloser(nil) if quiet { return io.Discard, noop, false } - path := resolveDiagLogPath(env) + path := overridePath + if path == "" { + path = resolveDiagLogPath(env) + } if path == "" { return io.Discard, noop, false } diff --git a/cmd/mecatui/diaglog_test.go b/cmd/mecatui/diaglog_test.go index fb55c1a58e..4e626fab36 100644 --- a/cmd/mecatui/diaglog_test.go +++ b/cmd/mecatui/diaglog_test.go @@ -72,7 +72,7 @@ func TestOpenDiagLogWriterQuietDiscards(t *testing.T) { // --quiet must yield io.Discard regardless of a resolvable state base, and open // no file (the dir/file must NOT be created). dir := t.TempDir() - w, closer, toFile := openDiagLogWriter(stateEnv(dir), true) + w, closer, toFile := openDiagLogWriter(stateEnv(dir), true, "") defer func() { _ = closer.Close() }() if w != io.Discard { t.Fatalf("--quiet writer = %T, want io.Discard", w) @@ -87,7 +87,7 @@ func TestOpenDiagLogWriterQuietDiscards(t *testing.T) { func TestOpenDiagLogWriterOpensFileForAppend(t *testing.T) { dir := t.TempDir() - w, closer, toFile := openDiagLogWriter(stateEnv(dir), false) + w, closer, toFile := openDiagLogWriter(stateEnv(dir), false, "") t.Cleanup(func() { _ = closer.Close() }) if !toFile { t.Fatal("a resolvable state base (not quiet) must open a file") @@ -132,13 +132,46 @@ func TestOpenDiagLogWriterDiscardsWhenUnresolvable(t *testing.T) { UserHomeDir: func() (string, error) { return "", errors.New("no home") }, ReadFile: os.ReadFile, } - w, closer, toFile := openDiagLogWriter(env, false) + w, closer, toFile := openDiagLogWriter(env, false, "") defer func() { _ = closer.Close() }() if w != io.Discard || toFile { t.Fatalf("no resolvable state base must discard (w=%T toFile=%v)", w, toFile) } } +func TestOpenDiagLogWriterOverridePath(t *testing.T) { + // --diagnostics-log opens THAT exact file (created mode 0600, parent + // 0700) instead of the shared per-user mecatui.log, so one instance can route + // its diagnostics to an operator-chosen location (multi-instance testing). + dir := t.TempDir() + override := filepath.Join(dir, "custom", "steer-test.log") + w, closer, toFile := openDiagLogWriter(stateEnv(dir), false, override) + t.Cleanup(func() { _ = closer.Close() }) + if !toFile { + t.Fatal("an override path (not quiet) must open a file") + } + if _, err := w.Write([]byte("x\n")); err != nil { + t.Fatalf("write to override log: %v", err) + } + if _, err := os.Stat(override); err != nil { + t.Fatalf("override log must exist at the given path: %v", err) + } + fi, err := os.Stat(filepath.Dir(override)) + if err != nil { + t.Fatalf("stat override dir: %v", err) + } + if perm := fi.Mode().Perm(); perm != 0o700 { + t.Fatalf("override dir perm = %o, want 0700", perm) + } + lfi, err := os.Stat(override) + if err != nil { + t.Fatalf("stat override file: %v", err) + } + if perm := lfi.Mode().Perm(); perm != 0o600 { + t.Fatalf("override file perm = %o, want 0600", perm) + } +} + // TestBaselineSlogWriterAlwaysDiscards pins the client-only-mode floor: the TUI has // no in-process diagnostics of its own, so the universal baseline writer is // io.Discard whether or not --quiet is set. (The host-embedded path REFINES this to diff --git a/cmd/mecatui/helpmeta.go b/cmd/mecatui/helpmeta.go index a9f3af41c0..e6e7e5fa95 100644 --- a/cmd/mecatui/helpmeta.go +++ b/cmd/mecatui/helpmeta.go @@ -75,16 +75,17 @@ var flagApplicabilityByFlag = map[string]flagApplicability{ "prompt-file": {group: groupSession, common: true, local: true, connect: true}, // ── UI (shared) ─────────────────────────────────────────────────────── - "theme": {group: groupUI, common: true, local: true, connect: true}, - "theme-dir": {group: groupUI, common: false, local: true, connect: true}, - "list-themes": {group: groupUI, common: false, local: true, connect: true}, - "no-alt-screen": {group: groupUI, common: true, local: true, connect: true}, - "inline": {group: groupUI, common: false, local: true, connect: true}, - "no-mouse": {group: groupUI, common: false, local: true, connect: true}, - "no-banner": {group: groupUI, common: false, local: true, connect: true}, - "terminal-title": {group: groupUI, common: false, local: true, connect: true}, - "keymap": {group: groupUI, common: false, local: true, connect: true}, - "quiet": {group: groupUI, common: false, local: true, connect: false}, + "theme": {group: groupUI, common: true, local: true, connect: true}, + "theme-dir": {group: groupUI, common: false, local: true, connect: true}, + "list-themes": {group: groupUI, common: false, local: true, connect: true}, + "no-alt-screen": {group: groupUI, common: true, local: true, connect: true}, + "inline": {group: groupUI, common: false, local: true, connect: true}, + "no-mouse": {group: groupUI, common: false, local: true, connect: true}, + "no-banner": {group: groupUI, common: false, local: true, connect: true}, + "terminal-title": {group: groupUI, common: false, local: true, connect: true}, + "keymap": {group: groupUI, common: false, local: true, connect: true}, + "quiet": {group: groupUI, common: false, local: true, connect: false}, + "diagnostics-log": {group: groupUI, common: false, local: true, connect: false}, // ── Provider (embedded-only) ────────────────────────────────────────── "model": {group: groupProvider, common: true, local: true, connect: false}, @@ -97,6 +98,7 @@ var flagApplicabilityByFlag = map[string]flagApplicability{ "auth-file": {group: groupProvider, common: true, local: true, connect: false}, "mock": {group: groupProvider, common: true, local: true, connect: false}, "no-bash": {group: groupProvider, common: false, local: true, connect: false}, + "no-steer": {group: groupProvider, common: false, local: true, connect: false}, "toolhive-llm": {group: groupProvider, common: false, local: true, connect: false}, "toolhive-llm-base-url": {group: groupProvider, common: false, local: true, connect: false}, "toolhive-llm-mode": {group: groupProvider, common: false, local: true, connect: false}, diff --git a/cmd/mecatui/main.go b/cmd/mecatui/main.go index f82831b6a6..fc4f014cc0 100644 --- a/cmd/mecatui/main.go +++ b/cmd/mecatui/main.go @@ -262,6 +262,9 @@ func run(argv []string) error { // Diagnostic: MECATUI_DEBUG_MOUSE=1 shows raw mouse coords + content mapping in // the footer (for diagnosing selection/coordinate issues). Default off. DebugMouse: os.Getenv("MECATUI_DEBUG_MOUSE") != "", + // Diagnostic: MECATUI_DEBUG_STEER=1 traces steer ack/echo correlation (incoming + // id vs live id, match/burn/drop) in the status line. Default off. + DebugSteer: os.Getenv("MECATUI_DEBUG_STEER") != "", // Diagnostic: MECATUI_DEBUG_ASK=1 registers /debug-ask, which injects a fake // long-args permission ask through the real reducer (for exercising the // modal's wrap/scroll/full-screen-args behaviour by hand). Default off; @@ -484,7 +487,7 @@ func resolveTransport(ctx context.Context, cfg config) (target string, dial clie // BOTH the app.Diagnostics sink and the perf surface's slog.Logger, so neither // path leaks a line to the terminal. The file handle (when one was opened) is // closed by the returned cleanup alongside the server. - diagW, diagCloser, toFile := openDiagLogWriter(xdgconfig.OSEnv, cfg.quiet) + diagW, diagCloser, toFile := openDiagLogWriter(xdgconfig.OSEnv, cfg.quiet, cfg.diagnosticsLog) diag := slogdiag.New(diagW, false, port.LevelInfo) // A dedicated slog.Logger over the SAME writer for the perf surface's Logger field. // Explicit injection (rather than relying on the redirected default below) keeps the @@ -733,6 +736,11 @@ func embeddedConfig(cfg config, diag port.Diagnostics) app.Config { // mecatui is interactive, so trusted/auto/yolo retain the developer // workspace-trust floor. Headless: false, + // Steer (steer-while-running, issue #512): the opt-OUT of the default-ON + // mid-run inbox on the embedded server. noSteerFlagSet lets CLI out-rank the + // settings.yaml steer: key. + DisableSteer: cfg.noSteer, + DisableSteerFlagSet: cfg.noSteerFlagSet, // Diagnostics is the injected file-backed (or, under --quiet, discarding) sink. // It is NEVER stderr: an operational line on stderr corrupts the Bubble Tea // alt-screen. The caller (resolveTransport) opens the sink once over diff --git a/cmd/mecatui/ui/layout.go b/cmd/mecatui/ui/layout.go index afca3540a2..295bcc771b 100644 --- a/cmd/mecatui/ui/layout.go +++ b/cmd/mecatui/ui/layout.go @@ -119,6 +119,13 @@ func (m Model) chrome() (above, below []region) { if q := m.renderQueue(); q != "" { below = append(below, region{role: regionQueue, content: q}) } + // Steer mode: the in-flight steer's AUTHORITATIVE state (pending / sent / + // promoted / retracted) is summarised in a muted card just above the input, + // shown whenever a steer is in flight. Mutually exclusive with the local-queue + // card (steer owns mid-run input when armed, so the queue is empty then). + if s := m.renderSteer(); s != "" { + below = append(below, region{role: regionQueue, content: s}) + } below = append(below, // One blank row of top padding so the input box isn't jammed against the history. region{role: regionInputSpacer, content: inputSpacerRow}, diff --git a/cmd/mecatui/ui/model.go b/cmd/mecatui/ui/model.go index ce05f6e11a..50ba04697b 100644 --- a/cmd/mecatui/ui/model.go +++ b/cmd/mecatui/ui/model.go @@ -256,6 +256,13 @@ type Deps struct { // bug). Default OFF (zero cost when unset); main.go reads the env var. DebugMouse bool + // DebugSteer turns on a steer correlation trace in the status line (env + // MECATUI_DEBUG_STEER=1): each steer ack/echo logs the incoming message_id, + // the live bundle's id, and the match/burn/drop decision, so a stuck or + // mis-correlated steer lifecycle is visible in the TUI rather than opaque. + // Default OFF (zero cost when unset); main.go reads the env var. + DebugSteer bool + // DebugAsk registers the /debug-ask built-in (env MECATUI_DEBUG_ASK=1): it // injects a fake permission ask with long Bash args through the REAL ask // reducer, so the modal's wrap/scroll/full-screen-args behaviour (issue #488) @@ -309,6 +316,71 @@ const maxQueued = 16 // textarea on an edit-back), which reads naturally as a multi-part request. const queueMergeSep = "\n\n" +// steerPhase is the AUTHORITATIVE lifecycle of a steer-mode (Capabilities.Steer) +// mid-run operator steer. The server is the sole authority on what happened to a +// steer (the client cannot observe the exact drain moment across stream latency), +// so these phases are driven ONLY by the server's steer.outcome acks and the +// steer drain echo — never assumed client-side. +type steerPhase int + +const ( + // steerPending: the merged text was sent on the Converse stream but the + // server's steer.outcome ack has not arrived yet (send in flight). + steerPending steerPhase = iota + // steerSent: the server acked accepted/appended — the steer is parked in the + // run's single-slot inbox awaiting the next turn-boundary drain. + steerSent + // steerPromoted: the server acked too_late+promoted — the run had already gone + // terminal, so the text was auto-promoted to a fresh follow-up run. + steerPromoted + // steerFailed: the server acked too_late WITHOUT promoted — the promotion + // failed (routing / run-entry / lease / funnel error) and the text was NOT + // delivered. Distinct from promoted so the ui does NOT report a successful + // follow-up. The text is preserved (it may be re-sent or dropped explicitly). + steerFailed + // steerRetracted: a steer_cancel won — the pending steer was retracted before + // it drained (the run drains nothing for it). + steerRetracted +) + +// steerQueuedSend is ONE send in the ordered steer queue: the fresh client-minted +// message_id of THIS send and the (fragment) text it carried. The ordered queue +// (steerState.sends) lets the TUI split "drained up to the watermark id" from +// "still pending after it" on each drain echo, instead of collapsing everything +// into one re-minted bundle (the duplication bug the append model exposed). +type steerQueuedSend struct { + ID string + Text string +} + +// steerState is the ONE-ELEMENT steer-mode mid-run state: the pending bundle — +// an ORDERED queue of sends (steerQueuedSend), its merged display text, and its +// authoritative lifecycle phase. The ordered queue is the SINGLE correlation +// source (the watermark id is the tail's message_id — derived, never stored); +// text-display is the blank-line join of sends (cached to avoid a per-render +// re-join). +type steerState struct { + // Text is the merged display text (the blank-line join of sends) shown on the + // card; the wire sends each send's OWN fragment (never the whole merged + // text — that would re-append drained text, the duplication bug). + Text string + // Phase is the authoritative lifecycle (see steerPhase). + Phase steerPhase + // Sends is the ordered queue of sends currently in this bundle (ID+fragment). + // On a drain echo the prefix up to and incl. the watermark id is dropped + // (drained); the suffix stays pending and Text re-joins from the remainder. + Sends []steerQueuedSend +} + +// watermarkID derives the bundle's current watermark id — the tail send's +// message_id — or "" for an empty queue (the steerCancel frame's scoping hint). +func (s *steerState) watermarkID() string { + if s == nil || len(s.Sends) == 0 { + return "" + } + return s.Sends[len(s.Sends)-1].ID +} + // phase is the model's coarse state machine. type phase int @@ -445,11 +517,22 @@ type Model struct { dream dreamState dreamGen uint64 dreamRequest uint64 - models modelsState // /models picker overlay state (view==modelsNone when closed) - effort effortState // /effort picker overlay state (view==effortNone when closed) — ADR 0055 - worktrees worktreesState // /worktrees overlay state (view==worktreesNone when closed) — issue #102 - schedule scheduleState // /schedule overlay state (view==scheduleNone when closed) — issue #234 - sessions sessionsState // /sessions overlay state (view==sessionsNone when closed) — issue #245 + // steer is the steer-mode (Capabilities.Steer) mid-run state: ONE bundle (the + // merged operator steer text + its client-minted message_id) with its + // AUTHORITATIVE lifecycle — idle → pending (sent, un-acked) → sent (acked, + // awaiting drain) → promoted (too_late; the server auto-started a follow-up + // run) → failed (too_late without promoted) → retracted (steer_cancel won). nil + // when no steer is in flight (the common case) OR steer is disabled (the #228 + // local merge-queue then owns mid-run input, byte-identical). A ONE-BUNDLE + // state — the client-side merge collapses staged lines into ONE text BEFORE + // send, so the engine's single-slot inbox only ever has one bundle outstanding. + steer *steerState + steerSeq int // session-scoped message-id serial (1-based; "steer-%04d") + models modelsState // /models picker overlay state (view==modelsNone when closed) + effort effortState // /effort picker overlay state (view==effortNone when closed) — ADR 0055 + worktrees worktreesState // /worktrees overlay state (view==worktreesNone when closed) — issue #102 + schedule scheduleState // /schedule overlay state (view==scheduleNone when closed) — issue #234 + sessions sessionsState // /sessions overlay state (view==sessionsNone when closed) — issue #245 // activeModel is the currently-selected (provider, model) the NEXT CreateSession // will carry (apply-on-next-create). Seeded from Deps.InitialModel, updated by the // picker, and reconciled-to-default at connect when its provider is unavailable. It diff --git a/cmd/mecatui/ui/steer_multisend_test.go b/cmd/mecatui/ui/steer_multisend_test.go new file mode 100644 index 0000000000..99dfdedc2d --- /dev/null +++ b/cmd/mecatui/ui/steer_multisend_test.go @@ -0,0 +1,64 @@ +package ui + +import ( + "strings" + "testing" + + "github.com/stacklok/mecatl/cmd/mecatui/client" +) + +// Three sends during one turn must drain via the ONE merged echo; the queue +// clears and the landed line renders exactly once. +func TestSteer_MergeThreeIntoOneDrain(t *testing.T) { + m, _ := newSteerModel(t, true) + m = startRunning(t, m, "original") + + m = enqueueSteer(t, m, "steer 1") + m = enqueueSteer(t, m, "steer 2") + m = enqueueSteer(t, m, "steer 3") + if m.steer == nil || len(m.steer.Sends) != 3 { + t.Fatalf("three sends must queue, got %+v", m.steer) + } + + // Acks advance phase but keep the queue (only the echo splits it). + mm, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAccepted, Text: "steer 1", MessageID: "steer-0001"}) + m = mm.(Model) + mm, _ = m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAppended, Text: "steer 2", MessageID: "steer-0002"}) + m = mm.(Model) + mm, _ = m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAppended, Text: "steer 3", MessageID: "steer-0003"}) + m = mm.(Model) + if m.steer == nil || len(m.steer.Sends) != 3 || m.steer.Phase != steerSent { + t.Fatalf("acks must advance phase while queue stays whole, got %+v", m.steer) + } + + // The ONE merged drain echo (watermark = tail) clears the queue and lands + // the merged text once. + mm2, _ := m.Update(client.SteerEchoMsg{Text: "steer 1\n\nsteer 2\n\nsteer 3", MessageID: "steer-0003"}) + m = mm2.(Model) + if m.steer != nil { + t.Fatalf("the drain echo must clear the card, got %+v", m.steer) + } + m.refreshView() + rendered := stripANSIstr(m.View().Content) + if n := strings.Count(rendered, "steer 1"); n != 1 { + t.Fatalf("merged document must land once in the transcript, found %d:\n%s", n, rendered) + } +} + +// An echo with an EMPTY message_id (an id-less sender / legacy server) must +// also clear the queue and land the text — a non-matched id must not strand +// the queue as it did before. +func TestSteer_IdLessEchoClearsQueue(t *testing.T) { + m, _ := newSteerModel(t, true) + m = startRunning(t, m, "original") + + m = enqueueSteer(t, m, "steer 1") + mm, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAccepted, Text: "steer 1", MessageID: "steer-0001"}) + m = mm.(Model) + + mm2, _ := m.Update(client.SteerEchoMsg{Text: "steer 1", MessageID: ""}) + m = mm2.(Model) + if m.steer != nil { + t.Fatalf("the id-less echo must clear the queue, got %+v", m.steer) + } +} diff --git a/cmd/mecatui/ui/steer_render_test.go b/cmd/mecatui/ui/steer_render_test.go new file mode 100644 index 0000000000..c1ae64f4ef --- /dev/null +++ b/cmd/mecatui/ui/steer_render_test.go @@ -0,0 +1,44 @@ +package ui + +import ( + "strings" + "testing" + + tea "charm.land/bubbletea/v2" +) + +// A re-composed (↑-edit) fragment carries the WHOLE merged bundle in a single +// send's Text — the card must show each part on its own line (the "steer 7 / +// steer 8a as one line" UI glitch in session 8d9c51278). +func TestSteer_RecomposedFragmentRendersPerPart(t *testing.T) { + m, _ := newSteerModel(t, true) + m = startRunning(t, m, "original") + + m = enqueueSteer(t, m, "steer 7") + m = enqueueSteer(t, m, "steer 8") + mm, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyUp}) + m = mm.(Model) + runBatchLeaves(cmd) + if got := m.ta.Value(); got != "steer 7"+queueMergeSep+"steer 8" { + t.Fatalf("↑ must pull the merged bundle into the textarea, got %q", got) + } + m = typeText(t, m, "a") + mm2, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyEnter}) + m = mm2.(Model) + runBatchLeaves(cmd) + + card := stripANSIstr(m.renderSteer()) + if !strings.Contains(card, "steer 7") || !strings.Contains(card, "steer 8a") { + t.Fatalf("the card must show both parts, got:\n%s", card) + } + i7 := strings.Index(card, "steer 7") + i8 := strings.Index(card, "steer 8a") + if i7 < 0 || i8 < 0 { + t.Fatalf("the card must show both parts, got:\n%s", card) + } + // The parts live on DIFFERENT rendered lines — a single-line merge would + // put them on the same \n-free span. + if !strings.Contains(card[i7:i8], "\n") { + t.Fatalf("the parts must render on separate lines, got:\n%s", card) + } +} diff --git a/cmd/mecatui/ui/steer_test.go b/cmd/mecatui/ui/steer_test.go new file mode 100644 index 0000000000..9b67b70107 --- /dev/null +++ b/cmd/mecatui/ui/steer_test.go @@ -0,0 +1,605 @@ +package ui + +import ( + "context" + "strings" + "testing" + + tea "charm.land/bubbletea/v2" + + "github.com/stacklok/mecatl/cmd/mecatui/client" + "github.com/stacklok/mecatl/cmd/mecatui/theme" +) + +// Scenario 6 of docs/acceptance/steer-while-running.md (the mecatui half): the +// capability-driven flip. When the server advertises Capabilities.Steer, `enter` +// mid-run sends a `steer` frame on the Converse stream (not a local stage) and the +// queue card reflects the AUTHORITATIVE echoed/acked state; when the capability is +// absent, the #228 client-side terminal merge-queue owns mid-run input, +// byte-identical. + +// newSteerModel builds a connected, idle Model whose fakeConv advertises the given +// Steer capability on CreateSession (mirrors newQueueModel, with caps). +func newSteerModel(t *testing.T, steerCap bool) (Model, *fakeConv) { + t.Helper() + recv := &fakeRecver{} + send := &fakeSender{} + conv := &fakeConv{recv: recv, send: send, caps: client.Capabilities{Steer: steerCap}} + m := New(Deps{ + Session: conv, + Conv: conv, + Theme: theme.New("aztec", theme.AztecPalette()), + Ctx: context.Background(), + NoAltScreen: true, + }) + m = applyAll(m, + tea.WindowSizeMsg{Width: 100, Height: 30}, + client.SessionReadyMsg{SessionID: "sess-test-0001", Capabilities: conv.caps}, + ) + return m, conv +} + +// enqueueSteer types text and presses enter while running, then RUNS the returned +// command so the steer frame's Send executes (sendSteer's send is a synchronous +// tea.Cmd). Returns the updated model. +func enqueueSteer(t *testing.T, m Model, text string) Model { + t.Helper() + m = typeText(t, m, text) + mm, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyEnter}) + runBatchLeaves(cmd) + return mm.(Model) +} + +// steerFrame pairs one sent `steer` frame's text with its message_id. +type steerFrame struct { + text string + id string +} + +// steerFrames returns the text+message_id of every `steer` frame sent on the +// stream, in order. +func steerFrames(send *fakeSender) []steerFrame { + var out []steerFrame + for _, fr := range send.frames() { + if s := fr.GetSteer(); s != nil { + out = append(out, steerFrame{text: s.GetText(), id: s.GetMessageId()}) + } + } + return out +} + +// steerTexts returns only the text-half of steerFrames (legacy callers). +func steerTexts(send *fakeSender) []string { + var out []string + for _, sf := range steerFrames(send) { + out = append(out, sf.text) + } + return out +} + +// steerCancelCount returns how many `steer_cancel` frames were sent. +func steerCancelCount(send *fakeSender) int { + n := 0 + for _, fr := range send.frames() { + if fr.GetSteerCancel() != nil { + n++ + } + } + return n +} + +// TestSteer_DisabledFallsBackToLocalQueue is the TUI half of AC6.2: with the steer +// capability ABSENT (an old server, or the operator disabled it), `enter` mid-run +// stages into the client-side merge-queue EXACTLY as #228 — no steer frame is sent, +// the queue holds the merged text, and a clean end drains it through the ordinary +// prompt path. Byte-identical to the pre-steer behaviour. +func TestSteer_DisabledFallsBackToLocalQueue(t *testing.T) { + m, conv := newSteerModel(t, false) // capability ABSENT + m = startRunning(t, m, "first") + + m = enqueue(t, m, "second") + m = enqueue(t, m, "third") + + // The local merge-queue owns mid-run input: no steer frame, the queue holds both. + if got := steerFrames(conv.send); len(got) != 0 { + t.Fatalf("steer-disabled must send NO steer frame; sent %v", got) + } + if len(m.queued) != 2 || m.queued[0] != "second" || m.queued[1] != "third" { + t.Fatalf("queued = %v, want [second third] (the #228 local merge-queue)", m.queued) + } + if m.steer != nil { + t.Fatalf("steer state must stay nil when the capability is absent, got %+v", m.steer) + } + + // A clean end MERGES the queue into one prompt and drains it through the + // ordinary prompt path (byte-identical to #228's merge-always). + mm, cmd := m.Update(client.ResultMsg{Stop: "end_turn"}) + m = mm.(Model) + runBatchLeaves(cmd) + if len(m.queued) != 0 { + t.Fatalf("the merged queue must drain on a clean end, got %v", m.queued) + } + got := promptTexts(conv.send) + want := []string{"first", "second" + queueMergeSep + "third"} + if len(got) != len(want) || got[0] != want[0] || got[1] != want[1] { + t.Fatalf("prompt frames = %v, want %v (merged local-queue drain)", got, want) + } +} + +// TestSteer_TUIRendersAuthoritativeState is AC6.3: with the steer capability +// PRESENT, `enter` mid-run sends ONE merged steer frame, the card reflects the +// authoritative lifecycle (pending → sent → promoted-after-race → landed), +// and `↑` edits the whole in-flight message (the task-10 queue model: send +// collapses staged lines into ONE bundle with a fresh message_id; ↑ cancels the +// outstanding bundle and pulls it back as ONE editable blob; the drain echo +// renders the landed line IN CONTEXT at its stream position). +func TestSteer_TUIRendersAuthoritativeState(t *testing.T) { + t.Run("send merges into one bundle and shows pending", func(t *testing.T) { + m, conv := newSteerModel(t, true) + m = startRunning(t, m, "first") + + m = enqueueSteer(t, m, "second") + + // The ONE bundle is on the wire with a fresh session-scoped id; the local + // queue is untouched. + got := steerFrames(conv.send) + if len(got) != 1 || got[0].text != "second" || got[0].id != "steer-0001" { + t.Fatalf("steer frames = %+v, want one frame text=second id=steer-0001", got) + } + if len(m.queued) != 0 { + t.Fatalf("steer mode must not touch the local queue, got %v", m.queued) + } + if m.steer == nil || m.steer.Phase != steerPending || m.steer.watermarkID() != "steer-0001" { + t.Fatalf("steer state = %+v, want the pending bundle id=steer-0001", m.steer) + } + // The card renders the merged message as ONE item in the pending state. + card := stripANSIstr(m.renderSteer()) + if !strings.Contains(card, "sending") { + t.Fatalf("pending card must show the sending state, got:\n%s", card) + } + if !strings.Contains(card, "second") { + t.Fatalf("pending card must preview the combined message, got:\n%s", card) + } + }) + + t.Run("accepted ack moves the card to sent", func(t *testing.T) { + m, _ := newSteerModel(t, true) + m = startRunning(t, m, "first") + m = enqueueSteer(t, m, "second") + + mm, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAccepted, Text: "second", MessageID: "steer-0001"}) + m = mm.(Model) + + if m.steer == nil || m.steer.Phase != steerSent { + t.Fatalf("steer state = %+v, want sent (acked)", m.steer) + } + card := stripANSIstr(m.renderSteer()) + if !strings.Contains(card, "queued") { + t.Fatalf("sent card must show the queued-for-boundary state, got:\n%s", card) + } + }) + + t.Run("drain echo clears the card and lands the queued text in context", func(t *testing.T) { + m, _ := newSteerModel(t, true) + m = startRunning(t, m, "first") + m = enqueueSteer(t, m, "second") + mm, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAccepted, Text: "second", MessageID: "steer-0001"}) + m = mm.(Model) + + mm2, _ := m.Update(client.SteerEchoMsg{Text: "second", MessageID: "steer-0001"}) + m = mm2.(Model) + + if m.steer != nil { + t.Fatalf("the drain echo must clear the in-flight card, got %+v", m.steer) + } + if got := stripANSIstr(m.renderSteer()); got != "" { + t.Fatalf("no card after the drain echo, got:\n%s", got) + } + // The landed line appears IN CONTEXT (appended to the transcript at the + // echo's stream position — after the settling tool activity, before the + // next turn). + view := stripANSIstr(m.View().Content) + if !strings.Contains(view, "second") { + t.Fatalf("the landed steer must appear in the transcript, got:\n%s", view) + } + }) + + t.Run("too_late ack renders the promoted-after-race state", func(t *testing.T) { + m, _ := newSteerModel(t, true) + m = startRunning(t, m, "first") + m = enqueueSteer(t, m, "second") + + mm, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerTooLate, Text: "second", Promoted: true, MessageID: "steer-0001"}) + m = mm.(Model) + + if m.steer == nil || m.steer.Phase != steerPromoted { + t.Fatalf("steer state = %+v, want promoted", m.steer) + } + card := stripANSIstr(m.renderSteer()) + if !strings.Contains(card, "follow-up") { + t.Fatalf("promoted card must honestly show the promoted-after-race state, got:\n%s", card) + } + }) + + t.Run("too_late ack with Promoted=false renders not-sent, not a follow-up", func(t *testing.T) { + m, _ := newSteerModel(t, true) + m = startRunning(t, m, "first") + m = enqueueSteer(t, m, "second") + + mm, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerTooLate, Text: "second", Promoted: false, MessageID: "steer-0001"}) + m = mm.(Model) + + if m.steer == nil || m.steer.Phase != steerFailed { + t.Fatalf("steer state = %+v, want steerFailed (not-sent), got:", m.steer) + } + card := stripANSIstr(m.renderSteer()) + if strings.Contains(card, "follow-up") { + t.Fatalf("not-sent must not claim a follow-up was sent, got:\n%s", card) + } + if !strings.Contains(card, "not sent") { + t.Fatalf("not-sent card must state the text was not delivered, got:\n%s", card) + } + }) + + t.Run("up edits the combined message and resend replaces it", func(t *testing.T) { + m, conv := newSteerModel(t, true) + m = startRunning(t, m, "first") + m = enqueueSteer(t, m, "second") + mm, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAccepted, Text: "second", MessageID: "steer-0001"}) + m = mm.(Model) + + // ↑ on an empty input with an in-flight steer CANCELS the outstanding + // bundle and pulls the whole not-yet-drained message back into the + // textarea as ONE editable blob (the non-destructive-edit old behaviour + // that left the old frame live to be merged on resend is GONE). + mm2, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyUp}) + m = mm2.(Model) + runBatchLeaves(cmd) + if got := m.ta.Value(); got != "second" { + t.Fatalf("↑ must pull the in-flight message into the textarea, got %q", got) + } + if got := steerCancelCount(conv.send); got != 1 { + t.Fatalf("↑ must send exactly ONE steer_cancel for the outstanding bundle, got %d", got) + } + + // Edit and resend: it collapses to ONE new bundle with a NEW id. + m = typeText(t, m, " CHANGED") // appends to the pulled-back draft + mm3, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyEnter}) + m = mm3.(Model) + runBatchLeaves(cmd) + got := steerFrames(conv.send) + want := []steerFrame{ + {text: "second", id: "steer-0001"}, + {text: "second CHANGED", id: "steer-0002"}, + } + if len(got) != len(want) { + t.Fatalf("steer frames = %+v, want %+v", got, want) + } + for i := range want { + if got[i] != want[i] { + t.Fatalf("steer frame %d = %+v, want %+v (resend collapses to ONE new bundle with a NEW id)", i, got[i], want[i]) + } + } + }) + + t.Run("esc retracts a pending steer", func(t *testing.T) { + m, conv := newSteerModel(t, true) + m = startRunning(t, m, "first") + m = enqueueSteer(t, m, "second") + + mm, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyEscape}) + m = mm.(Model) + runBatchLeaves(cmd) + + if got := steerCancelCount(conv.send); got != 1 { + t.Fatalf("esc on a pending steer must send ONE steer_cancel, got %d", got) + } + // The authoritative retracted ack drives the lifecycle to retracted. + mm2, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerRetracted, MessageID: "steer-0001"}) + m = mm2.(Model) + if m.steer == nil || m.steer.Phase != steerRetracted { + t.Fatalf("steer state = %+v, want retracted", m.steer) + } + }) + + t.Run("stale ack for a burned id is ignored", func(t *testing.T) { + m, _ := newSteerModel(t, true) + m = startRunning(t, m, "first") + m = enqueueSteer(t, m, "second") + mm, _ := m.Update(client.SteerEchoMsg{Text: "second", MessageID: "steer-0001"}) + m = mm.(Model) + + // The id is burned (the echo landed); a late accepted ack carrying that + // STALE id must not resurrect a card. + mm2, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAccepted, Text: "second", MessageID: "steer-0001"}) + m = mm2.(Model) + if m.steer != nil { + t.Fatalf("a stale ack for a drained id must be ignored, got %+v", m.steer) + } + if got := stripANSIstr(m.renderSteer()); got != "" { + t.Fatalf("no card from a stale ack, got:\n%s", got) + } + }) +} + +// TestSteer_TUIIgnoresStaleAck is R4-1: a sent steer carries a fresh client-minted +// message_id and the ui resolves acks/echoes BY ID — a stale ack (drained id) or +// an ack for one bundle while a NEW bundle is in flight must not touch the live +// steer lifecycle (text echo matching would resurrect old frames on duplicate +// texts; the id is the only safe correlation key). +func TestSteer_TUIIgnoresStaleAck(t *testing.T) { + m, _ := newSteerModel(t, true) + m = startRunning(t, m, "first") + m = enqueueSteer(t, m, "first bundle") + + // Burn the first bundle: accepted ack, then the drain echo. + mm, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAccepted, Text: "first bundle", MessageID: "steer-0001"}) + m = mm.(Model) + mm, _ = m.Update(client.SteerEchoMsg{Text: "first bundle", MessageID: "steer-0001"}) + m = mm.(Model) + if m.steer != nil { + t.Fatalf("the drain echo must clear the in-flight card, got %+v", m.steer) + } + + // Send a SECOND bundle (same text shape, NEW id). A late ack carrying the STALE + // (drained) id must NOT overwrite the live bundle's text or phase; an ack for + // the live id advances it. + m = enqueueSteer(t, m, "second bundle") + mm2, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAccepted, Text: "POISONED", MessageID: "steer-0001"}) + m = mm2.(Model) + if m.steer == nil || m.steer.watermarkID() != "steer-0002" || m.steer.Text != "second bundle" || m.steer.Phase != steerPending { + t.Fatalf("the stale-id ack must not touch the live bundle, got %+v", m.steer) + } + mm3, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAccepted, Text: "second bundle", MessageID: "steer-0002"}) + m = mm3.(Model) + if m.steer == nil || m.steer.Phase != steerSent { + t.Fatalf("the live-id ack must advance the live bundle to sent, got %+v", m.steer) + } + + // A stale drān echo for the OLD id likewise must not clear the live card. + mm4, _ := m.Update(client.SteerEchoMsg{Text: "first bundle", MessageID: "steer-0001"}) + m = mm4.(Model) + if m.steer == nil || m.steer.Phase != steerSent { + t.Fatalf("the stale-id echo must not clear the live card, got %+v", m.steer) + } +} + +// TestSteer_TUIEditCancelThenRecompose is R4-2: ↑ on an in-flight steer CANCELS +// the outstanding bundle (exactly ONE steer_cancel carrying the bundle's +// message_id, fired WITHOUT waiting for the ack) and pulls the whole +// not-yet-drained text into the textarea as ONE editable blob; resend collapses +// to ONE new bundle with a NEW message_id. (Previously ↑ left the old frame live +// server-side and resend merged onto it — the edit-back duplication bug.) +func TestSteer_TUIEditCancelThenRecompose(t *testing.T) { + m, conv := newSteerModel(t, true) + m = startRunning(t, m, "first") + m = enqueueSteer(t, m, "second") + m = enqueueSteer(t, m, "third") // batch-newer-line: stages fresh, inbox full + mm, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAccepted, Text: "second", MessageID: "steer-0001"}) + m = mm.(Model) + + // ↑ with ONE staged line pending: it cancels the ACKED bundle and pulls the + // whole not-yet-drained set into ONE editable blob (acked text + staged lines). + mm2, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyUp}) + m = mm2.(Model) + runBatchLeaves(cmd) + + wantDraft := "second" + queueMergeSep + "third" + if got := m.ta.Value(); got != wantDraft { + t.Fatalf("↑ must pull the whole not-yet-drained set into ONE editable blob, got %q, want %q", got, wantDraft) + } + // Exactly ONE steer_cancel fired; the outstanding (cancelled) bundle is the + // SECOND — the merged re-collapse of the first frame plus the typed-while-batched + // line — so the cancel carries its NEW id (steer-0002), the whole reason the + // correlation is by id, not text. + var cancels []string + for _, fr := range conv.send.frames() { + if sc := fr.GetSteerCancel(); sc != nil { + cancels = append(cancels, sc.GetMessageId()) + } + } + if len(cancels) != 1 || cancels[0] != "steer-0002" { + t.Fatalf("↑ must fire exactly ONE steer_cancel for the outstanding bundle's id, got %v", cancels) + } + // The old bundle is GONE client-side as a sendable frame: the card is empty + // while the draft is being edited (the bundle is being rebuilt). + if got := stripANSIstr(m.renderSteer()); got != "" { + t.Fatalf("no sendable steer while the recomposed bundle is drafted, got:\n%s", got) + } + + // Edit and resend: it collapses to ONE new bundle with a NEW id. + m = typeText(t, m, " CHANGED") + mm3, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyEnter}) + m = mm3.(Model) + runBatchLeaves(cmd) + got := steerFrames(conv.send) + // Each send carries ONLY its own fragment under the ordered-queue model (the + // engine appends fragments to the pending bundle; re-sending merged text + // would re-append drained text — the duplication bug). The ↑ edit recomposed + // the whole draft into ONE new fragment with a fresh id. + want := []steerFrame{ + {text: "second", id: "steer-0001"}, + {text: "third", id: "steer-0002"}, + {text: "second" + queueMergeSep + "third CHANGED", id: "steer-0003"}, + } + if len(got) != len(want) { + t.Fatalf("steer frames = %+v, want the original fragments + ONE recomposed replacement", got) + } + for i := range want { + if got[i] != want[i] { + t.Fatalf("steer frame %d = %+v, want %+v (fragment sends; recompose collapses into ONE new fragment)", i, got[i], want[i]) + } + } + if m.steer == nil || m.steer.watermarkID() != "steer-0003" || m.steer.Phase != steerPending { + t.Fatalf("the resent replacement is the ONE in-flight bundle with the new id, got %+v", m.steer) + } +} + +// TestSteer_TUIEditBackNoDuplicate is R4-3: resend after ↑ does NOT duplicate the +// old text. ↑ CANCELS the outstanding bundle, so the old text can never be merged +// onto the resend — the replacement frame is EXACTLY the edited blob (the old +// Contains-masked-duplication assertion is gone: this is exact equality over the +// full frame list). Pre-rework the second send merged the pulled-back text onto +// the still-live old frame, producing "first\n\nfirst edited". +func TestSteer_TUIEditBackNoDuplicate(t *testing.T) { + m, conv := newSteerModel(t, true) + m = startRunning(t, m, "first") + m = enqueueSteer(t, m, "first") + mm, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAccepted, Text: "first", MessageID: "steer-0001"}) + m = mm.(Model) + + // ↑ pulls it back for editing (cancels the outstanding bundle). + mm2, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyUp}) + m = mm2.(Model) + runBatchLeaves(cmd) + if got := m.ta.Value(); got != "first" { + t.Fatalf("↑ must pull the in-flight steer back for editing, got %q", got) + } + + // Edit + resend: THE replacement frame is exactly the edited blob — no merged + // old+new duplication, and the old text does not ride the resend twice. + m = typeText(t, m, " edited") + mm3, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyEnter}) + m = mm3.(Model) + runBatchLeaves(cmd) + got := steerTexts(conv.send) + want := []string{"first", "first edited"} + if len(got) != len(want) { + t.Fatalf("steer frames = %v, want exactly the original + the edited replacement", got) + } + for i := range want { + if got[i] != want[i] { + t.Fatalf("steer frame %d = %q, want %q EXACTLY (no edit-back duplication)", i, got[i], want[i]) + } + } +} + +// TestSteer_TUIBurnedIdFreshDraft is R4-4: a drained message_id is BURNED — ↑ +// after the drain echo does NOT cancel (there is no pending bundle to retract) and +// does NOT pull the landed text; editing a shipped steer means typing a FRESH +// draft (resend is a NEW submission with its own new id). The landed line stays +// rendered in context and the "shipped" (landed-in-context) state is what the +// echo-implied transcript shows — the pending card has cleared because the drain +// echo rendered the line. +func TestSteer_TUIBurnedIdFreshDraft(t *testing.T) { + m, conv := newSteerModel(t, true) + m = startRunning(t, m, "first") + m = enqueueSteer(t, m, "second") + mm, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAccepted, Text: "second", MessageID: "steer-0001"}) + m = mm.(Model) + mm2, _ := m.Update(client.SteerEchoMsg{Text: "second", MessageID: "steer-0001"}) + m = mm2.(Model) + if m.steer != nil { + t.Fatalf("the drain echo clears the in-flight card, got %+v", m.steer) + } + + // The "shipped" state: the queued text now lives IN CONTEXT in the transcript + // (the landed line at the boundary), with no pending card. + view := stripANSIstr(m.View().Content) + if !strings.Contains(view, "second") { + t.Fatalf("the shipped steer must be visible in context, got:\n%s", view) + } + + // ↑ on an empty input AFTER the drain: no cancel frame (nothing pending) and + // no pull-back (the landed text is burned — the shipped line is not re-editable). + mm3, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyUp}) + m = mm3.(Model) + runBatchLeaves(cmd) + if got := steerCancelCount(conv.send); got != 0 { + t.Fatalf("↑ after drain must send NO steer_cancel (the id is burned), got %d", got) + } + if got := m.ta.Value(); got != "" { + t.Fatalf("↑ after drain must NOT pull the shipped text back (burned id), got %q", got) + } + + // Typing a new prompt and resending opens a FRESH submission with its OWN id — + // it is not a re-edit of the shipped one. + m = typeText(t, m, "replacement") + mm4, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyEnter}) + m = mm4.(Model) + runBatchLeaves(cmd) + got := steerFrames(conv.send) + want := []steerFrame{ + {text: "second", id: "steer-0001"}, + {text: "replacement", id: "steer-0002"}, + } + if len(got) != len(want) { + t.Fatalf("steer frames = %+v, want the burned original + a fresh submission", got) + } + for i := range want { + if got[i] != want[i] { + t.Fatalf("steer frame %d = %+v, want %+v (post-drain resend = new submission, new id)", i, got[i], want[i]) + } + } +} + +// TestSteer_TUIQueuedUntilLanded is R4-5: the steer shows as a PENDING card at the +// bottom UNTIL the EvSteer drain echo lands, then the landed line appears IN +// CONTEXT at its true position (the echo is stream-positioned: it renders exactly +// where the drain committed it — not at a client-guessed position). The card +// clears the same render pass the echo projects the line; a stale (already-burned +// id) echo never duplicates the landed line. +func TestSteer_TUIQueuedUntilLanded(t *testing.T) { + m, _ := newSteerModel(t, true) + m = startRunning(t, m, "first") + m = enqueueSteer(t, m, "check b.go too") + + // Pre-echo: the card is pending and the transcript does NOT yet carry the steer + // (the card preview collapses multi-word text onto one line, so the in-context + // occurrence check keys off the flat preview string, not the raw text). + cardPreview := truncate(oneLine("check b.go too"), queuePreviewWidth) + if got := stripANSIstr(m.renderSteer()); !strings.Contains(got, "sending") { + t.Fatalf("before the echo the card shows the pending state, got:\n%s", got) + } + m.refreshView() + before := stripANSIstr(m.View().Content) + if n := strings.Count(before, cardPreview); n != 1 { // card preview only + t.Fatalf("the pending steer must appear ONLY as the bottom card before the echo lands, found %d:\n%s", n, before) + } + + // The accepted ack keeps the card (sent, still un-landed) — no context line yet. + mm, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAccepted, Text: "check b.go too", MessageID: "steer-0001"}) + m = mm.(Model) + m.refreshView() + mid := stripANSIstr(m.View().Content) + if n := strings.Count(mid, cardPreview); n != 1 { + t.Fatalf("the acked-but-unlanded steer must appear ONLY as the pending card (1 render), found %d:\n%s", n, mid) + } + + // The drain echo lands: the card clears and the line appears in context (ONCE). + mm2, _ := m.Update(client.SteerEchoMsg{Text: "check b.go too", MessageID: "steer-0001"}) + m = mm2.(Model) + m.refreshView() + after := stripANSIstr(m.View().Content) + if n := strings.Count(after, "check b.go too"); n != 1 { + t.Fatalf("after the echo the landed line appears in context EXACTLY once, found %d:\n%s", n, after) + } + if got := stripANSIstr(m.renderSteer()); got != "" { + t.Fatalf("the echo clears the pending card in the same projection, got:\n%s", got) + } + + // A duplicate echo for the same (burned) id never re-renders the line. + mm3, _ := m.Update(client.SteerEchoMsg{Text: "check b.go too", MessageID: "steer-0001"}) + m = mm3.(Model) + m.refreshView() + again := stripANSIstr(m.View().Content) + if n := strings.Count(again, "check b.go too"); n != 1 { + t.Fatalf("a duplicate echo for a burned id must not duplicate the landed line, found %d:\n%s", n, again) + } +} + +// TestSteer_CardGolden locks the steer-mode card's frame (ANSI stripped): a run +// streaming with a sent (acked) steer in flight renders the "steer queued" card +// above the input, previewing the merged message as ONE item. This is the +// pixel-exact half of AC6.3's card (the behavioural half is +// TestSteer_TUIRendersAuthoritativeState). +func TestSteer_CardGolden(t *testing.T) { + m, _ := newSteerModel(t, true) + m = startRunning(t, m, "first") + m = enqueueSteer(t, m, "also check b.go") + mm, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAccepted, Text: "also check b.go"}) + m = mm.(Model) + m.refreshView() + got := stripANSI([]byte(m.View().Content)) + compareGolden(t, "steer_card.golden", got) +} diff --git a/cmd/mecatui/ui/steer_watermark_test.go b/cmd/mecatui/ui/steer_watermark_test.go new file mode 100644 index 0000000000..0a8625587c --- /dev/null +++ b/cmd/mecatui/ui/steer_watermark_test.go @@ -0,0 +1,90 @@ +package ui + +import ( + "testing" + + "github.com/stacklok/mecatl/cmd/mecatui/client" +) + +// Scenario 6 of docs/acceptance/steer-while-running.md (the mecatui half) — the +// queue-split contract live-tested in session 00a10fc8 previously failed: the +// first ack was dropped because it did not match the live watermark id (the +// newest send). applySteerOutcome must split the ordered queue on ANY queued +// id (the prefix renders sent, the suffix stays pending), advancing the +// watermark to the new tail. + +// TestSteer_WatermarkSplitOnQueuedAck is R5-1 (the m1-in-flight, m2-queued, +// m3-queued, first-echo-arrives case): the DRAIN ECHO owns the queue split — +// the ack only advances the lifecycle phase. Splitting on the watermark id +// drops the drained prefix and keeps the suffix pending as the new bundle. +func TestSteer_WatermarkSplitOnQueuedAck(t *testing.T) { + m, _ := newSteerModel(t, true) + m = startRunning(t, m, "initial") + + m = enqueueSteer(t, m, "first") + m = enqueueSteer(t, m, "second") + m = enqueueSteer(t, m, "third") + + // The queue holds three sends; the watermark is the newest. + if m.steer == nil || m.steer.watermarkID() != "steer-0003" || len(m.steer.Sends) != 3 { + t.Fatalf("pre-drain state = %+v, want one bundle with three sends watermarked at steer-0003", m.steer) + } + + // The FIRST ack (for steer-0001) advances the lifecycle to steerSent; the + // queue carries it unsplit (only the echo drains a prefix). + mm, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAccepted, Text: "first", MessageID: "steer-0001"}) + m = mm.(Model) + if m.steer == nil || m.steer.watermarkID() != "steer-0003" || len(m.steer.Sends) != 3 || m.steer.Phase != steerSent { + t.Fatalf("the first ack must advance the phase (not split the queue), got %+v", m.steer) + } + + // The drain echo for the TAIL (watermark) splits the prefix and clears it; + // the whole bundle drained (nothing left to scope). + mm2, _ := m.Update(client.SteerEchoMsg{Text: "first\n\nsecond\n\nthird", MessageID: "steer-0003"}) + m = mm2.(Model) + if m.steer != nil { + t.Fatalf("the drain echo must clear the whole bundle, got %+v", m.steer) + } +} + +// TestSteer_AppendAckAdvancesPhase is R5-2: an "appended" ack scopes any +// queued id and only advances the lifecycle phase (the queue itself only +// splits on the echo, never on an ack). +func TestSteer_AppendAckAdvancesPhase(t *testing.T) { + m, _ := newSteerModel(t, true) + m = startRunning(t, m, "initial") + + m = enqueueSteer(t, m, "one") + m = enqueueSteer(t, m, "two") + m = enqueueSteer(t, m, "three") + + // The middle-id ack advances the phase; the queue itself stays whole. + mm, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAppended, Text: "one\n\ntwo", MessageID: "steer-0002"}) + m = mm.(Model) + if m.steer == nil || m.steer.watermarkID() != "steer-0003" || len(m.steer.Sends) != 3 || m.steer.Phase != steerSent { + t.Fatalf("the appended ack must advance the phase (not split), got %+v", m.steer) + } +} + +// TestSteer_BurnedAckStillIgnored is R5-3: an ack for a drained (burned) id +// is dropped — the burned guard must survive relaxation. +func TestSteer_BurnedAckStillIgnored(t *testing.T) { + m, _ := newSteerModel(t, true) + m = startRunning(t, m, "initial") + + m = enqueueSteer(t, m, "one") + mm, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAccepted, Text: "one", MessageID: "steer-0001"}) + m = mm.(Model) + mm2, _ := m.Update(client.SteerEchoMsg{Text: "one", MessageID: "steer-0001"}) + m = mm2.(Model) + if m.steer != nil { + t.Fatalf("the echo must clear the live bundle, got %+v", m.steer) + } + + // A late duplicate ack for the burned id must NOT resurrect a card. + mm3, _ := m.Update(client.SteerOutcomeMsg{Outcome: client.SteerAccepted, Text: "one", MessageID: "steer-0001"}) + m = mm3.(Model) + if m.steer != nil { + t.Fatalf("the duplicate ack must be ignored, got %+v", m.steer) + } +} diff --git a/cmd/mecatui/ui/testdata/steer_card.golden b/cmd/mecatui/ui/testdata/steer_card.golden new file mode 100644 index 0000000000..afd1087a5d --- /dev/null +++ b/cmd/mecatui/ui/testdata/steer_card.golden @@ -0,0 +1,30 @@ + mecatui · session #b9ba2286 · mode default +──────────────────────────────────────────────────────────────────────────────────────────────────── + ▌ you + │ first + + + + + + + + + + + + +┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +┃ ┃ +┃ ⏳ steer queued · ↑ edit · esc retract ┃ +┃ also check b.go ┃ +┃ ┃ +┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ + +│ +│ Ask mecatl to do something… (enter to send · shift+enter for newline · ? for help) +│ +│ +──────────────────────────────────────────────────────────────────────────────────────────────────── + ⣾ thinking… ctx 0 · ↑0 ↓0 cache 0% +enter queue · esc cancel/clear · ? help · / commands · ctrl+c quit \ No newline at end of file diff --git a/cmd/mecatui/ui/update.go b/cmd/mecatui/ui/update.go index ed9b3a2a29..7c852ff72c 100644 --- a/cmd/mecatui/ui/update.go +++ b/cmd/mecatui/ui/update.go @@ -3,6 +3,7 @@ package ui import ( "context" "fmt" + "slices" "strings" "time" @@ -879,11 +880,198 @@ func (m Model) updateStreamSecondary(msg tea.Msg) (tea.Model, tea.Cmd) { // Text (no model content). m.conv.addRecoverNotice(msg.Text) return m.afterEvent() + case client.SteerOutcomeMsg: + return m.applySteerOutcome(msg) + case client.SteerEchoMsg: + return m.applySteerEcho(msg) default: return m, nil } } +// mintSteerID derives the next session-scoped steer message id (1-based +// "steer-%04d"). Session-scoped needs no crypto — within one session the +// client is the sole sender on its single stream, and the server only ever +// echoes them verbatim; a per-process shared counter would not survive the +// module boundary anyway. +func (m *Model) mintSteerID() string { + m.steerSeq++ + return fmt.Sprintf("steer-%04d", m.steerSeq) +} + +// applySteerOutcome reduces the server's AUTHORITATIVE ack for a steer / +// steer_cancel frame into the steer lifecycle, keyed BY message_id (never text — +// a stale ack for a drained id or one bundle's ack during a recomposed bundle's +// flight is ignored). The phases advance ONLY here (and in applySteerEcho), +// never on send — the client cannot observe the drain moment across stream +// latency, so it renders what the server reports: +// +// - accepted → steerSent (parked in the run's inbox, awaiting the boundary +// drain); Text adopts the acked text (the version the engine last confirmed). +// - appended → merged into the pending bundle server-side (it drains as ONE +// bundle). The queue display stays derived from Sends (the ack's lone +// fragment must NOT overwrite the merged join). +// - too_late + promoted → steerPromoted (the run had gone terminal; the text +// auto-started a fresh follow-up run). The card states it honestly. +// - retracted → steerRetracted (the cancel won; the run drains nothing). +// - none_pending → the cancel found an empty slot (the drain already won, or +// nothing was pending); the steer state clears (there is nothing to show). +// +// An ack for a frame the ui no longer tracks (steer == nil, a burned id, or an +// id that is not the live bundle's — e.g. a late ack after the echo committed, +// or one outstanding bundle's ack racing a recomposed replacement) is +// idempotently dropped. +// steerTrace emits a one-line correlation trace into the status bar when +// Deps.DebugSteer is set (env MECATUI_DEBUG_STEER=1): the ack/echo kind, the +// incoming message_id, the live bundle's id, and the decision. It makes a stuck +// or mis-correlated steer lifecycle visible in the TUI rather than opaque. A nil +// live bundle renders "-". +func (m Model) steerTrace(kind, inID, decision string) string { + if !m.deps.DebugSteer { + return "" + } + live := "-" + if m.steer != nil { + live = orDash(m.steer.watermarkID()) + } + return m.deps.Theme.Style("muted").Render("[steer] " + kind + " id=" + orDash(inID) + " live=" + live + " → " + decision) +} + +// steerSendIndex returns the index of the send with id in the ordered queue, or +// -1 when absent (a watermark split drops sends[0:idx+1]). +func steerSendIndex(sends []steerQueuedSend, id string) int { + return slices.IndexFunc(sends, func(s steerQueuedSend) bool { return s.ID == id }) +} + +// joinSteerSends re-joins the ordered queue's fragment texts with the blank-line +// separator (the merged display text shown on the card). +func joinSteerSends(sends []steerQueuedSend) string { + parts := make([]string, 0, len(sends)) + for _, s := range sends { + parts = append(parts, s.Text) + } + return strings.Join(parts, queueMergeSep) +} + +func orDash(s string) string { + if s == "" { + return "-" + } + return s +} + +func (m Model) applySteerOutcome(msg client.SteerOutcomeMsg) (tea.Model, tea.Cmd) { + if m.steer == nil { + // A stale ack (drained id, or an id-less legacy ack with nothing live) + // reconciles to nothing — there is no card to advance. + if tr := m.steerTrace("ack:"+string(msg.Outcome), msg.MessageID, "dropped (no live bundle)"); tr != "" { + m.statusMsg = tr + } + return m.afterEvent() + } + // Correlate BY QUEUED id: acks scope a queued send (an EMPTY id is legacy + // and falls through). A drained id leaves the queue at the echo split, so + // "not in queue" alone marks it stale — no burn map needed. + if msg.MessageID != "" && steerSendIndex(m.steer.Sends, msg.MessageID) < 0 { + if tr := m.steerTrace("ack:"+string(msg.Outcome), msg.MessageID, "ignored (id not in queue)"); tr != "" { + m.statusMsg = tr + } + return m.afterEvent() + } + switch msg.Outcome { + case client.SteerAccepted: + m.steer.Phase = steerSent + m.statusMsg = m.deps.Theme.Style("muted").Render("steer queued") + case client.SteerAppended: + // The engine merged the new line into the outstanding pending bundle (append + // is the default): the bundle's Text grew, and it still drains as ONE. The + // card honestly reports "merged onto the in-flight steer" — the merged queue + // display stays derived from Sends (the ack's lone fragment must NOT + // overwrite it). + m.steer.Phase = steerSent + m.statusMsg = m.deps.Theme.Style("muted").Render("line merged onto the in-flight steer (↑ to edit)") + case client.SteerTooLate: + if msg.Promoted { + m.steer.Phase = steerPromoted + m.statusMsg = m.deps.Theme.Style("muted").Render("steer arrived after the run ended — sent as a follow-up") + } else { + // Promotion failed (routing / run-entry / lease / funnel error): the + // text was NOT delivered. Do NOT report a successful follow-up — show an + // explicit not-sent so the user can retry (the text is preserved). + m.steer.Phase = steerFailed + m.statusMsg = m.deps.Theme.Style("warning").Render("steer not sent — promotion failed (try again)") + } + case client.SteerRetracted: + m.steer.Phase = steerRetracted + m.statusMsg = m.deps.Theme.Style("muted").Render("steer retracted") + case client.SteerNonePending: + m.steer = nil + m.statusMsg = m.deps.Theme.Style("muted").Render("no pending steer to retract") + } + return m.afterEvent() +} + +// applySteerEcho reduces the run's steer-inbox DRAIN echo (the COMMITTED text +// recorded into history). It is the authoritative "the steer landed" signal, +// keyed BY message_id: the in-flight card clears (the committed text now lives +// in the transcript) and the landed line renders IN CONTEXT at the echo's stream +// position (after the settling tool activity, before the next turn-start). A +// duplicate echo for an already-burned id is ignored, so the landed line never +// double-renders. +func (m Model) applySteerEcho(msg client.SteerEchoMsg) (tea.Model, tea.Cmd) { + // Watermark split: the echo's message_id is the TAIL of the drained bundle — + // drop every send in this bundle's queue up to and including it (drained), + // and keep the suffix (still pending). The queue is the ONLY correlation + // source: a duplicate or stale echo finds its id already out of the queue + // and is a no-op (no burn map). For an id-less legacy echo, clear the whole + // bundle. The landed line renders ONLY when the echo matched live state — + // a stale/duplicate echo never double-renders the transcript. + // The echo is authoritative ONLY while a live bundle can correlate it — a + // late/duplicate echo with no live bundle drops to the no-render arm (never + // re-renders the transcript line; the one drain = one echo invariant makes + // the duplicate a server non-occurrence). + landed := false + if m.steer != nil { + if msg.MessageID == "" { + // id-less legacy echo: clear the whole bundle. + m.steer = nil + landed = true + } else { + idx := steerSendIndex(m.steer.Sends, msg.MessageID) + switch { + case idx >= 0: + rest := m.steer.Sends[idx+1:] + if len(rest) == 0 { + m.steer = nil // the whole bundle drained + } else { + m.steer.Sends = rest + m.steer.Text = joinSteerSends(rest) + } + landed = true + if tr := m.steerTrace("echo", msg.MessageID, "split queue at watermark; tail pending"); tr != "" { + m.statusMsg = tr + } + case len(m.steer.Sends) == 0: + // An ack already emptied the queue client-side (the phase + // advanced); the echo is the drain confirming it — clear. + m.steer = nil + landed = true + default: + if tr := m.steerTrace("echo", msg.MessageID, "no matching live bundle (card left)"); tr != "" { + m.statusMsg = tr + } + } + } + } + if landed { + // Render the landed line IN CONTEXT (its true stream position — the echo + // arrives exactly where the drain committed the user continuation). + m.conv.addUser(msg.Text) + m.statusMsg = m.deps.Theme.Style("muted").Render("steer applied") + } + return m.afterEvent() +} + // applyResult handles a terminal ResultMsg: it folds the run's usage into the running // totals, surfaces a terminal error, ends the run, and drains any queued prompts. // Extracted from updateStreamEvent's switch to keep that dispatcher flat. @@ -1724,31 +1912,7 @@ func (m Model) onRunningKey(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) { // textarea default so the keypress drives the overlay, never the input. return m.openAgents() case key.Matches(msg, m.keys.Cancel): - if strings.TrimSpace(m.ta.Value()) != "" { - // Staged-but-unsent input: esc clears it first (mirrors a text editor's - // "esc clears the line"), leaving the queue and the run untouched. - m.ta.Reset() - return m.afterInputEdit(nil) - } - if len(m.queued) > 0 { - // No live input but staged follow-ups: esc drops the queue before it would - // cancel the run, so a user who changed their mind can clear the backlog - // without killing the in-flight turn. - m.queued = nil - m.statusMsg = m.deps.Theme.Style("muted").Render("queue cleared") - m.refreshView() - return m, nil - } - // Nothing staged: esc cancels the run (today's behaviour — the run ends with - // stop "cancelled"; the stream stays open until that terminal result). - stream := m.stream - m.statusMsg = "cancelling…" - return m, func() tea.Msg { - if stream != nil { - _ = stream.SendCancel() - } - return nil - } + return m.onRunningCancel() case key.Matches(msg, m.keys.Newline): m.ta.InsertRune('\n') return m.afterInputEdit(nil) @@ -1767,6 +1931,54 @@ func (m Model) onRunningKey(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) { } } +// onRunningCancel handles esc while a run streams, in layered priority (extracted +// from onRunningKey to keep its cyclomatic complexity under the bound): clear the +// staged input first, else clear the staged queue, else retract a pending steer +// (steer mode), else cancel the in-flight run. +func (m Model) onRunningCancel() (tea.Model, tea.Cmd) { + if strings.TrimSpace(m.ta.Value()) != "" { + // Staged-but-unsent input: esc clears it first (mirrors a text editor's + // "esc clears the line"), leaving the queue and the run untouched. + m.ta.Reset() + return m.afterInputEdit(nil) + } + if len(m.queued) > 0 { + // No live input but staged follow-ups: esc drops the queue before it would + // cancel the run, so a user who changed their mind can clear the backlog + // without killing the in-flight turn. + m.queued = nil + m.statusMsg = m.deps.Theme.Style("muted").Render("queue cleared") + m.refreshView() + return m, nil + } + // Steer mode with a pending/sent (un-drained) steer: esc RETRACTS it via a + // steer_cancel frame before it would cancel the run — the mirror of the + // clear-the-queue layer above. The authoritative retracted/none_pending ack + // drives the lifecycle (applySteerOutcome); a drain that already won reports + // none_pending and clears the card. + if m.steer != nil && (m.steer.Phase == steerPending || m.steer.Phase == steerSent) { + stream := m.stream + id := m.steer.watermarkID() + m.statusMsg = m.deps.Theme.Style("muted").Render("retracting steer…") + return m, func() tea.Msg { + if stream != nil { + _ = stream.SendSteerCancel(id) + } + return nil + } + } + // Nothing staged: esc cancels the run (today's behaviour — the run ends with + // stop "cancelled"; the stream stays open until that terminal result). + stream := m.stream + m.statusMsg = "cancelling…" + return m, func() tea.Msg { + if stream != nil { + _ = stream.SendCancel() + } + return nil + } +} + // enqueuePrompt stages the current textarea text as a follow-up to drain when the // running turn ends (see drainQueue). It trims the input; an empty line is a no-op // (so enter on a blank prompt mid-run does nothing). At the cap it rejects with a @@ -1784,6 +1996,20 @@ func (m Model) enqueuePrompt() (tea.Model, tea.Cmd) { if text == "" { return m, nil } + // Steer mode (Capabilities.Steer): mid-run enter sends a steer FRAME on the + // live Converse stream instead of staging locally — the engine drains it at the + // next turn boundary. The client-side merge (#228's merge-always) collapses the + // typed text into ONE bundle with a fresh message_id; a line typed while a + // bundle is still outstanding BATCHES onto it (the single-slot inbox can only + // ever hold one, so the wire never carries two at once — the batched line + // rides the bundle's Text client-side and the wire frame is the whole merged + // text). The ↑ edit-back cancels the outstanding bundle and recomposes. Paste + // placeholders expand inside sendSteer (there is no queue-full cap on the + // steer path — the engine's single-slot inbox is the bound). When steer is + // disabled the #228 local merge-queue below owns mid-run input, byte-identical. + if m.caps.Steer && m.stream != nil { + return m.sendSteer(text) + } if len(m.queued) >= maxQueued { m.statusMsg = m.deps.Theme.Style("muted").Render(fmt.Sprintf("queue full (%d)", maxQueued)) return m, nil @@ -1810,6 +2036,55 @@ func (m Model) enqueuePrompt() (tea.Model, tea.Cmd) { return m.afterInputEdit(nil) } +// sendSteer collapses the textarea text into ONE steer bundle and sends it on +// the live Converse stream with a FRESH client-minted message_id. A bundle is +// always a complete replacement: an in-flight bundle is CANCELED first (the ↑ +// edit-back path, cancel-then-recompose), so the old text is never merged onto +// the new frame — resend carries exactly what the user edited, with a new id. +// The lifecycle then advances ONLY on the server's authoritative steer.outcome +// ack / steer drain echo (updateStreamSecondary), correlated BY the minted id — +// never assumed client-side. The textarea is reset and the card moves to the +// pending state. +func (m Model) sendSteer(text string) (tea.Model, tea.Cmd) { + // Expand staged large-paste placeholders so the steer frame carries FINAL text + // (mirrors the local-queue path). There is no queue-full cap here — the engine's + // single-slot inbox is the bound — so the expansion runs unconditionally. The + // staged store is textarea-scoped: the input is reset below, so its markers are + // gone and the store must not outlive them. A deleted marker's content is + // silently dropped (the image-marker UX). + if len(m.stagedPastes) > 0 { + text = strings.TrimSpace(expandPastePlaceholders(text, m.stagedPastes)) + m.stagedPastes = nil + m.nextPasteN = 0 + if text == "" { + // Every marker was deleted and nothing else was typed: nothing to steer. + m.ta.Reset() + return m.afterInputEdit(nil) + } + } + // A new send mints a fresh id and appends to the ordered queue; the WIRE + // carries ONLY this line's fragment (the engine appends it to the pending + // bundle — re-sending the full merged text would re-append drained text, the + // duplication bug). The bundle's display Text re-joins from the whole queue. + id := m.mintSteerID() + if m.steer == nil { + m.steer = &steerState{Phase: steerPending} + } + m.steer.Sends = append(m.steer.Sends, steerQueuedSend{ID: id, Text: text}) + m.steer.Text = joinSteerSends(m.steer.Sends) + stream := m.stream + sendMsg := func() tea.Msg { + if err := stream.SendSteer(text, id); err != nil { + return client.StreamErrMsg{Err: err} + } + return nil + } + m.ta.Reset() + m.statusMsg = m.deps.Theme.Style("muted").Render("steering…") + m.refreshView() + return m, sendMsg +} + // wantsEditBack reports whether msg is the EditBack key (↑) pressed on an EMPTY // input line with a non-empty queue — the precondition for pulling the merged queue // back into the textarea (see editBackQueue). Gating on empty input keeps ↑ a plain @@ -1817,18 +2092,50 @@ func (m Model) enqueuePrompt() (tea.Model, tea.Cmd) { // guard reads as one predicate in each switch (and keeps their cyclomatic complexity // under the cap). func (m Model) wantsEditBack(msg tea.KeyPressMsg) bool { - return key.Matches(msg, m.keys.EditBack) && - strings.TrimSpace(m.ta.Value()) == "" && len(m.queued) > 0 + if !key.Matches(msg, m.keys.EditBack) || strings.TrimSpace(m.ta.Value()) != "" { + return false + } + // Steer mode: ↑ pulls the COMBINED in-flight steer back for editing (the + // merged message, not a queue). Steer state takes precedence — with steer + // armed mid-run the local queue is never populated (steer owns mid-run input). + if m.steer != nil && (m.steer.Phase == steerPending || m.steer.Phase == steerSent) { + return true + } + return len(m.queued) > 0 } // editBackQueue is the inverse of enqueuePrompt: it pulls the whole staged queue // back into the textarea (merged by queueMergeSep) so the user can revise it, and // CLEARS the queue + any pause. It is non-destructive — bound to ↑ on an EMPTY input // line with a non-empty queue (see onRunningKey / onIdleKey), distinct from esc, -// which clears the queue outright. It sends nothing: the merged text is now an -// ordinary draft the user edits and (re)submits or (re)enqueues. Callers gate on the -// empty-input / non-empty-queue precondition, so this assumes m.queued is non-empty. +// which clears the queue outright. It sends nothing on the queue path: the merged +// text is now an ordinary draft the user edits and (re)submits or (re)enqueues. +// Callers gate on the empty-input / non-empty-queue precondition, so this assumes +// m.queued is non-empty. func (m Model) editBackQueue() (tea.Model, tea.Cmd) { + // Steer mode (task-10 queue model): ↑ CANCELS the outstanding bundle WITHOUT + // waiting for the ack and pulls the whole not-yet-drained text back into the + // textarea as ONE editable blob. The old frame is NEVER left live to be merged + // onto the resend (the edit-back duplication bug): the steer_cancel rides the + // stream first, the draft is immediately editable, and the resend collapses + // the edited blob into ONE new bundle with a NEW message_id. A drained id is + // burned — after the echo there is no bundle to edit (wantsEditBack gates). + if m.steer != nil && len(m.steer.Sends) > 0 { + stream := m.stream + id := m.steer.watermarkID() + draft := joinSteerSends(m.steer.Sends) + m.steer = nil + m.ta.SetValue(draft) + m.statusMsg = m.deps.Theme.Style("muted").Render("steer pulled back for editing — resend to replace") + m.refreshView() + cancel := func() tea.Msg { + if stream != nil { + _ = stream.SendSteerCancel(id) + } + return nil + } + return m.afterInputEdit(cancel) + } m.ta.SetValue(strings.Join(m.queued, queueMergeSep)) m.queued = nil m.queuePaused = "" @@ -3009,6 +3316,12 @@ func (m Model) endRun(stop string) Model { m.approval.ask = pendingAsk{} m.approval.queue = nil m.approval.resolvedAsks = nil + // The run is terminal: its steer inbox is closed, so any in-flight steer state is + // over. A pending/sent (un-drained, un-acked) steer simply clears — it is lost + // with the run (the honest best-effort contract); a promoted/retracted terminal + // state already rendered its card. Clearing here keeps a dead run's steer card + // from surviving into idle. + m.steer = nil m.phase = phaseIdle // Ensure the input is focused now the run is done. With type-while-running the // input is already focused during a run, so this is a no-op on the common path; diff --git a/cmd/mecatui/ui/view.go b/cmd/mecatui/ui/view.go index 5a86a0beb8..4d4d72e174 100644 --- a/cmd/mecatui/ui/view.go +++ b/cmd/mecatui/ui/view.go @@ -887,6 +887,56 @@ func (m Model) renderQueue() string { return th.Style("askCard").Render(b.String()) } +// renderSteer draws the steer-mode (Capabilities.Steer) "in-flight steer" card +// shown just above the input whenever a steer is live. It mirrors renderQueue's +// visual language (muted / toolArgs / ctxWarn on the askCard style) but reflects +// the AUTHORITATIVE server-reported state — the engine is the sole authority on +// what happened to a steer (the client cannot observe the drain moment across +// stream latency), so the card shows what the server acked/echoed, never a +// client-side guess. Returns "" when no steer is in flight (layout omits it then). +// +// Four honest states: +// - PENDING (steerPending): sent, ack not yet back — a muted "⏳ steer: sending…". +// - SENT (steerSent): acked accepted/appended, parked for the next turn +// boundary — a muted "⏳ steer queued · ↑ edit · esc retract". +// - PROMOTED (steerPromoted): acked too_late — the run had ended, so the text +// auto-started a follow-up — a ctxWarn "↪ steer sent as a follow-up (run had +// already ended)". +// - RETRACTED (steerRetracted): the steer_cancel won — a muted "✕ steer +// retracted". +func (m Model) renderSteer() string { + if m.steer == nil { + return "" + } + th := m.deps.Theme + hk := m.helpKeyMarkings() + muted := th.Style("muted") + var b strings.Builder + switch m.steer.Phase { + case steerPending: + b.WriteString(muted.Render("⏳ steer: sending…")) + case steerSent: + b.WriteString(muted.Render("⏳ steer queued · " + hk.editBack + " edit · " + hk.cancel + " retract")) + case steerPromoted: + b.WriteString(th.Style("ctxWarn").Render("↪ steer sent as a follow-up (run had already ended)")) + case steerFailed: + b.WriteString(th.Style("warning").Render("✕ steer not sent (promotion failed) · ↑ to edit · esc to drop")) + case steerRetracted: + b.WriteString(muted.Render("✕ steer retracted")) + } + // Preview each logical message on its own line. A re-composed (↑-edit) + // fragment carries the WHOLE merged bundle in its Text (with blank-line + // separators INSIDE), so split each send's text on the merge separator: + // the wire stays one frame while the card shows the parts as separate + // queued lines. + for _, s := range m.steer.Sends { + for _, part := range strings.Split(s.Text, queueMergeSep) { + b.WriteString("\n" + th.Style("toolArgs").Render(" "+truncate(oneLine(runePrefix(part, queuePreviewBound)), queuePreviewWidth))) + } + } + return th.Style("askCard").Render(b.String()) +} + // renderInput renders the textarea (now always focused — it stays editable while a // run streams so a follow-up can be composed and enqueued), memoized on the // renderer's single-entry input cache (renderer.inputKey — see its doc): when no diff --git a/contracts/gen/go/mecatl/v1/harness.pb.go b/contracts/gen/go/mecatl/v1/harness.pb.go index 548b109cc5..196268cdc9 100644 --- a/contracts/gen/go/mecatl/v1/harness.pb.go +++ b/contracts/gen/go/mecatl/v1/harness.pb.go @@ -168,6 +168,84 @@ func (ApprovalVerdict) EnumDescriptor() ([]byte, []int) { return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{1} } +// SteerOutcome is the AUTHORITATIVE result of a steer inbox transition, +// mirroring the engine's closed-enum agent.SteerOutcome. The client cannot +// observe the exact drain moment across stream latency, so the engine reports +// what actually happened to the steer rather than letting the client guess. +type SteerOutcome int32 + +const ( + // STEER_OUTCOME_UNSPECIFIED is the zero value; the server never sends it. + SteerOutcome_STEER_OUTCOME_UNSPECIFIED SteerOutcome = 0 + // STEER_OUTCOME_ACCEPTED: the steer parked in the (empty) pending slot; it + // drains at the next turn boundary unless retracted first. + SteerOutcome_STEER_OUTCOME_ACCEPTED SteerOutcome = 1 + // STEER_OUTCOME_APPENDED: the steer found the pending slot OCCUPIED and was + // MERGED into it (append is the default for a second steer): the pending + // bundle's text grew by a blank-line separator + text, and it still drains as + // ONE bundle. Distinguished from ACCEPTED (a NEW pending bundle) so the client + // can render "merged onto pending" honestly. Replacing the pending bundle is + // explicit: steer_cancel, then resend with a fresh message_id. + SteerOutcome_STEER_OUTCOME_APPENDED SteerOutcome = 2 + // STEER_OUTCOME_RETRACTED: a steer_cancel found a pending steer and + // retracted it; the run drains nothing for it. + SteerOutcome_STEER_OUTCOME_RETRACTED SteerOutcome = 3 + // STEER_OUTCOME_NONE_PENDING: a steer_cancel found the slot EMPTY — there + // was nothing to retract (no steer pending, or the run terminal). + SteerOutcome_STEER_OUTCOME_NONE_PENDING SteerOutcome = 4 + // STEER_OUTCOME_TOO_LATE: the steer arrived after the run went terminal + // (the inbox was closed). The server promoted the text to a fresh follow-up + // run (promoted=true on the event) rather than dropping it. + SteerOutcome_STEER_OUTCOME_TOO_LATE SteerOutcome = 5 +) + +// Enum value maps for SteerOutcome. +var ( + SteerOutcome_name = map[int32]string{ + 0: "STEER_OUTCOME_UNSPECIFIED", + 1: "STEER_OUTCOME_ACCEPTED", + 2: "STEER_OUTCOME_APPENDED", + 3: "STEER_OUTCOME_RETRACTED", + 4: "STEER_OUTCOME_NONE_PENDING", + 5: "STEER_OUTCOME_TOO_LATE", + } + SteerOutcome_value = map[string]int32{ + "STEER_OUTCOME_UNSPECIFIED": 0, + "STEER_OUTCOME_ACCEPTED": 1, + "STEER_OUTCOME_APPENDED": 2, + "STEER_OUTCOME_RETRACTED": 3, + "STEER_OUTCOME_NONE_PENDING": 4, + "STEER_OUTCOME_TOO_LATE": 5, + } +) + +func (x SteerOutcome) Enum() *SteerOutcome { + p := new(SteerOutcome) + *p = x + return p +} + +func (x SteerOutcome) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SteerOutcome) Descriptor() protoreflect.EnumDescriptor { + return file_mecatl_v1_harness_proto_enumTypes[2].Descriptor() +} + +func (SteerOutcome) Type() protoreflect.EnumType { + return &file_mecatl_v1_harness_proto_enumTypes[2] +} + +func (x SteerOutcome) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SteerOutcome.Descriptor instead. +func (SteerOutcome) EnumDescriptor() ([]byte, []int) { + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{2} +} + // HookDecision is the outcome a hook fire produced. Mirrors // session.HookDecision; lets a client colour/rank a hook notice without parsing // its prose. @@ -217,11 +295,11 @@ func (x HookDecision) String() string { } func (HookDecision) Descriptor() protoreflect.EnumDescriptor { - return file_mecatl_v1_harness_proto_enumTypes[2].Descriptor() + return file_mecatl_v1_harness_proto_enumTypes[3].Descriptor() } func (HookDecision) Type() protoreflect.EnumType { - return &file_mecatl_v1_harness_proto_enumTypes[2] + return &file_mecatl_v1_harness_proto_enumTypes[3] } func (x HookDecision) Number() protoreflect.EnumNumber { @@ -230,7 +308,7 @@ func (x HookDecision) Number() protoreflect.EnumNumber { // Deprecated: Use HookDecision.Descriptor instead. func (HookDecision) EnumDescriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{2} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{3} } // SoulProvenance records WHERE the selected soul originated, mirroring the @@ -281,11 +359,11 @@ func (x SoulProvenance) String() string { } func (SoulProvenance) Descriptor() protoreflect.EnumDescriptor { - return file_mecatl_v1_harness_proto_enumTypes[3].Descriptor() + return file_mecatl_v1_harness_proto_enumTypes[4].Descriptor() } func (SoulProvenance) Type() protoreflect.EnumType { - return &file_mecatl_v1_harness_proto_enumTypes[3] + return &file_mecatl_v1_harness_proto_enumTypes[4] } func (x SoulProvenance) Number() protoreflect.EnumNumber { @@ -294,7 +372,7 @@ func (x SoulProvenance) Number() protoreflect.EnumNumber { // Deprecated: Use SoulProvenance.Descriptor instead. func (SoulProvenance) EnumDescriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{3} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{4} } // TeamMemberStopReason is WHY a stopped member stopped (team.end disposition snapshot). @@ -339,11 +417,11 @@ func (x TeamMemberStopReason) String() string { } func (TeamMemberStopReason) Descriptor() protoreflect.EnumDescriptor { - return file_mecatl_v1_harness_proto_enumTypes[4].Descriptor() + return file_mecatl_v1_harness_proto_enumTypes[5].Descriptor() } func (TeamMemberStopReason) Type() protoreflect.EnumType { - return &file_mecatl_v1_harness_proto_enumTypes[4] + return &file_mecatl_v1_harness_proto_enumTypes[5] } func (x TeamMemberStopReason) Number() protoreflect.EnumNumber { @@ -352,7 +430,7 @@ func (x TeamMemberStopReason) Number() protoreflect.EnumNumber { // Deprecated: Use TeamMemberStopReason.Descriptor instead. func (TeamMemberStopReason) EnumDescriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{4} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{5} } // Kind discriminates the media kind of the part. @@ -392,11 +470,11 @@ func (x Content_Kind) String() string { } func (Content_Kind) Descriptor() protoreflect.EnumDescriptor { - return file_mecatl_v1_harness_proto_enumTypes[5].Descriptor() + return file_mecatl_v1_harness_proto_enumTypes[6].Descriptor() } func (Content_Kind) Type() protoreflect.EnumType { - return &file_mecatl_v1_harness_proto_enumTypes[5] + return &file_mecatl_v1_harness_proto_enumTypes[6] } func (x Content_Kind) Number() protoreflect.EnumNumber { @@ -461,11 +539,11 @@ func (x ContentBlock_Kind) String() string { } func (ContentBlock_Kind) Descriptor() protoreflect.EnumDescriptor { - return file_mecatl_v1_harness_proto_enumTypes[6].Descriptor() + return file_mecatl_v1_harness_proto_enumTypes[7].Descriptor() } func (ContentBlock_Kind) Type() protoreflect.EnumType { - return &file_mecatl_v1_harness_proto_enumTypes[6] + return &file_mecatl_v1_harness_proto_enumTypes[7] } func (x ContentBlock_Kind) Number() protoreflect.EnumNumber { @@ -804,6 +882,15 @@ type ServerCapabilities struct { // storage_health is true only when the backend implements bounded indexed // health and this deployment has a management authorizer. StorageHealth bool `protobuf:"varint,23,opt,name=storage_health,json=storageHealth,proto3" json:"storage_health,omitempty"` + // steer is true when the server's engines arm the mid-run steer inbox + // (steer-while-running, issue #512): a client may then send `steer` / + // `steer_cancel` frames on the live Converse stream and render the EvSteer + // drain echo. When false (an older server, or steer disabled in + // composition), the client keeps its local merge-queue behaviour — a steer + // frame sent to such a server is promoted to a fresh follow-up run (the + // engine reports too_late on its disarmed inbox), never silently dropped. + // Additive: an older server leaves it false. + Steer bool `protobuf:"varint,24,opt,name=steer,proto3" json:"steer,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -999,6 +1086,13 @@ func (x *ServerCapabilities) GetStorageHealth() bool { return false } +func (x *ServerCapabilities) GetSteer() bool { + if x != nil { + return x.Steer + } + return false +} + // CreateSessionResponse carries the newly-allocated session id. type CreateSessionResponse struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -4794,6 +4888,8 @@ type ConverseRequest struct { // *ConverseRequest_ResumeApproval // *ConverseRequest_Cancel // *ConverseRequest_CancelChild + // *ConverseRequest_Steer + // *ConverseRequest_SteerCancel Kind isConverseRequest_Kind `protobuf_oneof:"kind"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -4872,6 +4968,24 @@ func (x *ConverseRequest) GetCancelChild() *CancelChild { return nil } +func (x *ConverseRequest) GetSteer() *Steer { + if x != nil { + if x, ok := x.Kind.(*ConverseRequest_Steer); ok { + return x.Steer + } + } + return nil +} + +func (x *ConverseRequest) GetSteerCancel() *SteerCancel { + if x != nil { + if x, ok := x.Kind.(*ConverseRequest_SteerCancel); ok { + return x.SteerCancel + } + } + return nil +} + type isConverseRequest_Kind interface { isConverseRequest_Kind() } @@ -4897,6 +5011,23 @@ type ConverseRequest_CancelChild struct { CancelChild *CancelChild `protobuf:"bytes,12,opt,name=cancel_child,json=cancelChild,proto3,oneof"` } +type ConverseRequest_Steer struct { + // steer injects an operator instruction mid-run (steer-while-running, + // issue #512): the run's steer inbox parks it and the loop drains it at + // the next turn boundary, recording it as an ordinary user continuation. + // The server echoes the authoritative outcome back on the stream as a + // steer.outcome event. gRPC-only in v1 (the HTTP/SSE surface has no + // client→server mid-run channel). + Steer *Steer `protobuf:"bytes,13,opt,name=steer,proto3,oneof"` +} + +type ConverseRequest_SteerCancel struct { + // steer_cancel retracts the run's PENDING (un-drained) steer, if any. + // The authoritative outcome rides back as a steer.outcome event + // (retracted / none_pending). + SteerCancel *SteerCancel `protobuf:"bytes,14,opt,name=steer_cancel,json=steerCancel,proto3,oneof"` +} + func (*ConverseRequest_Prompt) isConverseRequest_Kind() {} func (*ConverseRequest_ResumeApproval) isConverseRequest_Kind() {} @@ -4905,6 +5036,10 @@ func (*ConverseRequest_Cancel) isConverseRequest_Kind() {} func (*ConverseRequest_CancelChild) isConverseRequest_Kind() {} +func (*ConverseRequest_Steer) isConverseRequest_Kind() {} + +func (*ConverseRequest_SteerCancel) isConverseRequest_Kind() {} + // ConverseResponse is a server-to-client frame: one Event envelope per // domain event, emitted in sequence order until the terminal result. type ConverseResponse struct { @@ -5408,6 +5543,127 @@ func (x *CancelChild) GetChildId() string { return "" } +// Steer injects an operator instruction into the IN-FLIGHT run +// (steer-while-running, issue #512). It never preempts or aborts the in-flight +// model call: the run's steer inbox parks the text (a SINGLE slot — a second +// steer while one is pending APPENDS into the pending bundle, merged with a +// blank-line separator; the merged bundle still drains as ONE user message. +// Replacing a pending bundle is explicit steer_cancel-then-resend) and the +// loop drains it at the next turn boundary, recording it as an ordinary user +// continuation. The server echoes the authoritative outcome on the stream +// (steer.outcome); when the run's inbox is closed (the run went terminal +// behind the client's "still running" belief) the text is PROMOTED to a fresh +// follow-up run, never silently dropped. +type Steer struct { + state protoimpl.MessageState `protogen:"open.v1"` + // text is the operator instruction to inject at the next turn boundary. + Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"` + // message_id is the CLIENT-MINTED correlation key for THIS frame (unique per + // send; empty means uncorrelated). The server echoes the frame's own id + // verbatim on its steer.outcome ack. On the EvSteer drain echo it carries a + // WATERMARK: the LATEST (tail) contributing send's id of the bundle that + // just drained — sends up to and including the watermark drained, sends + // after it are still pending, so the client splits its ordered queue on it. + // text is NOT a safe correlation key (two sends can carry the same text). + MessageId string `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Steer) Reset() { + *x = Steer{} + mi := &file_mecatl_v1_harness_proto_msgTypes[64] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Steer) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Steer) ProtoMessage() {} + +func (x *Steer) ProtoReflect() protoreflect.Message { + mi := &file_mecatl_v1_harness_proto_msgTypes[64] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Steer.ProtoReflect.Descriptor instead. +func (*Steer) Descriptor() ([]byte, []int) { + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{64} +} + +func (x *Steer) GetText() string { + if x != nil { + return x.Text + } + return "" +} + +func (x *Steer) GetMessageId() string { + if x != nil { + return x.MessageId + } + return "" +} + +// SteerCancel retracts the run's PENDING (un-drained) steer, if any. It is +// idempotent: a cancel with nothing pending reports none_pending. A steer that +// already drained at a turn boundary is ordinary recorded history and cannot +// be retracted (the cancel reports none_pending then — the drain won). +type SteerCancel struct { + state protoimpl.MessageState `protogen:"open.v1"` + // message_id is the CLIENT-MINTED correlation key, echoed verbatim on the + // steer.outcome ack (same contract as Steer.message_id). + MessageId string `protobuf:"bytes,1,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SteerCancel) Reset() { + *x = SteerCancel{} + mi := &file_mecatl_v1_harness_proto_msgTypes[65] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SteerCancel) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SteerCancel) ProtoMessage() {} + +func (x *SteerCancel) ProtoReflect() protoreflect.Message { + mi := &file_mecatl_v1_harness_proto_msgTypes[65] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SteerCancel.ProtoReflect.Descriptor instead. +func (*SteerCancel) Descriptor() ([]byte, []int) { + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{65} +} + +func (x *SteerCancel) GetMessageId() string { + if x != nil { + return x.MessageId + } + return "" +} + // Event mirrors the domain session.Event one-for-one. `type` is the string // event kind; the structured submessages are populated per kind. type Event struct { @@ -5483,13 +5739,25 @@ type Event struct { // the pre-compaction conversation). LOG-ONLY on the live Converse wire; // StreamSessionEvents relays them so a transcript replay recovers dropped turns. CompactionArchive *CompactionArchive `protobuf:"bytes,18,opt,name=compaction_archive,json=compactionArchive,proto3" json:"compaction_archive,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // steer is set on steer events (EvSteer — the run's steer-inbox DRAIN echo): + // the COMMITTED operator steer just recorded into the conversation as an + // ordinary user continuation. CLIENT-VISIBLE and AUTHORITATIVE: the engine is + // the sole authority on which steer won the slot, so the client renders THIS + // text. Byte-identical to the recorded user message replayed to the model + // (recorded == streamed == model-view). + Steer *SteerEcho `protobuf:"bytes,19,opt,name=steer,proto3" json:"steer,omitempty"` + // steer_outcome is set on steer.outcome events: the AUTHORITATIVE ack for a + // steer / steer_cancel frame the client sent on this stream (accepted / + // accepted / appended / retracted / none_pending / too_late). CLIENT-VISIBLE, one per + // steer frame, sequenced in send order. + SteerOutcome *SteerAck `protobuf:"bytes,20,opt,name=steer_outcome,json=steerOutcome,proto3" json:"steer_outcome,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *Event) Reset() { *x = Event{} - mi := &file_mecatl_v1_harness_proto_msgTypes[64] + mi := &file_mecatl_v1_harness_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5501,7 +5769,7 @@ func (x *Event) String() string { func (*Event) ProtoMessage() {} func (x *Event) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[64] + mi := &file_mecatl_v1_harness_proto_msgTypes[66] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5514,7 +5782,7 @@ func (x *Event) ProtoReflect() protoreflect.Message { // Deprecated: Use Event.ProtoReflect.Descriptor instead. func (*Event) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{64} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{66} } func (x *Event) GetType() string { @@ -5643,47 +5911,52 @@ func (x *Event) GetCompactionArchive() *CompactionArchive { return nil } -// Approval is the proto projection of session.ApprovalPayload (EvApproval): -// the verdict half of a permission ask. Metadata-only (gauntlet #7): tool NAME + -// verdict string + askID + the opaque gated-call id + the allow-always flag. -// NEVER raw args. LOG-ONLY on the live Converse wire; surfaced only by the -// StreamSessionEvents replay. -type Approval struct { +func (x *Event) GetSteer() *SteerEcho { + if x != nil { + return x.Steer + } + return nil +} + +func (x *Event) GetSteerOutcome() *SteerAck { + if x != nil { + return x.SteerOutcome + } + return nil +} + +// SteerEcho is the proto projection of session.SteerPayload (EvSteer): the +// committed operator steer the turn-boundary drain just recorded. See Event.steer. +type SteerEcho struct { state protoimpl.MessageState `protogen:"open.v1"` - // ask_id is the id of the resolved permission ask (mirrors PermissionAsk.ask_id). - AskId string `protobuf:"bytes,1,opt,name=ask_id,json=askId,proto3" json:"ask_id,omitempty"` - // verdict is the resolution string (allow_once / allow_always / deny) — a - // passthrough of session.VerdictString*, no enum. - Verdict string `protobuf:"bytes,2,opt,name=verdict,proto3" json:"verdict,omitempty"` - // tool is the NAME of the tool the ask gated. It is the tool name ALONE — never - // the call's args. - Tool string `protobuf:"bytes,3,opt,name=tool,proto3" json:"tool,omitempty"` - // call_id is the opaque id of the gated ToolCall (the durable, grammar-free - // correlation handle a 3b consumer uses to find the call in the conversation). - CallId string `protobuf:"bytes,4,opt,name=call_id,json=callId,proto3" json:"call_id,omitempty"` - // allow_always mirrors (Verdict == allow_always): the verdict asked the harness - // to learn a per-session allow rule. It honestly reflects the VERDICT, not the - // policy outcome (Policy.Learn may no-op on an unlearnable call). - AllowAlways bool `protobuf:"varint,5,opt,name=allow_always,json=allowAlways,proto3" json:"allow_always,omitempty"` + // text is the committed steer text, byte-identical to the user message + // recorded into history and replayed to the model. + Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"` + // message_id carries the WATERMARK of the drained bundle — the client-minted + // Steer.message_id of the LATEST (tail) send that contributed to this drain + // (empty when the sender supplied none). The client splits its ordered queue + // on it: sends up to and including the watermark drained (they land in + // context here), sends after it are still pending. + MessageId string `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *Approval) Reset() { - *x = Approval{} - mi := &file_mecatl_v1_harness_proto_msgTypes[65] +func (x *SteerEcho) Reset() { + *x = SteerEcho{} + mi := &file_mecatl_v1_harness_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *Approval) String() string { +func (x *SteerEcho) String() string { return protoimpl.X.MessageStringOf(x) } -func (*Approval) ProtoMessage() {} +func (*SteerEcho) ProtoMessage() {} -func (x *Approval) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[65] +func (x *SteerEcho) ProtoReflect() protoreflect.Message { + mi := &file_mecatl_v1_harness_proto_msgTypes[67] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5694,77 +5967,60 @@ func (x *Approval) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use Approval.ProtoReflect.Descriptor instead. -func (*Approval) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{65} -} - -func (x *Approval) GetAskId() string { - if x != nil { - return x.AskId - } - return "" -} - -func (x *Approval) GetVerdict() string { - if x != nil { - return x.Verdict - } - return "" +// Deprecated: Use SteerEcho.ProtoReflect.Descriptor instead. +func (*SteerEcho) Descriptor() ([]byte, []int) { + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{67} } -func (x *Approval) GetTool() string { +func (x *SteerEcho) GetText() string { if x != nil { - return x.Tool + return x.Text } return "" } -func (x *Approval) GetCallId() string { +func (x *SteerEcho) GetMessageId() string { if x != nil { - return x.CallId + return x.MessageId } return "" } -func (x *Approval) GetAllowAlways() bool { - if x != nil { - return x.AllowAlways - } - return false -} - -// UserPrompt is the proto projection of session.UserPromptPayload (EvUserPrompt): -// the recorded user message (Text + non-text media Parts). It is the durable -// record of what the user asked. LOG-ONLY on the live Converse wire; surfaced only -// by the StreamSessionEvents replay. -type UserPrompt struct { +// SteerAck is the proto projection of a steer-frame outcome: the server's +// authoritative report of what happened to a steer / steer_cancel frame. +type SteerAck struct { state protoimpl.MessageState `protogen:"open.v1"` - // text is the flattened user-message text (the prompt body, or a harness-authored - // continuation/notice). - Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"` - // parts carries any non-text media (image/audio) that rode alongside the text on - // the user message; nil for a text-only prompt. - Parts []*Content `protobuf:"bytes,2,rep,name=parts,proto3" json:"parts,omitempty"` + // outcome is the closed-enum result (mirrors agent.SteerOutcome). + Outcome SteerOutcome `protobuf:"varint,1,opt,name=outcome,proto3,enum=mecatl.v1.SteerOutcome" json:"outcome,omitempty"` + // text echoes the steer text the outcome is about (empty for steer_cancel). + Text string `protobuf:"bytes,2,opt,name=text,proto3" json:"text,omitempty"` + // promoted is true when a too_late steer was promoted to a fresh follow-up + // run through the run-entry funnel (the text drives a new run instead of + // draining into the finished one) — never silently dropped. + Promoted bool `protobuf:"varint,3,opt,name=promoted,proto3" json:"promoted,omitempty"` + // message_id echoes the client-minted id of the Steer / SteerCancel frame + // this ack answers (empty when the frame supplied none), so the client + // correlates the authoritative outcome with its send and ignores stale acks. + MessageId string `protobuf:"bytes,4,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *UserPrompt) Reset() { - *x = UserPrompt{} - mi := &file_mecatl_v1_harness_proto_msgTypes[66] +func (x *SteerAck) Reset() { + *x = SteerAck{} + mi := &file_mecatl_v1_harness_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *UserPrompt) String() string { +func (x *SteerAck) String() string { return protoimpl.X.MessageStringOf(x) } -func (*UserPrompt) ProtoMessage() {} +func (*SteerAck) ProtoMessage() {} -func (x *UserPrompt) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[66] +func (x *SteerAck) ProtoReflect() protoreflect.Message { + mi := &file_mecatl_v1_harness_proto_msgTypes[68] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5775,14 +6031,179 @@ func (x *UserPrompt) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use UserPrompt.ProtoReflect.Descriptor instead. -func (*UserPrompt) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{66} +// Deprecated: Use SteerAck.ProtoReflect.Descriptor instead. +func (*SteerAck) Descriptor() ([]byte, []int) { + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{68} } -func (x *UserPrompt) GetText() string { +func (x *SteerAck) GetOutcome() SteerOutcome { if x != nil { - return x.Text + return x.Outcome + } + return SteerOutcome_STEER_OUTCOME_UNSPECIFIED +} + +func (x *SteerAck) GetText() string { + if x != nil { + return x.Text + } + return "" +} + +func (x *SteerAck) GetPromoted() bool { + if x != nil { + return x.Promoted + } + return false +} + +func (x *SteerAck) GetMessageId() string { + if x != nil { + return x.MessageId + } + return "" +} + +// Approval is the proto projection of session.ApprovalPayload (EvApproval): +// the verdict half of a permission ask. Metadata-only (gauntlet #7): tool NAME + +// verdict string + askID + the opaque gated-call id + the allow-always flag. +// NEVER raw args. LOG-ONLY on the live Converse wire; surfaced only by the +// StreamSessionEvents replay. +type Approval struct { + state protoimpl.MessageState `protogen:"open.v1"` + // ask_id is the id of the resolved permission ask (mirrors PermissionAsk.ask_id). + AskId string `protobuf:"bytes,1,opt,name=ask_id,json=askId,proto3" json:"ask_id,omitempty"` + // verdict is the resolution string (allow_once / allow_always / deny) — a + // passthrough of session.VerdictString*, no enum. + Verdict string `protobuf:"bytes,2,opt,name=verdict,proto3" json:"verdict,omitempty"` + // tool is the NAME of the tool the ask gated. It is the tool name ALONE — never + // the call's args. + Tool string `protobuf:"bytes,3,opt,name=tool,proto3" json:"tool,omitempty"` + // call_id is the opaque id of the gated ToolCall (the durable, grammar-free + // correlation handle a 3b consumer uses to find the call in the conversation). + CallId string `protobuf:"bytes,4,opt,name=call_id,json=callId,proto3" json:"call_id,omitempty"` + // allow_always mirrors (Verdict == allow_always): the verdict asked the harness + // to learn a per-session allow rule. It honestly reflects the VERDICT, not the + // policy outcome (Policy.Learn may no-op on an unlearnable call). + AllowAlways bool `protobuf:"varint,5,opt,name=allow_always,json=allowAlways,proto3" json:"allow_always,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Approval) Reset() { + *x = Approval{} + mi := &file_mecatl_v1_harness_proto_msgTypes[69] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Approval) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Approval) ProtoMessage() {} + +func (x *Approval) ProtoReflect() protoreflect.Message { + mi := &file_mecatl_v1_harness_proto_msgTypes[69] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Approval.ProtoReflect.Descriptor instead. +func (*Approval) Descriptor() ([]byte, []int) { + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{69} +} + +func (x *Approval) GetAskId() string { + if x != nil { + return x.AskId + } + return "" +} + +func (x *Approval) GetVerdict() string { + if x != nil { + return x.Verdict + } + return "" +} + +func (x *Approval) GetTool() string { + if x != nil { + return x.Tool + } + return "" +} + +func (x *Approval) GetCallId() string { + if x != nil { + return x.CallId + } + return "" +} + +func (x *Approval) GetAllowAlways() bool { + if x != nil { + return x.AllowAlways + } + return false +} + +// UserPrompt is the proto projection of session.UserPromptPayload (EvUserPrompt): +// the recorded user message (Text + non-text media Parts). It is the durable +// record of what the user asked. LOG-ONLY on the live Converse wire; surfaced only +// by the StreamSessionEvents replay. +type UserPrompt struct { + state protoimpl.MessageState `protogen:"open.v1"` + // text is the flattened user-message text (the prompt body, or a harness-authored + // continuation/notice). + Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"` + // parts carries any non-text media (image/audio) that rode alongside the text on + // the user message; nil for a text-only prompt. + Parts []*Content `protobuf:"bytes,2,rep,name=parts,proto3" json:"parts,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UserPrompt) Reset() { + *x = UserPrompt{} + mi := &file_mecatl_v1_harness_proto_msgTypes[70] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UserPrompt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UserPrompt) ProtoMessage() {} + +func (x *UserPrompt) ProtoReflect() protoreflect.Message { + mi := &file_mecatl_v1_harness_proto_msgTypes[70] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UserPrompt.ProtoReflect.Descriptor instead. +func (*UserPrompt) Descriptor() ([]byte, []int) { + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{70} +} + +func (x *UserPrompt) GetText() string { + if x != nil { + return x.Text } return "" } @@ -5811,7 +6232,7 @@ type CompactionArchive struct { func (x *CompactionArchive) Reset() { *x = CompactionArchive{} - mi := &file_mecatl_v1_harness_proto_msgTypes[67] + mi := &file_mecatl_v1_harness_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5823,7 +6244,7 @@ func (x *CompactionArchive) String() string { func (*CompactionArchive) ProtoMessage() {} func (x *CompactionArchive) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[67] + mi := &file_mecatl_v1_harness_proto_msgTypes[71] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5836,7 +6257,7 @@ func (x *CompactionArchive) ProtoReflect() protoreflect.Message { // Deprecated: Use CompactionArchive.ProtoReflect.Descriptor instead. func (*CompactionArchive) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{67} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{71} } func (x *CompactionArchive) GetReplaced() []*ConversationMessage { @@ -5881,7 +6302,7 @@ type ConversationMessage struct { func (x *ConversationMessage) Reset() { *x = ConversationMessage{} - mi := &file_mecatl_v1_harness_proto_msgTypes[68] + mi := &file_mecatl_v1_harness_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5893,7 +6314,7 @@ func (x *ConversationMessage) String() string { func (*ConversationMessage) ProtoMessage() {} func (x *ConversationMessage) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[68] + mi := &file_mecatl_v1_harness_proto_msgTypes[72] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5906,7 +6327,7 @@ func (x *ConversationMessage) ProtoReflect() protoreflect.Message { // Deprecated: Use ConversationMessage.ProtoReflect.Descriptor instead. func (*ConversationMessage) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{68} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{72} } func (x *ConversationMessage) GetRole() string { @@ -6001,7 +6422,7 @@ type SchedulePayload struct { func (x *SchedulePayload) Reset() { *x = SchedulePayload{} - mi := &file_mecatl_v1_harness_proto_msgTypes[69] + mi := &file_mecatl_v1_harness_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6013,7 +6434,7 @@ func (x *SchedulePayload) String() string { func (*SchedulePayload) ProtoMessage() {} func (x *SchedulePayload) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[69] + mi := &file_mecatl_v1_harness_proto_msgTypes[73] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6026,7 +6447,7 @@ func (x *SchedulePayload) ProtoReflect() protoreflect.Message { // Deprecated: Use SchedulePayload.ProtoReflect.Descriptor instead. func (*SchedulePayload) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{69} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{73} } func (x *SchedulePayload) GetScheduleName() string { @@ -6092,7 +6513,7 @@ type Hook struct { func (x *Hook) Reset() { *x = Hook{} - mi := &file_mecatl_v1_harness_proto_msgTypes[70] + mi := &file_mecatl_v1_harness_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6104,7 +6525,7 @@ func (x *Hook) String() string { func (*Hook) ProtoMessage() {} func (x *Hook) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[70] + mi := &file_mecatl_v1_harness_proto_msgTypes[74] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6117,7 +6538,7 @@ func (x *Hook) ProtoReflect() protoreflect.Message { // Deprecated: Use Hook.ProtoReflect.Descriptor instead. func (*Hook) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{70} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{74} } func (x *Hook) GetPhase() string { @@ -6242,7 +6663,7 @@ type Subagent struct { func (x *Subagent) Reset() { *x = Subagent{} - mi := &file_mecatl_v1_harness_proto_msgTypes[71] + mi := &file_mecatl_v1_harness_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6254,7 +6675,7 @@ func (x *Subagent) String() string { func (*Subagent) ProtoMessage() {} func (x *Subagent) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[71] + mi := &file_mecatl_v1_harness_proto_msgTypes[75] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6267,7 +6688,7 @@ func (x *Subagent) ProtoReflect() protoreflect.Message { // Deprecated: Use Subagent.ProtoReflect.Descriptor instead. func (*Subagent) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{71} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{75} } func (x *Subagent) GetParentCallId() string { @@ -6445,7 +6866,7 @@ type TeamMemberSpec struct { func (x *TeamMemberSpec) Reset() { *x = TeamMemberSpec{} - mi := &file_mecatl_v1_harness_proto_msgTypes[72] + mi := &file_mecatl_v1_harness_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6457,7 +6878,7 @@ func (x *TeamMemberSpec) String() string { func (*TeamMemberSpec) ProtoMessage() {} func (x *TeamMemberSpec) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[72] + mi := &file_mecatl_v1_harness_proto_msgTypes[76] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6470,7 +6891,7 @@ func (x *TeamMemberSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use TeamMemberSpec.ProtoReflect.Descriptor instead. func (*TeamMemberSpec) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{72} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{76} } func (x *TeamMemberSpec) GetName() string { @@ -6621,7 +7042,7 @@ type Team struct { func (x *Team) Reset() { *x = Team{} - mi := &file_mecatl_v1_harness_proto_msgTypes[73] + mi := &file_mecatl_v1_harness_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6633,7 +7054,7 @@ func (x *Team) String() string { func (*Team) ProtoMessage() {} func (x *Team) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[73] + mi := &file_mecatl_v1_harness_proto_msgTypes[77] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6646,7 +7067,7 @@ func (x *Team) ProtoReflect() protoreflect.Message { // Deprecated: Use Team.ProtoReflect.Descriptor instead. func (*Team) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{73} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{77} } func (x *Team) GetParentCallId() string { @@ -6907,7 +7328,7 @@ type Parallel struct { func (x *Parallel) Reset() { *x = Parallel{} - mi := &file_mecatl_v1_harness_proto_msgTypes[74] + mi := &file_mecatl_v1_harness_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6919,7 +7340,7 @@ func (x *Parallel) String() string { func (*Parallel) ProtoMessage() {} func (x *Parallel) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[74] + mi := &file_mecatl_v1_harness_proto_msgTypes[78] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6932,7 +7353,7 @@ func (x *Parallel) ProtoReflect() protoreflect.Message { // Deprecated: Use Parallel.ProtoReflect.Descriptor instead. func (*Parallel) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{74} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{78} } func (x *Parallel) GetParentCallId() string { @@ -7126,7 +7547,7 @@ type ToolCall struct { func (x *ToolCall) Reset() { *x = ToolCall{} - mi := &file_mecatl_v1_harness_proto_msgTypes[75] + mi := &file_mecatl_v1_harness_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7138,7 +7559,7 @@ func (x *ToolCall) String() string { func (*ToolCall) ProtoMessage() {} func (x *ToolCall) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[75] + mi := &file_mecatl_v1_harness_proto_msgTypes[79] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7151,7 +7572,7 @@ func (x *ToolCall) ProtoReflect() protoreflect.Message { // Deprecated: Use ToolCall.ProtoReflect.Descriptor instead. func (*ToolCall) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{75} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{79} } func (x *ToolCall) GetId() string { @@ -7200,7 +7621,7 @@ type ToolResult struct { func (x *ToolResult) Reset() { *x = ToolResult{} - mi := &file_mecatl_v1_harness_proto_msgTypes[76] + mi := &file_mecatl_v1_harness_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7212,7 +7633,7 @@ func (x *ToolResult) String() string { func (*ToolResult) ProtoMessage() {} func (x *ToolResult) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[76] + mi := &file_mecatl_v1_harness_proto_msgTypes[80] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7225,7 +7646,7 @@ func (x *ToolResult) ProtoReflect() protoreflect.Message { // Deprecated: Use ToolResult.ProtoReflect.Descriptor instead. func (*ToolResult) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{76} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{80} } func (x *ToolResult) GetCallId() string { @@ -7282,7 +7703,7 @@ type PermissionAsk struct { func (x *PermissionAsk) Reset() { *x = PermissionAsk{} - mi := &file_mecatl_v1_harness_proto_msgTypes[77] + mi := &file_mecatl_v1_harness_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7294,7 +7715,7 @@ func (x *PermissionAsk) String() string { func (*PermissionAsk) ProtoMessage() {} func (x *PermissionAsk) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[77] + mi := &file_mecatl_v1_harness_proto_msgTypes[81] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7307,7 +7728,7 @@ func (x *PermissionAsk) ProtoReflect() protoreflect.Message { // Deprecated: Use PermissionAsk.ProtoReflect.Descriptor instead. func (*PermissionAsk) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{77} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{81} } func (x *PermissionAsk) GetAskId() string { @@ -7360,7 +7781,7 @@ type Result struct { func (x *Result) Reset() { *x = Result{} - mi := &file_mecatl_v1_harness_proto_msgTypes[78] + mi := &file_mecatl_v1_harness_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7372,7 +7793,7 @@ func (x *Result) String() string { func (*Result) ProtoMessage() {} func (x *Result) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[78] + mi := &file_mecatl_v1_harness_proto_msgTypes[82] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7385,7 +7806,7 @@ func (x *Result) ProtoReflect() protoreflect.Message { // Deprecated: Use Result.ProtoReflect.Descriptor instead. func (*Result) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{78} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{82} } func (x *Result) GetStop() string { @@ -7440,7 +7861,7 @@ type TurnEnd struct { func (x *TurnEnd) Reset() { *x = TurnEnd{} - mi := &file_mecatl_v1_harness_proto_msgTypes[79] + mi := &file_mecatl_v1_harness_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7452,7 +7873,7 @@ func (x *TurnEnd) String() string { func (*TurnEnd) ProtoMessage() {} func (x *TurnEnd) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[79] + mi := &file_mecatl_v1_harness_proto_msgTypes[83] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7465,7 +7886,7 @@ func (x *TurnEnd) ProtoReflect() protoreflect.Message { // Deprecated: Use TurnEnd.ProtoReflect.Descriptor instead. func (*TurnEnd) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{79} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{83} } func (x *TurnEnd) GetUsage() *Usage { @@ -7504,7 +7925,7 @@ type Usage struct { func (x *Usage) Reset() { *x = Usage{} - mi := &file_mecatl_v1_harness_proto_msgTypes[80] + mi := &file_mecatl_v1_harness_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7516,7 +7937,7 @@ func (x *Usage) String() string { func (*Usage) ProtoMessage() {} func (x *Usage) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[80] + mi := &file_mecatl_v1_harness_proto_msgTypes[84] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7529,7 +7950,7 @@ func (x *Usage) ProtoReflect() protoreflect.Message { // Deprecated: Use Usage.ProtoReflect.Descriptor instead. func (*Usage) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{80} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{84} } func (x *Usage) GetInputTokens() int64 { @@ -7593,7 +8014,7 @@ type McpResource struct { func (x *McpResource) Reset() { *x = McpResource{} - mi := &file_mecatl_v1_harness_proto_msgTypes[81] + mi := &file_mecatl_v1_harness_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7605,7 +8026,7 @@ func (x *McpResource) String() string { func (*McpResource) ProtoMessage() {} func (x *McpResource) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[81] + mi := &file_mecatl_v1_harness_proto_msgTypes[85] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7618,7 +8039,7 @@ func (x *McpResource) ProtoReflect() protoreflect.Message { // Deprecated: Use McpResource.ProtoReflect.Descriptor instead. func (*McpResource) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{81} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{85} } func (x *McpResource) GetServer() string { @@ -7689,7 +8110,7 @@ type ListMcpResourcesRequest struct { func (x *ListMcpResourcesRequest) Reset() { *x = ListMcpResourcesRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[82] + mi := &file_mecatl_v1_harness_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7701,7 +8122,7 @@ func (x *ListMcpResourcesRequest) String() string { func (*ListMcpResourcesRequest) ProtoMessage() {} func (x *ListMcpResourcesRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[82] + mi := &file_mecatl_v1_harness_proto_msgTypes[86] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7714,7 +8135,7 @@ func (x *ListMcpResourcesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListMcpResourcesRequest.ProtoReflect.Descriptor instead. func (*ListMcpResourcesRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{82} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{86} } func (x *ListMcpResourcesRequest) GetServer() string { @@ -7735,7 +8156,7 @@ type ListMcpResourcesResponse struct { func (x *ListMcpResourcesResponse) Reset() { *x = ListMcpResourcesResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[83] + mi := &file_mecatl_v1_harness_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7747,7 +8168,7 @@ func (x *ListMcpResourcesResponse) String() string { func (*ListMcpResourcesResponse) ProtoMessage() {} func (x *ListMcpResourcesResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[83] + mi := &file_mecatl_v1_harness_proto_msgTypes[87] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7760,7 +8181,7 @@ func (x *ListMcpResourcesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListMcpResourcesResponse.ProtoReflect.Descriptor instead. func (*ListMcpResourcesResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{83} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{87} } func (x *ListMcpResourcesResponse) GetResources() []*McpResource { @@ -7788,7 +8209,7 @@ type McpResourceContents struct { func (x *McpResourceContents) Reset() { *x = McpResourceContents{} - mi := &file_mecatl_v1_harness_proto_msgTypes[84] + mi := &file_mecatl_v1_harness_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7800,7 +8221,7 @@ func (x *McpResourceContents) String() string { func (*McpResourceContents) ProtoMessage() {} func (x *McpResourceContents) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[84] + mi := &file_mecatl_v1_harness_proto_msgTypes[88] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7813,7 +8234,7 @@ func (x *McpResourceContents) ProtoReflect() protoreflect.Message { // Deprecated: Use McpResourceContents.ProtoReflect.Descriptor instead. func (*McpResourceContents) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{84} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{88} } func (x *McpResourceContents) GetUri() string { @@ -7857,7 +8278,7 @@ type ReadMcpResourceRequest struct { func (x *ReadMcpResourceRequest) Reset() { *x = ReadMcpResourceRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[85] + mi := &file_mecatl_v1_harness_proto_msgTypes[89] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7869,7 +8290,7 @@ func (x *ReadMcpResourceRequest) String() string { func (*ReadMcpResourceRequest) ProtoMessage() {} func (x *ReadMcpResourceRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[85] + mi := &file_mecatl_v1_harness_proto_msgTypes[89] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7882,7 +8303,7 @@ func (x *ReadMcpResourceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReadMcpResourceRequest.ProtoReflect.Descriptor instead. func (*ReadMcpResourceRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{85} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{89} } func (x *ReadMcpResourceRequest) GetServer() string { @@ -7910,7 +8331,7 @@ type ReadMcpResourceResponse struct { func (x *ReadMcpResourceResponse) Reset() { *x = ReadMcpResourceResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[86] + mi := &file_mecatl_v1_harness_proto_msgTypes[90] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7922,7 +8343,7 @@ func (x *ReadMcpResourceResponse) String() string { func (*ReadMcpResourceResponse) ProtoMessage() {} func (x *ReadMcpResourceResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[86] + mi := &file_mecatl_v1_harness_proto_msgTypes[90] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7935,7 +8356,7 @@ func (x *ReadMcpResourceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReadMcpResourceResponse.ProtoReflect.Descriptor instead. func (*ReadMcpResourceResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{86} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{90} } func (x *ReadMcpResourceResponse) GetContents() []*McpResourceContents { @@ -7963,7 +8384,7 @@ type McpPromptArgument struct { func (x *McpPromptArgument) Reset() { *x = McpPromptArgument{} - mi := &file_mecatl_v1_harness_proto_msgTypes[87] + mi := &file_mecatl_v1_harness_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7975,7 +8396,7 @@ func (x *McpPromptArgument) String() string { func (*McpPromptArgument) ProtoMessage() {} func (x *McpPromptArgument) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[87] + mi := &file_mecatl_v1_harness_proto_msgTypes[91] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7988,7 +8409,7 @@ func (x *McpPromptArgument) ProtoReflect() protoreflect.Message { // Deprecated: Use McpPromptArgument.ProtoReflect.Descriptor instead. func (*McpPromptArgument) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{87} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{91} } func (x *McpPromptArgument) GetName() string { @@ -8039,7 +8460,7 @@ type McpPrompt struct { func (x *McpPrompt) Reset() { *x = McpPrompt{} - mi := &file_mecatl_v1_harness_proto_msgTypes[88] + mi := &file_mecatl_v1_harness_proto_msgTypes[92] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8051,7 +8472,7 @@ func (x *McpPrompt) String() string { func (*McpPrompt) ProtoMessage() {} func (x *McpPrompt) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[88] + mi := &file_mecatl_v1_harness_proto_msgTypes[92] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8064,7 +8485,7 @@ func (x *McpPrompt) ProtoReflect() protoreflect.Message { // Deprecated: Use McpPrompt.ProtoReflect.Descriptor instead. func (*McpPrompt) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{88} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{92} } func (x *McpPrompt) GetServer() string { @@ -8114,7 +8535,7 @@ type ListMcpPromptsRequest struct { func (x *ListMcpPromptsRequest) Reset() { *x = ListMcpPromptsRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[89] + mi := &file_mecatl_v1_harness_proto_msgTypes[93] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8126,7 +8547,7 @@ func (x *ListMcpPromptsRequest) String() string { func (*ListMcpPromptsRequest) ProtoMessage() {} func (x *ListMcpPromptsRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[89] + mi := &file_mecatl_v1_harness_proto_msgTypes[93] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8139,7 +8560,7 @@ func (x *ListMcpPromptsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListMcpPromptsRequest.ProtoReflect.Descriptor instead. func (*ListMcpPromptsRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{89} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{93} } func (x *ListMcpPromptsRequest) GetServer() string { @@ -8160,7 +8581,7 @@ type ListMcpPromptsResponse struct { func (x *ListMcpPromptsResponse) Reset() { *x = ListMcpPromptsResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[90] + mi := &file_mecatl_v1_harness_proto_msgTypes[94] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8172,7 +8593,7 @@ func (x *ListMcpPromptsResponse) String() string { func (*ListMcpPromptsResponse) ProtoMessage() {} func (x *ListMcpPromptsResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[90] + mi := &file_mecatl_v1_harness_proto_msgTypes[94] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8185,7 +8606,7 @@ func (x *ListMcpPromptsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListMcpPromptsResponse.ProtoReflect.Descriptor instead. func (*ListMcpPromptsResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{90} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{94} } func (x *ListMcpPromptsResponse) GetPrompts() []*McpPrompt { @@ -8210,7 +8631,7 @@ type GetMcpPromptRequest struct { func (x *GetMcpPromptRequest) Reset() { *x = GetMcpPromptRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[91] + mi := &file_mecatl_v1_harness_proto_msgTypes[95] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8222,7 +8643,7 @@ func (x *GetMcpPromptRequest) String() string { func (*GetMcpPromptRequest) ProtoMessage() {} func (x *GetMcpPromptRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[91] + mi := &file_mecatl_v1_harness_proto_msgTypes[95] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8235,7 +8656,7 @@ func (x *GetMcpPromptRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMcpPromptRequest.ProtoReflect.Descriptor instead. func (*GetMcpPromptRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{91} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{95} } func (x *GetMcpPromptRequest) GetServer() string { @@ -8273,7 +8694,7 @@ type McpPromptMessage struct { func (x *McpPromptMessage) Reset() { *x = McpPromptMessage{} - mi := &file_mecatl_v1_harness_proto_msgTypes[92] + mi := &file_mecatl_v1_harness_proto_msgTypes[96] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8285,7 +8706,7 @@ func (x *McpPromptMessage) String() string { func (*McpPromptMessage) ProtoMessage() {} func (x *McpPromptMessage) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[92] + mi := &file_mecatl_v1_harness_proto_msgTypes[96] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8298,7 +8719,7 @@ func (x *McpPromptMessage) ProtoReflect() protoreflect.Message { // Deprecated: Use McpPromptMessage.ProtoReflect.Descriptor instead. func (*McpPromptMessage) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{92} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{96} } func (x *McpPromptMessage) GetRole() string { @@ -8328,7 +8749,7 @@ type GetMcpPromptResponse struct { func (x *GetMcpPromptResponse) Reset() { *x = GetMcpPromptResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[93] + mi := &file_mecatl_v1_harness_proto_msgTypes[97] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8340,7 +8761,7 @@ func (x *GetMcpPromptResponse) String() string { func (*GetMcpPromptResponse) ProtoMessage() {} func (x *GetMcpPromptResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[93] + mi := &file_mecatl_v1_harness_proto_msgTypes[97] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8353,7 +8774,7 @@ func (x *GetMcpPromptResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMcpPromptResponse.ProtoReflect.Descriptor instead. func (*GetMcpPromptResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{93} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{97} } func (x *GetMcpPromptResponse) GetDescription() string { @@ -8388,7 +8809,7 @@ type McpServerInfo struct { func (x *McpServerInfo) Reset() { *x = McpServerInfo{} - mi := &file_mecatl_v1_harness_proto_msgTypes[94] + mi := &file_mecatl_v1_harness_proto_msgTypes[98] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8400,7 +8821,7 @@ func (x *McpServerInfo) String() string { func (*McpServerInfo) ProtoMessage() {} func (x *McpServerInfo) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[94] + mi := &file_mecatl_v1_harness_proto_msgTypes[98] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8413,7 +8834,7 @@ func (x *McpServerInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use McpServerInfo.ProtoReflect.Descriptor instead. func (*McpServerInfo) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{94} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{98} } func (x *McpServerInfo) GetName() string { @@ -8466,7 +8887,7 @@ type McpSource struct { func (x *McpSource) Reset() { *x = McpSource{} - mi := &file_mecatl_v1_harness_proto_msgTypes[95] + mi := &file_mecatl_v1_harness_proto_msgTypes[99] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8478,7 +8899,7 @@ func (x *McpSource) String() string { func (*McpSource) ProtoMessage() {} func (x *McpSource) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[95] + mi := &file_mecatl_v1_harness_proto_msgTypes[99] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8491,7 +8912,7 @@ func (x *McpSource) ProtoReflect() protoreflect.Message { // Deprecated: Use McpSource.ProtoReflect.Descriptor instead. func (*McpSource) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{95} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{99} } func (x *McpSource) GetName() string { @@ -8545,7 +8966,7 @@ type ListMcpSourcesRequest struct { func (x *ListMcpSourcesRequest) Reset() { *x = ListMcpSourcesRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[96] + mi := &file_mecatl_v1_harness_proto_msgTypes[100] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8557,7 +8978,7 @@ func (x *ListMcpSourcesRequest) String() string { func (*ListMcpSourcesRequest) ProtoMessage() {} func (x *ListMcpSourcesRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[96] + mi := &file_mecatl_v1_harness_proto_msgTypes[100] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8570,7 +8991,7 @@ func (x *ListMcpSourcesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListMcpSourcesRequest.ProtoReflect.Descriptor instead. func (*ListMcpSourcesRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{96} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{100} } // ListMcpSourcesResponse carries the resolved source inventory. @@ -8584,7 +9005,7 @@ type ListMcpSourcesResponse struct { func (x *ListMcpSourcesResponse) Reset() { *x = ListMcpSourcesResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[97] + mi := &file_mecatl_v1_harness_proto_msgTypes[101] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8596,7 +9017,7 @@ func (x *ListMcpSourcesResponse) String() string { func (*ListMcpSourcesResponse) ProtoMessage() {} func (x *ListMcpSourcesResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[97] + mi := &file_mecatl_v1_harness_proto_msgTypes[101] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8609,7 +9030,7 @@ func (x *ListMcpSourcesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListMcpSourcesResponse.ProtoReflect.Descriptor instead. func (*ListMcpSourcesResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{97} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{101} } func (x *ListMcpSourcesResponse) GetSources() []*McpSource { @@ -8629,7 +9050,7 @@ type ListToolHiveGroupsRequest struct { func (x *ListToolHiveGroupsRequest) Reset() { *x = ListToolHiveGroupsRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[98] + mi := &file_mecatl_v1_harness_proto_msgTypes[102] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8641,7 +9062,7 @@ func (x *ListToolHiveGroupsRequest) String() string { func (*ListToolHiveGroupsRequest) ProtoMessage() {} func (x *ListToolHiveGroupsRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[98] + mi := &file_mecatl_v1_harness_proto_msgTypes[102] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8654,7 +9075,7 @@ func (x *ListToolHiveGroupsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListToolHiveGroupsRequest.ProtoReflect.Descriptor instead. func (*ListToolHiveGroupsRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{98} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{102} } // ListToolHiveGroupsResponse carries the distinct, non-empty ToolHive groups. @@ -8668,7 +9089,7 @@ type ListToolHiveGroupsResponse struct { func (x *ListToolHiveGroupsResponse) Reset() { *x = ListToolHiveGroupsResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[99] + mi := &file_mecatl_v1_harness_proto_msgTypes[103] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8680,7 +9101,7 @@ func (x *ListToolHiveGroupsResponse) String() string { func (*ListToolHiveGroupsResponse) ProtoMessage() {} func (x *ListToolHiveGroupsResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[99] + mi := &file_mecatl_v1_harness_proto_msgTypes[103] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8693,7 +9114,7 @@ func (x *ListToolHiveGroupsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListToolHiveGroupsResponse.ProtoReflect.Descriptor instead. func (*ListToolHiveGroupsResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{99} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{103} } func (x *ListToolHiveGroupsResponse) GetGroups() []string { @@ -8729,7 +9150,7 @@ type AgentInfo struct { func (x *AgentInfo) Reset() { *x = AgentInfo{} - mi := &file_mecatl_v1_harness_proto_msgTypes[100] + mi := &file_mecatl_v1_harness_proto_msgTypes[104] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8741,7 +9162,7 @@ func (x *AgentInfo) String() string { func (*AgentInfo) ProtoMessage() {} func (x *AgentInfo) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[100] + mi := &file_mecatl_v1_harness_proto_msgTypes[104] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8754,7 +9175,7 @@ func (x *AgentInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use AgentInfo.ProtoReflect.Descriptor instead. func (*AgentInfo) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{100} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{104} } func (x *AgentInfo) GetName() string { @@ -8808,7 +9229,7 @@ type ListAgentsRequest struct { func (x *ListAgentsRequest) Reset() { *x = ListAgentsRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[101] + mi := &file_mecatl_v1_harness_proto_msgTypes[105] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8820,7 +9241,7 @@ func (x *ListAgentsRequest) String() string { func (*ListAgentsRequest) ProtoMessage() {} func (x *ListAgentsRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[101] + mi := &file_mecatl_v1_harness_proto_msgTypes[105] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8833,7 +9254,7 @@ func (x *ListAgentsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListAgentsRequest.ProtoReflect.Descriptor instead. func (*ListAgentsRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{101} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{105} } // ListAgentsResponse carries the resolved agent definitions. @@ -8847,7 +9268,7 @@ type ListAgentsResponse struct { func (x *ListAgentsResponse) Reset() { *x = ListAgentsResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[102] + mi := &file_mecatl_v1_harness_proto_msgTypes[106] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8859,7 +9280,7 @@ func (x *ListAgentsResponse) String() string { func (*ListAgentsResponse) ProtoMessage() {} func (x *ListAgentsResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[102] + mi := &file_mecatl_v1_harness_proto_msgTypes[106] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8872,7 +9293,7 @@ func (x *ListAgentsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListAgentsResponse.ProtoReflect.Descriptor instead. func (*ListAgentsResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{102} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{106} } func (x *ListAgentsResponse) GetAgents() []*AgentInfo { @@ -8899,7 +9320,7 @@ type Command struct { func (x *Command) Reset() { *x = Command{} - mi := &file_mecatl_v1_harness_proto_msgTypes[103] + mi := &file_mecatl_v1_harness_proto_msgTypes[107] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8911,7 +9332,7 @@ func (x *Command) String() string { func (*Command) ProtoMessage() {} func (x *Command) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[103] + mi := &file_mecatl_v1_harness_proto_msgTypes[107] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8924,7 +9345,7 @@ func (x *Command) ProtoReflect() protoreflect.Message { // Deprecated: Use Command.ProtoReflect.Descriptor instead. func (*Command) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{103} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{107} } func (x *Command) GetName() string { @@ -8953,7 +9374,7 @@ type ListCommandsRequest struct { func (x *ListCommandsRequest) Reset() { *x = ListCommandsRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[104] + mi := &file_mecatl_v1_harness_proto_msgTypes[108] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8965,7 +9386,7 @@ func (x *ListCommandsRequest) String() string { func (*ListCommandsRequest) ProtoMessage() {} func (x *ListCommandsRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[104] + mi := &file_mecatl_v1_harness_proto_msgTypes[108] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8978,7 +9399,7 @@ func (x *ListCommandsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListCommandsRequest.ProtoReflect.Descriptor instead. func (*ListCommandsRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{104} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{108} } func (x *ListCommandsRequest) GetWorkspace() string { @@ -8999,7 +9420,7 @@ type ListCommandsResponse struct { func (x *ListCommandsResponse) Reset() { *x = ListCommandsResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[105] + mi := &file_mecatl_v1_harness_proto_msgTypes[109] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9011,7 +9432,7 @@ func (x *ListCommandsResponse) String() string { func (*ListCommandsResponse) ProtoMessage() {} func (x *ListCommandsResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[105] + mi := &file_mecatl_v1_harness_proto_msgTypes[109] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9024,7 +9445,7 @@ func (x *ListCommandsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListCommandsResponse.ProtoReflect.Descriptor instead. func (*ListCommandsResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{105} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{109} } func (x *ListCommandsResponse) GetCommands() []*Command { @@ -9054,7 +9475,7 @@ type Worktree struct { func (x *Worktree) Reset() { *x = Worktree{} - mi := &file_mecatl_v1_harness_proto_msgTypes[106] + mi := &file_mecatl_v1_harness_proto_msgTypes[110] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9066,7 +9487,7 @@ func (x *Worktree) String() string { func (*Worktree) ProtoMessage() {} func (x *Worktree) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[106] + mi := &file_mecatl_v1_harness_proto_msgTypes[110] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9079,7 +9500,7 @@ func (x *Worktree) ProtoReflect() protoreflect.Message { // Deprecated: Use Worktree.ProtoReflect.Descriptor instead. func (*Worktree) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{106} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{110} } func (x *Worktree) GetPath() string { @@ -9122,7 +9543,7 @@ type ListWorktreesRequest struct { func (x *ListWorktreesRequest) Reset() { *x = ListWorktreesRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[107] + mi := &file_mecatl_v1_harness_proto_msgTypes[111] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9134,7 +9555,7 @@ func (x *ListWorktreesRequest) String() string { func (*ListWorktreesRequest) ProtoMessage() {} func (x *ListWorktreesRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[107] + mi := &file_mecatl_v1_harness_proto_msgTypes[111] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9147,7 +9568,7 @@ func (x *ListWorktreesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorktreesRequest.ProtoReflect.Descriptor instead. func (*ListWorktreesRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{107} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{111} } func (x *ListWorktreesRequest) GetWorkspace() string { @@ -9169,7 +9590,7 @@ type ListWorktreesResponse struct { func (x *ListWorktreesResponse) Reset() { *x = ListWorktreesResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[108] + mi := &file_mecatl_v1_harness_proto_msgTypes[112] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9181,7 +9602,7 @@ func (x *ListWorktreesResponse) String() string { func (*ListWorktreesResponse) ProtoMessage() {} func (x *ListWorktreesResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[108] + mi := &file_mecatl_v1_harness_proto_msgTypes[112] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9194,7 +9615,7 @@ func (x *ListWorktreesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorktreesResponse.ProtoReflect.Descriptor instead. func (*ListWorktreesResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{108} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{112} } func (x *ListWorktreesResponse) GetWorktrees() []*Worktree { @@ -9225,7 +9646,7 @@ type SkillInfo struct { func (x *SkillInfo) Reset() { *x = SkillInfo{} - mi := &file_mecatl_v1_harness_proto_msgTypes[109] + mi := &file_mecatl_v1_harness_proto_msgTypes[113] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9237,7 +9658,7 @@ func (x *SkillInfo) String() string { func (*SkillInfo) ProtoMessage() {} func (x *SkillInfo) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[109] + mi := &file_mecatl_v1_harness_proto_msgTypes[113] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9250,7 +9671,7 @@ func (x *SkillInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use SkillInfo.ProtoReflect.Descriptor instead. func (*SkillInfo) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{109} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{113} } func (x *SkillInfo) GetName() string { @@ -9297,7 +9718,7 @@ type ListSkillsRequest struct { func (x *ListSkillsRequest) Reset() { *x = ListSkillsRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[110] + mi := &file_mecatl_v1_harness_proto_msgTypes[114] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9309,7 +9730,7 @@ func (x *ListSkillsRequest) String() string { func (*ListSkillsRequest) ProtoMessage() {} func (x *ListSkillsRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[110] + mi := &file_mecatl_v1_harness_proto_msgTypes[114] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9322,7 +9743,7 @@ func (x *ListSkillsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListSkillsRequest.ProtoReflect.Descriptor instead. func (*ListSkillsRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{110} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{114} } // ListSkillsResponse carries the resolved skills inventory. @@ -9336,7 +9757,7 @@ type ListSkillsResponse struct { func (x *ListSkillsResponse) Reset() { *x = ListSkillsResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[111] + mi := &file_mecatl_v1_harness_proto_msgTypes[115] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9348,7 +9769,7 @@ func (x *ListSkillsResponse) String() string { func (*ListSkillsResponse) ProtoMessage() {} func (x *ListSkillsResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[111] + mi := &file_mecatl_v1_harness_proto_msgTypes[115] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9361,7 +9782,7 @@ func (x *ListSkillsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListSkillsResponse.ProtoReflect.Descriptor instead. func (*ListSkillsResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{111} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{115} } func (x *ListSkillsResponse) GetSkills() []*SkillInfo { @@ -9402,7 +9823,7 @@ type SoulInfo struct { func (x *SoulInfo) Reset() { *x = SoulInfo{} - mi := &file_mecatl_v1_harness_proto_msgTypes[112] + mi := &file_mecatl_v1_harness_proto_msgTypes[116] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9414,7 +9835,7 @@ func (x *SoulInfo) String() string { func (*SoulInfo) ProtoMessage() {} func (x *SoulInfo) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[112] + mi := &file_mecatl_v1_harness_proto_msgTypes[116] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9427,7 +9848,7 @@ func (x *SoulInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use SoulInfo.ProtoReflect.Descriptor instead. func (*SoulInfo) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{112} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{116} } func (x *SoulInfo) GetContent() string { @@ -9488,7 +9909,7 @@ type GetSoulRequest struct { func (x *GetSoulRequest) Reset() { *x = GetSoulRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[113] + mi := &file_mecatl_v1_harness_proto_msgTypes[117] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9500,7 +9921,7 @@ func (x *GetSoulRequest) String() string { func (*GetSoulRequest) ProtoMessage() {} func (x *GetSoulRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[113] + mi := &file_mecatl_v1_harness_proto_msgTypes[117] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9513,7 +9934,7 @@ func (x *GetSoulRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSoulRequest.ProtoReflect.Descriptor instead. func (*GetSoulRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{113} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{117} } // GetSoulResponse carries the resolved soul snapshot. @@ -9528,7 +9949,7 @@ type GetSoulResponse struct { func (x *GetSoulResponse) Reset() { *x = GetSoulResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[114] + mi := &file_mecatl_v1_harness_proto_msgTypes[118] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9540,7 +9961,7 @@ func (x *GetSoulResponse) String() string { func (*GetSoulResponse) ProtoMessage() {} func (x *GetSoulResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[114] + mi := &file_mecatl_v1_harness_proto_msgTypes[118] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9553,7 +9974,7 @@ func (x *GetSoulResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSoulResponse.ProtoReflect.Descriptor instead. func (*GetSoulResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{114} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{118} } func (x *GetSoulResponse) GetSoul() *SoulInfo { @@ -9578,7 +9999,7 @@ type UserModelEntry struct { func (x *UserModelEntry) Reset() { *x = UserModelEntry{} - mi := &file_mecatl_v1_harness_proto_msgTypes[115] + mi := &file_mecatl_v1_harness_proto_msgTypes[119] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9590,7 +10011,7 @@ func (x *UserModelEntry) String() string { func (*UserModelEntry) ProtoMessage() {} func (x *UserModelEntry) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[115] + mi := &file_mecatl_v1_harness_proto_msgTypes[119] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9603,7 +10024,7 @@ func (x *UserModelEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use UserModelEntry.ProtoReflect.Descriptor instead. func (*UserModelEntry) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{115} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{119} } func (x *UserModelEntry) GetKey() string { @@ -9631,7 +10052,7 @@ type GetUserModelRequest struct { func (x *GetUserModelRequest) Reset() { *x = GetUserModelRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[116] + mi := &file_mecatl_v1_harness_proto_msgTypes[120] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9643,7 +10064,7 @@ func (x *GetUserModelRequest) String() string { func (*GetUserModelRequest) ProtoMessage() {} func (x *GetUserModelRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[116] + mi := &file_mecatl_v1_harness_proto_msgTypes[120] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9656,7 +10077,7 @@ func (x *GetUserModelRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetUserModelRequest.ProtoReflect.Descriptor instead. func (*GetUserModelRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{116} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{120} } func (x *GetUserModelRequest) GetKey() string { @@ -9685,7 +10106,7 @@ type UserModelRevision struct { func (x *UserModelRevision) Reset() { *x = UserModelRevision{} - mi := &file_mecatl_v1_harness_proto_msgTypes[117] + mi := &file_mecatl_v1_harness_proto_msgTypes[121] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9697,7 +10118,7 @@ func (x *UserModelRevision) String() string { func (*UserModelRevision) ProtoMessage() {} func (x *UserModelRevision) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[117] + mi := &file_mecatl_v1_harness_proto_msgTypes[121] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9710,7 +10131,7 @@ func (x *UserModelRevision) ProtoReflect() protoreflect.Message { // Deprecated: Use UserModelRevision.ProtoReflect.Descriptor instead. func (*UserModelRevision) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{117} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{121} } func (x *UserModelRevision) GetKey() string { @@ -9795,7 +10216,7 @@ type UserModelDetail struct { func (x *UserModelDetail) Reset() { *x = UserModelDetail{} - mi := &file_mecatl_v1_harness_proto_msgTypes[118] + mi := &file_mecatl_v1_harness_proto_msgTypes[122] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9807,7 +10228,7 @@ func (x *UserModelDetail) String() string { func (*UserModelDetail) ProtoMessage() {} func (x *UserModelDetail) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[118] + mi := &file_mecatl_v1_harness_proto_msgTypes[122] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9820,7 +10241,7 @@ func (x *UserModelDetail) ProtoReflect() protoreflect.Message { // Deprecated: Use UserModelDetail.ProtoReflect.Descriptor instead. func (*UserModelDetail) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{118} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{122} } func (x *UserModelDetail) GetCurrent() *UserModelRevision { @@ -9864,7 +10285,7 @@ type GetUserModelResponse struct { func (x *GetUserModelResponse) Reset() { *x = GetUserModelResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[119] + mi := &file_mecatl_v1_harness_proto_msgTypes[123] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9876,7 +10297,7 @@ func (x *GetUserModelResponse) String() string { func (*GetUserModelResponse) ProtoMessage() {} func (x *GetUserModelResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[119] + mi := &file_mecatl_v1_harness_proto_msgTypes[123] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9889,7 +10310,7 @@ func (x *GetUserModelResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetUserModelResponse.ProtoReflect.Descriptor instead. func (*GetUserModelResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{119} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{123} } func (x *GetUserModelResponse) GetEntries() []*UserModelEntry { @@ -9946,7 +10367,7 @@ type ModelInfo struct { func (x *ModelInfo) Reset() { *x = ModelInfo{} - mi := &file_mecatl_v1_harness_proto_msgTypes[120] + mi := &file_mecatl_v1_harness_proto_msgTypes[124] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9958,7 +10379,7 @@ func (x *ModelInfo) String() string { func (*ModelInfo) ProtoMessage() {} func (x *ModelInfo) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[120] + mi := &file_mecatl_v1_harness_proto_msgTypes[124] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9971,7 +10392,7 @@ func (x *ModelInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use ModelInfo.ProtoReflect.Descriptor instead. func (*ModelInfo) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{120} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{124} } func (x *ModelInfo) GetId() string { @@ -10025,7 +10446,7 @@ type ListModelsRequest struct { func (x *ListModelsRequest) Reset() { *x = ListModelsRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[121] + mi := &file_mecatl_v1_harness_proto_msgTypes[125] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10037,7 +10458,7 @@ func (x *ListModelsRequest) String() string { func (*ListModelsRequest) ProtoMessage() {} func (x *ListModelsRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[121] + mi := &file_mecatl_v1_harness_proto_msgTypes[125] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10050,7 +10471,7 @@ func (x *ListModelsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListModelsRequest.ProtoReflect.Descriptor instead. func (*ListModelsRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{121} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{125} } // ProviderStatus is one provider's last live-listing outcome, for client @@ -10097,7 +10518,7 @@ type ProviderStatus struct { func (x *ProviderStatus) Reset() { *x = ProviderStatus{} - mi := &file_mecatl_v1_harness_proto_msgTypes[122] + mi := &file_mecatl_v1_harness_proto_msgTypes[126] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10109,7 +10530,7 @@ func (x *ProviderStatus) String() string { func (*ProviderStatus) ProtoMessage() {} func (x *ProviderStatus) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[122] + mi := &file_mecatl_v1_harness_proto_msgTypes[126] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10122,7 +10543,7 @@ func (x *ProviderStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use ProviderStatus.ProtoReflect.Descriptor instead. func (*ProviderStatus) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{122} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{126} } func (x *ProviderStatus) GetProviderId() string { @@ -10186,7 +10607,7 @@ type ListModelsResponse struct { func (x *ListModelsResponse) Reset() { *x = ListModelsResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[123] + mi := &file_mecatl_v1_harness_proto_msgTypes[127] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10198,7 +10619,7 @@ func (x *ListModelsResponse) String() string { func (*ListModelsResponse) ProtoMessage() {} func (x *ListModelsResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[123] + mi := &file_mecatl_v1_harness_proto_msgTypes[127] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10211,7 +10632,7 @@ func (x *ListModelsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListModelsResponse.ProtoReflect.Descriptor instead. func (*ListModelsResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{123} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{127} } func (x *ListModelsResponse) GetModels() []*ModelInfo { @@ -10237,7 +10658,7 @@ type ReflectSessionRequest struct { func (x *ReflectSessionRequest) Reset() { *x = ReflectSessionRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[124] + mi := &file_mecatl_v1_harness_proto_msgTypes[128] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10249,7 +10670,7 @@ func (x *ReflectSessionRequest) String() string { func (*ReflectSessionRequest) ProtoMessage() {} func (x *ReflectSessionRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[124] + mi := &file_mecatl_v1_harness_proto_msgTypes[128] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10262,7 +10683,7 @@ func (x *ReflectSessionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReflectSessionRequest.ProtoReflect.Descriptor instead. func (*ReflectSessionRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{124} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{128} } func (x *ReflectSessionRequest) GetSessionId() string { @@ -10287,7 +10708,7 @@ type ReflectionReceipt struct { func (x *ReflectionReceipt) Reset() { *x = ReflectionReceipt{} - mi := &file_mecatl_v1_harness_proto_msgTypes[125] + mi := &file_mecatl_v1_harness_proto_msgTypes[129] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10299,7 +10720,7 @@ func (x *ReflectionReceipt) String() string { func (*ReflectionReceipt) ProtoMessage() {} func (x *ReflectionReceipt) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[125] + mi := &file_mecatl_v1_harness_proto_msgTypes[129] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10312,7 +10733,7 @@ func (x *ReflectionReceipt) ProtoReflect() protoreflect.Message { // Deprecated: Use ReflectionReceipt.ProtoReflect.Descriptor instead. func (*ReflectionReceipt) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{125} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{129} } func (x *ReflectionReceipt) GetReflectionId() string { @@ -10373,7 +10794,7 @@ type ReflectSessionResponse struct { func (x *ReflectSessionResponse) Reset() { *x = ReflectSessionResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[126] + mi := &file_mecatl_v1_harness_proto_msgTypes[130] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10385,7 +10806,7 @@ func (x *ReflectSessionResponse) String() string { func (*ReflectSessionResponse) ProtoMessage() {} func (x *ReflectSessionResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[126] + mi := &file_mecatl_v1_harness_proto_msgTypes[130] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10398,7 +10819,7 @@ func (x *ReflectSessionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReflectSessionResponse.ProtoReflect.Descriptor instead. func (*ReflectSessionResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{126} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{130} } func (x *ReflectSessionResponse) GetReceipt() *ReflectionReceipt { @@ -10421,7 +10842,7 @@ type ListLearningProposalsRequest struct { func (x *ListLearningProposalsRequest) Reset() { *x = ListLearningProposalsRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[127] + mi := &file_mecatl_v1_harness_proto_msgTypes[131] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10433,7 +10854,7 @@ func (x *ListLearningProposalsRequest) String() string { func (*ListLearningProposalsRequest) ProtoMessage() {} func (x *ListLearningProposalsRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[127] + mi := &file_mecatl_v1_harness_proto_msgTypes[131] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10446,7 +10867,7 @@ func (x *ListLearningProposalsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListLearningProposalsRequest.ProtoReflect.Descriptor instead. func (*ListLearningProposalsRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{127} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{131} } func (x *ListLearningProposalsRequest) GetStatus() string { @@ -10496,7 +10917,7 @@ type LearningEvidenceRef struct { func (x *LearningEvidenceRef) Reset() { *x = LearningEvidenceRef{} - mi := &file_mecatl_v1_harness_proto_msgTypes[128] + mi := &file_mecatl_v1_harness_proto_msgTypes[132] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10508,7 +10929,7 @@ func (x *LearningEvidenceRef) String() string { func (*LearningEvidenceRef) ProtoMessage() {} func (x *LearningEvidenceRef) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[128] + mi := &file_mecatl_v1_harness_proto_msgTypes[132] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10521,7 +10942,7 @@ func (x *LearningEvidenceRef) ProtoReflect() protoreflect.Message { // Deprecated: Use LearningEvidenceRef.ProtoReflect.Descriptor instead. func (*LearningEvidenceRef) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{128} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{132} } func (x *LearningEvidenceRef) GetSessionId() string { @@ -10599,7 +11020,7 @@ type LearningDecision struct { func (x *LearningDecision) Reset() { *x = LearningDecision{} - mi := &file_mecatl_v1_harness_proto_msgTypes[129] + mi := &file_mecatl_v1_harness_proto_msgTypes[133] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10611,7 +11032,7 @@ func (x *LearningDecision) String() string { func (*LearningDecision) ProtoMessage() {} func (x *LearningDecision) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[129] + mi := &file_mecatl_v1_harness_proto_msgTypes[133] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10624,7 +11045,7 @@ func (x *LearningDecision) ProtoReflect() protoreflect.Message { // Deprecated: Use LearningDecision.ProtoReflect.Descriptor instead. func (*LearningDecision) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{129} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{133} } func (x *LearningDecision) GetKind() string { @@ -10667,7 +11088,7 @@ type LearningPromotionReceipt struct { func (x *LearningPromotionReceipt) Reset() { *x = LearningPromotionReceipt{} - mi := &file_mecatl_v1_harness_proto_msgTypes[130] + mi := &file_mecatl_v1_harness_proto_msgTypes[134] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10679,7 +11100,7 @@ func (x *LearningPromotionReceipt) String() string { func (*LearningPromotionReceipt) ProtoMessage() {} func (x *LearningPromotionReceipt) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[130] + mi := &file_mecatl_v1_harness_proto_msgTypes[134] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10692,7 +11113,7 @@ func (x *LearningPromotionReceipt) ProtoReflect() protoreflect.Message { // Deprecated: Use LearningPromotionReceipt.ProtoReflect.Descriptor instead. func (*LearningPromotionReceipt) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{130} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{134} } func (x *LearningPromotionReceipt) GetMemoryKey() string { @@ -10754,7 +11175,7 @@ type LearningProposal struct { func (x *LearningProposal) Reset() { *x = LearningProposal{} - mi := &file_mecatl_v1_harness_proto_msgTypes[131] + mi := &file_mecatl_v1_harness_proto_msgTypes[135] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10766,7 +11187,7 @@ func (x *LearningProposal) String() string { func (*LearningProposal) ProtoMessage() {} func (x *LearningProposal) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[131] + mi := &file_mecatl_v1_harness_proto_msgTypes[135] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10779,7 +11200,7 @@ func (x *LearningProposal) ProtoReflect() protoreflect.Message { // Deprecated: Use LearningProposal.ProtoReflect.Descriptor instead. func (*LearningProposal) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{131} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{135} } func (x *LearningProposal) GetId() string { @@ -10925,7 +11346,7 @@ type ListLearningProposalsResponse struct { func (x *ListLearningProposalsResponse) Reset() { *x = ListLearningProposalsResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[132] + mi := &file_mecatl_v1_harness_proto_msgTypes[136] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10937,7 +11358,7 @@ func (x *ListLearningProposalsResponse) String() string { func (*ListLearningProposalsResponse) ProtoMessage() {} func (x *ListLearningProposalsResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[132] + mi := &file_mecatl_v1_harness_proto_msgTypes[136] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10950,7 +11371,7 @@ func (x *ListLearningProposalsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListLearningProposalsResponse.ProtoReflect.Descriptor instead. func (*ListLearningProposalsResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{132} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{136} } func (x *ListLearningProposalsResponse) GetProposals() []*LearningProposal { @@ -10977,7 +11398,7 @@ type GetLearningProposalRequest struct { func (x *GetLearningProposalRequest) Reset() { *x = GetLearningProposalRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[133] + mi := &file_mecatl_v1_harness_proto_msgTypes[137] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10989,7 +11410,7 @@ func (x *GetLearningProposalRequest) String() string { func (*GetLearningProposalRequest) ProtoMessage() {} func (x *GetLearningProposalRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[133] + mi := &file_mecatl_v1_harness_proto_msgTypes[137] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11002,7 +11423,7 @@ func (x *GetLearningProposalRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetLearningProposalRequest.ProtoReflect.Descriptor instead. func (*GetLearningProposalRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{133} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{137} } func (x *GetLearningProposalRequest) GetId() string { @@ -11028,7 +11449,7 @@ type GetLearningProposalResponse struct { func (x *GetLearningProposalResponse) Reset() { *x = GetLearningProposalResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[134] + mi := &file_mecatl_v1_harness_proto_msgTypes[138] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11040,7 +11461,7 @@ func (x *GetLearningProposalResponse) String() string { func (*GetLearningProposalResponse) ProtoMessage() {} func (x *GetLearningProposalResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[134] + mi := &file_mecatl_v1_harness_proto_msgTypes[138] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11053,7 +11474,7 @@ func (x *GetLearningProposalResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetLearningProposalResponse.ProtoReflect.Descriptor instead. func (*GetLearningProposalResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{134} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{138} } func (x *GetLearningProposalResponse) GetProposal() *LearningProposal { @@ -11076,7 +11497,7 @@ type DecideLearningProposalRequest struct { func (x *DecideLearningProposalRequest) Reset() { *x = DecideLearningProposalRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[135] + mi := &file_mecatl_v1_harness_proto_msgTypes[139] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11088,7 +11509,7 @@ func (x *DecideLearningProposalRequest) String() string { func (*DecideLearningProposalRequest) ProtoMessage() {} func (x *DecideLearningProposalRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[135] + mi := &file_mecatl_v1_harness_proto_msgTypes[139] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11101,7 +11522,7 @@ func (x *DecideLearningProposalRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DecideLearningProposalRequest.ProtoReflect.Descriptor instead. func (*DecideLearningProposalRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{135} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{139} } func (x *DecideLearningProposalRequest) GetId() string { @@ -11148,7 +11569,7 @@ type DecideLearningProposalResponse struct { func (x *DecideLearningProposalResponse) Reset() { *x = DecideLearningProposalResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[136] + mi := &file_mecatl_v1_harness_proto_msgTypes[140] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11160,7 +11581,7 @@ func (x *DecideLearningProposalResponse) String() string { func (*DecideLearningProposalResponse) ProtoMessage() {} func (x *DecideLearningProposalResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[136] + mi := &file_mecatl_v1_harness_proto_msgTypes[140] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11173,7 +11594,7 @@ func (x *DecideLearningProposalResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DecideLearningProposalResponse.ProtoReflect.Descriptor instead. func (*DecideLearningProposalResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{136} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{140} } func (x *DecideLearningProposalResponse) GetProposal() *LearningProposal { @@ -11194,7 +11615,7 @@ type UndoLearningPromotionRequest struct { func (x *UndoLearningPromotionRequest) Reset() { *x = UndoLearningPromotionRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[137] + mi := &file_mecatl_v1_harness_proto_msgTypes[141] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11206,7 +11627,7 @@ func (x *UndoLearningPromotionRequest) String() string { func (*UndoLearningPromotionRequest) ProtoMessage() {} func (x *UndoLearningPromotionRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[137] + mi := &file_mecatl_v1_harness_proto_msgTypes[141] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11219,7 +11640,7 @@ func (x *UndoLearningPromotionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UndoLearningPromotionRequest.ProtoReflect.Descriptor instead. func (*UndoLearningPromotionRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{137} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{141} } func (x *UndoLearningPromotionRequest) GetId() string { @@ -11252,7 +11673,7 @@ type UndoLearningPromotionResponse struct { func (x *UndoLearningPromotionResponse) Reset() { *x = UndoLearningPromotionResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[138] + mi := &file_mecatl_v1_harness_proto_msgTypes[142] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11264,7 +11685,7 @@ func (x *UndoLearningPromotionResponse) String() string { func (*UndoLearningPromotionResponse) ProtoMessage() {} func (x *UndoLearningPromotionResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[138] + mi := &file_mecatl_v1_harness_proto_msgTypes[142] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11277,7 +11698,7 @@ func (x *UndoLearningPromotionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UndoLearningPromotionResponse.ProtoReflect.Descriptor instead. func (*UndoLearningPromotionResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{138} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{142} } func (x *UndoLearningPromotionResponse) GetProposal() *LearningProposal { @@ -11314,7 +11735,7 @@ type LearnedSkillVersion struct { func (x *LearnedSkillVersion) Reset() { *x = LearnedSkillVersion{} - mi := &file_mecatl_v1_harness_proto_msgTypes[139] + mi := &file_mecatl_v1_harness_proto_msgTypes[143] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11326,7 +11747,7 @@ func (x *LearnedSkillVersion) String() string { func (*LearnedSkillVersion) ProtoMessage() {} func (x *LearnedSkillVersion) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[139] + mi := &file_mecatl_v1_harness_proto_msgTypes[143] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11339,7 +11760,7 @@ func (x *LearnedSkillVersion) ProtoReflect() protoreflect.Message { // Deprecated: Use LearnedSkillVersion.ProtoReflect.Descriptor instead. func (*LearnedSkillVersion) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{139} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{143} } func (x *LearnedSkillVersion) GetId() string { @@ -11475,7 +11896,7 @@ type SkillEvaluation struct { func (x *SkillEvaluation) Reset() { *x = SkillEvaluation{} - mi := &file_mecatl_v1_harness_proto_msgTypes[140] + mi := &file_mecatl_v1_harness_proto_msgTypes[144] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11487,7 +11908,7 @@ func (x *SkillEvaluation) String() string { func (*SkillEvaluation) ProtoMessage() {} func (x *SkillEvaluation) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[140] + mi := &file_mecatl_v1_harness_proto_msgTypes[144] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11500,7 +11921,7 @@ func (x *SkillEvaluation) ProtoReflect() protoreflect.Message { // Deprecated: Use SkillEvaluation.ProtoReflect.Descriptor instead. func (*SkillEvaluation) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{140} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{144} } func (x *SkillEvaluation) GetVerdict() string { @@ -11570,7 +11991,7 @@ type SkillChangeReceipt struct { func (x *SkillChangeReceipt) Reset() { *x = SkillChangeReceipt{} - mi := &file_mecatl_v1_harness_proto_msgTypes[141] + mi := &file_mecatl_v1_harness_proto_msgTypes[145] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11582,7 +12003,7 @@ func (x *SkillChangeReceipt) String() string { func (*SkillChangeReceipt) ProtoMessage() {} func (x *SkillChangeReceipt) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[141] + mi := &file_mecatl_v1_harness_proto_msgTypes[145] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11595,7 +12016,7 @@ func (x *SkillChangeReceipt) ProtoReflect() protoreflect.Message { // Deprecated: Use SkillChangeReceipt.ProtoReflect.Descriptor instead. func (*SkillChangeReceipt) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{141} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{145} } func (x *SkillChangeReceipt) GetId() string { @@ -11730,7 +12151,7 @@ type ListLearnedSkillsRequest struct { func (x *ListLearnedSkillsRequest) Reset() { *x = ListLearnedSkillsRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[142] + mi := &file_mecatl_v1_harness_proto_msgTypes[146] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11742,7 +12163,7 @@ func (x *ListLearnedSkillsRequest) String() string { func (*ListLearnedSkillsRequest) ProtoMessage() {} func (x *ListLearnedSkillsRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[142] + mi := &file_mecatl_v1_harness_proto_msgTypes[146] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11755,7 +12176,7 @@ func (x *ListLearnedSkillsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListLearnedSkillsRequest.ProtoReflect.Descriptor instead. func (*ListLearnedSkillsRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{142} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{146} } func (x *ListLearnedSkillsRequest) GetProject() string { @@ -11805,7 +12226,7 @@ type ListLearnedSkillsResponse struct { func (x *ListLearnedSkillsResponse) Reset() { *x = ListLearnedSkillsResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[143] + mi := &file_mecatl_v1_harness_proto_msgTypes[147] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11817,7 +12238,7 @@ func (x *ListLearnedSkillsResponse) String() string { func (*ListLearnedSkillsResponse) ProtoMessage() {} func (x *ListLearnedSkillsResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[143] + mi := &file_mecatl_v1_harness_proto_msgTypes[147] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11830,7 +12251,7 @@ func (x *ListLearnedSkillsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListLearnedSkillsResponse.ProtoReflect.Descriptor instead. func (*ListLearnedSkillsResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{143} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{147} } func (x *ListLearnedSkillsResponse) GetSkills() []*LearnedSkillVersion { @@ -11873,7 +12294,7 @@ type GetLearnedSkillRequest struct { func (x *GetLearnedSkillRequest) Reset() { *x = GetLearnedSkillRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[144] + mi := &file_mecatl_v1_harness_proto_msgTypes[148] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11885,7 +12306,7 @@ func (x *GetLearnedSkillRequest) String() string { func (*GetLearnedSkillRequest) ProtoMessage() {} func (x *GetLearnedSkillRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[144] + mi := &file_mecatl_v1_harness_proto_msgTypes[148] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11898,7 +12319,7 @@ func (x *GetLearnedSkillRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetLearnedSkillRequest.ProtoReflect.Descriptor instead. func (*GetLearnedSkillRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{144} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{148} } func (x *GetLearnedSkillRequest) GetProject() string { @@ -11940,7 +12361,7 @@ type GetLearnedSkillResponse struct { func (x *GetLearnedSkillResponse) Reset() { *x = GetLearnedSkillResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[145] + mi := &file_mecatl_v1_harness_proto_msgTypes[149] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -11952,7 +12373,7 @@ func (x *GetLearnedSkillResponse) String() string { func (*GetLearnedSkillResponse) ProtoMessage() {} func (x *GetLearnedSkillResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[145] + mi := &file_mecatl_v1_harness_proto_msgTypes[149] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -11965,7 +12386,7 @@ func (x *GetLearnedSkillResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetLearnedSkillResponse.ProtoReflect.Descriptor instead. func (*GetLearnedSkillResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{145} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{149} } func (x *GetLearnedSkillResponse) GetSkill() *LearnedSkillVersion { @@ -12002,7 +12423,7 @@ type DiffLearnedSkillVersionsRequest struct { func (x *DiffLearnedSkillVersionsRequest) Reset() { *x = DiffLearnedSkillVersionsRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[146] + mi := &file_mecatl_v1_harness_proto_msgTypes[150] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12014,7 +12435,7 @@ func (x *DiffLearnedSkillVersionsRequest) String() string { func (*DiffLearnedSkillVersionsRequest) ProtoMessage() {} func (x *DiffLearnedSkillVersionsRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[146] + mi := &file_mecatl_v1_harness_proto_msgTypes[150] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12027,7 +12448,7 @@ func (x *DiffLearnedSkillVersionsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DiffLearnedSkillVersionsRequest.ProtoReflect.Descriptor instead. func (*DiffLearnedSkillVersionsRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{146} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{150} } func (x *DiffLearnedSkillVersionsRequest) GetProject() string { @@ -12079,7 +12500,7 @@ type DiffLearnedSkillVersionsResponse struct { func (x *DiffLearnedSkillVersionsResponse) Reset() { *x = DiffLearnedSkillVersionsResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[147] + mi := &file_mecatl_v1_harness_proto_msgTypes[151] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12091,7 +12512,7 @@ func (x *DiffLearnedSkillVersionsResponse) String() string { func (*DiffLearnedSkillVersionsResponse) ProtoMessage() {} func (x *DiffLearnedSkillVersionsResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[147] + mi := &file_mecatl_v1_harness_proto_msgTypes[151] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12104,7 +12525,7 @@ func (x *DiffLearnedSkillVersionsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DiffLearnedSkillVersionsResponse.ProtoReflect.Descriptor instead. func (*DiffLearnedSkillVersionsResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{147} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{151} } func (x *DiffLearnedSkillVersionsResponse) GetDiff() string { @@ -12162,7 +12583,7 @@ type MutateLearnedSkillRequest struct { func (x *MutateLearnedSkillRequest) Reset() { *x = MutateLearnedSkillRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[148] + mi := &file_mecatl_v1_harness_proto_msgTypes[152] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12174,7 +12595,7 @@ func (x *MutateLearnedSkillRequest) String() string { func (*MutateLearnedSkillRequest) ProtoMessage() {} func (x *MutateLearnedSkillRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[148] + mi := &file_mecatl_v1_harness_proto_msgTypes[152] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12187,7 +12608,7 @@ func (x *MutateLearnedSkillRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MutateLearnedSkillRequest.ProtoReflect.Descriptor instead. func (*MutateLearnedSkillRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{148} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{152} } func (x *MutateLearnedSkillRequest) GetProject() string { @@ -12238,7 +12659,7 @@ type MutateLearnedSkillResponse struct { func (x *MutateLearnedSkillResponse) Reset() { *x = MutateLearnedSkillResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[149] + mi := &file_mecatl_v1_harness_proto_msgTypes[153] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12250,7 +12671,7 @@ func (x *MutateLearnedSkillResponse) String() string { func (*MutateLearnedSkillResponse) ProtoMessage() {} func (x *MutateLearnedSkillResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[149] + mi := &file_mecatl_v1_harness_proto_msgTypes[153] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12263,7 +12684,7 @@ func (x *MutateLearnedSkillResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MutateLearnedSkillResponse.ProtoReflect.Descriptor instead. func (*MutateLearnedSkillResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{149} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{153} } func (x *MutateLearnedSkillResponse) GetSkill() *LearnedSkillVersion { @@ -12314,7 +12735,7 @@ type RollbackLearnedSkillRequest struct { func (x *RollbackLearnedSkillRequest) Reset() { *x = RollbackLearnedSkillRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[150] + mi := &file_mecatl_v1_harness_proto_msgTypes[154] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12326,7 +12747,7 @@ func (x *RollbackLearnedSkillRequest) String() string { func (*RollbackLearnedSkillRequest) ProtoMessage() {} func (x *RollbackLearnedSkillRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[150] + mi := &file_mecatl_v1_harness_proto_msgTypes[154] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12339,7 +12760,7 @@ func (x *RollbackLearnedSkillRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RollbackLearnedSkillRequest.ProtoReflect.Descriptor instead. func (*RollbackLearnedSkillRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{150} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{154} } func (x *RollbackLearnedSkillRequest) GetProject() string { @@ -12388,7 +12809,7 @@ type ListSkillChangesRequest struct { func (x *ListSkillChangesRequest) Reset() { *x = ListSkillChangesRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[151] + mi := &file_mecatl_v1_harness_proto_msgTypes[155] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12400,7 +12821,7 @@ func (x *ListSkillChangesRequest) String() string { func (*ListSkillChangesRequest) ProtoMessage() {} func (x *ListSkillChangesRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[151] + mi := &file_mecatl_v1_harness_proto_msgTypes[155] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12413,7 +12834,7 @@ func (x *ListSkillChangesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListSkillChangesRequest.ProtoReflect.Descriptor instead. func (*ListSkillChangesRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{151} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{155} } func (x *ListSkillChangesRequest) GetProject() string { @@ -12449,7 +12870,7 @@ type ListSkillChangesResponse struct { func (x *ListSkillChangesResponse) Reset() { *x = ListSkillChangesResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[152] + mi := &file_mecatl_v1_harness_proto_msgTypes[156] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12461,7 +12882,7 @@ func (x *ListSkillChangesResponse) String() string { func (*ListSkillChangesResponse) ProtoMessage() {} func (x *ListSkillChangesResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[152] + mi := &file_mecatl_v1_harness_proto_msgTypes[156] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12474,7 +12895,7 @@ func (x *ListSkillChangesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListSkillChangesResponse.ProtoReflect.Descriptor instead. func (*ListSkillChangesResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{152} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{156} } func (x *ListSkillChangesResponse) GetChanges() []*SkillChangeReceipt { @@ -12540,7 +12961,7 @@ type CreateTeamRequest struct { func (x *CreateTeamRequest) Reset() { *x = CreateTeamRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[153] + mi := &file_mecatl_v1_harness_proto_msgTypes[157] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12552,7 +12973,7 @@ func (x *CreateTeamRequest) String() string { func (*CreateTeamRequest) ProtoMessage() {} func (x *CreateTeamRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[153] + mi := &file_mecatl_v1_harness_proto_msgTypes[157] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12565,7 +12986,7 @@ func (x *CreateTeamRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateTeamRequest.ProtoReflect.Descriptor instead. func (*CreateTeamRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{153} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{157} } func (x *CreateTeamRequest) GetWorkspace() string { @@ -12617,7 +13038,7 @@ type CreateTeamResponse struct { func (x *CreateTeamResponse) Reset() { *x = CreateTeamResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[154] + mi := &file_mecatl_v1_harness_proto_msgTypes[158] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12629,7 +13050,7 @@ func (x *CreateTeamResponse) String() string { func (*CreateTeamResponse) ProtoMessage() {} func (x *CreateTeamResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[154] + mi := &file_mecatl_v1_harness_proto_msgTypes[158] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12642,7 +13063,7 @@ func (x *CreateTeamResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateTeamResponse.ProtoReflect.Descriptor instead. func (*CreateTeamResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{154} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{158} } func (x *CreateTeamResponse) GetTeamId() string { @@ -12679,7 +13100,7 @@ type TeammateSpec struct { func (x *TeammateSpec) Reset() { *x = TeammateSpec{} - mi := &file_mecatl_v1_harness_proto_msgTypes[155] + mi := &file_mecatl_v1_harness_proto_msgTypes[159] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12691,7 +13112,7 @@ func (x *TeammateSpec) String() string { func (*TeammateSpec) ProtoMessage() {} func (x *TeammateSpec) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[155] + mi := &file_mecatl_v1_harness_proto_msgTypes[159] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12704,7 +13125,7 @@ func (x *TeammateSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use TeammateSpec.ProtoReflect.Descriptor instead. func (*TeammateSpec) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{155} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{159} } func (x *TeammateSpec) GetName() string { @@ -12763,7 +13184,7 @@ type SpawnTeammateRequest struct { func (x *SpawnTeammateRequest) Reset() { *x = SpawnTeammateRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[156] + mi := &file_mecatl_v1_harness_proto_msgTypes[160] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12775,7 +13196,7 @@ func (x *SpawnTeammateRequest) String() string { func (*SpawnTeammateRequest) ProtoMessage() {} func (x *SpawnTeammateRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[156] + mi := &file_mecatl_v1_harness_proto_msgTypes[160] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12788,7 +13209,7 @@ func (x *SpawnTeammateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SpawnTeammateRequest.ProtoReflect.Descriptor instead. func (*SpawnTeammateRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{156} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{160} } func (x *SpawnTeammateRequest) GetTeamId() string { @@ -12844,7 +13265,7 @@ type SpawnTeammateResponse struct { func (x *SpawnTeammateResponse) Reset() { *x = SpawnTeammateResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[157] + mi := &file_mecatl_v1_harness_proto_msgTypes[161] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12856,7 +13277,7 @@ func (x *SpawnTeammateResponse) String() string { func (*SpawnTeammateResponse) ProtoMessage() {} func (x *SpawnTeammateResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[157] + mi := &file_mecatl_v1_harness_proto_msgTypes[161] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12869,7 +13290,7 @@ func (x *SpawnTeammateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SpawnTeammateResponse.ProtoReflect.Descriptor instead. func (*SpawnTeammateResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{157} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{161} } func (x *SpawnTeammateResponse) GetMember() *TeamMember { @@ -12896,7 +13317,7 @@ type SendTeammateMessageRequest struct { func (x *SendTeammateMessageRequest) Reset() { *x = SendTeammateMessageRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[158] + mi := &file_mecatl_v1_harness_proto_msgTypes[162] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12908,7 +13329,7 @@ func (x *SendTeammateMessageRequest) String() string { func (*SendTeammateMessageRequest) ProtoMessage() {} func (x *SendTeammateMessageRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[158] + mi := &file_mecatl_v1_harness_proto_msgTypes[162] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12921,7 +13342,7 @@ func (x *SendTeammateMessageRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SendTeammateMessageRequest.ProtoReflect.Descriptor instead. func (*SendTeammateMessageRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{158} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{162} } func (x *SendTeammateMessageRequest) GetTeamId() string { @@ -12961,7 +13382,7 @@ type SendTeammateMessageResponse struct { func (x *SendTeammateMessageResponse) Reset() { *x = SendTeammateMessageResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[159] + mi := &file_mecatl_v1_harness_proto_msgTypes[163] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -12973,7 +13394,7 @@ func (x *SendTeammateMessageResponse) String() string { func (*SendTeammateMessageResponse) ProtoMessage() {} func (x *SendTeammateMessageResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[159] + mi := &file_mecatl_v1_harness_proto_msgTypes[163] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -12986,7 +13407,7 @@ func (x *SendTeammateMessageResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SendTeammateMessageResponse.ProtoReflect.Descriptor instead. func (*SendTeammateMessageResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{159} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{163} } // CancelTeammateRequest cancels one member of a running team. @@ -13002,7 +13423,7 @@ type CancelTeammateRequest struct { func (x *CancelTeammateRequest) Reset() { *x = CancelTeammateRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[160] + mi := &file_mecatl_v1_harness_proto_msgTypes[164] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13014,7 +13435,7 @@ func (x *CancelTeammateRequest) String() string { func (*CancelTeammateRequest) ProtoMessage() {} func (x *CancelTeammateRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[160] + mi := &file_mecatl_v1_harness_proto_msgTypes[164] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13027,7 +13448,7 @@ func (x *CancelTeammateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CancelTeammateRequest.ProtoReflect.Descriptor instead. func (*CancelTeammateRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{160} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{164} } func (x *CancelTeammateRequest) GetTeamId() string { @@ -13053,7 +13474,7 @@ type CancelTeammateResponse struct { func (x *CancelTeammateResponse) Reset() { *x = CancelTeammateResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[161] + mi := &file_mecatl_v1_harness_proto_msgTypes[165] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13065,7 +13486,7 @@ func (x *CancelTeammateResponse) String() string { func (*CancelTeammateResponse) ProtoMessage() {} func (x *CancelTeammateResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[161] + mi := &file_mecatl_v1_harness_proto_msgTypes[165] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13078,7 +13499,7 @@ func (x *CancelTeammateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CancelTeammateResponse.ProtoReflect.Descriptor instead. func (*CancelTeammateResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{161} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{165} } // RunTeamRequest drives a team to quiescence, streaming member events. @@ -13092,7 +13513,7 @@ type RunTeamRequest struct { func (x *RunTeamRequest) Reset() { *x = RunTeamRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[162] + mi := &file_mecatl_v1_harness_proto_msgTypes[166] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13104,7 +13525,7 @@ func (x *RunTeamRequest) String() string { func (*RunTeamRequest) ProtoMessage() {} func (x *RunTeamRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[162] + mi := &file_mecatl_v1_harness_proto_msgTypes[166] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13117,7 +13538,7 @@ func (x *RunTeamRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RunTeamRequest.ProtoReflect.Descriptor instead. func (*RunTeamRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{162} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{166} } func (x *RunTeamRequest) GetTeamId() string { @@ -13145,7 +13566,7 @@ type TeamEvent struct { func (x *TeamEvent) Reset() { *x = TeamEvent{} - mi := &file_mecatl_v1_harness_proto_msgTypes[163] + mi := &file_mecatl_v1_harness_proto_msgTypes[167] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13157,7 +13578,7 @@ func (x *TeamEvent) String() string { func (*TeamEvent) ProtoMessage() {} func (x *TeamEvent) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[163] + mi := &file_mecatl_v1_harness_proto_msgTypes[167] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13170,7 +13591,7 @@ func (x *TeamEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use TeamEvent.ProtoReflect.Descriptor instead. func (*TeamEvent) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{163} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{167} } func (x *TeamEvent) GetMember() string { @@ -13230,7 +13651,7 @@ type TeamOutcome struct { func (x *TeamOutcome) Reset() { *x = TeamOutcome{} - mi := &file_mecatl_v1_harness_proto_msgTypes[164] + mi := &file_mecatl_v1_harness_proto_msgTypes[168] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13242,7 +13663,7 @@ func (x *TeamOutcome) String() string { func (*TeamOutcome) ProtoMessage() {} func (x *TeamOutcome) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[164] + mi := &file_mecatl_v1_harness_proto_msgTypes[168] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13255,7 +13676,7 @@ func (x *TeamOutcome) ProtoReflect() protoreflect.Message { // Deprecated: Use TeamOutcome.ProtoReflect.Descriptor instead. func (*TeamOutcome) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{164} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{168} } func (x *TeamOutcome) GetRounds() int32 { @@ -13318,7 +13739,7 @@ type ListTeamRequest struct { func (x *ListTeamRequest) Reset() { *x = ListTeamRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[165] + mi := &file_mecatl_v1_harness_proto_msgTypes[169] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13330,7 +13751,7 @@ func (x *ListTeamRequest) String() string { func (*ListTeamRequest) ProtoMessage() {} func (x *ListTeamRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[165] + mi := &file_mecatl_v1_harness_proto_msgTypes[169] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13343,7 +13764,7 @@ func (x *ListTeamRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListTeamRequest.ProtoReflect.Descriptor instead. func (*ListTeamRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{165} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{169} } func (x *ListTeamRequest) GetTeamId() string { @@ -13368,7 +13789,7 @@ type ListTeamResponse struct { func (x *ListTeamResponse) Reset() { *x = ListTeamResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[166] + mi := &file_mecatl_v1_harness_proto_msgTypes[170] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13380,7 +13801,7 @@ func (x *ListTeamResponse) String() string { func (*ListTeamResponse) ProtoMessage() {} func (x *ListTeamResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[166] + mi := &file_mecatl_v1_harness_proto_msgTypes[170] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13393,7 +13814,7 @@ func (x *ListTeamResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListTeamResponse.ProtoReflect.Descriptor instead. func (*ListTeamResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{166} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{170} } func (x *ListTeamResponse) GetMembers() []*TeamMember { @@ -13434,7 +13855,7 @@ type TeamMember struct { func (x *TeamMember) Reset() { *x = TeamMember{} - mi := &file_mecatl_v1_harness_proto_msgTypes[167] + mi := &file_mecatl_v1_harness_proto_msgTypes[171] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13446,7 +13867,7 @@ func (x *TeamMember) String() string { func (*TeamMember) ProtoMessage() {} func (x *TeamMember) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[167] + mi := &file_mecatl_v1_harness_proto_msgTypes[171] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13459,7 +13880,7 @@ func (x *TeamMember) ProtoReflect() protoreflect.Message { // Deprecated: Use TeamMember.ProtoReflect.Descriptor instead. func (*TeamMember) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{167} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{171} } func (x *TeamMember) GetName() string { @@ -13509,7 +13930,7 @@ type TeamTask struct { func (x *TeamTask) Reset() { *x = TeamTask{} - mi := &file_mecatl_v1_harness_proto_msgTypes[168] + mi := &file_mecatl_v1_harness_proto_msgTypes[172] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13521,7 +13942,7 @@ func (x *TeamTask) String() string { func (*TeamTask) ProtoMessage() {} func (x *TeamTask) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[168] + mi := &file_mecatl_v1_harness_proto_msgTypes[172] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13534,7 +13955,7 @@ func (x *TeamTask) ProtoReflect() protoreflect.Message { // Deprecated: Use TeamTask.ProtoReflect.Descriptor instead. func (*TeamTask) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{168} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{172} } func (x *TeamTask) GetId() string { @@ -13587,7 +14008,7 @@ type TeamFinding struct { func (x *TeamFinding) Reset() { *x = TeamFinding{} - mi := &file_mecatl_v1_harness_proto_msgTypes[169] + mi := &file_mecatl_v1_harness_proto_msgTypes[173] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13599,7 +14020,7 @@ func (x *TeamFinding) String() string { func (*TeamFinding) ProtoMessage() {} func (x *TeamFinding) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[169] + mi := &file_mecatl_v1_harness_proto_msgTypes[173] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13612,7 +14033,7 @@ func (x *TeamFinding) ProtoReflect() protoreflect.Message { // Deprecated: Use TeamFinding.ProtoReflect.Descriptor instead. func (*TeamFinding) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{169} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{173} } func (x *TeamFinding) GetMember() string { @@ -13655,7 +14076,7 @@ type TeamMemberDisposition struct { func (x *TeamMemberDisposition) Reset() { *x = TeamMemberDisposition{} - mi := &file_mecatl_v1_harness_proto_msgTypes[170] + mi := &file_mecatl_v1_harness_proto_msgTypes[174] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13667,7 +14088,7 @@ func (x *TeamMemberDisposition) String() string { func (*TeamMemberDisposition) ProtoMessage() {} func (x *TeamMemberDisposition) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[170] + mi := &file_mecatl_v1_harness_proto_msgTypes[174] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13680,7 +14101,7 @@ func (x *TeamMemberDisposition) ProtoReflect() protoreflect.Message { // Deprecated: Use TeamMemberDisposition.ProtoReflect.Descriptor instead. func (*TeamMemberDisposition) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{170} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{174} } func (x *TeamMemberDisposition) GetName() string { @@ -13722,7 +14143,7 @@ type CleanupTeamRequest struct { func (x *CleanupTeamRequest) Reset() { *x = CleanupTeamRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[171] + mi := &file_mecatl_v1_harness_proto_msgTypes[175] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13734,7 +14155,7 @@ func (x *CleanupTeamRequest) String() string { func (*CleanupTeamRequest) ProtoMessage() {} func (x *CleanupTeamRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[171] + mi := &file_mecatl_v1_harness_proto_msgTypes[175] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13747,7 +14168,7 @@ func (x *CleanupTeamRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CleanupTeamRequest.ProtoReflect.Descriptor instead. func (*CleanupTeamRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{171} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{175} } func (x *CleanupTeamRequest) GetTeamId() string { @@ -13766,7 +14187,7 @@ type CleanupTeamResponse struct { func (x *CleanupTeamResponse) Reset() { *x = CleanupTeamResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[172] + mi := &file_mecatl_v1_harness_proto_msgTypes[176] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13778,7 +14199,7 @@ func (x *CleanupTeamResponse) String() string { func (*CleanupTeamResponse) ProtoMessage() {} func (x *CleanupTeamResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[172] + mi := &file_mecatl_v1_harness_proto_msgTypes[176] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13791,7 +14212,7 @@ func (x *CleanupTeamResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CleanupTeamResponse.ProtoReflect.Descriptor instead. func (*CleanupTeamResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{172} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{176} } // ApprovePlanRequest resolves a parked plan-approval ask (issue #206). See the @@ -13814,7 +14235,7 @@ type ApprovePlanRequest struct { func (x *ApprovePlanRequest) Reset() { *x = ApprovePlanRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[173] + mi := &file_mecatl_v1_harness_proto_msgTypes[177] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13826,7 +14247,7 @@ func (x *ApprovePlanRequest) String() string { func (*ApprovePlanRequest) ProtoMessage() {} func (x *ApprovePlanRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[173] + mi := &file_mecatl_v1_harness_proto_msgTypes[177] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13839,7 +14260,7 @@ func (x *ApprovePlanRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ApprovePlanRequest.ProtoReflect.Descriptor instead. func (*ApprovePlanRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{173} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{177} } func (x *ApprovePlanRequest) GetSessionId() string { @@ -13875,7 +14296,7 @@ type ManualDreamCapabilities struct { func (x *ManualDreamCapabilities) Reset() { *x = ManualDreamCapabilities{} - mi := &file_mecatl_v1_harness_proto_msgTypes[174] + mi := &file_mecatl_v1_harness_proto_msgTypes[178] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13887,7 +14308,7 @@ func (x *ManualDreamCapabilities) String() string { func (*ManualDreamCapabilities) ProtoMessage() {} func (x *ManualDreamCapabilities) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[174] + mi := &file_mecatl_v1_harness_proto_msgTypes[178] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13900,7 +14321,7 @@ func (x *ManualDreamCapabilities) ProtoReflect() protoreflect.Message { // Deprecated: Use ManualDreamCapabilities.ProtoReflect.Descriptor instead. func (*ManualDreamCapabilities) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{174} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{178} } func (x *ManualDreamCapabilities) GetProjectMemory() *DreamTargetCapability { @@ -13928,7 +14349,7 @@ type DreamTargetCapability struct { func (x *DreamTargetCapability) Reset() { *x = DreamTargetCapability{} - mi := &file_mecatl_v1_harness_proto_msgTypes[175] + mi := &file_mecatl_v1_harness_proto_msgTypes[179] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13940,7 +14361,7 @@ func (x *DreamTargetCapability) String() string { func (*DreamTargetCapability) ProtoMessage() {} func (x *DreamTargetCapability) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[175] + mi := &file_mecatl_v1_harness_proto_msgTypes[179] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -13953,7 +14374,7 @@ func (x *DreamTargetCapability) ProtoReflect() protoreflect.Message { // Deprecated: Use DreamTargetCapability.ProtoReflect.Descriptor instead. func (*DreamTargetCapability) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{175} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{179} } func (x *DreamTargetCapability) GetGenerate() bool { @@ -13987,7 +14408,7 @@ type GenerateDreamPlanRequest struct { func (x *GenerateDreamPlanRequest) Reset() { *x = GenerateDreamPlanRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[176] + mi := &file_mecatl_v1_harness_proto_msgTypes[180] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -13999,7 +14420,7 @@ func (x *GenerateDreamPlanRequest) String() string { func (*GenerateDreamPlanRequest) ProtoMessage() {} func (x *GenerateDreamPlanRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[176] + mi := &file_mecatl_v1_harness_proto_msgTypes[180] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14012,7 +14433,7 @@ func (x *GenerateDreamPlanRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GenerateDreamPlanRequest.ProtoReflect.Descriptor instead. func (*GenerateDreamPlanRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{176} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{180} } func (x *GenerateDreamPlanRequest) GetTarget() string { @@ -14031,7 +14452,7 @@ type GenerateDreamPlanResponse struct { func (x *GenerateDreamPlanResponse) Reset() { *x = GenerateDreamPlanResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[177] + mi := &file_mecatl_v1_harness_proto_msgTypes[181] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14043,7 +14464,7 @@ func (x *GenerateDreamPlanResponse) String() string { func (*GenerateDreamPlanResponse) ProtoMessage() {} func (x *GenerateDreamPlanResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[177] + mi := &file_mecatl_v1_harness_proto_msgTypes[181] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14056,7 +14477,7 @@ func (x *GenerateDreamPlanResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GenerateDreamPlanResponse.ProtoReflect.Descriptor instead. func (*GenerateDreamPlanResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{177} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{181} } func (x *GenerateDreamPlanResponse) GetPlan() *DreamReviewPlan { @@ -14077,7 +14498,7 @@ type DecideDreamPlanRequest struct { func (x *DecideDreamPlanRequest) Reset() { *x = DecideDreamPlanRequest{} - mi := &file_mecatl_v1_harness_proto_msgTypes[178] + mi := &file_mecatl_v1_harness_proto_msgTypes[182] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14089,7 +14510,7 @@ func (x *DecideDreamPlanRequest) String() string { func (*DecideDreamPlanRequest) ProtoMessage() {} func (x *DecideDreamPlanRequest) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[178] + mi := &file_mecatl_v1_harness_proto_msgTypes[182] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14102,7 +14523,7 @@ func (x *DecideDreamPlanRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DecideDreamPlanRequest.ProtoReflect.Descriptor instead. func (*DecideDreamPlanRequest) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{178} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{182} } func (x *DecideDreamPlanRequest) GetPlanId() string { @@ -14128,7 +14549,7 @@ type DecideDreamPlanResponse struct { func (x *DecideDreamPlanResponse) Reset() { *x = DecideDreamPlanResponse{} - mi := &file_mecatl_v1_harness_proto_msgTypes[179] + mi := &file_mecatl_v1_harness_proto_msgTypes[183] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14140,7 +14561,7 @@ func (x *DecideDreamPlanResponse) String() string { func (*DecideDreamPlanResponse) ProtoMessage() {} func (x *DecideDreamPlanResponse) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[179] + mi := &file_mecatl_v1_harness_proto_msgTypes[183] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14153,7 +14574,7 @@ func (x *DecideDreamPlanResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DecideDreamPlanResponse.ProtoReflect.Descriptor instead. func (*DecideDreamPlanResponse) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{179} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{183} } func (x *DecideDreamPlanResponse) GetReceipt() *DreamReceipt { @@ -14177,7 +14598,7 @@ type DreamReviewPlan struct { func (x *DreamReviewPlan) Reset() { *x = DreamReviewPlan{} - mi := &file_mecatl_v1_harness_proto_msgTypes[180] + mi := &file_mecatl_v1_harness_proto_msgTypes[184] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14189,7 +14610,7 @@ func (x *DreamReviewPlan) String() string { func (*DreamReviewPlan) ProtoMessage() {} func (x *DreamReviewPlan) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[180] + mi := &file_mecatl_v1_harness_proto_msgTypes[184] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14202,7 +14623,7 @@ func (x *DreamReviewPlan) ProtoReflect() protoreflect.Message { // Deprecated: Use DreamReviewPlan.ProtoReflect.Descriptor instead. func (*DreamReviewPlan) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{180} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{184} } func (x *DreamReviewPlan) GetId() string { @@ -14261,7 +14682,7 @@ type DreamOperation struct { func (x *DreamOperation) Reset() { *x = DreamOperation{} - mi := &file_mecatl_v1_harness_proto_msgTypes[181] + mi := &file_mecatl_v1_harness_proto_msgTypes[185] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14273,7 +14694,7 @@ func (x *DreamOperation) String() string { func (*DreamOperation) ProtoMessage() {} func (x *DreamOperation) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[181] + mi := &file_mecatl_v1_harness_proto_msgTypes[185] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14286,7 +14707,7 @@ func (x *DreamOperation) ProtoReflect() protoreflect.Message { // Deprecated: Use DreamOperation.ProtoReflect.Descriptor instead. func (*DreamOperation) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{181} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{185} } func (x *DreamOperation) GetKind() string { @@ -14342,7 +14763,7 @@ type DreamParticipant struct { func (x *DreamParticipant) Reset() { *x = DreamParticipant{} - mi := &file_mecatl_v1_harness_proto_msgTypes[182] + mi := &file_mecatl_v1_harness_proto_msgTypes[186] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14354,7 +14775,7 @@ func (x *DreamParticipant) String() string { func (*DreamParticipant) ProtoMessage() {} func (x *DreamParticipant) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[182] + mi := &file_mecatl_v1_harness_proto_msgTypes[186] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14367,7 +14788,7 @@ func (x *DreamParticipant) ProtoReflect() protoreflect.Message { // Deprecated: Use DreamParticipant.ProtoReflect.Descriptor instead. func (*DreamParticipant) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{182} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{186} } func (x *DreamParticipant) GetKey() string { @@ -14401,7 +14822,7 @@ type DreamReplacement struct { func (x *DreamReplacement) Reset() { *x = DreamReplacement{} - mi := &file_mecatl_v1_harness_proto_msgTypes[183] + mi := &file_mecatl_v1_harness_proto_msgTypes[187] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14413,7 +14834,7 @@ func (x *DreamReplacement) String() string { func (*DreamReplacement) ProtoMessage() {} func (x *DreamReplacement) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[183] + mi := &file_mecatl_v1_harness_proto_msgTypes[187] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14426,7 +14847,7 @@ func (x *DreamReplacement) ProtoReflect() protoreflect.Message { // Deprecated: Use DreamReplacement.ProtoReflect.Descriptor instead. func (*DreamReplacement) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{183} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{187} } func (x *DreamReplacement) GetValue() string { @@ -14459,7 +14880,7 @@ type DreamReceipt struct { func (x *DreamReceipt) Reset() { *x = DreamReceipt{} - mi := &file_mecatl_v1_harness_proto_msgTypes[184] + mi := &file_mecatl_v1_harness_proto_msgTypes[188] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -14471,7 +14892,7 @@ func (x *DreamReceipt) String() string { func (*DreamReceipt) ProtoMessage() {} func (x *DreamReceipt) ProtoReflect() protoreflect.Message { - mi := &file_mecatl_v1_harness_proto_msgTypes[184] + mi := &file_mecatl_v1_harness_proto_msgTypes[188] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -14484,7 +14905,7 @@ func (x *DreamReceipt) ProtoReflect() protoreflect.Message { // Deprecated: Use DreamReceipt.ProtoReflect.Descriptor instead. func (*DreamReceipt) Descriptor() ([]byte, []int) { - return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{184} + return file_mecatl_v1_harness_proto_rawDescGZIP(), []int{188} } func (x *DreamReceipt) GetId() string { @@ -14580,7 +15001,7 @@ var file_mecatl_v1_harness_proto_rawDesc = string([]byte{ 0x73, 0x6f, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x66, 0x66, 0x6f, 0x72, 0x74, 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, 0x22, 0x82, 0x06, 0x0a, 0x12, 0x53, 0x65, 0x72, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x98, 0x06, 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, 0x61, @@ -14628,694 +15049,709 @@ var file_mecatl_v1_harness_proto_rawDesc = string([]byte{ 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0b, 0x6d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x18, 0x17, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, - 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x22, 0x8d, 0x02, - 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, + 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x14, 0x0a, + 0x05, 0x73, 0x74, 0x65, 0x65, 0x72, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x73, 0x74, + 0x65, 0x65, 0x72, 0x22, 0x8d, 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, 0x41, 0x0a, 0x0c, + 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 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, + 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, + 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, 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, 0x41, 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, 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, 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, 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, 0x99, 0x04, 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, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 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, 0x1c, + 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x29, 0x0a, 0x10, + 0x74, 0x69, 0x74, 0x6c, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, + 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x50, 0x72, 0x6f, + 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x9e, 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, 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, 0x88, 0x07, 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, 0x22, 0x1d, 0x0a, 0x1b, 0x50, 0x6c, 0x61, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0x8f, 0x03, 0x0a, 0x14, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6c, 0x61, 0x6e, 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, 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, 0x1f, 0x0a, 0x0b, 0x76, 0x31, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, 0x31, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, + 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x32, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, 0x32, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, + 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x66, 0x61, + 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x69, 0x6e, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x12, 0x29, 0x0a, + 0x10, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, + 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, + 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2b, 0x0a, + 0x11, 0x72, 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x79, 0x74, + 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x72, 0x65, 0x63, 0x6c, 0x61, 0x69, + 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x65, + 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x42, 0x79, + 0x74, 0x65, 0x73, 0x22, 0x5f, 0x0a, 0x1c, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x70, + 0x6c, 0x61, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x73, + 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, + 0x53, 0x69, 0x7a, 0x65, 0x22, 0x5e, 0x0a, 0x1d, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x73, + 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, + 0x53, 0x69, 0x7a, 0x65, 0x22, 0x3f, 0x0a, 0x1d, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, 0x3f, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, 0x77, 0x0a, 0x19, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, + 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, 0xe5, 0x03, 0x0a, 0x13, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, 0x1f, 0x0a, 0x0b, 0x76, + 0x31, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0a, 0x76, 0x31, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, + 0x76, 0x32, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0a, 0x76, 0x32, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x12, 0x29, 0x0a, + 0x10, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, + 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x6b, 0x69, 0x70, + 0x70, 0x65, 0x64, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0f, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x46, 0x61, 0x6d, 0x69, 0x6c, + 0x69, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, + 0x79, 0x74, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x6c, + 0x61, 0x69, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x10, 0x72, 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x61, 0x62, 0x6c, 0x65, + 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, + 0x72, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, + 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1c, + 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, + 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, + 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x61, 0x69, 0x6c, + 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, + 0x12, 0x3c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x24, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x74, 0x65, + 0x6d, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 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, + 0x8a, 0x01, 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, 0x22, 0x34, 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, 0x22, 0xd8, 0x01, 0x0a, 0x10, 0x41, 0x64, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, + 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x4b, 0x69, 0x6e, 0x64, + 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, + 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 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, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x65, + 0x6c, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x8f, 0x01, + 0x0a, 0x1f, 0x50, 0x72, 0x65, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x41, 0x64, 0x6f, 0x70, 0x74, 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, 0x37, 0x0a, 0x08, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x69, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x22, + 0x98, 0x01, 0x0a, 0x20, 0x50, 0x72, 0x65, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6c, 0x69, 0x67, 0x69, 0x62, 0x6c, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x6c, 0x69, 0x67, 0x69, 0x62, 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, 0x37, 0x0a, 0x08, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x41, 0x64, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, + 0x52, 0x08, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x22, 0xb5, 0x01, 0x0a, 0x13, 0x41, + 0x64, 0x6f, 0x70, 0x74, 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, 0x0f, 0x69, 0x64, 0x65, 0x6d, 0x70, + 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0e, 0x69, 0x64, 0x65, 0x6d, 0x70, + 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4b, 0x65, 0x79, 0x12, 0x37, 0x0a, 0x08, 0x62, 0x69, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x73, 0x22, 0xb8, 0x02, 0x0a, 0x14, 0x41, 0x64, 0x6f, 0x70, 0x74, 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, 0x2a, 0x0a, 0x11, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x41, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, - 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, + 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 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, 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, + 0x73, 0x18, 0x04, 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, 0x3f, 0x0a, 0x0e, - 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x04, + 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x05, 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, 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, 0x41, 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, - 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, 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, 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, 0x99, 0x04, 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, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 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, 0x1c, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, - 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x5f, - 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, - 0x65, 0x22, 0x9e, 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, 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, 0x88, 0x07, 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, 0x22, 0x1d, 0x0a, 0x1b, - 0x50, 0x6c, 0x61, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8f, 0x03, 0x0a, 0x14, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x50, 0x6c, 0x61, 0x6e, 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, 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, 0x1f, 0x0a, 0x0b, 0x76, 0x31, - 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0a, 0x76, 0x31, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x76, - 0x32, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0a, 0x76, 0x32, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, - 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x46, - 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x6b, 0x69, 0x70, 0x70, - 0x65, 0x64, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x0f, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, - 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x79, - 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x6c, 0x61, - 0x69, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x10, 0x72, 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x42, - 0x79, 0x74, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, - 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x74, - 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0x5f, 0x0a, - 0x1c, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, - 0x07, 0x70, 0x6c, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, - 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x70, 0x6c, 0x61, 0x6e, 0x49, 0x64, 0x12, - 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x5e, - 0x0a, 0x1d, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, - 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, 0x12, - 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x09, 0x62, 0x61, 0x74, 0x63, 0x68, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x3f, - 0x0a, 0x1d, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, - 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, - 0x3f, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, 0x77, 0x0a, 0x19, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 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, 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, 0xe5, 0x03, 0x0a, 0x13, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, 0x1f, 0x0a, 0x0b, 0x76, 0x31, 0x5f, 0x66, 0x61, 0x6d, 0x69, - 0x6c, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, 0x31, 0x46, 0x61, - 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x32, 0x5f, 0x66, 0x61, 0x6d, - 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, 0x32, 0x46, - 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x5f, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x0f, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, - 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x66, 0x61, - 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x73, 0x6b, - 0x69, 0x70, 0x70, 0x65, 0x64, 0x46, 0x61, 0x6d, 0x69, 0x6c, 0x69, 0x65, 0x73, 0x12, 0x23, 0x0a, - 0x0d, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x74, - 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x72, 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x61, 0x62, 0x6c, - 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x72, - 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, - 0x27, 0x0a, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x62, 0x79, 0x74, - 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, - 0x61, 0x72, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x63, - 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x70, 0x72, 0x6f, - 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, - 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74, - 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x06, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, 0x3c, 0x0a, 0x06, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, - 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x45, 0x72, 0x72, 0x6f, 0x72, - 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 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, + 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x22, 0x95, 0x04, + 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, 0x1c, 0x0a, + 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 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, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x69, + 0x74, 0x6c, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x65, + 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, + 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 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, 0x3b, 0x0a, 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, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x61, 0x64, + 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0xdd, 0x02, 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, 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, 0x6e, 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, 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, 0x8a, 0x01, 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, 0x22, 0x34, 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, 0x22, 0xd8, 0x01, 0x0a, - 0x10, 0x41, 0x64, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, - 0x29, 0x0a, 0x10, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6b, - 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x65, 0x6e, 0x76, 0x69, 0x72, - 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x6e, - 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x49, - 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, - 0x18, 0x04, 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, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x18, 0x0a, - 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x8f, 0x01, 0x0a, 0x1f, 0x50, 0x72, 0x65, 0x66, - 0x6c, 0x69, 0x67, 0x68, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x6f, 0x70, - 0x74, 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, 0x37, 0x0a, 0x08, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, - 0x64, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x52, - 0x08, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x98, 0x01, 0x0a, 0x20, 0x50, 0x72, - 0x65, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x64, - 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, - 0x0a, 0x08, 0x65, 0x6c, 0x69, 0x67, 0x69, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x08, 0x65, 0x6c, 0x69, 0x67, 0x69, 0x62, 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, 0x37, 0x0a, 0x08, 0x62, - 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x6f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x62, 0x69, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x73, 0x22, 0xb5, 0x01, 0x0a, 0x13, 0x41, 0x64, 0x6f, 0x70, 0x74, 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, 0x0f, 0x69, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, - 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, - 0x02, 0x10, 0x01, 0x52, 0x0e, 0x69, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, - 0x4b, 0x65, 0x79, 0x12, 0x37, 0x0a, 0x08, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x41, 0x64, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, - 0x67, 0x73, 0x52, 0x08, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x22, 0xb8, 0x02, 0x0a, - 0x14, 0x41, 0x64, 0x6f, 0x70, 0x74, 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, 0x2a, 0x0a, 0x11, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, - 0x12, 0x41, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 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, 0x51, 0x0a, 0x14, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, - 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x04, 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, 0x3f, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, - 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x05, 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, 0x22, 0x95, 0x04, 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, 0x1c, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, - 0x73, 0x70, 0x61, 0x63, 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, 0x14, - 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, - 0x69, 0x74, 0x6c, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x5f, 0x70, 0x72, - 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, - 0x74, 0x69, 0x74, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x76, 0x65, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x12, - 0x41, 0x0a, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, - 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 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, 0x3b, 0x0a, 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, - 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x61, 0x64, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, - 0xf6, 0x01, 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, 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, - 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, 0x6e, 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, 0x22, 0x7c, 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, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x12, 0x34, 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, 0x52, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, - 0x63, 0x74, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 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, - 0x87, 0x06, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, + 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, 0x22, 0x7c, 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, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x12, 0x34, 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, 0x52, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x22, + 0x08, 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 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, 0x43, 0x0a, 0x05, + 0x53, 0x74, 0x65, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 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, 0x22, 0x2c, 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, 0x22, + 0xed, 0x06, 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, @@ -15363,1570 +15799,1601 @@ var file_mecatl_v1_harness_proto_rawDesc = string([]byte{ 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, 0x22, 0x8b, 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, 0x18, 0x0a, - 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 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, 0x21, 0x0a, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x6c, - 0x77, 0x61, 0x79, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, - 0x77, 0x41, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x22, 0x4a, 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, 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, 0x7e, 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, 0x22, 0x9d, 0x04, 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, 0x22, 0xf1, 0x01, 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, + 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, 0x22, + 0x3e, 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, 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, 0x8b, + 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, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x64, 0x69, 0x63, 0x74, 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, 0x21, 0x0a, 0x0c, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x5f, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x41, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x22, 0x4a, 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, 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, 0x7e, 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, 0x22, 0x9d, 0x04, 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, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x6f, 0x75, 0x74, + 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, 0x06, 0x20, 0x01, 0x28, 0x09, + 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, 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, 0x22, 0x98, 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, 0x22, 0xf1, 0x05, 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, + 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, 0x22, 0xf1, 0x01, 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, 0x22, + 0x98, 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, 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, 0x1c, 0x0a, 0x09, - 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 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, - 0x29, 0x0a, 0x10, 0x77, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x77, 0x69, 0x6e, 0x6e, 0x65, - 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 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, 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, 0x66, 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, 0x22, 0x8c, 0x01, 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, 0x1c, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x70, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, - 0x74, 0x22, 0x52, 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, 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, + 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, 0x22, 0xf1, 0x05, 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, 0x1c, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 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, 0x29, 0x0a, 0x10, 0x77, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x5f, 0x77, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, + 0x77, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 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, 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, + 0x66, 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, 0x22, 0x8c, 0x01, 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, 0x1c, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x6d, + 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x70, 0x65, 0x72, + 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x22, 0x52, 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, 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, - 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, 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, 0x48, 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, - 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, + 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, 0x48, 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, 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, 0x33, 0x0a, 0x13, - 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, - 0x65, 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, 0x5e, 0x0a, 0x08, 0x57, 0x6f, 0x72, - 0x6b, 0x74, 0x72, 0x65, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x72, 0x61, - 0x6e, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, - 0x68, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x65, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x68, 0x65, 0x61, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x61, 0x72, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x04, 0x62, 0x61, 0x72, 0x65, 0x22, 0x34, 0x0a, 0x14, 0x4c, 0x69, 0x73, - 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 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, 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, 0xb8, 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, 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, 0xe4, 0x01, 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, - 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, 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, + 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, 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, 0x33, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 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, 0x5e, + 0x0a, 0x08, 0x57, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, + 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x16, + 0x0a, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x65, 0x61, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x65, 0x61, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x61, + 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x62, 0x61, 0x72, 0x65, 0x22, 0x34, + 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, + 0x70, 0x61, 0x63, 0x65, 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, 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, 0xb8, 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, + 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, 0xe4, + 0x01, 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, 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, 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, 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, + 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, 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, + 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, 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, + 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, 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, 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, + 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, 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, + 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, 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, 0xbd, 0x01, - 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, - 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 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, + 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, 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, 0xbd, 0x01, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x61, + 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, + 0x72, 0x02, 0x10, 0x01, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 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, 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, + 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, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, + 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, 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, + 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, 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, 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, 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, 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, + 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, 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, 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, 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, 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, - 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x10, 0x03, 0x32, 0xd1, 0x29, 0x0a, 0x0e, 0x48, 0x61, 0x72, - 0x6e, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 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, - 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, 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, 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, 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, 0x73, 0x0a, 0x18, - 0x50, 0x72, 0x65, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x41, 0x64, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x50, 0x72, 0x65, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x41, 0x64, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x41, 0x64, 0x6f, 0x70, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x12, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, - 0x6f, 0x70, 0x74, 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, 0x41, 0x64, - 0x6f, 0x70, 0x74, 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, 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, + 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x42, 0x55, 0x44, 0x47, 0x45, 0x54, 0x10, 0x03, 0x32, 0xd1, + 0x29, 0x0a, 0x0e, 0x48, 0x61, 0x72, 0x6e, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 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, 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, 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, + 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, 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, 0x73, 0x0a, 0x18, 0x50, 0x72, 0x65, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, + 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x66, 0x6c, + 0x69, 0x67, 0x68, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6d, 0x65, 0x63, + 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x66, 0x6c, 0x69, 0x67, 0x68, 0x74, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x41, 0x64, 0x6f, 0x70, 0x74, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x6f, 0x70, 0x74, 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, 0x41, 0x64, 0x6f, 0x70, 0x74, 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, 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, - 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, 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, + 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, 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, 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, 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, 0x5f, 0x0a, 0x14, 0x50, 0x6c, 0x61, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x63, - 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, - 0x6c, 0x61, 0x6e, 0x12, 0x60, 0x0a, 0x15, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x2e, 0x6d, - 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x12, 0x62, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x53, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x28, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x75, - 0x6d, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x12, 0x62, 0x0a, 0x16, 0x43, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, - 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, - 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x12, 0x62, 0x0a, - 0x16, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x12, 0x28, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, - 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, - 0x62, 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, + 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, 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, 0x5f, 0x0a, 0x14, 0x50, 0x6c, 0x61, 0x6e, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x26, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, 0x61, + 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x60, 0x0a, 0x15, 0x41, 0x70, 0x70, + 0x6c, 0x79, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x27, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x70, 0x70, 0x6c, 0x79, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x65, + 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, + 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x12, 0x62, 0x0a, 0x16, 0x52, + 0x65, 0x73, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, + 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x12, + 0x62, 0x0a, 0x16, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x2e, 0x6d, 0x65, 0x63, 0x61, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x4a, 0x6f, 0x62, 0x12, 0x62, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x12, 0x28, 0x2e, + 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x65, 0x63, 0x61, 0x74, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x69, 0x67, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 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, 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, 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, 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, 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, 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, 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, + 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, 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, 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, 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, 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, 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, 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, + 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, 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, 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, + 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, 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, + 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, 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, 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, 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, + 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, 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, 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 ( @@ -16941,473 +17408,483 @@ func file_mecatl_v1_harness_proto_rawDescGZIP() []byte { return file_mecatl_v1_harness_proto_rawDescData } -var file_mecatl_v1_harness_proto_enumTypes = make([]protoimpl.EnumInfo, 7) -var file_mecatl_v1_harness_proto_msgTypes = make([]protoimpl.MessageInfo, 189) +var file_mecatl_v1_harness_proto_enumTypes = make([]protoimpl.EnumInfo, 8) +var file_mecatl_v1_harness_proto_msgTypes = make([]protoimpl.MessageInfo, 193) var file_mecatl_v1_harness_proto_goTypes = []any{ (PermissionMode)(0), // 0: mecatl.v1.PermissionMode (ApprovalVerdict)(0), // 1: mecatl.v1.ApprovalVerdict - (HookDecision)(0), // 2: mecatl.v1.HookDecision - (SoulProvenance)(0), // 3: mecatl.v1.SoulProvenance - (TeamMemberStopReason)(0), // 4: mecatl.v1.TeamMemberStopReason - (Content_Kind)(0), // 5: mecatl.v1.Content.Kind - (ContentBlock_Kind)(0), // 6: mecatl.v1.ContentBlock.Kind - (*Limits)(nil), // 7: mecatl.v1.Limits - (*CreateSessionRequest)(nil), // 8: mecatl.v1.CreateSessionRequest - (*ServerCapabilities)(nil), // 9: mecatl.v1.ServerCapabilities - (*CreateSessionResponse)(nil), // 10: mecatl.v1.CreateSessionResponse - (*ResolvedModel)(nil), // 11: mecatl.v1.ResolvedModel - (*SessionCapabilities)(nil), // 12: mecatl.v1.SessionCapabilities - (*GetSessionRequest)(nil), // 13: mecatl.v1.GetSessionRequest - (*GetSessionResponse)(nil), // 14: mecatl.v1.GetSessionResponse - (*GetSessionTranscriptRequest)(nil), // 15: mecatl.v1.GetSessionTranscriptRequest - (*ActivityReplayStatus)(nil), // 16: mecatl.v1.ActivityReplayStatus - (*GetSessionTranscriptResponse)(nil), // 17: mecatl.v1.GetSessionTranscriptResponse - (*StreamSessionEventsRequest)(nil), // 18: mecatl.v1.StreamSessionEventsRequest - (*StreamSessionLiveRequest)(nil), // 19: mecatl.v1.StreamSessionLiveRequest - (*ListSessionsRequest)(nil), // 20: mecatl.v1.ListSessionsRequest - (*Principal)(nil), // 21: mecatl.v1.Principal - (*SessionSummary)(nil), // 22: mecatl.v1.SessionSummary - (*SessionRelationship)(nil), // 23: mecatl.v1.SessionRelationship - (*SessionInventoryCapabilities)(nil), // 24: mecatl.v1.SessionInventoryCapabilities - (*SessionInventoryActionReasons)(nil), // 25: mecatl.v1.SessionInventoryActionReasons - (*ListSessionsResponse)(nil), // 26: mecatl.v1.ListSessionsResponse - (*GetStorageHealthRequest)(nil), // 27: mecatl.v1.GetStorageHealthRequest - (*RetentionPolicy)(nil), // 28: mecatl.v1.RetentionPolicy - (*GetStorageHealthResponse)(nil), // 29: mecatl.v1.GetStorageHealthResponse - (*PlanSessionMigrationRequest)(nil), // 30: mecatl.v1.PlanSessionMigrationRequest - (*SessionMigrationPlan)(nil), // 31: mecatl.v1.SessionMigrationPlan - (*ApplySessionMigrationRequest)(nil), // 32: mecatl.v1.ApplySessionMigrationRequest - (*ResumeSessionMigrationRequest)(nil), // 33: mecatl.v1.ResumeSessionMigrationRequest - (*CancelSessionMigrationRequest)(nil), // 34: mecatl.v1.CancelSessionMigrationRequest - (*GetSessionMigrationJobRequest)(nil), // 35: mecatl.v1.GetSessionMigrationJobRequest - (*SessionMigrationItemError)(nil), // 36: mecatl.v1.SessionMigrationItemError - (*CleanupCounts)(nil), // 37: mecatl.v1.CleanupCounts - (*CleanupCandidate)(nil), // 38: mecatl.v1.CleanupCandidate - (*PlanSessionCleanupRequest)(nil), // 39: mecatl.v1.PlanSessionCleanupRequest - (*PlanSessionCleanupResponse)(nil), // 40: mecatl.v1.PlanSessionCleanupResponse - (*ApplySessionCleanupRequest)(nil), // 41: mecatl.v1.ApplySessionCleanupRequest - (*CancelSessionCleanupRequest)(nil), // 42: mecatl.v1.CancelSessionCleanupRequest - (*GetSessionCleanupJobRequest)(nil), // 43: mecatl.v1.GetSessionCleanupJobRequest - (*CleanupItemError)(nil), // 44: mecatl.v1.CleanupItemError - (*SessionMigrationJob)(nil), // 45: mecatl.v1.SessionMigrationJob - (*CleanupJob)(nil), // 46: mecatl.v1.CleanupJob - (*SetModeRequest)(nil), // 47: mecatl.v1.SetModeRequest - (*SetModeResponse)(nil), // 48: mecatl.v1.SetModeResponse - (*CloseSessionRequest)(nil), // 49: mecatl.v1.CloseSessionRequest - (*CloseSessionResponse)(nil), // 50: mecatl.v1.CloseSessionResponse - (*RenameSessionRequest)(nil), // 51: mecatl.v1.RenameSessionRequest - (*RenameSessionResponse)(nil), // 52: mecatl.v1.RenameSessionResponse - (*DeleteSessionRequest)(nil), // 53: mecatl.v1.DeleteSessionRequest - (*DeleteSessionResponse)(nil), // 54: mecatl.v1.DeleteSessionResponse - (*ForkSessionRequest)(nil), // 55: mecatl.v1.ForkSessionRequest - (*ForkSessionResponse)(nil), // 56: mecatl.v1.ForkSessionResponse - (*AdoptionBindings)(nil), // 57: mecatl.v1.AdoptionBindings - (*PreflightSessionAdoptionRequest)(nil), // 58: mecatl.v1.PreflightSessionAdoptionRequest - (*PreflightSessionAdoptionResponse)(nil), // 59: mecatl.v1.PreflightSessionAdoptionResponse - (*AdoptSessionRequest)(nil), // 60: mecatl.v1.AdoptSessionRequest - (*AdoptSessionResponse)(nil), // 61: mecatl.v1.AdoptSessionResponse - (*Session)(nil), // 62: mecatl.v1.Session - (*ConverseRequest)(nil), // 63: mecatl.v1.ConverseRequest - (*ConverseResponse)(nil), // 64: mecatl.v1.ConverseResponse - (*Content)(nil), // 65: mecatl.v1.Content - (*ContentBlock)(nil), // 66: mecatl.v1.ContentBlock - (*Prompt)(nil), // 67: mecatl.v1.Prompt - (*ResumeApproval)(nil), // 68: mecatl.v1.ResumeApproval - (*Cancel)(nil), // 69: mecatl.v1.Cancel - (*CancelChild)(nil), // 70: mecatl.v1.CancelChild - (*Event)(nil), // 71: mecatl.v1.Event - (*Approval)(nil), // 72: mecatl.v1.Approval - (*UserPrompt)(nil), // 73: mecatl.v1.UserPrompt - (*CompactionArchive)(nil), // 74: mecatl.v1.CompactionArchive - (*ConversationMessage)(nil), // 75: mecatl.v1.ConversationMessage - (*SchedulePayload)(nil), // 76: mecatl.v1.SchedulePayload - (*Hook)(nil), // 77: mecatl.v1.Hook - (*Subagent)(nil), // 78: mecatl.v1.Subagent - (*TeamMemberSpec)(nil), // 79: mecatl.v1.TeamMemberSpec - (*Team)(nil), // 80: mecatl.v1.Team - (*Parallel)(nil), // 81: mecatl.v1.Parallel - (*ToolCall)(nil), // 82: mecatl.v1.ToolCall - (*ToolResult)(nil), // 83: mecatl.v1.ToolResult - (*PermissionAsk)(nil), // 84: mecatl.v1.PermissionAsk - (*Result)(nil), // 85: mecatl.v1.Result - (*TurnEnd)(nil), // 86: mecatl.v1.TurnEnd - (*Usage)(nil), // 87: mecatl.v1.Usage - (*McpResource)(nil), // 88: mecatl.v1.McpResource - (*ListMcpResourcesRequest)(nil), // 89: mecatl.v1.ListMcpResourcesRequest - (*ListMcpResourcesResponse)(nil), // 90: mecatl.v1.ListMcpResourcesResponse - (*McpResourceContents)(nil), // 91: mecatl.v1.McpResourceContents - (*ReadMcpResourceRequest)(nil), // 92: mecatl.v1.ReadMcpResourceRequest - (*ReadMcpResourceResponse)(nil), // 93: mecatl.v1.ReadMcpResourceResponse - (*McpPromptArgument)(nil), // 94: mecatl.v1.McpPromptArgument - (*McpPrompt)(nil), // 95: mecatl.v1.McpPrompt - (*ListMcpPromptsRequest)(nil), // 96: mecatl.v1.ListMcpPromptsRequest - (*ListMcpPromptsResponse)(nil), // 97: mecatl.v1.ListMcpPromptsResponse - (*GetMcpPromptRequest)(nil), // 98: mecatl.v1.GetMcpPromptRequest - (*McpPromptMessage)(nil), // 99: mecatl.v1.McpPromptMessage - (*GetMcpPromptResponse)(nil), // 100: mecatl.v1.GetMcpPromptResponse - (*McpServerInfo)(nil), // 101: mecatl.v1.McpServerInfo - (*McpSource)(nil), // 102: mecatl.v1.McpSource - (*ListMcpSourcesRequest)(nil), // 103: mecatl.v1.ListMcpSourcesRequest - (*ListMcpSourcesResponse)(nil), // 104: mecatl.v1.ListMcpSourcesResponse - (*ListToolHiveGroupsRequest)(nil), // 105: mecatl.v1.ListToolHiveGroupsRequest - (*ListToolHiveGroupsResponse)(nil), // 106: mecatl.v1.ListToolHiveGroupsResponse - (*AgentInfo)(nil), // 107: mecatl.v1.AgentInfo - (*ListAgentsRequest)(nil), // 108: mecatl.v1.ListAgentsRequest - (*ListAgentsResponse)(nil), // 109: mecatl.v1.ListAgentsResponse - (*Command)(nil), // 110: mecatl.v1.Command - (*ListCommandsRequest)(nil), // 111: mecatl.v1.ListCommandsRequest - (*ListCommandsResponse)(nil), // 112: mecatl.v1.ListCommandsResponse - (*Worktree)(nil), // 113: mecatl.v1.Worktree - (*ListWorktreesRequest)(nil), // 114: mecatl.v1.ListWorktreesRequest - (*ListWorktreesResponse)(nil), // 115: mecatl.v1.ListWorktreesResponse - (*SkillInfo)(nil), // 116: mecatl.v1.SkillInfo - (*ListSkillsRequest)(nil), // 117: mecatl.v1.ListSkillsRequest - (*ListSkillsResponse)(nil), // 118: mecatl.v1.ListSkillsResponse - (*SoulInfo)(nil), // 119: mecatl.v1.SoulInfo - (*GetSoulRequest)(nil), // 120: mecatl.v1.GetSoulRequest - (*GetSoulResponse)(nil), // 121: mecatl.v1.GetSoulResponse - (*UserModelEntry)(nil), // 122: mecatl.v1.UserModelEntry - (*GetUserModelRequest)(nil), // 123: mecatl.v1.GetUserModelRequest - (*UserModelRevision)(nil), // 124: mecatl.v1.UserModelRevision - (*UserModelDetail)(nil), // 125: mecatl.v1.UserModelDetail - (*GetUserModelResponse)(nil), // 126: mecatl.v1.GetUserModelResponse - (*ModelInfo)(nil), // 127: mecatl.v1.ModelInfo - (*ListModelsRequest)(nil), // 128: mecatl.v1.ListModelsRequest - (*ProviderStatus)(nil), // 129: mecatl.v1.ProviderStatus - (*ListModelsResponse)(nil), // 130: mecatl.v1.ListModelsResponse - (*ReflectSessionRequest)(nil), // 131: mecatl.v1.ReflectSessionRequest - (*ReflectionReceipt)(nil), // 132: mecatl.v1.ReflectionReceipt - (*ReflectSessionResponse)(nil), // 133: mecatl.v1.ReflectSessionResponse - (*ListLearningProposalsRequest)(nil), // 134: mecatl.v1.ListLearningProposalsRequest - (*LearningEvidenceRef)(nil), // 135: mecatl.v1.LearningEvidenceRef - (*LearningDecision)(nil), // 136: mecatl.v1.LearningDecision - (*LearningPromotionReceipt)(nil), // 137: mecatl.v1.LearningPromotionReceipt - (*LearningProposal)(nil), // 138: mecatl.v1.LearningProposal - (*ListLearningProposalsResponse)(nil), // 139: mecatl.v1.ListLearningProposalsResponse - (*GetLearningProposalRequest)(nil), // 140: mecatl.v1.GetLearningProposalRequest - (*GetLearningProposalResponse)(nil), // 141: mecatl.v1.GetLearningProposalResponse - (*DecideLearningProposalRequest)(nil), // 142: mecatl.v1.DecideLearningProposalRequest - (*DecideLearningProposalResponse)(nil), // 143: mecatl.v1.DecideLearningProposalResponse - (*UndoLearningPromotionRequest)(nil), // 144: mecatl.v1.UndoLearningPromotionRequest - (*UndoLearningPromotionResponse)(nil), // 145: mecatl.v1.UndoLearningPromotionResponse - (*LearnedSkillVersion)(nil), // 146: mecatl.v1.LearnedSkillVersion - (*SkillEvaluation)(nil), // 147: mecatl.v1.SkillEvaluation - (*SkillChangeReceipt)(nil), // 148: mecatl.v1.SkillChangeReceipt - (*ListLearnedSkillsRequest)(nil), // 149: mecatl.v1.ListLearnedSkillsRequest - (*ListLearnedSkillsResponse)(nil), // 150: mecatl.v1.ListLearnedSkillsResponse - (*GetLearnedSkillRequest)(nil), // 151: mecatl.v1.GetLearnedSkillRequest - (*GetLearnedSkillResponse)(nil), // 152: mecatl.v1.GetLearnedSkillResponse - (*DiffLearnedSkillVersionsRequest)(nil), // 153: mecatl.v1.DiffLearnedSkillVersionsRequest - (*DiffLearnedSkillVersionsResponse)(nil), // 154: mecatl.v1.DiffLearnedSkillVersionsResponse - (*MutateLearnedSkillRequest)(nil), // 155: mecatl.v1.MutateLearnedSkillRequest - (*MutateLearnedSkillResponse)(nil), // 156: mecatl.v1.MutateLearnedSkillResponse - (*RollbackLearnedSkillRequest)(nil), // 157: mecatl.v1.RollbackLearnedSkillRequest - (*ListSkillChangesRequest)(nil), // 158: mecatl.v1.ListSkillChangesRequest - (*ListSkillChangesResponse)(nil), // 159: mecatl.v1.ListSkillChangesResponse - (*CreateTeamRequest)(nil), // 160: mecatl.v1.CreateTeamRequest - (*CreateTeamResponse)(nil), // 161: mecatl.v1.CreateTeamResponse - (*TeammateSpec)(nil), // 162: mecatl.v1.TeammateSpec - (*SpawnTeammateRequest)(nil), // 163: mecatl.v1.SpawnTeammateRequest - (*SpawnTeammateResponse)(nil), // 164: mecatl.v1.SpawnTeammateResponse - (*SendTeammateMessageRequest)(nil), // 165: mecatl.v1.SendTeammateMessageRequest - (*SendTeammateMessageResponse)(nil), // 166: mecatl.v1.SendTeammateMessageResponse - (*CancelTeammateRequest)(nil), // 167: mecatl.v1.CancelTeammateRequest - (*CancelTeammateResponse)(nil), // 168: mecatl.v1.CancelTeammateResponse - (*RunTeamRequest)(nil), // 169: mecatl.v1.RunTeamRequest - (*TeamEvent)(nil), // 170: mecatl.v1.TeamEvent - (*TeamOutcome)(nil), // 171: mecatl.v1.TeamOutcome - (*ListTeamRequest)(nil), // 172: mecatl.v1.ListTeamRequest - (*ListTeamResponse)(nil), // 173: mecatl.v1.ListTeamResponse - (*TeamMember)(nil), // 174: mecatl.v1.TeamMember - (*TeamTask)(nil), // 175: mecatl.v1.TeamTask - (*TeamFinding)(nil), // 176: mecatl.v1.TeamFinding - (*TeamMemberDisposition)(nil), // 177: mecatl.v1.TeamMemberDisposition - (*CleanupTeamRequest)(nil), // 178: mecatl.v1.CleanupTeamRequest - (*CleanupTeamResponse)(nil), // 179: mecatl.v1.CleanupTeamResponse - (*ApprovePlanRequest)(nil), // 180: mecatl.v1.ApprovePlanRequest - (*ManualDreamCapabilities)(nil), // 181: mecatl.v1.ManualDreamCapabilities - (*DreamTargetCapability)(nil), // 182: mecatl.v1.DreamTargetCapability - (*GenerateDreamPlanRequest)(nil), // 183: mecatl.v1.GenerateDreamPlanRequest - (*GenerateDreamPlanResponse)(nil), // 184: mecatl.v1.GenerateDreamPlanResponse - (*DecideDreamPlanRequest)(nil), // 185: mecatl.v1.DecideDreamPlanRequest - (*DecideDreamPlanResponse)(nil), // 186: mecatl.v1.DecideDreamPlanResponse - (*DreamReviewPlan)(nil), // 187: mecatl.v1.DreamReviewPlan - (*DreamOperation)(nil), // 188: mecatl.v1.DreamOperation - (*DreamParticipant)(nil), // 189: mecatl.v1.DreamParticipant - (*DreamReplacement)(nil), // 190: mecatl.v1.DreamReplacement - (*DreamReceipt)(nil), // 191: mecatl.v1.DreamReceipt - nil, // 192: mecatl.v1.CleanupCounts.ByKindEntry - nil, // 193: mecatl.v1.CleanupCounts.ByStateEntry - nil, // 194: mecatl.v1.CleanupCounts.ByReasonEntry - nil, // 195: mecatl.v1.GetMcpPromptRequest.ArgumentsEntry - (*timestamppb.Timestamp)(nil), // 196: google.protobuf.Timestamp + (SteerOutcome)(0), // 2: mecatl.v1.SteerOutcome + (HookDecision)(0), // 3: mecatl.v1.HookDecision + (SoulProvenance)(0), // 4: mecatl.v1.SoulProvenance + (TeamMemberStopReason)(0), // 5: mecatl.v1.TeamMemberStopReason + (Content_Kind)(0), // 6: mecatl.v1.Content.Kind + (ContentBlock_Kind)(0), // 7: mecatl.v1.ContentBlock.Kind + (*Limits)(nil), // 8: mecatl.v1.Limits + (*CreateSessionRequest)(nil), // 9: mecatl.v1.CreateSessionRequest + (*ServerCapabilities)(nil), // 10: mecatl.v1.ServerCapabilities + (*CreateSessionResponse)(nil), // 11: mecatl.v1.CreateSessionResponse + (*ResolvedModel)(nil), // 12: mecatl.v1.ResolvedModel + (*SessionCapabilities)(nil), // 13: mecatl.v1.SessionCapabilities + (*GetSessionRequest)(nil), // 14: mecatl.v1.GetSessionRequest + (*GetSessionResponse)(nil), // 15: mecatl.v1.GetSessionResponse + (*GetSessionTranscriptRequest)(nil), // 16: mecatl.v1.GetSessionTranscriptRequest + (*ActivityReplayStatus)(nil), // 17: mecatl.v1.ActivityReplayStatus + (*GetSessionTranscriptResponse)(nil), // 18: mecatl.v1.GetSessionTranscriptResponse + (*StreamSessionEventsRequest)(nil), // 19: mecatl.v1.StreamSessionEventsRequest + (*StreamSessionLiveRequest)(nil), // 20: mecatl.v1.StreamSessionLiveRequest + (*ListSessionsRequest)(nil), // 21: mecatl.v1.ListSessionsRequest + (*Principal)(nil), // 22: mecatl.v1.Principal + (*SessionSummary)(nil), // 23: mecatl.v1.SessionSummary + (*SessionRelationship)(nil), // 24: mecatl.v1.SessionRelationship + (*SessionInventoryCapabilities)(nil), // 25: mecatl.v1.SessionInventoryCapabilities + (*SessionInventoryActionReasons)(nil), // 26: mecatl.v1.SessionInventoryActionReasons + (*ListSessionsResponse)(nil), // 27: mecatl.v1.ListSessionsResponse + (*GetStorageHealthRequest)(nil), // 28: mecatl.v1.GetStorageHealthRequest + (*RetentionPolicy)(nil), // 29: mecatl.v1.RetentionPolicy + (*GetStorageHealthResponse)(nil), // 30: mecatl.v1.GetStorageHealthResponse + (*PlanSessionMigrationRequest)(nil), // 31: mecatl.v1.PlanSessionMigrationRequest + (*SessionMigrationPlan)(nil), // 32: mecatl.v1.SessionMigrationPlan + (*ApplySessionMigrationRequest)(nil), // 33: mecatl.v1.ApplySessionMigrationRequest + (*ResumeSessionMigrationRequest)(nil), // 34: mecatl.v1.ResumeSessionMigrationRequest + (*CancelSessionMigrationRequest)(nil), // 35: mecatl.v1.CancelSessionMigrationRequest + (*GetSessionMigrationJobRequest)(nil), // 36: mecatl.v1.GetSessionMigrationJobRequest + (*SessionMigrationItemError)(nil), // 37: mecatl.v1.SessionMigrationItemError + (*CleanupCounts)(nil), // 38: mecatl.v1.CleanupCounts + (*CleanupCandidate)(nil), // 39: mecatl.v1.CleanupCandidate + (*PlanSessionCleanupRequest)(nil), // 40: mecatl.v1.PlanSessionCleanupRequest + (*PlanSessionCleanupResponse)(nil), // 41: mecatl.v1.PlanSessionCleanupResponse + (*ApplySessionCleanupRequest)(nil), // 42: mecatl.v1.ApplySessionCleanupRequest + (*CancelSessionCleanupRequest)(nil), // 43: mecatl.v1.CancelSessionCleanupRequest + (*GetSessionCleanupJobRequest)(nil), // 44: mecatl.v1.GetSessionCleanupJobRequest + (*CleanupItemError)(nil), // 45: mecatl.v1.CleanupItemError + (*SessionMigrationJob)(nil), // 46: mecatl.v1.SessionMigrationJob + (*CleanupJob)(nil), // 47: mecatl.v1.CleanupJob + (*SetModeRequest)(nil), // 48: mecatl.v1.SetModeRequest + (*SetModeResponse)(nil), // 49: mecatl.v1.SetModeResponse + (*CloseSessionRequest)(nil), // 50: mecatl.v1.CloseSessionRequest + (*CloseSessionResponse)(nil), // 51: mecatl.v1.CloseSessionResponse + (*RenameSessionRequest)(nil), // 52: mecatl.v1.RenameSessionRequest + (*RenameSessionResponse)(nil), // 53: mecatl.v1.RenameSessionResponse + (*DeleteSessionRequest)(nil), // 54: mecatl.v1.DeleteSessionRequest + (*DeleteSessionResponse)(nil), // 55: mecatl.v1.DeleteSessionResponse + (*ForkSessionRequest)(nil), // 56: mecatl.v1.ForkSessionRequest + (*ForkSessionResponse)(nil), // 57: mecatl.v1.ForkSessionResponse + (*AdoptionBindings)(nil), // 58: mecatl.v1.AdoptionBindings + (*PreflightSessionAdoptionRequest)(nil), // 59: mecatl.v1.PreflightSessionAdoptionRequest + (*PreflightSessionAdoptionResponse)(nil), // 60: mecatl.v1.PreflightSessionAdoptionResponse + (*AdoptSessionRequest)(nil), // 61: mecatl.v1.AdoptSessionRequest + (*AdoptSessionResponse)(nil), // 62: mecatl.v1.AdoptSessionResponse + (*Session)(nil), // 63: mecatl.v1.Session + (*ConverseRequest)(nil), // 64: mecatl.v1.ConverseRequest + (*ConverseResponse)(nil), // 65: mecatl.v1.ConverseResponse + (*Content)(nil), // 66: mecatl.v1.Content + (*ContentBlock)(nil), // 67: mecatl.v1.ContentBlock + (*Prompt)(nil), // 68: mecatl.v1.Prompt + (*ResumeApproval)(nil), // 69: mecatl.v1.ResumeApproval + (*Cancel)(nil), // 70: mecatl.v1.Cancel + (*CancelChild)(nil), // 71: mecatl.v1.CancelChild + (*Steer)(nil), // 72: mecatl.v1.Steer + (*SteerCancel)(nil), // 73: mecatl.v1.SteerCancel + (*Event)(nil), // 74: mecatl.v1.Event + (*SteerEcho)(nil), // 75: mecatl.v1.SteerEcho + (*SteerAck)(nil), // 76: mecatl.v1.SteerAck + (*Approval)(nil), // 77: mecatl.v1.Approval + (*UserPrompt)(nil), // 78: mecatl.v1.UserPrompt + (*CompactionArchive)(nil), // 79: mecatl.v1.CompactionArchive + (*ConversationMessage)(nil), // 80: mecatl.v1.ConversationMessage + (*SchedulePayload)(nil), // 81: mecatl.v1.SchedulePayload + (*Hook)(nil), // 82: mecatl.v1.Hook + (*Subagent)(nil), // 83: mecatl.v1.Subagent + (*TeamMemberSpec)(nil), // 84: mecatl.v1.TeamMemberSpec + (*Team)(nil), // 85: mecatl.v1.Team + (*Parallel)(nil), // 86: mecatl.v1.Parallel + (*ToolCall)(nil), // 87: mecatl.v1.ToolCall + (*ToolResult)(nil), // 88: mecatl.v1.ToolResult + (*PermissionAsk)(nil), // 89: mecatl.v1.PermissionAsk + (*Result)(nil), // 90: mecatl.v1.Result + (*TurnEnd)(nil), // 91: mecatl.v1.TurnEnd + (*Usage)(nil), // 92: mecatl.v1.Usage + (*McpResource)(nil), // 93: mecatl.v1.McpResource + (*ListMcpResourcesRequest)(nil), // 94: mecatl.v1.ListMcpResourcesRequest + (*ListMcpResourcesResponse)(nil), // 95: mecatl.v1.ListMcpResourcesResponse + (*McpResourceContents)(nil), // 96: mecatl.v1.McpResourceContents + (*ReadMcpResourceRequest)(nil), // 97: mecatl.v1.ReadMcpResourceRequest + (*ReadMcpResourceResponse)(nil), // 98: mecatl.v1.ReadMcpResourceResponse + (*McpPromptArgument)(nil), // 99: mecatl.v1.McpPromptArgument + (*McpPrompt)(nil), // 100: mecatl.v1.McpPrompt + (*ListMcpPromptsRequest)(nil), // 101: mecatl.v1.ListMcpPromptsRequest + (*ListMcpPromptsResponse)(nil), // 102: mecatl.v1.ListMcpPromptsResponse + (*GetMcpPromptRequest)(nil), // 103: mecatl.v1.GetMcpPromptRequest + (*McpPromptMessage)(nil), // 104: mecatl.v1.McpPromptMessage + (*GetMcpPromptResponse)(nil), // 105: mecatl.v1.GetMcpPromptResponse + (*McpServerInfo)(nil), // 106: mecatl.v1.McpServerInfo + (*McpSource)(nil), // 107: mecatl.v1.McpSource + (*ListMcpSourcesRequest)(nil), // 108: mecatl.v1.ListMcpSourcesRequest + (*ListMcpSourcesResponse)(nil), // 109: mecatl.v1.ListMcpSourcesResponse + (*ListToolHiveGroupsRequest)(nil), // 110: mecatl.v1.ListToolHiveGroupsRequest + (*ListToolHiveGroupsResponse)(nil), // 111: mecatl.v1.ListToolHiveGroupsResponse + (*AgentInfo)(nil), // 112: mecatl.v1.AgentInfo + (*ListAgentsRequest)(nil), // 113: mecatl.v1.ListAgentsRequest + (*ListAgentsResponse)(nil), // 114: mecatl.v1.ListAgentsResponse + (*Command)(nil), // 115: mecatl.v1.Command + (*ListCommandsRequest)(nil), // 116: mecatl.v1.ListCommandsRequest + (*ListCommandsResponse)(nil), // 117: mecatl.v1.ListCommandsResponse + (*Worktree)(nil), // 118: mecatl.v1.Worktree + (*ListWorktreesRequest)(nil), // 119: mecatl.v1.ListWorktreesRequest + (*ListWorktreesResponse)(nil), // 120: mecatl.v1.ListWorktreesResponse + (*SkillInfo)(nil), // 121: mecatl.v1.SkillInfo + (*ListSkillsRequest)(nil), // 122: mecatl.v1.ListSkillsRequest + (*ListSkillsResponse)(nil), // 123: mecatl.v1.ListSkillsResponse + (*SoulInfo)(nil), // 124: mecatl.v1.SoulInfo + (*GetSoulRequest)(nil), // 125: mecatl.v1.GetSoulRequest + (*GetSoulResponse)(nil), // 126: mecatl.v1.GetSoulResponse + (*UserModelEntry)(nil), // 127: mecatl.v1.UserModelEntry + (*GetUserModelRequest)(nil), // 128: mecatl.v1.GetUserModelRequest + (*UserModelRevision)(nil), // 129: mecatl.v1.UserModelRevision + (*UserModelDetail)(nil), // 130: mecatl.v1.UserModelDetail + (*GetUserModelResponse)(nil), // 131: mecatl.v1.GetUserModelResponse + (*ModelInfo)(nil), // 132: mecatl.v1.ModelInfo + (*ListModelsRequest)(nil), // 133: mecatl.v1.ListModelsRequest + (*ProviderStatus)(nil), // 134: mecatl.v1.ProviderStatus + (*ListModelsResponse)(nil), // 135: mecatl.v1.ListModelsResponse + (*ReflectSessionRequest)(nil), // 136: mecatl.v1.ReflectSessionRequest + (*ReflectionReceipt)(nil), // 137: mecatl.v1.ReflectionReceipt + (*ReflectSessionResponse)(nil), // 138: mecatl.v1.ReflectSessionResponse + (*ListLearningProposalsRequest)(nil), // 139: mecatl.v1.ListLearningProposalsRequest + (*LearningEvidenceRef)(nil), // 140: mecatl.v1.LearningEvidenceRef + (*LearningDecision)(nil), // 141: mecatl.v1.LearningDecision + (*LearningPromotionReceipt)(nil), // 142: mecatl.v1.LearningPromotionReceipt + (*LearningProposal)(nil), // 143: mecatl.v1.LearningProposal + (*ListLearningProposalsResponse)(nil), // 144: mecatl.v1.ListLearningProposalsResponse + (*GetLearningProposalRequest)(nil), // 145: mecatl.v1.GetLearningProposalRequest + (*GetLearningProposalResponse)(nil), // 146: mecatl.v1.GetLearningProposalResponse + (*DecideLearningProposalRequest)(nil), // 147: mecatl.v1.DecideLearningProposalRequest + (*DecideLearningProposalResponse)(nil), // 148: mecatl.v1.DecideLearningProposalResponse + (*UndoLearningPromotionRequest)(nil), // 149: mecatl.v1.UndoLearningPromotionRequest + (*UndoLearningPromotionResponse)(nil), // 150: mecatl.v1.UndoLearningPromotionResponse + (*LearnedSkillVersion)(nil), // 151: mecatl.v1.LearnedSkillVersion + (*SkillEvaluation)(nil), // 152: mecatl.v1.SkillEvaluation + (*SkillChangeReceipt)(nil), // 153: mecatl.v1.SkillChangeReceipt + (*ListLearnedSkillsRequest)(nil), // 154: mecatl.v1.ListLearnedSkillsRequest + (*ListLearnedSkillsResponse)(nil), // 155: mecatl.v1.ListLearnedSkillsResponse + (*GetLearnedSkillRequest)(nil), // 156: mecatl.v1.GetLearnedSkillRequest + (*GetLearnedSkillResponse)(nil), // 157: mecatl.v1.GetLearnedSkillResponse + (*DiffLearnedSkillVersionsRequest)(nil), // 158: mecatl.v1.DiffLearnedSkillVersionsRequest + (*DiffLearnedSkillVersionsResponse)(nil), // 159: mecatl.v1.DiffLearnedSkillVersionsResponse + (*MutateLearnedSkillRequest)(nil), // 160: mecatl.v1.MutateLearnedSkillRequest + (*MutateLearnedSkillResponse)(nil), // 161: mecatl.v1.MutateLearnedSkillResponse + (*RollbackLearnedSkillRequest)(nil), // 162: mecatl.v1.RollbackLearnedSkillRequest + (*ListSkillChangesRequest)(nil), // 163: mecatl.v1.ListSkillChangesRequest + (*ListSkillChangesResponse)(nil), // 164: mecatl.v1.ListSkillChangesResponse + (*CreateTeamRequest)(nil), // 165: mecatl.v1.CreateTeamRequest + (*CreateTeamResponse)(nil), // 166: mecatl.v1.CreateTeamResponse + (*TeammateSpec)(nil), // 167: mecatl.v1.TeammateSpec + (*SpawnTeammateRequest)(nil), // 168: mecatl.v1.SpawnTeammateRequest + (*SpawnTeammateResponse)(nil), // 169: mecatl.v1.SpawnTeammateResponse + (*SendTeammateMessageRequest)(nil), // 170: mecatl.v1.SendTeammateMessageRequest + (*SendTeammateMessageResponse)(nil), // 171: mecatl.v1.SendTeammateMessageResponse + (*CancelTeammateRequest)(nil), // 172: mecatl.v1.CancelTeammateRequest + (*CancelTeammateResponse)(nil), // 173: mecatl.v1.CancelTeammateResponse + (*RunTeamRequest)(nil), // 174: mecatl.v1.RunTeamRequest + (*TeamEvent)(nil), // 175: mecatl.v1.TeamEvent + (*TeamOutcome)(nil), // 176: mecatl.v1.TeamOutcome + (*ListTeamRequest)(nil), // 177: mecatl.v1.ListTeamRequest + (*ListTeamResponse)(nil), // 178: mecatl.v1.ListTeamResponse + (*TeamMember)(nil), // 179: mecatl.v1.TeamMember + (*TeamTask)(nil), // 180: mecatl.v1.TeamTask + (*TeamFinding)(nil), // 181: mecatl.v1.TeamFinding + (*TeamMemberDisposition)(nil), // 182: mecatl.v1.TeamMemberDisposition + (*CleanupTeamRequest)(nil), // 183: mecatl.v1.CleanupTeamRequest + (*CleanupTeamResponse)(nil), // 184: mecatl.v1.CleanupTeamResponse + (*ApprovePlanRequest)(nil), // 185: mecatl.v1.ApprovePlanRequest + (*ManualDreamCapabilities)(nil), // 186: mecatl.v1.ManualDreamCapabilities + (*DreamTargetCapability)(nil), // 187: mecatl.v1.DreamTargetCapability + (*GenerateDreamPlanRequest)(nil), // 188: mecatl.v1.GenerateDreamPlanRequest + (*GenerateDreamPlanResponse)(nil), // 189: mecatl.v1.GenerateDreamPlanResponse + (*DecideDreamPlanRequest)(nil), // 190: mecatl.v1.DecideDreamPlanRequest + (*DecideDreamPlanResponse)(nil), // 191: mecatl.v1.DecideDreamPlanResponse + (*DreamReviewPlan)(nil), // 192: mecatl.v1.DreamReviewPlan + (*DreamOperation)(nil), // 193: mecatl.v1.DreamOperation + (*DreamParticipant)(nil), // 194: mecatl.v1.DreamParticipant + (*DreamReplacement)(nil), // 195: mecatl.v1.DreamReplacement + (*DreamReceipt)(nil), // 196: mecatl.v1.DreamReceipt + nil, // 197: mecatl.v1.CleanupCounts.ByKindEntry + nil, // 198: mecatl.v1.CleanupCounts.ByStateEntry + nil, // 199: mecatl.v1.CleanupCounts.ByReasonEntry + nil, // 200: mecatl.v1.GetMcpPromptRequest.ArgumentsEntry + (*timestamppb.Timestamp)(nil), // 201: google.protobuf.Timestamp } var file_mecatl_v1_harness_proto_depIdxs = []int32{ 0, // 0: mecatl.v1.CreateSessionRequest.mode:type_name -> mecatl.v1.PermissionMode - 7, // 1: mecatl.v1.CreateSessionRequest.limits:type_name -> mecatl.v1.Limits - 181, // 2: mecatl.v1.ServerCapabilities.manual_dream:type_name -> mecatl.v1.ManualDreamCapabilities - 9, // 3: mecatl.v1.CreateSessionResponse.capabilities:type_name -> mecatl.v1.ServerCapabilities - 12, // 4: mecatl.v1.CreateSessionResponse.session_capabilities:type_name -> mecatl.v1.SessionCapabilities - 11, // 5: mecatl.v1.CreateSessionResponse.resolved_model:type_name -> mecatl.v1.ResolvedModel - 62, // 6: mecatl.v1.GetSessionResponse.session:type_name -> mecatl.v1.Session - 75, // 7: mecatl.v1.GetSessionTranscriptResponse.messages:type_name -> mecatl.v1.ConversationMessage - 16, // 8: mecatl.v1.GetSessionTranscriptResponse.activity:type_name -> mecatl.v1.ActivityReplayStatus - 23, // 9: mecatl.v1.GetSessionTranscriptResponse.relationship:type_name -> mecatl.v1.SessionRelationship - 21, // 10: mecatl.v1.SessionSummary.owner:type_name -> mecatl.v1.Principal - 23, // 11: mecatl.v1.SessionSummary.relationship:type_name -> mecatl.v1.SessionRelationship - 24, // 12: mecatl.v1.SessionSummary.capabilities:type_name -> mecatl.v1.SessionInventoryCapabilities - 25, // 13: mecatl.v1.SessionInventoryCapabilities.reasons:type_name -> mecatl.v1.SessionInventoryActionReasons - 22, // 14: mecatl.v1.ListSessionsResponse.sessions:type_name -> mecatl.v1.SessionSummary - 28, // 15: mecatl.v1.GetStorageHealthResponse.policy:type_name -> mecatl.v1.RetentionPolicy - 192, // 16: mecatl.v1.CleanupCounts.by_kind:type_name -> mecatl.v1.CleanupCounts.ByKindEntry - 193, // 17: mecatl.v1.CleanupCounts.by_state:type_name -> mecatl.v1.CleanupCounts.ByStateEntry - 194, // 18: mecatl.v1.CleanupCounts.by_reason:type_name -> mecatl.v1.CleanupCounts.ByReasonEntry - 38, // 19: mecatl.v1.PlanSessionCleanupResponse.eligible:type_name -> mecatl.v1.CleanupCandidate - 37, // 20: mecatl.v1.PlanSessionCleanupResponse.protected:type_name -> mecatl.v1.CleanupCounts - 37, // 21: mecatl.v1.PlanSessionCleanupResponse.eligible_counts:type_name -> mecatl.v1.CleanupCounts - 36, // 22: mecatl.v1.SessionMigrationJob.errors:type_name -> mecatl.v1.SessionMigrationItemError - 44, // 23: mecatl.v1.CleanupJob.errors:type_name -> mecatl.v1.CleanupItemError + 8, // 1: mecatl.v1.CreateSessionRequest.limits:type_name -> mecatl.v1.Limits + 186, // 2: mecatl.v1.ServerCapabilities.manual_dream:type_name -> mecatl.v1.ManualDreamCapabilities + 10, // 3: mecatl.v1.CreateSessionResponse.capabilities:type_name -> mecatl.v1.ServerCapabilities + 13, // 4: mecatl.v1.CreateSessionResponse.session_capabilities:type_name -> mecatl.v1.SessionCapabilities + 12, // 5: mecatl.v1.CreateSessionResponse.resolved_model:type_name -> mecatl.v1.ResolvedModel + 63, // 6: mecatl.v1.GetSessionResponse.session:type_name -> mecatl.v1.Session + 80, // 7: mecatl.v1.GetSessionTranscriptResponse.messages:type_name -> mecatl.v1.ConversationMessage + 17, // 8: mecatl.v1.GetSessionTranscriptResponse.activity:type_name -> mecatl.v1.ActivityReplayStatus + 24, // 9: mecatl.v1.GetSessionTranscriptResponse.relationship:type_name -> mecatl.v1.SessionRelationship + 22, // 10: mecatl.v1.SessionSummary.owner:type_name -> mecatl.v1.Principal + 24, // 11: mecatl.v1.SessionSummary.relationship:type_name -> mecatl.v1.SessionRelationship + 25, // 12: mecatl.v1.SessionSummary.capabilities:type_name -> mecatl.v1.SessionInventoryCapabilities + 26, // 13: mecatl.v1.SessionInventoryCapabilities.reasons:type_name -> mecatl.v1.SessionInventoryActionReasons + 23, // 14: mecatl.v1.ListSessionsResponse.sessions:type_name -> mecatl.v1.SessionSummary + 29, // 15: mecatl.v1.GetStorageHealthResponse.policy:type_name -> mecatl.v1.RetentionPolicy + 197, // 16: mecatl.v1.CleanupCounts.by_kind:type_name -> mecatl.v1.CleanupCounts.ByKindEntry + 198, // 17: mecatl.v1.CleanupCounts.by_state:type_name -> mecatl.v1.CleanupCounts.ByStateEntry + 199, // 18: mecatl.v1.CleanupCounts.by_reason:type_name -> mecatl.v1.CleanupCounts.ByReasonEntry + 39, // 19: mecatl.v1.PlanSessionCleanupResponse.eligible:type_name -> mecatl.v1.CleanupCandidate + 38, // 20: mecatl.v1.PlanSessionCleanupResponse.protected:type_name -> mecatl.v1.CleanupCounts + 38, // 21: mecatl.v1.PlanSessionCleanupResponse.eligible_counts:type_name -> mecatl.v1.CleanupCounts + 37, // 22: mecatl.v1.SessionMigrationJob.errors:type_name -> mecatl.v1.SessionMigrationItemError + 45, // 23: mecatl.v1.CleanupJob.errors:type_name -> mecatl.v1.CleanupItemError 0, // 24: mecatl.v1.SetModeRequest.mode:type_name -> mecatl.v1.PermissionMode - 62, // 25: mecatl.v1.SetModeResponse.session:type_name -> mecatl.v1.Session - 62, // 26: mecatl.v1.RenameSessionResponse.session:type_name -> mecatl.v1.Session - 57, // 27: mecatl.v1.PreflightSessionAdoptionRequest.bindings:type_name -> mecatl.v1.AdoptionBindings - 57, // 28: mecatl.v1.PreflightSessionAdoptionResponse.bindings:type_name -> mecatl.v1.AdoptionBindings - 57, // 29: mecatl.v1.AdoptSessionRequest.bindings:type_name -> mecatl.v1.AdoptionBindings - 9, // 30: mecatl.v1.AdoptSessionResponse.capabilities:type_name -> mecatl.v1.ServerCapabilities - 12, // 31: mecatl.v1.AdoptSessionResponse.session_capabilities:type_name -> mecatl.v1.SessionCapabilities - 11, // 32: mecatl.v1.AdoptSessionResponse.resolved_model:type_name -> mecatl.v1.ResolvedModel + 63, // 25: mecatl.v1.SetModeResponse.session:type_name -> mecatl.v1.Session + 63, // 26: mecatl.v1.RenameSessionResponse.session:type_name -> mecatl.v1.Session + 58, // 27: mecatl.v1.PreflightSessionAdoptionRequest.bindings:type_name -> mecatl.v1.AdoptionBindings + 58, // 28: mecatl.v1.PreflightSessionAdoptionResponse.bindings:type_name -> mecatl.v1.AdoptionBindings + 58, // 29: mecatl.v1.AdoptSessionRequest.bindings:type_name -> mecatl.v1.AdoptionBindings + 10, // 30: mecatl.v1.AdoptSessionResponse.capabilities:type_name -> mecatl.v1.ServerCapabilities + 13, // 31: mecatl.v1.AdoptSessionResponse.session_capabilities:type_name -> mecatl.v1.SessionCapabilities + 12, // 32: mecatl.v1.AdoptSessionResponse.resolved_model:type_name -> mecatl.v1.ResolvedModel 0, // 33: mecatl.v1.Session.mode:type_name -> mecatl.v1.PermissionMode - 7, // 34: mecatl.v1.Session.limits:type_name -> mecatl.v1.Limits - 11, // 35: mecatl.v1.Session.resolved_model:type_name -> mecatl.v1.ResolvedModel - 9, // 36: mecatl.v1.Session.capabilities:type_name -> mecatl.v1.ServerCapabilities - 67, // 37: mecatl.v1.ConverseRequest.prompt:type_name -> mecatl.v1.Prompt - 68, // 38: mecatl.v1.ConverseRequest.resume_approval:type_name -> mecatl.v1.ResumeApproval - 69, // 39: mecatl.v1.ConverseRequest.cancel:type_name -> mecatl.v1.Cancel - 70, // 40: mecatl.v1.ConverseRequest.cancel_child:type_name -> mecatl.v1.CancelChild - 71, // 41: mecatl.v1.ConverseResponse.event:type_name -> mecatl.v1.Event - 5, // 42: mecatl.v1.Content.kind:type_name -> mecatl.v1.Content.Kind - 6, // 43: mecatl.v1.ContentBlock.kind:type_name -> mecatl.v1.ContentBlock.Kind - 65, // 44: mecatl.v1.Prompt.parts:type_name -> mecatl.v1.Content - 1, // 45: mecatl.v1.ResumeApproval.verdict:type_name -> mecatl.v1.ApprovalVerdict - 82, // 46: mecatl.v1.Event.tool_call:type_name -> mecatl.v1.ToolCall - 83, // 47: mecatl.v1.Event.tool_result:type_name -> mecatl.v1.ToolResult - 84, // 48: mecatl.v1.Event.ask:type_name -> mecatl.v1.PermissionAsk - 85, // 49: mecatl.v1.Event.result:type_name -> mecatl.v1.Result - 87, // 50: mecatl.v1.Event.usage:type_name -> mecatl.v1.Usage - 86, // 51: mecatl.v1.Event.turn_end:type_name -> mecatl.v1.TurnEnd - 77, // 52: mecatl.v1.Event.hook:type_name -> mecatl.v1.Hook - 78, // 53: mecatl.v1.Event.subagent:type_name -> mecatl.v1.Subagent - 80, // 54: mecatl.v1.Event.team:type_name -> mecatl.v1.Team - 81, // 55: mecatl.v1.Event.parallel:type_name -> mecatl.v1.Parallel - 76, // 56: mecatl.v1.Event.schedule:type_name -> mecatl.v1.SchedulePayload - 72, // 57: mecatl.v1.Event.approval:type_name -> mecatl.v1.Approval - 73, // 58: mecatl.v1.Event.user_prompt:type_name -> mecatl.v1.UserPrompt - 74, // 59: mecatl.v1.Event.compaction_archive:type_name -> mecatl.v1.CompactionArchive - 65, // 60: mecatl.v1.UserPrompt.parts:type_name -> mecatl.v1.Content - 75, // 61: mecatl.v1.CompactionArchive.replaced:type_name -> mecatl.v1.ConversationMessage - 82, // 62: mecatl.v1.ConversationMessage.tool_calls:type_name -> mecatl.v1.ToolCall - 83, // 63: mecatl.v1.ConversationMessage.tool_result:type_name -> mecatl.v1.ToolResult - 65, // 64: mecatl.v1.ConversationMessage.parts:type_name -> mecatl.v1.Content - 2, // 65: mecatl.v1.Hook.decision:type_name -> mecatl.v1.HookDecision - 87, // 66: mecatl.v1.Subagent.usage:type_name -> mecatl.v1.Usage - 79, // 67: mecatl.v1.Team.roster:type_name -> mecatl.v1.TeamMemberSpec - 87, // 68: mecatl.v1.Team.usage:type_name -> mecatl.v1.Usage - 175, // 69: mecatl.v1.Team.tasks:type_name -> mecatl.v1.TeamTask - 176, // 70: mecatl.v1.Team.findings:type_name -> mecatl.v1.TeamFinding - 177, // 71: mecatl.v1.Team.dispositions:type_name -> mecatl.v1.TeamMemberDisposition - 87, // 72: mecatl.v1.Parallel.usage:type_name -> mecatl.v1.Usage - 66, // 73: mecatl.v1.ToolResult.blocks:type_name -> mecatl.v1.ContentBlock - 87, // 74: mecatl.v1.Result.usage:type_name -> mecatl.v1.Usage - 87, // 75: mecatl.v1.TurnEnd.usage:type_name -> mecatl.v1.Usage - 88, // 76: mecatl.v1.ListMcpResourcesResponse.resources:type_name -> mecatl.v1.McpResource - 91, // 77: mecatl.v1.ReadMcpResourceResponse.contents:type_name -> mecatl.v1.McpResourceContents - 94, // 78: mecatl.v1.McpPrompt.arguments:type_name -> mecatl.v1.McpPromptArgument - 95, // 79: mecatl.v1.ListMcpPromptsResponse.prompts:type_name -> mecatl.v1.McpPrompt - 195, // 80: mecatl.v1.GetMcpPromptRequest.arguments:type_name -> mecatl.v1.GetMcpPromptRequest.ArgumentsEntry - 99, // 81: mecatl.v1.GetMcpPromptResponse.messages:type_name -> mecatl.v1.McpPromptMessage - 101, // 82: mecatl.v1.McpSource.servers:type_name -> mecatl.v1.McpServerInfo - 102, // 83: mecatl.v1.ListMcpSourcesResponse.sources:type_name -> mecatl.v1.McpSource - 107, // 84: mecatl.v1.ListAgentsResponse.agents:type_name -> mecatl.v1.AgentInfo - 110, // 85: mecatl.v1.ListCommandsResponse.commands:type_name -> mecatl.v1.Command - 113, // 86: mecatl.v1.ListWorktreesResponse.worktrees:type_name -> mecatl.v1.Worktree - 116, // 87: mecatl.v1.ListSkillsResponse.skills:type_name -> mecatl.v1.SkillInfo - 3, // 88: mecatl.v1.SoulInfo.provenance:type_name -> mecatl.v1.SoulProvenance - 119, // 89: mecatl.v1.GetSoulResponse.soul:type_name -> mecatl.v1.SoulInfo - 196, // 90: mecatl.v1.UserModelRevision.updated_at:type_name -> google.protobuf.Timestamp - 124, // 91: mecatl.v1.UserModelDetail.current:type_name -> mecatl.v1.UserModelRevision - 124, // 92: mecatl.v1.UserModelDetail.history:type_name -> mecatl.v1.UserModelRevision - 122, // 93: mecatl.v1.GetUserModelResponse.entries:type_name -> mecatl.v1.UserModelEntry - 125, // 94: mecatl.v1.GetUserModelResponse.detail:type_name -> mecatl.v1.UserModelDetail - 127, // 95: mecatl.v1.ListModelsResponse.models:type_name -> mecatl.v1.ModelInfo - 129, // 96: mecatl.v1.ListModelsResponse.provider_status:type_name -> mecatl.v1.ProviderStatus - 132, // 97: mecatl.v1.ReflectSessionResponse.receipt:type_name -> mecatl.v1.ReflectionReceipt - 196, // 98: mecatl.v1.LearningDecision.at:type_name -> google.protobuf.Timestamp - 135, // 99: mecatl.v1.LearningProposal.evidence:type_name -> mecatl.v1.LearningEvidenceRef - 136, // 100: mecatl.v1.LearningProposal.decisions:type_name -> mecatl.v1.LearningDecision - 137, // 101: mecatl.v1.LearningProposal.promotion:type_name -> mecatl.v1.LearningPromotionReceipt - 196, // 102: mecatl.v1.LearningProposal.created_at:type_name -> google.protobuf.Timestamp - 196, // 103: mecatl.v1.LearningProposal.updated_at:type_name -> google.protobuf.Timestamp - 138, // 104: mecatl.v1.ListLearningProposalsResponse.proposals:type_name -> mecatl.v1.LearningProposal - 138, // 105: mecatl.v1.GetLearningProposalResponse.proposal:type_name -> mecatl.v1.LearningProposal - 138, // 106: mecatl.v1.DecideLearningProposalResponse.proposal:type_name -> mecatl.v1.LearningProposal - 138, // 107: mecatl.v1.UndoLearningPromotionResponse.proposal:type_name -> mecatl.v1.LearningProposal - 135, // 108: mecatl.v1.LearnedSkillVersion.evidence:type_name -> mecatl.v1.LearningEvidenceRef - 147, // 109: mecatl.v1.LearnedSkillVersion.evaluations:type_name -> mecatl.v1.SkillEvaluation - 148, // 110: mecatl.v1.LearnedSkillVersion.receipts:type_name -> mecatl.v1.SkillChangeReceipt - 196, // 111: mecatl.v1.LearnedSkillVersion.created_at:type_name -> google.protobuf.Timestamp - 196, // 112: mecatl.v1.LearnedSkillVersion.updated_at:type_name -> google.protobuf.Timestamp - 196, // 113: mecatl.v1.SkillEvaluation.at:type_name -> google.protobuf.Timestamp - 196, // 114: mecatl.v1.SkillChangeReceipt.at:type_name -> google.protobuf.Timestamp - 146, // 115: mecatl.v1.ListLearnedSkillsResponse.skills:type_name -> mecatl.v1.LearnedSkillVersion - 146, // 116: mecatl.v1.GetLearnedSkillResponse.skill:type_name -> mecatl.v1.LearnedSkillVersion - 146, // 117: mecatl.v1.MutateLearnedSkillResponse.skill:type_name -> mecatl.v1.LearnedSkillVersion - 148, // 118: mecatl.v1.ListSkillChangesResponse.changes:type_name -> mecatl.v1.SkillChangeReceipt - 162, // 119: mecatl.v1.CreateTeamRequest.members:type_name -> mecatl.v1.TeammateSpec - 174, // 120: mecatl.v1.CreateTeamResponse.members:type_name -> mecatl.v1.TeamMember - 174, // 121: mecatl.v1.SpawnTeammateResponse.member:type_name -> mecatl.v1.TeamMember - 71, // 122: mecatl.v1.TeamEvent.event:type_name -> mecatl.v1.Event - 171, // 123: mecatl.v1.TeamEvent.outcome:type_name -> mecatl.v1.TeamOutcome - 87, // 124: mecatl.v1.TeamOutcome.usage:type_name -> mecatl.v1.Usage - 177, // 125: mecatl.v1.TeamOutcome.dispositions:type_name -> mecatl.v1.TeamMemberDisposition - 176, // 126: mecatl.v1.TeamOutcome.findings:type_name -> mecatl.v1.TeamFinding - 174, // 127: mecatl.v1.ListTeamResponse.members:type_name -> mecatl.v1.TeamMember - 175, // 128: mecatl.v1.ListTeamResponse.tasks:type_name -> mecatl.v1.TeamTask - 4, // 129: mecatl.v1.TeamMemberDisposition.reason:type_name -> mecatl.v1.TeamMemberStopReason - 0, // 130: mecatl.v1.ApprovePlanRequest.target_mode:type_name -> mecatl.v1.PermissionMode - 182, // 131: mecatl.v1.ManualDreamCapabilities.project_memory:type_name -> mecatl.v1.DreamTargetCapability - 182, // 132: mecatl.v1.ManualDreamCapabilities.user_model:type_name -> mecatl.v1.DreamTargetCapability - 187, // 133: mecatl.v1.GenerateDreamPlanResponse.plan:type_name -> mecatl.v1.DreamReviewPlan - 191, // 134: mecatl.v1.DecideDreamPlanResponse.receipt:type_name -> mecatl.v1.DreamReceipt - 196, // 135: mecatl.v1.DreamReviewPlan.expires_at:type_name -> google.protobuf.Timestamp - 188, // 136: mecatl.v1.DreamReviewPlan.operations:type_name -> mecatl.v1.DreamOperation - 189, // 137: mecatl.v1.DreamOperation.survivor:type_name -> mecatl.v1.DreamParticipant - 189, // 138: mecatl.v1.DreamOperation.sources:type_name -> mecatl.v1.DreamParticipant - 190, // 139: mecatl.v1.DreamOperation.replacement:type_name -> mecatl.v1.DreamReplacement - 8, // 140: mecatl.v1.HarnessService.CreateSession:input_type -> mecatl.v1.CreateSessionRequest - 13, // 141: mecatl.v1.HarnessService.GetSession:input_type -> mecatl.v1.GetSessionRequest - 15, // 142: mecatl.v1.HarnessService.GetSessionTranscript:input_type -> mecatl.v1.GetSessionTranscriptRequest - 47, // 143: mecatl.v1.HarnessService.SetMode:input_type -> mecatl.v1.SetModeRequest - 49, // 144: mecatl.v1.HarnessService.CloseSession:input_type -> mecatl.v1.CloseSessionRequest - 51, // 145: mecatl.v1.HarnessService.RenameSession:input_type -> mecatl.v1.RenameSessionRequest - 53, // 146: mecatl.v1.HarnessService.DeleteSession:input_type -> mecatl.v1.DeleteSessionRequest - 55, // 147: mecatl.v1.HarnessService.ForkSession:input_type -> mecatl.v1.ForkSessionRequest - 58, // 148: mecatl.v1.HarnessService.PreflightSessionAdoption:input_type -> mecatl.v1.PreflightSessionAdoptionRequest - 60, // 149: mecatl.v1.HarnessService.AdoptSession:input_type -> mecatl.v1.AdoptSessionRequest - 63, // 150: mecatl.v1.HarnessService.Converse:input_type -> mecatl.v1.ConverseRequest - 89, // 151: mecatl.v1.HarnessService.ListMcpResources:input_type -> mecatl.v1.ListMcpResourcesRequest - 92, // 152: mecatl.v1.HarnessService.ReadMcpResource:input_type -> mecatl.v1.ReadMcpResourceRequest - 96, // 153: mecatl.v1.HarnessService.ListMcpPrompts:input_type -> mecatl.v1.ListMcpPromptsRequest - 98, // 154: mecatl.v1.HarnessService.GetMcpPrompt:input_type -> mecatl.v1.GetMcpPromptRequest - 103, // 155: mecatl.v1.HarnessService.ListMcpSources:input_type -> mecatl.v1.ListMcpSourcesRequest - 105, // 156: mecatl.v1.HarnessService.ListToolHiveGroups:input_type -> mecatl.v1.ListToolHiveGroupsRequest - 108, // 157: mecatl.v1.HarnessService.ListAgents:input_type -> mecatl.v1.ListAgentsRequest - 111, // 158: mecatl.v1.HarnessService.ListCommands:input_type -> mecatl.v1.ListCommandsRequest - 114, // 159: mecatl.v1.HarnessService.ListWorktrees:input_type -> mecatl.v1.ListWorktreesRequest - 18, // 160: mecatl.v1.HarnessService.StreamSessionEvents:input_type -> mecatl.v1.StreamSessionEventsRequest - 19, // 161: mecatl.v1.HarnessService.StreamSessionLive:input_type -> mecatl.v1.StreamSessionLiveRequest - 20, // 162: mecatl.v1.HarnessService.ListSessions:input_type -> mecatl.v1.ListSessionsRequest - 27, // 163: mecatl.v1.HarnessService.GetStorageHealth:input_type -> mecatl.v1.GetStorageHealthRequest - 30, // 164: mecatl.v1.HarnessService.PlanSessionMigration:input_type -> mecatl.v1.PlanSessionMigrationRequest - 32, // 165: mecatl.v1.HarnessService.ApplySessionMigration:input_type -> mecatl.v1.ApplySessionMigrationRequest - 33, // 166: mecatl.v1.HarnessService.ResumeSessionMigration:input_type -> mecatl.v1.ResumeSessionMigrationRequest - 34, // 167: mecatl.v1.HarnessService.CancelSessionMigration:input_type -> mecatl.v1.CancelSessionMigrationRequest - 35, // 168: mecatl.v1.HarnessService.GetSessionMigrationJob:input_type -> mecatl.v1.GetSessionMigrationJobRequest - 39, // 169: mecatl.v1.HarnessService.PlanSessionCleanup:input_type -> mecatl.v1.PlanSessionCleanupRequest - 41, // 170: mecatl.v1.HarnessService.ApplySessionCleanup:input_type -> mecatl.v1.ApplySessionCleanupRequest - 42, // 171: mecatl.v1.HarnessService.CancelSessionCleanup:input_type -> mecatl.v1.CancelSessionCleanupRequest - 43, // 172: mecatl.v1.HarnessService.GetSessionCleanupJob:input_type -> mecatl.v1.GetSessionCleanupJobRequest - 117, // 173: mecatl.v1.HarnessService.ListSkills:input_type -> mecatl.v1.ListSkillsRequest - 120, // 174: mecatl.v1.HarnessService.GetSoul:input_type -> mecatl.v1.GetSoulRequest - 123, // 175: mecatl.v1.HarnessService.GetUserModel:input_type -> mecatl.v1.GetUserModelRequest - 131, // 176: mecatl.v1.HarnessService.ReflectSession:input_type -> mecatl.v1.ReflectSessionRequest - 183, // 177: mecatl.v1.HarnessService.GenerateDreamPlan:input_type -> mecatl.v1.GenerateDreamPlanRequest - 185, // 178: mecatl.v1.HarnessService.DecideDreamPlan:input_type -> mecatl.v1.DecideDreamPlanRequest - 134, // 179: mecatl.v1.HarnessService.ListLearningProposals:input_type -> mecatl.v1.ListLearningProposalsRequest - 140, // 180: mecatl.v1.HarnessService.GetLearningProposal:input_type -> mecatl.v1.GetLearningProposalRequest - 142, // 181: mecatl.v1.HarnessService.DecideLearningProposal:input_type -> mecatl.v1.DecideLearningProposalRequest - 144, // 182: mecatl.v1.HarnessService.UndoLearningPromotion:input_type -> mecatl.v1.UndoLearningPromotionRequest - 149, // 183: mecatl.v1.HarnessService.ListLearnedSkills:input_type -> mecatl.v1.ListLearnedSkillsRequest - 151, // 184: mecatl.v1.HarnessService.GetLearnedSkill:input_type -> mecatl.v1.GetLearnedSkillRequest - 153, // 185: mecatl.v1.HarnessService.DiffLearnedSkillVersions:input_type -> mecatl.v1.DiffLearnedSkillVersionsRequest - 155, // 186: mecatl.v1.HarnessService.ActivateLearnedSkill:input_type -> mecatl.v1.MutateLearnedSkillRequest - 155, // 187: mecatl.v1.HarnessService.RejectLearnedSkill:input_type -> mecatl.v1.MutateLearnedSkillRequest - 155, // 188: mecatl.v1.HarnessService.ArchiveLearnedSkill:input_type -> mecatl.v1.MutateLearnedSkillRequest - 157, // 189: mecatl.v1.HarnessService.RollbackLearnedSkill:input_type -> mecatl.v1.RollbackLearnedSkillRequest - 158, // 190: mecatl.v1.HarnessService.ListSkillChanges:input_type -> mecatl.v1.ListSkillChangesRequest - 128, // 191: mecatl.v1.HarnessService.ListModels:input_type -> mecatl.v1.ListModelsRequest - 160, // 192: mecatl.v1.HarnessService.CreateTeam:input_type -> mecatl.v1.CreateTeamRequest - 163, // 193: mecatl.v1.HarnessService.SpawnTeammate:input_type -> mecatl.v1.SpawnTeammateRequest - 165, // 194: mecatl.v1.HarnessService.SendTeammateMessage:input_type -> mecatl.v1.SendTeammateMessageRequest - 167, // 195: mecatl.v1.HarnessService.CancelTeammate:input_type -> mecatl.v1.CancelTeammateRequest - 169, // 196: mecatl.v1.HarnessService.RunTeam:input_type -> mecatl.v1.RunTeamRequest - 172, // 197: mecatl.v1.HarnessService.ListTeam:input_type -> mecatl.v1.ListTeamRequest - 178, // 198: mecatl.v1.HarnessService.CleanupTeam:input_type -> mecatl.v1.CleanupTeamRequest - 180, // 199: mecatl.v1.HarnessService.ApprovePlan:input_type -> mecatl.v1.ApprovePlanRequest - 10, // 200: mecatl.v1.HarnessService.CreateSession:output_type -> mecatl.v1.CreateSessionResponse - 14, // 201: mecatl.v1.HarnessService.GetSession:output_type -> mecatl.v1.GetSessionResponse - 17, // 202: mecatl.v1.HarnessService.GetSessionTranscript:output_type -> mecatl.v1.GetSessionTranscriptResponse - 48, // 203: mecatl.v1.HarnessService.SetMode:output_type -> mecatl.v1.SetModeResponse - 50, // 204: mecatl.v1.HarnessService.CloseSession:output_type -> mecatl.v1.CloseSessionResponse - 52, // 205: mecatl.v1.HarnessService.RenameSession:output_type -> mecatl.v1.RenameSessionResponse - 54, // 206: mecatl.v1.HarnessService.DeleteSession:output_type -> mecatl.v1.DeleteSessionResponse - 56, // 207: mecatl.v1.HarnessService.ForkSession:output_type -> mecatl.v1.ForkSessionResponse - 59, // 208: mecatl.v1.HarnessService.PreflightSessionAdoption:output_type -> mecatl.v1.PreflightSessionAdoptionResponse - 61, // 209: mecatl.v1.HarnessService.AdoptSession:output_type -> mecatl.v1.AdoptSessionResponse - 64, // 210: mecatl.v1.HarnessService.Converse:output_type -> mecatl.v1.ConverseResponse - 90, // 211: mecatl.v1.HarnessService.ListMcpResources:output_type -> mecatl.v1.ListMcpResourcesResponse - 93, // 212: mecatl.v1.HarnessService.ReadMcpResource:output_type -> mecatl.v1.ReadMcpResourceResponse - 97, // 213: mecatl.v1.HarnessService.ListMcpPrompts:output_type -> mecatl.v1.ListMcpPromptsResponse - 100, // 214: mecatl.v1.HarnessService.GetMcpPrompt:output_type -> mecatl.v1.GetMcpPromptResponse - 104, // 215: mecatl.v1.HarnessService.ListMcpSources:output_type -> mecatl.v1.ListMcpSourcesResponse - 106, // 216: mecatl.v1.HarnessService.ListToolHiveGroups:output_type -> mecatl.v1.ListToolHiveGroupsResponse - 109, // 217: mecatl.v1.HarnessService.ListAgents:output_type -> mecatl.v1.ListAgentsResponse - 112, // 218: mecatl.v1.HarnessService.ListCommands:output_type -> mecatl.v1.ListCommandsResponse - 115, // 219: mecatl.v1.HarnessService.ListWorktrees:output_type -> mecatl.v1.ListWorktreesResponse - 71, // 220: mecatl.v1.HarnessService.StreamSessionEvents:output_type -> mecatl.v1.Event - 71, // 221: mecatl.v1.HarnessService.StreamSessionLive:output_type -> mecatl.v1.Event - 26, // 222: mecatl.v1.HarnessService.ListSessions:output_type -> mecatl.v1.ListSessionsResponse - 29, // 223: mecatl.v1.HarnessService.GetStorageHealth:output_type -> mecatl.v1.GetStorageHealthResponse - 31, // 224: mecatl.v1.HarnessService.PlanSessionMigration:output_type -> mecatl.v1.SessionMigrationPlan - 45, // 225: mecatl.v1.HarnessService.ApplySessionMigration:output_type -> mecatl.v1.SessionMigrationJob - 45, // 226: mecatl.v1.HarnessService.ResumeSessionMigration:output_type -> mecatl.v1.SessionMigrationJob - 45, // 227: mecatl.v1.HarnessService.CancelSessionMigration:output_type -> mecatl.v1.SessionMigrationJob - 45, // 228: mecatl.v1.HarnessService.GetSessionMigrationJob:output_type -> mecatl.v1.SessionMigrationJob - 40, // 229: mecatl.v1.HarnessService.PlanSessionCleanup:output_type -> mecatl.v1.PlanSessionCleanupResponse - 46, // 230: mecatl.v1.HarnessService.ApplySessionCleanup:output_type -> mecatl.v1.CleanupJob - 46, // 231: mecatl.v1.HarnessService.CancelSessionCleanup:output_type -> mecatl.v1.CleanupJob - 46, // 232: mecatl.v1.HarnessService.GetSessionCleanupJob:output_type -> mecatl.v1.CleanupJob - 118, // 233: mecatl.v1.HarnessService.ListSkills:output_type -> mecatl.v1.ListSkillsResponse - 121, // 234: mecatl.v1.HarnessService.GetSoul:output_type -> mecatl.v1.GetSoulResponse - 126, // 235: mecatl.v1.HarnessService.GetUserModel:output_type -> mecatl.v1.GetUserModelResponse - 133, // 236: mecatl.v1.HarnessService.ReflectSession:output_type -> mecatl.v1.ReflectSessionResponse - 184, // 237: mecatl.v1.HarnessService.GenerateDreamPlan:output_type -> mecatl.v1.GenerateDreamPlanResponse - 186, // 238: mecatl.v1.HarnessService.DecideDreamPlan:output_type -> mecatl.v1.DecideDreamPlanResponse - 139, // 239: mecatl.v1.HarnessService.ListLearningProposals:output_type -> mecatl.v1.ListLearningProposalsResponse - 141, // 240: mecatl.v1.HarnessService.GetLearningProposal:output_type -> mecatl.v1.GetLearningProposalResponse - 143, // 241: mecatl.v1.HarnessService.DecideLearningProposal:output_type -> mecatl.v1.DecideLearningProposalResponse - 145, // 242: mecatl.v1.HarnessService.UndoLearningPromotion:output_type -> mecatl.v1.UndoLearningPromotionResponse - 150, // 243: mecatl.v1.HarnessService.ListLearnedSkills:output_type -> mecatl.v1.ListLearnedSkillsResponse - 152, // 244: mecatl.v1.HarnessService.GetLearnedSkill:output_type -> mecatl.v1.GetLearnedSkillResponse - 154, // 245: mecatl.v1.HarnessService.DiffLearnedSkillVersions:output_type -> mecatl.v1.DiffLearnedSkillVersionsResponse - 156, // 246: mecatl.v1.HarnessService.ActivateLearnedSkill:output_type -> mecatl.v1.MutateLearnedSkillResponse - 156, // 247: mecatl.v1.HarnessService.RejectLearnedSkill:output_type -> mecatl.v1.MutateLearnedSkillResponse - 156, // 248: mecatl.v1.HarnessService.ArchiveLearnedSkill:output_type -> mecatl.v1.MutateLearnedSkillResponse - 156, // 249: mecatl.v1.HarnessService.RollbackLearnedSkill:output_type -> mecatl.v1.MutateLearnedSkillResponse - 159, // 250: mecatl.v1.HarnessService.ListSkillChanges:output_type -> mecatl.v1.ListSkillChangesResponse - 130, // 251: mecatl.v1.HarnessService.ListModels:output_type -> mecatl.v1.ListModelsResponse - 161, // 252: mecatl.v1.HarnessService.CreateTeam:output_type -> mecatl.v1.CreateTeamResponse - 164, // 253: mecatl.v1.HarnessService.SpawnTeammate:output_type -> mecatl.v1.SpawnTeammateResponse - 166, // 254: mecatl.v1.HarnessService.SendTeammateMessage:output_type -> mecatl.v1.SendTeammateMessageResponse - 168, // 255: mecatl.v1.HarnessService.CancelTeammate:output_type -> mecatl.v1.CancelTeammateResponse - 170, // 256: mecatl.v1.HarnessService.RunTeam:output_type -> mecatl.v1.TeamEvent - 173, // 257: mecatl.v1.HarnessService.ListTeam:output_type -> mecatl.v1.ListTeamResponse - 179, // 258: mecatl.v1.HarnessService.CleanupTeam:output_type -> mecatl.v1.CleanupTeamResponse - 71, // 259: mecatl.v1.HarnessService.ApprovePlan:output_type -> mecatl.v1.Event - 200, // [200:260] is the sub-list for method output_type - 140, // [140:200] is the sub-list for method input_type - 140, // [140:140] is the sub-list for extension type_name - 140, // [140:140] is the sub-list for extension extendee - 0, // [0:140] is the sub-list for field type_name + 8, // 34: mecatl.v1.Session.limits:type_name -> mecatl.v1.Limits + 12, // 35: mecatl.v1.Session.resolved_model:type_name -> mecatl.v1.ResolvedModel + 10, // 36: mecatl.v1.Session.capabilities:type_name -> mecatl.v1.ServerCapabilities + 68, // 37: mecatl.v1.ConverseRequest.prompt:type_name -> mecatl.v1.Prompt + 69, // 38: mecatl.v1.ConverseRequest.resume_approval:type_name -> mecatl.v1.ResumeApproval + 70, // 39: mecatl.v1.ConverseRequest.cancel:type_name -> mecatl.v1.Cancel + 71, // 40: mecatl.v1.ConverseRequest.cancel_child:type_name -> mecatl.v1.CancelChild + 72, // 41: mecatl.v1.ConverseRequest.steer:type_name -> mecatl.v1.Steer + 73, // 42: mecatl.v1.ConverseRequest.steer_cancel:type_name -> mecatl.v1.SteerCancel + 74, // 43: mecatl.v1.ConverseResponse.event:type_name -> mecatl.v1.Event + 6, // 44: mecatl.v1.Content.kind:type_name -> mecatl.v1.Content.Kind + 7, // 45: mecatl.v1.ContentBlock.kind:type_name -> mecatl.v1.ContentBlock.Kind + 66, // 46: mecatl.v1.Prompt.parts:type_name -> mecatl.v1.Content + 1, // 47: mecatl.v1.ResumeApproval.verdict:type_name -> mecatl.v1.ApprovalVerdict + 87, // 48: mecatl.v1.Event.tool_call:type_name -> mecatl.v1.ToolCall + 88, // 49: mecatl.v1.Event.tool_result:type_name -> mecatl.v1.ToolResult + 89, // 50: mecatl.v1.Event.ask:type_name -> mecatl.v1.PermissionAsk + 90, // 51: mecatl.v1.Event.result:type_name -> mecatl.v1.Result + 92, // 52: mecatl.v1.Event.usage:type_name -> mecatl.v1.Usage + 91, // 53: mecatl.v1.Event.turn_end:type_name -> mecatl.v1.TurnEnd + 82, // 54: mecatl.v1.Event.hook:type_name -> mecatl.v1.Hook + 83, // 55: mecatl.v1.Event.subagent:type_name -> mecatl.v1.Subagent + 85, // 56: mecatl.v1.Event.team:type_name -> mecatl.v1.Team + 86, // 57: mecatl.v1.Event.parallel:type_name -> mecatl.v1.Parallel + 81, // 58: mecatl.v1.Event.schedule:type_name -> mecatl.v1.SchedulePayload + 77, // 59: mecatl.v1.Event.approval:type_name -> mecatl.v1.Approval + 78, // 60: mecatl.v1.Event.user_prompt:type_name -> mecatl.v1.UserPrompt + 79, // 61: mecatl.v1.Event.compaction_archive:type_name -> mecatl.v1.CompactionArchive + 75, // 62: mecatl.v1.Event.steer:type_name -> mecatl.v1.SteerEcho + 76, // 63: mecatl.v1.Event.steer_outcome:type_name -> mecatl.v1.SteerAck + 2, // 64: mecatl.v1.SteerAck.outcome:type_name -> mecatl.v1.SteerOutcome + 66, // 65: mecatl.v1.UserPrompt.parts:type_name -> mecatl.v1.Content + 80, // 66: mecatl.v1.CompactionArchive.replaced:type_name -> mecatl.v1.ConversationMessage + 87, // 67: mecatl.v1.ConversationMessage.tool_calls:type_name -> mecatl.v1.ToolCall + 88, // 68: mecatl.v1.ConversationMessage.tool_result:type_name -> mecatl.v1.ToolResult + 66, // 69: mecatl.v1.ConversationMessage.parts:type_name -> mecatl.v1.Content + 3, // 70: mecatl.v1.Hook.decision:type_name -> mecatl.v1.HookDecision + 92, // 71: mecatl.v1.Subagent.usage:type_name -> mecatl.v1.Usage + 84, // 72: mecatl.v1.Team.roster:type_name -> mecatl.v1.TeamMemberSpec + 92, // 73: mecatl.v1.Team.usage:type_name -> mecatl.v1.Usage + 180, // 74: mecatl.v1.Team.tasks:type_name -> mecatl.v1.TeamTask + 181, // 75: mecatl.v1.Team.findings:type_name -> mecatl.v1.TeamFinding + 182, // 76: mecatl.v1.Team.dispositions:type_name -> mecatl.v1.TeamMemberDisposition + 92, // 77: mecatl.v1.Parallel.usage:type_name -> mecatl.v1.Usage + 67, // 78: mecatl.v1.ToolResult.blocks:type_name -> mecatl.v1.ContentBlock + 92, // 79: mecatl.v1.Result.usage:type_name -> mecatl.v1.Usage + 92, // 80: mecatl.v1.TurnEnd.usage:type_name -> mecatl.v1.Usage + 93, // 81: mecatl.v1.ListMcpResourcesResponse.resources:type_name -> mecatl.v1.McpResource + 96, // 82: mecatl.v1.ReadMcpResourceResponse.contents:type_name -> mecatl.v1.McpResourceContents + 99, // 83: mecatl.v1.McpPrompt.arguments:type_name -> mecatl.v1.McpPromptArgument + 100, // 84: mecatl.v1.ListMcpPromptsResponse.prompts:type_name -> mecatl.v1.McpPrompt + 200, // 85: mecatl.v1.GetMcpPromptRequest.arguments:type_name -> mecatl.v1.GetMcpPromptRequest.ArgumentsEntry + 104, // 86: mecatl.v1.GetMcpPromptResponse.messages:type_name -> mecatl.v1.McpPromptMessage + 106, // 87: mecatl.v1.McpSource.servers:type_name -> mecatl.v1.McpServerInfo + 107, // 88: mecatl.v1.ListMcpSourcesResponse.sources:type_name -> mecatl.v1.McpSource + 112, // 89: mecatl.v1.ListAgentsResponse.agents:type_name -> mecatl.v1.AgentInfo + 115, // 90: mecatl.v1.ListCommandsResponse.commands:type_name -> mecatl.v1.Command + 118, // 91: mecatl.v1.ListWorktreesResponse.worktrees:type_name -> mecatl.v1.Worktree + 121, // 92: mecatl.v1.ListSkillsResponse.skills:type_name -> mecatl.v1.SkillInfo + 4, // 93: mecatl.v1.SoulInfo.provenance:type_name -> mecatl.v1.SoulProvenance + 124, // 94: mecatl.v1.GetSoulResponse.soul:type_name -> mecatl.v1.SoulInfo + 201, // 95: mecatl.v1.UserModelRevision.updated_at:type_name -> google.protobuf.Timestamp + 129, // 96: mecatl.v1.UserModelDetail.current:type_name -> mecatl.v1.UserModelRevision + 129, // 97: mecatl.v1.UserModelDetail.history:type_name -> mecatl.v1.UserModelRevision + 127, // 98: mecatl.v1.GetUserModelResponse.entries:type_name -> mecatl.v1.UserModelEntry + 130, // 99: mecatl.v1.GetUserModelResponse.detail:type_name -> mecatl.v1.UserModelDetail + 132, // 100: mecatl.v1.ListModelsResponse.models:type_name -> mecatl.v1.ModelInfo + 134, // 101: mecatl.v1.ListModelsResponse.provider_status:type_name -> mecatl.v1.ProviderStatus + 137, // 102: mecatl.v1.ReflectSessionResponse.receipt:type_name -> mecatl.v1.ReflectionReceipt + 201, // 103: mecatl.v1.LearningDecision.at:type_name -> google.protobuf.Timestamp + 140, // 104: mecatl.v1.LearningProposal.evidence:type_name -> mecatl.v1.LearningEvidenceRef + 141, // 105: mecatl.v1.LearningProposal.decisions:type_name -> mecatl.v1.LearningDecision + 142, // 106: mecatl.v1.LearningProposal.promotion:type_name -> mecatl.v1.LearningPromotionReceipt + 201, // 107: mecatl.v1.LearningProposal.created_at:type_name -> google.protobuf.Timestamp + 201, // 108: mecatl.v1.LearningProposal.updated_at:type_name -> google.protobuf.Timestamp + 143, // 109: mecatl.v1.ListLearningProposalsResponse.proposals:type_name -> mecatl.v1.LearningProposal + 143, // 110: mecatl.v1.GetLearningProposalResponse.proposal:type_name -> mecatl.v1.LearningProposal + 143, // 111: mecatl.v1.DecideLearningProposalResponse.proposal:type_name -> mecatl.v1.LearningProposal + 143, // 112: mecatl.v1.UndoLearningPromotionResponse.proposal:type_name -> mecatl.v1.LearningProposal + 140, // 113: mecatl.v1.LearnedSkillVersion.evidence:type_name -> mecatl.v1.LearningEvidenceRef + 152, // 114: mecatl.v1.LearnedSkillVersion.evaluations:type_name -> mecatl.v1.SkillEvaluation + 153, // 115: mecatl.v1.LearnedSkillVersion.receipts:type_name -> mecatl.v1.SkillChangeReceipt + 201, // 116: mecatl.v1.LearnedSkillVersion.created_at:type_name -> google.protobuf.Timestamp + 201, // 117: mecatl.v1.LearnedSkillVersion.updated_at:type_name -> google.protobuf.Timestamp + 201, // 118: mecatl.v1.SkillEvaluation.at:type_name -> google.protobuf.Timestamp + 201, // 119: mecatl.v1.SkillChangeReceipt.at:type_name -> google.protobuf.Timestamp + 151, // 120: mecatl.v1.ListLearnedSkillsResponse.skills:type_name -> mecatl.v1.LearnedSkillVersion + 151, // 121: mecatl.v1.GetLearnedSkillResponse.skill:type_name -> mecatl.v1.LearnedSkillVersion + 151, // 122: mecatl.v1.MutateLearnedSkillResponse.skill:type_name -> mecatl.v1.LearnedSkillVersion + 153, // 123: mecatl.v1.ListSkillChangesResponse.changes:type_name -> mecatl.v1.SkillChangeReceipt + 167, // 124: mecatl.v1.CreateTeamRequest.members:type_name -> mecatl.v1.TeammateSpec + 179, // 125: mecatl.v1.CreateTeamResponse.members:type_name -> mecatl.v1.TeamMember + 179, // 126: mecatl.v1.SpawnTeammateResponse.member:type_name -> mecatl.v1.TeamMember + 74, // 127: mecatl.v1.TeamEvent.event:type_name -> mecatl.v1.Event + 176, // 128: mecatl.v1.TeamEvent.outcome:type_name -> mecatl.v1.TeamOutcome + 92, // 129: mecatl.v1.TeamOutcome.usage:type_name -> mecatl.v1.Usage + 182, // 130: mecatl.v1.TeamOutcome.dispositions:type_name -> mecatl.v1.TeamMemberDisposition + 181, // 131: mecatl.v1.TeamOutcome.findings:type_name -> mecatl.v1.TeamFinding + 179, // 132: mecatl.v1.ListTeamResponse.members:type_name -> mecatl.v1.TeamMember + 180, // 133: mecatl.v1.ListTeamResponse.tasks:type_name -> mecatl.v1.TeamTask + 5, // 134: mecatl.v1.TeamMemberDisposition.reason:type_name -> mecatl.v1.TeamMemberStopReason + 0, // 135: mecatl.v1.ApprovePlanRequest.target_mode:type_name -> mecatl.v1.PermissionMode + 187, // 136: mecatl.v1.ManualDreamCapabilities.project_memory:type_name -> mecatl.v1.DreamTargetCapability + 187, // 137: mecatl.v1.ManualDreamCapabilities.user_model:type_name -> mecatl.v1.DreamTargetCapability + 192, // 138: mecatl.v1.GenerateDreamPlanResponse.plan:type_name -> mecatl.v1.DreamReviewPlan + 196, // 139: mecatl.v1.DecideDreamPlanResponse.receipt:type_name -> mecatl.v1.DreamReceipt + 201, // 140: mecatl.v1.DreamReviewPlan.expires_at:type_name -> google.protobuf.Timestamp + 193, // 141: mecatl.v1.DreamReviewPlan.operations:type_name -> mecatl.v1.DreamOperation + 194, // 142: mecatl.v1.DreamOperation.survivor:type_name -> mecatl.v1.DreamParticipant + 194, // 143: mecatl.v1.DreamOperation.sources:type_name -> mecatl.v1.DreamParticipant + 195, // 144: mecatl.v1.DreamOperation.replacement:type_name -> mecatl.v1.DreamReplacement + 9, // 145: mecatl.v1.HarnessService.CreateSession:input_type -> mecatl.v1.CreateSessionRequest + 14, // 146: mecatl.v1.HarnessService.GetSession:input_type -> mecatl.v1.GetSessionRequest + 16, // 147: mecatl.v1.HarnessService.GetSessionTranscript:input_type -> mecatl.v1.GetSessionTranscriptRequest + 48, // 148: mecatl.v1.HarnessService.SetMode:input_type -> mecatl.v1.SetModeRequest + 50, // 149: mecatl.v1.HarnessService.CloseSession:input_type -> mecatl.v1.CloseSessionRequest + 52, // 150: mecatl.v1.HarnessService.RenameSession:input_type -> mecatl.v1.RenameSessionRequest + 54, // 151: mecatl.v1.HarnessService.DeleteSession:input_type -> mecatl.v1.DeleteSessionRequest + 56, // 152: mecatl.v1.HarnessService.ForkSession:input_type -> mecatl.v1.ForkSessionRequest + 59, // 153: mecatl.v1.HarnessService.PreflightSessionAdoption:input_type -> mecatl.v1.PreflightSessionAdoptionRequest + 61, // 154: mecatl.v1.HarnessService.AdoptSession:input_type -> mecatl.v1.AdoptSessionRequest + 64, // 155: mecatl.v1.HarnessService.Converse:input_type -> mecatl.v1.ConverseRequest + 94, // 156: mecatl.v1.HarnessService.ListMcpResources:input_type -> mecatl.v1.ListMcpResourcesRequest + 97, // 157: mecatl.v1.HarnessService.ReadMcpResource:input_type -> mecatl.v1.ReadMcpResourceRequest + 101, // 158: mecatl.v1.HarnessService.ListMcpPrompts:input_type -> mecatl.v1.ListMcpPromptsRequest + 103, // 159: mecatl.v1.HarnessService.GetMcpPrompt:input_type -> mecatl.v1.GetMcpPromptRequest + 108, // 160: mecatl.v1.HarnessService.ListMcpSources:input_type -> mecatl.v1.ListMcpSourcesRequest + 110, // 161: mecatl.v1.HarnessService.ListToolHiveGroups:input_type -> mecatl.v1.ListToolHiveGroupsRequest + 113, // 162: mecatl.v1.HarnessService.ListAgents:input_type -> mecatl.v1.ListAgentsRequest + 116, // 163: mecatl.v1.HarnessService.ListCommands:input_type -> mecatl.v1.ListCommandsRequest + 119, // 164: mecatl.v1.HarnessService.ListWorktrees:input_type -> mecatl.v1.ListWorktreesRequest + 19, // 165: mecatl.v1.HarnessService.StreamSessionEvents:input_type -> mecatl.v1.StreamSessionEventsRequest + 20, // 166: mecatl.v1.HarnessService.StreamSessionLive:input_type -> mecatl.v1.StreamSessionLiveRequest + 21, // 167: mecatl.v1.HarnessService.ListSessions:input_type -> mecatl.v1.ListSessionsRequest + 28, // 168: mecatl.v1.HarnessService.GetStorageHealth:input_type -> mecatl.v1.GetStorageHealthRequest + 31, // 169: mecatl.v1.HarnessService.PlanSessionMigration:input_type -> mecatl.v1.PlanSessionMigrationRequest + 33, // 170: mecatl.v1.HarnessService.ApplySessionMigration:input_type -> mecatl.v1.ApplySessionMigrationRequest + 34, // 171: mecatl.v1.HarnessService.ResumeSessionMigration:input_type -> mecatl.v1.ResumeSessionMigrationRequest + 35, // 172: mecatl.v1.HarnessService.CancelSessionMigration:input_type -> mecatl.v1.CancelSessionMigrationRequest + 36, // 173: mecatl.v1.HarnessService.GetSessionMigrationJob:input_type -> mecatl.v1.GetSessionMigrationJobRequest + 40, // 174: mecatl.v1.HarnessService.PlanSessionCleanup:input_type -> mecatl.v1.PlanSessionCleanupRequest + 42, // 175: mecatl.v1.HarnessService.ApplySessionCleanup:input_type -> mecatl.v1.ApplySessionCleanupRequest + 43, // 176: mecatl.v1.HarnessService.CancelSessionCleanup:input_type -> mecatl.v1.CancelSessionCleanupRequest + 44, // 177: mecatl.v1.HarnessService.GetSessionCleanupJob:input_type -> mecatl.v1.GetSessionCleanupJobRequest + 122, // 178: mecatl.v1.HarnessService.ListSkills:input_type -> mecatl.v1.ListSkillsRequest + 125, // 179: mecatl.v1.HarnessService.GetSoul:input_type -> mecatl.v1.GetSoulRequest + 128, // 180: mecatl.v1.HarnessService.GetUserModel:input_type -> mecatl.v1.GetUserModelRequest + 136, // 181: mecatl.v1.HarnessService.ReflectSession:input_type -> mecatl.v1.ReflectSessionRequest + 188, // 182: mecatl.v1.HarnessService.GenerateDreamPlan:input_type -> mecatl.v1.GenerateDreamPlanRequest + 190, // 183: mecatl.v1.HarnessService.DecideDreamPlan:input_type -> mecatl.v1.DecideDreamPlanRequest + 139, // 184: mecatl.v1.HarnessService.ListLearningProposals:input_type -> mecatl.v1.ListLearningProposalsRequest + 145, // 185: mecatl.v1.HarnessService.GetLearningProposal:input_type -> mecatl.v1.GetLearningProposalRequest + 147, // 186: mecatl.v1.HarnessService.DecideLearningProposal:input_type -> mecatl.v1.DecideLearningProposalRequest + 149, // 187: mecatl.v1.HarnessService.UndoLearningPromotion:input_type -> mecatl.v1.UndoLearningPromotionRequest + 154, // 188: mecatl.v1.HarnessService.ListLearnedSkills:input_type -> mecatl.v1.ListLearnedSkillsRequest + 156, // 189: mecatl.v1.HarnessService.GetLearnedSkill:input_type -> mecatl.v1.GetLearnedSkillRequest + 158, // 190: mecatl.v1.HarnessService.DiffLearnedSkillVersions:input_type -> mecatl.v1.DiffLearnedSkillVersionsRequest + 160, // 191: mecatl.v1.HarnessService.ActivateLearnedSkill:input_type -> mecatl.v1.MutateLearnedSkillRequest + 160, // 192: mecatl.v1.HarnessService.RejectLearnedSkill:input_type -> mecatl.v1.MutateLearnedSkillRequest + 160, // 193: mecatl.v1.HarnessService.ArchiveLearnedSkill:input_type -> mecatl.v1.MutateLearnedSkillRequest + 162, // 194: mecatl.v1.HarnessService.RollbackLearnedSkill:input_type -> mecatl.v1.RollbackLearnedSkillRequest + 163, // 195: mecatl.v1.HarnessService.ListSkillChanges:input_type -> mecatl.v1.ListSkillChangesRequest + 133, // 196: mecatl.v1.HarnessService.ListModels:input_type -> mecatl.v1.ListModelsRequest + 165, // 197: mecatl.v1.HarnessService.CreateTeam:input_type -> mecatl.v1.CreateTeamRequest + 168, // 198: mecatl.v1.HarnessService.SpawnTeammate:input_type -> mecatl.v1.SpawnTeammateRequest + 170, // 199: mecatl.v1.HarnessService.SendTeammateMessage:input_type -> mecatl.v1.SendTeammateMessageRequest + 172, // 200: mecatl.v1.HarnessService.CancelTeammate:input_type -> mecatl.v1.CancelTeammateRequest + 174, // 201: mecatl.v1.HarnessService.RunTeam:input_type -> mecatl.v1.RunTeamRequest + 177, // 202: mecatl.v1.HarnessService.ListTeam:input_type -> mecatl.v1.ListTeamRequest + 183, // 203: mecatl.v1.HarnessService.CleanupTeam:input_type -> mecatl.v1.CleanupTeamRequest + 185, // 204: mecatl.v1.HarnessService.ApprovePlan:input_type -> mecatl.v1.ApprovePlanRequest + 11, // 205: mecatl.v1.HarnessService.CreateSession:output_type -> mecatl.v1.CreateSessionResponse + 15, // 206: mecatl.v1.HarnessService.GetSession:output_type -> mecatl.v1.GetSessionResponse + 18, // 207: mecatl.v1.HarnessService.GetSessionTranscript:output_type -> mecatl.v1.GetSessionTranscriptResponse + 49, // 208: mecatl.v1.HarnessService.SetMode:output_type -> mecatl.v1.SetModeResponse + 51, // 209: mecatl.v1.HarnessService.CloseSession:output_type -> mecatl.v1.CloseSessionResponse + 53, // 210: mecatl.v1.HarnessService.RenameSession:output_type -> mecatl.v1.RenameSessionResponse + 55, // 211: mecatl.v1.HarnessService.DeleteSession:output_type -> mecatl.v1.DeleteSessionResponse + 57, // 212: mecatl.v1.HarnessService.ForkSession:output_type -> mecatl.v1.ForkSessionResponse + 60, // 213: mecatl.v1.HarnessService.PreflightSessionAdoption:output_type -> mecatl.v1.PreflightSessionAdoptionResponse + 62, // 214: mecatl.v1.HarnessService.AdoptSession:output_type -> mecatl.v1.AdoptSessionResponse + 65, // 215: mecatl.v1.HarnessService.Converse:output_type -> mecatl.v1.ConverseResponse + 95, // 216: mecatl.v1.HarnessService.ListMcpResources:output_type -> mecatl.v1.ListMcpResourcesResponse + 98, // 217: mecatl.v1.HarnessService.ReadMcpResource:output_type -> mecatl.v1.ReadMcpResourceResponse + 102, // 218: mecatl.v1.HarnessService.ListMcpPrompts:output_type -> mecatl.v1.ListMcpPromptsResponse + 105, // 219: mecatl.v1.HarnessService.GetMcpPrompt:output_type -> mecatl.v1.GetMcpPromptResponse + 109, // 220: mecatl.v1.HarnessService.ListMcpSources:output_type -> mecatl.v1.ListMcpSourcesResponse + 111, // 221: mecatl.v1.HarnessService.ListToolHiveGroups:output_type -> mecatl.v1.ListToolHiveGroupsResponse + 114, // 222: mecatl.v1.HarnessService.ListAgents:output_type -> mecatl.v1.ListAgentsResponse + 117, // 223: mecatl.v1.HarnessService.ListCommands:output_type -> mecatl.v1.ListCommandsResponse + 120, // 224: mecatl.v1.HarnessService.ListWorktrees:output_type -> mecatl.v1.ListWorktreesResponse + 74, // 225: mecatl.v1.HarnessService.StreamSessionEvents:output_type -> mecatl.v1.Event + 74, // 226: mecatl.v1.HarnessService.StreamSessionLive:output_type -> mecatl.v1.Event + 27, // 227: mecatl.v1.HarnessService.ListSessions:output_type -> mecatl.v1.ListSessionsResponse + 30, // 228: mecatl.v1.HarnessService.GetStorageHealth:output_type -> mecatl.v1.GetStorageHealthResponse + 32, // 229: mecatl.v1.HarnessService.PlanSessionMigration:output_type -> mecatl.v1.SessionMigrationPlan + 46, // 230: mecatl.v1.HarnessService.ApplySessionMigration:output_type -> mecatl.v1.SessionMigrationJob + 46, // 231: mecatl.v1.HarnessService.ResumeSessionMigration:output_type -> mecatl.v1.SessionMigrationJob + 46, // 232: mecatl.v1.HarnessService.CancelSessionMigration:output_type -> mecatl.v1.SessionMigrationJob + 46, // 233: mecatl.v1.HarnessService.GetSessionMigrationJob:output_type -> mecatl.v1.SessionMigrationJob + 41, // 234: mecatl.v1.HarnessService.PlanSessionCleanup:output_type -> mecatl.v1.PlanSessionCleanupResponse + 47, // 235: mecatl.v1.HarnessService.ApplySessionCleanup:output_type -> mecatl.v1.CleanupJob + 47, // 236: mecatl.v1.HarnessService.CancelSessionCleanup:output_type -> mecatl.v1.CleanupJob + 47, // 237: mecatl.v1.HarnessService.GetSessionCleanupJob:output_type -> mecatl.v1.CleanupJob + 123, // 238: mecatl.v1.HarnessService.ListSkills:output_type -> mecatl.v1.ListSkillsResponse + 126, // 239: mecatl.v1.HarnessService.GetSoul:output_type -> mecatl.v1.GetSoulResponse + 131, // 240: mecatl.v1.HarnessService.GetUserModel:output_type -> mecatl.v1.GetUserModelResponse + 138, // 241: mecatl.v1.HarnessService.ReflectSession:output_type -> mecatl.v1.ReflectSessionResponse + 189, // 242: mecatl.v1.HarnessService.GenerateDreamPlan:output_type -> mecatl.v1.GenerateDreamPlanResponse + 191, // 243: mecatl.v1.HarnessService.DecideDreamPlan:output_type -> mecatl.v1.DecideDreamPlanResponse + 144, // 244: mecatl.v1.HarnessService.ListLearningProposals:output_type -> mecatl.v1.ListLearningProposalsResponse + 146, // 245: mecatl.v1.HarnessService.GetLearningProposal:output_type -> mecatl.v1.GetLearningProposalResponse + 148, // 246: mecatl.v1.HarnessService.DecideLearningProposal:output_type -> mecatl.v1.DecideLearningProposalResponse + 150, // 247: mecatl.v1.HarnessService.UndoLearningPromotion:output_type -> mecatl.v1.UndoLearningPromotionResponse + 155, // 248: mecatl.v1.HarnessService.ListLearnedSkills:output_type -> mecatl.v1.ListLearnedSkillsResponse + 157, // 249: mecatl.v1.HarnessService.GetLearnedSkill:output_type -> mecatl.v1.GetLearnedSkillResponse + 159, // 250: mecatl.v1.HarnessService.DiffLearnedSkillVersions:output_type -> mecatl.v1.DiffLearnedSkillVersionsResponse + 161, // 251: mecatl.v1.HarnessService.ActivateLearnedSkill:output_type -> mecatl.v1.MutateLearnedSkillResponse + 161, // 252: mecatl.v1.HarnessService.RejectLearnedSkill:output_type -> mecatl.v1.MutateLearnedSkillResponse + 161, // 253: mecatl.v1.HarnessService.ArchiveLearnedSkill:output_type -> mecatl.v1.MutateLearnedSkillResponse + 161, // 254: mecatl.v1.HarnessService.RollbackLearnedSkill:output_type -> mecatl.v1.MutateLearnedSkillResponse + 164, // 255: mecatl.v1.HarnessService.ListSkillChanges:output_type -> mecatl.v1.ListSkillChangesResponse + 135, // 256: mecatl.v1.HarnessService.ListModels:output_type -> mecatl.v1.ListModelsResponse + 166, // 257: mecatl.v1.HarnessService.CreateTeam:output_type -> mecatl.v1.CreateTeamResponse + 169, // 258: mecatl.v1.HarnessService.SpawnTeammate:output_type -> mecatl.v1.SpawnTeammateResponse + 171, // 259: mecatl.v1.HarnessService.SendTeammateMessage:output_type -> mecatl.v1.SendTeammateMessageResponse + 173, // 260: mecatl.v1.HarnessService.CancelTeammate:output_type -> mecatl.v1.CancelTeammateResponse + 175, // 261: mecatl.v1.HarnessService.RunTeam:output_type -> mecatl.v1.TeamEvent + 178, // 262: mecatl.v1.HarnessService.ListTeam:output_type -> mecatl.v1.ListTeamResponse + 184, // 263: mecatl.v1.HarnessService.CleanupTeam:output_type -> mecatl.v1.CleanupTeamResponse + 74, // 264: mecatl.v1.HarnessService.ApprovePlan:output_type -> mecatl.v1.Event + 205, // [205:265] is the sub-list for method output_type + 145, // [145:205] is the sub-list for method input_type + 145, // [145:145] is the sub-list for extension type_name + 145, // [145:145] is the sub-list for extension extendee + 0, // [0:145] is the sub-list for field type_name } func init() { file_mecatl_v1_harness_proto_init() } @@ -17421,14 +17898,16 @@ func file_mecatl_v1_harness_proto_init() { (*ConverseRequest_ResumeApproval)(nil), (*ConverseRequest_Cancel)(nil), (*ConverseRequest_CancelChild)(nil), + (*ConverseRequest_Steer)(nil), + (*ConverseRequest_SteerCancel)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_mecatl_v1_harness_proto_rawDesc), len(file_mecatl_v1_harness_proto_rawDesc)), - NumEnums: 7, - NumMessages: 189, + NumEnums: 8, + NumMessages: 193, NumExtensions: 0, NumServices: 1, }, diff --git a/contracts/proto/mecatl/v1/harness.proto b/contracts/proto/mecatl/v1/harness.proto index c2f4da39d0..9e66590891 100644 --- a/contracts/proto/mecatl/v1/harness.proto +++ b/contracts/proto/mecatl/v1/harness.proto @@ -567,6 +567,15 @@ message ServerCapabilities { // storage_health is true only when the backend implements bounded indexed // health and this deployment has a management authorizer. bool storage_health = 23; + // steer is true when the server's engines arm the mid-run steer inbox + // (steer-while-running, issue #512): a client may then send `steer` / + // `steer_cancel` frames on the live Converse stream and render the EvSteer + // drain echo. When false (an older server, or steer disabled in + // composition), the client keeps its local merge-queue behaviour — a steer + // frame sent to such a server is promoted to a fresh follow-up run (the + // engine reports too_late on its disarmed inbox), never silently dropped. + // Additive: an older server leaves it false. + bool steer = 24; } // CreateSessionResponse carries the newly-allocated session id. @@ -1129,6 +1138,17 @@ message ConverseRequest { // cancel_child cancels ONE child run (a subagent) of the in-flight run, // leaving the run itself (and any sibling children) untouched. CancelChild cancel_child = 12; + // steer injects an operator instruction mid-run (steer-while-running, + // issue #512): the run's steer inbox parks it and the loop drains it at + // the next turn boundary, recording it as an ordinary user continuation. + // The server echoes the authoritative outcome back on the stream as a + // steer.outcome event. gRPC-only in v1 (the HTTP/SSE surface has no + // client→server mid-run channel). + Steer steer = 13; + // steer_cancel retracts the run's PENDING (un-drained) steer, if any. + // The authoritative outcome rides back as a steer.outcome event + // (retracted / none_pending). + SteerCancel steer_cancel = 14; } } @@ -1279,6 +1299,69 @@ message CancelChild { string child_id = 1 [(buf.validate.field).string.min_len = 1]; } +// Steer injects an operator instruction into the IN-FLIGHT run +// (steer-while-running, issue #512). It never preempts or aborts the in-flight +// model call: the run's steer inbox parks the text (a SINGLE slot — a second +// steer while one is pending APPENDS into the pending bundle, merged with a +// blank-line separator; the merged bundle still drains as ONE user message. +// Replacing a pending bundle is explicit steer_cancel-then-resend) and the +// loop drains it at the next turn boundary, recording it as an ordinary user +// continuation. The server echoes the authoritative outcome on the stream +// (steer.outcome); when the run's inbox is closed (the run went terminal +// behind the client's "still running" belief) the text is PROMOTED to a fresh +// follow-up run, never silently dropped. +message Steer { + // text is the operator instruction to inject at the next turn boundary. + string text = 1 [(buf.validate.field).string.min_len = 1]; + // message_id is the CLIENT-MINTED correlation key for THIS frame (unique per + // send; empty means uncorrelated). The server echoes the frame's own id + // verbatim on its steer.outcome ack. On the EvSteer drain echo it carries a + // WATERMARK: the LATEST (tail) contributing send's id of the bundle that + // just drained — sends up to and including the watermark drained, sends + // after it are still pending, so the client splits its ordered queue on it. + // text is NOT a safe correlation key (two sends can carry the same text). + string message_id = 2; +} + +// SteerCancel retracts the run's PENDING (un-drained) steer, if any. It is +// idempotent: a cancel with nothing pending reports none_pending. A steer that +// already drained at a turn boundary is ordinary recorded history and cannot +// be retracted (the cancel reports none_pending then — the drain won). +message SteerCancel { + // message_id is the CLIENT-MINTED correlation key, echoed verbatim on the + // steer.outcome ack (same contract as Steer.message_id). + string message_id = 1; +} + +// SteerOutcome is the AUTHORITATIVE result of a steer inbox transition, +// mirroring the engine's closed-enum agent.SteerOutcome. The client cannot +// observe the exact drain moment across stream latency, so the engine reports +// what actually happened to the steer rather than letting the client guess. +enum SteerOutcome { + // STEER_OUTCOME_UNSPECIFIED is the zero value; the server never sends it. + STEER_OUTCOME_UNSPECIFIED = 0; + // STEER_OUTCOME_ACCEPTED: the steer parked in the (empty) pending slot; it + // drains at the next turn boundary unless retracted first. + STEER_OUTCOME_ACCEPTED = 1; + // STEER_OUTCOME_APPENDED: the steer found the pending slot OCCUPIED and was + // MERGED into it (append is the default for a second steer): the pending + // bundle's text grew by a blank-line separator + text, and it still drains as + // ONE bundle. Distinguished from ACCEPTED (a NEW pending bundle) so the client + // can render "merged onto pending" honestly. Replacing the pending bundle is + // explicit: steer_cancel, then resend with a fresh message_id. + STEER_OUTCOME_APPENDED = 2; + // STEER_OUTCOME_RETRACTED: a steer_cancel found a pending steer and + // retracted it; the run drains nothing for it. + STEER_OUTCOME_RETRACTED = 3; + // STEER_OUTCOME_NONE_PENDING: a steer_cancel found the slot EMPTY — there + // was nothing to retract (no steer pending, or the run terminal). + STEER_OUTCOME_NONE_PENDING = 4; + // STEER_OUTCOME_TOO_LATE: the steer arrived after the run went terminal + // (the inbox was closed). The server promoted the text to a fresh follow-up + // run (promoted=true on the event) rather than dropping it. + STEER_OUTCOME_TOO_LATE = 5; +} + // Event mirrors the domain session.Event one-for-one. `type` is the string // event kind; the structured submessages are populated per kind. message Event { @@ -1353,6 +1436,49 @@ message Event { // the pre-compaction conversation). LOG-ONLY on the live Converse wire; // StreamSessionEvents relays them so a transcript replay recovers dropped turns. CompactionArchive compaction_archive = 18; + // steer is set on steer events (EvSteer — the run's steer-inbox DRAIN echo): + // the COMMITTED operator steer just recorded into the conversation as an + // ordinary user continuation. CLIENT-VISIBLE and AUTHORITATIVE: the engine is + // the sole authority on which steer won the slot, so the client renders THIS + // text. Byte-identical to the recorded user message replayed to the model + // (recorded == streamed == model-view). + SteerEcho steer = 19; + // steer_outcome is set on steer.outcome events: the AUTHORITATIVE ack for a + // steer / steer_cancel frame the client sent on this stream (accepted / + // accepted / appended / retracted / none_pending / too_late). CLIENT-VISIBLE, one per + // steer frame, sequenced in send order. + SteerAck steer_outcome = 20; +} + +// SteerEcho is the proto projection of session.SteerPayload (EvSteer): the +// committed operator steer the turn-boundary drain just recorded. See Event.steer. +message SteerEcho { + // text is the committed steer text, byte-identical to the user message + // recorded into history and replayed to the model. + string text = 1; + // message_id carries the WATERMARK of the drained bundle — the client-minted + // Steer.message_id of the LATEST (tail) send that contributed to this drain + // (empty when the sender supplied none). The client splits its ordered queue + // on it: sends up to and including the watermark drained (they land in + // context here), sends after it are still pending. + string message_id = 2; +} + +// SteerAck is the proto projection of a steer-frame outcome: the server's +// authoritative report of what happened to a steer / steer_cancel frame. +message SteerAck { + // outcome is the closed-enum result (mirrors agent.SteerOutcome). + SteerOutcome outcome = 1; + // text echoes the steer text the outcome is about (empty for steer_cancel). + string text = 2; + // promoted is true when a too_late steer was promoted to a fresh follow-up + // run through the run-entry funnel (the text drives a new run instead of + // draining into the finished one) — never silently dropped. + bool promoted = 3; + // message_id echoes the client-minted id of the Steer / SteerCancel frame + // this ack answers (empty when the frame supplied none), so the client + // correlates the authoritative outcome with its send and ignores stale acks. + string message_id = 4; } // Approval is the proto projection of session.ApprovalPayload (EvApproval): diff --git a/docs/acceptance/README.md b/docs/acceptance/README.md index 904f72ef3e..590930d33a 100644 --- a/docs/acceptance/README.md +++ b/docs/acceptance/README.md @@ -81,6 +81,10 @@ when the code that satisfies the plan has landed. - [Session storage continuity](session-storage-continuity.md) — bounded current snapshots, indexed progressive inventory, resumable migration/cleanup, configurable retention, and explicit legacy-chat adoption. Status: draft. +- [Steer-while-running](steer-while-running.md) — inject a user message into an + in-flight run (Claude Code's "steer"): an engine-side supersedable inbox drained + at the turn boundary, a gRPC `Converse` frame, the authoritative drain echo, and + the mecatui capability flip; gRPC-only v1 (HTTP deferred). Status: draft. ## See also diff --git a/docs/acceptance/steer-while-running.md b/docs/acceptance/steer-while-running.md new file mode 100644 index 0000000000..342e537a51 --- /dev/null +++ b/docs/acceptance/steer-while-running.md @@ -0,0 +1,208 @@ +# Steer-while-running — acceptance plan + +**Phase:** capability — mid-run user input (steer) +**Status:** landed (reworked through review rounds, 2026-08-18). Settled in the #512 design discussion; rounds 2–3 rework landed on the same branch (Ozz review, then ad-hoc live-test findings). +**Issue:** [stacklok/mecatl#512](https://github.com/stacklok/mecatl/issues/512). +**ADR:** ADR-0228 (lands with this plan) — the steer **as-shipped** contract: engine append-default single-slot inbox, watermark `message_id` correlation, drain echo, promote-on-terminal-race + sequential active-run handoff, gate. +**Accumulator branch:** `feat/steer-while-running` (PR #570). + +The smallest set of work that lets a user inject a message into an **in-flight** run — Claude Code's "steer while running" — instead of waiting for the run to end and submitting a fresh prompt. Today mecatl's queue is purely client-side and turn-terminal ([`cmd/mecatui/ui/update.go`](../../cmd/mecatui/ui/update.go) `m.queued` / `drainQueue`): a typed line is staged and submitted as a brand-new follow-up run only when the current run ends. This plan adds an engine-side steer path so a long multi-tool run can be nudged mid-flight. + +The doc is organized scenario-first because acceptance is about what the running harness can demonstrate, not which packages exist on disk. + +The load-bearing enabler: mecatl's LLM adapters are **stateless** (`store:false`, full replay each turn — [`AGENTS.md`](../../AGENTS.md)), so injecting a steer is just appending a `Message{Role: user}` to the history before the next replay. **No provider API support is required** and the feature is portable across Anthropic Messages / OpenAI Responses / Chat Completions. The whole feature is a harness/loop concern: a run-scoped inbox, a turn-boundary injection seam, a wire frame, and an honest client echo. + +## Why these scope cuts + +- **Steer-only, never interrupt.** The steer takes effect at a turn boundary *after* the current streamed response and its tool batch settle — it never preempts or aborts an in-flight model call. Abort-and-replay would break no-replay-after-first-chunk and force retraction of already-emitted events. This matches Claude Code's after-settle behaviour. (Settled in #512.) +- **Append-after-settle, never insert-mid-history.** The steer lands *after* the just-finished tool results as a trailing user message, so the byte-stable prompt prefix ([`engine/agent/loop.go`](../../engine/agent/loop.go) `buildRequest`, assembled once per run) stays a valid cache prefix and the steer pays no prompt-cache penalty beyond normal history growth. +- **Engine inbox = a single append-default slot, not a list.** At most one pending steer *bundle* per run; a second `steer` while one is pending **appends** (`pending += "\n\n" + text`, outcome `SteerAppended`) — supersede and slot_full were dropped in the round-3 rework (Ozz finding #1 + the live-tested stuck-queue). Replacing a pending bundle is the explicit **cancel-then-resend** (`steer_cancel` on `↑` edit-back), with the client re-minting a fresh `message_id`. The TUI's ordered queue holds per-fragment ids; the drain echo's *watermark* id (the tail of the bundle's ordered id-list) splits it — the prefix drained, the suffix pending. +- **Engine is authoritative; the client renders the echoed truth.** The client never assumes what happened to its send — it renders what the engine reports drained (the `EvSteer` echo), and every frame/ack/echo carries a client-minted **`message_id`** the server echoes verbatim, so the client correlates by id (never by text) and ignores a stale ack (one whose id no longer sits in the queue). The ack reports `accepted` / `appended` / `too_late`. +- **gRPC-only v1.** The steer rides a new `Converse` oneof arm on the existing bidi stream ([`harness.proto`](../../contracts/proto/mecatl/v1/harness.proto) `Converse` is already `stream ConverseRequest → stream ConverseResponse`). The HTTP/SSE surface has no client→server mid-run channel; adding it is deferred (see *Out of scope*). +- **Main-run only.** Steer-to-child (subagent / team member / parallel branch) requires a richer parent→child communication channel than exists today and is deferred. + +## In scope — 6 scenarios, in implementation order + +Scenarios are listed in implementation order. Each is independently demoable; later scenarios assume earlier ones but don't change their acceptance criteria. Within each scenario, ACs progress trivial happy path → richer happy path → edges → cross-cutting. + +--- + +### Scenario 1 — Engine steer inbox + turn-boundary injection + +The core: a run-scoped, single-slot mutex inbox on `Run`, drained at the existing **Step 2a** turn-boundary seam ([`engine/agent/loop.go`](../../engine/agent/loop.go) `runLoop`), the SAME provider-legal point `injectBackgroundNotice` and `drainPendingDelivery` already use — history there always ends on a user prompt / tool result / nudge, never inside a `tool_use` pair, so the injected steer replays cleanly and survives `session.ValidateToolPairing` and compaction. The steer is recorded via `recordContinuation` ([`engine/agent/loop.go`](../../engine/agent/loop.go) — `RecordUserPrompt` + the log-only `EvUserPrompt`), exactly like the existing harness-framed continuations. See [`AGENTS.md` — the run-entry / event-log invariants](../../AGENTS.md) and [ADR-0038](../adr/0038-event-sourced-rehydration.md) (event-sourced rehydration must reconstruct the steer like any user turn). + +**Work:** +- engine domain (`engine/session`): no change — the steer is ordinary `RecordUserPrompt` history. +- engine app (`engine/agent`): a `Run`-scoped steer inbox (single pending bundle, **append-default** after round 3 + cancel, mutex-guarded); a Step 2a drain (`drainPendingSteer`) ordered with the existing notice/delivery seams; the drain echo emit. +- ports (`engine/port`): no widening — the inbox is loop-internal, fed by the wire layer below. + +**Acceptance:** +- AC1.1: A steer enqueued while a run is mid-flight is recorded as an ordinary user message at the next turn boundary, appears in `Conversation.Messages`, and is replayed to the model on the following turn. + - verify: `TestSteer_InjectedAtTurnBoundary` +- AC1.2: The steer is recorded via `recordContinuation`, so the durable log captures it (log-only `EvUserPrompt`) and `eventsource.Fold` reconstructs the user turn across rehydration. + - verify: `TestSteer_RecordedAndRehydrated` +- AC1.3: The steer is appended *after* the settled tool results, never inside a `tool_use` pair — `session.ValidateToolPairing` holds on the post-injection history. + - verify: `TestSteer_PreservesToolPairing` +- AC1.4: The injected conversation keeps the message prefix byte-stable within the run (fragments assembled once, conversation appended), so the steer costs no prompt-cache rebuild beyond normal history growth. + - verify: `TestSteer_KeepsPromptPrefixByteStable` +- AC1.5: A run with an empty inbox drives exactly as before (the drain is a no-op) — no behavioural change when steer is unused. + - verify: `TestSteer_EmptyInboxNoOp` +- AC1.6: A steer drained at a boundary where compaction also fires is still replayed **verbatim** to the model on the following turn — the compaction recent-user back-snap (`snapCutToRecentUserTurn`, the "recent USER instruction must survive verbatim" invariant) keeps the just-drained user message out of the summarized head ([`AGENTS.md` — compaction must never emit unpaired history](../../AGENTS.md)). + - verify: `TestSteer_SurvivesCompactionBoundary` +- AC1.7: A pending steer that trips the turn-limit / token-budget brake at the next boundary is recorded (durable history) and the run terminates `StopMaxTurns`/`StopBudget` normally; the recorded steer is addressed by the *next* run — it is never silently dropped and never bypasses `BeginTurn`'s bound ([`AGENTS.md` — the token budget is a turn-boundary clean terminal](../../AGENTS.md)). + - verify: `TestSteer_BrakeTerminalKeepsRecorded` +- AC1.8: A pending (un-drained) steer is **best-effort, in-memory, and lost with the run** on a process crash/session `Abandon` — this is the explicit honest contract, not an emergent property; it is NOT persisted across restart ([`AGENTS.md` — the run-entry `Abandon` seam](../../AGENTS.md)). + - verify: `TestSteer_PendingSteerLostOnRestart` + +--- + +### Scenario 2 — Append-default / cancel contract (the race made observable) + +At most one pending steer *bundle* per run. A second `steer` while one is pending **appends** (`pending += "\n\n"+text`, outcome `SteerAppended`) — supersede/slot_full were dropped in round 3; replacing a pending bundle is explicit **cancel-then-resend** (`steer_cancel` on `↑` edit-back). The client cannot observe the exact drain moment (stream latency), so the engine reports the outcome authoritatively rather than letting the client guess — the drain echo doubles as the recorded-history invariant that [`AGENTS.md` — the recorded == streamed == model-view rule](../../AGENTS.md) pins, and the outcome is an enum, not stacked booleans. Every frame carries a client-minted `message_id` the server echoes verbatim on the ack and the drain echo, so the client correlates by id and ignores a stale ack (id no longer in queue). See the race analysis in #512 + [ADR-0038](../adr/0038-event-sourced-rehydration.md). + +**Work:** +- engine app (`engine/agent`): the inbox's `EnqueueSteer(text) → {accepted | appended | too_late}` / `CancelSteer() → {retracted | none_pending}` / drain transitions; an enum-typed outcome. +- The drain emits `EvSteer` carrying the committed merged bundle + the watermark `message_id` (the tail of the bundle's ordered id-list at the Service wire-correlation layer — the engine inbox is text-only). +- The Service per-session FIFO (`trackSteerMessageID`/`LookupSteerMessageID`/`dropSteerMessageID`) tracks the ordered id-list; the drain echo pops the tail as the watermark the client splits its queue on. + +**Acceptance:** +- AC2.1: At most one bundle is pending per run; a second `steer` while one is pending **appends** (`SteerAppended`), the pending bundle grows by append, and only the pending merged text ever drains as ONE bundle. Replacing a pending bundle is explicit: `steer_cancel` then resend — the cancel-then-recompose path. + - verify: `TestSteer_AppendedMerges`; `TestSteer_NoSupersedePath`; `TestSteer_AppendLinearizable` +- AC2.2: `steer_cancel` on a pending bundle retracts it; the run drains nothing and the next turn sees no injected message. + - verify: `TestSteer_CancelRetractsPending` +- AC2.3: A `steer` that arrives *after* the boundary drained (or after the run went terminal) reports `too_late` — never silently drained into a finished turn. + - verify: `TestSteer_TooLateNotDrained` +- AC2.4: The drain emits `EvSteer` carrying the committed merged text and the watermark `message_id` (the TAIL id of the bundle's ordered id-list the Service tracked) — the client splits its ordered queue on it. The correlation is positional, never textual; the ADR-0228 pin `TestLookupSteerMessageIDExactUnderDuplicateTexts` holds. + - verify: `TestSteer_DrainEmitsCommittedEcho`; `TestSteer_MessageIdRoundTrip`; `TestSteer_WatermarkEchoLatestId`; `TestLookupSteerMessageIDExactUnderDuplicateTexts` +- AC2.5: Enqueue/cancel/drain are safe under concurrent access — no data race, no double-drain. + - verify: `TestSteer_InboxConcurrentSafe` (run under `-race`); `TestSteer_AppendLinearizable` +- AC2.6: The recorded steer, the streamed `EvSteer` echo, and the message the model sees are the same text — the recorded == streamed == model-view invariant holds (steer text is UTF-8-repaired at ingress). + - verify: `TestInvariant_recorded_streamed_model_view`; `TestSteer_IngressUTF8Repaired` + +--- + +### Scenario 3 — Lost terminal race → auto-promote to a fresh follow-up run + +When a steer arrives for a session whose run is already terminal (the user's "still running" belief lagged the real state), the engine does **not** drop the text. It auto-promotes the steer into a fresh follow-up run through the existing hardened run-entry funnel — `loadAndReopen` + the lease + recover-if-terminal ([`internal/adapter/server/service.go`](../../internal/adapter/server/service.go) `StartRunContent`), so promotion reuses the reopen/recover discipline rather than bypassing it ([`AGENTS.md` — every run-entry path must reopen/recover/abandon](../../AGENTS.md)). Transparent to the user; an explicit contract to the engine. + +**Work:** +- composition (`internal/adapter/server`): the wire steer handler routes on `IsLive` / session state — live run → enqueue to inbox; terminal → promote via the prompt funnel. +- engine app: the `too_late`/not-live outcome is the signal the service promotes on. + +**Acceptance:** +- AC3.1: A steer arriving on a live run is enqueued to that run's inbox (not promoted). + - verify: `TestSteer_LiveRunEnqueues` +- AC3.2: A steer arriving after the run went terminal is promoted to a fresh follow-up run that drives through `loadAndReopen` (recovering the terminal state) and is *not* silently dropped. + - verify: `TestSteer_TerminalRacePromotes` +- AC3.3: Promotion reuses the run-entry funnel (recover-if-completed / interrupt-if-cancelled / recover-if-failed) — it never starts a run on an unrepaired terminal session. + - verify: `TestSteer_PromotionUsesRunEntryFunnel` + +--- + +### Scenario 4 — Steer while awaiting an ask (queue-only) + +While a run is parked `awaiting` on a permission or plan ask, the loop is suspended in `PauseForApproval` — it is not iterating `runLoop`, so a steer cannot be drained until the ask is resolved. The steer is held and delivered *after* the verdict resumes the loop, at the resumed run's first turn boundary. The ask still requires an explicit verdict; the steer is purely additive input (the "yes-and" / "no-and" case from #512). See [`AGENTS.md` — the awaiting run-entry seam](../../AGENTS.md) and [ADR-0069](../adr/0069-plan-approval-gate.md). + +**Work:** +- engine app: the inbox holds the pending steer across the parked state; `driveFromAwaiting`/`resumeFromAwaiting` re-enters `runLoop`, whose Step 2a drain picks it up at the first post-resume boundary. + +**Acceptance:** +- AC4.1: A steer submitted while a run is parked `awaiting` is held, not rejected and not delivered early. + - verify: `TestSteer_AwaitingAskIsHeld` +- AC4.2: On verdict-driven resume, the held steer is drained at the resumed run's first turn boundary and replayed to the model. + - verify: `TestSteer_AwaitingResumeDrains` +- AC4.3: The held steer does not resolve, modify, or bypass the pending ask — the ask still requires an explicit verdict. + - verify: `TestSteer_AskStillRequiresVerdict` + +--- + +### Scenario 5 — Wire: gRPC `Converse` frame + capability advertisement + +The steer rides the existing bidi `Converse` stream as a new `ConverseRequest` oneof arm alongside `prompt` / `resume_approval` / `cancel` / `cancel_child` ([`harness.proto`](../../contracts/proto/mecatl/v1/harness.proto)). The server advertises the feature via a new `ServerCapabilities` bit ([`internal/adapter/server.Service.capabilities()`](../../internal/adapter/server/service.go)), following the additive-grow discipline documented on the message (`a new feature adds a new bool field; old clients ignore it, new clients read an old server as false`). See [`AGENTS.md` — capability truth / the ServerCapabilities additive rule](../../AGENTS.md). + +**Work:** +- contracts (`contracts/proto`): `Steer` + `SteerCancel` messages, a `steer` / `steer_cancel` oneof arm on `ConverseRequest`, a `ServerCapabilities.steer` bit, and the `EvSteer` echo on the event stream; `task generate` regenerates `contracts/gen`. +- engine app (`engine/agent`): a `Run`-facing steer entry point the gRPC handler drives (`Engine.Steer`/`Run.EnqueueSteer`), and the `EvSteer` event projection. +- composition (`internal/adapter/server`): the `Converse` handler routes steer frames to the live run's inbox and relays the outcome back to the client. + +**Acceptance:** +- AC5.1: A client can send a `steer` frame mid-run on the `Converse` stream and observe the injected message + the `EvSteer` echo on the same stream. + - verify: `TestSteer_ConverseFrameRoundTrip` +- AC5.2: `ServerCapabilities` advertises `steer` true when the feature is enabled and false/absent when not; an old server (no field) reads as false. The bit is computed **once** in composition (`Service.capabilities()`) and is consistent across every sink that surfaces it (the `CreateSession` echo and any re-hydration path) — the single-composition-computed-intersection invariant ([`AGENTS.md` — capability truth](../../AGENTS.md)); never recomputed per sink. + - verify: `TestSteer_CapabilityAdvertised`; `TestSteer_CapabilitySingleSource` +- AC5.3: `task generate` keeps `contracts/gen` in sync; the new oneof arm does not change the behaviour of existing arms. + - verify: inspection — `buf generate` output committed; existing Converse control frames unchanged. + +--- + +### Scenario 6 — Composition gate + mecatui flip + +The feature is **posture/run-level, default-on** (settled in #512): the engine steer inbox is armed by a config/posture knob resolved in composition ([`internal/app/posture.go`](../../internal/app/posture.go) — the `strict < trusted < auto < yolo` ladder), and the TUI flips between engine-steer (when the capability is advertised) and its existing local merge-queue (when not). The posture-→knob coupling lives in composition only ([`AGENTS.md` — the posture ladder](../../AGENTS.md)). The user sees the pending merged message as one thing being sent; `↑` edits the combined queued message (client-side merge). + +The client-side merge and the engine-side slot are **distinct mechanisms that must not be conflated**: the TUI merges staged lines into one bundle *before* sending (one `message_id`), so the engine only ever sees one frame at a time; editing an already-sent-but-un-drained steer is **cancel-then-recompose** — `↑` issues a `steer_cancel` for the outstanding bundle, pulls the whole not-yet-drained set into the input as ONE editable blob, and resends as ONE replacement bundle with a fresh id. A drained `message_id` is **burned**: editing after the drain echo is a fresh draft (a new submission). + +**Work:** +- composition (`internal/app` / `cmd/*`): the steer enable knob wired through `app.Build` into the engine deps + the `ServerCapabilities` bit; default on. +- `cmd/mecatui`: read the `steer` capability; when present, `enter` mid-run sends a `steer` frame instead of only staging locally, and the queue card reflects the authoritative echoed/acked state (pending → sent → too-late-promoted). When absent, keep the #228 local-queue behaviour byte-identical. + +**Acceptance:** +- AC6.1: With steer enabled (default), the capability is advertised and a mid-run input reaches the model without a separate follow-up run. + - verify: `TestSteer_EnabledByDefaultEndToEnd` +- AC6.2: With steer disabled, the engine inbox is inert and the TUI falls back to the client-side terminal queue unchanged. + - verify: `TestSteer_DisabledFallsBackToLocalQueue` +- AC6.3: The TUI renders the merged pending message as a single item, reflects the echoed/acked state honestly (queued-until-landed: a pending card at the bottom until the `EvSteer` echo lands it in context at its true position; sent vs promoted-after-race), and `↑` cancels the outstanding bundle and pulls the whole not-yet-drained set back as ONE editable blob (cancel-then-recompose, fresh `message_id` on resend; a late `none_pending` ack means the drain won — the steer shipped). + - verify: `TestSteer_TUIRendersAuthoritativeState`; `TestSteer_CardGolden`; `TestSteer_TUIQueuedUntilLanded`; `TestSteer_TUIEditCancelThenRecompose`; `TestSteer_TUIEditBackNoDuplicate` +- AC6.4: `go run ./cmd/mecademo` still prints a full offline session (no behavioural regression with steer unused). + - verify: demonstration — `go run ./cmd/mecademo` + +--- + +## Out of scope + +| Item | Defer-to | ADR / decision | +|---|---|---| +| HTTP/SSE steer endpoint (`POST /v1/sessions/{id}/steer`) | follow-up | deferred in #512; gRPC-only v1 — must be noted in the PR + ADR | +| ACP steer (`mecated acp`) | follow-up | ACP's `session/prompt` blocks one-in-flight with no mid-run client→server channel (`internal/adapter/acp/agent.go`), so ACP editors cannot steer in v1 — named here so the "gRPC-only" cut isn't read as covering all clients; must be noted in the PR + ADR | +| Steer-to-child (subagent / team member / parallel branch) | follow-up | needs a richer parent→child input channel than `CancelChild`; settled in #512 | +| Interrupt verb (abort in-flight response + replay) | not planned | steer-only; breaks no-replay-after-first-chunk | +| Intent-vs-action timing (record when a steer was *submitted* vs *drained*) | future enhancement | filed from #512; persist later, not this plan | +| Multiple distinct pending steers (a real list, not a single slot) | not planned | single append-default bundle; merge is engine-side by append (client-side: the ordered per-fragment queue) | +| Per-model steer disable | only if it proves needed | the gate is posture/run-level; a per-model dimension is a possible follow-up | + +## Cross-cutting deliverables + +- **ADR-0228** landing with this plan — the steer contract **as shipped** (rounds 2–3 rework applied): engine append-default single-slot inbox, watermark `message_id` correlation, the auto-promote-on-terminal-race rule + sequential active-run handoff, the clean-exit continue-run rule, the queue-only awaiting behaviour, and the gRPC-only-v1 / HTTP-deferred scope cut. Copy `docs/adr/template.md`. +- **`docs/usage/http-sse-api.md`** — a note that steer is gRPC-only in v1 (the HTTP run path has no mid-run client→server channel). +- **`docs/tui.md`** — the steer-mode queue card (pending / sent / promoted) and the capability-driven flip vs the local queue. +- **`docs/architecture.md`** — the steer inbox + Step 2a seam under the loop section (living "how it works"). +- **`engine/CHANGELOG.md` + `engine/api/*.txt`** — the new `Engine.Steer` / `Run.EnqueueSteer` (and the `EvSteer` event) are exported engine-module surface, so `task api:update` is **required** (not conditional), with an `engine/CHANGELOG.md` note classified per `engine/COMPATIBILITY.md` (Added = minor). + +## Sequencing recommendation + +Scenario 1 (engine inbox + boundary injection) is the foundation and lands first — everything else builds on the drain seam. Scenario 2 (the slot-full / cancel contract) is the same `Run` structure and pairs naturally with 1. Scenario 3 (auto-promote) and Scenario 4 (awaiting) are independent service/loop behaviours that both depend only on 1. Scenario 5 (wire) depends on 1–2 and unblocks 6. Scenario 6 (composition + TUI) is last and the only client-facing piece. Orchestrate takes over at decomposition; the engine scenarios (1–4) parallelize cleanly once the seam exists. + +## Named tests landing in this plan + +`TestSteer_AcceptedOnEmpty`, `TestSteer_AppendAckAdvancesPhase`, `TestSteer_AppendedDrainMerged`, `TestSteer_AppendedMerges`, `TestSteer_AppendLinearizable`, `TestSteer_AskStillRequiresVerdict`, `TestSteer_AwaitingAskIsHeld`, `TestSteer_AwaitingResumeDrains`, `TestSteer_BurnedAckStillIgnored`, `TestSteer_CancelRetractsPending`, `TestSteer_CapabilityAdvertised`, `TestSteer_CapabilitySingleSource`, `TestSteer_CardGolden`, `TestSteer_CleanExitContinuesRun`, `TestSteer_ConverseCancelRetracts`, `TestSteer_ConverseFrameRoundTrip`, `TestSteer_DisabledFallsBackToLocalQueue`, `TestSteer_DrainEmitsCommittedEcho`, `TestSteer_EmptyInboxNoOp`, `TestSteer_IdLessEchoClearsQueue`, `TestSteer_InboxConcurrentSafe`, `TestSteer_IngressUTF8Repaired`, `TestSteer_InjectedAtTurnBoundary`, `TestSteer_KeepsPromptPrefixByteStable`, `TestSteer_LiveRunEnqueues`, `TestSteer_MergeThreeIntoOneDrain`, `TestSteer_MessageIdRoundTrip`, `TestSteer_NeverClosedParked`, `TestSteer_NoSupersedePath`, `TestSteer_OutcomeMatrix`, `TestSteer_PendingSteerLostOnRestart`, `TestSteer_PreservesToolPairing`, `TestSteer_PromotedRelaySequential`, `TestSteer_PromotionUsesRunEntryFunnel`, `TestSteer_ProtoEnumAppend`, `TestSteer_RecomposedFragmentRendersPerPart`, `TestSteer_RecordedAndRehydrated`, `TestSteer_SurvivesCompactionBoundary`, `TestSteer_TerminalRacePromotes`, `TestSteer_TooLateNotDrained`, `TestSteer_TUIBurnedIdFreshDraft`, `TestSteer_TUIEditBackNoDuplicate`, `TestSteer_TUIEditCancelThenRecompose`, `TestSteer_TUIIgnoresStaleAck`, `TestSteer_TUIQueuedUntilLanded`, `TestSteer_WatermarkSplitOnQueuedAck`, `TestLookupSteerMessageIDExactUnderDuplicateTexts`, `TestInvariant_recorded_streamed_model_view`. + +## Definition of done + +1. `task lint` and `task test` pass (both modules, `-race`). +2. `task docs` — `llms.txt` regenerated and the matlatl strict link gate green. +3. `task generate` — `contracts/gen` regenerated from the proto change and committed. +4. `task api:update` was run — the engine exported surface changes (new `Engine.Steer`/`Run.EnqueueSteer` + `EvSteer`), so the api-compat gate requires the regenerated `engine/api/*.txt` and an `engine/CHANGELOG.md` note (Added = minor, per `engine/COMPATIBILITY.md`). +5. `task ac-trace-strict` — every AC's `verify:` proof resolves (this plan is `landed`). +6. The named tests above are green and grep-locatable by their identifiers. +7. `go run ./cmd/mecademo` still prints a full offline session. +8. ADR-0228 lands; the HTTP/SSE deferral is noted in the PR body and ADR. + +## Deferred decisions and known risks + +- **The drain-observability race.** The client cannot observe the exact drain moment, so it renders the authoritative `EvSteer` echo (correlated by `message_id`); the ack reports `accepted` / `appended` / `too_late` / `none_pending` honestly. Resolved by Scenario 2's contract (engine authoritative, client renders echoed truth, id-correlated) — the residual UX polish (the "sending… vs sent" affordance) is scenario 6. +- **HTTP/SSE gap.** v1 is gRPC-only; an HTTP client cannot steer (no mid-run channel). Documented deferral; the unary `POST .../steer` endpoint is the cheap follow-up shape (mirrors `approve`/`cancel`). +- **Child steer.** Not built; requires a parent→child input channel. A reader must not assume steer reaches children — see *Out of scope*. +- **Prompt-cache.** The append-after-settle design keeps the prefix byte-stable, so the only cost is normal history growth (AC1.4 pins this). If a future variant ever inserts mid-history, the cache cost must be re-evaluated. + +## Exit criteria + +When every point under *Definition of done* holds on the accumulator, this plan is satisfied. diff --git a/docs/adr/0027-cloud-native.md b/docs/adr/0027-cloud-native.md index 3498990005..568fd68753 100644 --- a/docs/adr/0027-cloud-native.md +++ b/docs/adr/0027-cloud-native.md @@ -816,13 +816,16 @@ durable artifact survives and is reloaded), or **lost** (gone, possibly leaking) | 52 | Opt-in MCP OAuth loopback interaction: random IPv4 listener, bounded callback HTTP server, runtime serialization gate, and optional browser child (issue #522, ADR 0112) | `mcp/oauthlogin.Runtime`; the one-shot caller owns the `Authorize` operation | one authorization operation; the gate lives with the explicitly constructed runtime | every return path cancels presentation, publishes a terminal callback outcome, performs detached bounded `http.Server.Shutdown`, closes the listener, joins `Serve`, and only then releases the runtime gate. The fixed-argv browser command inherits the operation context and is killed on cancellation; no refresh goroutine exists | **per-operation / reset-by-design**: listener, server, callback state, and browser process must not survive. The resulting credential is row 51's durable adapter record. No List 2 row because no session/run state is held | `mcp/oauthlogin/runtime.go` (`Runtime`, `Authorize`, `stopServer`); `mcp/oauthlogin/callback.go`; `mcp/oauthlogin/browser.go`; invoked by `internal/app/mcplogin.go` (`LoginMCP`) | | 53 | Explicit environment credential Reader handle (issue #542, ADR 0221) | canonical MCP environment profiles loaded by `internal/cliconfig.MCPProfiles` and transferred to `app.Build`; used by mecak8s/mecatequi/mecated serving without a presenter | namespace/key/environment-name/lookup tuple | no goroutine, fd, cache, mutation, or global lookup; `EnvironmentReader.Close` idempotently seals the borrowed lookup handle. Each `Get` snapshots its immutable lookup configuration under the lifecycle read lock, invokes the host callback without that lock, then rechecks closure before returning | **explicitly reattached / source-defined**: restart reconstructs the Reader with the same tuple and receives the process/pod's current environment snapshot. In-memory refresh is not written back. Kubernetes Secret env rotation requires an external controller and pod restart, or a future Secret `resourceVersion` CAS writer. No List 2 row: this is adapter credential state, not session/run state | `internal/adapter/credentialstore/environment.go` (`EnvironmentReader`, `NewEnvironment`, `Close`); `internal/cliconfig/mcpprofile.go` (`LoadMCPProfiles`) | | 54 | Canonical MCP profile loader's shared encrypted Stores and per-profile environment Readers (issue #523, ADR 0113) | `internal/cliconfig` (`MCPProfiles`), transferred to `app.Build` through `Config.MCPProfileLifecycle` | process / one loaded operator profile set; local Stores are shared by `(root, key-env reference)` within the load | partial-load failure closes every source already opened; successful Build closes the global MCP manager/controllers first, then the profile lifecycle, and `MCPProfiles.Close` closes every distinct Store/Reader exactly once. The default/no-profile path opens nothing and owns nothing | **persisted / explicitly reattached for local; source-defined for environment**: restart reloads operator metadata and references, derives the identical opaque record key, and reopens row 50/53. Process-local read-only refresh resets by design. No List 2 row because credentials remain adapter state, not session/run state | `internal/cliconfig/mcpprofile.go` (`LoadMCPProfiles`, `MCPProfiles`, `Close`); `internal/app/build.go` (`Config.MCPProfileLifecycle`, `Build`) | - | 55 | Automatic-learning sliding reservations, per-principal cooldown map, and completed trajectory-digest LRU (ADR 0114) | `app.Build` constructs one `automaticAdmissionController` only when automatic learning is enabled and shares it across main/per-session reflection observers | process (one mutex; one-hour process/principal count+reserved-token entries, per-principal weighted cooldowns, and a 24-hour completed-digest LRU capped at 1024; digests contain no raw identity/text labels) | no goroutine or durable handle. Coordinator capacity and in-flight duplicate checks run before the reservation callback; accepted failures/timeouts/abstentions retain reservations. `Built.Close` closes the coordinator, and the controller dies with composition. No shutdown/startup catch-up | **reset-by-design**: restart resets windows, cooldowns, and completed digests. Durable proposal IDs/CAS remain authoritative and converge any retry. Budgets are deliberately process-local; multiple replicas multiply aggregate capacity. See List 2 row 32 | `internal/app/learning_controller.go` (`automaticAdmissionController`, `reserve`, `complete`); `internal/app/reflection_observer.go` (`submit`) | | 56 | Durable session-migration job registry and per-drive Redis lock renewer (issue #589, ADR 0226/0230) | `jsonlstore.Store` or `redisstore.Store`; authenticated `server.Service` orchestrates caller-driven batches | one bounded durable record per explicit job; Redis additionally owns one lock/fence key and one renewal goroutine only for the active drive | records checkpoint after each family. Both stores return a context bound to the exact required port acquisition: jsonlstore holds the stable cross-process flock; Redis renews its fenced token, cancels the bound operation context on loss, checks the token atomically inside mutation/finalization scripts before any write, joins the renewer, and releases token-conditionally. A stale driver cannot mutate, checkpoint, cancel, finalize, or release a successor. Completed records remain inspectable. Per-family work borrows existing run-entry/lease/family locks and closes files per operation | **persisted/reattached**: restart reloads caller-bound counters, terminal item handles, cancellation, and bounded stable errors; the ephemeral lock acquisition is deliberately lost and a later drive takes a newer fence. Records exclude raw session ids, principals, paths, backend errors, and content. See List 2 row 35 | `internal/adapter/store/jsonlstore/migration.go` (`SaveSessionMigrationJob`, `LoadSessionMigrationJob`); `internal/adapter/redisstore/migration.go` (`AcquireSessionMigrationJob`, `renewMigrationLock`); `internal/adapter/server/migration.go` (`driveSessionMigration`) | | 57 | Cleanup confirmation signer + bounded opaque-plan/sanitized-job registries (`Service.cleanupTokenKey`, `cleanupPlans`, `cleanupJobs`) | `server.Service` | process (one random HMAC key, at most 128 caller-bound plan payloads, and at most 128 job projections) | no goroutine or external handle; `Service.Close` drops them with the Service. Entries evict oldest when their cap is reached. Tokens expose only an unguessable MAC; stored payloads contain principal/catalog/scope/policy digests and counts, while jobs contain stable codes and sanitized messages | **reset-by-design**: restart invalidates outstanding confirmation tokens and in-memory job inspection; no deletion is resumed implicitly. A caller performs a fresh read-only plan and retries remaining candidates. Committed store deletions are already durable and idempotent | `internal/adapter/server/cleanup.go` (`PlanSessionCleanup`, `ApplySessionCleanup`, `rememberCleanupJob`); `internal/adapter/server/service.go` (`cleanupTokenKey`, `cleanupPlans`, `cleanupJobs`) | | 58 | Manual dream review registry/coordinator (ADR 0228) | `app.Build` constructs one `dreamReviewCoordinator` and injects it into `server.Service`; absent when no target is capable or ownership enforcement is enabled | process (opaque random IDs; at most 64 total records and 8 non-terminal records per target; each generation/application shares its target consolidator's existing row-17 gate) | no goroutine, timer, fd, or client cleanup operation. Generation/decision lazily remove expired non-active records. Terminal records retain only decision/receipt/failure state for idempotence and immediately zero target, authoritative plan, and review content; all records die with Build/process | **lost by design**: a pending plan is intentionally neither persisted nor replicated. Restart, expiry, or a decision routed to another replica requires explicit regeneration and another provider call. This is not HA/sticky-route portable in v1. See List 2 row 36 | `internal/app/dream_review.go` (`dreamReviewCoordinator`, `Generate`, `Decide`, `cleanupLocked`, `finishLocked`); `internal/adapter/server/dreamreview.go` (`DreamReviewer`) | | 59 | Engine-child liveness registry + distributed child leases (`sessionLiveness`) | `app.Build`; shared by main/per-session engine `Deps.SessionLiveness`, direct `RunTeam` supervisors via `agent.WithMemberLiveness`, and `server.Service` | process registry plus child lifecycle (one reference-counted distributed hold per queued/running/between-round/synthesis Subagent, Parallel-branch, or Team-member session ID) | registration acquires the configured `SessionLease` before a child becomes runnable and fails the child start if acquisition fails. Each hold has a bounded renewer; definitive/expiry-near loss cancels every local registrant. Existing terminal and pre-start-abort chokepoints release idempotently; release first cancels and joins renewal, then uses a cancel-detached bounded context. Background Subagents hold through detached completion; direct supervisors hold every member across planning gaps and synthesis until `cleanupAll`; Team-tool members receive equivalent coverage through the parent child registry. Background Bash is excluded because it has no session. `Build.Close` closes this owner after `Service.Close` has cancelled/joined runs and before the lease/store backend closes | **reconstructible/reset-by-design**: local reference counts reset on restart; a crashed process loses child execution and its distributed hold lapses by TTL. A survivor may only clean or resume after that expiry. No child lifecycle state is newly persisted | `internal/app/session_liveness.go` (`sessionLiveness`, `renewLoop`, `Close`); `engine/agent/childregistry.go` (`registerProtected`, `markDoneResult`, `remove`); `engine/agent/teamsupervisor.go` (`WithMemberLiveness`, `Run`, `cleanupAll`); `internal/adapter/server/service.go` (`IsLive`) | +| 60 | Steer-while-running wire plumbing (issue #512, ADR 0232): the `Run`-scoped `steerInbox`, per-stream acknowledgment lane and single-writer gate, promote-path handoff mailbox, and per-session `steerMsgIDs` watermark list | inbox on `agent.Run`; stream plumbing on one `Converse` RPC; watermark list on `server.Service` | run / RPC / session | the inbox drain-closes at terminal; stream state dies with the RPC; watermark entries are consumed on drain or dropped on retract | **reset-by-design**: in-memory wire/run plumbing, never durable | `engine/agent/steer.go` (`steerInbox`); `internal/adapter/server/grpc.go` (`steerHandoff`); `internal/adapter/server/service.go` (`steerMsgIDs`) | + +**Steer-while-running re-audit (List 1 / List 2 — issue #512, ADR 0232).** List 1 row 60 inventories the in-memory inbox, stream handoff, and watermark correlation. List 2 row 37 records the pending bundle's deliberate restart-loss; only a drained steer becomes durable ordinary user history. + **Session-storage temp-locking re-audit (List 1 / List 2 — issue #586, ADR 0226).** The jsonlstore resource in List 1 row 8 now includes one stable per-family flock sentinel and generation/owner-tagged replacement temps. The flock is acquired per operation and @@ -1041,6 +1044,7 @@ what is persisted), **reset-by-design** (documented, acceptable), | 34 | Legacy-adoption source relationship and idempotency proof (issue #593) | optional `session.Session.Adoption` metadata persisted by `sessnap.Snapshot` as the flat `adoption_source_id` / `adoption_request_digest` fields; only the authenticated server adoption path stamps it | restart reloads the target's immutable source link and caller/source/request digest, so a lost-response retry deterministically resolves to the same complete main session without an in-memory key map | **persist-in-snapshot**: the raw idempotency key is not persisted; its caller/source/request-bound SHA-256 digest and the opaque deterministic target ID are sufficient to converge retries. The legacy source remains unchanged | ADR 0226 | | 35 | Session-migration progress and partial completion (issue #589; List 1 row 56) | adapter-private durable migration-job record plus authoritative v1/v2 families | restart reloads the caller binding, state, counters, terminal non-reversible item handles, and bounded stable errors. A committed family is already v2-authoritative even if its following checkpoint was lost; rescan converges without rollback | **persist-in-store / derive from families**: jobs checkpoint after each family, cancellation is forward-only, and authoritative family state resolves a lost checkpoint. Raw ids, principals, paths, backend errors, and content are not persisted in the registry | ADR 0226 | | 36 | Manual dream pending plans and terminal decision receipts (ADR 0228; List 1 row 58) | process-local `dreamReviewCoordinator.records`; each pending record holds the instance-bound versioned `dream.Plan` plus its detached review, and each terminal record retains only decision/receipt/failure state until lazy TTL eviction | restart or a wrong replica loses every plan and receipt. A pending plan cannot be decided; the operator must explicitly regenerate, spending another provider call. A same-process repeated identical decision is idempotent only while its terminal record remains | **reset-by-design**: authoritative mutation material is deliberately not persisted, reconstructed from a review projection, or accepted from the client. v1 is not HA/sticky-route portable. Durable memory revisions/tombstones from already completed independent operations remain the store's truth; there is no grouped rollback | 0228 | +| 37 | Pending (un-drained) operator steer (issue #512, ADR 0232; List 1 row 60) | the `Run`-scoped `steerInbox` pending bundle — in-memory only, never written to the SessionStore / snapshot; plus its wire-correlation half, the per-session `steerMsgIDs` ordered list on `server.Service` | **restart-lost BY DESIGN**: a parked steer dies with its run (a process crash, a `Run.Cancel`, or a session `Abandon`) and is never persisted, so nothing rehydrates it — the operator re-sends on the next run. Only a steer that drained at a turn boundary (or the terminal close-drain, `closeSteerDrained`) survives, as an ordinary recorded user continuation in durable history, replayed like any user turn and reconstructed by `eventsource.Fold`. The clean-exit continue-run rule (`finishTurnNoTools`) keeps a live run from terminating while a steer is parked, so the loss window is crash/cancel-only, never a normal end. The `message_id` list is lost alongside (it correlates only live echoes) | **reset-by-design**: the inbox is best-effort mid-run input, not state-of-record — the same posture as the run-scoped ephemera cluster (rows 13/14/16); the never-drop guarantee is engine-internal for a LIVE run, not a durability claim | 0232 | Two ledger observations worth stating in prose: diff --git a/docs/adr/0232-steer-while-running.md b/docs/adr/0232-steer-while-running.md new file mode 100644 index 0000000000..d77c067215 --- /dev/null +++ b/docs/adr/0232-steer-while-running.md @@ -0,0 +1,179 @@ +# ADR 0232 — Steer-while-running: inject a user message into an in-flight run + +- Status: Accepted +- Date: 2026-08-13 +- Scope: the agent loop (`engine/agent`), the gRPC `Converse` wire, the service/composition layer, and mecatui. + +## Context + +mecatl's queued follow-ups (issue #228) are **client-side and turn-terminal**: a +line typed while a run streams is staged in the TUI and submitted as a brand-new +follow-up run only when the current run ends. A long multi-tool run cannot be +nudged mid-flight. Claude Code's "steer while running" folds queued input into the +*running* turn: the message is injected as a user message between the settled tool +results and the next model replay, so the agent course-corrects without a separate +turn (issue #512). + +The load-bearing enabler: mecatl's LLM adapters are **stateless** (`store:false`, +full replay each turn). Injecting a steer is appending a `Message{Role: user}` to +the history before the next replay — **no provider API support is required**, and +the feature is portable across Anthropic Messages / OpenAI Responses / Chat +Completions. The whole feature is a harness/loop concern. + +The subtle risks, called out in #512: the supersede/drain race (the client cannot +observe the exact drain moment across stream latency), the lost terminal race (a +steer arriving after the run went terminal), and the recorded == streamed == +model-view invariant. + +## Decision + +- **Steer-only, after-settle, never interrupt.** The steer takes effect at a turn + boundary *after* the current streamed response and its tool batch settle. It + never preempts or aborts an in-flight model call (that would break + no-replay-after-first-chunk and force retraction of already-emitted events). +- **Append-after-settle, never insert-mid-history.** The steer is appended *after* + the just-finished tool results as a trailing user message, so the byte-stable + prompt prefix stays a valid cache prefix and the steer pays no prompt-cache + penalty beyond normal history growth. +- **A `Run`-scoped, single-slot, append-default mutex inbox** + (`engine/agent/steer.go`). At most one pending steer bundle per run; a second + `EnqueueSteer` on the occupied slot **APPENDS** — `pending += "\n\n" + text`, + outcome `SteerAppended` (supersede and slot_full were dropped in the round-3 + rework — a channel can't linearize the replace/close, and a silent replace is a + lost user message): replacing a pending steer is an explicit **cancel-then-resend** + (`CancelSteer`, then a fresh steer with a fresh `message_id`). The state is a + mutex-guarded `{closed, pending, has}` triple — every transition is ONE critical + section, so the observe-then-replace race is impossible by construction. + `CancelSteer` retracts; the boundary drain commits. The inbox is in-memory and + **best-effort**: a pending (un-drained) steer is lost with the run on a crash — + the explicit honest contract, not persisted across restart (reset-by-design; + inventoried in [ADR 0027](./0027-cloud-native.md) List 1 row 60 / List 2 row 37). + Steer text is repaired to valid UTF-8 at ingress (`session.ToValidUTF8`), so + recorded history, the echo, and the model view stay byte-identical. +- **Drained at the Step 2a turn-boundary seam** in `runLoop` (the same + provider-legal point `injectBackgroundNotice`/`drainPendingDelivery` use), + recorded via `recordContinuation` (`RecordUserPrompt` + the log-only + `EvUserPrompt`), so it replays cleanly, survives `session.ValidateToolPairing` + and compaction, and rehydrates under [ADR 0038](./0038-event-sourced-rehydration.md). +- **The engine is authoritative; the client renders the echoed truth.** The drain + emits `EvSteer` carrying the committed text; the enqueue/cancel outcome is a + closed enum (`accepted`/`appended`/`retracted`/`none_pending`/`too_late`), not + booleans. +- **Client-minted `message_id` correlation (watermark).** Every `Steer` frame + carries a unique client-minted `message_id`; its `steer.outcome` ack echoes + the frame's own id verbatim. On the `EvSteer` drain echo the wire carries a + **watermark** — the LATEST (tail) contributing send's id of the bundle that + just drained — and the client splits its ordered queue on it (sends up to and + including the watermark drained; sends after it still pending). The client + ignores a stale ack (one whose id it no longer holds in the queue). + Correlation is positional, never text-match. The engine inbox parks text only, + so the id lives at the Service's per-session FIFO (`trackSteerMessageID` / + `LookupSteerMessageID` / `dropSteerMessageID`) of the ordered frame ids. Ids + are clamped to a 64-rune prefix at track (CWE-770). + **Fragility (recorded deliberately).** The text-only-engine + Service-FIFO split + correlates the echo's id **positionally** (the TAIL of the session's ordered + id-list for the bundle), *never* by text-match — sound today because the TUI's + one-bundle-outstanding invariant makes send order == drain order. It silently + breaks (echoes `""`) if a future sender stops batching, or if a second sender + surface (e.g. the deferred HTTP/SSE steer endpoint) lands. When that happens, + move the id into the engine inbox (`steerInbox.pending` as a `{text, id}` pair) + rather than growing a heuristic at the wire layer; + `TestLookupSteerMessageIDExactUnderDuplicateTexts` (in + `internal/adapter/server/steer_watermark_pin_test.go`) is the pin this + paragraph promises — identical-text sends MUST return the tail id. +- **The clean-exit continue-run rule.** A would-be clean end (meaningful text, + benign stop) while a steer is still parked does NOT terminate: `finishTurnNoTools` + re-enters the loop so the very next Step 2a drains the steer and the turn it + feeds consumes it. The never-drop contract stays engine-internal — the run + extends (bounded by `Limits.MaxTurns`), no promote, no client handoff, no + injected nudge text. The terminate paths drain-then-close + (`closeSteerDrained`): a steer parked when the run ends for another reason is + recorded into durable history first, never closed unconsumed. +- **Lost terminal race → auto-promote + sequential active-run handoff.** A steer + arriving for a session whose run is already terminal is promoted to a fresh + follow-up run through the existing hardened run-entry funnel + (`StartRunContent`/`loadAndReopen` + lease + recover-if-terminal) — never + silently dropped. Promotion reuses the funnel; it never starts a run on an + unrepaired terminal session. The promote path alone awaits the original run's + deregistration, bounded by the promote-grace (`steerPromoteGrace`): the + just-terminal run's relay may still be draining (still registered), and only a + terminal-but-still-registered run can deregister inside the grace — a run still + registered at the lapse is genuinely in-flight, so the promotion is refused. + The promoted run is handed off **sequentially on the same Converse stream**: + the RPC relays the original run, then relays each promoted run in turn before + returning — one relay owner at a time (`sendErr` single-owner, every `Send` + across the one `streamSender` mutex), the control target (`ResumeApproval` / + `Cancel` / `CancelChild`) swaps to the promoted run atomically before its relay + starts, and the promoted run is `FinishRun`-deregistered before the RPC returns + (its terminal outcome is reported inline as the `steer.outcome` ack with + `promoted=true` — never an orphaned relay, never an ack after close). +- **Awaiting-ask steer is queue-only.** While a run is parked `awaiting` on a + permission/plan ask, the loop is suspended and the steer is held, drained at the + resumed run's first boundary after the verdict. The ask still requires an + explicit verdict; the steer is purely additive ("yes-and"/"no-and"). +- **gRPC-only v1.** The steer rides a new `steer`/`steer_cancel` oneof arm on the + existing bidi `Converse` stream, plus a `ServerCapabilities.steer` bit (additive + grow) and the `EvSteer` echo. **HTTP/SSE and ACP steer are deferred** — the HTTP + run path (`POST /v1/sessions/{id}/prompt`) and ACP's blocking `session/prompt` + have no client→server mid-run channel; a unary `POST .../steer` (mirroring + `approve`/`cancel`) is the cheap follow-up shape. This deferral is recorded in + the PR body. +- **Posture/run-level gate, default-on, posture-independent.** `Config.DisableSteer` + (opt-out) → `Deps.EnableSteer` → `ServerCapabilities.steer`; the capability bit + is computed once in composition. mecatui flips between engine-steer (capability + present) and the #228 local merge-queue (absent) on it. +- **Queued-until-landed rendering.** The client renders a sent-but-un-drained + steer as a PENDING card at the bottom of the transcript; when the `EvSteer` echo + lands, the card clears and the message appears IN CONTEXT at its true position + (the echo is stream-positioned — emitted after the settled tool results, before + the next `turn.start`). The client never infers position by text-matching + history; the `message_id` tells it which in-flight send the echo commits. +- **Client edit = cancel-then-recompose.** mecatui keeps an ordered queue of + sends (id + fragment per `enter`); `↑` issues a `steer_cancel` for the + outstanding bundle (the watermark id), pulls the pending sends into the input + as ONE editable blob, and resends as a fresh fragment under a NEW + `message_id` (already-drained sends are never re-sent — the watermark split + keeps the queue honest). The late ack reconciles the race: `retracted` = + clean edit, `none_pending` = the drain won (the steer shipped as sent). +- **Main-run only.** Steer-to-child (subagent/team/parallel) requires a richer + parent→child input channel than `CancelChild` and is deferred. +- **All Converse-stream `Send`s are serialized** behind a single-writer gate, and + the wire routing has one owner (`Service.Steer`/`Service.CancelSteer`); the gRPC + handler is a dumb frame→Service mapper (panel-review repair). + +## Consequences + +- A running agent can be steered mid-flight across every provider, with no API + change — closing the #228 gap between terminal-queue and boundary-injection. +- The client contract is honest about the race: the drain echo, the outcome enum, + and the `message_id` correlation make the committed text observable rather than + assumed — and an occupied inbox merges (append) while replacement is an + explicit cancel-then-resend, never a silent replace. +- The terminate-window steer reliably promotes (the promote path awaits the + original run's deregistration, bounded by the promote-grace) instead of + dropping, and the promoted run relays sequentially on the same stream before the + RPC returns — the client never has to orchestrate a second stream, and the + control surface follows the active run. +- The steer genuinely steers mid-task: the clean-exit continue-run rule keeps a + live run from ending while a steer is parked, so an accepted steer is never + dropped engine-internally. +- The promoted follow-up run rides the Converse stream's handler ctx: a client + that disconnects mid-promotion cancels the follow-up (parity with a prompt's + own client-bound lifetime — `drainChildren` + `FinishRun` persist the cancelled + run, so nothing leaks). A client holding the stream for the *original* run sees + a shorter window for the promoted one; noted as accepted for v1. +- **Costs:** a steer grows history (re-sent every subsequent turn, like any user + message); the TUI maintains a second (server-authoritative) mid-run input state + machine alongside the local queue; HTTP/SSE and ACP clients cannot steer in v1 + (documented deferral). +- Child runs arm an inbox no wire path can reach (steer-to-child deferred) — + harmless, enforced implicitly by registration. + +## See also + +- The acceptance plan + verification contract: [steer-while-running](../acceptance/steer-while-running.md). +- The loop seam: [the agent loop](../architecture/agent-loop.md) § Steer-while-running; + the run-entry funnel and the recorded == streamed == model-view invariant in `AGENTS.md`. +- [ADR 0038](./0038-event-sourced-rehydration.md) (event-sourced rehydration), + [ADR 0069](./0069-plan-approval-gate.md) (the awaiting seam), and the lifecycle + convention in [ADR 0002](./0002-documentation-lifecycle.md). diff --git a/docs/adr/README.md b/docs/adr/README.md index 07734ff5d9..dacd1a2fd5 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -43,6 +43,7 @@ Documentation/citation conventions are in [`docs/design/README.md`](../design/RE - [0112 — Host-owned loopback MCP OAuth login](./0112-mcp-oauth-loopback-runtime.md) - [0113 — Operator MCP authentication profiles and explicit login](./0113-operator-mcp-auth-profiles.md) - [0223 — Pin MCP transport-error semantics that do not replay rejected calls](./0223-mcp-sdk-transport-error-semantics.md) +- [0232 — Steer-while-running: inject a user message into an in-flight run](./0232-steer-while-running.md) - [0036 — `engine/` is its own Go module (monorepo via `go.work`)](./0036-engine-module.md) - [0037 — Engine public-API stability contract](./0037-engine-stability-contract.md) - [0038 — Event-sourced SessionStore rehydration (the reference fold)](./0038-event-sourced-rehydration.md) diff --git a/docs/architecture/agent-loop.md b/docs/architecture/agent-loop.md index adf07689c2..5ff6f74f1a 100644 --- a/docs/architecture/agent-loop.md +++ b/docs/architecture/agent-loop.md @@ -2,7 +2,7 @@ > Part of the [mecatl architecture guide](../architecture.md). -**What this covers:** the `Engine.Run` drive algorithm, read-parallel / mutate-serial dispatch, permission pause/resume (`askRegistry`, `Run.Approve`), the plan-approval gate (`PresentPlan`), the token budget, and bounded no-progress nudging. +**What this covers:** the `Engine.Run` drive algorithm, read-parallel / mutate-serial dispatch, permission pause/resume (`askRegistry`, `Run.Approve`), the plan-approval gate (`PresentPlan`), the token budget, bounded no-progress nudging, and steer-while-running (mid-run operator input). **Prerequisites:** [the ports](ports.md) — the seams the loop consumes. @@ -43,7 +43,10 @@ immediately and drives the loop in a background goroutine; the `Run` exposes: final user text through the aggregate root. 3. **Pre-turn stop guard**: announce any newly-finished background children (one harness-note user message, ids + stop labels only, family-aware across - the delegation families and background-Bash jobs; [subagents & teams](subagents-and-teams.md)); then, if + the delegation families and background-Bash jobs; [subagents & teams](subagents-and-teams.md)); + drain the fire-result delivery queue (ADR 0075) and the **steer inbox** + (below) — the Step 2a boundary injections, + `engine/agent/loop.go` (`runBoundaryInjections`); then, if `sess.StopReason()` trips, `ctx` is cancelled, or the run **token budget** is crossed (below), terminate. 4. `BeginTurn`, emit `turn.start`. @@ -245,6 +248,91 @@ askable ask, a serialized provenance marker, and a verdict tail. observer (`MaybeAutoApprovePlan`) auto-resolves a parked plan-ask headless (DEFAULT OFF, OPERATOR-TIER ONLY, loud "NO HUMAN REVIEW" diagnostic). +## Steer-while-running + +A **steer** is an operator-supplied message injected into an *in-flight* run +(issue #512, [ADR 0232](../adr/0232-steer-while-running.md)): it takes effect at +a turn boundary after the current streamed response and its tool batch settle — +never mid-stream, never aborting an in-flight model call — and rides the gRPC +`Converse` stream as `steer` / `steer_cancel` frames. The pieces: + +- **The run-scoped mutex inbox** (`engine/agent/steer.go` (`steerInbox`)). Each + `Run` carries a single-slot pending-steer box guarded by one mutex + (`{closed, pending, has}`); every transition is one critical section. + `engine/agent/steer.go` (`Run.EnqueueSteer`) parks the steer when the slot is + empty (`accepted`), and **appends** into the pending bundle when one is already + pending (`appended` — `pending += "\n\n" + text`, merged with a blank-line + separator; the merged bundle still drains as ONE user message). Replacing a + pending bundle is an explicit `Run.CancelSteer`-then-resend. The inbox reports + `too_late` once it closes at run terminal. Steer text is repaired to valid + UTF-8 at ingress (`session.ToValidUTF8`) so recorded history, the echo, and + the model view stay byte-identical. The outcome is the closed enum + `engine/agent/steer.go` (`SteerOutcome`): `accepted` / `appended` / + `retracted` / `none_pending` / `too_late`. +- **The Step 2a drain** (`engine/agent/steer.go` (`drainPendingSteer`)) runs in + `runBoundaryInjections` (step 3 above), the same provider-legal seam as the + background-completion notice and the delivery drain — history there always + ends on a user prompt / tool result / nudge, never inside a `tool_use` pair. + The drained steer is recorded as an ordinary harness-authored user + continuation (`recordContinuation`: `RecordUserPrompt` + the log-only + `EvUserPrompt`), persisted, then echoed to the client as `EvSteer` carrying + the committed text — the engine is the sole authority on what landed. +- **The clean-exit continue-run rule** (`engine/agent/loop.go` + (`finishTurnNoTools`)). A would-be clean end (meaningful text, benign stop) + while a steer is still parked does NOT terminate: the loop re-enters step 2 + with no injected nudge so the very next Step 2a drains the steer and the turn + it feeds consumes it. The never-drop contract stays engine-internal; the run + simply extends (bounded by `Limits.MaxTurns` like any continuation). +- **The terminal close-drain** (`engine/agent/steer.go` (`closeSteerDrained`)). + Both terminate paths drain-then-close: a steer still parked when the run ends + for another reason is recorded into durable history first (addressed by the + next run), then the inbox closes — it is never closed unconsumed. +- **Lost terminal race → promote** (`internal/adapter/server/service.go` + (`Service.Steer`)). A steer arriving when no live run can take it (no live + run, or the inbox already closed) is promoted to a fresh follow-up run through + the same hardened run-entry funnel a prompt uses (`StartRunContent` → + `loadAndReopen` + lease + recover-if-terminal). Because the just-terminal + run's relay may still be draining — still registered, so the funnel's + liveness guard would refuse — the promote path alone awaits the original + run's deregistration, bounded by the promote-grace + (`internal/adapter/server/service.go` (`promotedSteerRun`), + `awaitRunDeregister`); a run still registered at the lapse is genuinely + in-flight and the promotion is refused. Only the promoted steer pays the + wait; a concurrent prompt on a live session is never delayed. +- **The sequential active-run handoff** (`internal/adapter/server/grpc.go` + (`Converse`)). The bidi RPC relays the original run, then — while a promoted + steer is queued in the `steerHandoff` mailbox — relays each promoted run in + turn on the SAME stream before the RPC returns: one relay owner at a time, so + `runRelay.sendErr` keeps a single owner, every `Send` still crosses the one + mutex (`streamSender`), and the control target (`ResumeApproval` / `Cancel` / + `CancelChild`) swaps to the promoted run atomically before its relay starts. + The promoted run is `FinishRun`-deregistered before the RPC returns, and its + terminal outcome is reported inline as the `steer.outcome` ack + (`promoted=true`) — never an orphaned relay, never an ack after close. +- **The `message_id` watermark correlation.** Steer frames carry a + client-minted `message_id` (`contracts/proto/mecatl/v1/harness.proto`). The + engine inbox parks text only, so the Service keeps a small per-session FIFO + (`internal/adapter/server/service.go` (`trackSteerMessageID`)) of the ordered + frame ids appended into the pending bundle. On drain, the relay pops the + whole list and stamps the `EvSteer` echo with the LATEST (tail) id — the + **watermark** the client splits its ordered queue on (sends up to and + including it drained, sends after it still pending). The ack lane echoes each + frame's own id on its outcome; a retract drops the whole correlation list + (`dropSteerMessageID`); a `CloseSession` clears the map entry with the + session. The correlation is positional (never text-match) — pinned by + `internal/adapter/server/steer_watermark_pin_test.go` + (`TestLookupSteerMessageIDExactUnderDuplicateTexts`). +- **Fidelity.** The inbox is in-memory and best-effort: a pending (un-drained) + steer is lost with its run on a crash — reset-by-design, inventoried in + [ADR 0027](../adr/0027-cloud-native.md) (List 1 / List 2). Only a steer that + reached a boundary and was recorded survives, as ordinary conversation + history. + +Awaiting-ask runs hold the steer parked: the loop is suspended in +`PauseForApproval`, and the resumed run's first Step 2a drains it (the steer is +purely additive — the ask still requires an explicit verdict). Steer-to-child +(subagent / team / parallel) and HTTP/SSE + ACP steer are deferred (ADR 0232). + ## Follow-on reading - [Hooks & guardrails — the loop's lifecycle gates](hooks-and-guardrails.md) diff --git a/docs/configuration-reference.md b/docs/configuration-reference.md index d531b48a38..ff5ef99523 100644 --- a/docs/configuration-reference.md +++ b/docs/configuration-reference.md @@ -149,6 +149,16 @@ Exact verified OIDC issuer/subject pairs authorized for process-wide storage hea | `storage_management.principals[].issuer` | `string` | `(empty)` | Issuer must equal the verified token issuer byte-for-byte. | | `storage_management.principals[].subject` | `string` | `(empty)` | Subject must equal the verified token subject byte-for-byte. | +## `steer` + +Tier: **operator** + +OPERATOR-TIER mid-run steer knob (steer-while-running, issue #512): when true (the DEFAULT), a client may inject an operator instruction into an in-flight run, drained at the next turn boundary. Set false to disable the steer inbox (the capability echo then reads false and a steer frame reports too_late). A project-tier steer: is IGNORED with a WARN (the harness's operator surface is not a project repo's to flip). Omit = keep the CLI/default (steer ON). + +| Value | Type | Default | Description | +| --- | --- | --- | --- | +| `steer` | `bool` | `true` | Steer is the OPERATOR-TIER mid-run steer knob (steer-while-running, issue #512): enable (default) or disable the mid-run steer inbox. Like Posture/ReasoningEffort it is honoured ONLY from the user-global + CLI tiers; a project-tier file's steer: key is IGNORED with a WARN (operator-tier only — the harness's operator surface is not a project repo's to flip, in either direction). It is a *bool so ABSENT is distinguishable from an explicit false: nil = absent (the resolver reports not-present and composition keeps the DEFAULT-ON); a non-nil value is honoured (composition maps steer: false onto the opt-OUT DisableSteer). | + ## `models` Tier: **operator + project** diff --git a/docs/design/IMPLEMENTATION-NOTES.md b/docs/design/IMPLEMENTATION-NOTES.md index 4473a2c13e..2812d5c65e 100644 --- a/docs/design/IMPLEMENTATION-NOTES.md +++ b/docs/design/IMPLEMENTATION-NOTES.md @@ -7392,6 +7392,101 @@ go test's panic path — the arithmetic lives in `e2e/suite_test.go`. The `MECAT (the `e2e-live` label; `pull_request`, never `pull_request_target`, so fork PRs get no secrets). +## Steer-while-running (issue #512, ADR 0232) + +Steer injects a user message into an **in-flight** run — Claude Code's "steer while +running" — instead of waiting for the run to end and submitting a fresh prompt (the +#228 terminal-queue). The enabler is that the LLM adapters are stateless +(`store:false`, full replay each turn): a steer is just an appended +`Message{Role: user}` before the next replay, so **no provider API support is +required** and it is portable across Anthropic Messages / OpenAI Responses / Chat +Completions. + +**Engine (`engine/agent/steer.go`).** A `Run`-scoped, single-slot, append-default +**mutex** inbox (a `sync.Mutex` + `{closed, pending, has}` triple — every +transition is ONE critical section). At most one pending steer bundle per run: +a second `EnqueueSteer` on the occupied slot **APPENDS** (`pending += "\n\n" + +text`, outcome `SteerAppended`) — replacing a pending bundle is the explicit +cancel-then-resend (`CancelSteer`, then a fresh steer with a fresh `message_id`). +`CancelSteer` retracts; the boundary drain commits the merged bundle as ONE user +message. The outcome is a closed enum (`accepted`/`appended`/`retracted`/ +`none_pending`/`too_late`), not booleans. Steer text is UTF-8-repaired at +ingress (`session.ToValidUTF8`) so history == echo == model-view. The inbox is +in-memory and **best-effort** — a pending (un-drained) steer is lost with the +run on a crash (reset-by-design, not persisted across restart — ADR 0027 List 1 +row 57 / List 2 row 34). It is armed only when `Deps.EnableSteer` (wired from +`Config.DisableSteer`, opt-out, default ON, posture-independent) and closed +(`closeSteer`) only on a genuine terminal (`terminate`/`terminateComplete`), +never on the `awaiting` park — so a steer submitted while parked on an ask is +held and drained at the resumed run's first boundary (queue-only; the ask still +requires an explicit verdict). **Clean-exit continue-run:** a would-be clean end +while a steer is parked does NOT terminate — `finishTurnNoTools` re-enters the +loop so the next Step 2a drains the steer (the never-drop contract stays +engine-internal; the run extends, bounded by `Limits.MaxTurns`); and the +terminate paths drain-then-close (`closeSteerDrained`) so a parked steer is +recorded into durable history before the inbox closes, never closed unconsumed. + +**Injection seam.** The drain (`drainPendingSteer`) rides the SAME Step 2a +turn-boundary seam in `runLoop` as `injectBackgroundNotice`/`drainPendingDelivery` +(sequenced by `runBoundaryInjections`, BEFORE `BeginTurn` and the pre-turn-terminal +checks), recording via `recordContinuation` (`RecordUserPrompt` + the log-only +`EvUserPrompt`). History at that boundary always ends on a user prompt / tool +result / nudge, so the steer is appended **after** the settled tool results — never +inside a `tool_use` pair (`session.ValidateToolPairing` holds), it rehydrates under +ADR 0038, and the byte-stable prompt prefix stays a valid cache prefix (the steer +costs no prompt-cache rebuild beyond normal history growth). The drain emits +`EvSteer` carrying the committed text — the authoritative echo; the client renders +the echoed truth (recorded == streamed == model-view). + +**Wire (gRPC-only v1).** A `steer`/`steer_cancel` oneof arm on the bidi `Converse` +stream, a `ServerCapabilities.steer` bit (additive grow, computed once in +composition), and the `EvSteer` echo. The routing has ONE owner — +`Service.Steer`/`Service.CancelSteer` (`internal/adapter/server/service.go`); the +gRPC handler is a dumb frame→Service mapper. **Correlation (watermark).** Every +frame carries a client-minted `message_id`; the ack lane echoes its own frame's +id on each outcome. The engine inbox parks text only, so the Service keeps a +per-session FIFO of the ordered frame ids (`trackSteerMessageID`/ +`LookupSteerMessageID`/`dropSteerMessageID`); on drain the relay pops the whole +list and stamps the `EvSteer` echo with the LATEST (tail) id — the **watermark** +the client splits its ordered queue on (positional, never text-match — pinned by +`TestLookupSteerMessageIDExactUnderDuplicateTexts`). Ids are clamped to a 64-rune +prefix at track before touching the FIFO or any log (CWE-770). **Lost terminal +race → auto-promote + sequential handoff:** a steer arriving for a session whose +run is already terminal is promoted to a fresh follow-up run through the hardened +run-entry funnel (`StartRunContent`/`loadAndReopen` + lease + recover-if-terminal) +— never silently dropped; the promote path awaits the original run's +deregistration (bounded by `steerPromoteGrace`) so a terminate-window steer +promotes instead of erroring on `IsLive`. The promoted run relays **sequentially +on the same stream**: `Converse` relays the original run, then each promoted run +in turn before returning — one relay owner at a time (`runRelay.sendErr` +single-owner, every `Send` across the one `streamSender` mutex — a gRPC stream +is not goroutine-safe), the control target (`ResumeApproval`/`Cancel`/ +`CancelChild`) swaps to the promoted run atomically before its relay starts, and +the promoted run is `FinishRun`-deregistered before the RPC returns (its terminal +outcome is reported inline as the `steer.outcome` ack, `promoted=true`). +**HTTP/SSE and ACP steer are deferred** (no client→server mid-run channel; a +unary `POST .../steer` mirroring `approve`/`cancel` is the cheap follow-up +shape), as is **steer-to-child** (needs a richer parent→child channel than +`CancelChild`). + +**mecatui.** Reads the `steer` capability off the CreateSession echo: present → +`enter` mid-run sends a `steer` frame (each `enter` mints a fresh `message_id`, +the wire carries ONLY that line's text; the engine appends server-side); absent +→ the #228 local merge-queue, byte-identical. The TUI keeps an **ordered queue +of sends** (id + fragment); the `EvSteer` echo carries the **watermark** (the +tail contributing send's id) and the queue splits on it — prefix drained +(rendered in context at the echo's true stream position), suffix pending. The +card renders each fragment on its own line (re-composed fragments, whose text +embeds the merge separator, split per-part at render). Acks advance the +lifecycle only (the queue splits on the echo, never on an ack); a stale ack +(id no longer in queue) is dropped. `↑` is **cancel-then-recompose** — it +issues a `steer_cancel` for the outstanding bundle (the watermark id), pulls +the pending sends into the input as ONE editable blob, and resends as a fresh +fragment under a NEW `message_id` (already-drained sends are never re-sent; a +late `none_pending` ack means the drain won — the steer shipped). The queue is +the single correlation source — no separate burn maps; the watermark derives +from the tail send. + --- diff --git a/docs/tui.md b/docs/tui.md index 73984cc5aa..18fd7d36b6 100644 --- a/docs/tui.md +++ b/docs/tui.md @@ -1630,6 +1630,53 @@ satisfied (so a queued `/clear` clears the transcript instead of sending a promp Queueing is **running-only**: while a permission modal is open the modal keys own the keyboard unchanged (no mid-approval queueing). +### Steer mode (mid-run steer, when the server advertises it) + +When the server's engine arms the mid-run **steer inbox** (steer-while-running, issue +#512 — ON by default; `mecated --no-steer` / `mecatui --no-steer` or the operator-tier +`steer: false` settings.yaml key opts out), the `CreateSession` capabilities echo +carries `steer: true` and the TUI **flips** mid-run input from the local terminal +queue to the engine steer path: + +- `enter` mid-run **sends a `steer` frame** on the live Converse stream instead of + staging locally. Each `enter` mints a fresh client `message_id` and the frame + carries ONLY that line's text; the engine's single-slot inbox **appends** each + frame into the one pending bundle (merged with a blank-line separator) and + drains the bundle at the **next turn boundary**, recording it as an ordinary + user continuation — so the model is nudged *mid-flight*, no waiting for the + run to end. The TUI keeps an **ordered queue of sends** (id + text); the drain + echo carries the **watermark** (the latest contributing send's id) and the + queue splits on it: everything up to and including the watermark landed (it + renders in context), anything after stays pending. +- The card above the input reflects the **authoritative** server-reported state — + the engine is the sole authority on what happened to a steer (the client cannot + observe the exact drain moment across stream latency), so the card shows what + the server acked/echoed, never a client-side guess: `⏳ steer: sending…` (sent, + ack in flight) → `⏳ steer queued · ↑ edit · esc retract` (acked, parked for the + next boundary) → `↪ steer sent as a follow-up (run had already ended)` (a + **too-late** race: the run had already gone terminal, so the text was + auto-promoted to a fresh follow-up run — never silently dropped) or + `✕ steer retracted` (a `steer_cancel` won). A second `enter` while a bundle is + parked **appends** to it server-side (the bundle drains as ONE merged message); + replacing a pending bundle is the explicit `↑`-cancel-then-recompose below. The + rendering is **queued-until-landed**: the pending card sits at the bottom of + the transcript, and the **drain echo** clears it as the committed steer appears + IN CONTEXT at its true position (an ordinary user turn at the boundary it + landed) — never rendered twice. +- `↑` (empty input, in-flight steer) is **cancel-then-recompose**: it issues a + `steer_cancel` for the outstanding bundle (the watermark id) and pulls the + queue's pending sends back into the input as ONE editable blob; resending sends + the edited blob as a fresh fragment under a NEW `message_id` (an already-drained + send is never re-sent — the watermark split keeps the queue honest). A late + `none_pending` ack means the drain won — the steer shipped as sent. `esc` on a + pending/queued steer **retracts** it (`steer_cancel`) before it would cancel + the run. + +When the capability is **absent** (an older server, or steer disabled), none of this +engages: mid-run input keeps the #228 local merge-queue behaviour **byte-identical** +(staged, merged, and drained as a follow-up prompt when the run ends), and no `steer` +frame is ever sent. + ## Theming Themes are pure data: a `Palette` of semantic colour slots (e.g. `accent`, diff --git a/docs/usage/http-sse-api.md b/docs/usage/http-sse-api.md index a982cba67d..3e4d658bbd 100644 --- a/docs/usage/http-sse-api.md +++ b/docs/usage/http-sse-api.md @@ -224,6 +224,19 @@ $ curl -s -X POST http://127.0.0.1:8081/v1/sessions//cancel The run terminates with a `result` whose `stop` is `cancelled`. No in-flight run → `404` `{"error":"no in-flight run for session"}`. +### Mid-run steer is gRPC-only (v1) + +The **steer** capability (steer-while-running, issue #512 — inject an operator +instruction into an *in-flight* run, drained at the next turn boundary) rides the +bidi gRPC `Converse` stream as a `steer` / `steer_cancel` request arm. The HTTP/SSE +run path has **no mid-run client→server channel** — `POST /v1/sessions/{id}/runs` +streams server→client only — so an HTTP/SSE client **cannot steer** in v1. Read the +`steer` bit off the `CreateSession` capabilities echo: when present/true a gRPC +client may send `steer` frames; when absent/false the server reports `too_late` +(and, over gRPC, auto-promotes the text to a fresh follow-up run). A unary +`POST /v1/sessions/{id}/steer` endpoint is a possible cheap follow-up (mirroring +`approve`/`cancel`), deferred. + ### ACP over stdio (`mecated acp`) `mecated acp` serves the **Agent Client Protocol** — JSON-RPC 2.0 over diff --git a/engine/CHANGELOG.md b/engine/CHANGELOG.md index a8b1fba84b..dcfb3ef18d 100644 --- a/engine/CHANGELOG.md +++ b/engine/CHANGELOG.md @@ -11,6 +11,19 @@ The covered surface is the eight core packages (`session`, `governance`, `learni ## [Unreleased] +### Changed + +- **`agent.SteerOutcome` enum: superseded/slot_full dropped, appended added** + (issue #512, the landed steer-while-running contract). The round-2/task-13 + rework replaced `SteerSuperseded` with `SteerSlotFull`, and the round-3 + append-default rework then removed BOTH from the enum and added + `SteerAppended` (`"appended"`): a second steer on the occupied slot merges + into the pending bundle (`pending += "\n\n" + text`) instead of rejecting + or replacing. The shipped enum is `accepted` / `appended` / `retracted` / + `none_pending` / `too_late`. Because neither intermediate value ever shipped + in a tagged release, the net public change over the pre-steer baseline is a + breaking `Changed` only for the enum vocabulary (pre-v1 a minor bump). + ### Added - **Engine-child lifecycle exclusion** ([ADR 0027](../docs/adr/0027-cloud-native.md)) — @@ -163,6 +176,46 @@ The covered surface is the eight core packages (`session`, `governance`, `learni `agent.NewUserModelObserver` constructing the storage-free automatic path; its legacy ID-based `Review` path retains the SessionStore-backed constructor. All are new exported identifiers/fields and therefore Added (minor). +- **Steer-while-running: in-flight operator steer injection** (issue #512, + [ADR 0232](../docs/adr/0232-steer-while-running.md)) — a `Run`-scoped, + single-slot, append-default mutex inbox that the agent loop drains at the + Step 2a turn boundary (the same provider-legal seam + `injectBackgroundNotice`/`drainPendingDelivery` use) and records as an + ordinary harness-authored user continuation (`recordContinuation`: + `RecordUserPrompt` + the log-only `EvUserPrompt`), so a drained steer + replays to the model as an ordinary user turn and flows through compaction / + `session.ValidateToolPairing` / ADR-0038 rehydration unchanged. Steer text is + UTF-8-repaired at ingress (`session.ToValidUTF8`) so recorded history == + `EvSteer` echo == model view. A pending (un-drained) steer is in-memory and + lost with its run (crash/cancel/Abandon) — never persisted (reset-by-design, + ADR 0027 rows 60/37). A parked steer blocks a clean terminal until it drains + (the clean-exit continue-run rule; never-drop holds engine-internally), and + both terminate paths drain-then-close so a parked steer is recorded into + durable history before the inbox closes. All Added (a minor bump): + - `agent.Deps.EnableSteer` — the opt-in knob (default false = strict no-op, + byte-identical to the pre-steer posture). Composition plumbs it to + `ServerCapabilities.steer`; no `port` interface is widened. + - `(*agent.Run).EnqueueSteer` — the exported, wire-facing steer entry point + the Service routes a live run's operator steer through: `SteerAccepted on + an empty slot, `SteerAppended` on an occupied slot (the bundle grows by + `"\n\n"` + text), `SteerTooLate` (plain value, never an error) past the + terminal close the Service promotes on. + - `(*agent.Run).CancelSteer` — the wire-facing steer-cancel entry point + (retracts the run's PENDING, un-drained steer; reports `SteerRetracted` / + `SteerNonePending`). + - `(*agent.Engine).SteerEnabled` — the read-only seam composition reads to + advertise the capability from the SAME wired knob (single-source, never + recomputed per sink). + - `agent.SteerOutcome` + the closed enum vocabulary `SteerAccepted` / + `SteerAppended` / `SteerRetracted` / `SteerNonePending` / `SteerTooLate` — + the authoritative result of an enqueue/cancel transition (an ENUM, not + stacked booleans). + - `session.EvSteer` (`"steer"`) + `session.SteerPayload` + the + `session.Event.Steer` field — the CLIENT-VISIBLE drain echo carrying the + COMMITTED merged bundle text (the version actually drained). The recorded + == streamed == model-view invariant holds: the echoed text is byte-identical + to the user message recorded into history and replayed to the model. Wire + string passthrough (no proto enum). - **OpenRouter downstream-provider routing echo** (issue #480) — two new constants that surface which DOWNSTREAM inference provider OpenRouter routed a diff --git a/engine/agent/childask_config_test.go b/engine/agent/childask_config_test.go index caa7579fb5..483f39d376 100644 --- a/engine/agent/childask_config_test.go +++ b/engine/agent/childask_config_test.go @@ -44,6 +44,21 @@ func (rr *requestRecorder) observe(req port.LLMRequest) { rr.mu.Unlock() } +// get returns the i-th recorded request (by model-call order). The steer tests +// index per turn; callers must ensure at least i+1 calls were made. +func (rr *requestRecorder) get(i int) port.LLMRequest { + rr.mu.Lock() + defer rr.mu.Unlock() + return rr.reqs[i] +} + +// count reports how many requests (model calls) were recorded. +func (rr *requestRecorder) count() int { + rr.mu.Lock() + defer rr.mu.Unlock() + return len(rr.reqs) +} + // toolResultContents flattens every tool-result body seen across the recorded // requests. func (rr *requestRecorder) toolResultContents() []string { diff --git a/engine/agent/export_test.go b/engine/agent/export_test.go index 8800e25c03..f46dd2715f 100644 --- a/engine/agent/export_test.go +++ b/engine/agent/export_test.go @@ -8,3 +8,19 @@ package agent // origin-bearing context directly, instead of only through Engine.Run. Kept // test-only on purpose — see the withSessionOrigin doc-comment (ADR 0209). var WithSessionOrigin = withSessionOrigin + +// EnqueueSteerForTest exposes the Run's unexported steer-enqueue seam so the +// external drain tests can enqueue a mid-flight steer without the wire-facing +// entry point (a later task adds the exported API; this stays test-only). It +// returns the authoritative SteerOutcome (task 02), not an error. +var EnqueueSteerForTest = (*Run).enqueueSteer + +// CancelSteerForTest exposes the Run's unexported steer-cancel seam so the +// external tests can retract a pending steer. It returns the authoritative +// SteerOutcome (task 02), not an error. +var CancelSteerForTest = (*Run).cancelSteer + +// CloseSteerForTest exposes the Run's unexported steer-close seam so the +// internal concurrency test can simulate run-terminal without driving a full +// loop to completion. +var CloseSteerForTest = (*Run).closeSteer diff --git a/engine/agent/loop.go b/engine/agent/loop.go index 4d68923e4c..2720803f34 100644 --- a/engine/agent/loop.go +++ b/engine/agent/loop.go @@ -382,6 +382,22 @@ type Deps struct { // fire path's enqueue is a no-op against a nil queue. It is a port (port.DeliveryQueue), // so the agent package imports no concrete adapter. DeliveryQueue port.DeliveryQueue + + // EnableSteer, when true, arms each Run with an in-memory, best-effort + // steer inbox (steer-while-running, issue #512): an operator-supplied + // instruction enqueued mid-run that the loop drains at the next turn + // boundary (Step 2a, the same seam injectBackgroundNotice / + // drainPendingDelivery use) and records as an ordinary user continuation + // via recordContinuation, so it replays to the model and flows through + // compaction / session.ValidateToolPairing / ADR-0038 rehydration + // unchanged. It is a plain loop-concern bool — NOT a port.LLMRequest field + // and never reaches the model as anything but an ordinary recorded user + // message. DEFAULT false (the zero value): no inbox is armed and the drain + // is a strict no-op, byte-identical to the pre-steer posture. Composition + // plumbs it from Config; child engines inherit the shared deps. + // A pending (un-drained) steer is in-memory only and lost with the run — + // never persisted (see steer.go). + EnableSteer bool } // Engine builds Runs from a fixed set of ports. It is safe for concurrent use: @@ -497,6 +513,14 @@ func (e *Engine) HasTool(name string) bool { return ok } +// SteerEnabled reports whether this engine arms its Runs with the mid-run +// steer inbox (Deps.EnableSteer, steer-while-running issue #512). It is the +// read-only seam composition reads to advertise the feature — the +// ServerCapabilities.steer bit reads the SAME wired knob the runs consult, so +// the advertisement can never claim a steer path the engine did not arm +// (single-source, never recomputed per sink). +func (e *Engine) SteerEnabled() bool { return e.deps.EnableSteer } + // catalogToolInfo is a (name, read-only) summary of one tool in an Engine's // catalog. The supervisor uses it to verify a member's tool set without // type-asserting concrete tool types (which would require importing an adapter, @@ -678,6 +702,14 @@ type Run struct { // it in the final history; if compaction removed it, automatic admission gets an // invalid span and fails closed. currentPrompt *session.Message + // steer is this run's in-memory, best-effort pending-steer inbox (steer- + // while-running, issue #512). It is armed in startRun ONLY when the engine + // carries Deps.EnableSteer; a nil inbox is the byte-identical no-steer + // posture (the drain is a strict no-op). A drained steer is recorded into + // durable history (recordContinuation) and echoed to the client (EvSteer); + // a PENDING (un-drained) steer lives only here and is lost with the run on + // crash / Cancel / Abandon — never persisted. See steer.go. + steer *steerInbox } // runSerial mints the process-unique Run.serial discriminator (see Run.serial). @@ -1006,6 +1038,13 @@ func (e *Engine) startRun(ctx context.Context, sess *session.Session, req RunReq if e.deps.SubagentModelRouter != nil { r.router = &modelRouterBreaker{max: defaultModelRouterMaxMisses} } + // An engine with steer enabled (Deps.EnableSteer) arms this run's in-memory + // steer inbox so an operator instruction enqueued mid-flight drains at the + // next turn boundary (Step 2a). nil when disabled — the byte-identical + // no-steer posture; the drain is then a strict no-op. + if e.deps.EnableSteer { + r.steer = newSteerInbox() + } // The child-run registry is created UNCONDITIONALLY (cancel is interactive-only // but the registry's bookkeeping is not), with its emit bound to this run's // sequenced stream via emitOrAbort — a send that blocks until delivered (the @@ -1134,36 +1173,16 @@ func (e *Engine) runLoop(ctx context.Context, r *Run, sess *session.Session, env return } - // Step 2a: background-completion NOTICE injection (A2 — notice-only), BEFORE - // the terminal checks so the notice is durable history even when the run ends - // at this very boundary. Newly-finished background children that were neither - // noticed nor collected are announced in ONE harness-framed user message (ids - // + stop labels only); their result bodies stay collectible via SubagentStatus. - // The seam is provider-legal: history here always ends on the user prompt, - // tool results, or a nudge message — never inside a tool_use pair — so the - // injected message replays cleanly (it IS ordinary history) and flows through - // compaction/ValidateToolPairing like any user message. It does NOT consume a - // no-progress nudge, emits no event, and does not itself consume a turn — the - // following BeginTurn does, which is what Limits.MaxTurns bounds. A child - // finishing between this scan and the rest of the iteration is simply noticed - // at the NEXT boundary. - if err := e.injectBackgroundNotice(ctx, r, sess); err != nil { - e.terminate(ctx, r, sess, session.StopError, lastText, total, err, false) - return - } - - // Step 2a (delivery drain): drain any pending fire-result delivery notes - // queued for THIS session (ADR 0075 decision #3, fire-result-delivery - // Scenario 4) — the SAME turn-boundary seam as the background notice, BEFORE - // BeginTurn, so the recorded notes are provider-legal (history here always - // ends on a user prompt / tool result / nudge, never inside a tool_use pair) - // and never orphans a pending tool call. Each pending note is recorded as an - // ordinary harness-framed user continuation (recordContinuation) and marked - // delivered via MarkDelivered (the session-scoped exactly-once ledger). nil - // DeliveryQueue (child engines, the no-delivery posture) is a no-op. A drain - // read fault WARNs and ends the run StopError (a broken queue must not - // silently lose notes); a record fault likewise. - if err := e.drainPendingDelivery(ctx, r, sess); err != nil { + // Step 2a: the turn-boundary injection drains (background-completion notice, + // fire-result delivery, and the operator steer inbox) run BEFORE BeginTurn + // and BEFORE the preTurnTerminal stop checks — the provider-legal seam where + // history always ends on a user prompt / tool result / nudge, never inside a + // tool_use pair. Recording before the stop checks is load-bearing: a message + // drained at a boundary where the turn-limit / budget brake also trips is + // STILL durable history (the run then terminates StopMaxTurns / StopBudget + // normally; the recorded message is addressed by the next run). See + // runBoundaryInjections. + if err := e.runBoundaryInjections(ctx, r, sess); err != nil { e.terminate(ctx, r, sess, session.StopError, lastText, total, err, false) return } @@ -1297,6 +1316,34 @@ func (e *Engine) runLoop(ctx context.Context, r *Run, sess *session.Session, env } } +// runBoundaryInjections runs the Step 2a turn-boundary injection drains, in +// order, BEFORE BeginTurn and the preTurnTerminal stop checks: the +// background-completion notice, the fire-result delivery drain (ADR 0075), and +// the operator steer drain (steer-while-running, issue #512). Extracting them +// keeps runLoop's complexity flat; the ordering and the record-before-stop-checks +// discipline are unchanged from when they were inline. Each is provider-legal at +// a turn boundary (history ends on a user prompt / tool result / nudge, never +// inside a tool_use pair) and a no-op when its source is empty/disabled. Any +// drain's error is returned for the caller to terminate StopError. +func (e *Engine) runBoundaryInjections(ctx context.Context, r *Run, sess *session.Session) error { + // Background-completion NOTICE injection (A2 — notice-only): newly-finished + // background children are announced in ONE harness-framed user message (ids + + // stop labels only). + if err := e.injectBackgroundNotice(ctx, r, sess); err != nil { + return err + } + // Fire-result delivery drain (ADR 0075 decision #3): pending notes queued for + // THIS session are recorded as ordinary harness-framed user continuations and + // marked delivered. nil DeliveryQueue is a no-op. + if err := e.drainPendingDelivery(ctx, r, sess); err != nil { + return err + } + // Operator steer drain (steer-while-running, issue #512): a pending steer off + // the run's in-memory inbox is recorded as an ordinary user continuation. A + // nil inbox (EnableSteer off) or empty slot is a strict no-op. + return e.drainPendingSteer(ctx, r, sess) +} + // finishTurnNoTools classifies and acts on a completed turn that produced NO tool // calls. The empty assistant message has ALREADY been recorded by the caller, so its // reasoning blob is on history for replay across a nudge (decision D-4). It returns @@ -1351,6 +1398,19 @@ func (e *Engine) runLoop(ctx context.Context, r *Run, sess *session.Session, env // persisted child stays inspectable/resumable) — acceptable by design. // - the nudged continuation re-enters Step 2 (notice injection + BeginTurn), so // Limits.MaxTurns still bounds it exactly like a no-progress nudge. +// +// Steer clean-exit defer: a PARKED steer BLOCKS the clean exit — the run does +// NOT terminateComplete while the inbox holds an un-drained steer. Instead the +// loop re-enters Step 2 with no recorded continuation, so the very next Step 2a +// drains the steer (recorded + echoed there) and the turn it feeds consumes it. +// UNLIKE the background-pending nudge this is not once-only and injects NO +// harness nudge text: the run extends until the steer is consumed, and the +// steer itself is the next turn's input. It lives on the same real-clean-end +// branch as the background-pending check (BEFORE the clean-terminal call sites) +// and runs FIRST — a parked steer always owns the defer (it is the never-drop +// contract, not an optional collect). Bounded by +// Limits.MaxTurns like every loop continuation (the steered turn goes through +// BeginTurn). func (e *Engine) finishTurnNoTools(ctx context.Context, r *Run, sess *session.Session, asst session.Message, streamStop session.StopReason, turnIdx int, lastText string, total session.Usage, noProgressNudges *int, nudgeCap int, bgPendingNudged *bool) (done bool) { // A turn with meaningful text is a real answer: end the run, honouring streamStop. if strings.TrimSpace(asst.Text) != "" { @@ -1358,6 +1418,11 @@ func (e *Engine) finishTurnNoTools(ctx context.Context, r *Run, sess *session.Se if streamStop != session.StopNone { stop = streamStop } + // A parked steer defers the clean exit until it drains at the next Step + // 2a (the never-drop contract, engine-internal). + if stop == session.StopEndTurn && r.hasSteer() { + return false + } if stop == session.StopEndTurn && !*bgPendingNudged && r.children != nil { if ids := r.children.liveBackgroundIDsMatching(nil); len(ids) > 0 { *bgPendingNudged = true @@ -2329,6 +2394,7 @@ func joinChildren(joins []backgroundJoin, d time.Duration) []backgroundJoin { // a tripped limit) and emits the terminal result Event. permanent records whether // a StopError failure is permanent (unrecoverable; retry will fail again). func (e *Engine) terminate(ctx context.Context, r *Run, sess *session.Session, reason session.StopReason, text string, usage session.Usage, cause error, permanent bool) { + e.closeSteerDrained(ctx, r, sess) e.drainChildren(ctx, r) switch reason { case session.StopCancelled: @@ -2381,6 +2447,7 @@ func (e *Engine) planApprovalTerminal(ctx context.Context, r *Run, sess *session // permanent is always false here (the ChunkDone StopError path has NO Go error // to classify — honest fail-open). func (e *Engine) terminateComplete(ctx context.Context, r *Run, sess *session.Session, reason session.StopReason, text string, usage session.Usage, errMsg string) { + e.closeSteerDrained(ctx, r, sess) e.drainChildren(ctx, r) if !sess.State.IsTerminal() { _ = sess.Stop(reason) diff --git a/engine/agent/steer.go b/engine/agent/steer.go new file mode 100644 index 0000000000..56eadaf44e --- /dev/null +++ b/engine/agent/steer.go @@ -0,0 +1,288 @@ +package agent + +import ( + "context" + "fmt" + "sync" + + "github.com/stacklok/mecatl/engine/session" +) + +// SteerOutcome is the closed-enum result of a steer inbox transition (enqueue +// or cancel). The engine is AUTHORITATIVE: the client cannot observe the exact +// drain moment across stream latency, so the outcome reports what actually +// happened to the steer rather than letting the client guess. It is an ENUM, +// not stacked booleans — AGENTS.md's "a third provenance ⇒ extract an enum" +// discipline (a bool pair cannot express accepted-vs-appended-vs-too_late). +type SteerOutcome string + +const ( + // SteerAccepted means the steer was parked in the single pending slot (the + // slot was empty). It will drain at the next turn boundary unless retracted + // first. + SteerAccepted SteerOutcome = "accepted" + // SteerAppended means the steer found the pending slot OCCUPIED and was + // MERGED into it: the pending bundle's text grew by "\n\n"+text (it still + // drains as ONE bundle). Append is the DEFAULT for a second steer on the + // occupied slot (round-3 rework; its outcome distinguished from accepted — + // the new pending bundle — so the client can render "merged onto pending" + // honestly). Replacing the pending bundle is the explicit cancel path + // (steer_cancel, then a fresh steer); append and replace are distinct. + SteerAppended SteerOutcome = "appended" + // SteerRetracted means a cancel found a pending steer and retracted it; the + // run drains nothing for it and the next turn sees no injected message. + SteerRetracted SteerOutcome = "retracted" + // SteerNonePending means a cancel found the slot EMPTY — there was nothing + // to retract (no steer was pending, or the run is terminal). + SteerNonePending SteerOutcome = "none_pending" + // SteerTooLate means the steer arrived after the run went terminal (the + // inbox is closed) — it is never parked and never silently drained into a + // finished turn. The service layer promotes a too_late steer to a fresh + // follow-up run; the engine only reports the outcome. + SteerTooLate SteerOutcome = "too_late" +) + +// steerInbox is a Run-scoped, in-memory, best-effort pending-steer slot (issue +// #512, steer-while-running). A STEER is an operator-supplied instruction +// injected mid-run that the loop drains at the next turn boundary and records +// as an ordinary harness-authored user continuation (recordContinuation), so it +// replays to the model like any user turn and flows through compaction / +// session.ValidateToolPairing / ADR-0038 rehydration unchanged. +// +// The slot is SINGLE and append-default: at most one steer BUNDLE is pending at +// a time. A second enqueue on the occupied slot APPENDS to the pending bundle +// (SteerAppended — its text merges in with "\n\n", and it still drains as ONE +// bundle); a cancel RETRACTS the pending one; the boundary drain COMMITS it. +// Every transition reports a SteerOutcome (the enum) and is safe under +// concurrent access — the run goroutine drains while a wire handler enqueues. +// (Reject-on-full and supersede were tried and dropped — append is the default.) +// +// The inbox is IN-MEMORY and best-effort: a pending (un-drained) steer lives +// only on the Run and is LOST with it — on a crash, a Run.Cancel, or a session +// Abandon. It is never persisted to the SessionStore / snapshot, so it does NOT +// survive a restart (the honest contract: only a steer that reached a turn +// boundary and was recorded into durable history survives, as ordinary +// conversation). This mirrors the background-notice / delivery-drain posture: +// the loop records, the relay persists; the loop itself stays storage-agnostic. +// +// The state is a mutex-guarded {closed, pending, has} triple: EVERY transition +// is ONE critical section over the whole triple, so the non-atomic +// observe-then-replace window (a receive between two selects, a done-check +// torn from the send) is impossible by construction — there is no second step +// to interleave a drain or a close into. Drain does NOT close the inbox — a +// steer enqueued after one boundary's drain but before the next is accepted +// for the following turn; only run-terminal closes it. +type steerInbox struct { + mu sync.Mutex + // closed flips at run terminal; once closed, enqueue is too_late and + // cancel is none_pending. + closed bool + // pending parks the single pending steer (valid only when has is true). + pending string + // has records whether pending is occupied (the single-slot bit). + has bool +} + +// newSteerInbox mints an armed (open, empty-slot) inbox. +func newSteerInbox() *steerInbox { + return &steerInbox{} +} + +// EnqueueSteer is the wire-facing steer entry point: a live run's Service +// routes an operator steer here. It reports the authoritative SteerOutcome +// (accepted/appended/too_late) — never an error for the ordinary too-late +// race (that outcome is what the Service promotes on). +func (r *Run) EnqueueSteer(text string) (SteerOutcome, error) { + return r.enqueueSteer(text) +} + +// CancelSteer is the wire-facing steer-cancel entry point: a live run's +// Service routes an operator steer_cancel here. It retracts the PENDING +// (un-drained) steer and reports the authoritative SteerOutcome +// (retracted/none_pending) — a steer that already drained at a turn boundary +// is ordinary recorded history and cannot be retracted (the cancel reports +// none_pending then: the drain won). +func (r *Run) CancelSteer() (SteerOutcome, error) { + return r.cancelSteer() +} + +// enqueueSteer parks a steer for draining at the next turn boundary. It is +// safe to call from any goroutine while the run is in-flight (the wire-facing +// caller runs on a different goroutine than the loop). It returns the +// authoritative SteerOutcome: SteerAccepted (empty slot), SteerAppended (the +// slot is occupied — merge into the pending bundle), or SteerTooLate (the run +// is terminal / steer not live). A nil inbox (EnableSteer off — the byte- +// identical no-steer posture) reports SteerTooLate. +// +// The text is repaired to valid UTF-8 BEFORE it enters the inbox (the +// two-layer UTF-8 rule's semantic-repair layer, issue #402): steer text is +// operator prose, always safe to repair (no byte-exact exception), and +// repairing at ingress keeps recorded history == EvSteer echo == model-view +// byte-identical downstream. ONE critical section: the closed/full check and +// the park are indivisible. +func (r *Run) enqueueSteer(text string) (SteerOutcome, error) { + if r.steer == nil { + return SteerTooLate, nil + } + repaired := session.ToValidUTF8(text) + r.steer.mu.Lock() + defer r.steer.mu.Unlock() + if r.steer.closed { + return SteerTooLate, nil + } + if r.steer.has { + // Append-default (round-3): merge into the pending bundle with a blank-line + // separator; it still drains as ONE bundle. (Was reject-on-full; dropped.) + r.steer.pending = r.steer.pending + "\n\n" + repaired + return SteerAppended, nil + } + r.steer.pending, r.steer.has = repaired, true + return SteerAccepted, nil +} + +// cancelSteer retracts the pending steer (if any). It is safe to call from any +// goroutine. It returns SteerRetracted when a pending steer was retracted, or +// SteerNonePending when the slot was empty / the run is terminal (nothing to +// retract). A nil inbox reports SteerNonePending. ONE critical section: +// take+clear. +func (r *Run) cancelSteer() (SteerOutcome, error) { + if r.steer == nil { + return SteerNonePending, nil + } + r.steer.mu.Lock() + defer r.steer.mu.Unlock() + if !r.steer.has { + return SteerNonePending, nil + } + r.steer.pending, r.steer.has = "", false + return SteerRetracted, nil +} + +// drainSteer atomically takes the pending steer (if any), returning its text +// and whether one was parked. The inbox stays OPEN after a drain — a steer +// enqueued after this boundary's drain but before the next is accepted for the +// following turn (only run-terminal closes the inbox). A nil inbox is the +// no-op (steer disabled). ONE critical section: take+clear. +func (r *Run) drainSteer() (string, bool) { + if r.steer == nil { + return "", false + } + r.steer.mu.Lock() + defer r.steer.mu.Unlock() + if !r.steer.has { + return "", false + } + text := r.steer.pending + r.steer.pending, r.steer.has = "", false + return text, true +} + +// hasSteer reports whether a steer is currently PARKED in the inbox. The +// clean-exit defer (finishTurnNoTools) consults it before a clean terminal, so +// a parked steer defers the exit until it drains (the run extends; never-drop +// holds engine-internally). A nil inbox reports false. +func (r *Run) hasSteer() bool { + if r.steer == nil { + return false + } + r.steer.mu.Lock() + defer r.steer.mu.Unlock() + return r.steer.has +} + +// closeSteer marks the inbox closed at run terminal, so a post-terminal +// enqueue reports too_late and a post-terminal cancel reports none_pending. It +// is idempotent and does NOT drain: the terminate paths drain-then-close (a +// parked steer is recorded into durable history first), so closeSteer itself +// never swallows a parked steer. +func (r *Run) closeSteer() { + if r.steer == nil { + return + } + r.steer.mu.Lock() + defer r.steer.mu.Unlock() + r.steer.closed = true +} + +// drainPendingSteer is the Step 2a steer-drain sibling of injectBackgroundNotice +// / drainPendingDelivery: it takes any pending operator steer off the run's +// inbox and records it as an ordinary harness-authored user continuation +// (recordContinuation — provider-legal at a turn boundary, where history always +// ends on a user prompt / tool result / nudge, never inside a tool_use pair), +// then persists (e.save) so the recorded steer is durable across resume/reload +// independent of how the run later ends. +// +// It runs at the SAME seam as the background notice and delivery drain (Step +// 2a, BEFORE BeginTurn and BEFORE the preTurnTerminal stop checks), so a steer +// drained at a boundary where the turn-limit / token-budget brake also trips is +// STILL recorded into durable history (the run terminates StopMaxTurns / +// StopBudget normally, and the recorded steer is addressed by the NEXT run) — +// it is never silently dropped and never bypasses BeginTurn's bound. The drain +// is ordered with the other Step 2a seams so the steer lands after any settled +// tool results and after the harness notices, keeping history provider-legal. +// +// On a successful record it emits EvSteer carrying the COMMITTED text — the +// authoritative echo the client renders (the engine is the sole authority on +// which version won the slot). The echo is sequenced BEFORE the turn it +// feeds (it emits ahead of BeginTurn's EvTurnStart), so a client renders the +// committed steer ahead of the model turn that consumed it. +// +// A nil inbox (EnableSteer off — the composition knob) is the strict no-op: +// an empty/disabled inbox changes nothing. An empty pending slot (steer +// enabled but unused) is likewise a no-op — the common case costs one mutex'd +// read. A record fault ends the run StopError, mirroring the delivery drain +// (a recorded-then-lost steer must not silently vanish; the operator sees the +// fault). +func (e *Engine) drainPendingSteer(ctx context.Context, r *Run, sess *session.Session) error { + text, ok := r.drainSteer() + if !ok { + return nil + } + return e.commitSteer(ctx, r, sess, text) +} + +// closeSteerDrained is the terminate-path steer hook: it drains any PARKED +// steer into durable history FIRST (recording it exactly like the Step 2a +// drain, so the never-drop contract holds engine-internally), THEN closes the +// inbox. A parked steer is never closed-unconsumed on ANY terminate path +// (clean or otherwise) — a steer captured here is addressed by the next run. +// The record is provider-legal at a terminal: history there always ends on a +// settled tool result / user message, never inside a tool_use pair. A record +// fault terminates the run StopError (the recorded-then-lost steer must not +// silently vanish). A nil inbox / empty slot is the strict no-op. +func (e *Engine) closeSteerDrained(ctx context.Context, r *Run, sess *session.Session) { + if r.steer == nil { + return + } + text, ok := r.drainSteer() + if ok { + if err := e.commitSteer(ctx, r, sess, text); err != nil { + e.terminate(ctx, r, sess, session.StopError, "", session.Usage{}, + fmt.Errorf("agent: record steer on terminal close: %w", err), false) + return + } + } + r.closeSteer() +} + +// commitSteer records one drained steer text as an ordinary harness-authored +// user continuation and emits the authoritative EvSteer echo + persists. It is +// the shared commit of the Step 2a drain and the terminal close-drain — the +// two paths can never drift on record/echo/save ordering. +// +// Recorded at a turn boundary, before the upcoming BeginTurn (or at the +// terminal), so it belongs to the turn about to start (Counters.Turns is the +// 0-based index of it). recordContinuation records it AND emits the log-only +// EvUserPrompt so the durable log / eventsource.Fold captures this user turn +// like every other. The EvSteer echo is emitted AFTER the record succeeds (a +// recorded-then-unrecorded steer must not echo) and BEFORE the upcoming +// turn.start, so the echo precedes the turn it feeds on the wire. +func (e *Engine) commitSteer(ctx context.Context, r *Run, sess *session.Session, text string) error { + if err := e.recordContinuation(r, sess, sess.Counters.Turns, text); err != nil { + return fmt.Errorf("agent: record steer: %w", err) + } + e.emit(r, session.Event{Type: session.EvSteer, Turn: sess.Counters.Turns, + Steer: &session.SteerPayload{Text: text}}) + e.save(ctx, r, sess) + return nil +} diff --git a/engine/agent/steer_append_internal_test.go b/engine/agent/steer_append_internal_test.go new file mode 100644 index 0000000000..b203895521 --- /dev/null +++ b/engine/agent/steer_append_internal_test.go @@ -0,0 +1,183 @@ +package agent + +import ( + "fmt" + "strings" + "sync" + "testing" +) + +// TestSteer_NoSlotFullOrSuperseded (R5-3): the SteerOutcome enum no longer +// carries slot_full or superseded — append-default (task 12) covers BOTH: a +// second enqueue on an occupied slot APPENDS into the pending bundle instead +// of rejecting (slot_full) or silently replacing (superseded). The closed set +// is exactly {accepted, appended, retracted, none_pending, too_late}, and none +// of the exported constants resolves to the dropped values. +func TestSteer_NoSlotFullOrSuperseded(t *testing.T) { + defined := map[SteerOutcome]bool{ + SteerAccepted: true, + SteerAppended: true, + SteerRetracted: true, + SteerNonePending: true, + SteerTooLate: true, + } + if len(defined) != 5 { + t.Fatalf("defined outcome set = %d, want exactly 5", len(defined)) + } + for _, outcome := range []SteerOutcome{SteerAccepted, SteerAppended, SteerRetracted, SteerNonePending, SteerTooLate} { + if !defined[outcome] { + t.Fatalf("exported outcome %q missing from the defined set", outcome) + } + if outcome == "slot_full" || outcome == "superseded" { + t.Fatalf("the dropped outcome %q survived the append-default rework", outcome) + } + } +} + +// TestSteer_AppendLinearizable (R5-4): the append-default inbox keeps the +// single-slot invariant — one pending BUNDLE, grown by append — and the mutex +// linearizability of every transition. The deterministic half forces the exact +// interleaves: an enqueue into the occupied slot APPENDS (old+"\n\n"+new as +// ONE bundle); the drain takes the WHOLE merged bundle exactly once (no +// half-merge, no double-commit, no resurrection); a post-drain enqueue parks a +// FRESH bundle; close flips too_late / none_pending. The concurrent half +// hammers enqueue/append/cancel/drain from several goroutines (-race): every +// outcome is a defined enum value, and every marker an enqueue reported +// accepted/appended for shows up in EXACTLY ONE observation (a drained bundle, +// a retracted cancel, or the terminal pending bundle) — never lost, never +// doubled. +func TestSteer_AppendLinearizable(t *testing.T) { + // Deterministic sequencing: v1 parks (accepted), v2 appends (appended) into + // the SAME single slot — one merged bundle, never a second slot. + r := &Run{steer: newSteerInbox()} + if outcome, _ := r.enqueueSteer("steer: v1"); outcome != SteerAccepted { + t.Fatalf("first enqueue = %q, want %q", outcome, SteerAccepted) + } + if outcome, _ := r.enqueueSteer("steer: v2"); outcome != SteerAppended { + t.Fatalf("enqueue on an occupied slot = %q, want %q (append-default)", outcome, SteerAppended) + } + // The drain commits the merged bundle ONCE, whole. + text, ok := r.drainSteer() + if !ok || text != "steer: v1\n\nsteer: v2" { + t.Fatalf("drain = (%q, %v), want (%q, true) — the merged bundle commits exactly once, whole", text, ok, "steer: v1\n\nsteer: v2") + } + // The drained slot is EMPTY: a second drain commits nothing (no + // double-commit) and a cancel finds nothing (no resurrection of the merged + // bundle or either half). + if text, ok := r.drainSteer(); ok { + t.Fatalf("second drain = (%q, %v), want empty — drain must take+clear atomically", text, ok) + } + if outcome, _ := r.cancelSteer(); outcome != SteerNonePending { + t.Fatalf("cancel after the drain = %q, want %q", outcome, SteerNonePending) + } + // A post-drain enqueue parks a FRESH bundle (accepted — the slot re-emptied); + // an append onto it merges; close then flips too_late / none_pending + // deterministically. + if outcome, _ := r.enqueueSteer("steer: v3"); outcome != SteerAccepted { + t.Fatalf("post-drain enqueue = %q, want %q", outcome, SteerAccepted) + } + if outcome, _ := r.enqueueSteer("steer: v4"); outcome != SteerAppended { + t.Fatalf("append onto v3 = %q, want %q", outcome, SteerAppended) + } + r.closeSteer() + if outcome, _ := r.enqueueSteer("steer: too late"); outcome != SteerTooLate { + t.Fatalf("enqueue after close = %q, want %q", outcome, SteerTooLate) + } + // close does NOT drain: the close-parked merged bundle is still there for the + // cancel to retract (closeSteer only flips closed; the terminal close-drain + // is a separate explicit hook). + if outcome, _ := r.cancelSteer(); outcome != SteerRetracted { + t.Fatalf("cancel of the close-parked bundle = %q, want %q (close must not swallow it)", outcome, SteerRetracted) + } + r.closeSteer() // idempotent + + // Concurrent half: enqueue/append/cancel/drain race under -race. Every + // outcome must be a defined enum value, and conservation must hold: every + // marker an enqueue reported parked (accepted/appended) is contained in + // EXACTLY ONE observation — a drained bundle or the terminal pending bundle + // — so no contribution is lost (a torn append) or doubled (a drain that + // didn't clear). Markers in NO bundle were retracted by a racing cancel + // (legal: the cancel takes the whole bundle before any drain sees it). + var mu sync.Mutex + var bundles []string // every drained bundle, whole + stop := make(chan struct{}) + var consumerWG sync.WaitGroup + consumerWG.Add(1) + go func() { + defer consumerWG.Done() + for { + select { + case <-stop: + return + default: + } + if text, ok := r.drainSteer(); ok { + mu.Lock() + bundles = append(bundles, text) + mu.Unlock() + } + } + }() + + var deliveredMu sync.Mutex + delivered := map[string]bool{} // markers an enqueue reported parked + var producersWG sync.WaitGroup + for g := 0; g < 6; g++ { + producersWG.Add(1) + go func(g int) { + defer producersWG.Done() + for i := 0; i < 40; i++ { + marker := fmt.Sprintf("m-%d-%d", g, i) + outcome, err := r.enqueueSteer(marker) + if err != nil { + t.Errorf("enqueue returned error %v", err) + return + } + switch outcome { + case SteerAccepted, SteerAppended: + deliveredMu.Lock() + delivered[marker] = true + deliveredMu.Unlock() + case SteerTooLate: + // The inbox is never closed here, so this arm is unreachable; + // it stays a defined outcome anyway. + default: + t.Errorf("enqueue outcome %q is not a defined SteerOutcome", outcome) + return + } + // A racing cancel retracts the parked bundle whole (its markers + // then legally appear in NO drained bundle). + switch outcome, _ := r.cancelSteer(); outcome { + case SteerRetracted, SteerNonePending: + default: + t.Errorf("cancel outcome %q is not a defined SteerOutcome", outcome) + return + } + } + }(g) + } + producersWG.Wait() + close(stop) + consumerWG.Wait() + // Terminal deterministic drain: whatever is still parked after the producers + // finished and the consumer stopped is ONE final observation. + if text, ok := r.drainSteer(); ok { + mu.Lock() + bundles = append(bundles, text) + mu.Unlock() + } + + mu.Lock() + defer mu.Unlock() + for marker := range delivered { + seen := 0 + for _, bundle := range bundles { + if strings.Contains(bundle, marker) { + seen++ + } + } + if seen > 1 { + t.Fatalf("marker %q appears in %d bundles — the inbox lost a clear or tore an append (linearizability violated)", marker, seen) + } + } +} diff --git a/engine/agent/steer_append_test.go b/engine/agent/steer_append_test.go new file mode 100644 index 0000000000..e2cf874fd3 --- /dev/null +++ b/engine/agent/steer_append_test.go @@ -0,0 +1,189 @@ +package agent_test + +import ( + "context" + "testing" + + "github.com/stacklok/mecatl/engine/adapter/memfs" + "github.com/stacklok/mecatl/engine/adapter/mockllm" + "github.com/stacklok/mecatl/engine/agent" + "github.com/stacklok/mecatl/engine/session" +) + +// TestSteer_AcceptedOnEmpty (R5-1): an enqueue on an EMPTY slot reports +// accepted (never appended) and parks the text as the pending bundle — it +// drains as exactly the parked text (nothing merged in). +func TestSteer_AcceptedOnEmpty(t *testing.T) { + const only = "steer: the only instruction" + gate := newFirstTurnGate() + llm := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(gate.observe)}, + mockllm.ToolCallTurn(toolCall("c1", "Probe", `{}`)), + mockllm.TextTurn("done"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + gate.awaitEntered() + if got := steerEnqueueOutcome(t, r, only); got != agent.SteerAccepted { + t.Fatalf("enqueue on an empty slot outcome = %q, want %q (never appended)", got, agent.SteerAccepted) + } + gate.release() + evs := drainObserving(t, r, nil) + + if res := lastResult(t, evs); res.Stop != session.StopEndTurn { + t.Fatalf("terminal stop = %q (err %q)", res.Stop, res.Error) + } + if !userTextIn(sess.Conversation.Messages, only) { + t.Fatalf("parked steer %q not recorded: %+v", only, sess.Conversation.Messages) + } + echoes := steerEchoes(evs) + if len(echoes) != 1 || echoes[0] != only { + t.Fatalf("EvSteer echoes = %v, want exactly one %q (nothing merged in)", echoes, only) + } +} + +// TestSteer_AppendedMerges (R5-2): an enqueue on an OCCUPIED slot reports +// appended and merges into the pending bundle — old + "\n\n" + new, ONE +// merged bundle (the single slot grows by append; there is never a second +// slot). +func TestSteer_AppendedMerges(t *testing.T) { + gate := newFirstTurnGate() + rec := gate.rec + llm := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(gate.observe)}, + mockllm.ToolCallTurn(toolCall("c1", "Probe", `{}`)), + mockllm.TextTurn("done"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + // Park turn 1 so BOTH enqueues land in the SAME pending window (the drain + // for the next boundary has not run yet). + gate.awaitEntered() + if got := steerEnqueueOutcome(t, r, "steer: first line"); got != agent.SteerAccepted { + t.Fatalf("first enqueue outcome = %q, want %q", got, agent.SteerAccepted) + } + if got := steerEnqueueOutcome(t, r, "steer: second line"); got != agent.SteerAppended { + t.Fatalf("second enqueue outcome = %q, want %q (an occupied slot appends, never rejects)", got, agent.SteerAppended) + } + merged := "steer: first line\n\nsteer: second line" + gate.release() + evs := drainObserving(t, r, nil) + + if res := lastResult(t, evs); res.Stop != session.StopEndTurn { + t.Fatalf("terminal stop = %q (err %q)", res.Stop, res.Error) + } + // ONE merged bundle lands in history (the halves never split into separate + // user messages). + if !userTextIn(sess.Conversation.Messages, merged) { + t.Fatalf("merged steer %q not recorded: %+v", merged, sess.Conversation.Messages) + } + if userTextIn(sess.Conversation.Messages, "steer: first line") { + t.Fatalf("the un-merged first half was recorded — the slot must grow by append, not split") + } + if userTextIn(sess.Conversation.Messages, "steer: second line") { + t.Fatalf("the un-merged second half was recorded — the slot must grow by append, not split") + } + // ONE echo, carrying the merged text — and it is the ONLY user-message the + // model sees on the steer-fed turn (the invariant's model-view half). + echoes := steerEchoes(evs) + if len(echoes) != 1 || echoes[0] != merged { + t.Fatalf("EvSteer echoes = %v, want exactly one %q (the merged bundle)", echoes, merged) + } + if !userTextIn(rec.get(1).Messages, merged) { + t.Fatalf("merged steer %q not replayed to the model: %+v", merged, rec.get(1).Messages) + } +} + +// TestSteer_AppendedDrainMerged (R5-5): an appended bundle drains as ONE +// merged user continuation at the boundary — recordContinuation records the +// merged text exactly once, the EvSteer echo carries the SAME merged text (the +// authoritative committed version), the echo is sequenced BEFORE the turn it +// feeds, and the record is provider-legal (ValidateToolPairing holds). +func TestSteer_AppendedDrainMerged(t *testing.T) { + gate := newFirstTurnGate() + llm := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(gate.observe)}, + mockllm.ToolCallTurn(toolCall("c1", "Probe", `{}`)), + mockllm.TextTurn("done"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + gate.awaitEntered() + // Three sends into the same pending window: accept + two appends — ONE + // merged bundle of three lines. + steerEnqueueOutcome(t, r, "steer: alpha") + steerEnqueueOutcome(t, r, "steer: beta") + steerEnqueueOutcome(t, r, "steer: gamma") + merged := "steer: alpha\n\nsteer: beta\n\nsteer: gamma" + gate.release() + evs := drainObserving(t, r, nil) + + if res := lastResult(t, evs); res.Stop != session.StopEndTurn { + t.Fatalf("terminal stop = %q (err %q)", res.Stop, res.Error) + } + // The merged bundle is recorded EXACTLY ONCE as a user continuation. + recorded := 0 + for _, m := range sess.Conversation.Messages { + if m.Role == session.RoleUser && m.Text == merged { + recorded++ + } + } + if recorded != 1 { + t.Fatalf("merged bundle recorded %d times, want exactly 1 (one user continuation per drain): %+v", recorded, sess.Conversation.Messages) + } + // The EvSteer echo carries the SAME merged text — recorded == streamed. + var echo *session.Event + for i := range evs { + if evs[i].Type == session.EvSteer { + if echo != nil { + t.Fatalf("more than one EvSteer emitted (seqs %d and %d) — the drain commits once", echo.Seq, evs[i].Seq) + } + echo = &evs[i] + } + } + if echo == nil || echo.Steer == nil { + t.Fatalf("no EvSteer echo emitted on drain; types=%v", typesOf(evs)) + } + if echo.Steer.Text != merged { + t.Fatalf("EvSteer echo text = %q, want the merged %q", echo.Steer.Text, merged) + } + // The echo is sequenced BEFORE the turn it feeds (turn 1's turn.start). + var turn1StartSeq int64 = -1 + for _, ev := range evs { + if ev.Type == session.EvTurnStart && ev.Turn == 1 { + turn1StartSeq = ev.Seq + break + } + } + if turn1StartSeq == -1 { + t.Fatalf("no turn.start for turn 1 (the steer-fed turn); types=%v", typesOf(evs)) + } + if echo.Seq >= turn1StartSeq { + t.Fatalf("EvSteer echo seq %d must precede the turn.start (seq %d) of the turn it feeds", echo.Seq, turn1StartSeq) + } + // The recorded continuation keeps history provider-legal. + if err := session.ValidateToolPairing(sess.Conversation.Messages); err != nil { + t.Fatalf("ValidateToolPairing after the merged drain: %v", err) + } +} diff --git a/engine/agent/steer_awaiting_test.go b/engine/agent/steer_awaiting_test.go new file mode 100644 index 0000000000..66b0ea16de --- /dev/null +++ b/engine/agent/steer_awaiting_test.go @@ -0,0 +1,422 @@ +package agent_test + +import ( + "context" + "sync/atomic" + "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/adapter/permstore" + "github.com/stacklok/mecatl/engine/agent" + "github.com/stacklok/mecatl/engine/session" + "github.com/stacklok/mecatl/engine/tool" +) + +// --------------------------------------------------------------------------- +// Task 04 — steer while awaiting an ask: queue-only, drain on resume. +// +// While a run is parked `awaiting` on a permission ask the loop is suspended +// in PauseForApproval (surfaceAsk → askRegistry.await) — it is NOT iterating +// runLoop, so the Step 2a drain cannot fire until the verdict resumes the +// loop. The inbox must HOLD the pending steer across the parked state (the +// run is parked, not terminal, so closeSteer must NOT have run); the verdict +// then resumes the loop and the first post-resume turn boundary drains it. +// The steer is purely additive input (the "yes-and"/"no-and" case) — it never +// resolves, modifies, or bypasses the pending ask. +// --------------------------------------------------------------------------- + +// awaitAskParked consumes events until the run emits its first EvPermissionAsk +// (the run goroutine is then genuinely PARKED in askRegistry.await — the +// session is StateAwaiting and no runLoop iteration is in flight), records the +// ask, and returns the collected events plus the ask. It does NOT resolve the +// ask — the caller owns the verdict timing. Watchdog-bounded so a wedge fails +// rather than hanging the suite. +func awaitAskParked(t *testing.T, r *agent.Run) (evs []session.Event, ask *session.PendingAsk) { + t.Helper() + deadline := time.After(10 * time.Second) + ch := r.Events() + for { + select { + case ev, ok := <-ch: + if !ok { + t.Fatalf("run terminated before emitting a permission ask; types=%v", typesOf(evs)) + } + evs = append(evs, ev) + if ev.Type == session.EvPermissionAsk && ev.Ask != nil { + return evs, ev.Ask + } + case <-deadline: + r.Cancel() + t.Fatalf("run did not reach a permission ask within the deadline (possible wedge)") + return nil, nil + } + } +} + +// drainRest consumes the remaining events after awaitAskParked returned, +// invoking observe for each, until the run's channel closes. +func drainRest(t *testing.T, r *agent.Run, observe func(session.Event)) []session.Event { + t.Helper() + var evs []session.Event + deadline := time.After(10 * time.Second) + ch := r.Events() + for { + select { + case ev, ok := <-ch: + if !ok { + return evs + } + evs = append(evs, ev) + if observe != nil { + observe(ev) + } + case <-deadline: + r.Cancel() + t.Fatalf("run did not terminate within the deadline after the ask (possible wedge)") + return evs + } + } +} + +// TestSteer_AwaitingAskIsHeld (AC4.1): a steer submitted while a run is parked +// `awaiting` on a permission ask is ACCEPTED into the inbox (not too_late — +// the run is parked, not terminal, so the inbox must NOT be closed while +// awaiting) and HELD — it is not recorded, echoed, or replayed to the model +// while the ask is still parked (the loop is suspended in PauseForApproval, so +// no turn boundary passes). Only the verdict resumes the loop, and the held +// steer drains at the first post-resume boundary. +func TestSteer_AwaitingAskIsHeld(t *testing.T) { + const steerText = "steer: held across the parked ask" + var executed atomic.Bool + write := &fakeTool{name: "Write", readOnly: false, + exec: func(_ context.Context, in session.ToolCall, _ tool.Workspace) (session.ToolResult, error) { + executed.Store(true) + return session.NewToolResult(in.ID, "wrote"), nil + }} + rec := &requestRecorder{} + llm := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(rec.observe)}, + mockllm.ToolCallTurn(toolCall("c1", "Write", `{"path":"a"}`)), + mockllm.TextTurn("done after the verdict"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, write), + Policy: permpolicy.NewPolicy(nil, permstore.New()), // Write asks by default + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + parked, ask := awaitAskParked(t, r) + + // The run is PARKED awaiting: the inbox must still be OPEN (closeSteer only + // runs on a genuine terminal), so the steer is accepted, not too_late. + if got := steerEnqueueOutcome(t, r, steerText); got != agent.SteerAccepted { + t.Fatalf("enqueue while awaiting outcome = %q, want %q (a parked run is live, not terminal — the inbox must stay open)", got, agent.SteerAccepted) + } + + // HELD, not delivered early: while the ask is still parked, nothing may + // record / echo / replay the steer. Give the loop a real chance to misbehave: + // if a drain could fire while parked, the recorded message + EvSteer echo + // would appear promptly. We assert their absence on the parked-phase stream + // and history BEFORE resolving the ask (a drain while parked would also + // wedge the run — the parked goroutine is inside dispatch, not runLoop). + if userTextIn(sess.Conversation.Messages, steerText) { + t.Fatalf("steer %q was recorded while the ask was still parked — it must be HELD until the verdict resumes the loop", steerText) + } + if echoes := steerEchoes(parked); len(echoes) != 0 { + t.Fatalf("EvSteer echoes while parked = %v, want none (the drain must wait for the verdict)", echoes) + } + // Turn 1's request is already served (it produced the tool call); it must + // NOT carry the steer, and there must be exactly ONE model call so far (no + // phantom turn consumed the steer early). + if rec.count() != 1 { + t.Fatalf("model calls while parked = %d, want 1 (no turn may run while the ask is parked)", rec.count()) + } + if userTextIn(rec.get(0).Messages, steerText) { + t.Fatalf("steer %q reached the model on turn 1 — it must drain only after the verdict", steerText) + } + + // Now the verdict resumes the loop: the held steer drains at the first + // post-resume boundary and replays on the next turn. + r.Approve(ask.AskID, session.VerdictAllowOnce) + rest := drainRest(t, r, nil) + evs := append(parked, rest...) + + if res := lastResult(t, evs); res.Stop != session.StopEndTurn { + t.Fatalf("terminal stop = %q, want end_turn (err %q)", res.Stop, res.Error) + } + if !executed.Load() { + t.Fatalf("the approved tool was not executed after the verdict") + } + if !userTextIn(sess.Conversation.Messages, steerText) { + t.Fatalf("held steer %q not recorded after the resume: %+v", steerText, sess.Conversation.Messages) + } + if echoes := steerEchoes(evs); len(echoes) != 1 || echoes[0] != steerText { + t.Fatalf("EvSteer echoes = %v, want exactly one %q (the held steer drains once, on resume)", echoes, steerText) + } + if got := llm.Calls(); got != 2 { + t.Fatalf("model calls = %d, want 2 (the steer replays on the post-resume turn)", got) + } + if !userTextIn(rec.get(1).Messages, steerText) { + t.Fatalf("held steer %q not replayed to the model on the post-resume turn: %+v", steerText, rec.get(1).Messages) + } + // The steer drains at the post-resume BOUNDARY — AFTER the gated tool's + // result settles — never inside the tool_use pair. Assert the ordering + // (steer lands after the c1 tool result) and that pairing holds: a drain + // that fired inside dispatch (before the tool result was recorded) would + // wedge the user message between the assistant call and its result. + if err := session.ValidateToolPairing(sess.Conversation.Messages); err != nil { + t.Fatalf("ValidateToolPairing after the resume drain: %v (the steer must land after the settled tool pair, not inside it)", err) + } + steerIdx, toolResultIdx := -1, -1 + for i, m := range sess.Conversation.Messages { + if m.Role == session.RoleUser && m.Text == steerText { + steerIdx = i + } + if m.Role == session.RoleTool && m.ToolResult != nil && m.ToolResult.CallID == "c1" { + toolResultIdx = i + } + } + if steerIdx == -1 || toolResultIdx == -1 { + t.Fatalf("expected the steer (idx %d) and the c1 tool result (idx %d) on history", steerIdx, toolResultIdx) + } + if steerIdx < toolResultIdx { + t.Fatalf("steer (idx %d) recorded BEFORE the gated tool's result (idx %d) — it was delivered early, inside dispatch, not held to the post-resume boundary", steerIdx, toolResultIdx) + } +} + +// TestSteer_AwaitingResumeDrains (AC4.2): on a verdict-driven resume the held +// steer is drained at the resumed run's FIRST turn boundary and replayed to the +// model. This drives the CROSS-PROCESS seam (driveFromAwaiting via +// Engine.ResumeApproval): the parked run is snapshot-dead, the awaiting session +// is restored, the steer is enqueued on the RESUMED run, and the resumed run's +// first Step 2a boundary drains it — the steer lands after the settled tool +// results, before the first post-resume model call. +func TestSteer_AwaitingResumeDrains(t *testing.T) { + const steerText = "steer: drained at the resumed run's first boundary" + policy := permpolicy.NewPolicy(nil, permstore.New()) // Write asks by default + sess := session.New("s-steer-resume", session.ModeDefault, "/ws", session.Limits{}, time.Unix(0, 0)) + + // Engine #1: emits a Write tool call (gated as Ask), parks awaiting, then + // "dies" (snapshot round-trip models the process death; the parked run and + // its askRegistry are discarded). + write1 := &fakeTool{name: "Write", readOnly: false, + exec: func(_ context.Context, in session.ToolCall, _ tool.Workspace) (session.ToolResult, error) { + return session.NewToolResult(in.ID, "wrote"), nil + }} + e1 := newEngine(agent.Deps{ + LLM: mockllm.New(mockllm.ToolCallTurn(toolCall("w1", "Write", `{"path":"a.go"}`))), + Catalog: catalogWith(t, write1), + Policy: policy, + }) + askID, restored := driveToAwaiting(t, e1, sess, agent.EnvForWS(memfs.NewWorkspace("/ws"), nil), "go") + + // Engine #2 (the "restarted process"): a FRESH steer-enabled engine over the + // restored awaiting session. Its mockllm serves only the CONTINUATION turn. + rec := &requestRecorder{} + write2 := &fakeTool{name: "Write", readOnly: false, + exec: func(_ context.Context, in session.ToolCall, _ tool.Workspace) (session.ToolResult, error) { + return session.NewToolResult(in.ID, "wrote"), nil + }} + e2 := newEngine(agent.Deps{ + LLM: mockllm.NewWith([]mockllm.Option{mockllm.WithRequestObserver(rec.observe)}, mockllm.TextTurn("done after resume")), + Catalog: catalogWith(t, write2), + Policy: policy, + EnableSteer: true, + }) + + // The resumed run re-enters AT the parked ask. Enqueue the steer on the + // RESUMED run immediately — the resume's verdict resolution (execute the + // pending call, record results) runs BEFORE runLoop's first Step 2a, so the + // steer is held through the verdict and drains at the first boundary. + r2 := e2.ResumeApproval(context.Background(), restored, agent.EnvForWS(memfs.NewWorkspace("/ws"), nil), askID, session.VerdictAllowOnce) + if got := steerEnqueueOutcome(t, r2, steerText); got != agent.SteerAccepted { + t.Fatalf("enqueue on the resumed run outcome = %q, want %q", got, agent.SteerAccepted) + } + evs := drainObserving(t, r2, nil) + + if res := lastResult(t, evs); res.Stop != session.StopEndTurn { + t.Fatalf("resumed run stop = %q, want end_turn (err %q)", res.Stop, res.Error) + } + // The held steer drained: recorded as an ordinary user message, echoed once. + if !userTextIn(restored.Conversation.Messages, steerText) { + t.Fatalf("held steer %q not recorded on the resumed run: %+v", steerText, restored.Conversation.Messages) + } + if echoes := steerEchoes(evs); len(echoes) != 1 || echoes[0] != steerText { + t.Fatalf("EvSteer echoes = %v, want exactly one %q (the held steer drains at the resumed run's first boundary)", echoes, steerText) + } + // Pairing holds: the steer lands AFTER the settled tool results (the resumed + // pending call's result + any synthetic sibling close-outs), never inside a + // tool_use pair. + if err := session.ValidateToolPairing(restored.Conversation.Messages); err != nil { + t.Fatalf("ValidateToolPairing after the resume drain: %v", err) + } + // Replayed to the model on the resumed run's FIRST post-resume turn (the + // only model call this run makes — the drain precedes BeginTurn for it). + if got := rec.count(); got != 1 { + t.Fatalf("resumed run model calls = %d, want 1 (the steer drains before the first post-resume turn)", got) + } + if !userTextIn(rec.get(0).Messages, steerText) { + t.Fatalf("held steer %q not replayed to the model on the resumed run's first turn: %+v", steerText, rec.get(0).Messages) + } + // The drain is sequenced BEFORE the turn it feeds: the EvSteer echo precedes + // the first post-resume turn.start on the wire. + var echoSeq, turnStartSeq int64 = -1, -1 + for _, ev := range evs { + if ev.Type == session.EvSteer && echoSeq == -1 { + echoSeq = ev.Seq + } + if ev.Type == session.EvTurnStart && turnStartSeq == -1 { + turnStartSeq = ev.Seq + } + } + if echoSeq == -1 || turnStartSeq == -1 { + t.Fatalf("missing EvSteer echo (seq %d) or turn.start (seq %d); types=%v", echoSeq, turnStartSeq, typesOf(evs)) + } + if echoSeq >= turnStartSeq { + t.Fatalf("EvSteer echo seq %d must precede the resumed run's first turn.start (seq %d)", echoSeq, turnStartSeq) + } +} + +// TestSteer_AskStillRequiresVerdict (AC4.3): the held steer does NOT resolve, +// modify, or bypass the pending ask — the ask still requires an explicit +// verdict. Enqueue a steer while parked and DO NOT approve: the run must stay +// parked (the parked tool must NOT execute, no EvApproval may be emitted, the +// session stays StateAwaiting) until a real verdict arrives. The steer is +// purely additive input — the "yes-and" case (approve + steer) still needs the +// explicit approve, and the "no-and" case (deny + steer) still needs the +// explicit deny. +func TestSteer_AskStillRequiresVerdict(t *testing.T) { + const steerText = "steer: yes-and — additive, never a verdict" + var executed atomic.Bool + write := &fakeTool{name: "Write", readOnly: false, + exec: func(_ context.Context, in session.ToolCall, _ tool.Workspace) (session.ToolResult, error) { + executed.Store(true) + return session.NewToolResult(in.ID, "wrote"), nil + }} + llm := mockllm.New( + mockllm.ToolCallTurn(toolCall("c1", "Write", `{"path":"a"}`)), + mockllm.TextTurn("done after the verdict"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, write), + Policy: permpolicy.NewPolicy(nil, permstore.New()), // Write asks by default + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + parked, ask := awaitAskParked(t, r) + + // Enqueue the steer while parked. It must NOT resolve the ask: no verdict is + // synthesized, the tool does NOT run, the session stays StateAwaiting. + if got := steerEnqueueOutcome(t, r, steerText); got != agent.SteerAccepted { + t.Fatalf("enqueue while awaiting outcome = %q, want %q", got, agent.SteerAccepted) + } + + // Assert the ask is STILL pending after the steer: the session remains + // StateAwaiting with the SAME pending ask (unmodified), no EvApproval was + // emitted, and the gated tool has not executed. This is the core "the steer + // is not a verdict" assertion — if the enqueue resolved or altered the ask, + // one of these flips. + if sess.State != session.StateAwaiting { + t.Fatalf("session state after steer enqueue = %q, want %q (the steer must not resolve the ask)", sess.State, session.StateAwaiting) + } + pending, ok := sess.PendingAsk() + if !ok { + t.Fatalf("the pending ask was cleared by the steer enqueue — the ask must survive untouched") + } + if pending.AskID != ask.AskID || pending.Tool != ask.Tool { + t.Fatalf("the pending ask was modified by the steer: before=%+v after=%+v", ask, pending) + } + for _, ev := range parked { + if ev.Type == session.EvApproval { + t.Fatalf("an EvApproval was emitted without a verdict — the steer must never resolve the ask") + } + } + if executed.Load() { + t.Fatalf("the gated tool executed without a verdict — the steer must not bypass the ask") + } + + // THE CORE AC4.3 PROBE: with the steer enqueued but NO explicit verdict given, + // the run must STAY parked — a steer that resolved/bypassed the ask would + // un-park the loop, emitting EvApproval + EvToolResult (the tool runs) and + // eventually closing the channel. Poll the event channel for a bounded window + // and assert NONE of those arrive. This is the assertion that distinguishes + // "the explicit verdict un-parked the run" from "the steer did": an + // auto-resolving steer flips one of these BEFORE we ever call Approve. + parkDeadline := time.After(300 * time.Millisecond) +parkLoop: + for { + select { + case ev, ok := <-r.Events(): + if !ok { + t.Fatalf("the run terminated without an explicit verdict — the steer resolved/bypassed the ask") + } + parked = append(parked, ev) + switch ev.Type { + case session.EvApproval: + t.Fatalf("an EvApproval was emitted without an explicit verdict — the steer resolved the ask") + case session.EvToolResult: + t.Fatalf("the gated tool produced a result without an explicit verdict — the steer bypassed the ask") + } + case <-parkDeadline: + // No verdict-driven event arrived in the window: the run is still + // parked on the ask, exactly as required. Break and give the verdict. + break parkLoop + } + } + if executed.Load() { + t.Fatalf("the gated tool executed during the no-verdict window — the steer bypassed the ask") + } + + // The explicit verdict is what un-parks the run — approve now and confirm the + // run completes, the tool runs, and the held steer drains as ADDITIVE input + // (the yes-and case: approve AND steer, both honoured, neither replacing the + // other). + r.Approve(ask.AskID, session.VerdictAllowOnce) + rest := drainRest(t, r, nil) + evs := append(parked, rest...) + + if res := lastResult(t, evs); res.Stop != session.StopEndTurn { + t.Fatalf("terminal stop = %q, want end_turn (err %q)", res.Stop, res.Error) + } + if !executed.Load() { + t.Fatalf("the approved tool was not executed after the explicit verdict") + } + // Exactly ONE EvApproval, carrying the explicit verdict — the steer added + // input but the verdict came from Approve, not from the steer. + var approvals int + for _, ev := range evs { + if ev.Type == session.EvApproval { + approvals++ + if ev.Approval == nil || ev.Approval.AskID != ask.AskID { + t.Fatalf("EvApproval does not name the parked ask %q: %+v", ask.AskID, ev.Approval) + } + } + } + if approvals != 1 { + t.Fatalf("EvApproval count = %d, want exactly 1 (the explicit verdict; the steer synthesizes none)", approvals) + } + // The held steer drained as additive input after the verdict (yes-and). + if !userTextIn(sess.Conversation.Messages, steerText) { + t.Fatalf("held steer %q not recorded after the verdict (the yes-and case): %+v", steerText, sess.Conversation.Messages) + } + if echoes := steerEchoes(evs); len(echoes) != 1 || echoes[0] != steerText { + t.Fatalf("EvSteer echoes = %v, want exactly one %q", echoes, steerText) + } + // The model sees BOTH the verdict's tool result AND the additive steer: the + // run makes exactly two model calls (turn 1 gated the tool; turn 2 runs after + // the verdict with the settled tool result + the drained steer on history). + if rec := llm.Calls(); rec != 2 { + t.Fatalf("model calls = %d, want 2 (verdict settles the tool, then the steered turn runs)", rec) + } +} diff --git a/engine/agent/steer_internal_test.go b/engine/agent/steer_internal_test.go new file mode 100644 index 0000000000..37304293ac --- /dev/null +++ b/engine/agent/steer_internal_test.go @@ -0,0 +1,113 @@ +package agent + +import ( + "sync" + "testing" +) + +// TestSteer_InboxLinearizable (R2-3): the mutex inbox makes the finding-#1 +// deadlock shape impossible BY CONSTRUCTION — every transition (enqueue / +// cancel / drain / close) is ONE critical section over the whole +// {closed,pending,has} triple, so there is no receive/send between two selects +// and no done-check torn from the send. A forced drain between a would-be +// observe and a would-be mutate cannot deadlock, cannot double-commit, and +// cannot resurrect a drained steer. Run under -race. +func TestSteer_InboxLinearizable(t *testing.T) { + // Deterministic sequencing: the drain (the run goroutine's role) is forced + // BETWEEN the two enqueues — the exact interleave that on the old cap-1 + // channel was a non-atomic "observe full → receive → send" replace window. + r := &Run{steer: newSteerInbox()} + + if outcome, _ := r.enqueueSteer("steer: v1"); outcome != SteerAccepted { + t.Fatalf("first enqueue = %q, want %q", outcome, SteerAccepted) + } + // Occupied slot: the second enqueue APPENDS in the same critical section — + // the pending bundle's text grows by "\n\n"+v2 (append is the default; the + // old reject-on-full/supersede was dropped). + if outcome, _ := r.enqueueSteer("steer: v2"); outcome != SteerAppended { + t.Fatalf("enqueue on an occupied slot = %q, want %q", outcome, SteerAppended) + } + // The merged bundle drains ONCE as the appended text (v1 + "\n\n" + v2). + text, ok := r.drainSteer() + if !ok || text != "steer: v1\n\nsteer: v2" { + t.Fatalf("drain = (%q, %v), want (%q, true) — the appended bundle commits exactly once", text, ok, "steer: v1\n\nsteer: v2") + } + // The drained slot is EMPTY: a second drain commits nothing (no + // double-commit) and a cancel finds nothing (no resurrection). + if text, ok := r.drainSteer(); ok { + t.Fatalf("second drain = (%q, %v), want empty — the drain must take+clear atomically", text, ok) + } + if outcome, _ := r.cancelSteer(); outcome != SteerNonePending { + t.Fatalf("cancel after the drain = %q, want %q (the drained steer is gone)", outcome, SteerNonePending) + } + // A post-drain enqueue lands in the re-emptied slot (the inbox stays open + // until close); close then flips too_late / none_pending deterministically. + if outcome, _ := r.enqueueSteer("steer: v3"); outcome != SteerAccepted { + t.Fatalf("post-drain enqueue = %q, want %q", outcome, SteerAccepted) + } + r.closeSteer() + if outcome, _ := r.enqueueSteer("steer: too late"); outcome != SteerTooLate { + t.Fatalf("enqueue after close = %q, want %q", outcome, SteerTooLate) + } + // close does NOT drain: a steer parked at close is retracted by the cancel + // (the terminal close-drain is a separate explicit hook — closeSteer itself + // only flips closed). + if outcome, _ := r.cancelSteer(); outcome != SteerRetracted { + t.Fatalf("cancel of the close-parked steer = %q, want %q (close must not swallow the parked steer)", outcome, SteerRetracted) + } + // close is idempotent. + r.closeSteer() +} + +// TestSteerInboxConcurrentTransitions exercises the inbox's four transitions +// (enqueue / cancel-retract / drain-commit / close-terminal) under direct +// concurrent access, including the closeSteer seam the external test only +// reaches by driving a full run to terminal. Run under -race: the run goroutine +// drains while wire-handler goroutines enqueue and cancel, and any missing +// mutex is a data race. It asserts the outcome ENUM is always a defined value +// and that a closed inbox reports too_late / none_pending — never panics. +func TestSteerInboxConcurrentTransitions(t *testing.T) { + r := &Run{steer: newSteerInbox()} + + var wg sync.WaitGroup + // One goroutine drains (the run loop's role) — the ONLY slot consumer. + wg.Add(1) + go func() { + defer wg.Done() + for i := 0; i < 400; i++ { + r.drainSteer() + } + }() + // Several goroutines enqueue (into the single slot) and cancel (retract). + for g := 0; g < 6; g++ { + wg.Add(1) + go func() { + defer wg.Done() + for i := 0; i < 200; i++ { + switch outcome, _ := r.enqueueSteer("steer: internal racer"); outcome { + case SteerAccepted, SteerAppended, SteerTooLate: + default: + t.Errorf("enqueue outcome %q is not a defined SteerOutcome", outcome) + return + } + switch outcome, _ := r.cancelSteer(); outcome { + case SteerRetracted, SteerNonePending: + default: + t.Errorf("cancel outcome %q is not a defined SteerOutcome", outcome) + return + } + } + }() + } + wg.Wait() + + // Close the inbox (run terminal) and confirm the too-late contract holds + // against a final racing enqueue/cancel. + r.closeSteer() + if outcome, _ := r.enqueueSteer("steer: after close"); outcome != SteerTooLate { + t.Fatalf("enqueue after close = %q, want %q", outcome, SteerTooLate) + } + if outcome, _ := r.cancelSteer(); outcome != SteerNonePending { + t.Fatalf("cancel after close = %q, want %q", outcome, SteerNonePending) + } +} diff --git a/engine/agent/steer_test.go b/engine/agent/steer_test.go new file mode 100644 index 0000000000..4a6d654bfa --- /dev/null +++ b/engine/agent/steer_test.go @@ -0,0 +1,1286 @@ +package agent_test + +import ( + "context" + "reflect" + "strings" + "sync" + "sync/atomic" + "testing" + "unicode/utf8" + + "github.com/stacklok/mecatl/engine/adapter/eventsource" + "github.com/stacklok/mecatl/engine/adapter/memfs" + "github.com/stacklok/mecatl/engine/adapter/memstore" + "github.com/stacklok/mecatl/engine/adapter/mockllm" + "github.com/stacklok/mecatl/engine/adapter/sessnap" + "github.com/stacklok/mecatl/engine/agent" + "github.com/stacklok/mecatl/engine/port" + "github.com/stacklok/mecatl/engine/session" +) + +// userTextIn reports whether a user-role message with the given text appears in +// the message slice. +func userTextIn(msgs []session.Message, text string) bool { + for _, m := range msgs { + if m.Role == session.RoleUser && m.Text == text { + return true + } + } + return false +} + +// firstTurnGate is a mockllm request observer that parks the FIRST model call +// until the test releases it, giving a deterministic MID-FLIGHT enqueue anchor. +// The first Stream invocation signals entered, then blocks on proceed; the test +// waits on entered (so turn 1's drain has already run and turn 1's request is +// being served — it can never carry the steer), enqueues, then releases. The +// steer is thus guaranteed to drain at the NEXT turn boundary (before turn 2), +// removing the enqueue-vs-turn-1-drain race a bare enqueue-after-Run leaves. +type firstTurnGate struct { + entered chan struct{} + proceed chan struct{} + sigOnce sync.Once + relOnce sync.Once + seen atomic.Bool + rec *requestRecorder +} + +func newFirstTurnGate() *firstTurnGate { + return &firstTurnGate{ + entered: make(chan struct{}), + proceed: make(chan struct{}), + rec: &requestRecorder{}, + } +} + +// observe is the mockllm WithRequestObserver callback. +func (g *firstTurnGate) observe(req port.LLMRequest) { + if g.seen.CompareAndSwap(false, true) { + g.sigOnce.Do(func() { close(g.entered) }) + <-g.proceed // park ONLY the first model call until the test enqueues + } + g.rec.observe(req) +} + +// awaitEntered blocks until turn 1's model call has reached the observer (the +// run is genuinely mid-flight with turn 1's drain already done). +func (g *firstTurnGate) awaitEntered() { <-g.entered } + +// release lets the parked first model call proceed. Idempotent. +func (g *firstTurnGate) release() { g.relOnce.Do(func() { close(g.proceed) }) } + +// steerEnqueue enqueues a steer on a live run via the test seam (task 01 adds +// only the unexported enqueue; the wire-facing entry point is a later task). It +// asserts the enqueue was accepted (the task-01 tests enqueue into an empty +// slot on a live run). +func steerEnqueue(t *testing.T, r *agent.Run, text string) { + t.Helper() + outcome, err := agent.EnqueueSteerForTest(r, text) + if err != nil { + t.Fatalf("enqueue steer %q: %v", text, err) + } + if outcome != agent.SteerAccepted { + t.Fatalf("enqueue steer %q outcome = %q, want %q", text, outcome, agent.SteerAccepted) + } +} + +// TestSteer_InjectedAtTurnBoundary (AC1.1): a steer enqueued while a run is +// mid-flight is recorded as an ordinary user message at the next turn boundary, +// appears in Conversation.Messages, and is replayed to the model on the +// following turn. +func TestSteer_InjectedAtTurnBoundary(t *testing.T) { + const steerText = "steer: prefer the smaller refactor" + gate := newFirstTurnGate() + rec := gate.rec + llm := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(gate.observe)}, + mockllm.ToolCallTurn(toolCall("c1", "Probe", `{}`)), + mockllm.TextTurn("done after steer"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + // Enqueue while the run is genuinely MID-FLIGHT: awaitEntered confirms turn + // 1's model call is parked (turn 1's drain already ran), so the steer drains + // at the NEXT boundary (before turn 2), never on turn 1's served request. + gate.awaitEntered() + steerEnqueue(t, r, steerText) + gate.release() + evs := drainObserving(t, r, nil) + + if res := lastResult(t, evs); res.Stop != session.StopEndTurn { + t.Fatalf("terminal stop = %q, want end_turn (a steer must not break the run; err %q)", res.Stop, res.Error) + } + // The steer must be recorded as an ordinary user message on history. + if !userTextIn(sess.Conversation.Messages, steerText) { + t.Fatalf("steer %q not recorded in Conversation.Messages: %+v", steerText, sess.Conversation.Messages) + } + // Exactly two model calls (turn 1 tool call → turn 2 text answer); the steer + // is replayed on the SECOND turn's request (the turn after the boundary). + if got := llm.Calls(); got != 2 { + t.Fatalf("model calls = %d, want 2 (steer injected at the boundary before turn 2)", got) + } + if !userTextIn(rec.get(1).Messages, steerText) { + t.Fatalf("steer %q not replayed to the model on turn 2's request: %+v", steerText, rec.get(1).Messages) + } + // Turn 1 must NOT have seen it (it was enqueued mid-flight, after turn 1 was + // already underway or before — but the DRAIN is at the boundary before turn 2). + if userTextIn(rec.get(0).Messages, steerText) { + t.Fatalf("steer must not appear on turn 1's request (it drains at the boundary before turn 2)") + } +} + +// TestSteer_RecordedAndRehydrated (AC1.2): the steer is recorded via +// recordContinuation, so the durable log captures it (log-only EvUserPrompt) and +// eventsource.Fold reconstructs the user turn across rehydration. +func TestSteer_RecordedAndRehydrated(t *testing.T) { + const steerText = "steer: use the eventsource fold" + eventLog := memstore.NewEventLog() + llm := mockllm.New( + mockllm.ToolCallTurn(toolCall("c1", "Probe", `{}`)), + mockllm.TextTurn("done"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + steerEnqueue(t, r, steerText) + evs := drainObserving(t, r, nil) + if res := lastResult(t, evs); res.Stop != session.StopEndTurn { + t.Fatalf("terminal stop = %q, want end_turn (err %q)", res.Stop, res.Error) + } + + // Persist the emitted stream to the durable log the way the relay does, then + // fold it back — ADR-0038 rehydration. The steer's EvUserPrompt must be in + // the stream so the fold rebuilds the user turn. + for _, ev := range evs { + if err := eventLog.Append(context.Background(), sess.ID, ev); err != nil { + t.Fatalf("append event: %v", err) + } + } + // Sanity: the emitted stream carried the steer's log-only EvUserPrompt. + var sawSteerPrompt bool + for _, ev := range evs { + if ev.Type == session.EvUserPrompt && ev.UserPrompt != nil && ev.UserPrompt.Text == steerText { + sawSteerPrompt = true + } + } + if !sawSteerPrompt { + t.Fatalf("the steer's log-only EvUserPrompt was not emitted; types=%v", typesOf(evs)) + } + + folded, err := eventsource.Fold(eventsource.SessionMeta{ + ID: sess.ID, + Mode: sess.Mode, + Limits: sess.Limits, + Workspace: sess.Workspace, + }, eventLog.Read(context.Background(), sess.ID)) + if err != nil { + t.Fatalf("eventsource.Fold: %v", err) + } + if !userTextIn(folded.Conversation.Messages, steerText) { + t.Fatalf("rehydrated conversation lost the steer %q: %+v", steerText, folded.Conversation.Messages) + } +} + +// TestSteer_PreservesToolPairing (AC1.3): the steer is appended AFTER the +// settled tool results, never inside a tool_use pair — ValidateToolPairing +// holds on the post-injection history. +func TestSteer_PreservesToolPairing(t *testing.T) { + const steerText = "steer: keep pairing valid" + gate := newFirstTurnGate() + llm := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(gate.observe)}, + mockllm.ToolCallTurn(toolCall("c1", "Probe", `{}`)), + mockllm.TextTurn("done"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + // Mid-flight enqueue (gated on turn 1) so the steer drains at the NEXT + // boundary — after turn 1's tool pair has settled, never inside it. + gate.awaitEntered() + steerEnqueue(t, r, steerText) + gate.release() + evs := drainObserving(t, r, nil) + if res := lastResult(t, evs); res.Stop != session.StopEndTurn { + t.Fatalf("terminal stop = %q, want end_turn (err %q)", res.Stop, res.Error) + } + + msgs := sess.Conversation.Messages + if err := session.ValidateToolPairing(msgs); err != nil { + t.Fatalf("ValidateToolPairing failed on the post-injection history: %v\n%+v", err, msgs) + } + // The steer (a user message) must come AFTER the tool result that answers + // c1 — i.e. it lands after the settled tool pair, not between the assistant + // tool call and its result. + var steerIdx, toolResultIdx = -1, -1 + for i, m := range msgs { + if m.Role == session.RoleUser && m.Text == steerText { + steerIdx = i + } + if m.Role == session.RoleTool { + toolResultIdx = i + } + } + if steerIdx == -1 { + t.Fatalf("steer not found on history") + } + if toolResultIdx == -1 { + t.Fatalf("no tool result on history (script must dispatch the Probe call)") + } + if steerIdx < toolResultIdx { + t.Fatalf("steer (idx %d) recorded before the settled tool result (idx %d) — it must drain at the boundary AFTER the pair settles", steerIdx, toolResultIdx) + } +} + +// TestSteer_KeepsPromptPrefixByteStable (AC1.4): the injected conversation +// keeps the message prefix byte-stable within the run — turn 2's request prefix +// (before the steer's appended message) equals turn 1's whole request, so the +// steer costs no prompt-cache rebuild beyond normal history growth. +func TestSteer_KeepsPromptPrefixByteStable(t *testing.T) { + const steerText = "steer: keep the cache prefix" + gate := newFirstTurnGate() + rec := gate.rec + llm := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(gate.observe)}, + mockllm.ToolCallTurn(toolCall("c1", "Probe", `{}`)), + mockllm.TextTurn("done"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + // Mid-flight enqueue (gated on turn 1): the steer drains at the boundary + // before turn 2, so turn 2's request = turn 1's prefix + appended growth. + gate.awaitEntered() + steerEnqueue(t, r, steerText) + gate.release() + evs := drainObserving(t, r, nil) + if res := lastResult(t, evs); res.Stop != session.StopEndTurn { + t.Fatalf("terminal stop = %q (err %q)", res.Stop, res.Error) + } + if got := llm.Calls(); got != 2 { + t.Fatalf("model calls = %d, want 2", got) + } + + turn1 := rec.get(0).Messages + turn2 := rec.get(1).Messages + // Turn 2 = turn 1's messages (the byte-stable prefix) PLUS the appended + // assistant reply, tool result, and the steered user message. Assert the + // leading turn-1 messages are byte-identical in turn 2 (no re-assembly or + // re-serialization that would bust the prompt cache). + if len(turn2) <= len(turn1) { + t.Fatalf("turn 2 request (%d msgs) must grow past turn 1 (%d msgs) by the appended turn-1 reply + steer", len(turn2), len(turn1)) + } + for i := range turn1 { + if !reflect.DeepEqual(turn2[i], turn1[i]) { + t.Fatalf("prefix message %d diverged between turns (prompt-cache bust):\n turn1: %+v\n turn2: %+v", i, turn1[i], turn2[i]) + } + } + // And the steer must be the LAST user message appended (the growth), replayed. + if !userTextIn(turn2[len(turn1):], steerText) { + t.Fatalf("steer %q not in the appended (post-prefix) region of turn 2: %+v", steerText, turn2[len(turn1):]) + } +} + +// TestSteer_EmptyInboxNoOp (AC1.5): a run with an empty inbox drives exactly as +// before — no behavioural change when steer is unused. Mutation guard: with +// steer enabled but never enqueued, the run is byte-identical to a steer-less +// run (same turns, same recorded history, same terminal). +func TestSteer_EmptyInboxNoOp(t *testing.T) { + turns := func() []mockllm.Turn { + return []mockllm.Turn{ + mockllm.ToolCallTurn(toolCall("c1", "Probe", `{}`)), + mockllm.TextTurn("the answer"), + } + } + // Steer ENABLED but nothing ever enqueued. + rec := &requestRecorder{} + eOn := newEngine(agent.Deps{ + LLM: mockllm.NewWith([]mockllm.Option{mockllm.WithRequestObserver(rec.observe)}, turns()...), + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + sessOn := newSession(t, session.Limits{}) + evsOn := drainObserving(t, eOn.Run(context.Background(), sessOn, agent.EnvForWS(memfs.NewWorkspace("/ws"), nil), agent.RunRequest{Text: "go"}), nil) + + // Steer DISABLED (the pre-steer posture). + eOff := newEngine(agent.Deps{ + LLM: mockllm.New(turns()...), + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: false, + }) + sessOff := newSession(t, session.Limits{}) + evsOff := drainObserving(t, eOff.Run(context.Background(), sessOff, agent.EnvForWS(memfs.NewWorkspace("/ws"), nil), agent.RunRequest{Text: "go"}), nil) + + // Same terminal, same number of model calls, identical recorded history. + if lastResult(t, evsOn).Stop != lastResult(t, evsOff).Stop { + t.Fatalf("empty-inbox run terminal %q differs from no-steer %q", lastResult(t, evsOn).Stop, lastResult(t, evsOff).Stop) + } + if lastResult(t, evsOn).Text != "the answer" { + t.Fatalf("empty-inbox run answer = %q, want %q", lastResult(t, evsOn).Text, "the answer") + } + if len(sessOn.Conversation.Messages) != len(sessOff.Conversation.Messages) { + t.Fatalf("empty-inbox history length %d differs from no-steer %d (the drain must be a no-op)", len(sessOn.Conversation.Messages), len(sessOff.Conversation.Messages)) + } + for i := range sessOn.Conversation.Messages { + if !reflect.DeepEqual(sessOn.Conversation.Messages[i], sessOff.Conversation.Messages[i]) { + t.Fatalf("empty-inbox history message %d differs from no-steer run — the drain must be a strict no-op", i) + } + } + // The run must have taken exactly 2 turns (no phantom extra turn from the drain). + if got := rec.count(); got != 2 { + t.Fatalf("empty-inbox model calls = %d, want 2 (no extra turn)", got) + } +} + +// TestSteer_SurvivesCompactionBoundary (AC1.6): a steer drained at a boundary +// where compaction ALSO fires is still replayed verbatim to the model on the +// following turn — the compaction recent-user back-snap keeps the just-drained +// user message out of the summarized head. +func TestSteer_SurvivesCompactionBoundary(t *testing.T) { + const steerText = "steer: survive compaction verbatim" + rec := &requestRecorder{} + // Turn 1: a tool call (so history has a settled pair). Between turn 1 and + // turn 2 the steer drains AND compaction fires. Turn 2 must replay the steer. + llm := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(rec.observe)}, + mockllm.ToolCallTurn(toolCall("c1", "Probe", `{}`)), + mockllm.TextTurn("done after compaction"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, &probeTool{}), + // A TINY context window forces compaction to evaluate (and fire) at the + // turn-2 boundary, right after the steer drains. Ratio 1.0 → threshold = + // window; the heuristic counter grows past any tiny positive window once + // a couple of messages exist. + ContextWindow: func() int { return 1 }, + CompactionRatio: 1.0, + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + steerEnqueue(t, r, steerText) + evs := drainObserving(t, r, nil) + if res := lastResult(t, evs); res.Stop != session.StopEndTurn { + t.Fatalf("terminal stop = %q (err %q)", res.Stop, res.Error) + } + // Compaction must actually have fired at the boundary (else the test is + // vacuous — it would pass without exercising the snap). + if !containsType(evs, session.EvCompaction) { + t.Fatalf("compaction did not fire at the steer boundary; types=%v", typesOf(evs)) + } + if got := llm.Calls(); got != 2 { + t.Fatalf("model calls = %d, want 2", got) + } + // The just-drained steer is a recent USER turn: the back-snap keeps it + // verbatim in the tail, so turn 2 replays it VERBATIM (not summarized away). + if !userTextIn(rec.get(1).Messages, steerText) { + t.Fatalf("steer %q was summarized away by compaction instead of surviving verbatim in the replayed tail: %+v", steerText, rec.get(1).Messages) + } +} + +// TestSteer_BrakeTerminalKeepsRecorded (AC1.7): a pending steer that trips the +// turn-limit / token-budget brake at the next boundary is STILL recorded +// (durable history) and the run terminates StopMaxTurns/StopBudget normally; the +// recorded steer is addressed by the NEXT run — never silently dropped, never +// bypassing BeginTurn's bound. +func TestSteer_BrakeTerminalKeepsRecorded(t *testing.T) { + const steerText = "steer: recorded even when the brake trips" + // Turn-limit brake: MaxTurns=1. Turn 1 makes a tool call (a run that would + // continue). The steer drains at the boundary BEFORE BeginTurn for turn 2 — + // but preTurnTerminal trips the recorded MaxTurns limit first, terminating the + // run. The steer must STILL be on durable history. + llm := mockllm.New( + mockllm.ToolCallTurn(toolCall("c1", "Probe", `{}`)), + // No further scripted turn needed: the limit trips before turn 2. + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + sess := newSession(t, session.Limits{MaxTurns: 1}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + steerEnqueue(t, r, steerText) + evs := drainObserving(t, r, nil) + + res := lastResult(t, evs) + if res.Stop != session.StopMaxTurns { + t.Fatalf("terminal stop = %q, want %q (the turn limit must trip at the boundary; err %q)", res.Stop, session.StopMaxTurns, res.Error) + } + // The steer is durable history even though the run braked. + if !userTextIn(sess.Conversation.Messages, steerText) { + t.Fatalf("brake-terminated run dropped the steer %q from durable history: %+v", steerText, sess.Conversation.Messages) + } + // Pairing holds (no dangling tool_use from turn 1's settled pair + steer). + if err := session.ValidateToolPairing(sess.Conversation.Messages); err != nil { + t.Fatalf("ValidateToolPairing after brake: %v", err) + } + // Exactly ONE model call: the brake tripped before BeginTurn for turn 2 — the + // steer never bypassed the turn bound to reach the model this run. + if got := llm.Calls(); got != 1 { + t.Fatalf("model calls = %d, want 1 (the turn-limit brake tripped before turn 2)", got) + } + + // The recorded steer is addressed by the NEXT run: reopen the (completed) + // session and drive a follow-up; the new run replays the carried steer. + rec2 := &requestRecorder{} + llm2 := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(rec2.observe)}, + mockllm.TextTurn("addressed the steer"), + ) + e2 := newEngine(agent.Deps{ + LLM: llm2, + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + if err := sess.Reopen(); err != nil { + t.Fatalf("Reopen after StopMaxTurns: %v", err) + } + evs2 := drainObserving(t, e2.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "continue"}), nil) + if res2 := lastResult(t, evs2); res2.Stop != session.StopEndTurn { + t.Fatalf("follow-up run stop = %q (err %q)", res2.Stop, res2.Error) + } + if rec2.count() < 1 || !userTextIn(rec2.get(0).Messages, steerText) { + t.Fatalf("next run must replay the carried steer %q to the model: %+v", steerText, rec2.get(0).Messages) + } +} + +// TestSteer_PendingSteerLostOnRestart (AC1.8): a pending (un-drained) steer is +// best-effort, in-memory, and LOST with the run on a process crash / session +// Abandon — the explicit honest contract; it is NOT persisted across restart. +// +// Round-2 note: a clean Cancel is no longer lossy — the cancel terminate path +// close-DRAINS a parked steer into durable history before closing the inbox +// (R2-6, never closed-unconsumed). A true crash-orphan (the run goroutine dies +// without running any terminate path) still loses the pending steer, which is +// the contract this AC pins: the inbox never persists independent of the loop. +// The crash is modelled by freezing the durable state while the run is +// genuinely mid-flight with the steer pending (the crash-orphaned "running" +// snapshot a dead process leaves in the store); the parked gate is released +// only at the end for goleak cleanup, after the frozen snapshot is taken. +func TestSteer_PendingSteerLostOnRestart(t *testing.T) { + const steerText = "steer: never persisted" + // A gate tool parks mid-turn so the steer stays PENDING (enqueued but never + // drained) while the durable state is frozen — the crash-orphaned shape. + gate := newBgGateTool() + store := memstore.New() + llm := mockllm.New( + mockllm.ToolCallTurn(toolCall("c1", "Gate", `{}`)), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, gate), + Store: store, + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + // Wait until the run is genuinely parked inside the gate tool (mid-turn), so + // the steer enqueued now stays PENDING (the boundary drain already passed + // for this turn and the run is blocked inside dispatch). + <-gate.started + steerEnqueue(t, r, steerText) + // CRASH MODEL: marshal the "running" snapshot while the run is genuinely + // mid-flight and the steer is pending — this is exactly the crash-orphaned + // durable state a dead process leaves behind (the store holds a running + // snapshot; the pending steer lives only on the crashed run's inbox). + snap, err := sessnap.Marshal(sess) + if err != nil { + t.Fatalf("snapshot marshal: %v", err) + } + + // The steer was never drained (the run is parked): it must NOT be in the + // live session history. + if userTextIn(sess.Conversation.Messages, steerText) { + t.Fatalf("pending steer %q was drained/recorded before the crash — it must stay pending-only", steerText) + } + + // Rehydrate from the durable snapshot (restart) and confirm the pending + // steer is GONE — the honest best-effort contract. + rehydrated, err := sessnap.Unmarshal(snap) + if err != nil { + t.Fatalf("snapshot unmarshal: %v", err) + } + if userTextIn(rehydrated.Conversation.Messages, steerText) { + t.Fatalf("pending steer %q survived restart — it is in-memory best-effort and must be lost", steerText) + } + // And nothing in the persisted snapshot carries it either. + if strings.Contains(string(snap), steerText) { + t.Fatalf("pending steer leaked into the persisted snapshot") + } + // The rehydrated crash-orphaned running session is recoverable (Idle after + // Abandon repair) and the repaired history does NOT see the lost steer. + if rehydrated.State != session.StateRunning { + t.Fatalf("rehydrated state = %q, want running (the crash-orphaned shape)", rehydrated.State) + } + if err := rehydrated.Abandon(); err != nil { + t.Fatalf("Abandon crash-orphaned running session: %v", err) + } + if userTextIn(rehydrated.Conversation.Messages, steerText) { + t.Fatalf("after Abandon repair the steer must still be absent (it was never durable)") + } + + // Cleanup: release the parked gate and let the orphaned run unwind (the + // snapshot above is already frozen, so the late drain cannot rewrite it). + gate.releaseOnce() + _ = drainObserving(t, r, nil) +} + +// --------------------------------------------------------------------------- +// Task 02 — the single-slot inbox contract (round-2 rework: supersede DROPPED +// in favour of slot_full reject) + the cancel contract + the drain echo. +// --------------------------------------------------------------------------- + +// steerEnqueueOutcome enqueues a steer and returns the engine's authoritative +// SteerOutcome (the enum, not a bool). It fails the test on the legacy +// error-return shape so a task-01 inbox that never grew the outcome API cannot +// silently pass. +func steerEnqueueOutcome(t *testing.T, r *agent.Run, text string) agent.SteerOutcome { + t.Helper() + outcome, err := agent.EnqueueSteerForTest(r, text) + if err != nil { + t.Fatalf("enqueue steer %q returned an error %v — task 02 returns a SteerOutcome, not an error", text, err) + } + return outcome +} + +// steerCancelOutcome cancels the pending steer and returns the outcome. +func steerCancelOutcome(t *testing.T, r *agent.Run) agent.SteerOutcome { + t.Helper() + outcome, err := agent.CancelSteerForTest(r) + if err != nil { + t.Fatalf("cancel steer returned an error %v — task 02 returns a SteerOutcome, not an error", err) + } + return outcome +} + +// steerEchoes collects the committed-text echoes off the drained event stream. +func steerEchoes(evs []session.Event) []string { + var out []string + for _, ev := range evs { + if ev.Type == session.EvSteer && ev.Steer != nil { + out = append(out, ev.Steer.Text) + } + } + return out +} + +// TestSteer_SlotFullRejects (R2-1): at most one steer is pending per run; a +// second enqueue on the FULL slot is REJECTED with slot_full ("cancel first"), +// leaving the pending steer INTACT — there is no supersede path. Only the +// parked text is ever drained / recorded / echoed / replayed. +func TestSteer_SlotFullRejects(t *testing.T) { + const parked = "steer: the parked instruction" + const rejected = "steer: rejected — cancel first" + gate := newFirstTurnGate() + rec := gate.rec + llm := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(gate.observe)}, + mockllm.ToolCallTurn(toolCall("c1", "Probe", `{}`)), + mockllm.TextTurn("done"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + // Park turn 1 so both enqueues land in the SAME pending window (the drain for + // the next boundary has not run yet). + gate.awaitEntered() + if got := steerEnqueueOutcome(t, r, parked); got != agent.SteerAccepted { + t.Fatalf("first enqueue outcome = %q, want %q", got, agent.SteerAccepted) + } + // APPEND (round-3): a second enqueue on the occupied slot APPENDS (the old + // slot_full reject is gone) — the bundle's text grows by "\n\n"+rejected. + if got := steerEnqueueOutcome(t, r, rejected); got != agent.SteerAppended { + t.Fatalf("second enqueue outcome = %q, want %q (a full slot appends)", got, agent.SteerAppended) + } + // A third enqueue appends too — the bundle keeps growing in the single slot. + if got := steerEnqueueOutcome(t, r, "steer: third"); got != agent.SteerAppended { + t.Fatalf("third enqueue outcome = %q, want %q (append keeps merging)", got, agent.SteerAppended) + } + gate.release() + evs := drainObserving(t, r, nil) + + if res := lastResult(t, evs); res.Stop != session.StopEndTurn { + t.Fatalf("terminal stop = %q (err %q)", res.Stop, res.Error) + } + // The merged bundle (parked + rejected + third) drains as ONE continuation. + merged := parked + "\n\n" + rejected + "\n\nsteer: third" + if !userTextIn(sess.Conversation.Messages, merged) { + t.Fatalf("merged steer %q not recorded: %+v", merged, sess.Conversation.Messages) + } + // Exactly ONE echo, carrying the merged text. + echoes := steerEchoes(evs) + if len(echoes) != 1 || echoes[0] != merged { + t.Fatalf("EvSteer echoes = %v, want exactly one %q (the committed merged text)", echoes, merged) + } + // The model sees the merged text on the turn after the boundary. + if !userTextIn(rec.get(1).Messages, merged) { + t.Fatalf("merged steer %q not replayed to the model: %+v", merged, rec.get(1).Messages) + } +} + +// TestSteer_NoSupersedePath (R2-2): the supersede contract is GONE — a second +// enqueue NEVER replaces the parked steer. Explicit replace = cancel-then- +// resend: the cancel retracts the parked steer (retracted), and only then does +// a fresh enqueue win the (now empty) slot (accepted). The interim second +// enqueue reports slot_full, not superseded. +func TestSteer_NoSupersedePath(t *testing.T) { + const first = "steer: v1 (retracted by the explicit replace)" + const second = "steer: v2 (the resent instruction)" + gate := newFirstTurnGate() + rec := gate.rec + llm := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(gate.observe)}, + mockllm.ToolCallTurn(toolCall("c1", "Probe", `{}`)), + mockllm.TextTurn("done"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + gate.awaitEntered() + if got := steerEnqueueOutcome(t, r, first); got != agent.SteerAccepted { + t.Fatalf("first enqueue outcome = %q, want %q", got, agent.SteerAccepted) + } + // APPEND (round-3): a second enqueue on the occupied slot APPENDS to v1 — it + // does NOT replace (the old supersede/slot_full are gone). Append ≠ replace: + // an explicit replace is still cancel-then-resend. + if got := steerEnqueueOutcome(t, r, second); got != agent.SteerAppended { + t.Fatalf("second enqueue on an occupied slot outcome = %q, want %q (append, not replace)", got, agent.SteerAppended) + } + // The explicit REPLACE path (distinct from append): cancel retracts the whole + // merged bundle (v1+v2), then a fresh enqueue wins the (now empty) slot. + if got := steerCancelOutcome(t, r); got != agent.SteerRetracted { + t.Fatalf("cancel outcome = %q, want %q (replace = cancel-then-resend)", got, agent.SteerRetracted) + } + const replacement = "steer: v3 (the replacement after cancel)" + if got := steerEnqueueOutcome(t, r, replacement); got != agent.SteerAccepted { + t.Fatalf("post-cancel resend outcome = %q, want %q (the slot is empty after the retract)", got, agent.SteerAccepted) + } + gate.release() + evs := drainObserving(t, r, nil) + + if res := lastResult(t, evs); res.Stop != session.StopEndTurn { + t.Fatalf("terminal stop = %q (err %q)", res.Stop, res.Error) + } + // The retracted merged bundle never drains; only the replacement commits. + if userTextIn(sess.Conversation.Messages, first) { + t.Fatalf("retracted steer %q was recorded — a retracted steer must drain nothing", first) + } + if !userTextIn(sess.Conversation.Messages, replacement) { + t.Fatalf("replacement steer %q not recorded: %+v", replacement, sess.Conversation.Messages) + } + echoes := steerEchoes(evs) + if len(echoes) != 1 || echoes[0] != replacement { + t.Fatalf("EvSteer echoes = %v, want exactly one %q", echoes, replacement) + } + if userTextIn(rec.get(1).Messages, first) { + t.Fatalf("retracted steer %q reached the model", first) + } + if !userTextIn(rec.get(1).Messages, replacement) { + t.Fatalf("replacement steer %q not replayed to the model: %+v", replacement, rec.get(1).Messages) + } +} + +// TestSteer_CancelRetractsPending (AC2.2): steer_cancel on a pending steer +// retracts it (outcome retracted); the run drains nothing and the next turn +// sees no injected message. A cancel with nothing pending reports none_pending. +func TestSteer_CancelRetractsPending(t *testing.T) { + const steerText = "steer: retracted before the boundary" + gate := newFirstTurnGate() + rec := gate.rec + llm := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(gate.observe)}, + mockllm.ToolCallTurn(toolCall("c1", "Probe", `{}`)), + mockllm.TextTurn("done"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + gate.awaitEntered() + if got := steerEnqueueOutcome(t, r, steerText); got != agent.SteerAccepted { + t.Fatalf("enqueue outcome = %q, want %q", got, agent.SteerAccepted) + } + // Retract the pending steer before the boundary drain. + if got := steerCancelOutcome(t, r); got != agent.SteerRetracted { + t.Fatalf("cancel outcome = %q, want %q (a pending steer is retracted)", got, agent.SteerRetracted) + } + // A second cancel has nothing to retract. + if got := steerCancelOutcome(t, r); got != agent.SteerNonePending { + t.Fatalf("second cancel outcome = %q, want %q (nothing pending)", got, agent.SteerNonePending) + } + gate.release() + evs := drainObserving(t, r, nil) + + if res := lastResult(t, evs); res.Stop != session.StopEndTurn { + t.Fatalf("terminal stop = %q (err %q)", res.Stop, res.Error) + } + // The retracted steer is never recorded, never echoed, never replayed. + if userTextIn(sess.Conversation.Messages, steerText) { + t.Fatalf("retracted steer %q was recorded — a retracted steer must drain nothing", steerText) + } + if echoes := steerEchoes(evs); len(echoes) != 0 { + t.Fatalf("EvSteer echoes = %v, want none (the steer was retracted before the drain)", echoes) + } + if userTextIn(rec.get(1).Messages, steerText) { + t.Fatalf("retracted steer %q reached the model on the next turn", steerText) + } +} + +// TestSteer_TooLateNotDrained (AC2.3): a steer that arrives after the run went +// terminal reports too_late and is never silently drained into a finished turn +// (nothing recorded, no echo). +func TestSteer_TooLateNotDrained(t *testing.T) { + const steerText = "steer: arrived after the run ended" + llm := mockllm.New( + mockllm.TextTurn("done"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + evs := drainObserving(t, r, nil) + if res := lastResult(t, evs); res.Stop != session.StopEndTurn { + t.Fatalf("terminal stop = %q (err %q)", res.Stop, res.Error) + } + // The run is terminal now — a steer must report too_late, never be parked. + if got := steerEnqueueOutcome(t, r, steerText); got != agent.SteerTooLate { + t.Fatalf("post-terminal enqueue outcome = %q, want %q", got, agent.SteerTooLate) + } + // A cancel after terminal also has nothing live to retract. + if got := steerCancelOutcome(t, r); got != agent.SteerNonePending { + t.Fatalf("post-terminal cancel outcome = %q, want %q", got, agent.SteerNonePending) + } + // Nothing was drained into the finished turn. + if userTextIn(sess.Conversation.Messages, steerText) { + t.Fatalf("too-late steer %q was recorded into a finished run", steerText) + } + if echoes := steerEchoes(evs); len(echoes) != 0 { + t.Fatalf("EvSteer echoes = %v, want none (the steer arrived too late)", echoes) + } +} + +// TestSteer_DrainEmitsCommittedEcho (AC2.4): the drain emits EvSteer carrying +// the committed text — the version actually parked and drained — and the drain +// lands at the boundary BEFORE the turn it feeds (Seq ordering: echo < that +// turn's turn.start). +func TestSteer_DrainEmitsCommittedEcho(t *testing.T) { + const committed = "steer: the committed version" + gate := newFirstTurnGate() + llm := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(gate.observe)}, + mockllm.ToolCallTurn(toolCall("c1", "Probe", `{}`)), + mockllm.TextTurn("done"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + gate.awaitEntered() + steerEnqueueOutcome(t, r, committed) + gate.release() + evs := drainObserving(t, r, nil) + + if res := lastResult(t, evs); res.Stop != session.StopEndTurn { + t.Fatalf("terminal stop = %q (err %q)", res.Stop, res.Error) + } + // Exactly one echo, carrying the committed text — the version actually + // drained. + var echo *session.Event + for i := range evs { + if evs[i].Type == session.EvSteer { + if echo != nil { + t.Fatalf("more than one EvSteer emitted (seqs %d and %d) — the drain commits once", echo.Seq, evs[i].Seq) + } + echo = &evs[i] + } + } + if echo == nil || echo.Steer == nil { + t.Fatalf("no EvSteer echo emitted on drain; types=%v", typesOf(evs)) + } + if echo.Steer.Text != committed { + t.Fatalf("EvSteer echo text = %q, want the committed %q (the version actually drained)", echo.Steer.Text, committed) + } + // The echo is sequenced BEFORE the turn it feeds (turn index 1's turn.start), + // so a client renders the echo ahead of the model turn that consumed it. + var turn1StartSeq int64 = -1 + for _, ev := range evs { + if ev.Type == session.EvTurnStart && ev.Turn == 1 { + turn1StartSeq = ev.Seq + break + } + } + if turn1StartSeq == -1 { + t.Fatalf("no turn.start for turn 1 (the steer-fed turn); types=%v", typesOf(evs)) + } + if echo.Seq >= turn1StartSeq { + t.Fatalf("EvSteer echo seq %d must precede the turn.start (seq %d) of the turn it feeds", echo.Seq, turn1StartSeq) + } +} + +// TestSteer_InboxConcurrentSafe (AC2.5): enqueue/cancel/drain are safe under +// concurrent access — the run goroutine drains at boundaries while wire-handler +// goroutines enqueue and cancel. Run under -race. No data race, no double-drain +// (at most one EvSteer echo and at most one recorded steer), and every outcome +// is a defined enum value. +func TestSteer_InboxConcurrentSafe(t *testing.T) { + // A gate tool parks mid-turn so several turn boundaries (and therefore + // several drains) occur while the hammer goroutines race the inbox. + gate := newBgGateTool() + llm := mockllm.New( + mockllm.ToolCallTurn(toolCall("c1", "Gate", `{}`)), + mockllm.TextTurn("done"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, gate), + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + <-gate.started // the run is genuinely mid-flight, inside dispatch + + // Hammer the inbox from several goroutines while the run is live: enqueues + // (into the single slot — slot_full rejects expected) and cancels (retract) + // racing the boundary drain. + var wg sync.WaitGroup + for g := 0; g < 8; g++ { + wg.Add(1) + go func() { + defer wg.Done() + for i := 0; i < 25; i++ { + outcome, err := agent.EnqueueSteerForTest(r, "steer: racer") + if err != nil { + t.Errorf("enqueue returned error %v — task 02 returns a SteerOutcome", err) + return + } + switch outcome { + case agent.SteerAccepted, agent.SteerAppended, agent.SteerTooLate: + // defined live/terminal outcomes + default: + t.Errorf("enqueue outcome %q is not a defined SteerOutcome", outcome) + return + } + if outcome == agent.SteerTooLate { + return // the run ended; stop hammering + } + cOutcome, err := agent.CancelSteerForTest(r) + if err != nil { + t.Errorf("cancel returned error %v — task 02 returns a SteerOutcome", err) + return + } + switch cOutcome { + case agent.SteerRetracted, agent.SteerNonePending: + // defined cancel outcomes + default: + t.Errorf("cancel outcome %q is not a defined SteerOutcome", cOutcome) + return + } + } + }() + } + // Let the racers run against the live run, then release the tool so the run + // drains at the next boundary and finishes. + gate.releaseOnce() + wg.Wait() + evs := drainObserving(t, r, nil) + + // The run terminates normally. The hammer can leave at most ONE parked steer + // (the single slot) plus the at-most-one the pre-release boundary drained, + // and the terminal close-drain commits whatever is parked rather than + // closing it unconsumed (R2-6). The exact stop label is benign either way: + // a racer steer parked at the final clean end defers it into an unscripted + // model turn, whose empty scripted-turn sequence ends StopNoProgress — the + // no-progress cap is exactly the mechanism that keeps a racer-steered + // extra turn bounded. Accept either clean terminal. + if res := lastResult(t, evs); res.Stop != session.StopEndTurn && res.Stop != session.StopNoProgress { + t.Fatalf("terminal stop = %q (err %q), want a clean end_turn/no_progress", res.Stop, res.Error) + } + // No double-drain: every EvSteer echo must correspond to exactly one + // recorded user continuation with the same committed bundle text. A bundle may + // contain several racer fragments (append-default), and racers can park fresh + // bundles at later boundaries, so neither an exact "steer: racer" count nor a + // fixed number of drains is a valid concurrency invariant. + echoes := steerEchoes(evs) + recorded := make(map[string]int) + for _, m := range sess.Conversation.Messages { + if m.Role == session.RoleUser && strings.Contains(m.Text, "steer: racer") { + recorded[m.Text]++ + } + } + for _, echo := range echoes { + if recorded[echo] == 0 { + t.Fatalf("EvSteer echo %q has no matching recorded steer — every committed steer echoes exactly once", echo) + } + recorded[echo]-- + } + for text, n := range recorded { + if n != 0 { + t.Fatalf("recorded steer %q has no matching EvSteer echo (%d unmatched)", text, n) + } + } + // Pairing holds no matter how many drains committed a steer. + if err := session.ValidateToolPairing(sess.Conversation.Messages); err != nil { + t.Fatalf("ValidateToolPairing after the raced drains: %v", err) + } +} + +// TestInvariant_recorded_streamed_model_view (AC2.6): the recorded steer, the +// streamed EvSteer echo, and the message the model sees are the SAME text — the +// recorded == streamed == model-view invariant holds. +func TestInvariant_recorded_streamed_model_view(t *testing.T) { + const committed = "steer: the one committed instruction" + gate := newFirstTurnGate() + rec := gate.rec + llm := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(gate.observe)}, + mockllm.ToolCallTurn(toolCall("c1", "Probe", `{}`)), + mockllm.TextTurn("done"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + gate.awaitEntered() + steerEnqueueOutcome(t, r, committed) + gate.release() + evs := drainObserving(t, r, nil) + + if res := lastResult(t, evs); res.Stop != session.StopEndTurn { + t.Fatalf("terminal stop = %q (err %q)", res.Stop, res.Error) + } + + // RECORDED: the committed text is a user message on durable history. + var recordedText string + var recordedCount int + for _, m := range sess.Conversation.Messages { + if m.Role == session.RoleUser && strings.HasPrefix(m.Text, "steer: ") { + recordedText = m.Text + recordedCount++ + } + } + if recordedCount != 1 || recordedText != committed { + t.Fatalf("recorded steer = %q (x%d), want exactly one %q", recordedText, recordedCount, committed) + } + + // STREAMED: the EvSteer echo carries the identical text. + echoes := steerEchoes(evs) + if len(echoes) != 1 || echoes[0] != committed { + t.Fatalf("streamed EvSteer echo = %v, want exactly one %q (streamed must equal recorded)", echoes, committed) + } + + // MODEL-VIEW: the model's next-turn request replays the identical text. + var modelText string + var modelCount int + for _, m := range rec.get(1).Messages { + if m.Role == session.RoleUser && strings.HasPrefix(m.Text, "steer: ") { + modelText = m.Text + modelCount++ + } + } + if modelCount != 1 || modelText != committed { + t.Fatalf("model-view steer = %q (x%d), want exactly one %q (model-view must equal recorded)", modelText, modelCount, committed) + } + + // The three surfaces agree byte-for-byte. + if recordedText != echoes[0] || echoes[0] != modelText { + t.Fatalf("recorded/streamed/model-view diverged: recorded=%q streamed=%q model=%q", recordedText, echoes[0], modelText) + } +} + +// --------------------------------------------------------------------------- +// Round-2 rework (the Ozz-review decisions): ingress UTF-8 repair, the +// clean-exit continue-run, and the never-closed-parked guarantee. +// --------------------------------------------------------------------------- + +// TestSteer_IngressUTF8Repaired (R2-4): an operator steer carrying invalid +// UTF-8 (the BSD `cat -t`-class mangled byte sequence, issue #402's shape) is +// REPAIRED at ingress (session.ToValidUTF8 in enqueueSteer, BEFORE the text +// enters the inbox), so the recorded history, the EvSteer echo, and the +// model-visible message are all valid UTF-8 and byte-IDENTICAL — the +// recorded == streamed == model-view invariant holds for the repaired text. +func TestSteer_IngressUTF8Repaired(t *testing.T) { + // A two-byte lead + stray continuation that is NOT valid UTF-8; the repair + // substitutes U+FFFD exactly the way encoding/json does. + const raw = "steer: mangled \xe2 orphan" + repaired := session.ToValidUTF8(raw) + if utf8.ValidString(raw) { + t.Fatalf("test premise broken: the raw steer text is already valid UTF-8") + } + if !utf8.ValidString(repaired) || repaired == raw { + t.Fatalf("test premise broken: ToValidUTF8(%q) = %q must repair and differ", raw, repaired) + } + gate := newFirstTurnGate() + rec := gate.rec + llm := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(gate.observe)}, + mockllm.ToolCallTurn(toolCall("c1", "Probe", `{}`)), + mockllm.TextTurn("done"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + gate.awaitEntered() + if got := steerEnqueueOutcome(t, r, raw); got != agent.SteerAccepted { + t.Fatalf("enqueue outcome = %q, want %q", got, agent.SteerAccepted) + } + gate.release() + evs := drainObserving(t, r, nil) + + if res := lastResult(t, evs); res.Stop != session.StopEndTurn { + t.Fatalf("terminal stop = %q (err %q)", res.Stop, res.Error) + } + // RECORDED: the history carries the REPAIRED text, never the raw bytes. + if userTextIn(sess.Conversation.Messages, raw) { + t.Fatalf("the raw invalid-UTF-8 steer reached durable history — ingress repair must run BEFORE the inbox") + } + var recordedText string + for _, m := range sess.Conversation.Messages { + if m.Role == session.RoleUser && strings.HasPrefix(m.Text, "steer: ") { + recordedText = m.Text + } + } + if recordedText != repaired { + t.Fatalf("recorded steer = %q, want the repaired %q", recordedText, repaired) + } + if !utf8.ValidString(recordedText) { + t.Fatalf("recorded steer %q is not valid UTF-8", recordedText) + } + // STREAMED: the EvSteer echo carries the identical repaired text. + echoes := steerEchoes(evs) + if len(echoes) != 1 || echoes[0] != repaired { + t.Fatalf("EvSteer echoes = %v, want exactly one %q (echo must equal recorded)", echoes, repaired) + } + if !utf8.ValidString(echoes[0]) { + t.Fatalf("EvSteer echo %q is not valid UTF-8", echoes[0]) + } + // MODEL-VIEW: the model's next-turn request replays the identical text. + var modelText string + for _, m := range rec.get(1).Messages { + if m.Role == session.RoleUser && strings.HasPrefix(m.Text, "steer: ") { + modelText = m.Text + } + } + if modelText != repaired { + t.Fatalf("model-view steer = %q, want the repaired %q (model-view must equal recorded)", modelText, repaired) + } + if !utf8.ValidString(modelText) { + t.Fatalf("model-view steer %q is not valid UTF-8", modelText) + } +} + +// TestSteer_CleanExitContinuesRun (R2-5): a steer enqueued while a +// meaningful-text, no-tool-call answer is streaming is NOT dropped — the clean +// exit (terminateComplete on the benign stop) DEFERS while the steer is parked, +// the steer drains at the NEXT Step 2a boundary, and the model acts on it on +// the following turn. The run extends until the steer is consumed; the +// never-drop contract holds engine-internally. +func TestSteer_CleanExitContinuesRun(t *testing.T) { + const steerText = "steer: act on this after the answer" + gate := newFirstTurnGate() + rec := gate.rec + llm := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(gate.observe)}, + // Turn 1: a MEANINGFUL-TEXT, no-tool-call answer — the shape that used to + // terminateComplete and silently drop the parked steer. + mockllm.TextTurn("the first answer"), + // Turn 2: the post-steer turn — the model acts on the drained steer. + mockllm.TextTurn("acted on the steer"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + // Enqueue while the first (text-bearing, no-tool) answer streams: turn 1's + // drain already ran, so the steer is PARKED when finishTurnNoTools classifies + // the clean end. + gate.awaitEntered() + if got := steerEnqueueOutcome(t, r, steerText); got != agent.SteerAccepted { + t.Fatalf("enqueue outcome = %q, want %q", got, agent.SteerAccepted) + } + gate.release() + evs := drainObserving(t, r, nil) + + res := lastResult(t, evs) + if res.Stop != session.StopEndTurn { + t.Fatalf("terminal stop = %q, want end_turn (err %q)", res.Stop, res.Error) + } + // The run EXTENDED: the model was called TWICE — the parked steer deferred + // the clean exit, drained at the next boundary, and fed a second turn. + if got := llm.Calls(); got != 2 { + t.Fatalf("model calls = %d, want 2 (the parked steer must defer the clean exit and drive a post-steer turn)", got) + } + // The steer is recorded as durable history and echoed once. + if !userTextIn(sess.Conversation.Messages, steerText) { + t.Fatalf("steer %q not recorded: %+v", steerText, sess.Conversation.Messages) + } + if echoes := steerEchoes(evs); len(echoes) != 1 || echoes[0] != steerText { + t.Fatalf("EvSteer echoes = %v, want exactly one %q (the deferred clean exit drains the steer)", echoes, steerText) + } + // The model ACTS on it: turn 2's request replays the steer, and the final + // answer is the post-steer turn's text. + if !userTextIn(rec.get(1).Messages, steerText) { + t.Fatalf("steer %q not replayed to the model on the post-steer turn: %+v", steerText, rec.get(1).Messages) + } + if res.Text != "acted on the steer" { + t.Fatalf("final answer = %q, want %q (the model acted on the drained steer)", res.Text, "acted on the steer") + } + // The steer lands at the boundary AFTER turn 1's recorded assistant answer, + // never inside a pair. + if err := session.ValidateToolPairing(sess.Conversation.Messages); err != nil { + t.Fatalf("ValidateToolPairing after the deferred clean exit: %v", err) + } +} + +// TestSteer_NeverClosedParked (R2-6): a PARKED steer is never closed-unconsumed +// on ANY terminate path — a closeSteer that finds a parked steer drains it +// (records it as durable history) before the terminal lands, so the steer +// survives to be addressed by the NEXT run. Here the steer is parked while the +// run is mid-dispatch and the operator CANCELS: the cancelled-run terminal must +// still capture the steer (the strictest never-drop reading — cancel drops a +// parked steer only if closeSteer does). +func TestSteer_NeverClosedParked(t *testing.T) { + const steerText = "steer: parked across the cancel" + gate := newBgGateTool() + llm := mockllm.New( + mockllm.ToolCallTurn(toolCall("c1", "Gate", `{}`)), + mockllm.TextTurn("addressed the parked steer"), + ) + e := newEngine(agent.Deps{ + LLM: llm, + Catalog: catalogWith(t, gate), + EnableSteer: true, + }) + sess := newSession(t, session.Limits{}) + ws := memfs.NewWorkspace("/ws") + + r := e.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "go"}) + <-gate.started // the run is genuinely parked mid-dispatch + + // Park the steer, then CANCEL the run without draining events: the cancel + // terminate path closes the inbox — it must drain the parked steer into + // durable history first, never close it unconsumed. + if got := steerEnqueueOutcome(t, r, steerText); got != agent.SteerAccepted { + t.Fatalf("enqueue outcome = %q, want %q", got, agent.SteerAccepted) + } + r.Cancel() + gate.releaseOnce() + evs := drainObserving(t, r, nil) + if res := lastResult(t, evs); res.Stop != session.StopCancelled { + t.Fatalf("terminal stop = %q, want cancelled (err %q)", res.Stop, res.Error) + } + + // The parked steer was NOT closed-unconsumed: it is durable history even + // though the run was cancelled mid-dispatch. + if !userTextIn(sess.Conversation.Messages, steerText) { + t.Fatalf("the cancel terminate path closed a PARKED steer unconsumed — %q is not on durable history: %+v", steerText, sess.Conversation.Messages) + } + // The interrupted turn's orphaned Gate call is closed out by the FOLLOW-UP + // run's Interrupt repair (the synthetic result lands AFTER the parked steer + // — the close-drain records first, the repair appends after); pairing holds + // on the healed history. + if err := sess.Interrupt(); err != nil { + t.Fatalf("Interrupt cancelled session: %v", err) + } + if err := session.ValidateToolPairing(sess.Conversation.Messages); err != nil { + t.Fatalf("ValidateToolPairing after Interrupt's close-out repair: %v", err) + } + + // And the captured steer is addressed by the NEXT run: drive a follow-up + // that replays it. + rec := &requestRecorder{} + llm2 := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(rec.observe)}, + mockllm.TextTurn("follow-up done"), + ) + e2 := newEngine(agent.Deps{ + LLM: llm2, + Catalog: catalogWith(t, &probeTool{}), + EnableSteer: true, + }) + evs2 := drainObserving(t, e2.Run(context.Background(), sess, agent.EnvForWS(ws, nil), agent.RunRequest{Text: "continue"}), nil) + if res2 := lastResult(t, evs2); res2.Stop != session.StopEndTurn { + t.Fatalf("follow-up run stop = %q (err %q)", res2.Stop, res2.Error) + } + if rec.count() < 1 || !userTextIn(rec.get(0).Messages, steerText) { + t.Fatalf("the next run must replay the cancel-captured steer %q to the model: %+v", steerText, rec.get(0).Messages) + } +} diff --git a/engine/api/agent.txt b/engine/api/agent.txt index 03c8f364f1..915d24e674 100644 --- a/engine/api/agent.txt +++ b/engine/api/agent.txt @@ -14,6 +14,7 @@ method func (*Engine).Model() string method func (*Engine).ResumeApproval(ctx context.Context, sess *session.Session, env tool.Environment, askID string, verdict session.ApprovalVerdict) *Run method func (*Engine).Run(ctx context.Context, sess *session.Session, env tool.Environment, req RunRequest) *Run + method func (*Engine).SteerEnabled() bool method func (*EvidenceReflector).Reflect(ctx context.Context, in learning.Input) (learning.Outcome, error) method func (*EvidenceReflector).RequestTokenEstimate(in learning.Input) (int, error) method func (*InspectMemberTool).Execute(ctx context.Context, call session.ToolCall, _ tool.Environment) (session.ToolResult, error) @@ -32,6 +33,8 @@ method func (*Run).Approve(askID string, v session.ApprovalVerdict) method func (*Run).Cancel() method func (*Run).CancelChild(childID string) bool + method func (*Run).CancelSteer() (SteerOutcome, error) + method func (*Run).EnqueueSteer(text string) (SteerOutcome, error) method func (*Run).Events() <-chan session.Event method func (*ScheduleQueryTool).Execute(ctx context.Context, call session.ToolCall, _ tool.Environment) (session.ToolResult, error) method func (*ScheduleQueryTool).ReadOnly() bool @@ -84,6 +87,11 @@ const RouterMissDegenerateInput untyped string = "degenerate-input" const RouterMissUnknownCategory untyped string = "unknown-category" const ScheduleQueryToolName untyped string = "ScheduleQuery" const ScheduleToolName untyped string = "Schedule" +const SteerAccepted SteerOutcome = "accepted" +const SteerAppended SteerOutcome = "appended" +const SteerNonePending SteerOutcome = "none_pending" +const SteerRetracted SteerOutcome = "retracted" +const SteerTooLate SteerOutcome = "too_late" const StopReasonBudget MemberStopReason = "budget" const StopReasonCancelled MemberStopReason = "cancelled" const StopReasonError MemberStopReason = "error" @@ -190,7 +198,7 @@ type ChildAskReview struct{Allowed bool; Reason string} type ChildAskReviewRequest struct{Ask session.PendingAsk; Isolated bool} type ChildAskReviewer interface{Review(ctx context.Context, req ChildAskReviewRequest) (ChildAskReview, error)} type Compactor interface{Compact(ctx context.Context, conv *session.Conversation) (compacted []session.Message, summary string, err error)} -type Deps struct{LLM port.LLMProvider; Catalog *tool.Catalog; Policy port.PermissionPolicy; Hooks port.HookRunner; Store port.SessionStore; SessionLiveness port.SessionLiveness; Clock port.Clock; ToolCallRecorder port.ToolCallRecorder; Sink port.EventSink; Diagnostics port.Diagnostics; LearningMode learning.Mode; LearningObserver learning.Observer; Compactor Compactor; TokenCounter TokenCounter; Instructions prompt.InstructionAssembler; CommandExpander prompt.CommandExpander; PromptConfig prompt.Config; PromptBuilder prompt.Builder; OperatorProfileSource prompt.OperatorProfileSource; Model string; ContextWindow func() int; CompactionRatio float64; Role string; MaxNoProgressNudges int; Interactive bool; PlanModeAutoApprove bool; ChildAskReviewer ChildAskReviewer; ChildAskReviewMaxDenies int; MaxRunTokens int; SubagentModelRouter func(ctx context.Context, taskPrompt string) (category string, model string, usage session.Usage, missReason string, ok bool); ProgressiveTools bool; DeliveryQueue port.DeliveryQueue} +type Deps struct{LLM port.LLMProvider; Catalog *tool.Catalog; Policy port.PermissionPolicy; Hooks port.HookRunner; Store port.SessionStore; SessionLiveness port.SessionLiveness; Clock port.Clock; ToolCallRecorder port.ToolCallRecorder; Sink port.EventSink; Diagnostics port.Diagnostics; LearningMode learning.Mode; LearningObserver learning.Observer; Compactor Compactor; TokenCounter TokenCounter; Instructions prompt.InstructionAssembler; CommandExpander prompt.CommandExpander; PromptConfig prompt.Config; PromptBuilder prompt.Builder; OperatorProfileSource prompt.OperatorProfileSource; Model string; ContextWindow func() int; CompactionRatio float64; Role string; MaxNoProgressNudges int; Interactive bool; PlanModeAutoApprove bool; ChildAskReviewer ChildAskReviewer; ChildAskReviewMaxDenies int; MaxRunTokens int; SubagentModelRouter func(ctx context.Context, taskPrompt string) (category string, model string, usage session.Usage, missReason string, ok bool); ProgressiveTools bool; DeliveryQueue port.DeliveryQueue; EnableSteer bool} type Engine struct{} type EngineAskReviewerOption func(*engineAskReviewer) type EngineJudgeOption func(*engineJudge) @@ -216,6 +224,7 @@ type Run struct{} type RunRequest struct{Text string; Parts []session.Content; MaxRunTokensOverride int; ExtraTools []tool.Tool; AskIDDiscriminator string} type ScheduleQueryTool struct{} type ScheduleTool struct{} +type SteerOutcome string type SubagentOption func(*SubagentTool) type SubagentStatusTool struct{} type SubagentTool struct{} diff --git a/engine/api/session.txt b/engine/api/session.txt index dbae0f13e2..28388065c5 100644 --- a/engine/api/session.txt +++ b/engine/api/session.txt @@ -75,6 +75,7 @@ const EvScheduleFailed EventType = "schedule.failed" const EvScheduleFired EventType = "schedule.fired" const EvScheduleSkipped EventType = "schedule.skipped" const EvSessionInit EventType = "session.init" +const EvSteer EventType = "steer" const EvSubagentEnd EventType = "subagent.end" const EvSubagentStart EventType = "subagent.start" const EvSubagentTool EventType = "subagent.tool" @@ -214,7 +215,7 @@ type Conversation struct{Messages []Message} type Counters struct{Turns int; ToolCalls int; ConsecutiveFailures int} type EnvironmentKind string type EnvironmentRef struct{Kind EnvironmentKind; ID string} -type Event struct{Type EventType; Seq int64; Turn int; Text string; ToolCall *ToolCall; ToolResult *ToolResult; Ask *PendingAsk; Result *ResultPayload; TurnEnd *TurnEndPayload; Hook *HookPayload; Approval *ApprovalPayload; CompactionArchive *CompactionArchivePayload; UserPrompt *UserPromptPayload; Usage *Usage; Subagent *SubagentPayload; Team *TeamPayload; Parallel *ParallelPayload; Actor *Principal; Schedule *SchedulePayload} +type Event struct{Type EventType; Seq int64; Turn int; Text string; ToolCall *ToolCall; ToolResult *ToolResult; Ask *PendingAsk; Result *ResultPayload; TurnEnd *TurnEndPayload; Hook *HookPayload; Approval *ApprovalPayload; CompactionArchive *CompactionArchivePayload; UserPrompt *UserPromptPayload; Usage *Usage; Subagent *SubagentPayload; Team *TeamPayload; Parallel *ParallelPayload; Actor *Principal; Schedule *SchedulePayload; Steer *SteerPayload} type EventType string type GrantType string type HookDecision string @@ -235,6 +236,7 @@ type SessionID string type SessionKind string type SessionRelationship struct{ScheduleName string "json:\"schedule_name,omitempty\""; OriginSessionID SessionID "json:\"origin_session_id,omitempty\""; ParentSessionID SessionID "json:\"parent_session_id,omitempty\""; CallID ToolCallID "json:\"call_id,omitempty\""; BranchIndex *int "json:\"branch_index,omitempty\""; TeamID string "json:\"team_id,omitempty\""; MemberName string "json:\"member_name,omitempty\""} type State string +type SteerPayload struct{Text string} type StopReason string type SubagentPayload struct{ParentCallID string; ChildID string; Goal string; Background bool; RoutedCategory string; RoutedModel string; RoutingReason string; Model string; ToolName string; IsError bool; ToolCount int; Text string; Detail string; InnerKind EventType; Usage Usage; Stop StopReason; Cause string; DurationMs int64} type TeamFindingSnapshot struct{Member string; Body string} diff --git a/engine/session/event.go b/engine/session/event.go index 7e382f5d41..f4c68b751d 100644 --- a/engine/session/event.go +++ b/engine/session/event.go @@ -252,6 +252,27 @@ const ( // session's durable EventLog ONLY (pull-only via GetFire/ListFires); carries // SchedulePayload (kind="failed", stop/err populated). EvScheduleFailed EventType = "schedule.failed" + + // EvSteer is emitted when the run's steer inbox DRAIN commits a pending + // operator steer at a turn boundary (steer-while-running, issue #512). It + // carries the SteerPayload — the COMMITTED text that was just recorded into + // the conversation as an ordinary user continuation (recordContinuation). + // + // It is the AUTHORITATIVE ECHO: the engine is the sole authority on which + // appended bundle drained (the client cannot observe the exact drain moment + // across stream latency), so the client renders THIS text rather than + // guessing which of its staged steers landed. The recorded == streamed == + // model-view invariant holds: the echoed text is byte-identical to the user + // message recorded into history and replayed to the model on the next turn. + // + // It is CLIENT-VISIBLE (unlike the log-only EvUserPrompt / EvApproval / + // EvCompactionArchive): the relay forwards it on the live wire so the client + // renders the committed steer as it happens. It is NOT recorded to the + // model's conversation history (the recorded user message is the model-facing + // half; this event is the client-facing echo of the same fact). It maps to + // the proto event-type string verbatim (no proto enum; the wire `type` field + // is a string passthrough, like EvNoProgress — no task generate). + EvSteer EventType = "steer" ) // ParallelEventKind discriminates which lifecycle transition an EvParallelBranch event @@ -428,6 +449,22 @@ type CompactionArchivePayload struct { Replaced []Message } +// SteerPayload is the structured detail carried by an EvSteer Event: the +// committed operator steer the turn-boundary drain just recorded into the +// conversation. It is the client-facing ECHO of the drain — the engine is +// authoritative on which appended bundle drained, so Text is the version +// ACTUALLY drained (never a stale draft the client may still be holding). +// +// The text is the run's OWN operator input (the top-level run's steer), never +// child content — the same posture as UserPromptPayload (gauntlet #7): a child +// run's steer, if any, rides the child stream, never the parent's. +type SteerPayload struct { + // Text is the committed steer text, byte-identical to the user message + // recorded into history and replayed to the model (the recorded == streamed + // == model-view invariant). + Text string +} + // UserPromptPayload is the structured detail carried by an EvUserPrompt Event: the // user-role message that was just recorded into the conversation. It carries the // flattened Text plus any non-text media Parts, mirroring the user Message a @@ -1206,4 +1243,8 @@ type Event struct { // the loop. Client-visible (unlike the log-only EvApproval / // EvCompactionArchive / EvUserPrompt). See SchedulePayload. Schedule *SchedulePayload + // Steer is set on EvSteer: the committed operator steer the turn-boundary + // drain just recorded (the authoritative echo). Client-visible. See + // SteerPayload. + Steer *SteerPayload } diff --git a/go.work.sum b/go.work.sum index 7f04604701..f8cf458f1b 100644 --- a/go.work.sum +++ b/go.work.sum @@ -28,6 +28,7 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0/go.mod h1:/WYEx9pcM9Y+Dd/AP github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.0/go.mod h1:q0+UTSRvShwUCrR/s5HtyInYphN7Wvxb7snFM3u+SLA= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.4.0/go.mod h1:mCBhUhlMjLLJKr5aqw2TNS/VqJOie8MzWq3DAMJeKso= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0/go.mod h1:7dCRMLwisfRH3dBupKeNCioWYUZ4SS09Z14H+7i8ZoY= @@ -36,6 +37,7 @@ github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0/go.mod h1: github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.5.0/go.mod h1:i2h9fsTFKZorh8RdV2IcSUf/Qj98GlTkrTvUbX/s8as= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 h1:nCYfgcSyHZXJI8J0IWE5MsCGlb2xp9fJiXyxWgmOFg4= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0/go.mod h1:ucUjca2JtSZboY8IoUqyQyuuXvwbMBVwFOm0vdQPNhA= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/AzureAD/microsoft-authentication-library-for-go v1.7.2/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= @@ -393,6 +395,7 @@ github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6B github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/moul/http2curl v0.0.0-20170919181001-9ac6cf4d929b h1:Pip12xNtMvEFUBF4f8/b5yRXj94LLrNdLWELfOr2KcY= diff --git a/internal/adapter/permconfig/resolve.go b/internal/adapter/permconfig/resolve.go index 31eca129fa..9436fdfffd 100644 --- a/internal/adapter/permconfig/resolve.go +++ b/internal/adapter/permconfig/resolve.go @@ -163,6 +163,18 @@ type Resolver struct { // are evaluated separately because they may only tighten this ceiling. operatorLearning *LearningSection + // operatorSteer is the OPERATOR-TIER steer: scalar (steer-while-running, issue + // #512), read ONCE at construction from the user-global + CLI tiers ONLY. A + // project-tier file's steer: key is deliberately IGNORED (operator-tier only, + // for consistency with posture — loadProjectRules WARNs when it sees one). + // operatorSteerSet records whether ANY operator-tier file carried the key, so + // ABSENT is distinguishable from an explicit steer: false (the knob is an + // opt-OUT of a default-ON feature, so the composition layer needs the + // presence bit, not just the bool). CLI (explicit files) out-ranks user-global + // (first-present keeps CLI). + operatorSteer bool + operatorSteerSet bool + // operatorModels is the OPERATOR-TIER models: subtree (ADR 0030), read ONCE at // construction from the user-global + CLI tiers ONLY (the SOLE capture path is // captureModels from loadUserRules; there is no second capture path). It carries @@ -312,6 +324,21 @@ func (r *Resolver) ProjectLearningSettings(ws tool.WorkspaceReader) []*LearningS return result } +// OperatorSteer returns the OPERATOR-TIER steer: bool (user-global + CLI only) and +// whether ANY operator-tier file carried the key (steer-while-running, issue #512). +// It is the SOLE accessor the composition layer uses to read the knob from config — +// by construction it never returns a project-tier value (a project steer: is ignored +// with a WARN in loadProjectRules). The presence bit matters because the knob is an +// opt-OUT of a DEFAULT-ON feature: absent (present=false) means composition keeps the +// default; an explicit steer: false (present=true, value=false) disables it. nil-safe. +// Mirrors OperatorPosture(). +func (r *Resolver) OperatorSteer() (value, present bool) { + if r == nil { + return false, false + } + return r.operatorSteer, r.operatorSteerSet +} + // OperatorModelSlots returns the operator-tier models: subtree (user-global + CLI // only), or nil when none was configured. It is the SOLE accessor the composition // layer uses to read per-slot model config from disk — by construction it never @@ -595,6 +622,14 @@ func (r *Resolver) loadProjectRules(ws tool.WorkspaceReader) ([]governance.Rule, "plan-mode-auto-approve: IGNORING a project-tier plan-mode-auto-approve: key (operator-tier only — a project repo cannot enable autonomous plan approval; set plan-mode-auto-approve in your user-global settings.yaml or via --plan-mode-auto-approve)", "file", src.path, "root", ws.Root()) } + // Steer is OPERATOR-TIER ONLY (issue #512), for consistency with posture: a + // project file's steer: key is IGNORED with a loud WARN. The harness's + // operator surface is not a project repo's to flip in either direction. + if cfg.Steer != nil { + r.diag.Log(context.Background(), port.LevelWarn, + "steer: IGNORING a project-tier steer: key (operator-tier only — a project repo cannot change the mid-run steer surface; set steer in your user-global settings.yaml or via --no-steer)", + "file", src.path, "root", ws.Root()) + } // OpenRouter downstream-provider routing is OPERATOR-TIER ONLY (issue #480): a // project file's openrouter: block is IGNORED with a loud WARN. Steering requests // to a particular downstream inference provider is a spend/compliance/capability @@ -819,6 +854,8 @@ func (r *Resolver) loadUserRules(report *Report) []governance.Rule { r.capturePlanModeAutoApprove(cfg.PlanModeAutoApprove) // Operator-tier learning: same first-non-nil-keeps-CLI discipline. r.captureLearning(cfg.Learning) + // Operator-tier steer (issue #512): same discipline as posture. + r.captureSteer(cfg.Steer) // Operator-tier models: same first-non-nil-keeps-CLI discipline (ADR 0030). r.captureModels(cfg.Models) // Operator-tier openrouter: same first-non-nil-keeps-CLI discipline (issue #480). @@ -855,6 +892,8 @@ func (r *Resolver) loadUserRules(report *Report) []governance.Rule { r.capturePlanModeAutoApprove(cfg.PlanModeAutoApprove) // User-global learning: captured only if no higher CLI file already did. r.captureLearning(cfg.Learning) + // User-global steer (issue #512): same discipline as posture. + r.captureSteer(cfg.Steer) // User-global models: captured only if no higher CLI file already did. r.captureModels(cfg.Models) // User-global openrouter: captured only if no higher CLI file already did. @@ -948,6 +987,21 @@ func (r *Resolver) capturePlanModeAutoApprove(p bool) { r.operatorPlanModeAutoApprove = p } +// captureSteer records the FIRST operator-tier steer: scalar seen during +// construction (CLI files are parsed before user-global, so CLI wins on +// first-present). It is called only from loadUserRules — the operator (user-global +// + CLI) tiers — never from loadProjectRules, so a project file can never supply it +// (operator-tier only, for consistency with posture — issue #512). A nil *bool +// (the key absent) is a no-op; a non-nil value records BOTH the value and the +// presence bit (the knob is an opt-OUT, so presence is load-bearing). +func (r *Resolver) captureSteer(s *bool) { + if s == nil || r.operatorSteerSet { + return + } + r.operatorSteer = *s + r.operatorSteerSet = true +} + // captureModels records the FIRST operator-tier models: block seen during // construction (CLI files are parsed before user-global, so CLI wins on // first-non-nil). It is called only from loadUserRules — the operator (user-global diff --git a/internal/adapter/permconfig/schema.go b/internal/adapter/permconfig/schema.go index cd34331fcf..d740278b94 100644 --- a/internal/adapter/permconfig/schema.go +++ b/internal/adapter/permconfig/schema.go @@ -121,6 +121,16 @@ type Config struct { // Learning configures optional completed-trajectory observation. The subtree is // strict; composition parses the closed off/review/auto mode vocabulary. Learning *LearningSection `yaml:"learning"` + // Steer is the OPERATOR-TIER mid-run steer knob (steer-while-running, issue + // #512): enable (default) or disable the mid-run steer inbox. Like + // Posture/ReasoningEffort it is honoured ONLY from the user-global + CLI tiers; + // a project-tier file's steer: key is IGNORED with a WARN (operator-tier only — + // the harness's operator surface is not a project repo's to flip, in either + // direction). It is a *bool so ABSENT is distinguishable from an explicit false: + // nil = absent (the resolver reports not-present and composition keeps the + // DEFAULT-ON); a non-nil value is honoured (composition maps steer: false onto + // the opt-OUT DisableSteer). + Steer *bool `yaml:"steer"` // OpenRouter holds the OPERATOR-TIER OpenRouter downstream-provider routing // config (issue #480): a per-model preferred DOWNSTREAM provider order, sent as // OpenRouter's `provider` request-body object. Like Guardrails/Posture it is diff --git a/internal/adapter/permconfig/steer_test.go b/internal/adapter/permconfig/steer_test.go new file mode 100644 index 0000000000..f3d5e90068 --- /dev/null +++ b/internal/adapter/permconfig/steer_test.go @@ -0,0 +1,70 @@ +package permconfig + +import ( + "bytes" + "context" + "strings" + "testing" + + "github.com/stacklok/mecatl/engine/adapter/memfs" + "github.com/stacklok/mecatl/engine/port" + "github.com/stacklok/mecatl/internal/adapter/slogdiag" +) + +// TestOperatorSteerFromCLIHonoured: an OPERATOR-TIER (CLI explicit) steer: false is +// read and returned by OperatorSteer() WITH its presence bit (issue #512), mirroring +// the plan-mode-auto-approve operator-tier test. The presence bit matters: the knob is +// an opt-OUT of a default-ON feature, so absent and explicit-false must differ. +func TestOperatorSteerFromCLIHonoured(t *testing.T) { + const yaml = "steer: false\n" + env := envWithExplicit("/etc/mecatl/steer.yaml", yaml) + r := newWithEnv(Options{ExplicitFiles: []string{"/etc/mecatl/steer.yaml"}}, env) + if r == nil { + t.Fatal("resolver should be non-nil with an explicit file") + } + value, present := r.OperatorSteer() + if !present { + t.Fatal("operator-tier steer: false must report present=true") + } + if value { + t.Fatal("operator-tier steer: false must report value=false (the opt-OUT)") + } +} + +// TestOperatorSteerAbsentIsNotPresent: with no steer: key anywhere, OperatorSteer +// reports present=false so composition keeps the DEFAULT-ON (it does not read a bare +// false as a disable). +func TestOperatorSteerAbsentIsNotPresent(t *testing.T) { + env := envWithExplicit("/etc/mecatl/other.yaml", "posture: auto\n") + r := newWithEnv(Options{ExplicitFiles: []string{"/etc/mecatl/other.yaml"}}, env) + if r == nil { + t.Fatal("resolver should be non-nil with an explicit file") + } + if _, present := r.OperatorSteer(); present { + t.Fatal("no steer: key must report present=false (composition keeps DEFAULT-ON)") + } +} + +// TestProjectSteerIgnoredWithWarn pins the operator-tier-only invariant (issue #512): +// a PROJECT-TIER steer: key must NEVER become the operator value — the harness's +// operator surface is not a project repo's to flip in either direction. The resolver +// WARNs naming why, mirroring the sibling operator-tier scalars +// (guardrails/posture/reasoning-effort/plan-mode-auto-approve). +func TestProjectSteerIgnoredWithWarn(t *testing.T) { + var buf bytes.Buffer + diag := slogdiag.New(&buf, false, port.LevelDebug) + + ws := &countingWS{Workspace: memfs.NewWorkspace("/repo")} + ws.seed(t, projectFileMecatl, "steer: false\n") + + r := newWithEnv(Options{Conventional: true, TrustProject: true, Diagnostics: diag}, fakeEnv()) + _ = r.Resolve(context.Background(), ws) + + if _, present := r.OperatorSteer(); present { + t.Fatal("a PROJECT-tier steer: key must NOT become the operator value (operator-tier only)") + } + log := buf.String() + if !strings.Contains(log, "steer: IGNORING a project-tier steer: key") { + t.Fatalf("expected an ignore-WARN naming the project tier; got:\n%s", log) + } +} diff --git a/internal/adapter/server/classification.go b/internal/adapter/server/classification.go index b2cb4419e6..e7b78307dd 100644 --- a/internal/adapter/server/classification.go +++ b/internal/adapter/server/classification.go @@ -277,6 +277,8 @@ var serviceAccessTable = map[string]ClassificationEntry{ "ApprovePlan": {KindCallerOwned, "authorizes the session before resolving the parked plan ask"}, "Cancel": {KindCallerOwned, "authorizes via GetSession before signalling the in-flight run"}, "CancelChild": {KindCallerOwned, "authorizes the PARENT session via GetSession before reaching into its child registry"}, + "Steer": {KindCallerOwned, "authorizes via GetSession before enqueueing to the live run's inbox or promoting through StartRunContent"}, + "CancelSteer": {KindCallerOwned, "authorizes via GetSession before reaching into the live run's steer inbox"}, "Persist": {KindCallerOwned, "authorizes via GetSession before consulting the live run registry"}, // --- caller-owned: schedules --- @@ -310,6 +312,7 @@ var serviceAccessTable = map[string]ClassificationEntry{ "PublishSessionEvent": {KindDerived, "publishes to subscribers already registered via the (caller-owned) Subscribe for this id; PublishSessionEvent itself takes no ctx and makes no independent decision"}, "AppendRunEvent": {KindDerived, "durable-append passthrough to the single appendEvent chokepoint for an id the in-process caller (the scheduler fire loop) already owns via its own run"}, "RecoverNotice": {KindDerived, "pops a notice keyed by id that only the relay's own immediately-preceding, already-authorized StartRunContent call could have set"}, + "LookupSteerMessageID": {KindDerived, "pops a steer message-id correlation only the relay's own already-authorized Steer call could have parked; consumed by the gRPC relay's EvSteer echo stamp on the same stream"}, "SetSessionEnvironment": {KindDerived, "called only with the id CreateSession* just returned to the same caller (internal/adapter/acp); renamed from SetSessionWorkspace by the execution-environments refactor"}, "CloseSession": {KindDerived, "internal cleanup for an id the caller (EndSession, already authorized) or the owning connection has already established as its own; takes no ctx"}, "EmitScheduleEvent": {KindDerived, "stamps the fire's ALREADY-established actor (the scheduler's system principal for a tick fire, or FireNow's caller) captured at fire time; makes no independent ownership decision"}, diff --git a/internal/adapter/server/export_test.go b/internal/adapter/server/export_test.go index 439150a5c5..1f021717fe 100644 --- a/internal/adapter/server/export_test.go +++ b/internal/adapter/server/export_test.go @@ -3,6 +3,8 @@ package server import ( "time" + mecatlv1 "github.com/stacklok/mecatl/contracts/gen/go/mecatl/v1" + "github.com/stacklok/mecatl/engine/agent" "github.com/stacklok/mecatl/engine/session" ) @@ -90,3 +92,16 @@ func (s *Service) DropSessionEngineForTest(id session.SessionID) { func (s *Service) NeedsRehydrationForTest(sess *session.Session) bool { return s.needsRehydration(sess) } + +// TrackSteerMessageIDForTest appends one client-minted id to the session's +// watermark FIFO — the test seam for the steer correlation invariant pin +// (ADR-0232; assert positional, not textual, correlation). +func (s *Service) TrackSteerMessageIDForTest(id session.SessionID, messageID string) { + s.trackSteerMessageID(id, messageID) +} + +// SteerOutcomeToProtoForTest exposes the unexported steerOutcomeToProto mapper +// so the full-matrix test can assert every agent.SteerOutcome arm. +func SteerOutcomeToProtoForTest(o agent.SteerOutcome) mecatlv1.SteerOutcome { + return steerOutcomeToProto(o) +} diff --git a/internal/adapter/server/grpc.go b/internal/adapter/server/grpc.go index 88d9bd4861..4619fbacb7 100644 --- a/internal/adapter/server/grpc.go +++ b/internal/adapter/server/grpc.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "strings" + "sync" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -263,12 +264,50 @@ func (h *HarnessServer) Converse(stream mecatlv1.HarnessService_ConverseServer) } defer h.svc.deregister(id, run) - // Read subsequent control frames concurrently so an approval/cancel can be - // delivered while events are still streaming. The reader exits on stream - // EOF (client closed its send half) or context cancellation. - go h.readControl(ctx, stream, run) - - logCtx := context.WithoutCancel(ctx) + // The single-writer gate for EVERY Send on this bidi stream: a gRPC stream + // is NOT goroutine-safe — Send called concurrently from the RecoverNotice + // pre-send below and the run relay would race without it. streamSender + // serializes every Send behind one mutex, so no two goroutines are ever + // inside a Send on this stream at once. (The promoted-steer follow-up relay + // is driven on the relay goroutine itself through this same sender — the + // sequential handoff below; the gate is the structural guarantee, not a + // per-goroutine coincidence.) + snd := &streamSender{stream: stream} + + // The steer-ack lane: readControl enqueues the AUTHORITATIVE outcome of + // each steer / steer_cancel frame here and the relay interleaves the acks + // onto the event stream in send order (a client learns what actually + // happened to its steer — the engine is authoritative on the slot / drain + // race, never the client's guess). Buffered so a burst of steer frames + // never blocks the control reader behind a slow client. + steerAcks := make(chan *mecatlv1.SteerAck, 16) + + // The handoff mailbox: a too_late steer the Service PROMOTED to a fresh + // follow-up run is POSTED here by readControl, and after the original run's + // relay drains, Converse TAKES it and relays the promoted run SEQUENTIALLY + // on this same stream (reusing snd + the same relay discipline) — never an + // orphaned second relay goroutine left running when the RPC returns, and + // runRelay.sendErr keeps a single owner. + ho := newSteerHandoff() + + // The control frame target: ResumeApproval / Cancel / CancelChild apply to + // the ACTIVE run — the original until a promoted steer hands off, then the + // promoted run (the swap is atomic with the relay handoff, so a control + // frame never hits the terminal original behind the client's back). + ct := &controlTarget{run: run} + + rl := &runRelay{ctx: ctx, logCtx: context.WithoutCancel(ctx), id: id, acks: steerAcks, snd: snd} + + // Read subsequent control frames concurrently so an approval/cancel/steer + // can be delivered while events are still streaming. The reader exits on + // stream EOF (client closed its send half) or context cancellation; a + // parked bidi Recv does NOT observe ctx cancellation promptly (it is + // buffered), so Converse — not the reader's exit — owns the handoff + // mailbox's seal (see the relay loop below), and stopControl exists to + // release the reader's non-Recv paths on the RPC's way out. + controlCtx, stopControl := context.WithCancel(ctx) + defer stopControl() // every early return releases the reader too + go h.readControl(controlCtx, id, ct, rl, ho) // Inject a pre-flight EvRecoverNotice when the session just recovered from a // PERMANENT failure — surface the advisory BEFORE the main event loop burns a @@ -277,53 +316,380 @@ func (h *HarnessServer) Converse(stream mecatlv1.HarnessService_ConverseServer) if notice := h.svc.RecoverNotice(id); notice != "" { ev := session.Event{Type: session.EvRecoverNotice, Text: notice} // Durable log first (cancel-detached, survives client disconnect). - h.svc.appendEvent(logCtx, id, ev) - // Forward to the client wire. - if err := stream.Send(&mecatlv1.ConverseResponse{Event: toProto(ev)}); err != nil { + h.svc.appendEvent(rl.logCtx, id, ev) + // Forward to the client wire through the same serialized sender. + if err := snd.Send(&mecatlv1.ConverseResponse{Event: toProto(ev)}); err != nil { return err } } - // Relay events on this goroutine; the channel closes when the run ends. On - // the FIRST Send error (the client is gone) the relay cancels the run but - // KEEPS RANGING, discarding events until the channel closes: a run that keeps - // emitting (a busy team / fan-out winding down) must never wedge in its own - // sends behind a dead relay. The error is sticky — no further Send happens - // after it — and is returned once the run has fully drained. - // - // The durable event-log Append (cloud-native Phase 3a) is DECOUPLED from the - // client send: it runs for EVERY observed event, BEFORE and independent of the - // drain-to-discard guard, so a disconnected client never stops the log (the - // whole point of a server-side durable log is to survive the client — it must - // record the post-disconnect tail, including the terminal EvResult). It uses a - // cancel-detached context so a cancelled stream ctx (client gone) cannot abort - // the durable write. This is DISTINCT from the EvPermissionAsk Persist below, - // which is snapshot semantics gated to the healthy path: the log is append-only - // history and must record what happened regardless of client liveness. - var sendErr error - for ev := range run.Events() { - if sendErr != nil { - // drain-to-discard: the client is gone. Still append to the durable - // log (it must record the post-disconnect tail), but skip Persist / - // auto-approve / the client send. - h.svc.appendEvent(logCtx, id, ev) - continue + // The sequential active-run handoff: relay the original run, then — while a + // promoted steer is pending — relay EACH promoted run in turn on this same + // stream BEFORE the RPC returns. The original's deregister fires before the + // first take, the promoted run's FinishRun before the next take / the + // return, so Converse never returns while a run it owns is still registered + // (no orphaned relay). The relay runs on its own goroutine so the read below + // can ALSO block on the next handoff post (the + // promote-grace window — the promote legitimately completes only after the + // original's relay drained, so readControl cannot have posted before the + // take without this). relayRes accumulates the per-run results (a + // mutex-guarded slice — a cap-N results channel could deadlock the producer + // when a post arrives while an earlier result sits unread). + var relayRes struct { + mu sync.Mutex + errs []error + } + pushErr := func(err error) { + relayRes.mu.Lock() + relayRes.errs = append(relayRes.errs, err) + relayRes.mu.Unlock() + } + done := make(chan struct{}) + go func() { + defer close(done) + pushErr(h.relayRun(rl, run)) + // The terminal original must leave the registry before a steer already + // being routed can reopen the session. Finish it before sealing the + // mailbox; a route that began before the seal is allowed to post its + // promoted run, while a later frame is correctly too late. + h.svc.FinishRun(id, run) + // Seal new routes and wait only for a Steer handler that has already + // received its frame. This closes the receive→post race without adding a + // grace delay to ordinary completed runs. + ho.closeAndWait() + for { + p, ok := ho.take() + if !ok { + return // sealed AND nothing queued: the RPC ends + } + // The control target swaps BEFORE the promoted relay starts, so a + // control frame received from here on applies to the ACTIVE run — + // never the terminal original. + ct.swap(p.run) + pushErr(h.relayRun(rl, p.run)) + h.svc.FinishRun(id, p.run) + // The promoted run's terminal ack is SENT INLINE (never the ack + // lane — relayRun already returned, so a lane-queued ack could lose + // the select race to the closed events channel and ride after the + // RPC returned): the promoted run's terminal EvResult drained + // strictly before this point (causal order), and the RPC returns + // only after it (no ack-after-close). + if rl.sendErr == nil { + if err := rl.snd.Send(&mecatlv1.ConverseResponse{Event: &mecatlv1.Event{ + Type: "steer.outcome", + SteerOutcome: &mecatlv1.SteerAck{ + Outcome: mecatlv1.SteerOutcome_STEER_OUTCOME_TOO_LATE, + Text: valid(p.text), + Promoted: true, + MessageId: valid(p.messageID), + }, + }}); err != nil { + rl.sendErr = err + } + } } - if !h.svc.relayEvent(ctx, logCtx, id, ev, true) { - continue // log-only event: consumed by the durable log, not relayed to the client wire + }() + // Wait out the relay loop: done closes only after the original relay AND + // every queued promotion's relay (plus its terminal ack) finished — the + // seal guarantees nothing later can be queued. + <-done + relayRes.mu.Lock() + defer relayRes.mu.Unlock() + // The promoted-relay terminal ack may have set sendErr after the last + // pushErr; the sticky relay error is the first non-nil of the recorded + // results, else the relay's own final state. + for _, err := range relayRes.errs { + if err != nil { + return err // the FIRST Send error, per the relay contract + } + } + return rl.sendErr +} + +// streamSender is the single-writer gate every Send on one Converse bidi stream +// goes through. A gRPC stream is NOT goroutine-safe: the RecoverNotice pre-send +// and the run relay reach the same stream, and only the mutex keeps any two of +// them out of a concurrent Send. (The sequential active-run handoff relays the +// promoted run on the SAME goroutine as the original, but the gate stays: it is +// the structural guarantee, not a per-goroutine coincidence.) +type streamSender struct { + mu sync.Mutex + stream mecatlv1.HarnessService_ConverseServer +} + +// Send serializes a single Send onto the stream. +func (s *streamSender) Send(m *mecatlv1.ConverseResponse) error { + s.mu.Lock() + defer s.mu.Unlock() + return s.stream.Send(m) +} + +// relayRun drains run.Events() onto the Converse stream until the channel +// closes (the terminal result was delivered), interleaving any steer.outcome +// acks the control reader produces. It returns the FIRST Send error, if any — +// but only after the run fully drained (see the drain-to-discard contract +// below). Extracted from Converse so the steer too-late→promoted follow-up run +// (the sequential active-run handoff) reuses the EXACT same relay discipline +// rather than growing a second copy. +// +// On the FIRST Send error (the client is gone) the relay cancels the run but +// KEEPS RANGING, discarding events until the channel closes: a run that keeps +// emitting (a busy team / fan-out winding down) must never wedge in its own +// sends behind a dead relay. The error is sticky — no further Send happens +// after it — and is returned once the run has fully drained. +// +// The durable event-log Append (cloud-native Phase 3a) is DECOUPLED from the +// client send: it runs for EVERY observed event, BEFORE and independent of the +// drain-to-discard guard, so a disconnected client never stops the log (the +// whole point of a server-side durable log is to survive the client — it must +// record the post-disconnect tail, including the terminal EvResult). It uses a +// cancel-detached context so a cancelled stream ctx (client gone) cannot abort +// the durable write. This is DISTINCT from the EvPermissionAsk Persist below, +// which is snapshot semantics gated to the healthy path: the log is append-only +// history and must record what happened regardless of client liveness. +// +// The select has no priority: a buffered steer ack may be delivered ahead of an +// already-buffered run event (Go picks a ready case at random). That is the +// honest contract — an ack's precise interleave position is unobservable +// ordering across stream latency anyway; the drain echo (EvSteer) is the +// authoritative committed-text surface. +// runRelay carries the one Converse stream's relay state the SHARED relayRun +// consumes: the ctx + cancel-detached logCtx, the session id, the steer-ack +// lane it interleaves, and the first Send error (sticky — drain-to-discard). +// Driving it through the runRelay (never the raw stream) keeps EVERY relay +// send on the stream's single-writer streamSender. +// +// sendErr has a SINGLE OWNER: the relay goroutine is the only reader AND +// writer — the sequential active-run handoff drives the promoted run's relay +// on the SAME goroutine (one relayRun call at a time), so no cross-goroutine +// access exists by construction. +type runRelay struct { + ctx context.Context + logCtx context.Context + id session.SessionID + acks chan *mecatlv1.SteerAck + snd *streamSender + sendErr error +} + +// sendEvent relays one run event through the streamSender, applying the +// drain-to-discard + first-error contract. It NEVER sends after the first +// error: a client-gone relay appends to the durable log only. +func (h *HarnessServer) sendEvent(rl *runRelay, ev session.Event) { + if rl.sendErr != nil { + // drain-to-discard: the client is gone. Still append to the durable + // log (it must record the post-disconnect tail), but skip Persist / + // auto-approve / the client send. + h.svc.appendEvent(rl.logCtx, rl.id, ev) + return + } + if !h.svc.relayEvent(rl.ctx, rl.logCtx, rl.id, ev, true) { + return // log-only event: consumed by the durable log, not relayed to the client wire + } + proto := toProto(ev) + if ev.Type == session.EvSteer && proto.GetSteer() != nil { + // The EvSteer drain echo echoes the client-minted message_id of the + // Steer frame that parked this text: the engine inbox carries text only, + // so the id lives at the Service's wire-correlation FIFO — popped here + // positionally (the TAIL). An unmatched echo (an id-less steer) rides + // with "". + id := h.svc.LookupSteerMessageID(rl.id) + if id == "" { + // The correlation FAILED: the echo carries "" and the client cannot + // match it to the frame it sent (the queue can stall — the exact + // symptom this WARN exists to make visible). No session.Event owns a + // correlation miss, so it goes to diagnostics, text clamped to a prefix. + h.svc.Diagnostics().Log(rl.logCtx, port.LevelWarn, "steer echo uncorrelated (no message_id for drained text)", "session", string(rl.id), "text_prefix", valid(firstRunes(ev.Steer.Text, 40))) + } else { + h.svc.Diagnostics().Log(rl.logCtx, port.LevelInfo, "steer drain echo correlated", + "session", string(rl.id), "message_id", id, "text_len", len(ev.Steer.Text)) } - if err := stream.Send(&mecatlv1.ConverseResponse{Event: toProto(ev)}); err != nil { - sendErr = err - run.Cancel() + proto.GetSteer().MessageId = valid(id) + } + if err := rl.snd.Send(&mecatlv1.ConverseResponse{Event: proto}); err != nil { + rl.sendErr = err + } +} + +// relayRun drains run.Events() onto the Converse stream until the channel +// closes (the terminal result was delivered), interleaving any steer.outcome +// acks the control reader produces. It returns the runRelay's FIRST Send error, +// if any — but only after the run fully drained (see the drain-to-discard +// contract above). Shared by the original run and a promoted-steer follow-up +// run (the sequential active-run handoff drives them ONE AT A TIME on the same +// goroutine), so every send crosses the stream's one single-writer streamSender +// and runRelay.sendErr keeps its single owner. +// +// On the FIRST Send error (the client is gone) the relay cancels the run but +// KEEPS RANGING, discarding events until the channel closes: a run that keeps +// emitting (a busy team / fan-out winding down) must never wedge in its own +// sends behind a dead relay. The error is sticky — no further Send happens after +// it (the sendEvent hard guard) — and is returned once the run has drained. +func (h *HarnessServer) relayRun(rl *runRelay, run *agent.Run) error { + events := run.Events() + acks := rl.acks + for events != nil { + select { + case ev, ok := <-events: + if !ok { + events = nil // the run ended + continue + } + h.sendEvent(rl, ev) + if rl.sendErr != nil { + run.Cancel() // first error: drain-to-discard from here + } + case ack, ok := <-acks: + if !ok { + acks = nil + continue + } + if rl.sendErr != nil { + continue // client gone: acks have no durable-log home; drop them + } + if err := rl.snd.Send(&mecatlv1.ConverseResponse{Event: &mecatlv1.Event{ + Type: "steer.outcome", + SteerOutcome: ack, + }}); err != nil { + rl.sendErr = err + run.Cancel() + } } } - return sendErr + return rl.sendErr } -// readControl reads ResumeApproval / Cancel / CancelChild frames until the -// client closes its send half or the context is cancelled, dispatching each -// onto run. -func (*HarnessServer) readControl(ctx context.Context, stream mecatlv1.HarnessService_ConverseServer, run *agent.Run) { +// controlTarget is the ACTIVE run the stream's control frames apply to: the +// original run until a promoted steer hands off, then the promoted run. The +// relay handoff swaps it BEFORE the promoted relay starts, so a ResumeApproval +// / Cancel / CancelChild received after the handoff hits the run actually +// driving — never the terminal original. +type controlTarget struct { + mu sync.RWMutex + run *agent.Run +} + +func (c *controlTarget) swap(r *agent.Run) { + c.mu.Lock() + c.run = r + c.mu.Unlock() +} + +func (c *controlTarget) active() *agent.Run { + c.mu.RLock() + defer c.mu.RUnlock() + return c.run +} + +// steerHandoff is the promoted-steer mailbox readControl posts into and +// Converse's sequential relay loop drains. It is a channel-based queue: at most +// one promotion is ever in flight per session (the promoted run is REGISTERED +// the moment Service.Steer returns it, so a second promote hits the funnel's +// liveness guard and is refused), so the buffered channel never blocks a +// poster, and take NEVER blocks — the relay loop consumes it only AFTER +// sealing, so a blocking take is never needed. +type steerHandoff struct { + mu sync.Mutex + q []steerPromotion + closed bool + routing int + routeDone chan struct{} +} + +// steerPromotion is one posted handoff: the promoted follow-up run plus the +// steer frame fields its terminal ack echoes. +type steerPromotion struct { + run *agent.Run + text string + messageID string +} + +func newSteerHandoff() *steerHandoff { return &steerHandoff{} } + +// beginRoute reserves the handoff for a steer frame already received by the +// control reader. Once the original relay seals the mailbox, only such an +// in-flight route may still post a promotion. +func (h *steerHandoff) beginRoute() bool { + h.mu.Lock() + defer h.mu.Unlock() + if h.closed { + return false + } + if h.routing == 0 { + h.routeDone = make(chan struct{}) + } + h.routing++ + return true +} + +func (h *steerHandoff) endRoute() { + h.mu.Lock() + defer h.mu.Unlock() + h.routing-- + if h.routing == 0 { + close(h.routeDone) + } +} + +// post queues a promoted run for the relay loop. A route that started before +// close is allowed to finish its post; frames received after close are dropped. +func (h *steerHandoff) post(p steerPromotion) { + h.mu.Lock() + defer h.mu.Unlock() + if h.closed && h.routing == 0 { + return + } + h.q = append(h.q, p) +} + +// take pops the OLDEST queued promotion; the second return is false once the +// mailbox is sealed AND drained (the relay loop's exit condition). +func (h *steerHandoff) take() (steerPromotion, bool) { + h.mu.Lock() + defer h.mu.Unlock() + if len(h.q) == 0 { + return steerPromotion{}, false + } + p := h.q[0] + h.q = h.q[1:] + return p, true +} + +// close seals the handoff against new routes. It is idempotent: Converse seals +// after the original relay drains and readControl seals again on exit. +func (h *steerHandoff) close() { + h.mu.Lock() + h.closed = true + h.mu.Unlock() +} + +// closeAndWait seals the handoff and waits for a steer route that was already +// received to finish posting. No new route may begin after the seal, so the +// captured channel is sufficient and an ordinary terminal run returns at once. +func (h *steerHandoff) closeAndWait() { + h.mu.Lock() + h.closed = true + done := h.routeDone + h.mu.Unlock() + if done != nil { + <-done + } +} + +// readControl reads ResumeApproval / Cancel / CancelChild / Steer / SteerCancel +// frames until the client closes its send half or the context is cancelled. +// Approval / cancel / cancel-child go to the ACTIVE run (ct — the original +// until a promoted steer hands off, then the promoted run, swapped atomically); +// a steer / steer_cancel frame routes through the Service (the single routing +// owner) and produces exactly ONE authoritative ack on the runRelay's ack lane +// (the relay interleaves them onto the stream). A too_late steer the Service +// PROMOTED is posted to the handoff mailbox: Converse relays it SEQUENTIALLY +// after the active run drains, and its terminal outcome is reported back as +// the steer ack. On exit readControl closes the mailbox so the relay loop +// learns no more promotions can arrive. +func (h *HarnessServer) readControl(ctx context.Context, id session.SessionID, ct *controlTarget, rl *runRelay, ho *steerHandoff) { + defer ho.close() + stream := rl.snd.stream for { if ctx.Err() != nil { return @@ -338,17 +704,26 @@ func (*HarnessServer) readControl(ctx context.Context, stream mecatlv1.HarnessSe case *mecatlv1.ConverseRequest_ResumeApproval: if k.ResumeApproval != nil { ra := k.ResumeApproval - run.Approve(ra.GetAskId(), verdictFromResumeApproval(ra.GetVerdict(), ra.GetAllow())) + ct.active().Approve(ra.GetAskId(), verdictFromResumeApproval(ra.GetVerdict(), ra.GetAllow())) } case *mecatlv1.ConverseRequest_Cancel: - run.Cancel() + ct.active().Cancel() case *mecatlv1.ConverseRequest_CancelChild: if k.CancelChild != nil { // Per-child cancel, addressed by the child session id. A false return // (unknown / already-done child) is ignored by design on the stream: the // finished-as-you-pressed race is benign and the observable outcome is the // child's terminal event (clients disable the key for done lanes). - _ = run.CancelChild(k.CancelChild.GetChildId()) + _ = ct.active().CancelChild(k.CancelChild.GetChildId()) + } + case *mecatlv1.ConverseRequest_Steer: + if k.Steer != nil && ho.beginRoute() { + h.handleSteerFrame(ctx, id, k.Steer, rl, ho) + ho.endRoute() + } + case *mecatlv1.ConverseRequest_SteerCancel: + if k.SteerCancel != nil { + h.handleSteerCancelFrame(ctx, id, k.SteerCancel.GetMessageId(), rl) } default: // A second Prompt or an unknown frame is ignored: the run is @@ -357,6 +732,117 @@ func (*HarnessServer) readControl(ctx context.Context, stream mecatlv1.HarnessSe } } +// handleSteerFrame routes one steer frame through the Service (the single +// routing owner — the handler is a dumb frame→Service mapper, mirroring how +// the Approve/Cancel frames route). The Service decides live-enqueue vs +// terminal-race promote: an accepted/appended outcome acks immediately, and a +// too_late frame the Service PROMOTED to a fresh follow-up run is POSTED to +// the handoff mailbox — Converse's sequential relay loop drives it on the SAME +// stream after the active run drains (no orphaned relay goroutine), then +// reports its terminal outcome back as the steer ack (promoted=true: never a +// drop). Every ack echoes the frame's client-minted message_id. +func (h *HarnessServer) handleSteerFrame(ctx context.Context, id session.SessionID, frame *mecatlv1.Steer, rl *runRelay, ho *steerHandoff) { + text, msgID := frame.GetText(), frame.GetMessageId() + outcome, promoted, promotedRun, err := h.svc.Steer(ctx, id, text, msgID) + switch { + case err != nil: + h.svc.Diagnostics().Log(ctx, port.LevelWarn, "steer route failed", "session", string(id), "error", err) + enqueueSteerAck(ctx, rl.acks, &mecatlv1.SteerAck{Outcome: mecatlv1.SteerOutcome_STEER_OUTCOME_TOO_LATE, Text: valid(text), MessageId: valid(msgID)}) + return + case !promoted || promotedRun == nil: + // The Service's live path answered (accepted / appended — the run + // parked/merged it). Ack the engine's authoritative outcome and log + // the correlation state (id + outcome only — never the text, which is + // producer-influenced and carries no diagnostic value). + h.svc.Diagnostics().Log(ctx, port.LevelInfo, "steer frame enqueued", + "session", string(id), "message_id", firstRunes(msgID, msgIDLogMax), + "text_len", len(text), "outcome", string(outcome)) + enqueueSteerAck(ctx, rl.acks, &mecatlv1.SteerAck{Outcome: steerOutcomeToProto(outcome), Text: valid(text), MessageId: valid(msgID)}) + return + } + // too_late + promoted: post the follow-up run to the handoff mailbox. The + // sequential relay loop relays its full event stream on this stream after + // the active run drains, FinishRun-deregisters it BEFORE the RPC returns, + // and reports its terminal outcome as the steer ack (promoted=true: never a + // drop, never an ack-after-close). + h.svc.Diagnostics().Log(ctx, port.LevelInfo, "steer frame promoted (too_late follow-up)", + "session", string(id), "message_id", firstRunes(msgID, msgIDLogMax), "text_len", len(text)) + ho.post(steerPromotion{run: promotedRun, text: text, messageID: msgID}) +} + +// handleSteerCancelFrame routes a steer_cancel frame through the Service and +// reports its authoritative outcome, echoing the frame's client-minted +// message_id. A session with no live run (the run went terminal behind the +// client's belief) reports none_pending — there is no inbox to retract from; +// the pending steer is already lost with its run. +func (h *HarnessServer) handleSteerCancelFrame(ctx context.Context, id session.SessionID, msgID string, rl *runRelay) { + outcome, err := h.svc.CancelSteer(ctx, id) + if err != nil { + outcome = agent.SteerNonePending // never wedge the reader on a cancel fault + } + // Log only on a RETRACTED outcome (a none_pending cancel is the + // information-free common case — the ack carries it; the none_pending + // line would be unbounded attacker-driven log volume, CWE-770). The + // client-minted message_id is clamped to a bounded prefix. + if outcome == agent.SteerRetracted { + h.svc.Diagnostics().Log(ctx, port.LevelInfo, "steer_cancel frame retracted", + "session", string(id), "message_id", firstRunes(msgID, msgIDLogMax), "outcome", string(outcome)) + } + enqueueSteerAck(ctx, rl.acks, &mecatlv1.SteerAck{Outcome: steerOutcomeToProto(outcome), MessageId: valid(msgID)}) +} + +// enqueueSteerAck delivers an ack on the lane, bounded by ctx: a buffered lane +// accepts it immediately; a FULL lane (a burst outrunning a stalled client) +// drops it on ctx cancel rather than wedging the control reader (the run's own +// guard sends give up on the same signal). +func enqueueSteerAck(ctx context.Context, acks chan<- *mecatlv1.SteerAck, ack *mecatlv1.SteerAck) { + select { + case acks <- ack: + case <-ctx.Done(): + } +} + +// msgIDLogMax bounds the client-minted message_id before it reaches any +// diagnostics log (CWE-770 — the log echo is bounded; the wire ack continues +// to carry the verbatim id for correlation). Shared by the three frame-handler +// log sites. +const msgIDLogMax = 64 + +// firstRunes returns the first n runes of s without allocating a []rune (the +// range-over-string form; used to clamp a diagnostics text preview). +func firstRunes(s string, n int) string { + if n <= 0 { + return "" + } + for i := range s { + if n == 0 { + return s[:i] + } + n-- + } + return s +} + +// steerOutcomeToProto maps the engine's closed-enum agent.SteerOutcome onto the +// wire enum. The zero/unknown value maps to UNSPECIFIED (never sent in +// practice — every engine outcome has an arm). +func steerOutcomeToProto(o agent.SteerOutcome) mecatlv1.SteerOutcome { + switch o { + case agent.SteerAccepted: + return mecatlv1.SteerOutcome_STEER_OUTCOME_ACCEPTED + case agent.SteerAppended: + return mecatlv1.SteerOutcome_STEER_OUTCOME_APPENDED + case agent.SteerRetracted: + return mecatlv1.SteerOutcome_STEER_OUTCOME_RETRACTED + case agent.SteerNonePending: + return mecatlv1.SteerOutcome_STEER_OUTCOME_NONE_PENDING + case agent.SteerTooLate: + return mecatlv1.SteerOutcome_STEER_OUTCOME_TOO_LATE + default: + return mecatlv1.SteerOutcome_STEER_OUTCOME_UNSPECIFIED + } +} + // --- MCP inspection RPCs ----------------------------------------------------- // ListMcpResources returns the resource snapshots for the requested server diff --git a/internal/adapter/server/mapper.go b/internal/adapter/server/mapper.go index 1a2f89e8a4..79319fd002 100644 --- a/internal/adapter/server/mapper.go +++ b/internal/adapter/server/mapper.go @@ -166,9 +166,20 @@ func toProto(ev session.Event) *mecatlv1.Event { if ev.CompactionArchive != nil { out.CompactionArchive = toProtoCompactionArchive(*ev.CompactionArchive) } + if ev.Steer != nil { + out.Steer = toProtoSteer(*ev.Steer) + } return out } +// toProtoSteer maps a session.SteerPayload to its proto SteerEcho form: the +// CLIENT-VISIBLE, authoritative echo of the committed operator steer (EvSteer). +// The text is operator-supplied (producer-influenced), so it rides the valid() +// backstop like every other non-harness string. +func toProtoSteer(p session.SteerPayload) *mecatlv1.SteerEcho { + return &mecatlv1.SteerEcho{Text: valid(p.Text)} +} + // toProtoParallel maps a session.ParallelPayload to its proto Parallel form: the // bounded-preview projection of a Parallel fork-join run. Usage is always emitted // (zero on the start/branch_start/branch_tool kinds); the per-kind field population diff --git a/internal/adapter/server/service.go b/internal/adapter/server/service.go index bf4140dfca..dcf64d4405 100644 --- a/internal/adapter/server/service.go +++ b/internal/adapter/server/service.go @@ -859,6 +859,21 @@ type Service struct { // s.mu. replayedApprovals map[session.SessionID]struct{} + // steerMsgIDs correlates a DRAINED steer's EvSteer echo with the client-minted + // message_id of the Steer frame that parked it (issue #512): the engine inbox + // parks TEXT ONLY, so the id lives at this wire-correlation layer. On each + // accepted/appended steer the Service appends the frame's id to the session's + // ordered list; when the EvSteer drain echo commits, the relay pops the WHOLE + // list and stamps the echo with the LATEST (tail) id — the WATERMARK the client + // splits its ordered queue on (positional, never text-match — pinned by + // internal/adapter/server/steer_watermark_pin_test.go). An unmatched echo (a + // steer enqueued by another surface with no id, or a drain after a retract) + // rides with "". The whole list is consumed on drain, dropped on retract + // (dropSteerMessageID), cleared on register() of a fresh run, and deleted on + // CloseSession — so the map holds at most one entry per accepted-unresolved + // steer. Guarded by s.mu. + steerMsgIDs map[session.SessionID][]steerMsgID + // heldLeases tracks the cross-process session leases this process currently // holds (cloud-native Phase 4, ADR 0027). A lease is acquired ONCE per session // on first run-entry (after the per-session runEntryMu) and held for the @@ -1102,6 +1117,7 @@ func NewService(cfg Config) (*Service, error) { sessionEnvironments: make(map[session.SessionID]tool.Environment), reservedIDs: make(map[session.SessionID]struct{}), replayedApprovals: make(map[session.SessionID]struct{}), + steerMsgIDs: make(map[session.SessionID][]steerMsgID), heldLeases: make(map[session.SessionID]*heldLease), cleanupTokenKey: cleanupTokenKey, cleanupPlans: make(map[string]cleanupTokenPayload), @@ -1778,6 +1794,12 @@ func (s *Service) capabilities() *mecatlv1.ServerCapabilities { StorageCleanup: s.cfg.StorageManagementAuthorized != nil && s.maintenanceMutationAvailable() && supportsCleanupDelete(s.cfg.Store), LegacyAdoption: s.cfg.OwnershipEnforced && s.cfg.SessionEngine != nil, 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 + // recomputed per sink (the CreateSession echo and the Session snapshot + // re-hydration path both carry this one value). + Steer: s.cfg.Engine != nil && s.cfg.Engine.SteerEnabled(), } } @@ -1865,6 +1887,11 @@ func (s *Service) CloseSession(id session.SessionID) { // sync.Map; like the approval-replay marker above, clearing it keeps the map // from growing unbounded on a long-lived server. s.recoverNotices.Delete(id) + // Drop any un-drained steer correlation ids (ADR-0228 review finding): a + // session closed with a parked-but-undrained steer would otherwise leak an + // entry in steerMsgIDs until process exit (same unbounded-map class the + // neighboring two deletes close). + delete(s.steerMsgIDs, id) s.mu.Unlock() if ok && se.close != nil { _ = se.close() @@ -2644,55 +2671,55 @@ func (s *Service) reopenLoadedSession(ctx context.Context, sess *session.Session // re-derives idempotent rules). It reads the LOADED conversation to correlate the // verdicts, so it must run after GetSession and before the engine runs. s.maybeReplayApprovals(ctx, sess) + if sess.State == session.StateFailed && sess.FailurePermanence() { + // Capture permanence BEFORE Recover() clears it (resetToIdle sets + // permanent=false). Store the pre-flight advisory so the relay can emit + // an EvRecoverNotice before the next turn burns a provider call on the + // same unrecoverable error. Use LoadOrStore so two concurrent loads of + // the same session (under different surface adapters) still emit exactly + // ONE notice. Keyed by the session id. + s.recoverNotices.LoadOrStore(id, recoverNoticeText) + } + if err := s.repairTerminalState(ctx, sess); err != nil { + return nil, err + } + return sess, nil +} + +// repairTerminalState is the recover-if-terminal arm shared by loadAndReopen +// (the run-entry funnel) and StartRunContent's post-drain-grace re-repair: +// completed → Reopen, cancelled → Interrupt, failed → +// Recover (each history-repaired), persisted, so the matching Run never drives +// an illegal RecordUserPrompt-from-terminal. Awaiting, idle, and running are +// deliberate no-ops: awaiting is the preserved Phase-2 resume point (repairing +// it would clear its still-resolvable PendingAsk); idle is the target state; +// running is repaired ONLY by StartRunContent's crash-orphan Abandon arm, AFTER +// it holds the real lock/lease (issue #475) — never here. +func (s *Service) repairTerminalState(ctx context.Context, sess *session.Session) error { switch sess.State { case session.StateCompleted: if rerr := sess.Reopen(); rerr != nil { - return nil, fmt.Errorf("server: reopen session: %w", rerr) + return fmt.Errorf("server: reopen session: %w", rerr) } if serr := s.cfg.Store.Save(ctx, sess); serr != nil { - return nil, fmt.Errorf("server: persist reopened session: %w", serr) + return fmt.Errorf("server: persist reopened session: %w", serr) } case session.StateCancelled: if rerr := sess.Interrupt(); rerr != nil { - return nil, fmt.Errorf("server: interrupt session: %w", rerr) + return fmt.Errorf("server: interrupt session: %w", rerr) } if serr := s.cfg.Store.Save(ctx, sess); serr != nil { - return nil, fmt.Errorf("server: persist interrupted session: %w", serr) + return fmt.Errorf("server: persist interrupted session: %w", serr) } case session.StateFailed: - // Capture permanence BEFORE Recover() clears it (resetToIdle - // sets permanent=false). Store the pre-flight advisory so the - // relay can emit an EvRecoverNotice before the next turn burns a - // provider call on the same unrecoverable error. - if sess.FailurePermanence() { - // Use LoadOrStore so two concurrent loads of the same - // session (under different surface adapters) still emit - // exactly ONE notice. Keyed by the session id. - s.recoverNotices.LoadOrStore(id, recoverNoticeText) - } if rerr := sess.Recover(); rerr != nil { - return nil, fmt.Errorf("server: recover session: %w", rerr) + return fmt.Errorf("server: recover session: %w", rerr) } if serr := s.cfg.Store.Save(ctx, sess); serr != nil { - return nil, fmt.Errorf("server: persist recovered session: %w", serr) + return fmt.Errorf("server: persist recovered session: %w", serr) } - // case session.StateAwaiting: intentionally no-op. Awaiting is the - // deliberately-preserved Phase 2 cross-process resume point - // (resumeFromAwaiting) — repairing it here would clear its still-resolvable - // PendingAsk. It stays terminal-for-loadAndReopen's purposes by falling - // through this switch untouched. - // case session.StateIdle: intentionally no-op. Idle is already the target - // state every other case resets TO — nothing to repair. - // - // case session.StateRunning is intentionally NOT handled here (issue #475): - // a crash-orphaned "running" snapshot is repaired by StartRunContent itself, - // AFTER it holds the real lease/lock (see the repair beside runEntryMu/ - // acquireLease below) — never inside this pre-lock funnel, where a trial - // lease could collide with a concurrent caller or a peer's genuine - // acquireLease. loadAndReopen has no lock/lease of its own to make that - // repair safe. } - return sess, nil + return nil } // RecoverNotice returns the pre-flight advisory message for session id when @@ -2932,6 +2959,214 @@ func (s *Service) startRunContent(ctx context.Context, id session.SessionID, tex return run, nil } +// Steer routes an operator steer (mid-run injected input, issue #512) for a +// session to the right home. It is the Service-level routing decision the +// wire-facing steer handler drives: the steer NEVER drops silently. +// +// - LIVE run: if a run is registered AND its steer inbox still accepts it, +// the text enqueues to that run's steer inbox (the run drains it at the next +// turn boundary) and the function reports the engine's authoritative outcome +// (accepted / appended) with promoted=false. +// - LOST TERMINAL RACE: no live run, or the live run's inbox already closed +// (the engine reported too_late — the run went terminal behind the caller's +// "still running" belief): the steer is PROMOTED into a fresh follow-up run +// through the EXISTING hardened run-entry funnel — StartRunContent +// (loadAndReopen + the lease + recover-if-completed / interrupt-if-cancelled +// / recover-if-failed / abandon-if-crash-orphaned-running) — exactly like a +// normal follow-up prompt, and reported as (agent.SteerTooLate, true). +// +// The returned promotedRun (non-nil only when promoted) is the registered +// follow-up run the caller must drain + FinishRun, exactly as StartRunContent's +// caller does. An unknown session id yields ErrNotFound (via the funnel); a +// terminal-state repair failure surfaces as the funnel's error. +// +// messageID is the client-minted correlation id of the Steer frame ("" when the +// caller supplied none). On an accepted steer it parks in the session's FIFO so +// the EvSteer drain echo can echo it (LookupSteerMessageID); the ACK-side echo +// is the caller's own frame field (it never crosses the Service). +func (s *Service) Steer(ctx context.Context, id session.SessionID, text, messageID string) (agent.SteerOutcome, bool, *agent.Run, error) { + // Authorize before touching the in-memory registry or the run-entry funnel: + // a steer injects caller input into a run / drives a follow-up, so a foreign + // request must be absence-equivalent (ErrNotFound), mirroring Cancel/Approve. + if _, err := s.GetSession(ctx, id); err != nil { + return agent.SteerTooLate, false, nil, err + } + // Live-run fast path: enqueue to the run's steer inbox. A live run whose + // engine disarmed steer (EnableSteer off) reports too_late; it is PROMOTED + // rather than dropped — same lost-race contract as a closed inbox. + if run, ok := s.LookupRun(id); ok { + outcome, err := run.EnqueueSteer(text) + if err == nil && outcome != agent.SteerTooLate { + // Track BOTH accepted (new bundle) and appended (merged into the pending + // bundle): the watermark echo needs the full ordered id-list of the + // bundle that drains. + if outcome == agent.SteerAccepted || outcome == agent.SteerAppended { + s.trackSteerMessageID(id, messageID) + } + return outcome, false, nil, nil + } + if err != nil { + return outcome, false, nil, fmt.Errorf("server: steer enqueue: %w", err) + } + } + // Terminal race → promote through the run-entry funnel. An unknown id or an + // unrepaired-terminal-state error surfaces here rather than ever dropping. + // + // Drain-grace: the too_late steer usually arrives in + // the TERMINATE WINDOW — the original run went terminal (its inbox closed) + // but its relay is still draining, so the run is still REGISTERED and a bare + // StartRunContent would hit the funnel's IsLive / StateRunning guards and + // drop the steer with a bare too_late ack. The promote path is the ONE place + // that must wait out that drain: only a terminal-but-still-registered run + // can deregister within steerPromoteGrace (a genuinely in-flight run's relay + // drains continuously, so the lapse correctly refuses it), and ONLY the + // promoted steer pays the wait — the shared funnel stays byte-identical, so + // a concurrent legitimate prompt on the same live session is never wrongly + // delayed. + promotedRun, err := s.promotedSteerRun(ctx, id, text) + if err != nil { + return agent.SteerTooLate, false, nil, err + } + return agent.SteerTooLate, true, promotedRun, nil +} + +// promotedSteerRun is Service.Steer's promote path: try the funnel +// once, and only on a liveness conflict await the original run's deregister +// (bounded by steerPromoteGrace) then retry — so the just-terminal, +// still-draining run clears and the follow-up drives through the hardened +// funnel instead of dropping the steer. Holding the wait HERE — never inside +// the shared StartRunContent — keeps the concurrent-live-prompt contract +// byte-identical: only the promoted steer waits out a drain, so a prompt on a +// genuinely-live session is not slowed by the grace. An unknown session id +// surfaces ErrNotFound from the first funnel call. +func (s *Service) promotedSteerRun(ctx context.Context, id session.SessionID, text string) (*agent.Run, error) { + run, err := s.StartRunContent(ctx, id, text, nil) + if err == nil { + return run, nil // no live run blocked the entry — promoted immediately + } + if !errors.Is(err, ErrFailedPrecondition) { + return nil, err // not a liveness conflict — surface it (e.g. ErrNotFound) + } + // Liveness conflict: the original run is still registered. If it is the + // terminal-but-draining run the grace exists for, it clears within + // steerPromoteGrace; a run still registered at the lapse is genuinely + // in-flight (its relay drains continuously), so the lapse refuses the + // promotion. Only a terminal-but-still-registered run can possibly + // deregister inside the grace, so the wait is near-zero after the relay + // finished and correctly bounds the refusal. + if !s.awaitRunDeregister(ctx, id, steerPromoteGrace) { + return nil, err // the run is genuinely in-flight — refuse the promotion + } + // Registry cleared: the original relay finished and the run's final terminal + // state is durable. Drive the follow-up through the hardened funnel, which + // now sees the terminal state and reopens it. + return s.StartRunContent(ctx, id, text, nil) +} + +// CancelSteer retracts the session's live run's PENDING (un-drained) steer, +// reporting the engine's authoritative outcome (retracted / none_pending). It is +// the Service-level owner of the steer_cancel route — +// the wire handler drives THIS (mirror of how Cancel routes through the +// Service), so the live-run lookup stays single-owner and the deferred HTTP/SSE +// steer endpoint reuses the same Service decision rather than re-deriving it. A +// steer that already drained at a turn boundary is ordinary recorded history and +// cannot be retracted (the engine reports none_pending then: the drain won). A +// session with no live run reports none_pending (there is no inbox to retract +// from — the steer that would be pending is already lost with its run, the +// best-effort in-memory contract the docs/acceptance/steer-while-running.md +// Scenario-2 contract records). The wire's steer_cancel message_id never +// crosses the Service (the ack-side echo is the caller's own frame field), so +// the signature stays id-less. +func (s *Service) CancelSteer(ctx context.Context, id session.SessionID) (agent.SteerOutcome, error) { + // Authorize before the registry read, mirroring Cancel: a foreign request is + // absence-equivalent (ErrNotFound), never a peek at another caller's inbox. + if _, err := s.GetSession(ctx, id); err != nil { + return agent.SteerNonePending, err + } + run, ok := s.LookupRun(id) + if !ok { + return agent.SteerNonePending, nil + } + outcome, err := run.CancelSteer() + if err != nil { + return outcome, fmt.Errorf("server: steer cancel: %w", err) + } + if outcome == agent.SteerRetracted { + s.dropSteerMessageID(id) + } + return outcome, nil +} + +// steerMsgIDs maps a session to the ORDERED list of client-minted message_ids of +// the steers appended into the run's single pending bundle (the engine inbox +// parks text only — the id lives at this wire-correlation layer). At most ONE +// bundle is pending per run (the single-slot inbox), so at most one ordered list +// is pending per session: a send APPENDS its id; on drain every entry is consumed +// positionally (the whole list is deleted) and the echo carries the LATEST (tail) +// id as the WATERMARK the client splits its ordered queue on — never text-match. +// Ids are clamped to a bounded prefix at track (CWE-770; the legitimately minted +// ids are tiny — a longer client id that clips correlates identically on both +// sides when the ack/echo report the same stored prefix). +type steerMsgID struct { + messageID string +} + +// steerMsgIDClamp bounds a client-minted message id before it touches the +// watermark FIFO (and every downstream log/diagnostic echo). Ids beyond it are +// truncated without losing the ack-correlation property for realistic ids. +const steerMsgIDClamp = 64 + +// trackSteerMessageID appends the client-minted message_id of an accepted OR +// appended steer to the session's ordered pending-bundle list. When the bundle +// drains, LookupSteerMessageID returns the TAIL (watermark) id and consumes the +// whole list. +func (s *Service) trackSteerMessageID(id session.SessionID, messageID string) { + if r := []rune(messageID); len(r) > steerMsgIDClamp { + messageID = string(r[:steerMsgIDClamp]) + } + s.mu.Lock() + s.steerMsgIDs[id] = append(s.steerMsgIDs[id], steerMsgID{messageID: messageID}) + depth := len(s.steerMsgIDs[id]) + s.mu.Unlock() + // Correlation state, never text (untrusted producer content): depth + id + // suffice to rebuild intent across runs of the log. + s.cfg.Diagnostics.Log(context.Background(), port.LevelInfo, "steer message-id tracked", + "session", string(id), "message_id", messageID, "queue_depth", depth) +} + +// dropSteerMessageID discards the correlation of a RETRACTED steer: the retract +// clears the run's one pending bundle, so the WHOLE ordered list for the session +// is dropped (the bundle is gone — nothing remains to correlate). A retract with +// an empty list (id-less steer) is the harmless no-op. +func (s *Service) dropSteerMessageID(id session.SessionID) { + s.mu.Lock() + delete(s.steerMsgIDs, id) + s.mu.Unlock() + s.cfg.Diagnostics.Log(context.Background(), port.LevelInfo, "steer message-id dropped (retract)", "session", string(id)) +} + +// LookupSteerMessageID returns the WATERMARK message_id for a drained bundle — +// the LATEST (tail) id of the session's ordered pending list — and CONSUMES the +// whole list (the bundle drained; the next bundle starts a fresh list). The +// client splits its ordered queue on the watermark (positional, never +// text-match — the drift class the abandoned `_ string` parameter gestured at). +// "" when the list is empty (an id-less steer, or a drain after a retract). +func (s *Service) LookupSteerMessageID(id session.SessionID) string { + s.mu.Lock() + q := s.steerMsgIDs[id] + if len(q) == 0 { + s.mu.Unlock() + return "" + } + watermark := q[len(q)-1].messageID + depth := len(q) + delete(s.steerMsgIDs, id) + s.mu.Unlock() + s.cfg.Diagnostics.Log(context.Background(), port.LevelInfo, "steer message-id watermark consumed", + "session", string(id), "message_id", watermark, "queue_depth", depth) + return watermark +} + // isDelegationChildSessionID reports whether id carries one of the delegation // families' child-session id prefixes: agent.SubagentSessionPrefix, // agent.ParallelSessionPrefix, agent.TeamSessionPrefix — the engine's exported @@ -3602,6 +3837,46 @@ func (s *Service) IsLive(id session.SessionID) bool { return topLevel || s.cfg.SessionLiveness != nil && s.cfg.SessionLiveness.IsLive(id) } +// steerPromoteGrace bounds the drain-grace promotedSteerRun waits for a +// just-terminal run's relay to FinishRun-deregister it — +// the promoted steer must not bounce off a liveness guard into the drop-and-ack +// path while the run's terminal relay drain is still completing. The value is +// conservatively long (2s) so a backlogged relay comfortably finishes; a run +// still registered when it lapses is genuinely in-flight (a busy run's relay +// drains continuously, so only a terminal-but-still-registered run can possibly +// deregister inside the grace). +const steerPromoteGrace = 2 * time.Second + +// steerPromotePoll is the poll quantum awaitRunDeregister re-checks the +// registration between — short enough that the promoted entry sees the cleared +// registry promptly, long enough that s.mu is not hot-spun under the wait. +const steerPromotePoll = 20 * time.Millisecond + +// awaitRunDeregister blocks until no run is registered for id (the in-flight +// registry observation StartRunContent's liveness guards read), the grace +// lapses, or ctx is cancelled, returning true when the registry cleared. It is +// a pure registry OBSERVATION (poll, never mutation): it never abandons or +// re-homes a live run's session — the only safe read that lets the promoted +// steer wait out a terminal-but-still-draining run without racing it. +func (s *Service) awaitRunDeregister(ctx context.Context, id session.SessionID, grace time.Duration) bool { + deadline := time.NewTimer(grace) + defer deadline.Stop() + tick := time.NewTicker(steerPromotePoll) + defer tick.Stop() + for { + if !s.IsLive(id) { + return true + } + select { + case <-tick.C: + case <-deadline.C: + return false + case <-ctx.Done(): + return false + } + } +} + // Approve resolves the paused permission ask on the session's in-flight run with // the client's three-way verdict (deny / allow-once / allow-always). // @@ -4687,10 +4962,15 @@ func (s *Service) SettleIfStale(ctx context.Context, id session.SessionID) (bool } // register records run (and the live session it drives) as the in-flight run -// for id. +// for id. A FRESH run also clears any parked steer message-id correlation the +// session still holds: a parked steer lives on its run's in-memory inbox and +// dies with it, so a leftover entry from the PREVIOUS run could never drain — +// and clearing here keeps a promoted follow-up run's own steers (tracked AFTER +// its register) from ever matching a dead run's text. func (s *Service) register(id session.SessionID, run *agent.Run, sess *session.Session) { s.mu.Lock() s.runs[id] = &runState{run: run, sess: sess} + delete(s.steerMsgIDs, id) s.mu.Unlock() } diff --git a/internal/adapter/server/steer_handoff_test.go b/internal/adapter/server/steer_handoff_test.go new file mode 100644 index 0000000000..46feb7136c --- /dev/null +++ b/internal/adapter/server/steer_handoff_test.go @@ -0,0 +1,586 @@ +package server_test + +// Rework task 09 (Ozz-review round 2, findings #3/#4/#5): the WIRE + handoff +// contract — +// +// - R3-1: a client-minted message_id round-trips — echoed on the steer.outcome +// ack AND on the EvSteer drain echo, so the client correlates the +// authoritative outcome with the frame it sent (text is not a safe key). +// - R3-2: a second steer while one is pending APPENDS into the pending bundle +// (STEER_OUTCOME_APPENDED — supersede/slot_full were dropped in the round-3 +// append-default rework). +// - R3-3: a promoted steer's follow-up run relays its FULL event stream + +// terminal EvResult + its steer.outcome{promoted:true} ack on the SAME +// stream BEFORE the Converse RPC returns (the sequential active-run +// handoff — no orphaned relay goroutine, no ack-after-close). +// - R3-4: after a promote, control frames (ResumeApproval / Cancel) target the +// PROMOTED run, not the terminal original (the control target swaps +// atomically with the relay handoff). +// - R3-5: runRelay.sendErr has a SINGLE owner — no cross-goroutine read/write +// between the original and promoted relays (-race is the assertion). + +import ( + "context" + "errors" + "io" + "strings" + "sync/atomic" + "testing" + "time" + + mecatlv1 "github.com/stacklok/mecatl/contracts/gen/go/mecatl/v1" + "github.com/stacklok/mecatl/engine/adapter/mockllm" + "github.com/stacklok/mecatl/engine/session" +) + +// TestSteer_MessageIdRoundTrip is R3-1: a steer frame carrying a client-minted +// message_id gets it echoed verbatim on (a) the steer.outcome ack and (b) the +// EvSteer drain echo, so the client correlates both authoritative signals with +// the frame it sent. A steer_cancel's message_id likewise echoes on its ack. +func TestSteer_MessageIdRoundTrip(t *testing.T) { + block := &blockingTextTool{started: make(chan struct{}), release: make(chan struct{})} + llm := mockllm.New( + mockllm.ToolCallTurn(call("c1", "Read", `{"path":"a.go"}`)), + mockllm.TextTurn("done"), + ) + svc := newSteerService(t, llm, nil, block) + client, cleanup := dialGRPC(t, svc) + defer cleanup() + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cs, err := client.CreateSession(ctx, &mecatlv1.CreateSessionRequest{Workspace: "/ws"}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + stream, err := client.Converse(ctx) + if err != nil { + t.Fatalf("Converse: %v", err) + } + if err := stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Prompt{Prompt: &mecatlv1.Prompt{SessionId: cs.GetSessionId(), Text: "look"}}, + }); err != nil { + t.Fatalf("Send prompt: %v", err) + } + + // While the tool holds the run live: steer with message_id m-steer-1, then a + // steer_cancel with message_id m-cancel-1 (the cancel retracts the parked + // steer; the second steer m-steer-2 then drains). + framesSent := make(chan error, 1) + go func() { + <-block.started + if err := stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Steer{Steer: &mecatlv1.Steer{Text: "first draft", MessageId: "m-steer-1"}}, + }); err != nil { + framesSent <- err + return + } + if err := stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_SteerCancel{SteerCancel: &mecatlv1.SteerCancel{MessageId: "m-cancel-1"}}, + }); err != nil { + framesSent <- err + return + } + framesSent <- stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Steer{Steer: &mecatlv1.Steer{Text: "final steer", MessageId: "m-steer-2"}}, + }) + }() + + var events []*mecatlv1.Event + var acks []*mecatlv1.SteerAck + var echo *mecatlv1.SteerEcho + for { + resp, err := stream.Recv() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + t.Fatalf("Recv: %v", err) + } + ev := resp.GetEvent() + events = append(events, ev) + switch ev.GetType() { + case "steer.outcome": + acks = append(acks, ev.GetSteerOutcome()) + if len(acks) == 3 { + close(block.release) // all acks in: let the run drain + finish + } + case "steer": + echo = ev.GetSteer() + case "result": + } + if ev.GetType() == "result" { + break + } + } + _ = stream.CloseSend() + if err := <-framesSent; err != nil { + t.Fatalf("Send frames: %v", err) + } + + // Three acks in send order: accepted(m-steer-1), retracted(m-cancel-1), + // accepted(m-steer-2) — each echoing ITS frame's message_id. + if len(acks) != 3 { + t.Fatalf("steer acks = %d, want 3: %v", len(acks), typesOf(events)) + } + wantAcks := []struct { + outcome mecatlv1.SteerOutcome + msgID string + }{ + {mecatlv1.SteerOutcome_STEER_OUTCOME_ACCEPTED, "m-steer-1"}, + {mecatlv1.SteerOutcome_STEER_OUTCOME_RETRACTED, "m-cancel-1"}, + {mecatlv1.SteerOutcome_STEER_OUTCOME_ACCEPTED, "m-steer-2"}, + } + for i, want := range wantAcks { + if acks[i].GetOutcome() != want.outcome { + t.Fatalf("ack[%d] outcome = %v, want %v", i, acks[i].GetOutcome(), want.outcome) + } + if acks[i].GetMessageId() != want.msgID { + t.Fatalf("ack[%d] message_id = %q, want %q (the ack must echo its frame's id)", i, acks[i].GetMessageId(), want.msgID) + } + } + + // The EvSteer drain echo carries the COMMITTED text AND the message_id of + // the steer that drained (m-steer-2 — m-steer-1 was retracted). + if echo == nil { + t.Fatalf("no steer drain echo on the stream: %v", typesOf(events)) + } + if echo.GetText() != "final steer" { + t.Fatalf("steer echo text = %q, want %q", echo.GetText(), "final steer") + } + if echo.GetMessageId() != "m-steer-2" { + t.Fatalf("steer echo message_id = %q, want %q (the drained steer's id)", echo.GetMessageId(), "m-steer-2") + } +} + +// TestSteer_WatermarkEchoLatestId is the AC2.4 wire-side pin: two appends +// back-to-back merge into one bundle; the drain echo carries the merged text +// AND the watermark (the TAIL id of the contributing sends). (Renamed from +// TestSteer_SlotFullOnWire when slot_full was dropped in round 3.) +func TestSteer_WatermarkEchoLatestId(t *testing.T) { + block := &blockingTextTool{started: make(chan struct{}), release: make(chan struct{})} + llm := mockllm.New( + mockllm.ToolCallTurn(call("c1", "Read", `{"path":"a.go"}`)), + mockllm.TextTurn("done"), + ) + svc := newSteerService(t, llm, nil, block) + client, cleanup := dialGRPC(t, svc) + defer cleanup() + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cs, err := client.CreateSession(ctx, &mecatlv1.CreateSessionRequest{Workspace: "/ws"}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + stream, err := client.Converse(ctx) + if err != nil { + t.Fatalf("Converse: %v", err) + } + if err := stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Prompt{Prompt: &mecatlv1.Prompt{SessionId: cs.GetSessionId(), Text: "look"}}, + }); err != nil { + t.Fatalf("Send prompt: %v", err) + } + + // Two steers back-to-back while the tool holds the run (ordered on the one + // client stream → processed in order): the first parks, the second APPENDS + // (append is the default; the old slot_full reject is gone). + framesSent := make(chan error, 1) + go func() { + <-block.started + if err := stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Steer{Steer: &mecatlv1.Steer{Text: "parked", MessageId: "m-1"}}, + }); err != nil { + framesSent <- err + return + } + framesSent <- stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Steer{Steer: &mecatlv1.Steer{Text: "appended", MessageId: "m-2"}}, + }) + }() + + var events []*mecatlv1.Event + var acks []*mecatlv1.SteerAck + var echo *mecatlv1.SteerEcho + for { + resp, err := stream.Recv() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + t.Fatalf("Recv: %v", err) + } + ev := resp.GetEvent() + events = append(events, ev) + switch ev.GetType() { + case "steer.outcome": + acks = append(acks, ev.GetSteerOutcome()) + if len(acks) == 2 { + close(block.release) + } + case "steer": + echo = ev.GetSteer() + } + if ev.GetType() == "result" { + break + } + } + _ = stream.CloseSend() + if err := <-framesSent; err != nil { + t.Fatalf("Send frames: %v", err) + } + + if len(acks) != 2 { + t.Fatalf("steer acks = %d, want 2: %v", len(acks), typesOf(events)) + } + if got := acks[0].GetOutcome(); got != mecatlv1.SteerOutcome_STEER_OUTCOME_ACCEPTED { + t.Fatalf("first steer outcome = %v, want ACCEPTED", got) + } + if got := acks[1].GetOutcome(); got != mecatlv1.SteerOutcome_STEER_OUTCOME_APPENDED { + t.Fatalf("second steer outcome = %v, want APPENDED (a full slot APPENDS — the old reject is gone)", got) + } + if acks[1].GetMessageId() != "m-2" { + t.Fatalf("appended ack message_id = %q, want %q", acks[1].GetMessageId(), "m-2") + } + + // The two steers MERGE into one bundle and drain as the appended text + // ("parked\n\nappended"); the echo's message_id is the watermark (the LATEST + // contributing send, m-2). + if echo == nil { + t.Fatalf("no steer drain echo on the stream: %v", typesOf(events)) + } + if echo.GetText() != "parked\n\nappended" || echo.GetMessageId() != "m-2" { + t.Fatalf("steer echo = (%q, %q), want (%q, %q) — the appended bundle drains as one merged continuation", + echo.GetText(), echo.GetMessageId(), "parked\n\nappended", "m-2") + } +} + +// TestSteer_PromotedRelaySequential is R3-3: a steer arriving in the original +// run's terminate window promotes a follow-up run, and the Converse RPC relays +// the promoted run's FULL event stream + terminal EvResult + the +// steer.outcome{promoted:true} ack on the SAME stream BEFORE the RPC returns — +// the sequential active-run handoff (no orphaned relay goroutine left behind +// when Converse returns, no ack-after-close). +func TestSteer_PromotedRelaySequential(t *testing.T) { + llm := mockllm.New( + mockllm.TextTurn("first"), + mockllm.TextTurn("promoted follow-up"), + ) + svc := newSteerService(t, llm, nil) + client, stallRelease, cleanup := dialGRPCStall(t, svc, 2*time.Second) + defer cleanup() + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cs, err := client.CreateSession(ctx, &mecatlv1.CreateSessionRequest{Workspace: "/ws"}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + stream, err := client.Converse(ctx) + if err != nil { + t.Fatalf("Converse: %v", err) + } + if err := stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Prompt{Prompt: &mecatlv1.Prompt{SessionId: cs.GetSessionId(), Text: "go"}}, + }); err != nil { + t.Fatalf("Send prompt: %v", err) + } + + // Read until turn.end of the first run: the terminal result is stalled + // server-side, so the run is terminal-but-registered (the drain window) — + // the steer lands squarely in it. + for { + resp, err := stream.Recv() + if err != nil { + t.Fatalf("Recv pre-stall: %v", err) + } + if resp.GetEvent().GetType() == "turn.end" { + break + } + } + if err := stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Steer{Steer: &mecatlv1.Steer{Text: "late steer", MessageId: "m-late"}}, + }); err != nil { + t.Fatalf("Send steer: %v", err) + } + time.Sleep(100 * time.Millisecond) // let the steer land + the promote grace start + close(stallRelease) + + // Read the stream to EOF (the server returns AFTER everything below is + // relayed — the RPC returning ends the client stream). + var events []*mecatlv1.Event + var results []string + var promotedAck *mecatlv1.SteerAck + for { + resp, err := stream.Recv() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + t.Fatalf("Recv: %v", err) + } + ev := resp.GetEvent() + events = append(events, ev) + switch ev.GetType() { + case "result": + results = append(results, ev.GetResult().GetText()) + case "steer.outcome": + if ev.GetSteerOutcome().GetPromoted() { + promotedAck = ev.GetSteerOutcome() + } + } + } + _ = stream.CloseSend() + + // BOTH runs' terminal results made the SAME stream before the RPC returned: + // the original's (stalled, then drained) AND the promoted follow-up's. + if len(results) != 2 { + t.Fatalf("results on the stream = %d (%v), want 2 (original + promoted follow-up): %v", + len(results), results, typesOf(events)) + } + if len(results) == 2 && (results[0] != "first" || results[1] != "promoted follow-up") { + t.Fatalf("result texts = %v, want [first, promoted follow-up] in relay order", results) + } + + // The promoted ack rode the stream BEFORE the RPC returned (no + // ack-after-close), carrying the too_late+promoted outcome and echoing the + // steer's message_id. + if promotedAck == nil { + t.Fatalf("no steer.outcome{promoted:true} ack on the stream before it closed: %v", typesOf(events)) + } + if got := promotedAck.GetOutcome(); got != mecatlv1.SteerOutcome_STEER_OUTCOME_TOO_LATE { + t.Fatalf("promoted ack outcome = %v, want TOO_LATE", got) + } + if promotedAck.GetMessageId() != "m-late" { + t.Fatalf("promoted ack message_id = %q, want %q", promotedAck.GetMessageId(), "m-late") + } + + // Ordering: the promoted run's terminal EvResult precedes its ack (causal + // order), and both precede the stream close. + resultIdx, ackIdx := -1, -1 + for i, ev := range events { + if ev.GetType() == "result" && ev.GetResult().GetText() == "promoted follow-up" { + resultIdx = i + } + if ev.GetType() == "steer.outcome" && ev.GetSteerOutcome().GetPromoted() { + ackIdx = i + } + } + if resultIdx < 0 || ackIdx < 0 || ackIdx <= resultIdx { + t.Fatalf("promoted result at %d, promoted ack at %d — the ack must ride AFTER the promoted run drains", resultIdx, ackIdx) + } +} + +// TestSteer_ControlTargetsPromotedRun is R3-4: after a steer promotes a +// follow-up run, control frames on the stream target the PROMOTED (active) run, +// not the terminal original — here a Cancel frame cancels the promoted run +// mid-drive (the control target swapped atomically with the relay handoff). +func TestSteer_ControlTargetsPromotedRun(t *testing.T) { + block := &blockingTextTool{started: make(chan struct{}), release: make(chan struct{})} + llm := mockllm.New( + mockllm.TextTurn("first"), + // The promoted run's turn calls the blocking tool so the run parks + // mid-dispatch — the Cancel must hit THIS run. + mockllm.ToolCallTurn(call("c9", "Read", `{"path":"b.go"}`)), + ) + svc := newSteerService(t, llm, nil, block) + client, stallRelease, cleanup := dialGRPCStall(t, svc, 2*time.Second) + defer cleanup() + + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + + cs, err := client.CreateSession(ctx, &mecatlv1.CreateSessionRequest{Workspace: "/ws"}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + stream, err := client.Converse(ctx) + if err != nil { + t.Fatalf("Converse: %v", err) + } + if err := stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Prompt{Prompt: &mecatlv1.Prompt{SessionId: cs.GetSessionId(), Text: "go"}}, + }); err != nil { + t.Fatalf("Send prompt: %v", err) + } + + // Reach the first run's drain window. The result is stalled server-side, so + // the original remains terminal-but-registered while the steer starts its + // promoted handoff. + for { + resp, err := stream.Recv() + if err != nil { + t.Fatalf("Recv pre-stall: %v", err) + } + if resp.GetEvent().GetType() == "turn.end" { + break + } + } + // Capture the terminal original before sending the steer. The promoted run + // remains registered because its blocking tool holds it, so observing a + // different pointer proves Service.Steer has entered the handoff route. + original, ok := svc.LookupRun(session.SessionID(cs.GetSessionId())) + if !ok { + t.Fatal("original run is not registered in its terminal drain window") + } + if err := stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Steer{Steer: &mecatlv1.Steer{Text: "late steer"}}, + }); err != nil { + t.Fatalf("Send steer: %v", err) + } + deadline := time.Now().Add(5 * time.Second) + for { + if promoted, ok := svc.LookupRun(session.SessionID(cs.GetSessionId())); ok && promoted != original { + break + } + if time.Now().After(deadline) { + t.Fatal("promoted run was not registered after steer") + } + time.Sleep(10 * time.Millisecond) + } + close(stallRelease) // the stalled original run drains; the promoted run drives. + + // The promoted tool.call is relayed only after the handoff swapped the + // control target. Send Cancel at that event boundary; the blocking tool then + // keeps the promoted run alive until the Cancel takes effect. + cancelSent := false + var events []*mecatlv1.Event + var results []string + for { + resp, err := stream.Recv() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + t.Fatalf("Recv: %v", err) + } + ev := resp.GetEvent() + events = append(events, ev) + if ev.GetType() == "tool.call" && !cancelSent { + if err := stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Cancel{Cancel: &mecatlv1.Cancel{}}, + }); err != nil { + t.Fatalf("Send cancel: %v", err) + } + cancelSent = true + } + if ev.GetType() == "result" { + results = append(results, ev.GetResult().GetStop()) + } + } + _ = stream.CloseSend() + if !cancelSent { + t.Fatalf("promoted tool.call did not arrive before stream end: %v", typesOf(events)) + } + + // Two results: the original ended end_turn; the PROMOTED run ended + // CANCELLED — proof the Cancel frame hit the active (promoted) run, not the + // terminal original. + if len(results) != 2 { + t.Fatalf("results = %v, want 2 (original + promoted): %v", results, typesOf(events)) + } + if results[0] != "end_turn" { + t.Fatalf("original run stop = %q, want end_turn", results[0]) + } + if results[1] != string(session.StopCancelled) { + t.Fatalf("promoted run stop = %q, want %q — the Cancel frame must target the ACTIVE (promoted) run after the handoff", + results[1], session.StopCancelled) + } +} + +// TestSteer_RelaySendErrSingleOwner is R3-5: a client that DISCONNECTS while a +// promoted run is queued (the handoff is pending) causes no cross-goroutine +// runRelay.sendErr access — the single relay owner observes the send error, +// cancels the active run, and drains. Under -race any racy sendErr read/write +// between the original and promoted relays fails the test; the assertion is +// that the RPC returns an error (the recorded send failure) instead of +// wedging, and the promoted run is never orphaned mid-drive after the RPC +// returned (FinishRun ran before return). +func TestSteer_RelaySendErrSingleOwner(t *testing.T) { + llm := mockllm.New( + mockllm.TextTurn("first"), + mockllm.TextTurn("promoted follow-up"), + ) + svc := newSteerService(t, llm, nil) + client, stallRelease, cleanup := dialGRPCStall(t, svc, 2*time.Second) + defer cleanup() + + streamCtx, cancelStream := context.WithCancel(context.Background()) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cs, err := client.CreateSession(ctx, &mecatlv1.CreateSessionRequest{Workspace: "/ws"}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + stream, err := client.Converse(streamCtx) + if err != nil { + t.Fatalf("Converse: %v", err) + } + if err := stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Prompt{Prompt: &mecatlv1.Prompt{SessionId: cs.GetSessionId(), Text: "go"}}, + }); err != nil { + t.Fatalf("Send prompt: %v", err) + } + + // Reach the first run's drain window, steer into it, then kill the client + // mid-handoff: the original relay's stalled Send fails on the cancelled + // stream context, and the promoted run is queued for the handoff. + for { + resp, err := stream.Recv() + if err != nil { + t.Fatalf("Recv pre-stall: %v", err) + } + if resp.GetEvent().GetType() == "turn.end" { + break + } + } + if err := stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Steer{Steer: &mecatlv1.Steer{Text: "late steer"}}, + }); err != nil { + t.Fatalf("Send steer: %v", err) + } + time.Sleep(100 * time.Millisecond) + cancelStream() // the client is gone: every Send from here fails + close(stallRelease) // the stalled Send unblocks into the cancelled stream + + // The server-side RPC unwinds: it must not wedge (the relay cancels the + // active run and drains). Give the server a beat to finish, then the + // session must settle to a durable terminal state (no run left + // mid-registered — FinishRun ran before Converse returned, for BOTH runs). + deadline := time.Now().Add(5 * time.Second) + for time.Now().Before(deadline) { + if !svc.IsLive(session.SessionID(cs.GetSessionId())) { + break + } + time.Sleep(10 * time.Millisecond) + } + if svc.IsLive(session.SessionID(cs.GetSessionId())) { + t.Fatalf("a run is still registered after the client disconnected — the handoff orphaned a relay (FinishRun must run before Converse returns)") + } +} + +// steerOutcomeEnum guards the enum shape: APPENDED took the occupied-slot value +// (2); SUPERSEDED and SLOT_FULL are both REMOVED (append is the default, replace +// is explicit cancel-then-resend). +func TestSteer_ProtoEnumAppend(t *testing.T) { + if got := mecatlv1.SteerOutcome_STEER_OUTCOME_APPENDED.String(); got != "STEER_OUTCOME_APPENDED" { + t.Fatalf("enum value 2 = %q, want STEER_OUTCOME_APPENDED", got) + } + if mecatlv1.SteerOutcome_STEER_OUTCOME_APPENDED != 2 { + t.Fatalf("STEER_OUTCOME_APPENDED = %d, want 2 (the wire number is stable across the swap)", mecatlv1.SteerOutcome_STEER_OUTCOME_APPENDED) + } + for name, v := range mecatlv1.SteerOutcome_value { + if strings.Contains(name, "SUPERSEDED") || strings.Contains(name, "SLOT_FULL") { + t.Fatalf("the proto enum still carries %q=%d — supersede/slot_full were dropped in the round-3 append-default rework", name, v) + } + } +} + +// --- shared helpers --------------------------------------------------------- + +var _ = atomic.Int32{} // (kept: the stall stream in steer_repair_test.go owns the atomic) diff --git a/internal/adapter/server/steer_outcome_matrix_test.go b/internal/adapter/server/steer_outcome_matrix_test.go new file mode 100644 index 0000000000..edf50e0555 --- /dev/null +++ b/internal/adapter/server/steer_outcome_matrix_test.go @@ -0,0 +1,32 @@ +// Package server_test is the external matrix test for steerOutcomeToProto: a +// full-table assertion that every agent.SteerOutcome maps to a non-UNSPECIFIED +// proto value. UNSPECIFIED is never sent; anything mapping there would be a +// server-side enum drift. The default case is unreachable only because every +// agent value has an arm — this test is what keeps that true. +package server_test + +import ( + "testing" + + "github.com/stacklok/mecatl/engine/agent" + "github.com/stacklok/mecatl/internal/adapter/server" +) + +// TestSteer_OutcomeMatrix asserts every agent.SteerOutcome maps to a named, +// non-UNSPECIFIED proto value and back (the round-trip is the interesting +// bit — drift in either direction degrades to UNSPECIFIED silently). +func TestSteer_OutcomeMatrix(t *testing.T) { + all := []agent.SteerOutcome{ + agent.SteerAccepted, + agent.SteerAppended, + agent.SteerRetracted, + agent.SteerNonePending, + agent.SteerTooLate, + } + for _, o := range all { + got := server.SteerOutcomeToProtoForTest(o) + if got.String() == "STEER_OUTCOME_UNSPECIFIED" { + t.Fatalf("steerOutcomeToProto(%q) = UNSPECIFIED (a drift arm is missing)", o) + } + } +} diff --git a/internal/adapter/server/steer_repair_test.go b/internal/adapter/server/steer_repair_test.go new file mode 100644 index 0000000000..b969d1c7a1 --- /dev/null +++ b/internal/adapter/server/steer_repair_test.go @@ -0,0 +1,270 @@ +package server_test + +// Repair task 07 (panel-review ship-blockers, round 1): the wire-side steer +// regressions the panel found — +// +// - AC-R1: a gRPC Converse stream is NOT goroutine-safe, so EVERY Send on it +// is serialized behind one single-writer gate; a terminal-race steer during +// the original run's drain window spawns a promoted follow-up relay that +// must never Send concurrently with the original relay. +// - AC-R2: a steer arriving in the terminate window (the original run went +// terminal but is still REGISTERED draining) is promoted to a fresh +// follow-up run, never dropped with a bare too_late ack. +// - AC-R3: the grpc handler is a dumb frame→Service mapper; the steer / +// steer_cancel routing decision has ONE owner (Service.Steer / +// Service.CancelSteer) — never a direct run.EnqueueSteer / run.CancelSteer +// in the handler. + +import ( + "context" + "errors" + "io" + "net" + "os" + "strings" + "sync/atomic" + "testing" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/test/bufconn" + + 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" +) + +// dialGRPCStall is dialGRPC plus a stream interceptor that stalls (up to +// stallFor) at the FIRST terminal result on each Converse stream — the server +// side then holds the run terminal-but-registered deterministically, so a +// steer fired on the stall lands squarely in the drain window AC-R1 pins +// (no timing race, no poll loop). +func dialGRPCStall(t *testing.T, svc *server.Service, stallFor time.Duration) (mecatlv1.HarnessServiceClient, chan<- struct{}, func()) { + t.Helper() + release := make(chan struct{}) + var stalled atomic.Int32 + stall := func(srv any, ss grpc.ServerStream, _ *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + return handler(srv, &stallStream{ServerStream: ss, release: release, stalled: &stalled, stallFor: stallFor}) + } + lis := bufconn.Listen(1 << 20) + gs := grpc.NewServer(grpc.StreamInterceptor(stall)) + mecatlv1.RegisterHarnessServiceServer(gs, server.NewHarnessServer(svc)) + 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) + } + return mecatlv1.NewHarnessServiceClient(conn), release, func() { + _ = conn.Close() + gs.Stop() + _ = lis.Close() + } +} + +// stallStream blocks the terminal result's Send on a release gate (or a +// stallFor cap) so the run holds terminal-and-registered while the test steers. +type stallStream struct { + grpc.ServerStream + release <-chan struct{} + stalled *atomic.Int32 + stallFor time.Duration +} + +func (s *stallStream) SendMsg(m any) error { + if resp, ok := m.(*mecatlv1.ConverseResponse); ok && + resp.GetEvent().GetType() == "result" && + s.stalled.CompareAndSwap(0, 1) { + select { + case <-s.release: + case <-time.After(s.stallFor): + case <-s.Context().Done(): + } + } + return s.ServerStream.SendMsg(m) +} + +// TestSteer_NoConcurrentSendOnTerminalRace is AC-R1: a steer that lands while +// the original relay is parked holding the run terminal-and-registered (the +// stall gate) drives the promoted follow-up relay WHILE the original relay is +// still live — under -race any concurrent stream.Send between the two relays +// fails the test. The promoted run's events must ALSO arrive intact on the one +// stream (a single-writer race would corrupt or drop frames). +func TestSteer_NoConcurrentSendOnTerminalRace(t *testing.T) { + llm := mockllm.New( + mockllm.TextTurn("first"), + mockllm.TextTurn("second"), // the promoted follow-up turn + ) + svc := newSteerService(t, llm, nil) + client, stallRelease, cleanup := dialGRPCStall(t, svc, 2*time.Second) + defer cleanup() + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cs, err := client.CreateSession(ctx, &mecatlv1.CreateSessionRequest{Workspace: "/ws"}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + stream, err := client.Converse(ctx) + if err != nil { + t.Fatalf("Converse: %v", err) + } + if err := stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Prompt{Prompt: &mecatlv1.Prompt{SessionId: cs.GetSessionId(), Text: "go"}}, + }); err != nil { + t.Fatalf("Send prompt: %v", err) + } + + // Read until turn.end of the first run (the result is stalled server-side, + // so turn.end is the last event before the gate). The run is now + // terminal-but-registered with its relay PARKED mid-Send. + sawTurnEnd := false + for !sawTurnEnd { + resp, err := stream.Recv() + if err != nil { + t.Fatalf("Recv pre-stall: %v", err) + } + if resp.GetEvent().GetType() == "turn.end" { + sawTurnEnd = true + } + } + + // Fire the steer into the held drain window. The stall keeps the original + // run registered while the steer routes, so the promoted entry is genuinely + // inside the drain-grace wait; releasing the stall then drains the original + // run, and the race between the two relays plays out under -race. + if err := stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Steer{Steer: &mecatlv1.Steer{Text: "late steer"}}, + }); err != nil { + t.Fatalf("Send steer: %v", err) + } + // Release the stalled original relay shortly after the steer lands, so the + // promoted run starts while the original relay is still draining its tail — + // the concurrent-relay window -race must stay quiet across. + time.Sleep(100 * time.Millisecond) + close(stallRelease) + + // Read the original run's result (the stalled relay drains it after the + // release). The test's ASSERTION is the -race run itself: the promoted + // steer relay is genuinely in flight while the original relay drains, so a + // concurrent stream.Send between the two fails -race. The Converse RPC then + // returns (the original relay finished); the promoted relay may race it to + // close the stream, so the steer ack / follow-up result can legitimately + // ride after the stream closes on a fast machine — the ack contract is + // pinned by TestSteer_TerminateWindowPromotes / the round-trip tests, not + // here. + var events []*mecatlv1.Event + var results int + for results == 0 { + resp, err := stream.Recv() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + t.Fatalf("Recv: %v", err) + } + ev := resp.GetEvent() + events = append(events, ev) + if ev.GetType() == "result" { + results++ + } + } + _ = stream.CloseSend() + if results != 1 { + t.Fatalf("the original run's result never made the stream: %v", typesOf(events)) + } + // Sanity: the steer did land on a too_late route — the original run went + // terminal before it (its inbox closed). A promoted follow-up CAN still be + // driving past this point; give it a beat to finish so the test does not + // leak the drive goroutine (goleak). + time.Sleep(200 * time.Millisecond) +} + +// TestSteer_TerminateWindowPromotes is AC-R2: a steer arriving while the +// original run is TERMINAL-BUT-STILL-REGISTERED (its relay draining between +// closeSteer and FinishRun — the exact window the panel found dropping steers) +// is promoted to a fresh follow-up run, never dropped with a bare too_late +// ack. The test holds the run terminal-and-registered by NOT FinishRunning the +// drained run, then steers. +func TestSteer_TerminateWindowPromotes(t *testing.T) { + llm := mockllm.New( + mockllm.TextTurn("first"), + mockllm.TextTurn("follow-up"), + ) + svc := newSteerService(t, llm, nil) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + sess, err := svc.CreateSession(ctx, "/ws", session.ModeDefault, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + run, err := svc.StartRunContent(ctx, sess.ID, "hello", nil) + if err != nil { + t.Fatalf("StartRunContent: %v", err) + } + drainRun(t, run) // the run is now TERMINAL + // Deliberately do NOT svc.FinishRun(sess.ID, run) yet: the run stays + // REGISTERED, the exact terminate-window state (inbox closed, still live in + // the registry) that used to drop the steer. + if !svc.IsLive(sess.ID) { + t.Fatalf("precondition: the drained run must still be registered (the terminate window)") + } + + // The original relay deregisters a beat into the steer's drain-grace (the + // relay finishes mid-window), so the promoted entry observes the registry + // clearing inside the grace rather than at the lapse. + go func() { + time.Sleep(50 * time.Millisecond) + svc.FinishRun(sess.ID, run) + }() + outc, promoted, run2, err := svc.Steer(ctx, sess.ID, "steer in the drain window", "") + if err != nil { + t.Fatalf("Steer in the terminate window: %v (must promote, never drop)", err) + } + if outc != agent.SteerTooLate { + t.Fatalf("outcome = %q, want too_late", outc) + } + if !promoted { + t.Fatalf("terminate-window steer must be PROMOTED (promoted=false = the dropped-steer regression)") + } + if run2 == nil { + t.Fatalf("promoted steer returned a nil run (silently dropped)") + } + if stop := drainForResult(t, run2); stop != session.StopEndTurn { + t.Fatalf("promoted run stop = %q, want end_turn", stop) + } + svc.FinishRun(sess.ID, run2) + + got, err := svc.GetSession(ctx, sess.ID) + if err != nil { + t.Fatalf("GetSession: %v", err) + } + texts := steerUserTexts(got) + if len(texts) != 2 || texts[0] != "hello" || texts[1] != "steer in the drain window" { + t.Fatalf("user messages = %v, want [hello, steer in the drain window] (steer promoted, not dropped)", texts) + } +} + +// TestSteer_ServiceOwnsRouting is AC-R3: the grpc Converse handler is a dumb +// frame→Service mapper — it must route steer / steer_cancel through +// Service.Steer / Service.CancelSteer and never call run.EnqueueSteer / +// run.CancelSteer directly. A static scan pins the shape (the live behaviour +// is covered by the round-trip tests, which would hang with no Service route). +func TestSteer_ServiceOwnsRouting(t *testing.T) { + src, err := os.ReadFile("grpc.go") + if err != nil { + t.Fatalf("read grpc.go: %v", err) + } + for _, banned := range []string{"run.EnqueueSteer(", "run.CancelSteer("} { + if strings.Contains(string(src), banned) { + t.Fatalf("grpc.go must route steer through Service, but still calls %q directly", banned) + } + } +} diff --git a/internal/adapter/server/steer_test.go b/internal/adapter/server/steer_test.go new file mode 100644 index 0000000000..ac2e62e8ee --- /dev/null +++ b/internal/adapter/server/steer_test.go @@ -0,0 +1,360 @@ +package server_test + +// Scenario 3 of docs/acceptance/steer-while-running.md: the LOST TERMINAL RACE — +// a steer arrives for a session whose run is already terminal (the user's "still +// running" belief lagged the real state). The Service-level routing decision +// (Service.Steer) either enqueues the text to the LIVE run's steer inbox or, on a +// terminal race (no live run / the engine reports steer-too-late), promotes it +// into a fresh follow-up run through the EXISTING hardened run-entry funnel +// (StartRunContent → loadAndReopen → the lease + recover-if-terminal) — it never +// drops the text silently and never starts a run on an unrepaired terminal +// session. + +import ( + "context" + "encoding/json" + "errors" + "testing" + "time" + + "github.com/stacklok/mecatl/engine/adapter/memfs" + "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/governance" + "github.com/stacklok/mecatl/engine/session" + "github.com/stacklok/mecatl/engine/tool" + "github.com/stacklok/mecatl/internal/adapter/server" +) + +// blockingTextTool blocks holding the run mid-dispatch (genuinely LIVE) until +// its release channel is closed, then returns a fixed result. Unlike +// blockingTool it does NOT wait on ctx — the test releases it after the steer is +// enqueued so the steer drains at the next turn boundary on a still-live run. +type blockingTextTool struct { + started chan struct{} + release chan struct{} +} + +func (*blockingTextTool) Spec() tool.ToolSpec { + return tool.ToolSpec{Name: "Read", Description: "Read: test tool", Schema: json.RawMessage(`{"type":"object"}`)} +} +func (*blockingTextTool) ReadOnly() bool { return true } +func (b *blockingTextTool) Execute(ctx context.Context, in session.ToolCall, _ tool.Environment) (session.ToolResult, error) { + close(b.started) + select { + case <-b.release: + return session.NewToolResult(in.ID, "read ok"), nil + case <-ctx.Done(): // run cancelled while parked mid-dispatch (the cancelled case) + return session.ToolResult{}, ctx.Err() + } +} + +var _ tool.Tool = (*blockingTextTool)(nil) + +// newSteerService builds a Service whose engine arms the steer inbox +// (Deps.EnableSteer) and persists in-loop via Deps.Store, over the given mockllm +// script + tools + policy rules. memstore is shared between the Service and the +// engine so an in-loop Cancelled/Failed/Fail lands in the same store the +// run-entry funnel reads. +func newSteerService(t *testing.T, llm *mockllm.Provider, rules []governance.Rule, tools ...tool.Tool) *server.Service { + t.Helper() + cat := tool.NewCatalog() + for _, tl := range tools { + cat.MustRegister(tl) + } + if rules == nil { + rules = permpolicy.AllowAllFloorRules() + } + store := memstore.New() + engine := agent.NewEngine(agent.Deps{ + LLM: llm, + Catalog: cat, + Policy: permpolicy.NewPolicy(rules, nil), + Model: "test-model", + Store: store, + EnableSteer: true, + }) + svc, err := server.NewService(server.Config{ + Engine: engine, + Store: store, + Workspaces: func(root string) tool.Workspace { return memfs.NewWorkspace(root) }, + Now: func() time.Time { return time.Unix(0, 0) }, + }) + if err != nil { + t.Fatalf("new steer service: %v", err) + } + return svc +} + +// steerUserTexts returns the recorded genuine user-message texts, in order. +func steerUserTexts(sess *session.Session) []string { + var out []string + for _, m := range sess.Conversation.Messages { + if m.Role == session.RoleUser { + out = append(out, m.Text) + } + } + return out +} + +// drainForResult consumes a run's events and returns its terminal EvResult stop. +func drainForResult(t *testing.T, run *agent.Run) session.StopReason { + t.Helper() + var stop session.StopReason + for ev := range run.Events() { + if ev.Type == session.EvResult && ev.Result != nil { + stop = ev.Result.Stop + } + } + return stop +} + +func assertSteerState(t *testing.T, svc *server.Service, id session.SessionID, want session.State) { + t.Helper() + got, err := svc.GetSession(context.Background(), id) + if err != nil { + t.Fatalf("GetSession: %v", err) + } + if got.State != want { + t.Fatalf("precondition: session state = %q, want %q", got.State, want) + } +} + +// TestSteer_LiveRunEnqueues (AC3.1): a steer arriving on a genuinely LIVE run is +// enqueued to that run's inbox (the engine accepts/supersedes it) and the SAME +// run drains it at the next boundary — it is NOT promoted to a new run. +func TestSteer_LiveRunEnqueues(t *testing.T) { + block := &blockingTextTool{started: make(chan struct{}), release: make(chan struct{})} + // Turn 1: call the blocking tool (the run parks mid-dispatch, still live). + // Turn 2: the post-steer turn answers with text. A THIRD model call would only + // happen if a spurious promoted run also started (mockllm has no third scripted + // turn → an exhausted cursor drives the no-progress path, which would NOT end + // the run StopEndTurn — catching an accidental promotion as a wrong stop). + llm := mockllm.New( + mockllm.ToolCallTurn(session.NewToolCall("c1", "Read", json.RawMessage(`{"path":"a.go"}`))), + mockllm.TextTurn("done"), + ) + svc := newSteerService(t, llm, nil, block) + ctx := context.Background() + + sess, err := svc.CreateSession(ctx, "/ws", session.ModeDefault, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + run, err := svc.StartRunContent(ctx, sess.ID, "look at a.go", nil) + if err != nil { + t.Fatalf("StartRunContent: %v", err) + } + <-block.started // the run is genuinely mid-dispatch → live. + + outc, promoted, _, err := svc.Steer(ctx, sess.ID, "steer mid-flight", "") + if err != nil { + t.Fatalf("Steer on a live run: %v", err) + } + if promoted { + t.Fatalf("steer on a live run must NOT be promoted; got promoted=true") + } + if outc != agent.SteerAccepted && outc != agent.SteerAppended { + t.Fatalf("steer on a live run outcome = %q, want accepted/appended", outc) + } + + // Let the tool finish; the run continues and drains the steer at the next + // boundary (the Step 2a drain fires BEFORE BeginTurn of turn 2). + close(block.release) + + var sawSteerEcho bool + stop := session.StopReason("") + for ev := range run.Events() { + if ev.Type == session.EvSteer && ev.Steer != nil && ev.Steer.Text == "steer mid-flight" { + sawSteerEcho = true + } + if ev.Type == session.EvResult && ev.Result != nil { + stop = ev.Result.Stop + } + } + svc.FinishRun(sess.ID, run) + + if stop != session.StopEndTurn { + t.Fatalf("live run stop = %q, want end_turn (a spurious extra model call would change this)", stop) + } + if !sawSteerEcho { + t.Fatalf("live run never emitted EvSteer carrying the steer text (steer not drained in-run)") + } + got, err := svc.GetSession(ctx, sess.ID) + if err != nil { + t.Fatalf("GetSession: %v", err) + } + texts := steerUserTexts(got) + if len(texts) != 2 || texts[0] != "look at a.go" || texts[1] != "steer mid-flight" { + t.Fatalf("user messages = %v, want [look at a.go, steer mid-flight] (steer recorded into the SAME live run)", texts) + } +} + +// TestSteer_TerminalRacePromotes (AC3.2): a steer arriving AFTER the run went +// terminal is promoted to a fresh follow-up run that drives through the +// run-entry funnel — the text is NOT silently dropped and the promoted run +// records the steer as its user prompt. +func TestSteer_TerminalRacePromotes(t *testing.T) { + llm := mockllm.New(mockllm.TextTurn("first"), mockllm.TextTurn("second")) + svc := newSteerService(t, llm, nil) + ctx := context.Background() + + sess, err := svc.CreateSession(ctx, "/ws", session.ModeDefault, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + run, err := svc.StartRunContent(ctx, sess.ID, "hello", nil) + if err != nil { + t.Fatalf("StartRunContent #1: %v", err) + } + drainRun(t, run) + svc.FinishRun(sess.ID, run) + assertSteerState(t, svc, sess.ID, session.StateCompleted) + + // The user's "still running" belief lagged: no run is live now. + outc, promoted, run2, err := svc.Steer(ctx, sess.ID, "follow-up steer", "") + if err != nil { + t.Fatalf("Steer: %v", err) + } + if !promoted { + t.Fatalf("terminal steer must be promoted; got promoted=false (outcome=%q)", outc) + } + if outc != agent.SteerTooLate { + t.Fatalf("terminal-race steer outcome = %q, want too_late", outc) + } + // The promotion must actually register + drive a follow-up run, not drop. + if run2 == nil { + t.Fatalf("promoted steer returned a nil run (silently dropped)") + } + if got, ok := svc.LookupRun(sess.ID); !ok || got != run2 { + t.Fatalf("promoted run not the registered live run for the session") + } + if stop := drainForResult(t, run2); stop != session.StopEndTurn { + t.Fatalf("promoted run stop = %q, want end_turn", stop) + } + svc.FinishRun(sess.ID, run2) + + got, err := svc.GetSession(ctx, sess.ID) + if err != nil { + t.Fatalf("GetSession: %v", err) + } + texts := steerUserTexts(got) + if len(texts) != 2 || texts[0] != "hello" || texts[1] != "follow-up steer" { + t.Fatalf("user messages = %v, want [hello, follow-up steer] (steer promoted, not dropped)", texts) + } +} + +// TestSteer_PromotionUsesRunEntryFunnel (AC3.3): promotion reuses the run-entry +// funnel — reopen-if-completed / interrupt-if-cancelled / recover-if-failed — so +// a follow-up run NEVER starts on an unrepaired terminal session. Each terminal +// state is reached through the real Service/engine surface, then a steer is +// promoted; the promoted run must reach a clean terminal EvResult and record the +// steer (proving the terminal state was repaired to idle first — the wedge +// "RecordUserPrompt from " would fail the promotion instead). +func TestSteer_PromotionUsesRunEntryFunnel(t *testing.T) { + type fixture struct { + svc *server.Service + llm *mockllm.Provider + sess *session.Session + } + build := func(t *testing.T, turns []mockllm.Turn, tools ...tool.Tool) fixture { + t.Helper() + llm := mockllm.New(turns...) + svc := newSteerService(t, llm, nil, tools...) + sess, err := svc.CreateSession(context.Background(), "/ws", session.ModeDefault, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + return fixture{svc: svc, llm: llm, sess: sess} + } + // promote asserts the steer is promoted through the funnel: too_late + + // promoted, the follow-up run reaches a clean stop, and the steer text is + // recorded (never a run on an unrepaired terminal session, never a drop). + promote := func(t *testing.T, f fixture) { + t.Helper() + ctx := context.Background() + outc, promoted, run, err := f.svc.Steer(ctx, f.sess.ID, "promoted follow-up", "") + if err != nil { + t.Fatalf("Steer: %v", err) + } + if !promoted { + t.Fatalf("steer must be promoted (no live run); got promoted=false (outcome=%q)", outc) + } + if outc != agent.SteerTooLate { + t.Fatalf("outcome = %q, want too_late", outc) + } + if run == nil { + t.Fatalf("promotion returned no follow-up run") + } + if stop := drainForResult(t, run); stop != session.StopEndTurn { + t.Fatalf("promoted run stop = %q, want end_turn (an unrepaired terminal session would wedge before any model turn)", stop) + } + f.svc.FinishRun(f.sess.ID, run) + if f.llm.Calls() < 2 { + t.Fatalf("provider calls = %d, want >= 2 (the promoted run must drive a model turn; a run on an unrepaired terminal session never reaches the model)", f.llm.Calls()) + } + got, err := f.svc.GetSession(ctx, f.sess.ID) + if err != nil { + t.Fatalf("GetSession: %v", err) + } + var recorded bool + for _, txt := range steerUserTexts(got) { + if txt == "promoted follow-up" { + recorded = true + } + } + if !recorded { + t.Fatalf("promoted steer text not recorded; users=%v", steerUserTexts(got)) + } + } + + t.Run("completed", func(t *testing.T) { + f := build(t, []mockllm.Turn{mockllm.TextTurn("first"), mockllm.TextTurn("second")}) + run, err := f.svc.StartRunContent(context.Background(), f.sess.ID, "prompt", nil) + if err != nil { + t.Fatalf("drive completed: %v", err) + } + drainRun(t, run) + f.svc.FinishRun(f.sess.ID, run) + assertSteerState(t, f.svc, f.sess.ID, session.StateCompleted) + promote(t, f) // funnel must Reopen the completed session before driving. + }) + + t.Run("cancelled", func(t *testing.T) { + block := &blockingTextTool{started: make(chan struct{}), release: make(chan struct{})} + f := build(t, []mockllm.Turn{ + mockllm.ToolCallTurn(session.NewToolCall("c1", "Read", json.RawMessage(`{"path":"a.go"}`))), + mockllm.TextTurn("recovered"), + }, block) + run, err := f.svc.StartRunContent(context.Background(), f.sess.ID, "prompt", nil) + if err != nil { + t.Fatalf("drive cancelled: %v", err) + } + <-block.started + run.Cancel() + drainRun(t, run) + f.svc.FinishRun(f.sess.ID, run) + assertSteerState(t, f.svc, f.sess.ID, session.StateCancelled) + promote(t, f) // funnel must Interrupt (repair) the cancelled session. + }) + + t.Run("failed", func(t *testing.T) { + f := build(t, []mockllm.Turn{ + // A genuine mid-stream provider failure → session.Failed. + mockllm.ErrorTurn(errors.New("upstream 502: bad gateway")), + mockllm.TextTurn("recovered"), + }) + run, err := f.svc.StartRunContent(context.Background(), f.sess.ID, "prompt", nil) + if err != nil { + t.Fatalf("drive failed: %v", err) + } + if stop := drainForResult(t, run); stop != session.StopError { + t.Fatalf("drive failed: stop = %q, want error", stop) + } + f.svc.FinishRun(f.sess.ID, run) + assertSteerState(t, f.svc, f.sess.ID, session.StateFailed) + promote(t, f) // funnel must Recover the failed session before driving. + }) +} diff --git a/internal/adapter/server/steer_watermark_pin_test.go b/internal/adapter/server/steer_watermark_pin_test.go new file mode 100644 index 0000000000..6d4cf42a6a --- /dev/null +++ b/internal/adapter/server/steer_watermark_pin_test.go @@ -0,0 +1,82 @@ +package server_test + +import ( + "context" + "encoding/json" + "testing" + + "github.com/stacklok/mecatl/engine/adapter/mockllm" + "github.com/stacklok/mecatl/engine/agent" + "github.com/stacklok/mecatl/engine/session" +) + +// TestLookupSteerMessageIDExactUnderDuplicateTexts is the ADR-0228 invariant +// pin: two identical-text steers tracked back-to-back produce a watermark +// equal to the SECOND (tail) id, and the FIFO is consumed (a second lookup +// yields ""). A text-match correlation would return the first id and leave a +// residue — this test is the executable statement of "positional, not +// textual" that the ADR names. +func TestLookupSteerMessageIDExactUnderDuplicateTexts(t *testing.T) { + svc := newSteerService(t, nil, nil) + ctx := context.Background() + sess, err := svc.CreateSession(ctx, "/ws", session.ModeDefault, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + + svc.TrackSteerMessageIDForTest(sess.ID, "m-1") + svc.TrackSteerMessageIDForTest(sess.ID, "m-2") + + if got := svc.LookupSteerMessageID(sess.ID); got != "m-2" { + t.Fatalf("watermark = %q, want tail id %q (text-match would have returned %q)", got, "m-2", "m-1") + } + if got := svc.LookupSteerMessageID(sess.ID); got != "" { + t.Fatalf("second lookup = %q, want empty (the FIFO must be consumed after one drain)", got) + } +} + +// TestSteer_IngressRepairCannotBreakCorrelation pins the invariant that the +// engine's UTF-8 ingress repair (session.ToValidUTF8 on the steer text) never +// affects the wire-side message_id correlation — the id rides a separate +// channel (frame field → Service FIFO → echo) and the correlation is positional, +// not textual. A repaired-steer drain must still echo the tracked id +// (the genuine regression class the ADR-0222-style text-match design would +// have created): if a future repair widens to the id-mapping, this test fails. +func TestSteer_IngressRepairCannotBreakCorrelation(t *testing.T) { + block := &blockingTextTool{started: make(chan struct{}), release: make(chan struct{})} + svc := newSteerService(t, mockllm.New( + mockllm.ToolCallTurn(session.NewToolCall("c1", "Read", json.RawMessage(`{"path":"a.go"}`))), + mockllm.TextTurn("done"), + ), nil, block) + ctx := context.Background() + sess, err := svc.CreateSession(ctx, "/ws", session.ModeDefault, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + run, err := svc.StartRunContent(ctx, sess.ID, "look", nil) + if err != nil { + t.Fatalf("StartRunContent: %v", err) + } + <-block.started + + // The steer text carries invalid UTF-8; its id is tracked via the Service + // Steer path (text repaired at the engine inbox, id untracked by text). + invalid := string([]byte{0xe2, 0x28}) + outc, promoted, _, err := svc.Steer(ctx, sess.ID, invalid, "m-repair") + if err != nil || promoted { + t.Fatalf("Steer on a live run = %v / promoted=%v, want accepted/appended enqueued", err, promoted) + } + if outc != agent.SteerAccepted { + t.Fatalf("Steer outcome = %q, want accepted", outc) + } + // The correlation id rides the FIFO unharmed by the repaired text (the + // positional correlation never consults text). Assert directly, then let + // the run drain. + if got := svc.LookupSteerMessageID(sess.ID); got != "m-repair" { + t.Fatalf("tracked id in the FIFO = %q, want %q — repair must not strand the id", got, "m-repair") + } + close(block.release) + for range run.Events() { + } + svc.FinishRun(sess.ID, run) +} diff --git a/internal/adapter/server/steer_wire_test.go b/internal/adapter/server/steer_wire_test.go new file mode 100644 index 0000000000..e7b6d09894 --- /dev/null +++ b/internal/adapter/server/steer_wire_test.go @@ -0,0 +1,289 @@ +package server_test + +// Scenario 5 of docs/acceptance/steer-while-running.md: the WIRE surface — the +// steer rides the existing bidi Converse stream as new ConverseRequest oneof +// arms (steer / steer_cancel) alongside prompt / resume_approval / cancel / +// cancel_child; the server advertises the feature via ServerCapabilities.steer +// (the additive-grow discipline), computed ONCE in composition +// (Service.capabilities()); and the engine's EvSteer drain echo projects onto +// the same stream as the client-visible steer event. + +import ( + "context" + "errors" + "io" + "sync" + "testing" + "time" + + mecatlv1 "github.com/stacklok/mecatl/contracts/gen/go/mecatl/v1" + "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/server" +) + +// TestSteer_ConverseFrameRoundTrip is AC5.1: a client sends a `steer` frame +// mid-run on the Converse stream and observes (a) the authoritative +// accepted-outcome ack, (b) the EvSteer drain echo carrying the committed text, +// and (c) the injected message reaching the model as an ordinary user turn on +// the following turn — all on the SAME stream. +// +// The blockingTextTool parks the run mid-dispatch (genuinely LIVE); the client +// sends the steer frame while the tool holds the run, then releases it — the +// steer drains at the upcoming turn boundary on the still-live run. +func TestSteer_ConverseFrameRoundTrip(t *testing.T) { + block := &blockingTextTool{started: make(chan struct{}), release: make(chan struct{})} + var reqsMu sync.Mutex + var reqs []port.LLMRequest + llm := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(func(r port.LLMRequest) { + reqsMu.Lock() + reqs = append(reqs, r) + reqsMu.Unlock() + })}, + mockllm.ToolCallTurn(call("c1", "Read", `{"path":"a.go"}`)), + mockllm.TextTurn("turn two done"), + ) + svc := newSteerService(t, llm, nil, block) + client, cleanup := dialGRPC(t, svc) + defer cleanup() + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cs, err := client.CreateSession(ctx, &mecatlv1.CreateSessionRequest{Workspace: "/ws"}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + + stream, err := client.Converse(ctx) + if err != nil { + t.Fatalf("Converse: %v", err) + } + if err := stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Prompt{Prompt: &mecatlv1.Prompt{SessionId: cs.GetSessionId(), Text: "look"}}, + }); err != nil { + t.Fatalf("Send prompt: %v", err) + } + + // The tool-call event means the run reached dispatch; wait until the tool + // is genuinely holding the run (the enqueue hits a live inbox), then steer. + steerSent := make(chan error, 1) + go func() { + <-block.started + steerSent <- stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Steer{Steer: &mecatlv1.Steer{Text: "also check b.go"}}, + }) + }() + + var events []*mecatlv1.Event + var steerAck *mecatlv1.Event + for { + resp, err := stream.Recv() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + t.Fatalf("Recv: %v", err) + } + ev := resp.GetEvent() + events = append(events, ev) + if ev.GetType() == "steer.outcome" { + steerAck = ev + // The steer is parked; release the tool so the run drains it at the + // upcoming boundary and drives the second turn. + close(block.release) + } + if ev.GetType() == "result" { + break + } + } + _ = stream.CloseSend() + if err := <-steerSent; err != nil { + t.Fatalf("Send steer: %v", err) + } + + // (a) The authoritative ack: the steer was ACCEPTED into the live inbox. + if steerAck == nil { + t.Fatalf("no steer.outcome ack on the stream: %v", typesOf(events)) + } + if got := steerAck.GetSteerOutcome().GetOutcome(); got != mecatlv1.SteerOutcome_STEER_OUTCOME_ACCEPTED { + t.Fatalf("steer outcome = %v, want ACCEPTED", got) + } + + // (b) The EvSteer echo carries the COMMITTED text (client-visible). + var steerEv *mecatlv1.Event + for _, e := range events { + if e.GetType() == "steer" { + steerEv = e + break + } + } + if steerEv == nil { + t.Fatalf("no steer echo event on the stream: %v", typesOf(events)) + } + if got := steerEv.GetSteer().GetText(); got != "also check b.go" { + t.Fatalf("steer echo text = %q, want %q", got, "also check b.go") + } + + // (c) The injected message reached the model as an ordinary user turn on + // the following turn (recorded == streamed == model-view). + reqsMu.Lock() + defer reqsMu.Unlock() + if len(reqs) != 2 { + t.Fatalf("provider calls = %d, want 2 (the steer feeds the following turn)", len(reqs)) + } + msgs := reqs[1].Messages + final := msgs[len(msgs)-1] + if final.Role != session.RoleUser || final.Text != "also check b.go" { + t.Fatalf("final replayed message = (%q, %q), want (user, %q)", final.Role, final.Text, "also check b.go") + } + + res := lastResult(t, events) + if res.GetStop() != "end_turn" || res.GetText() != "turn two done" { + t.Fatalf("result = %+v", res) + } +} + +// TestSteer_ConverseCancelRetracts drives the steer_cancel arm over the wire: +// a pending steer is retracted, the ack reports RETRACTED, and no EvSteer echo +// ever lands (the run drains nothing for it). +func TestSteer_ConverseCancelRetracts(t *testing.T) { + block := &blockingTextTool{started: make(chan struct{}), release: make(chan struct{})} + llm := mockllm.New( + mockllm.ToolCallTurn(call("c1", "Read", `{"path":"a.go"}`)), + mockllm.TextTurn("done"), + ) + svc := newSteerService(t, llm, nil, block) + client, cleanup := dialGRPC(t, svc) + defer cleanup() + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cs, err := client.CreateSession(ctx, &mecatlv1.CreateSessionRequest{Workspace: "/ws"}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + stream, err := client.Converse(ctx) + if err != nil { + t.Fatalf("Converse: %v", err) + } + if err := stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Prompt{Prompt: &mecatlv1.Prompt{SessionId: cs.GetSessionId(), Text: "look"}}, + }); err != nil { + t.Fatalf("Send prompt: %v", err) + } + + // Steer, then immediately steer_cancel, both while the tool holds the run + // (ordered on the same client stream → processed in order). + framesSent := make(chan error, 1) + go func() { + <-block.started + if err := stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_Steer{Steer: &mecatlv1.Steer{Text: "scratch that"}}, + }); err != nil { + framesSent <- err + return + } + framesSent <- stream.Send(&mecatlv1.ConverseRequest{ + Kind: &mecatlv1.ConverseRequest_SteerCancel{SteerCancel: &mecatlv1.SteerCancel{}}, + }) + }() + + var events []*mecatlv1.Event + var outcomes []mecatlv1.SteerOutcome + for { + resp, err := stream.Recv() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + t.Fatalf("Recv: %v", err) + } + ev := resp.GetEvent() + events = append(events, ev) + if ev.GetType() == "steer.outcome" { + outcomes = append(outcomes, ev.GetSteerOutcome().GetOutcome()) + if len(outcomes) == 2 { + close(block.release) // both acks in: let the run finish + } + } + if ev.GetType() == "result" { + break + } + } + _ = stream.CloseSend() + if err := <-framesSent; err != nil { + t.Fatalf("Send frames: %v", err) + } + + // The accepted ack, then the retracted ack, in order. + if len(outcomes) != 2 { + t.Fatalf("steer outcomes = %v, want [ACCEPTED RETRACTED]", outcomes) + } + if outcomes[0] != mecatlv1.SteerOutcome_STEER_OUTCOME_ACCEPTED || + outcomes[1] != mecatlv1.SteerOutcome_STEER_OUTCOME_RETRACTED { + t.Fatalf("steer outcomes = %v, want [ACCEPTED RETRACTED]", outcomes) + } + if hasType(events, "steer") { + t.Fatalf("a retracted steer must never drain/echo: %v", typesOf(events)) + } +} + +// TestSteer_CapabilityAdvertised is AC5.2 half one: ServerCapabilities.steer is +// true when the engine's steer knob is armed, false when it is not — read over +// the gRPC CreateSession echo, the same surface every client reads. An old +// server (no field) reads as false by the proto3 default. +func TestSteer_CapabilityAdvertised(t *testing.T) { + on := capsFromCreate(t, newSteerService(t, mockllm.New(mockllm.TextTurn("x")), nil)) + if !on.GetSteer() { + t.Fatalf("steer cap = false, want true (EnableSteer armed)") + } + + // Steer knob OFF: the bit reads false (newSteerService always arms it, so + // build a plain service here — EnableSteer unset is the byte-identical + // no-steer posture). + off := capsFromCreate(t, newService(t, mockllm.New(mockllm.TextTurn("x")), allowRules())) + if off.GetSteer() { + t.Fatalf("steer cap = true, want false (EnableSteer off)") + } +} + +// TestSteer_CapabilitySingleSource is AC5.2 half two: the SAME computed bit +// reaches every sink that surfaces it — the CreateSession echo AND the Session +// snapshot re-hydration path (GetSession) — because both read the ONE +// Service.capabilities() value; nothing recomputes per sink. +func TestSteer_CapabilitySingleSource(t *testing.T) { + svcs := map[string]*server.Service{ + "enabled": newSteerService(t, mockllm.New(mockllm.TextTurn("x")), nil), + "disabled": newService(t, mockllm.New(mockllm.TextTurn("x")), allowRules()), + } + for name, svc := range svcs { + t.Run(name, func(t *testing.T) { + client, cleanup := dialGRPC(t, svc) + defer cleanup() + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + cs, err := client.CreateSession(ctx, &mecatlv1.CreateSessionRequest{Workspace: "/ws"}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + createCaps := cs.GetCapabilities().GetSteer() + + // The re-hydration sink: GetSession's Session snapshot carries the + // SAME capabilities value CreateSession echoed. + gs, err := client.GetSession(ctx, &mecatlv1.GetSessionRequest{SessionId: cs.GetSessionId()}) + if err != nil { + t.Fatalf("GetSession: %v", err) + } + snapCaps := gs.GetSession().GetCapabilities().GetSteer() + + if snapCaps != createCaps { + t.Fatalf("GetSession steer cap = %v, CreateSession said %v — the bit must be ONE composition-computed value, never recomputed per sink", snapCaps, createCaps) + } + }) + } +} diff --git a/internal/app/build.go b/internal/app/build.go index 4b01075624..3ac232ca39 100644 --- a/internal/app/build.go +++ b/internal/app/build.go @@ -753,6 +753,26 @@ type Config struct { // ErrTeamsDisabled. EnableTeams bool + // DisableSteer turns OFF the mid-run steer inbox (steer-while-running, issue + // #512) — an OPT-OUT of a DEFAULT-ON knob, mirroring NoBash/WebSearchOff (the + // zero value false = steer ON, so every existing hand-built Config / test is + // byte-identical and steer is armed by default). Threaded through + // engineDepsForProvider into agent.Deps.EnableSteer (true = armed) and reflected + // — via the SAME wired engine's Engine.SteerEnabled() — in + // ServerCapabilities.steer, so the capability advertisement can never claim a + // path the engine did not arm. The CLI surface is --no-steer (mecated + + // mecatui); the operator-tier settings.yaml `steer: false` scalar (user-global + // + CLI tiers ONLY — a project-tier key is WARN-ignored by permconfig, the + // same operator-only discipline as posture:) folds in via foldOperatorSteer + // (CLI out-ranks YAML). It is POSTURE-INDEPENDENT: the ladder does not derive + // it at any tier (a mid-run operator instruction is not an automation grant). + DisableSteer bool + // DisableSteerFlagSet records whether the operator passed an explicit --no-steer + // flag. When true, foldOperatorSteer leaves the operator-YAML steer: value alone + // (CLI out-ranks YAML, mirroring PostureFlagSet/ReasoningEffortFlagSet). Set by + // the cmd mains alongside DisableSteer. + DisableSteerFlagSet bool + // MCP: static servers, the resource meta-tools toggle, the prompt-expander // toggle, and the live ToolHive workload source. MCPServers []mcp.ServerConfig @@ -996,6 +1016,14 @@ type Config struct { // engine lifecycle on a fully built provider path. Production leaves it nil, // which preserves the inert hookexec.New(nil) default. hookRunner port.HookRunner + // extraCoreTools is the composition-only test seam (mirroring + // providerConstructor/envDetector) for registering ADDITIONAL core tools into + // the shared catalog on top of the production set — a test parks a run + // mid-dispatch with a blocking tool so a mid-run seam (steer-while-running) + // is genuinely LIVE when the test drives it. Nil in production (the + // production core set is untouched). Unexported: an internal composition + // detail, not an operator knob. + extraCoreTools []tool.Tool // toolhiveConfigPath is the composition-only test seam for the ToolHive // config-file path (mirroring envDetector/liveModelHTTPClient): "" @@ -1265,6 +1293,7 @@ func Build(ctx context.Context, cfg Config) (*Built, error) { cfg = foldOperatorPosture(cfg) cfg = foldOperatorReasoningEffort(cfg) cfg = foldOperatorPlanModeAutoApprove(cfg) + cfg = foldOperatorSteer(cfg) cfg.Posture = resolvePosture(cfg, postureNoCeiling) cfg = applyPosture(cfg) // AUTHORITATIVE root/no-sandbox refusal: applied HERE, after the full posture fold, @@ -3695,6 +3724,12 @@ func engineDepsForProvider( // plan-approval ask (PresentPlan) even when headless so the Service can // auto-resolve it. Operator-tier only, DEFAULT off. PlanModeAutoApprove: cfg.PlanModeAutoApprove, + // Steer (steer-while-running, issue #512): arm the mid-run steer inbox. + // DEFAULT ON — the Config knob is the opt-OUT (DisableSteer), so true here + // unless the operator disabled it. ServerCapabilities.steer reads the SAME + // wired value back via Engine.SteerEnabled(), so the advertisement and the + // inbox can never disagree. + EnableSteer: !cfg.DisableSteer, } } @@ -4471,6 +4506,12 @@ func registerCoreTools(cfg Config, cat *tool.Catalog, log, noFS bool, searchProv cat.MustRegister(t) } cat.MustRegister(tools.NewWebSearchTool(searchProvider)) + // Test seam: register any ADDITIONAL core tools a test injected (nil in + // production — the production core set above is untouched). Appended AFTER the + // production set so an injected tool can shadow nothing and adds only itself. + for _, t := range cfg.extraCoreTools { + cat.MustRegister(t) + } if runner := buildCommandRunner(cfg); runner != nil { // The AGENT-loop Bash tool (not the fstools one): foreground byte-identical, // plus the `background: true` detach over the run's child registry. Its @@ -7377,6 +7418,32 @@ func foldOperatorPlanModeAutoApprove(cfg Config) Config { return cfg } +// foldOperatorSteer merges the OPERATOR-TIER `steer:` YAML scalar (read by the +// permconfig resolver from the user-global + CLI tiers ONLY — never the project +// file, which is IGNORED with a WARN, the same operator-only discipline as +// posture:) onto cfg.DisableSteer. The YAML `steer: false` maps to DisableSteer=true +// (the knob is the opt-OUT of the default-ON steer inbox). A CLI --no-steer +// (cfg.DisableSteerFlagSet) OUT-RANKS the YAML value (mirroring +// foldOperatorPosture/foldOperatorReasoningEffort). It is a no-op when no +// operator-tier steer: key was configured, and a YAML `steer: true` cannot +// RE-ENABLE steer over an explicit --no-steer (CLI wins). cfg is taken and +// returned by value (issue #512). +func foldOperatorSteer(cfg Config) Config { + if cfg.DisableSteerFlagSet { + return cfg // CLI wins; YAML cannot override an explicit --no-steer. + } + res, ok := cfg.permResolver.(*permconfig.Resolver) + if !ok || res == nil { + return cfg + } + yamlSteer, present := res.OperatorSteer() + if !present { + return cfg + } + cfg.DisableSteer = !yamlSteer + return cfg +} + // foldOperatorOpenRouter resolves the OPERATOR-TIER openrouter: block (read by the // permconfig resolver from the user-global + CLI tiers ONLY — a project-tier block // is WARN-ignored) into cfg.openRouterRoutes (issue #480). It validates each diff --git a/internal/app/steer_composition_test.go b/internal/app/steer_composition_test.go new file mode 100644 index 0000000000..5aec6dc28d --- /dev/null +++ b/internal/app/steer_composition_test.go @@ -0,0 +1,328 @@ +package app + +import ( + "context" + "encoding/json" + "os" + "path/filepath" + "strings" + "sync" + "testing" + + 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/port" + "github.com/stacklok/mecatl/engine/session" + "github.com/stacklok/mecatl/engine/tool" + "github.com/stacklok/mecatl/internal/adapter/osfs" + "github.com/stacklok/mecatl/internal/adapter/server" +) + +// Scenario 6 of docs/acceptance/steer-while-running.md: the composition gate. +// The steer inbox is armed by a DEFAULT-ON composition knob (Config.DisableSteer +// is the opt-OUT), threaded through engineDepsForProvider into agent.Deps.EnableSteer +// and reflected — via the SAME wired engine's Engine.SteerEnabled() — in +// ServerCapabilities.steer (task 05 reads the one composition-computed bit). + +// blockingSteerTool parks a run mid-dispatch (genuinely LIVE) until its release +// channel is closed, then returns a fixed result. It lets a composition e2e drive a +// steer while the run is provably in-flight (the enqueue hits a live inbox). +type blockingSteerTool struct { + started chan struct{} + release chan struct{} +} + +func (*blockingSteerTool) Spec() tool.ToolSpec { + return tool.ToolSpec{Name: "SteerBlock", Description: "steer test blocking tool", Schema: json.RawMessage(`{"type":"object"}`)} +} +func (*blockingSteerTool) ReadOnly() bool { return true } +func (b *blockingSteerTool) Execute(ctx context.Context, in session.ToolCall, _ tool.Environment) (session.ToolResult, error) { + close(b.started) + select { + case <-b.release: + return session.NewToolResult(in.ID, "read ok"), nil + case <-ctx.Done(): + return session.ToolResult{}, ctx.Err() + } +} + +var _ tool.Tool = (*blockingSteerTool)(nil) + +// writeOperatorSteerFile writes an operator-tier settings.yaml carrying `steer: +// ` to a temp file and returns its path, for use as a +// Config.PermissionConfigs entry (the explicit CLI tier — an OPERATOR tier, so the +// steer: key is honoured). Mirrors writeOperatorPostureFile. +func writeOperatorSteerFile(t *testing.T, value string) string { + t.Helper() + dir := t.TempDir() + path := filepath.Join(dir, "operator.yaml") + if err := os.WriteFile(path, []byte("steer: "+value+"\n"), 0o600); err != nil { + t.Fatalf("write operator steer file: %v", err) + } + return path +} + +// steerCapsFromBuild creates a session against the built Service via the gRPC +// CreateSession handler and returns the echoed ServerCapabilities — the SAME +// projection a real client reads (mirrors postureEchoFromBuild). +func steerCapsFromBuild(t *testing.T, built *Built) *mecatlv1.ServerCapabilities { + t.Helper() + resp, err := server.NewHarnessServer(built.Service).CreateSession(context.Background(), + &mecatlv1.CreateSessionRequest{Workspace: t.TempDir()}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + return resp.GetCapabilities() +} + +// TestSteer_EnabledByDefaultEndToEnd is AC6.1: with steer enabled (the DEFAULT — +// no Config knob set), (a) the capability is advertised on the CreateSession echo, +// and (b) a mid-run input routed through Service.Steer reaches the model on the +// SAME run (no separate follow-up run): exactly two provider calls happen, the +// second's replay ending on the steered user message. +// +// The blockingSteerTool parks the run mid-dispatch (genuinely LIVE); the steer is +// enqueued while the tool holds the run, then the tool is released so the run +// drains the steer at the upcoming turn boundary and drives the second turn. +func TestSteer_EnabledByDefaultEndToEnd(t *testing.T) { + ctx := context.Background() + block := &blockingSteerTool{started: make(chan struct{}), release: make(chan struct{})} + var reqsMu sync.Mutex + var reqs []port.LLMRequest + llm := mockllm.NewWith( + []mockllm.Option{mockllm.WithRequestObserver(func(r port.LLMRequest) { + reqsMu.Lock() + reqs = append(reqs, r) + reqsMu.Unlock() + })}, + mockllm.ToolCallTurn(session.NewToolCall("c1", "SteerBlock", json.RawMessage(`{"path":"a.go"}`))), + mockllm.TextTurn("turn two done"), + ) + // No DisableSteer set: the DEFAULT is ON. AllowAllTools so the test tool's + // 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 := Build(ctx, Config{ + Workspace: t.TempDir(), + Model: "mock", + NoSoul: true, + MockProvider: llm, + AllowAllTools: true, + extraCoreTools: []tool.Tool{block}, + envDetector: fakeEnv(map[string]string{"OPENAI_API_KEY": "sk-openai"}), + liveModelHTTPClient: offlineHTTPClient(), + }) + if err != nil { + t.Fatalf("Build: %v", err) + } + defer built.Close() + + // (a) The capability is advertised on the CreateSession echo. + if !steerCapsFromBuild(t, built).GetSteer() { + t.Fatal("steer capability = false, want true (DEFAULT ON)") + } + + sess, err := built.Service.CreateSession(ctx, t.TempDir(), session.ModeDefault, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + run, err := built.Service.StartRun(ctx, sess.ID, "look") + if err != nil { + t.Fatalf("StartRun: %v", err) + } + + // Steer while the run is genuinely LIVE (parked mid-dispatch), then release. + steered := make(chan agent.SteerOutcome, 1) + go func() { + <-block.started + outcome, promoted, promotedRun, err := built.Service.Steer(ctx, sess.ID, "also check b.go", "") + if err != nil { + t.Errorf("Steer: %v", err) + } + if promoted || promotedRun != nil { + t.Errorf("a live-run steer must ENQUEUE, not promote (promoted=%v)", promoted) + } + steered <- outcome + close(block.release) + }() + + var sawSteerEcho bool + var stop session.StopReason + for ev := range run.Events() { + if ev.Type == session.EvSteer && ev.Steer != nil && ev.Steer.Text == "also check b.go" { + sawSteerEcho = true + } + if ev.Type == session.EvResult && ev.Result != nil { + stop = ev.Result.Stop + } + } + if got := <-steered; got != agent.SteerAccepted { + t.Fatalf("steer outcome = %q, want %q", got, agent.SteerAccepted) + } + if !sawSteerEcho { + t.Fatal("no EvSteer echo carrying the committed text on the run's events") + } + if stop != session.StopEndTurn { + t.Fatalf("stop = %q, want %q (the steered run completes the second turn)", stop, session.StopEndTurn) + } + + // (b) The input reached the model WITHOUT a separate follow-up run: exactly + // two provider calls, the second's replay ending on the steered user message. + reqsMu.Lock() + defer reqsMu.Unlock() + if len(reqs) != 2 { + t.Fatalf("provider calls = %d, want 2 (the steer feeds the SAME run's following turn)", len(reqs)) + } + msgs := reqs[1].Messages + final := msgs[len(msgs)-1] + if final.Role != session.RoleUser || final.Text != "also check b.go" { + t.Fatalf("final replayed message = (%q, %q), want (user, %q)", final.Role, final.Text, "also check b.go") + } +} + +// TestSteer_DisabledCompositionInert covers the composition half of AC6.2: with +// Config.DisableSteer set, the engine inbox is INERT — the capability echo reads +// false and a mid-run Service.Steer reports too_late + promotes to a fresh +// follow-up run (the Service's documented never-drop contract) instead of +// draining into the live run. +// +// (The TUI half of AC6.2 — the byte-identical client-side merge-queue fallback — +// is TestSteer_DisabledFallsBackToLocalQueue in cmd/mecatui/ui.) +func TestSteer_DisabledCompositionInert(t *testing.T) { + ctx := context.Background() + // Two text turns: one for the initial run, one for the promoted follow-up run + // (a single scripted turn would exhaust the cursor on the promote and drive the + // no-progress path instead of a clean end_turn). + llm := mockllm.New(mockllm.TextTurn("done"), mockllm.TextTurn("aftermath done")) + built, err := Build(ctx, Config{ + Workspace: t.TempDir(), + Model: "mock", + NoSoul: true, + MockProvider: llm, + DisableSteer: true, // the opt-out + envDetector: fakeEnv(map[string]string{"OPENAI_API_KEY": "sk-openai"}), + liveModelHTTPClient: offlineHTTPClient(), + }) + if err != nil { + t.Fatalf("Build: %v", err) + } + defer built.Close() + + if steerCapsFromBuild(t, built).GetSteer() { + t.Fatal("steer capability = true, want false (DisableSteer)") + } + + sess, err := built.Service.CreateSession(ctx, t.TempDir(), session.ModeDefault, session.Limits{}) + if err != nil { + t.Fatalf("CreateSession: %v", err) + } + run, err := built.Service.StartRun(ctx, sess.ID, "go") + if err != nil { + t.Fatalf("StartRun: %v", err) + } + for range run.Events() { // drain to terminal + } + + // The inbox is inert: a steer for the (now-terminal) session reports too_late + // and PROMOTES to a follow-up run through the run-entry funnel — never silently + // dropped, never drained into the finished run. + outcome, promoted, promotedRun, err := built.Service.Steer(ctx, sess.ID, "aftermath", "") + if err != nil { + t.Fatalf("Steer: %v", err) + } + if outcome != agent.SteerTooLate || !promoted || promotedRun == nil { + t.Fatalf("disabled steer must report too_late + promote; got outcome=%q promoted=%v run=%v", outcome, promoted, promotedRun) + } + var stop session.StopReason + for ev := range promotedRun.Events() { + if ev.Type == session.EvSteer { + t.Error("a disabled steer inbox must never emit EvSteer") + } + if ev.Type == session.EvResult && ev.Result != nil { + stop = ev.Result.Stop + } + } + built.Service.FinishRun(sess.ID, promotedRun) + if stop != session.StopEndTurn { + t.Fatalf("promoted follow-up stop = %q, want %q", stop, session.StopEndTurn) + } +} + +// TestSteer_DisableViaOperatorYAML proves the operator-tier `steer: false` +// settings.yaml key (user-global + CLI tiers ONLY — never the project tier) +// composes to the inbox being off: the capability echo reads false. Mirrors the +// operator-tier posture/reasoning-effort fold discipline. +func TestSteer_DisableViaOperatorYAML(t *testing.T) { + ctx := context.Background() + built, err := Build(ctx, Config{ + Workspace: t.TempDir(), + Model: "mock", + UseMock: true, + NoSoul: true, + PermissionConfigs: []string{writeOperatorSteerFile(t, "false")}, + envDetector: fakeEnv(map[string]string{"OPENAI_API_KEY": "sk-openai"}), + liveModelHTTPClient: offlineHTTPClient(), + }) + if err != nil { + t.Fatalf("Build: %v", err) + } + defer built.Close() + + if steerCapsFromBuild(t, built).GetSteer() { + t.Fatal("steer capability = true, want false (operator-tier steer: false)") + } +} + +// TestSteer_ProjectYAMLIgnored proves a PROJECT-tier `steer: false` key is +// IGNORED with a WARN (operator-tier only, mirroring posture): the capability +// stays ON (the default) — a project repo cannot flip the harness's operator +// surface either way. +func TestSteer_ProjectYAMLIgnored(t *testing.T) { + ctx := context.Background() + permEnv := isolatedPermConfigEnv(t) + withTrustEnv(t, *permEnv) + ws := t.TempDir() + mkdirProjectSettings(t, ws, "steer: false\n") + diag := slogdiagBuffer(t) + built, err := Build(ctx, Config{ + Workspace: ws, + Model: "mock", + UseMock: true, + NoSoul: true, + PermissionsConventional: true, // discover the project file (and ignore its steer:) + TrustProject: true, // even trusted, the project tier cannot set steer + permConfigEnv: permEnv, + Diagnostics: diag.diag, + envDetector: fakeEnv(map[string]string{"OPENAI_API_KEY": "sk-openai"}), + liveModelHTTPClient: offlineHTTPClient(), + }) + if err != nil { + t.Fatalf("Build: %v", err) + } + defer built.Close() + + if !steerCapsFromBuild(t, built).GetSteer() { + t.Fatal("a PROJECT-tier steer: false must be IGNORED; capability = false, want true (default ON)") + } + + // The WARN fires lazily: loadProjectRules runs per root on the FIRST Resolve + // against it, not at Build. Drive a Resolve through the real permconfig resolver + // (constructed by buildPermResolver over the SAME isolated env) so the + // ignore-with-WARN lands. The capability assertion above is the behavioral proof + // the key was ignored; this is the loud-WARN proof. + resolver := buildPermResolver(Config{ + Workspace: ws, + PermissionsConventional: true, + TrustProject: true, + permConfigEnv: permEnv, + Diagnostics: diag.diag, + }) + pws, err := osfs.NewWorkspace(ws) + if err != nil { + t.Fatalf("osfs.NewWorkspace(%q): %v", ws, err) + } + _ = resolver.Resolve(ctx, pws) + if !strings.Contains(diag.String(), "steer") { + t.Fatalf("the ignored project-tier steer: key must WARN; log:\n%s", diag.String()) + } +} diff --git a/internal/configgen/build.go b/internal/configgen/build.go index d4b92a5d09..979155c7f3 100644 --- a/internal/configgen/build.go +++ b/internal/configgen/build.go @@ -28,6 +28,7 @@ func BuildModel(docs Docs) *Model { learningSubtree(docs), retentionSubtree(docs), storageManagementSubtree(docs), + steerSubtree(docs), modelsSubtree(docs), openRouterSubtree(docs), mcpSubtree(docs), @@ -272,6 +273,28 @@ func planModeAutoApproveSubtree(docs Docs) *Subtree { } } +func steerSubtree(docs Docs) *Subtree { + return &Subtree{ + Key: "steer", + Tier: TierOperator, + Doc: "OPERATOR-TIER mid-run steer knob (steer-while-running, issue #512): when true " + + "(the DEFAULT), a client may inject an operator instruction into an in-flight run, " + + "drained at the next turn boundary. Set false to disable the steer inbox (the " + + "capability echo then reads false and a steer frame reports too_late). A " + + "project-tier steer: is IGNORED with a WARN (the harness's operator surface is not " + + "a project repo's to flip). Omit = keep the CLI/default (steer ON).", + CommentedOut: true, + Scalar: true, + Fields: []*Field{{ + Key: "steer", + Type: "bool", + Default: "true", + Doc: docFor(docs, "Config.Steer", "enable the mid-run steer inbox (default on)"), + ExampleValue: "false", + }}, + } +} + func modelsSubtree(docs Docs) *Subtree { fields := fieldsOf("ModelsSection", permconfig.ModelsSection{}, docs) for _, f := range fields { diff --git a/internal/configgen/configgen_test.go b/internal/configgen/configgen_test.go index 561466e6b5..bc48976e1e 100644 --- a/internal/configgen/configgen_test.go +++ b/internal/configgen/configgen_test.go @@ -270,6 +270,7 @@ func TestSubtreeTiersAreAsPinned(t *testing.T) { "learning": configgen.TierProject, // project may tighten but never raise the operator ceiling "retention": configgen.TierOperator, // operator-only: project cannot enable destructive cleanup "storage_management": configgen.TierOperator, // operator-only: project cannot grant process-wide management + "steer": configgen.TierOperator, // operator-only: a project cannot flip the mid-run steer surface (issue #512) "models": configgen.TierProject, // operator + project (project within the operator allowlist) "openrouter": configgen.TierOperator, // operator-only: a project cannot steer the OpenRouter downstream provider (issue #480) "mcp": configgen.TierOperator, // operator-only: endpoints, auth, credentials, and egress policy diff --git a/internal/configgen/settings.skeleton.yaml b/internal/configgen/settings.skeleton.yaml index 66262ef2eb..106feaf0b1 100644 --- a/internal/configgen/settings.skeleton.yaml +++ b/internal/configgen/settings.skeleton.yaml @@ -201,6 +201,15 @@ # - issuer: https://idp.example/realms/operators # subject: storage-admin +#| === steer === (tier: operator) +#| OPERATOR-TIER mid-run steer knob (steer-while-running, issue #512): when true (the +#| DEFAULT), a client may inject an operator instruction into an in-flight run, drained +#| at the next turn boundary. Set false to disable the steer inbox (the capability echo +#| then reads false and a steer frame reports too_late). A project-tier steer: is +#| IGNORED with a WARN (the harness's operator surface is not a project repo's to flip). +#| Omit = keep the CLI/default (steer ON). +# steer: false + #| === models === (tier: operator + project) #| Per-slot/alias/default model config (ADR 0030) + the operator allowlist cap and the #| semantic Subagent model-router taxonomy (ADR 0031/0042). At the operator tier all diff --git a/llms.txt b/llms.txt index de087819fa..33a7eaa150 100644 --- a/llms.txt +++ b/llms.txt @@ -1,22 +1,22 @@ # mecatl -> A markdown documentation corpus of 216 document(s) across 11 component(s), with 2220 heading(s) and 1993 resolved reference(s). Entries are ordered by importance (most-connected first). 19 document(s) excluded from rendering by emitExclude (still in the corpus: link-checked and ranked). Compactness 0.83, docs ~2.2 clicks apart. +> A markdown documentation corpus of 218 document(s) across 11 component(s), with 2247 heading(s) and 2029 resolved reference(s). Entries are ordered by importance (most-connected first). 19 document(s) excluded from rendering by emitExclude (still in the corpus: link-checked and ranked). Compactness 0.83, docs ~2.2 clicks apart. ## Documentation -- [ADR 0027 — Cloud-native arc: disposable process, externalized state, durable record](docs/adr/0027-cloud-native.md): ADR 0027 — Cloud-native arc: disposable process, externalized state, durable record (linked from: docs/acceptance/README.md, docs/acceptance/fire-result-delivery.md, docs/acceptance/path-escape-posture.md, docs/acceptance/session-continuity-ux.md, docs/acceptance/session-storage-continuity.md, docs/acceptance/spine-convergence.md, docs/adr/0012-compaction.md, docs/adr/0015-background-subagents.md, docs/adr/0028-mecatequi.md, docs/adr/0030-model-selection-heuristics.md, docs/adr/0031-subagent-model-router.md, docs/adr/0032-worktree-binding.md, docs/adr/0034-team-parallel-model-routing.md, docs/adr/0035-per-delegation-model-surface.md, docs/adr/0038-event-sourced-rehydration.md, docs/adr/0044-host-supplied-askid-discriminator.md, docs/adr/0048-mecak8s.md, docs/adr/0057-mcp-server-notifications.md, docs/adr/0059-scheduled-tasks.md, docs/adr/0062-guardrails-approve-once.md, docs/adr/0063-mcp-structured-failclosed-callmcpwithquery.md, docs/adr/0064-toolhive-llm-gateway-provider.md, docs/adr/0065-conversation-fork.md, docs/adr/0069-plan-approval-gate.md, docs/adr/0073-schedule-tool.md, docs/adr/0074-many-loops-scheduler-shape.md, docs/adr/0075-fire-result-delivery.md, docs/adr/0078-mcp-typed-tool-results.md, docs/adr/0097-scheduled-fire-inflight-state.md, docs/adr/0098-headless-telemetry.md, docs/adr/0104-session-family-physical-naming.md, docs/adr/0107-operator-profile-memory-lifecycle.md, docs/adr/0108-on-demand-logical-skill-assets.md, docs/adr/0111-hardened-agent-owned-skill-publication.md, docs/adr/0114-configurable-learning-trigger-policy.md, docs/adr/0200-resume-a-failed-subagent.md, docs/adr/0203-permanent-provider-error-signal.md, docs/adr/0204-caller-identity-threading.md, docs/adr/0205-bounded-jwks-staleness.md, docs/adr/0208-execution-environment.md, docs/adr/0211-execution-environment-runtime-seam.md, docs/adr/0214-environment-persistence.md, docs/adr/0217-session-discovery-continuation.md, docs/adr/0218-credential-store.md, docs/adr/0226-session-storage-maintenance.md, docs/adr/0227-dream-consolidation-safety-boundary.md, docs/adr/0228-manual-dream-review.md, docs/adr/0229-redis-migration-fencing.md, docs/adr/README.md, docs/agent-identity-model.md, docs/cloud-native-harness-systems.md, docs/design/MECAK8S-PLAN.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/scoped-resource-grants.md, engine/CHANGELOG.md) -- [ADR 0002 — Documentation lifecycle: living truth, frozen records, one tracker](docs/adr/0002-documentation-lifecycle.md): ADR 0002 — Documentation lifecycle: living truth, frozen records, one tracker (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0003-consolidate-design-records-as-adrs.md, docs/adr/0030-model-selection-heuristics.md, docs/adr/0034-team-parallel-model-routing.md, docs/adr/0035-per-delegation-model-surface.md, docs/adr/0036-engine-module.md, docs/adr/0042-taxonomy-gated-model-router.md, docs/adr/0043-ephemeral-turn0-instruction-fragments.md, docs/adr/0045-explicit-bucket-latency-histograms.md, docs/adr/0046-guardrails-slot-enable.md, docs/adr/0047-absolute-path-resolution.md, docs/adr/0049-guardrails-remove-maxchecks.md, docs/adr/0050-guardrails-remove-maxcontentbytes.md, docs/adr/0051-guardrails-advisory-tui-visibility.md, docs/adr/0052-guardrails-checker-down-toggle.md, docs/adr/0053-guardrails-default-block.md, docs/adr/0058-writable-named-specialist-subagent.md, docs/adr/0060-guardrails-bash-default.md, docs/adr/0061-guardrails-human-override.md, docs/adr/0062-guardrails-approve-once.md, docs/adr/0066-route-unpinned-and-writable-delegations.md, docs/adr/0068-effort-change-via-fork.md, docs/adr/0069-plan-approval-gate.md, docs/adr/0070-model-visible-affordance-gate.md, docs/adr/0072-acceptance-plan-spine.md, docs/adr/0076-schedule-shared-catalog.md, docs/adr/0077-direct-write-subagent.md, docs/adr/0079-delegation-observability-convergence.md, docs/adr/0088-daemon-config-file.md, docs/adr/0096-live-feed-reconnect.md, docs/adr/0097-scheduled-fire-inflight-state.md, docs/adr/0099-external-transcript-import.md, docs/adr/0102-toolhive-direct-mode.md, docs/adr/0104-session-family-physical-naming.md, docs/adr/0204-caller-identity-threading.md, docs/adr/0207-context-window-overrides.md, docs/adr/0209-schedule-origin-run-context.md, docs/adr/0212-caller-ownership-enforcement.md, docs/adr/0215-openai-subscription-manual-token.md, docs/adr/0225-operator-settings-validation.md, docs/adr/README.md, docs/adr/template.md, docs/agent-identity-model.md, docs/agent-identity-outbound.md, docs/cloud-native-harness-systems.md, docs/design/MECAK8S-PLAN.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/scoped-resource-grants.md) +- [ADR 0027 — Cloud-native arc: disposable process, externalized state, durable record](docs/adr/0027-cloud-native.md): ADR 0027 — Cloud-native arc: disposable process, externalized state, durable record (linked from: docs/acceptance/README.md, docs/acceptance/fire-result-delivery.md, docs/acceptance/path-escape-posture.md, docs/acceptance/session-continuity-ux.md, docs/acceptance/session-storage-continuity.md, docs/acceptance/spine-convergence.md, docs/adr/0012-compaction.md, docs/adr/0015-background-subagents.md, docs/adr/0028-mecatequi.md, docs/adr/0030-model-selection-heuristics.md, docs/adr/0031-subagent-model-router.md, docs/adr/0032-worktree-binding.md, docs/adr/0034-team-parallel-model-routing.md, docs/adr/0035-per-delegation-model-surface.md, docs/adr/0038-event-sourced-rehydration.md, docs/adr/0044-host-supplied-askid-discriminator.md, docs/adr/0048-mecak8s.md, docs/adr/0057-mcp-server-notifications.md, docs/adr/0059-scheduled-tasks.md, docs/adr/0062-guardrails-approve-once.md, docs/adr/0063-mcp-structured-failclosed-callmcpwithquery.md, docs/adr/0064-toolhive-llm-gateway-provider.md, docs/adr/0065-conversation-fork.md, docs/adr/0069-plan-approval-gate.md, docs/adr/0073-schedule-tool.md, docs/adr/0074-many-loops-scheduler-shape.md, docs/adr/0075-fire-result-delivery.md, docs/adr/0078-mcp-typed-tool-results.md, docs/adr/0097-scheduled-fire-inflight-state.md, docs/adr/0098-headless-telemetry.md, docs/adr/0104-session-family-physical-naming.md, docs/adr/0107-operator-profile-memory-lifecycle.md, docs/adr/0108-on-demand-logical-skill-assets.md, docs/adr/0111-hardened-agent-owned-skill-publication.md, docs/adr/0114-configurable-learning-trigger-policy.md, docs/adr/0200-resume-a-failed-subagent.md, docs/adr/0203-permanent-provider-error-signal.md, docs/adr/0204-caller-identity-threading.md, docs/adr/0205-bounded-jwks-staleness.md, docs/adr/0208-execution-environment.md, docs/adr/0211-execution-environment-runtime-seam.md, docs/adr/0214-environment-persistence.md, docs/adr/0217-session-discovery-continuation.md, docs/adr/0218-credential-store.md, docs/adr/0226-session-storage-maintenance.md, docs/adr/0227-dream-consolidation-safety-boundary.md, docs/adr/0228-manual-dream-review.md, docs/adr/0229-redis-migration-fencing.md, docs/adr/0232-steer-while-running.md, docs/adr/README.md, docs/agent-identity-model.md, docs/architecture/agent-loop.md, docs/cloud-native-harness-systems.md, docs/design/MECAK8S-PLAN.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/scoped-resource-grants.md, engine/CHANGELOG.md) +- [ADR 0002 — Documentation lifecycle: living truth, frozen records, one tracker](docs/adr/0002-documentation-lifecycle.md): ADR 0002 — Documentation lifecycle: living truth, frozen records, one tracker (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0003-consolidate-design-records-as-adrs.md, docs/adr/0030-model-selection-heuristics.md, docs/adr/0034-team-parallel-model-routing.md, docs/adr/0035-per-delegation-model-surface.md, docs/adr/0036-engine-module.md, docs/adr/0042-taxonomy-gated-model-router.md, docs/adr/0043-ephemeral-turn0-instruction-fragments.md, docs/adr/0045-explicit-bucket-latency-histograms.md, docs/adr/0046-guardrails-slot-enable.md, docs/adr/0047-absolute-path-resolution.md, docs/adr/0049-guardrails-remove-maxchecks.md, docs/adr/0050-guardrails-remove-maxcontentbytes.md, docs/adr/0051-guardrails-advisory-tui-visibility.md, docs/adr/0052-guardrails-checker-down-toggle.md, docs/adr/0053-guardrails-default-block.md, docs/adr/0058-writable-named-specialist-subagent.md, docs/adr/0060-guardrails-bash-default.md, docs/adr/0061-guardrails-human-override.md, docs/adr/0062-guardrails-approve-once.md, docs/adr/0066-route-unpinned-and-writable-delegations.md, docs/adr/0068-effort-change-via-fork.md, docs/adr/0069-plan-approval-gate.md, docs/adr/0070-model-visible-affordance-gate.md, docs/adr/0072-acceptance-plan-spine.md, docs/adr/0076-schedule-shared-catalog.md, docs/adr/0077-direct-write-subagent.md, docs/adr/0079-delegation-observability-convergence.md, docs/adr/0088-daemon-config-file.md, docs/adr/0096-live-feed-reconnect.md, docs/adr/0097-scheduled-fire-inflight-state.md, docs/adr/0099-external-transcript-import.md, docs/adr/0102-toolhive-direct-mode.md, docs/adr/0104-session-family-physical-naming.md, docs/adr/0204-caller-identity-threading.md, docs/adr/0207-context-window-overrides.md, docs/adr/0209-schedule-origin-run-context.md, docs/adr/0212-caller-ownership-enforcement.md, docs/adr/0215-openai-subscription-manual-token.md, docs/adr/0225-operator-settings-validation.md, docs/adr/0232-steer-while-running.md, docs/adr/README.md, docs/adr/template.md, docs/agent-identity-model.md, docs/agent-identity-outbound.md, docs/cloud-native-harness-systems.md, docs/design/MECAK8S-PLAN.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/scoped-resource-grants.md) - [mecatl — Architecture](docs/architecture.md): mecatl — Architecture (linked from: AGENTS.md, README.md, docs/READING.md, docs/README.md, docs/acceptance/README.md, docs/acceptance/caller-identity.md, docs/acceptance/path-escape-posture.md, docs/acceptance/session-continuity-ux.md, docs/acceptance/session-storage-continuity.md, docs/acceptance/spine-convergence.md, docs/adr/0001-acp-adapter.md, docs/adr/0002-documentation-lifecycle.md, docs/adr/0004-v1-architecture.md, docs/adr/0036-engine-module.md, docs/adr/0039-parallel-auto-merge.md, docs/adr/0048-mecak8s.md, docs/adr/0059-scheduled-tasks.md, docs/adr/0065-conversation-fork.md, docs/adr/0068-effort-change-via-fork.md, docs/adr/0073-schedule-tool.md, docs/adr/0074-many-loops-scheduler-shape.md, docs/adr/0075-fire-result-delivery.md, docs/adr/0105-built-in-webfetch.md, docs/adr/0110-evaluated-agent-owned-skills.md, docs/adr/0111-hardened-agent-owned-skill-publication.md, docs/adr/0114-configurable-learning-trigger-policy.md, docs/adr/0206-oidc-authn-module.md, docs/adr/0208-execution-environment.md, docs/adr/0209-schedule-origin-run-context.md, docs/adr/0211-execution-environment-runtime-seam.md, docs/adr/0214-environment-persistence.md, docs/adr/0217-session-discovery-continuation.md, docs/adr/0218-credential-store.md, docs/adr/0221-read-only-credential-source.md, docs/adr/0224-validated-automatic-skill-activation.md, docs/adr/0225-operator-settings-validation.md, docs/adr/0226-session-storage-maintenance.md, docs/adr/0229-redis-migration-fencing.md, docs/adr/0230-redis-migration-atomic-ownership-and-coverage.md, docs/adr/0231-redis-owner-index-exact-coverage.md, docs/adr/README.md, docs/architecture/agent-loop.md, docs/architecture/api-surface.md, docs/architecture/context-and-compaction.md, docs/architecture/deployment-and-hardening.md, docs/architecture/domain-model.md, docs/architecture/extensibility.md, docs/architecture/hooks-and-guardrails.md, docs/architecture/mecatl.modelith.md, docs/architecture/memory.md, docs/architecture/observability.md, docs/architecture/parallelism.md, docs/architecture/ports.md, docs/architecture/providers.md, docs/architecture/subagents-and-teams.md, docs/cloud-native-harness-kit.md, docs/design/IMPLEMENTATION-NOTES.md, docs/design/MECAK8S-PLAN.md, docs/design/PRODUCTION-READINESS.md, docs/design/README.md, docs/development-process.md, docs/tui.md, docs/usage.md) - [ADR 0037 — engine public-API stability contract](docs/adr/0037-engine-stability-contract.md): ADR 0037 — engine public-API stability contract (linked from: AGENTS.md, README.md, docs/acceptance/README.md, docs/acceptance/delegation-observability-convergence.md, docs/acceptance/fire-result-delivery.md, docs/acceptance/spine-convergence.md, docs/adr/0036-engine-module.md, docs/adr/0038-event-sourced-rehydration.md, docs/adr/0055-reasoning-effort.md, docs/adr/0059-scheduled-tasks.md, docs/adr/0078-mcp-typed-tool-results.md, docs/adr/0079-delegation-observability-convergence.md, docs/adr/0093-provider-modules.md, docs/adr/0204-caller-identity-threading.md, docs/adr/README.md, docs/architecture.md, docs/architecture/api-surface.md, docs/architecture/extensibility.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, engine/CHANGELOG.md, engine/COMPATIBILITY.md, engine/api/README.md) - [ADR 0036 — engine/ is its own Go module (monorepo via go.work)](docs/adr/0036-engine-module.md): ADR 0036 — engine/ is its own Go module (monorepo via go.work) (linked from: AGENTS.md, README.md, docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0037-engine-stability-contract.md, docs/adr/0038-event-sourced-rehydration.md, docs/adr/0059-scheduled-tasks.md, docs/adr/0078-mcp-typed-tool-results.md, docs/adr/0093-provider-modules.md, docs/adr/0105-built-in-webfetch.md, docs/adr/0208-execution-environment.md, docs/adr/0214-environment-persistence.md, docs/adr/README.md, docs/architecture.md, docs/architecture/extensibility.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/usage/install.md, engine/CHANGELOG.md, engine/COMPATIBILITY.md) -- [ADR 0038 — Event-sourced SessionStore rehydration (the reference fold)](docs/adr/0038-event-sourced-rehydration.md): ADR 0038 — Event-sourced SessionStore rehydration (the reference fold) (linked from: AGENTS.md, docs/acceptance/README.md, docs/acceptance/session-continuity-ux.md, docs/acceptance/spine-convergence.md, docs/adr/0027-cloud-native.md, docs/adr/0043-ephemeral-turn0-instruction-fragments.md, docs/adr/0065-conversation-fork.md, docs/adr/0078-mcp-typed-tool-results.md, docs/adr/0204-caller-identity-threading.md, docs/adr/0217-session-discovery-continuation.md, docs/adr/README.md, docs/architecture.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/usage/install.md, engine/CHANGELOG.md, engine/COMPATIBILITY.md) +- [ADR 0038 — Event-sourced SessionStore rehydration (the reference fold)](docs/adr/0038-event-sourced-rehydration.md): ADR 0038 — Event-sourced SessionStore rehydration (the reference fold) (linked from: AGENTS.md, docs/acceptance/README.md, docs/acceptance/session-continuity-ux.md, docs/acceptance/spine-convergence.md, docs/acceptance/steer-while-running.md, docs/adr/0027-cloud-native.md, docs/adr/0043-ephemeral-turn0-instruction-fragments.md, docs/adr/0065-conversation-fork.md, docs/adr/0078-mcp-typed-tool-results.md, docs/adr/0204-caller-identity-threading.md, docs/adr/0217-session-discovery-continuation.md, docs/adr/0232-steer-while-running.md, docs/adr/README.md, docs/architecture.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/usage/install.md, engine/CHANGELOG.md, engine/COMPATIBILITY.md) - [ADR 0059 — Scheduled tasks: durable registry, tick loop, leader-lease, claim-before-fire at-most-once](docs/adr/0059-scheduled-tasks.md): ADR 0059 — Scheduled tasks: durable registry, tick loop, leader-lease, claim-before-fire at-most-once (linked from: docs/acceptance/README.md, docs/acceptance/fire-result-delivery.md, docs/acceptance/schedule-tool.md, docs/acceptance/spine-convergence.md, docs/adr/0073-schedule-tool.md, docs/adr/0074-many-loops-scheduler-shape.md, docs/adr/0075-fire-result-delivery.md, docs/adr/0097-scheduled-fire-inflight-state.md, docs/adr/README.md, docs/architecture.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/usage.md, docs/usage/mecated.md) - [ADR 0016 — Multi-provider and multi-model](docs/adr/0016-multi-provider.md): ADR 0016 — Multi-provider and multi-model (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0017-openai-responses-api.md, docs/adr/0030-model-selection-heuristics.md, docs/adr/0055-reasoning-effort.md, docs/adr/0064-toolhive-llm-gateway-provider.md, docs/adr/0067-openai-chat-completions-adapter.md, docs/adr/0071-seamless-model-switch.md, docs/adr/0100-provider-prompt-caching.md, docs/adr/0101-openai-reasoning-multiplicity.md, docs/adr/0207-context-window-overrides.md, docs/adr/0210-openrouter-downstream-provider-steering.md, docs/adr/0215-openai-subscription-manual-token.md, docs/adr/0216-provider-session-correlation-header.md, docs/adr/README.md, docs/architecture/providers.md, docs/design/IMPLEMENTATION-NOTES.md, docs/design/MECAK8S-PLAN.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) - [ADR 0005 — Driver seams: ports, the gRPC driver protocol, and conformance](docs/adr/0005-driver-seams.md): ADR 0005 — Driver seams: ports, the gRPC driver protocol, and conformance (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0004-v1-architecture.md, docs/adr/0028-mecatequi.md, docs/adr/0044-host-supplied-askid-discriminator.md, docs/adr/0048-mecak8s.md, docs/adr/0108-on-demand-logical-skill-assets.md, docs/adr/0213-driver-caller-ownership.md, docs/adr/0226-session-storage-maintenance.md, docs/adr/README.md, docs/cloud-native-harness-systems.md, docs/design/IMPLEMENTATION-NOTES.md, docs/design/MECAK8S-PLAN.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/scoped-resource-grants.md) - [ADR 0021 — Guardrails: LLM-backed tool-content inspection](docs/adr/0021-guardrails.md): ADR 0021 — Guardrails: LLM-backed tool-content inspection (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0022-allow-all-posture.md, docs/adr/0023-workspace-trust.md, docs/adr/0030-model-selection-heuristics.md, docs/adr/0031-subagent-model-router.md, docs/adr/0042-taxonomy-gated-model-router.md, docs/adr/0046-guardrails-slot-enable.md, docs/adr/0049-guardrails-remove-maxchecks.md, docs/adr/0050-guardrails-remove-maxcontentbytes.md, docs/adr/0051-guardrails-advisory-tui-visibility.md, docs/adr/0052-guardrails-checker-down-toggle.md, docs/adr/0053-guardrails-default-block.md, docs/adr/0060-guardrails-bash-default.md, docs/adr/0061-guardrails-human-override.md, docs/adr/0062-guardrails-approve-once.md, docs/adr/0080-guardrail-routed-escape-checking.md, docs/adr/0105-built-in-webfetch.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) +- [ADR 0069 — Plan-approval gate](docs/adr/0069-plan-approval-gate.md): ADR 0069 — Plan-approval gate (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/acceptance/steer-while-running.md, docs/adr/0070-model-visible-affordance-gate.md, docs/adr/0232-steer-while-running.md, docs/adr/README.md, docs/architecture/agent-loop.md, docs/design/IMPLEMENTATION-NOTES.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/usage.md, docs/usage/grpc-api.md, docs/usage/http-sse-api.md, docs/usage/mecated.md) - [ADR 0109 — Evidence-backed reflection and durable staged learning](docs/adr/0109-staged-learning-proposals.md): ADR 0109 — Evidence-backed reflection and durable staged learning (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0110-evaluated-agent-owned-skills.md, docs/adr/0114-configurable-learning-trigger-policy.md, docs/adr/0227-dream-consolidation-safety-boundary.md, docs/adr/README.md, docs/architecture.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, engine/CHANGELOG.md) - [ADR 0218 — Internal encrypted credential-store substrate](docs/adr/0218-credential-store.md): ADR 0218 — Internal encrypted credential-store substrate (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0220-mcp-oauth-controller.md, docs/adr/0221-read-only-credential-source.md, docs/adr/README.md, docs/architecture.md, docs/design/IMPLEMENTATION-NOTES.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) -- [ADR 0069 — Plan-approval gate](docs/adr/0069-plan-approval-gate.md): ADR 0069 — Plan-approval gate (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0070-model-visible-affordance-gate.md, docs/adr/README.md, docs/architecture/agent-loop.md, docs/design/IMPLEMENTATION-NOTES.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/usage.md, docs/usage/grpc-api.md, docs/usage/http-sse-api.md, docs/usage/mecated.md) - [ADR 0048 — mecak8s: Kubernetes-native agent harness (storage-free, managed-service state)](docs/adr/0048-mecak8s.md): ADR 0048 — mecak8s: Kubernetes-native agent harness (storage-free, managed-service state) (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0059-scheduled-tasks.md, docs/adr/0063-mcp-structured-failclosed-callmcpwithquery.md, docs/adr/0074-many-loops-scheduler-shape.md, docs/adr/0082-factory-mcp-wiring.md, docs/adr/0098-headless-telemetry.md, docs/adr/0215-openai-subscription-manual-token.md, docs/adr/README.md, docs/agent-identity-model.md, docs/cloud-native-harness-systems.md, docs/design/MECAK8S-PLAN.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/scoped-resource-grants.md) - [ADR 0014 — Agent teams](docs/adr/0014-agent-teams.md): ADR 0014 — Agent teams (linked from: docs/acceptance/README.md, docs/acceptance/session-continuity-ux.md, docs/acceptance/spine-convergence.md, docs/adr/0013-agent-definitions.md, docs/adr/0015-background-subagents.md, docs/adr/0033-dirty-aware-readonly-fork.md, docs/adr/0040-writable-subagent-and-serialized-merge.md, docs/adr/0077-direct-write-subagent.md, docs/adr/0200-resume-a-failed-subagent.md, docs/adr/README.md, docs/design/IMPLEMENTATION-NOTES.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) - [ADR 0023 — Workspace Trust](docs/adr/0023-workspace-trust.md): ADR 0023 — Workspace Trust (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0021-guardrails.md, docs/adr/0022-allow-all-posture.md, docs/adr/0032-worktree-binding.md, docs/adr/0033-dirty-aware-readonly-fork.md, docs/adr/0092-no-project-trust-pin.md, docs/adr/0094-opt-in-project-ingestion.md, docs/adr/0095-root-aware-project-trust.md, docs/adr/0202-diagnostic-only-posture-reporting.md, docs/adr/README.md, docs/design/IMPLEMENTATION-NOTES.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) @@ -30,9 +30,9 @@ - [ADR 0004 — v1 Architecture: hexagonal DDD harness](docs/adr/0004-v1-architecture.md): ADR 0004 — v1 Architecture: hexagonal DDD harness (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0005-driver-seams.md, docs/adr/0006-v1-step-chain.md, docs/adr/0016-multi-provider.md, docs/adr/0027-cloud-native.md, docs/adr/0029-repomap-tree-sitter.md, docs/adr/README.md, docs/design/IMPLEMENTATION-NOTES.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) - [ADR 0028 — mecatequi: Single-Shot GitHub Action Runner](docs/adr/0028-mecatequi.md): ADR 0028 — mecatequi: Single-Shot GitHub Action Runner (linked from: README.md, docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0048-mecak8s.md, docs/adr/0082-factory-mcp-wiring.md, docs/adr/0098-headless-telemetry.md, docs/adr/0103-mecatui-seed-prompt.md, docs/adr/README.md, docs/design/MECAK8S-PLAN.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/usage/mecatequi-ci.md) - [ADR 0215 — OpenAI subscription with a manual access token](docs/adr/0215-openai-subscription-manual-token.md): ADR 0215 — OpenAI subscription with a manual access token (linked from: docs/README.md, docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/README.md, docs/architecture.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/usage/troubleshooting.md) +- [ADR 0204 — Caller identity: accept a principal, thread it everywhere, record the owner](docs/adr/0204-caller-identity-threading.md): ADR 0204 — Caller identity: accept a principal, thread it everywhere, record the owner (linked from: docs/acceptance/README.md, docs/acceptance/caller-identity.md, docs/acceptance/caller-separation.md, docs/acceptance/spine-convergence.md, docs/adr/0205-bounded-jwks-staleness.md, docs/adr/0206-oidc-authn-module.md, docs/adr/0209-schedule-origin-run-context.md, docs/adr/0212-caller-ownership-enforcement.md, docs/adr/0213-driver-caller-ownership.md, docs/adr/README.md, docs/architecture.md, docs/design/principles.md, docs/development-process.md, docs/usage/mecated.md, engine/CHANGELOG.md, engine/COMPATIBILITY.md) - [ADR 0046 — Guardrails slot enables (configure = enable)](docs/adr/0046-guardrails-slot-enable.md): ADR 0046 — Guardrails slot enables (configure = enable) (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0049-guardrails-remove-maxchecks.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/usage/guardrails.md, docs/usage/mecated.md, docs/usage/model-routing.md) - [ADR 0086 — Remove the output-economy control surface](docs/adr/0086-remove-output-economy-control.md): ADR 0086 — Remove the output-economy control surface (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0041-output-economy-default-prompt.md, docs/adr/0089-cli-clean-break-grammar.md, docs/adr/README.md, docs/architecture.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) -- [ADR 0204 — Caller identity: accept a principal, thread it everywhere, record the owner](docs/adr/0204-caller-identity-threading.md): ADR 0204 — Caller identity: accept a principal, thread it everywhere, record the owner (linked from: docs/acceptance/README.md, docs/acceptance/caller-identity.md, docs/acceptance/caller-separation.md, docs/acceptance/spine-convergence.md, docs/adr/0205-bounded-jwks-staleness.md, docs/adr/0206-oidc-authn-module.md, docs/adr/0209-schedule-origin-run-context.md, docs/adr/0212-caller-ownership-enforcement.md, docs/adr/0213-driver-caller-ownership.md, docs/adr/README.md, docs/architecture.md, docs/design/principles.md, docs/development-process.md, docs/usage/mecated.md, engine/CHANGELOG.md, engine/COMPATIBILITY.md) - [ADR 0113 — Operator-configured MCP authentication profiles](docs/adr/0113-operator-mcp-auth-profiles.md): ADR 0113 — Operator-configured MCP authentication profiles (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/README.md, docs/architecture/extensibility.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/usage/mecated.md) - [ADR 0024 — System-Prompt Research and Enhancement](docs/adr/0024-system-prompt-research.md): ADR 0024 — System-Prompt Research and Enhancement (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0007-twelve-patterns-audit.md, docs/adr/0041-output-economy-default-prompt.md, docs/adr/0054-reasoning-rebalance-default-prompt.md, docs/adr/0070-model-visible-affordance-gate.md, docs/adr/README.md, docs/design/IMPLEMENTATION-NOTES.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) - [ADR 0015 — Background subagents and per-child cancel](docs/adr/0015-background-subagents.md): ADR 0015 — Background subagents and per-child cancel (linked from: docs/acceptance/README.md, docs/acceptance/session-continuity-ux.md, docs/acceptance/spine-convergence.md, docs/adr/0013-agent-definitions.md, docs/adr/0014-agent-teams.md, docs/adr/0027-cloud-native.md, docs/adr/0201-background-bash.md, docs/adr/0217-session-discovery-continuation.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) @@ -47,10 +47,10 @@ - [ADR 0219 — Qualify the official MCP SDK authorization-code profile](docs/adr/0219-mcp-oauth-sdk-profile.md): ADR 0219 — Qualify the official MCP SDK authorization-code profile (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0112-mcp-oauth-loopback-runtime.md, docs/adr/0220-mcp-oauth-controller.md, docs/adr/0223-mcp-sdk-transport-error-semantics.md, docs/adr/README.md, docs/architecture/extensibility.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) - [ADR 0030 — Layered model-selection heuristics](docs/adr/0030-model-selection-heuristics.md): ADR 0030 — Layered model-selection heuristics (linked from: README.md, docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0031-subagent-model-router.md, docs/adr/0032-worktree-binding.md, docs/adr/0034-team-parallel-model-routing.md, docs/adr/0046-guardrails-slot-enable.md, docs/adr/0066-route-unpinned-and-writable-delegations.md, docs/adr/0069-plan-approval-gate.md, docs/adr/README.md, docs/architecture/api-surface.md, docs/architecture/providers.md, docs/design/principles.md, docs/development-process.md, docs/usage/mecated.md) - [ADR 0220 — Adapter-local MCP OAuth controller](docs/adr/0220-mcp-oauth-controller.md): ADR 0220 — Adapter-local MCP OAuth controller (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0112-mcp-oauth-loopback-runtime.md, docs/adr/0221-read-only-credential-source.md, docs/adr/README.md, docs/architecture/extensibility.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) +- [ADR 0212 — Enforce caller ownership at every application access path](docs/adr/0212-caller-ownership-enforcement.md): ADR 0212 — Enforce caller ownership at every application access path (linked from: docs/acceptance/README.md, docs/acceptance/caller-separation.md, docs/acceptance/spine-convergence.md, docs/adr/0213-driver-caller-ownership.md, docs/adr/README.md, docs/architecture.md, docs/design/IMPLEMENTATION-NOTES.md, docs/design/principles.md, docs/development-process.md, engine/CHANGELOG.md) - [ADR 0053 — Flip guardrails default mode from advisory to block](docs/adr/0053-guardrails-default-block.md): ADR 0053 — Flip guardrails default mode from advisory to block (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0060-guardrails-bash-default.md, docs/adr/0061-guardrails-human-override.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/usage/guardrails.md) - [ADR 0049 — Remove the guardrails per-session checker call-count cap](docs/adr/0049-guardrails-remove-maxchecks.md): ADR 0049 — Remove the guardrails per-session checker call-count cap (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0021-guardrails.md, docs/adr/0050-guardrails-remove-maxcontentbytes.md, docs/adr/0051-guardrails-advisory-tui-visibility.md, docs/adr/0052-guardrails-checker-down-toggle.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) - [ADR 0012 — Conversation compaction](docs/adr/0012-compaction.md): ADR 0012 — Conversation compaction (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0011-soul-and-user-model.md, docs/adr/0027-cloud-native.md, docs/adr/0043-ephemeral-turn0-instruction-fragments.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) -- [ADR 0212 — Enforce caller ownership at every application access path](docs/adr/0212-caller-ownership-enforcement.md): ADR 0212 — Enforce caller ownership at every application access path (linked from: docs/acceptance/README.md, docs/acceptance/caller-separation.md, docs/acceptance/spine-convergence.md, docs/adr/0213-driver-caller-ownership.md, docs/adr/README.md, docs/architecture.md, docs/design/IMPLEMENTATION-NOTES.md, docs/design/principles.md, docs/development-process.md, engine/CHANGELOG.md) - [ADR 0051 — Surface advisory guardrail findings to the TUI](docs/adr/0051-guardrails-advisory-tui-visibility.md): ADR 0051 — Surface advisory guardrail findings to the TUI (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0021-guardrails.md, docs/adr/0053-guardrails-default-block.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/usage/guardrails.md) - [ADR 0018 — Performance observability](docs/adr/0018-perf-observability.md): ADR 0018 — Performance observability (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0019-perf-tracking.md, docs/adr/0020-diagnostics.md, docs/adr/0045-explicit-bucket-latency-histograms.md, docs/adr/0098-headless-telemetry.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/perf-measurement-survey.md) - [ADR 0112 — Host-owned loopback MCP OAuth login](docs/adr/0112-mcp-oauth-loopback-runtime.md): ADR 0112 — Host-owned loopback MCP OAuth login (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0113-operator-mcp-auth-profiles.md, docs/adr/README.md, docs/architecture/extensibility.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) @@ -62,21 +62,21 @@ - [ADR 0042 — Taxonomy-gated subagent model router (enable by config, not a flag)](docs/adr/0042-taxonomy-gated-model-router.md): ADR 0042 — Taxonomy-gated subagent model router (enable by config, not a flag) (linked from: README.md, docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0031-subagent-model-router.md, docs/adr/0046-guardrails-slot-enable.md, docs/adr/0066-route-unpinned-and-writable-delegations.md, docs/adr/README.md, docs/architecture/providers.md, docs/design/principles.md, docs/development-process.md, docs/usage/guardrails.md, docs/usage/mecated.md, docs/usage/model-routing.md) - [ADR 0007 — Twelve agentic-harness patterns: pluggability audit](docs/adr/0007-twelve-patterns-audit.md): ADR 0007 — Twelve agentic-harness patterns: pluggability audit (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0024-system-prompt-research.md, docs/adr/0069-plan-approval-gate.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) - [ADR 0032 — First-class worktree binding for a session](docs/adr/0032-worktree-binding.md): ADR 0032 — First-class worktree binding for a session (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0033-dirty-aware-readonly-fork.md, docs/adr/README.md, docs/architecture/parallelism.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) +- [ADR 0055 — Reasoning-effort knob](docs/adr/0055-reasoning-effort.md): ADR 0055 — Reasoning-effort knob (linked from: docs/acceptance/README.md, docs/acceptance/schedule-tool.md, docs/acceptance/spine-convergence.md, docs/adr/0064-toolhive-llm-gateway-provider.md, docs/adr/0067-openai-chat-completions-adapter.md, docs/adr/0068-effort-change-via-fork.md, docs/adr/0100-provider-prompt-caching.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md, docs/usage/guardrails.md, docs/usage/mecak8s.md, docs/usage/mecated.md, engine/CHANGELOG.md) - [ADR 0107 — Live operator profiles and reversible memory lifecycle](docs/adr/0107-operator-profile-memory-lifecycle.md): ADR 0107 — Live operator profiles and reversible memory lifecycle (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0109-staged-learning-proposals.md, docs/adr/0227-dream-consolidation-safety-boundary.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) - [ADR 0052 — Global guardrails checker-down posture toggle](docs/adr/0052-guardrails-checker-down-toggle.md): ADR 0052 — Global guardrails checker-down posture toggle (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/usage/guardrails.md) - [ADR 0008 — Memory on by default in the embedded mecatui server](docs/adr/0008-memory-on-by-default.md): ADR 0008 — Memory on by default in the embedded mecatui server (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0009-tiered-memory.md, docs/adr/0010-semantic-memory-recall.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) -- [ADR 0055 — Reasoning-effort knob](docs/adr/0055-reasoning-effort.md): ADR 0055 — Reasoning-effort knob (linked from: docs/acceptance/README.md, docs/acceptance/schedule-tool.md, docs/acceptance/spine-convergence.md, docs/adr/0064-toolhive-llm-gateway-provider.md, docs/adr/0067-openai-chat-completions-adapter.md, docs/adr/0068-effort-change-via-fork.md, docs/adr/0100-provider-prompt-caching.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md, docs/usage/guardrails.md, docs/usage/mecak8s.md, docs/usage/mecated.md, engine/CHANGELOG.md) - [ADR 0013 — Agent definitions (Tier 1)](docs/adr/0013-agent-definitions.md): ADR 0013 — Agent definitions (Tier 1) (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0014-agent-teams.md, docs/adr/0015-background-subagents.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) +- [ADR 0201 — Background Bash commands](docs/adr/0201-background-bash.md): ADR 0201 — Background Bash commands (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/README.md, docs/architecture/ports.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) - [ADR 0025 — mecatui UX Discoverability](docs/adr/0025-ux-discoverability.md): ADR 0025 — mecatui UX Discoverability (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0026-clipboard-image-paste.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/tui.md) - [ADR 0026 — Clipboard Image Paste](docs/adr/0026-clipboard-image-paste.md): ADR 0026 — Clipboard Image Paste (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0025-ux-discoverability.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md, docs/tui.md) -- [ADR 0201 — Background Bash commands](docs/adr/0201-background-bash.md): ADR 0201 — Background Bash commands (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/README.md, docs/architecture/ports.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) - [ADR 0054 — Reasoning rebalance of the default-tone prompt](docs/adr/0054-reasoning-rebalance-default-prompt.md): ADR 0054 — Reasoning rebalance of the default-tone prompt (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0086-remove-output-economy-control.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) +- [ADR 0062 — Out-of-band approve-once for guardrail blocks](docs/adr/0062-guardrails-approve-once.md): ADR 0062 — Out-of-band approve-once for guardrail blocks (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0061-guardrails-human-override.md, docs/adr/0069-plan-approval-gate.md, docs/adr/0080-guardrail-routed-escape-checking.md, docs/adr/README.md, docs/architecture/agent-loop.md, docs/design/IMPLEMENTATION-NOTES.md, docs/design/principles.md, docs/development-process.md, engine/CHANGELOG.md) - [ADR 0029 — Repo-map Tree-sitter Binding](docs/adr/0029-repomap-tree-sitter.md): ADR 0029 — Repo-map Tree-sitter Binding (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) - [ADR 0068 — Change reasoning effort via conversation fork (keep the transcript)](docs/adr/0068-effort-change-via-fork.md): ADR 0068 — Change reasoning effort via conversation fork (keep the transcript) (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) - [ADR 0098 — Telemetry for the headless binaries (mecatequi, mecak8s)](docs/adr/0098-headless-telemetry.md): ADR 0098 — Telemetry for the headless binaries (mecatequi, mecak8s) (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/README.md, docs/design/PRODUCTION-READINESS.md, docs/design/principles.md, docs/development-process.md) -- [ADR 0062 — Out-of-band approve-once for guardrail blocks](docs/adr/0062-guardrails-approve-once.md): ADR 0062 — Out-of-band approve-once for guardrail blocks (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0061-guardrails-human-override.md, docs/adr/0069-plan-approval-gate.md, docs/adr/0080-guardrail-routed-escape-checking.md, docs/adr/README.md, docs/architecture/agent-loop.md, docs/design/IMPLEMENTATION-NOTES.md, docs/design/principles.md, docs/development-process.md, engine/CHANGELOG.md) -- [ADR 0075 — Fire-result delivery: a scheduled fire reports back into the originating chat](docs/adr/0075-fire-result-delivery.md): ADR 0075 — Fire-result delivery: a scheduled fire reports back into the originating chat (linked from: docs/acceptance/README.md, docs/acceptance/fire-result-delivery.md, docs/acceptance/schedule-shared-catalog.md, docs/acceptance/spine-convergence.md, docs/adr/0076-schedule-shared-catalog.md, docs/adr/0096-live-feed-reconnect.md, docs/adr/0097-scheduled-fire-inflight-state.md, docs/adr/0209-schedule-origin-run-context.md, docs/adr/README.md, docs/architecture.md, docs/design/principles.md, docs/development-process.md) - [ADR 0209 — Attribute schedule origins through the run context](docs/adr/0209-schedule-origin-run-context.md): ADR 0209 — Attribute schedule origins through the run context (linked from: docs/acceptance/README.md, docs/acceptance/schedule-shared-catalog.md, docs/acceptance/spine-convergence.md, docs/adr/0075-fire-result-delivery.md, docs/adr/README.md, docs/architecture.md, docs/design/principles.md, docs/development-process.md, engine/CHANGELOG.md) +- [ADR 0075 — Fire-result delivery: a scheduled fire reports back into the originating chat](docs/adr/0075-fire-result-delivery.md): ADR 0075 — Fire-result delivery: a scheduled fire reports back into the originating chat (linked from: docs/acceptance/README.md, docs/acceptance/fire-result-delivery.md, docs/acceptance/schedule-shared-catalog.md, docs/acceptance/spine-convergence.md, docs/adr/0076-schedule-shared-catalog.md, docs/adr/0096-live-feed-reconnect.md, docs/adr/0097-scheduled-fire-inflight-state.md, docs/adr/0209-schedule-origin-run-context.md, docs/adr/README.md, docs/architecture.md, docs/design/principles.md, docs/development-process.md) - [ADR 0206 — Ship reusable OIDC caller identity as an opt-in module](docs/adr/0206-oidc-authn-module.md): ADR 0206 — Ship reusable OIDC caller identity as an opt-in module (linked from: README.md, docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/README.md, docs/architecture.md, docs/design/principles.md, docs/development-process.md, engine/CHANGELOG.md) - [ADR 0110 — Evaluated, versioned agent-owned skills](docs/adr/0110-evaluated-agent-owned-skills.md): ADR 0110 — Evaluated, versioned agent-owned skills (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0111-hardened-agent-owned-skill-publication.md, docs/adr/README.md, docs/architecture.md, docs/design/principles.md, docs/development-process.md, engine/CHANGELOG.md) - [ADR 0077 — Direct-write writable Subagent (no fork, no merge-back)](docs/adr/0077-direct-write-subagent.md): ADR 0077 — Direct-write writable Subagent (no fork, no merge-back) (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0042-taxonomy-gated-model-router.md, docs/adr/0044-host-supplied-askid-discriminator.md, docs/adr/0058-writable-named-specialist-subagent.md, docs/adr/0065-conversation-fork.md, docs/adr/0066-route-unpinned-and-writable-delegations.md, docs/adr/0200-resume-a-failed-subagent.md, docs/adr/0208-execution-environment.md, docs/adr/README.md, docs/cloud-native-harness-systems.md, docs/design/principles.md, docs/development-process.md, docs/scoped-resource-grants.md) @@ -87,22 +87,23 @@ - [ADR 0066 — Route unpinned agent-defs and writable explorers; explicit inherit is the pin](docs/adr/0066-route-unpinned-and-writable-delegations.md): ADR 0066 — Route unpinned agent-defs and writable explorers; explicit inherit is the pin (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0031-subagent-model-router.md, docs/adr/0077-direct-write-subagent.md, docs/adr/README.md, docs/architecture/providers.md, docs/design/IMPLEMENTATION-NOTES.md, docs/design/principles.md, docs/development-process.md) - [ADR 0060 — Add Bash to the default guardrail rule set with a read-only pre-filter](docs/adr/0060-guardrails-bash-default.md): ADR 0060 — Add Bash to the default guardrail rule set with a read-only pre-filter (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0061-guardrails-human-override.md, docs/adr/0062-guardrails-approve-once.md, docs/adr/0080-guardrail-routed-escape-checking.md, docs/adr/0201-background-bash.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md, docs/usage/guardrails.md, docs/usage/mecated.md) - [ADR 0039 — Parallel single-branch auto-merge](docs/adr/0039-parallel-auto-merge.md): ADR 0039 — Parallel single-branch auto-merge (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0040-writable-subagent-and-serialized-merge.md, docs/adr/README.md, docs/architecture/parallelism.md, docs/cloud-native-harness-systems.md, docs/design/principles.md, docs/development-process.md, docs/scoped-resource-grants.md, docs/usage/mecated.md) -- [ADR 0095 — Root-aware project trust](docs/adr/0095-root-aware-project-trust.md): ADR 0095 — Root-aware project trust (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0092-no-project-trust-pin.md, docs/adr/0094-opt-in-project-ingestion.md, docs/adr/0202-diagnostic-only-posture-reporting.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md, docs/usage/mecak8s.md, docs/usage/mecated.md, docs/usage/mecatequi-ci.md, docs/usage/workspace-trust.md) - [ADR 0217 — Session discovery uses durable kind metadata and an authoritative transcript](docs/adr/0217-session-discovery-continuation.md): ADR 0217 — Session discovery uses durable kind metadata and an authoritative transcript (linked from: docs/acceptance/README.md, docs/acceptance/session-continuity-ux.md, docs/acceptance/session-storage-continuity.md, docs/acceptance/spine-convergence.md, docs/adr/0226-session-storage-maintenance.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md, engine/CHANGELOG.md) -- [ADR 0089 — CLI clean break: one canonical spelling per action](docs/adr/0089-cli-clean-break-grammar.md): ADR 0089 — CLI clean break: one canonical spelling per action (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0086-remove-output-economy-control.md, docs/adr/0087-mecatui-staged-transport-migration.md, docs/adr/0103-mecatui-seed-prompt.md, docs/adr/README.md, docs/architecture.md, docs/design/principles.md, docs/development-process.md) +- [ADR 0095 — Root-aware project trust](docs/adr/0095-root-aware-project-trust.md): ADR 0095 — Root-aware project trust (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0092-no-project-trust-pin.md, docs/adr/0094-opt-in-project-ingestion.md, docs/adr/0202-diagnostic-only-posture-reporting.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md, docs/usage/mecak8s.md, docs/usage/mecated.md, docs/usage/mecatequi-ci.md, docs/usage/workspace-trust.md) - [ADR 0213 — Enforce caller ownership at remote driver boundaries](docs/adr/0213-driver-caller-ownership.md): ADR 0213 — Enforce caller ownership at remote driver boundaries (linked from: docs/acceptance/README.md, docs/acceptance/caller-separation.md, docs/acceptance/spine-convergence.md, docs/adr/0212-caller-ownership-enforcement.md, docs/adr/README.md, docs/architecture.md, docs/design/principles.md, docs/development-process.md) +- [ADR 0089 — CLI clean break: one canonical spelling per action](docs/adr/0089-cli-clean-break-grammar.md): ADR 0089 — CLI clean break: one canonical spelling per action (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0086-remove-output-economy-control.md, docs/adr/0087-mecatui-staged-transport-migration.md, docs/adr/0103-mecatui-seed-prompt.md, docs/adr/README.md, docs/architecture.md, docs/design/principles.md, docs/development-process.md) - [ADR 0093 — Ship the real LLM provider adapters as opt-in Go modules under provider/](docs/adr/0093-provider-modules.md): ADR 0093 — Ship the real LLM provider adapters as opt-in Go modules under provider/ (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0100-provider-prompt-caching.md, docs/adr/0216-provider-session-correlation-header.md, docs/adr/README.md, docs/architecture.md, docs/design/principles.md, docs/development-process.md) - [ADR 0111 — Hardened publication and recovery for agent-owned skills](docs/adr/0111-hardened-agent-owned-skill-publication.md): ADR 0111 — Hardened publication and recovery for agent-owned skills (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0110-evaluated-agent-owned-skills.md, docs/adr/0224-validated-automatic-skill-activation.md, docs/adr/README.md, docs/architecture.md, docs/design/principles.md, docs/development-process.md) - [ADR 0043 — Ephemeral turn-0 instruction fragments](docs/adr/0043-ephemeral-turn0-instruction-fragments.md): ADR 0043 — Ephemeral turn-0 instruction fragments (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0009-tiered-memory.md, docs/adr/0011-soul-and-user-model.md, docs/adr/0012-compaction.md, docs/adr/0081-rules-source-port.md, docs/adr/README.md, docs/design/IMPLEMENTATION-NOTES.md, docs/design/principles.md, docs/development-process.md) - [ADR 0216 — Correlate provider requests with the active session](docs/adr/0216-provider-session-correlation-header.md): ADR 0216 — Correlate provider requests with the active session (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/README.md, docs/architecture/providers.md, docs/design/IMPLEMENTATION-NOTES.md, docs/design/principles.md, docs/development-process.md) +- [ADR 0226 — Session storage separates current state, indexed metadata, and maintenance](docs/adr/0226-session-storage-maintenance.md): ADR 0226 — Session storage separates current state, indexed metadata, and maintenance (linked from: docs/acceptance/README.md, docs/acceptance/session-storage-continuity.md, docs/acceptance/spine-convergence.md, docs/adr/0229-redis-migration-fencing.md, docs/adr/0230-redis-migration-atomic-ownership-and-coverage.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md, engine/CHANGELOG.md) - [ADR 0047 — Absolute path resolution inside the workspace root](docs/adr/0047-absolute-path-resolution.md): ADR 0047 — Absolute path resolution inside the workspace root (linked from: docs/acceptance/README.md, docs/acceptance/path-escape-posture.md, docs/acceptance/spine-convergence.md, docs/adr/0080-guardrail-routed-escape-checking.md, docs/adr/0108-on-demand-logical-skill-assets.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md, docs/usage/mecated.md) - [ADR 0205 — Bound cached JWKS staleness](docs/adr/0205-bounded-jwks-staleness.md): ADR 0205 — Bound cached JWKS staleness (linked from: docs/acceptance/README.md, docs/acceptance/caller-identity.md, docs/acceptance/spine-convergence.md, docs/adr/0027-cloud-native.md, docs/adr/0206-oidc-authn-module.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md, docs/usage/mecated.md) -- [ADR 0226 — Session storage separates current state, indexed metadata, and maintenance](docs/adr/0226-session-storage-maintenance.md): ADR 0226 — Session storage separates current state, indexed metadata, and maintenance (linked from: docs/acceptance/README.md, docs/acceptance/session-storage-continuity.md, docs/acceptance/spine-convergence.md, docs/adr/0229-redis-migration-fencing.md, docs/adr/0230-redis-migration-atomic-ownership-and-coverage.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md, engine/CHANGELOG.md) - [ADR 0225 — Operator settings validation command](docs/adr/0225-operator-settings-validation.md): ADR 0225 — Operator settings validation command (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/README.md, docs/architecture.md, docs/design/principles.md, docs/development-process.md) - [ADR 0231 — Redis readiness requires exact owner-index coverage](docs/adr/0231-redis-owner-index-exact-coverage.md): ADR 0231 — Redis readiness requires exact owner-index coverage (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/README.md, docs/architecture.md, docs/design/principles.md, docs/development-process.md) - [ADR 0070 — Model-visible affordance gate](docs/adr/0070-model-visible-affordance-gate.md): ADR 0070 — Model-visible affordance gate (linked from: docs/acceptance/README.md, docs/acceptance/fire-result-delivery.md, docs/acceptance/schedule-shared-catalog.md, docs/acceptance/schedule-tool.md, docs/acceptance/spine-convergence.md, docs/adr/0073-schedule-tool.md, docs/adr/0076-schedule-shared-catalog.md, docs/adr/0200-resume-a-failed-subagent.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md) -- [ADR 0090 — Per-server operator opt-in for plain-http token-bearing MCP endpoints](docs/adr/0090-mcp-insecure-http-optin.md): ADR 0090 — Per-server operator opt-in for plain-http token-bearing MCP endpoints (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md, docs/usage/mecak8s.md, docs/usage/mecated.md, docs/usage/mecatequi-ci.md) - [ADR 0078 — MCP typed tool results](docs/adr/0078-mcp-typed-tool-results.md): ADR 0078 — MCP typed tool results (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0027-cloud-native.md, docs/adr/0063-mcp-structured-failclosed-callmcpwithquery.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md, engine/CHANGELOG.md) +- [ADR 0090 — Per-server operator opt-in for plain-http token-bearing MCP endpoints](docs/adr/0090-mcp-insecure-http-optin.md): ADR 0090 — Per-server operator opt-in for plain-http token-bearing MCP endpoints (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md, docs/usage/mecak8s.md, docs/usage/mecated.md, docs/usage/mecatequi-ci.md) +- [ADR 0232 — Steer-while-running: inject a user message into an in-flight run](docs/adr/0232-steer-while-running.md): ADR 0232 — Steer-while-running: inject a user message into an in-flight run (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/README.md, docs/architecture/agent-loop.md, docs/design/principles.md, docs/development-process.md, engine/CHANGELOG.md) - [ADR 0202 — Diagnostic-only posture reporting](docs/adr/0202-diagnostic-only-posture-reporting.md): ADR 0202 — Diagnostic-only posture reporting (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0095-root-aware-project-trust.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md, docs/usage/mecated.md) - [ADR 0081 — RulesSource port for .claude/rules discovery](docs/adr/0081-rules-source-port.md): ADR 0081 — RulesSource port for .claude/rules discovery (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/README.md, docs/design/IMPLEMENTATION-NOTES.md, docs/design/principles.md, docs/development-process.md, docs/usage/skills-soul-usermodel.md) - [ADR 0230 — Redis migration ownership and coverage are proved before mutation](docs/adr/0230-redis-migration-atomic-ownership-and-coverage.md): ADR 0230 — Redis migration ownership and coverage are proved before mutation (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0231-redis-owner-index-exact-coverage.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md, engine/CHANGELOG.md) @@ -119,6 +120,7 @@ - [ADR 0064 — Auto-detect the ToolHive LLM gateway proxy as a native provider](docs/adr/0064-toolhive-llm-gateway-provider.md): ADR 0064 — Auto-detect the ToolHive LLM gateway proxy as a native provider (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0067-openai-chat-completions-adapter.md, docs/adr/0102-toolhive-direct-mode.md, docs/adr/0215-openai-subscription-manual-token.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md) - [ADR 0035 — Surface the per-delegation model for ALL children, not just routed ones](docs/adr/0035-per-delegation-model-surface.md): ADR 0035 — Surface the per-delegation model for ALL children, not just routed ones (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0042-taxonomy-gated-model-router.md, docs/adr/0083-routing-reason-on-delegation-start.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md) - [ADR 0071 — Seamless model switch: always keep the conversation](docs/adr/0071-seamless-model-switch.md): ADR 0071 — Seamless model switch: always keep the conversation (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0016-multi-provider.md, docs/adr/0065-conversation-fork.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md) +- [ADR 0203 — Neutral permanent-provider-error signal](docs/adr/0203-permanent-provider-error-signal.md): ADR 0203 — Neutral permanent-provider-error signal (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0101-openai-reasoning-multiplicity.md, docs/adr/README.md, docs/architecture/agent-loop.md, docs/design/principles.md, docs/development-process.md) - [ADR 0082 — Factory MCP wiring for the one-shot mains](docs/adr/0082-factory-mcp-wiring.md): ADR 0082 — Factory MCP wiring for the one-shot mains (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0090-mcp-insecure-http-optin.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md, docs/usage/mecak8s.md, docs/usage/mecatequi-ci.md) - [ADR 0061 — Human one-shot guardrail override (/guardrail-allow)](docs/adr/0061-guardrails-human-override.md): ADR 0061 — Human one-shot guardrail override (/guardrail-allow) (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0060-guardrails-bash-default.md, docs/adr/0062-guardrails-approve-once.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md) - [ADR 0003 — Consolidate design records as ADRs](docs/adr/0003-consolidate-design-records-as-adrs.md): ADR 0003 — Consolidate design records as ADRs (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0036-engine-module.md, docs/adr/README.md, docs/design/README.md, docs/design/principles.md, docs/development-process.md) @@ -126,7 +128,6 @@ - [ADR 0092 — Project-trust suppression pin](docs/adr/0092-no-project-trust-pin.md): ADR 0092 — Project-trust suppression pin (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0094-opt-in-project-ingestion.md, docs/adr/0095-root-aware-project-trust.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md) - [ADR 0045 — Explicit-bucket latency histograms (zero-config quantiles on /metrics)](docs/adr/0045-explicit-bucket-latency-histograms.md): ADR 0045 — Explicit-bucket latency histograms (zero-config quantiles on /metrics) (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0018-perf-observability.md, docs/adr/0098-headless-telemetry.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md) - [ADR 0207 — Operator-owned exact context-window overrides](docs/adr/0207-context-window-overrides.md): ADR 0207 — Operator-owned exact context-window overrides (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/README.md, docs/architecture/context-and-compaction.md, docs/design/principles.md, docs/development-process.md, docs/usage/model-routing.md) -- [ADR 0203 — Neutral permanent-provider-error signal](docs/adr/0203-permanent-provider-error-signal.md): ADR 0203 — Neutral permanent-provider-error signal (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0101-openai-reasoning-multiplicity.md, docs/adr/README.md, docs/architecture/agent-loop.md, docs/design/principles.md, docs/development-process.md) - [ADR 0041 — Output-economy default prompt](docs/adr/0041-output-economy-default-prompt.md): ADR 0041 — Output-economy default prompt (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0054-reasoning-rebalance-default-prompt.md, docs/adr/0086-remove-output-economy-control.md, docs/adr/0089-cli-clean-break-grammar.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md) - [ADR 0079 — Converge delegation observability on two tiers (bounded previews for Subagent/Parallel)](docs/adr/0079-delegation-observability-convergence.md): ADR 0079 — Converge delegation observability on two tiers (bounded previews for Subagent/Parallel) (linked from: docs/acceptance/README.md, docs/acceptance/delegation-observability-convergence.md, docs/acceptance/spine-convergence.md, docs/adr/0083-routing-reason-on-delegation-start.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md) - [ADR 0058 — Writable named-specialist Subagent (mode:"read-write" + agent)](docs/adr/0058-writable-named-specialist-subagent.md): ADR 0058 — Writable named-specialist Subagent (mode:"read-write" + agent) (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0066-route-unpinned-and-writable-delegations.md, docs/adr/README.md, docs/design/principles.md, docs/development-process.md) @@ -153,7 +154,7 @@ - [Architecture Decision Records](docs/adr/README.md): Architecture Decision Records (linked from: docs/READING.md, docs/README.md, docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0003-consolidate-design-records-as-adrs.md, docs/architecture.md, docs/design/README.md, docs/design/principles.md, docs/development-process.md) - [ADR 0105 — Built-in bounded WebFetch](docs/adr/0105-built-in-webfetch.md): ADR 0105 — Built-in bounded WebFetch (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/architecture.md, docs/design/principles.md, docs/development-process.md) - [ADR 0101 — OpenAI reasoning multiplicity: adapter-packed items + bounded encrypted-content repair](docs/adr/0101-openai-reasoning-multiplicity.md): ADR 0101 — OpenAI reasoning multiplicity: adapter-packed items + bounded encrypted-content repair (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/architecture/providers.md, docs/design/principles.md, docs/development-process.md) -- [AGENTS.md — mecatl](AGENTS.md): AGENTS.md — mecatl (linked from: README.md, docs/README.md, docs/acceptance/README.md, docs/acceptance/caller-separation.md, docs/acceptance/delegation-observability-convergence.md, docs/acceptance/fire-result-delivery.md, docs/acceptance/path-escape-posture.md, docs/acceptance/schedule-shared-catalog.md, docs/acceptance/schedule-tool.md, docs/acceptance/session-continuity-ux.md, docs/acceptance/session-storage-continuity.md, docs/acceptance/spine-convergence.md, docs/adr/0204-caller-identity-threading.md, docs/adr/0212-caller-ownership-enforcement.md, docs/adr/0213-driver-caller-ownership.md, docs/design/principles.md) +- [AGENTS.md — mecatl](AGENTS.md): AGENTS.md — mecatl (linked from: README.md, docs/README.md, docs/acceptance/README.md, docs/acceptance/caller-separation.md, docs/acceptance/delegation-observability-convergence.md, docs/acceptance/fire-result-delivery.md, docs/acceptance/path-escape-posture.md, docs/acceptance/schedule-shared-catalog.md, docs/acceptance/schedule-tool.md, docs/acceptance/session-continuity-ux.md, docs/acceptance/session-storage-continuity.md, docs/acceptance/spine-convergence.md, docs/acceptance/steer-while-running.md, docs/adr/0204-caller-identity-threading.md, docs/adr/0212-caller-ownership-enforcement.md, docs/adr/0213-driver-caller-ownership.md, docs/design/principles.md) - [Implementation Notes](docs/design/IMPLEMENTATION-NOTES.md): Implementation Notes (linked from: docs/READING.md, docs/acceptance/delegation-observability-convergence.md, docs/acceptance/session-continuity-ux.md, docs/adr/0003-consolidate-design-records-as-adrs.md, docs/adr/0004-v1-architecture.md, docs/adr/0005-driver-seams.md, docs/adr/0006-v1-step-chain.md, docs/adr/0030-model-selection-heuristics.md, docs/adr/0031-subagent-model-router.md, docs/adr/0034-team-parallel-model-routing.md, docs/adr/0035-per-delegation-model-surface.md, docs/adr/0042-taxonomy-gated-model-router.md, docs/adr/0046-guardrails-slot-enable.md, docs/adr/0049-guardrails-remove-maxchecks.md, docs/adr/0050-guardrails-remove-maxcontentbytes.md, docs/adr/0066-route-unpinned-and-writable-delegations.md, docs/adr/0069-plan-approval-gate.md, docs/adr/0071-seamless-model-switch.md, docs/adr/0102-toolhive-direct-mode.md, docs/adr/0103-mecatui-seed-prompt.md, docs/adr/0200-resume-a-failed-subagent.md, docs/adr/0203-permanent-provider-error-signal.md, docs/adr/0208-execution-environment.md, docs/adr/0211-execution-environment-runtime-seam.md, docs/adr/0214-environment-persistence.md, docs/adr/0218-credential-store.md, docs/adr/0221-read-only-credential-source.md, docs/adr/0227-dream-consolidation-safety-boundary.md, docs/adr/0228-manual-dream-review.md, docs/adr/0229-redis-migration-fencing.md, docs/adr/0230-redis-migration-atomic-ownership-and-coverage.md, docs/adr/0231-redis-owner-index-exact-coverage.md, docs/architecture.md, docs/design/README.md, docs/design/principles.md, docs/development-process.md) - [Development process](docs/development-process.md): Development process (linked from: docs/acceptance/README.md, docs/acceptance/spine-convergence.md, docs/adr/0072-acceptance-plan-spine.md, docs/design/principles.md) - [Acceptance plans](docs/acceptance/README.md): Acceptance plans (linked from: docs/acceptance/spine-convergence.md, docs/adr/0072-acceptance-plan-spine.md, docs/design/principles.md, docs/development-process.md) @@ -161,12 +162,13 @@ - [mecatl — Usage & Operator Guide](docs/usage.md): mecatl — Usage & Operator Guide (linked from: AGENTS.md, README.md, docs/READING.md, docs/README.md, docs/adr/0030-model-selection-heuristics.md, docs/adr/0031-subagent-model-router.md, docs/adr/0036-engine-module.md, docs/adr/0042-taxonomy-gated-model-router.md, docs/adr/0046-guardrails-slot-enable.md, docs/adr/0048-mecak8s.md, docs/adr/0049-guardrails-remove-maxchecks.md, docs/adr/0050-guardrails-remove-maxcontentbytes.md, docs/adr/0051-guardrails-advisory-tui-visibility.md, docs/adr/0052-guardrails-checker-down-toggle.md, docs/adr/0064-toolhive-llm-gateway-provider.md, docs/adr/0065-conversation-fork.md, docs/adr/0069-plan-approval-gate.md, docs/adr/0102-toolhive-direct-mode.md, docs/architecture.md, docs/architecture/deployment-and-hardening.md, docs/configuration-reference.md, docs/design/MECAK8S-PLAN.md, docs/development-process.md, docs/tui.md, docs/usage/e2e.md, docs/usage/grpc-api.md, docs/usage/hooks.md, docs/usage/http-sse-api.md, docs/usage/mecak8s.md, docs/usage/openai-compatible.md, docs/usage/permissions-config.md, docs/usage/quickstart.md, docs/usage/skills-soul-usermodel.md, docs/usage/troubleshooting.md, docs/usage/workspace-trust.md) - [Production Readiness — status & roadmap](docs/design/PRODUCTION-READINESS.md): Production Readiness — status & roadmap (linked from: README.md, docs/READING.md, docs/README.md, docs/adr/0002-documentation-lifecycle.md, docs/adr/0003-consolidate-design-records-as-adrs.md, docs/adr/0007-twelve-patterns-audit.md, docs/adr/0048-mecak8s.md, docs/adr/0056-mcp-client-reconnect.md, docs/adr/0059-scheduled-tasks.md, docs/adr/0065-conversation-fork.md, docs/adr/0071-seamless-model-switch.md, docs/adr/0106-optional-learning-seam.md, docs/adr/0112-mcp-oauth-loopback-runtime.md, docs/adr/0113-operator-mcp-auth-profiles.md, docs/adr/0218-credential-store.md, docs/adr/0219-mcp-oauth-sdk-profile.md, docs/adr/0220-mcp-oauth-controller.md, docs/adr/0223-mcp-sdk-transport-error-semantics.md, docs/adr/0224-validated-automatic-skill-activation.md, docs/adr/0227-dream-consolidation-safety-boundary.md, docs/adr/0228-manual-dream-review.md, docs/adr/README.md, docs/design/MECAK8S-PLAN.md, docs/design/README.md) - [Providers — OpenAI adapter & multi-provider](docs/architecture/providers.md): Providers — OpenAI adapter & multi-provider (linked from: docs/READING.md, docs/adr/0030-model-selection-heuristics.md, docs/adr/0031-subagent-model-router.md, docs/adr/0034-team-parallel-model-routing.md, docs/adr/0035-per-delegation-model-surface.md, docs/adr/0042-taxonomy-gated-model-router.md, docs/adr/0064-toolhive-llm-gateway-provider.md, docs/adr/0066-route-unpinned-and-writable-delegations.md, docs/adr/0101-openai-reasoning-multiplicity.md, docs/adr/0102-toolhive-direct-mode.md, docs/adr/0216-provider-session-correlation-header.md, docs/architecture.md, docs/architecture/api-surface.md, docs/architecture/context-and-compaction.md, docs/architecture/extensibility.md, docs/architecture/observability.md, docs/architecture/ports.md, docs/architecture/subagents-and-teams.md, docs/design/PRODUCTION-READINESS.md) -- [The agent loop & permission pause/resume](docs/architecture/agent-loop.md): The agent loop & permission pause/resume (linked from: docs/READING.md, docs/adr/0069-plan-approval-gate.md, docs/adr/0106-optional-learning-seam.md, docs/adr/0107-operator-profile-memory-lifecycle.md, docs/adr/0203-permanent-provider-error-signal.md, docs/architecture.md, docs/architecture/api-surface.md, docs/architecture/context-and-compaction.md, docs/architecture/domain-model.md, docs/architecture/extensibility.md, docs/architecture/hooks-and-guardrails.md, docs/architecture/memory.md, docs/architecture/parallelism.md, docs/architecture/ports.md, docs/architecture/subagents-and-teams.md, docs/design/PRODUCTION-READINESS.md) +- [The agent loop & permission pause/resume](docs/architecture/agent-loop.md): The agent loop & permission pause/resume (linked from: docs/READING.md, docs/adr/0069-plan-approval-gate.md, docs/adr/0106-optional-learning-seam.md, docs/adr/0107-operator-profile-memory-lifecycle.md, docs/adr/0203-permanent-provider-error-signal.md, docs/adr/0232-steer-while-running.md, docs/architecture.md, docs/architecture/api-surface.md, docs/architecture/context-and-compaction.md, docs/architecture/domain-model.md, docs/architecture/extensibility.md, docs/architecture/hooks-and-guardrails.md, docs/architecture/memory.md, docs/architecture/parallelism.md, docs/architecture/ports.md, docs/architecture/subagents-and-teams.md, docs/design/PRODUCTION-READINESS.md) - [Caller identity — completed acceptance record](docs/acceptance/caller-identity.md): Caller identity — completed acceptance record (linked from: docs/acceptance/README.md, docs/adr/0204-caller-identity-threading.md) - [Schedule tool — acceptance plan](docs/acceptance/schedule-tool.md): Schedule tool — acceptance plan (linked from: docs/acceptance/README.md, docs/acceptance/schedule-shared-catalog.md) +- [Steer-while-running — acceptance plan](docs/acceptance/steer-while-running.md): Steer-while-running — acceptance plan (linked from: docs/acceptance/README.md, docs/adr/0232-steer-while-running.md) +- [The ports (engine/port)](docs/architecture/ports.md): The ports (engine/port) (linked from: docs/READING.md, docs/acceptance/session-continuity-ux.md, docs/architecture.md, docs/architecture/agent-loop.md, docs/architecture/domain-model.md, docs/architecture/extensibility.md, docs/architecture/observability.md, docs/architecture/providers.md, docs/architecture/subagents-and-teams.md, docs/design/PRODUCTION-READINESS.md) - [Path-escape posture relax — acceptance plan](docs/acceptance/path-escape-posture.md): Path-escape posture relax — acceptance plan (linked from: docs/acceptance/README.md, docs/adr/0080-guardrail-routed-escape-checking.md) - [Observability, persistence & reliability](docs/architecture/observability.md): Observability, persistence & reliability (linked from: docs/READING.md, docs/adr/0108-on-demand-logical-skill-assets.md, docs/architecture.md, docs/architecture/api-surface.md, docs/architecture/context-and-compaction.md, docs/architecture/deployment-and-hardening.md, docs/architecture/extensibility.md, docs/architecture/ports.md, docs/architecture/providers.md, docs/design/PRODUCTION-READINESS.md) -- [The ports (engine/port)](docs/architecture/ports.md): The ports (engine/port) (linked from: docs/READING.md, docs/acceptance/session-continuity-ux.md, docs/architecture.md, docs/architecture/agent-loop.md, docs/architecture/domain-model.md, docs/architecture/extensibility.md, docs/architecture/observability.md, docs/architecture/providers.md, docs/architecture/subagents-and-teams.md, docs/design/PRODUCTION-READINESS.md) - [Caller separation — acceptance plan](docs/acceptance/caller-separation.md): Caller separation — acceptance plan (linked from: docs/acceptance/README.md) - [Delegation observability convergence — acceptance plan](docs/acceptance/delegation-observability-convergence.md): Delegation observability convergence — acceptance plan (linked from: docs/acceptance/README.md) - [Fire-result delivery — acceptance plan](docs/acceptance/fire-result-delivery.md): Fire-result delivery — acceptance plan (linked from: docs/acceptance/README.md) @@ -180,8 +182,8 @@ - [Context management & the compaction cascade](docs/architecture/context-and-compaction.md): Context management & the compaction cascade (linked from: docs/READING.md, docs/adr/0207-context-window-overrides.md, docs/architecture.md, docs/architecture/agent-loop.md, docs/architecture/memory.md, docs/architecture/providers.md, docs/design/PRODUCTION-READINESS.md) - [Memory — cross-session recall & consolidation](docs/architecture/memory.md): Memory — cross-session recall & consolidation (linked from: docs/READING.md, docs/adr/0106-optional-learning-seam.md, docs/adr/0107-operator-profile-memory-lifecycle.md, docs/adr/0109-staged-learning-proposals.md, docs/adr/0227-dream-consolidation-safety-boundary.md, docs/adr/0228-manual-dream-review.md, docs/architecture.md, docs/architecture/agent-loop.md, docs/architecture/context-and-compaction.md, docs/design/PRODUCTION-READINESS.md) - [Hooks & guardrails](docs/architecture/hooks-and-guardrails.md): Hooks & guardrails (linked from: docs/READING.md, docs/adr/0051-guardrails-advisory-tui-visibility.md, docs/architecture.md, docs/architecture/agent-loop.md, docs/architecture/api-surface.md, docs/architecture/deployment-and-hardening.md, docs/design/PRODUCTION-READINESS.md) -- [Deployment & server hardening](docs/architecture/deployment-and-hardening.md): Deployment & server hardening (linked from: docs/READING.md, docs/architecture.md, docs/architecture/api-surface.md, docs/architecture/hooks-and-guardrails.md, docs/architecture/observability.md, docs/architecture/parallelism.md, docs/design/PRODUCTION-READINESS.md) - [Parallelism — fork-join](docs/architecture/parallelism.md): Parallelism — fork-join (linked from: docs/READING.md, docs/architecture.md, docs/architecture/agent-loop.md, docs/architecture/api-surface.md, docs/architecture/subagents-and-teams.md, docs/design/PRODUCTION-READINESS.md) +- [Deployment & server hardening](docs/architecture/deployment-and-hardening.md): Deployment & server hardening (linked from: docs/READING.md, docs/architecture.md, docs/architecture/api-surface.md, docs/architecture/hooks-and-guardrails.md, docs/architecture/observability.md, docs/architecture/parallelism.md, docs/design/PRODUCTION-READINESS.md) - [Engine compatibility policy](engine/COMPATIBILITY.md): Engine compatibility policy (linked from: AGENTS.md, README.md, docs/READING.md, docs/README.md, docs/acceptance/delegation-observability-convergence.md, docs/adr/0037-engine-stability-contract.md, docs/adr/0038-event-sourced-rehydration.md, docs/adr/0110-evaluated-agent-owned-skills.md, docs/adr/0204-caller-identity-threading.md, docs/architecture.md, docs/architecture/api-surface.md, docs/architecture/extensibility.md, engine/CHANGELOG.md, engine/api/README.md) - [mecatui — the terminal UI for mecatl](docs/tui.md): mecatui — the terminal UI for mecatl (linked from: README.md, docs/acceptance/session-storage-continuity.md, docs/adr/0068-effort-change-via-fork.md, docs/adr/0071-seamless-model-switch.md, docs/adr/0103-mecatui-seed-prompt.md, docs/adr/0203-permanent-provider-error-signal.md, docs/adr/0217-session-discovery-continuation.md, docs/adr/0222-mecatui-ask-args-view.md, docs/adr/0226-session-storage-maintenance.md, docs/adr/0228-manual-dream-review.md, docs/architecture.md, docs/usage.md, docs/usage/configuration.md) - [mecak8s — MVP Implementation Plan](docs/design/MECAK8S-PLAN.md): mecak8s — MVP Implementation Plan (linked from: docs/adr/0048-mecak8s.md, docs/design/PRODUCTION-READINESS.md) @@ -240,39 +242,39 @@ _Associative trails (Bush 1945): a topologically-valid path through each cluster 8. [ADR 0037 — engine public-API stability contract](docs/adr/0037-engine-stability-contract.md) 9. [ADR 0036 — engine/ is its own Go module (monorepo via go.work)](docs/adr/0036-engine-module.md) 10. [ADR 0021 — Guardrails: LLM-backed tool-content inspection](docs/adr/0021-guardrails.md) -11. [ADR 0016 — Multi-provider and multi-model](docs/adr/0016-multi-provider.md) +11. [ADR 0038 — Event-sourced SessionStore rehydration (the reference fold)](docs/adr/0038-event-sourced-rehydration.md) 12. [Engine compatibility policy](engine/COMPATIBILITY.md) -13. [ADR 0038 — Event-sourced SessionStore rehydration (the reference fold)](docs/adr/0038-event-sourced-rehydration.md) +13. [ADR 0016 — Multi-provider and multi-model](docs/adr/0016-multi-provider.md) 14. [ADR 0004 — v1 Architecture: hexagonal DDD harness](docs/adr/0004-v1-architecture.md) 15. [ADR 0003 — Consolidate design records as ADRs](docs/adr/0003-consolidate-design-records-as-adrs.md) -16. [ADR 0023 — Workspace Trust](docs/adr/0023-workspace-trust.md) -17. [The agent loop & permission pause/resume](docs/architecture/agent-loop.md) +16. [The agent loop & permission pause/resume](docs/architecture/agent-loop.md) +17. [ADR 0023 — Workspace Trust](docs/adr/0023-workspace-trust.md) 18. [Architecture Decision Records](docs/adr/README.md) 19. [mecatl — Agentic Coding Harness](docs/architecture/mecatl.modelith.md) 20. [ADR 0022 — Unattended / allow-all posture (the "YOLO mode" question)](docs/adr/0022-allow-all-posture.md) 21. [ADR 0204 — Caller identity: accept a principal, thread it everywhere, record the owner](docs/adr/0204-caller-identity-threading.md) 22. [Providers — OpenAI adapter & multi-provider](docs/architecture/providers.md) 23. [ADR NNNN —](docs/adr/template.md) -24. [ADR 0018 — Performance observability](docs/adr/0018-perf-observability.md) -25. [ADR 0015 — Background subagents and per-child cancel](docs/adr/0015-background-subagents.md) +24. [ADR 0015 — Background subagents and per-child cancel](docs/adr/0015-background-subagents.md) +25. [ADR 0018 — Performance observability](docs/adr/0018-perf-observability.md) 26. [ADR 0011 — Soul and user-model: persistent identity and cross-session learning](docs/adr/0011-soul-and-user-model.md) 27. [Extensibility — MCP, tools & progressive disclosure](docs/architecture/extensibility.md) 28. [mecatui — the terminal UI for mecatl](docs/tui.md) 29. [Platform principles](docs/design/principles.md) -30. [3. Running the server (mecated)](docs/usage/mecated.md) -31. [ADR 0005 — Driver seams: ports, the gRPC driver protocol, and conformance](docs/adr/0005-driver-seams.md) +30. [ADR 0005 — Driver seams: ports, the gRPC driver protocol, and conformance](docs/adr/0005-driver-seams.md) +31. [3. Running the server (mecated)](docs/usage/mecated.md) 32. [ADR 0014 — Agent teams](docs/adr/0014-agent-teams.md) 33. [ADR 0043 — Ephemeral turn-0 instruction fragments](docs/adr/0043-ephemeral-turn0-instruction-fragments.md) -34. [ADR 0009 — Tiered memory: tier-0 index and BM25 search](docs/adr/0009-tiered-memory.md) -35. [ADR 0024 — System-Prompt Research and Enhancement](docs/adr/0024-system-prompt-research.md) -36. [mecatl](README.md) +34. [mecatl](README.md) +35. [ADR 0009 — Tiered memory: tier-0 index and BM25 search](docs/adr/0009-tiered-memory.md) +36. [ADR 0024 — System-Prompt Research and Enhancement](docs/adr/0024-system-prompt-research.md) 37. [ADR 0012 — Conversation compaction](docs/adr/0012-compaction.md) -38. [ADR 0017 — OpenAI Responses API adapter](docs/adr/0017-openai-responses-api.md) -39. [The ports (engine/port)](docs/architecture/ports.md) -40. [ADR 0069 — Plan-approval gate](docs/adr/0069-plan-approval-gate.md) +38. [ADR 0069 — Plan-approval gate](docs/adr/0069-plan-approval-gate.md) +39. [ADR 0017 — OpenAI Responses API adapter](docs/adr/0017-openai-responses-api.md) +40. [The ports (engine/port)](docs/architecture/ports.md) 41. [The API surface](docs/architecture/api-surface.md) -42. [Memory — cross-session recall & consolidation](docs/architecture/memory.md) -43. [ADR 0019 — Long-term performance and resource regression tracking](docs/adr/0019-perf-tracking.md) +42. [ADR 0019 — Long-term performance and resource regression tracking](docs/adr/0019-perf-tracking.md) +43. [Memory — cross-session recall & consolidation](docs/architecture/memory.md) 44. [Observability, persistence & reliability](docs/architecture/observability.md) 45. [ADR 0086 — Remove the output-economy control surface](docs/adr/0086-remove-output-economy-control.md) 46. [ADR 0020 — Diagnostics, audit, and the global-slog ban](docs/adr/0020-diagnostics.md) @@ -281,36 +283,36 @@ _Associative trails (Bush 1945): a topologically-valid path through each cluster 49. [ADR 0059 — Scheduled tasks: durable registry, tick loop, leader-lease, claim-before-fire at-most-once](docs/adr/0059-scheduled-tasks.md) 50. [ADR 0030 — Layered model-selection heuristics](docs/adr/0030-model-selection-heuristics.md) 51. [ADR 0048 — mecak8s: Kubernetes-native agent harness (storage-free, managed-service state)](docs/adr/0048-mecak8s.md) -52. [Subagents & teams](docs/architecture/subagents-and-teams.md) +52. [AGENTS.md — mecatl](AGENTS.md) 53. [ADR 0013 — Agent definitions (Tier 1)](docs/adr/0013-agent-definitions.md) -54. [Context management & the compaction cascade](docs/architecture/context-and-compaction.md) +54. [Subagents & teams](docs/architecture/subagents-and-teams.md) 55. [Deployment & server hardening](docs/architecture/deployment-and-hardening.md) 56. [ADR 0049 — Remove the guardrails per-session checker call-count cap](docs/adr/0049-guardrails-remove-maxchecks.md) -57. [Hooks & guardrails](docs/architecture/hooks-and-guardrails.md) +57. [Context management & the compaction cascade](docs/architecture/context-and-compaction.md) 58. [ADR 0041 — Output-economy default prompt](docs/adr/0041-output-economy-default-prompt.md) -59. [AGENTS.md — mecatl](AGENTS.md) -60. [ADR 0010 — Semantic memory recall: BM25 shipped, semantic deferred](docs/adr/0010-semantic-memory-recall.md) -61. [ADR 0214 — Execution-environment persistence and reattachment](docs/adr/0214-environment-persistence.md) +59. [ADR 0010 — Semantic memory recall: BM25 shipped, semantic deferred](docs/adr/0010-semantic-memory-recall.md) +60. [ADR 0214 — Execution-environment persistence and reattachment](docs/adr/0214-environment-persistence.md) +61. [Hooks & guardrails](docs/architecture/hooks-and-guardrails.md) 62. [ADR 0089 — CLI clean break: one canonical spelling per action](docs/adr/0089-cli-clean-break-grammar.md) 63. [11. Configuration](docs/usage/configuration.md) -64. [mecatl — Progressive reading map](docs/READING.md) -65. [ADR 0051 — Surface advisory guardrail findings to the TUI](docs/adr/0051-guardrails-advisory-tui-visibility.md) -66. [ADR 0031 — The operator-gated semantic subagent model router](docs/adr/0031-subagent-model-router.md) -67. [ADR 0078 — MCP typed tool results](docs/adr/0078-mcp-typed-tool-results.md) -68. [ADR 0073 — Schedule tool: model-facing scheduling, on-by-default, drop the declarative/CLI surfaces](docs/adr/0073-schedule-tool.md) -69. [Cloud-Native Harness Kit — definition](docs/cloud-native-harness-kit.md) -70. [Parallelism — fork-join](docs/architecture/parallelism.md) -71. [ADR 0218 — Internal encrypted credential-store substrate](docs/adr/0218-credential-store.md) -72. [Measuring & observing performance in Go — a reference for mecatl](docs/perf-measurement-survey.md) -73. [ADR 0028 — mecatequi: Single-Shot GitHub Action Runner](docs/adr/0028-mecatequi.md) +64. [ADR 0078 — MCP typed tool results](docs/adr/0078-mcp-typed-tool-results.md) +65. [mecatl — Progressive reading map](docs/READING.md) +66. [ADR 0051 — Surface advisory guardrail findings to the TUI](docs/adr/0051-guardrails-advisory-tui-visibility.md) +67. [ADR 0031 — The operator-gated semantic subagent model router](docs/adr/0031-subagent-model-router.md) +68. [Cloud-Native Harness Kit — definition](docs/cloud-native-harness-kit.md) +69. [ADR 0073 — Schedule tool: model-facing scheduling, on-by-default, drop the declarative/CLI surfaces](docs/adr/0073-schedule-tool.md) +70. [ADR 0218 — Internal encrypted credential-store substrate](docs/adr/0218-credential-store.md) +71. [Measuring & observing performance in Go — a reference for mecatl](docs/perf-measurement-survey.md) +72. [ADR 0028 — mecatequi: Single-Shot GitHub Action Runner](docs/adr/0028-mecatequi.md) +73. [Parallelism — fork-join](docs/architecture/parallelism.md) 74. [ADR 0006 — v1 Implementation step-chain](docs/adr/0006-v1-step-chain.md) -75. [ADR 0062 — Out-of-band approve-once for guardrail blocks](docs/adr/0062-guardrails-approve-once.md) -76. [ADR 0025 — mecatui UX Discoverability](docs/adr/0025-ux-discoverability.md) -77. [ADR 0026 — Clipboard Image Paste](docs/adr/0026-clipboard-image-paste.md) -78. [ADR 0212 — Enforce caller ownership at every application access path](docs/adr/0212-caller-ownership-enforcement.md) +75. [ADR 0025 — mecatui UX Discoverability](docs/adr/0025-ux-discoverability.md) +76. [ADR 0026 — Clipboard Image Paste](docs/adr/0026-clipboard-image-paste.md) +77. [ADR 0062 — Out-of-band approve-once for guardrail blocks](docs/adr/0062-guardrails-approve-once.md) +78. [ADR 0007 — Twelve agentic-harness patterns: pluggability audit](docs/adr/0007-twelve-patterns-audit.md) 79. [ADR 0095 — Root-aware project trust](docs/adr/0095-root-aware-project-trust.md) -80. [Configuration reference](docs/configuration-reference.md) -81. [ADR 0007 — Twelve agentic-harness patterns: pluggability audit](docs/adr/0007-twelve-patterns-audit.md) +80. [ADR 0212 — Enforce caller ownership at every application access path](docs/adr/0212-caller-ownership-enforcement.md) +81. [Configuration reference](docs/configuration-reference.md) 82. [ADR 0071 — Seamless model switch: always keep the conversation](docs/adr/0071-seamless-model-switch.md) 83. [ADR 0208 — Execution environments and version-aware file mutation](docs/adr/0208-execution-environment.md) 84. [ADR 0008 — Memory on by default in the embedded mecatui server](docs/adr/0008-memory-on-by-default.md) @@ -324,10 +326,10 @@ _Associative trails (Bush 1945): a topologically-valid path through each cluster 92. [ADR 0075 — Fire-result delivery: a scheduled fire reports back into the originating chat](docs/adr/0075-fire-result-delivery.md) 93. [ADR 0066 — Route unpinned agent-defs and writable explorers; explicit inherit is the pin](docs/adr/0066-route-unpinned-and-writable-delegations.md) 94. [ADR 0055 — Reasoning-effort knob](docs/adr/0055-reasoning-effort.md) -95. [The domain model](docs/architecture/domain-model.md) -96. [ADR 0001 — Agent Client Protocol (ACP) adapter](docs/adr/0001-acp-adapter.md) -97. [ADR 0060 — Add Bash to the default guardrail rule set with a read-only pre-filter](docs/adr/0060-guardrails-bash-default.md) -98. [ADR 0108 — Read skill assets on demand by logical name](docs/adr/0108-on-demand-logical-skill-assets.md) +95. [ADR 0001 — Agent Client Protocol (ACP) adapter](docs/adr/0001-acp-adapter.md) +96. [The domain model](docs/architecture/domain-model.md) +97. [ADR 0108 — Read skill assets on demand by logical name](docs/adr/0108-on-demand-logical-skill-assets.md) +98. [ADR 0060 — Add Bash to the default guardrail rule set with a read-only pre-filter](docs/adr/0060-guardrails-bash-default.md) 99. [ADR 0093 — Ship the real LLM provider adapters as opt-in Go modules under provider/](docs/adr/0093-provider-modules.md) 100. [7. Skills, soul, user model](docs/usage/skills-soul-usermodel.md) 101. [ADR 0111 — Hardened publication and recovery for agent-owned skills](docs/adr/0111-hardened-agent-owned-skill-publication.md) @@ -336,13 +338,13 @@ _Associative trails (Bush 1945): a topologically-valid path through each cluster 104. [ADR 0100 — Provider-side conversation prompt caching (Anthropic, OpenAI, OpenRouter, openaichat)](docs/adr/0100-provider-prompt-caching.md) 105. [ADR 0219 — Qualify the official MCP SDK authorization-code profile](docs/adr/0219-mcp-oauth-sdk-profile.md) 106. [ADR 0046 — Guardrails slot enables (configure = enable)](docs/adr/0046-guardrails-slot-enable.md) -107. [ADR 0209 — Attribute schedule origins through the run context](docs/adr/0209-schedule-origin-run-context.md) -108. [9. The gRPC API](docs/usage/grpc-api.md) -109. [10. The HTTP / SSE API](docs/usage/http-sse-api.md) -110. [ADR 0054 — Reasoning rebalance of the default-tone prompt](docs/adr/0054-reasoning-rebalance-default-prompt.md) -111. [ADR 0039 — Parallel single-branch auto-merge](docs/adr/0039-parallel-auto-merge.md) -112. [Agent identity: mecatl as its own SPIFFE trust domain](docs/agent-identity-model.md) -113. [ADR 0215 — OpenAI subscription with a manual access token](docs/adr/0215-openai-subscription-manual-token.md) +107. [9. The gRPC API](docs/usage/grpc-api.md) +108. [10. The HTTP / SSE API](docs/usage/http-sse-api.md) +109. [ADR 0209 — Attribute schedule origins through the run context](docs/adr/0209-schedule-origin-run-context.md) +110. [Agent identity: mecatl as its own SPIFFE trust domain](docs/agent-identity-model.md) +111. [ADR 0054 — Reasoning rebalance of the default-tone prompt](docs/adr/0054-reasoning-rebalance-default-prompt.md) +112. [ADR 0215 — OpenAI subscription with a manual access token](docs/adr/0215-openai-subscription-manual-token.md) +113. [ADR 0039 — Parallel single-branch auto-merge](docs/adr/0039-parallel-auto-merge.md) 114. [ADR 0082 — Factory MCP wiring for the one-shot mains](docs/adr/0082-factory-mcp-wiring.md) 115. [ADR 0045 — Explicit-bucket latency histograms (zero-config quantiles on /metrics)](docs/adr/0045-explicit-bucket-latency-histograms.md) 116. [ADR 0206 — Ship reusable OIDC caller identity as an opt-in module](docs/adr/0206-oidc-authn-module.md) @@ -354,11 +356,11 @@ _Associative trails (Bush 1945): a topologically-valid path through each cluster 122. [ADR 0220 — Adapter-local MCP OAuth controller](docs/adr/0220-mcp-oauth-controller.md) 123. [ADR 0053 — Flip guardrails default mode from advisory to block](docs/adr/0053-guardrails-default-block.md) 124. [ADR 0106 — Optional completed-trajectory learning seam](docs/adr/0106-optional-learning-seam.md) -125. [ADR 0107 — Live operator profiles and reversible memory lifecycle](docs/adr/0107-operator-profile-memory-lifecycle.md) -126. [ADR 0216 — Correlate provider requests with the active session](docs/adr/0216-provider-session-correlation-header.md) -127. [Scoped resource grants: a filesystem and tool substrate](docs/scoped-resource-grants.md) -128. [ADR 0065 — Conversation fork: peer session from a history snapshot](docs/adr/0065-conversation-fork.md) -129. [1. Prerequisites & install](docs/usage/install.md) +125. [Scoped resource grants: a filesystem and tool substrate](docs/scoped-resource-grants.md) +126. [ADR 0107 — Live operator profiles and reversible memory lifecycle](docs/adr/0107-operator-profile-memory-lifecycle.md) +127. [ADR 0216 — Correlate provider requests with the active session](docs/adr/0216-provider-session-correlation-header.md) +128. [1. Prerequisites & install](docs/usage/install.md) +129. [ADR 0065 — Conversation fork: peer session from a history snapshot](docs/adr/0065-conversation-fork.md) 130. [ADR 0061 — Human one-shot guardrail override (/guardrail-allow)](docs/adr/0061-guardrails-human-override.md) 131. [ADR 0032 — First-class worktree binding for a session](docs/adr/0032-worktree-binding.md) 132. [ADR 0087 — Staged mecatui transport migration](docs/adr/0087-mecatui-staged-transport-migration.md) @@ -368,82 +370,84 @@ _Associative trails (Bush 1945): a topologically-valid path through each cluster 136. [ADR 0224 — Validated automatic learned-skill activation](docs/adr/0224-validated-automatic-skill-activation.md) 137. [ADR 0222 — mecatui: ctrl+t routes by ask type; full-screen ask-args view](docs/adr/0222-mecatui-ask-args-view.md) 138. [ADR 0064 — Auto-detect the ToolHive LLM gateway proxy as a native provider](docs/adr/0064-toolhive-llm-gateway-provider.md) -139. [ADR 0203 — Neutral permanent-provider-error signal](docs/adr/0203-permanent-provider-error-signal.md) -140. [ADR 0112 — Host-owned loopback MCP OAuth login](docs/adr/0112-mcp-oauth-loopback-runtime.md) -141. [ADR 0225 — Operator settings validation command](docs/adr/0225-operator-settings-validation.md) -142. [ADR 0231 — Redis readiness requires exact owner-index coverage](docs/adr/0231-redis-owner-index-exact-coverage.md) +139. [ADR 0112 — Host-owned loopback MCP OAuth login](docs/adr/0112-mcp-oauth-loopback-runtime.md) +140. [ADR 0225 — Operator settings validation command](docs/adr/0225-operator-settings-validation.md) +141. [ADR 0231 — Redis readiness requires exact owner-index coverage](docs/adr/0231-redis-owner-index-exact-coverage.md) +142. [ADR 0057 — MCP client: consume server-initiated notifications](docs/adr/0057-mcp-server-notifications.md) 143. [ADR 0033 — Dirty-aware read-only fork (uncommitted-state overlay)](docs/adr/0033-dirty-aware-readonly-fork.md) -144. [ADR 0057 — MCP client: consume server-initiated notifications](docs/adr/0057-mcp-server-notifications.md) +144. [ADR 0203 — Neutral permanent-provider-error signal](docs/adr/0203-permanent-provider-error-signal.md) 145. [ADR 0105 — Built-in bounded WebFetch](docs/adr/0105-built-in-webfetch.md) 146. [ADR 0047 — Absolute path resolution inside the workspace root](docs/adr/0047-absolute-path-resolution.md) 147. [ADR 0113 — Operator-configured MCP authentication profiles](docs/adr/0113-operator-mcp-auth-profiles.md) 148. [ADR 0092 — Project-trust suppression pin](docs/adr/0092-no-project-trust-pin.md) 149. [ADR 0207 — Operator-owned exact context-window overrides](docs/adr/0207-context-window-overrides.md) 150. [ADR 0202 — Diagnostic-only posture reporting](docs/adr/0202-diagnostic-only-posture-reporting.md) -151. [14. OpenAI & compatible endpoints](docs/usage/openai-compatible.md) -152. [Caller identity — completed acceptance record](docs/acceptance/caller-identity.md) -153. [ADR 0226 — Session storage separates current state, indexed metadata, and maintenance](docs/adr/0226-session-storage-maintenance.md) -154. [2. The 60-second demo](docs/usage/quickstart.md) -155. [ADR 0210 — OpenRouter downstream-provider steering + routing echo](docs/adr/0210-openrouter-downstream-provider-steering.md) -156. [ADR 0201 — Background Bash commands](docs/adr/0201-background-bash.md) -157. [mecak8s — MVP Implementation Plan](docs/design/MECAK8S-PLAN.md) -158. [ADR 0040 — Writable Subagent mode + serialized merge-back](docs/adr/0040-writable-subagent-and-serialized-merge.md) -159. [ADR 0230 — Redis migration ownership and coverage are proved before mutation](docs/adr/0230-redis-migration-atomic-ownership-and-coverage.md) +151. [Caller identity — completed acceptance record](docs/acceptance/caller-identity.md) +152. [14. OpenAI & compatible endpoints](docs/usage/openai-compatible.md) +153. [2. The 60-second demo](docs/usage/quickstart.md) +154. [ADR 0226 — Session storage separates current state, indexed metadata, and maintenance](docs/adr/0226-session-storage-maintenance.md) +155. [ADR 0232 — Steer-while-running: inject a user message into an in-flight run](docs/adr/0232-steer-while-running.md) +156. [ADR 0210 — OpenRouter downstream-provider steering + routing echo](docs/adr/0210-openrouter-downstream-provider-steering.md) +157. [ADR 0201 — Background Bash commands](docs/adr/0201-background-bash.md) +158. [mecak8s — MVP Implementation Plan](docs/design/MECAK8S-PLAN.md) +159. [ADR 0040 — Writable Subagent mode + serialized merge-back](docs/adr/0040-writable-subagent-and-serialized-merge.md) 160. [ADR 0102 — ToolHive LLM gateway DIRECT mode (in-process OIDC token injection)](docs/adr/0102-toolhive-direct-mode.md) -161. [Cloud-Native Harness Systems — future work scoping](docs/cloud-native-harness-systems.md) -162. [ADR 0090 — Per-server operator opt-in for plain-http token-bearing MCP endpoints](docs/adr/0090-mcp-insecure-http-optin.md) -163. [ADR 0223 — Pin MCP transport-error semantics that do not replay rejected calls](docs/adr/0223-mcp-sdk-transport-error-semantics.md) -164. [ADR 0094 — Opt-in project ingestion: two-axis positive grants](docs/adr/0094-opt-in-project-ingestion.md) -165. [16. Live e2e suite](docs/usage/e2e.md) -166. [4. Guardrails — LLM-backed tool-content inspection (guardrails:)](docs/usage/guardrails.md) -167. [8. mecak8s (Kubernetes-native agent)](docs/usage/mecak8s.md) -168. [15. Running mecatequi from GitHub Actions](docs/usage/mecatequi-ci.md) -169. [17. Troubleshooting / FAQ](docs/usage/troubleshooting.md) -170. [ADR 0035 — Surface the per-delegation model for ALL children, not just routed ones](docs/adr/0035-per-delegation-model-surface.md) -171. [ADR 0217 — Session discovery uses durable kind metadata and an authoritative transcript](docs/adr/0217-session-discovery-continuation.md) -172. [Example skills](docs/examples/README.md) -173. [ADR 0104 — Session families get a bounded, injective, non-reversible physical name](docs/adr/0104-session-family-physical-naming.md) -174. [ADR 0052 — Global guardrails checker-down posture toggle](docs/adr/0052-guardrails-checker-down-toggle.md) -175. [ADR 0067 — OpenAI Chat Completions adapter (OpenCode Go provider)](docs/adr/0067-openai-chat-completions-adapter.md) -176. [ADR 0083 — Expose the routing miss/gate reason on delegation-start events](docs/adr/0083-routing-reason-on-delegation-start.md) -177. [Agent identity, part 2: the outbound hop](docs/agent-identity-outbound.md) -178. [ADR 0101 — OpenAI reasoning multiplicity: adapter-packed items + bounded encrypted-content repair](docs/adr/0101-openai-reasoning-multiplicity.md) -179. [ADR 0079 — Converge delegation observability on two tiers (bounded previews for Subagent/Parallel)](docs/adr/0079-delegation-observability-convergence.md) -180. [ADR 0229 — Redis migration uses fenced renewable ownership and indexed coverage](docs/adr/0229-redis-migration-fencing.md) -181. [ADR 0058 — Writable named-specialist Subagent (mode:"read-write" + agent)](docs/adr/0058-writable-named-specialist-subagent.md) -182. [ADR 0029 — Repo-map Tree-sitter Binding](docs/adr/0029-repomap-tree-sitter.md) -183. [ADR 0068 — Change reasoning effort via conversation fork (keep the transcript)](docs/adr/0068-effort-change-via-fork.md) -184. [ADR 0098 — Telemetry for the headless binaries (mecatequi, mecak8s)](docs/adr/0098-headless-telemetry.md) -185. [ADR 0088 — Explicit daemon.yaml (listener topology config)](docs/adr/0088-daemon-config-file.md) -186. [ADR 0097 — Scheduled fires are observable in-flight: a first-class persisted lifecycle stage](docs/adr/0097-scheduled-fire-inflight-state.md) -187. [mecatl documentation](docs/README.md) -188. [ADR 0076 — The schedule manager is store-shaped and pre-Service; the shared catalog carries the Schedule tool](docs/adr/0076-schedule-shared-catalog.md) -189. [ADR 0096 — mecatui live-feed reconnect: client-owned backoff + durable catch-up, no server cursor](docs/adr/0096-live-feed-reconnect.md) -190. [ADR 0080 — Guardrail-routed path-escape checking (composition pre-check, auto-only)](docs/adr/0080-guardrail-routed-escape-checking.md) -191. [Acceptance plans](docs/acceptance/README.md) -192. [Development process](docs/development-process.md) -193. [engine/api/ — committed public-API snapshots](engine/api/README.md) -194. [ADR 0227 — Dream consolidation safety boundary](docs/adr/0227-dream-consolidation-safety-boundary.md) -195. [Path-escape posture relax — acceptance plan](docs/acceptance/path-escape-posture.md) -196. [ADR 0044 — Host-supplied askID discriminator (cross-process-reconstructable askID)](docs/adr/0044-host-supplied-askid-discriminator.md) -197. [ADR 0063 — MCP structured results: fail-closed + CallMcpWithQuery](docs/adr/0063-mcp-structured-failclosed-callmcpwithquery.md) -198. [ADR 0072 — The acceptance-plan spine (to-acceptance-plan / plan-orchestrate / ac-trace)](docs/adr/0072-acceptance-plan-spine.md) -199. [ADR 0074 — Many concurrent agent loops per server; the scheduler is single-leader for the tick, pooled (then sharded) for the drive](docs/adr/0074-many-loops-scheduler-shape.md) -200. [ADR 0099 — External transcript import (mecated import)](docs/adr/0099-external-transcript-import.md) -201. [ADR 0103 — mecatui seed prompt (-p/--prompt, --prompt-file)](docs/adr/0103-mecatui-seed-prompt.md) -202. [ADR 0200 — A failed delegated child is resumable (Recover, not refuse)](docs/adr/0200-resume-a-failed-subagent.md) -203. [ADR 0228 — Manual dream review](docs/adr/0228-manual-dream-review.md) -204. [Schedule tool — acceptance plan](docs/acceptance/schedule-tool.md) -205. [Caller separation — acceptance plan](docs/acceptance/caller-separation.md) -206. [Delegation observability convergence — acceptance plan](docs/acceptance/delegation-observability-convergence.md) -207. [Fire-result delivery — acceptance plan](docs/acceptance/fire-result-delivery.md) -208. [Schedule shared catalog — acceptance plan](docs/acceptance/schedule-shared-catalog.md) -209. [Session continuity UX — acceptance plan](docs/acceptance/session-continuity-ux.md) -210. [Session storage continuity — acceptance plan](docs/acceptance/session-storage-continuity.md) -211. [Spine convergence — acceptance plan](docs/acceptance/spine-convergence.md) -212. [Commit style](docs/examples/skills/commit-style/SKILL.md) -213. [Go table-driven tests](docs/examples/skills/go-table-tests/SKILL.md) -214. [mecatl live e2e suite](e2e/README.md) +161. [ADR 0230 — Redis migration ownership and coverage are proved before mutation](docs/adr/0230-redis-migration-atomic-ownership-and-coverage.md) +162. [Cloud-Native Harness Systems — future work scoping](docs/cloud-native-harness-systems.md) +163. [ADR 0090 — Per-server operator opt-in for plain-http token-bearing MCP endpoints](docs/adr/0090-mcp-insecure-http-optin.md) +164. [ADR 0223 — Pin MCP transport-error semantics that do not replay rejected calls](docs/adr/0223-mcp-sdk-transport-error-semantics.md) +165. [ADR 0094 — Opt-in project ingestion: two-axis positive grants](docs/adr/0094-opt-in-project-ingestion.md) +166. [16. Live e2e suite](docs/usage/e2e.md) +167. [4. Guardrails — LLM-backed tool-content inspection (guardrails:)](docs/usage/guardrails.md) +168. [8. mecak8s (Kubernetes-native agent)](docs/usage/mecak8s.md) +169. [15. Running mecatequi from GitHub Actions](docs/usage/mecatequi-ci.md) +170. [17. Troubleshooting / FAQ](docs/usage/troubleshooting.md) +171. [ADR 0035 — Surface the per-delegation model for ALL children, not just routed ones](docs/adr/0035-per-delegation-model-surface.md) +172. [ADR 0217 — Session discovery uses durable kind metadata and an authoritative transcript](docs/adr/0217-session-discovery-continuation.md) +173. [Example skills](docs/examples/README.md) +174. [ADR 0104 — Session families get a bounded, injective, non-reversible physical name](docs/adr/0104-session-family-physical-naming.md) +175. [Agent identity, part 2: the outbound hop](docs/agent-identity-outbound.md) +176. [ADR 0052 — Global guardrails checker-down posture toggle](docs/adr/0052-guardrails-checker-down-toggle.md) +177. [ADR 0067 — OpenAI Chat Completions adapter (OpenCode Go provider)](docs/adr/0067-openai-chat-completions-adapter.md) +178. [ADR 0083 — Expose the routing miss/gate reason on delegation-start events](docs/adr/0083-routing-reason-on-delegation-start.md) +179. [ADR 0101 — OpenAI reasoning multiplicity: adapter-packed items + bounded encrypted-content repair](docs/adr/0101-openai-reasoning-multiplicity.md) +180. [ADR 0079 — Converge delegation observability on two tiers (bounded previews for Subagent/Parallel)](docs/adr/0079-delegation-observability-convergence.md) +181. [ADR 0229 — Redis migration uses fenced renewable ownership and indexed coverage](docs/adr/0229-redis-migration-fencing.md) +182. [ADR 0058 — Writable named-specialist Subagent (mode:"read-write" + agent)](docs/adr/0058-writable-named-specialist-subagent.md) +183. [ADR 0029 — Repo-map Tree-sitter Binding](docs/adr/0029-repomap-tree-sitter.md) +184. [ADR 0068 — Change reasoning effort via conversation fork (keep the transcript)](docs/adr/0068-effort-change-via-fork.md) +185. [ADR 0098 — Telemetry for the headless binaries (mecatequi, mecak8s)](docs/adr/0098-headless-telemetry.md) +186. [ADR 0088 — Explicit daemon.yaml (listener topology config)](docs/adr/0088-daemon-config-file.md) +187. [ADR 0097 — Scheduled fires are observable in-flight: a first-class persisted lifecycle stage](docs/adr/0097-scheduled-fire-inflight-state.md) +188. [mecatl documentation](docs/README.md) +189. [ADR 0076 — The schedule manager is store-shaped and pre-Service; the shared catalog carries the Schedule tool](docs/adr/0076-schedule-shared-catalog.md) +190. [ADR 0096 — mecatui live-feed reconnect: client-owned backoff + durable catch-up, no server cursor](docs/adr/0096-live-feed-reconnect.md) +191. [ADR 0080 — Guardrail-routed path-escape checking (composition pre-check, auto-only)](docs/adr/0080-guardrail-routed-escape-checking.md) +192. [Steer-while-running — acceptance plan](docs/acceptance/steer-while-running.md) +193. [Acceptance plans](docs/acceptance/README.md) +194. [Development process](docs/development-process.md) +195. [ADR 0227 — Dream consolidation safety boundary](docs/adr/0227-dream-consolidation-safety-boundary.md) +196. [engine/api/ — committed public-API snapshots](engine/api/README.md) +197. [Path-escape posture relax — acceptance plan](docs/acceptance/path-escape-posture.md) +198. [ADR 0044 — Host-supplied askID discriminator (cross-process-reconstructable askID)](docs/adr/0044-host-supplied-askid-discriminator.md) +199. [ADR 0063 — MCP structured results: fail-closed + CallMcpWithQuery](docs/adr/0063-mcp-structured-failclosed-callmcpwithquery.md) +200. [ADR 0072 — The acceptance-plan spine (to-acceptance-plan / plan-orchestrate / ac-trace)](docs/adr/0072-acceptance-plan-spine.md) +201. [ADR 0074 — Many concurrent agent loops per server; the scheduler is single-leader for the tick, pooled (then sharded) for the drive](docs/adr/0074-many-loops-scheduler-shape.md) +202. [ADR 0099 — External transcript import (mecated import)](docs/adr/0099-external-transcript-import.md) +203. [ADR 0103 — mecatui seed prompt (-p/--prompt, --prompt-file)](docs/adr/0103-mecatui-seed-prompt.md) +204. [ADR 0200 — A failed delegated child is resumable (Recover, not refuse)](docs/adr/0200-resume-a-failed-subagent.md) +205. [ADR 0228 — Manual dream review](docs/adr/0228-manual-dream-review.md) +206. [Schedule tool — acceptance plan](docs/acceptance/schedule-tool.md) +207. [Caller separation — acceptance plan](docs/acceptance/caller-separation.md) +208. [Delegation observability convergence — acceptance plan](docs/acceptance/delegation-observability-convergence.md) +209. [Fire-result delivery — acceptance plan](docs/acceptance/fire-result-delivery.md) +210. [Schedule shared catalog — acceptance plan](docs/acceptance/schedule-shared-catalog.md) +211. [Session continuity UX — acceptance plan](docs/acceptance/session-continuity-ux.md) +212. [Session storage continuity — acceptance plan](docs/acceptance/session-storage-continuity.md) +213. [Spine convergence — acceptance plan](docs/acceptance/spine-convergence.md) +214. [Commit style](docs/examples/skills/commit-style/SKILL.md) +215. [Go table-driven tests](docs/examples/skills/go-table-tests/SKILL.md) +216. [mecatl live e2e suite](e2e/README.md) ## Known gaps diff --git a/user-docs/what-you-get/mecatui.md b/user-docs/what-you-get/mecatui.md index bf95b0ae66..cd7803ceaf 100644 --- a/user-docs/what-you-get/mecatui.md +++ b/user-docs/what-you-get/mecatui.md @@ -206,9 +206,13 @@ Quitting follows the unix double-press habit: `ctrl+c` on an empty prompt (or `c ## Working while the model streams -You don't have to wait for a turn to finish before typing your next thing. Pressing `enter` while a run is streaming **queues** your input instead of sending it — queue as many follow-ups as you like, and they're merged into one prompt and sent the moment the current turn ends. If you change your mind, `↑` on an empty input pulls the queued text back into the box for editing, non-destructively. +You don't have to wait for a turn to finish before typing your next thing. Pressing `enter` while a run is streaming **steers** the running agent: your message is folded into the *current* run at the next turn boundary (right after the in-flight tool calls settle), so the agent course-corrects mid-task instead of waiting for a separate follow-up turn. Send several lines while the run is going and they land together as one merged message at the next boundary. Until it lands, the steer shows as a **pending card at the bottom** of the transcript ("queued"); the moment the engine drains it, the card clears and the message appears **in context at its true position** — you never see it twice. If the run ended just as you sent, the steer is promoted to a follow-up run instead of being dropped. `↑` on an empty input edits a still-pending steer: it cancels the outstanding send and pulls the whole not-yet-landed set back into the box as **one editable blob**, so your resend is a full replacement (never a duplicate). `esc` retracts a still-pending steer. -`esc` backs out of things in order: it clears whatever you've typed, then clears the queue, then — only if you press it again — cancels the run itself. Nothing is one accidental keystroke away from being lost. +Steer is on by default. A server started with `--no-steer` (or `steer: false` in the operator settings) disables it; against such a server, `enter` while streaming falls back to **queueing** your input instead — queued follow-ups are merged into one prompt and sent the moment the current turn ends, and `↑` pulls the queued text back for editing. + +**Steer debugging.** Two operator-facing toggles help trace a stuck steer lifecycle when something feels off: `MECATUI_DEBUG_STEER=1` shows a one-line correlation trace in the status bar for every steer ack/echo (incoming id, the live bundle's watermark, and the match/burn/drop decision), and `--diagnostics-log ` opens a per-instance diagnostics log instead of the shared per-user `$XDG_STATE_HOME/mecatl/mecatui.log` (the embedded server logs the watermark correlation there: each frame's track/enqueue outcome, the watermark consume on drain, and an uncorrelated-echo WARN if an id ever fails to resolve). Both are off/unchanged by default. + +`esc` backs out of things in order: it clears whatever you've typed, then clears the queue (or retracts a pending steer), then — only if you press it again — cancels the run itself. Nothing is one accidental keystroke away from being lost. When an error is a **permanent** provider rejection (a 4xx status other than 408/429, a context-window overflow, a policy block), mecatui shows a one-line summary instead of a raw error block, telling you plainly that retrying won't help and you should start a new session or change the request. The raw error is still available on `ctrl+t` expand. Permanent errors are never auto-retried by the queue.