diff --git a/crates/agent-gateway/internal/proto/v2/gateway.pb.go b/crates/agent-gateway/internal/proto/v2/gateway.pb.go index bccca859a..32b9e8b69 100644 --- a/crates/agent-gateway/internal/proto/v2/gateway.pb.go +++ b/crates/agent-gateway/internal/proto/v2/gateway.pb.go @@ -335,6 +335,7 @@ type GatewayEnvelope struct { // *GatewayEnvelope_HistoryBranch // *GatewayEnvelope_ProviderUsage // *GatewayEnvelope_ChatFileOpen + // *GatewayEnvelope_HistorySetCwd Payload isGatewayEnvelope_Payload `protobuf_oneof:"payload"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -832,6 +833,15 @@ func (x *GatewayEnvelope) GetChatFileOpen() *ChatFileOpenRequest { return nil } +func (x *GatewayEnvelope) GetHistorySetCwd() *HistorySetCwdRequest { + if x != nil { + if x, ok := x.Payload.(*GatewayEnvelope_HistorySetCwd); ok { + return x.HistorySetCwd + } + } + return nil +} + type isGatewayEnvelope_Payload interface { isGatewayEnvelope_Payload() } @@ -1032,6 +1042,10 @@ type GatewayEnvelope_ChatFileOpen struct { ChatFileOpen *ChatFileOpenRequest `protobuf:"bytes,94,opt,name=chat_file_open,json=chatFileOpen,proto3,oneof"` } +type GatewayEnvelope_HistorySetCwd struct { + HistorySetCwd *HistorySetCwdRequest `protobuf:"bytes,95,opt,name=history_set_cwd,json=historySetCwd,proto3,oneof"` +} + func (*GatewayEnvelope_ChatCommand) isGatewayEnvelope_Payload() {} func (*GatewayEnvelope_CronManage) isGatewayEnvelope_Payload() {} @@ -1130,6 +1144,8 @@ func (*GatewayEnvelope_ProviderUsage) isGatewayEnvelope_Payload() {} func (*GatewayEnvelope_ChatFileOpen) isGatewayEnvelope_Payload() {} +func (*GatewayEnvelope_HistorySetCwd) isGatewayEnvelope_Payload() {} + type AgentEnvelope struct { state protoimpl.MessageState `protogen:"open.v1"` RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` @@ -1198,6 +1214,7 @@ type AgentEnvelope struct { // *AgentEnvelope_ChatIngressFragment // *AgentEnvelope_ChatFileOpenResp // *AgentEnvelope_Error + // *AgentEnvelope_HistorySetCwdResp Payload isAgentEnvelope_Payload `protobuf_oneof:"payload"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -1812,6 +1829,15 @@ func (x *AgentEnvelope) GetError() *ErrorResponse { return nil } +func (x *AgentEnvelope) GetHistorySetCwdResp() *HistorySetCwdResponse { + if x != nil { + if x, ok := x.Payload.(*AgentEnvelope_HistorySetCwdResp); ok { + return x.HistorySetCwdResp + } + } + return nil +} + type isAgentEnvelope_Payload interface { isAgentEnvelope_Payload() } @@ -2064,6 +2090,10 @@ type AgentEnvelope_Error struct { Error *ErrorResponse `protobuf:"bytes,99,opt,name=error,proto3,oneof"` } +type AgentEnvelope_HistorySetCwdResp struct { + HistorySetCwdResp *HistorySetCwdResponse `protobuf:"bytes,100,opt,name=history_set_cwd_resp,json=historySetCwdResp,proto3,oneof"` +} + func (*AgentEnvelope_ChatEvent) isAgentEnvelope_Payload() {} func (*AgentEnvelope_CronManageResp) isAgentEnvelope_Payload() {} @@ -2188,6 +2218,8 @@ func (*AgentEnvelope_ChatFileOpenResp) isAgentEnvelope_Payload() {} func (*AgentEnvelope_Error) isAgentEnvelope_Payload() {} +func (*AgentEnvelope_HistorySetCwdResp) isAgentEnvelope_Payload() {} + type ChatSelectedModel struct { state protoimpl.MessageState `protogen:"open.v1"` CustomProviderId string `protobuf:"bytes,1,opt,name=custom_provider_id,json=customProviderId,proto3" json:"custom_provider_id,omitempty"` @@ -12533,11 +12565,107 @@ func (x *ChatIngressAck) GetErrorMessage() string { return "" } +type HistorySetCwdRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ConversationId string `protobuf:"bytes,1,opt,name=conversation_id,json=conversationId,proto3" json:"conversation_id,omitempty"` + Cwd string `protobuf:"bytes,2,opt,name=cwd,proto3" json:"cwd,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *HistorySetCwdRequest) Reset() { + *x = HistorySetCwdRequest{} + mi := &file_proto_v2_gateway_proto_msgTypes[151] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *HistorySetCwdRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HistorySetCwdRequest) ProtoMessage() {} + +func (x *HistorySetCwdRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_v2_gateway_proto_msgTypes[151] + 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 HistorySetCwdRequest.ProtoReflect.Descriptor instead. +func (*HistorySetCwdRequest) Descriptor() ([]byte, []int) { + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{151} +} + +func (x *HistorySetCwdRequest) GetConversationId() string { + if x != nil { + return x.ConversationId + } + return "" +} + +func (x *HistorySetCwdRequest) GetCwd() string { + if x != nil { + return x.Cwd + } + return "" +} + +type HistorySetCwdResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Conversation *ConversationSummary `protobuf:"bytes,1,opt,name=conversation,proto3" json:"conversation,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *HistorySetCwdResponse) Reset() { + *x = HistorySetCwdResponse{} + mi := &file_proto_v2_gateway_proto_msgTypes[152] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *HistorySetCwdResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HistorySetCwdResponse) ProtoMessage() {} + +func (x *HistorySetCwdResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_v2_gateway_proto_msgTypes[152] + 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 HistorySetCwdResponse.ProtoReflect.Descriptor instead. +func (*HistorySetCwdResponse) Descriptor() ([]byte, []int) { + return file_proto_v2_gateway_proto_rawDescGZIP(), []int{152} +} + +func (x *HistorySetCwdResponse) GetConversation() *ConversationSummary { + if x != nil { + return x.Conversation + } + return nil +} + var File_proto_v2_gateway_proto protoreflect.FileDescriptor const file_proto_v2_gateway_proto_rawDesc = "" + "\n" + - "\x16proto/v2/gateway.proto\x12\x14liveagent.gateway.v2\"\xb2!\n" + + "\x16proto/v2/gateway.proto\x12\x14liveagent.gateway.v2\"\x88\"\n" + "\x0fGatewayEnvelope\x12\x1d\n" + "\n" + "request_id\x18\x01 \x01(\tR\trequestId\x12\x1c\n" + @@ -12597,8 +12725,9 @@ const file_proto_v2_gateway_proto_rawDesc = "" + "\x17managed_process_request\x18[ \x01(\v2+.liveagent.gateway.v2.ManagedProcessRequestH\x00R\x15managedProcessRequest\x12S\n" + "\x0ehistory_branch\x18\\ \x01(\v2*.liveagent.gateway.v2.HistoryBranchRequestH\x00R\rhistoryBranch\x12S\n" + "\x0eprovider_usage\x18] \x01(\v2*.liveagent.gateway.v2.ProviderUsageRequestH\x00R\rproviderUsage\x12Q\n" + - "\x0echat_file_open\x18^ \x01(\v2).liveagent.gateway.v2.ChatFileOpenRequestH\x00R\fchatFileOpenB\t\n" + - "\apayloadJ\x04\bC\x10DJ\x04\bD\x10EJ\x04\bE\x10FJ\x04\bJ\x10K\"\x8e-\n" + + "\x0echat_file_open\x18^ \x01(\v2).liveagent.gateway.v2.ChatFileOpenRequestH\x00R\fchatFileOpen\x12T\n" + + "\x0fhistory_set_cwd\x18_ \x01(\v2*.liveagent.gateway.v2.HistorySetCwdRequestH\x00R\rhistorySetCwdB\t\n" + + "\apayloadJ\x04\bC\x10DJ\x04\bD\x10EJ\x04\bE\x10FJ\x04\bJ\x10K\"\xee-\n" + "\rAgentEnvelope\x12\x1d\n" + "\n" + "request_id\x18\x01 \x01(\tR\trequestId\x12\x1c\n" + @@ -12668,7 +12797,8 @@ const file_proto_v2_gateway_proto_rawDesc = "" + "\x13chat_ingress_resume\x18` \x01(\v2'.liveagent.gateway.v2.ChatIngressResumeH\x00R\x11chatIngressResume\x12_\n" + "\x15chat_ingress_fragment\x18a \x01(\v2).liveagent.gateway.v2.ChatIngressFragmentH\x00R\x13chatIngressFragment\x12[\n" + "\x13chat_file_open_resp\x18b \x01(\v2*.liveagent.gateway.v2.ChatFileOpenResponseH\x00R\x10chatFileOpenResp\x12;\n" + - "\x05error\x18c \x01(\v2#.liveagent.gateway.v2.ErrorResponseH\x00R\x05errorB\t\n" + + "\x05error\x18c \x01(\v2#.liveagent.gateway.v2.ErrorResponseH\x00R\x05error\x12^\n" + + "\x14history_set_cwd_resp\x18d \x01(\v2+.liveagent.gateway.v2.HistorySetCwdResponseH\x00R\x11historySetCwdRespB\t\n" + "\apayloadJ\x04\bC\x10DJ\x04\bD\x10EJ\x04\bE\x10FJ\x04\bN\x10O\"|\n" + "\x11ChatSelectedModel\x12,\n" + "\x12custom_provider_id\x18\x01 \x01(\tR\x10customProviderId\x12\x14\n" + @@ -13579,7 +13709,12 @@ const file_proto_v2_gateway_proto_rawDesc = "" + "\bCONTINUE\x10\x01\x12\x18\n" + "\x14REPLAY_FROM_EXPECTED\x10\x02\x12\x13\n" + "\x0fSEND_CHECKPOINT\x10\x03\x12\f\n" + - "\bREJECTED\x10\x04*\xc6\x04\n" + + "\bREJECTED\x10\x04\"Q\n" + + "\x14HistorySetCwdRequest\x12'\n" + + "\x0fconversation_id\x18\x01 \x01(\tR\x0econversationId\x12\x10\n" + + "\x03cwd\x18\x02 \x01(\tR\x03cwd\"f\n" + + "\x15HistorySetCwdResponse\x12M\n" + + "\fconversation\x18\x01 \x01(\v2).liveagent.gateway.v2.ConversationSummaryR\fconversation*\xc6\x04\n" + "\x0fTunnelFrameKind\x12!\n" + "\x1dTUNNEL_FRAME_KIND_UNSPECIFIED\x10\x00\x12(\n" + "$TUNNEL_FRAME_KIND_HTTP_REQUEST_START\x10\x01\x12'\n" + @@ -13616,7 +13751,7 @@ func file_proto_v2_gateway_proto_rawDescGZIP() []byte { } var file_proto_v2_gateway_proto_enumTypes = make([]protoimpl.EnumInfo, 4) -var file_proto_v2_gateway_proto_msgTypes = make([]protoimpl.MessageInfo, 151) +var file_proto_v2_gateway_proto_msgTypes = make([]protoimpl.MessageInfo, 153) var file_proto_v2_gateway_proto_goTypes = []any{ (TunnelFrameKind)(0), // 0: liveagent.gateway.v2.TunnelFrameKind (TunnelWsMessageType)(0), // 1: liveagent.gateway.v2.TunnelWsMessageType @@ -13773,6 +13908,8 @@ var file_proto_v2_gateway_proto_goTypes = []any{ (*ChatIngressRunResume)(nil), // 152: liveagent.gateway.v2.ChatIngressRunResume (*ChatIngressFragment)(nil), // 153: liveagent.gateway.v2.ChatIngressFragment (*ChatIngressAck)(nil), // 154: liveagent.gateway.v2.ChatIngressAck + (*HistorySetCwdRequest)(nil), // 155: liveagent.gateway.v2.HistorySetCwdRequest + (*HistorySetCwdResponse)(nil), // 156: liveagent.gateway.v2.HistorySetCwdResponse } var file_proto_v2_gateway_proto_depIdxs = []int32{ 56, // 0: liveagent.gateway.v2.GatewayEnvelope.chat_command:type_name -> liveagent.gateway.v2.ChatCommandRequest @@ -13824,138 +13961,141 @@ var file_proto_v2_gateway_proto_depIdxs = []int32{ 76, // 46: liveagent.gateway.v2.GatewayEnvelope.history_branch:type_name -> liveagent.gateway.v2.HistoryBranchRequest 143, // 47: liveagent.gateway.v2.GatewayEnvelope.provider_usage:type_name -> liveagent.gateway.v2.ProviderUsageRequest 128, // 48: liveagent.gateway.v2.GatewayEnvelope.chat_file_open:type_name -> liveagent.gateway.v2.ChatFileOpenRequest - 60, // 49: liveagent.gateway.v2.AgentEnvelope.chat_event:type_name -> liveagent.gateway.v2.ChatEvent - 66, // 50: liveagent.gateway.v2.AgentEnvelope.cron_manage_resp:type_name -> liveagent.gateway.v2.CronManageResponse - 68, // 51: liveagent.gateway.v2.AgentEnvelope.history_list_resp:type_name -> liveagent.gateway.v2.HistoryListResponse - 71, // 52: liveagent.gateway.v2.AgentEnvelope.history_get_resp:type_name -> liveagent.gateway.v2.HistoryGetResponse - 75, // 53: liveagent.gateway.v2.AgentEnvelope.history_rename_resp:type_name -> liveagent.gateway.v2.HistoryRenameResponse - 91, // 54: liveagent.gateway.v2.AgentEnvelope.history_delete_resp:type_name -> liveagent.gateway.v2.HistoryDeleteResponse - 92, // 55: liveagent.gateway.v2.AgentEnvelope.history_sync:type_name -> liveagent.gateway.v2.HistorySyncEvent - 73, // 56: liveagent.gateway.v2.AgentEnvelope.history_prefix_resp:type_name -> liveagent.gateway.v2.HistoryPrefixResponse - 79, // 57: liveagent.gateway.v2.AgentEnvelope.history_pin_resp:type_name -> liveagent.gateway.v2.HistoryPinResponse - 82, // 58: liveagent.gateway.v2.AgentEnvelope.history_share_get_resp:type_name -> liveagent.gateway.v2.HistoryShareGetResponse - 84, // 59: liveagent.gateway.v2.AgentEnvelope.history_share_set_resp:type_name -> liveagent.gateway.v2.HistoryShareSetResponse - 86, // 60: liveagent.gateway.v2.AgentEnvelope.history_share_resolve_resp:type_name -> liveagent.gateway.v2.HistoryShareResolveResponse - 89, // 61: liveagent.gateway.v2.AgentEnvelope.history_workdirs_resp:type_name -> liveagent.gateway.v2.HistoryWorkdirsResponse - 94, // 62: liveagent.gateway.v2.AgentEnvelope.provider_list_resp:type_name -> liveagent.gateway.v2.ProviderListResponse - 96, // 63: liveagent.gateway.v2.AgentEnvelope.settings_get_resp:type_name -> liveagent.gateway.v2.SettingsGetResponse - 98, // 64: liveagent.gateway.v2.AgentEnvelope.settings_update_resp:type_name -> liveagent.gateway.v2.SettingsUpdateResponse - 101, // 65: liveagent.gateway.v2.AgentEnvelope.settings_sync:type_name -> liveagent.gateway.v2.SettingsSyncEvent - 103, // 66: liveagent.gateway.v2.AgentEnvelope.skill_files_list_resp:type_name -> liveagent.gateway.v2.SkillFilesListResponse - 105, // 67: liveagent.gateway.v2.AgentEnvelope.skill_metadata_read_resp:type_name -> liveagent.gateway.v2.SkillMetadataReadResponse - 107, // 68: liveagent.gateway.v2.AgentEnvelope.skill_text_read_resp:type_name -> liveagent.gateway.v2.SkillTextReadResponse - 112, // 69: liveagent.gateway.v2.AgentEnvelope.file_mention_list_resp:type_name -> liveagent.gateway.v2.FileMentionListResponse - 11, // 70: liveagent.gateway.v2.AgentEnvelope.upload_readable_files_resp:type_name -> liveagent.gateway.v2.UploadReadableFilesResponse - 115, // 71: liveagent.gateway.v2.AgentEnvelope.fs_roots_resp:type_name -> liveagent.gateway.v2.FsRootsResponse - 139, // 72: liveagent.gateway.v2.AgentEnvelope.pong:type_name -> liveagent.gateway.v2.PongResponse - 118, // 73: liveagent.gateway.v2.AgentEnvelope.fs_list_dirs_resp:type_name -> liveagent.gateway.v2.FsListDirsResponse - 13, // 74: liveagent.gateway.v2.AgentEnvelope.uploaded_image_preview_resp:type_name -> liveagent.gateway.v2.UploadedImagePreviewResponse - 32, // 75: liveagent.gateway.v2.AgentEnvelope.memory_manage_resp:type_name -> liveagent.gateway.v2.MemoryManageResponse - 109, // 76: liveagent.gateway.v2.AgentEnvelope.skill_manage_resp:type_name -> liveagent.gateway.v2.SkillManageResponse - 120, // 77: liveagent.gateway.v2.AgentEnvelope.fs_create_project_folder_resp:type_name -> liveagent.gateway.v2.FsCreateProjectFolderResponse - 48, // 78: liveagent.gateway.v2.AgentEnvelope.terminal_response:type_name -> liveagent.gateway.v2.TerminalResponse - 49, // 79: liveagent.gateway.v2.AgentEnvelope.terminal_event:type_name -> liveagent.gateway.v2.TerminalEvent - 123, // 80: liveagent.gateway.v2.AgentEnvelope.fs_list_resp:type_name -> liveagent.gateway.v2.FsListResponse - 131, // 81: liveagent.gateway.v2.AgentEnvelope.fs_write_text_resp:type_name -> liveagent.gateway.v2.FsWriteTextResponse - 133, // 82: liveagent.gateway.v2.AgentEnvelope.fs_create_dir_resp:type_name -> liveagent.gateway.v2.FsCreateDirResponse - 135, // 83: liveagent.gateway.v2.AgentEnvelope.fs_rename_resp:type_name -> liveagent.gateway.v2.FsRenameResponse - 137, // 84: liveagent.gateway.v2.AgentEnvelope.fs_delete_resp:type_name -> liveagent.gateway.v2.FsDeleteResponse - 52, // 85: liveagent.gateway.v2.AgentEnvelope.git_response:type_name -> liveagent.gateway.v2.GitResponse - 125, // 86: liveagent.gateway.v2.AgentEnvelope.fs_read_editable_text_resp:type_name -> liveagent.gateway.v2.FsReadEditableTextResponse - 127, // 87: liveagent.gateway.v2.AgentEnvelope.fs_read_workspace_image_resp:type_name -> liveagent.gateway.v2.FsReadWorkspaceImageResponse - 39, // 88: liveagent.gateway.v2.AgentEnvelope.sftp_response:type_name -> liveagent.gateway.v2.SftpResponse - 40, // 89: liveagent.gateway.v2.AgentEnvelope.sftp_event:type_name -> liveagent.gateway.v2.SftpEvent - 58, // 90: liveagent.gateway.v2.AgentEnvelope.chat_queue_resp:type_name -> liveagent.gateway.v2.ChatQueueResponse - 59, // 91: liveagent.gateway.v2.AgentEnvelope.chat_queue_event:type_name -> liveagent.gateway.v2.ChatQueueEvent - 61, // 92: liveagent.gateway.v2.AgentEnvelope.chat_control:type_name -> liveagent.gateway.v2.ChatControlEvent - 63, // 93: liveagent.gateway.v2.AgentEnvelope.runtime_status:type_name -> liveagent.gateway.v2.RuntimeStatusEvent - 100, // 94: liveagent.gateway.v2.AgentEnvelope.settings_reset_ssh_known_host_resp:type_name -> liveagent.gateway.v2.SettingsResetSshKnownHostResponse - 62, // 95: liveagent.gateway.v2.AgentEnvelope.chat_runtime_snapshot:type_name -> liveagent.gateway.v2.ChatRuntimeSnapshot - 142, // 96: liveagent.gateway.v2.AgentEnvelope.provider_models_resp:type_name -> liveagent.gateway.v2.ProviderModelsResponse - 15, // 97: liveagent.gateway.v2.AgentEnvelope.tunnel_desired:type_name -> liveagent.gateway.v2.TunnelDesiredState - 20, // 98: liveagent.gateway.v2.AgentEnvelope.tunnel_mutation_result:type_name -> liveagent.gateway.v2.TunnelMutationResult - 24, // 99: liveagent.gateway.v2.AgentEnvelope.tunnel_frame:type_name -> liveagent.gateway.v2.TunnelFrame - 22, // 100: liveagent.gateway.v2.AgentEnvelope.tunnel_probe_report:type_name -> liveagent.gateway.v2.TunnelProbeReport - 26, // 101: liveagent.gateway.v2.AgentEnvelope.workspace_activity:type_name -> liveagent.gateway.v2.WorkspaceActivityEvent - 30, // 102: liveagent.gateway.v2.AgentEnvelope.managed_process_response:type_name -> liveagent.gateway.v2.ManagedProcessResponse - 28, // 103: liveagent.gateway.v2.AgentEnvelope.managed_process_snapshot:type_name -> liveagent.gateway.v2.ManagedProcessSnapshot - 77, // 104: liveagent.gateway.v2.AgentEnvelope.history_branch_resp:type_name -> liveagent.gateway.v2.HistoryBranchResponse - 144, // 105: liveagent.gateway.v2.AgentEnvelope.provider_usage_resp:type_name -> liveagent.gateway.v2.ProviderUsageResponse - 145, // 106: liveagent.gateway.v2.AgentEnvelope.chat_ingress_batch:type_name -> liveagent.gateway.v2.ChatIngressBatch - 151, // 107: liveagent.gateway.v2.AgentEnvelope.chat_ingress_resume:type_name -> liveagent.gateway.v2.ChatIngressResume - 153, // 108: liveagent.gateway.v2.AgentEnvelope.chat_ingress_fragment:type_name -> liveagent.gateway.v2.ChatIngressFragment - 129, // 109: liveagent.gateway.v2.AgentEnvelope.chat_file_open_resp:type_name -> liveagent.gateway.v2.ChatFileOpenResponse - 140, // 110: liveagent.gateway.v2.AgentEnvelope.error:type_name -> liveagent.gateway.v2.ErrorResponse - 9, // 111: liveagent.gateway.v2.UploadReadableFilesRequest.files:type_name -> liveagent.gateway.v2.UploadReadableFile - 8, // 112: liveagent.gateway.v2.UploadReadableFilesResponse.files:type_name -> liveagent.gateway.v2.ChatUploadedFile - 14, // 113: liveagent.gateway.v2.TunnelDesiredState.tunnels:type_name -> liveagent.gateway.v2.TunnelSpec - 16, // 114: liveagent.gateway.v2.TunnelStatus.local:type_name -> liveagent.gateway.v2.TunnelHealth - 17, // 115: liveagent.gateway.v2.TunnelStateSnapshot.tunnels:type_name -> liveagent.gateway.v2.TunnelStatus - 16, // 116: liveagent.gateway.v2.TunnelStateSnapshot.relay:type_name -> liveagent.gateway.v2.TunnelHealth - 16, // 117: liveagent.gateway.v2.TunnelProbeResult.local:type_name -> liveagent.gateway.v2.TunnelHealth - 21, // 118: liveagent.gateway.v2.TunnelProbeReport.results:type_name -> liveagent.gateway.v2.TunnelProbeResult - 0, // 119: liveagent.gateway.v2.TunnelFrame.kind:type_name -> liveagent.gateway.v2.TunnelFrameKind - 23, // 120: liveagent.gateway.v2.TunnelFrame.headers:type_name -> liveagent.gateway.v2.TunnelHeader - 1, // 121: liveagent.gateway.v2.TunnelFrame.ws_message_type:type_name -> liveagent.gateway.v2.TunnelWsMessageType - 27, // 122: liveagent.gateway.v2.ManagedProcessSnapshot.processes:type_name -> liveagent.gateway.v2.ManagedProcessRecord - 28, // 123: liveagent.gateway.v2.ManagedProcessResponse.snapshot:type_name -> liveagent.gateway.v2.ManagedProcessSnapshot - 35, // 124: liveagent.gateway.v2.TerminalSession.ssh:type_name -> liveagent.gateway.v2.TerminalSshMetadata - 37, // 125: liveagent.gateway.v2.SftpResponse.entries:type_name -> liveagent.gateway.v2.SftpEntry - 37, // 126: liveagent.gateway.v2.SftpResponse.entry:type_name -> liveagent.gateway.v2.SftpEntry - 38, // 127: liveagent.gateway.v2.SftpResponse.transfer:type_name -> liveagent.gateway.v2.SftpTransfer - 38, // 128: liveagent.gateway.v2.SftpEvent.transfer:type_name -> liveagent.gateway.v2.SftpTransfer - 43, // 129: liveagent.gateway.v2.TerminalSshTabsSnapshot.tabs:type_name -> liveagent.gateway.v2.TerminalSshTab - 45, // 130: liveagent.gateway.v2.TerminalSshLocalForwardsSnapshot.forwards:type_name -> liveagent.gateway.v2.TerminalSshLocalForward - 45, // 131: liveagent.gateway.v2.TerminalSshLocalForwardAction.forward:type_name -> liveagent.gateway.v2.TerminalSshLocalForward - 34, // 132: liveagent.gateway.v2.TerminalResponse.sessions:type_name -> liveagent.gateway.v2.TerminalSession - 34, // 133: liveagent.gateway.v2.TerminalResponse.session:type_name -> liveagent.gateway.v2.TerminalSession - 42, // 134: liveagent.gateway.v2.TerminalResponse.shell_options:type_name -> liveagent.gateway.v2.TerminalShellOption - 41, // 135: liveagent.gateway.v2.TerminalResponse.ssh_prompt:type_name -> liveagent.gateway.v2.TerminalSshPrompt - 44, // 136: liveagent.gateway.v2.TerminalResponse.ssh_tabs:type_name -> liveagent.gateway.v2.TerminalSshTabsSnapshot - 46, // 137: liveagent.gateway.v2.TerminalResponse.ssh_local_forwards:type_name -> liveagent.gateway.v2.TerminalSshLocalForwardsSnapshot - 47, // 138: liveagent.gateway.v2.TerminalResponse.ssh_local_forward:type_name -> liveagent.gateway.v2.TerminalSshLocalForwardAction - 34, // 139: liveagent.gateway.v2.TerminalEvent.session:type_name -> liveagent.gateway.v2.TerminalSession - 44, // 140: liveagent.gateway.v2.TerminalEvent.ssh_tabs:type_name -> liveagent.gateway.v2.TerminalSshTabsSnapshot - 47, // 141: liveagent.gateway.v2.TerminalEvent.ssh_local_forward:type_name -> liveagent.gateway.v2.TerminalSshLocalForwardAction - 34, // 142: liveagent.gateway.v2.TerminalStreamFrame.session:type_name -> liveagent.gateway.v2.TerminalSession - 6, // 143: liveagent.gateway.v2.ChatRequest.selected_model:type_name -> liveagent.gateway.v2.ChatSelectedModel - 8, // 144: liveagent.gateway.v2.ChatRequest.uploaded_files:type_name -> liveagent.gateway.v2.ChatUploadedFile - 7, // 145: liveagent.gateway.v2.ChatRequest.runtime_controls:type_name -> liveagent.gateway.v2.ChatRuntimeControls - 53, // 146: liveagent.gateway.v2.ChatCommandRequest.request:type_name -> liveagent.gateway.v2.ChatRequest - 54, // 147: liveagent.gateway.v2.ChatCommandRequest.base_message_ref:type_name -> liveagent.gateway.v2.ChatMessageRef - 55, // 148: liveagent.gateway.v2.ChatCommandRequest.cancel:type_name -> liveagent.gateway.v2.CancelChatRequest - 2, // 149: liveagent.gateway.v2.ChatEvent.type:type_name -> liveagent.gateway.v2.ChatEvent.ChatEventType - 64, // 150: liveagent.gateway.v2.RuntimeStatusEvent.active_runs:type_name -> liveagent.gateway.v2.ChatRunReport - 64, // 151: liveagent.gateway.v2.RuntimeStatusEvent.finished_runs:type_name -> liveagent.gateway.v2.ChatRunReport - 69, // 152: liveagent.gateway.v2.HistoryListResponse.conversations:type_name -> liveagent.gateway.v2.ConversationSummary - 69, // 153: liveagent.gateway.v2.HistoryGetResponse.conversation:type_name -> liveagent.gateway.v2.ConversationSummary - 54, // 154: liveagent.gateway.v2.HistoryPrefixRequest.base_message_ref:type_name -> liveagent.gateway.v2.ChatMessageRef - 69, // 155: liveagent.gateway.v2.HistoryPrefixResponse.conversation:type_name -> liveagent.gateway.v2.ConversationSummary - 69, // 156: liveagent.gateway.v2.HistoryRenameResponse.conversation:type_name -> liveagent.gateway.v2.ConversationSummary - 54, // 157: liveagent.gateway.v2.HistoryBranchRequest.base_message_ref:type_name -> liveagent.gateway.v2.ChatMessageRef - 69, // 158: liveagent.gateway.v2.HistoryBranchResponse.conversation:type_name -> liveagent.gateway.v2.ConversationSummary - 69, // 159: liveagent.gateway.v2.HistoryPinResponse.conversation:type_name -> liveagent.gateway.v2.ConversationSummary - 80, // 160: liveagent.gateway.v2.HistoryShareGetResponse.share:type_name -> liveagent.gateway.v2.HistoryShareStatus - 80, // 161: liveagent.gateway.v2.HistoryShareSetResponse.share:type_name -> liveagent.gateway.v2.HistoryShareStatus - 69, // 162: liveagent.gateway.v2.HistoryShareResolveResponse.conversation:type_name -> liveagent.gateway.v2.ConversationSummary - 88, // 163: liveagent.gateway.v2.HistoryWorkdirsResponse.workdirs:type_name -> liveagent.gateway.v2.HistoryWorkdirSummary - 69, // 164: liveagent.gateway.v2.HistorySyncEvent.conversation:type_name -> liveagent.gateway.v2.ConversationSummary - 111, // 165: liveagent.gateway.v2.FileMentionListResponse.entries:type_name -> liveagent.gateway.v2.FileMentionEntry - 113, // 166: liveagent.gateway.v2.FsRootsResponse.roots:type_name -> liveagent.gateway.v2.FsRoot - 117, // 167: liveagent.gateway.v2.FsListDirsResponse.entries:type_name -> liveagent.gateway.v2.FsDirEntry - 122, // 168: liveagent.gateway.v2.FsListResponse.entries:type_name -> liveagent.gateway.v2.FsListEntry - 146, // 169: liveagent.gateway.v2.ChatIngressBatch.records:type_name -> liveagent.gateway.v2.ChatIngressRecord - 147, // 170: liveagent.gateway.v2.ChatIngressRecord.delta:type_name -> liveagent.gateway.v2.ChatIngressDelta - 149, // 171: liveagent.gateway.v2.ChatIngressRecord.checkpoint:type_name -> liveagent.gateway.v2.ChatIngressCheckpoint - 150, // 172: liveagent.gateway.v2.ChatIngressRecord.terminal:type_name -> liveagent.gateway.v2.ChatIngressTerminal - 148, // 173: liveagent.gateway.v2.ChatIngressRecord.heartbeat:type_name -> liveagent.gateway.v2.ChatIngressHeartbeat - 152, // 174: liveagent.gateway.v2.ChatIngressResume.runs:type_name -> liveagent.gateway.v2.ChatIngressRunResume - 3, // 175: liveagent.gateway.v2.ChatIngressAck.action:type_name -> liveagent.gateway.v2.ChatIngressAck.Action - 176, // [176:176] is the sub-list for method output_type - 176, // [176:176] is the sub-list for method input_type - 176, // [176:176] is the sub-list for extension type_name - 176, // [176:176] is the sub-list for extension extendee - 0, // [0:176] is the sub-list for field type_name + 155, // 49: liveagent.gateway.v2.GatewayEnvelope.history_set_cwd:type_name -> liveagent.gateway.v2.HistorySetCwdRequest + 60, // 50: liveagent.gateway.v2.AgentEnvelope.chat_event:type_name -> liveagent.gateway.v2.ChatEvent + 66, // 51: liveagent.gateway.v2.AgentEnvelope.cron_manage_resp:type_name -> liveagent.gateway.v2.CronManageResponse + 68, // 52: liveagent.gateway.v2.AgentEnvelope.history_list_resp:type_name -> liveagent.gateway.v2.HistoryListResponse + 71, // 53: liveagent.gateway.v2.AgentEnvelope.history_get_resp:type_name -> liveagent.gateway.v2.HistoryGetResponse + 75, // 54: liveagent.gateway.v2.AgentEnvelope.history_rename_resp:type_name -> liveagent.gateway.v2.HistoryRenameResponse + 91, // 55: liveagent.gateway.v2.AgentEnvelope.history_delete_resp:type_name -> liveagent.gateway.v2.HistoryDeleteResponse + 92, // 56: liveagent.gateway.v2.AgentEnvelope.history_sync:type_name -> liveagent.gateway.v2.HistorySyncEvent + 73, // 57: liveagent.gateway.v2.AgentEnvelope.history_prefix_resp:type_name -> liveagent.gateway.v2.HistoryPrefixResponse + 79, // 58: liveagent.gateway.v2.AgentEnvelope.history_pin_resp:type_name -> liveagent.gateway.v2.HistoryPinResponse + 82, // 59: liveagent.gateway.v2.AgentEnvelope.history_share_get_resp:type_name -> liveagent.gateway.v2.HistoryShareGetResponse + 84, // 60: liveagent.gateway.v2.AgentEnvelope.history_share_set_resp:type_name -> liveagent.gateway.v2.HistoryShareSetResponse + 86, // 61: liveagent.gateway.v2.AgentEnvelope.history_share_resolve_resp:type_name -> liveagent.gateway.v2.HistoryShareResolveResponse + 89, // 62: liveagent.gateway.v2.AgentEnvelope.history_workdirs_resp:type_name -> liveagent.gateway.v2.HistoryWorkdirsResponse + 94, // 63: liveagent.gateway.v2.AgentEnvelope.provider_list_resp:type_name -> liveagent.gateway.v2.ProviderListResponse + 96, // 64: liveagent.gateway.v2.AgentEnvelope.settings_get_resp:type_name -> liveagent.gateway.v2.SettingsGetResponse + 98, // 65: liveagent.gateway.v2.AgentEnvelope.settings_update_resp:type_name -> liveagent.gateway.v2.SettingsUpdateResponse + 101, // 66: liveagent.gateway.v2.AgentEnvelope.settings_sync:type_name -> liveagent.gateway.v2.SettingsSyncEvent + 103, // 67: liveagent.gateway.v2.AgentEnvelope.skill_files_list_resp:type_name -> liveagent.gateway.v2.SkillFilesListResponse + 105, // 68: liveagent.gateway.v2.AgentEnvelope.skill_metadata_read_resp:type_name -> liveagent.gateway.v2.SkillMetadataReadResponse + 107, // 69: liveagent.gateway.v2.AgentEnvelope.skill_text_read_resp:type_name -> liveagent.gateway.v2.SkillTextReadResponse + 112, // 70: liveagent.gateway.v2.AgentEnvelope.file_mention_list_resp:type_name -> liveagent.gateway.v2.FileMentionListResponse + 11, // 71: liveagent.gateway.v2.AgentEnvelope.upload_readable_files_resp:type_name -> liveagent.gateway.v2.UploadReadableFilesResponse + 115, // 72: liveagent.gateway.v2.AgentEnvelope.fs_roots_resp:type_name -> liveagent.gateway.v2.FsRootsResponse + 139, // 73: liveagent.gateway.v2.AgentEnvelope.pong:type_name -> liveagent.gateway.v2.PongResponse + 118, // 74: liveagent.gateway.v2.AgentEnvelope.fs_list_dirs_resp:type_name -> liveagent.gateway.v2.FsListDirsResponse + 13, // 75: liveagent.gateway.v2.AgentEnvelope.uploaded_image_preview_resp:type_name -> liveagent.gateway.v2.UploadedImagePreviewResponse + 32, // 76: liveagent.gateway.v2.AgentEnvelope.memory_manage_resp:type_name -> liveagent.gateway.v2.MemoryManageResponse + 109, // 77: liveagent.gateway.v2.AgentEnvelope.skill_manage_resp:type_name -> liveagent.gateway.v2.SkillManageResponse + 120, // 78: liveagent.gateway.v2.AgentEnvelope.fs_create_project_folder_resp:type_name -> liveagent.gateway.v2.FsCreateProjectFolderResponse + 48, // 79: liveagent.gateway.v2.AgentEnvelope.terminal_response:type_name -> liveagent.gateway.v2.TerminalResponse + 49, // 80: liveagent.gateway.v2.AgentEnvelope.terminal_event:type_name -> liveagent.gateway.v2.TerminalEvent + 123, // 81: liveagent.gateway.v2.AgentEnvelope.fs_list_resp:type_name -> liveagent.gateway.v2.FsListResponse + 131, // 82: liveagent.gateway.v2.AgentEnvelope.fs_write_text_resp:type_name -> liveagent.gateway.v2.FsWriteTextResponse + 133, // 83: liveagent.gateway.v2.AgentEnvelope.fs_create_dir_resp:type_name -> liveagent.gateway.v2.FsCreateDirResponse + 135, // 84: liveagent.gateway.v2.AgentEnvelope.fs_rename_resp:type_name -> liveagent.gateway.v2.FsRenameResponse + 137, // 85: liveagent.gateway.v2.AgentEnvelope.fs_delete_resp:type_name -> liveagent.gateway.v2.FsDeleteResponse + 52, // 86: liveagent.gateway.v2.AgentEnvelope.git_response:type_name -> liveagent.gateway.v2.GitResponse + 125, // 87: liveagent.gateway.v2.AgentEnvelope.fs_read_editable_text_resp:type_name -> liveagent.gateway.v2.FsReadEditableTextResponse + 127, // 88: liveagent.gateway.v2.AgentEnvelope.fs_read_workspace_image_resp:type_name -> liveagent.gateway.v2.FsReadWorkspaceImageResponse + 39, // 89: liveagent.gateway.v2.AgentEnvelope.sftp_response:type_name -> liveagent.gateway.v2.SftpResponse + 40, // 90: liveagent.gateway.v2.AgentEnvelope.sftp_event:type_name -> liveagent.gateway.v2.SftpEvent + 58, // 91: liveagent.gateway.v2.AgentEnvelope.chat_queue_resp:type_name -> liveagent.gateway.v2.ChatQueueResponse + 59, // 92: liveagent.gateway.v2.AgentEnvelope.chat_queue_event:type_name -> liveagent.gateway.v2.ChatQueueEvent + 61, // 93: liveagent.gateway.v2.AgentEnvelope.chat_control:type_name -> liveagent.gateway.v2.ChatControlEvent + 63, // 94: liveagent.gateway.v2.AgentEnvelope.runtime_status:type_name -> liveagent.gateway.v2.RuntimeStatusEvent + 100, // 95: liveagent.gateway.v2.AgentEnvelope.settings_reset_ssh_known_host_resp:type_name -> liveagent.gateway.v2.SettingsResetSshKnownHostResponse + 62, // 96: liveagent.gateway.v2.AgentEnvelope.chat_runtime_snapshot:type_name -> liveagent.gateway.v2.ChatRuntimeSnapshot + 142, // 97: liveagent.gateway.v2.AgentEnvelope.provider_models_resp:type_name -> liveagent.gateway.v2.ProviderModelsResponse + 15, // 98: liveagent.gateway.v2.AgentEnvelope.tunnel_desired:type_name -> liveagent.gateway.v2.TunnelDesiredState + 20, // 99: liveagent.gateway.v2.AgentEnvelope.tunnel_mutation_result:type_name -> liveagent.gateway.v2.TunnelMutationResult + 24, // 100: liveagent.gateway.v2.AgentEnvelope.tunnel_frame:type_name -> liveagent.gateway.v2.TunnelFrame + 22, // 101: liveagent.gateway.v2.AgentEnvelope.tunnel_probe_report:type_name -> liveagent.gateway.v2.TunnelProbeReport + 26, // 102: liveagent.gateway.v2.AgentEnvelope.workspace_activity:type_name -> liveagent.gateway.v2.WorkspaceActivityEvent + 30, // 103: liveagent.gateway.v2.AgentEnvelope.managed_process_response:type_name -> liveagent.gateway.v2.ManagedProcessResponse + 28, // 104: liveagent.gateway.v2.AgentEnvelope.managed_process_snapshot:type_name -> liveagent.gateway.v2.ManagedProcessSnapshot + 77, // 105: liveagent.gateway.v2.AgentEnvelope.history_branch_resp:type_name -> liveagent.gateway.v2.HistoryBranchResponse + 144, // 106: liveagent.gateway.v2.AgentEnvelope.provider_usage_resp:type_name -> liveagent.gateway.v2.ProviderUsageResponse + 145, // 107: liveagent.gateway.v2.AgentEnvelope.chat_ingress_batch:type_name -> liveagent.gateway.v2.ChatIngressBatch + 151, // 108: liveagent.gateway.v2.AgentEnvelope.chat_ingress_resume:type_name -> liveagent.gateway.v2.ChatIngressResume + 153, // 109: liveagent.gateway.v2.AgentEnvelope.chat_ingress_fragment:type_name -> liveagent.gateway.v2.ChatIngressFragment + 129, // 110: liveagent.gateway.v2.AgentEnvelope.chat_file_open_resp:type_name -> liveagent.gateway.v2.ChatFileOpenResponse + 140, // 111: liveagent.gateway.v2.AgentEnvelope.error:type_name -> liveagent.gateway.v2.ErrorResponse + 156, // 112: liveagent.gateway.v2.AgentEnvelope.history_set_cwd_resp:type_name -> liveagent.gateway.v2.HistorySetCwdResponse + 9, // 113: liveagent.gateway.v2.UploadReadableFilesRequest.files:type_name -> liveagent.gateway.v2.UploadReadableFile + 8, // 114: liveagent.gateway.v2.UploadReadableFilesResponse.files:type_name -> liveagent.gateway.v2.ChatUploadedFile + 14, // 115: liveagent.gateway.v2.TunnelDesiredState.tunnels:type_name -> liveagent.gateway.v2.TunnelSpec + 16, // 116: liveagent.gateway.v2.TunnelStatus.local:type_name -> liveagent.gateway.v2.TunnelHealth + 17, // 117: liveagent.gateway.v2.TunnelStateSnapshot.tunnels:type_name -> liveagent.gateway.v2.TunnelStatus + 16, // 118: liveagent.gateway.v2.TunnelStateSnapshot.relay:type_name -> liveagent.gateway.v2.TunnelHealth + 16, // 119: liveagent.gateway.v2.TunnelProbeResult.local:type_name -> liveagent.gateway.v2.TunnelHealth + 21, // 120: liveagent.gateway.v2.TunnelProbeReport.results:type_name -> liveagent.gateway.v2.TunnelProbeResult + 0, // 121: liveagent.gateway.v2.TunnelFrame.kind:type_name -> liveagent.gateway.v2.TunnelFrameKind + 23, // 122: liveagent.gateway.v2.TunnelFrame.headers:type_name -> liveagent.gateway.v2.TunnelHeader + 1, // 123: liveagent.gateway.v2.TunnelFrame.ws_message_type:type_name -> liveagent.gateway.v2.TunnelWsMessageType + 27, // 124: liveagent.gateway.v2.ManagedProcessSnapshot.processes:type_name -> liveagent.gateway.v2.ManagedProcessRecord + 28, // 125: liveagent.gateway.v2.ManagedProcessResponse.snapshot:type_name -> liveagent.gateway.v2.ManagedProcessSnapshot + 35, // 126: liveagent.gateway.v2.TerminalSession.ssh:type_name -> liveagent.gateway.v2.TerminalSshMetadata + 37, // 127: liveagent.gateway.v2.SftpResponse.entries:type_name -> liveagent.gateway.v2.SftpEntry + 37, // 128: liveagent.gateway.v2.SftpResponse.entry:type_name -> liveagent.gateway.v2.SftpEntry + 38, // 129: liveagent.gateway.v2.SftpResponse.transfer:type_name -> liveagent.gateway.v2.SftpTransfer + 38, // 130: liveagent.gateway.v2.SftpEvent.transfer:type_name -> liveagent.gateway.v2.SftpTransfer + 43, // 131: liveagent.gateway.v2.TerminalSshTabsSnapshot.tabs:type_name -> liveagent.gateway.v2.TerminalSshTab + 45, // 132: liveagent.gateway.v2.TerminalSshLocalForwardsSnapshot.forwards:type_name -> liveagent.gateway.v2.TerminalSshLocalForward + 45, // 133: liveagent.gateway.v2.TerminalSshLocalForwardAction.forward:type_name -> liveagent.gateway.v2.TerminalSshLocalForward + 34, // 134: liveagent.gateway.v2.TerminalResponse.sessions:type_name -> liveagent.gateway.v2.TerminalSession + 34, // 135: liveagent.gateway.v2.TerminalResponse.session:type_name -> liveagent.gateway.v2.TerminalSession + 42, // 136: liveagent.gateway.v2.TerminalResponse.shell_options:type_name -> liveagent.gateway.v2.TerminalShellOption + 41, // 137: liveagent.gateway.v2.TerminalResponse.ssh_prompt:type_name -> liveagent.gateway.v2.TerminalSshPrompt + 44, // 138: liveagent.gateway.v2.TerminalResponse.ssh_tabs:type_name -> liveagent.gateway.v2.TerminalSshTabsSnapshot + 46, // 139: liveagent.gateway.v2.TerminalResponse.ssh_local_forwards:type_name -> liveagent.gateway.v2.TerminalSshLocalForwardsSnapshot + 47, // 140: liveagent.gateway.v2.TerminalResponse.ssh_local_forward:type_name -> liveagent.gateway.v2.TerminalSshLocalForwardAction + 34, // 141: liveagent.gateway.v2.TerminalEvent.session:type_name -> liveagent.gateway.v2.TerminalSession + 44, // 142: liveagent.gateway.v2.TerminalEvent.ssh_tabs:type_name -> liveagent.gateway.v2.TerminalSshTabsSnapshot + 47, // 143: liveagent.gateway.v2.TerminalEvent.ssh_local_forward:type_name -> liveagent.gateway.v2.TerminalSshLocalForwardAction + 34, // 144: liveagent.gateway.v2.TerminalStreamFrame.session:type_name -> liveagent.gateway.v2.TerminalSession + 6, // 145: liveagent.gateway.v2.ChatRequest.selected_model:type_name -> liveagent.gateway.v2.ChatSelectedModel + 8, // 146: liveagent.gateway.v2.ChatRequest.uploaded_files:type_name -> liveagent.gateway.v2.ChatUploadedFile + 7, // 147: liveagent.gateway.v2.ChatRequest.runtime_controls:type_name -> liveagent.gateway.v2.ChatRuntimeControls + 53, // 148: liveagent.gateway.v2.ChatCommandRequest.request:type_name -> liveagent.gateway.v2.ChatRequest + 54, // 149: liveagent.gateway.v2.ChatCommandRequest.base_message_ref:type_name -> liveagent.gateway.v2.ChatMessageRef + 55, // 150: liveagent.gateway.v2.ChatCommandRequest.cancel:type_name -> liveagent.gateway.v2.CancelChatRequest + 2, // 151: liveagent.gateway.v2.ChatEvent.type:type_name -> liveagent.gateway.v2.ChatEvent.ChatEventType + 64, // 152: liveagent.gateway.v2.RuntimeStatusEvent.active_runs:type_name -> liveagent.gateway.v2.ChatRunReport + 64, // 153: liveagent.gateway.v2.RuntimeStatusEvent.finished_runs:type_name -> liveagent.gateway.v2.ChatRunReport + 69, // 154: liveagent.gateway.v2.HistoryListResponse.conversations:type_name -> liveagent.gateway.v2.ConversationSummary + 69, // 155: liveagent.gateway.v2.HistoryGetResponse.conversation:type_name -> liveagent.gateway.v2.ConversationSummary + 54, // 156: liveagent.gateway.v2.HistoryPrefixRequest.base_message_ref:type_name -> liveagent.gateway.v2.ChatMessageRef + 69, // 157: liveagent.gateway.v2.HistoryPrefixResponse.conversation:type_name -> liveagent.gateway.v2.ConversationSummary + 69, // 158: liveagent.gateway.v2.HistoryRenameResponse.conversation:type_name -> liveagent.gateway.v2.ConversationSummary + 54, // 159: liveagent.gateway.v2.HistoryBranchRequest.base_message_ref:type_name -> liveagent.gateway.v2.ChatMessageRef + 69, // 160: liveagent.gateway.v2.HistoryBranchResponse.conversation:type_name -> liveagent.gateway.v2.ConversationSummary + 69, // 161: liveagent.gateway.v2.HistoryPinResponse.conversation:type_name -> liveagent.gateway.v2.ConversationSummary + 80, // 162: liveagent.gateway.v2.HistoryShareGetResponse.share:type_name -> liveagent.gateway.v2.HistoryShareStatus + 80, // 163: liveagent.gateway.v2.HistoryShareSetResponse.share:type_name -> liveagent.gateway.v2.HistoryShareStatus + 69, // 164: liveagent.gateway.v2.HistoryShareResolveResponse.conversation:type_name -> liveagent.gateway.v2.ConversationSummary + 88, // 165: liveagent.gateway.v2.HistoryWorkdirsResponse.workdirs:type_name -> liveagent.gateway.v2.HistoryWorkdirSummary + 69, // 166: liveagent.gateway.v2.HistorySyncEvent.conversation:type_name -> liveagent.gateway.v2.ConversationSummary + 111, // 167: liveagent.gateway.v2.FileMentionListResponse.entries:type_name -> liveagent.gateway.v2.FileMentionEntry + 113, // 168: liveagent.gateway.v2.FsRootsResponse.roots:type_name -> liveagent.gateway.v2.FsRoot + 117, // 169: liveagent.gateway.v2.FsListDirsResponse.entries:type_name -> liveagent.gateway.v2.FsDirEntry + 122, // 170: liveagent.gateway.v2.FsListResponse.entries:type_name -> liveagent.gateway.v2.FsListEntry + 146, // 171: liveagent.gateway.v2.ChatIngressBatch.records:type_name -> liveagent.gateway.v2.ChatIngressRecord + 147, // 172: liveagent.gateway.v2.ChatIngressRecord.delta:type_name -> liveagent.gateway.v2.ChatIngressDelta + 149, // 173: liveagent.gateway.v2.ChatIngressRecord.checkpoint:type_name -> liveagent.gateway.v2.ChatIngressCheckpoint + 150, // 174: liveagent.gateway.v2.ChatIngressRecord.terminal:type_name -> liveagent.gateway.v2.ChatIngressTerminal + 148, // 175: liveagent.gateway.v2.ChatIngressRecord.heartbeat:type_name -> liveagent.gateway.v2.ChatIngressHeartbeat + 152, // 176: liveagent.gateway.v2.ChatIngressResume.runs:type_name -> liveagent.gateway.v2.ChatIngressRunResume + 3, // 177: liveagent.gateway.v2.ChatIngressAck.action:type_name -> liveagent.gateway.v2.ChatIngressAck.Action + 69, // 178: liveagent.gateway.v2.HistorySetCwdResponse.conversation:type_name -> liveagent.gateway.v2.ConversationSummary + 179, // [179:179] is the sub-list for method output_type + 179, // [179:179] is the sub-list for method input_type + 179, // [179:179] is the sub-list for extension type_name + 179, // [179:179] is the sub-list for extension extendee + 0, // [0:179] is the sub-list for field type_name } func init() { file_proto_v2_gateway_proto_init() } @@ -14013,6 +14153,7 @@ func file_proto_v2_gateway_proto_init() { (*GatewayEnvelope_HistoryBranch)(nil), (*GatewayEnvelope_ProviderUsage)(nil), (*GatewayEnvelope_ChatFileOpen)(nil), + (*GatewayEnvelope_HistorySetCwd)(nil), } file_proto_v2_gateway_proto_msgTypes[1].OneofWrappers = []any{ (*AgentEnvelope_ChatEvent)(nil), @@ -14077,6 +14218,7 @@ func file_proto_v2_gateway_proto_init() { (*AgentEnvelope_ChatIngressFragment)(nil), (*AgentEnvelope_ChatFileOpenResp)(nil), (*AgentEnvelope_Error)(nil), + (*AgentEnvelope_HistorySetCwdResp)(nil), } file_proto_v2_gateway_proto_msgTypes[15].OneofWrappers = []any{} file_proto_v2_gateway_proto_msgTypes[23].OneofWrappers = []any{} @@ -14097,7 +14239,7 @@ func file_proto_v2_gateway_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_v2_gateway_proto_rawDesc), len(file_proto_v2_gateway_proto_rawDesc)), NumEnums: 4, - NumMessages: 151, + NumMessages: 153, NumExtensions: 0, NumServices: 0, }, diff --git a/crates/agent-gateway/internal/protocol/pbws/guard.go b/crates/agent-gateway/internal/protocol/pbws/guard.go index 26edd31a8..3b3ef86a6 100644 --- a/crates/agent-gateway/internal/protocol/pbws/guard.go +++ b/crates/agent-gateway/internal/protocol/pbws/guard.go @@ -34,6 +34,7 @@ func vetAgentRequest(sm session.AgentView, env *gatewayv2.GatewayEnvelope) error *gatewayv2.GatewayEnvelope_HistoryDelete, *gatewayv2.GatewayEnvelope_HistoryPrefix, *gatewayv2.GatewayEnvelope_HistoryPin, + *gatewayv2.GatewayEnvelope_HistorySetCwd, *gatewayv2.GatewayEnvelope_HistoryShareGet, *gatewayv2.GatewayEnvelope_HistoryShareSet, *gatewayv2.GatewayEnvelope_HistoryWorkdirs, diff --git a/crates/agent-gateway/proto/v2/gateway.proto b/crates/agent-gateway/proto/v2/gateway.proto index 5706cab87..65e8ce569 100644 --- a/crates/agent-gateway/proto/v2/gateway.proto +++ b/crates/agent-gateway/proto/v2/gateway.proto @@ -61,6 +61,7 @@ message GatewayEnvelope { HistoryBranchRequest history_branch = 92; ProviderUsageRequest provider_usage = 93; ChatFileOpenRequest chat_file_open = 94; + HistorySetCwdRequest history_set_cwd = 95; } // Legacy tunnel control/frame payloads (pre-rewrite protocol) and the @@ -135,6 +136,7 @@ message AgentEnvelope { ChatIngressFragment chat_ingress_fragment = 97; ChatFileOpenResponse chat_file_open_resp = 98; ErrorResponse error = 99; + HistorySetCwdResponse history_set_cwd_resp = 100; } // Legacy tunnel control/frame payloads (pre-rewrite protocol) and the @@ -1280,3 +1282,12 @@ message ChatIngressAck { REJECTED = 4; } } + +message HistorySetCwdRequest { + string conversation_id = 1; + string cwd = 2; +} + +message HistorySetCwdResponse { + ConversationSummary conversation = 1; +} diff --git a/crates/agent-gateway/test/webui/gateway-socket-client.test.mjs b/crates/agent-gateway/test/webui/gateway-socket-client.test.mjs index e37d00d75..7f297d663 100644 --- a/crates/agent-gateway/test/webui/gateway-socket-client.test.mjs +++ b/crates/agent-gateway/test/webui/gateway-socket-client.test.mjs @@ -1043,6 +1043,44 @@ test("GatewayWebSocketClient sends history branch requests with the base message resetGatewayWebSocketClient(); }); +test("GatewayWebSocketClient sends history cwd update requests", async () => { + installBrowser(); + const { codec, getGatewayWebSocketClient, resetGatewayWebSocketClient } = loadGatewaySocket(); + resetGatewayWebSocketClient(); + + const client = getGatewayWebSocketClient("token"); + const updatePromise = client.setHistoryCwd("conversation-1", "/tmp/project-b"); + const socket = await connectAndAuth(codec); + await waitFor(() => findAgentRequest(codec, socket, "history_set_cwd"), "history cwd frame"); + const request = findAgentRequest(codec, socket, "history_set_cwd"); + assert.deepEqual(request.json.agent_request.history_set_cwd, { + conversation_id: "conversation-1", + cwd: "/tmp/project-b", + }); + socket.receiveBinary( + codec.encodeServerFrame({ + request_id: request.requestId, + agent_response: { + history_set_cwd_resp: { + conversation: { + id: "conversation-1", + title: "Moved conversation", + cwd: "/tmp/project-b", + message_count: 6, + created_at: 1700000000100, + updated_at: 1700000000200, + }, + }, + }, + }), + ); + const updated = await updatePromise; + assert.equal(updated.id, "conversation-1"); + assert.equal(updated.cwd, "/tmp/project-b"); + + resetGatewayWebSocketClient(); +}); + test("GatewayWebSocketClient reconnects before read requests when an authenticated socket goes stale", async () => { installBrowser(); const { codec, getGatewayWebSocketClient, resetGatewayWebSocketClient } = loadGatewaySocket(); diff --git a/crates/agent-gateway/web/src/app/sidebar/GatewaySidebarContainer.tsx b/crates/agent-gateway/web/src/app/sidebar/GatewaySidebarContainer.tsx index 2fb4021a2..0efc019e7 100644 --- a/crates/agent-gateway/web/src/app/sidebar/GatewaySidebarContainer.tsx +++ b/crates/agent-gateway/web/src/app/sidebar/GatewaySidebarContainer.tsx @@ -214,6 +214,27 @@ export function GatewaySidebarContainer(props: GatewaySidebarContainerProps) { void store.setPinned(id, isPinned); }); + const handleMoveToWorkspace = useStableCallback((id: string, cwd: string) => { + if (sectionsDisabled) { + return; + } + clearMutationErrors(); + void store.setCwd(id, cwd); + }); + + const handleMoveConversationsToWorkspace = useStableCallback( + async (ids: readonly string[], cwd: string) => { + if (sectionsDisabled) { + return ids; + } + clearMutationErrors(); + const results = await Promise.all( + ids.map(async (id) => ({ id, moved: await store.setCwd(id, cwd) })), + ); + return results.filter((result) => !result.moved).map((result) => result.id); + }, + ); + const handleDeleteConversation = useStableCallback((id: string) => { if (sectionsDisabled) { return; @@ -369,6 +390,8 @@ export function GatewaySidebarContainer(props: GatewaySidebarContainerProps) { onCommitRename={handleCommitRename} onCancelRename={handleCancelRename} onSetPinned={handleSetPinned} + onMoveToWorkspace={handleMoveToWorkspace} + onMoveConversationsToWorkspace={handleMoveConversationsToWorkspace} canShareConversations={props.canShareConversations} sharedConversationCount={props.sharedConversationCount} onShareConversation={props.onShareConversation} diff --git a/crates/agent-gateway/web/src/i18n/config.ts b/crates/agent-gateway/web/src/i18n/config.ts index 7a91c125d..2d84eeae0 100644 --- a/crates/agent-gateway/web/src/i18n/config.ts +++ b/crates/agent-gateway/web/src/i18n/config.ts @@ -87,6 +87,7 @@ export const translations: Record> = { "chat.conversationPin": "置顶对话", "chat.conversationUnpin": "取消置顶", "chat.conversationRename": "修改标题", + "chat.conversationMoveToWorkspace": "移动到工作空间", "chat.conversationShare": "分享", "chat.conversationDelete": "删除对话", "chat.conversationDeleteConfirm": "删除「{title}」?", @@ -110,6 +111,8 @@ export const translations: Record> = { "chat.history.renameFailed": "修改历史对话标题失败", "chat.history.renameBlockedRunning": "后台任务仍在运行,暂时不能修改该对话标题。", "chat.history.pinFailed": "更新历史对话置顶状态失败", + "chat.history.moveFailed": "移动历史对话工作空间失败", + "chat.history.moveBlockedRunning": "后台任务仍在运行,暂时不能移动该对话。", "chat.history.deleteFailed": "删除历史对话失败", "chat.history.deleteBlockedRunning": "后台任务仍在运行,暂时不能删除该对话。", "chat.history.openFailed": "读取历史对话失败", @@ -2300,6 +2303,7 @@ export const translations: Record> = { "chat.conversationPin": "Pin conversation", "chat.conversationUnpin": "Unpin", "chat.conversationRename": "Rename", + "chat.conversationMoveToWorkspace": "Move to workspace", "chat.conversationShare": "Share", "chat.conversationDelete": "Delete conversation", "chat.conversationDeleteConfirm": 'Delete "{title}"?', @@ -2326,6 +2330,9 @@ export const translations: Record> = { "chat.history.renameBlockedRunning": "A background task is still running; the title cannot be changed yet.", "chat.history.pinFailed": "Failed to update pin state", + "chat.history.moveFailed": "Failed to move conversation to workspace", + "chat.history.moveBlockedRunning": + "A background task is still running; the conversation cannot be moved yet.", "chat.history.deleteFailed": "Failed to delete conversation", "chat.history.deleteBlockedRunning": "A background task is still running; the conversation cannot be deleted yet.", diff --git a/crates/agent-gateway/web/src/lib/gatewaySocket.ts b/crates/agent-gateway/web/src/lib/gatewaySocket.ts index 0740f8bf9..eafcf228a 100644 --- a/crates/agent-gateway/web/src/lib/gatewaySocket.ts +++ b/crates/agent-gateway/web/src/lib/gatewaySocket.ts @@ -2527,6 +2527,13 @@ export class GatewayWebSocketClient { }); } + async setHistoryCwd(conversationId: string, cwd: string): Promise { + return this.request("history.set_cwd", { + conversation_id: conversationId, + cwd, + }); + } + async getHistoryShare(conversationId: string): Promise { return this.requestWithRecovery("history.share.get", { conversation_id: conversationId, @@ -3816,6 +3823,7 @@ export type GatewayWebSocketClientLike = { baseMessageRef: HistoryMessageRef, ): Promise; pinHistory(conversationId: string, isPinned: boolean): Promise; + setHistoryCwd(conversationId: string, cwd: string): Promise; getHistoryShare(conversationId: string): Promise; setHistoryShare( conversationId: string, diff --git a/crates/agent-gateway/web/src/lib/gatewaySocketV2/adapters.ts b/crates/agent-gateway/web/src/lib/gatewaySocketV2/adapters.ts index ebfa05117..a492740dd 100644 --- a/crates/agent-gateway/web/src/lib/gatewaySocketV2/adapters.ts +++ b/crates/agent-gateway/web/src/lib/gatewaySocketV2/adapters.ts @@ -60,6 +60,7 @@ import { HistoryPinRequestSchema, HistoryPrefixRequestSchema, HistoryRenameRequestSchema, + HistorySetCwdRequestSchema, HistoryShareGetRequestSchema, HistoryShareSetRequestSchema, HistoryWorkdirsRequestSchema, @@ -542,6 +543,14 @@ function agentRequestPayload(type: string, body: J): GatewayEnvelope["payload"] isPinned: bool(body.is_pinned), }), }; + case "history.set_cwd": + return { + case: "historySetCwd", + value: create(HistorySetCwdRequestSchema, { + conversationId: trimStr(body.conversation_id), + cwd: trimStr(body.cwd), + }), + }; case "history.share.get": return { case: "historyShareGet", @@ -1010,6 +1019,7 @@ function decodeAgentResponse(envelope: AgentEnvelope, options: { agentOnline: bo case "historyRenameResp": case "historyBranchResp": case "historyPinResp": + case "historySetCwdResp": if (!payload.value.conversation) { frameError("unexpected agent response"); } diff --git a/crates/agent-gateway/web/src/lib/proto/gen/proto/v2/gateway_pb.ts b/crates/agent-gateway/web/src/lib/proto/gen/proto/v2/gateway_pb.ts index d8ad964f6..4c6948049 100644 --- a/crates/agent-gateway/web/src/lib/proto/gen/proto/v2/gateway_pb.ts +++ b/crates/agent-gateway/web/src/lib/proto/gen/proto/v2/gateway_pb.ts @@ -10,7 +10,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file proto/v2/gateway.proto. */ export const file_proto_v2_gateway: GenFile = /*@__PURE__*/ - fileDesc("ChZwcm90by92Mi9nYXRld2F5LnByb3RvEhRsaXZlYWdlbnQuZ2F0ZXdheS52MiK2GwoPR2F0ZXdheUVudmVsb3BlEhIKCnJlcXVlc3RfaWQYASABKAkSEQoJdGltZXN0YW1wGAIgASgDEkAKDGNoYXRfY29tbWFuZBgKIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRDb21tYW5kUmVxdWVzdEgAEj4KC2Nyb25fbWFuYWdlGBQgASgLMicubGl2ZWFnZW50LmdhdGV3YXkudjIuQ3Jvbk1hbmFnZVJlcXVlc3RIABJACgxoaXN0b3J5X2xpc3QYHiABKAsyKC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5TGlzdFJlcXVlc3RIABI+CgtoaXN0b3J5X2dldBgfIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlHZXRSZXF1ZXN0SAASRAoOaGlzdG9yeV9yZW5hbWUYICABKAsyKi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5UmVuYW1lUmVxdWVzdEgAEkQKDmhpc3RvcnlfZGVsZXRlGCEgASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeURlbGV0ZVJlcXVlc3RIABJECg5oaXN0b3J5X3ByZWZpeBgiIAEoCzIqLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlQcmVmaXhSZXF1ZXN0SAASPgoLaGlzdG9yeV9waW4YIyABKAsyJy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5UGluUmVxdWVzdEgAEkkKEWhpc3Rvcnlfc2hhcmVfZ2V0GCQgASgLMiwubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVNoYXJlR2V0UmVxdWVzdEgAEkkKEWhpc3Rvcnlfc2hhcmVfc2V0GCUgASgLMiwubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVNoYXJlU2V0UmVxdWVzdEgAElEKFWhpc3Rvcnlfc2hhcmVfcmVzb2x2ZRgmIAEoCzIwLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlTaGFyZVJlc29sdmVSZXF1ZXN0SAASSAoQaGlzdG9yeV93b3JrZGlycxgnIAEoCzIsLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlXb3JrZGlyc1JlcXVlc3RIABJCCg1wcm92aWRlcl9saXN0GCggASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuUHJvdmlkZXJMaXN0UmVxdWVzdEgAEkAKDHNldHRpbmdzX2dldBgpIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNldHRpbmdzR2V0UmVxdWVzdEgAEkYKD3NldHRpbmdzX3VwZGF0ZRgqIAEoCzIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNldHRpbmdzVXBkYXRlUmVxdWVzdEgAEkcKEHNraWxsX2ZpbGVzX2xpc3QYKyABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Ta2lsbEZpbGVzTGlzdFJlcXVlc3RIABJNChNza2lsbF9tZXRhZGF0YV9yZWFkGCwgASgLMi4ubGl2ZWFnZW50LmdhdGV3YXkudjIuU2tpbGxNZXRhZGF0YVJlYWRSZXF1ZXN0SAASRQoPc2tpbGxfdGV4dF9yZWFkGC0gASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuU2tpbGxUZXh0UmVhZFJlcXVlc3RIABJJChFmaWxlX21lbnRpb25fbGlzdBguIAEoCzIsLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZpbGVNZW50aW9uTGlzdFJlcXVlc3RIABJRChV1cGxvYWRfcmVhZGFibGVfZmlsZXMYLyABKAsyMC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5VcGxvYWRSZWFkYWJsZUZpbGVzUmVxdWVzdEgAEjgKCGZzX3Jvb3RzGDAgASgLMiQubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNSb290c1JlcXVlc3RIABI/Cgxmc19saXN0X2RpcnMYMSABKAsyJy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc0xpc3REaXJzUmVxdWVzdEgAEjEKBHBpbmcYMiABKAsyIS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5QaW5nUmVxdWVzdEgAElMKFnVwbG9hZGVkX2ltYWdlX3ByZXZpZXcYMyABKAsyMS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5VcGxvYWRlZEltYWdlUHJldmlld1JlcXVlc3RIABJCCg1tZW1vcnlfbWFuYWdlGDQgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuTWVtb3J5TWFuYWdlUmVxdWVzdEgAEkAKDHNraWxsX21hbmFnZRg1IAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNraWxsTWFuYWdlUmVxdWVzdEgAElYKGGZzX2NyZWF0ZV9wcm9qZWN0X2ZvbGRlchg2IAEoCzIyLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzQ3JlYXRlUHJvamVjdEZvbGRlclJlcXVlc3RIABJBChB0ZXJtaW5hbF9yZXF1ZXN0GDcgASgLMiUubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxSZXF1ZXN0SAASNgoHZnNfbGlzdBg4IAEoCzIjLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzTGlzdFJlcXVlc3RIABJBCg1mc193cml0ZV90ZXh0GDkgASgLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNXcml0ZVRleHRSZXF1ZXN0SAASQQoNZnNfY3JlYXRlX2Rpchg6IAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzQ3JlYXRlRGlyUmVxdWVzdEgAEjoKCWZzX3JlbmFtZRg7IAEoCzIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUmVuYW1lUmVxdWVzdEgAEjoKCWZzX2RlbGV0ZRg8IAEoCzIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzRGVsZXRlUmVxdWVzdEgAEjcKC2dpdF9yZXF1ZXN0GD0gASgLMiAubGl2ZWFnZW50LmdhdGV3YXkudjIuR2l0UmVxdWVzdEgAElAKFWZzX3JlYWRfZWRpdGFibGVfdGV4dBg+IAEoCzIvLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUmVhZEVkaXRhYmxlVGV4dFJlcXVlc3RIABJUChdmc19yZWFkX3dvcmtzcGFjZV9pbWFnZRg/IAEoCzIxLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUmVhZFdvcmtzcGFjZUltYWdlUmVxdWVzdEgAEjkKDHNmdHBfcmVxdWVzdBhAIAEoCzIhLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNmdHBSZXF1ZXN0SAASRgoPcHJvdmlkZXJfbW9kZWxzGEEgASgLMisubGl2ZWFnZW50LmdhdGV3YXkudjIuUHJvdmlkZXJNb2RlbHNSZXF1ZXN0SAASXwodc2V0dGluZ3NfcmVzZXRfc3NoX2tub3duX2hvc3QYSCABKAsyNi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZXR0aW5nc1Jlc2V0U3NoS25vd25Ib3N0UmVxdWVzdEgAEjwKCmNoYXRfcXVldWUYSSABKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0UXVldWVSZXF1ZXN0SAASQAoQY2hhdF9pbmdyZXNzX2FjaxhLIAEoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRJbmdyZXNzQWNrSAASQQoMdHVubmVsX3N0YXRlGFAgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuVHVubmVsU3RhdGVTbmFwc2hvdEgAEj8KD3R1bm5lbF9tdXRhdGlvbhhRIAEoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbE11dGF0aW9uSAASOQoMdHVubmVsX2ZyYW1lGFIgASgLMiEubGl2ZWFnZW50LmdhdGV3YXkudjIuVHVubmVsRnJhbWVIABJGCg93b3Jrc3BhY2Vfd2F0Y2gYWiABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Xb3Jrc3BhY2VXYXRjaFJlcXVlc3RIABJOChdtYW5hZ2VkX3Byb2Nlc3NfcmVxdWVzdBhbIAEoCzIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLk1hbmFnZWRQcm9jZXNzUmVxdWVzdEgAEkQKDmhpc3RvcnlfYnJhbmNoGFwgASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeUJyYW5jaFJlcXVlc3RIABJECg5wcm92aWRlcl91c2FnZRhdIAEoCzIqLmxpdmVhZ2VudC5nYXRld2F5LnYyLlByb3ZpZGVyVXNhZ2VSZXF1ZXN0SAASQwoOY2hhdF9maWxlX29wZW4YXiABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0RmlsZU9wZW5SZXF1ZXN0SABCCQoHcGF5bG9hZEoECEMQREoECEQQRUoECEUQRkoECEoQSyKaJAoNQWdlbnRFbnZlbG9wZRISCgpyZXF1ZXN0X2lkGAEgASgJEhEKCXRpbWVzdGFtcBgCIAEoAxI1CgpjaGF0X2V2ZW50GAogASgLMh8ubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdEV2ZW50SAASRAoQY3Jvbl9tYW5hZ2VfcmVzcBgUIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNyb25NYW5hZ2VSZXNwb25zZUgAEkYKEWhpc3RvcnlfbGlzdF9yZXNwGB4gASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeUxpc3RSZXNwb25zZUgAEkQKEGhpc3RvcnlfZ2V0X3Jlc3AYHyABKAsyKC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5R2V0UmVzcG9uc2VIABJKChNoaXN0b3J5X3JlbmFtZV9yZXNwGCAgASgLMisubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVJlbmFtZVJlc3BvbnNlSAASSgoTaGlzdG9yeV9kZWxldGVfcmVzcBghIAEoCzIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlEZWxldGVSZXNwb25zZUgAEj4KDGhpc3Rvcnlfc3luYxgiIAEoCzImLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlTeW5jRXZlbnRIABJKChNoaXN0b3J5X3ByZWZpeF9yZXNwGCMgASgLMisubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVByZWZpeFJlc3BvbnNlSAASRAoQaGlzdG9yeV9waW5fcmVzcBgkIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlQaW5SZXNwb25zZUgAEk8KFmhpc3Rvcnlfc2hhcmVfZ2V0X3Jlc3AYJSABKAsyLS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5U2hhcmVHZXRSZXNwb25zZUgAEk8KFmhpc3Rvcnlfc2hhcmVfc2V0X3Jlc3AYJiABKAsyLS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5U2hhcmVTZXRSZXNwb25zZUgAElcKGmhpc3Rvcnlfc2hhcmVfcmVzb2x2ZV9yZXNwGCcgASgLMjEubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVNoYXJlUmVzb2x2ZVJlc3BvbnNlSAASTgoVaGlzdG9yeV93b3JrZGlyc19yZXNwGDggASgLMi0ubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVdvcmtkaXJzUmVzcG9uc2VIABJIChJwcm92aWRlcl9saXN0X3Jlc3AYKCABKAsyKi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Qcm92aWRlckxpc3RSZXNwb25zZUgAEkYKEXNldHRpbmdzX2dldF9yZXNwGCkgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuU2V0dGluZ3NHZXRSZXNwb25zZUgAEkwKFHNldHRpbmdzX3VwZGF0ZV9yZXNwGCogASgLMiwubGl2ZWFnZW50LmdhdGV3YXkudjIuU2V0dGluZ3NVcGRhdGVSZXNwb25zZUgAEkAKDXNldHRpbmdzX3N5bmMYKyABKAsyJy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZXR0aW5nc1N5bmNFdmVudEgAEk0KFXNraWxsX2ZpbGVzX2xpc3RfcmVzcBgsIAEoCzIsLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNraWxsRmlsZXNMaXN0UmVzcG9uc2VIABJTChhza2lsbF9tZXRhZGF0YV9yZWFkX3Jlc3AYLSABKAsyLy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Ta2lsbE1ldGFkYXRhUmVhZFJlc3BvbnNlSAASSwoUc2tpbGxfdGV4dF9yZWFkX3Jlc3AYLiABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Ta2lsbFRleHRSZWFkUmVzcG9uc2VIABJPChZmaWxlX21lbnRpb25fbGlzdF9yZXNwGC8gASgLMi0ubGl2ZWFnZW50LmdhdGV3YXkudjIuRmlsZU1lbnRpb25MaXN0UmVzcG9uc2VIABJXChp1cGxvYWRfcmVhZGFibGVfZmlsZXNfcmVzcBgwIAEoCzIxLmxpdmVhZ2VudC5nYXRld2F5LnYyLlVwbG9hZFJlYWRhYmxlRmlsZXNSZXNwb25zZUgAEj4KDWZzX3Jvb3RzX3Jlc3AYMSABKAsyJS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc1Jvb3RzUmVzcG9uc2VIABIyCgRwb25nGDIgASgLMiIubGl2ZWFnZW50LmdhdGV3YXkudjIuUG9uZ1Jlc3BvbnNlSAASRQoRZnNfbGlzdF9kaXJzX3Jlc3AYMyABKAsyKC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc0xpc3REaXJzUmVzcG9uc2VIABJZCht1cGxvYWRlZF9pbWFnZV9wcmV2aWV3X3Jlc3AYNCABKAsyMi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5VcGxvYWRlZEltYWdlUHJldmlld1Jlc3BvbnNlSAASSAoSbWVtb3J5X21hbmFnZV9yZXNwGDUgASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuTWVtb3J5TWFuYWdlUmVzcG9uc2VIABJGChFza2lsbF9tYW5hZ2VfcmVzcBg2IAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNraWxsTWFuYWdlUmVzcG9uc2VIABJcCh1mc19jcmVhdGVfcHJvamVjdF9mb2xkZXJfcmVzcBg3IAEoCzIzLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzQ3JlYXRlUHJvamVjdEZvbGRlclJlc3BvbnNlSAASQwoRdGVybWluYWxfcmVzcG9uc2UYOSABKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbFJlc3BvbnNlSAASPQoOdGVybWluYWxfZXZlbnQYOiABKAsyIy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbEV2ZW50SAASPAoMZnNfbGlzdF9yZXNwGDsgASgLMiQubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNMaXN0UmVzcG9uc2VIABJHChJmc193cml0ZV90ZXh0X3Jlc3AYPCABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc1dyaXRlVGV4dFJlc3BvbnNlSAASRwoSZnNfY3JlYXRlX2Rpcl9yZXNwGD0gASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNDcmVhdGVEaXJSZXNwb25zZUgAEkAKDmZzX3JlbmFtZV9yZXNwGD4gASgLMiYubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNSZW5hbWVSZXNwb25zZUgAEkAKDmZzX2RlbGV0ZV9yZXNwGD8gASgLMiYubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNEZWxldGVSZXNwb25zZUgAEjkKDGdpdF9yZXNwb25zZRhAIAEoCzIhLmxpdmVhZ2VudC5nYXRld2F5LnYyLkdpdFJlc3BvbnNlSAASVgoaZnNfcmVhZF9lZGl0YWJsZV90ZXh0X3Jlc3AYQSABKAsyMC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc1JlYWRFZGl0YWJsZVRleHRSZXNwb25zZUgAEloKHGZzX3JlYWRfd29ya3NwYWNlX2ltYWdlX3Jlc3AYQiABKAsyMi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc1JlYWRXb3Jrc3BhY2VJbWFnZVJlc3BvbnNlSAASOwoNc2Z0cF9yZXNwb25zZRhJIAEoCzIiLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNmdHBSZXNwb25zZUgAEjUKCnNmdHBfZXZlbnQYSiABKAsyHy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZnRwRXZlbnRIABJCCg9jaGF0X3F1ZXVlX3Jlc3AYSyABKAsyJy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0UXVldWVSZXNwb25zZUgAEkAKEGNoYXRfcXVldWVfZXZlbnQYTCABKAsyJC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0UXVldWVFdmVudEgAEj4KDGNoYXRfY29udHJvbBhGIAEoCzImLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRDb250cm9sRXZlbnRIABJCCg5ydW50aW1lX3N0YXR1cxhHIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLlJ1bnRpbWVTdGF0dXNFdmVudEgAEmUKInNldHRpbmdzX3Jlc2V0X3NzaF9rbm93bl9ob3N0X3Jlc3AYSCABKAsyNy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZXR0aW5nc1Jlc2V0U3NoS25vd25Ib3N0UmVzcG9uc2VIABJKChVjaGF0X3J1bnRpbWVfc25hcHNob3QYTSABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0UnVudGltZVNuYXBzaG90SAASTAoUcHJvdmlkZXJfbW9kZWxzX3Jlc3AYTyABKAsyLC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Qcm92aWRlck1vZGVsc1Jlc3BvbnNlSAASQgoOdHVubmVsX2Rlc2lyZWQYUCABKAsyKC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxEZXNpcmVkU3RhdGVIABJMChZ0dW5uZWxfbXV0YXRpb25fcmVzdWx0GFEgASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuVHVubmVsTXV0YXRpb25SZXN1bHRIABI5Cgx0dW5uZWxfZnJhbWUYUiABKAsyIS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxGcmFtZUgAEkYKE3R1bm5lbF9wcm9iZV9yZXBvcnQYUyABKAsyJy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxQcm9iZVJlcG9ydEgAEkoKEndvcmtzcGFjZV9hY3Rpdml0eRhaIAEoCzIsLmxpdmVhZ2VudC5nYXRld2F5LnYyLldvcmtzcGFjZUFjdGl2aXR5RXZlbnRIABJQChhtYW5hZ2VkX3Byb2Nlc3NfcmVzcG9uc2UYWyABKAsyLC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5NYW5hZ2VkUHJvY2Vzc1Jlc3BvbnNlSAASUAoYbWFuYWdlZF9wcm9jZXNzX3NuYXBzaG90GFwgASgLMiwubGl2ZWFnZW50LmdhdGV3YXkudjIuTWFuYWdlZFByb2Nlc3NTbmFwc2hvdEgAEkoKE2hpc3RvcnlfYnJhbmNoX3Jlc3AYXSABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5QnJhbmNoUmVzcG9uc2VIABJKChNwcm92aWRlcl91c2FnZV9yZXNwGF4gASgLMisubGl2ZWFnZW50LmdhdGV3YXkudjIuUHJvdmlkZXJVc2FnZVJlc3BvbnNlSAASRAoSY2hhdF9pbmdyZXNzX2JhdGNoGF8gASgLMiYubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdEluZ3Jlc3NCYXRjaEgAEkYKE2NoYXRfaW5ncmVzc19yZXN1bWUYYCABKAsyJy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0SW5ncmVzc1Jlc3VtZUgAEkoKFWNoYXRfaW5ncmVzc19mcmFnbWVudBhhIAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRJbmdyZXNzRnJhZ21lbnRIABJJChNjaGF0X2ZpbGVfb3Blbl9yZXNwGGIgASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdEZpbGVPcGVuUmVzcG9uc2VIABI0CgVlcnJvchhjIAEoCzIjLmxpdmVhZ2VudC5nYXRld2F5LnYyLkVycm9yUmVzcG9uc2VIAEIJCgdwYXlsb2FkSgQIQxBESgQIRBBFSgQIRRBGSgQIThBPIlUKEUNoYXRTZWxlY3RlZE1vZGVsEhoKEmN1c3RvbV9wcm92aWRlcl9pZBgBIAEoCRINCgVtb2RlbBgCIAEoCRIVCg1wcm92aWRlcl90eXBlGAMgASgJImUKE0NoYXRSdW50aW1lQ29udHJvbHMSGAoQdGhpbmtpbmdfZW5hYmxlZBgBIAEoCBIhChluYXRpdmVfd2ViX3NlYXJjaF9lbmFibGVkGAIgASgIEhEKCXJlYXNvbmluZxgDIAEoCSJ1ChBDaGF0VXBsb2FkZWRGaWxlEhUKDXJlbGF0aXZlX3BhdGgYASABKAkSFQoNYWJzb2x1dGVfcGF0aBgCIAEoCRIRCglmaWxlX25hbWUYAyABKAkSDAoEa2luZBgEIAEoCRISCgpzaXplX2J5dGVzGAUgASgDIksKElVwbG9hZFJlYWRhYmxlRmlsZRIRCglmaWxlX25hbWUYASABKAkSEQoJbWltZV90eXBlGAIgASgJEg8KB2NvbnRlbnQYAyABKAwiZgoaVXBsb2FkUmVhZGFibGVGaWxlc1JlcXVlc3QSDwoHd29ya2RpchgBIAEoCRI3CgVmaWxlcxgCIAMoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLlVwbG9hZFJlYWRhYmxlRmlsZSJlChtVcGxvYWRSZWFkYWJsZUZpbGVzUmVzcG9uc2USNQoFZmlsZXMYASADKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0VXBsb2FkZWRGaWxlEg8KB3NraXBwZWQYAiADKAkiRQobVXBsb2FkZWRJbWFnZVByZXZpZXdSZXF1ZXN0Eg8KB3dvcmtkaXIYASABKAkSFQoNYWJzb2x1dGVfcGF0aBgCIAEoCSI/ChxVcGxvYWRlZEltYWdlUHJldmlld1Jlc3BvbnNlEhEKCW1pbWVfdHlwZRgBIAEoCRIMCgRkYXRhGAIgASgJInsKClR1bm5lbFNwZWMSCgoCaWQYASABKAkSEQoJc2x1Z19oaW50GAIgASgJEgwKBG5hbWUYAyABKAkSEgoKdGFyZ2V0X3VybBgEIAEoCRISCgpleHBpcmVzX2F0GAUgASgDEhgKEHByb2plY3RfcGF0aF9rZXkYBiABKAkiWQoSVHVubmVsRGVzaXJlZFN0YXRlEjEKB3R1bm5lbHMYASADKAsyIC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxTcGVjEhAKCHJldmlzaW9uGAIgASgEImYKDFR1bm5lbEhlYWx0aBIOCgZzdGF0dXMYASABKAkSEwoLaHR0cF9zdGF0dXMYAiABKA0SDQoFZXJyb3IYAyABKAkSEgoKY2hlY2tlZF9hdBgEIAEoAxIOCgZydHRfbXMYBSABKA0i8AEKDFR1bm5lbFN0YXR1cxIKCgJpZBgBIAEoCRIMCgRzbHVnGAIgASgJEgwKBG5hbWUYAyABKAkSEgoKdGFyZ2V0X3VybBgEIAEoCRITCgtwdWJsaWNfcGF0aBgFIAEoCRISCgpjcmVhdGVkX2F0GAYgASgDEhIKCmV4cGlyZXNfYXQYByABKAMSGgoSYWN0aXZlX2Nvbm5lY3Rpb25zGAggASgNEhgKEHByb2plY3RfcGF0aF9rZXkYCSABKAkSMQoFbG9jYWwYCiABKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxIZWFsdGgipQEKE1R1bm5lbFN0YXRlU25hcHNob3QSMwoHdHVubmVscxgBIAMoCzIiLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbFN0YXR1cxIQCghyZXZpc2lvbhgCIAEoBBIUCgxhZ2VudF9vbmxpbmUYAyABKAgSMQoFcmVsYXkYBCABKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxIZWFsdGgimQEKDlR1bm5lbE11dGF0aW9uEg4KBmFjdGlvbhgBIAEoCRIRCgl0dW5uZWxfaWQYAiABKAkSEgoKdGFyZ2V0X3VybBgDIAEoCRIMCgRuYW1lGAQgASgJEhgKC3R0bF9zZWNvbmRzGAUgASgNSACIAQESGAoQcHJvamVjdF9wYXRoX2tleRgGIAEoCUIOCgxfdHRsX3NlY29uZHMiVAoUVHVubmVsTXV0YXRpb25SZXN1bHQSEQoJdHVubmVsX2lkGAEgASgJEhIKCmVycm9yX2NvZGUYAiABKAkSFQoNZXJyb3JfbWVzc2FnZRgDIAEoCSJZChFUdW5uZWxQcm9iZVJlc3VsdBIRCgl0dW5uZWxfaWQYASABKAkSMQoFbG9jYWwYAiABKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxIZWFsdGgiTQoRVHVubmVsUHJvYmVSZXBvcnQSOAoHcmVzdWx0cxgBIAMoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbFByb2JlUmVzdWx0IisKDFR1bm5lbEhlYWRlchIMCgRuYW1lGAEgASgJEg0KBXZhbHVlGAIgASgJIvUCCgtUdW5uZWxGcmFtZRIRCglzdHJlYW1faWQYASABKAkSMwoEa2luZBgCIAEoDjIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbEZyYW1lS2luZBISCgp0YXJnZXRfdXJsGAMgASgJEg4KBm1ldGhvZBgEIAEoCRIMCgRwYXRoGAUgASgJEjMKB2hlYWRlcnMYBiADKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxIZWFkZXISDgoGc3RhdHVzGAcgASgNEgwKBGJvZHkYCCABKAwSDQoFZXJyb3IYCSABKAkSQgoPd3NfbWVzc2FnZV90eXBlGAogASgOMikubGl2ZWFnZW50LmdhdGV3YXkudjIuVHVubmVsV3NNZXNzYWdlVHlwZRIWCg53c19zdWJwcm90b2NvbBgLIAEoCRIVCg13c19jbG9zZV9jb2RlGAwgASgNEhcKD3dzX2Nsb3NlX3JlYXNvbhgNIAEoCSIpChVXb3Jrc3BhY2VXYXRjaFJlcXVlc3QSEAoId29ya2RpcnMYASADKAkifgoWV29ya3NwYWNlQWN0aXZpdHlFdmVudBIPCgd3b3JrZGlyGAEgASgJEhAKCHJldmlzaW9uGAIgASgEEgoKAmZzGAMgASgIEgsKA2dpdBgEIAEoCBIVCg1jaGFuZ2VkX3BhdGhzGAUgAygJEhEKCXRydW5jYXRlZBgGIAEoCCKWAgoUTWFuYWdlZFByb2Nlc3NSZWNvcmQSCgoCaWQYASABKAkSDQoFbGFiZWwYAiABKAkSDwoHY29tbWFuZBgDIAEoCRILCgNjd2QYBCABKAkSDQoFc2hlbGwYBSABKAkSCwoDcGlkGAYgASgNEhAKCGxvZ19wYXRoGAcgASgJEhIKCnN0YXJ0ZWRfYXQYCCABKAMSGAoLZmluaXNoZWRfYXQYCSABKANIAIgBARIWCglleGl0X2NvZGUYCiABKAVIAYgBARIPCgdydW5uaW5nGAsgASgIEhAKCGlzb2xhdGVkGAwgASgIEhAKCHJlc3RvcmVkGA0gASgIQg4KDF9maW5pc2hlZF9hdEIMCgpfZXhpdF9jb2RlImkKFk1hbmFnZWRQcm9jZXNzU25hcHNob3QSPQoJcHJvY2Vzc2VzGAEgAygLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuTWFuYWdlZFByb2Nlc3NSZWNvcmQSEAoIcmV2aXNpb24YAiABKAQiTgoVTWFuYWdlZFByb2Nlc3NSZXF1ZXN0Eg4KBmFjdGlvbhgBIAEoCRISCgpwcm9jZXNzX2lkGAIgASgJEhEKCW1heF9ieXRlcxgDIAEoDSK3AQoWTWFuYWdlZFByb2Nlc3NSZXNwb25zZRIOCgZhY3Rpb24YASABKAkSPgoIc25hcHNob3QYAiABKAsyLC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5NYW5hZ2VkUHJvY2Vzc1NuYXBzaG90EhMKC2xvZ19jb250ZW50GAMgASgJEhAKCGxvZ19wYXRoGAQgASgJEhUKDWxvZ190cnVuY2F0ZWQYBSABKAgSDwoHc3RvcHBlZBgGIAEoCCI5ChNNZW1vcnlNYW5hZ2VSZXF1ZXN0Eg8KB2NvbW1hbmQYASABKAkSEQoJYXJnc19qc29uGAIgASgJIisKFE1lbW9yeU1hbmFnZVJlc3BvbnNlEhMKC3Jlc3VsdF9qc29uGAEgASgJIpgDCg9UZXJtaW5hbFJlcXVlc3QSDgoGYWN0aW9uGAEgASgJEhIKCnNlc3Npb25faWQYAiABKAkSGAoQcHJvamVjdF9wYXRoX2tleRgDIAEoCRILCgNjd2QYBCABKAkSDQoFc2hlbGwYBSABKAkSDQoFdGl0bGUYBiABKAkSDAoEZGF0YRgHIAEoCRIMCgRjb2xzGAggASgNEgwKBHJvd3MYCSABKA0SEQoJbWF4X2J5dGVzGAogASgNEhMKC3NzaF9ob3N0X2lkGAsgASgJEhEKCXByb21wdF9pZBgMIAEoCRIVCg1wcm9tcHRfYW5zd2VyGA0gASgJEhYKDnRydXN0X2hvc3Rfa2V5GA4gASgIEhQKDHNmdHBfZW5hYmxlZBgPIAEoCBIOCgZ0YWJfaWQYECABKAkSEAoIdGFiX2tpbmQYESABKAkSEwoLcmVtb3RlX2hvc3QYEiABKAkSEwoLcmVtb3RlX3BvcnQYEyABKA0SEgoKbG9jYWxfcG9ydBgUIAEoDRISCgpmb3J3YXJkX2lkGBUgASgJIrICCg9UZXJtaW5hbFNlc3Npb24SCgoCaWQYASABKAkSGAoQcHJvamVjdF9wYXRoX2tleRgCIAEoCRILCgNjd2QYAyABKAkSDQoFc2hlbGwYBCABKAkSDQoFdGl0bGUYBSABKAkSCwoDcGlkGAYgASgNEgwKBGNvbHMYByABKA0SDAoEcm93cxgIIAEoDRISCgpjcmVhdGVkX2F0GAkgASgEEhIKCnVwZGF0ZWRfYXQYCiABKAQSEwoLZmluaXNoZWRfYXQYCyABKAQSEQoJZXhpdF9jb2RlGAwgASgFEg8KB3J1bm5pbmcYDSABKAgSDAoEa2luZBgOIAEoCRI2CgNzc2gYDyABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbFNzaE1ldGFkYXRhItsBChNUZXJtaW5hbFNzaE1ldGFkYXRhEg8KB2hvc3RfaWQYASABKAkSEQoJaG9zdF9uYW1lGAIgASgJEhAKCHVzZXJuYW1lGAMgASgJEgwKBGhvc3QYBCABKAkSDAoEcG9ydBgFIAEoDRIRCglhdXRoX3R5cGUYBiABKAkSDgoGc3RhdHVzGAcgASgJEhkKEXJlY29ubmVjdF9hdHRlbXB0GAggASgNEh4KFnJlY29ubmVjdF9tYXhfYXR0ZW1wdHMYCSABKA0SFAoMc2Z0cF9lbmFibGVkGAogASgIIvcBCgtTZnRwUmVxdWVzdBIOCgZhY3Rpb24YASABKAkSEgoKc2Vzc2lvbl9pZBgCIAEoCRIYChBwcm9qZWN0X3BhdGhfa2V5GAMgASgJEg8KB3dvcmtkaXIYBCABKAkSEgoKbG9jYWxfcGF0aBgFIAEoCRITCgtyZW1vdGVfcGF0aBgGIAEoCRIRCglmcm9tX3BhdGgYByABKAkSDwoHdG9fcGF0aBgIIAEoCRIRCglkaXJlY3Rpb24YCSABKAkSEwoLdGFyZ2V0X3BhdGgYCiABKAkSEQoJcmVjdXJzaXZlGAsgASgIEhEKCW92ZXJ3cml0ZRgMIAEoCCJYCglTZnRwRW50cnkSDAoEcGF0aBgBIAEoCRIMCgRuYW1lGAIgASgJEgwKBGtpbmQYAyABKAkSEgoKc2l6ZV9ieXRlcxgEIAEoBBINCgVtdGltZRgFIAEoBCLyAQoMU2Z0cFRyYW5zZmVyEgoKAmlkGAEgASgJEhIKCnNlc3Npb25faWQYAiABKAkSEQoJZGlyZWN0aW9uGAMgASgJEg4KBnN0YXR1cxgEIAEoCRITCgtzb3VyY2VfcGF0aBgFIAEoCRITCgt0YXJnZXRfcGF0aBgGIAEoCRIUCgxjdXJyZW50X3BhdGgYByABKAkSEgoKYnl0ZXNfZG9uZRgIIAEoBBITCgtieXRlc190b3RhbBgJIAEoBBISCgpmaWxlc19kb25lGAogASgNEhMKC2ZpbGVzX3RvdGFsGAsgASgNEg0KBWVycm9yGAwgASgJItQBCgxTZnRwUmVzcG9uc2USDgoGYWN0aW9uGAEgASgJEgwKBHBhdGgYAiABKAkSMAoHZW50cmllcxgDIAMoCzIfLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNmdHBFbnRyeRIuCgVlbnRyeRgEIAEoCzIfLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNmdHBFbnRyeRIOCgZleGlzdHMYBSABKAgSNAoIdHJhbnNmZXIYBiABKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZnRwVHJhbnNmZXIiTwoJU2Z0cEV2ZW50EgwKBGtpbmQYASABKAkSNAoIdHJhbnNmZXIYAiABKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZnRwVHJhbnNmZXIiwQEKEVRlcm1pbmFsU3NoUHJvbXB0EgoKAmlkGAEgASgJEgwKBGtpbmQYAiABKAkSDwoHaG9zdF9pZBgDIAEoCRIRCglob3N0X25hbWUYBCABKAkSDAoEaG9zdBgFIAEoCRIMCgRwb3J0GAYgASgNEg8KB21lc3NhZ2UYByABKAkSGgoSZmluZ2VycHJpbnRfc2hhMjU2GAggASgJEhAKCGtleV90eXBlGAkgASgJEhMKC2Fuc3dlcl9lY2hvGAogASgIIkEKE1Rlcm1pbmFsU2hlbGxPcHRpb24SCgoCaWQYASABKAkSDQoFbGFiZWwYAiABKAkSDwoHY29tbWFuZBgDIAEoCSKAAQoOVGVybWluYWxTc2hUYWISCgoCaWQYASABKAkSEgoKc2Vzc2lvbl9pZBgCIAEoCRIYChBwcm9qZWN0X3BhdGhfa2V5GAMgASgJEgwKBGtpbmQYBCABKAkSEgoKY3JlYXRlZF9hdBgFIAEoBBISCgp1cGRhdGVkX2F0GAYgASgEIn8KF1Rlcm1pbmFsU3NoVGFic1NuYXBzaG90EhgKEHByb2plY3RfcGF0aF9rZXkYASABKAkSMgoEdGFicxgCIAMoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU3NoVGFiEhAKCHJldmlzaW9uGAQgASgESgQIAxAEIv0BChdUZXJtaW5hbFNzaExvY2FsRm9yd2FyZBIKCgJpZBgBIAEoCRISCgpzZXNzaW9uX2lkGAIgASgJEhgKEHByb2plY3RfcGF0aF9rZXkYAyABKAkSEgoKbG9jYWxfaG9zdBgEIAEoCRISCgpsb2NhbF9wb3J0GAUgASgNEg8KB2FkZHJlc3MYBiABKAkSEwoLcmVtb3RlX2hvc3QYByABKAkSEwoLcmVtb3RlX3BvcnQYCCABKA0SDgoGc3RhdHVzGAkgASgJEhIKCmNyZWF0ZWRfYXQYCiABKAQSEgoKdXBkYXRlZF9hdBgLIAEoBBINCgVlcnJvchgMIAEoCSJ1CiBUZXJtaW5hbFNzaExvY2FsRm9yd2FyZHNTbmFwc2hvdBI/Cghmb3J3YXJkcxgBIAMoCzItLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU3NoTG9jYWxGb3J3YXJkEhAKCHJldmlzaW9uGAIgASgEIn8KHVRlcm1pbmFsU3NoTG9jYWxGb3J3YXJkQWN0aW9uEj4KB2ZvcndhcmQYASABKAsyLS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbFNzaExvY2FsRm9yd2FyZBIQCghyZXZpc2lvbhgCIAEoBBIMCgRraW5kGAMgASgJIqcFChBUZXJtaW5hbFJlc3BvbnNlEg4KBmFjdGlvbhgBIAEoCRI3CghzZXNzaW9ucxgCIAMoCzIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU2Vzc2lvbhI2CgdzZXNzaW9uGAMgASgLMiUubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTZXNzaW9uEg4KBm91dHB1dBgEIAEoDBIRCgl0cnVuY2F0ZWQYBSABKAgSQAoNc2hlbGxfb3B0aW9ucxgGIAMoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU2hlbGxPcHRpb24SFQoNZGVmYXVsdF9zaGVsbBgHIAEoCRIbChNvdXRwdXRfc3RhcnRfb2Zmc2V0GAggASgEEhkKEW91dHB1dF9lbmRfb2Zmc2V0GAkgASgEEjsKCnNzaF9wcm9tcHQYCiABKAsyJy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbFNzaFByb21wdBISCgpsYXRlbmN5X21zGAsgASgNEj8KCHNzaF90YWJzGAwgASgLMi0ubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTc2hUYWJzU25hcHNob3QSUgoSc3NoX2xvY2FsX2ZvcndhcmRzGA0gASgLMjYubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTc2hMb2NhbEZvcndhcmRzU25hcHNob3QSTgoRc3NoX2xvY2FsX2ZvcndhcmQYDiABKAsyMy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbFNzaExvY2FsRm9yd2FyZEFjdGlvbhIoCiBzc2hfbG9jYWxfZm9yd2FyZF9wb3J0X2F2YWlsYWJsZRgPIAEoCCLaAgoNVGVybWluYWxFdmVudBIMCgRraW5kGAEgASgJEhIKCnNlc3Npb25faWQYAiABKAkSGAoQcHJvamVjdF9wYXRoX2tleRgDIAEoCRI2CgdzZXNzaW9uGAQgASgLMiUubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTZXNzaW9uEgwKBGRhdGEYBSABKAwSGwoTb3V0cHV0X3N0YXJ0X29mZnNldBgGIAEoBBIZChFvdXRwdXRfZW5kX29mZnNldBgHIAEoBBI/Cghzc2hfdGFicxgIIAEoCzItLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU3NoVGFic1NuYXBzaG90Ek4KEXNzaF9sb2NhbF9mb3J3YXJkGAkgASgLMjMubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTc2hMb2NhbEZvcndhcmRBY3Rpb24isgIKE1Rlcm1pbmFsU3RyZWFtRnJhbWUSDAoEa2luZBgBIAEoCRIRCglzdHJlYW1faWQYAiABKAkSEgoKc2Vzc2lvbl9pZBgDIAEoCRIYChBwcm9qZWN0X3BhdGhfa2V5GAQgASgJEgsKA3NlcRgFIAEoBBIUCgxzdGFydF9vZmZzZXQYBiABKAQSEgoKZW5kX29mZnNldBgHIAEoBBIMCgRjb2xzGAggASgNEgwKBHJvd3MYCSABKA0SEQoJbWF4X2J5dGVzGAogASgNEhEKCXRydW5jYXRlZBgLIAEoCBINCgVlcnJvchgMIAEoCRI2CgdzZXNzaW9uGA0gASgLMiUubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTZXNzaW9uEgwKBGRhdGEYDiABKAwiQAoKR2l0UmVxdWVzdBIOCgZhY3Rpb24YASABKAkSDwoHd29ya2RpchgCIAEoCRIRCglhcmdzX2pzb24YAyABKAkiMgoLR2l0UmVzcG9uc2USDgoGYWN0aW9uGAEgASgJEhMKC3Jlc3VsdF9qc29uGAIgASgJIvQCCgtDaGF0UmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSDwoHbWVzc2FnZRgCIAEoCRI/Cg5zZWxlY3RlZF9tb2RlbBgDIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRTZWxlY3RlZE1vZGVsEhYKDmV4ZWN1dGlvbl9tb2RlGAQgASgJEg8KB3dvcmtkaXIYBSABKAkSPgoOdXBsb2FkZWRfZmlsZXMYByADKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0VXBsb2FkZWRGaWxlEhkKEWNsaWVudF9yZXF1ZXN0X2lkGAggASgJEkMKEHJ1bnRpbWVfY29udHJvbHMYCSABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0UnVudGltZUNvbnRyb2xzEhQKDHF1ZXVlX3BvbGljeRgKIAEoCUoECAYQB1IVc2VsZWN0ZWRfc3lzdGVtX3Rvb2xzIooBCg5DaGF0TWVzc2FnZVJlZhIVCg1zZWdtZW50X2luZGV4GAEgASgFEhUKDW1lc3NhZ2VfaW5kZXgYAiABKAUSEgoKc2VnbWVudF9pZBgDIAEoCRISCgptZXNzYWdlX2lkGAQgASgJEgwKBHJvbGUYBSABKAkSFAoMY29udGVudF9oYXNoGAYgASgJIjwKEUNhbmNlbENoYXRSZXF1ZXN0EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRIOCgZydW5faWQYAiABKAkizwEKEkNoYXRDb21tYW5kUmVxdWVzdBIMCgR0eXBlGAEgASgJEjIKB3JlcXVlc3QYAiABKAsyIS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0UmVxdWVzdBI+ChBiYXNlX21lc3NhZ2VfcmVmGAMgASgLMiQubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdE1lc3NhZ2VSZWYSNwoGY2FuY2VsGAQgASgLMicubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2FuY2VsQ2hhdFJlcXVlc3QiuAEKEENoYXRRdWV1ZVJlcXVlc3QSDgoGYWN0aW9uGAEgASgJEhcKD2NvbnZlcnNhdGlvbl9pZBgCIAEoCRIPCgdpdGVtX2lkGAMgASgJEhEKCWRpcmVjdGlvbhgEIAEoCRIQCghyZXZpc2lvbhgFIAEoBBISCgpkcmFmdF9qc29uGAYgASgJEhsKE3VwbG9hZGVkX2ZpbGVzX2pzb24YByABKAkSFAoMcmVxdWVzdF9qc29uGAggASgJIoYBChFDaGF0UXVldWVSZXNwb25zZRIQCghhY2NlcHRlZBgBIAEoCBIPCgdtZXNzYWdlGAIgASgJEhUKDXNuYXBzaG90X2pzb24YAyABKAkSEQoJaXRlbV9qc29uGAQgASgJEhIKCmVycm9yX2NvZGUYBSABKAkSEAoIcmV2aXNpb24YBiABKAQiUgoOQ2hhdFF1ZXVlRXZlbnQSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEhUKDXNuYXBzaG90X2pzb24YAiABKAkSEAoIcmV2aXNpb24YAyABKAQihQIKCUNoYXRFdmVudBI7CgR0eXBlGAEgASgOMi0ubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdEV2ZW50LkNoYXRFdmVudFR5cGUSFwoPY29udmVyc2F0aW9uX2lkGAIgASgJEgwKBGRhdGEYAyABKAkikwEKDUNoYXRFdmVudFR5cGUSCQoFVE9LRU4QABIMCghUSElOS0lORxABEg0KCVRPT0xfQ0FMTBACEg8KC1RPT0xfUkVTVUxUEAMSCAoERE9ORRAEEgkKBUVSUk9SEAUSDwoLVE9PTF9TVEFUVVMQBhIRCg1IT1NURURfU0VBUkNIEAcSEAoMVVNFUl9NRVNTQUdFEAgivAEKEENoYXRDb250cm9sRXZlbnQSEgoKcmVxdWVzdF9pZBgBIAEoCRIZChFjbGllbnRfcmVxdWVzdF9pZBgCIAEoCRIXCg9jb252ZXJzYXRpb25faWQYAyABKAkSEQoJcnVuX2Vwb2NoGAQgASgDEgwKBHR5cGUYBSABKAkSDQoFc3RhdGUYBiABKAkSEgoKZXJyb3JfY29kZRgHIAEoCRIPCgdtZXNzYWdlGAggASgJEgsKA3NlcRgJIAEoAyL8AQoTQ2hhdFJ1bnRpbWVTbmFwc2hvdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSDgoGcnVuX2lkGAIgASgJEhkKEWNsaWVudF9yZXF1ZXN0X2lkGAMgASgJEhEKCXdvcmtlcl9pZBgEIAEoCRINCgVzdGF0ZRgFIAEoCRILCgNjd2QYBiABKAkSEgoKdXBkYXRlZF9hdBgHIAEoAxIQCghyZXZpc2lvbhgIIAEoAxIUCgxlbnRyaWVzX2pzb24YCSABKAkSEwoLdG9vbF9zdGF0dXMYCiABKAkSIQoZdG9vbF9zdGF0dXNfaXNfY29tcGFjdGlvbhgLIAEoCCLqAQoSUnVudGltZVN0YXR1c0V2ZW50EhEKCXdvcmtlcl9pZBgBIAEoCRINCgVzdGF0ZRgCIAEoCRIPCgd2aXNpYmxlGAMgASgIEhgKEGFjdGl2ZV9ydW5fY291bnQYBCABKA0SEQoJdGltZXN0YW1wGAUgASgDEjgKC2FjdGl2ZV9ydW5zGAYgAygLMiMubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdFJ1blJlcG9ydBI6Cg1maW5pc2hlZF9ydW5zGAcgAygLMiMubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdFJ1blJlcG9ydCKAAQoNQ2hhdFJ1blJlcG9ydBIOCgZydW5faWQYASABKAkSFwoPY29udmVyc2F0aW9uX2lkGAIgASgJEg0KBXN0YXRlGAMgASgJEhIKCmVycm9yX2NvZGUYBCABKAkSDwoHbWVzc2FnZRgFIAEoCRISCgp1cGRhdGVkX2F0GAYgASgDIkcKEUNyb25NYW5hZ2VSZXF1ZXN0Eg4KBmFjdGlvbhgBIAEoCRIPCgd0YXNrX2lkGAIgASgJEhEKCXRhc2tfanNvbhgDIAEoCSI5ChJDcm9uTWFuYWdlUmVzcG9uc2USDgoGYWN0aW9uGAEgASgJEhMKC3Jlc3VsdF9qc29uGAIgASgJIlUKEkhpc3RvcnlMaXN0UmVxdWVzdBIMCgRwYWdlGAEgASgFEhEKCXBhZ2Vfc2l6ZRgCIAEoBRILCgNjd2QYAyABKAkSEQoJY3dkX2VtcHR5GAQgASgIImwKE0hpc3RvcnlMaXN0UmVzcG9uc2USQAoNY29udmVyc2F0aW9ucxgBIAMoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNvbnZlcnNhdGlvblN1bW1hcnkSEwoLdG90YWxfY291bnQYAiABKAUiigIKE0NvbnZlcnNhdGlvblN1bW1hcnkSCgoCaWQYASABKAkSDQoFdGl0bGUYAiABKAkSEgoKY3JlYXRlZF9hdBgDIAEoAxISCgp1cGRhdGVkX2F0GAQgASgDEhUKDW1lc3NhZ2VfY291bnQYBSABKAUSEwoLcHJvdmlkZXJfaWQYBiABKAkSDQoFbW9kZWwYByABKAkSEgoKc2Vzc2lvbl9pZBgIIAEoCRILCgNjd2QYCSABKAkSEQoJaXNfcGlubmVkGAogASgIEhEKCXBpbm5lZF9hdBgLIAEoAxIRCglpc19zaGFyZWQYDCABKAgSGwoTc2VsZWN0ZWRfbW9kZWxfanNvbhgNIAEoCSJCChFIaXN0b3J5R2V0UmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSFAoMbWF4X21lc3NhZ2VzGAIgASgFItQBChJIaXN0b3J5R2V0UmVzcG9uc2USFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEhUKDW1lc3NhZ2VzX2pzb24YAiABKAkSGwoTdG90YWxfbWVzc2FnZV9jb3VudBgDIAEoBRIeChZyZXR1cm5lZF9tZXNzYWdlX2NvdW50GAQgASgFEhAKCGhhc19tb3JlGAUgASgIEj8KDGNvbnZlcnNhdGlvbhgGIAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNvbnZlcnNhdGlvblN1bW1hcnkihQEKFEhpc3RvcnlQcmVmaXhSZXF1ZXN0EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRIUCgxtYXhfbWVzc2FnZXMYAiABKAUSPgoQYmFzZV9tZXNzYWdlX3JlZhgDIAEoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRNZXNzYWdlUmVmItcBChVIaXN0b3J5UHJlZml4UmVzcG9uc2USFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEhUKDW1lc3NhZ2VzX2pzb24YAiABKAkSGwoTdG90YWxfbWVzc2FnZV9jb3VudBgDIAEoBRIeChZyZXR1cm5lZF9tZXNzYWdlX2NvdW50GAQgASgFEhAKCGhhc19tb3JlGAUgASgIEj8KDGNvbnZlcnNhdGlvbhgGIAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNvbnZlcnNhdGlvblN1bW1hcnkiPgoUSGlzdG9yeVJlbmFtZVJlcXVlc3QSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEg0KBXRpdGxlGAIgASgJIlgKFUhpc3RvcnlSZW5hbWVSZXNwb25zZRI/Cgxjb252ZXJzYXRpb24YASABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Db252ZXJzYXRpb25TdW1tYXJ5Im8KFEhpc3RvcnlCcmFuY2hSZXF1ZXN0EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRI+ChBiYXNlX21lc3NhZ2VfcmVmGAIgASgLMiQubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdE1lc3NhZ2VSZWYiWAoVSGlzdG9yeUJyYW5jaFJlc3BvbnNlEj8KDGNvbnZlcnNhdGlvbhgBIAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNvbnZlcnNhdGlvblN1bW1hcnkiPwoRSGlzdG9yeVBpblJlcXVlc3QSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEhEKCWlzX3Bpbm5lZBgCIAEoCCJVChJIaXN0b3J5UGluUmVzcG9uc2USPwoMY29udmVyc2F0aW9uGAEgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ29udmVyc2F0aW9uU3VtbWFyeSKSAQoSSGlzdG9yeVNoYXJlU3RhdHVzEhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRIPCgdlbmFibGVkGAIgASgIEg0KBXRva2VuGAMgASgJEhIKCmNyZWF0ZWRfYXQYBCABKAMSEgoKdXBkYXRlZF9hdBgFIAEoAxIbChNyZWRhY3RfdG9vbF9jb250ZW50GAYgASgIIjEKFkhpc3RvcnlTaGFyZUdldFJlcXVlc3QSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJIlIKF0hpc3RvcnlTaGFyZUdldFJlc3BvbnNlEjcKBXNoYXJlGAEgASgLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVNoYXJlU3RhdHVzInwKFkhpc3RvcnlTaGFyZVNldFJlcXVlc3QSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEg8KB2VuYWJsZWQYAiABKAgSIAoTcmVkYWN0X3Rvb2xfY29udGVudBgDIAEoCEgAiAEBQhYKFF9yZWRhY3RfdG9vbF9jb250ZW50IlIKF0hpc3RvcnlTaGFyZVNldFJlc3BvbnNlEjcKBXNoYXJlGAEgASgLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVNoYXJlU3RhdHVzIisKGkhpc3RvcnlTaGFyZVJlc29sdmVSZXF1ZXN0Eg0KBXRva2VuGAEgASgJIsgBChtIaXN0b3J5U2hhcmVSZXNvbHZlUmVzcG9uc2USFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEhUKDW1lc3NhZ2VzX2pzb24YAiABKAkSGwoTdG90YWxfbWVzc2FnZV9jb3VudBgDIAEoBRI/Cgxjb252ZXJzYXRpb24YBCABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Db252ZXJzYXRpb25TdW1tYXJ5EhsKE3JlZGFjdF90b29sX2NvbnRlbnQYBSABKAgiGAoWSGlzdG9yeVdvcmtkaXJzUmVxdWVzdCJVChVIaXN0b3J5V29ya2RpclN1bW1hcnkSDAoEcGF0aBgBIAEoCRIaChJjb252ZXJzYXRpb25fY291bnQYAiABKAUSEgoKdXBkYXRlZF9hdBgDIAEoAyJYChdIaXN0b3J5V29ya2RpcnNSZXNwb25zZRI9Cgh3b3JrZGlycxgBIAMoCzIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlXb3JrZGlyU3VtbWFyeSIvChRIaXN0b3J5RGVsZXRlUmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkiFwoVSGlzdG9yeURlbGV0ZVJlc3BvbnNlInoKEEhpc3RvcnlTeW5jRXZlbnQSDAoEa2luZBgBIAEoCRI/Cgxjb252ZXJzYXRpb24YAiABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Db252ZXJzYXRpb25TdW1tYXJ5EhcKD2NvbnZlcnNhdGlvbl9pZBgDIAEoCSIVChNQcm92aWRlckxpc3RSZXF1ZXN0Ii4KFFByb3ZpZGVyTGlzdFJlc3BvbnNlEhYKDnByb3ZpZGVyc19qc29uGAEgASgJIhQKElNldHRpbmdzR2V0UmVxdWVzdCIsChNTZXR0aW5nc0dldFJlc3BvbnNlEhUKDXNldHRpbmdzX2pzb24YASABKAkiLgoVU2V0dGluZ3NVcGRhdGVSZXF1ZXN0EhUKDXNldHRpbmdzX2pzb24YASABKAkiOwoWU2V0dGluZ3NVcGRhdGVSZXNwb25zZRIQCghhY2NlcHRlZBgBIAEoCBIPCgdtZXNzYWdlGAIgASgJIj4KIFNldHRpbmdzUmVzZXRTc2hLbm93bkhvc3RSZXF1ZXN0EgwKBGhvc3QYASABKAkSDAoEcG9ydBgCIAEoDSI0CiFTZXR0aW5nc1Jlc2V0U3NoS25vd25Ib3N0UmVzcG9uc2USDwoHZGVsZXRlZBgBIAEoDSIqChFTZXR0aW5nc1N5bmNFdmVudBIVCg1zZXR0aW5nc19qc29uGAEgASgJIhcKFVNraWxsRmlsZXNMaXN0UmVxdWVzdCJMChZTa2lsbEZpbGVzTGlzdFJlc3BvbnNlEhAKCHJvb3RfZGlyGAEgASgJEg0KBXBhdGhzGAIgAygJEhEKCXRydW5jYXRlZBgDIAEoCCIoChhTa2lsbE1ldGFkYXRhUmVhZFJlcXVlc3QSDAoEcGF0aBgBIAEoCSI+ChlTa2lsbE1ldGFkYXRhUmVhZFJlc3BvbnNlEgwKBG5hbWUYASABKAkSEwoLZGVzY3JpcHRpb24YAiABKAkiRAoUU2tpbGxUZXh0UmVhZFJlcXVlc3QSDAoEcGF0aBgBIAEoCRIOCgZvZmZzZXQYAiABKA0SDgoGbGVuZ3RoGAMgASgNIjsKFVNraWxsVGV4dFJlYWRSZXNwb25zZRIPCgdjb250ZW50GAEgASgJEhEKCXRydW5jYXRlZBgCIAEoCCIqChJTa2lsbE1hbmFnZVJlcXVlc3QSFAoMcGF5bG9hZF9qc29uGAEgASgJIioKE1NraWxsTWFuYWdlUmVzcG9uc2USEwoLcmVzdWx0X2pzb24YASABKAkidwoWRmlsZU1lbnRpb25MaXN0UmVxdWVzdBIPCgd3b3JrZGlyGAEgASgJEhMKC21heF9yZXN1bHRzGAIgASgNEg0KBXF1ZXJ5GAMgASgJEhgKC3Nob3dfaGlkZGVuGAQgASgISACIAQFCDgoMX3Nob3dfaGlkZGVuIj4KEEZpbGVNZW50aW9uRW50cnkSDAoEcGF0aBgBIAEoCRIMCgRraW5kGAIgASgJEg4KBmhpZGRlbhgDIAEoCCJlChdGaWxlTWVudGlvbkxpc3RSZXNwb25zZRI3CgdlbnRyaWVzGAEgAygLMiYubGl2ZWFnZW50LmdhdGV3YXkudjIuRmlsZU1lbnRpb25FbnRyeRIRCgl0cnVuY2F0ZWQYAiABKAgiPwoGRnNSb290EgoKAmlkGAEgASgJEgwKBHBhdGgYAiABKAkSDAoEa2luZBgDIAEoCRINCgVsYWJlbBgEIAEoCSIQCg5Gc1Jvb3RzUmVxdWVzdCI+Cg9Gc1Jvb3RzUmVzcG9uc2USKwoFcm9vdHMYASADKAsyHC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc1Jvb3QiNgoRRnNMaXN0RGlyc1JlcXVlc3QSDAoEcGF0aBgBIAEoCRITCgttYXhfcmVzdWx0cxgCIAEoDSIoCgpGc0RpckVudHJ5EgwKBHBhdGgYASABKAkSDAoEbmFtZRgCIAEoCSJoChJGc0xpc3REaXJzUmVzcG9uc2USDAoEcGF0aBgBIAEoCRIxCgdlbnRyaWVzGAIgAygLMiAubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNEaXJFbnRyeRIRCgl0cnVuY2F0ZWQYAyABKAgiPAocRnNDcmVhdGVQcm9qZWN0Rm9sZGVyUmVxdWVzdBIOCgZwYXJlbnQYASABKAkSDAoEbmFtZRgCIAEoCSItCh1Gc0NyZWF0ZVByb2plY3RGb2xkZXJSZXNwb25zZRIMCgRwYXRoGAEgASgJIowBCg1Gc0xpc3RSZXF1ZXN0Eg8KB3dvcmtkaXIYASABKAkSDAoEcGF0aBgCIAEoCRINCgVkZXB0aBgDIAEoDRIOCgZvZmZzZXQYBCABKA0SEwoLbWF4X3Jlc3VsdHMYBSABKA0SGAoLc2hvd19oaWRkZW4YBiABKAhIAIgBAUIOCgxfc2hvd19oaWRkZW4iOQoLRnNMaXN0RW50cnkSDAoEcGF0aBgBIAEoCRIMCgRraW5kGAIgASgJEg4KBmhpZGRlbhgDIAEoCCK5AQoORnNMaXN0UmVzcG9uc2USDAoEcGF0aBgBIAEoCRIQCghoYXNfcGF0aBgCIAEoCBINCgVkZXB0aBgDIAEoDRIOCgZvZmZzZXQYBCABKA0SEwoLbWF4X3Jlc3VsdHMYBSABKA0SDQoFdG90YWwYBiABKA0SEAoIaGFzX21vcmUYByABKAgSMgoHZW50cmllcxgIIAMoCzIhLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzTGlzdEVudHJ5IjoKGUZzUmVhZEVkaXRhYmxlVGV4dFJlcXVlc3QSDwoHd29ya2RpchgBIAEoCRIMCgRwYXRoGAIgASgJIowBChpGc1JlYWRFZGl0YWJsZVRleHRSZXNwb25zZRIMCgRwYXRoGAEgASgJEg8KB2NvbnRlbnQYAiABKAkSEAoIbXRpbWVfbXMYAyABKAQSFAoMY29udGVudF9oYXNoGAQgASgJEhIKCnNpemVfYnl0ZXMYBSABKAQSEwoLdG90YWxfbGluZXMYBiABKAQiPAobRnNSZWFkV29ya3NwYWNlSW1hZ2VSZXF1ZXN0Eg8KB3dvcmtkaXIYASABKAkSDAoEcGF0aBgCIAEoCSKJAQocRnNSZWFkV29ya3NwYWNlSW1hZ2VSZXNwb25zZRIMCgRwYXRoGAEgASgJEhEKCW1pbWVfdHlwZRgCIAEoCRIMCgRkYXRhGAMgASgJEhIKCnNpemVfYnl0ZXMYBCABKAQSEAoIbXRpbWVfbXMYBSABKAQSFAoMY29udGVudF9oYXNoGAYgASgJItsBChNDaGF0RmlsZU9wZW5SZXF1ZXN0EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRIPCgd3b3JrZGlyGAIgASgJEgwKBHBhdGgYAyABKAkSDgoGc291cmNlGAQgASgJEhEKBGxpbmUYBSABKA1IAIgBARIVCghlbmRfbGluZRgGIAEoDUgBiAEBEhMKBmNvbHVtbhgHIAEoDUgCiAEBEhwKFG9wZW5faW5fZmlsZV9tYW5hZ2VyGAggASgIQgcKBV9saW5lQgsKCV9lbmRfbGluZUIJCgdfY29sdW1uIs4BChRDaGF0RmlsZU9wZW5SZXNwb25zZRIOCgZhY3Rpb24YASABKAkSDAoEa2luZBgCIAEoCRIPCgd3b3JrZGlyGAMgASgJEgwKBHBhdGgYBCABKAkSEQoEbGluZRgFIAEoDUgAiAEBEhUKCGVuZF9saW5lGAYgASgNSAGIAQESEwoGY29sdW1uGAcgASgNSAKIAQESGQoRb3V0c2lkZV93b3Jrc3BhY2UYCCABKAhCBwoFX2xpbmVCCwoJX2VuZF9saW5lQgkKB19jb2x1bW4izgEKEkZzV3JpdGVUZXh0UmVxdWVzdBIPCgd3b3JrZGlyGAEgASgJEgwKBHBhdGgYAiABKAkSDwoHY29udGVudBgDIAEoCRIMCgRtb2RlGAQgASgJEhkKEWV4cGVjdGVkX210aW1lX21zGAUgASgEEh0KFWV4cGVjdGVkX2NvbnRlbnRfaGFzaBgGIAEoCRIdChVoYXNfZXhwZWN0ZWRfbXRpbWVfbXMYByABKAgSIQoZaGFzX2V4cGVjdGVkX2NvbnRlbnRfaGFzaBgIIAEoCCKdAQoTRnNXcml0ZVRleHRSZXNwb25zZRIMCgRwYXRoGAEgASgJEgwKBG1vZGUYAiABKAkSFgoOZXhpc3RlZF9iZWZvcmUYAyABKAgSFQoNYnl0ZXNfd3JpdHRlbhgEIAEoBBIQCghtdGltZV9tcxgFIAEoBBIUCgxjb250ZW50X2hhc2gYBiABKAkSEwoLdG90YWxfbGluZXMYByABKAQiMwoSRnNDcmVhdGVEaXJSZXF1ZXN0Eg8KB3dvcmtkaXIYASABKAkSDAoEcGF0aBgCIAEoCSIxChNGc0NyZWF0ZURpclJlc3BvbnNlEgwKBHBhdGgYASABKAkSDAoEa2luZBgCIAEoCSJGCg9Gc1JlbmFtZVJlcXVlc3QSDwoHd29ya2RpchgBIAEoCRIRCglmcm9tX3BhdGgYAiABKAkSDwoHdG9fcGF0aBgDIAEoCSJBChBGc1JlbmFtZVJlc3BvbnNlEhEKCWZyb21fcGF0aBgBIAEoCRIMCgRwYXRoGAIgASgJEgwKBGtpbmQYAyABKAkiMAoPRnNEZWxldGVSZXF1ZXN0Eg8KB3dvcmtkaXIYASABKAkSDAoEcGF0aBgCIAEoCSIuChBGc0RlbGV0ZVJlc3BvbnNlEgwKBHBhdGgYASABKAkSDAoEa2luZBgCIAEoCSIgCgtQaW5nUmVxdWVzdBIRCgl0aW1lc3RhbXAYASABKAMiIQoMUG9uZ1Jlc3BvbnNlEhEKCXRpbWVzdGFtcBgBIAEoAyIuCg1FcnJvclJlc3BvbnNlEgwKBGNvZGUYASABKAUSDwoHbWVzc2FnZRgCIAEoCSJrChVQcm92aWRlck1vZGVsc1JlcXVlc3QSFQoNcHJvdmlkZXJfdHlwZRgBIAEoCRIQCghiYXNlX3VybBgCIAEoCRIPCgdhcGlfa2V5GAMgASgJEhgKEHVzZV9zeXN0ZW1fcHJveHkYBCABKAgiLQoWUHJvdmlkZXJNb2RlbHNSZXNwb25zZRITCgttb2RlbHNfanNvbhgBIAEoCSJRChRQcm92aWRlclVzYWdlUmVxdWVzdBITCgtwcm92aWRlcl9pZBgBIAEoCRIPCgdyZWZyZXNoGAIgASgIEhMKC2NvbmZpZ19qc29uGAMgASgJIiwKFVByb3ZpZGVyVXNhZ2VSZXNwb25zZRITCgtyZXN1bHRfanNvbhgBIAEoCSKIAQoQQ2hhdEluZ3Jlc3NCYXRjaBIOCgZydW5faWQYASABKAkSFwoPY29udmVyc2F0aW9uX2lkGAIgASgJEhEKCWZpcnN0X3NlcRgDIAEoBBI4CgdyZWNvcmRzGAQgAygLMicubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdEluZ3Jlc3NSZWNvcmQimgIKEUNoYXRJbmdyZXNzUmVjb3JkEjcKBWRlbHRhGAEgASgLMiYubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdEluZ3Jlc3NEZWx0YUgAEkEKCmNoZWNrcG9pbnQYAiABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0SW5ncmVzc0NoZWNrcG9pbnRIABI9Cgh0ZXJtaW5hbBgDIAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRJbmdyZXNzVGVybWluYWxIABI/CgloZWFydGJlYXQYBCABKAsyKi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0SW5ncmVzc0hlYXJ0YmVhdEgAQgkKB3BheWxvYWQiOQoQQ2hhdEluZ3Jlc3NEZWx0YRISCgpldmVudF9qc29uGAEgASgJEhEKCXdvcmtlcl9pZBgCIAEoCSIqChRDaGF0SW5ncmVzc0hlYXJ0YmVhdBISCgp1cGRhdGVkX2F0GAEgASgDIsQBChVDaGF0SW5ncmVzc0NoZWNrcG9pbnQSGgoSY292ZXJzX3Rocm91Z2hfc2VxGAEgASgEEhAKCHJldmlzaW9uGAIgASgEEh0KFWNvbXByZXNzZWRfcHJvamVjdGlvbhgDIAEoDBIaChJ1bmNvbXByZXNzZWRfYnl0ZXMYBCABKAQSDgoGc2hhMjU2GAUgASgJEhgKEGNvbnRlbnRfY29tcGxldGUYBiABKAgSGAoQaGlzdG9yeV9yZXF1aXJlZBgHIAEoCCL8AQoTQ2hhdEluZ3Jlc3NUZXJtaW5hbBIaChJjb3ZlcnNfdGhyb3VnaF9zZXEYASABKAQSEAoIcmV2aXNpb24YAiABKAQSHQoVY29tcHJlc3NlZF9wcm9qZWN0aW9uGAMgASgMEhoKEnVuY29tcHJlc3NlZF9ieXRlcxgEIAEoBBIOCgZzaGEyNTYYBSABKAkSGAoQY29udGVudF9jb21wbGV0ZRgGIAEoCBIYChBoaXN0b3J5X3JlcXVpcmVkGAcgASgIEg0KBXN0YXRlGAggASgJEhIKCmVycm9yX2NvZGUYCSABKAkSFQoNZXJyb3JfbWVzc2FnZRgKIAEoCSJNChFDaGF0SW5ncmVzc1Jlc3VtZRI4CgRydW5zGAEgAygLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdEluZ3Jlc3NSdW5SZXN1bWUi1QEKFENoYXRJbmdyZXNzUnVuUmVzdW1lEg4KBnJ1bl9pZBgBIAEoCRIXCg9jb252ZXJzYXRpb25faWQYAiABKAkSFwoPcmVwbGF5X2Zyb21fc2VxGAMgASgEEhoKEnJlcGxheV90aHJvdWdoX3NlcRgEIAEoBBIQCghuZXh0X3NlcRgFIAEoBBIdChVsYXRlc3RfY2hlY2twb2ludF9zZXEYBiABKAQSFAoMdGVybWluYWxfc2VxGAcgASgEEhgKEHRlcm1pbmFsX3BlbmRpbmcYCCABKAgizgEKE0NoYXRJbmdyZXNzRnJhZ21lbnQSDgoGcnVuX2lkGAEgASgJEhcKD2NvbnZlcnNhdGlvbl9pZBgCIAEoCRISCgpzb3VyY2Vfc2VxGAMgASgEEhYKDmZyYWdtZW50X2luZGV4GAQgASgNEhYKDmZyYWdtZW50X2NvdW50GAUgASgNEhwKFGVuY29kZWRfcmVjb3JkX2NodW5rGAYgASgMEhwKFGVuY29kZWRfcmVjb3JkX2J5dGVzGAcgASgEEg4KBnNoYTI1NhgIIAEoCSLcAgoOQ2hhdEluZ3Jlc3NBY2sSDgoGcnVuX2lkGAEgASgJEhcKD2NvbnZlcnNhdGlvbl9pZBgCIAEoCRIZChFjb21taXR0ZWRfdGhyb3VnaBgDIAEoBBIVCg1leHBlY3RlZF9uZXh0GAQgASgEEjsKBmFjdGlvbhgFIAEoDjIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRJbmdyZXNzQWNrLkFjdGlvbhIaChJ0ZXJtaW5hbF9jb21taXR0ZWQYBiABKAgSEgoKZXJyb3JfY29kZRgHIAEoCRIVCg1lcnJvcl9tZXNzYWdlGAggASgJImsKBkFjdGlvbhIWChJBQ1RJT05fVU5TUEVDSUZJRUQQABIMCghDT05USU5VRRABEhgKFFJFUExBWV9GUk9NX0VYUEVDVEVEEAISEwoPU0VORF9DSEVDS1BPSU5UEAMSDAoIUkVKRUNURUQQBCrGBAoPVHVubmVsRnJhbWVLaW5kEiEKHVRVTk5FTF9GUkFNRV9LSU5EX1VOU1BFQ0lGSUVEEAASKAokVFVOTkVMX0ZSQU1FX0tJTkRfSFRUUF9SRVFVRVNUX1NUQVJUEAESJwojVFVOTkVMX0ZSQU1FX0tJTkRfSFRUUF9SRVFVRVNUX0JPRFkQAhImCiJUVU5ORUxfRlJBTUVfS0lORF9IVFRQX1JFUVVFU1RfRU5EEAMSKQolVFVOTkVMX0ZSQU1FX0tJTkRfSFRUUF9SRVNQT05TRV9TVEFSVBAEEigKJFRVTk5FTF9GUkFNRV9LSU5EX0hUVFBfUkVTUE9OU0VfQk9EWRAFEicKI1RVTk5FTF9GUkFNRV9LSU5EX0hUVFBfUkVTUE9OU0VfRU5EEAYSHQoZVFVOTkVMX0ZSQU1FX0tJTkRfV1NfRElBTBAHEiAKHFRVTk5FTF9GUkFNRV9LSU5EX1dTX0RJQUxfT0sQCBIjCh9UVU5ORUxfRlJBTUVfS0lORF9XU19ESUFMX0VSUk9SEAkSHgoaVFVOTkVMX0ZSQU1FX0tJTkRfV1NfRlJBTUUQChIeChpUVU5ORUxfRlJBTUVfS0lORF9XU19DTE9TRRALEhsKF1RVTk5FTF9GUkFNRV9LSU5EX0VSUk9SEAwSHAoYVFVOTkVMX0ZSQU1FX0tJTkRfQ0FOQ0VMEA0SGgoWVFVOTkVMX0ZSQU1FX0tJTkRfUElORxAOEhoKFlRVTk5FTF9GUkFNRV9LSU5EX1BPTkcQDyqBAQoTVHVubmVsV3NNZXNzYWdlVHlwZRImCiJUVU5ORUxfV1NfTUVTU0FHRV9UWVBFX1VOU1BFQ0lGSUVEEAASHwobVFVOTkVMX1dTX01FU1NBR0VfVFlQRV9URVhUEAESIQodVFVOTkVMX1dTX01FU1NBR0VfVFlQRV9CSU5BUlkQAkJAWj5naXRodWIuY29tL2xpdmVhZ2VudC9hZ2VudC1nYXRld2F5L2ludGVybmFsL3Byb3RvL3YyO2dhdGV3YXl2MmIGcHJvdG8z"); + fileDesc("ChZwcm90by92Mi9nYXRld2F5LnByb3RvEhRsaXZlYWdlbnQuZ2F0ZXdheS52MiL9GwoPR2F0ZXdheUVudmVsb3BlEhIKCnJlcXVlc3RfaWQYASABKAkSEQoJdGltZXN0YW1wGAIgASgDEkAKDGNoYXRfY29tbWFuZBgKIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRDb21tYW5kUmVxdWVzdEgAEj4KC2Nyb25fbWFuYWdlGBQgASgLMicubGl2ZWFnZW50LmdhdGV3YXkudjIuQ3Jvbk1hbmFnZVJlcXVlc3RIABJACgxoaXN0b3J5X2xpc3QYHiABKAsyKC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5TGlzdFJlcXVlc3RIABI+CgtoaXN0b3J5X2dldBgfIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlHZXRSZXF1ZXN0SAASRAoOaGlzdG9yeV9yZW5hbWUYICABKAsyKi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5UmVuYW1lUmVxdWVzdEgAEkQKDmhpc3RvcnlfZGVsZXRlGCEgASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeURlbGV0ZVJlcXVlc3RIABJECg5oaXN0b3J5X3ByZWZpeBgiIAEoCzIqLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlQcmVmaXhSZXF1ZXN0SAASPgoLaGlzdG9yeV9waW4YIyABKAsyJy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5UGluUmVxdWVzdEgAEkkKEWhpc3Rvcnlfc2hhcmVfZ2V0GCQgASgLMiwubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVNoYXJlR2V0UmVxdWVzdEgAEkkKEWhpc3Rvcnlfc2hhcmVfc2V0GCUgASgLMiwubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVNoYXJlU2V0UmVxdWVzdEgAElEKFWhpc3Rvcnlfc2hhcmVfcmVzb2x2ZRgmIAEoCzIwLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlTaGFyZVJlc29sdmVSZXF1ZXN0SAASSAoQaGlzdG9yeV93b3JrZGlycxgnIAEoCzIsLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlXb3JrZGlyc1JlcXVlc3RIABJCCg1wcm92aWRlcl9saXN0GCggASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuUHJvdmlkZXJMaXN0UmVxdWVzdEgAEkAKDHNldHRpbmdzX2dldBgpIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNldHRpbmdzR2V0UmVxdWVzdEgAEkYKD3NldHRpbmdzX3VwZGF0ZRgqIAEoCzIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNldHRpbmdzVXBkYXRlUmVxdWVzdEgAEkcKEHNraWxsX2ZpbGVzX2xpc3QYKyABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Ta2lsbEZpbGVzTGlzdFJlcXVlc3RIABJNChNza2lsbF9tZXRhZGF0YV9yZWFkGCwgASgLMi4ubGl2ZWFnZW50LmdhdGV3YXkudjIuU2tpbGxNZXRhZGF0YVJlYWRSZXF1ZXN0SAASRQoPc2tpbGxfdGV4dF9yZWFkGC0gASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuU2tpbGxUZXh0UmVhZFJlcXVlc3RIABJJChFmaWxlX21lbnRpb25fbGlzdBguIAEoCzIsLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZpbGVNZW50aW9uTGlzdFJlcXVlc3RIABJRChV1cGxvYWRfcmVhZGFibGVfZmlsZXMYLyABKAsyMC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5VcGxvYWRSZWFkYWJsZUZpbGVzUmVxdWVzdEgAEjgKCGZzX3Jvb3RzGDAgASgLMiQubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNSb290c1JlcXVlc3RIABI/Cgxmc19saXN0X2RpcnMYMSABKAsyJy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc0xpc3REaXJzUmVxdWVzdEgAEjEKBHBpbmcYMiABKAsyIS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5QaW5nUmVxdWVzdEgAElMKFnVwbG9hZGVkX2ltYWdlX3ByZXZpZXcYMyABKAsyMS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5VcGxvYWRlZEltYWdlUHJldmlld1JlcXVlc3RIABJCCg1tZW1vcnlfbWFuYWdlGDQgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuTWVtb3J5TWFuYWdlUmVxdWVzdEgAEkAKDHNraWxsX21hbmFnZRg1IAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNraWxsTWFuYWdlUmVxdWVzdEgAElYKGGZzX2NyZWF0ZV9wcm9qZWN0X2ZvbGRlchg2IAEoCzIyLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzQ3JlYXRlUHJvamVjdEZvbGRlclJlcXVlc3RIABJBChB0ZXJtaW5hbF9yZXF1ZXN0GDcgASgLMiUubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxSZXF1ZXN0SAASNgoHZnNfbGlzdBg4IAEoCzIjLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzTGlzdFJlcXVlc3RIABJBCg1mc193cml0ZV90ZXh0GDkgASgLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNXcml0ZVRleHRSZXF1ZXN0SAASQQoNZnNfY3JlYXRlX2Rpchg6IAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzQ3JlYXRlRGlyUmVxdWVzdEgAEjoKCWZzX3JlbmFtZRg7IAEoCzIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUmVuYW1lUmVxdWVzdEgAEjoKCWZzX2RlbGV0ZRg8IAEoCzIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzRGVsZXRlUmVxdWVzdEgAEjcKC2dpdF9yZXF1ZXN0GD0gASgLMiAubGl2ZWFnZW50LmdhdGV3YXkudjIuR2l0UmVxdWVzdEgAElAKFWZzX3JlYWRfZWRpdGFibGVfdGV4dBg+IAEoCzIvLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUmVhZEVkaXRhYmxlVGV4dFJlcXVlc3RIABJUChdmc19yZWFkX3dvcmtzcGFjZV9pbWFnZRg/IAEoCzIxLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUmVhZFdvcmtzcGFjZUltYWdlUmVxdWVzdEgAEjkKDHNmdHBfcmVxdWVzdBhAIAEoCzIhLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNmdHBSZXF1ZXN0SAASRgoPcHJvdmlkZXJfbW9kZWxzGEEgASgLMisubGl2ZWFnZW50LmdhdGV3YXkudjIuUHJvdmlkZXJNb2RlbHNSZXF1ZXN0SAASXwodc2V0dGluZ3NfcmVzZXRfc3NoX2tub3duX2hvc3QYSCABKAsyNi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZXR0aW5nc1Jlc2V0U3NoS25vd25Ib3N0UmVxdWVzdEgAEjwKCmNoYXRfcXVldWUYSSABKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0UXVldWVSZXF1ZXN0SAASQAoQY2hhdF9pbmdyZXNzX2FjaxhLIAEoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRJbmdyZXNzQWNrSAASQQoMdHVubmVsX3N0YXRlGFAgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuVHVubmVsU3RhdGVTbmFwc2hvdEgAEj8KD3R1bm5lbF9tdXRhdGlvbhhRIAEoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbE11dGF0aW9uSAASOQoMdHVubmVsX2ZyYW1lGFIgASgLMiEubGl2ZWFnZW50LmdhdGV3YXkudjIuVHVubmVsRnJhbWVIABJGCg93b3Jrc3BhY2Vfd2F0Y2gYWiABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Xb3Jrc3BhY2VXYXRjaFJlcXVlc3RIABJOChdtYW5hZ2VkX3Byb2Nlc3NfcmVxdWVzdBhbIAEoCzIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLk1hbmFnZWRQcm9jZXNzUmVxdWVzdEgAEkQKDmhpc3RvcnlfYnJhbmNoGFwgASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeUJyYW5jaFJlcXVlc3RIABJECg5wcm92aWRlcl91c2FnZRhdIAEoCzIqLmxpdmVhZ2VudC5nYXRld2F5LnYyLlByb3ZpZGVyVXNhZ2VSZXF1ZXN0SAASQwoOY2hhdF9maWxlX29wZW4YXiABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0RmlsZU9wZW5SZXF1ZXN0SAASRQoPaGlzdG9yeV9zZXRfY3dkGF8gASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVNldEN3ZFJlcXVlc3RIAEIJCgdwYXlsb2FkSgQIQxBESgQIRBBFSgQIRRBGSgQIShBLIuckCg1BZ2VudEVudmVsb3BlEhIKCnJlcXVlc3RfaWQYASABKAkSEQoJdGltZXN0YW1wGAIgASgDEjUKCmNoYXRfZXZlbnQYCiABKAsyHy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0RXZlbnRIABJEChBjcm9uX21hbmFnZV9yZXNwGBQgASgLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuQ3Jvbk1hbmFnZVJlc3BvbnNlSAASRgoRaGlzdG9yeV9saXN0X3Jlc3AYHiABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5TGlzdFJlc3BvbnNlSAASRAoQaGlzdG9yeV9nZXRfcmVzcBgfIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlHZXRSZXNwb25zZUgAEkoKE2hpc3RvcnlfcmVuYW1lX3Jlc3AYICABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5UmVuYW1lUmVzcG9uc2VIABJKChNoaXN0b3J5X2RlbGV0ZV9yZXNwGCEgASgLMisubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeURlbGV0ZVJlc3BvbnNlSAASPgoMaGlzdG9yeV9zeW5jGCIgASgLMiYubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVN5bmNFdmVudEgAEkoKE2hpc3RvcnlfcHJlZml4X3Jlc3AYIyABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5UHJlZml4UmVzcG9uc2VIABJEChBoaXN0b3J5X3Bpbl9yZXNwGCQgASgLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVBpblJlc3BvbnNlSAASTwoWaGlzdG9yeV9zaGFyZV9nZXRfcmVzcBglIAEoCzItLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlTaGFyZUdldFJlc3BvbnNlSAASTwoWaGlzdG9yeV9zaGFyZV9zZXRfcmVzcBgmIAEoCzItLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlTaGFyZVNldFJlc3BvbnNlSAASVwoaaGlzdG9yeV9zaGFyZV9yZXNvbHZlX3Jlc3AYJyABKAsyMS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5U2hhcmVSZXNvbHZlUmVzcG9uc2VIABJOChVoaXN0b3J5X3dvcmtkaXJzX3Jlc3AYOCABKAsyLS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5V29ya2RpcnNSZXNwb25zZUgAEkgKEnByb3ZpZGVyX2xpc3RfcmVzcBgoIAEoCzIqLmxpdmVhZ2VudC5nYXRld2F5LnYyLlByb3ZpZGVyTGlzdFJlc3BvbnNlSAASRgoRc2V0dGluZ3NfZ2V0X3Jlc3AYKSABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZXR0aW5nc0dldFJlc3BvbnNlSAASTAoUc2V0dGluZ3NfdXBkYXRlX3Jlc3AYKiABKAsyLC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZXR0aW5nc1VwZGF0ZVJlc3BvbnNlSAASQAoNc2V0dGluZ3Nfc3luYxgrIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNldHRpbmdzU3luY0V2ZW50SAASTQoVc2tpbGxfZmlsZXNfbGlzdF9yZXNwGCwgASgLMiwubGl2ZWFnZW50LmdhdGV3YXkudjIuU2tpbGxGaWxlc0xpc3RSZXNwb25zZUgAElMKGHNraWxsX21ldGFkYXRhX3JlYWRfcmVzcBgtIAEoCzIvLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNraWxsTWV0YWRhdGFSZWFkUmVzcG9uc2VIABJLChRza2lsbF90ZXh0X3JlYWRfcmVzcBguIAEoCzIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNraWxsVGV4dFJlYWRSZXNwb25zZUgAEk8KFmZpbGVfbWVudGlvbl9saXN0X3Jlc3AYLyABKAsyLS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5GaWxlTWVudGlvbkxpc3RSZXNwb25zZUgAElcKGnVwbG9hZF9yZWFkYWJsZV9maWxlc19yZXNwGDAgASgLMjEubGl2ZWFnZW50LmdhdGV3YXkudjIuVXBsb2FkUmVhZGFibGVGaWxlc1Jlc3BvbnNlSAASPgoNZnNfcm9vdHNfcmVzcBgxIAEoCzIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUm9vdHNSZXNwb25zZUgAEjIKBHBvbmcYMiABKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Qb25nUmVzcG9uc2VIABJFChFmc19saXN0X2RpcnNfcmVzcBgzIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzTGlzdERpcnNSZXNwb25zZUgAElkKG3VwbG9hZGVkX2ltYWdlX3ByZXZpZXdfcmVzcBg0IAEoCzIyLmxpdmVhZ2VudC5nYXRld2F5LnYyLlVwbG9hZGVkSW1hZ2VQcmV2aWV3UmVzcG9uc2VIABJIChJtZW1vcnlfbWFuYWdlX3Jlc3AYNSABKAsyKi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5NZW1vcnlNYW5hZ2VSZXNwb25zZUgAEkYKEXNraWxsX21hbmFnZV9yZXNwGDYgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuU2tpbGxNYW5hZ2VSZXNwb25zZUgAElwKHWZzX2NyZWF0ZV9wcm9qZWN0X2ZvbGRlcl9yZXNwGDcgASgLMjMubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNDcmVhdGVQcm9qZWN0Rm9sZGVyUmVzcG9uc2VIABJDChF0ZXJtaW5hbF9yZXNwb25zZRg5IAEoCzImLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsUmVzcG9uc2VIABI9Cg50ZXJtaW5hbF9ldmVudBg6IAEoCzIjLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsRXZlbnRIABI8Cgxmc19saXN0X3Jlc3AYOyABKAsyJC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc0xpc3RSZXNwb25zZUgAEkcKEmZzX3dyaXRlX3RleHRfcmVzcBg8IAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzV3JpdGVUZXh0UmVzcG9uc2VIABJHChJmc19jcmVhdGVfZGlyX3Jlc3AYPSABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc0NyZWF0ZURpclJlc3BvbnNlSAASQAoOZnNfcmVuYW1lX3Jlc3AYPiABKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc1JlbmFtZVJlc3BvbnNlSAASQAoOZnNfZGVsZXRlX3Jlc3AYPyABKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc0RlbGV0ZVJlc3BvbnNlSAASOQoMZ2l0X3Jlc3BvbnNlGEAgASgLMiEubGl2ZWFnZW50LmdhdGV3YXkudjIuR2l0UmVzcG9uc2VIABJWChpmc19yZWFkX2VkaXRhYmxlX3RleHRfcmVzcBhBIAEoCzIwLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUmVhZEVkaXRhYmxlVGV4dFJlc3BvbnNlSAASWgocZnNfcmVhZF93b3Jrc3BhY2VfaW1hZ2VfcmVzcBhCIAEoCzIyLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzUmVhZFdvcmtzcGFjZUltYWdlUmVzcG9uc2VIABI7Cg1zZnRwX3Jlc3BvbnNlGEkgASgLMiIubGl2ZWFnZW50LmdhdGV3YXkudjIuU2Z0cFJlc3BvbnNlSAASNQoKc2Z0cF9ldmVudBhKIAEoCzIfLmxpdmVhZ2VudC5nYXRld2F5LnYyLlNmdHBFdmVudEgAEkIKD2NoYXRfcXVldWVfcmVzcBhLIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRRdWV1ZVJlc3BvbnNlSAASQAoQY2hhdF9xdWV1ZV9ldmVudBhMIAEoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRRdWV1ZUV2ZW50SAASPgoMY2hhdF9jb250cm9sGEYgASgLMiYubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdENvbnRyb2xFdmVudEgAEkIKDnJ1bnRpbWVfc3RhdHVzGEcgASgLMigubGl2ZWFnZW50LmdhdGV3YXkudjIuUnVudGltZVN0YXR1c0V2ZW50SAASZQoic2V0dGluZ3NfcmVzZXRfc3NoX2tub3duX2hvc3RfcmVzcBhIIAEoCzI3LmxpdmVhZ2VudC5nYXRld2F5LnYyLlNldHRpbmdzUmVzZXRTc2hLbm93bkhvc3RSZXNwb25zZUgAEkoKFWNoYXRfcnVudGltZV9zbmFwc2hvdBhNIAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRSdW50aW1lU25hcHNob3RIABJMChRwcm92aWRlcl9tb2RlbHNfcmVzcBhPIAEoCzIsLmxpdmVhZ2VudC5nYXRld2F5LnYyLlByb3ZpZGVyTW9kZWxzUmVzcG9uc2VIABJCCg50dW5uZWxfZGVzaXJlZBhQIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbERlc2lyZWRTdGF0ZUgAEkwKFnR1bm5lbF9tdXRhdGlvbl9yZXN1bHQYUSABKAsyKi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxNdXRhdGlvblJlc3VsdEgAEjkKDHR1bm5lbF9mcmFtZRhSIAEoCzIhLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbEZyYW1lSAASRgoTdHVubmVsX3Byb2JlX3JlcG9ydBhTIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbFByb2JlUmVwb3J0SAASSgoSd29ya3NwYWNlX2FjdGl2aXR5GFogASgLMiwubGl2ZWFnZW50LmdhdGV3YXkudjIuV29ya3NwYWNlQWN0aXZpdHlFdmVudEgAElAKGG1hbmFnZWRfcHJvY2Vzc19yZXNwb25zZRhbIAEoCzIsLmxpdmVhZ2VudC5nYXRld2F5LnYyLk1hbmFnZWRQcm9jZXNzUmVzcG9uc2VIABJQChhtYW5hZ2VkX3Byb2Nlc3Nfc25hcHNob3QYXCABKAsyLC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5NYW5hZ2VkUHJvY2Vzc1NuYXBzaG90SAASSgoTaGlzdG9yeV9icmFuY2hfcmVzcBhdIAEoCzIrLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlCcmFuY2hSZXNwb25zZUgAEkoKE3Byb3ZpZGVyX3VzYWdlX3Jlc3AYXiABKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Qcm92aWRlclVzYWdlUmVzcG9uc2VIABJEChJjaGF0X2luZ3Jlc3NfYmF0Y2gYXyABKAsyJi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0SW5ncmVzc0JhdGNoSAASRgoTY2hhdF9pbmdyZXNzX3Jlc3VtZRhgIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRJbmdyZXNzUmVzdW1lSAASSgoVY2hhdF9pbmdyZXNzX2ZyYWdtZW50GGEgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdEluZ3Jlc3NGcmFnbWVudEgAEkkKE2NoYXRfZmlsZV9vcGVuX3Jlc3AYYiABKAsyKi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0RmlsZU9wZW5SZXNwb25zZUgAEjQKBWVycm9yGGMgASgLMiMubGl2ZWFnZW50LmdhdGV3YXkudjIuRXJyb3JSZXNwb25zZUgAEksKFGhpc3Rvcnlfc2V0X2N3ZF9yZXNwGGQgASgLMisubGl2ZWFnZW50LmdhdGV3YXkudjIuSGlzdG9yeVNldEN3ZFJlc3BvbnNlSABCCQoHcGF5bG9hZEoECEMQREoECEQQRUoECEUQRkoECE4QTyJVChFDaGF0U2VsZWN0ZWRNb2RlbBIaChJjdXN0b21fcHJvdmlkZXJfaWQYASABKAkSDQoFbW9kZWwYAiABKAkSFQoNcHJvdmlkZXJfdHlwZRgDIAEoCSJlChNDaGF0UnVudGltZUNvbnRyb2xzEhgKEHRoaW5raW5nX2VuYWJsZWQYASABKAgSIQoZbmF0aXZlX3dlYl9zZWFyY2hfZW5hYmxlZBgCIAEoCBIRCglyZWFzb25pbmcYAyABKAkidQoQQ2hhdFVwbG9hZGVkRmlsZRIVCg1yZWxhdGl2ZV9wYXRoGAEgASgJEhUKDWFic29sdXRlX3BhdGgYAiABKAkSEQoJZmlsZV9uYW1lGAMgASgJEgwKBGtpbmQYBCABKAkSEgoKc2l6ZV9ieXRlcxgFIAEoAyJLChJVcGxvYWRSZWFkYWJsZUZpbGUSEQoJZmlsZV9uYW1lGAEgASgJEhEKCW1pbWVfdHlwZRgCIAEoCRIPCgdjb250ZW50GAMgASgMImYKGlVwbG9hZFJlYWRhYmxlRmlsZXNSZXF1ZXN0Eg8KB3dvcmtkaXIYASABKAkSNwoFZmlsZXMYAiADKAsyKC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5VcGxvYWRSZWFkYWJsZUZpbGUiZQobVXBsb2FkUmVhZGFibGVGaWxlc1Jlc3BvbnNlEjUKBWZpbGVzGAEgAygLMiYubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdFVwbG9hZGVkRmlsZRIPCgdza2lwcGVkGAIgAygJIkUKG1VwbG9hZGVkSW1hZ2VQcmV2aWV3UmVxdWVzdBIPCgd3b3JrZGlyGAEgASgJEhUKDWFic29sdXRlX3BhdGgYAiABKAkiPwocVXBsb2FkZWRJbWFnZVByZXZpZXdSZXNwb25zZRIRCgltaW1lX3R5cGUYASABKAkSDAoEZGF0YRgCIAEoCSJ7CgpUdW5uZWxTcGVjEgoKAmlkGAEgASgJEhEKCXNsdWdfaGludBgCIAEoCRIMCgRuYW1lGAMgASgJEhIKCnRhcmdldF91cmwYBCABKAkSEgoKZXhwaXJlc19hdBgFIAEoAxIYChBwcm9qZWN0X3BhdGhfa2V5GAYgASgJIlkKElR1bm5lbERlc2lyZWRTdGF0ZRIxCgd0dW5uZWxzGAEgAygLMiAubGl2ZWFnZW50LmdhdGV3YXkudjIuVHVubmVsU3BlYxIQCghyZXZpc2lvbhgCIAEoBCJmCgxUdW5uZWxIZWFsdGgSDgoGc3RhdHVzGAEgASgJEhMKC2h0dHBfc3RhdHVzGAIgASgNEg0KBWVycm9yGAMgASgJEhIKCmNoZWNrZWRfYXQYBCABKAMSDgoGcnR0X21zGAUgASgNIvABCgxUdW5uZWxTdGF0dXMSCgoCaWQYASABKAkSDAoEc2x1ZxgCIAEoCRIMCgRuYW1lGAMgASgJEhIKCnRhcmdldF91cmwYBCABKAkSEwoLcHVibGljX3BhdGgYBSABKAkSEgoKY3JlYXRlZF9hdBgGIAEoAxISCgpleHBpcmVzX2F0GAcgASgDEhoKEmFjdGl2ZV9jb25uZWN0aW9ucxgIIAEoDRIYChBwcm9qZWN0X3BhdGhfa2V5GAkgASgJEjEKBWxvY2FsGAogASgLMiIubGl2ZWFnZW50LmdhdGV3YXkudjIuVHVubmVsSGVhbHRoIqUBChNUdW5uZWxTdGF0ZVNuYXBzaG90EjMKB3R1bm5lbHMYASADKAsyIi5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxTdGF0dXMSEAoIcmV2aXNpb24YAiABKAQSFAoMYWdlbnRfb25saW5lGAMgASgIEjEKBXJlbGF5GAQgASgLMiIubGl2ZWFnZW50LmdhdGV3YXkudjIuVHVubmVsSGVhbHRoIpkBCg5UdW5uZWxNdXRhdGlvbhIOCgZhY3Rpb24YASABKAkSEQoJdHVubmVsX2lkGAIgASgJEhIKCnRhcmdldF91cmwYAyABKAkSDAoEbmFtZRgEIAEoCRIYCgt0dGxfc2Vjb25kcxgFIAEoDUgAiAEBEhgKEHByb2plY3RfcGF0aF9rZXkYBiABKAlCDgoMX3R0bF9zZWNvbmRzIlQKFFR1bm5lbE11dGF0aW9uUmVzdWx0EhEKCXR1bm5lbF9pZBgBIAEoCRISCgplcnJvcl9jb2RlGAIgASgJEhUKDWVycm9yX21lc3NhZ2UYAyABKAkiWQoRVHVubmVsUHJvYmVSZXN1bHQSEQoJdHVubmVsX2lkGAEgASgJEjEKBWxvY2FsGAIgASgLMiIubGl2ZWFnZW50LmdhdGV3YXkudjIuVHVubmVsSGVhbHRoIk0KEVR1bm5lbFByb2JlUmVwb3J0EjgKB3Jlc3VsdHMYASADKAsyJy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxQcm9iZVJlc3VsdCIrCgxUdW5uZWxIZWFkZXISDAoEbmFtZRgBIAEoCRINCgV2YWx1ZRgCIAEoCSL1AgoLVHVubmVsRnJhbWUSEQoJc3RyZWFtX2lkGAEgASgJEjMKBGtpbmQYAiABKA4yJS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UdW5uZWxGcmFtZUtpbmQSEgoKdGFyZ2V0X3VybBgDIAEoCRIOCgZtZXRob2QYBCABKAkSDAoEcGF0aBgFIAEoCRIzCgdoZWFkZXJzGAYgAygLMiIubGl2ZWFnZW50LmdhdGV3YXkudjIuVHVubmVsSGVhZGVyEg4KBnN0YXR1cxgHIAEoDRIMCgRib2R5GAggASgMEg0KBWVycm9yGAkgASgJEkIKD3dzX21lc3NhZ2VfdHlwZRgKIAEoDjIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLlR1bm5lbFdzTWVzc2FnZVR5cGUSFgoOd3Nfc3VicHJvdG9jb2wYCyABKAkSFQoNd3NfY2xvc2VfY29kZRgMIAEoDRIXCg93c19jbG9zZV9yZWFzb24YDSABKAkiKQoVV29ya3NwYWNlV2F0Y2hSZXF1ZXN0EhAKCHdvcmtkaXJzGAEgAygJIn4KFldvcmtzcGFjZUFjdGl2aXR5RXZlbnQSDwoHd29ya2RpchgBIAEoCRIQCghyZXZpc2lvbhgCIAEoBBIKCgJmcxgDIAEoCBILCgNnaXQYBCABKAgSFQoNY2hhbmdlZF9wYXRocxgFIAMoCRIRCgl0cnVuY2F0ZWQYBiABKAgilgIKFE1hbmFnZWRQcm9jZXNzUmVjb3JkEgoKAmlkGAEgASgJEg0KBWxhYmVsGAIgASgJEg8KB2NvbW1hbmQYAyABKAkSCwoDY3dkGAQgASgJEg0KBXNoZWxsGAUgASgJEgsKA3BpZBgGIAEoDRIQCghsb2dfcGF0aBgHIAEoCRISCgpzdGFydGVkX2F0GAggASgDEhgKC2ZpbmlzaGVkX2F0GAkgASgDSACIAQESFgoJZXhpdF9jb2RlGAogASgFSAGIAQESDwoHcnVubmluZxgLIAEoCBIQCghpc29sYXRlZBgMIAEoCBIQCghyZXN0b3JlZBgNIAEoCEIOCgxfZmluaXNoZWRfYXRCDAoKX2V4aXRfY29kZSJpChZNYW5hZ2VkUHJvY2Vzc1NuYXBzaG90Ej0KCXByb2Nlc3NlcxgBIAMoCzIqLmxpdmVhZ2VudC5nYXRld2F5LnYyLk1hbmFnZWRQcm9jZXNzUmVjb3JkEhAKCHJldmlzaW9uGAIgASgEIk4KFU1hbmFnZWRQcm9jZXNzUmVxdWVzdBIOCgZhY3Rpb24YASABKAkSEgoKcHJvY2Vzc19pZBgCIAEoCRIRCgltYXhfYnl0ZXMYAyABKA0itwEKFk1hbmFnZWRQcm9jZXNzUmVzcG9uc2USDgoGYWN0aW9uGAEgASgJEj4KCHNuYXBzaG90GAIgASgLMiwubGl2ZWFnZW50LmdhdGV3YXkudjIuTWFuYWdlZFByb2Nlc3NTbmFwc2hvdBITCgtsb2dfY29udGVudBgDIAEoCRIQCghsb2dfcGF0aBgEIAEoCRIVCg1sb2dfdHJ1bmNhdGVkGAUgASgIEg8KB3N0b3BwZWQYBiABKAgiOQoTTWVtb3J5TWFuYWdlUmVxdWVzdBIPCgdjb21tYW5kGAEgASgJEhEKCWFyZ3NfanNvbhgCIAEoCSIrChRNZW1vcnlNYW5hZ2VSZXNwb25zZRITCgtyZXN1bHRfanNvbhgBIAEoCSKYAwoPVGVybWluYWxSZXF1ZXN0Eg4KBmFjdGlvbhgBIAEoCRISCgpzZXNzaW9uX2lkGAIgASgJEhgKEHByb2plY3RfcGF0aF9rZXkYAyABKAkSCwoDY3dkGAQgASgJEg0KBXNoZWxsGAUgASgJEg0KBXRpdGxlGAYgASgJEgwKBGRhdGEYByABKAkSDAoEY29scxgIIAEoDRIMCgRyb3dzGAkgASgNEhEKCW1heF9ieXRlcxgKIAEoDRITCgtzc2hfaG9zdF9pZBgLIAEoCRIRCglwcm9tcHRfaWQYDCABKAkSFQoNcHJvbXB0X2Fuc3dlchgNIAEoCRIWCg50cnVzdF9ob3N0X2tleRgOIAEoCBIUCgxzZnRwX2VuYWJsZWQYDyABKAgSDgoGdGFiX2lkGBAgASgJEhAKCHRhYl9raW5kGBEgASgJEhMKC3JlbW90ZV9ob3N0GBIgASgJEhMKC3JlbW90ZV9wb3J0GBMgASgNEhIKCmxvY2FsX3BvcnQYFCABKA0SEgoKZm9yd2FyZF9pZBgVIAEoCSKyAgoPVGVybWluYWxTZXNzaW9uEgoKAmlkGAEgASgJEhgKEHByb2plY3RfcGF0aF9rZXkYAiABKAkSCwoDY3dkGAMgASgJEg0KBXNoZWxsGAQgASgJEg0KBXRpdGxlGAUgASgJEgsKA3BpZBgGIAEoDRIMCgRjb2xzGAcgASgNEgwKBHJvd3MYCCABKA0SEgoKY3JlYXRlZF9hdBgJIAEoBBISCgp1cGRhdGVkX2F0GAogASgEEhMKC2ZpbmlzaGVkX2F0GAsgASgEEhEKCWV4aXRfY29kZRgMIAEoBRIPCgdydW5uaW5nGA0gASgIEgwKBGtpbmQYDiABKAkSNgoDc3NoGA8gASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTc2hNZXRhZGF0YSLbAQoTVGVybWluYWxTc2hNZXRhZGF0YRIPCgdob3N0X2lkGAEgASgJEhEKCWhvc3RfbmFtZRgCIAEoCRIQCgh1c2VybmFtZRgDIAEoCRIMCgRob3N0GAQgASgJEgwKBHBvcnQYBSABKA0SEQoJYXV0aF90eXBlGAYgASgJEg4KBnN0YXR1cxgHIAEoCRIZChFyZWNvbm5lY3RfYXR0ZW1wdBgIIAEoDRIeChZyZWNvbm5lY3RfbWF4X2F0dGVtcHRzGAkgASgNEhQKDHNmdHBfZW5hYmxlZBgKIAEoCCL3AQoLU2Z0cFJlcXVlc3QSDgoGYWN0aW9uGAEgASgJEhIKCnNlc3Npb25faWQYAiABKAkSGAoQcHJvamVjdF9wYXRoX2tleRgDIAEoCRIPCgd3b3JrZGlyGAQgASgJEhIKCmxvY2FsX3BhdGgYBSABKAkSEwoLcmVtb3RlX3BhdGgYBiABKAkSEQoJZnJvbV9wYXRoGAcgASgJEg8KB3RvX3BhdGgYCCABKAkSEQoJZGlyZWN0aW9uGAkgASgJEhMKC3RhcmdldF9wYXRoGAogASgJEhEKCXJlY3Vyc2l2ZRgLIAEoCBIRCglvdmVyd3JpdGUYDCABKAgiWAoJU2Z0cEVudHJ5EgwKBHBhdGgYASABKAkSDAoEbmFtZRgCIAEoCRIMCgRraW5kGAMgASgJEhIKCnNpemVfYnl0ZXMYBCABKAQSDQoFbXRpbWUYBSABKAQi8gEKDFNmdHBUcmFuc2ZlchIKCgJpZBgBIAEoCRISCgpzZXNzaW9uX2lkGAIgASgJEhEKCWRpcmVjdGlvbhgDIAEoCRIOCgZzdGF0dXMYBCABKAkSEwoLc291cmNlX3BhdGgYBSABKAkSEwoLdGFyZ2V0X3BhdGgYBiABKAkSFAoMY3VycmVudF9wYXRoGAcgASgJEhIKCmJ5dGVzX2RvbmUYCCABKAQSEwoLYnl0ZXNfdG90YWwYCSABKAQSEgoKZmlsZXNfZG9uZRgKIAEoDRITCgtmaWxlc190b3RhbBgLIAEoDRINCgVlcnJvchgMIAEoCSLUAQoMU2Z0cFJlc3BvbnNlEg4KBmFjdGlvbhgBIAEoCRIMCgRwYXRoGAIgASgJEjAKB2VudHJpZXMYAyADKAsyHy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZnRwRW50cnkSLgoFZW50cnkYBCABKAsyHy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5TZnRwRW50cnkSDgoGZXhpc3RzGAUgASgIEjQKCHRyYW5zZmVyGAYgASgLMiIubGl2ZWFnZW50LmdhdGV3YXkudjIuU2Z0cFRyYW5zZmVyIk8KCVNmdHBFdmVudBIMCgRraW5kGAEgASgJEjQKCHRyYW5zZmVyGAIgASgLMiIubGl2ZWFnZW50LmdhdGV3YXkudjIuU2Z0cFRyYW5zZmVyIsEBChFUZXJtaW5hbFNzaFByb21wdBIKCgJpZBgBIAEoCRIMCgRraW5kGAIgASgJEg8KB2hvc3RfaWQYAyABKAkSEQoJaG9zdF9uYW1lGAQgASgJEgwKBGhvc3QYBSABKAkSDAoEcG9ydBgGIAEoDRIPCgdtZXNzYWdlGAcgASgJEhoKEmZpbmdlcnByaW50X3NoYTI1NhgIIAEoCRIQCghrZXlfdHlwZRgJIAEoCRITCgthbnN3ZXJfZWNobxgKIAEoCCJBChNUZXJtaW5hbFNoZWxsT3B0aW9uEgoKAmlkGAEgASgJEg0KBWxhYmVsGAIgASgJEg8KB2NvbW1hbmQYAyABKAkigAEKDlRlcm1pbmFsU3NoVGFiEgoKAmlkGAEgASgJEhIKCnNlc3Npb25faWQYAiABKAkSGAoQcHJvamVjdF9wYXRoX2tleRgDIAEoCRIMCgRraW5kGAQgASgJEhIKCmNyZWF0ZWRfYXQYBSABKAQSEgoKdXBkYXRlZF9hdBgGIAEoBCJ/ChdUZXJtaW5hbFNzaFRhYnNTbmFwc2hvdBIYChBwcm9qZWN0X3BhdGhfa2V5GAEgASgJEjIKBHRhYnMYAiADKAsyJC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbFNzaFRhYhIQCghyZXZpc2lvbhgEIAEoBEoECAMQBCL9AQoXVGVybWluYWxTc2hMb2NhbEZvcndhcmQSCgoCaWQYASABKAkSEgoKc2Vzc2lvbl9pZBgCIAEoCRIYChBwcm9qZWN0X3BhdGhfa2V5GAMgASgJEhIKCmxvY2FsX2hvc3QYBCABKAkSEgoKbG9jYWxfcG9ydBgFIAEoDRIPCgdhZGRyZXNzGAYgASgJEhMKC3JlbW90ZV9ob3N0GAcgASgJEhMKC3JlbW90ZV9wb3J0GAggASgNEg4KBnN0YXR1cxgJIAEoCRISCgpjcmVhdGVkX2F0GAogASgEEhIKCnVwZGF0ZWRfYXQYCyABKAQSDQoFZXJyb3IYDCABKAkidQogVGVybWluYWxTc2hMb2NhbEZvcndhcmRzU25hcHNob3QSPwoIZm9yd2FyZHMYASADKAsyLS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbFNzaExvY2FsRm9yd2FyZBIQCghyZXZpc2lvbhgCIAEoBCJ/Ch1UZXJtaW5hbFNzaExvY2FsRm9yd2FyZEFjdGlvbhI+Cgdmb3J3YXJkGAEgASgLMi0ubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTc2hMb2NhbEZvcndhcmQSEAoIcmV2aXNpb24YAiABKAQSDAoEa2luZBgDIAEoCSKnBQoQVGVybWluYWxSZXNwb25zZRIOCgZhY3Rpb24YASABKAkSNwoIc2Vzc2lvbnMYAiADKAsyJS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbFNlc3Npb24SNgoHc2Vzc2lvbhgDIAEoCzIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU2Vzc2lvbhIOCgZvdXRwdXQYBCABKAwSEQoJdHJ1bmNhdGVkGAUgASgIEkAKDXNoZWxsX29wdGlvbnMYBiADKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbFNoZWxsT3B0aW9uEhUKDWRlZmF1bHRfc2hlbGwYByABKAkSGwoTb3V0cHV0X3N0YXJ0X29mZnNldBgIIAEoBBIZChFvdXRwdXRfZW5kX29mZnNldBgJIAEoBBI7Cgpzc2hfcHJvbXB0GAogASgLMicubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTc2hQcm9tcHQSEgoKbGF0ZW5jeV9tcxgLIAEoDRI/Cghzc2hfdGFicxgMIAEoCzItLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU3NoVGFic1NuYXBzaG90ElIKEnNzaF9sb2NhbF9mb3J3YXJkcxgNIAEoCzI2LmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU3NoTG9jYWxGb3J3YXJkc1NuYXBzaG90Ek4KEXNzaF9sb2NhbF9mb3J3YXJkGA4gASgLMjMubGl2ZWFnZW50LmdhdGV3YXkudjIuVGVybWluYWxTc2hMb2NhbEZvcndhcmRBY3Rpb24SKAogc3NoX2xvY2FsX2ZvcndhcmRfcG9ydF9hdmFpbGFibGUYDyABKAgi2gIKDVRlcm1pbmFsRXZlbnQSDAoEa2luZBgBIAEoCRISCgpzZXNzaW9uX2lkGAIgASgJEhgKEHByb2plY3RfcGF0aF9rZXkYAyABKAkSNgoHc2Vzc2lvbhgEIAEoCzIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU2Vzc2lvbhIMCgRkYXRhGAUgASgMEhsKE291dHB1dF9zdGFydF9vZmZzZXQYBiABKAQSGQoRb3V0cHV0X2VuZF9vZmZzZXQYByABKAQSPwoIc3NoX3RhYnMYCCABKAsyLS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5UZXJtaW5hbFNzaFRhYnNTbmFwc2hvdBJOChFzc2hfbG9jYWxfZm9yd2FyZBgJIAEoCzIzLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU3NoTG9jYWxGb3J3YXJkQWN0aW9uIrICChNUZXJtaW5hbFN0cmVhbUZyYW1lEgwKBGtpbmQYASABKAkSEQoJc3RyZWFtX2lkGAIgASgJEhIKCnNlc3Npb25faWQYAyABKAkSGAoQcHJvamVjdF9wYXRoX2tleRgEIAEoCRILCgNzZXEYBSABKAQSFAoMc3RhcnRfb2Zmc2V0GAYgASgEEhIKCmVuZF9vZmZzZXQYByABKAQSDAoEY29scxgIIAEoDRIMCgRyb3dzGAkgASgNEhEKCW1heF9ieXRlcxgKIAEoDRIRCgl0cnVuY2F0ZWQYCyABKAgSDQoFZXJyb3IYDCABKAkSNgoHc2Vzc2lvbhgNIAEoCzIlLmxpdmVhZ2VudC5nYXRld2F5LnYyLlRlcm1pbmFsU2Vzc2lvbhIMCgRkYXRhGA4gASgMIkAKCkdpdFJlcXVlc3QSDgoGYWN0aW9uGAEgASgJEg8KB3dvcmtkaXIYAiABKAkSEQoJYXJnc19qc29uGAMgASgJIjIKC0dpdFJlc3BvbnNlEg4KBmFjdGlvbhgBIAEoCRITCgtyZXN1bHRfanNvbhgCIAEoCSL0AgoLQ2hhdFJlcXVlc3QSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEg8KB21lc3NhZ2UYAiABKAkSPwoOc2VsZWN0ZWRfbW9kZWwYAyABKAsyJy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0U2VsZWN0ZWRNb2RlbBIWCg5leGVjdXRpb25fbW9kZRgEIAEoCRIPCgd3b3JrZGlyGAUgASgJEj4KDnVwbG9hZGVkX2ZpbGVzGAcgAygLMiYubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdFVwbG9hZGVkRmlsZRIZChFjbGllbnRfcmVxdWVzdF9pZBgIIAEoCRJDChBydW50aW1lX2NvbnRyb2xzGAkgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdFJ1bnRpbWVDb250cm9scxIUCgxxdWV1ZV9wb2xpY3kYCiABKAlKBAgGEAdSFXNlbGVjdGVkX3N5c3RlbV90b29scyKKAQoOQ2hhdE1lc3NhZ2VSZWYSFQoNc2VnbWVudF9pbmRleBgBIAEoBRIVCg1tZXNzYWdlX2luZGV4GAIgASgFEhIKCnNlZ21lbnRfaWQYAyABKAkSEgoKbWVzc2FnZV9pZBgEIAEoCRIMCgRyb2xlGAUgASgJEhQKDGNvbnRlbnRfaGFzaBgGIAEoCSI8ChFDYW5jZWxDaGF0UmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSDgoGcnVuX2lkGAIgASgJIs8BChJDaGF0Q29tbWFuZFJlcXVlc3QSDAoEdHlwZRgBIAEoCRIyCgdyZXF1ZXN0GAIgASgLMiEubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdFJlcXVlc3QSPgoQYmFzZV9tZXNzYWdlX3JlZhgDIAEoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRNZXNzYWdlUmVmEjcKBmNhbmNlbBgEIAEoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNhbmNlbENoYXRSZXF1ZXN0IrgBChBDaGF0UXVldWVSZXF1ZXN0Eg4KBmFjdGlvbhgBIAEoCRIXCg9jb252ZXJzYXRpb25faWQYAiABKAkSDwoHaXRlbV9pZBgDIAEoCRIRCglkaXJlY3Rpb24YBCABKAkSEAoIcmV2aXNpb24YBSABKAQSEgoKZHJhZnRfanNvbhgGIAEoCRIbChN1cGxvYWRlZF9maWxlc19qc29uGAcgASgJEhQKDHJlcXVlc3RfanNvbhgIIAEoCSKGAQoRQ2hhdFF1ZXVlUmVzcG9uc2USEAoIYWNjZXB0ZWQYASABKAgSDwoHbWVzc2FnZRgCIAEoCRIVCg1zbmFwc2hvdF9qc29uGAMgASgJEhEKCWl0ZW1fanNvbhgEIAEoCRISCgplcnJvcl9jb2RlGAUgASgJEhAKCHJldmlzaW9uGAYgASgEIlIKDkNoYXRRdWV1ZUV2ZW50EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRIVCg1zbmFwc2hvdF9qc29uGAIgASgJEhAKCHJldmlzaW9uGAMgASgEIoUCCglDaGF0RXZlbnQSOwoEdHlwZRgBIAEoDjItLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRFdmVudC5DaGF0RXZlbnRUeXBlEhcKD2NvbnZlcnNhdGlvbl9pZBgCIAEoCRIMCgRkYXRhGAMgASgJIpMBCg1DaGF0RXZlbnRUeXBlEgkKBVRPS0VOEAASDAoIVEhJTktJTkcQARINCglUT09MX0NBTEwQAhIPCgtUT09MX1JFU1VMVBADEggKBERPTkUQBBIJCgVFUlJPUhAFEg8KC1RPT0xfU1RBVFVTEAYSEQoNSE9TVEVEX1NFQVJDSBAHEhAKDFVTRVJfTUVTU0FHRRAIIrwBChBDaGF0Q29udHJvbEV2ZW50EhIKCnJlcXVlc3RfaWQYASABKAkSGQoRY2xpZW50X3JlcXVlc3RfaWQYAiABKAkSFwoPY29udmVyc2F0aW9uX2lkGAMgASgJEhEKCXJ1bl9lcG9jaBgEIAEoAxIMCgR0eXBlGAUgASgJEg0KBXN0YXRlGAYgASgJEhIKCmVycm9yX2NvZGUYByABKAkSDwoHbWVzc2FnZRgIIAEoCRILCgNzZXEYCSABKAMi/AEKE0NoYXRSdW50aW1lU25hcHNob3QSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEg4KBnJ1bl9pZBgCIAEoCRIZChFjbGllbnRfcmVxdWVzdF9pZBgDIAEoCRIRCgl3b3JrZXJfaWQYBCABKAkSDQoFc3RhdGUYBSABKAkSCwoDY3dkGAYgASgJEhIKCnVwZGF0ZWRfYXQYByABKAMSEAoIcmV2aXNpb24YCCABKAMSFAoMZW50cmllc19qc29uGAkgASgJEhMKC3Rvb2xfc3RhdHVzGAogASgJEiEKGXRvb2xfc3RhdHVzX2lzX2NvbXBhY3Rpb24YCyABKAgi6gEKElJ1bnRpbWVTdGF0dXNFdmVudBIRCgl3b3JrZXJfaWQYASABKAkSDQoFc3RhdGUYAiABKAkSDwoHdmlzaWJsZRgDIAEoCBIYChBhY3RpdmVfcnVuX2NvdW50GAQgASgNEhEKCXRpbWVzdGFtcBgFIAEoAxI4CgthY3RpdmVfcnVucxgGIAMoCzIjLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRSdW5SZXBvcnQSOgoNZmluaXNoZWRfcnVucxgHIAMoCzIjLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRSdW5SZXBvcnQigAEKDUNoYXRSdW5SZXBvcnQSDgoGcnVuX2lkGAEgASgJEhcKD2NvbnZlcnNhdGlvbl9pZBgCIAEoCRINCgVzdGF0ZRgDIAEoCRISCgplcnJvcl9jb2RlGAQgASgJEg8KB21lc3NhZ2UYBSABKAkSEgoKdXBkYXRlZF9hdBgGIAEoAyJHChFDcm9uTWFuYWdlUmVxdWVzdBIOCgZhY3Rpb24YASABKAkSDwoHdGFza19pZBgCIAEoCRIRCgl0YXNrX2pzb24YAyABKAkiOQoSQ3Jvbk1hbmFnZVJlc3BvbnNlEg4KBmFjdGlvbhgBIAEoCRITCgtyZXN1bHRfanNvbhgCIAEoCSJVChJIaXN0b3J5TGlzdFJlcXVlc3QSDAoEcGFnZRgBIAEoBRIRCglwYWdlX3NpemUYAiABKAUSCwoDY3dkGAMgASgJEhEKCWN3ZF9lbXB0eRgEIAEoCCJsChNIaXN0b3J5TGlzdFJlc3BvbnNlEkAKDWNvbnZlcnNhdGlvbnMYASADKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Db252ZXJzYXRpb25TdW1tYXJ5EhMKC3RvdGFsX2NvdW50GAIgASgFIooCChNDb252ZXJzYXRpb25TdW1tYXJ5EgoKAmlkGAEgASgJEg0KBXRpdGxlGAIgASgJEhIKCmNyZWF0ZWRfYXQYAyABKAMSEgoKdXBkYXRlZF9hdBgEIAEoAxIVCg1tZXNzYWdlX2NvdW50GAUgASgFEhMKC3Byb3ZpZGVyX2lkGAYgASgJEg0KBW1vZGVsGAcgASgJEhIKCnNlc3Npb25faWQYCCABKAkSCwoDY3dkGAkgASgJEhEKCWlzX3Bpbm5lZBgKIAEoCBIRCglwaW5uZWRfYXQYCyABKAMSEQoJaXNfc2hhcmVkGAwgASgIEhsKE3NlbGVjdGVkX21vZGVsX2pzb24YDSABKAkiQgoRSGlzdG9yeUdldFJlcXVlc3QSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEhQKDG1heF9tZXNzYWdlcxgCIAEoBSLUAQoSSGlzdG9yeUdldFJlc3BvbnNlEhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRIVCg1tZXNzYWdlc19qc29uGAIgASgJEhsKE3RvdGFsX21lc3NhZ2VfY291bnQYAyABKAUSHgoWcmV0dXJuZWRfbWVzc2FnZV9jb3VudBgEIAEoBRIQCghoYXNfbW9yZRgFIAEoCBI/Cgxjb252ZXJzYXRpb24YBiABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Db252ZXJzYXRpb25TdW1tYXJ5IoUBChRIaXN0b3J5UHJlZml4UmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSFAoMbWF4X21lc3NhZ2VzGAIgASgFEj4KEGJhc2VfbWVzc2FnZV9yZWYYAyABKAsyJC5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0TWVzc2FnZVJlZiLXAQoVSGlzdG9yeVByZWZpeFJlc3BvbnNlEhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRIVCg1tZXNzYWdlc19qc29uGAIgASgJEhsKE3RvdGFsX21lc3NhZ2VfY291bnQYAyABKAUSHgoWcmV0dXJuZWRfbWVzc2FnZV9jb3VudBgEIAEoBRIQCghoYXNfbW9yZRgFIAEoCBI/Cgxjb252ZXJzYXRpb24YBiABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Db252ZXJzYXRpb25TdW1tYXJ5Ij4KFEhpc3RvcnlSZW5hbWVSZXF1ZXN0EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRINCgV0aXRsZRgCIAEoCSJYChVIaXN0b3J5UmVuYW1lUmVzcG9uc2USPwoMY29udmVyc2F0aW9uGAEgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ29udmVyc2F0aW9uU3VtbWFyeSJvChRIaXN0b3J5QnJhbmNoUmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSPgoQYmFzZV9tZXNzYWdlX3JlZhgCIAEoCzIkLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRNZXNzYWdlUmVmIlgKFUhpc3RvcnlCcmFuY2hSZXNwb25zZRI/Cgxjb252ZXJzYXRpb24YASABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Db252ZXJzYXRpb25TdW1tYXJ5Ij8KEUhpc3RvcnlQaW5SZXF1ZXN0EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRIRCglpc19waW5uZWQYAiABKAgiVQoSSGlzdG9yeVBpblJlc3BvbnNlEj8KDGNvbnZlcnNhdGlvbhgBIAEoCzIpLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNvbnZlcnNhdGlvblN1bW1hcnkikgEKEkhpc3RvcnlTaGFyZVN0YXR1cxIXCg9jb252ZXJzYXRpb25faWQYASABKAkSDwoHZW5hYmxlZBgCIAEoCBINCgV0b2tlbhgDIAEoCRISCgpjcmVhdGVkX2F0GAQgASgDEhIKCnVwZGF0ZWRfYXQYBSABKAMSGwoTcmVkYWN0X3Rvb2xfY29udGVudBgGIAEoCCIxChZIaXN0b3J5U2hhcmVHZXRSZXF1ZXN0EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCSJSChdIaXN0b3J5U2hhcmVHZXRSZXNwb25zZRI3CgVzaGFyZRgBIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlTaGFyZVN0YXR1cyJ8ChZIaXN0b3J5U2hhcmVTZXRSZXF1ZXN0EhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRIPCgdlbmFibGVkGAIgASgIEiAKE3JlZGFjdF90b29sX2NvbnRlbnQYAyABKAhIAIgBAUIWChRfcmVkYWN0X3Rvb2xfY29udGVudCJSChdIaXN0b3J5U2hhcmVTZXRSZXNwb25zZRI3CgVzaGFyZRgBIAEoCzIoLmxpdmVhZ2VudC5nYXRld2F5LnYyLkhpc3RvcnlTaGFyZVN0YXR1cyIrChpIaXN0b3J5U2hhcmVSZXNvbHZlUmVxdWVzdBINCgV0b2tlbhgBIAEoCSLIAQobSGlzdG9yeVNoYXJlUmVzb2x2ZVJlc3BvbnNlEhcKD2NvbnZlcnNhdGlvbl9pZBgBIAEoCRIVCg1tZXNzYWdlc19qc29uGAIgASgJEhsKE3RvdGFsX21lc3NhZ2VfY291bnQYAyABKAUSPwoMY29udmVyc2F0aW9uGAQgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ29udmVyc2F0aW9uU3VtbWFyeRIbChNyZWRhY3RfdG9vbF9jb250ZW50GAUgASgIIhgKFkhpc3RvcnlXb3JrZGlyc1JlcXVlc3QiVQoVSGlzdG9yeVdvcmtkaXJTdW1tYXJ5EgwKBHBhdGgYASABKAkSGgoSY29udmVyc2F0aW9uX2NvdW50GAIgASgFEhIKCnVwZGF0ZWRfYXQYAyABKAMiWAoXSGlzdG9yeVdvcmtkaXJzUmVzcG9uc2USPQoId29ya2RpcnMYASADKAsyKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5IaXN0b3J5V29ya2RpclN1bW1hcnkiLwoUSGlzdG9yeURlbGV0ZVJlcXVlc3QSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJIhcKFUhpc3RvcnlEZWxldGVSZXNwb25zZSJ6ChBIaXN0b3J5U3luY0V2ZW50EgwKBGtpbmQYASABKAkSPwoMY29udmVyc2F0aW9uGAIgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ29udmVyc2F0aW9uU3VtbWFyeRIXCg9jb252ZXJzYXRpb25faWQYAyABKAkiFQoTUHJvdmlkZXJMaXN0UmVxdWVzdCIuChRQcm92aWRlckxpc3RSZXNwb25zZRIWCg5wcm92aWRlcnNfanNvbhgBIAEoCSIUChJTZXR0aW5nc0dldFJlcXVlc3QiLAoTU2V0dGluZ3NHZXRSZXNwb25zZRIVCg1zZXR0aW5nc19qc29uGAEgASgJIi4KFVNldHRpbmdzVXBkYXRlUmVxdWVzdBIVCg1zZXR0aW5nc19qc29uGAEgASgJIjsKFlNldHRpbmdzVXBkYXRlUmVzcG9uc2USEAoIYWNjZXB0ZWQYASABKAgSDwoHbWVzc2FnZRgCIAEoCSI+CiBTZXR0aW5nc1Jlc2V0U3NoS25vd25Ib3N0UmVxdWVzdBIMCgRob3N0GAEgASgJEgwKBHBvcnQYAiABKA0iNAohU2V0dGluZ3NSZXNldFNzaEtub3duSG9zdFJlc3BvbnNlEg8KB2RlbGV0ZWQYASABKA0iKgoRU2V0dGluZ3NTeW5jRXZlbnQSFQoNc2V0dGluZ3NfanNvbhgBIAEoCSIXChVTa2lsbEZpbGVzTGlzdFJlcXVlc3QiTAoWU2tpbGxGaWxlc0xpc3RSZXNwb25zZRIQCghyb290X2RpchgBIAEoCRINCgVwYXRocxgCIAMoCRIRCgl0cnVuY2F0ZWQYAyABKAgiKAoYU2tpbGxNZXRhZGF0YVJlYWRSZXF1ZXN0EgwKBHBhdGgYASABKAkiPgoZU2tpbGxNZXRhZGF0YVJlYWRSZXNwb25zZRIMCgRuYW1lGAEgASgJEhMKC2Rlc2NyaXB0aW9uGAIgASgJIkQKFFNraWxsVGV4dFJlYWRSZXF1ZXN0EgwKBHBhdGgYASABKAkSDgoGb2Zmc2V0GAIgASgNEg4KBmxlbmd0aBgDIAEoDSI7ChVTa2lsbFRleHRSZWFkUmVzcG9uc2USDwoHY29udGVudBgBIAEoCRIRCgl0cnVuY2F0ZWQYAiABKAgiKgoSU2tpbGxNYW5hZ2VSZXF1ZXN0EhQKDHBheWxvYWRfanNvbhgBIAEoCSIqChNTa2lsbE1hbmFnZVJlc3BvbnNlEhMKC3Jlc3VsdF9qc29uGAEgASgJIncKFkZpbGVNZW50aW9uTGlzdFJlcXVlc3QSDwoHd29ya2RpchgBIAEoCRITCgttYXhfcmVzdWx0cxgCIAEoDRINCgVxdWVyeRgDIAEoCRIYCgtzaG93X2hpZGRlbhgEIAEoCEgAiAEBQg4KDF9zaG93X2hpZGRlbiI+ChBGaWxlTWVudGlvbkVudHJ5EgwKBHBhdGgYASABKAkSDAoEa2luZBgCIAEoCRIOCgZoaWRkZW4YAyABKAgiZQoXRmlsZU1lbnRpb25MaXN0UmVzcG9uc2USNwoHZW50cmllcxgBIAMoCzImLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZpbGVNZW50aW9uRW50cnkSEQoJdHJ1bmNhdGVkGAIgASgIIj8KBkZzUm9vdBIKCgJpZBgBIAEoCRIMCgRwYXRoGAIgASgJEgwKBGtpbmQYAyABKAkSDQoFbGFiZWwYBCABKAkiEAoORnNSb290c1JlcXVlc3QiPgoPRnNSb290c1Jlc3BvbnNlEisKBXJvb3RzGAEgAygLMhwubGl2ZWFnZW50LmdhdGV3YXkudjIuRnNSb290IjYKEUZzTGlzdERpcnNSZXF1ZXN0EgwKBHBhdGgYASABKAkSEwoLbWF4X3Jlc3VsdHMYAiABKA0iKAoKRnNEaXJFbnRyeRIMCgRwYXRoGAEgASgJEgwKBG5hbWUYAiABKAkiaAoSRnNMaXN0RGlyc1Jlc3BvbnNlEgwKBHBhdGgYASABKAkSMQoHZW50cmllcxgCIAMoCzIgLmxpdmVhZ2VudC5nYXRld2F5LnYyLkZzRGlyRW50cnkSEQoJdHJ1bmNhdGVkGAMgASgIIjwKHEZzQ3JlYXRlUHJvamVjdEZvbGRlclJlcXVlc3QSDgoGcGFyZW50GAEgASgJEgwKBG5hbWUYAiABKAkiLQodRnNDcmVhdGVQcm9qZWN0Rm9sZGVyUmVzcG9uc2USDAoEcGF0aBgBIAEoCSKMAQoNRnNMaXN0UmVxdWVzdBIPCgd3b3JrZGlyGAEgASgJEgwKBHBhdGgYAiABKAkSDQoFZGVwdGgYAyABKA0SDgoGb2Zmc2V0GAQgASgNEhMKC21heF9yZXN1bHRzGAUgASgNEhgKC3Nob3dfaGlkZGVuGAYgASgISACIAQFCDgoMX3Nob3dfaGlkZGVuIjkKC0ZzTGlzdEVudHJ5EgwKBHBhdGgYASABKAkSDAoEa2luZBgCIAEoCRIOCgZoaWRkZW4YAyABKAgiuQEKDkZzTGlzdFJlc3BvbnNlEgwKBHBhdGgYASABKAkSEAoIaGFzX3BhdGgYAiABKAgSDQoFZGVwdGgYAyABKA0SDgoGb2Zmc2V0GAQgASgNEhMKC21heF9yZXN1bHRzGAUgASgNEg0KBXRvdGFsGAYgASgNEhAKCGhhc19tb3JlGAcgASgIEjIKB2VudHJpZXMYCCADKAsyIS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5Gc0xpc3RFbnRyeSI6ChlGc1JlYWRFZGl0YWJsZVRleHRSZXF1ZXN0Eg8KB3dvcmtkaXIYASABKAkSDAoEcGF0aBgCIAEoCSKMAQoaRnNSZWFkRWRpdGFibGVUZXh0UmVzcG9uc2USDAoEcGF0aBgBIAEoCRIPCgdjb250ZW50GAIgASgJEhAKCG10aW1lX21zGAMgASgEEhQKDGNvbnRlbnRfaGFzaBgEIAEoCRISCgpzaXplX2J5dGVzGAUgASgEEhMKC3RvdGFsX2xpbmVzGAYgASgEIjwKG0ZzUmVhZFdvcmtzcGFjZUltYWdlUmVxdWVzdBIPCgd3b3JrZGlyGAEgASgJEgwKBHBhdGgYAiABKAkiiQEKHEZzUmVhZFdvcmtzcGFjZUltYWdlUmVzcG9uc2USDAoEcGF0aBgBIAEoCRIRCgltaW1lX3R5cGUYAiABKAkSDAoEZGF0YRgDIAEoCRISCgpzaXplX2J5dGVzGAQgASgEEhAKCG10aW1lX21zGAUgASgEEhQKDGNvbnRlbnRfaGFzaBgGIAEoCSLbAQoTQ2hhdEZpbGVPcGVuUmVxdWVzdBIXCg9jb252ZXJzYXRpb25faWQYASABKAkSDwoHd29ya2RpchgCIAEoCRIMCgRwYXRoGAMgASgJEg4KBnNvdXJjZRgEIAEoCRIRCgRsaW5lGAUgASgNSACIAQESFQoIZW5kX2xpbmUYBiABKA1IAYgBARITCgZjb2x1bW4YByABKA1IAogBARIcChRvcGVuX2luX2ZpbGVfbWFuYWdlchgIIAEoCEIHCgVfbGluZUILCglfZW5kX2xpbmVCCQoHX2NvbHVtbiLOAQoUQ2hhdEZpbGVPcGVuUmVzcG9uc2USDgoGYWN0aW9uGAEgASgJEgwKBGtpbmQYAiABKAkSDwoHd29ya2RpchgDIAEoCRIMCgRwYXRoGAQgASgJEhEKBGxpbmUYBSABKA1IAIgBARIVCghlbmRfbGluZRgGIAEoDUgBiAEBEhMKBmNvbHVtbhgHIAEoDUgCiAEBEhkKEW91dHNpZGVfd29ya3NwYWNlGAggASgIQgcKBV9saW5lQgsKCV9lbmRfbGluZUIJCgdfY29sdW1uIs4BChJGc1dyaXRlVGV4dFJlcXVlc3QSDwoHd29ya2RpchgBIAEoCRIMCgRwYXRoGAIgASgJEg8KB2NvbnRlbnQYAyABKAkSDAoEbW9kZRgEIAEoCRIZChFleHBlY3RlZF9tdGltZV9tcxgFIAEoBBIdChVleHBlY3RlZF9jb250ZW50X2hhc2gYBiABKAkSHQoVaGFzX2V4cGVjdGVkX210aW1lX21zGAcgASgIEiEKGWhhc19leHBlY3RlZF9jb250ZW50X2hhc2gYCCABKAginQEKE0ZzV3JpdGVUZXh0UmVzcG9uc2USDAoEcGF0aBgBIAEoCRIMCgRtb2RlGAIgASgJEhYKDmV4aXN0ZWRfYmVmb3JlGAMgASgIEhUKDWJ5dGVzX3dyaXR0ZW4YBCABKAQSEAoIbXRpbWVfbXMYBSABKAQSFAoMY29udGVudF9oYXNoGAYgASgJEhMKC3RvdGFsX2xpbmVzGAcgASgEIjMKEkZzQ3JlYXRlRGlyUmVxdWVzdBIPCgd3b3JrZGlyGAEgASgJEgwKBHBhdGgYAiABKAkiMQoTRnNDcmVhdGVEaXJSZXNwb25zZRIMCgRwYXRoGAEgASgJEgwKBGtpbmQYAiABKAkiRgoPRnNSZW5hbWVSZXF1ZXN0Eg8KB3dvcmtkaXIYASABKAkSEQoJZnJvbV9wYXRoGAIgASgJEg8KB3RvX3BhdGgYAyABKAkiQQoQRnNSZW5hbWVSZXNwb25zZRIRCglmcm9tX3BhdGgYASABKAkSDAoEcGF0aBgCIAEoCRIMCgRraW5kGAMgASgJIjAKD0ZzRGVsZXRlUmVxdWVzdBIPCgd3b3JrZGlyGAEgASgJEgwKBHBhdGgYAiABKAkiLgoQRnNEZWxldGVSZXNwb25zZRIMCgRwYXRoGAEgASgJEgwKBGtpbmQYAiABKAkiIAoLUGluZ1JlcXVlc3QSEQoJdGltZXN0YW1wGAEgASgDIiEKDFBvbmdSZXNwb25zZRIRCgl0aW1lc3RhbXAYASABKAMiLgoNRXJyb3JSZXNwb25zZRIMCgRjb2RlGAEgASgFEg8KB21lc3NhZ2UYAiABKAkiawoVUHJvdmlkZXJNb2RlbHNSZXF1ZXN0EhUKDXByb3ZpZGVyX3R5cGUYASABKAkSEAoIYmFzZV91cmwYAiABKAkSDwoHYXBpX2tleRgDIAEoCRIYChB1c2Vfc3lzdGVtX3Byb3h5GAQgASgIIi0KFlByb3ZpZGVyTW9kZWxzUmVzcG9uc2USEwoLbW9kZWxzX2pzb24YASABKAkiUQoUUHJvdmlkZXJVc2FnZVJlcXVlc3QSEwoLcHJvdmlkZXJfaWQYASABKAkSDwoHcmVmcmVzaBgCIAEoCBITCgtjb25maWdfanNvbhgDIAEoCSIsChVQcm92aWRlclVzYWdlUmVzcG9uc2USEwoLcmVzdWx0X2pzb24YASABKAkiiAEKEENoYXRJbmdyZXNzQmF0Y2gSDgoGcnVuX2lkGAEgASgJEhcKD2NvbnZlcnNhdGlvbl9pZBgCIAEoCRIRCglmaXJzdF9zZXEYAyABKAQSOAoHcmVjb3JkcxgEIAMoCzInLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRJbmdyZXNzUmVjb3JkIpoCChFDaGF0SW5ncmVzc1JlY29yZBI3CgVkZWx0YRgBIAEoCzImLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRJbmdyZXNzRGVsdGFIABJBCgpjaGVja3BvaW50GAIgASgLMisubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdEluZ3Jlc3NDaGVja3BvaW50SAASPQoIdGVybWluYWwYAyABKAsyKS5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0SW5ncmVzc1Rlcm1pbmFsSAASPwoJaGVhcnRiZWF0GAQgASgLMioubGl2ZWFnZW50LmdhdGV3YXkudjIuQ2hhdEluZ3Jlc3NIZWFydGJlYXRIAEIJCgdwYXlsb2FkIjkKEENoYXRJbmdyZXNzRGVsdGESEgoKZXZlbnRfanNvbhgBIAEoCRIRCgl3b3JrZXJfaWQYAiABKAkiKgoUQ2hhdEluZ3Jlc3NIZWFydGJlYXQSEgoKdXBkYXRlZF9hdBgBIAEoAyLEAQoVQ2hhdEluZ3Jlc3NDaGVja3BvaW50EhoKEmNvdmVyc190aHJvdWdoX3NlcRgBIAEoBBIQCghyZXZpc2lvbhgCIAEoBBIdChVjb21wcmVzc2VkX3Byb2plY3Rpb24YAyABKAwSGgoSdW5jb21wcmVzc2VkX2J5dGVzGAQgASgEEg4KBnNoYTI1NhgFIAEoCRIYChBjb250ZW50X2NvbXBsZXRlGAYgASgIEhgKEGhpc3RvcnlfcmVxdWlyZWQYByABKAgi/AEKE0NoYXRJbmdyZXNzVGVybWluYWwSGgoSY292ZXJzX3Rocm91Z2hfc2VxGAEgASgEEhAKCHJldmlzaW9uGAIgASgEEh0KFWNvbXByZXNzZWRfcHJvamVjdGlvbhgDIAEoDBIaChJ1bmNvbXByZXNzZWRfYnl0ZXMYBCABKAQSDgoGc2hhMjU2GAUgASgJEhgKEGNvbnRlbnRfY29tcGxldGUYBiABKAgSGAoQaGlzdG9yeV9yZXF1aXJlZBgHIAEoCBINCgVzdGF0ZRgIIAEoCRISCgplcnJvcl9jb2RlGAkgASgJEhUKDWVycm9yX21lc3NhZ2UYCiABKAkiTQoRQ2hhdEluZ3Jlc3NSZXN1bWUSOAoEcnVucxgBIAMoCzIqLmxpdmVhZ2VudC5nYXRld2F5LnYyLkNoYXRJbmdyZXNzUnVuUmVzdW1lItUBChRDaGF0SW5ncmVzc1J1blJlc3VtZRIOCgZydW5faWQYASABKAkSFwoPY29udmVyc2F0aW9uX2lkGAIgASgJEhcKD3JlcGxheV9mcm9tX3NlcRgDIAEoBBIaChJyZXBsYXlfdGhyb3VnaF9zZXEYBCABKAQSEAoIbmV4dF9zZXEYBSABKAQSHQoVbGF0ZXN0X2NoZWNrcG9pbnRfc2VxGAYgASgEEhQKDHRlcm1pbmFsX3NlcRgHIAEoBBIYChB0ZXJtaW5hbF9wZW5kaW5nGAggASgIIs4BChNDaGF0SW5ncmVzc0ZyYWdtZW50Eg4KBnJ1bl9pZBgBIAEoCRIXCg9jb252ZXJzYXRpb25faWQYAiABKAkSEgoKc291cmNlX3NlcRgDIAEoBBIWCg5mcmFnbWVudF9pbmRleBgEIAEoDRIWCg5mcmFnbWVudF9jb3VudBgFIAEoDRIcChRlbmNvZGVkX3JlY29yZF9jaHVuaxgGIAEoDBIcChRlbmNvZGVkX3JlY29yZF9ieXRlcxgHIAEoBBIOCgZzaGEyNTYYCCABKAki3AIKDkNoYXRJbmdyZXNzQWNrEg4KBnJ1bl9pZBgBIAEoCRIXCg9jb252ZXJzYXRpb25faWQYAiABKAkSGQoRY29tbWl0dGVkX3Rocm91Z2gYAyABKAQSFQoNZXhwZWN0ZWRfbmV4dBgEIAEoBBI7CgZhY3Rpb24YBSABKA4yKy5saXZlYWdlbnQuZ2F0ZXdheS52Mi5DaGF0SW5ncmVzc0Fjay5BY3Rpb24SGgoSdGVybWluYWxfY29tbWl0dGVkGAYgASgIEhIKCmVycm9yX2NvZGUYByABKAkSFQoNZXJyb3JfbWVzc2FnZRgIIAEoCSJrCgZBY3Rpb24SFgoSQUNUSU9OX1VOU1BFQ0lGSUVEEAASDAoIQ09OVElOVUUQARIYChRSRVBMQVlfRlJPTV9FWFBFQ1RFRBACEhMKD1NFTkRfQ0hFQ0tQT0lOVBADEgwKCFJFSkVDVEVEEAQiPAoUSGlzdG9yeVNldEN3ZFJlcXVlc3QSFwoPY29udmVyc2F0aW9uX2lkGAEgASgJEgsKA2N3ZBgCIAEoCSJYChVIaXN0b3J5U2V0Q3dkUmVzcG9uc2USPwoMY29udmVyc2F0aW9uGAEgASgLMikubGl2ZWFnZW50LmdhdGV3YXkudjIuQ29udmVyc2F0aW9uU3VtbWFyeSrGBAoPVHVubmVsRnJhbWVLaW5kEiEKHVRVTk5FTF9GUkFNRV9LSU5EX1VOU1BFQ0lGSUVEEAASKAokVFVOTkVMX0ZSQU1FX0tJTkRfSFRUUF9SRVFVRVNUX1NUQVJUEAESJwojVFVOTkVMX0ZSQU1FX0tJTkRfSFRUUF9SRVFVRVNUX0JPRFkQAhImCiJUVU5ORUxfRlJBTUVfS0lORF9IVFRQX1JFUVVFU1RfRU5EEAMSKQolVFVOTkVMX0ZSQU1FX0tJTkRfSFRUUF9SRVNQT05TRV9TVEFSVBAEEigKJFRVTk5FTF9GUkFNRV9LSU5EX0hUVFBfUkVTUE9OU0VfQk9EWRAFEicKI1RVTk5FTF9GUkFNRV9LSU5EX0hUVFBfUkVTUE9OU0VfRU5EEAYSHQoZVFVOTkVMX0ZSQU1FX0tJTkRfV1NfRElBTBAHEiAKHFRVTk5FTF9GUkFNRV9LSU5EX1dTX0RJQUxfT0sQCBIjCh9UVU5ORUxfRlJBTUVfS0lORF9XU19ESUFMX0VSUk9SEAkSHgoaVFVOTkVMX0ZSQU1FX0tJTkRfV1NfRlJBTUUQChIeChpUVU5ORUxfRlJBTUVfS0lORF9XU19DTE9TRRALEhsKF1RVTk5FTF9GUkFNRV9LSU5EX0VSUk9SEAwSHAoYVFVOTkVMX0ZSQU1FX0tJTkRfQ0FOQ0VMEA0SGgoWVFVOTkVMX0ZSQU1FX0tJTkRfUElORxAOEhoKFlRVTk5FTF9GUkFNRV9LSU5EX1BPTkcQDyqBAQoTVHVubmVsV3NNZXNzYWdlVHlwZRImCiJUVU5ORUxfV1NfTUVTU0FHRV9UWVBFX1VOU1BFQ0lGSUVEEAASHwobVFVOTkVMX1dTX01FU1NBR0VfVFlQRV9URVhUEAESIQodVFVOTkVMX1dTX01FU1NBR0VfVFlQRV9CSU5BUlkQAkJAWj5naXRodWIuY29tL2xpdmVhZ2VudC9hZ2VudC1nYXRld2F5L2ludGVybmFsL3Byb3RvL3YyO2dhdGV3YXl2MmIGcHJvdG8z"); /** * @generated from message liveagent.gateway.v2.GatewayEnvelope @@ -323,6 +323,12 @@ export type GatewayEnvelope = Message<"liveagent.gateway.v2.GatewayEnvelope"> & */ value: ChatFileOpenRequest; case: "chatFileOpen"; + } | { + /** + * @generated from field: liveagent.gateway.v2.HistorySetCwdRequest history_set_cwd = 95; + */ + value: HistorySetCwdRequest; + case: "historySetCwd"; } | { case: undefined; value?: undefined }; }; @@ -722,6 +728,12 @@ export type AgentEnvelope = Message<"liveagent.gateway.v2.AgentEnvelope"> & { */ value: ErrorResponse; case: "error"; + } | { + /** + * @generated from field: liveagent.gateway.v2.HistorySetCwdResponse history_set_cwd_resp = 100; + */ + value: HistorySetCwdResponse; + case: "historySetCwdResp"; } | { case: undefined; value?: undefined }; }; @@ -5801,6 +5813,45 @@ export enum ChatIngressAck_Action { export const ChatIngressAck_ActionSchema: GenEnum = /*@__PURE__*/ enumDesc(file_proto_v2_gateway, 150, 0); +/** + * @generated from message liveagent.gateway.v2.HistorySetCwdRequest + */ +export type HistorySetCwdRequest = Message<"liveagent.gateway.v2.HistorySetCwdRequest"> & { + /** + * @generated from field: string conversation_id = 1; + */ + conversationId: string; + + /** + * @generated from field: string cwd = 2; + */ + cwd: string; +}; + +/** + * Describes the message liveagent.gateway.v2.HistorySetCwdRequest. + * Use `create(HistorySetCwdRequestSchema)` to create a new message. + */ +export const HistorySetCwdRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_proto_v2_gateway, 151); + +/** + * @generated from message liveagent.gateway.v2.HistorySetCwdResponse + */ +export type HistorySetCwdResponse = Message<"liveagent.gateway.v2.HistorySetCwdResponse"> & { + /** + * @generated from field: liveagent.gateway.v2.ConversationSummary conversation = 1; + */ + conversation?: ConversationSummary | undefined; +}; + +/** + * Describes the message liveagent.gateway.v2.HistorySetCwdResponse. + * Use `create(HistorySetCwdResponseSchema)` to create a new message. + */ +export const HistorySetCwdResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_proto_v2_gateway, 152); + /** * @generated from enum liveagent.gateway.v2.TunnelFrameKind */ diff --git a/crates/agent-gateway/web/src/lib/sidebar/webSidebarBackend.ts b/crates/agent-gateway/web/src/lib/sidebar/webSidebarBackend.ts index b8ab17a39..207e515f6 100644 --- a/crates/agent-gateway/web/src/lib/sidebar/webSidebarBackend.ts +++ b/crates/agent-gateway/web/src/lib/sidebar/webSidebarBackend.ts @@ -134,6 +134,7 @@ type WebSidebarApi = { listHistoryWorkdirs(): Promise; renameHistory(conversationId: string, title: string): Promise; pinHistory(conversationId: string, isPinned: boolean): Promise; + setHistoryCwd(conversationId: string, cwd: string): Promise; deleteHistory(conversationId: string): Promise; subscribeHistory(listener: (event: GatewayHistoryEvent) => void): () => void; subscribeConnection(listener: (connected: boolean) => void): () => void; @@ -187,6 +188,10 @@ export function createWebSidebarBackend(deps: WebSidebarBackendDeps): SidebarBac return normalizeGatewayConversationSummary(await api.pinHistory(id, isPinned)); }, + async setConversationCwd(id, cwd) { + return normalizeGatewayConversationSummary(await api.setHistoryCwd(id, cwd)); + }, + async deleteConversation(id) { await api.deleteHistory(id); }, @@ -329,6 +334,7 @@ export function createIdleSidebarBackend(): SidebarBackend { listWorkdirs: () => Promise.resolve([]), renameConversation: notReady, setConversationPinned: notReady, + setConversationCwd: notReady, deleteConversation: notReady, subscribeEvents: () => () => {}, getProtectedConversationIds: () => [], diff --git a/crates/agent-gateway/web/test/sidebar-store.test.mjs b/crates/agent-gateway/web/test/sidebar-store.test.mjs index d00e4f92d..92845ffcd 100644 --- a/crates/agent-gateway/web/test/sidebar-store.test.mjs +++ b/crates/agent-gateway/web/test/sidebar-store.test.mjs @@ -35,12 +35,22 @@ function createFakeBackend() { listError: null, workdirs: [], workdirsError: null, - calls: { list: [], workdirs: 0, rename: [], pin: [], delete: [], subscribes: 0, unsubscribes: 0 }, + calls: { + list: [], + workdirs: 0, + rename: [], + pin: [], + move: [], + delete: [], + subscribes: 0, + unsubscribes: 0, + }, listeners: new Set(), connectionListeners: new Set(), protectedIds: [], listImpl: null, renameImpl: null, + moveImpl: null, deleteImpl: null, }; @@ -81,6 +91,13 @@ function createFakeBackend() { state.calls.pin.push({ id, isPinned }); return conversation(id, { isPinned, pinnedAt: isPinned ? 500 : null }); }, + setConversationCwd: async (id, cwd) => { + state.calls.move.push({ id, cwd }); + if (state.moveImpl) { + return state.moveImpl(id, cwd); + } + return conversation(id, { cwd }); + }, deleteConversation: async (id) => { state.calls.delete.push(id); if (state.deleteImpl) { @@ -277,6 +294,44 @@ test("rename is blocked for a running conversation without a backend call", asyn store.stop(); }); +test("move leaves the current scope optimistically, rolls back on failure, and blocks running rows", async () => { + const fake = createFakeBackend(); + fake.state.pages.set("cwd:/tmp/a", [ + conversation("one", { cwd: "/tmp/a", updatedAt: 20 }), + conversation("two", { cwd: "/tmp/a", updatedAt: 10 }), + ]); + const store = createSidebarStore(fake.backend); + store.setScope(SCOPE_A); + store.start(); + await tick(); + + let resolveMove; + fake.state.moveImpl = (id, cwd) => + new Promise((resolve) => { + resolveMove = () => resolve(conversation(id, { cwd })); + }); + const moving = store.setCwd("one", "/tmp/b"); + assert.deepEqual( + store.getSnapshot().conversations.map((item) => item.id), + ["two"], + ); + assert.equal(store.getSnapshot().mutations.get("one"), "move"); + resolveMove(); + assert.equal(await moving, true); + assert.deepEqual(fake.state.calls.move, [{ id: "one", cwd: "/tmp/b" }]); + + fake.state.moveImpl = () => Promise.reject(new Error("move failed")); + assert.equal(await store.setCwd("two", "/tmp/b"), false); + assert.equal(store.getSnapshot().conversations[0].cwd, "/tmp/a"); + assert.equal(store.getSnapshot().mutationErrors.get("two"), "moveFailed"); + + store.applyRunningPatch({ conversationId: "two", running: true, workdir: "/tmp/a" }); + assert.equal(await store.setCwd("two", "/tmp/b"), false); + assert.equal(store.getSnapshot().mutationErrors.get("two"), "moveBlockedRunning"); + assert.equal(fake.state.calls.move.length, 2); + store.stop(); +}); + test("idle tombstone rejects stale running snapshots", () => { const fake = createFakeBackend(); const store = createSidebarStore(fake.backend, { now: () => 999 }); diff --git a/crates/agent-gateway/web/test/web-sidebar-backend.test.mjs b/crates/agent-gateway/web/test/web-sidebar-backend.test.mjs index c55345efb..3f9d09798 100644 --- a/crates/agent-gateway/web/test/web-sidebar-backend.test.mjs +++ b/crates/agent-gateway/web/test/web-sidebar-backend.test.mjs @@ -43,7 +43,7 @@ function createFakeApi() { historyListeners: new Set(), connectionListeners: new Set(), statusListeners: new Set(), - calls: { list: [], workdirs: 0, rename: [], pin: [], delete: [] }, + calls: { list: [], workdirs: 0, rename: [], pin: [], move: [], delete: [] }, }; const api = { listHistory: async (page, pageSize, filter) => { @@ -62,6 +62,10 @@ function createFakeApi() { state.calls.pin.push({ id, isPinned }); return summary(id, { is_pinned: isPinned, pinned_at: isPinned ? SECONDS : 0 }); }, + setHistoryCwd: async (id, cwd) => { + state.calls.move.push({ id, cwd }); + return summary(id, { cwd }); + }, deleteHistory: async (id) => { state.calls.delete.push(id); }, @@ -339,6 +343,10 @@ test("mutations delegate to the gateway api and normalize returned summaries", a assert.equal(pinned.isPinned, true); assert.equal(pinned.pinnedAt, MILLIS); + const moved = await backend.setConversationCwd("c1", "/tmp/b"); + assert.deepEqual(state.calls.move, [{ id: "c1", cwd: "/tmp/b" }]); + assert.equal(moved.cwd, "/tmp/b"); + await backend.deleteConversation("c1"); assert.deepEqual(state.calls.delete, ["c1"]); diff --git a/crates/agent-gui/src-tauri/src/commands/history/chat_history/commands.rs b/crates/agent-gui/src-tauri/src/commands/history/chat_history/commands.rs index cd4355afc..94a1a5949 100644 --- a/crates/agent-gui/src-tauri/src/commands/history/chat_history/commands.rs +++ b/crates/agent-gui/src-tauri/src/commands/history/chat_history/commands.rs @@ -410,6 +410,31 @@ pub async fn chat_history_set_pinned( Ok(summary) } +pub(crate) async fn chat_history_set_cwd_inner( + id: String, + cwd: String, +) -> Result { + tauri::async_runtime::spawn_blocking(move || { + let conn = open_db()?; + set_chat_history_cwd_sync(&conn, &id, &cwd) + }) + .await + .map_err(|e| format!("chat_history_set_cwd join 失败:{e}"))? +} + +#[tauri::command] +pub async fn chat_history_set_cwd( + id: String, + cwd: String, + gateway_controller: tauri::State<'_, Arc>, +) -> Result { + let summary = chat_history_set_cwd_inner(id, cwd).await?; + gateway_controller + .publish_history_sync(build_history_sync_upsert(&summary)) + .await; + Ok(summary) +} + pub(crate) async fn chat_history_set_model_inner( id: String, selected_model_json: String, diff --git a/crates/agent-gui/src-tauri/src/commands/history/chat_history/segments.rs b/crates/agent-gui/src-tauri/src/commands/history/chat_history/segments.rs index f2ead519c..63460fcb6 100644 --- a/crates/agent-gui/src-tauri/src/commands/history/chat_history/segments.rs +++ b/crates/agent-gui/src-tauri/src/commands/history/chat_history/segments.rs @@ -526,6 +526,39 @@ fn set_chat_history_pinned_sync( get_summary_by_id(conn, chat_id) } + +fn set_chat_history_cwd_sync( + conn: &Connection, + id: &str, + cwd: &str, +) -> Result { + let chat_id = id.trim(); + if chat_id.is_empty() { + return Err("历史对话 id 不能为空".to_string()); + } + + let target = cwd.trim(); + if target.is_empty() { + return Err("目标工作空间不能为空".to_string()); + } + + let affected = conn + .execute( + " + UPDATE chatHistory + SET cwd = ?1 + WHERE id = ?2 + ", + params![target, chat_id], + ) + .map_err(|e| format!("更新历史对话工作空间失败:{e}"))?; + + if affected == 0 { + return Err("未找到对应的历史对话".to_string()); + } + + get_summary_by_id(conn, chat_id) +} fn rename_chat_history_sync( conn: &Connection, id: &str, diff --git a/crates/agent-gui/src-tauri/src/commands/history/chat_history/tests.rs b/crates/agent-gui/src-tauri/src/commands/history/chat_history/tests.rs index 3833f93f4..9d1fc26c0 100644 --- a/crates/agent-gui/src-tauri/src/commands/history/chat_history/tests.rs +++ b/crates/agent-gui/src-tauri/src/commands/history/chat_history/tests.rs @@ -526,6 +526,43 @@ mod tests { assert_eq!(summary.title, "Updated Conversation"); } + #[test] + fn set_cwd_moves_conversation_between_workspaces() { + let conn = open_test_db().expect("open test db"); + let mut conversation = sample_conversation(); + conversation.cwd = Some("/tmp/project-a".to_string()); + upsert_chat_history_header(&conn, &conversation).expect("upsert header"); + + let summary = set_chat_history_cwd_sync(&conn, "conv-1", "/tmp/project-b") + .expect("move conversation to another workspace"); + assert_eq!(summary.cwd.as_deref(), Some("/tmp/project-b")); + + let reloaded = get_summary_by_id(&conn, "conv-1").expect("reload summary"); + assert_eq!(reloaded.cwd.as_deref(), Some("/tmp/project-b")); + + // Deletes the conversation from the workdirs grouping of the old cwd. + let workdirs = list_chat_history_workdirs_sync(&conn).expect("list workdirs"); + assert_eq!(workdirs.workdirs.len(), 1); + assert_eq!(workdirs.workdirs[0].path, "/tmp/project-b"); + } + + #[test] + fn set_cwd_rejects_empty_target_or_missing_conversation() { + let conn = open_test_db().expect("open test db"); + let mut conversation = sample_conversation(); + conversation.cwd = Some("/tmp/project-a".to_string()); + upsert_chat_history_header(&conn, &conversation).expect("upsert header"); + + let empty_target = + set_chat_history_cwd_sync(&conn, "conv-1", " ").expect_err("reject empty target"); + assert!(empty_target.contains("工作空间")); + + let missing = + set_chat_history_cwd_sync(&conn, "does-not-exist", "/tmp/project-b") + .expect_err("reject missing conversation"); + assert!(missing.contains("未找到")); + } + #[test] fn v1_database_gains_selected_model_column_via_v2_migration() { // 复现存量库场景:完整的 v1 schema(无 selected_model_json)且 diff --git a/crates/agent-gui/src-tauri/src/lib.rs b/crates/agent-gui/src-tauri/src/lib.rs index 24de3b4cb..bc80581c1 100644 --- a/crates/agent-gui/src-tauri/src/lib.rs +++ b/crates/agent-gui/src-tauri/src/lib.rs @@ -54,6 +54,7 @@ macro_rules! app_invoke_handler { commands::chat_history::chat_history_replace_from_message, commands::chat_history::chat_history_set_pinned, commands::chat_history::chat_history_set_model, + commands::chat_history::chat_history_set_cwd, commands::chat_history::chat_history_share_get, commands::chat_history::chat_history_share_set, commands::chat_history::chat_history_delete, diff --git a/crates/agent-gui/src-tauri/src/services/gateway/envelope_handler.rs b/crates/agent-gui/src-tauri/src/services/gateway/envelope_handler.rs index e37b6afff..3906d67d0 100644 --- a/crates/agent-gui/src-tauri/src/services/gateway/envelope_handler.rs +++ b/crates/agent-gui/src-tauri/src/services/gateway/envelope_handler.rs @@ -320,6 +320,40 @@ impl GatewayController { }); Ok(()) } + Some(proto::gateway_envelope::Payload::HistorySetCwd(request)) => { + let controller = Arc::clone(self); + tauri::async_runtime::spawn(async move { + let result = match gateway_bridge::handle_history_set_cwd(request).await { + Ok(response) => { + if let Some(conversation) = response.conversation.as_ref() { + controller + .publish_history_sync(build_history_sync_upsert_from_proto( + conversation, + )) + .await; + } + controller + .send_agent_envelope(proto::AgentEnvelope { + request_id: request_id.clone(), + timestamp: now_unix_seconds(), + payload: Some( + proto::agent_envelope::Payload::HistorySetCwdResp(response), + ), + }) + .await + } + Err(error) => { + controller + .send_error_response(request_id.clone(), 500, error) + .await + } + }; + if let Err(err) = result { + eprintln!("gateway history.set_cwd handler failed: {err}"); + } + }); + Ok(()) + } Some(proto::gateway_envelope::Payload::HistoryShareGet(request)) => { let controller = Arc::clone(self); tauri::async_runtime::spawn(async move { diff --git a/crates/agent-gui/src-tauri/src/services/gateway_bridge.rs b/crates/agent-gui/src-tauri/src/services/gateway_bridge.rs index 5ee3dacd9..334f5430d 100644 --- a/crates/agent-gui/src-tauri/src/services/gateway_bridge.rs +++ b/crates/agent-gui/src-tauri/src/services/gateway_bridge.rs @@ -303,6 +303,17 @@ pub async fn handle_history_pin( }) } +pub async fn handle_history_set_cwd( + request: proto::HistorySetCwdRequest, +) -> Result { + let summary = + chat_history::chat_history_set_cwd_inner(request.conversation_id, request.cwd).await?; + + Ok(proto::HistorySetCwdResponse { + conversation: Some(build_proto_conversation_summary(summary)), + }) +} + pub async fn handle_history_share_get( request: proto::HistoryShareGetRequest, ) -> Result { diff --git a/crates/agent-gui/src/i18n/config.ts b/crates/agent-gui/src/i18n/config.ts index 05c69c337..c09034fe3 100644 --- a/crates/agent-gui/src/i18n/config.ts +++ b/crates/agent-gui/src/i18n/config.ts @@ -114,6 +114,7 @@ export const translations: Record> = { "chat.conversationPin": "置顶对话", "chat.conversationUnpin": "取消置顶", "chat.conversationRename": "修改标题", + "chat.conversationMoveToWorkspace": "移动到工作空间", "chat.conversationShare": "分享", "chat.conversationDelete": "删除对话", "chat.conversationDeleteConfirm": "删除「{title}」?", @@ -137,6 +138,8 @@ export const translations: Record> = { "chat.history.renameFailed": "修改历史对话标题失败", "chat.history.renameBlockedRunning": "后台任务仍在运行,暂时不能修改该对话标题。", "chat.history.pinFailed": "更新历史对话置顶状态失败", + "chat.history.moveFailed": "移动历史对话工作空间失败", + "chat.history.moveBlockedRunning": "后台任务仍在运行,暂时不能移动该对话。", "chat.history.deleteFailed": "删除历史对话失败", "chat.history.deleteBlockedRunning": "后台任务仍在运行,暂时不能删除该对话。", "chat.history.openFailed": "读取历史对话失败", @@ -2389,6 +2392,7 @@ export const translations: Record> = { "chat.conversationPin": "Pin conversation", "chat.conversationUnpin": "Unpin", "chat.conversationRename": "Rename", + "chat.conversationMoveToWorkspace": "Move to workspace", "chat.conversationShare": "Share", "chat.conversationDelete": "Delete conversation", "chat.conversationDeleteConfirm": 'Delete "{title}"?', @@ -2415,6 +2419,9 @@ export const translations: Record> = { "chat.history.renameBlockedRunning": "A background task is still running; the title cannot be changed yet.", "chat.history.pinFailed": "Failed to update pin state", + "chat.history.moveFailed": "Failed to move conversation to workspace", + "chat.history.moveBlockedRunning": + "A background task is still running; the conversation cannot be moved yet.", "chat.history.deleteFailed": "Failed to delete conversation", "chat.history.deleteBlockedRunning": "A background task is still running; the conversation cannot be deleted yet.", diff --git a/crates/agent-gui/src/lib/chat/history/chatHistory.ts b/crates/agent-gui/src/lib/chat/history/chatHistory.ts index 53b37b635..5c5e05863 100644 --- a/crates/agent-gui/src/lib/chat/history/chatHistory.ts +++ b/crates/agent-gui/src/lib/chat/history/chatHistory.ts @@ -478,6 +478,12 @@ export async function setChatHistoryModel(id: string, selectedModelJson: string) ); } +export async function setChatHistoryCwd(id: string, cwd: string) { + return withConversationWriteLock(id, () => + invoke("chat_history_set_cwd", { id, cwd }), + ); +} + export async function getChatHistoryShare(id: string) { return invoke("chat_history_share_get", { id }); } diff --git a/crates/agent-gui/src/lib/sidebar/guiSidebarBackend.ts b/crates/agent-gui/src/lib/sidebar/guiSidebarBackend.ts index ce44285a8..c16501605 100644 --- a/crates/agent-gui/src/lib/sidebar/guiSidebarBackend.ts +++ b/crates/agent-gui/src/lib/sidebar/guiSidebarBackend.ts @@ -11,6 +11,7 @@ import { listChatHistory, listChatHistoryWorkdirs, renameChatHistory, + setChatHistoryCwd, setChatHistoryPinned, } from "../chat/history/chatHistory"; @@ -77,6 +78,7 @@ export function createGuiSidebarBackend(): SidebarBackend { renameConversation: (id, title) => renameChatHistory(id, title), setConversationPinned: (id, isPinned) => setChatHistoryPinned(id, isPinned), + setConversationCwd: (id, cwd) => setChatHistoryCwd(id, cwd), deleteConversation: (id) => deleteChatHistory(id), subscribeEvents: (listener) => { diff --git a/crates/agent-gui/src/pages/chat/sidebar/ChatSidebarContainer.tsx b/crates/agent-gui/src/pages/chat/sidebar/ChatSidebarContainer.tsx index ade33bfdc..ee40faa5c 100644 --- a/crates/agent-gui/src/pages/chat/sidebar/ChatSidebarContainer.tsx +++ b/crates/agent-gui/src/pages/chat/sidebar/ChatSidebarContainer.tsx @@ -150,6 +150,27 @@ export function ChatSidebarContainer(props: ChatSidebarContainerProps) { [store], ); + const handleMoveToWorkspace = useCallback( + (id: string, cwd: string) => { + store.clearMutationError(id); + void store.setCwd(id, cwd); + }, + [store], + ); + + const handleMoveConversationsToWorkspace = useCallback( + async (ids: readonly string[], cwd: string) => { + const results = await Promise.all( + ids.map(async (id) => { + store.clearMutationError(id); + return { id, moved: await store.setCwd(id, cwd) }; + }), + ); + return results.filter((result) => !result.moved).map((result) => result.id); + }, + [store], + ); + const handleDeleteConversation = useCallback( (id: string) => { store.clearMutationError(id); @@ -249,6 +270,8 @@ export function ChatSidebarContainer(props: ChatSidebarContainerProps) { onCommitRename={handleCommitRename} onCancelRename={handleCancelRename} onSetPinned={handleSetPinned} + onMoveToWorkspace={handleMoveToWorkspace} + onMoveConversationsToWorkspace={handleMoveConversationsToWorkspace} canShareConversations={props.canShareConversations} sharedConversationCount={props.sharedConversationCount} onShareConversation={props.onShareConversation} diff --git a/crates/agent-gui/test/chat/sidebar-store.test.mjs b/crates/agent-gui/test/chat/sidebar-store.test.mjs index 44bf5c5f5..989ec95d1 100644 --- a/crates/agent-gui/test/chat/sidebar-store.test.mjs +++ b/crates/agent-gui/test/chat/sidebar-store.test.mjs @@ -31,12 +31,22 @@ function createFakeBackend() { listError: null, workdirs: [], workdirsError: null, - calls: { list: [], workdirs: 0, rename: [], pin: [], delete: [], subscribes: 0, unsubscribes: 0 }, + calls: { + list: [], + workdirs: 0, + rename: [], + pin: [], + move: [], + delete: [], + subscribes: 0, + unsubscribes: 0, + }, listeners: new Set(), connectionListeners: new Set(), protectedIds: [], listImpl: null, renameImpl: null, + moveImpl: null, deleteImpl: null, }; @@ -77,6 +87,13 @@ function createFakeBackend() { state.calls.pin.push({ id, isPinned }); return conversation(id, { isPinned, pinnedAt: isPinned ? 500 : null }); }, + setConversationCwd: async (id, cwd) => { + state.calls.move.push({ id, cwd }); + if (state.moveImpl) { + return state.moveImpl(id, cwd); + } + return conversation(id, { cwd }); + }, deleteConversation: async (id) => { state.calls.delete.push(id); if (state.deleteImpl) { @@ -273,6 +290,44 @@ test("rename is blocked for a running conversation without a backend call", asyn store.stop(); }); +test("move leaves the current scope optimistically, rolls back on failure, and blocks running rows", async () => { + const fake = createFakeBackend(); + fake.state.pages.set("cwd:/tmp/a", [ + conversation("one", { cwd: "/tmp/a", updatedAt: 20 }), + conversation("two", { cwd: "/tmp/a", updatedAt: 10 }), + ]); + const store = createSidebarStore(fake.backend); + store.setScope(SCOPE_A); + store.start(); + await tick(); + + let resolveMove; + fake.state.moveImpl = (id, cwd) => + new Promise((resolve) => { + resolveMove = () => resolve(conversation(id, { cwd })); + }); + const moving = store.setCwd("one", "/tmp/b"); + assert.deepEqual( + store.getSnapshot().conversations.map((item) => item.id), + ["two"], + ); + assert.equal(store.getSnapshot().mutations.get("one"), "move"); + resolveMove(); + assert.equal(await moving, true); + assert.deepEqual(fake.state.calls.move, [{ id: "one", cwd: "/tmp/b" }]); + + fake.state.moveImpl = () => Promise.reject(new Error("move failed")); + assert.equal(await store.setCwd("two", "/tmp/b"), false); + assert.equal(store.getSnapshot().conversations[0].cwd, "/tmp/a"); + assert.equal(store.getSnapshot().mutationErrors.get("two"), "moveFailed"); + + store.applyRunningPatch({ conversationId: "two", running: true, workdir: "/tmp/a" }); + assert.equal(await store.setCwd("two", "/tmp/b"), false); + assert.equal(store.getSnapshot().mutationErrors.get("two"), "moveBlockedRunning"); + assert.equal(fake.state.calls.move.length, 2); + store.stop(); +}); + test("mutations are tracked per row, not globally", async () => { const fake = createFakeBackend(); fake.state.pages.set("cwd:/tmp/a", [ diff --git a/crates/agent-ui/src/components/chat/ChatHistorySidebar.tsx b/crates/agent-ui/src/components/chat/ChatHistorySidebar.tsx index 5d30e2181..83082eab2 100644 --- a/crates/agent-ui/src/components/chat/ChatHistorySidebar.tsx +++ b/crates/agent-ui/src/components/chat/ChatHistorySidebar.tsx @@ -9,6 +9,7 @@ import { ChevronRight, CirclePlus, Edit3, + Folder, FolderClosed, FolderOpen, FolderTree, @@ -35,6 +36,9 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, DropdownMenuTrigger, } from "@liveagent/ui/components/ui/dropdown-menu"; import { Input } from "@liveagent/ui/components/ui/input"; @@ -66,7 +70,7 @@ import { import type { SidebarConversation } from "../../lib/sidebar/types"; export type ChatHistorySidebarListStatus = "initial" | "loading" | "syncing" | "ready"; -export type ChatHistorySidebarMutationKind = "rename" | "pin" | "delete"; +export type ChatHistorySidebarMutationKind = "rename" | "pin" | "move" | "delete"; type ChatHistorySidebarProps = { items: readonly SidebarConversation[]; @@ -131,6 +135,11 @@ type ChatHistorySidebarProps = { onCommitRename: () => void; onCancelRename: () => void; onSetPinned: (id: string, isPinned: boolean) => void; + onMoveToWorkspace: (id: string, cwd: string) => void; + onMoveConversationsToWorkspace: ( + ids: readonly string[], + cwd: string, + ) => Promise; canShareConversations: boolean; sharedConversationCount: number; onShareConversation: (item: SidebarConversation) => void; @@ -224,6 +233,8 @@ type HistoryRowProps = { onCommitRename: () => void; onCancelRename: () => void; onSetPinned: (id: string, isPinned: boolean) => void; + onMoveToWorkspace: (id: string, cwd: string) => void; + moveWorkspaces: readonly WorkspaceProject[]; onShareConversation: (item: SidebarConversation) => void; onDeleteConversation: (id: string) => void; onSetPendingDelete: (id: string | null) => void; @@ -238,6 +249,7 @@ function areRenderedHistoryItemsEqual(previous: SidebarConversation, next: Sideb return ( previous.id === next.id && previous.title === next.title && + previous.cwd === next.cwd && previous.isPinned === next.isPinned && previous.isShared === next.isShared && previous.isPending === next.isPending @@ -268,6 +280,8 @@ function areHistoryRowPropsEqual(previous: HistoryRowProps, next: HistoryRowProp previous.onCommitRename === next.onCommitRename && previous.onCancelRename === next.onCancelRename && previous.onSetPinned === next.onSetPinned && + previous.onMoveToWorkspace === next.onMoveToWorkspace && + previous.moveWorkspaces === next.moveWorkspaces && previous.onShareConversation === next.onShareConversation && previous.onDeleteConversation === next.onDeleteConversation && previous.onSetPendingDelete === next.onSetPendingDelete && @@ -299,6 +313,8 @@ const HistoryRow = memo(function HistoryRow(props: HistoryRowProps) { onCommitRename, onCancelRename, onSetPinned, + onMoveToWorkspace, + moveWorkspaces, onShareConversation, onDeleteConversation, onSetPendingDelete, @@ -375,6 +391,15 @@ const HistoryRow = memo(function HistoryRow(props: HistoryRowProps) { onSetPinned(item.id, item.isPinned !== true); }, [isInteractionDisabled, item.id, item.isPinned, onSetPinned]); + const handleMoveToWorkspace = useCallback( + (cwd: string) => { + if (!isInteractionDisabled) { + onMoveToWorkspace(item.id, cwd); + } + }, + [isInteractionDisabled, item.id, onMoveToWorkspace], + ); + const handleShare = useCallback(() => { if (isInteractionDisabled) { return; @@ -859,6 +884,35 @@ const HistoryRow = memo(function HistoryRow(props: HistoryRowProps) { {t("chat.conversationRename")} + + + + {t("chat.conversationMoveToWorkspace")} + + + {moveWorkspaces.map((workspace) => ( + handleMoveToWorkspace(workspace.path)} + className="gap-2" + > + + {workspace.path} + + ))} + + {canShareConversation && !item.isPending ? ( new Set(), ); const [isBulkDeleting, setIsBulkDeleting] = useState(false); + const [isBulkMoving, setIsBulkMoving] = useState(false); + const [bulkMoveMenuOpen, setBulkMoveMenuOpen] = useState(false); const [pendingProjectRemoveId, setPendingProjectRemoveId] = useState(null); const [showAllProjects, setShowAllProjects] = useState(false); const [openMenuId, setOpenMenuId] = useState(null); @@ -1502,6 +1560,7 @@ export const ChatHistorySidebar = memo(function ChatHistorySidebar(props: ChatHi // Bumped to invalidate an in-flight bulk delete: its shouldStop callback // starts returning true and its continuation stops touching state. const bulkDeleteRunRef = useRef(0); + const bulkMoveRunRef = useRef(0); const { confirm: requestBulkDeleteConfirm, dialog: bulkDeleteDialog } = useConfirmDialog(); const orderedConversationIds = useMemo(() => items.map((item) => item.id), [items]); const selectableConversationIds = useMemo( @@ -1544,6 +1603,19 @@ export const ChatHistorySidebar = memo(function ChatHistorySidebar(props: ChatHi onSetPinned(id, isPinned); } }); + const handleMoveToWorkspace = useStableEvent((id: string, cwd: string) => { + if (!sectionsDisabled) { + onMoveToWorkspace(id, cwd); + } + }); + const handleMoveConversationsToWorkspace = useStableEvent( + (ids: readonly string[], cwd: string) => { + if (sectionsDisabled) { + return Promise.resolve(ids); + } + return onMoveConversationsToWorkspace(ids, cwd); + }, + ); const handleShareConversation = useStableEvent((item: SidebarConversation) => { if (!sectionsDisabled) { onShareConversation(item); @@ -1661,7 +1733,10 @@ export const ChatHistorySidebar = memo(function ChatHistorySidebar(props: ChatHi }); const exitSelectionMode = useCallback(() => { bulkDeleteRunRef.current += 1; + bulkMoveRunRef.current += 1; setIsBulkDeleting(false); + setIsBulkMoving(false); + setBulkMoveMenuOpen(false); setSelectionMode(false); setSelectedConversationIds(new Set()); selectionAnchorRef.current = null; @@ -1759,6 +1834,35 @@ export const ChatHistorySidebar = memo(function ChatHistorySidebar(props: ChatHi } } }); + const handleBulkMove = useStableEvent(async (cwd: string) => { + const ids = orderedConversationIds.filter( + (id) => selectedConversationIds.has(id) && selectableConversationIds.has(id), + ); + if (ids.length === 0 || isBulkMoving || sectionsDisabled) { + return; + } + + const runId = bulkMoveRunRef.current + 1; + bulkMoveRunRef.current = runId; + setIsBulkMoving(true); + try { + const failedIds = await handleMoveConversationsToWorkspace(ids, cwd); + if (bulkMoveRunRef.current !== runId) { + return; + } + const orderedConversationIdSet = new Set(orderedConversationIds); + const remainingFailedIds = failedIds.filter((id) => orderedConversationIdSet.has(id)); + setSelectedConversationIds(new Set(remainingFailedIds)); + selectionAnchorRef.current = remainingFailedIds[0] ?? null; + if (remainingFailedIds.length === 0) { + setSelectionMode(false); + } + } finally { + if (bulkMoveRunRef.current === runId) { + setIsBulkMoving(false); + } + } + }); // Archived rows are split into their own collapsed group at the list end; // the render cap only applies to the active rows. const activeProjects = useMemo( @@ -2236,7 +2340,9 @@ export const ChatHistorySidebar = memo(function ChatHistorySidebar(props: ChatHi isPendingDelete={pendingDeleteId === item.id} isSelectionMode={selectionMode} isSelected={selectedConversationIds.has(item.id)} - isSelectionDisabled={isBulkDeleting || !selectableConversationIds.has(item.id)} + isSelectionDisabled={ + isBulkDeleting || isBulkMoving || !selectableConversationIds.has(item.id) + } isInteractionDisabled={sectionsDisabled} isMobileMenuLayout={isMobileMenuLayout} renameDraft={renamingId === item.id ? renameDraft : ""} @@ -2246,6 +2352,8 @@ export const ChatHistorySidebar = memo(function ChatHistorySidebar(props: ChatHi onCommitRename={handleCommitRename} onCancelRename={handleCancelRename} onSetPinned={handleSetPinned} + onMoveToWorkspace={handleMoveToWorkspace} + moveWorkspaces={activeProjects} onShareConversation={handleShareConversation} onDeleteConversation={handleDeleteConversation} onSetPendingDelete={handleSetPendingDelete} @@ -2265,14 +2373,17 @@ export const ChatHistorySidebar = memo(function ChatHistorySidebar(props: ChatHi handleMenuOpenChange, handleRenameDraftChange, handleSelectConversation, + handleMoveToWorkspace, handleSetPinned, handleSetPendingDelete, handleShareConversation, handleStartRenaming, busyConversationIds, canShareConversations, + activeProjects, enterSelectionMode, isBulkDeleting, + isBulkMoving, isMobileMenuLayout, menuSide, openMenuId, @@ -2641,13 +2752,65 @@ export const ChatHistorySidebar = memo(function ChatHistorySidebar(props: ChatHi
{selectionMode ? ( <> + { + if (!sectionsDisabled || !open) { + setBulkMoveMenuOpen(open); + } + }} + > + + {isBulkMoving ? ( + + ) : ( + + )} + + + {activeProjects.map((workspace) => ( + void handleBulkMove(workspace.path)} + className="gap-2" + > + + {workspace.path} + + ))} + +